import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm"; function getOrCreateNode(id, name, nodes, dict) { let node = dict[id]; if (!node) { node = {} node.id = id; node.name = name; nodes.push(node); dict[id] = node; } return node; } function toGraphData(data) { let graph = {}; let nodes = graph.nodes = []; let links = graph.links = []; let idToNode = []; let columns = data.head.vars; data.results.bindings.forEach(item => { const srcID = item[columns[0]].value; const trgtID = item[columns[1]].value; const src = getOrCreateNode(srcID, srcID, nodes, idToNode); const trgt = getOrCreateNode(trgtID, trgtID, nodes, idToNode); let link = {}; link.source = src.id; link.target = trgt.id; links.push(link); }); return graph; } function drawGraph(data, containerID, width = 500, height = 300) { let graph = toGraphData(data) let container = d3.select(containerID); let svg = container.append('svg').attr("width", width).attr("height", height); // arrow def svg.append('defs').append('marker') .attr('id', 'arrowhead') .attr('viewBox', '-0 -5 10 10') .attr('refX', 13) .attr('refY', 0) .attr('orient', 'auto') .attr('markerWidth', 13) .attr('markerHeight', 13) .attr('xoverflow', 'visible') .append('svg:path') .attr('d', 'M 0,-5 L 10 ,0 L 0,5') .attr('fill', '#808080') .style('stroke', 'none'); let nodes = graph.nodes; let links = graph.links; let simulation = d3.forceSimulation() .force("link", d3.forceLink().id(function (d) { return d.id; }).distance(80)) .force("charge", d3.forceManyBody().strength(-100)) .force("center", d3.forceCenter(width / 2, height / 2)); let link = svg.selectAll(".link") .data(links) .enter() .append("line") .attr("class", "link") .attr('marker-end', 'url(#arrowhead)') .style('stroke', '#808080'); let node = svg.selectAll(".node") .data(nodes) .enter() .append("g") .attr("class", "node") .call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)); node.append("circle") .attr("r", 5) .attr("fill", "#006496") node.append("title") .text(function (d) { return d.name; }); node.append("text") .attr("dy", -3) .text(function (d) { return d.id }); simulation.nodes(nodes).on("tick", ticked); simulation.force("link").links(links); function ticked() { link .attr("x1", function (d) { return getX(d.source.x); }) .attr("y1", function (d) { return getY(d.source.y); }) .attr("x2", function (d) { return getX(d.target.x); }) .attr("y2", function (d) { return getY(d.target.y); }); node.attr("transform", function (d) { const dx = getX(d.x); const dy = getY(d.y); return "translate(" + dx + ", " + dy + ")"; } ); function getX(x) { let dx = x; if (dx < 20) dx = 20; if (dx > width - 20) dx = width - 20; return dx; } function getY(y) { let dy = y; if (dy < 20) dy = 20; if (dy > height - 20) dy = height - 20; return dy; } } function dragstarted(event) { if (!event.active) simulation.alphaTarget(0.3).restart() event.subject.fx = event.subject.x; event.subject.fy = event.subject.y; } function dragged(event) { event.subject.fx = event.x; event.subject.fy = event.y; } function dragended(event) { if (!event.active) simulation.alphaTarget(0); event.subject.fx = null; event.subject.fy = null; } } export {drawGraph, toGraphData}; export default drawGraph;