Permalink
Browse files

Simplify force calculations.

We don't actually need the separate `fx` and `fy` attributes on each node;
instead we can modify the `x` and `y` attributes directly, since we're using
position Verlet! This commit also adds the layout's `alpha` parameter to the
tick event, such that normalized external forces can be applied.
  • Loading branch information...
1 parent ee30e29 commit 51b8e023c1fdceb92141d0d496676d8ac950b261 @mbostock mbostock committed Apr 28, 2011
Showing with 74 additions and 88 deletions.
  1. +1 −1 d3.js
  2. +35 −42 d3.layout.js
  3. +1 −1 d3.layout.min.js
  4. +1 −1 d3.min.js
  5. +1 −1 src/core/core.js
  6. +35 −42 src/layout/force.js
View
2 d3.js
@@ -1,4 +1,4 @@
-(function(){d3 = {version: "1.13.2"}; // semver
+(function(){d3 = {version: "1.13.3"}; // semver
if (!Date.now) Date.now = function() {
return +new Date();
};
View
@@ -166,27 +166,6 @@ d3.layout.force = function() {
links,
distances;
- function accumulate(quad) {
- var cx = 0,
- cy = 0;
- quad.count = 0;
- if (!quad.leaf) {
- quad.nodes.forEach(function(c) {
- accumulate(c);
- quad.count += c.count;
- cx += c.count * c.cx;
- cy += c.count * c.cy;
- });
- }
- if (quad.point) {
- quad.count++;
- cx += quad.point.x;
- cy += quad.point.y;
- }
- quad.cx = cx / quad.count;
- quad.cy = cy / quad.count;
- }
-
function repulse(node, kc) {
return function(quad, x1, y1, x2, y2) {
if (quad.point != node) {
@@ -197,15 +176,15 @@ d3.layout.force = function() {
/* Barnes-Hut criterion. */
if ((x2 - x1) * dn < theta) {
var k = kc * quad.count * dn * dn;
- node.fx += dx * k;
- node.fy += dy * k;
+ node.x += dx * k;
+ node.y += dy * k;
return true;
}
if (quad.point) {
var k = kc * dn * dn;
- node.fx += dx * k;
- node.fy += dy * k;
+ node.x += dx * k;
+ node.y += dy * k;
}
}
};
@@ -223,20 +202,15 @@ d3.layout.force = function() {
x, // x-distance
y; // y-distance
- // reset forces
- i = -1; while (++i < n) {
- (o = nodes[i]).fx = o.fy = 0;
- }
-
// gauss-seidel relaxation for links
for (i = 0; i < m; ++i) {
o = links[i];
s = o.source;
t = o.target;
x = t.x - s.x;
y = t.y - s.y;
- if (l = Math.sqrt(x * x + y * y)) {
- l = alpha * (l - distance) / l;
+ if (l = (x * x + y * y)) {
+ l = alpha * ((l = Math.sqrt(l)) - distance) / l;
x *= l;
y *= l;
t.x -= x;
@@ -246,19 +220,19 @@ d3.layout.force = function() {
}
}
- // compute quadtree center of mass
- accumulate(q);
-
// apply gravity forces
var kg = alpha * gravity;
x = size[0] / 2;
y = size[1] / 2;
i = -1; while (++i < n) {
o = nodes[i];
- o.fx += (x - o.x) * kg;
- o.fy += (y - o.y) * kg;
+ o.x += (x - o.x) * kg;
+ o.y += (y - o.y) * kg;
}
+ // compute quadtree center of mass
+ d3_layout_forceAccumulate(q);
+
// apply charge forces
var kc = alpha * charge;
i = -1; while (++i < n) {
@@ -272,14 +246,12 @@ d3.layout.force = function() {
o.x = o.px;
o.y = o.py;
} else {
- x = o.px - (o.px = o.x);
- y = o.py - (o.py = o.y);
- o.x += o.fx - x * drag;
- o.y += o.fy - y * drag;
+ o.x -= (o.px - (o.px = o.x)) * drag;
+ o.y -= (o.py - (o.py = o.y)) * drag;
}
}
- event.tick.dispatch({type: "tick"});
+ event.tick.dispatch({type: "tick", alpha: alpha});
// simulated annealing, basically
return (alpha *= .99) < .005;
@@ -490,6 +462,27 @@ function d3_layout_forceCancel() {
d3.event.stopPropagation();
d3.event.preventDefault();
}
+
+function d3_layout_forceAccumulate(quad) {
+ var cx = 0,
+ cy = 0;
+ quad.count = 0;
+ if (!quad.leaf) {
+ quad.nodes.forEach(function(c) {
+ d3_layout_forceAccumulate(c);
+ quad.count += c.count;
+ cx += c.count * c.cx;
+ cy += c.count * c.cy;
+ });
+ }
+ if (quad.point) {
+ quad.count++;
+ cx += quad.point.x;
+ cy += quad.point.y;
+ }
+ quad.cx = cx / quad.count;
+ quad.cy = cy / quad.count;
+}
d3.layout.partition = function() {
var hierarchy = d3.layout.hierarchy(),
size = [1, 1]; // width, height
View

Large diffs are not rendered by default.

Oops, something went wrong.
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -1 +1 @@
-d3 = {version: "1.13.2"}; // semver
+d3 = {version: "1.13.3"}; // semver
View
@@ -14,27 +14,6 @@ d3.layout.force = function() {
links,
distances;
- function accumulate(quad) {
- var cx = 0,
- cy = 0;
- quad.count = 0;
- if (!quad.leaf) {
- quad.nodes.forEach(function(c) {
- accumulate(c);
- quad.count += c.count;
- cx += c.count * c.cx;
- cy += c.count * c.cy;
- });
- }
- if (quad.point) {
- quad.count++;
- cx += quad.point.x;
- cy += quad.point.y;
- }
- quad.cx = cx / quad.count;
- quad.cy = cy / quad.count;
- }
-
function repulse(node, kc) {
return function(quad, x1, y1, x2, y2) {
if (quad.point != node) {
@@ -45,15 +24,15 @@ d3.layout.force = function() {
/* Barnes-Hut criterion. */
if ((x2 - x1) * dn < theta) {
var k = kc * quad.count * dn * dn;
- node.fx += dx * k;
- node.fy += dy * k;
+ node.x += dx * k;
+ node.y += dy * k;
return true;
}
if (quad.point) {
var k = kc * dn * dn;
- node.fx += dx * k;
- node.fy += dy * k;
+ node.x += dx * k;
+ node.y += dy * k;
}
}
};
@@ -71,20 +50,15 @@ d3.layout.force = function() {
x, // x-distance
y; // y-distance
- // reset forces
- i = -1; while (++i < n) {
- (o = nodes[i]).fx = o.fy = 0;
- }
-
// gauss-seidel relaxation for links
for (i = 0; i < m; ++i) {
o = links[i];
s = o.source;
t = o.target;
x = t.x - s.x;
y = t.y - s.y;
- if (l = Math.sqrt(x * x + y * y)) {
- l = alpha * (l - distance) / l;
+ if (l = (x * x + y * y)) {
+ l = alpha * ((l = Math.sqrt(l)) - distance) / l;
x *= l;
y *= l;
t.x -= x;
@@ -94,19 +68,19 @@ d3.layout.force = function() {
}
}
- // compute quadtree center of mass
- accumulate(q);
-
// apply gravity forces
var kg = alpha * gravity;
x = size[0] / 2;
y = size[1] / 2;
i = -1; while (++i < n) {
o = nodes[i];
- o.fx += (x - o.x) * kg;
- o.fy += (y - o.y) * kg;
+ o.x += (x - o.x) * kg;
+ o.y += (y - o.y) * kg;
}
+ // compute quadtree center of mass
+ d3_layout_forceAccumulate(q);
+
// apply charge forces
var kc = alpha * charge;
i = -1; while (++i < n) {
@@ -120,14 +94,12 @@ d3.layout.force = function() {
o.x = o.px;
o.y = o.py;
} else {
- x = o.px - (o.px = o.x);
- y = o.py - (o.py = o.y);
- o.x += o.fx - x * drag;
- o.y += o.fy - y * drag;
+ o.x -= (o.px - (o.px = o.x)) * drag;
+ o.y -= (o.py - (o.py = o.y)) * drag;
}
}
- event.tick.dispatch({type: "tick"});
+ event.tick.dispatch({type: "tick", alpha: alpha});
// simulated annealing, basically
return (alpha *= .99) < .005;
@@ -338,3 +310,24 @@ function d3_layout_forceCancel() {
d3.event.stopPropagation();
d3.event.preventDefault();
}
+
+function d3_layout_forceAccumulate(quad) {
+ var cx = 0,
+ cy = 0;
+ quad.count = 0;
+ if (!quad.leaf) {
+ quad.nodes.forEach(function(c) {
+ d3_layout_forceAccumulate(c);
+ quad.count += c.count;
+ cx += c.count * c.cx;
+ cy += c.count * c.cy;
+ });
+ }
+ if (quad.point) {
+ quad.count++;
+ cx += quad.point.x;
+ cy += quad.point.y;
+ }
+ quad.cx = cx / quad.count;
+ quad.cy = cy / quad.count;
+}

0 comments on commit 51b8e02

Please sign in to comment.