index.html
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <style> 5 html, 6 body { 7 width: 100%; 8 height: 100%; 9 margin: 0; 10 padding: 0; 11 overflow: hidden; 12 } 13 </style> 14 </head> 15 <body> 16 <script src="https://d3js.org/d3.v6.min.js"></script> 17 <script> 18 // Size of the SVG canvas 19 const width = window.innerWidth; 20 const height = window.innerHeight / 2; 21 const radius = 5; // radius of nodes 22 const columns = 10; // Updated value to show the number of rounds 23 const rows = 4; // Update value to show the number of nodes 24 const cellWidth = width / columns; 25 const cellHeight = height / rows; 26 27 // Create the SVG canvas 28 const svg = d3.select("body") 29 .append("svg") 30 .attr("width", width) 31 .attr("height", height); 32 33 function exampleResponse() { 34 const nodes = []; 35 for (let column = 0; column < 10; column++) { // loop for rounds 36 for (let row = 0; row < 4; row++) { // loop for nodes 37 nodes.push({ 38 "id": `node${row}-${column}`, 39 "column": column, 40 "row": row 41 }); 42 } 43 } 44 45 const links = []; 46 for (let column = 1; column < 10; column++) { 47 for (let row = 0; row < 4; row++) { 48 // choose randomly 3 or 4 nodes from the previous round 49 const prevNodes = [...Array(4).keys()]; 50 const numLinks = Math.floor(Math.random() * 2) + 3; // random number either 3 or 4 51 for (let i = 0; i < numLinks; i++) { 52 const randIndex = Math.floor(Math.random() * prevNodes.length); 53 const targetRow = prevNodes.splice(randIndex, 1)[0]; 54 links.push({ 55 "source": `node${row}-${column}`, 56 "target": `node${targetRow}-${column - 1}`, 57 "color": "black" 58 }); 59 } 60 } 61 } 62 63 return { 64 "nodes": nodes, 65 "links": links 66 }; 67 } 68 69 // Fetch data from the server 70 async function fetchData() { 71 // const response = await d3.json("http://127.0.0.1:3000/dag"); 72 // const response = { 73 // "nodes": [ 74 // {"id": "node0-0", "column": 0, "row": 0}, 75 // {"id": "node1-0", "column": 0, "row": 1}, 76 // {"id": "node2-0", "column": 0, "row": 2}, 77 // {"id": "node3-0", "column": 0, "row": 3}, 78 // {"id": "node0-1", "column": 1, "row": 0}, 79 // {"id": "node1-1", "column": 1, "row": 1}, 80 // {"id": "node2-1", "column": 1, "row": 2}, 81 // {"id": "node3-1", "column": 1, "row": 3}, 82 // // More nodes... 83 // ], 84 // "links": [ 85 // {"source": "node0-0", "target": "node0-1", "color": "black"}, 86 // {"source": "node0-0", "target": "node1-1", "color": "black"}, 87 // {"source": "node0-0", "target": "node3-1", "color": "black"}, 88 // {"source": "node1-0", "target": "node0-1", "color": "black"}, 89 // {"source": "node1-0", "target": "node1-1", "color": "black"}, 90 // {"source": "node1-0", "target": "node2-1", "color": "black"}, 91 // {"source": "node2-0", "target": "node1-1", "color": "black"}, 92 // {"source": "node2-0", "target": "node2-1", "color": "black"}, 93 // {"source": "node3-0", "target": "node2-1", "color": "black"}, 94 // {"source": "node3-0", "target": "node3-1", "color": "black"}, 95 // // More links... 96 // ] 97 // }; 98 const response = exampleResponse(); 99 100 // Map node ids to node objects for easier lookup 101 const nodeById = new Map(response.nodes.map(node => [node.id, node])); 102 103 // Replace source and target ids in links with the actual objects 104 response.links.forEach(link => { 105 link.source = nodeById.get(link.source); 106 link.target = nodeById.get(link.target); 107 }); 108 109 const nodes = response.nodes; 110 const links = response.links; 111 112 updateGraph(nodes, links); 113 } 114 115 function updateGraph(nodes, links) { 116 // Update nodes 117 const node = svg.selectAll("circle") 118 .data(nodes, d => d.id) 119 .join("circle") 120 .attr("r", radius) 121 .attr("cx", d => d.column * cellWidth + cellWidth / 2) // position nodes in the middle of their cell 122 .attr("cy", d => d.row * cellHeight + cellHeight / 2); 123 124 // Update links 125 svg.selectAll("line") 126 .data(links, d => `${d.source.id}-${d.target.id}`) 127 .join("line") 128 .style("stroke", d => d.color) 129 .attr("x1", d => d.source.column * cellWidth + cellWidth / 2) 130 .attr("y1", d => d.source.row * cellHeight + cellHeight / 2) 131 .attr("x2", d => d.target.column * cellWidth + cellWidth / 2) 132 .attr("y2", d => d.target.row * cellHeight + cellHeight / 2); 133 134 // Update row labels 135 svg.selectAll("rowLabel") 136 .data([...new Set(nodes.map(n => n.row))]) // unique rows 137 .join("text") 138 .attr("x", 0) 139 .attr("y", d => d * cellHeight + cellHeight / 2) 140 .text(d => `Node ${d}`); 141 142 // Update column labels 143 svg.selectAll("columnLabel") 144 .data([...new Set(nodes.map(n => n.column))]) // unique columns 145 .join("text") 146 .attr("x", d => d * cellWidth + cellWidth / 2) 147 .attr("y", 20) // set y coordinate so that labels are on top 148 .style("text-anchor", "middle") // centering the text 149 .text(d => `Round ${d}`); 150 } 151 152 // Fetch new data every second 153 d3.interval(fetchData, 1000); 154 155 // Initial fetch 156 fetchData(); 157 </script> 158 </body> 159 </html>