I've been trying to implement a directed force layout using a json file that I've written. Before I tried to make it begin with all nodes collapsed, it was working properly. I've declared a property called "index" for all the nodes, indicating which level of the tree they belong (root's index is 0, it's children are 1, etc.) I'm guessing that there is a problem with "index" property of the nodes because when I first start my page their values are correct, but when I collapse and re-open one a node the index values of related nodes change and it does not draw the links properly anymore.
Any ideas on why it keeps changing the index values that are taken from json whenever I click anything, or do you have any reference projects I might look into to solve the problem?
Thanks,
Here is my script code:
var indx = 0;
var width = 1260, height = 1220, root;
var force = d3.layout.force().linkDistance(80).charge(-300)
.gravity(.05).size([ width, height ]).on("tick", tick);
var svg = d3.select("#chart").append("svg")
.attr("width", width).attr("height", height);
var link = svg.selectAll(".link"), node = svg
.selectAll(".node");
var text = svg.selectAll(".link");
d3.json("graph.json", function(error, json) {
root = json;
update();
});
function toggle(d) {
indx = d.index;
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update();
}
function prepareLinks(allNodes) {
var newLinks = new Array();
var allLinks = d3.layout.tree().links(allNodes);
for ( var i = 0; allLinks.length > i; i++) {
if (allLinks[i].source.index <= indx) {
console.log("source : " + allLinks[i].source.index
+ "-" + allLinks[i].source.name);
newLinks.push(allLinks[i]);
}
console.log(allLinks[i].target.index + "-"
+ allLinks[i].target.name);
}
for ( var i = 0; newLinks.length > i; i++) {
console.log("newLinks : " + newLinks[i].source.index
+ "-" + newLinks[i].source.name);
console.log(newLinks[i].target.index + "-"
+ newLinks[i].target.name);
}
return newLinks;
}
function update() {
var nodes = flatten(root, indx);
var links = prepareLinks(nodes);
// Restart the force layout.
force.nodes(nodes).links(links).start();
// Update links.
link = link.data(links, function(d) {
return d.target.id;
});
link.exit().remove();
link.enter().insert("line", ".node").attr("class", "link");
svg.selectAll("g.ltext").remove();
text = svg.append("svg:g").selectAll("g").data(links);
text.enter().append("svg:g").attr("class", "ltext");
text.append("svg:text").attr("class", "linktext").attr(
"dx", 5).attr("dy", ".35em").text(
function(d) {
return d.source.type;
});
// Update nodes.
node = node.data(nodes, function(d) {
return d.id;
});
node.exit().remove();
var nodeEnter = node.enter().append("g").attr("class",
"node").on("click", function(d) {
toggle(d);
update(d);
}).call(force.drag);
nodeEnter.append("circle").attr("r", function(d) {
return Math.sqrt(d.size) / 10 || 10;
});
nodeEnter.append("text").attr("dy", ".35em").text(
function(d) {
return d.name;
});
node.select("circle").style("fill", color);
}
function tick() {
link.attr("x1", function(d) {
return d.target.x;
}).attr("y1", function(d) {
return d.target.y;
}).attr("x2", function(d) {
return d.source.x;
}).attr("y2", function(d) {
return d.source.y;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
text.attr("transform", function(d) {
var sourcex = d.source.x;
var sourcey = d.source.y;
var targetx = d.target.x;
var targety = d.target.y;
return "translate(" + (sourcex + targetx) / 2 + ","
+ (sourcey + targety) / 2 + ")";
});
}
function color(d) {
return d._children ? "#BA8343" // collapsed package
: d.children ? "#7C4C6B" // expanded package
: "#6F704A"; // leaf node
}
// Toggle children on click.
function click(d) {
if (d3.event.defaultPrevented)
return; // ignore drag
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
// Returns a list of all nodes under the root.
function flatten(root, indx) {
var nodes = [], i = 0;
function recurse(node) {
console.log("flatten'da başta index: " + node.index
+ "ad " + node.name);
if (node.children) {
node.children.forEach(recurse);
}
if (!node.id)
node.id = ++i;
if (indx >= node.index || indx + 1 == node.index) {
nodes.push(node);
// console.log("flatten'da index: "+ node.index+ "ad "+ node.name);
}
}
recurse(root);
return nodes;
}
And here is an example from my json file:
{
"name": "Data Mining","type":"related","index":2,
"children": [
{
"name": "Yapay Sinir Ağları", "size": 7074,"type":"related","index":3,
"children": [
{
"name": "Compututional Intelligence","type":"narrower","index":4,
"children":[
{"name": "Genetik Algoritmalar", "size": 7074,"type":"related","index":5},
{"name": "Bulanık Mantık", "size": 7074,"type":"related","index":5},
{"name":"Soft Computing","type":"related","index":5,
"children": [
{"name": "Esnek Hesaplama", "size": 7074,"type":"related","index":6}
]
}
]
}
]
}