Skip to content

力模拟

🌐 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)

来源 · 使用指定的 节点 数组创建新的模拟,并且没有 。如果未指定 节点,则默认为空数组。

WARNING

此函数是非纯的;它会修改传入的 nodes。见 simulation.nodes

js
const simulation = d3.forceSimulation(nodes);

模拟器会自动启动;在模拟运行时使用simulation.on监听周期事件。如果你希望手动运行模拟,请调用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.alphaTargetsimulation.alpha,此方法可在交互过程中“重新加热”模拟,例如在拖动节点时,或在使用 simulation.stop 暂时暂停模拟后恢复模拟。

simulation.stop()

来源 · 停止模拟的内部计时器(如果它正在运行),并返回模拟。如果计时器已经停止,该方法不会执行任何操作。此方法对于手动运行模拟非常有用;请参见 simulation.tick

simulation.tick(iterations)

来源 · 按指定的迭代次数手动步进模拟,并返回模拟结果。如果未指定迭代次数,默认值为1(单步)。

对于每次迭代,它将当前的alpha增加(alphaTarget - alpha) × alphaDecay;然后调用每个注册的force,传入新的alpha;然后将每个node的速度减少velocity × velocityDecay;最后将每个节点的位置增加velocity

🌐 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.

此方法不会分发事件;事件仅在模拟在创建时自动启动或通过调用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)

· 如果指定了 nodes,将模拟的节点设置为指定的对象数组,在必要时初始化它们的位置和速度,然后重新初始化任何绑定的;返回该模拟。如果未指定 nodes,则返回模拟的节点数组,如构造函数中所指定的。

WARNING

这个函数是非纯的;它会修改传入的 nodes,给每个节点分配索引 node.index、位置 node.x 和 node.y,以及速度 node.vx 和 node.vy。随着模拟的运行,位置和速度会通过 simulation.tick 进一步更新。

每个节点必须是一个对象。以下属性由模拟分配:

🌐 Each node must be an object. The following properties are assigned by the simulation:

  • index - 节点在 nodes 中的零起始索引
  • x - 节点当前的x位置
  • y - 节点当前的y位置
  • vx - 该节点当前的x速度
  • vy - 该节点当前的y速度

位置 ⟨x,y⟩ 和速度 ⟨vx,vy⟩ 可能随后会被 和模拟修改。如果 vxvy 为 NaN,速度将初始化为 ⟨0,0⟩。如果 xy 为 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 位置
  • fy - 节点的固定y位置

在每个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)

来源 · alpha 大致相当于 模拟退火 中的温度。它会随着时间的推移而下降,正如模拟“冷却”一样。当 alpha 达到 alphaMin 时,模拟停止;参见 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)

来源 · 如果指定了 min,则将最小 alpha 设置为指定范围 [0,1] 内的数字,并返回此模拟。如果未指定 min,则返回当前最小 alpha 值,默认值为 0.001。当当前 alpha 小于最小 alpha 时,模拟的内部计时器会停止。默认的 alpha 衰减率 约为 0.0228,对应 300 次迭代。

simulation.alphaDecay(decay)

来源 · 如果指定了 decay,则将 alpha 衰减率设置为指定范围 [0,1] 内的数值,并返回此模拟。如果未指定 decay,则返回当前 alpha 衰减率,其默认值为 0.0228… = 1 - pow(0.001, 1 / 300),其中 0.001 是默认的 最小 alpha

α衰减速率决定当前 α 向所需 目标 α 插值的速度;由于默认目标 α 为零,因此默认情况下它控制模拟冷却的速度。较高的衰减速率会使模拟更快稳定,但有可能陷入局部最小值;较低的值会使模拟运行时间更长,但通常会收敛到更好的布局。要让模拟在当前 α 下永久运行,将 decay 速率设为零;或者,将 目标 α 设置为大于 最小 α 的值。

🌐 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)

来源 · 如果指定了 target,将当前目标 alpha 设置为指定范围 [0,1] 内的数值,并返回此模拟。如果未指定 target,则返回当前目标 alpha 值,默认值为 0。

simulation.velocityDecay(decay)

来源 · 如果指定了 decay,则将速度衰减系数设置为指定范围 [0,1] 内的数值,并返回此模拟。如果未指定 decay,则返回当前的速度衰减系数,默认为 0.4。衰减系数类似于大气摩擦力;在每次 tick 应用任何力之后,每个节点的速度都会乘以 1 - decay。与降低 alpha 衰减率 类似,较小的速度衰减可能会收敛到更好的解,但存在数值不稳定和振荡的风险。

simulation.force(name, force)

来源 · 如果指定了 force,则为指定的 name 分配 force,并返回此模拟。如果未指定 force,则返回具有指定名称的 force,如果不存在这样的 force,则返回 undefined。(默认情况下,新的模拟没有 forces。)例如,要创建一个用于布局图的新模拟,你可以这样说:

js
const simulation = d3.forceSimulation(nodes)
    .force("charge", d3.forceManyBody())
    .force("link", d3.forceLink(links))
    .force("center", d3.forceCenter());

要移除给定 name 的力,将 force 设为 null。例如,要移除电荷力:

🌐 To remove the force with the given name, pass null as the force. For example, to remove the charge force:

js
simulation.force("charge", null);

simulation.find(x, y, radius)

来源 · 返回距离位置 ⟨x,y⟩ 最近的、具有给定搜索 radius 的节点。如果未指定 radius,默认为无限大。如果搜索区域内没有节点,返回 undefined。

simulation.randomSource(source)

来源 · 如果指定了 source,则设置用于生成随机数的函数;该函数应返回一个介于 0(包含)和 1(不包含)之间的数字。如果未指定 source,则返回此模拟的当前随机源,默认是一个固定种子的线性同余生成器。另请参见 random.source

simulation.on(typenames, listener)

来源 · 如果指定了 listener,则为指定的 typenames 设置事件 listener 并返回此模拟。如果相同类型和名称的事件监听器已注册,则在添加新监听器之前会移除现有监听器。如果 listener 为 null,则移除指定 typenames 的当前事件监听器(如果有)。如果未指定 listener,则返回与指定 typenames 匹配的第一个当前分配的监听器(如果有)。当分发指定事件时,每个 listener 将以 this 上下文调用该模拟。

typenames 是一个包含一个或多个 typename 的字符串,它们由空格分隔。每个 typename 是一个 type,可选地后跟一个句点(.)和一个 name,例如 tick.footick.bar;该名称允许为相同的 type 注册多个监听器。type 必须是以下之一:

🌐 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 - 模拟内部计时器每次计时后。
  • end - 当模拟的计时器在 alpha < alphaMin 时停止。

请注意,当手动调用 simulation.tick 时,tick 事件不会被派发;事件仅由内部定时器派发,并用于模拟的交互式渲染。要影响模拟,请注册 ,而不是在 tick 事件监听器中修改节点的位置或速度。

🌐 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:

js
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⟩;这对于通过[迭代松弛](https://en.wikipedia.org/wiki/Relaxation_\(iterative_method\)解决几何约束是必要的。力也可以直接修改位置,这在某些情况下很有用,例如在不向模拟中添加能量的情况下重新将模拟居中于视口时。

🌐 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)

施加此力,可选择地观察指定的 alpha。通常,该力会施加到之前传递给 force.initialize 的节点数组上,然而,一些力可能仅施加到节点的子集,或者表现不同。例如,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)

向此力提供 nodesrandom 来源的数组。当通过 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.