Using D3.js's force layout I'm trying to make the links automatically generate based on the node data. The nodes already appear exactly as expected.
The database json strings use the following format:
{
"id": 1,
"title": "name"
},
{
"id": 2,
"title": "other name",
"primaryDependants": 1
}
and I'm basically trying to have it say:
'for each node, check if it has a primaryDependant property, if it does then make a link between that node and the one identified as the primary dependant.'
however I haven't dealt with force graphs before and tutorials are few and far between so I'm really struggling with how to make any change to my code without breaking it. Currently it is based on the answer here and I use the Meteor framework if that's of any relevance.
Template.tree.rendered = function () {
var graph = new myGraph("#svgdiv");
Lessons.find().observe({
added: function (doc) {
graph.addNode(doc._id, doc.title);
},
removed: function (doc) {
graph.removeNode(doc._id);
}
});
function myGraph(el) {
w = 1500,
h = 1000;
var svg = d3.select(el)
.append("svg")
.attr("width", w)
.attr("height", h)
.attr("pointer-events", "all")
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "lightgrey");
var vis = svg.append('g');
var force2 = d3.layout.force();
var links = force2.links();
var nodes = force2.nodes();
var update = function () {
var link = vis.selectAll("line")
.data(links, function(d) {return d.source.id + "-" + d.target.id;});
link.enter().append("line")
.attr("id",function(d){return d.source.id + "-" + d.target.id;})
.attr("class","link");
link.append("title")
.text(function(d){
return d.value;
});
link.exit().remove();
var node = vis.selectAll("g")
.data(nodes, function(d) { return d.id;});
var nodeEnter = node.enter()
.append("g")
.call(force2.drag)
.append("circle")
.attr("r", 8)
.attr("fill", "#585858")
.attr("stroke", "#008db7")
.attr("stroke-width", 3)
.attr("id", function(e) { return "Node;"+e.id;})
.attr("class", ( function(f){return f.id;}));
node.exit().remove();
force2.on("tick", function() {
node.attr("transform", function(g) { return "translate(" + g.x + "," + g.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; });
});
force2
.gravity(.02)
.linkDistance( 200 )
.size([w, h])
.start();
};
update();
var findNode = function(id) {
for (var i in nodes) {
if (nodes[i]["id"] === id) return nodes[i];};
};
var findNodeIndex = function(id) {
for (var i=0;i<nodes.length;i++) {
if (nodes[i].id==id){
return i;
}
};
};
this.addNode = function (id, title) {
nodes.push({"id":id,"title":title});
update();
};
this.addLink = function (source, target, value) {
links.push({"source":findNode(source),"target":findNode(target),"value":value});
update();
};
this.removeNode = function (id) {
var i = 0;
var n = findNode(id);
nodes.splice(findNodeIndex(id),1);
update();
};
}