d3.js tree layout is a great tool, but it only allows children to have a single parent by default. I would like to be able to let children have more than one parent. I am happy with the position of the nodes provided by the tree's default behaviour. All I want is to draw extra diagonal links between childless parents and existing children after the default tree has been calculated. My script currently looks like this :
<script>
var h = 870,
w = 1200;
var dimcirc = [60,30,10,8];
var offsetL = 60;
var couleurs = [
"#a2a2ff",
"#87ff87",
"#ffc55c",
"#ff844d",
"#ffe452"];
var tree = d3.layout.tree()
.size([h-100, w-400])
.separation(function(a, b) { return (a.parent == b.parent ? 1 : 1.2); });
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y + offsetL, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h)
.append("g");
d3.json("donnees.json", function(error, root) {
if (error) throw error;
var nodes = tree.nodes(root),
links = tree.links(nodes);
var link = svg.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("d", diagonal);
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", function(d){return "niv_" + d.depth.toString();})
.classed("node", true)
.attr("transform", function(d) { return "translate(" + (d.y + offsetL).toString() + "," + d.x +")"; })
// Draw node circles
node.append("circle")
.attr("fill", function(d){
console.log(d.couleur);
if(d.couleur!=null){
return couleurs[d.couleur];
}
else {return couleurs[4];}
})
.attr("r", function(d){
return dimcirc[d.depth];
});
node.append("text")
.attr("dy", "0.31em")
//.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.attr("transform", function(d){
return "translate(" + (dimcirc[d.depth] + 10).toString() + ")"
})
.text(function(d) { return d.name; })
.call(wrap, 230);
});
// Wrap text
function wrap(text, width) {
// A function to help wrap svg text.
}
</script>
I have tried to copy the children of a parent node to a childless node using the root object, to no avail (the structure of the root object is quite obscure to me).
Any help appreciated.
PS : I am aware of a similar question (d3.js tree nodes with same parents), but the lack of answers didn't help me much or course.
EDIT
I've managed to get what I want by :
1) Identifying the nodes of interest : those without children that are to be connected to existing children, and the parent of the later.
2) Extracting the coordinates of the source (node without children) and target (children to be connected to this node) from the root object.
3) Creating the extra paths using the same diagonal as for the "standard" paths created by the tree.
Here's the code I've added to get the source and target for each path :
var link2 = [];
var loopLink2 = [{"a":0,"b":1, "c":0,"d":0},{"a":1,"b":1, "c":1,"d":0},{"a":3,"b":0, "c":2,"d":1}];
loopLink2.forEach(function(d){
var sourX = root.children[d.a].children[d.b].x;
var sourY = root.children[d.a].children[d.b].y;
root.children[d.c].children[d.d].children.forEach(function(d){
link2.push({"source":{"x":sourX,"y":sourY}, "target":{"x":d.x,"y":d.y}});
console.log(link2);
});
});
And here's where the actual creation of the paths takes place :
svg.selectAll(".link2")
.data(link2)
.enter().append("path")
.attr("class", "link2")
.attr("d", diagonal)
.attr("transform", function(d) { return "translate(0," + offsetT +")"; });
Someone has a nicer solution ?