source: node_modules/d3-hierarchy/src/tree.js@ e4c61dd

Last change on this file since e4c61dd was e4c61dd, checked in by istevanoska <ilinastevanoska@…>, 6 months ago

Prototype 1.1

  • Property mode set to 100644
File size: 6.9 KB
Line 
1import {Node} from "./hierarchy/index.js";
2
3function defaultSeparation(a, b) {
4 return a.parent === b.parent ? 1 : 2;
5}
6
7// function radialSeparation(a, b) {
8// return (a.parent === b.parent ? 1 : 2) / a.depth;
9// }
10
11// This function is used to traverse the left contour of a subtree (or
12// subforest). It returns the successor of v on this contour. This successor is
13// either given by the leftmost child of v or by the thread of v. The function
14// returns null if and only if v is on the highest level of its subtree.
15function nextLeft(v) {
16 var children = v.children;
17 return children ? children[0] : v.t;
18}
19
20// This function works analogously to nextLeft.
21function nextRight(v) {
22 var children = v.children;
23 return children ? children[children.length - 1] : v.t;
24}
25
26// Shifts the current subtree rooted at w+. This is done by increasing
27// prelim(w+) and mod(w+) by shift.
28function moveSubtree(wm, wp, shift) {
29 var change = shift / (wp.i - wm.i);
30 wp.c -= change;
31 wp.s += shift;
32 wm.c += change;
33 wp.z += shift;
34 wp.m += shift;
35}
36
37// All other shifts, applied to the smaller subtrees between w- and w+, are
38// performed by this function. To prepare the shifts, we have to adjust
39// change(w+), shift(w+), and change(w-).
40function executeShifts(v) {
41 var shift = 0,
42 change = 0,
43 children = v.children,
44 i = children.length,
45 w;
46 while (--i >= 0) {
47 w = children[i];
48 w.z += shift;
49 w.m += shift;
50 shift += w.s + (change += w.c);
51 }
52}
53
54// If vi-’s ancestor is a sibling of v, returns vi-’s ancestor. Otherwise,
55// returns the specified (default) ancestor.
56function nextAncestor(vim, v, ancestor) {
57 return vim.a.parent === v.parent ? vim.a : ancestor;
58}
59
60function TreeNode(node, i) {
61 this._ = node;
62 this.parent = null;
63 this.children = null;
64 this.A = null; // default ancestor
65 this.a = this; // ancestor
66 this.z = 0; // prelim
67 this.m = 0; // mod
68 this.c = 0; // change
69 this.s = 0; // shift
70 this.t = null; // thread
71 this.i = i; // number
72}
73
74TreeNode.prototype = Object.create(Node.prototype);
75
76function treeRoot(root) {
77 var tree = new TreeNode(root, 0),
78 node,
79 nodes = [tree],
80 child,
81 children,
82 i,
83 n;
84
85 while (node = nodes.pop()) {
86 if (children = node._.children) {
87 node.children = new Array(n = children.length);
88 for (i = n - 1; i >= 0; --i) {
89 nodes.push(child = node.children[i] = new TreeNode(children[i], i));
90 child.parent = node;
91 }
92 }
93 }
94
95 (tree.parent = new TreeNode(null, 0)).children = [tree];
96 return tree;
97}
98
99// Node-link tree diagram using the Reingold-Tilford "tidy" algorithm
100export default function() {
101 var separation = defaultSeparation,
102 dx = 1,
103 dy = 1,
104 nodeSize = null;
105
106 function tree(root) {
107 var t = treeRoot(root);
108
109 // Compute the layout using Buchheim et al.’s algorithm.
110 t.eachAfter(firstWalk), t.parent.m = -t.z;
111 t.eachBefore(secondWalk);
112
113 // If a fixed node size is specified, scale x and y.
114 if (nodeSize) root.eachBefore(sizeNode);
115
116 // If a fixed tree size is specified, scale x and y based on the extent.
117 // Compute the left-most, right-most, and depth-most nodes for extents.
118 else {
119 var left = root,
120 right = root,
121 bottom = root;
122 root.eachBefore(function(node) {
123 if (node.x < left.x) left = node;
124 if (node.x > right.x) right = node;
125 if (node.depth > bottom.depth) bottom = node;
126 });
127 var s = left === right ? 1 : separation(left, right) / 2,
128 tx = s - left.x,
129 kx = dx / (right.x + s + tx),
130 ky = dy / (bottom.depth || 1);
131 root.eachBefore(function(node) {
132 node.x = (node.x + tx) * kx;
133 node.y = node.depth * ky;
134 });
135 }
136
137 return root;
138 }
139
140 // Computes a preliminary x-coordinate for v. Before that, FIRST WALK is
141 // applied recursively to the children of v, as well as the function
142 // APPORTION. After spacing out the children by calling EXECUTE SHIFTS, the
143 // node v is placed to the midpoint of its outermost children.
144 function firstWalk(v) {
145 var children = v.children,
146 siblings = v.parent.children,
147 w = v.i ? siblings[v.i - 1] : null;
148 if (children) {
149 executeShifts(v);
150 var midpoint = (children[0].z + children[children.length - 1].z) / 2;
151 if (w) {
152 v.z = w.z + separation(v._, w._);
153 v.m = v.z - midpoint;
154 } else {
155 v.z = midpoint;
156 }
157 } else if (w) {
158 v.z = w.z + separation(v._, w._);
159 }
160 v.parent.A = apportion(v, w, v.parent.A || siblings[0]);
161 }
162
163 // Computes all real x-coordinates by summing up the modifiers recursively.
164 function secondWalk(v) {
165 v._.x = v.z + v.parent.m;
166 v.m += v.parent.m;
167 }
168
169 // The core of the algorithm. Here, a new subtree is combined with the
170 // previous subtrees. Threads are used to traverse the inside and outside
171 // contours of the left and right subtree up to the highest common level. The
172 // vertices used for the traversals are vi+, vi-, vo-, and vo+, where the
173 // superscript o means outside and i means inside, the subscript - means left
174 // subtree and + means right subtree. For summing up the modifiers along the
175 // contour, we use respective variables si+, si-, so-, and so+. Whenever two
176 // nodes of the inside contours conflict, we compute the left one of the
177 // greatest uncommon ancestors using the function ANCESTOR and call MOVE
178 // SUBTREE to shift the subtree and prepare the shifts of smaller subtrees.
179 // Finally, we add a new thread (if necessary).
180 function apportion(v, w, ancestor) {
181 if (w) {
182 var vip = v,
183 vop = v,
184 vim = w,
185 vom = vip.parent.children[0],
186 sip = vip.m,
187 sop = vop.m,
188 sim = vim.m,
189 som = vom.m,
190 shift;
191 while (vim = nextRight(vim), vip = nextLeft(vip), vim && vip) {
192 vom = nextLeft(vom);
193 vop = nextRight(vop);
194 vop.a = v;
195 shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
196 if (shift > 0) {
197 moveSubtree(nextAncestor(vim, v, ancestor), v, shift);
198 sip += shift;
199 sop += shift;
200 }
201 sim += vim.m;
202 sip += vip.m;
203 som += vom.m;
204 sop += vop.m;
205 }
206 if (vim && !nextRight(vop)) {
207 vop.t = vim;
208 vop.m += sim - sop;
209 }
210 if (vip && !nextLeft(vom)) {
211 vom.t = vip;
212 vom.m += sip - som;
213 ancestor = v;
214 }
215 }
216 return ancestor;
217 }
218
219 function sizeNode(node) {
220 node.x *= dx;
221 node.y = node.depth * dy;
222 }
223
224 tree.separation = function(x) {
225 return arguments.length ? (separation = x, tree) : separation;
226 };
227
228 tree.size = function(x) {
229 return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], tree) : (nodeSize ? null : [dx, dy]);
230 };
231
232 tree.nodeSize = function(x) {
233 return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], tree) : (nodeSize ? [dx, dy] : null);
234 };
235
236 return tree;
237}
Note: See TracBrowser for help on using the repository browser.