diff --git a/display/graph.js b/display/graph.js index a906eddd46905ed612ff1758d3a7be2418070782..e54a14fe2a058a6bba0f85a87414caf5e048d6d1 100644 --- a/display/graph.js +++ b/display/graph.js @@ -29,6 +29,7 @@ class Graph { this.highlightLinks = new Set(); this.hoverNode = null; this.edgeColors = {}; + this.nodeColors = {}; this.idToNode = {}; this.firstTick = true; @@ -49,6 +50,7 @@ class Graph { this.gData = await loadGraphJson(spaceId); this.graph = ForceGraph3D({ extraRenderers: [new CSS2DRenderer(), new CSS3DRenderer()], + rendererConfig: { antialias: true }, })(document.getElementById("3d-graph")) .graphData(this.gData) .nodeLabel("id") @@ -64,7 +66,10 @@ class Graph { this.updateHighlight(); }) .onLinkHover((link) => this.onLinkHover(link)) - .linkColor((link) => this.getLinkColor(link)) + //.linkColor((link) => this.getLinkColor(link)) + .linkPositionUpdate((line, { start, end }) => + this.updateLinkPosition(line, start, end) + ) .linkOpacity(0.8) .nodeThreeObjectExtend(false) .nodeThreeObject((node) => this.drawNode(node)) @@ -79,11 +84,17 @@ class Graph { */ initializeModel() { if (this.firstTick) { - this.mapEdgeColors(); + // Initialize data structures + this.mapLinkColors(); + this.mapNodeColors(); this.updateNodeData(); this.updateNodeMap(); this.addBackground(); + // Can only be called after link colors have been mapped. + this.graph.linkThreeObject((link) => this.drawLink(link)); + + // Add overlay components loadComponents(); // Catch resize events @@ -131,7 +142,15 @@ class Graph { this.graph .graphData() .links.forEach((link) => linkClasses.push(link.type)); - return [...new Set(linkClasses)]; + return [...new Set(linkClasses)].map((c) => String(c)); + } + + getNodeClasses() { + const nodeClasses = []; + this.graph + .graphData() + .nodes.forEach((node) => nodeClasses.push(node.type)); + return [...new Set(nodeClasses)]; } onNodeHover(node) { @@ -308,7 +327,7 @@ class Graph { /** * Maps the colors of the color palette to the different edge types */ - mapEdgeColors() { + mapLinkColors() { const linkClasses = this.getLinkClasses(); for (let i = 0; i < linkClasses.length; i++) { this.edgeColors[linkClasses[i]] = @@ -316,6 +335,65 @@ class Graph { } } + mapNodeColors() { + const nodeClasses = this.getNodeClasses(); + this.nodeColors["undefined"] = Config.COLOR_PALETTE[0]; // Default color + for (let i = 0; i < nodeClasses.length; i++) { + this.nodeColors[nodeClasses[i]] = + Config.COLOR_PALETTE[i % Config.COLOR_PALETTE.length]; + } + } + + drawLink(link) { + const colors = new Float32Array( + [].concat( + ...[link.target, link.source] + .map((node) => this.nodeColors[node.type]) + .map((color) => color.replace(/[^\d,]/g, "").split(",")) // Extract rgb() color components + .map((rgb) => rgb.map((v) => v / 255)) + ) + ); + + const material = new THREE.LineBasicMaterial({ + vertexColors: THREE.VertexColors, + }); + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute( + "position", + new THREE.BufferAttribute(new Float32Array(2 * 3), 3) + ); + geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3)); + + return new THREE.Line(geometry, material); + } + + updateLinkPosition(line, start, end) { + const startR = 4; + const endR = 4; + // const startR = Graph.nodeRelSize(); + // const endR = Graph.nodeRelSize(); + const lineLen = Math.sqrt( + ["x", "y", "z"] + .map((dim) => Math.pow((end[dim] || 0) - (start[dim] || 0), 2)) + .reduce((acc, v) => acc + v, 0) + ); + + const linePos = line.geometry.getAttribute("position"); + + // calculate coordinate on the node's surface instead of center + linePos.set( + [startR / lineLen, 1 - endR / lineLen] + .map((t) => + ["x", "y", "z"].map( + (dim) => start[dim] + (end[dim] - start[dim]) * t + ) + ) + .flat() + ); + linePos.needsUpdate = true; + return true; + } + drawNode(node) { // Draw node as label + image const nodeDiv = document.createElement("div"); @@ -399,9 +477,8 @@ var infooverlay; // Only execute, if corresponding dom is present if (document.getElementById("3d-graph") !== null) { - G = new Graph(space_id); // space_id defined globaly through knowledge-space.php + G = new Graph(space_id); // space_id defined globaly through knowledge-space.php linkoverlay = new LinkSelectionOverlay(G); infooverlay = new NodeInfoOverlay(G); G.infooverlay = infooverlay; } -