d3-force/src/simulation.js
2016-04-29 09:33:17 -07:00

116 lines
2.6 KiB
JavaScript

import {dispatch} from "d3-dispatch";
import {map} from "d3-collection";
import {timer} from "d3-timer";
import {pi} from "./math";
export function x(d) {
return d.x;
}
export function y(d) {
return d.y;
}
var initialRadius = 10,
initialAngle = pi * (3 - Math.sqrt(5));
export default function(nodes) {
var simulation,
iteration = 0,
alphaMin = 0.001,
alphaDecay = -0.02,
drag = 0.6,
forces = map(),
stepper = timer(step),
event = dispatch("tick", "end");
if (nodes == null) nodes = [];
function restart() {
iteration = 0;
stepper.restart(step);
return simulation;
}
function stop() {
stepper.stop();
return simulation;
}
function step() {
var stop = tick();
event.call("tick", simulation);
if (stop) {
stepper.stop();
event.call("end", simulation);
}
}
function tick() {
var alpha = Math.exp(++iteration * alphaDecay);
forces.each(function(force) {
force(alpha);
});
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i];
node.x += node.vx *= drag;
node.y += node.vy *= drag;
}
return alpha < alphaMin;
}
function initializeNodes() {
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i], node.index = i;
if (isNaN(node.x) || isNaN(node.y)) {
var radius = initialRadius * Math.sqrt(i), angle = i * initialAngle;
node.x = radius * Math.cos(angle);
node.y = radius * Math.sin(angle);
}
if (isNaN(node.vx) || isNaN(node.vy)) {
node.vx = node.vy = 0;
}
}
}
function initializeForce(force) {
if (force.initialize) force.initialize(nodes);
return force;
}
initializeNodes();
return simulation = {
restart: restart,
stop: stop,
tick: tick,
nodes: function(_) {
return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes;
},
alphaMin: function(_) {
return arguments.length ? (alphaMin = _, simulation) : alphaMin;
},
alphaDecay: function(_) {
return arguments.length ? (iteration = +_ ? Math.round(iteration * alphaDecay / -_) : 0, alphaDecay = -_, simulation) : -alphaDecay;
},
drag: function(_) {
return arguments.length ? (drag = 1 - _, simulation) : 1 - drag;
},
force: function(name, _) {
return arguments.length > 1 ? ((_ == null ? forces.remove(name) : forces.set(name, initializeForce(_))), simulation) : forces.get(name);
},
on: function(name, _) {
return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);
}
};
}