Commit b567b5d1 authored by Mike Bostock's avatar Mike Bostock
Browse files

Non-recursive hierarchy layout.

parent 9d0bbefa
...@@ -6332,41 +6332,30 @@ ...@@ -6332,41 +6332,30 @@
var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity; var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity;
d3.layout.hierarchy = function() { d3.layout.hierarchy = function() {
var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue;
function recurse(node, depth, nodes) { function hierarchy(root) {
var childs = children.call(hierarchy, node, depth); var stack = [ root ], nodes = [], node;
node.depth = depth; root.depth = 0;
nodes.push(node); while ((node = stack.pop()) != null) {
if (childs && (n = childs.length)) { nodes.push(node);
var i = -1, n, c = node.children = new Array(n), v = 0, j = depth + 1, d; if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) {
while (++i < n) { var n, childs, child;
d = c[i] = recurse(childs[i], j, nodes); while (--n >= 0) {
d.parent = node; stack.push(child = childs[n]);
v += d.value; child.parent = node;
} child.depth = node.depth + 1;
if (sort) c.sort(sort); }
if (value) node.value = v; if (value) node.value = 0;
} else { node.children = childs;
delete node.children; } else {
if (value) { if (value) node.value = +value.call(hierarchy, node, node.depth) || 0;
node.value = +value.call(hierarchy, node, depth) || 0; delete node.children;
} }
} }
return node; d3_layout_hierarchyVisitAfter(root, function(node) {
} var childs, parent;
function revalue(node, depth) { if (sort && (childs = node.children)) childs.sort(sort);
var children = node.children, v = 0; if (value && (parent = node.parent)) parent.value += node.value;
if (children && (n = children.length)) { });
var i = -1, n, j = depth + 1;
while (++i < n) v += revalue(children[i], j);
} else if (value) {
v = +value.call(hierarchy, node, depth) || 0;
}
if (value) node.value = v;
return v;
}
function hierarchy(d) {
var nodes = [];
recurse(d, 0, nodes);
return nodes; return nodes;
} }
hierarchy.sort = function(x) { hierarchy.sort = function(x) {
...@@ -6385,7 +6374,11 @@ ...@@ -6385,7 +6374,11 @@
return hierarchy; return hierarchy;
}; };
hierarchy.revalue = function(root) { hierarchy.revalue = function(root) {
revalue(root, 0); if (value) d3_layout_hierarchyVisitAfter(root, function(node) {
var parent;
node.value = node.children ? 0 : +value.call(hierarchy, node, node.depth) || 0;
if (parent = node.parent) parent.value += node.value;
});
return root; return root;
}; };
return hierarchy; return hierarchy;
...@@ -6396,6 +6389,29 @@ ...@@ -6396,6 +6389,29 @@
object.links = d3_layout_hierarchyLinks; object.links = d3_layout_hierarchyLinks;
return object; return object;
} }
function d3_layout_hierarchyVisitBefore(node, callback) {
var nodes = [ node ];
while ((node = nodes.pop()) != null) {
callback(node);
if ((children = node.children) && (n = children.length)) {
var n, children;
while (--n >= 0) nodes.push(children[n]);
}
}
}
function d3_layout_hierarchyVisitAfter(node, callback) {
var nodes = [ node ], nodes2 = [];
while ((node = nodes.pop()) != null) {
nodes2.push(node);
if ((children = node.children) && (n = children.length)) {
var i = -1, n, children;
while (++i < n) nodes.push(children[i]);
}
}
while ((node = nodes2.pop()) != null) {
callback(node);
}
}
function d3_layout_hierarchyChildren(d) { function d3_layout_hierarchyChildren(d) {
return d.children; return d.children;
} }
...@@ -6711,20 +6727,174 @@ ...@@ -6711,20 +6727,174 @@
function d3_layout_histogramRange(values) { function d3_layout_histogramRange(values) {
return [ d3.min(values), d3.max(values) ]; return [ d3.min(values), d3.max(values) ];
} }
d3.layout.pack = function() {
var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
function pack(d, i) {
var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() {
return radius;
};
root.x = root.y = 0;
d3_layout_hierarchyVisitAfter(root, function(d) {
d.r = +r(d.value);
});
d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
if (padding) {
var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
d3_layout_hierarchyVisitAfter(root, function(d) {
d.r += dr;
});
d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
d3_layout_hierarchyVisitAfter(root, function(d) {
d.r -= dr;
});
}
d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
return nodes;
}
pack.size = function(_) {
if (!arguments.length) return size;
size = _;
return pack;
};
pack.radius = function(_) {
if (!arguments.length) return radius;
radius = _ == null || typeof _ === "function" ? _ : +_;
return pack;
};
pack.padding = function(_) {
if (!arguments.length) return padding;
padding = +_;
return pack;
};
return d3_layout_hierarchyRebind(pack, hierarchy);
};
function d3_layout_packSort(a, b) {
return a.value - b.value;
}
function d3_layout_packInsert(a, b) {
var c = a._pack_next;
a._pack_next = b;
b._pack_prev = a;
b._pack_next = c;
c._pack_prev = b;
}
function d3_layout_packSplice(a, b) {
a._pack_next = b;
b._pack_prev = a;
}
function d3_layout_packIntersects(a, b) {
var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r;
return .999 * dr * dr > dx * dx + dy * dy;
}
function d3_layout_packSiblings(node) {
if (!(nodes = node.children) || !(n = nodes.length)) return;
var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n;
function bound(node) {
xMin = Math.min(node.x - node.r, xMin);
xMax = Math.max(node.x + node.r, xMax);
yMin = Math.min(node.y - node.r, yMin);
yMax = Math.max(node.y + node.r, yMax);
}
nodes.forEach(d3_layout_packLink);
a = nodes[0];
a.x = -a.r;
a.y = 0;
bound(a);
if (n > 1) {
b = nodes[1];
b.x = b.r;
b.y = 0;
bound(b);
if (n > 2) {
c = nodes[2];
d3_layout_packPlace(a, b, c);
bound(c);
d3_layout_packInsert(a, c);
a._pack_prev = c;
d3_layout_packInsert(c, b);
b = a._pack_next;
for (i = 3; i < n; i++) {
d3_layout_packPlace(a, b, c = nodes[i]);
var isect = 0, s1 = 1, s2 = 1;
for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
if (d3_layout_packIntersects(j, c)) {
isect = 1;
break;
}
}
if (isect == 1) {
for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
if (d3_layout_packIntersects(k, c)) {
break;
}
}
}
if (isect) {
if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b);
i--;
} else {
d3_layout_packInsert(a, c);
b = c;
bound(c);
}
}
}
}
var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0;
for (i = 0; i < n; i++) {
c = nodes[i];
c.x -= cx;
c.y -= cy;
cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y));
}
node.r = cr;
nodes.forEach(d3_layout_packUnlink);
}
function d3_layout_packLink(node) {
node._pack_next = node._pack_prev = node;
}
function d3_layout_packUnlink(node) {
delete node._pack_next;
delete node._pack_prev;
}
function d3_layout_packTransform(node, x, y, k) {
var children = node.children;
node.x = x += k * node.x;
node.y = y += k * node.y;
node.r *= k;
if (children) {
var i = -1, n = children.length;
while (++i < n) d3_layout_packTransform(children[i], x, y, k);
}
}
function d3_layout_packPlace(a, b, c) {
var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y;
if (db && (dx || dy)) {
var da = b.r + c.r, dc = dx * dx + dy * dy;
da *= da;
db *= db;
var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
c.x = a.x + x * dx + y * dy;
c.y = a.y + x * dy - y * dx;
} else {
c.x = a.x + db;
c.y = a.y;
}
}
d3.layout.tree = function() { d3.layout.tree = function() {
var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false; var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
function tree(d, i) { function tree(d, i) {
var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0); var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0);
d3_layout_treeVisitAfter(root1, firstWalk), root1.parent.mod = -root1.prelim; d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.mod = -root1.prelim;
d3_layout_treeVisitBefore(root1, secondWalk); d3_layout_hierarchyVisitBefore(root1, secondWalk);
if (nodeSize) { if (nodeSize) {
d3_layout_treeVisitBefore(root1, function(node) { d3_layout_hierarchyVisitBefore(root1, function(node) {
node.node.x *= size[0]; node.node.x *= size[0];
node.node.y = node.node.depth * size[1]; node.node.y = node.node.depth * size[1];
}); });
} else { } 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; 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_treeVisitBefore(root1, function(node) { d3_layout_hierarchyVisitBefore(root1, function(node) {
node.node.x = (node.node.x - x0) / (x1 - x0) * size[0]; node.node.x = (node.node.x - x0) / (x1 - x0) * size[0];
node.node.y = node.node.depth / y1 * size[1]; node.node.y = node.node.depth / y1 * size[1];
}); });
...@@ -6845,23 +7015,6 @@ ...@@ -6845,23 +7015,6 @@
function d3_layout_treeDeepest(a, b) { function d3_layout_treeDeepest(a, b) {
return a.depth - b.depth; return a.depth - b.depth;
} }
function d3_layout_treeVisitBefore(node, callback) {
var nodes = [ node ];
while ((node = nodes.pop()) != null) {
callback(node);
if ((children = node.children) && (n = children.length)) {
var i = -1, n, children;
while (++i < n) nodes.push(children[i]);
}
}
}
function d3_layout_treeVisitAfter(node, callback) {
var nodes = [];
d3_layout_treeVisitBefore(node, function(node) {
nodes.push(node);
});
while ((node = nodes.pop()) != null) callback(node);
}
function d3_layout_treeLeft(v) { function d3_layout_treeLeft(v) {
var children = v.children; var children = v.children;
return children.length ? children[0] : v.thread; return children.length ? children[0] : v.thread;
...@@ -6890,165 +7043,11 @@ ...@@ -6890,165 +7043,11 @@
function d3_layout_treeAncestor(vim, v, ancestor) { function d3_layout_treeAncestor(vim, v, ancestor) {
return vim.ancestor.parent === v.parent ? vim.ancestor : ancestor; return vim.ancestor.parent === v.parent ? vim.ancestor : ancestor;
} }
d3.layout.pack = function() {
var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
function pack(d, i) {
var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() {
return radius;
};
root.x = root.y = 0;
d3_layout_treeVisitAfter(root, function(d) {
d.r = +r(d.value);
});
d3_layout_treeVisitAfter(root, d3_layout_packSiblings);
if (padding) {
var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
d3_layout_treeVisitAfter(root, function(d) {
d.r += dr;
});
d3_layout_treeVisitAfter(root, d3_layout_packSiblings);
d3_layout_treeVisitAfter(root, function(d) {
d.r -= dr;
});
}
d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
return nodes;
}
pack.size = function(_) {
if (!arguments.length) return size;
size = _;
return pack;
};
pack.radius = function(_) {
if (!arguments.length) return radius;
radius = _ == null || typeof _ === "function" ? _ : +_;
return pack;
};
pack.padding = function(_) {
if (!arguments.length) return padding;
padding = +_;
return pack;
};
return d3_layout_hierarchyRebind(pack, hierarchy);
};
function d3_layout_packSort(a, b) {
return a.value - b.value;
}
function d3_layout_packInsert(a, b) {
var c = a._pack_next;
a._pack_next = b;
b._pack_prev = a;
b._pack_next = c;
c._pack_prev = b;
}
function d3_layout_packSplice(a, b) {
a._pack_next = b;
b._pack_prev = a;
}
function d3_layout_packIntersects(a, b) {
var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r;
return .999 * dr * dr > dx * dx + dy * dy;
}
function d3_layout_packSiblings(node) {
if (!(nodes = node.children) || !(n = nodes.length)) return;
var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n;
function bound(node) {
xMin = Math.min(node.x - node.r, xMin);
xMax = Math.max(node.x + node.r, xMax);
yMin = Math.min(node.y - node.r, yMin);
yMax = Math.max(node.y + node.r, yMax);
}
nodes.forEach(d3_layout_packLink);
a = nodes[0];
a.x = -a.r;
a.y = 0;
bound(a);
if (n > 1) {
b = nodes[1];
b.x = b.r;
b.y = 0;
bound(b);
if (n > 2) {
c = nodes[2];
d3_layout_packPlace(a, b, c);
bound(c);
d3_layout_packInsert(a, c);
a._pack_prev = c;
d3_layout_packInsert(c, b);
b = a._pack_next;
for (i = 3; i < n; i++) {
d3_layout_packPlace(a, b, c = nodes[i]);
var isect = 0, s1 = 1, s2 = 1;
for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
if (d3_layout_packIntersects(j, c)) {
isect = 1;
break;
}
}
if (isect == 1) {
for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
if (d3_layout_packIntersects(k, c)) {
break;
}
}
}
if (isect) {
if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b);
i--;
} else {
d3_layout_packInsert(a, c);
b = c;
bound(c);
}
}
}
}
var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0;
for (i = 0; i < n; i++) {
c = nodes[i];
c.x -= cx;
c.y -= cy;
cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y));
}
node.r = cr;
nodes.forEach(d3_layout_packUnlink);
}
function d3_layout_packLink(node) {
node._pack_next = node._pack_prev = node;
}
function d3_layout_packUnlink(node) {
delete node._pack_next;
delete node._pack_prev;
}
function d3_layout_packTransform(node, x, y, k) {
var children = node.children;
node.x = x += k * node.x;
node.y = y += k * node.y;
node.r *= k;
if (children) {
var i = -1, n = children.length;
while (++i < n) d3_layout_packTransform(children[i], x, y, k);
}
}
function d3_layout_packPlace(a, b, c) {
var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y;
if (db && (dx || dy)) {
var da = b.r + c.r, dc = dx * dx + dy * dy;
da *= da;
db *= db;
var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
c.x = a.x + x * dx + y * dy;
c.y = a.y + x * dy - y * dx;
} else {
c.x = a.x + db;
c.y = a.y;
}
}
d3.layout.cluster = function() { d3.layout.cluster = function() {
var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false; var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
function cluster(d, i) { function cluster(d, i) {
var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0; var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0;
d3_layout_treeVisitAfter(root, function(node) { d3_layout_hierarchyVisitAfter(root, function(node) {
var children = node.children; var children = node.children;
if (children && children.length) { if (children && children.length) {
node.x = d3_layout_clusterX(children); node.x = d3_layout_clusterX(children);
...@@ -7060,7 +7059,7 @@ ...@@ -7060,7 +7059,7 @@
} }
}); });
var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2;
d3_layout_treeVisitAfter(root, nodeSize ? function(node) { d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) {
node.x = (node.x - root.x) * size[0]; node.x = (node.x - root.x) * size[0];
node.y = (root.y - node.y) * size[1]; node.y = (root.y - node.y) * size[1];
} : function(node) { } : function(node) {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -18,7 +18,7 @@ d3.layout.cluster = function() { ...@@ -18,7 +18,7 @@ d3.layout.cluster = function() {
x = 0; x = 0;
// First walk, computing the initial x & y values. // First walk, computing the initial x & y values.
d3_layout_treeVisitAfter(root, function(node) { d3_layout_hierarchyVisitAfter(root, function(node) {
var children = node.children; var children = node.children;
if (children && children.length) { if (children && children.length) {
node.x = d3_layout_clusterX(children); node.x = d3_layout_clusterX(children);
...@@ -37,7 +37,7 @@ d3.layout.cluster = function() { ...@@ -37,7 +37,7 @@ d3.layout.cluster = function() {
x1 = right.x + separation(right, left) / 2; x1 = right.x + separation(right, left) / 2;
// Second walk, normalizing x & y to the desired size. // Second walk, normalizing x & y to the desired size.
d3_layout_treeVisitAfter(root, nodeSize ? function(node) { d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) {
node.x = (node.x - root.x) * size[0]; node.x = (node.x - root.x) * size[0];
node.y = (root.y - node.y) * size[1]; node.y = (root.y - node.y) * size[1];
} : function(node) { } : function(node) {
......
...@@ -7,54 +7,36 @@ d3.layout.hierarchy = function() { ...@@ -7,54 +7,36 @@ d3.layout.hierarchy = function() {
children = d3_layout_hierarchyChildren, children = d3_layout_hierarchyChildren,
value = d3_layout_hierarchyValue; value = d3_layout_hierarchyValue;
// Recursively compute the node depth and value. function hierarchy(root) {
// Also converts to a standard hierarchy structure. var stack = [root],
function recurse(node, depth, nodes) { nodes = [],
var childs = children.call(hierarchy, node, depth); node;
node.depth = depth;
nodes.push(node); root.depth = 0;
if (childs && (n = childs.length)) {
var i = -1, while ((node = stack.pop()) != null) {
n, nodes.push(node);
c = node.children = new Array(n), if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) {
v = 0, var n, childs, child;
j = depth + 1, while (--n >= 0) {
d; stack.push(