Comparing version 0.5.0 to 0.6.0
@@ -7,3 +7,3 @@ (function (global, factory) { | ||
var version = "0.5.0"; | ||
var version = "0.6.0"; | ||
@@ -68,4 +68,3 @@ function center(x, y) { | ||
radii, | ||
radiusMax, | ||
strength = 0.7, | ||
strength = 1, | ||
iterations = 1; | ||
@@ -77,22 +76,17 @@ | ||
var i, n = nodes.length, | ||
tree = d3Quadtree.quadtree(nodes, x, y), | ||
tree, | ||
node, | ||
nx, | ||
ny, | ||
nr, | ||
vx, | ||
vy, | ||
nx0, | ||
ny0, | ||
nx1, | ||
ny1; | ||
xi, | ||
yi, | ||
ri, | ||
ri2; | ||
for (var k = 0; k < iterations; ++k) { | ||
tree = d3Quadtree.quadtree(nodes, x, y).visitAfter(prepare); | ||
for (i = 0; i < n; ++i) { | ||
node = nodes[i], nr = radii[i] + radiusMax, vx = vy = 0; | ||
nx = node.x + node.vx, nx0 = nx - nr, nx1 = nx + nr; | ||
ny = node.y + node.vy, ny0 = ny - nr, ny1 = ny + nr; | ||
tree.remove(node).visit(apply); | ||
node.vx += vx * strength, node.vy += vy * strength; | ||
tree.add(node); | ||
node = nodes[i]; | ||
ri = radii[i], ri2 = ri * ri; | ||
xi = node.x + node.vx; | ||
yi = node.y + node.vy; | ||
tree.visit(apply); | ||
} | ||
@@ -102,25 +96,36 @@ } | ||
function apply(quad, x0, y0, x1, y1) { | ||
if (x0 > nx1 || x1 < nx0 || y0 > ny1 || y1 < ny0) return true; | ||
if (quad.length) return; | ||
var x = nx - quad.data.x - quad.data.vx || jiggle(), | ||
y = ny - quad.data.y - quad.data.vy || jiggle(), | ||
l = x * x + y * y, | ||
r = radii[i] + radii[quad.data.index]; | ||
if (l < r * r) { | ||
l = Math.sqrt(l); | ||
l = (r - l) / l; | ||
vx += x * l, vy += y * l; | ||
var data = quad.data, rj = quad.r, r = ri + rj; | ||
if (data) { | ||
if (data.index > i) { | ||
var x = xi - data.x - data.vx, | ||
y = yi - data.y - data.vy, | ||
l = x * x + y * y; | ||
if (l < r * r) { | ||
if (x === 0) x = jiggle(), l += x * x; | ||
if (y === 0) y = jiggle(), l += y * y; | ||
l = (r - (l = Math.sqrt(l))) / l * strength; | ||
node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj)); | ||
node.vy += (y *= l) * r; | ||
data.vx -= x * (r = 1 - r); | ||
data.vy -= y * r; | ||
} | ||
} | ||
return; | ||
} | ||
return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r; | ||
} | ||
} | ||
force.initialize = function(_) { | ||
var i, n = (nodes = _).length, r; | ||
radii = new Array(n); | ||
radiusMax = 0; | ||
for (i = 0; i < n; ++i) { | ||
if ((radii[i] = r = +radius(nodes[i], i, nodes)) > radiusMax) { | ||
radiusMax = r; | ||
function prepare(quad) { | ||
if (quad.data) return quad.r = radii[quad.data.index]; | ||
for (var i = quad.r = 0; i < 4; ++i) { | ||
if (quad[i] && quad[i].r > quad.r) { | ||
quad.r = quad[i].r; | ||
} | ||
} | ||
} | ||
force.initialize = function(_) { | ||
var i, n = (nodes = _).length; radii = new Array(n); | ||
for (i = 0; i < n; ++i) radii[i] = +radius(nodes[i], i, nodes); | ||
}; | ||
@@ -261,7 +266,9 @@ | ||
var simulation, | ||
iteration = 0, | ||
alpha = 1, | ||
alphaMin = 0.001, | ||
alphaDecay = -0.02, | ||
alphaDecay = 1 - Math.pow(alphaMin, 1 / 300), | ||
alphaTarget = 0, | ||
drag = 0.6, | ||
forces = d3Collection.map(), | ||
fixes = {}, | ||
stepper = d3Timer.timer(step), | ||
@@ -272,17 +279,6 @@ event = d3Dispatch.dispatch("tick", "end"); | ||
function restart() { | ||
iteration = 0; | ||
stepper.restart(step); | ||
return simulation; | ||
} | ||
function stop() { | ||
stepper.stop(); | ||
return simulation; | ||
} | ||
function step() { | ||
var stop = tick(); | ||
tick(); | ||
event.call("tick", simulation); | ||
if (stop) { | ||
if (alpha < alphaMin) { | ||
stepper.stop(); | ||
@@ -294,4 +290,6 @@ event.call("end", simulation); | ||
function tick() { | ||
var alpha = Math.exp(++iteration * alphaDecay); | ||
var i, n = nodes.length, node, fix; | ||
alpha += (alphaTarget - alpha) * alphaDecay; | ||
forces.each(function(force) { | ||
@@ -301,3 +299,3 @@ force(alpha); | ||
for (var i = 0, n = nodes.length, node; i < n; ++i) { | ||
for (i = 0; i < n; ++i) { | ||
node = nodes[i]; | ||
@@ -308,3 +306,9 @@ node.x += node.vx *= drag; | ||
return alpha < alphaMin; | ||
for (i in fixes) { | ||
fix = fixes[i], node = nodes[i]; | ||
node.x = fix.x; | ||
node.y = fix.y; | ||
node.vx = | ||
node.vy = 0; | ||
} | ||
} | ||
@@ -334,6 +338,12 @@ | ||
return simulation = { | ||
restart: restart, | ||
stop: stop, | ||
tick: tick, | ||
restart: function() { | ||
return stepper.restart(step), simulation; | ||
}, | ||
stop: function() { | ||
return stepper.stop(), simulation; | ||
}, | ||
nodes: function(_) { | ||
@@ -343,10 +353,18 @@ return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes; | ||
alpha: function(_) { | ||
return arguments.length ? (alpha = +_, simulation) : alpha; | ||
}, | ||
alphaMin: function(_) { | ||
return arguments.length ? (alphaMin = _, simulation) : alphaMin; | ||
return arguments.length ? (alphaMin = +_, simulation) : alphaMin; | ||
}, | ||
alphaDecay: function(_) { | ||
return arguments.length ? (iteration = +_ ? Math.round(iteration * alphaDecay / -_) : 0, alphaDecay = -_, simulation) : -alphaDecay; | ||
return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay; | ||
}, | ||
alphaTarget: function(_) { | ||
return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget; | ||
}, | ||
drag: function(_) { | ||
@@ -360,2 +378,10 @@ return arguments.length ? (drag = 1 - _, simulation) : 1 - drag; | ||
fix: function(node, x, y) { | ||
return fixes[node.index] = {x: x == null ? node.x : +x, y: y == null ? node.y : +y}, simulation; | ||
}, | ||
unfix: function(node) { | ||
return delete fixes[node.index], simulation; | ||
}, | ||
on: function(name, _) { | ||
@@ -362,0 +388,0 @@ return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name); |
@@ -1,1 +0,1 @@ | ||
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("d3-quadtree"),require("d3-collection"),require("d3-dispatch"),require("d3-timer")):"function"==typeof define&&define.amd?define(["exports","d3-quadtree","d3-collection","d3-dispatch","d3-timer"],t):t(n.d3_force=n.d3_force||{},n.d3_quadtree,n.d3_collection,n.d3_dispatch,n.d3_timer)}(this,function(n,t,e,r,i){"use strict";function o(n,t){function e(){var e,i,o=r.length,u=0,f=0;for(e=0;o>e;++e)i=r[e],u+=i.x,f+=i.y;for(u=u/o-n,f=f/o-t,e=0;o>e;++e)i=r[e],i.x-=u,i.y-=f}var r;return null==n&&(n=0),null==t&&(t=0),e.initialize=function(n){r=n},e.x=function(t){return arguments.length?(n=+t,e):n},e.y=function(n){return arguments.length?(t=+n,e):t},e}function u(n){return function(){return n}}function f(){return 1e-6*(Math.random()-.5)}function a(n){return n.x+n.vx}function c(n){return n.y+n.vy}function l(n){function e(){function n(n,t,r,o,u){if(t>M||x>o||r>q||p>u)return!0;if(!n.length){var a=v-n.data.x-n.data.vx||f(),c=d-n.data.y-n.data.vy||f(),l=a*a+c*c,h=i[e]+i[n.data.index];h*h>l&&(l=Math.sqrt(l),l=(h-l)/l,g+=a*l,s+=c*l)}}for(var e,u,v,d,y,g,s,x,p,M,q,m=r.length,w=t.quadtree(r,a,c),A=0;h>A;++A)for(e=0;m>e;++e)u=r[e],y=i[e]+o,g=s=0,v=u.x+u.vx,x=v-y,M=v+y,d=u.y+u.vy,p=d-y,q=d+y,w.remove(u).visit(n),u.vx+=g*l,u.vy+=s*l,w.add(u)}var r,i,o,l=.7,h=1;return"function"!=typeof n&&(n=u(null==n?1:+n)),e.initialize=function(t){var e,u,f=(r=t).length;for(i=new Array(f),o=0,e=0;f>e;++e)(i[e]=u=+n(r[e],e,r))>o&&(o=u)},e.iterations=function(n){return arguments.length?(h=+n,e):h},e.strength=function(n){return arguments.length?(l=+n,e):l},e.radius=function(t){return arguments.length?(n="function"==typeof t?t:u(+t),e):n},e}function h(n,t){return t}function v(n){function t(t){for(var e=0,r=n.length;s>e;++e)for(var i,o,u,l,h,d,y,g=0;r>g;++g)i=n[g],o=i.source,u=i.target,l=u.x+u.vx-o.x-o.vx||f(),h=u.y+u.vy-o.y-o.vy||f(),d=Math.sqrt(l*l+h*h),d=(d-c[g])/d*t*a[g],l*=d,h*=d,u.vx-=l*(y=v[g]),u.vy-=h*y,o.vx+=l*(y=1-y),o.vy+=h*y}function r(){if(l){var t,r,u=l.length,f=n.length,h=new Array(u),y=e.map(l,d);for(t=0;u>t;++t)h[t]=0;for(t=0,v=new Array(f);f>t;++t)r=n[t],r.index=t,"object"!=typeof r.source&&(r.source=y.get(r.source)),"object"!=typeof r.target&&(r.target=y.get(r.target)),++h[r.source.index],++h[r.target.index];for(t=0;f>t;++t)r=n[t],v[t]=h[r.source.index]/(h[r.source.index]+h[r.target.index]);a=new Array(f),i(),c=new Array(f),o()}}function i(){if(l)for(var t=0,e=n.length;e>t;++t)a[t]=+y(n[t])}function o(){if(l)for(var t=0,e=n.length;e>t;++t)c[t]=+g(n[t])}var a,c,l,v,d=h,y=u(.7),g=u(30),s=1;return null==n&&(n=[]),t.initialize=function(n){l=n,r()},t.links=function(e){return arguments.length?(n=e,r(),t):n},t.id=function(n){return arguments.length?(d=n,t):d},t.iterations=function(n){return arguments.length?(s=+n,t):s},t.strength=function(n){return arguments.length?(y="function"==typeof n?n:u(+n),i(),t):y},t.distance=function(n){return arguments.length?(g="function"==typeof n?n:u(+n),o(),t):g},t}function d(n){return n.x}function y(n){return n.y}function g(n){function t(){return h=0,s.restart(u),l}function o(){return s.stop(),l}function u(){var n=f();x.call("tick",l),n&&(s.stop(),x.call("end",l))}function f(){var t=Math.exp(++h*d);g.each(function(n){n(t)});for(var e,r=0,i=n.length;i>r;++r)e=n[r],e.x+=e.vx*=y,e.y+=e.vy*=y;return v>t}function a(){for(var t,e=0,r=n.length;r>e;++e){if(t=n[e],t.index=e,isNaN(t.x)||isNaN(t.y)){var i=q*Math.sqrt(e),o=e*m;t.x=i*Math.cos(o),t.y=i*Math.sin(o)}(isNaN(t.vx)||isNaN(t.vy))&&(t.vx=t.vy=0)}}function c(t){return t.initialize&&t.initialize(n),t}var l,h=0,v=.001,d=-.02,y=.6,g=e.map(),s=i.timer(u),x=r.dispatch("tick","end");return null==n&&(n=[]),a(),l={restart:t,stop:o,tick:f,nodes:function(t){return arguments.length?(n=t,a(),g.each(c),l):n},alphaMin:function(n){return arguments.length?(v=n,l):v},alphaDecay:function(n){return arguments.length?(h=+n?Math.round(h*d/-n):0,d=-n,l):-d},drag:function(n){return arguments.length?(y=1-n,l):1-y},force:function(n,t){return arguments.length>1?(null==t?g.remove(n):g.set(n,c(t)),l):g.get(n)},on:function(n,t){return arguments.length>1?(x.on(n,t),l):x.on(n)}}}function s(){function n(n){var e,u=o.length,f=t.quadtree(o,d,y).visitAfter(r);for(c=n,e=0;u>e;++e)a=o[e],f.visit(i)}function e(){if(o){var n,t=o.length;for(l=new Array(t),n=0;t>n;++n)l[n]=+h(o[n],n,o)}}function r(n){var t,e,r,i,o,u=0;if(n.length){for(r=i=o=0;4>o;++o)(t=n[o])&&(e=t.value)&&(u+=e,r+=e*t.x,i+=e*t.y);n.x=r/u,n.y=i/u}else{t=n,t.x=t.data.x,t.y=t.data.y;do u+=l[t.data.index];while(t=t.next)}n.value=u}function i(n,t,e,r){if(!n.value)return!0;var i=n.x-a.x,o=n.y-a.y,u=r-t,h=i*i+o*o;if(h>u*u/s)return g>h&&(0===i&&(i=f(),h+=i*i),0===o&&(o=f(),h+=o*o),v>h&&(h=Math.sqrt(v*h)),a.vx+=i*n.value*c/h,a.vy+=o*n.value*c/h),!0;if(!(n.length||h>=g)){(n.data!==a||n.next)&&(0===i&&(i=f(),h+=i*i),0===o&&(o=f(),h+=o*o),v>h&&(h=Math.sqrt(v*h)));do n.data!==a&&(u=l[n.data.index]*c/h,a.vx+=i*u,a.vy+=o*u);while(n=n.next)}}var o,a,c,l,h=u(-100),v=1,g=1/0,s=.81;return n.initialize=function(n){o=n,e()},n.strength=function(t){return arguments.length?(h="function"==typeof t?t:u(+t),e(),n):h},n.distanceMin=function(t){return arguments.length?(v=t*t,n):Math.sqrt(v)},n.distanceMax=function(t){return arguments.length?(g=t*t,n):Math.sqrt(g)},n.theta=function(t){return arguments.length?(s=t*t,n):Math.sqrt(s)},n}function x(n){function t(n){for(var t,e=0,u=r.length;u>e;++e)t=r[e],t.vx+=(o[e]-t.x)*i[e]*n}function e(){if(r){var t,e=r.length;for(i=new Array(e),o=new Array(e),t=0;e>t;++t)i[t]=+f(r[t],t,r),o[t]=+n(r[t],t,r)}}var r,i,o,f=u(.1);return"function"!=typeof n&&(n=u(null==n?0:+n)),t.initialize=function(n){r=n,e()},t.strength=function(n){return arguments.length?(f="function"==typeof n?n:u(+n),e(),t):f},t.x=function(r){return arguments.length?(n="function"==typeof r?r:u(+r),e(),t):n},t}function p(n){function t(n){for(var t,e=0,u=r.length;u>e;++e)t=r[e],t.vy+=(o[e]-t.y)*i[e]*n}function e(){if(r){var t,e=r.length;for(i=new Array(e),o=new Array(e),t=0;e>t;++t)i[t]=+f(r[t],t,r),o[t]=+n(r[t],t,r)}}var r,i,o,f=u(.1);return"function"!=typeof n&&(n=u(null==n?0:+n)),t.initialize=function(n){r=n,e()},t.strength=function(n){return arguments.length?(f="function"==typeof n?n:u(+n),e(),t):f},t.y=function(r){return arguments.length?(n="function"==typeof r?r:u(+r),e(),t):n},t}var M="0.5.0",q=10,m=Math.PI*(3-Math.sqrt(5));n.version=M,n.forceCenter=o,n.forceCollide=l,n.forceLink=v,n.forceManyBody=s,n.forceSimulation=g,n.forceX=x,n.forceY=p}); | ||
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("d3-quadtree"),require("d3-collection"),require("d3-dispatch"),require("d3-timer")):"function"==typeof define&&define.amd?define(["exports","d3-quadtree","d3-collection","d3-dispatch","d3-timer"],t):t(n.d3_force=n.d3_force||{},n.d3_quadtree,n.d3_collection,n.d3_dispatch,n.d3_timer)}(this,function(n,t,e,r,i){"use strict";function o(n,t){function e(){var e,i,o=r.length,u=0,f=0;for(e=0;o>e;++e)i=r[e],u+=i.x,f+=i.y;for(u=u/o-n,f=f/o-t,e=0;o>e;++e)i=r[e],i.x-=u,i.y-=f}var r;return null==n&&(n=0),null==t&&(t=0),e.initialize=function(n){r=n},e.x=function(t){return arguments.length?(n=+t,e):n},e.y=function(n){return arguments.length?(t=+n,e):t},e}function u(n){return function(){return n}}function f(){return 1e-6*(Math.random()-.5)}function a(n){return n.x+n.vx}function c(n){return n.y+n.vy}function l(n){function e(){function n(n,t,r,i,o){var u=n.data,a=n.r,c=x+a;{if(!u)return t>y+c||y-c>i||r>d+c||d-c>o;if(u.index>e){var h=y-u.x-u.vx,s=d-u.y-u.vy,p=h*h+s*s;c*c>p&&(0===h&&(h=f(),p+=h*h),0===s&&(s=f(),p+=s*s),p=(c-(p=Math.sqrt(p)))/p*l,v.vx+=(h*=p)*(c=(a*=a)/(g+a)),v.vy+=(s*=p)*c,u.vx-=h*(c=1-c),u.vy-=s*c)}}}for(var e,u,v,y,d,x,g,s=i.length,p=0;h>p;++p)for(u=t.quadtree(i,a,c).visitAfter(r),e=0;s>e;++e)v=i[e],x=o[e],g=x*x,y=v.x+v.vx,d=v.y+v.vy,u.visit(n)}function r(n){if(n.data)return n.r=o[n.data.index];for(var t=n.r=0;4>t;++t)n[t]&&n[t].r>n.r&&(n.r=n[t].r)}var i,o,l=1,h=1;return"function"!=typeof n&&(n=u(null==n?1:+n)),e.initialize=function(t){var e,r=(i=t).length;for(o=new Array(r),e=0;r>e;++e)o[e]=+n(i[e],e,i)},e.iterations=function(n){return arguments.length?(h=+n,e):h},e.strength=function(n){return arguments.length?(l=+n,e):l},e.radius=function(t){return arguments.length?(n="function"==typeof t?t:u(+t),e):n},e}function h(n,t){return t}function v(n){function t(t){for(var e=0,r=n.length;g>e;++e)for(var i,o,u,l,h,y,d,x=0;r>x;++x)i=n[x],o=i.source,u=i.target,l=u.x+u.vx-o.x-o.vx||f(),h=u.y+u.vy-o.y-o.vy||f(),y=Math.sqrt(l*l+h*h),y=(y-c[x])/y*t*a[x],l*=y,h*=y,u.vx-=l*(d=v[x]),u.vy-=h*d,o.vx+=l*(d=1-d),o.vy+=h*d}function r(){if(l){var t,r,u=l.length,f=n.length,h=new Array(u),d=e.map(l,y);for(t=0;u>t;++t)h[t]=0;for(t=0,v=new Array(f);f>t;++t)r=n[t],r.index=t,"object"!=typeof r.source&&(r.source=d.get(r.source)),"object"!=typeof r.target&&(r.target=d.get(r.target)),++h[r.source.index],++h[r.target.index];for(t=0;f>t;++t)r=n[t],v[t]=h[r.source.index]/(h[r.source.index]+h[r.target.index]);a=new Array(f),i(),c=new Array(f),o()}}function i(){if(l)for(var t=0,e=n.length;e>t;++t)a[t]=+d(n[t])}function o(){if(l)for(var t=0,e=n.length;e>t;++t)c[t]=+x(n[t])}var a,c,l,v,y=h,d=u(.7),x=u(30),g=1;return null==n&&(n=[]),t.initialize=function(n){l=n,r()},t.links=function(e){return arguments.length?(n=e,r(),t):n},t.id=function(n){return arguments.length?(y=n,t):y},t.iterations=function(n){return arguments.length?(g=+n,t):g},t.strength=function(n){return arguments.length?(d="function"==typeof n?n:u(+n),i(),t):d},t.distance=function(n){return arguments.length?(x="function"==typeof n?n:u(+n),o(),t):x},t}function y(n){return n.x}function d(n){return n.y}function x(n){function t(){o(),s.call("tick",a),l>c&&(g.stop(),s.call("end",a))}function o(){var t,e,r,i=n.length;for(c+=(v-c)*h,d.each(function(n){n(c)}),t=0;i>t;++t)e=n[t],e.x+=e.vx*=y,e.y+=e.vy*=y;for(t in x)r=x[t],e=n[t],e.x=r.x,e.y=r.y,e.vx=e.vy=0}function u(){for(var t,e=0,r=n.length;r>e;++e){if(t=n[e],t.index=e,isNaN(t.x)||isNaN(t.y)){var i=M*Math.sqrt(e),o=e*w;t.x=i*Math.cos(o),t.y=i*Math.sin(o)}(isNaN(t.vx)||isNaN(t.vy))&&(t.vx=t.vy=0)}}function f(t){return t.initialize&&t.initialize(n),t}var a,c=1,l=.001,h=1-Math.pow(l,1/300),v=0,y=.6,d=e.map(),x={},g=i.timer(t),s=r.dispatch("tick","end");return null==n&&(n=[]),u(),a={tick:o,restart:function(){return g.restart(t),a},stop:function(){return g.stop(),a},nodes:function(t){return arguments.length?(n=t,u(),d.each(f),a):n},alpha:function(n){return arguments.length?(c=+n,a):c},alphaMin:function(n){return arguments.length?(l=+n,a):l},alphaDecay:function(n){return arguments.length?(h=+n,a):+h},alphaTarget:function(n){return arguments.length?(v=+n,a):v},drag:function(n){return arguments.length?(y=1-n,a):1-y},force:function(n,t){return arguments.length>1?(null==t?d.remove(n):d.set(n,f(t)),a):d.get(n)},fix:function(n,t,e){return x[n.index]={x:null==t?n.x:+t,y:null==e?n.y:+e},a},unfix:function(n){return delete x[n.index],a},on:function(n,t){return arguments.length>1?(s.on(n,t),a):s.on(n)}}}function g(){function n(n){var e,u=o.length,f=t.quadtree(o,y,d).visitAfter(r);for(c=n,e=0;u>e;++e)a=o[e],f.visit(i)}function e(){if(o){var n,t=o.length;for(l=new Array(t),n=0;t>n;++n)l[n]=+h(o[n],n,o)}}function r(n){var t,e,r,i,o,u=0;if(n.length){for(r=i=o=0;4>o;++o)(t=n[o])&&(e=t.value)&&(u+=e,r+=e*t.x,i+=e*t.y);n.x=r/u,n.y=i/u}else{t=n,t.x=t.data.x,t.y=t.data.y;do u+=l[t.data.index];while(t=t.next)}n.value=u}function i(n,t,e,r){if(!n.value)return!0;var i=n.x-a.x,o=n.y-a.y,u=r-t,h=i*i+o*o;if(h>u*u/g)return x>h&&(0===i&&(i=f(),h+=i*i),0===o&&(o=f(),h+=o*o),v>h&&(h=Math.sqrt(v*h)),a.vx+=i*n.value*c/h,a.vy+=o*n.value*c/h),!0;if(!(n.length||h>=x)){(n.data!==a||n.next)&&(0===i&&(i=f(),h+=i*i),0===o&&(o=f(),h+=o*o),v>h&&(h=Math.sqrt(v*h)));do n.data!==a&&(u=l[n.data.index]*c/h,a.vx+=i*u,a.vy+=o*u);while(n=n.next)}}var o,a,c,l,h=u(-100),v=1,x=1/0,g=.81;return n.initialize=function(n){o=n,e()},n.strength=function(t){return arguments.length?(h="function"==typeof t?t:u(+t),e(),n):h},n.distanceMin=function(t){return arguments.length?(v=t*t,n):Math.sqrt(v)},n.distanceMax=function(t){return arguments.length?(x=t*t,n):Math.sqrt(x)},n.theta=function(t){return arguments.length?(g=t*t,n):Math.sqrt(g)},n}function s(n){function t(n){for(var t,e=0,u=r.length;u>e;++e)t=r[e],t.vx+=(o[e]-t.x)*i[e]*n}function e(){if(r){var t,e=r.length;for(i=new Array(e),o=new Array(e),t=0;e>t;++t)i[t]=+f(r[t],t,r),o[t]=+n(r[t],t,r)}}var r,i,o,f=u(.1);return"function"!=typeof n&&(n=u(null==n?0:+n)),t.initialize=function(n){r=n,e()},t.strength=function(n){return arguments.length?(f="function"==typeof n?n:u(+n),e(),t):f},t.x=function(r){return arguments.length?(n="function"==typeof r?r:u(+r),e(),t):n},t}function p(n){function t(n){for(var t,e=0,u=r.length;u>e;++e)t=r[e],t.vy+=(o[e]-t.y)*i[e]*n}function e(){if(r){var t,e=r.length;for(i=new Array(e),o=new Array(e),t=0;e>t;++t)i[t]=+f(r[t],t,r),o[t]=+n(r[t],t,r)}}var r,i,o,f=u(.1);return"function"!=typeof n&&(n=u(null==n?0:+n)),t.initialize=function(n){r=n,e()},t.strength=function(n){return arguments.length?(f="function"==typeof n?n:u(+n),e(),t):f},t.y=function(r){return arguments.length?(n="function"==typeof r?r:u(+r),e(),t):n},t}var q="0.6.0",M=10,w=Math.PI*(3-Math.sqrt(5));n.version=q,n.forceCenter=o,n.forceCollide=l,n.forceLink=v,n.forceManyBody=g,n.forceSimulation=x,n.forceX=s,n.forceY=p}); |
export var name = "d3-force"; | ||
export var version = "0.5.0"; | ||
export var version = "0.6.0"; | ||
export var description = "Force-directed graph layout using velocity Verlet integration."; | ||
@@ -10,4 +10,4 @@ export var keywords = ["d3","layout","network","graphc","force","verlet","infovis"]; | ||
export var repository = {"type":"git","url":"https://github.com/d3/d3-force.git"}; | ||
export var scripts = {"pretest":"rm -rf build && mkdir build && json2module package.json > build/package.js && rollup -g d3-collection:d3_collection,d3-dispatch:d3_dispatch,d3-quadtree:d3_quadtree,d3-timer:d3_timer -f umd -n d3_force -o build/d3-force.js -- index.js","test":"tape 'test/**/*-test.js' && eslint index.js src","prepublish":"npm run test && uglifyjs build/d3-force.js -c -m -o build/d3-force.min.js","postpublish":"VERSION=`node -e 'console.log(require(\"./package.json\").version)'`; git push && git push --tags && cp build/d3-force.js ../d3.github.com/d3-force.v0.5.js && cp build/d3-force.min.js ../d3.github.com/d3-force.v0.5.min.js && cd ../d3.github.com && git add d3-force.v0.5.js d3-force.v0.5.min.js && git commit -m \"d3-force ${VERSION}\" && git push && cd - && zip -j build/d3-force.zip -- LICENSE README.md build/d3-force.js build/d3-force.min.js"}; | ||
export var scripts = {"pretest":"rm -rf build && mkdir build && json2module package.json > build/package.js && rollup -g d3-collection:d3_collection,d3-dispatch:d3_dispatch,d3-quadtree:d3_quadtree,d3-timer:d3_timer -f umd -n d3_force -o build/d3-force.js -- index.js","test":"tape 'test/**/*-test.js' && eslint index.js src","prepublish":"npm run test && uglifyjs build/d3-force.js -c -m -o build/d3-force.min.js","postpublish":"VERSION=`node -e 'console.log(require(\"./package.json\").version)'`; git push && git push --tags && cp build/d3-force.js ../d3.github.com/d3-force.v0.6.js && cp build/d3-force.min.js ../d3.github.com/d3-force.v0.6.min.js && cd ../d3.github.com && git add d3-force.v0.6.js d3-force.v0.6.min.js && git commit -m \"d3-force ${VERSION}\" && git push && cd - && zip -j build/d3-force.zip -- LICENSE README.md build/d3-force.js build/d3-force.min.js"}; | ||
export var dependencies = {"d3-collection":"0.1","d3-dispatch":"0.4","d3-quadtree":"0.7","d3-timer":"0.4"}; | ||
export var devDependencies = {"json2module":"0.0","rollup":"0.26","tape":"4","uglify-js":"2"}; |
{ | ||
"name": "d3-force", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"description": "Force-directed graph layout using velocity Verlet integration.", | ||
@@ -30,3 +30,3 @@ "keywords": [ | ||
"prepublish": "npm run test && uglifyjs build/d3-force.js -c -m -o build/d3-force.min.js", | ||
"postpublish": "VERSION=`node -e 'console.log(require(\"./package.json\").version)'`; git push && git push --tags && cp build/d3-force.js ../d3.github.com/d3-force.v0.5.js && cp build/d3-force.min.js ../d3.github.com/d3-force.v0.5.min.js && cd ../d3.github.com && git add d3-force.v0.5.js d3-force.v0.5.min.js && git commit -m \"d3-force ${VERSION}\" && git push && cd - && zip -j build/d3-force.zip -- LICENSE README.md build/d3-force.js build/d3-force.min.js" | ||
"postpublish": "VERSION=`node -e 'console.log(require(\"./package.json\").version)'`; git push && git push --tags && cp build/d3-force.js ../d3.github.com/d3-force.v0.6.js && cp build/d3-force.min.js ../d3.github.com/d3-force.v0.6.min.js && cd ../d3.github.com && git add d3-force.v0.6.js d3-force.v0.6.min.js && git commit -m \"d3-force ${VERSION}\" && git push && cd - && zip -j build/d3-force.zip -- LICENSE README.md build/d3-force.js build/d3-force.min.js" | ||
}, | ||
@@ -33,0 +33,0 @@ "dependencies": { |
# d3-force | ||
This simplified, extensible [velocity Verlet](https://en.wikipedia.org/wiki/Verlet_integration) numerical integrator simulates physical forces on particles. In the domain of information visualization, this is often useful for studying graphs and hierarchies! | ||
This module implements a [velocity Verlet](https://en.wikipedia.org/wiki/Verlet_integration) numerical integrator for simulating physical forces on particles. The simulation is simplified: it assumes a constant unit time step Δ*t* = 1 for each step, and a constant unit mass *m* = 1 for all particles. As a result, a force *F* acting on a particle is equivalent to a constant acceleration *a* over the time interval Δ*t*, and can be simulated simply by adding to the particle’s velocity, which is then added to the particle’s position. | ||
In the domain of information visualization, physical simulations are useful for studying [networks](http://bl.ocks.org/mbostock/f584aa36df54c451c94a9d0798caed35) and [hierarchies](http://bl.ocks.org/mbostock/95aa92e2f4e8345aaa55a4a94d41ce37)! | ||
[<img alt="Force-Directed Graph" src="https://raw.githubusercontent.com/d3/d3-force/master/img/graph.png" width="420" height="219">](http://bl.ocks.org/mbostock/f584aa36df54c451c94a9d0798caed35)[<img alt="Force-Directed Tree" src="https://raw.githubusercontent.com/d3/d3-force/master/img/tree.png" width="420" height="219">](http://bl.ocks.org/mbostock/95aa92e2f4e8345aaa55a4a94d41ce37) | ||
You can also simulate circles (disks) with collision, such as for [bubble charts](http://www.nytimes.com/interactive/2012/09/06/us/politics/convention-word-counts.html): | ||
You can also simulate circles (disks) with collision, such as for [bubble charts](http://www.nytimes.com/interactive/2012/09/06/us/politics/convention-word-counts.html) or [beeswam plots](http://bl.ocks.org/mbostock/6526445e2b44303eebf21da3b6627320): | ||
[<img alt="Collision Detection" src="https://raw.githubusercontent.com/d3/d3-force/master/img/collide.png" width="480" height="250">](http://bl.ocks.org/mbostock/31ce330646fa8bcb7289ff3b97aab3f5) | ||
[<img alt="Collision Detection" src="https://raw.githubusercontent.com/d3/d3-force/master/img/collide.png" width="420" height="219">](http://bl.ocks.org/mbostock/31ce330646fa8bcb7289ff3b97aab3f5)[<img alt="Beeswarm" src="https://raw.githubusercontent.com/d3/d3-force/master/img/beeswarm.png" width="420" height="219">](http://bl.ocks.org/mbostock/6526445e2b44303eebf21da3b6627320) | ||
@@ -19,3 +21,3 @@ You can even use it as a rudimentary physics engine, say to simulate cloth: | ||
If you use NPM, `npm install d3-force`. Otherwise, download the [latest release](https://github.com/d3/d3-force/releases/latest). You can also load directly from [d3js.org](https://d3js.org), either as a [standalone library](https://d3js.org/d3-force.v0.5.min.js) or as part of [D3 4.0 alpha](https://github.com/mbostock/d3/tree/4). AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3_force` global is exported: | ||
If you use NPM, `npm install d3-force`. Otherwise, download the [latest release](https://github.com/d3/d3-force/releases/latest). You can also load directly from [d3js.org](https://d3js.org), either as a [standalone library](https://d3js.org/d3-force.v0.6.min.js) or as part of [D3 4.0 alpha](https://github.com/mbostock/d3/tree/4). AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3_force` global is exported: | ||
@@ -27,3 +29,3 @@ ```html | ||
<script src="https://d3js.org/d3-timer.v0.4.min.js"></script> | ||
<script src="https://d3js.org/d3-force.v0.5.min.js"></script> | ||
<script src="https://d3js.org/d3-force.v0.6.min.js"></script> | ||
<script> | ||
@@ -48,20 +50,12 @@ | ||
Resets the current iteration count, setting the current alpha to one, restarts the simulation‘s internal timer, and returns the simulation. This method can be used to “reheat” the simulation after interaction, such as when dragging a node, or to resume the simulation after temporarily pausing it with [*simulation*.stop](#simulation_stop). | ||
Restarts the simulation’s internal timer and returns the simulation. In conjunction with [*simulation*.alphaTarget](#simulation_alphaTarget) or [*simulation*.alpha](#simulation_alpha), this method can be used to “reheat” the simulation during interaction, such as when dragging a node, or to resume the simulation after temporarily pausing it with [*simulation*.stop](#simulation_stop). | ||
<a name="simulation_stop" href="#simulation_stop">#</a> <i>simulation</i>.<b>stop</b>() | ||
Stops the simulation‘s internal timer, if it is running, and returns the simulation. If the timer is already stopped, this method does nothing. This method is useful for running the simulation manually; see [*simulation*.tick](#simulation_tick). | ||
Stops the simulation’s internal timer, if it is running, and returns the simulation. If the timer is already stopped, this method does nothing. This method is useful for running the simulation manually; see [*simulation*.tick](#simulation_tick). | ||
<a name="simulation_tick" href="#simulation_tick">#</a> <i>simulation</i>.<b>tick</b>() | ||
Invokes each registered [force](#simulation_force), passing the current *alpha*; then updates the positions and velocities of each [node](#simulation_nodes) according to the following formula: *velocity* \*= 1 - [*drag*](#simulation_drag), *position* += *velocity*. Returns true if the current alpha is less than [*alphaMin*](#simulation_alphaMin), indicating that the simulation would normally stop after this tick, and false otherwise. | ||
Increments the current [*alpha*](#simulation_alpha) by ([*alphaTarget*](#simulation_alphaTarget) - *alpha*) × [*alphaDecay*](#simulation_alphaDecay); then invokes each registered [force](#simulation_force), passing the new *alpha*; then decrements each [node](#simulation_nodes)’s velocity by *velocity* × [*drag*](#simulation_drag); lastly increments each node’s position by *velocity*. | ||
The current *alpha* is defined as exp(*iteration* × [*alphaDecay*](#simulation_alphaDecay)) where *iteration* is the number of times this method has been called since the simulation started. Thus, the exact number of iterations needed to terminate the simulation naturally is ⌈log([*alphaMin*](#simulation_alphaMin)) / -[*alphaDecay*](#simulation_alphaDecay)⌉. For example, to run the simulation manually, as when computing a [static layout](http://bl.ocks.org/mbostock/01ab2e85e8727d6529d20391c0fd9a16) in a background web worker or on the server: | ||
```js | ||
for (var i = 0, n = Math.ceil(Math.log(simulation.alphaMin()) / -simulation.alphaDecay()); i < n; ++i) { | ||
simulation.tick(); | ||
} | ||
``` | ||
This method does not dispatch [events](#simulation_on); events are only dispatched by the internal timer when the simulation is started automatically upon [creation](#forceSimulation) or by calling [*simulation*.restart](#simulation_restart). | ||
@@ -83,13 +77,23 @@ | ||
<a name="simulation_alphaMin" href="#simulation_alphaMin">#</a> <i>simulation</i>.<b>alphaMin</b>([<i>alpha</i>]) | ||
<a name="simulation_alpha" href="#simulation_alpha">#</a> <i>simulation</i>.<b>alpha</b>([<i>alpha</i>]) | ||
If *alpha* is specified, sets the minimum alpha to the specified number and returns this simulation. If *alpha* is not specified, returns the current minimum alpha value, which defaults to 0.001. The minimum alpha value determines when the simulation will stop automatically: when the current alpha is less than the minimum alpha. Assuming the default [alpha decay rate](#simulation_alphaDecay) of 0.02, this corresponds to 346 iterations. | ||
If *alpha* is specified, sets the current alpha to the specified number in [0,1] and returns this simulation. If *alpha* is not specified, returns the current alpha value, which defaults to 1. | ||
<a name="simulation_alphaMin" href="#simulation_alphaMin">#</a> <i>simulation</i>.<b>alphaMin</b>([<i>min</i>]) | ||
If *min* is specified, sets the minimum *alpha* to the specified number in [0,1] and returns this simulation. If *min* is not specified, returns the current minimum *alpha* value, which defaults to 0.001. The simulation’s internal timer stops when the current [*alpha*](#simulation_alpha) is less than the minimum *alpha*. The default [alpha decay rate](#simulation_alphaDecay) of ~0.0228 corresponds to 300 iterations. | ||
<a name="simulation_alphaDecay" href="#simulation_alphaDecay">#</a> <i>simulation</i>.<b>alphaDecay</b>([<i>decay</i>]) | ||
If *decay* is specified, sets the [exponential decay](https://en.wikipedia.org/wiki/Exponential_decay) rate constant λ to the specified number and returns this simulation. If *decay* is not specified, returns the current alpha decay rate, which defaults to 0.02. The alpha decay rate determines how quickly the simulation stabilizes. Higher values cause the simulation to stabilize more quickly, but risk getting stuck in a local minimum; lower values cause the simulation to take longer to run, but typically converge on a better layout. To have the simulation run forever, set the *decay* rate to zero. | ||
If *decay* is specified, sets the [*alpha*](#simulation_alpha) decay rate to the specified number in [0,1] and returns this simulation. If *decay* is not specified, returns the current *alpha* decay rate, which defaults to 0.0228… = 1 - *pow*(0.001, 1 / 300). | ||
The alpha decay rate determines how quickly the current alpha interpolates towards the desired [target *alpha*](#simulation_alphaTarget); since the default target *alpha* is zero, by default this controls how quickly the simulation cools. Higher decay rates cause the simulation to stabilize more quickly, but risk getting stuck in a local minimum; lower values cause the simulation to take longer to run, but typically converge on a better layout. To have the simulation run forever at the current *alpha*, set the *decay* rate to zero; alternatively, set a [target *alpha*](#simulation_alphaTarget) greater than the [minimum *alpha*](#simulation_alphaMin). | ||
<a name="simulation_alphaTarget" href="#simulation_alphaTarget">#</a> <i>simulation</i>.<b>alphaTarget</b>([<i>target</i>]) | ||
If *target* is specified, sets the current target [*alpha*](#simulation_alpha) to the specified number in [0,1] and returns this simulation. If *target* is not specified, returns the current target alpha value, which defaults to 0. | ||
<a name="simulation_drag" href="#simulation_drag">#</a> <i>simulation</i>.<b>drag</b>([<i>drag</i>]) | ||
If *drag* is specified, sets the drag factor to the specified number in the range [0,1] and returns this simulation. If *drag* is not specified, returns the current drag factor, which defaults to 0.4. The drag factor affects how quickly nodes’ velocities decay; at each [tick](#simulation_tick), the velocities are updated according to the following formula: *velocity* \*= 1 - *drag*. As with lowering the [alpha decay rate](#simulation_alphaDecay), less drag may converge on a better solution, but it also risks numerical instabilities and oscillations. | ||
If *drag* is specified, sets the drag factor to the specified number in [0,1] and returns this simulation. If *drag* is not specified, returns the current drag factor, which defaults to 0.4. The drag factor affects how quickly nodes’ velocities decay; after the application of any forces during a [tick](#simulation_tick), each node’s velocity is multiplied by 1 - *drag*. As with lowering the [alpha decay rate](#simulation_alphaDecay), less drag may converge on a better solution, but it also risks numerical instabilities and oscillations. | ||
@@ -107,2 +111,10 @@ <a name="simulation_force" href="#simulation_force">#</a> <i>simulation</i>.<b>force</b>(<i>name</i>[, <i>force</i>]) | ||
<a name="simulation_fix" href="#simulation_fix">#</a> <i>simulation</i>.<b>fix</b>(<i>node</i>[, <i>x</i>, <i>y</i>]) | ||
Fixes the given *node*’s position to ⟨*x*,*y*⟩ and the velocity to zero, and returns this simulation. If *x* and *y* are not specified, the fixed position defaults to the node’s current position. When a node is fixed, its position is reset to ⟨*x*,*y*⟩ and its velocity is reset to zero at the end of each tick, after the application of any forces. This method is intended to temporarily freeze the position of one or more nodes during interaction, such as when dragging or hovering. After the interaction completes, use [*simulation*.unfix](#simulation_unfix) to release the node. | ||
<a name="simulation_unfix" href="#simulation_unfix">#</a> <i>simulation</i>.<b>unfix</b>(<i>node</i>) | ||
If the given *node* is currently [fixed](#simulation_fix), unfixes (frees) the *node*’s position and returns this simulation. | ||
<a name="simulation_on" href="#simulation_on">#</a> <i>simulation</i>.<b>on</b>(<i>typenames</i>, [<i>listener</i>]) | ||
@@ -327,3 +339,3 @@ | ||
To accelerate computation, this force implements the [Barnes–Hut approximation](http://en.wikipedia.org/wiki/Barnes–Hut_simulation) which takes O(*n* log *n*) per application where *n* is the number of [nodes](#simulation_nodes). For each application, a [quadtree](https://github.com/d3/d3-quadtree) stores the current node positions; then for each node, the combined force of all other nodes on the given node is computed. For a cluster of nodes that is far away, the charge force can be approximated by treating the cluster as a single, larger node. The *theta* parameter determines the accuracy of the approximation: if the ratio of the area of the quadtree cell to the distance from the node to the cell’s center of mass is less than *theta*, all nodes in the given cell are treated as a single node rather than computed individually. | ||
To accelerate computation, this force implements the [Barnes–Hut approximation](http://en.wikipedia.org/wiki/Barnes–Hut_simulation) which takes O(*n* log *n*) per application where *n* is the number of [nodes](#simulation_nodes). For each application, a [quadtree](https://github.com/d3/d3-quadtree) stores the current node positions; then for each node, the combined force of all other nodes on the given node is computed. For a cluster of nodes that is far away, the charge force can be approximated by treating the cluster as a single, larger node. The *theta* parameter determines the accuracy of the approximation: if the ratio *w* / *l* of the width *w* of the quadtree cell to the distance *l* from the node to the cell’s center of mass is less than *theta*, all nodes in the given cell are treated as a single node rather than individually. | ||
@@ -340,3 +352,3 @@ <a name="manyBody_distanceMin" href="#manyBody_distanceMin">#</a> <i>manyBody</i>.<b>distanceMin</b>([<i>distance</i>]) | ||
The positioning forces pushes nodes towards a desired [*x*](#forceX)- or [*y*](#forceY)-position with a configurable strength. The strength of the force is proportional to the one-dimensional distance between the node’s position and the target position. | ||
The [*x*](#forceX)- and [*y*](#forceY)-positioning forces push nodes towards a desired position along the given dimension with a configurable strength. The strength of the force is proportional to the one-dimensional distance between the node’s position and the target position. While these forces can be used to position individual nodes, they are intended primarily for global forces that apply to all (or most) nodes; see [*simulation*.fix](#simulation_fix). | ||
@@ -349,3 +361,3 @@ <a name="forceX" href="#forceX">#</a> d3.<b>forceX</b>([<i>x</i>]) | ||
If *strength* is specified, sets the strength accessor to the specified number or function, re-evaluates the strength accessor for each node, and returns this force. The *strength* determines how to modify the node’s *x*-velocity according to the following formula: *node*.vx += ([*x*](#x_x) - *node*.x) × *strength*. For example, a value of 0.1 indicates that the node should move a tenth of the way from its current *x*-position to the target *x*-position with each application. Higher values moves nodes more quickly to the target position, often at the expense of other forces or constraints. A value outside the range [0,1] is not recommended. | ||
If *strength* is specified, sets the strength accessor to the specified number or function, re-evaluates the strength accessor for each node, and returns this force. The *strength* determines how much to increment the node’s *x*-velocity: ([*x*](#x_x) - *node*.x) × *strength*. For example, a value of 0.1 indicates that the node should move a tenth of the way from its current *x*-position to the target *x*-position with each application. Higher values moves nodes more quickly to the target position, often at the expense of other forces or constraints. A value outside the range [0,1] is not recommended. | ||
@@ -380,3 +392,3 @@ If *strength* is not specified, returns the current strength accessor, which defaults to: | ||
If *strength* is specified, sets the strength accessor to the specified number or function, re-evaluates the strength accessor for each node, and returns this force. The *strength* determines how to modify the node’s *y*-velocity according to the following formula: *node*.vy += ([*y*](#y_y) - *node*.y) × *strength*. For example, a value of 0.1 indicates that the node should move a tenth of the way from its current *y*-position to the target *y*-position with each application. Higher values moves nodes more quickly to the target position, often at the expense of other forces or constraints. A value outside the range [0,1] is not recommended. | ||
If *strength* is specified, sets the strength accessor to the specified number or function, re-evaluates the strength accessor for each node, and returns this force. The *strength* determines how much to increment the node’s *y*-velocity: ([*y*](#y_y) - *node*.y) × *strength*. For example, a value of 0.1 indicates that the node should move a tenth of the way from its current *y*-position to the target *y*-position with each application. Higher values moves nodes more quickly to the target position, often at the expense of other forces or constraints. A value outside the range [0,1] is not recommended. | ||
@@ -383,0 +395,0 @@ If *strength* is not specified, returns the current strength accessor, which defaults to: |
@@ -16,4 +16,3 @@ import constant from "./constant"; | ||
radii, | ||
radiusMax, | ||
strength = 0.7, | ||
strength = 1, | ||
iterations = 1; | ||
@@ -25,22 +24,17 @@ | ||
var i, n = nodes.length, | ||
tree = quadtree(nodes, x, y), | ||
tree, | ||
node, | ||
nx, | ||
ny, | ||
nr, | ||
vx, | ||
vy, | ||
nx0, | ||
ny0, | ||
nx1, | ||
ny1; | ||
xi, | ||
yi, | ||
ri, | ||
ri2; | ||
for (var k = 0; k < iterations; ++k) { | ||
tree = quadtree(nodes, x, y).visitAfter(prepare); | ||
for (i = 0; i < n; ++i) { | ||
node = nodes[i], nr = radii[i] + radiusMax, vx = vy = 0; | ||
nx = node.x + node.vx, nx0 = nx - nr, nx1 = nx + nr; | ||
ny = node.y + node.vy, ny0 = ny - nr, ny1 = ny + nr; | ||
tree.remove(node).visit(apply); | ||
node.vx += vx * strength, node.vy += vy * strength; | ||
tree.add(node); | ||
node = nodes[i]; | ||
ri = radii[i], ri2 = ri * ri; | ||
xi = node.x + node.vx; | ||
yi = node.y + node.vy; | ||
tree.visit(apply); | ||
} | ||
@@ -50,25 +44,36 @@ } | ||
function apply(quad, x0, y0, x1, y1) { | ||
if (x0 > nx1 || x1 < nx0 || y0 > ny1 || y1 < ny0) return true; | ||
if (quad.length) return; | ||
var x = nx - quad.data.x - quad.data.vx || jiggle(), | ||
y = ny - quad.data.y - quad.data.vy || jiggle(), | ||
l = x * x + y * y, | ||
r = radii[i] + radii[quad.data.index]; | ||
if (l < r * r) { | ||
l = Math.sqrt(l); | ||
l = (r - l) / l; | ||
vx += x * l, vy += y * l; | ||
var data = quad.data, rj = quad.r, r = ri + rj; | ||
if (data) { | ||
if (data.index > i) { | ||
var x = xi - data.x - data.vx, | ||
y = yi - data.y - data.vy, | ||
l = x * x + y * y; | ||
if (l < r * r) { | ||
if (x === 0) x = jiggle(), l += x * x; | ||
if (y === 0) y = jiggle(), l += y * y; | ||
l = (r - (l = Math.sqrt(l))) / l * strength; | ||
node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj)); | ||
node.vy += (y *= l) * r; | ||
data.vx -= x * (r = 1 - r); | ||
data.vy -= y * r; | ||
} | ||
} | ||
return; | ||
} | ||
return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r; | ||
} | ||
} | ||
force.initialize = function(_) { | ||
var i, n = (nodes = _).length, r; | ||
radii = new Array(n); | ||
radiusMax = 0; | ||
for (i = 0; i < n; ++i) { | ||
if ((radii[i] = r = +radius(nodes[i], i, nodes)) > radiusMax) { | ||
radiusMax = r; | ||
function prepare(quad) { | ||
if (quad.data) return quad.r = radii[quad.data.index]; | ||
for (var i = quad.r = 0; i < 4; ++i) { | ||
if (quad[i] && quad[i].r > quad.r) { | ||
quad.r = quad[i].r; | ||
} | ||
} | ||
} | ||
force.initialize = function(_) { | ||
var i, n = (nodes = _).length; radii = new Array(n); | ||
for (i = 0; i < n; ++i) radii[i] = +radius(nodes[i], i, nodes); | ||
}; | ||
@@ -75,0 +80,0 @@ |
@@ -18,7 +18,9 @@ import {dispatch} from "d3-dispatch"; | ||
var simulation, | ||
iteration = 0, | ||
alpha = 1, | ||
alphaMin = 0.001, | ||
alphaDecay = -0.02, | ||
alphaDecay = 1 - Math.pow(alphaMin, 1 / 300), | ||
alphaTarget = 0, | ||
drag = 0.6, | ||
forces = map(), | ||
fixes = {}, | ||
stepper = timer(step), | ||
@@ -29,17 +31,6 @@ event = dispatch("tick", "end"); | ||
function restart() { | ||
iteration = 0; | ||
stepper.restart(step); | ||
return simulation; | ||
} | ||
function stop() { | ||
stepper.stop(); | ||
return simulation; | ||
} | ||
function step() { | ||
var stop = tick(); | ||
tick(); | ||
event.call("tick", simulation); | ||
if (stop) { | ||
if (alpha < alphaMin) { | ||
stepper.stop(); | ||
@@ -51,4 +42,6 @@ event.call("end", simulation); | ||
function tick() { | ||
var alpha = Math.exp(++iteration * alphaDecay); | ||
var i, n = nodes.length, node, fix; | ||
alpha += (alphaTarget - alpha) * alphaDecay; | ||
forces.each(function(force) { | ||
@@ -58,3 +51,3 @@ force(alpha); | ||
for (var i = 0, n = nodes.length, node; i < n; ++i) { | ||
for (i = 0; i < n; ++i) { | ||
node = nodes[i]; | ||
@@ -65,3 +58,9 @@ node.x += node.vx *= drag; | ||
return alpha < alphaMin; | ||
for (i in fixes) { | ||
fix = fixes[i], node = nodes[i]; | ||
node.x = fix.x; | ||
node.y = fix.y; | ||
node.vx = | ||
node.vy = 0; | ||
} | ||
} | ||
@@ -91,6 +90,12 @@ | ||
return simulation = { | ||
restart: restart, | ||
stop: stop, | ||
tick: tick, | ||
restart: function() { | ||
return stepper.restart(step), simulation; | ||
}, | ||
stop: function() { | ||
return stepper.stop(), simulation; | ||
}, | ||
nodes: function(_) { | ||
@@ -100,10 +105,18 @@ return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes; | ||
alpha: function(_) { | ||
return arguments.length ? (alpha = +_, simulation) : alpha; | ||
}, | ||
alphaMin: function(_) { | ||
return arguments.length ? (alphaMin = _, simulation) : alphaMin; | ||
return arguments.length ? (alphaMin = +_, simulation) : alphaMin; | ||
}, | ||
alphaDecay: function(_) { | ||
return arguments.length ? (iteration = +_ ? Math.round(iteration * alphaDecay / -_) : 0, alphaDecay = -_, simulation) : -alphaDecay; | ||
return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay; | ||
}, | ||
alphaTarget: function(_) { | ||
return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget; | ||
}, | ||
drag: function(_) { | ||
@@ -117,2 +130,10 @@ return arguments.length ? (drag = 1 - _, simulation) : 1 - drag; | ||
fix: function(node, x, y) { | ||
return fixes[node.index] = {x: x == null ? node.x : +x, y: y == null ? node.y : +y}, simulation; | ||
}, | ||
unfix: function(node) { | ||
return delete fixes[node.index], simulation; | ||
}, | ||
on: function(name, _) { | ||
@@ -119,0 +140,0 @@ return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
360104
23
968
407