Skip to content

Instantly share code, notes, and snippets.

@nsonnad
Last active August 11, 2021 04:38
Show Gist options
  • Save nsonnad/5993342 to your computer and use it in GitHub Desktop.
Save nsonnad/5993342 to your computer and use it in GitHub Desktop.
Circle-bound D3 force layout

Circle-bound nodes in d3.layout.force(), using a variation of Mike Bostock's rectangular Bounded Force Layout. Here we calculate the maximum and minimum x for a given y in the circle, and vice versa.

<html>
<head>
<title>Circular bounds</title>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="chart"></div>
<script>
var margin = 30,
w = 500 - margin * 2,
h = w,
radius = w / 2,
strokeWidth = 4,
hyp2 = Math.pow(radius, 2),
nodeBaseRad = 5;
var svg = d3.select('#chart').append('svg')
.attr('width', w + margin * 2)
.attr('height', h + margin * 2)
.append('g')
.attr('transform', 'translate(' + margin + ',' + margin + ')');
var pool = svg.append('circle')
.style('stroke-width', strokeWidth * 2)
.attr({
class: 'pool',
r: radius,
cy: 0,
cx: 0,
transform: 'translate(' + w / 2 + ',' + h / 2 + ')'
});
function randomNodes(n) {
var data = [],
range = d3.range(n);
for (var i = range.length - 1; i >= 0; i--) {
data.push({
rad: Math.floor(Math.random() * 12)
});
}
return data;
}
var nodeData = randomNodes(80);
var force = d3.layout.force()
.charge(-50)
.gravity(0.05)
.nodes(nodeData)
.size([w, h]);
var nodes = svg.selectAll('.nodes')
.data(nodeData)
.enter().append('circle')
.attr({
class: 'nodes',
r: function (d) { return d.rad + nodeBaseRad; },
fill: "#fff8d1"
})
function pythag(r, b, coord) {
r += nodeBaseRad;
// force use of b coord that exists in circle to avoid sqrt(x<0)
b = Math.min(w - r - strokeWidth, Math.max(r + strokeWidth, b));
var b2 = Math.pow((b - radius), 2),
a = Math.sqrt(hyp2 - b2);
// radius - sqrt(hyp^2 - b^2) < coord < sqrt(hyp^2 - b^2) + radius
coord = Math.max(radius - a + r + strokeWidth,
Math.min(a + radius - r - strokeWidth, coord));
return coord;
}
function tick() {
nodes.attr('cx', function (d) { return d.x = pythag(d.rad, d.y, d.x); })
.attr('cy', function (d) { return d.y = pythag(d.rad, d.x, d.y); });
}
nodes.call(force.drag);
force.on('tick', tick)
.start();
</script>
</body>
</html>
#chart {
width: 600px;
margin: 0 auto;
}
.pool {
fill: #3db1ff;
stroke: #4f4f4f;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment