From d756caa0d852d981d0ad96ec65cb1b4cf4862ec9 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Sat, 11 Jun 2011 11:31:42 -0700 Subject: [PATCH] Simplify subclassing of hierarchy layout. The subclasses can't use the same object as the parent class, because they are functions. But, there's no reason to duplicate the code that rebinds the methods onto the subclass. --- d3.layout.js | 61 +++++++--------- d3.layout.min.js | 2 +- examples/tree/tree-dynamic.html | 119 ++++++++++++++++++++++++++++++++ src/layout/cluster.js | 6 +- src/layout/hierarchy.js | 18 +++++ src/layout/pack.js | 8 +-- src/layout/partition.js | 6 +- src/layout/tree.js | 17 +---- src/layout/treemap.js | 6 +- 9 files changed, 170 insertions(+), 73 deletions(-) create mode 100644 examples/tree/tree-dynamic.html diff --git a/d3.layout.js b/d3.layout.js index c9f0b1cd..f0f844c4 100644 --- a/d3.layout.js +++ b/d3.layout.js @@ -529,17 +529,13 @@ d3.layout.partition = function() { return nodes; } - partition.sort = d3.rebind(partition, hierarchy.sort); - partition.children = d3.rebind(partition, hierarchy.children); - partition.value = d3.rebind(partition, hierarchy.value); - partition.size = function(x) { if (!arguments.length) return size; size = x; return partition; }; - return partition; + return d3_layout_hierarchyRebind(partition, hierarchy); }; d3.layout.pie = function() { var value = Number, @@ -1054,6 +1050,15 @@ d3.layout.hierarchy = function() { return hierarchy; } +// A method assignment helper for hierarchy subclasses. +function d3_layout_hierarchyRebind(object, hierarchy) { + object.sort = d3.rebind(object, hierarchy.sort); + object.children = d3.rebind(object, hierarchy.children); + object.links = d3_layout_hierarchyLinks; + object.value = d3.rebind(object, hierarchy.value); + return object; +} + function d3_layout_hierarchyChildren(d) { return d.children; } @@ -1065,8 +1070,17 @@ function d3_layout_hierarchyValue(d) { function d3_layout_hierarchySort(a, b) { return b.value - a.value; } + +// Returns an array source+target objects for the specified nodes. +function d3_layout_hierarchyLinks(nodes) { + return d3.merge(nodes.map(function(parent) { + return (parent.children || []).map(function(child) { + return {source: parent, target: child}; + }); + })); +} d3.layout.pack = function() { - var hierarchy = d3.layout.hierarchy(), + var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), size = [1, 1]; function pack(d, i) { @@ -1087,17 +1101,13 @@ d3.layout.pack = function() { return nodes; } - pack.sort = d3.rebind(pack, hierarchy.sort); - pack.children = d3.rebind(pack, hierarchy.children); - pack.value = d3.rebind(pack, hierarchy.value); - pack.size = function(x) { if (!arguments.length) return size; size = x; return pack; }; - return pack.sort(d3_layout_packSort); + return d3_layout_hierarchyRebind(pack, hierarchy); }; function d3_layout_packSort(a, b) { @@ -1310,10 +1320,6 @@ d3.layout.cluster = function() { return nodes; } - cluster.sort = d3.rebind(cluster, hierarchy.sort); - cluster.children = d3.rebind(cluster, hierarchy.children); - cluster.links = d3_layout_treeLinks; - cluster.separation = function(x) { if (!arguments.length) return separation; separation = x; @@ -1326,7 +1332,7 @@ d3.layout.cluster = function() { return cluster; }; - return cluster; + return d3_layout_hierarchyRebind(cluster, hierarchy); }; function d3_layout_clusterY(children) { @@ -1465,7 +1471,7 @@ d3.layout.tree = function() { 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; + y1 = deep.depth || 1; // Clear temporary layout variables; transform x and y. d3_layout_treeVisitAfter(root, function(node) { @@ -1477,10 +1483,6 @@ d3.layout.tree = function() { return nodes; } - tree.sort = d3.rebind(tree, hierarchy.sort); - tree.children = d3.rebind(tree, hierarchy.children); - tree.links = d3_layout_treeLinks; - tree.separation = function(x) { if (!arguments.length) return separation; separation = x; @@ -1493,18 +1495,9 @@ d3.layout.tree = function() { return tree; }; - return tree; + return d3_layout_hierarchyRebind(tree, hierarchy); }; -// Returns an array source+target objects for the specified nodes. -function d3_layout_treeLinks(nodes) { - return d3.merge(nodes.map(function(parent) { - return (parent.children || []).map(function(child) { - return {source: parent, target: child}; - }); - })); -} - function d3_layout_treeSeparation(a, b) { return a.parent == b.parent ? 1 : 2; } @@ -1740,10 +1733,6 @@ d3.layout.treemap = function() { return nodes; } - treemap.sort = d3.rebind(treemap, hierarchy.sort); - treemap.children = d3.rebind(treemap, hierarchy.children); - treemap.value = d3.rebind(treemap, hierarchy.value); - treemap.size = function(x) { if (!arguments.length) return size; size = x; @@ -1769,6 +1758,6 @@ d3.layout.treemap = function() { return treemap; }; - return treemap; + return d3_layout_hierarchyRebind(treemap, hierarchy); }; })() \ No newline at end of file diff --git a/d3.layout.min.js b/d3.layout.min.js index fb423160..dcab42ef 100644 --- a/d3.layout.min.js +++ b/d3.layout.min.js @@ -1 +1 @@ -(function(){function Y(a,b,c){return a._tree.ancestor.parent==b.parent?a._tree.ancestor:c}function X(a,b,c){a=a._tree,b=b._tree;var d=c/(b.number-a.number);a.change+=d,b.change-=d,b.shift+=c,b.prelim+=c,b.mod+=c}function W(a){var b=0,c=0,d=a.children,e=d.length,f;while(--e>=0)f=d[e]._tree,f.prelim+=b,f.mod+=b,b+=f.shift+(c+=f.change)}function V(a,b){function c(a,d){var e=a.children;if(e){var f,g=null,h=-1,i=e.length;while(++h0&&(a=d)}return a}function Q(a){return a.children?a.children[a.children.length-1]:a._tree.thread}function P(a){return a.children?a.children[0]:a._tree.thread}function O(a,b){return a.parent==b.parent?1:2}function N(a){return d3.merge(a.map(function(a){return(a.children||[]).map(function(b){return{source:a,target:b}})}))}function M(a){var b=a.children;return b?M(b[b.length-1]):a}function L(a){var b=a.children;return b?L(b[0]):a}function K(a){return a.reduce(function(a,b){return a+b.x},0)/a.length}function J(a){return 1+d3.max(a,function(a){return a.y})}function I(a,b,c){var d=b.r+c.r,e=a.r+c.r,f=b.x-a.x,g=b.y-a.y,h=Math.sqrt(f*f+g*g),i=(e*e+h*h-d*d)/(2*e*h),j=Math.acos(i),k=i*e,l=Math.sin(j)*e;f/=h,g/=h,c.x=a.x+k*f+l*g,c.y=a.y+k*g-l*f}function H(a,b,c,d){var e=a.children;a.x=b+=d*a.x,a.y=c+=d*a.y,a.r*=d;if(e){var f=-1,g=e.length;while(++f1){h=a[1],h.x=h.r,h.y=0,l(h);if(f>2){i=a[2],I(g,h,i),l(i),A(g,i),g._pack_prev=i,A(i,h),h=g._pack_next;for(var m=3;m0?(B(g,j),h=j,m--):(B(j,h),g=j,m--)}}}var q=(b+c)/2,r=(d+e)/2,s=0;for(var m=0;m.001}function B(a,b){a._pack_next=b,b._pack_prev=a}function A(a,b){var c=a._pack_next;a._pack_next=b,b._pack_prev=a,b._pack_next=c,c._pack_prev=b}function z(a,b){return a.value-b.value}function y(a,b){return b.value-a.value}function x(a){return a.value}function w(a){return a.children}function v(a){return[d3.min(a),d3.max(a)]}function u(a,b){var c=-1,d=+a[0],e=(a[1]-d)/b,f=[];while(++c<=b)f[c]=e*c+d;return f}function t(a,b){return u(a,Math.ceil(Math.log(b.length)/Math.LN2+1))}function s(a,b){return a+b[1]}function r(a){return a.reduce(s,0)}function q(a){var b=1,c=0,d=a[0][1],e,f=a.length;for(;bd&&(c=b,d=e);return c}function n(a,b,c){a.y0=b,a.y=c}function m(a){return a.y}function l(a){return a.x}function k(a){var b=0,c=0;a.count=0,a.leaf||a.nodes.forEach(function(d){k(d),a.count+=d.count,b+=d.count*d.cx,c+=d.count*d.cy}),a.point&&(a.leaf||(a.point.x+=Math.random()-.5,a.point.y+=Math.random()-.5),a.count++,b+=a.point.x,c+=a.point.y),a.cx=b/a.count,a.cy=c/a.count}function j(){d3.event.stopPropagation(),d3.event.preventDefault()}function i(){c&&(j(),c=!1)}function h(c,d){(a=c).fixed=!0,b=!1,e=this,j()}function g(b){b!==a&&(b.fixed=!1)}function f(a){a.fixed=!0}d3.layout={},d3.layout.chord=function(){function k(){b.sort(function(a,b){a=Math.min(a.source.value,a.target.value),b=Math.min(b.source.value,b.target.value);return i(a,b)})}function j(){var a={},j=[],l=d3.range(e),m=[],n,o,p,q,r;b=[],c=[],n=0,q=-1;while(++qe&&(e=h),d.push(h)}for(g=0;g=i[0]&&o<=i[1]&&(k=g[d3.bisect(j,o,1,m)-1],k.y+=n,k.push(e[f]));return g}var a=!0,b=Number,c=v,d=t;e.value=function(a){if(!arguments.length)return b;b=a;return e},e.range=function(a){if(!arguments.length)return c;c=d3.functor(a);return e},e.bins=function(a){if(!arguments.length)return d;d=typeof a=="number"?function(b){return u(b,a)}:d3.functor(a);return e},e.frequency=function(b){if(!arguments.length)return a;a=!!b;return e};return e},d3.layout.hierarchy=function(){function g(a){var b=[];e(a,0,b);return b}function f(a,b){var d=a.children,e=0;if(d){var h=-1,i=d.length,j=b+1;while(++h0&&(X(Y(g,a,d),a,m),i+=m,j+=m),k+=g._tree.mod,i+=e._tree.mod,l+=h._tree.mod,j+=f._tree.mod;g&&!Q(f)&&(f._tree.thread=g,f._tree.mod+=k-j),e&&!P(h)&&(h._tree.thread=e,h._tree.mod+=i-l,d=a)}return d}function i(a,b){a.x=a._tree.prelim+b;var c=a.children;if(c){var d=-1,e=c.length;b+=a._tree.mod;while(++dd.dy)j=d.dy;while(++fd.dx)j=d.dx;while(++fe&&(e=d);c*=c,b*=b;return Math.max(b*e*f/c,c/(b*g*f))}function i(a){if(!!a.children){var b={x:a.x,y:a.y,dx:a.dx,dy:a.dy},c=a.children.slice(),d,e=[];e.area=0;while(d=c.pop())e.push(d),e.area+=d.area,d.z!=null&&(k(e,d.z?b.dx:b.dy,b,!c.length),e.length=e.area=0);a.children.forEach(i)}}function h(a){if(!!a.children){var b={x:a.x,y:a.y,dx:a.dx,dy:a.dy},c=[],d=a.children.slice(),e,f=Infinity,g,i=Math.min(b.dx,b.dy),l;c.area=0;while((l=d.length)>0)c.push(e=d[l-1]),c.area+=e.area,(g=j(c,i))<=f?(d.pop(),f=g):(c.area-=c.pop().area,k(c,i,b,!1),i=Math.min(b.dx,b.dy),c.length=c.area=0,f=Infinity);c.length&&(k(c,i,b,!0),c.length=c.area=0),a.children.forEach(h)}}function g(a,b){var c=a.children;a.area=a.value*b;if(c){var d=-1,e=c.length;while(++d=0)f=d[e]._tree,f.prelim+=b,f.mod+=b,b+=f.shift+(c+=f.change)}function W(a,b){function c(a,d){var e=a.children;if(e){var f,g=null,h=-1,i=e.length;while(++h0&&(a=d)}return a}function R(a){return a.children?a.children[a.children.length-1]:a._tree.thread}function Q(a){return a.children?a.children[0]:a._tree.thread}function P(a,b){return a.parent==b.parent?1:2}function O(a){var b=a.children;return b?O(b[b.length-1]):a}function N(a){var b=a.children;return b?N(b[0]):a}function M(a){return a.reduce(function(a,b){return a+b.x},0)/a.length}function L(a){return 1+d3.max(a,function(a){return a.y})}function K(a,b,c){var d=b.r+c.r,e=a.r+c.r,f=b.x-a.x,g=b.y-a.y,h=Math.sqrt(f*f+g*g),i=(e*e+h*h-d*d)/(2*e*h),j=Math.acos(i),k=i*e,l=Math.sin(j)*e;f/=h,g/=h,c.x=a.x+k*f+l*g,c.y=a.y+k*g-l*f}function J(a,b,c,d){var e=a.children;a.x=b+=d*a.x,a.y=c+=d*a.y,a.r*=d;if(e){var f=-1,g=e.length;while(++f1){h=a[1],h.x=h.r,h.y=0,l(h);if(f>2){i=a[2],K(g,h,i),l(i),C(g,i),g._pack_prev=i,C(i,h),h=g._pack_next;for(var m=3;m0?(D(g,j),h=j,m--):(D(j,h),g=j,m--)}}}var q=(b+c)/2,r=(d+e)/2,s=0;for(var m=0;m.001}function D(a,b){a._pack_next=b,b._pack_prev=a}function C(a,b){var c=a._pack_next;a._pack_next=b,b._pack_prev=a,b._pack_next=c,c._pack_prev=b}function B(a,b){return a.value-b.value}function A(a){return d3.merge(a.map(function(a){return(a.children||[]).map(function(b){return{source:a,target:b}})}))}function z(a,b){return b.value-a.value}function y(a){return a.value}function x(a){return a.children}function w(a,b){a.sort=d3.rebind(a,b.sort),a.children=d3.rebind(a,b.children),a.links=A,a.value=d3.rebind(a,b.value);return a}function v(a){return[d3.min(a),d3.max(a)]}function u(a,b){var c=-1,d=+a[0],e=(a[1]-d)/b,f=[];while(++c<=b)f[c]=e*c+d;return f}function t(a,b){return u(a,Math.ceil(Math.log(b.length)/Math.LN2+1))}function s(a,b){return a+b[1]}function r(a){return a.reduce(s,0)}function q(a){var b=1,c=0,d=a[0][1],e,f=a.length;for(;bd&&(c=b,d=e);return c}function n(a,b,c){a.y0=b,a.y=c}function m(a){return a.y}function l(a){return a.x}function k(a){var b=0,c=0;a.count=0,a.leaf||a.nodes.forEach(function(d){k(d),a.count+=d.count,b+=d.count*d.cx,c+=d.count*d.cy}),a.point&&(a.leaf||(a.point.x+=Math.random()-.5,a.point.y+=Math.random()-.5),a.count++,b+=a.point.x,c+=a.point.y),a.cx=b/a.count,a.cy=c/a.count}function j(){d3.event.stopPropagation(),d3.event.preventDefault()}function i(){c&&(j(),c=!1)}function h(c,d){(a=c).fixed=!0,b=!1,e=this,j()}function g(b){b!==a&&(b.fixed=!1)}function f(a){a.fixed=!0}d3.layout={},d3.layout.chord=function(){function k(){b.sort(function(a,b){a=Math.min(a.source.value,a.target.value),b=Math.min(b.source.value,b.target.value);return i(a,b)})}function j(){var a={},j=[],l=d3.range(e),m=[],n,o,p,q,r;b=[],c=[],n=0,q=-1;while(++qe&&(e=h),d.push(h)}for(g=0;g=i[0]&&o<=i[1]&&(k=g[d3.bisect(j,o,1,m)-1],k.y+=n,k.push(e[f]));return g}var a=!0,b=Number,c=v,d=t;e.value=function(a){if(!arguments.length)return b;b=a;return e},e.range=function(a){if(!arguments.length)return c;c=d3.functor(a);return e},e.bins=function(a){if(!arguments.length)return d;d=typeof a=="number"?function(b){return u(b,a)}:d3.functor(a);return e},e.frequency=function(b){if(!arguments.length)return a;a=!!b;return e};return e},d3.layout.hierarchy=function(){function g(a){var b=[];e(a,0,b);return b}function f(a,b){var d=a.children,e=0;if(d){var h=-1,i=d.length,j=b+1;while(++h0&&(Y(Z(g,a,d),a,m),i+=m,j+=m),k+=g._tree.mod,i+=e._tree.mod,l+=h._tree.mod,j+=f._tree.mod;g&&!R(f)&&(f._tree.thread=g,f._tree.mod+=k-j),e&&!Q(h)&&(h._tree.thread=e,h._tree.mod+=i-l,d=a)}return d}function i(a,b){a.x=a._tree.prelim+b;var c=a.children;if(c){var d=-1,e=c.length;b+=a._tree.mod;while(++dd.dy)j=d.dy;while(++fd.dx)j=d.dx;while(++fe&&(e=d);c*=c,b*=b;return Math.max(b*e*f/c,c/(b*g*f))}function i(a){if(!!a.children){var b={x:a.x,y:a.y,dx:a.dx,dy:a.dy},c=a.children.slice(),d,e=[];e.area=0;while(d=c.pop())e.push(d),e.area+=d.area,d.z!=null&&(k(e,d.z?b.dx:b.dy,b,!c.length),e.length=e.area=0);a.children.forEach(i)}}function h(a){if(!!a.children){var b={x:a.x,y:a.y,dx:a.dx,dy:a.dy},c=[],d=a.children.slice(),e,f=Infinity,g,i=Math.min(b.dx,b.dy),l;c.area=0;while((l=d.length)>0)c.push(e=d[l-1]),c.area+=e.area,(g=j(c,i))<=f?(d.pop(),f=g):(c.area-=c.pop().area,k(c,i,b,!1),i=Math.min(b.dx,b.dy),c.length=c.area=0,f=Infinity);c.length&&(k(c,i,b,!0),c.length=c.area=0),a.children.forEach(h)}}function g(a,b){var c=a.children;a.area=a.value*b;if(c){var d=-1,e=c.length;while(++d + + + + Node-Link Tree + + + + + + + + diff --git a/src/layout/cluster.js b/src/layout/cluster.js index fae458dd..e030f45b 100644 --- a/src/layout/cluster.js +++ b/src/layout/cluster.js @@ -39,10 +39,6 @@ d3.layout.cluster = function() { return nodes; } - cluster.sort = d3.rebind(cluster, hierarchy.sort); - cluster.children = d3.rebind(cluster, hierarchy.children); - cluster.links = d3_layout_treeLinks; - cluster.separation = function(x) { if (!arguments.length) return separation; separation = x; @@ -55,7 +51,7 @@ d3.layout.cluster = function() { return cluster; }; - return cluster; + return d3_layout_hierarchyRebind(cluster, hierarchy); }; function d3_layout_clusterY(children) { diff --git a/src/layout/hierarchy.js b/src/layout/hierarchy.js index 46dd4c15..96142881 100644 --- a/src/layout/hierarchy.js +++ b/src/layout/hierarchy.js @@ -78,6 +78,15 @@ d3.layout.hierarchy = function() { return hierarchy; } +// A method assignment helper for hierarchy subclasses. +function d3_layout_hierarchyRebind(object, hierarchy) { + object.sort = d3.rebind(object, hierarchy.sort); + object.children = d3.rebind(object, hierarchy.children); + object.links = d3_layout_hierarchyLinks; + object.value = d3.rebind(object, hierarchy.value); + return object; +} + function d3_layout_hierarchyChildren(d) { return d.children; } @@ -89,3 +98,12 @@ function d3_layout_hierarchyValue(d) { function d3_layout_hierarchySort(a, b) { return b.value - a.value; } + +// Returns an array source+target objects for the specified nodes. +function d3_layout_hierarchyLinks(nodes) { + return d3.merge(nodes.map(function(parent) { + return (parent.children || []).map(function(child) { + return {source: parent, target: child}; + }); + })); +} diff --git a/src/layout/pack.js b/src/layout/pack.js index 6f1a07ad..527ac27c 100644 --- a/src/layout/pack.js +++ b/src/layout/pack.js @@ -1,5 +1,5 @@ d3.layout.pack = function() { - var hierarchy = d3.layout.hierarchy(), + var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), size = [1, 1]; function pack(d, i) { @@ -20,17 +20,13 @@ d3.layout.pack = function() { return nodes; } - pack.sort = d3.rebind(pack, hierarchy.sort); - pack.children = d3.rebind(pack, hierarchy.children); - pack.value = d3.rebind(pack, hierarchy.value); - pack.size = function(x) { if (!arguments.length) return size; size = x; return pack; }; - return pack.sort(d3_layout_packSort); + return d3_layout_hierarchyRebind(pack, hierarchy); }; function d3_layout_packSort(a, b) { diff --git a/src/layout/partition.js b/src/layout/partition.js index aacbf292..171d3fc1 100644 --- a/src/layout/partition.js +++ b/src/layout/partition.js @@ -38,15 +38,11 @@ d3.layout.partition = function() { return nodes; } - partition.sort = d3.rebind(partition, hierarchy.sort); - partition.children = d3.rebind(partition, hierarchy.children); - partition.value = d3.rebind(partition, hierarchy.value); - partition.size = function(x) { if (!arguments.length) return size; size = x; return partition; }; - return partition; + return d3_layout_hierarchyRebind(partition, hierarchy); }; diff --git a/src/layout/tree.js b/src/layout/tree.js index d48cabb3..40452866 100644 --- a/src/layout/tree.js +++ b/src/layout/tree.js @@ -113,7 +113,7 @@ d3.layout.tree = function() { 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; + y1 = deep.depth || 1; // Clear temporary layout variables; transform x and y. d3_layout_treeVisitAfter(root, function(node) { @@ -125,10 +125,6 @@ d3.layout.tree = function() { return nodes; } - tree.sort = d3.rebind(tree, hierarchy.sort); - tree.children = d3.rebind(tree, hierarchy.children); - tree.links = d3_layout_treeLinks; - tree.separation = function(x) { if (!arguments.length) return separation; separation = x; @@ -141,18 +137,9 @@ d3.layout.tree = function() { return tree; }; - return tree; + return d3_layout_hierarchyRebind(tree, hierarchy); }; -// Returns an array source+target objects for the specified nodes. -function d3_layout_treeLinks(nodes) { - return d3.merge(nodes.map(function(parent) { - return (parent.children || []).map(function(child) { - return {source: parent, target: child}; - }); - })); -} - function d3_layout_treeSeparation(a, b) { return a.parent == b.parent ? 1 : 2; } diff --git a/src/layout/treemap.js b/src/layout/treemap.js index 3ef0e17a..7309aa19 100644 --- a/src/layout/treemap.js +++ b/src/layout/treemap.js @@ -141,10 +141,6 @@ d3.layout.treemap = function() { return nodes; } - treemap.sort = d3.rebind(treemap, hierarchy.sort); - treemap.children = d3.rebind(treemap, hierarchy.children); - treemap.value = d3.rebind(treemap, hierarchy.value); - treemap.size = function(x) { if (!arguments.length) return size; size = x; @@ -170,5 +166,5 @@ d3.layout.treemap = function() { return treemap; }; - return treemap; + return d3_layout_hierarchyRebind(treemap, hierarchy); };