{
const figHeight = width * 0.4;
const svg = d3.create("svg").attr("viewBox", [0, 0, width, figHeight]);
const arrow = svg
.append("defs")
.append("marker")
.attr("id", "arrow")
.attr("viewBox", [0, 0, 10, 10])
.attr("refX", 5)
.attr("refY", 5)
.attr("markerWidth", 10)
.attr("markerHeight", 10)
.attr("orient", "auto-start-reverse")
.append("path")
.attr(
"d",
d3.line()([
[0, 0],
[0, 10],
[10, 5]
])
)
.attr("stroke", "#333");
const edges = svg
.selectAll("line")
.data(change_user_graph.edges)
.join("line")
.attr("marker-start", "url(#arrow)")
.style("stroke", "#333")
.style("stroke-width", 1);
const text = svg
.selectAll("text")
.data(change_user_graph.nodes)
.join("text")
.text((d) => d.id)
.style("font-size", "8pt")
.style("text-anchor", "middle")
.style("fill", "#333");
const simulation = d3
.forceSimulation(change_user_graph.nodes)
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, figHeight / 2))
.force(
"links",
d3.forceLink(edges).id((d) => d.id)
)
.force("boundary", forceBoundary(0, 0, width, figHeight))
.on("tick", () => {
text.attr("x", (node) => node.x).attr("y", (node) => node.y);
edges
.attr("x1", (edge) => edge.source.x)
.attr("y1", (edge) => edge.source.y)
.attr("x2", (edge) => edge.target.x)
.attr("y2", (edge) => edge.target.y);
});
return svg.node();
}