block by fil c5bac96e6898753d19f336abaa662df3

Expand and fill force (work in progress)

Full Screen

A force that tries to expand to lower density regions.

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://raw.githack.com/d3/d3-plugins/master/hexbin/hexbin.js"></script>
<script>

var width = 960,
    height = 500,
    τ = 2 * Math.PI,
    maxLength = 10000,
    maxLength2 = maxLength * maxLength;

var hexbin = d3.hexbin()
    .size([width, height])
    .radius(20);

var nodes = d3.range(500).map(function() {
  return {
    x: width * (5 + Math.random())/11,
    y: height * (5 + Math.random())/11,
    r: 2 + 6 * Math.random(),
    c: d3.rgb(255*Math.random(), 255*Math.random(), 255*Math.random())
  };
});

  var step = 0;
var force = d3.forceSimulation()
    .nodes(nodes)
.force('manybody', d3.forceManyBody().strength(-1))
.force('grid', function(alpha) {
  var g = 40;
  for(var i=0, n=nodes.length; i<n; i++) {
    var d = nodes[i],
        dx = d.x - g * Math.round(d.x/g),
        dy = d.y - g * Math.round(d.y/g);
      d.vx += -dx / 10;
      d.vy += -dy / 10;
  };
})
.force('expand', function(alpha) {
  var radius = 10 + 40 * Math.random(),
      hx = (Math.random() - 0.5)*radius,
      hy = (Math.random() - 0.5)*radius,
      angle = Math.random() * 2 * Math.PI,
      s = Math.sin(angle),
      c = Math.cos(angle),
      e = 3; // alpha * 10
      
  hexbin
  .radius(radius)
  .x(d => s * d.x + c * d.y + hx)
  .y(d => c * d.x - s * d.y + hy);

  var hex = hexbin(nodes)
  .map(h => {
    h.weight = d3.sum(h, d => d.r);
    return h;
  });
  var hexmap = d3.map(hex, d => [d.i, d.j]);
  hex
  .forEach(function(h) {
    var dest = [];
    for (var i = -1; i < 2; i++){
    for (var j = -1; j < 2; j++){
      var k = [h.i + i, h.j + j]
      if (!hexmap.has(k) || hexmap.get(k).weight < h.weight) {
        dest.push([i,j]);
      }
    }
    }
    if (dest.length > 0)
    h.forEach(function(d){
      var l = dest[Math.floor(Math.random()*dest.length)];
      d.vx += (s * l[0] + c * l[1]) * e;
      d.vy += (c * l[0] - s * l[1]) * e;
    });

  });
})
.force('bounds', function(alpha) {
  for(var i=0, n=nodes.length; i<n; i++) {
    var d = nodes[i],
        dx = d.x - width/2,
        dy = d.y - height/2,
        dist2 = Math.max(dx*dx , dy*dy),
        over = Math.sqrt(dist2) - 200;
    if (over > 0) {
      d.vx = -dx / 200 * (over+1);
      d.vy = -dy / 200 * (over+1);
    }
  };
})
.force('collide', d3.forceCollide(d => 1 + d.r || 1))
.on("tick", ticked);

  
   
var canvas = d3.select("body").append("canvas")
    .attr("width", width)
    .attr("height", height)
    .on("ontouchstart" in document ? "touchmove" : "mousemove", moved);

var context = canvas.node().getContext("2d");

function moved() {
  var p1 = d3.mouse(this);
  nodes[0].fx = p1[0];
  nodes[0].fy = p1[1];
  force.alpha(0.1).restart();
}

  

  
function ticked() {
  //var diagram = voronoi(nodes);

  var links = [];//diagram.links();
  
  context.clearRect(0, 0, width, height);

  context.beginPath();
  for (var i = 0, n = links.length; i < n; ++i) {
    var link = links[i],
        dx = link.source.x - link.target.x,
        dy = link.source.y - link.target.y;
    if (dx * dx + dy * dy < maxLength2) {
      context.moveTo(link.source.x, link.source.y);
      context.lineTo(link.target.x, link.target.y);
    }
  }
  context.lineWidth = 1;
  context.strokeStyle = "#bbb";
  context.stroke();

  for (var i = 0, n = nodes.length; i < n; ++i) {
    var node = nodes[i];
  context.beginPath();
    context.moveTo(node.x, node.y);
    context.arc(node.x, node.y, node.r, 0, τ);
  context.fillStyle = node.c;
  context.fill();
  }
}

</script>