block by Fil 10a29cd5074cad3f04522c77f1c1c3c4

Merge Sites, One Move

Full Screen

In answer to a question on the d3 Slack channel #help

See the more complex version, with transitions for each step, at Merge Sites.

Built with blockbuilder.org

index.html

<!DOCTYPE html> 
<head>
  <meta charset="utf-8">
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <style>
    body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
  </style>
</head>

<body>
  <script>
    // Feel free to change or delete any of the code you see in this editor!
    var svg = d3.select("body").append("svg")
      .attr("width", 960)
      .attr("height", 500)

    var data = d3.range(300).map(d => {
      return {
        x: 100 + 760 * Math.random(),
        y: 100 + 300 * Math.random(),
        w: 300 * Math.random(),
      }
    })

    function merge() {
      var merged = 0;
      data.forEach((d,i) => {
        if (d.w == 0) return;
        d.r = Math.sqrt(d.w);
        data.slice(0, i).forEach((e,j) => {
          if (e.w == 0) return;
          var dx = e.x - d.x,
              dy = e.y - d.y,
              dist2 = dx * dx + dy * dy,
              r2 = (d.r + e.r) * (d.r + e.r);
          if (dist2 < r2) {
            merged ++;
            if (d.w >= e.w) {
              d.x += dx * e.w / (d.w + e.w);
              d.y += dy * e.w / (d.w + e.w);
              d.w += e.w;
              e.w = 0;
            } else {
              e.x -= dx * d.w / (d.w + e.w);
              e.y -= dy * d.w / (d.w + e.w);
              e.w += d.w;
              d.w = 0;
            }
          }
        });
      });
      return merged;
    }
    
    
    var circles = svg.selectAll('circle')
    .data(data)
    .enter()
    .append('circle')
    .attr("fill", "#3498f7")
    .attr("opacity", 0.5)
      .attr('r', d => Math.sqrt(d.w))
      .attr('cx', d => d.x)
      .attr('cy', d => d.y);

    while(merge());

    circles.transition()
      .duration(2000)
      .attr('r', d => Math.sqrt(d.w))
      .attr('cx', d => d.x)
      .attr('cy', d => d.y);
      circles.filter(d => !d.w)
        .transition()
        .duration(2000)
        .attr('opacity', 0)
        .remove();
    
    
  </script>
</body>