Dynamically resize the d3 tree layout based on number of childnodes

user1773438 picture user1773438 · Oct 27, 2012 · Viewed 22.3k times · Source

From this example http://mbostock.github.com/d3/talk/20111018/tree.html I have build a d3 tree layout where the root is in the middle(root.x0 = width/2) in the browser window and the nodes are in the downward direction instead of facing to the right.

Is it possible to re-size the tree, such that the width of the tree is dependent on the number of nodes of the tree such that if number of nodes is less, then width is less or else if number of nodes is greater then width is large ?

I also need to know how d3 tree layout currently calculates the "d.x" attribute? How can I manipulate "d.x" attribute to adjust the spacing between the nodes of the d3 tree layout.

Answer

Superboggly picture Superboggly · Jan 3, 2013

So when you set the "size" of the tree layout all you are doing is setting the value the tree layout will interpolate the x and y values to. So there is no reason you can't compute the width or height you want and change it in the layout on each update call.

I copied the example you gave to a jsFiddle and added the following to the update function:

// compute the new height
var levelWidth = [1];
var childCount = function(level, n) {

  if(n.children && n.children.length > 0) {
    if(levelWidth.length <= level + 1) levelWidth.push(0);

    levelWidth[level+1] += n.children.length;
    n.children.forEach(function(d) {
      childCount(level + 1, d);
    });
  }
};
childCount(0, root);  
var newHeight = d3.max(levelWidth) * 20; // 20 pixels per line  
tree = tree.size([newHeight, w]);

Note that this is a pretty rough calculation and does not factor in where the children are w.r.t their parents or anything - but you get the idea.

As for manipulating the x values of the nodes the easiest is probably to simply modify the nodes after the layout has done it's thing. In fact you can see that this is already done in the example with the y coordinate:

// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; }); 

He does this so that even if children are hidden a given level of nodes stays at the same y position, otherwise if you collapsed all the children the remaining levels would stretch to take up the whole width (try commenting that line out and see what happens as you toggle whole levels invisible).

As for going top down I think you can pretty much just flip everywhere you see x and y (and x0 and y0). Don't forget to do the same to the diagonal projection to make sure your lines flip too.