Skip to content

Instantly share code, notes, and snippets.

@bjtucker
Last active December 6, 2015 20:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bjtucker/1288c91d1a0e9c9928f7 to your computer and use it in GitHub Desktop.
Save bjtucker/1288c91d1a0e9c9928f7 to your computer and use it in GitHub Desktop.
Multi-Foci Force Layout with columns

Click to perturb or drag the nodes!

This example demonstrates the flexibility of D3’s force layout. By using position Verlet integration, it is easy to add custom forces to a layout. In this example, the nodes are clustered around four foci using additional forces: the odd nodes are pushed down, the even nodes are pushed up, and a similar bisecting force is applied laterally. These custom forces are based purely on the index of the node, but they could just as easily be derived from properties of data!

forked from mbostock's block: Multi-Foci Force Layout

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
stroke-width: 1.5px;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500;
var fill = d3.scale.category10();
var nodes = d3.range(40).map(function(i) {
return {index: i};
});
var force = d3.layout.force()
.nodes(nodes)
.size([width, height])
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 8)
.style("fill", function(d, i) { return fill(i & 3); })
.style("stroke", function(d, i) { return d3.rgb(fill(i & 3)).darker(2); })
.call(force.drag)
.on("mousedown", function() { d3.event.stopPropagation(); });
svg.style("opacity", 1e-6)
.transition()
.duration(1000)
.style("opacity", 1);
d3.select("body")
.on("mousedown", mousedown);
function tick(e) {
// Push different nodes in different directions for clustering.
var k = 6 * e.alpha;
var sumx=[0,0,0,0]
var countx=[0,0,0,0]
nodes.forEach(function(o, i) {
sumx[i%4]+= o.x
countx[i%4]+=1
})
var avgx=[]
sumx.forEach(function(o,i) {
avgx[i]=sumx[i]/countx[i]
})
nodes.forEach(function(o, i) {
o.y += i & 1 ? k : -k;
o.x += i & 2 ? k : -k;
o.x = i&4? o.x: avgx[i%4]
});
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
function mousedown() {
nodes.forEach(function(o, i) {
o.x += (Math.random() - .5) * 40;
o.y += (Math.random() - .5) * 40;
});
force.resume();
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment