d3-force/src/simulation.js

132 lines
3.3 KiB
JavaScript
Raw Normal View History

2016-04-22 04:27:10 +08:00
import {quadtree} from "d3-quadtree";
2016-04-06 04:50:36 +08:00
import {dispatch as newDispatch} from "d3-dispatch";
import {map as newMap} from "d3-collection";
import {timer as newTimer} from "d3-timer";
2016-04-06 04:50:36 +08:00
export default function(nodes) {
var simulation,
iteration = 0,
alpha = 1,
2016-04-07 04:51:59 +08:00
alphaMin = 0.0001,
alphaDecay = -0.02,
velocityDecay = 0.5,
force = newMap(),
2016-04-22 04:27:10 +08:00
tree,
2016-04-06 04:50:36 +08:00
timer = newTimer(tick),
dispatch = newDispatch("start", "tick", "end");
2016-04-06 04:50:36 +08:00
2016-04-07 04:21:23 +08:00
function start() {
2016-04-07 04:51:59 +08:00
if (iteration < Infinity) {
iteration = 0, alpha = 1;
} else {
iteration = 0, alpha = 1;
dispatch.call("start", simulation);
2016-04-07 04:21:23 +08:00
timer.restart(tick);
}
return simulation;
2016-04-07 04:21:23 +08:00
}
function stop() {
2016-04-07 04:51:59 +08:00
if (iteration < Infinity) {
iteration = Infinity, alpha = 0;
dispatch.call("end", simulation);
2016-04-07 04:21:23 +08:00
timer.stop();
2016-04-06 04:50:36 +08:00
}
return simulation;
2016-04-06 04:50:36 +08:00
}
function tick() {
alpha = Math.exp(++iteration * alphaDecay);
2016-04-07 04:51:59 +08:00
if (!(alpha > alphaMin)) return stop();
force.each(apply);
2016-04-06 04:50:36 +08:00
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i];
2016-04-07 04:21:23 +08:00
node.x += node.vx *= velocityDecay;
node.y += node.vy *= velocityDecay;
2016-04-06 04:50:36 +08:00
}
2016-04-22 04:27:10 +08:00
tree = null;
dispatch.call("tick", simulation);
2016-04-06 04:50:36 +08:00
}
function initializeNodes() {
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i], node.index = i;
if (isNaN(node.x)) node.x = Math.random() * 100 - 50;
if (isNaN(node.y)) node.y = Math.random() * 100 - 50;
if (isNaN(node.vx)) node.vx = 0;
if (isNaN(node.vy)) node.vy = 0;
}
}
function initializeForce(force) {
2016-04-22 04:44:52 +08:00
if (force.initialize) force.initialize(simulation);
2016-04-21 03:53:35 +08:00
return force;
}
function apply(force) {
2016-04-22 04:44:52 +08:00
force(alpha);
}
initializeNodes();
return simulation = {
start: start,
stop: stop,
tick: tick,
2016-04-22 06:52:14 +08:00
2016-04-06 04:50:36 +08:00
nodes: function(_) {
return arguments.length ? (nodes = _, initializeNodes(), force.each(initializeForce), simulation) : nodes;
2016-04-06 04:50:36 +08:00
},
2016-04-22 06:52:14 +08:00
2016-04-22 04:27:10 +08:00
quadtree: function() {
if (tree) return tree;
2016-04-22 06:52:14 +08:00
var i,
n = nodes.length,
node,
x0 = Infinity,
y0 = Infinity,
x1 = -Infinity,
y1 = -Infinity;
2016-04-22 04:27:10 +08:00
for (i = 0; i < n; ++i) {
node = nodes[i];
if (node.x < x0) x0 = node.x;
if (node.x > x1) x1 = node.x;
if (node.y < y0) y0 = node.y;
if (node.y > y1) y1 = node.y;
}
2016-04-22 06:52:14 +08:00
2016-04-22 04:27:10 +08:00
tree = quadtree().cover(x0, y0).cover(x1, y1);
2016-04-22 06:52:14 +08:00
2016-04-22 04:27:10 +08:00
for (i = 0; i < n; ++i) {
2016-04-22 06:52:14 +08:00
node = nodes[i], tree.add(node.x, node.y).index = i;
2016-04-22 04:27:10 +08:00
}
2016-04-22 06:52:14 +08:00
2016-04-22 04:27:10 +08:00
return tree;
},
2016-04-22 06:52:14 +08:00
2016-04-07 04:51:59 +08:00
alphaMin: function(_) {
return arguments.length ? (alphaMin = _, simulation) : alphaMin;
2016-04-06 04:50:36 +08:00
},
2016-04-22 06:52:14 +08:00
2016-04-06 04:50:36 +08:00
alphaDecay: function(_) {
return arguments.length ? (alphaDecay = -_, iteration = Math.round(Math.log(alpha) / alphaDecay), simulation) : -alphaDecay;
2016-04-06 04:50:36 +08:00
},
2016-04-22 06:52:14 +08:00
2016-04-07 04:21:23 +08:00
friction: function(_) {
return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay;
},
2016-04-22 06:52:14 +08:00
force: function(name, _) {
return arguments.length > 1 ? ((_ == null ? force.remove(name) : force.set(name, initializeForce(_))), simulation) : force.get(name);
2016-04-06 04:50:36 +08:00
},
2016-04-22 06:52:14 +08:00
2016-04-06 04:50:36 +08:00
on: function(name, _) {
return arguments.length > 1 ? (dispatch.on(name, _), simulation) : dispatch.on(name);
2016-04-06 04:50:36 +08:00
}
};
}