iterate method
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;
}