score:2

Accepted answer

i found solution in detecting collision function when generating random coordinates, here is the jsfiddle link and code:

    (function () {
    var angle, offset, data, x, y, r,
        collision, circle1, circle2,
        circles      = [],
        size    = [8, 15],
        width   = 500,
        color   = d3.scale.category10(),
        height  = 600,
        radius  = 130,
        dispersion = 10,
        svgcontainer = d3.select('body').append("svg")
            .attr("width", width)
            .attr("height", height);


    function detectcollision(c2, c1) {
        var dx   = c1.cx - c2.cx;
        var dy   = c1.cy - c2.cy;
        var rsum = c1.r + c2.r;

        return ((math.pow(dx, 2) + math.pow(dy, 2)) < math.pow(rsum, 2));
    }

    var sh = 2, elements = 55;
    data = d3.range(elements).map(function (i) {
        do {
            // dispersion += i / 50;
            angle  = math.random() * math.pi * 2;
            offset = math.max(size[0], size[1]) + radius + dispersion + (elements/sh);

            x = offset + math.cos(angle) * radius + rand(- (dispersion + i / sh), dispersion + i / sh);
            y = offset + math.sin(angle) * radius + rand(- (dispersion + i / sh), dispersion + i / sh);
            r = rand(size[0], size[1]);

            circle2 = {cx : x, cy : y, r : r};

            collision = false;
            if (circles.length > 1) {
                circles.foreach(function (d) {
                    circle1 = {cx : d.cx, cy : d.cy, r : d.r};
                    if (detectcollision(circle1, circle2)) {
                        collision = true;
                    }
                });
            }

        } while (collision);

        circles.push(circle2);

        return circles[circles.length - 1];

    });

    svgcontainer.selectall("circle")
        .data(data)
        .enter().append("circle")

        .attr({
            r    : function (d) {
                return d.r
            },
            cx : function (d) {
                return d.cx
            },
            cy : function (d) {
                return d.cy
            },
            fill : function (d, i) {
                return color(i % 3)
            }
        });

    function rand(min, max) {
        return math.floor(math.random() * (max - min + 1)) + min;
    }
})();

score:2

yes, you can achieve this by force layout. the idea is to keep all the links display none and center node display none none. something like this:

nodeenter.append("circle")
        .style("display", function (d) {
        return d.children ? "none" : ""; //ceneter node has children thus display will be none
    })

in such a case it will just look like the visualization you want to have.

working code here

hope this helps!

score:7

i have made some updates to your fiddle and applied collision detection as in the demo i mentioned in the comment. hope this helps.

var angle, offset, data,
  size = [8, 15],
  width = 500,
  color = d3.scale.category10(),
  height = 600,
  radius = 200,
  dispersion = 10,    
  svgcontainer = d3.select('body').append("svg")
  .attr("width", width)
  .attr("height", height);

var force = d3.layout.force()
  .gravity(0.05)
  .charge(function(d, i) {
    return i ? 0 : -2000;
  })
  .distance(500)
  .size([width, height]);

data = d3.range(100).map(function() {
  angle = math.random() * math.pi * 2;
  offset = math.max(size[0], size[1]) + radius + dispersion;
  return {
    x: offset + math.cos(angle) * radius + rand(-dispersion, dispersion),
    y: offset + math.sin(angle) * radius + rand(-dispersion, dispersion),
    radius: rand(size[0], size[1])
  };

});

force
  .nodes(data)
  .start();

root = data[0],
color = d3.scale.category10();

root.radius = 0;
root.fixed = true;

root.px = 250; //center x
root.py = 275; //center y

var nodes = svgcontainer.selectall("circle")
  .data(data)
  .enter().append("circle")
  .attr({
    r: function(d) {
      return d.radius
    },
    cx: function(d) {
      return d.x
    },
    cy: function(d) {
      return d.y
    },
    fill: function(d, i) {
      return color(i % 3)
    }
  });

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

  while (++i < n) q.visit(collide(data[i]));

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

function rand(min, max) {
  return math.floor(math.random() * (max - min + 1)) + min;
}

function collide(node) {
  var r = node.radius + 16,
    nx1 = node.x - r,
    nx2 = node.x + r,
    ny1 = node.y - r,
    ny2 = node.y + r;
  return function(quad, x1, y1, x2, y2) {
    if (quad.point && (quad.point !== node)) {
      var x = node.x - quad.point.x,
        y = node.y - quad.point.y,
        l = math.sqrt(x * x + y * y),
        r = node.radius + quad.point.radius;
      if (l < r) {
        l = (l - r) / l * .5;
        node.x -= x *= l;
        node.y -= y *= l;
        quad.point.x += x;
        quad.point.y += y;
      }
    }
    return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
  };
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

edit:

do you mean something like this?

var angle, offset, data,
  size = [8, 15],
  width = 500,
  color = d3.scale.category10(),
  height = 600,
  radius = 200,
  dispersion = 10,    
  svgcontainer = d3.select('body').append("svg")
  .attr("width", width)
  .attr("height", height).append("g");

var force = d3.layout.force()
  .gravity(0.05)
  .charge(function(d, i) {    
    return i ? -20 : -2000;
  })
  .distance(500)
  .size([width, height]);

data = d3.range(100).map(function() {
  angle = math.random() * math.pi * 2;
  offset = math.max(size[0], size[1]) + radius + dispersion;
  return {
    x: offset + math.cos(angle) * radius + rand(-dispersion, dispersion),
    y: offset + math.sin(angle) * radius + rand(-dispersion, dispersion),
    radius: rand(size[0], size[1])
  };

});

force
  .nodes(data)
  .start();

root = data[0],
color = d3.scale.category10();

root.radius = 0;
root.fixed = true;

root.px = 250; //center x
root.py = 275; //center y

var nodes = svgcontainer.selectall("circle")
  .data(data)
  .enter().append("circle")
  .attr({
    r: function(d) {
      return d.radius
    },
    cx: function(d) {
      return d.x
    },
    cy: function(d) {
      return d.y
    },
    fill: function(d, i) {
      return color(i % 3)
    }
  });

var rotation = 0;
setinterval(function(){
  if(force.alpha()==0){
  if(!rotation)    
    rotation = math.random() * 50;
  else
    rotation = rotation+1;
  svgcontainer.attr("transform","rotate("+rotation+", "+(width/2)+","+(height/2)+")");
  }
  //force.theta(0.5);
},250);

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

  while (++i < n) q.visit(collide(data[i]));

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

function rand(min, max) {
  return math.floor(math.random() * (max - min + 1)) + min;
}

function collide(node) {
  var r = node.radius + 16,
    nx1 = node.x - r,
    nx2 = node.x + r,
    ny1 = node.y - r,
    ny2 = node.y + r;
  return function(quad, x1, y1, x2, y2) {
    if (quad.point && (quad.point !== node)) {
      var x = node.x - quad.point.x,
        y = node.y - quad.point.y,
        l = math.sqrt(x * x + y * y),
        r = node.radius + quad.point.radius;
      if (l < r) {
        l = (l - r) / l * .5;
        node.x -= x *= l;
        node.y -= y *= l;
        quad.point.x += x;
        quad.point.y += y;
      }
    }
    return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
  };
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>


Related Query