chart = {
const height = 600;
const nodes = Array.from({length: n}, (_, i) => ({id: i*2 + 1}));
const links = edges.map(([i, j]) => ({source: i, target: j}));
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody().strength(-150))
.force("x", d3.forceX())
.force("y", d3.forceY())
.on("tick", ticked);
const svg = d3.select(DOM.svg(width, height))
.attr("viewBox", [-width / 2, -height / 2, width, height]);
const link = svg.append("g")
.attr("stroke", "#999")
.attr("stroke-width", 0.5)
.selectAll("line")
.data(links)
.enter().append("line");
const node = svg.append("g")
.style("font", "12px sans-serif")
.style("user-select", "none")
.style("cursor", "move")
.attr("text-anchor", "middle")
.attr("stroke", "white")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 3)
.selectAll("g")
.data(nodes)
.enter().append("g")
.call(drag(simulation));
node.append("text")
.attr("dy", "0.35em")
.text(d => d.id)
.clone(true)
.attr("stroke", "none");
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("transform", d => `translate(${d.x},${d.y})`);
}
invalidation.then(() => simulation.stop());
return svg.node();
}