d3-force/src/manyBody.js

119 lines
2.9 KiB
JavaScript
Raw Normal View History

import constant from "./constant";
2016-04-23 00:45:03 +08:00
import {quadtree} from "d3-quadtree";
function x(d) {
return d.x;
}
function y(d) {
return d.y;
}
2016-04-07 04:21:23 +08:00
2016-04-16 23:00:59 +08:00
var tau = 2 * Math.PI;
2016-04-12 02:03:07 +08:00
export default function() {
2016-04-23 00:45:03 +08:00
var nodes,
2016-04-23 00:55:12 +08:00
node,
alpha,
2016-04-21 06:31:37 +08:00
strength = constant(-100),
strengths,
2016-04-12 02:03:07 +08:00
distanceMin2 = 1,
distanceMax2 = Infinity,
2016-04-23 00:55:12 +08:00
theta2 = 0.81;
2016-04-07 04:21:23 +08:00
2016-04-23 00:55:12 +08:00
function force(_) {
var i, n = nodes.length, tree = quadtree(nodes, x, y).visitAfter(accumulate);
for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply);
2016-04-07 04:21:23 +08:00
}
2016-04-09 06:07:00 +08:00
function initialize() {
if (!nodes) return;
var i, n = nodes.length;
2016-04-21 06:31:37 +08:00
strengths = new Array(n);
for (i = 0; i < n; ++i) strengths[i] = +strength(nodes[i], i, nodes);
2016-04-09 06:07:00 +08:00
}
function accumulate(quad) {
2016-04-21 06:31:37 +08:00
var strength = 0, q, c, x, y, i;
2016-04-21 06:31:37 +08:00
// For internal nodes, accumulate forces from child quadrants.
2016-04-21 00:52:14 +08:00
if (quad.length) {
for (x = y = i = 0; i < 4; ++i) {
2016-04-23 00:45:03 +08:00
if ((q = quad[i]) && (c = q.value)) {
2016-04-21 06:31:37 +08:00
strength += c, x += c * q.x, y += c * q.y;
2016-04-21 00:52:14 +08:00
}
2016-04-20 08:18:35 +08:00
}
2016-04-21 06:31:37 +08:00
quad.x = x / strength;
quad.y = y / strength;
2016-04-21 00:52:14 +08:00
}
2016-04-21 06:31:37 +08:00
// For leaf nodes, accumulate forces from coincident quadrants.
2016-04-21 00:52:14 +08:00
else {
q = quad;
2016-04-23 00:55:12 +08:00
q.x = q.data.x;
q.y = q.data.y;
do strength += strengths[q.data.index];
2016-04-21 00:52:14 +08:00
while (q = q.next);
}
2016-04-23 00:45:03 +08:00
quad.value = strength;
}
2016-04-07 04:21:23 +08:00
function apply(quad, x1, _, x2) {
2016-04-23 00:45:03 +08:00
if (!quad.value) return true;
2016-04-12 02:03:07 +08:00
2016-04-23 00:55:12 +08:00
var dx = quad.x - node.x,
dy = quad.y - node.y,
2016-04-21 00:52:14 +08:00
w = x2 - x1,
l = dx * dx + dy * dy;
2016-04-12 02:03:07 +08:00
2016-04-21 00:52:14 +08:00
// Limit forces for very close nodes.
// Randomize direction for exactly-coincident nodes.
if (l < distanceMin2) {
if (!l) l = Math.random() * tau, dx = Math.cos(l), dy = Math.sin(l), l = 1;
l = Math.sqrt(l / distanceMin2), dx /= l, dy /= l, l = distanceMin2;
}
2016-04-07 04:21:23 +08:00
2016-04-21 00:52:14 +08:00
// Apply the Barnes-Hut approximation if possible.
if (w * w / theta2 < l) {
if (l < distanceMax2) {
2016-04-23 00:55:12 +08:00
l = quad.value * alpha / l;
node.vx += dx * l;
node.vy += dy * l;
2016-04-07 04:21:23 +08:00
}
2016-04-21 00:52:14 +08:00
return true;
2016-04-07 04:21:23 +08:00
}
2016-04-21 00:52:14 +08:00
// Otherwise, process points directly.
else if (quad.length || l >= distanceMax2) return;
do if (quad.data !== node) {
2016-04-23 00:55:12 +08:00
w = strengths[quad.data.index] * alpha / l;
node.vx += dx * w;
node.vy += dy * w;
2016-04-21 00:52:14 +08:00
} while (quad = quad.next);
2016-04-07 04:21:23 +08:00
}
2016-04-22 04:44:52 +08:00
force.initialize = function(_) {
2016-04-23 00:45:03 +08:00
nodes = _.nodes(), initialize();
2016-04-07 04:21:23 +08:00
};
2016-04-21 06:31:37 +08:00
force.strength = function(_) {
return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength;
2016-04-07 04:21:23 +08:00
};
2016-04-12 02:03:07 +08:00
force.distanceMin = function(_) {
return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2);
};
force.distanceMax = function(_) {
return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2);
2016-04-07 04:21:23 +08:00
};
force.theta = function(_) {
return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2);
2016-04-07 04:21:23 +08:00
};
return force;
}