iterate method

  1. @override
bool iterate()
override

Perform one iteration of the algorithm, updating nodeLayout.

Returns true if running one iteration does not change the position of each node by much in each axis.

Implementation

@override
bool iterate() {
  // Initially assume this layout is stable.
  var isStable = true;

  // Nodes which are free to move.
  final unconstrainedNodes =
      graph.adjacencyList.keys.toSet().difference(constrainedNodes);

  // Calculate the forces on each node in the graph.
  for (final node in unconstrainedNodes) {
    final nodePosition = nodeLayout[node]!;
    var forceOnThisNode = Vector2.zero();

    // Iterate over every _other_ node in the graph.
    for (final otherNode
        in graph.adjacencyList.keys.where((other) => other != node)) {
      // A vector from node to otherNode.
      final thisToOther = nodeLayout[otherNode]! - nodePosition;
      final d = thisToOther.length;

      if (graph.adjacencyList[node]!.contains(otherNode)) {
        // If otherNode is adjacent to node, apply an attractive force.
        forceOnThisNode += thisToOther.normalized().scaled(c1 * log(d / c2));
      } else {
        // Otherwise, apply a repulsive force.
        forceOnThisNode -= thisToOther.normalized().scaled(c3 / pow(d, 2));
      }
    }

    // A small attractive force pulls each node to the centre.
    forceOnThisNode += (layoutCentre - nodePosition).scaled(c5);

    nodeLayout.update(node, (position) {
      final positionChange = forceOnThisNode.scaled(c4);
      final newPosition = position + positionChange;

      clampNodeVector(newPosition);

      // If the position of this node changes too much, the layout is unstable.
      isStable = isStable && positionChange.length < stableThreshold;

      return newPosition;
    });
  }

  return isStable;
}