Woody
Woody

Reputation: 8002

D3 Force Layout - subgraph clustering feature

I have some data I am trying to display with the D3 force layout. Apologies if this is a naive question, or if the terminology i employ in the question title is not accurate. I couldn't see an answer quite what i was looking for.

I made a fiddle with a sample showing what I am on about here :

http://jsfiddle.net/stevendwood/f3GJT/8/

enter image description here

In the example I have one node (0) which has lots of links. Another node (16) has a smaller amount of links, 0 and 16 are both connected to 15.

So what i would like is for 0 and 16 to be little clusters with their connected nodes appearing in a nice circle around them.

I vainly tried to customise the charge based on the number of links, but I think what i want to do is somehow make nodes more attracted to nodes they are connected to and less attracted to nodes that they are not connected to.

I would like something like this if possible :

enter image description here

var w = 500,
h = 500,
nodes = [],
links = [];

/* Fake up some data */
for (var i=0; i<20; i++) {
    nodes.push({
        name: ""+i
    });
}

for (i=0; i<16; i++) {
    links.push({
        source: nodes[i],
        target: nodes[0]
    });
}

links.push({
     source: nodes[16],
     target: nodes[15]
});

for (i=17; i<20; i++) {

    links.push({
        source: nodes[i],
        target: nodes[16]
    });
}

var countLinks = function(n) {
    var count = 0;
    links.forEach(function(l) {
        if (l.source === n || l.target === n) {
            count++;
        }
    });

    return count;
}

/////////////////////////////////////////////

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

var force = d3.layout.force()
    .nodes(nodes)
    .links([])
    .gravity(0.05)
    .charge(function(d) {
        return countLinks(d) * -50;     
     })
    .linkDistance(300)
    .size([w, h]);


 var link = vis.selectAll(".link")
        .data(links)
        .enter().append("line")
        .attr("class", "link")
        .attr("stroke", "#CCC")
        .attr("fill", "none");

 var node = vis.selectAll("circle.node")
     .data(nodes)
     .enter().append("g")
     .attr("class", "node")
     .call(force.drag);

node.append("svg:circle")
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; })
    .attr("r", 14)
    .style("fill", "#CCC")
    .style("stroke", "#AAA")
    .style("stroke-width", 1.5)

node.append("text").text(function(d) { return d.name; })
    .attr("x", -6)
    .attr("y", 6);

force.on("tick", function(e) {
    node.attr("transform", function(d, i) {
        return "translate(" + d.x + "," + d.y + ")"; 
    });

    link.attr("x1", function(d)   { return d.source.x; })
        .attr("y1", function(d)   { return d.source.y; })
        .attr("x2", function(d)   { return d.target.x; })
        .attr("y2", function(d)   { return d.target.y; }) 
});

force.start();

Upvotes: 4

Views: 2453

Answers (1)

Chris Wilson
Chris Wilson

Reputation: 6719

Why did you leave out the links when declaring the force layout? If you add them back in, it looks much closer to what you wanted:

var force = d3.layout.force()
    .nodes(nodes)
    //.links([])
    .links(links)
    .gravity(0.1)
    .charge(-400)
    .linkDistance(75)
    .size([w, h]);

http://jsfiddle.net/f3GJT/11/

enter image description here

Upvotes: 6

Related Questions