Spaces:
Sleeping
Sleeping
| """Force-directed network graph template — network family. | |
| Adapted from d3-graph-gallery.com (Yan Holtz) — | |
| https://d3-graph-gallery.com/graph/network_basic.html (d3 v6 version: | |
| d3.forceSimulation() with forceLink/forceManyBody/forceCenter, driven by | |
| data_network.json loaded via d3.json()). Here the JSON is injected inline | |
| as GraphData; nodes are additionally colored by `group` via an ordinal | |
| color scale, ticking uses the "tick" event (not "end") so the simulation | |
| animates into place, and a drag behavior is added so nodes can be | |
| repositioned. | |
| """ | |
| from __future__ import annotations | |
| from app.agents.d3.registry import D3Template, register | |
| from app.agents.d3.templates._graph import GraphData, GraphNode, GraphLink | |
| _HTML = """<!-- Adapted from d3-graph-gallery.com (Yan Holtz) — https://d3-graph-gallery.com/graph/network_basic.html --> | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <script src="https://d3js.org/d3.v7.min.js"></script> | |
| <style> | |
| html, body { margin: 0; padding: 0; width: 100vw; height: 100vh; background: #FAF7F2; color: #1A3557; font-family: Georgia, 'Libre Caslon Text', serif; overflow: hidden; } | |
| #my_dataviz { width: 100vw; height: 100vh; } | |
| .chart-title { fill: #1A3557; font-size: 18px; text-anchor: middle; } | |
| .link { stroke: #1A3557; stroke-opacity: 0.35; } | |
| .node { stroke: #1A3557; stroke-width: 1.5px; cursor: grab; } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="my_dataviz"></div> | |
| <script> | |
| const data = __DATA__; | |
| const margin = {top: 50, right: 30, bottom: 20, left: 30}, | |
| width = 960 - margin.left - margin.right, | |
| height = 600 - margin.top - margin.bottom; | |
| const svg = d3.select("#my_dataviz") | |
| .append("svg") | |
| .attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`) | |
| .attr("preserveAspectRatio", "xMidYMid meet") | |
| .style("width", "100%") | |
| .style("height", "100%") | |
| .append("g") | |
| .attr("transform", `translate(${margin.left},${margin.top})`); | |
| svg.append("text") | |
| .attr("class", "chart-title") | |
| .attr("x", width / 2) | |
| .attr("y", -20) | |
| .text(data.title); | |
| const groups = Array.from(new Set(data.nodes.map(d => d.group))); | |
| const color = d3.scaleOrdinal(groups, ["#4A7FB5", "#B5764A", "#6FA37A", "#A05A9C", "#C2A03C"]); | |
| function drag(simulation) { | |
| function dragstarted(event, d) { | |
| if (!event.active) simulation.alphaTarget(0.3).restart(); | |
| d.fx = d.x; | |
| d.fy = d.y; | |
| } | |
| function dragged(event, d) { | |
| d.fx = event.x; | |
| d.fy = event.y; | |
| } | |
| function dragended(event, d) { | |
| if (!event.active) simulation.alphaTarget(0); | |
| d.fx = null; | |
| d.fy = null; | |
| } | |
| return d3.drag() | |
| .on("start", dragstarted) | |
| .on("drag", dragged) | |
| .on("end", dragended); | |
| } | |
| const simulation = d3.forceSimulation(data.nodes) | |
| .force("link", d3.forceLink(data.links).id(d => d.id)) | |
| .force("charge", d3.forceManyBody().strength(-250)) | |
| .force("center", d3.forceCenter(width / 2, height / 2)) | |
| .on("tick", ticked); | |
| const link = svg | |
| .selectAll("line") | |
| .data(data.links) | |
| .join("line") | |
| .attr("class", "link") | |
| .style("stroke-width", d => Math.sqrt(d.value)); | |
| const node = svg | |
| .selectAll("circle") | |
| .data(data.nodes) | |
| .join("circle") | |
| .attr("class", "node") | |
| .attr("r", 10) | |
| .style("fill", d => color(d.group)) | |
| .call(drag(simulation)); | |
| function ticked() { | |
| link | |
| .attr("x1", d => d.source.x) | |
| .attr("y1", d => d.source.y) | |
| .attr("x2", d => d.target.x) | |
| .attr("y2", d => d.target.y); | |
| node | |
| .attr("cx", d => d.x) | |
| .attr("cy", d => d.y); | |
| } | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| golden_sample = GraphData( | |
| title="Demo", | |
| nodes=[ | |
| GraphNode(id="A", group="x"), | |
| GraphNode(id="B", group="x"), | |
| GraphNode(id="C", group="y"), | |
| ], | |
| links=[ | |
| GraphLink(source="A", target="B", value=2), | |
| GraphLink(source="B", target="C", value=1), | |
| ], | |
| ) | |
| register( | |
| D3Template( | |
| id="network_force_graph", | |
| family="network", | |
| title="Force-directed network graph", | |
| when_to_use="Show relationships between named entities as a connected node-link network.", | |
| data_requirements="A list of nodes (id, optional group) and links (source id, target id, optional weight).", | |
| schema=GraphData, | |
| html_template=_HTML, | |
| golden_sample=golden_sample, | |
| ) | |
| ) | |