{
const size = 640;
const x = d3.randomUniform(0, size);
const r = d3.randomUniform(5, 18);
const nodes = Array.from({ length: n }, (d) => ({ x: x(), y: x(), r: r() }));
const state = cm.state({ nodes, selected: null });
const ticker = cm.ticker().on("animate", () => simulation.tick());
const simulation = manyBodyForce(nodes, {
ontick: (I, positions) => {
for (const i of I) {
const { x, y } = positions[i];
state.nodes[i].x = x;
state.nodes[i].y = y;
}
state.nodes = [...state.nodes];
}
});
invalidation.then(() => ticker.dispose());
function ondrag(event, index) {
const { x, y } = event;
simulation.set(index, x, y);
}
function ondragstart(event, index) {
state.selected = index;
}
function ondragend(event, index) {
state.selected = null;
}
return cm.svg`<svg ${{ width: size, height: size }}>
${() =>
state.nodes.map((d, i) =>
cm.draggable(
{
ondrag: (e) => ondrag(e, i),
onstart: (e) => ondragstart(e, i),
onend: (e) => ondragend(e, i)
},
[
cm.svg`<circle
${{ cx: d.x, cy: d.y, r: d.r, style: "cursor:pointer" }}
${{ fill: state.selected === i ? "red" : "black" }}
/>`
]
)
)}
</svg>`;
}