ferozcoder
ferozcoder

Reputation: 454

Force layout collision detection with group nodes

How to use collision detection function for force layout d3. As i searched i got this code but if consider layout doesn't contain any circle.It contains only group with some images and text. So how can i rewrite this function.

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;
};}

Upvotes: 0

Views: 1323

Answers (1)

Gilsha
Gilsha

Reputation: 14591

Since the height and width of each node is same, the only required change would be to use the size of group instead of node.radius in the collide function. Ensure to have a unique data bonded to each node.

var width = 960,
    height = 500;

var nodes = d3.range(200).map(function() {
        return {
            radius: Math.random() * 12 + 4
        };
    }),
    root = nodes[0],
    color = d3.scale.category10();

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

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

force.start();

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

var node = svg.selectAll(".node")
    .data(nodes.slice(1))
    .enter()
    .append("g")
    .attr("class", "node")
    .attr("id",function(d,i){ return d.id = "node"+i; });

node.append("svg:image")  
    .attr("xlink:href", "http://www.clker.com/cliparts/g/l/R/7/h/u/teamstijl-person-icon-blue.svg")
    .attr("width", "30px")
    .attr("height", "30px");   

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

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

    svg.selectAll(".node")
        .attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")"
        });
});

svg.on("mousemove", function() {
    var p1 = d3.mouse(this);
    root.px = p1[0];
    root.py = p1[1];
    force.resume();
});

function collide(node) {
    var nodeEl = svg.selectAll("g.node")
        .filter(function(d, i) {
            return node.id == d.id
        }).node();   
    var nodeSize = nodeEl.getBBox().height+16;//You can remove/reduce this static value 16 to decrease the gap between nodes.
    var r = nodeSize / 2 + 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 = nodeSize / 2 + 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>

Upvotes: 1

Related Questions