In answer to a question on the d3 Slack channel #help
See the more complex version, with transitions for each step, at Merge Sites.
Built with blockbuilder.org
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<script>
// Feel free to change or delete any of the code you see in this editor!
var svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500)
var data = d3.range(300).map(d => {
return {
x: 100 + 760 * Math.random(),
y: 100 + 300 * Math.random(),
w: 300 * Math.random(),
}
})
function merge() {
var merged = 0;
data.forEach((d,i) => {
if (d.w == 0) return;
d.r = Math.sqrt(d.w);
data.slice(0, i).forEach((e,j) => {
if (e.w == 0) return;
var dx = e.x - d.x,
dy = e.y - d.y,
dist2 = dx * dx + dy * dy,
r2 = (d.r + e.r) * (d.r + e.r);
if (dist2 < r2) {
merged ++;
if (d.w >= e.w) {
d.x += dx * e.w / (d.w + e.w);
d.y += dy * e.w / (d.w + e.w);
d.w += e.w;
e.w = 0;
} else {
e.x -= dx * d.w / (d.w + e.w);
e.y -= dy * d.w / (d.w + e.w);
e.w += d.w;
d.w = 0;
}
}
});
});
return merged;
}
var circles = svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr("fill", "#3498f7")
.attr("opacity", 0.5)
.attr('r', d => Math.sqrt(d.w))
.attr('cx', d => d.x)
.attr('cy', d => d.y);
while(merge());
circles.transition()
.duration(2000)
.attr('r', d => Math.sqrt(d.w))
.attr('cx', d => d.x)
.attr('cy', d => d.y);
circles.filter(d => !d.w)
.transition()
.duration(2000)
.attr('opacity', 0)
.remove();
</script>
</body>
https://d3js.org/d3.v4.min.js