Published
Edited
Sep 25, 2019
1 fork
41 stars
Insert cell
Insert cell
{
const width = 960,
height = 600;

const nodes = d3.range(200).map(() => ({radius: Math.random() * 15 + 5})),
root = nodes[0],
color = d3.scaleOrdinal(d3.schemeTableau10);
const svg = d3.select(DOM.svg(width, height));

root.radius = 0;
root.fixed = true;

const simulation = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody().strength((d, i) => { return i ? 0 : -100; }))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("collide", forceCollide());

svg.selectAll("circle")
.data(nodes.slice(1))
.enter().append("circle")
.attr("r", d => d.radius)
.style("fill", (d, i) => { return color(i % 3); });

simulation.on("tick", e => {
svg.selectAll("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y);
});

svg.on("mousemove", function() {
const p1 = d3.mouse(this);
root.fx = p1[0];
root.fy = p1[1];

simulation.alpha(0.9).restart();//reheat the simulation
});

return svg.node();
}
Insert cell
Insert cell
// collision detection with quadtree
function forceCollide() {
const alpha = 0.5;
let nodes;
let maxRadius = 16;

function force() {
const quadtree = d3.quadtree()
.x(d => d.x)
.y(d => d.y)
.addAll(nodes);
for (const node of nodes) {
var r = node.radius + maxRadius,
nx1 = node.x - r,
nx2 = node.x + r,
ny1 = node.y - r,
ny2 = node.y + r;

// visit each squares in the quadtree
// x1 y1 x2 y2 constitues the coordinates of the square
// we want to check if each square is a leaf node (has data prop)
quadtree.visit((visited, x1, y1, x2, y2) => {
if (visited.data && (visited.data !== node)) {
let x = node.x - visited.data.x,
y = node.y - visited.data.y,
l = Math.sqrt(x * x + y * y),
r = node.radius + visited.data.radius;
if (l < r) { // if quadtree leaf and input node collides
l = (l - r) / l * alpha;
node.x -= x *= l;
node.y -= y *= l;
visited.data.x += x;
visited.data.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
}
}

force.initialize = _ => nodes = _;

return force;
}
Insert cell
d3 = require('//d3js.org/d3.v5.min.js')
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more