0
votes

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 ?

1
A graph where nodes have multiple parents isn't a tree anymore. I don't think the tree layout is the right choice here; maybe the force layout instead?Lars Kotthoff

1 Answers

0
votes

I don't know whether this is exactly what you are looking for. But this might help.

Dagre

Dagre is a library that uses d3 to plot acyclic graphs. You can try modifying this. Here is the github wiki.

Family Tree

Here you can find an excellent answer for creating a family tree , that can have multiple parents for one child. This might fulfill your requirement if the structure doesn't matter.