Reputation: 8002
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/
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 :
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
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]);
Upvotes: 6