This example uses d3.forceCollide to prevent circles from overlapping.
forked from mbostock‘s block: Collision Detection
See also Bounded Force Layout - very tightly packed.
forked from Fil‘s block: Collision Detection in a tight square
<!DOCTYPE html>
<meta charset="utf-8">
<canvas width="800" height="500"></canvas>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var canvas = document.querySelector("canvas"),
context = canvas.getContext("2d"),
width = canvas.width,
height = canvas.height,
tau = 2 * Math.PI;
var buildings = d3.range(10).map(function(i) {
return {
x: (-0.5 + Math.random()) * width * 0.8,
y: (-0.5 + Math.random()) * height * 0.8,
};
});
var nodes = d3.range(400).map(function(i) {
return {
r: Math.random() * 8 + 3,
building: Math.floor(Math.random() * buildings.length)
};
});
function shape(alpha){
for (var i = 0, n = nodes.length, node, k = alpha * 0.1; i < n; ++i) {
node = nodes[i];
var dx = node.x - buildings[node.building].x,
dy = node.y - buildings[node.building].y;
node.vx -= dx * k;
node.vy -= dy * k;
}
}
var simulation = d3.forceSimulation(nodes)
//.velocityDecay(0.2)
.force("shape", shape)
.force("collide", d3.forceCollide().radius(function(d) { return d.r + 0.5; }).iterations(2))
.on("tick", ticked);
var color = d3.scaleOrdinal(d3.schemeCategory10);
function ticked() {
context.clearRect(0, 0, width, height);
context.save();
context.translate(width / 2, height / 2);
nodes.forEach(function(d,i) {
context.beginPath();
context.moveTo(d.x + d.r, d.y);
context.arc(d.x, d.y, d.r, 0, tau);
context.fillStyle = color(d.building);
context.fill();
context.strokeStyle = "white";
context.stroke();
});
context.restore();
}
</script>