{
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.style("background", "#2D2537")
.style("box-shadow", "0 0 20px 0 inset rgba(10,10,10,0.1)");
d3.selectAll("div.tooltip").remove();
const tooltip = d3
.select("body")
.append("div")
.attr("class", `tooltip ${classes}`)
.style("position", "absolute")
.style("opacity", 0)
.text("I'm a circle!");
const nodes = data.map((d) => ({ ...d }));
const circles = svg
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", (d) => d.r)
.attr("fill", (d) => color(d.group));
circles
.on("mouseover", (_evt, d) => {
tooltip
.style("opacity", 1)
.html(`x = ${d.x.toFixed(2)}<br>y = ${d.y.toFixed(2)}`)
.style("border-color", color(d.group));
})
.on("mousemove", (evt, d) => {
tooltip
.style("top", evt.pageY - 10 + "px")
.style("left", evt.pageX + 10 + "px");
})
.on("mouseout", () => {
tooltip.style("opacity", 0);
});
const simulation = d3
.forceSimulation(nodes)
.alphaTarget(0.3)
.velocityDecay(0.1)
.force("x", d3.forceX().strength((0.01 * height) / width))
.force("y", d3.forceY().strength(0.01))
.force(
"collide",
d3
.forceCollide()
.radius((d) => d.r + 4)
.iterations(3)
)
.force(
"charge",
d3.forceManyBody().strength((d, i) => -1)
)
.on("tick", function () {
circles
.data(nodes)
.join("circle")
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y);
});
return svg.node();
}