source: Dev/branches/jQueryUI/client/d3/src/layout/tree.js @ 249

Last change on this file since 249 was 249, checked in by hendrikvanantwerpen, 13 years ago

This one's for Subversion, because it's so close...

First widget (stripped down sequencer).
Seperated client and server code in two direcotry trees.

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.