d3-force/src/simulation.js

136 lines
3.1 KiB
JavaScript
Raw Normal View History

2016-04-23 00:45:03 +08:00
import {dispatch} from "d3-dispatch";
import {map} from "d3-collection";
import {timer} from "d3-timer";
export function x(d) {
return d.x;
}
export function y(d) {
return d.y;
}
var initialRadius = 10,
initialAngle = Math.PI * (3 - Math.sqrt(5));
2016-04-06 04:50:36 +08:00
export default function(nodes) {
var simulation,
alpha = 1,
2016-04-28 07:35:41 +08:00
alphaMin = 0.001,
alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),
alphaTarget = 0,
2016-04-28 02:20:10 +08:00
drag = 0.6,
2016-04-27 00:34:32 +08:00
forces = map(),
fixes = {},
stepper = timer(step),
event = dispatch("tick", "end");
2016-04-06 04:50:36 +08:00
2016-04-26 08:04:51 +08:00
if (nodes == null) nodes = [];
function step() {
tick();
event.call("tick", simulation);
if (alpha < alphaMin) {
stepper.stop();
event.call("end", simulation);
}
}
function tick() {
var i, n = nodes.length, node, fix;
alpha += (alphaTarget - alpha) * alphaDecay;
2016-04-27 00:34:32 +08:00
forces.each(function(force) {
force(alpha);
});
2016-04-06 04:50:36 +08:00
2016-05-04 22:31:42 +08:00
for (i = 0; i < n; ++i) {
2016-04-06 04:50:36 +08:00
node = nodes[i];
2016-04-26 01:42:19 +08:00
node.x += node.vx *= drag;
node.y += node.vy *= drag;
2016-04-06 04:50:36 +08:00
}
for (i in fixes) {
fix = fixes[i], node = nodes[i];
node.x = fix.x;
node.y = fix.y;
node.vx =
node.vy = 0;
}
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) || 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) {
2016-04-26 12:47:24 +08:00
if (force.initialize) force.initialize(nodes);
2016-04-21 03:53:35 +08:00
return force;
}
2016-04-26 08:04:51 +08:00
initializeNodes();
return simulation = {
tick: tick,
2016-04-22 06:52:14 +08:00
restart: function() {
return stepper.restart(step), simulation;
},
stop: function() {
return stepper.stop(), simulation;
},
2016-04-06 04:50:36 +08:00
nodes: function(_) {
2016-04-27 00:34:32 +08:00
return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes;
2016-04-06 04:50:36 +08:00
},
2016-04-22 06:52:14 +08:00
alpha: function(_) {
return arguments.length ? (alpha = +_, simulation) : alpha;
},
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 = +_, simulation) : +alphaDecay;
},
alphaTarget: function(_) {
return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget;
2016-04-06 04:50:36 +08:00
},
2016-04-22 06:52:14 +08:00
2016-04-26 01:42:19 +08:00
drag: function(_) {
return arguments.length ? (drag = 1 - _, simulation) : 1 - drag;
},
2016-04-22 06:52:14 +08:00
force: function(name, _) {
2016-04-27 00:34:32 +08:00
return arguments.length > 1 ? ((_ == null ? forces.remove(name) : forces.set(name, initializeForce(_))), simulation) : forces.get(name);
2016-04-06 04:50:36 +08:00
},
2016-04-22 06:52:14 +08:00
fix: function(node, x, y) {
return fixes[node.index] = {x: x == null ? node.x : +x, y: y == null ? node.y : +y}, simulation;
},
unfix: function(node) {
return delete fixes[node.index], simulation;
},
2016-04-06 04:50:36 +08:00
on: function(name, _) {
2016-04-23 00:45:03 +08:00
return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);
2016-04-06 04:50:36 +08:00
}
};
}