source: Dev/trunk/d3/src/layout/tree.js @ 76

Last change on this file since 76 was 76, checked in by fpvanagthoven, 14 years ago

d3

File size: 6.4 KB
Line 
1// Node-link tree diagram using the Reingold-Tilford "tidy" algorithm
2d3.layout.tree = function() {
3  var hierarchy = d3.layout.hierarchy().sort(null).value(null),
4      separation = d3_layout_treeSeparation,
5      size = [1, 1]; // width, height
6
7  function tree(d, i) {
8    var nodes = hierarchy.call(this, d, i),
9        root = nodes[0];
10
11    function firstWalk(node, previousSibling) {
12      var children = node.children,
13          layout = node._tree;
14      if (children) {
15        var n = children.length,
16            firstChild = children[0],
17            previousChild,
18            ancestor = firstChild,
19            child,
20            i = -1;
21        while (++i < n) {
22          child = children[i];
23          firstWalk(child, previousChild);
24          ancestor = apportion(child, previousChild, ancestor);
25          previousChild = child;
26        }
27        d3_layout_treeShift(node);
28        var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim);
29        if (previousSibling) {
30          layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
31          layout.mod = layout.prelim - midpoint;
32        } else {
33          layout.prelim = midpoint;
34        }
35      } else {
36        if (previousSibling) {
37          layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
38        }
39      }
40    }
41
42    function secondWalk(node, x) {
43      node.x = node._tree.prelim + x;
44      var children = node.children;
45      if (children) {
46        var i = -1,
47            n = children.length;
48        x += node._tree.mod;
49        while (++i < n) {
50          secondWalk(children[i], x);
51        }
52      }
53    }
54
55    function apportion(node, previousSibling, ancestor) {
56      if (previousSibling) {
57        var vip = node,
58            vop = node,
59            vim = previousSibling,
60            vom = node.parent.children[0],
61            sip = vip._tree.mod,
62            sop = vop._tree.mod,
63            sim = vim._tree.mod,
64            som = vom._tree.mod,
65            shift;
66        while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
67          vom = d3_layout_treeLeft(vom);
68          vop = d3_layout_treeRight(vop);
69          vop._tree.ancestor = node;
70          shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip);
71          if (shift > 0) {
72            d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift);
73            sip += shift;
74            sop += shift;
75          }
76          sim += vim._tree.mod;
77          sip += vip._tree.mod;
78          som += vom._tree.mod;
79          sop += vop._tree.mod;
80        }
81        if (vim && !d3_layout_treeRight(vop)) {
82          vop._tree.thread = vim;
83          vop._tree.mod += sim - sop;
84        }
85        if (vip && !d3_layout_treeLeft(vom)) {
86          vom._tree.thread = vip;
87          vom._tree.mod += sip - som;
88          ancestor = node;
89        }
90      }
91      return ancestor;
92    }
93
94    // Initialize temporary layout variables.
95    d3_layout_treeVisitAfter(root, function(node, previousSibling) {
96      node._tree = {
97        ancestor: node,
98        prelim: 0,
99        mod: 0,
100        change: 0,
101        shift: 0,
102        number: previousSibling ? previousSibling._tree.number + 1 : 0
103      };
104    });
105
106    // Compute the layout using Buchheim et al.'s algorithm.
107    firstWalk(root);
108    secondWalk(root, -root._tree.prelim);
109
110    // Compute the left-most, right-most, and depth-most nodes for extents.
111    var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost),
112        right = d3_layout_treeSearch(root, d3_layout_treeRightmost),
113        deep = d3_layout_treeSearch(root, d3_layout_treeDeepest),
114        x0 = left.x - separation(left, right) / 2,
115        x1 = right.x + separation(right, left) / 2,
116        y1 = deep.depth || 1;
117
118    // Clear temporary layout variables; transform x and y.
119    d3_layout_treeVisitAfter(root, function(node) {
120      node.x = (node.x - x0) / (x1 - x0) * size[0];
121      node.y = node.depth / y1 * size[1];
122      delete node._tree;
123    });
124
125    return nodes;
126  }
127
128  tree.separation = function(x) {
129    if (!arguments.length) return separation;
130    separation = x;
131    return tree;
132  };
133
134  tree.size = function(x) {
135    if (!arguments.length) return size;
136    size = x;
137    return tree;
138  };
139
140  return d3_layout_hierarchyRebind(tree, hierarchy);
141};
142
143function d3_layout_treeSeparation(a, b) {
144  return a.parent == b.parent ? 1 : 2;
145}
146
147// function d3_layout_treeSeparationRadial(a, b) {
148//   return (a.parent == b.parent ? 1 : 2) / a.depth;
149// }
150
151function d3_layout_treeLeft(node) {
152  return node.children ? node.children[0] : node._tree.thread;
153}
154
155function d3_layout_treeRight(node) {
156  return node.children ? node.children[node.children.length - 1] : node._tree.thread;
157}
158
159function d3_layout_treeSearch(node, compare) {
160  var children = node.children;
161  if (children) {
162    var child,
163        n = children.length,
164        i = -1;
165    while (++i < n) {
166      if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) {
167        node = child;
168      }
169    }
170  }
171  return node;
172}
173
174function d3_layout_treeRightmost(a, b) {
175  return a.x - b.x;
176}
177
178function d3_layout_treeLeftmost(a, b) {
179  return b.x - a.x;
180}
181
182function d3_layout_treeDeepest(a, b) {
183  return a.depth - b.depth;
184}
185
186function d3_layout_treeVisitAfter(node, callback) {
187  function visit(node, previousSibling) {
188    var children = node.children;
189    if (children) {
190      var child,
191          previousChild = null,
192          i = -1,
193          n = children.length;
194      while (++i < n) {
195        child = children[i];
196        visit(child, previousChild);
197        previousChild = child;
198      }
199    }
200    callback(node, previousSibling);
201  }
202  visit(node, null);
203}
204
205function d3_layout_treeShift(node) {
206  var shift = 0,
207      change = 0,
208      children = node.children,
209      i = children.length,
210      child;
211  while (--i >= 0) {
212    child = children[i]._tree;
213    child.prelim += shift;
214    child.mod += shift;
215    shift += child.shift + (change += child.change);
216  }
217}
218
219function d3_layout_treeMove(ancestor, node, shift) {
220  ancestor = ancestor._tree;
221  node = node._tree;
222  var change = shift / (node.number - ancestor.number);
223  ancestor.change += change;
224  node.change -= change;
225  node.shift += shift;
226  node.prelim += shift;
227  node.mod += shift;
228}
229
230function d3_layout_treeAncestor(vim, node, ancestor) {
231  return vim._tree.ancestor.parent == node.parent
232      ? vim._tree.ancestor
233      : ancestor;
234}
Note: See TracBrowser for help on using the repository browser.