Reputation: 103
I was trying to recreate the cluster force layout using D3 v4 like this: https://bl.ocks.org/mbostock/1747543. I reused the cluster function from Mike's code, but the result was not good (http://codepen.io/aizizhang/pen/OXzJdK). Also, if I passed in an alpha parameter larger than 1, the cx and cy will not be calculated properly. Can someone give me a hand?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Clustered Bubble Chart</title>
<script src="https://d3js.org/d3.v4.js"></script>
</head>
<body>
<script>
let margin = {
top: 100,
right: 100,
bottom: 100,
left: 100
},
height = window.innerHeight,
width = window.innerWidth,
padding = 1.5, // separation between same-color nodes
clusterPadding = 6, // separation between different-color nodes
maxRadius = 12,
n = 200, // total number of nodes
m = 10, // number of distinct clusters
z = d3.scaleOrdinal(d3.schemeCategory20),
clusters = new Array(m);
let svg = d3.select('body')
.append('svg')
.attr('height', height)
.attr('width', width)
.append('g').attr('transform', `translate(${margin.right}, ${margin.top})`);
let nodes = d3.range(200).map(() => {
let i = Math.floor(Math.random() * m),
radius = Math.sqrt((i + 1) / m * -Math.log(Math.random())) * maxRadius,
d = {
cluster: i,
r: radius,
x: Math.random() * 200,
y: Math.random() * 200
};
if (!clusters[i] || (radius > clusters[i].r)) clusters[i] = d;
return d;
});
let circles = svg.append('g')
.datum(nodes)
.selectAll('.circle')
.data(d => d)
.enter().append('circle')
.attr('r', (d) => d.r)
.attr('cx', (d) => d.x)
.attr('cy', (d) => d.y)
.attr('fill', (d) => z(d.cluster))
.attr('stroke', 'black')
.attr('stroke-width', 1);
let simulation = d3.forceSimulation(nodes)
.velocityDecay(0.2)
.force("x", d3.forceX().x(200).strength(.5))
.force("y", d3.forceY().y(200).strength(.5))
.force("collide", d3.forceCollide().radius(function(d) {
return d.r + 0.5;
}).strength(0.5).iterations(2))
.on("tick", ticked)
// .force("charge", d3.forceManyBody(100))
function ticked() {
// let alpha = simulation.alpha();
circles
.each(clustering(0.5))
.attr('cx', (d) => d.x)
.attr('cy', (d) => d.y);
}
// Move d to be adjacent to the cluster node.
function clustering(alpha) {
return function(d) {
var cluster = clusters[d.cluster];
if (cluster === d) return;
var x = d.x - cluster.x,
y = d.y - cluster.y,
l = Math.sqrt(x * x + y * y),
r = d.r + cluster.r;
if (l !== r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
cluster.x += x;
cluster.y += y;
}
};
}
</script>
</body>
</html>
Upvotes: 3
Views: 2507
Reputation: 2455
I think part of the problem is you are using the new 4.0 collide force but you need to use the collide calculations from the original block you were copying. Here's a port of the original example:
http://bl.ocks.org/shancarter/f621ac5d93498aa1223d8d20e5d3a0f4
Hope that helps!
Upvotes: 4