Skip to content
Snippets Groups Projects
Commit b6ad4f75 authored by Matthias Konitzny's avatar Matthias Konitzny :fire:
Browse files

Re-implemented node and edge highlighting

parent 2baa1c30
No related branches found
No related tags found
1 merge request!3Master into new editor
import { LineMaterial } from "three-fatline";
export interface GraphNode extends NodeData {
x: number;
y: number;
z: number;
vx: number;
vy: number;
vz: number;
fx: number;
fy: number;
fz: number;
color: string;
}
export interface GraphLink extends LinkData {
material?: LineMaterial;
}
interface Link { interface Link {
source: string; source: string;
target: string; target: string;
......
...@@ -18,16 +18,28 @@ import { Line2, LineGeometry, LineMaterial } from "three-fatline"; ...@@ -18,16 +18,28 @@ import { Line2, LineGeometry, LineMaterial } from "three-fatline";
import React from "react"; import React from "react";
import PropTypes, { InferType } from "prop-types"; import PropTypes, { InferType } from "prop-types";
import SpriteText from "three-spritetext"; import SpriteText from "three-spritetext";
import { Object3D, Vector3 } from "three"; import { Object3D } from "three";
import Graph, { import Graph, { Coordinate, LinkData, NodeData } from "./graph";
Coordinate,
GraphLink,
GraphNode,
LinkData,
NodeData,
} from "./graph";
// import { graph } from "../editor/js/editor"; // import { graph } from "../editor/js/editor";
export interface GraphNode extends NodeData {
x: number;
y: number;
z: number;
vx: number;
vy: number;
vz: number;
fx: number;
fy: number;
fz: number;
color: string;
__threeObj: THREE.Group;
}
export interface GraphLink extends LinkData {
__lineObj?: Line2;
}
export class GraphRenderer extends React.Component< export class GraphRenderer extends React.Component<
InferType<typeof GraphRenderer.propTypes>, InferType<typeof GraphRenderer.propTypes>,
InferType<typeof GraphRenderer.stateTypes> InferType<typeof GraphRenderer.stateTypes>
...@@ -37,6 +49,9 @@ export class GraphRenderer extends React.Component< ...@@ -37,6 +49,9 @@ export class GraphRenderer extends React.Component<
forceGraph: React.RefObject<any>; // using typeof ForceGraph3d produces an error here... forceGraph: React.RefObject<any>; // using typeof ForceGraph3d produces an error here...
edgeColors: Map<string, string>; edgeColors: Map<string, string>;
nodeColors: Map<string, string>; nodeColors: Map<string, string>;
highlightedNodes: Set<GraphNode>;
highlightedLinks: Set<GraphLink>;
hoverNode: GraphNode;
static propTypes = { static propTypes = {
graph: PropTypes.instanceOf(Graph).isRequired, graph: PropTypes.instanceOf(Graph).isRequired,
...@@ -45,19 +60,14 @@ export class GraphRenderer extends React.Component< ...@@ -45,19 +60,14 @@ export class GraphRenderer extends React.Component<
isFullscreen: PropTypes.bool, isFullscreen: PropTypes.bool,
}; };
static stateTypes = { static stateTypes = {};
highlightedNodes: PropTypes.array,
highlightedLinks: PropTypes.array,
hoverNode: PropTypes.object,
};
constructor(props: InferType<typeof GraphRenderer.propTypes>) { constructor(props: InferType<typeof GraphRenderer.propTypes>) {
super(props); super(props);
this.state = {
highlightedNodes: [], this.highlightedNodes = new Set();
highlightedLinks: [], this.highlightedLinks = new Set();
hoverNode: null, this.hoverNode = null;
};
this.forceGraph = React.createRef(); this.forceGraph = React.createRef();
this.edgeColors = new Map<string, string>(); this.edgeColors = new Map<string, string>();
this.nodeColors = new Map<string, string>(); this.nodeColors = new Map<string, string>();
...@@ -98,6 +108,7 @@ export class GraphRenderer extends React.Component< ...@@ -98,6 +108,7 @@ export class GraphRenderer extends React.Component<
text.borderColor = "black"; text.borderColor = "black";
text.translateY(12); text.translateY(12);
text.material.opacity = 0.85; text.material.opacity = 0.85;
text.renderOrder = 999;
group.add(text); group.add(text);
// Draw node circle image // Draw node circle image
...@@ -118,7 +129,7 @@ export class GraphRenderer extends React.Component< ...@@ -118,7 +129,7 @@ export class GraphRenderer extends React.Component<
}); });
const sprite = new THREE.Sprite(material); const sprite = new THREE.Sprite(material);
sprite.renderOrder = 999; // This may not be optimal. But it allows us to render the sprite on top of everything else. //sprite.renderOrder = 999; // This may not be optimal. But it allows us to render the sprite on top of everything else.
sprite.scale.set(8, 8, 8); sprite.scale.set(8, 8, 8);
group.add(sprite); group.add(sprite);
...@@ -162,47 +173,62 @@ export class GraphRenderer extends React.Component< ...@@ -162,47 +173,62 @@ export class GraphRenderer extends React.Component<
onNodeHover(node: GraphNode) { onNodeHover(node: GraphNode) {
// no state change // no state change
if ( if (
(!node && !this.state.highlightNodes.size) || (!node && !this.highlightedNodes.size) ||
(node && this.state.hoverNode === node) (node && this.hoverNode === node)
) )
return; return;
const highlightNodes: Set<NodeData> = new Set<NodeData>(); const highlightedNodes = new Set<GraphNode>();
const highlightLinks: Set<LinkData> = new Set<LinkData>(); const highlightedLinks = new Set<GraphLink>();
if (node) { if (node) {
highlightNodes.add(node); highlightedNodes.add(node);
node.neighbors.forEach((neighbor) => highlightNodes.add(neighbor)); node.neighbors.forEach((neighbor) =>
node.links.forEach((link) => highlightLinks.add(link)); highlightedNodes.add(neighbor as GraphNode)
);
node.links.forEach((link) =>
highlightedLinks.add(link as GraphLink)
);
} }
this.setState({ this.hoverNode = node || null;
highlightedNodes: highlightNodes, this.updateHighlight(highlightedNodes, highlightedLinks);
highlightedLinks: highlightLinks,
hoverNode: node || null,
});
} }
onLinkHover(link: GraphLink, previousLink: GraphLink) { onLinkHover(link: GraphLink) {
const highlightNodes: Set<NodeData> = new Set<NodeData>(); const highlightedNodes = new Set<GraphNode>();
const highlightLinks: Set<LinkData> = new Set<LinkData>(); const highlightedLinks = new Set<GraphLink>();
if (previousLink && previousLink.material) { if (link && link.__lineObj) {
// A bit hacky, but the alternative would require additional data structures highlightedLinks.add(link);
previousLink.material.linewidth = Config.LINK_WIDTH; highlightedNodes.add(link.source as GraphNode);
highlightedNodes.add(link.target as GraphNode);
} }
if (link && link.material) { this.updateHighlight(highlightedNodes, highlightedLinks);
link.material.linewidth = Config.HOVER_LINK_WIDTH; }
highlightLinks.add(link); updateHighlight(
highlightNodes.add(link.source); highlightedNodes: Set<GraphNode>,
highlightNodes.add(link.target); highlightedLinks: Set<GraphLink>
} ) {
// Update Links
this.highlightedLinks.forEach(
(link) => (link.__lineObj.material.linewidth = Config.LINK_WIDTH)
);
this.highlightedLinks = highlightedLinks;
this.highlightedLinks.forEach(
(link) =>
(link.__lineObj.material.linewidth = Config.HOVER_LINK_WIDTH)
);
this.setState({ // Update Nodes
highlightedNodes: highlightNodes, this.highlightedNodes.forEach((node) => {
highlightedLinks: highlightLinks, node.__threeObj.children[1].scale.set(8, 8, 8);
});
this.highlightedNodes = highlightedNodes;
this.highlightedNodes.forEach((node) => {
node.__threeObj.children[1].scale.set(12, 12, 12);
}); });
} }
...@@ -296,7 +322,7 @@ export class GraphRenderer extends React.Component< ...@@ -296,7 +322,7 @@ export class GraphRenderer extends React.Component<
if (!(line instanceof Line2)) { if (!(line instanceof Line2)) {
return false; return false;
} }
console.log("Updating links"); // console.log("Updating links");
const startR = 4; const startR = 4;
const endR = 4; const endR = 4;
const lineLen = Math.sqrt( const lineLen = Math.sqrt(
...@@ -330,21 +356,17 @@ export class GraphRenderer extends React.Component< ...@@ -330,21 +356,17 @@ export class GraphRenderer extends React.Component<
ref={this.forceGraph} ref={this.forceGraph}
width={Helpers.getWidth()} // TODO: Replace Helpers downstream? This could be a prop width={Helpers.getWidth()} // TODO: Replace Helpers downstream? This could be a prop
height={Helpers.getHeight()} height={Helpers.getHeight()}
// extraRenderers={[new CSS3DRenderer()]}
graphData={this.props.graph} graphData={this.props.graph}
rendererConfig={{ antialias: true }} rendererConfig={{ antialias: true }}
nodeLabel={"hidden"} // nodeLabel={"hidden"}
// nodeThreeObjectExtend={false}
nodeThreeObject={(node: GraphNode) => this.drawNode(node)} nodeThreeObject={(node: GraphNode) => this.drawNode(node)}
linkThreeObject={(link: LinkData) => this.drawLink(link)} linkThreeObject={(link: LinkData) => this.drawLink(link)}
onNodeClick={(node: GraphNode) => this.onNodeClick(node)} onNodeClick={(node: GraphNode) => this.onNodeClick(node)}
//d3AlphaDecay={0.1} //d3AlphaDecay={0.1}
warmupTicks={150} warmupTicks={150}
cooldownTime={1000} // TODO: Do we want the simulation to unfreeze on node drag? cooldownTime={1000} // TODO: Do we want the simulation to unfreeze on node drag?
// onNodeHover={(node: GraphNode) => this.onNodeHover(node)} onNodeHover={(node: GraphNode) => this.onNodeHover(node)}
// onLinkHover={(link: GraphLink, previousLink: GraphLink) => onLinkHover={(link: GraphLink) => this.onLinkHover(link)}
// this.onLinkHover(link, previousLink)
// }
linkPositionUpdate={( linkPositionUpdate={(
line: Line2, line: Line2,
coords: { start: Coordinate; end: Coordinate } coords: { start: Coordinate; end: Coordinate }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment