{
function x(d) {
return d.x + d.vx;
}
function y(d) {
return d.y + d.vy;
}
function wallCollide(radius) {
var nodes,
radii,
random,
strength = 1,
iterations = 1;
if (typeof radius !== "function") radius = constant(radius == null ? 1 : +radius);
function force() {
var i, n = nodes.length,
tree,
node,
xi,
yi,
ri,
ri2;
for (var k = 0; k < iterations; ++k) {
tree = quadtree(nodes, x, y).visitAfter(prepare);
for (i = 0; i < n; ++i) {
node = nodes[i];
// radius and radius-squared for this node
ri = radii[node.index], ri2 = ri * ri;
xi = node.x + node.vx; // projected position of this node after next tick
yi = node.y + node.vy;
// pre-order traverse the tree calling apply for every node
tree.visit(apply);
}
}
function apply(quad, x0, y0, x1, y1) {
// quad is the node of the quadtree
// x0 etc are the bounds of the node
var data = quad.data, rj = quad.r, r = ri + rj;
// if the quad is a leaf node i.e. contains only one (force-graph) node, data is the data for that node
// otherwise data is undefined
// rj is the radius of the largest node in this quad
if (data) {
// this node is a leaf node
// we only want to evaluate each node pair once, so compare indices and evaluate one way round
if (data.index > node.index) { // i.e. 'data' node has not yet been evaluated at the force() level. Nor has 'node' because that's the one we're doing now.
// Do they both have their original positions and velocities? not necessarily, if either had been involved in multiple collisions they may already have been adjusted. How does this work?
var x = xi - data.x - data.vx, // difference between projected position of this node after next tick and back-projected position of data node on the last tick? But why?
y = yi - data.y - data.vy,
l = x * x + y * y; // square euclidean distance
if (l < r * r) { // distance at projected collision point (??) is less than sum of radii
if (x === 0) x = jiggle(random), l += x * x; // if the x component of l is zero, make it nonzero
if (y === 0) y = jiggle(random), l += y * y; // likewise y
// l = (r - (l = Math.sqrt(l))) / l * strength;
//expanded:
l = Math.sqrt(l) // euclidean distance
l = ((r - l) / l); // idk we are still calling this l but this is the amount of overlap as a percentage of the distance between the two node centers at collision. Not clear why the centre distance is a better denominator than the sum of radii? => So the force can get very large when the nodes are very close, rather than being capped at strength*1
l = l * strength; // apply strength coefficient => overall force coefficient
node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj)); //ughhh
// xl = x*l // = x above; x component of overlap times force coefficient
// rj2 = rj*rj // = rj above; square of data node radius
// r_new = rj2/(ri2+rj2) // mass fraction of data node
// node.vx += xl * r_new // x force times mass fraction of other node
node.vy += (y *= l) * r; // y force times mass frac of other node
data.vx -= x * (r = 1 - r); // x force times mass frac of this node, opposite direction
data.vy -= y * r; // y force times mass frac of this node, opposite direction
// note that only velocities are changed, not positions
// the default strength of 1 will produce what? Something like elastic collisions?
}
}
return;
}
// if the
return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;
}
}
function prepare(quad) {
// if it's a leaf node, assign its r property to the radius of the contained node
if (quad.data) return quad.r = radii[quad.data.index];
// otherwise, loop over its child nodes and assign its r property to the largest r of those
for (var i = quad.r = 0; i < 4; ++i) {
if (quad[i] && quad[i].r > quad.r) {
quad.r = quad[i].r;
}
}
}
function initialize() {
if (!nodes) return;
var i, n = nodes.length, node;
radii = new Array(n);
for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes);
}
force.initialize = function(_nodes, _random) {
nodes = _nodes;
random = _random;
initialize();
};
force.iterations = function(_) {
return arguments.length ? (iterations = +_, force) : iterations;
};
force.strength = function(_) {
return arguments.length ? (strength = +_, force) : strength;
};
force.radius = function(_) {
return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), initialize(), force) : radius;
};
return force;
}
}