d3graph.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
  2. function getOrCreateNode(id, name, nodes, dict) {
  3. let node = dict[id];
  4. if (!node) {
  5. node = {}
  6. node.id = id;
  7. node.name = name;
  8. nodes.push(node);
  9. dict[id] = node;
  10. }
  11. return node;
  12. }
  13. function toGraphData(data) {
  14. let graph = {};
  15. let nodes = graph.nodes = [];
  16. let links = graph.links = [];
  17. let idToNode = [];
  18. let columns = data.head.vars;
  19. data.results.bindings.forEach(item => {
  20. const srcID = item[columns[0]].value;
  21. const trgtID = item[columns[1]].value;
  22. const src = getOrCreateNode(srcID, srcID, nodes, idToNode);
  23. const trgt = getOrCreateNode(trgtID, trgtID, nodes, idToNode);
  24. let link = {};
  25. link.source = src.id;
  26. link.target = trgt.id;
  27. links.push(link);
  28. });
  29. return graph;
  30. }
  31. function drawGraph(data, containerID, width = 500, height = 300) {
  32. let graph = toGraphData(data)
  33. let container = d3.select(containerID);
  34. let svg = container.append('svg').attr("width", width).attr("height", height);
  35. // arrow def
  36. svg.append('defs').append('marker')
  37. .attr('id', 'arrowhead')
  38. .attr('viewBox', '-0 -5 10 10')
  39. .attr('refX', 13)
  40. .attr('refY', 0)
  41. .attr('orient', 'auto')
  42. .attr('markerWidth', 13)
  43. .attr('markerHeight', 13)
  44. .attr('xoverflow', 'visible')
  45. .append('svg:path')
  46. .attr('d', 'M 0,-5 L 10 ,0 L 0,5')
  47. .attr('fill', '#808080')
  48. .style('stroke', 'none');
  49. let nodes = graph.nodes;
  50. let links = graph.links;
  51. let simulation = d3.forceSimulation()
  52. .force("link", d3.forceLink().id(function (d) {
  53. return d.id;
  54. }).distance(80))
  55. .force("charge", d3.forceManyBody().strength(-100))
  56. .force("center", d3.forceCenter(width / 2, height / 2));
  57. let link = svg.selectAll(".link")
  58. .data(links)
  59. .enter()
  60. .append("line")
  61. .attr("class", "link")
  62. .attr('marker-end', 'url(#arrowhead)')
  63. .style('stroke', '#808080');
  64. let node = svg.selectAll(".node")
  65. .data(nodes)
  66. .enter()
  67. .append("g")
  68. .attr("class", "node")
  69. .call(d3.drag()
  70. .on("start", dragstarted)
  71. .on("drag", dragged)
  72. .on("end", dragended));
  73. node.append("circle")
  74. .attr("r", 5)
  75. .attr("fill", "#006496")
  76. node.append("title")
  77. .text(function (d) {
  78. return d.name;
  79. });
  80. node.append("text")
  81. .attr("dy", -3)
  82. .text(function (d) {
  83. return d.id
  84. });
  85. simulation.nodes(nodes).on("tick", ticked);
  86. simulation.force("link").links(links);
  87. function ticked() {
  88. link
  89. .attr("x1", function (d) {
  90. return getX(d.source.x);
  91. })
  92. .attr("y1", function (d) {
  93. return getY(d.source.y);
  94. })
  95. .attr("x2", function (d) {
  96. return getX(d.target.x);
  97. })
  98. .attr("y2", function (d) {
  99. return getY(d.target.y);
  100. });
  101. node.attr("transform",
  102. function (d) {
  103. const dx = getX(d.x);
  104. const dy = getY(d.y);
  105. return "translate(" + dx + ", " + dy + ")";
  106. }
  107. );
  108. function getX(x) {
  109. let dx = x;
  110. if (dx < 20) dx = 20;
  111. if (dx > width - 20) dx = width - 20;
  112. return dx;
  113. }
  114. function getY(y) {
  115. let dy = y;
  116. if (dy < 20) dy = 20;
  117. if (dy > height - 20) dy = height - 20;
  118. return dy;
  119. }
  120. }
  121. function dragstarted(event) {
  122. if (!event.active) simulation.alphaTarget(0.3).restart()
  123. event.subject.fx = event.subject.x;
  124. event.subject.fy = event.subject.y;
  125. }
  126. function dragged(event) {
  127. event.subject.fx = event.x;
  128. event.subject.fy = event.y;
  129. }
  130. function dragended(event) {
  131. if (!event.active) simulation.alphaTarget(0);
  132. event.subject.fx = null;
  133. event.subject.fy = null;
  134. }
  135. }
  136. export {drawGraph, toGraphData};
  137. export default drawGraph;