力模拟
¥Force simulations
力模拟实现了一个 Verlet 速度 数值积分器,用于模拟粒子(节点)上的物理力。模拟假设每一步的单位时间步长为常数 Δt = 1,所有粒子的单位质量为常数 m = 1。因此,作用于粒子的力 F 相当于时间间隔 Δt 内的恒定加速度 a,可以通过将其添加到粒子的速度上,然后将速度添加到粒子的位置来模拟。
¥A force simulation implements a velocity Verlet numerical integrator for simulating physical forces on particles (nodes). The simulation assumes a constant unit time step Δt = 1 for each step and a constant unit mass m = 1 for all particles. As a result, a force F acting on a particle is equivalent to a constant acceleration a over the time interval Δt, and can be simulated simply by adding to the particle’s velocity, which is then added to the particle’s position.
forceSimulation(nodes)
源代码 · 使用指定的 nodes 数组创建一个新的模拟,但不包含 forces。如果未指定 nodes,则默认为空数组。
¥Source · Creates a new simulation with the specified array of nodes and no forces. If nodes is not specified, it defaults to the empty array.
警告
此函数不纯;它会改变传入的节点。请参阅 simulation.nodes。
¥This function is impure; it mutates the passed-in nodes. See simulation.nodes.
const simulation = d3.forceSimulation(nodes);
模拟器 starts 自动;在模拟运行时使用 simulation.on 监听 tick 事件。如果你希望手动运行模拟,请调用 simulation.stop,然后根据需要调用 simulation.tick。
¥The simulator starts automatically; use simulation.on to listen for tick events as the simulation runs. If you wish to run the simulation manually instead, call simulation.stop, and then call simulation.tick as desired.
simulation.restart() {#simulation_restart}
源代码 · 重启模拟的内部计时器并返回模拟结果。与 simulation.alphaTarget 或 simulation.alpha 结合使用时,此方法可用于在交互过程中“重新加热”模拟,例如拖动节点时,或在使用 simulation.stop 暂时暂停模拟后恢复模拟。
¥Source · Restarts the simulation’s internal timer and returns the simulation. In conjunction with simulation.alphaTarget or simulation.alpha, this method can be used to “reheat” the simulation during interaction, such as when dragging a node, or to resume the simulation after temporarily pausing it with simulation.stop.
simulation.stop() {#simulation_stop}
源代码 · 如果模拟的内部计时器正在运行,则停止它并返回模拟。如果计时器已停止,则此方法不执行任何操作。此方法对于手动运行模拟很有用;参见 simulation.tick。
¥Source · Stops the simulation’s internal timer, if it is running, and returns the simulation. If the timer is already stopped, this method does nothing. This method is useful for running the simulation manually; see simulation.tick.
simulation.tick(iterations) {#simulation_tick}
源代码 · 手动按指定的迭代次数进行模拟,并返回模拟结果。如果未指定 iterations,则默认为 1(单步)。
¥Source · Manually steps the simulation by the specified number of iterations, and returns the simulation. If iterations is not specified, it defaults to 1 (single step).
对于每次迭代,它会将当前的 alpha 递增 (alphaTarget - alpha) × alphaDecay;然后调用每个已注册的 force 函数,并传递新的 alpha 值;然后将每个 node 的速度减少速度 × velocityDecay;最后,将每个节点的位置按速度递增。
¥For each iteration, it increments the current alpha by (alphaTarget - alpha) × alphaDecay; then invokes each registered force, passing the new alpha; then decrements each node’s velocity by velocity × velocityDecay; lastly increments each node’s position by velocity.
此方法不会调度 events;仅当模拟在 creation 上自动启动或调用 simulation.restart 时,内部计时器才会调度事件。模拟开始时的自然刻数为 ⌈log(alphaMin) / log(1 - alphaDecay)⌉;默认情况下,此值为 300。
¥This method does not dispatch events; events are only dispatched by the internal timer when the simulation is started automatically upon creation or by calling simulation.restart. The natural number of ticks when the simulation is started is ⌈log(alphaMin) / log(1 - alphaDecay)⌉; by default, this is 300.
此方法可与 simulation.stop 结合使用,以计算 静态力布局。对于大型图表,应使用 在 Web Worker 中 计算静态布局,以避免冻结用户界面。
¥This method can be used in conjunction with simulation.stop to compute a static force layout. For large graphs, static layouts should be computed in a web worker to avoid freezing the user interface.
simulation.nodes(nodes) {#simulation_nodes}
源代码 · 如果指定了 nodes,则将模拟的节点设置为指定的对象数组,并在必要时初始化它们的位置和速度,然后 re-initializes 任何绑定 forces;返回模拟。如果未指定 nodes,则返回模拟的节点数组,该数组由 构造函数 指定。
¥Source · If nodes is specified, sets the simulation’s nodes to the specified array of objects, initializing their positions and velocities if necessary, and then re-initializes any bound forces; returns the simulation. If nodes is not specified, returns the simulation’s array of nodes as specified to the constructor.
警告
此函数不纯;它会改变传入的节点,为其分配索引 node.index、位置 node.x 和 node.y,以及速度 node.vx 和 node.vy。模拟运行时,位置和速度会由 simulation.tick 进一步更新。
¥This function is impure; it mutates the passed-in nodes to assign the index node.index, the position node.x & node.y, and the velocity node.vx & node.vy. The position and velocity are further updated as the simulation runs by simulation.tick.
每个节点都必须是一个对象。模拟会分配以下属性:
¥Each node must be an object. The following properties are assigned by the simulation:
index
- 节点在节点中的索引(从零开始)¥
index
- the node’s zero-based index into nodesx
- 节点当前的 x 位置¥
x
- the node’s current x-positiony
- 节点当前的 y 位置¥
y
- the node’s current y-positionvx
- 节点当前的 x 速度¥
vx
- the node’s current x-velocityvy
- 节点当前的 y 速度¥
vy
- the node’s current y-velocity
位置 ⟨x,y⟩ 和速度 ⟨vx,vy⟩ 随后可能会被 forces 和模拟修改。如果 vx 或 vy 为 NaN,则速度初始化为 ⟨0,0⟩。如果 x 或 y 为 NaN,则位置初始化为 叶序排列,选择 叶序排列 是为了确保确定性的均匀分布。
¥The position ⟨x,y⟩ and velocity ⟨vx,vy⟩ may be subsequently modified by forces and by the simulation. If either vx or vy is NaN, the velocity is initialized to ⟨0,0⟩. If either x or y is NaN, the position is initialized in a phyllotaxis arrangement, so chosen to ensure a deterministic, uniform distribution.
要将节点固定在给定位置,你可以指定两个附加属性:
¥To fix a node in a given position, you may specify two additional properties:
fx
- 节点的固定 x 位置¥
fx
- the node’s fixed x-positionfy
- 节点的固定 y 位置¥
fy
- the node’s fixed y-position
在每个 tick 结束时,施加任何力后,具有已定义 node.fx 的节点的 node.x 会重置为该值,并将 node.vx 设置为零;同样,定义了 node.fy 的节点会将 node.y 重置为该值,并将 node.vy 设置为零。要取消之前已修复的节点,请将 node.fx 和 node.fy 设置为 null,或删除这些属性。
¥At the end of each tick, after the application of any forces, a node with a defined node.fx has node.x reset to this value and node.vx set to zero; likewise, a node with a defined node.fy has node.y reset to this value and node.vy set to zero. To unfix a node that was previously fixed, set node.fx and node.fy to null, or delete these properties.
如果指定的节点数组被修改(例如,在模拟中添加或移除节点时),则必须使用新的(或更改后的)数组再次调用此方法,以通知模拟和绑定的强制执行;模拟不会对指定数组进行防御性复制。
¥If the specified array of nodes is modified, such as when nodes are added to or removed from the simulation, this method must be called again with the new (or changed) array to notify the simulation and bound forces of the change; the simulation does not make a defensive copy of the specified array.
simulation.alpha(alpha) {#simulation_alpha}
源代码 · alpha 值与 模拟退火 中的温度大致类似。随着模拟“冷却”,它会随着时间的推移而减小。当 alpha 达到 alphaMin 时,模拟停止;参见 simulation.restart。
¥Source · alpha is roughly analogous to temperature in simulated annealing. It decreases over time as the simulation “cools down”. When alpha reaches alphaMin, the simulation stops; see simulation.restart.
如果指定了 alpha,则将当前 alpha 设置为 [0,1] 范围内的指定数字,并返回本次模拟。如果未指定 alpha,则返回当前 alpha 值,默认为 1。
¥If alpha is specified, sets the current alpha to the specified number in the range [0,1] and returns this simulation. If alpha is not specified, returns the current alpha value, which defaults to 1.
simulation.alphaMin(min) {#simulation_alphaMin}
源代码 · 如果指定了 min,则将最小 alpha 值设置为 [0,1] 范围内的指定数字,并返回此模拟。如果未指定 min,则返回当前最小 alpha 值,默认为 0.001。当当前 alpha 小于最小 alpha 值时,模拟的内部计时器将停止。默认的 alpha 衰减率 约为 0.0228,相当于 300 次迭代。
¥Source · If min is specified, sets the minimum alpha to the specified number in the range [0,1] and returns this simulation. If min is not specified, returns the current minimum alpha value, which defaults to 0.001. The simulation’s internal timer stops when the current alpha is less than the minimum alpha. The default alpha decay rate of ~0.0228 corresponds to 300 iterations.
simulation.alphaDecay(decay) {#simulation_alphaDecay}
源代码 · 如果指定了 decay,则将 alpha 衰减率设置为 [0,1] 范围内的指定数字并返回模拟结果。如果未指定 decay,则返回当前的 alpha 衰减率,默认为 0.0228… = 1 - pow(0.001, 1 / 300),其中 0.001 是默认的 最小 alpha 值。
¥Source · If decay is specified, sets the alpha decay rate to the specified number in the range [0,1] and returns this simulation. If decay is not specified, returns the current alpha decay rate, which defaults to 0.0228… = 1 - pow(0.001, 1 / 300) where 0.001 is the default minimum alpha.
alpha 衰减率决定了当前 alpha 值向期望 目标透明度 方向插值的速度;由于默认目标 alpha 值为零,因此默认情况下,这将控制模拟的冷却速度。更高的衰减率会使模拟更快地稳定下来,但有陷入局部最小值的风险;较低的值会导致模拟运行时间更长,但通常会收敛到更好的布局。要使模拟以当前 alpha 值永久运行,请将衰减率设置为零;或者,将 目标透明度 设置为大于 最小 alpha 值。
¥The alpha decay rate determines how quickly the current alpha interpolates towards the desired target alpha; since the default target alpha is zero, by default this controls how quickly the simulation cools. Higher decay rates cause the simulation to stabilize more quickly, but risk getting stuck in a local minimum; lower values cause the simulation to take longer to run, but typically converge on a better layout. To have the simulation run forever at the current alpha, set the decay rate to zero; alternatively, set a target alpha greater than the minimum alpha.
simulation.alphaTarget(target) {#simulation_alphaTarget}
源代码 · 如果指定了 target,则将当前目标 alpha 设置为 [0,1] 范围内的指定数字,并返回此模拟。如果未指定目标,则返回当前目标 alpha 值,默认为 0。
¥Source · If target is specified, sets the current target alpha to the specified number in the range [0,1] and returns this simulation. If target is not specified, returns the current target alpha value, which defaults to 0.
simulation.velocityDecay(decay) {#simulation_velocityDecay}
源代码 · 如果指定了 decay,则将速度衰减因子设置为 [0,1] 范围内的指定数字并返回模拟结果。如果未指定 decay,则返回当前的速度衰减因子,默认为 0.4。衰减因子类似于大气摩擦;在 tick 期间施加任何力后,每个节点的速度都会乘以 1。 - 衰减。与降低 alpha 衰减率 值一样,较小的速度衰减可能收敛到更优解,但存在数值不稳定和振荡的风险。
¥Source · If decay is specified, sets the velocity decay factor to the specified number in the range [0,1] and returns this simulation. If decay is not specified, returns the current velocity decay factor, which defaults to 0.4. The decay factor is akin to atmospheric friction; after the application of any forces during a tick, each node’s velocity is multiplied by 1 - decay. As with lowering the alpha decay rate, less velocity decay may converge on a better solution, but risks numerical instabilities and oscillation.
simulation.force(name, force) {#simulation_force}
源代码 · 如果指定了 force,则将指定名称的 force 赋值给此模拟。如果未指定 force,则返回具有指定名称的 force,如果不存在这样的 force,则返回 undefined。(默认情况下,新的模拟没有力。)例如,要创建一个新的模拟来布局图形,你可以这样写:
¥Source · If force is specified, assigns the force for the specified name and returns this simulation. If force is not specified, returns the force with the specified name, or undefined if there is no such force. (By default, new simulations have no forces.) For example, to create a new simulation to layout a graph, you might say:
const simulation = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody())
.force("link", d3.forceLink(links))
.force("center", d3.forceCenter());
要删除具有给定名称的力,请将 null 作为力传递。例如,移除电荷力:
¥To remove the force with the given name, pass null as the force. For example, to remove the charge force:
simulation.force("charge", null);
simulation.find(x, y, radius) {#simulation_find}
源代码 · 返回距离指定搜索半径且位置 ⟨x,y⟩ 最近的节点。如果未指定半径,则默认为无穷大。如果搜索区域内没有节点,则返回 undefined。
¥Source · Returns the node closest to the position ⟨x,y⟩ with the given search radius. If radius is not specified, it defaults to infinity. If there is no node within the search area, returns undefined.
simulation.randomSource(source) {#simulation_randomSource}
源代码)
¥Source)
如果指定了 source,则设置用于生成随机数的函数;这应该是一个返回介于 0(含)和 1(不含)之间的数字的函数。如果未指定 source,则返回此模拟的当前随机源,默认为固定种子 线性同余生成器。另请参阅 random.source。
¥If source is specified, sets the function used to generate random numbers; this should be a function that returns a number between 0 (inclusive) and 1 (exclusive). If source is not specified, returns this simulation’s current random source which defaults to a fixed-seed linear congruential generator. See also random.source.
simulation.on(typenames, listener) {#simulation_on}
源代码 · 如果指定了 listener,则为指定的 typenames 设置事件监听器并返回此模拟。如果已为相同类型和名称注册了事件监听器,则在添加新监听器之前,会先移除现有监听器。如果 listener 为 null,则移除指定 typenames 的当前事件监听器(如果有)。如果未指定 listener,则返回第一个当前已分配的、与指定 typenames 匹配的监听器(如果有)。当调度指定事件时,将使用 this
上下文作为模拟调用每个监听器。
¥Source · If listener is specified, sets the event listener for the specified typenames and returns this simulation. If an event listener was already registered for the same type and name, the existing listener is removed before the new listener is added. If listener is null, removes the current event listeners for the specified typenames, if any. If listener is not specified, returns the first currently-assigned listener matching the specified typenames, if any. When a specified event is dispatched, each listener will be invoked with the this
context as the simulation.
typenames 是一个字符串,包含一个或多个用空格分隔的 typename。每个类型名称都是一种类型,后面可以跟一个句点 (.
) 和一个名称,例如 tick.foo
和 tick.bar
;该名称允许为同一类型注册多个监听器。类型必须是以下之一:
¥The typenames is a string containing one or more typename separated by whitespace. Each typename is a type, optionally followed by a period (.
) and a name, such as tick.foo
and tick.bar
; the name allows multiple listeners to be registered for the same type. The type must be one of the following:
tick
- 模拟内部计时器每次计时后。¥
tick
- after each tick of the simulation’s internal timer.end
- 模拟计时器在 alpha < alphaMin 时停止后。¥
end
- after the simulation’s timer stops when alpha < alphaMin.
请注意,手动调用 simulation.tick 时不会分派 tick 事件;事件仅由内部计时器调度,用于模拟的交互式渲染。要影响模拟,请注册 forces,而不是在刻度事件监听器中修改节点的位置或速度。
¥Note that tick events are not dispatched when simulation.tick is called manually; events are only dispatched by the internal timer and are intended for interactive rendering of the simulation. To affect the simulation, register forces instead of modifying nodes’ positions or velocities inside a tick event listener.
详情请参阅 dispatch.on。
¥See dispatch.on for details.
自定义力
¥Custom forces
力是修改节点位置或速度的函数。它可以模拟物理力,例如电荷或重力,也可以解决几何约束,例如将节点保持在边界框内或使链接节点保持固定距离。例如,这是一个将节点移向原点的力:
¥A force is a function that modifies nodes’ positions or velocities. It can simulate a physical force such as electrical charge or gravity, or it can resolve a geometric constraint such as keeping nodes within a bounding box or keeping linked nodes a fixed distance apart. For example, here is a force that moves nodes towards the origin:
function force(alpha) {
for (let i = 0, n = nodes.length, node, k = alpha * 0.1; i < n; ++i) {
node = nodes[i];
node.vx -= node.x * k;
node.vy -= node.y * k;
}
}
力场通常读取节点的当前位置⟨x,y⟩,然后改变节点的速度⟨vx,vy⟩。力场还可以“预知”节点的预期下一个位置,⟨x + vx,y + vy⟩;这对于通过 迭代松弛 解决几何约束是必要的。力场也可以直接修改位置,这有时有助于避免在模拟中添加能量,例如在视口中重新定位模拟时。
¥Forces typically read the node’s current position ⟨x,y⟩ and then mutate the node’s velocity ⟨vx,vy⟩. Forces may also “peek ahead” to the anticipated next position of the node, ⟨x + vx,y + vy⟩; this is necessary for resolving geometric constraints through iterative relaxation. Forces may also modify the position directly, which is sometimes useful to avoid adding energy to the simulation, such as when recentering the simulation in the viewport.
force(alpha) {#_force}
应用此力,可选择观察指定的 alpha 值。Typically, the force is applied to the array of nodes previously passed to force.initialize, however, some forces may apply to a subset of nodes, or behave differently.例如,forceLink 适用于每个链接的源和目标。
¥Applies this force, optionally observing the specified alpha. Typically, the force is applied to the array of nodes previously passed to force.initialize, however, some forces may apply to a subset of nodes, or behave differently. For example, forceLink applies to the source and target of each link.
force.initialize(nodes) {#force_initialize}
向此力提供节点数组和随机源。当通过 simulation.force 将力绑定到模拟,以及模拟的节点通过 simulation.nodes 发生变化时,会调用此方法。力可以在初始化期间执行必要的工作,例如评估每个节点的参数,以避免在每次施加力时重复执行工作。
¥Supplies the array of nodes and random source to this force. This method is called when a force is bound to a simulation via simulation.force and when the simulation’s nodes change via simulation.nodes. A force may perform necessary work during initialization, such as evaluating per-node parameters, to avoid repeatedly performing work during each application of the force.