block by pnavarrc 62aa6a1346a81de038dd

Voronoi Generated Network

Full Screen

Automatic generation of flat network structure with the Voronoi algorithm.

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="//d3js.org/d3.v3.min.js"></script>
  </head>

  <style>
  .node {
    stroke: #fff;
    stroke-width: 2;
    cursor: pointer;
  }

  .link {
    stroke-width: 2;
  }
  </style>

  <body>

  <svg class="gravity">
    <g class="links-container"></g>
    <g class="nodes-container"></g>
  </svg>

  <script type="text/javascript" src="main.js"></script>
  </body>
</html>

main.js



// Chart Parameters
var numNodes = 30,
    width = 600,
    height = 600,
    r = 15,
    b = 0.01;

var c0 = '#FF6B6B',
    c1 = '#490A3D';

// Generate the nodes (data)

var nodes = d3.range(numNodes).map(function(k) { return {num: k}; }),
    links = [];

var circles,
    lines;

// Compute the initial positions with the force layout
var f0 = d3.layout.force()
  .size([width, height])
  .nodes(nodes)
  .charge(-250)
  .start();

// Cool down the force
while (f0.alpha() > 1e-5) {
  f0.tick();
}

var svg = d3.select('.gravity'),
    gnodes = svg.select('g.nodes-container'),
    glinks = svg.select('g.links-container');

svg.attr('width', width).attr('height', height);

var circles = gnodes.selectAll('circle.node').data(nodes);
circles.enter().append('circle').classed('node', true);
circles
  .attr('r', r)
  .attr('cx', function(d) { return d.x; })
  .attr('cy', function(d) { return d.y; })
circles.exit().remove();

updateCircles();

// Compute the links with the voronoi triangulation
links = d3.geom.voronoi()
  .x(function(d) { return d.x; })
  .y(function(d) { return d.y; })
  .links(nodes);

var lines = glinks.selectAll('line.link').data(links);
lines.enter().append('line').classed('link', true);
lines.exit().remove();

var h = d3.scale.linear()
  .domain([0, Math.sqrt(width * width + height * height) / 2])
  .range([2, 6]);

var c = d3.scale.linear()
  .domain(h.domain())
  .range([c0, c1]);

updateLines();

function updateCircles() {
  circles
    .attr('cx', function(d) { return d.x; })
    .attr('cy', function(d) { return d.y; })
    .attr('fill', function(d) { return d.l ? c(d.l) : c0; });
}

function updateLines() {
  lines
    .attr('x1', function(d) { return d.source.x; })
    .attr('y1', function(d) { return d.source.y; })
    .attr('x2', function(d) { return d.target.x; })
    .attr('y2', function(d) { return d.target.y; })
    .attr('stroke', function(d) {

      if (d.source.l || d.target.l) {
        return d.source.l > d.target.l ? c(d.source.l) : c(d.target.l);
      }

      return c0;
    });
}

var u = nodes[10];
nodes.forEach(function(d) {
  var dx = d.x - u.x,
      dy = d.y - u.y;
  var l = Math.sqrt(dx * dx + dy * dy);
  d.l = l;
});
updateCircles();
updateLines();

circles.on('click', function(u) {

  nodes.forEach(function(d) {
    var dx = d.x - u.x,
        ux = dx / Math.abs(dx),
        dy = d.y - u.y,
        uy = dy / Math.abs(dy);

    var l = Math.sqrt(dx * dx + dy * dy);

    d.x = d.index !== u.index ? d.x + b * ux * h(l) : d.x;
    d.y = d.index !== u.index ? d.y + b * uy * h(l) : d.y;
    d.l = l;
  });

  f0.resume();

});

f0.on('tick', function() {
  updateCircles();
  updateLines();
});