From 02ae0c918c33bf8d55e298e3f79ceae1d904d292 Mon Sep 17 00:00:00 2001 From: Marcel Stimberg Date: Mon, 13 Jan 2025 18:10:36 +0100 Subject: [PATCH] feat: Create "meta-node" to avoid unconnected nodes in graph --- assets/cy-style.json | 9 +++++++++ graph.js | 37 +++++++++++++++++++++++++++++-------- index.js | 7 +++++-- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/assets/cy-style.json b/assets/cy-style.json index 8d1b4e0..d38be3e 100644 --- a/assets/cy-style.json +++ b/assets/cy-style.json @@ -21,6 +21,15 @@ "edge-text-rotation": "autorotate" } }, + { + "selector": ".meta", + "style": { + "background-color": "#fff", + "font-style": "italic", + "shape": "hexagon", + "border-width": "3px" + } +}, { "selector": ".standard", "style": { diff --git a/graph.js b/graph.js index e51457a..c091e9d 100644 --- a/graph.js +++ b/graph.js @@ -2,6 +2,8 @@ var elements = []; var cy; var cy_layout; var removed = []; +var meta_node; +var meta_node_edges; function selectionChanged() { removed.toReversed().forEach(eles => eles.restore()); @@ -28,9 +30,6 @@ function selectionChanged() { } function layoutNodes() { - unconnected = cy.filter(function(element, i) { - return element.isNode() && element.connectedEdges().length == 0 - }).remove(); cy_layout = cy.layout({ name: "cola", animate: "end", @@ -39,7 +38,6 @@ function layoutNodes() { nodeDimensionsIncludeLabels: true, centerGraph: false, }); - unconnected.forEach((eles, i) => {eles.restore(); eles.position("x", 250); eles.position("y", 200 + i*50);}); cy_layout.run(); } @@ -91,6 +89,12 @@ function urlButton(type, url) { } function highlightNode(node) { + if (node.id() == "simulators") { + return; + } + // Ignore the meta node + meta_node.deselect(); + meta_node.remove(); // change opacity if node or edge is not connected to the clicked node const nhood = node.closedNeighbourhood(); const connectedEdges = node.connectedEdges(); @@ -123,9 +127,13 @@ function highlightNode(node) { } function showNodeDetails(node) { - showDetails(node.data(), node.outgoers("edge").map((edge) => { - return {target: edge.target().id(), label: edge.data("label"), source: edge.source().id()}; - })); + if (node.id() == "simulators") { + showDetails(null, null); + } else { + showDetails(node.data(), node.outgoers("edge").map((edge) => { + return {target: edge.target().id(), label: edge.data("label"), source: edge.source().id()}; + })); + } } function highlightEdge(edge) { @@ -188,6 +196,10 @@ function highlightElement(event) { } function unhighlightNode(event) { + // Ignore the meta node + meta_node.restore(); + meta_node_edges.restore(); + // Re-add the edges cy.elements().forEach(n => n.style("opacity", 1)); showDetails(null, null); } @@ -222,9 +234,14 @@ function newEdge(name, relation) { } function create_cy_elements(data, style) { - console.log("Creating", data); + // Create a "meta-node" for all simulators + elements.push(newNode("simulators", {full_name: "Simulators", features: "meta"})); for (const [name, description] of Object.entries(data)) { elements.push(newNode(name, description)); + // Connect all simulators to the meta node + if (description["features"].includes("simulator")) { + elements.push(newEdge("simulators", {name: name, description: "simulator"})); + } if (description["relations"] !== undefined) { for (let relation of description["relations"]){ if (relation["description"] === undefined) @@ -239,7 +256,11 @@ function create_cy_elements(data, style) { layout: { name: 'random' }, style: style }); + // store the meta_node, since we need to remove it when highlighting nodes + meta_node = cy.$("#simulators"); + meta_node_edges = meta_node.connectedEdges(); cy.on("select tap dbltap", "*", highlightElement); cy.on("unselect", "*", unhighlightNode); + cy.$("#simulators").select(); selectionChanged(); } diff --git a/index.js b/index.js index 427090f..9d121d1 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,6 @@ const selected = []; // If params are null, show a default message function showDetails(data, outgoers) { - console.log("showDetails called") // Show details about the simulator const details = document.getElementById("details"); // Basic description @@ -62,7 +61,11 @@ function showDetails(data, outgoers) { const url = new URL(window.location.href); const params = new URLSearchParams(url.search); - params.set('selected', data["full_name"]); + if (data === null) + params.delete('selected'); + else { + params.set('selected', data["full_name"]); + } url.search = params.toString(); window.history.pushState({}, "", url); }