Skip to content

Iterative-Relaxation in d3.forceLink #193

@gvarnavi

Description

@gvarnavi

Initially posted as vasturiano/d3-force-registry#8, was prompted to also post this here for broader visibility.

The documentation suggests d3.forceLink does the following (emphasis added):

The strength of the force is proportional to the difference between the linked nodes’ distance and the target distance, similar to a spring force.

Unfortunately, this might be misleading as d3.forceLink actually performs iterative relaxation.
The responsible lines in the source code are:

        x = target.x + target.vx - source.x - source.vx || jiggle(random);
        y = target.y + target.vy - source.y - source.vy || jiggle(random);

Note the link distance is specified by 'peeking ahead' to the anticipated position of the node ⟨x + vx,y + vy⟩.

This has implications in energy-conservation, e.g. the two pendulum based blocks here (1 2) and in the examples in this notebook, despite all the examples explicitly specifying 0 alphaDecay and velocityDecay.

  const sim = d3.forceSimulation(nodes)
  .force("link", d3.forceLink(edges).distance(1))
  .alphaDecay(0)
  .velocityDecay(0);

Perhaps the documentation should alert users to this?
Alternatively, and since a change away from iterative relaxation would render the iterations option nonsensical, the function could handle iterations(0) as a special case doing the following instead?

        x = target.x - source.x  || jiggle(random);
        y = target.y - source.y  || jiggle(random);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions