99 lines
2.2 KiB
JavaScript
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);
|
|
}
|