d3 Node Tree

by Sam Fereday

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<canvas width="960" height="500"></canvas>

JavaScript

var canvas = d3.select("canvas"),
  context = canvas.node().getContext("2d"),
  width = canvas.property("width"),
  height = canvas.property("height"),
  radius = 2.5,
  transform = d3.zoomIdentity;

var points = d3.range(200).map(phyllotaxis(10));

canvas
  .call(d3.drag().subject(dragsubject).on("drag", dragged))
  .call(d3.zoom().scaleExtent([1 / 2, 8]).on("zoom", zoomed))
  .call(render);

function zoomed() {
  transform = d3.event.transform;
  render();
}

function dragsubject() {
  var i,
    x = transform.invertX(d3.event.x),
    y = transform.invertY(d3.event.y),
    dx,
    dy;

  for (i = points.length - 1; i >= 0; --i) {
    point = points[i];
    dx = x - point[0];
    dy = y - point[1];
    if (dx * dx + dy * dy < radius * radius) {
      point.x = transform.applyX(point[0]);
      point.y = transform.applyY(point[1]);
      return point;
    }
  }
}

function dragged() {
  d3.event.subject[0] = transform.invertX(d3.event.x);
  d3.event.subject[1] = transform.invertY(d3.event.y);
  render();
}

function render() {
  context.save();
  context.clearRect(0, 0, width, height);
  context.beginPath();
  context.translate(transform.x, transform.y);
  context.scale(transform.k, transform.k);
  points.forEach(drawPoint);
  context.fill();
  context.restore();
}

function drawPoint(point) {
  //context.moveTo(point[0] + radius, point[1]);
  //context.arc(point[0], point[1], radius, 0, 2 * Math.PI);
  
  context.rect(point[0],point[1],10,10);
	context.stroke();
}

function phyllotaxis(radius) {
  var theta = Math.PI * (3 - Math.sqrt(5));
  return function(i) {
    var r = radius * Math.sqrt(i),
      a = theta * i;
    return [
      width / 2 + r * Math.cos(a),
      height / 2 + r * Math.sin(a)
    ];
  };
}