d3-force/src/manyBody.js
2016-04-06 13:21:23 -07:00

99 lines
2.2 KiB
JavaScript

import {quadtree} from "d3-quadtree";
var nodeQuadtree = quadtree()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
function accumulate(quad, k) {
var cx = 0, cy = 0, charge = 0;
// quad.charge = 0;
if (!quad.leaf) {
for (var i = 0, child; i < 4; ++i) {
child = quad.nodes[i];
if (child == null) continue;
accumulate(child, k);
charge += child.charge;
cx += child.charge * child.cx;
cy += child.charge * child.cy;
}
}
if (quad.data) {
// TODO Jitter internal nodes that are coincident?
// if (!quad.leaf) {
// quad.data.x += Math.random() - 0.5;
// quad.data.y += Math.random() - 0.5;
// }
charge += quad.selfCharge = k;
cx += k * quad.data.x;
cy += k * quad.data.y;
}
quad.cx = cx / charge;
quad.cy = cy / charge;
quad.charge = charge;
}
function custom(nodes, strength, distance2, theta2) {
var target;
function force(alpha) {
var root = nodeQuadtree(nodes);
accumulate(root, alpha * strength);
for (var i = 0, n = nodes.length; i < n; ++i) target = nodes[i], root.visit(apply);
}
function apply(quad, x1, _, x2) {
if (quad.data !== target) {
var dx = quad.cx - target.x,
dy = quad.cy - target.y,
dw = x2 - x1,
dn = dx * dx + dy * dy,
k;
if (dw * dw / theta2 < dn) { // Barnes-Hut criterion
if (dn < distance2) {
k = quad.charge / dn;
target.vx += dx * k;
target.vy += dy * k;
}
return true;
}
if (quad.data && dn && dn < distance2) {
k = quad.selfCharge / dn;
target.vx += dx * k;
target.vy += dy * k;
}
}
return !quad.charge;
}
force.nodes = function(_) {
return custom(_, strength, distance2, theta2);
};
force.strength = function(_) {
return custom(nodes, +_, distance2, theta2);
};
force.distance = function(_) {
return custom(nodes, strength, _ * _, theta2);
};
force.theta = function(_) {
return custom(nodes, strength, distance2, _ * _);
};
return force;
}
export default function(nodes) {
return custom(nodes, -30, Infinity, 0.64);
}