From 0aba0702a55e15e7c510725895f669fa978aaab7 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 22 Jun 2011 09:46:52 -0700 Subject: [PATCH] Hierarchical edge bundling improvements. The input to the layout is now an array of edges to bundle, rather than nodes. This eliminates the need for an `outgoing` accessor, since the links are passed to the bundle layout directly. The svg line generator now supports a beta (straightening; bundle strength) parameter. I haven't decided if this is the right place or the right name for it, but it seems like a reasonable starting point. I'm not happy with the cos & sin needed to produce radial lines (both here and for the diagonal projection in other examples), but I don't have a good alternative yet. This commit also tries to make the construction of the links from the layout nodes a bit easier to follow. The previous code was used another intermediate representation, and I think it's cleaner to construct the default format expected by the layouts. However, there's still a good chunk of code required to massage the JSON format into a node hierarchy and array of dependencies, so I'd like to find a way to simplify that, too. --- d3.js | 29 +++++++- d3.layout.js | 45 +++--------- d3.layout.min.js | 2 +- d3.min.js | 4 +- examples/bundle/bundle-radial.html | 2 +- examples/bundle/bundle-radial.js | 74 ++++++++++--------- examples/bundle/bundle-treemap.css | 2 +- examples/bundle/bundle-treemap.html | 13 +--- examples/bundle/bundle-treemap.js | 108 +++++++++++----------------- src/layout/bundle.js | 45 +++--------- src/svg/line.js | 29 +++++++- 11 files changed, 164 insertions(+), 189 deletions(-) diff --git a/d3.js b/d3.js index 111eb43e3f805..ea1a943e6843d 100644 --- a/d3.js +++ b/d3.js @@ -2718,11 +2718,12 @@ d3.svg.line = function() { y = d3_svg_lineY, interpolate = "linear", interpolator = d3_svg_lineInterpolators[interpolate], - tension = .7; + tension = .7, + beta = 1; function line(d) { return d.length < 1 ? null - : "M" + interpolator(d3_svg_linePoints(this, d, x, y), tension); + : "M" + interpolator(d3_svg_lineStraighten(d3_svg_linePoints(this, d, x, y), beta), tension); } line.x = function(v) { @@ -2749,9 +2750,33 @@ d3.svg.line = function() { return line; }; + line.beta = function(v) { + if (!arguments.length) return beta; + beta = v; + return line; + }; + return line; }; +function d3_svg_lineStraighten(points, beta) { + var n = points.length - 1, + x0 = points[0][0], + y0 = points[0][1], + dx = points[n][0] - x0, + dy = points[n][1] - y0, + i = -1, + p, + t; + while (++i <= n) { + p = points[i]; + t = i / n; + p[0] = beta * p[0] + (1 - beta) * (x0 + t * dx); + p[1] = beta * p[1] + (1 - beta) * (y0 + t * dy); + } + return points; +} + // Converts the specified array of data into an array of points // (x-y tuples), by evaluating the specified `x` and `y` functions on each // data point. The `this` context of the evaluated functions is the specified diff --git a/d3.layout.js b/d3.layout.js index 2fa4bf5f16767..bdf2aec4a17f4 100644 --- a/d3.layout.js +++ b/d3.layout.js @@ -4,42 +4,26 @@ // the parent hierarchy to the least common ancestor, and then back down to the // destination node. d3.layout.bundle = function() { - var beta = .85, - outgoing = d3_layout_bundleOutgoing; - - function bundle(nodes) { + return function(links) { var splines = [], i = -1, - n = nodes.length; - while (++i < n) { - var node = nodes[i], - // TODO cache outgoing? - targets = outgoing.call(this, node, i); - for (var j = 0; j < targets.length; j++) { - splines.push(d3_layout_bundleSpline(node, targets[j])); - } - } + n = links.length; + while (++i < n) splines.push(d3_layout_bundleSpline(links[i])); return splines; }; - - bundle.outgoing = function(x) { - if (!arguments.length) return outgoing; - outgoing = x; - return bundle; - }; - - return bundle; }; -function d3_layout_bundleSpline(start, end) { - var lca = d3_layout_bundleLeastCommonAncestor(start, end), +function d3_layout_bundleSpline(link) { + var start = link.source, + end = link.target, + lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [start]; - while (start != lca) { + while (start !== lca) { start = start.parent; points.push(start); } var k = points.length; - while (end != lca) { + while (end !== lca) { points.splice(k, 0, end); end = end.parent; } @@ -59,26 +43,19 @@ function d3_layout_bundleAncestors(node) { } function d3_layout_bundleLeastCommonAncestor(a, b) { - if (a == b) { - return a; - } + if (a === b) return a; var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; - - while (aNode == bNode) { + while (aNode === bNode) { sharedNode = aNode; aNode = aNodes.pop(); bNode = bNodes.pop(); } return sharedNode; } - -function d3_layout_bundleOutgoing(d, i) { - return d.outgoing; -} d3.layout.chord = function() { var chord = {}, chords, diff --git a/d3.layout.min.js b/d3.layout.min.js index 7ded584e7f8d0..d10347e1c7994 100644 --- a/d3.layout.min.js +++ b/d3.layout.min.js @@ -1 +1 @@ -(function(){function bb(a,b,c){return a._tree.ancestor.parent==b.parent?a._tree.ancestor:c}function ba(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 _(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 $(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 V(a){return a.children?a.children[a.children.length-1]:a._tree.thread}function U(a){return a.children?a.children[0]:a._tree.thread}function T(a,b){return a.parent==b.parent?1:2}function S(a){var b=a.children;return b?S(b[b.length-1]):a}function R(a){var b=a.children;return b?R(b[0]):a}function Q(a){return a.reduce(function(a,b){return a+b.x},0)/a.length}function P(a){return 1+d3.max(a,function(a){return a.y})}function O(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 N(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],O(g,h,i),l(i),G(g,i),g._pack_prev=i,G(i,h),h=g._pack_next;for(var m=3;m0?(H(g,j),h=j,m--):(H(j,h),g=j,m--)}}}var q=(b+c)/2,r=(d+e)/2,s=0;for(var m=0;m.001}function H(a,b){a._pack_next=b,b._pack_prev=a}function G(a,b){var c=a._pack_next;a._pack_next=b,b._pack_prev=a,b._pack_next=c,c._pack_prev=b}function F(a,b){return a.value-b.value}function E(a){return d3.merge(a.map(function(a){return(a.children||[]).map(function(b){return{source:a,target:b}})}))}function D(a,b){return b.value-a.value}function C(a){return a.value}function B(a){return a.children}function A(a,b){a.sort=d3.rebind(a,b.sort),a.children=d3.rebind(a,b.children),a.links=E,a.value=d3.rebind(a,b.value);return a}function z(a){return[d3.min(a),d3.max(a)]}function y(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 x(a,b){return y(a,Math.ceil(Math.log(b.length)/Math.LN2+1))}function w(a,b){return a+b[1]}function v(a){return a.reduce(w,0)}function u(a){var b=1,c=0,d=a[0][1],e,f=a.length;for(;bd&&(c=b,d=e);return c}function r(a,b,c){a.y0=b,a.y=c}function q(a){return a.y}function p(a){return a.x}function o(a){var b=0,c=0;a.count=0,a.leaf||a.nodes.forEach(function(d){o(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 n(){d3.event.stopPropagation(),d3.event.preventDefault()}function m(){h&&(n(),h=!1)}function l(a,b){(f=a).fixed=!0,g=!1,i=this,n()}function k(a){a!==f&&(a.fixed=!1)}function j(a){a.fixed=!0}function e(a,b){return a.outgoing}function c(a,c){if(a==c)return a;var d=b(a),e=b(c),f=d.pop(),g=e.pop(),h=null;while(f==g)h=f,f=d.pop(),g=e.pop();return h}function b(a){var b=[],c=a.parent;while(c!=null)b.push(a),a=c,c=c.parent;b.push(a);return b}function a(a,b){var d=c(a,b),e=[a];while(a!=d)a=a.parent,e.push(a);var f=e.length;while(b!=d)e.splice(f,0,b),b=b.parent;return e}d3.layout={},d3.layout.bundle=function(){function d(b){var d=[],e=-1,f=b.length;while(++ee&&(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=z,d=x;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 y(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&&(ba(bb(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&&!V(f)&&(f._tree.thread=g,f._tree.mod+=k-j),e&&!U(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 Z(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 U(a){return a.children?a.children[a.children.length-1]:a._tree.thread}function T(a){return a.children?a.children[0]:a._tree.thread}function S(a,b){return a.parent==b.parent?1:2}function R(a){var b=a.children;return b?R(b[b.length-1]):a}function Q(a){var b=a.children;return b?Q(b[0]):a}function P(a){return a.reduce(function(a,b){return a+b.x},0)/a.length}function O(a){return 1+d3.max(a,function(a){return a.y})}function N(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 M(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],N(g,h,i),l(i),F(g,i),g._pack_prev=i,F(i,h),h=g._pack_next;for(var m=3;m0?(G(g,j),h=j,m--):(G(j,h),g=j,m--)}}}var q=(b+c)/2,r=(d+e)/2,s=0;for(var m=0;m.001}function G(a,b){a._pack_next=b,b._pack_prev=a}function F(a,b){var c=a._pack_next;a._pack_next=b,b._pack_prev=a,b._pack_next=c,c._pack_prev=b}function E(a,b){return a.value-b.value}function D(a){return d3.merge(a.map(function(a){return(a.children||[]).map(function(b){return{source:a,target:b}})}))}function C(a,b){return b.value-a.value}function B(a){return a.value}function A(a){return a.children}function z(a,b){a.sort=d3.rebind(a,b.sort),a.children=d3.rebind(a,b.children),a.links=D,a.value=d3.rebind(a,b.value);return a}function y(a){return[d3.min(a),d3.max(a)]}function x(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 w(a,b){return x(a,Math.ceil(Math.log(b.length)/Math.LN2+1))}function v(a,b){return a+b[1]}function u(a){return a.reduce(v,0)}function t(a){var b=1,c=0,d=a[0][1],e,f=a.length;for(;bd&&(c=b,d=e);return c}function q(a,b,c){a.y0=b,a.y=c}function p(a){return a.y}function o(a){return a.x}function n(a){var b=0,c=0;a.count=0,a.leaf||a.nodes.forEach(function(d){n(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 m(){d3.event.stopPropagation(),d3.event.preventDefault()}function l(){g&&(m(),g=!1)}function k(a,b){(e=a).fixed=!0,f=!1,h=this,m()}function j(a){a!==e&&(a.fixed=!1)}function i(a){a.fixed=!0}function c(a,c){if(a===c)return a;var d=b(a),e=b(c),f=d.pop(),g=e.pop(),h=null;while(f===g)h=f,f=d.pop(),g=e.pop();return h}function b(a){var b=[],c=a.parent;while(c!=null)b.push(a),a=c,c=c.parent;b.push(a);return b}function a(a){var b=a.source,d=a.target,e=c(b,d),f=[b];while(b!==e)b=b.parent,f.push(b);var g=f.length;while(d!==e)f.splice(g,0,d),d=d.parent;return f}d3.layout={},d3.layout.bundle=function(){return function(b){var c=[],d=-1,e=b.length;while(++de&&(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=y,d=w;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 x(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&&(_(ba(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&&!U(f)&&(f._tree.thread=g,f._tree.mod+=k-j),e&&!T(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(++d9&&(f=c*3/Math.sqrt(f),g[h]=f*d,g[h+1]=f*e));h=-1;while(++h<=i)f=(a[Math.min(i,h+1)][0]-a[Math.max(0,h-1)][0])/(6*(1+g[h]*g[h])),b.push([f||0,g[h]*f||0]);return b}function bW(a){var b=0,c=a.length-1,d=[],e=a[0],f=a[1],g=d[0]=bV(e,f);while(++b1){h=b[1],f=a[i],i++,d+="C"+(e[0]+g[0])+","+(e[1]+g[1])+","+(f[0]-h[0])+","+(f[1]-h[1])+","+f[0]+","+f[1];for(var j=2;jc.delay&&(c.flush=c.callback(a)),c=c.next;var d=bg()-b;d>24?(isFinite(d)&&(clearTimeout(bd),bd=setTimeout(bf,d)),bc=0):(bc=1,bh(bf))}function be(a,b){var c=Date.now(),d=!1,e,f=bb;if(!!isFinite(b)){while(f){if(f.callback===a){f.then=c,f.delay=b,d=!0;break}e=f,f=f.next}d||(bb={callback:a,then:c,delay:b,next:bb}),bc||(bd=clearTimeout(bd),bc=1,bh(bf))}}function ba(a){return typeof a=="function"?function(b,c,d){var e=a.call(this,b,c)+"";return d!=e&&d3.interpolate(d,e)}:(a=a+"",function(b,c,d){return d!=a&&d3.interpolate(d,a)})}function _(a){function n(b){var h=!0,l=-1;a.each(function(){if(i[++l]!==2){var a=(b-j[l])/k[l],n=this.__transition__,o,p,q=e[l];if(a<1){h=!1;if(a<0)return}else a=1;if(i[l]){if(!n||n.active!==c){i[l]=2;return}}else{if(!n||n.active>c){i[l]=2;return}i[l]=1,g.start.dispatch.apply(this,arguments),q=e[l]={},n.active=c;for(p in d)if(o=d[p].apply(this,arguments))q[p]=o}o=m(a);for(p in q)q[p].call(this,o);if(a===1){i[l]=2;if(n.active===c){var r=n.owner;r===c&&(delete this.__transition__,f&&this.parentNode.removeChild(this)),$=c,g.end.dispatch.apply(this,arguments),$=0,n.owner=r}}}});return h}var b={},c=$||++Z,d={},e=[],f=!1,g=d3.dispatch("start","end"),i=[],j=[],k=[],l,m=d3.ease("cubic-in-out");a.each(function(){(this.__transition__||(this.__transition__={})).owner=c}),b.delay=function(c){var d=Infinity,e=-1;typeof c=="function"?a.each(function(a,b){var f=j[++e]=+c.apply(this,arguments);fl&&(l=e)})):(l=+c,a.each(function(a,b){k[++d]=l}));return b},b.ease=function(a){m=typeof a=="function"?a:d3.ease.apply(d3,arguments);return b},b.attrTween=function(a,c){function f(b,d){var e=c.call(this,b,d,this.getAttributeNS(a.space,a.local));return e&&function(b){this.setAttributeNS(a.space,a.local,e(b))}}function e(b,d){var e=c.call(this,b,d,this.getAttribute(a));return e&&function(b){this.setAttribute(a,e(b))}}d["attr."+a]=a.local?f:e;return b},b.attr=function(a,c){return b.attrTween(a,ba(c))},b.styleTween=function(a,c,e){function f(b,d){var f=c.call(this,b,d,window.getComputedStyle(this,null).getPropertyValue(a));return f&&function(b){this.style.setProperty(a,f(b),e)}}arguments.length<3&&(e=null),d["style."+a]=f;return b},b.style=function(a,c,d){arguments.length<3&&(d=null);return b.styleTween(a,ba(c),d)},b.text=function(a){d.text=function(b,c){this.textContent=typeof a=="function"?a.call(this,b,c):a};return b},b.select=function(b){var c,d=_(a.select(b)).ease(m);c=-1,d.delay(function(a,b){return j[++c]}),c=-1,d.duration(function(a,b){return k[++c]});return d},b.selectAll=function(b){var c,d=_(a.selectAll(b)).ease(m);c=-1,d.delay(function(a,b){return j[b?c:++c]}),c=-1,d.duration(function(a,b){return k[b?c:++c]});return d},b.remove=function(){f=!0;return b},b.each=function(a,c){g[a].add(c);return b},b.call=h;return b.delay(0).duration(250)}function Y(a){return{__data__:a}}function X(a){arguments.length||(a=d3.ascending);return function(b,c){return a(b&&b.__data__,c&&c.__data__)}}function W(a){function b(b){var c=[],d,e,f,g;for(var h=0,i=a.length;h360?a-=360:a<0&&(a+=360);if(a<60)return d+(e-d)*a/60;if(a<180)return e;if(a<240)return d+(e-d)*(240-a)/60;return d}var d,e;a=a%360,a<0&&(a+=360),b=b<0?0:b>1?1:b,c=c<0?0:c>1?1:c,e=c<=.5?c*(1+b):c+b-c*b,d=2*c-e;return H(g(a+120),g(a),g(a-120))}function Q(a,b,c){this.h=a,this.s=b,this.l=c}function P(a,b,c){return new Q(a,b,c)}function M(a){var b=parseFloat(a);return a.charAt(a.length-1)==="%"?Math.round(b*2.55):b}function L(a,b,c){var d=Math.min(a/=255,b/=255,c/=255),e=Math.max(a,b,c),f=e-d,g,h,i=(e+d)/2;f?(h=i<.5?f/(e+d):f/(2-e-d),a==e?g=(b-c)/f+(b=0?a.substring(b):(b=a.length,""),d=[];while(b>0)d.push(a.substring(b-=3,b+3));return d.reverse().join(",")+c}function m(a){return a+""}function j(a){var b={},c=[];b.add=function(a){for(var d=0;db?1:0},d3.descending=function(a,b){return ba?1:0},d3.min=function(a,b){var c=-1,d=a.length,e,f;if(arguments.length===1){while(++cf&&(e=f)}else{while(++cf&&(e=f)}return e},d3.max=function(a,b){var c=-1,d=a.length,e,f;if(arguments.length===1){while(++ce&&(e=f)}else{while(++ce&&(e=f)}return e},d3.sum=function(a,b){var c=0,d=a.length,e,f=-1;if(arguments.length===1)while(++f>1;a[e]>1;b=b.length)return a;var e=[],f=c[d++],h;for(h in a)e.push({key:h,values:g(a[h],d)});f&&e.sort(function(a,b){return f(a.key,b.key)});return e}function f(c,g){if(g>=b.length)return e?e.call(a,c):d?c.sort(d):c;var h=-1,i=c.length,j=b[g++],k,l,m={};while(++hb)d.push(f);else while((f=a+c*++e)=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/,l={g:function(a,b){return a.toPrecision(b)},e:function(a,b){return a.toExponential(b)},f:function(a,b){return a.toFixed(b)},r:function(a,b){var c=1+Math.floor(1e-15+Math.log(a)/Math.LN10);return d3.round(a,b-c).toFixed(Math.max(0,b-c))}},o=v(2),p=v(3),q={linear:function(){return u},poly:v,quad:function(){return o},cubic:function(){return p},sin:function(){return w},exp:function(){return x},circle:function(){return y},elastic:z,back:A,bounce:function(){return B}},r={"in":function(a){return a},out:s,"in-out":t,"out-in":function(a){return t(s(a))}};d3.ease=function(a){var b=a.indexOf("-"),c=b>=0?a.substring(0,b):a,d=b>=0?a.substring(b+1):"in";return r[d](q[c].apply(null,Array.prototype.slice.call(arguments,1)))},d3.event=null,d3.interpolate=function(a,b){var c=d3.interpolators.length,d;while(--c>=0&&!(d=d3.interpolators[c](a,b)));return d},d3.interpolateNumber=function(a,b){b-=a;return function(c){return a+b*c}},d3.interpolateRound=function(a,b){b-=a;return function(c){return Math.round(a+b*c)}},d3.interpolateString=function(a,b){var c,d,e,f=0,g=0,h=[],i=[],j,k;C.lastIndex=0;for(d=0;c=C.exec(b);++d)c.index&&h.push(b.substring(f,g=c.index)),i.push({i:h.length,x:c[0]}),h.push(null),f=C.lastIndex;f0;j--)e.push(c(f)*j)}else{for(;fi;g--);e=e.slice(f,g)}return e},d.tickFormat=function(){return function(a){return a.toPrecision(1)}};return d},bn.pow=function(a){return Math.pow(10,a)},bo.pow=function(a){return-Math.pow(10,-a)},d3.scale.pow=function(){function f -(b){return a(d(b))}var a=d3.scale.linear(),b=d3.scale.linear(),c=1,d=Number,e=d;f.invert=function(b){return e(a.invert(b))},f.domain=function(g){if(!arguments.length)return a.domain().map(e);var h=(g[0]||g[1])<0?bq:bp;d=h(c),e=h(1/c),a.domain(g.map(d)),b.domain(g);return f},f.range=d3.rebind(f,a.range),f.rangeRound=d3.rebind(f,a.rangeRound),f.interpolate=d3.rebind(f,a.interpolate),f.clamp=d3.rebind(f,a.clamp),f.ticks=b.ticks,f.tickFormat=b.tickFormat,f.nice=function(){return f.domain(bi(f.domain(),bk))},f.exponent=function(a){if(!arguments.length)return c;var b=f.domain();c=a;return f.domain(b)};return f},d3.scale.sqrt=function(){return d3.scale.pow().exponent(.5)},d3.scale.ordinal=function(){function e(d){var e=d in b?b[d]:b[d]=a.push(d)-1;return c[e%c.length]}var a=[],b={},c=[],d=0;e.domain=function(c){if(!arguments.length)return a;a=c,b={};var d=-1,f=-1,g=a.length;while(++d=bw?e?"M0,"+f+"A"+f+","+f+" 0 1,1 0,"+ -f+"A"+f+","+f+" 0 1,1 0,"+f+"M0,"+e+"A"+e+","+e+" 0 1,1 0,"+ -e+"A"+e+","+e+" 0 1,1 0,"+e+"Z":"M0,"+f+"A"+f+","+f+" 0 1,1 0,"+ -f+"A"+f+","+f+" 0 1,1 0,"+f+"Z":e?"M"+f*k+","+f*l+"A"+f+","+f+" 0 "+j+",1 "+f*m+","+f*n+"L"+e*m+","+e*n+"A"+e+","+e+" 0 "+j+",0 "+e*k+","+e*l+"Z":"M"+f*k+","+f*l+"A"+f+","+f+" 0 "+j+",1 "+f*m+","+f*n+"L0,0"+"Z"}var a=bx,b=by,c=bz,d=bA;e.innerRadius=function(b){if(!arguments.length)return a;a=d3.functor(b);return e},e.outerRadius=function(a){if(!arguments.length)return b;b=d3.functor(a);return e},e.startAngle=function(a){if(!arguments.length)return c;c=d3.functor(a);return e},e.endAngle=function(a){if(!arguments.length)return d;d=d3.functor(a);return e},e.centroid=function(){var e=(a.apply(this,arguments)+b.apply(this,arguments))/2,f=(c.apply(this,arguments)+d.apply(this,arguments))/2+bv;return[Math.cos(f)*e,Math.sin(f)*e]};return e};var bv=-Math.PI/2,bw=2*Math.PI-1e-6;d3.svg.line=function(){function f(c){return c.length<1?null:"M"+d(bB(this,c,a,b),e)}var a=bC,b=bD,c="linear",d=bE[c],e=.7;f.x=function(b){if(!arguments.length)return a;a=b;return f},f.y=function(a){if(!arguments.length)return b;b=a;return f},f.interpolate=function(a){if(!arguments.length)return c;d=bE[c=a];return f},f.tension=function(a){if(!arguments.length)return e;e=a;return f};return f};var bE={linear:bF,"step-before":bG,"step-after":bH,basis:bN,"basis-open":bO,"basis-closed":bP,cardinal:bK,"cardinal-open":bI,"cardinal-closed":bJ,monotone:bY},bR=[0,2/3,1/3,0],bS=[0,1/3,2/3,0],bT=[0,1/6,2/3,1/6];d3.svg.area=function(){function g(d){return d.length<1?null:"M"+e(bB(this,d,a,c),f)+"L"+e(bB(this,d,a,b).reverse(),f)+"Z"}var a=bC,b=bZ,c=bD,d="linear",e=bE[d],f=.7;g.x=function(b){if(!arguments.length)return a;a=b;return g},g.y0=function(a){if(!arguments.length)return b;b=a;return g},g.y1=function(a){if(!arguments.length)return c;c=a;return g},g.interpolate=function(a){if(!arguments.length)return d;e=bE[d=a];return g},g.tension=function(a){if(!arguments.length)return f;f=a;return g};return g},d3.svg.chord=function(){function j(a,b,c,d){return"Q 0,0 "+d}function i(a,b){return"A"+a+","+a+" 0 0,1 "+b}function h(a,b){return a.a0==b.a0&&a.a1==b.a1}function g(a,b,f,g){var h=b.call(a,f,g),i=c.call(a,h,g),j=d.call(a,h,g)+bv,k=e.call(a,h,g)+bv;return{r:i,a0:j,a1:k,p0:[i*Math.cos(j),i*Math.sin(j)],p1:[i*Math.cos(k),i*Math.sin(k)]}}function f(c,d){var e=g(this,a,c,d),f=g(this,b,c,d);return"M"+e.p0+i(e.r,e.p1)+(h(e,f)?j(e.r,e.p1,e.r,e.p0):j(e.r,e.p1,f.r,f.p0)+i(f.r,f.p1)+j(f.r,f.p1,e.r,e.p0))+"Z"}var a=b$,b=b_,c=ca,d=bz,e=bA;f.radius=function(a){if(!arguments.length)return c;c=d3.functor(a);return f},f.source=function(b){if(!arguments.length)return a;a=d3.functor(b);return f},f.target=function(a){if(!arguments.length)return b;b=d3.functor(a);return f},f.startAngle=function(a){if(!arguments.length)return d;d=d3.functor(a);return f},f.endAngle=function(a){if(!arguments.length)return e;e=d3.functor(a);return f};return f},d3.svg.diagonal=function(){function d(d,e){var f=a.call(this,d,e),g=b.call(this,d,e),h=(f.y+g.y)/2,i=[f,{x:f.x,y:h},{x:g.x,y:h},g];i=i.map(c);return"M"+i[0]+"C"+i[1]+" "+i[2]+" "+i[3]}var a=b$,b=b_,c=cd;d.source=function(b){if(!arguments.length)return a;a=d3.functor(b);return d},d.target=function(a){if(!arguments.length)return b;b=d3.functor(a);return d},d.projection=function(a){if(!arguments.length)return c;c=a;return d};return d},d3.svg.mouse=function(a){return cf(a,d3.event)};var ce=/WebKit/.test(navigator.userAgent)?-1:0;d3.svg.touches=function(b){var c=d3.event.touches;return c?a(c).map(function(a){var c=cf(b,a);c.identifier=a.identifier;return c}):[]},d3.svg.symbol=function(){function c(c,d){return(ci[a.call(this,c,d)]||ci.circle)(b.call(this,c,d))}var a=ch,b=cg;c.type=function(b){if(!arguments.length)return a;a=d3.functor(b);return c},c.size=function(a){if(!arguments.length)return b;b=d3.functor(a);return c};return c};var ci={circle:function(a){var b=Math.sqrt(a/Math.PI);return"M0,"+b+"A"+b+","+b+" 0 1,1 0,"+ -b+"A"+b+","+b+" 0 1,1 0,"+b+"Z"},cross:function(a){var b=Math.sqrt(a/5)/2;return"M"+ -3*b+","+ -b+"H"+ -b+"V"+ -3*b+"H"+b+"V"+ -b+"H"+3*b+"V"+b+"H"+b+"V"+3*b+"H"+ -b+"V"+b+"H"+ -3*b+"Z"},diamond:function(a){var b=Math.sqrt(a/(2*ck)),c=b*ck;return"M0,"+ -b+"L"+c+",0"+" 0,"+b+" "+ -c+",0"+"Z"},square:function(a){var b=Math.sqrt(a)/2;return"M"+ -b+","+ -b+"L"+b+","+ -b+" "+b+","+b+" "+ -b+","+b+"Z"},"triangle-down":function(a){var b=Math.sqrt(a/cj),c=b*cj/2;return"M0,"+c+"L"+b+","+ -c+" "+ -b+","+ -c+"Z"},"triangle-up":function(a){var b=Math.sqrt(a/cj),c=b*cj/2;return"M0,"+ -c+"L"+b+","+c+" "+ -b+","+c+"Z"}};d3.svg.symbolTypes=d3.keys(ci);var cj=Math.sqrt(3),ck=Math.tan(30*Math.PI/180)})() \ No newline at end of file +(function(){function ci(){return"circle"}function ch(){return 64}function cg(a,b){var c=(a.ownerSVGElement||a).createSVGPoint();if(cf<0&&(window.scrollX||window.scrollY)){var d=d3.select(document.body).append("svg:svg").style("position","absolute").style("top",0).style("left",0),e=d[0][0].getScreenCTM();cf=!e.f&&!e.e,d.remove()}cf?(c.x=b.pageX,c.y=b.pageY):(c.x=b.clientX,c.y=b.clientY),c=c.matrixTransform(a.getScreenCTM().inverse());return[c.x,c.y]}function ce(a){return[a.x,a.y]}function cd(a){return a.endAngle}function cc(a){return a.startAngle}function cb(a){return a.radius}function ca(a){return a.target}function b_(a){return a.source}function b$(){return 0}function bZ(a){return a.length<3?bG(a):a[0]+bM(a,bY(a))}function bY(a){var b=[],c,d,e,f,g=bX(a),h=-1,i=a.length-1;while(++h9&&(f=c*3/Math.sqrt(f),g[h]=f*d,g[h+1]=f*e));h=-1;while(++h<=i)f=(a[Math.min(i,h+1)][0]-a[Math.max(0,h-1)][0])/(6*(1+g[h]*g[h])),b.push([f||0,g[h]*f||0]);return b}function bX(a){var b=0,c=a.length-1,d=[],e=a[0],f=a[1],g=d[0]=bW(e,f);while(++b1){h=b[1],f=a[i],i++,d+="C"+(e[0]+g[0])+","+(e[1]+g[1])+","+(f[0]-h[0])+","+(f[1]-h[1])+","+f[0]+","+f[1];for(var j=2;jc.delay&&(c.flush=c.callback(a)),c=c.next;var d=bg()-b;d>24?(isFinite(d)&&(clearTimeout(bd),bd=setTimeout(bf,d)),bc=0):(bc=1,bh(bf))}function be(a,b){var c=Date.now(),d=!1,e,f=bb;if(!!isFinite(b)){while(f){if(f.callback===a){f.then=c,f.delay=b,d=!0;break}e=f,f=f.next}d||(bb={callback:a,then:c,delay:b,next:bb}),bc||(bd=clearTimeout(bd),bc=1,bh(bf))}}function ba(a){return typeof a=="function"?function(b,c,d){var e=a.call(this,b,c)+"";return d!=e&&d3.interpolate(d,e)}:(a=a+"",function(b,c,d){return d!=a&&d3.interpolate(d,a)})}function _(a){function n(b){var h=!0,l=-1;a.each(function(){if(i[++l]!==2){var a=(b-j[l])/k[l],n=this.__transition__,o,p,q=e[l];if(a<1){h=!1;if(a<0)return}else a=1;if(i[l]){if(!n||n.active!==c){i[l]=2;return}}else{if(!n||n.active>c){i[l]=2;return}i[l]=1,g.start.dispatch.apply(this,arguments),q=e[l]={},n.active=c;for(p in d)if(o=d[p].apply(this,arguments))q[p]=o}o=m(a);for(p in q)q[p].call(this,o);if(a===1){i[l]=2;if(n.active===c){var r=n.owner;r===c&&(delete this.__transition__,f&&this.parentNode.removeChild(this)),$=c,g.end.dispatch.apply(this,arguments),$=0,n.owner=r}}}});return h}var b={},c=$||++Z,d={},e=[],f=!1,g=d3.dispatch("start","end"),i=[],j=[],k=[],l,m=d3.ease("cubic-in-out");a.each(function(){(this.__transition__||(this.__transition__={})).owner=c}),b.delay=function(c){var d=Infinity,e=-1;typeof c=="function"?a.each(function(a,b){var f=j[++e]=+c.apply(this,arguments);fl&&(l=e)})):(l=+c,a.each(function(a,b){k[++d]=l}));return b},b.ease=function(a){m=typeof a=="function"?a:d3.ease.apply(d3,arguments);return b},b.attrTween=function(a,c){function f(b,d){var e=c.call(this,b,d,this.getAttributeNS(a.space,a.local));return e&&function(b){this.setAttributeNS(a.space,a.local,e(b))}}function e(b,d){var e=c.call(this,b,d,this.getAttribute(a));return e&&function(b){this.setAttribute(a,e(b))}}d["attr."+a]=a.local?f:e;return b},b.attr=function(a,c){return b.attrTween(a,ba(c))},b.styleTween=function(a,c,e){function f(b,d){var f=c.call(this,b,d,window.getComputedStyle(this,null).getPropertyValue(a));return f&&function(b){this.style.setProperty(a,f(b),e)}}arguments.length<3&&(e=null),d["style."+a]=f;return b},b.style=function(a,c,d){arguments.length<3&&(d=null);return b.styleTween(a,ba(c),d)},b.text=function(a){d.text=function(b,c){this.textContent=typeof a=="function"?a.call(this,b,c):a};return b},b.select=function(b){var c,d=_(a.select(b)).ease(m);c=-1,d.delay(function(a,b){return j[++c]}),c=-1,d.duration(function(a,b){return k[++c]});return d},b.selectAll=function(b){var c,d=_(a.selectAll(b)).ease(m);c=-1,d.delay(function(a,b){return j[b?c:++c]}),c=-1,d.duration(function(a,b){return k[b?c:++c]});return d},b.remove=function(){f=!0;return b},b.each=function(a,c){g[a].add(c);return b},b.call=h;return b.delay(0).duration(250)}function Y(a){return{__data__:a}}function X(a){arguments.length||(a=d3.ascending);return function(b,c){return a(b&&b.__data__,c&&c.__data__)}}function W(a){function b(b){var c=[],d,e,f,g;for(var h=0,i=a.length;h360?a-=360:a<0&&(a+=360);if(a<60)return d+(e-d)*a/60;if(a<180)return e;if(a<240)return d+(e-d)*(240-a)/60;return d}var d,e;a=a%360,a<0&&(a+=360),b=b<0?0:b>1?1:b,c=c<0?0:c>1?1:c,e=c<=.5?c*(1+b):c+b-c*b,d=2*c-e;return H(g(a+120),g(a),g(a-120))}function Q(a,b,c){this.h=a,this.s=b,this.l=c}function P(a,b,c){return new Q(a,b,c)}function M(a){var b=parseFloat(a);return a.charAt(a.length-1)==="%"?Math.round(b*2.55):b}function L(a,b,c){var d=Math.min(a/=255,b/=255,c/=255),e=Math.max(a,b,c),f=e-d,g,h,i=(e+d)/2;f?(h=i<.5?f/(e+d):f/(2-e-d),a==e?g=(b-c)/f+(b=0?a.substring(b):(b=a.length,""),d=[];while(b>0)d.push(a.substring(b-=3,b+3));return d.reverse().join(",")+c}function m(a){return a+""}function j(a){var b={},c=[];b.add=function(a){for(var d=0;db?1:0},d3.descending=function(a,b){return ba?1:0},d3.min=function(a,b){var c=-1,d=a.length,e,f;if(arguments.length===1){while(++cf&&(e=f)}else{while(++cf&&(e=f)}return e},d3.max=function(a,b){var c=-1,d=a.length,e,f;if(arguments.length===1){while(++ce&&(e=f)}else{while(++ce&&(e=f)}return e},d3.sum=function(a,b){var c=0,d=a.length,e,f=-1;if(arguments.length===1)while(++f>1;a[e]>1;b=b.length)return a;var e=[],f=c[d++],h;for(h in a)e.push({key:h,values:g(a[h],d)});f&&e.sort(function(a,b){return f(a.key,b.key)});return e}function f(c,g){if(g>=b.length)return e?e.call(a,c):d?c.sort(d):c;var h=-1,i=c.length,j=b[g++],k,l,m={};while(++hb)d.push(f);else while((f=a+c*++e)=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/,l={g:function(a,b){return a.toPrecision(b)},e:function(a,b){return a.toExponential(b)},f:function(a,b){return a.toFixed(b)},r:function(a,b){var c=1+Math.floor(1e-15+Math.log(a)/Math.LN10);return d3.round(a,b-c).toFixed(Math.max(0,b-c))}},o=v(2),p=v(3),q={linear:function(){return u},poly:v,quad:function(){return o},cubic:function(){return p},sin:function(){return w},exp:function(){return x},circle:function(){return y},elastic:z,back:A,bounce:function(){return B}},r={"in":function(a){return a},out:s,"in-out":t,"out-in":function(a){return t(s(a))}};d3.ease=function(a){var b=a.indexOf("-"),c=b>=0?a.substring(0,b):a,d=b>=0?a.substring(b+1):"in";return r[d](q[c].apply(null,Array.prototype.slice.call(arguments,1)))},d3.event=null,d3.interpolate=function(a,b){var c=d3.interpolators.length,d;while(--c>=0&&!(d=d3.interpolators[c](a,b)));return d},d3.interpolateNumber=function(a,b){b-=a;return function(c){return a+b*c}},d3.interpolateRound=function(a,b){b-=a;return function(c){return Math.round(a+b*c)}},d3.interpolateString=function(a,b){var c,d,e,f=0,g=0,h=[],i=[],j,k;C.lastIndex=0;for(d=0;c=C.exec(b);++d)c.index&&h.push(b.substring(f,g=c.index)),i.push({i:h.length,x:c[0]}),h.push(null),f=C.lastIndex;f0;j--)e.push(c(f)*j)}else{for(;fi;g--);e=e.slice(f,g)}return e},d.tickFormat=function( +){return function(a){return a.toPrecision(1)}};return d},bn.pow=function(a){return Math.pow(10,a)},bo.pow=function(a){return-Math.pow(10,-a)},d3.scale.pow=function(){function f(b){return a(d(b))}var a=d3.scale.linear(),b=d3.scale.linear(),c=1,d=Number,e=d;f.invert=function(b){return e(a.invert(b))},f.domain=function(g){if(!arguments.length)return a.domain().map(e);var h=(g[0]||g[1])<0?bq:bp;d=h(c),e=h(1/c),a.domain(g.map(d)),b.domain(g);return f},f.range=d3.rebind(f,a.range),f.rangeRound=d3.rebind(f,a.rangeRound),f.interpolate=d3.rebind(f,a.interpolate),f.clamp=d3.rebind(f,a.clamp),f.ticks=b.ticks,f.tickFormat=b.tickFormat,f.nice=function(){return f.domain(bi(f.domain(),bk))},f.exponent=function(a){if(!arguments.length)return c;var b=f.domain();c=a;return f.domain(b)};return f},d3.scale.sqrt=function(){return d3.scale.pow().exponent(.5)},d3.scale.ordinal=function(){function e(d){var e=d in b?b[d]:b[d]=a.push(d)-1;return c[e%c.length]}var a=[],b={},c=[],d=0;e.domain=function(c){if(!arguments.length)return a;a=c,b={};var d=-1,f=-1,g=a.length;while(++d=bw?e?"M0,"+f+"A"+f+","+f+" 0 1,1 0,"+ -f+"A"+f+","+f+" 0 1,1 0,"+f+"M0,"+e+"A"+e+","+e+" 0 1,1 0,"+ -e+"A"+e+","+e+" 0 1,1 0,"+e+"Z":"M0,"+f+"A"+f+","+f+" 0 1,1 0,"+ -f+"A"+f+","+f+" 0 1,1 0,"+f+"Z":e?"M"+f*k+","+f*l+"A"+f+","+f+" 0 "+j+",1 "+f*m+","+f*n+"L"+e*m+","+e*n+"A"+e+","+e+" 0 "+j+",0 "+e*k+","+e*l+"Z":"M"+f*k+","+f*l+"A"+f+","+f+" 0 "+j+",1 "+f*m+","+f*n+"L0,0"+"Z"}var a=bx,b=by,c=bz,d=bA;e.innerRadius=function(b){if(!arguments.length)return a;a=d3.functor(b);return e},e.outerRadius=function(a){if(!arguments.length)return b;b=d3.functor(a);return e},e.startAngle=function(a){if(!arguments.length)return c;c=d3.functor(a);return e},e.endAngle=function(a){if(!arguments.length)return d;d=d3.functor(a);return e},e.centroid=function(){var e=(a.apply(this,arguments)+b.apply(this,arguments))/2,f=(c.apply(this,arguments)+d.apply(this,arguments))/2+bv;return[Math.cos(f)*e,Math.sin(f)*e]};return e};var bv=-Math.PI/2,bw=2*Math.PI-1e-6;d3.svg.line=function(){function g(c){return c.length<1?null:"M"+d(bB(bC(this,c,a,b),f),e)}var a=bD,b=bE,c="linear",d=bF[c],e=.7,f=1;g.x=function(b){if(!arguments.length)return a;a=b;return g},g.y=function(a){if(!arguments.length)return b;b=a;return g},g.interpolate=function(a){if(!arguments.length)return c;d=bF[c=a];return g},g.tension=function(a){if(!arguments.length)return e;e=a;return g},g.beta=function(a){if(!arguments.length)return f;f=a;return g};return g};var bF={linear:bG,"step-before":bH,"step-after":bI,basis:bO,"basis-open":bP,"basis-closed":bQ,cardinal:bL,"cardinal-open":bJ,"cardinal-closed":bK,monotone:bZ},bS=[0,2/3,1/3,0],bT=[0,1/3,2/3,0],bU=[0,1/6,2/3,1/6];d3.svg.area=function(){function g(d){return d.length<1?null:"M"+e(bC(this,d,a,c),f)+"L"+e(bC(this,d,a,b).reverse(),f)+"Z"}var a=bD,b=b$,c=bE,d="linear",e=bF[d],f=.7;g.x=function(b){if(!arguments.length)return a;a=b;return g},g.y0=function(a){if(!arguments.length)return b;b=a;return g},g.y1=function(a){if(!arguments.length)return c;c=a;return g},g.interpolate=function(a){if(!arguments.length)return d;e=bF[d=a];return g},g.tension=function(a){if(!arguments.length)return f;f=a;return g};return g},d3.svg.chord=function(){function j(a,b,c,d){return"Q 0,0 "+d}function i(a,b){return"A"+a+","+a+" 0 0,1 "+b}function h(a,b){return a.a0==b.a0&&a.a1==b.a1}function g(a,b,f,g){var h=b.call(a,f,g),i=c.call(a,h,g),j=d.call(a,h,g)+bv,k=e.call(a,h,g)+bv;return{r:i,a0:j,a1:k,p0:[i*Math.cos(j),i*Math.sin(j)],p1:[i*Math.cos(k),i*Math.sin(k)]}}function f(c,d){var e=g(this,a,c,d),f=g(this,b,c,d);return"M"+e.p0+i(e.r,e.p1)+(h(e,f)?j(e.r,e.p1,e.r,e.p0):j(e.r,e.p1,f.r,f.p0)+i(f.r,f.p1)+j(f.r,f.p1,e.r,e.p0))+"Z"}var a=b_,b=ca,c=cb,d=bz,e=bA;f.radius=function(a){if(!arguments.length)return c;c=d3.functor(a);return f},f.source=function(b){if(!arguments.length)return a;a=d3.functor(b);return f},f.target=function(a){if(!arguments.length)return b;b=d3.functor(a);return f},f.startAngle=function(a){if(!arguments.length)return d;d=d3.functor(a);return f},f.endAngle=function(a){if(!arguments.length)return e;e=d3.functor(a);return f};return f},d3.svg.diagonal=function(){function d(d,e){var f=a.call(this,d,e),g=b.call(this,d,e),h=(f.y+g.y)/2,i=[f,{x:f.x,y:h},{x:g.x,y:h},g];i=i.map(c);return"M"+i[0]+"C"+i[1]+" "+i[2]+" "+i[3]}var a=b_,b=ca,c=ce;d.source=function(b){if(!arguments.length)return a;a=d3.functor(b);return d},d.target=function(a){if(!arguments.length)return b;b=d3.functor(a);return d},d.projection=function(a){if(!arguments.length)return c;c=a;return d};return d},d3.svg.mouse=function(a){return cg(a,d3.event)};var cf=/WebKit/.test(navigator.userAgent)?-1:0;d3.svg.touches=function(b){var c=d3.event.touches;return c?a(c).map(function(a){var c=cg(b,a);c.identifier=a.identifier;return c}):[]},d3.svg.symbol=function(){function c(c,d){return(cj[a.call(this,c,d)]||cj.circle)(b.call(this,c,d))}var a=ci,b=ch;c.type=function(b){if(!arguments.length)return a;a=d3.functor(b);return c},c.size=function(a){if(!arguments.length)return b;b=d3.functor(a);return c};return c};var cj={circle:function(a){var b=Math.sqrt(a/Math.PI);return"M0,"+b+"A"+b+","+b+" 0 1,1 0,"+ -b+"A"+b+","+b+" 0 1,1 0,"+b+"Z"},cross:function(a){var b=Math.sqrt(a/5)/2;return"M"+ -3*b+","+ -b+"H"+ -b+"V"+ -3*b+"H"+b+"V"+ -b+"H"+3*b+"V"+b+"H"+b+"V"+3*b+"H"+ -b+"V"+b+"H"+ -3*b+"Z"},diamond:function(a){var b=Math.sqrt(a/(2*cl)),c=b*cl;return"M0,"+ -b+"L"+c+",0"+" 0,"+b+" "+ -c+",0"+"Z"},square:function(a){var b=Math.sqrt(a)/2;return"M"+ -b+","+ -b+"L"+b+","+ -b+" "+b+","+b+" "+ -b+","+b+"Z"},"triangle-down":function(a){var b=Math.sqrt(a/ck),c=b*ck/2;return"M0,"+c+"L"+b+","+ -c+" "+ -b+","+ -c+"Z"},"triangle-up":function(a){var b=Math.sqrt(a/ck),c=b*ck/2;return"M0,"+ -c+"L"+b+","+c+" "+ -b+","+c+"Z"}};d3.svg.symbolTypes=d3.keys(cj);var ck=Math.sqrt(3),cl=Math.tan(30*Math.PI/180)})() \ No newline at end of file diff --git a/examples/bundle/bundle-radial.html b/examples/bundle/bundle-radial.html index e93bd491b0432..dd8218506fee9 100644 --- a/examples/bundle/bundle-radial.html +++ b/examples/bundle/bundle-radial.html @@ -2,7 +2,7 @@ - Hierarchical Edge Bundling + Hierarchical Edge Bundling (Radial Tree) diff --git a/examples/bundle/bundle-radial.js b/examples/bundle/bundle-radial.js index 88805341bcdab..2429b3832cb0e 100644 --- a/examples/bundle/bundle-radial.js +++ b/examples/bundle/bundle-radial.js @@ -1,23 +1,16 @@ var r = 960 / 2, - stroke = d3.scale.linear().domain([0, 1e4]).range(["black", "steelblue"]), - nodeMap = {}; + stroke = d3.scale.linear().domain([0, 1e4]).range(["brown", "steelblue"]); var cluster = d3.layout.cluster() .size([360, r - 120]) .sort(null) - .value(function(d) { return d.value.size; }) - .children(function(d) { return isNaN(d.value.size) ? d3.entries(d.value) : null; }); + .value(function(d) { return d.size; }); -var bundle = d3.layout.bundle() - .outgoing(function(d) { - if (!d.data.value.imports) return []; - return d.data.value.imports.map(function(d) { - return nodeMap[d]; - }); - }); +var bundle = d3.layout.bundle(); var line = d3.svg.line() .interpolate("basis") + .beta(.85) .x(function(d) { var r = d.y, a = (d.x - 90) / 180 * Math.PI; return r * Math.cos(a); @@ -33,43 +26,56 @@ var vis = d3.select("#chart").append("svg:svg") .append("svg:g") .attr("transform", "translate(" + r + "," + r + ")"); -d3.json("dependency-data.json", function(json) { - var tree = {}; - json.forEach(function(d) { - var path = d.name.split("."), - last = path.length - 1, - node = tree; - path.forEach(function(part, i) { - if (i === last) { - node[part] = d; - return; - } - if (!(part in node)) { - node[part] = {}; +d3.json("dependency-data.json", function(classes) { + var map = {}, + links = []; + + function find(name, data) { + var node = map[name], i; + if (!node) { + node = map[name] = data || {name: name, children: []}; + if (name.length) { + node.parent = find(name.substring(0, i = name.lastIndexOf("."))); + node.parent.children.push(node); + node.key = name.substring(i + 1); } - node = node[part]; - }); + } + return node; + } + + // Lazily construct the package hierarchy from class names. + classes.forEach(function(d) { + find(d.name, d); }); - var nodes = cluster(d3.entries(tree)[0]); - nodes.forEach(function(node) { - if (node.data.value.name) nodeMap[node.data.value.name] = node; + // Compute the cluster layout, starting at the root node! + var nodes = cluster(map[""]); + + // Store a reference from class data object to the layout node. + nodes.forEach(function(d) { + d.data.node = d; }); - var link = vis.selectAll("path.link") - .data(bundle(nodes)) + // For each import, construct a link from the source to target node. + classes.forEach(function(d) { + d.imports.forEach(function(i) { + links.push({source: map[d.name].node, target: map[i].node}); + }); + }); + + vis.selectAll("path.link") + .data(bundle(links)) .enter().append("svg:path") .style("stroke", function(d) { return stroke(d[0].value); }) .attr("class", "link") .attr("d", line); - var node = vis.selectAll("g.node") + vis.selectAll("g.node") .data(nodes.filter(function(n) { return !n.children; })) .enter().append("svg:g") .attr("class", "node") .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; }) - - node.append("svg:text") + .append("svg:text") .attr("dx", function(d) { return d.x < 180 ? 8 : -8; }) .attr("dy", ".31em") .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; }) diff --git a/examples/bundle/bundle-treemap.css b/examples/bundle/bundle-treemap.css index 3533d7f76cb75..62d9b41859389 100644 --- a/examples/bundle/bundle-treemap.css +++ b/examples/bundle/bundle-treemap.css @@ -9,6 +9,6 @@ .link { stroke: #000; - stroke-width: .5px; + stroke-opacity: .5; fill: none; } diff --git a/examples/bundle/bundle-treemap.html b/examples/bundle/bundle-treemap.html index 3584308f5e9cd..ceb1723cc1c27 100644 --- a/examples/bundle/bundle-treemap.html +++ b/examples/bundle/bundle-treemap.html @@ -2,21 +2,14 @@ - Treemap + Hierarchical Edge Bundling (Treemap) + - -
-

-

+
diff --git a/examples/bundle/bundle-treemap.js b/examples/bundle/bundle-treemap.js index e98a2da42149f..29cc1edc3e192 100644 --- a/examples/bundle/bundle-treemap.js +++ b/examples/bundle/bundle-treemap.js @@ -1,23 +1,13 @@ var w = 960, h = 500, - color = d3.scale.category20c(), - stroke = d3.scale.linear().domain([0, 1e4]).range(["black", "steelblue"]), - nodeMap = {}, - root = null; + fill = d3.scale.ordinal().range(colorbrewer.Greys[9].slice(1, 4)), + stroke = d3.scale.linear().domain([0, 1e4]).range(["brown", "steelblue"]); var treemap = d3.layout.treemap() .size([w, h]) - .children(function(d) { return isNaN(d.value.size) ? d3.entries(d.value) : null; }) - .value(function(d) { return d.value.size; }) - .sticky(true); + .value(function(d) { return d.size; }); -var bundle = d3.layout.bundle() - .outgoing(function(d) { - if (!d.data.value.imports) return []; - return d.data.value.imports.map(function(d) { - return nodeMap[d]; - }); - }); +var bundle = d3.layout.bundle(); var div = d3.select("#chart").append("div") .style("position", "relative") @@ -26,39 +16,52 @@ var div = d3.select("#chart").append("div") var line = d3.svg.line() .interpolate("basis") - .x(function(d) { return d.x; }) - .y(function(d) { return d.y; }); + .beta(.85) + .x(function(d) { return d.x + d.dx / 2; }) + .y(function(d) { return d.y + d.dy / 2; }); -d3.json("dependency-data.json", function(json) { - var tree = {}; - json.forEach(function(d) { - var path = d.name.split("."), - last = path.length - 1, - node = tree; - path.forEach(function(part, i) { - if (i === last) { - node[part] = d; - return; - } - if (!(part in node)) { - node[part] = {}; +d3.json("dependency-data.json", function(classes) { + var map = {}, + links = []; + + function find(name, data) { + var node = map[name], i; + if (!node) { + node = map[name] = data || {name: name, children: []}; + if (name.length) { + node.parent = find(name.substring(0, i = name.lastIndexOf("."))); + node.parent.children.push(node); + node.key = name.substring(i + 1); } - node = node[part]; - }); + } + return node; + } + + // Lazily construct the package hierarchy from class names. + classes.forEach(function(d) { + find(d.name, d); }); - root = d3.entries(tree)[0]; - var nodes = treemap(root); + // Compute the treemap layout, starting at the root node! + var nodes = treemap(map[""]); - nodes.forEach(function(node) { - if (node.data.value.name) nodeMap[node.data.value.name] = node; + // Store a reference from class data object to the layout node. + nodes.forEach(function(d) { + d.data.node = d; + }); + + // For each import, construct a link from the source to target node. + classes.forEach(function(d) { + d.imports.forEach(function(i) { + links.push({source: map[d.name].node, target: map[i].node}); + }); }); div.selectAll("div") .data(nodes) .enter().append("div") .attr("class", "cell") - .style("background", function(d) { return d.children ? color(d.data.key) : null; }) + .style("background", function(d) { return d.children ? fill(d.data.key) : null; }) .call(cell) .text(function(d) { return d.children ? null : d.data.key; }); @@ -67,44 +70,13 @@ d3.json("dependency-data.json", function(json) { .attr("height", h) .style("position", "absolute") .selectAll("path.link") - .data(bundle(nodes)) + .data(bundle(links)) .enter().append("svg:path") .style("stroke", function(d) { return stroke(d[0].value); }) .attr("class", "link") .attr("d", line); - - d3.select("#size").on("click", function() { - treemap.value(function(d) { return d.value.size; }); - transition(); - d3.select("#size").classed("active", true); - d3.select("#count").classed("active", false); - }); - - d3.select("#count").on("click", function() { - treemap.value(function(d) { return 1; }); - transition(); - d3.select("#size").classed("active", false); - d3.select("#count").classed("active", true); - }); }); -function transition() { - nodes = treemap(root); - - div.selectAll("div") - .data(nodes) - .transition() - .duration(1500) - .call(cell); - - div.selectAll("path.link") - .data(bundle(nodes)) - .transition() - .duration(1500) - .style("stroke", function(d) { return stroke(d[0].value); }) - .attr("d", line); -} - function cell() { this .style("left", function(d) { return d.x + "px"; }) diff --git a/src/layout/bundle.js b/src/layout/bundle.js index 9ffa90cc078ce..8d4e92226065e 100644 --- a/src/layout/bundle.js +++ b/src/layout/bundle.js @@ -3,42 +3,26 @@ // the parent hierarchy to the least common ancestor, and then back down to the // destination node. d3.layout.bundle = function() { - var beta = .85, - outgoing = d3_layout_bundleOutgoing; - - function bundle(nodes) { + return function(links) { var splines = [], i = -1, - n = nodes.length; - while (++i < n) { - var node = nodes[i], - // TODO cache outgoing? - targets = outgoing.call(this, node, i); - for (var j = 0; j < targets.length; j++) { - splines.push(d3_layout_bundleSpline(node, targets[j])); - } - } + n = links.length; + while (++i < n) splines.push(d3_layout_bundleSpline(links[i])); return splines; }; - - bundle.outgoing = function(x) { - if (!arguments.length) return outgoing; - outgoing = x; - return bundle; - }; - - return bundle; }; -function d3_layout_bundleSpline(start, end) { - var lca = d3_layout_bundleLeastCommonAncestor(start, end), +function d3_layout_bundleSpline(link) { + var start = link.source, + end = link.target, + lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [start]; - while (start != lca) { + while (start !== lca) { start = start.parent; points.push(start); } var k = points.length; - while (end != lca) { + while (end !== lca) { points.splice(k, 0, end); end = end.parent; } @@ -58,23 +42,16 @@ function d3_layout_bundleAncestors(node) { } function d3_layout_bundleLeastCommonAncestor(a, b) { - if (a == b) { - return a; - } + if (a === b) return a; var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; - - while (aNode == bNode) { + while (aNode === bNode) { sharedNode = aNode; aNode = aNodes.pop(); bNode = bNodes.pop(); } return sharedNode; } - -function d3_layout_bundleOutgoing(d, i) { - return d.outgoing; -} diff --git a/src/svg/line.js b/src/svg/line.js index 5c1ff06a78caa..36be1d11eb263 100644 --- a/src/svg/line.js +++ b/src/svg/line.js @@ -3,11 +3,12 @@ d3.svg.line = function() { y = d3_svg_lineY, interpolate = "linear", interpolator = d3_svg_lineInterpolators[interpolate], - tension = .7; + tension = .7, + beta = 1; function line(d) { return d.length < 1 ? null - : "M" + interpolator(d3_svg_linePoints(this, d, x, y), tension); + : "M" + interpolator(d3_svg_lineStraighten(d3_svg_linePoints(this, d, x, y), beta), tension); } line.x = function(v) { @@ -34,9 +35,33 @@ d3.svg.line = function() { return line; }; + line.beta = function(v) { + if (!arguments.length) return beta; + beta = v; + return line; + }; + return line; }; +function d3_svg_lineStraighten(points, beta) { + var n = points.length - 1, + x0 = points[0][0], + y0 = points[0][1], + dx = points[n][0] - x0, + dy = points[n][1] - y0, + i = -1, + p, + t; + while (++i <= n) { + p = points[i]; + t = i / n; + p[0] = beta * p[0] + (1 - beta) * (x0 + t * dx); + p[1] = beta * p[1] + (1 - beta) * (y0 + t * dy); + } + return points; +} + // Converts the specified array of data into an array of points // (x-y tuples), by evaluating the specified `x` and `y` functions on each // data point. The `this` context of the evaluated functions is the specified