Commit 082c9593 authored by Mike Bostock's avatar Mike Bostock
Browse files

Don’t store temporary variables on tree nodes.

Rather than creating a temporary _tree hash on the tree nodes to store temporary
variables needed to compute the tree layout, the tree is wrapped. This
eliminates the risk of a namespace collision, and eliminates the need to
subsequently delete temporary variables. (They will be garbage collected.)
parent 49ba8afe
......@@ -6714,95 +6714,108 @@
d3.layout.tree = function() {
var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
function tree(d, i) {
var nodes = hierarchy.call(this, d, i), root = nodes[0];
function firstWalk(node, previousSibling) {
var children = node.children, layout = node._tree;
if (children && (n = children.length)) {
var n, firstChild = children[0], previousChild, ancestor = firstChild, child, i = -1;
while (++i < n) {
child = children[i];
firstWalk(child, previousChild);
ancestor = apportion(child, previousChild, ancestor);
previousChild = child;
}
d3_layout_treeShift(node);
var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim);
if (previousSibling) {
layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
layout.mod = layout.prelim - midpoint;
} else {
layout.prelim = midpoint;
}
var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0);
firstWalk(root1);
secondWalk(root1, -root1.z);
if (nodeSize) {
d3_layout_treeVisitAfter(root0, function(node) {
node.x *= size[0];
node.y = node.depth * size[1];
});
} else {
var left = d3_layout_treeSearch(root0, d3_layout_treeLeftmost), right = d3_layout_treeSearch(root0, d3_layout_treeRightmost), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2, y1 = d3_layout_treeSearch(root0, d3_layout_treeDeepest).depth || 1;
d3_layout_treeVisitAfter(root0, function(node) {
node.x = (node.x - x0) / (x1 - x0) * size[0];
node.y = node.depth / y1 * size[1];
});
}
return nodes;
}
function wrapTree(root0) {
var root1 = {
c: [ root0 ]
}, queue = [ root1 ], node1;
while ((node1 = queue.pop()) != null) {
for (var children = node1.c, child, i = 0, n = children.length; i < n; ++i) {
queue.push((children[i] = child = {
_: children[i],
p: node1,
c: (child = children[i].children) && child.slice() || [],
a: null,
z: 0,
m: 0,
d: 0,
s: 0,
i: i
}).a = child);
}
}
root1 = root1.c[0];
root1.p = null;
return root1;
}
function firstWalk(node, previousSibling) {
var children = node.c;
if (n = children.length) {
var n, firstChild = children[0], previousChild, ancestor = firstChild, child, i = -1;
while (++i < n) {
child = children[i];
firstWalk(child, previousChild);
ancestor = apportion(child, previousChild, ancestor);
previousChild = child;
}
d3_layout_treeShift(node);
var midpoint = .5 * (firstChild.z + child.z);
if (previousSibling) {
node.z = previousSibling.z + separation(node._, previousSibling._);
node.m = node.z - midpoint;
} else {
if (previousSibling) {
layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
}
node.z = midpoint;
}
} else if (previousSibling) {
node.z = previousSibling.z + separation(node._, previousSibling._);
}
function secondWalk(node, x) {
node.x = node._tree.prelim + x;
var children = node.children;
if (children && (n = children.length)) {
var i = -1, n;
x += node._tree.mod;
while (++i < n) {
secondWalk(children[i], x);
}
function secondWalk(node, x) {
node._.x = node.z + x;
var children = node.c;
if (n = children.length) {
var i = -1, n;
x += node.m;
while (++i < n) {
secondWalk(children[i], x);
}
}
}
function apportion(node, previousSibling, ancestor) {
if (previousSibling) {
var vip = node, vop = node, vim = previousSibling, vom = node.p.c[0], sip = vip.m, sop = vop.m, sim = vim.m, som = vom.m, shift;
while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
vom = d3_layout_treeLeft(vom);
vop = d3_layout_treeRight(vop);
vop.a = node;
shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
if (shift > 0) {
d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift);
sip += shift;
sop += shift;
}
sim += vim.m;
sip += vip.m;
som += vom.m;
sop += vop.m;
}
}
function apportion(node, previousSibling, ancestor) {
if (previousSibling) {
var vip = node, vop = node, vim = previousSibling, vom = node.parent.children[0], sip = vip._tree.mod, sop = vop._tree.mod, sim = vim._tree.mod, som = vom._tree.mod, shift;
while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
vom = d3_layout_treeLeft(vom);
vop = d3_layout_treeRight(vop);
vop._tree.ancestor = node;
shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip);
if (shift > 0) {
d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift);
sip += shift;
sop += shift;
}
sim += vim._tree.mod;
sip += vip._tree.mod;
som += vom._tree.mod;
sop += vop._tree.mod;
}
if (vim && !d3_layout_treeRight(vop)) {
vop._tree.thread = vim;
vop._tree.mod += sim - sop;
}
if (vip && !d3_layout_treeLeft(vom)) {
vom._tree.thread = vip;
vom._tree.mod += sip - som;
ancestor = node;
}
if (vim && !d3_layout_treeRight(vop)) {
vop.t = vim;
vop.m += sim - sop;
}
if (vip && !d3_layout_treeLeft(vom)) {
vom.t = vip;
vom.m += sip - som;
ancestor = node;
}
return ancestor;
}
d3_layout_treeVisitAfter(root, function(node, previousSibling) {
node._tree = {
ancestor: node,
prelim: 0,
mod: 0,
change: 0,
shift: 0,
number: previousSibling ? previousSibling._tree.number + 1 : 0
};
});
firstWalk(root);
secondWalk(root, -root._tree.prelim);
var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost), right = d3_layout_treeSearch(root, d3_layout_treeRightmost), deep = d3_layout_treeSearch(root, d3_layout_treeDeepest), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2, y1 = deep.depth || 1;
d3_layout_treeVisitAfter(root, nodeSize ? function(node) {
node.x *= size[0];
node.y = node.depth * size[1];
delete node._tree;
} : function(node) {
node.x = (node.x - x0) / (x1 - x0) * size[0];
node.y = node.depth / y1 * size[1];
delete node._tree;
});
return nodes;
return ancestor;
}
tree.separation = function(x) {
if (!arguments.length) return separation;
......@@ -6825,12 +6838,12 @@
return a.parent == b.parent ? 1 : 2;
}
function d3_layout_treeLeft(node) {
var children = node.children;
return children && children.length ? children[0] : node._tree.thread;
var children = node.c;
return children.length ? children[0] : node.t;
}
function d3_layout_treeRight(node) {
var children = node.children, n;
return children && (n = children.length) ? children[n - 1] : node._tree.thread;
var children = node.c, n;
return (n = children.length) ? children[n - 1] : node.t;
}
function d3_layout_treeSearch(node, compare) {
var children = node.children;
......@@ -6854,41 +6867,34 @@
return a.depth - b.depth;
}
function d3_layout_treeVisitAfter(node, callback) {
function visit(node, previousSibling) {
(function visit(node) {
var children = node.children;
if (children && (n = children.length)) {
var child, previousChild = null, i = -1, n;
while (++i < n) {
child = children[i];
visit(child, previousChild);
previousChild = child;
}
var i = -1, n;
while (++i < n) visit(children[i]);
}
callback(node, previousSibling);
}
visit(node, null);
callback(node);
})(node);
}
function d3_layout_treeShift(node) {
var shift = 0, change = 0, children = node.children, i = children.length, child;
var shift = 0, change = 0, children = node.c, i = children.length, child;
while (--i >= 0) {
child = children[i]._tree;
child.prelim += shift;
child.mod += shift;
shift += child.shift + (change += child.change);
child = children[i];
child.z += shift;
child.m += shift;
shift += child.s + (change += child.d);
}
}
function d3_layout_treeMove(ancestor, node, shift) {
ancestor = ancestor._tree;
node = node._tree;
var change = shift / (node.number - ancestor.number);
ancestor.change += change;
node.change -= change;
node.shift += shift;
node.prelim += shift;
node.mod += shift;
var change = shift / (node.i - ancestor.i);
ancestor.d += change;
node.d -= change;
node.s += shift;
node.z += shift;
node.m += shift;
}
function d3_layout_treeAncestor(vim, node, ancestor) {
return vim._tree.ancestor.parent == node.parent ? vim._tree.ancestor : ancestor;
return vim.a.p === node.p ? vim.a : ancestor;
}
d3.layout.pack = function() {
var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -10,127 +10,143 @@ d3.layout.tree = function() {
function tree(d, i) {
var nodes = hierarchy.call(this, d, i),
root = nodes[0];
function firstWalk(node, previousSibling) {
var children = node.children,
layout = node._tree;
if (children && (n = children.length)) {
var n,
firstChild = children[0],
previousChild,
ancestor = firstChild,
child,
i = -1;
while (++i < n) {
child = children[i];
firstWalk(child, previousChild);
ancestor = apportion(child, previousChild, ancestor);
previousChild = child;
}
d3_layout_treeShift(node);
var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim);
if (previousSibling) {
layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
layout.mod = layout.prelim - midpoint;
} else {
layout.prelim = midpoint;
}
} else {
if (previousSibling) {
layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
}
}
root0 = nodes[0],
root1 = wrapTree(root0);
// Compute the layout using Buchheim et al.'s algorithm.
// Temporary variables are stored on the wrapped tree.
firstWalk(root1);
secondWalk(root1, -root1.z);
// If a fixed node size is specified, scale x and y.
if (nodeSize) {
d3_layout_treeVisitAfter(root0, function(node) {
node.x *= size[0];
node.y = node.depth * size[1];
});
}
function secondWalk(node, x) {
node.x = node._tree.prelim + x;
var children = node.children;
if (children && (n = children.length)) {
var i = -1,
n;
x += node._tree.mod;
while (++i < n) {
secondWalk(children[i], x);
}
}
// If a fixed tree size is specified, scale x and y based on the extent.
// Compute the left-most, right-most, and depth-most nodes for extents.
else {
var left = d3_layout_treeSearch(root0, d3_layout_treeLeftmost),
right = d3_layout_treeSearch(root0, d3_layout_treeRightmost),
x0 = left.x - separation(left, right) / 2,
x1 = right.x + separation(right, left) / 2,
y1 = d3_layout_treeSearch(root0, d3_layout_treeDeepest).depth || 1;
d3_layout_treeVisitAfter(root0, function(node) {
node.x = (node.x - x0) / (x1 - x0) * size[0];
node.y = node.depth / y1 * size[1];
});
}
function apportion(node, previousSibling, ancestor) {
if (previousSibling) {
var vip = node,
vop = node,
vim = previousSibling,
vom = node.parent.children[0],
sip = vip._tree.mod,
sop = vop._tree.mod,
sim = vim._tree.mod,
som = vom._tree.mod,
shift;
while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
vom = d3_layout_treeLeft(vom);
vop = d3_layout_treeRight(vop);
vop._tree.ancestor = node;
shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip);
if (shift > 0) {
d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift);
sip += shift;
sop += shift;
}
sim += vim._tree.mod;
sip += vip._tree.mod;
som += vom._tree.mod;
sop += vop._tree.mod;
}
if (vim && !d3_layout_treeRight(vop)) {
vop._tree.thread = vim;
vop._tree.mod += sim - sop;
}
if (vip && !d3_layout_treeLeft(vom)) {
vom._tree.thread = vip;
vom._tree.mod += sip - som;
ancestor = node;
}
return nodes;
}
function wrapTree(root0) {
var root1 = {c: [root0]},
queue = [root1],
node1;
while ((node1 = queue.pop()) != null) {
for (var children = node1.c, child, i = 0, n = children.length; i < n; ++i) {
queue.push((children[i] = child = {
_: children[i], // source node
p: node1, // parent
c: (child = children[i].children) && child.slice() || [], // children
a: null, // ancestor
z: 0, // prelim
m: 0, // mod
d: 0, // change
s: 0, // shift
i: i
}).a = child);
}
return ancestor;
}
// Initialize temporary layout variables.
d3_layout_treeVisitAfter(root, function(node, previousSibling) {
node._tree = {
ancestor: node,
prelim: 0,
mod: 0,
change: 0,
shift: 0,
number: previousSibling ? previousSibling._tree.number + 1 : 0
};
});
root1 = root1.c[0];
root1.p = null;
return root1;
}
// Compute the layout using Buchheim et al.'s algorithm.
firstWalk(root);
secondWalk(root, -root._tree.prelim);
function firstWalk(node, previousSibling) {
if (n = (children = node.c).length) {
var n,
children,
firstChild = children[0],
previousChild,
ancestor = firstChild,
child,
i = -1;
while (++i < n) {
child = children[i];
firstWalk(child, previousChild);
ancestor = apportion(child, previousChild, ancestor);
previousChild = child;
}
d3_layout_treeShift(node);
var midpoint = .5 * (firstChild.z + child.z);
if (previousSibling) {
node.z = previousSibling.z + separation(node._, previousSibling._);
node.m = node.z - midpoint;
} else {
node.z = midpoint;
}
} else if (previousSibling) {
node.z = previousSibling.z + separation(node._, previousSibling._);
}
}
// Compute the left-most, right-most, and depth-most nodes for extents.
var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost),
right = d3_layout_treeSearch(root, d3_layout_treeRightmost),
deep = d3_layout_treeSearch(root, d3_layout_treeDeepest),
x0 = left.x - separation(left, right) / 2,
x1 = right.x + separation(right, left) / 2,
y1 = deep.depth || 1;
// Clear temporary layout variables; transform x and y.
d3_layout_treeVisitAfter(root, nodeSize ? function(node) {
node.x *= size[0];
node.y = node.depth * size[1];
delete node._tree;
} : function(node) {
node.x = (node.x - x0) / (x1 - x0) * size[0];
node.y = node.depth / y1 * size[1];
delete node._tree;
});
function secondWalk(node, x) {
node._.x = node.z + x;
var children = node.c;
if (n = children.length) {
var i = -1,
n;
x += node.m;
while (++i < n) {
secondWalk(children[i], x);
}
}
}
return nodes;
function apportion(node, previousSibling, ancestor) {
if (previousSibling) {
var vip = node,
vop = node,
vim = previousSibling,
vom = node.p.c[0],
sip = vip.m,
sop = vop.m,
sim = vim.m,
som = vom.m,
shift;
while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
vom = d3_layout_treeLeft(vom);
vop = d3_layout_treeRight(vop);
vop.a = node;
shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
if (shift > 0) {
d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift);
sip += shift;
sop += shift;
}
sim += vim.m;
sip += vip.m;
som += vom.m;
sop += vop.m;
}
if (vim && !d3_layout_treeRight(vop)) {
vop.t = vim;
vop.m += sim - sop;
}
if (vip && !d3_layout_treeLeft(vom)) {
vom.t = vip;
vom.m += sip - som;
ancestor = node;
}
}
return ancestor;
}
tree.separation = function(x) {
......@@ -163,14 +179,13 @@ function d3_layout_treeSeparation(a, b) {
// }
function d3_layout_treeLeft(node) {
var children = node.children;
return children && children.length ? children[0] : node._tree.thread;
var children = node.c;
return children.length ? children[0] : node.t;
}
function d3_layout_treeRight(node) {
var children = node.children,
n;
return children && (n = children.length) ? children[n - 1] : node._tree.thread;
var children = node.c, n;
return (n = children.length) ? children[n - 1] : node.t;
}
function d3_layout_treeSearch(node, compare) {
......@@ -201,51 +216,41 @@ function d3_layout_treeDeepest(a, b) {
}
function d3_layout_treeVisitAfter(node, callback) {
function visit(node, previousSibling) {
(function visit(node) {
var children = node.children;
if (children && (n = children.length)) {
var child,
previousChild = null,
i = -1,
n;
while (++i < n) {
child = children[i];
visit(child, previousChild);
previousChild = child;
}
var i = -1, n;
while (++i < n) visit(children[i]);
}
callback(node, previousSibling);
}
visit(node, null);
callback(node);
})(node);
}
function d3_layout_treeShift(node) {
var shift = 0,
change = 0,
children = node.children,
children = node.c,
i = children.length,
child;
while (--i >= 0) {
child = children[i]._tree;
child.prelim += shift;
child.mod += shift;
shift += child.shift + (change += child.change);
child = children[i];
child.z += shift;
child.m += shift;
shift += child.s + (change += child.d);
}
}
function d3_layout_treeMove(ancestor, node, shift) {
ancestor = ancestor._tree;
node = node._tree;
var change = shift / (node.number - ancestor.number);
ancestor.change += change;
node.change -= change;
node.shift += shift;
node.prelim += shift;
node.mod += shift;
var change = shift / (node.i - ancestor.i);
ancestor.d += change;