block by vlandham 2125175

Force Layout with Multiple Foci

Full Screen

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!

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Force-Directed Layout (Multiple Foci)</title>
    <script type="text/javascript" src="//mbostock.github.com/d3/d3.js"></script>
    <script type="text/javascript" src="//mbostock.github.com/d3/d3.geom.js"></script>
    <script type="text/javascript" src="//mbostock.github.com/d3/d3.layout.js"></script>
  </head>
  <body>
    <script type="text/javascript">

var w = 960,
    h = 500,
    fill = d3.scale.category10(),
    nodes = d3.range(100).map(Object);

var vis = d3.select("body").append("svg:svg")
    .attr("width", w)
    .attr("height", h);

var force = d3.layout.force()
    .nodes(nodes)
    .links([])
    .size([w, h])
    .start();

var node = vis.selectAll("circle.node")
    .data(nodes)
  .enter().append("svg: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); })
    .style("stroke-width", 1.5)
    .call(force.drag);

vis.style("opacity", 1e-6)
  .transition()
    .duration(1000)
    .style("opacity", 1);

function update(e) {
  console.log(e);
 // Push different nodes in different directions for clustering.
  var k = 6 * e.alpha;
  nodes.forEach(function(o, i) {
    o.y += i & 1 ? k : -k;
    o.x += i & 2 ? k : -k;
  });

  node.attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });

}

force.on("tick", update);

d3.select("body").on("click", function() {
  nodes.forEach(function(o, i) {
    o.x += (Math.random() - .5) * 40;
    o.y += (Math.random() - .5) * 40;
  });
  force.resume();
});

    </script>
  </body>
</html>