block by mbostock 835cf2925ba217821434

N-Body Problem

Full Screen

A recreation of an earlier Protovis example.

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>

body {
  background: #000;
}

</style>
<canvas width="960" height="500"></canvas>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>

var canvas = document.querySelector("canvas"),
    context = canvas.getContext("2d"),
    width = canvas.width,
    height = canvas.height;

var n = 200,
    tau = 2 * Math.PI,
    nodes = d3.range(n).map(function() { return {radius: Math.random() * 8 + 2}; });

var force = d3.layout.force()
    .charge(0.5)
    .gravity(0)
    .friction(1)
    .nodes(nodes)
    .size([width, height])
    .start();

var stroke = d3.scale.linear()
    .domain([0, 1])
    .range(["steelblue", "brown"]);

force.on("tick", function(e) {
  var q = d3.geom.quadtree(nodes), node, i, vx, vy;
  context.clearRect(0, 0, width, height);
  context.lineWidth = 1.5;

  for (i = 0; i < n; ++i) {
    q.visit(collide(nodes[i]));
  }

  for (i = 0; i < n; ++i) {
    node = nodes[i];
    context.beginPath();
    context.arc(node.x, node.y, node.radius - 0.75, 0, tau);
    context.strokeStyle = stroke((vx = node.x - node.px) * vx + (vy = node.y - node.py) * vy);
    context.stroke();
  }

  context.beginPath();
  for (i = 0; i < n; ++i) {
    node = nodes[i];
    context.moveTo(node.x, node.y);
    context.lineTo(node.x + (node.x - node.px) * 3, node.y + (node.y - node.py) * 3);
  }
  context.strokeStyle = "#fff";
  context.stroke();

  force.resume();
});

function collide(node) {
  var r = node.radius + 16,
      nx1 = node.x - r,
      nx2 = node.x + r,
      ny1 = node.y - r,
      ny2 = node.y + r;
  return function(quad, x1, y1, x2, y2) {
    if (quad.point && (quad.point !== node)) {
      var x = node.x - quad.point.x,
          y = node.y - quad.point.y,
          l = Math.sqrt(x * x + y * y),
          r = node.radius + quad.point.radius;
      if (l < r) {
        l = (l - r) / (l * 2);
        node.x -= x *= l;
        node.y -= y *= l;
        quad.point.x += x;
        quad.point.y += y;
      }
    }
    return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
  }
}

</script>