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();
removeChild()
but it didnt help. – Swaind.className
gives the text of the bubble that is being dragged, but I am not sure how to filter the data since there is no keyclassName
in the input data. – Swain