Comparing version 0.3.0 to 0.4.0
@@ -7,3 +7,3 @@ (function (global, factory) { | ||
var version = "0.3.0"; | ||
var version = "0.4.0"; | ||
@@ -135,58 +135,2 @@ function center(x, y) { | ||
function contain(radius, x, y) { | ||
var nodes, | ||
rz, | ||
xz, | ||
yz; | ||
if (typeof radius !== "function") radius = constant(radius == null ? 100 : +radius); | ||
if (typeof x !== "function") x = constant(x == null ? 0 : +x); | ||
if (typeof y !== "function") y = constant(y == null ? 0 : +y); | ||
function force() { | ||
for (var i = 0, n = nodes.length, node, x, y, l, r; i < n; ++i) { | ||
node = nodes[i]; | ||
x = node.x - xz[i]; | ||
y = node.y - yz[i]; | ||
if ((l = x * x + y * y) > (r = rz[i]) * r) { | ||
l = Math.sqrt(l), l = (l - r) / l; | ||
node.vx -= x * l; | ||
node.vy -= y * l; | ||
} | ||
} | ||
} | ||
function initialize() { | ||
if (!nodes) return; | ||
var i, n = nodes.length; | ||
rz = new Array(n); | ||
xz = new Array(n); | ||
yz = new Array(n); | ||
for (i = 0; i < n; ++i) { | ||
rz[i] = +radius(nodes[i], i, nodes); | ||
xz[i] = +x(nodes[i], i, nodes); | ||
yz[i] = +y(nodes[i], i, nodes); | ||
} | ||
} | ||
force.initialize = function(_) { | ||
nodes = _; | ||
initialize(); | ||
}; | ||
force.radius = function(_) { | ||
return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), initialize(), force) : radius; | ||
}; | ||
force.x = function(_) { | ||
return arguments.length ? (x = typeof _ === "function" ? _ : constant(+_), initialize(), force) : x; | ||
}; | ||
force.y = function(_) { | ||
return arguments.length ? (y = typeof _ === "function" ? _ : constant(+_), initialize(), force) : y; | ||
}; | ||
return force; | ||
} | ||
var tau = 2 * Math.PI; | ||
@@ -228,3 +172,3 @@ | ||
function initialize() { | ||
if (!nodes || !links) return; | ||
if (!nodes) return; | ||
@@ -253,7 +197,14 @@ var i, | ||
if (!strengths) for (i = 0, strengths = new Array(m); i < m; ++i) { | ||
strengths = new Array(m), initializeStrength(); | ||
distances = new Array(m), initializeDistance(); | ||
} | ||
function initializeStrength() { | ||
for (var i = 0, n = links.length; i < n; ++i) { | ||
strengths[i] = +strength(links[i]); | ||
} | ||
} | ||
if (!distances) for (i = 0, distances = new Array(m); i < m; ++i) { | ||
function initializeDistance() { | ||
for (var i = 0, n = links.length; i < n; ++i) { | ||
distances[i] = +distance(links[i]); | ||
@@ -269,7 +220,7 @@ } | ||
force.links = function(_) { | ||
return arguments.length ? (links = _, strengths = distances = null, initialize(), force) : links; | ||
return arguments.length ? (links = _, initialize(), force) : links; | ||
}; | ||
force.id = function(_) { | ||
return arguments.length ? (id = _, initialize(), force) : id; | ||
return arguments.length ? (id = _, force) : id; | ||
}; | ||
@@ -282,7 +233,7 @@ | ||
force.strength = function(_) { | ||
return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), strengths = null, initialize(), force) : strength; | ||
return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initializeStrength(), force) : strength; | ||
}; | ||
force.distance = function(_) { | ||
return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), distances = null, initialize(), force) : distance; | ||
return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), initializeDistance(), force) : distance; | ||
}; | ||
@@ -306,3 +257,3 @@ | ||
iteration = 0, | ||
alphaMin = 0.0001, | ||
alphaMin = 0.001, | ||
alphaDecay = -0.02, | ||
@@ -570,3 +521,2 @@ drag = 0.6, | ||
exports.forceCollide = collide; | ||
exports.forceContain = contain; | ||
exports.forceLink = link; | ||
@@ -573,0 +523,0 @@ exports.forceManyBody = manyBody; |
@@ -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(n){return n.x+n.vx}function a(n){return n.y+n.vy}function c(n){function e(){function n(n,t,r,o,u){if(t>p||g>o||r>M||x>u)return!0;if(!n.length){var f=h-n.data.x-n.data.vx,a=y-n.data.y-n.data.vy,c=f*f+a*a,l=i[e]+i[n.data.index];l*l>c&&(c=(l-(c=Math.sqrt(c)))/c,v+=f*c,s+=a*c)}}for(var e,u,h,y,d,v,s,g,x,p,M,q=r.length,w=t.quadtree(r,f,a),m=0;l>m;++m)for(e=0;q>e;++e)u=r[e],d=i[e]+o,v=s=0,h=u.x+u.vx,g=h-d,p=h+d,y=u.y+u.vy,x=y-d,M=y+d,w.remove(u).visit(n),u.vx+=v*c,u.vy+=s*c,w.add(u)}var r,i,o,c=.7,l=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?(l=+n,e):l},e.strength=function(n){return arguments.length?(c=+n,e):c},e.radius=function(t){return arguments.length?(n="function"==typeof t?t:u(+t),e):n},e}function l(n,t,e){function r(){for(var n,t,e,r,i,u=0,l=o.length;l>u;++u)n=o[u],t=n.x-a[u],e=n.y-c[u],(r=t*t+e*e)>(i=f[u])*i&&(r=Math.sqrt(r),r=(r-i)/r,n.vx-=t*r,n.vy-=e*r)}function i(){if(o){var r,i=o.length;for(f=new Array(i),a=new Array(i),c=new Array(i),r=0;i>r;++r)f[r]=+n(o[r],r,o),a[r]=+t(o[r],r,o),c[r]=+e(o[r],r,o)}}var o,f,a,c;return"function"!=typeof n&&(n=u(null==n?100:+n)),"function"!=typeof t&&(t=u(null==t?0:+t)),"function"!=typeof e&&(e=u(null==e?0:+e)),r.initialize=function(n){o=n,i()},r.radius=function(t){return arguments.length?(n="function"==typeof t?t:u(+t),i(),r):n},r.x=function(n){return arguments.length?(t="function"==typeof n?n:u(+n),i(),r):t},r.y=function(n){return arguments.length?(e="function"==typeof n?n:u(+n),i(),r):e},r}function h(n,t){return t}function y(n){function t(t){for(var e=0,r=n.length;d>e;++e)for(var u,f,c,l,h,y,v,s=0;r>s;++s)u=n[s],f=u.source,c=u.target,l=c.x+c.vx-f.x-f.vx,h=c.y+c.vy-f.y-f.vy,(y=l*l+h*h)?(y=Math.sqrt(y),y=(y-o[s])/y):(y=Math.random()*M,l=Math.cos(y),h=Math.sin(y),y=o[s]),y*=t*i[s],l*=y,h*=y,c.vx-=l*(v=a[s]),c.vy-=h*v,f.vx+=l*(v=1-v),f.vy+=h*v}function r(){if(f&&n){var t,r,u=f.length,h=n.length,d=new Array(u),v=e.map(f,c);for(t=0;u>t;++t)d[t]=0;for(t=0,a=new Array(h);h>t;++t)r=n[t],r.index=t,"object"!=typeof r.source&&(r.source=v.get(r.source)),"object"!=typeof r.target&&(r.target=v.get(r.target)),++d[r.source.index],++d[r.target.index];for(t=0;h>t;++t)r=n[t],a[t]=d[r.source.index]/(d[r.source.index]+d[r.target.index]);if(!i)for(t=0,i=new Array(h);h>t;++t)i[t]=+l(n[t]);if(!o)for(t=0,o=new Array(h);h>t;++t)o[t]=+y(n[t])}}var i,o,f,a,c=h,l=u(.7),y=u(30),d=1;return null==n&&(n=[]),t.initialize=function(n){f=n,r()},t.links=function(e){return arguments.length?(n=e,i=o=null,r(),t):n},t.id=function(n){return arguments.length?(c=n,r(),t):c},t.iterations=function(n){return arguments.length?(d=+n,t):d},t.strength=function(n){return arguments.length?(l="function"==typeof n?n:u(+n),i=null,r(),t):l},t.distance=function(n){return arguments.length?(y="function"==typeof n?n:u(+n),o=null,r(),t):y},t}function d(n){return n.x}function v(n){return n.y}function s(n){function t(){return h=0,g.restart(u),l}function o(){return g.stop(),l}function u(){var n=f();x.call("tick",l),n&&(g.stop(),x.call("end",l))}function f(){var t=Math.exp(++h*d);s.each(function(n){n(t)});for(var e,r=0,i=n.length;i>r;++r)e=n[r],e.x+=e.vx*=v,e.y+=e.vy*=v;return y>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*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 c(t){return t.initialize&&t.initialize(n),t}var l,h=0,y=1e-4,d=-.02,v=.6,s=e.map(),g=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(),s.each(c),l):n},alphaMin:function(n){return arguments.length?(y=n,l):y},alphaDecay:function(n){return arguments.length?(h=+n?Math.round(h*d/-n):0,d=-n,l):-d},drag:function(n){return arguments.length?(v=1-n,l):1-v},force:function(n,t){return arguments.length>1?(null==t?s.remove(n):s.set(n,c(t)),l):s.get(n)},on:function(n,t){return arguments.length>1?(x.on(n,t),l):x.on(n)}}}function g(){function n(n){var e,u=o.length,c=t.quadtree(o,d,v).visitAfter(r);for(a=n,e=0;u>e;++e)f=o[e],c.visit(i)}function e(){if(o){var n,t=o.length;for(c=new Array(t),n=0;t>n;++n)c[n]=+l(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+=c[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-f.x,o=n.y-f.y,u=r-t,l=i*i+o*o;if(l>u*u/s)return y>l&&(h>l&&(l=Math.sqrt(l/h),i/=l,o/=l,l=h),l=n.value*a/l,f.vx+=i*l,f.vy+=o*l),!0;if(!(n.length||l>=y)){h>l&&(l||(l=Math.random()*m,i=Math.cos(l),o=Math.sin(l),l=1),l=Math.sqrt(l/h),i/=l,o/=l,l=h);do n.data!==f&&(u=c[n.data.index]*a/l,f.vx+=i*u,f.vy+=o*u);while(n=n.next)}}var o,f,a,c,l=u(-100),h=1,y=1/0,s=.81;return n.initialize=function(n){o=n,e()},n.strength=function(t){return arguments.length?(l="function"==typeof t?t:u(+t),e(),n):l},n.distanceMin=function(t){return arguments.length?(h=t*t,n):Math.sqrt(h)},n.distanceMax=function(t){return arguments.length?(y=t*t,n):Math.sqrt(y)},n.theta=function(t){return arguments.length?(s=t*t,n):Math.sqrt(s)},n}function x(n,t){function e(n){for(var t,e,r=0,u=i.length;u>r;++r)t=i[r],e=o[r]*n,t.vx+=(f[r]-t.x)*e,t.vy+=(a[r]-t.y)*e}function r(){if(i){var e,r=i.length;for(o=new Array(r),f=new Array(r),a=new Array(r),e=0;r>e;++e)o[e]=+c(i[e],e,i),f[e]=+n(i[e],e,i),a[e]=+t(i[e],e,i)}}var i,o,f,a,c=u(.1);return"function"!=typeof n&&(n=u(null==n?0:+n)),"function"!=typeof t&&(t=u(null==t?0:+t)),e.initialize=function(n){i=n,r()},e.strength=function(n){return arguments.length?(c="function"==typeof n?n:u(+n),r(),e):c},e.x=function(t){return arguments.length?(n="function"==typeof t?t:u(+t),r(),e):n},e.y=function(n){return arguments.length?(t="function"==typeof n?n:u(+n),r(),e):t},e}var p="0.3.0",M=2*Math.PI,q=10,w=Math.PI*(3-Math.sqrt(5)),m=2*Math.PI;n.version=p,n.forceCenter=o,n.forceCollide=c,n.forceContain=l,n.forceLink=y,n.forceManyBody=g,n.forcePosition=x,n.forceSimulation=s}); | ||
!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,a=0;for(e=0;o>e;++e)i=r[e],u+=i.x,a+=i.y;for(u=u/o-n,a=a/o-t,e=0;o>e;++e)i=r[e],i.x-=u,i.y-=a}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 a(n){return n.x+n.vx}function f(n){return n.y+n.vy}function c(n){function e(){function n(n,t,r,o,u){if(t>p||g>o||r>M||x>u)return!0;if(!n.length){var a=h-n.data.x-n.data.vx,f=d-n.data.y-n.data.vy,c=a*a+f*f,l=i[e]+i[n.data.index];l*l>c&&(c=(l-(c=Math.sqrt(c)))/c,y+=a*c,s+=f*c)}}for(var e,u,h,d,v,y,s,g,x,p,M,q=r.length,m=t.quadtree(r,a,f),w=0;l>w;++w)for(e=0;q>e;++e)u=r[e],v=i[e]+o,y=s=0,h=u.x+u.vx,g=h-v,p=h+v,d=u.y+u.vy,x=d-v,M=d+v,m.remove(u).visit(n),u.vx+=y*c,u.vy+=s*c,m.add(u)}var r,i,o,c=.7,l=1;return"function"!=typeof n&&(n=u(null==n?1:+n)),e.initialize=function(t){var e,u,a=(r=t).length;for(i=new Array(a),o=0,e=0;a>e;++e)(i[e]=u=+n(r[e],e,r))>o&&(o=u)},e.iterations=function(n){return arguments.length?(l=+n,e):l},e.strength=function(n){return arguments.length?(c=+n,e):c},e.radius=function(t){return arguments.length?(n="function"==typeof t?t:u(+t),e):n},e}function l(n,t){return t}function h(n){function t(t){for(var e=0,r=n.length;s>e;++e)for(var i,o,u,c,l,d,v,y=0;r>y;++y)i=n[y],o=i.source,u=i.target,c=u.x+u.vx-o.x-o.vx,l=u.y+u.vy-o.y-o.vy,(d=c*c+l*l)?(d=Math.sqrt(d),d=(d-f[y])/d):(d=Math.random()*p,c=Math.cos(d),l=Math.sin(d),d=f[y]),d*=t*a[y],c*=d,l*=d,u.vx-=c*(v=h[y]),u.vy-=l*v,o.vx+=c*(v=1-v),o.vy+=l*v}function r(){if(c){var t,r,u=c.length,l=n.length,v=new Array(u),y=e.map(c,d);for(t=0;u>t;++t)v[t]=0;for(t=0,h=new Array(l);l>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)),++v[r.source.index],++v[r.target.index];for(t=0;l>t;++t)r=n[t],h[t]=v[r.source.index]/(v[r.source.index]+v[r.target.index]);a=new Array(l),i(),f=new Array(l),o()}}function i(){for(var t=0,e=n.length;e>t;++t)a[t]=+v(n[t])}function o(){for(var t=0,e=n.length;e>t;++t)f[t]=+y(n[t])}var a,f,c,h,d=l,v=u(.7),y=u(30),s=1;return null==n&&(n=[]),t.initialize=function(n){c=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?(v="function"==typeof n?n:u(+n),i(),t):v},t.distance=function(n){return arguments.length?(y="function"==typeof n?n:u(+n),o(),t):y},t}function d(n){return n.x}function v(n){return n.y}function y(n){function t(){return h=0,g.restart(u),l}function o(){return g.stop(),l}function u(){var n=a();x.call("tick",l),n&&(g.stop(),x.call("end",l))}function a(){var t=Math.exp(++h*v);s.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 d>t}function f(){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*q;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,d=.001,v=-.02,y=.6,s=e.map(),g=i.timer(u),x=r.dispatch("tick","end");return null==n&&(n=[]),f(),l={restart:t,stop:o,tick:a,nodes:function(t){return arguments.length?(n=t,f(),s.each(c),l):n},alphaMin:function(n){return arguments.length?(d=n,l):d},alphaDecay:function(n){return arguments.length?(h=+n?Math.round(h*v/-n):0,v=-n,l):-v},drag:function(n){return arguments.length?(y=1-n,l):1-y},force:function(n,t){return arguments.length>1?(null==t?s.remove(n):s.set(n,c(t)),l):s.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,c=t.quadtree(o,d,v).visitAfter(r);for(f=n,e=0;u>e;++e)a=o[e],c.visit(i)}function e(){if(o){var n,t=o.length;for(c=new Array(t),n=0;t>n;++n)c[n]=+l(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+=c[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,l=i*i+o*o;if(l>u*u/s)return y>l&&(h>l&&(l=Math.sqrt(l/h),i/=l,o/=l,l=h),l=n.value*f/l,a.vx+=i*l,a.vy+=o*l),!0;if(!(n.length||l>=y)){h>l&&(l||(l=Math.random()*m,i=Math.cos(l),o=Math.sin(l),l=1),l=Math.sqrt(l/h),i/=l,o/=l,l=h);do n.data!==a&&(u=c[n.data.index]*f/l,a.vx+=i*u,a.vy+=o*u);while(n=n.next)}}var o,a,f,c,l=u(-100),h=1,y=1/0,s=.81;return n.initialize=function(n){o=n,e()},n.strength=function(t){return arguments.length?(l="function"==typeof t?t:u(+t),e(),n):l},n.distanceMin=function(t){return arguments.length?(h=t*t,n):Math.sqrt(h)},n.distanceMax=function(t){return arguments.length?(y=t*t,n):Math.sqrt(y)},n.theta=function(t){return arguments.length?(s=t*t,n):Math.sqrt(s)},n}function g(n,t){function e(n){for(var t,e,r=0,u=i.length;u>r;++r)t=i[r],e=o[r]*n,t.vx+=(a[r]-t.x)*e,t.vy+=(f[r]-t.y)*e}function r(){if(i){var e,r=i.length;for(o=new Array(r),a=new Array(r),f=new Array(r),e=0;r>e;++e)o[e]=+c(i[e],e,i),a[e]=+n(i[e],e,i),f[e]=+t(i[e],e,i)}}var i,o,a,f,c=u(.1);return"function"!=typeof n&&(n=u(null==n?0:+n)),"function"!=typeof t&&(t=u(null==t?0:+t)),e.initialize=function(n){i=n,r()},e.strength=function(n){return arguments.length?(c="function"==typeof n?n:u(+n),r(),e):c},e.x=function(t){return arguments.length?(n="function"==typeof t?t:u(+t),r(),e):n},e.y=function(n){return arguments.length?(t="function"==typeof n?n:u(+n),r(),e):t},e}var x="0.4.0",p=2*Math.PI,M=10,q=Math.PI*(3-Math.sqrt(5)),m=2*Math.PI;n.version=x,n.forceCenter=o,n.forceCollide=c,n.forceLink=h,n.forceManyBody=s,n.forcePosition=g,n.forceSimulation=y}); |
export var name = "d3-force"; | ||
export var version = "0.3.0"; | ||
export var version = "0.4.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.3.js && cp build/d3-force.min.js ../d3.github.com/d3-force.v0.3.min.js && cd ../d3.github.com && git add d3-force.v0.3.js d3-force.v0.3.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.4.js && cp build/d3-force.min.js ../d3.github.com/d3-force.v0.4.min.js && cd ../d3.github.com && git add d3-force.v0.4.js d3-force.v0.4.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"}; |
export {version} from "./build/package"; | ||
export {default as forceCenter} from "./src/center"; | ||
export {default as forceCollide} from "./src/collide"; | ||
export {default as forceContain} from "./src/contain"; | ||
export {default as forceLink} from "./src/link"; | ||
@@ -6,0 +5,0 @@ export {default as forceManyBody} from "./src/manyBody"; |
{ | ||
"name": "d3-force", | ||
"version": "0.3.0", | ||
"version": "0.4.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.3.js && cp build/d3-force.min.js ../d3.github.com/d3-force.v0.3.min.js && cd ../d3.github.com && git add d3-force.v0.3.js d3-force.v0.3.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.4.js && cp build/d3-force.min.js ../d3.github.com/d3-force.v0.4.min.js && cd ../d3.github.com && git add d3-force.v0.4.js d3-force.v0.4.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": { |
285
README.md
# 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! | ||
[<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): | ||
[<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) | ||
You can even use it as a rudimentary physics engine, say to simulate cloth: | ||
[<img alt="Force-Directed Lattice" src="https://raw.githubusercontent.com/d3/d3-force/master/img/lattice.png" width="480" height="250">](http://bl.ocks.org/mbostock/1b64ec067fcfc51e7471d944f51f1611) | ||
To use this module, create a [simulation](#simulation) for an array of [nodes](#simulation_nodes), and compose the desired [forces](#simulation_force). Then [listen](#simulation_on) for tick events to render the nodes as they update in your preferred graphics system, such as Canvas or SVG. | ||
## Installing | ||
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.3.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.4.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: | ||
@@ -14,3 +26,3 @@ ```html | ||
<script src="https://d3js.org/d3-timer.v0.4.min.js"></script> | ||
<script src="https://d3js.org/d3-force.v0.3.min.js"></script> | ||
<script src="https://d3js.org/d3-force.v0.4.min.js"></script> | ||
<script> | ||
@@ -27,25 +39,21 @@ | ||
… | ||
### Simulation | ||
… | ||
<a name="forceSimulation" href="#forceSimulation">#</a> d3.<b>forceSimulation</b>([<i>nodes</i>]) | ||
… | ||
Creates a new simulation with the specified array of [*nodes*](#simulation_nodes) and no [forces](#simulation_force). If *nodes* is not specified, it defaults to the empty array. The simulator [starts](#simulation_restart) automatically; use [*simulation*.on](#simulation_on) to listen for tick events as the simulation runs. If you wish to run the simulation manually instead, call [*simulation*.stop](#simulation_stop), and then call [*simulation*.tick](#simulation_tick) as desired. | ||
<a name="simulation_restart" href="#simulation_restart">#</a> <i>simulation</i>.<b>restart</b>() | ||
… | ||
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). | ||
<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). | ||
<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. | ||
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: | ||
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: | ||
@@ -58,7 +66,7 @@ ```js | ||
This may be useful for computing [static force-directed layouts](http://bl.ocks.org/mbostock/01ab2e85e8727d6529d20391c0fd9a16), such as in a background web worker or on the server. | ||
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). | ||
<a name="simulation_nodes" href="#simulation_nodes">#</a> <i>simulation</i>.<b>nodes</b>([<i>nodes</i>]) | ||
… | ||
If *nodes* is specified, sets the simulation’s nodes to the specified array, initializing their positions and velocities if necessary, and then [re-initializes](#force_initialize) any bound [forces](#simulation_force); returns the simulation. If *nodes* is not specified, returns the simulation’s array of nodes as specified to the [constructor](#forceSimulation). Each *node* must be an object; the following properties are initialized by the simulation: | ||
@@ -71,29 +79,60 @@ * `index` - the node’s zero-based index into *nodes* | ||
The position ⟨*x*,*y*⟩ and velocity ⟨*vx*,*vy*⟩ may be subsequently modified by [forces](#forces) and by the simulation. If either *vx* or *vy* is NaN, the velocity is initialized to ⟨0,0⟩. If either *x* or *y* is NaN, the position is initialized in a [phyllotaxis arrangement](http://bl.ocks.org/mbostock/11478058), so chosen to ensure a deterministic, uniform distribution around the origin. | ||
If the specified array of *nodes* is modified, such as when nodes are added to or removed from the simulation, this method must be called again with the new (or changed) array to notify the simulation and bound forces of the change; the simulation does not make a defensive copy of the specified array. | ||
<a name="simulation_alphaMin" href="#simulation_alphaMin">#</a> <i>simulation</i>.<b>alphaMin</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. | ||
<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. | ||
<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. | ||
<a name="simulation_force" href="#simulation_force">#</a> <i>simulation</i>.<b>force</b>(<i>name</i>[, <i>force</i>]) | ||
… | ||
If *force* is specified, assigns the [force](#forces) for the specified *name* and returns this simulation. If *force* is not specified, returns the force with the specified name, or undefined if there is no such force. (By default, new simulations have no forces.) For example, to create a new simulation to layout a graph, you might say: | ||
<a name="simulation_on" href="#simulation_on">#</a> <i>simulation</i>.<b>on</b>(<i>typenames</i>, [<i>callback</i>]) | ||
```js | ||
var simulation = d3.forceSimulation(nodes) | ||
.force("charge", d3.forceManyBody()) | ||
.force("link", d3.forceLink(links)) | ||
.force("center", d3.forceCenter()); | ||
``` | ||
… | ||
<a name="simulation_on" href="#simulation_on">#</a> <i>simulation</i>.<b>on</b>(<i>typenames</i>, [<i>listener</i>]) | ||
If *listener* is specified, sets the event *listener* for the specified *typenames* and returns this simulation. If an event listener was already registered for the same type and name, the existing listener is removed before the new listener is added. If *listener* is null, removes the current event listeners for the specified *typenames*, if any. If *listener* is not specified, returns the first currently-assigned listener matching the specified *typenames*, if any. When a specified event is dispatched, each *listener* will be invoked with the `this` context as the simulation. | ||
The *typenames* is a string containing one or more *typename* separated by whitespace. Each *typename* is a *type*, optionally followed by a period (`.`) and a *name*, such as `tick.foo` and `tick.bar`; the name allows multiple listeners to be registered for the same *type*. The *type* must be one of the following: | ||
* `tick` - after each [tick](#simulation_tick) of the simulation. | ||
* `end` - after the simulation ends, when *alpha* < [*alphaMin*](#simulation_alphaMin). | ||
See [*dispatch*.on](https://github.com/d3/d3-dispatch#dispatch_on) for details. | ||
### Forces | ||
[Simulations](#simulation) compose multiple arbitrary forces. By default, simulations have no bound forces; add or remove a force using [*simulation*.force](#simulation_force). This module provides several built-in forces: | ||
A *force* is simply a function that modifies nodes’ positions or velocities; in this context, a *force* can apply a classical physical force such as electrical charge or gravity, or it can resolve a geometric constraint, such as keeping nodes within a bounding box or keeping linked nodes a fixed distance apart. For example, a simple positioning force that moves nodes towards the origin ⟨0,0⟩ might be implemented as: | ||
```js | ||
function force(alpha) { | ||
for (var i = 0, n = nodes.length, node, k = alpha * 0.1; i < n; ++i) { | ||
node = nodes[i]; | ||
node.vx -= node.x * k; | ||
node.vy -= node.y * k; | ||
} | ||
} | ||
``` | ||
Forces typically read the node’s current position ⟨*x*,*y*⟩ and then add to (or subtract from) the node’s velocity ⟨*vx*,*vy*⟩. However, forces may also “peek ahead” to the anticipated next position of the node, ⟨*x* + *vx*,*y* + *vy*⟩; this is necessary for resolving geometric constraints through [iterative relaxation](https://en.wikipedia.org/wiki/Relaxation_\(iterative_method\)). Forces may also modify the position directly, which is sometimes useful to avoid adding energy to the simulation, such as when recentering the simulation in the viewport. | ||
Simulations typically compose multiple forces as desired. This module provides several for your enjoyment: | ||
* [Centering](#centering) | ||
* [Circle Collision](#circle-collision) | ||
* [Circle Containment](#circle-containment) | ||
* [Collision](#collision) | ||
* [Links](#links) | ||
@@ -103,136 +142,240 @@ * [Many-Body](#many-body) | ||
You may also implement your own custom force. A force is simply a [function](#_force) that takes the simulation’s current *alpha* value and then modifies nodes’ positions or velocities. Forces may optionally implement [*force*.initialize](#force_initialize) to receive the simulation’s array of nodes. | ||
Forces may optionally implement [*force*.initialize](#force_initialize) to receive the simulation’s array of nodes. | ||
<a name="_force" href="#_force">#</a> <i>force</i>(<i>alpha</i>) | ||
… | ||
Applies this force, optionally observing the specified *alpha*. Typically, the force is applied to the array of nodes previously passed to [*force*.initialize](#force_initialize), however, some forces may apply to a subset of nodes, or behave differently. For example, [d3.forceLink](#links) applies to the source and target of each link. | ||
<a name="force_initialize" href="#force_initialize">#</a> <i>force</i>.<b>initialize</b>(<i>nodes</i>) | ||
… | ||
Assigns the array of *nodes* to this force. This method is called when a force is bound to a simulation via [*simulation*.force](#simulation_force) and when the simulation’s nodes change via [*simulation*.nodes](#simulation_nodes). A force may perform necessary work during initialization, such as evaluating per-node parameters, to avoid repeatedly performing work during each application of the force. | ||
#### Centering | ||
The centering force moves nodes so that their center of mass (assuming all nodes are equal-weight) is at the given position ⟨[*x*](#center_x),[*y*](#center_y)⟩. (These parameters are only recomputed when the force is initialized, not on every application.) | ||
The centering force translates nodes uniformly so that the mean position of all nodes (the center of mass if all nodes have equal weight) is at the given position ⟨[*x*](#center_x),[*y*](#center_y)⟩. This force modifies the positions of nodes on each application; it does not modify velocities, as doing so would typically cause the nodes to overshoot and oscillate around the desired center. This force helps keeps nodes in the center of the viewport, and unlike the [positioning force](#positioning), it does not distort their relative positions. | ||
<a name="forceCenter" href="#forceCenter">#</a> d3.<b>forceCenter</b>([<i>x</i>, <i>y</i>]) | ||
… | ||
Creates a new centering force with the specified [*x*-](#center_x) and [*y*-](#center_y) coordinates. If *x* and *y* are not specified, they default to ⟨0,0⟩. | ||
<a name="center_x" href="#center_x">#</a> <i>center</i>.<b>x</b>([<i>x</i>]) | ||
… | ||
If *x* is specified, sets the *x*-coordinate of the centering position to the specified number and returns this force. If *x* is not specified, returns the current *x*-coordinate, which defaults to zero. | ||
<a name="center_y" href="#center_y">#</a> <i>center</i>.<b>y</b>([<i>y</i>]) | ||
… | ||
If *y* is specified, sets the *y*-coordinate of the centering position to the specified number and returns this force. If *y* is not specified, returns the current *y*-coordinate, which defaults to zero. | ||
#### Circle Collision | ||
#### Collision | ||
The circle collision force prevents circular nodes with a given [radius](#collide_radius) from overlapping. More formally, two nodes *a* and *b* are separated so that the distance between *a* and *b* is at least *radius*(*a*) + *radius*(*b*). To reduce jitter, this is by default a “soft” constraint with a configurable [strength](#collide_strength). (These parameters are only recomputed when the force is initialized, not on every application.) | ||
The collision force treats nodes as circles with a given [radius](#collide_radius), rather than points, and prevents nodes from overlapping. More formally, two nodes *a* and *b* are separated so that the distance between *a* and *b* is at least *radius*(*a*) + *radius*(*b*). To reduce jitter, this is by default a “soft” constraint with a configurable [strength](#collide_strength) and [iteration count](#collide_iterations). | ||
<a name="forceCollide" href="#forceCollide">#</a> d3.<b>forceCollide</b>([<i>radius</i>]) | ||
… | ||
Creates a new circle collision force with the specified [*radius*](#collide_radius). If *radius* is not specified, it defaults to the constant one for all nodes. | ||
<a name="collide_radius" href="#collide_radius">#</a> <i>collide</i>.<b>radius</b>([<i>radius</i>]) | ||
… | ||
If *radius* is specified, sets the radius accessor to the specified number or function, re-evaluates the radius accessor for each node, and returns this force. If *radius* is not specified, returns the current radius accessor, which defaults to: | ||
<a name="collide_strength" href="#collide_strength">#</a> <i>collide</i>.<b>strength</b>([<i>strength</i>]) | ||
```js | ||
function radius() { | ||
return 1; | ||
} | ||
``` | ||
… | ||
The radius accessor is invoked for each [node](#simulation_nodes) in the simulation, being passed the *node* and its zero-based *index*. The resulting number is then stored internally, such that the radius of each node is only recomputed when the force is initialized or when this method is called, and not on every application of the force. | ||
#### Circle Containment | ||
<a name="collide_strength" href="#collide_strength">#</a> <i>collide</i>.<b>strength</b>([<i>strength</i>]) | ||
The circle containment force constrains nodes to fit within a circle of a given [*radius*](#contain_radius) and center ⟨[*x*](#contain_x),[*y*](#contain_y)⟩. The radius and center can be specified on a per-node basis. (These parameters are only recomputed when the force is initialized, not on every application.) | ||
If *strength* is specified, sets the force strength to the specified number in [0,1] and returns this force. If *strength* is not specified, returns the current strength which defaults to 0.7. | ||
<a name="forceContain" href="#forceContain">#</a> d3.<b>forceContain</b>([<i>radius</i>[, <i>x</i>, <i>y</i>]]) | ||
Overlapping nodes are resolved through iterative relaxation. For each node, the other nodes that are anticipated to overlap at the the at the next tick (using the anticipated positions ⟨*x* + *vx*,*y* + *vy*⟩) are determined; the node’s velocity is then modified to push the node out of each overlapping node. The change in velocity is dampened by the force’s strength such that the resolution of simultaneous overlaps can be blended together to find a stable solution. | ||
… | ||
<a name="collide_iterations" href="#collide_iterations">#</a> <i>collide</i>.<b>iterations</b>([<i>iterations</i>]) | ||
<a name="contain_radius" href="#contain_radius">#</a> <i>contain</i>.<b>radius</b>([<i>radius</i>]) | ||
If *iterations* is specified, sets the number of iterations per application to the specified number and returns this force. If *iterations* is not specified, returns the current iteration count which defaults to 1. Increasing the number of iterations greatly increases the rigidity of the constraint and avoids partial overlap of nodes, but also increases the runtime cost to evaluate the force. | ||
… | ||
<a name="contain_x" href="#contain_x">#</a> <i>contain</i>.<b>x</b>([<i>x</i>]) | ||
… | ||
<a name="contain_y" href="#contain_y">#</a> <i>contain</i>.<b>y</b>([<i>y</i>]) | ||
… | ||
#### Links | ||
The link force pushes linked nodes closer together or farther apart according to the desired [link distance](#link_distance), which may be specified on a per-node basis. (These parameters are only recomputed when the force is initialized, not on every application.) | ||
The link force pushes linked nodes together or apart according to the desired [link distance](#link_distance). The strength of the force is proportional to the difference between the linked nodes’ distance and the target distance, similar to a spring force. | ||
<a name="forceLink" href="#forceLink">#</a> d3.<b>forceLink</b>([<i>links</i>]) | ||
… | ||
Creates a new link force with the specified *links* and default parameters. If *links* is not specified, it defaults to the empty array. | ||
<a name="link_links" href="#link_links">#</a> <i>link</i>.<b>links</b>([<i>links</i>]) | ||
… | ||
If *links* is specified, sets the array of links associated with this force, recomputes the [distance](#link_distance) and [strength](#link_strength) parameters for each link, and returns this force. If *links* is not specified, returns the current array of links, which defaults to the empty array. | ||
* `index` - the zero-based index into *links* | ||
Each link is an object with the following properties: | ||
* `source` - the link’s source node; see [*simulation*.nodes](#simulation_nodes) | ||
* `target` - the link’s target node; see [*simulation*.nodes](#simulation_nodes) | ||
* `index` - the zero-based index into *links*, assigned by this method | ||
The source and target properties may be initialized using [*link*.id](#link_id). | ||
For convenience, a link’s source and target properties may be initialized using numeric or string identifiers rather than object references; see [*link*.id](#link_id). | ||
If the specified array of *links* is modified, such as when links are added to or removed from the simulation, this method must be called again with the new (or changed) array to notify the force of the change; the force does not make a defensive copy of the specified array. | ||
<a name="link_id" href="#link_id">#</a> <i>link</i>.<b>id</b>([<i>id</i>]) | ||
… | ||
If *id* is specified, sets the node id accessor to the specified function and returns this force. If *id* is not specified, returns the current node id accessor, which defaults to the numeric index of the node: | ||
<a name="link_strength" href="#link_strength">#</a> <i>link</i>.<b>strength</b>([<i>strength</i>]) | ||
```js | ||
function id(d, i) { | ||
return i; | ||
} | ||
``` | ||
… | ||
The default id accessor allows each link’s source and target to be specified as a zero-based index into the [nodes](#simulation_nodes) array. For example: | ||
```js | ||
var nodes = [ | ||
{"id": "Alice"}, | ||
{"id": "Bob"}, | ||
{"id": "Carol"} | ||
]; | ||
var links = [ | ||
{"source": 0, "target": 1}, // Alice → Bob | ||
{"source": 1, "target": 2} // Bob → Carol | ||
]; | ||
``` | ||
Now consider a different id accessor that returns a string: | ||
```js | ||
function id(d) { | ||
return d.id; | ||
} | ||
``` | ||
With this accessor, you can use named sources and targets: | ||
```js | ||
var nodes = [ | ||
{"id": "Alice"}, | ||
{"id": "Bob"}, | ||
{"id": "Carol"} | ||
]; | ||
var links = [ | ||
{"source": "Alice", "target": "Bob"}, | ||
{"source": "Bob", "target": "Carol"} | ||
]; | ||
``` | ||
This is particularly useful when representing graphs in JSON, as JSON does not allow references. See [this example](http://bl.ocks.org/mbostock/f584aa36df54c451c94a9d0798caed35). | ||
The id accessor is invoked for each node whenever the force is initialized, as when the [nodes](#simulation_nodes) or [links](#link_links) change, being passed the node and its zero-based index. | ||
<a name="link_distance" href="#link_distance">#</a> <i>link</i>.<b>distance</b>([<i>distance</i>]) | ||
… | ||
If *distance* is specified, sets the distance accessor to the specified number or function, re-evaluates the distance accessor for each link, and returns this force. If *distance* is not specified, returns the current distance accessor, which defaults to: | ||
```js | ||
function distance() { | ||
return 30; | ||
} | ||
``` | ||
The distance accessor is invoked for each [link](#link_links), being passed the *link* and its zero-based *index*. The resulting number is then stored internally, such that the distance of each link is only recomputed when the force is initialized or when this method is called, and not on every application of the force. | ||
<a name="link_strength" href="#link_strength">#</a> <i>link</i>.<b>strength</b>([<i>strength</i>]) | ||
If *strength* is specified, sets the strength accessor to the specified number or function, re-evaluates the strength accessor for each link, and returns this force. If *strength* is not specified, returns the current strength accessor, which defaults to: | ||
```js | ||
function strength() { | ||
return 0.7; | ||
} | ||
``` | ||
The strength accessor is invoked for each [link](#link_links), being passed the *link* and its zero-based *index*. The resulting number is then stored internally, such that the strength of each link is only recomputed when the force is initialized or when this method is called, and not on every application of the force. | ||
<a name="link_iterations" href="#link_iterations">#</a> <i>link</i>.<b>iterations</b>([<i>iterations</i>]) | ||
If *iterations* is specified, sets the number of iterations per application to the specified number and returns this force. If *iterations* is not specified, returns the current iteration count which defaults to 1. Increasing the number of iterations greatly increases the rigidity of the constraint and is useful for [complex structures such as lattices](http://bl.ocks.org/mbostock/1b64ec067fcfc51e7471d944f51f1611), but also increases the runtime cost to evaluate the force. | ||
#### Many-Body | ||
The many-body (or *n*-body) force applies mutally amongst all [nodes](#simulation_nodes). It can be used to simulate gravity (attraction) if the [strength](#manyBody_strength) is positive, or eletrical charge (repulsion) if the strength is negative. This implementation uses quadtrees and the [Barnes–Hut approximation](https://en.wikipedia.org/wiki/Barnes–Hut_simulation) to greatly improve performance; the accuracy can be customized using the [theta](#manyBody_theta) parameter. The strength can be specified on a per-node basis. (This parameter is only recomputed when the force is initialized, not on every application.) | ||
The many-body (or *n*-body) force applies mutally amongst all [nodes](#simulation_nodes). It can be used to simulate gravity (attraction) if the [strength](#manyBody_strength) is positive, or electrostatic charge (repulsion) if the strength is negative. This implementation uses quadtrees and the [Barnes–Hut approximation](https://en.wikipedia.org/wiki/Barnes–Hut_simulation) to greatly improve performance; the accuracy can be customized using the [theta](#manyBody_theta) parameter. | ||
Unlike links, which only affect two linked nodes, the charge force is global: every node affects every other node, even if they are on disconnected subgraphs. | ||
<a name="forceManyBody" href="#forceManyBody">#</a> d3.<b>forceManyBody</b>() | ||
… | ||
Creates a new many-body force with the default parameters. | ||
<a name="manyBody_strength" href="#manyBody_strength">#</a> <i>manyBody</i>.<b>strength</b>([<i>strength</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. A positive value causes nodes to attract each other, similar to gravity, while a negative value causes nodes to repel each other, similar to electrostic charge. If *strength* is not specified, returns the current strength accessor, which defaults to: | ||
```js | ||
function strength() { | ||
return -100; | ||
} | ||
``` | ||
The strength accessor is invoked for each [node](#simulation_nodes) in the simulation, being passed the *node* and its zero-based *index*. The resulting number is then stored internally, such that the strength of each node is only recomputed when the force is initialized or when this method is called, and not on every application of the force. | ||
<a name="manyBody_theta" href="#manyBody_theta">#</a> <i>manyBody</i>.<b>theta</b>([<i>theta</i>]) | ||
… | ||
If *theta* is specified, sets the Barnes–Hut approximation criterion to the specified number and returns this force. If *theta* is not specified, returns the current value, which defaults to 0.9. | ||
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. | ||
<a name="manyBody_distanceMin" href="#manyBody_distanceMin">#</a> <i>manyBody</i>.<b>distanceMin</b>([<i>distance</i>]) | ||
… | ||
If *distance* is specified, sets the minimum distance between nodes over which this force is considered. If *distance* is not specified, returns the current minimum distance, which defaults to 1. A minimum distance establishes an upper bound on the strength of the force between two nearby nodes, avoiding instability. In particular, it avoids an infinitely-strong force if two nodes are exactly coincident; in this case, the direction of the force is random. | ||
<a name="manyBody_distanceMax" href="#manyBody_distanceMax">#</a> <i>manyBody</i>.<b>distanceMax</b>([<i>distance</i>]) | ||
… | ||
If *distance* is specified, sets the maximum distance between nodes over which this force is considered. If *distance* is not specified, returns the current maximum distance, which defaults to infinity. Specifying a finite maximum distance improves performance and produces a more localized layout. | ||
#### Positioning | ||
The positioning force pushes nodes towards a desired position ⟨[*x*](#position_x),[*y*](#position_y)⟩. The position and [strength](#position_strength) can be specified on a per-node basis. (These parameters are only recomputed when the force is initialized, not on every application.) | ||
The positioning force pushes nodes towards a desired position ⟨[*x*](#position_x),[*y*](#position_y)⟩ with a configurable [strength](#position_strength). The strength of the force is proportional to the distance between the node’s position and the target position, similar to a spring force. | ||
<a name="forcePosition" href="#forcePosition">#</a> d3.<b>forcePosition</b>([<i>x</i>, <i>y</i>]) | ||
… | ||
Creates a new positioning force towards the given position ⟨[*x*](#position_x),[*y*](#position_y)⟩. If *x* and *y* are not specified, the position defaults to ⟨0,0⟩. | ||
<a name="position_strength" href="#position_strength">#</a> <i>position</i>.<b>strength</b>([<i>strength</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* corresponds to the proportion of the vector from the node’s position to the force’s target position that should be added to the node’s velocity: *velocity* += (*target* - *position*) × *strength*. For example, a value of 0.1 indicates that the node should move a tenth of the way from its current position to the target 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 not specified, returns the current strength accessor, which defaults to: | ||
```js | ||
function strength() { | ||
return 0.1; | ||
} | ||
``` | ||
The strength accessor is invoked for each [node](#simulation_nodes) in the simulation, being passed the *node* and its zero-based *index*. The resulting number is then stored internally, such that the strength of each node is only recomputed when the force is initialized or when this method is called, and not on every application of the force. | ||
<a name="position_x" href="#position_x">#</a> <i>position</i>.<b>x</b>([<i>x</i>]) | ||
… | ||
If *x* is specified, sets the *x*-coordinate accessor to the specified number or function, re-evaluates the *x*-accessor for each node, and returns this force. If *x* is not specified, returns the current *x*-accessor, which defaults to: | ||
```js | ||
function x() { | ||
return 0; | ||
} | ||
``` | ||
The *x*-accessor is invoked for each [node](#simulation_nodes) in the simulation, being passed the *node* and its zero-based *index*. The resulting number is then stored internally, such that the target *x*-coordinate of each node is only recomputed when the force is initialized or when this method is called, and not on every application of the force. | ||
<a name="position_y" href="#position_y">#</a> <i>position</i>.<b>y</b>([<i>y</i>]) | ||
… | ||
If *y* is specified, sets the *y*-coordinate accessor to the specified number or function, re-evaluates the *y*-accessor for each node, and returns this force. If *y* is not specified, returns the current *y*-accessor, which defaults to: | ||
```js | ||
function y() { | ||
return 0; | ||
} | ||
``` | ||
The *y*-accessor is invoked for each [node](#simulation_nodes) in the simulation, being passed the *node* and its zero-based *index*. The resulting number is then stored internally, such that the target *y*-coordinate of each node is only recomputed when the force is initialized or when this method is called, and not on every application of the force. |
@@ -40,3 +40,3 @@ import {map} from "d3-collection"; | ||
function initialize() { | ||
if (!nodes || !links) return; | ||
if (!nodes) return; | ||
@@ -65,7 +65,14 @@ var i, | ||
if (!strengths) for (i = 0, strengths = new Array(m); i < m; ++i) { | ||
strengths = new Array(m), initializeStrength(); | ||
distances = new Array(m), initializeDistance(); | ||
} | ||
function initializeStrength() { | ||
for (var i = 0, n = links.length; i < n; ++i) { | ||
strengths[i] = +strength(links[i]); | ||
} | ||
} | ||
if (!distances) for (i = 0, distances = new Array(m); i < m; ++i) { | ||
function initializeDistance() { | ||
for (var i = 0, n = links.length; i < n; ++i) { | ||
distances[i] = +distance(links[i]); | ||
@@ -81,7 +88,7 @@ } | ||
force.links = function(_) { | ||
return arguments.length ? (links = _, strengths = distances = null, initialize(), force) : links; | ||
return arguments.length ? (links = _, initialize(), force) : links; | ||
}; | ||
force.id = function(_) { | ||
return arguments.length ? (id = _, initialize(), force) : id; | ||
return arguments.length ? (id = _, force) : id; | ||
}; | ||
@@ -94,7 +101,7 @@ | ||
force.strength = function(_) { | ||
return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), strengths = null, initialize(), force) : strength; | ||
return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initializeStrength(), force) : strength; | ||
}; | ||
force.distance = function(_) { | ||
return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), distances = null, initialize(), force) : distance; | ||
return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), initializeDistance(), force) : distance; | ||
}; | ||
@@ -101,0 +108,0 @@ |
@@ -19,3 +19,3 @@ import {dispatch} from "d3-dispatch"; | ||
iteration = 0, | ||
alphaMin = 0.0001, | ||
alphaMin = 0.001, | ||
alphaDecay = -0.02, | ||
@@ -22,0 +22,0 @@ drag = 0.6, |
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
338311
20
377
866