score:1

Here's some code that might achieve the effect you're looking for. It's largely attributable to Mike Bostock's Bubble Chart and Collision Detection examples.

This uses D3's pack layout to initially position the nodes. Then a force layout is used to "auto adjust" the other circles when dragging a node.

Working example at bl.ocks.org

var width = 900;
var height = 500;

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

var nodes = d3.range(128).map(function () { return {radius: Math.random() * 16 + 8}; });
var nodesCopy = $.extend(true, [], nodes);

function dblclick(d) {
  d3.select(this).classed("fixed", d.fixed = false);
}

function dragstart(d) {
  d3.select(this).classed("fixed", d.fixed = true);
}

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

function packup() {
  var pack = d3.layout.pack()
      .sort(null)
      .size([width, height])
      .padding(0)
      .value(function (d) { return d.radius; });

  svg.selectAll(".node")
      .data(pack.nodes({"children": nodes})
      .filter(function (d) { return !d.children; }))
    .enter().append("circle")
      .attr("r", function (d) { return d.radius; })
      .attr("cx", function (d) { return d.x; })
      .attr("cy", function (d) { return d.y; });
}

function forceup() {
  var force = d3.layout.force()
      .nodes(nodes)
      .gravity(0.05)
      .charge(0)
      .size([width, height])
      .start();

  var drag = force.drag().on("dragstart", dragstart);

  force.on("tick", function () {
    var q = d3.geom.quadtree(nodes);
    var i = 0;
    var n = nodes.length;

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

    svg.selectAll("circle")
        .attr("cx", function (d) { return d.x; })
        .attr("cy", function (d) { return d.y; });
  });

  d3.selectAll("circle")
    .on("dblclick", dblclick)
    .call(drag);
}

function reset() {
  svg.selectAll("*").remove();
  nodes = $.extend(true, [], nodesCopy);
  packup();
  forceup();
}

packup();
forceup();

Related Query

More Query from same tag