Force-directed layout required many iterations to "complete"

Per my understanding, setting isValidLayout to false triggers the diagram to do the layout:
myDiagram.layout = GoJs(ImportForceDirectedLayout, { isValidLayout: false });
Here is ImportForceDirectedLayout:

    function ImportForceDirectedLayout() {
        go.ForceDirectedLayout.call(this);
        this.defaultElectricalCharge = 50;
        this.defaultGravitationalMass = 1;
        this.defaultSpringLength = 150;
        this.defaultSpringStiffness = .05; // default 0.05
        this.maxIterations = 10000;
    }
    go.Diagram.inherit(ImportForceDirectedLayout, go.ForceDirectedLayout);

    ImportForceDirectedLayout.prototype.needsClusterLayout = function() { return true; };
    ImportForceDirectedLayout.prototype.makeNetwork = function(coll) {
        var net = go.ForceDirectedLayout.prototype.makeNetwork.call(this, coll);
        net.vertexes.each(function(v) { v.isFixed = ((v.node.data.category === 'attributes' || v.node.data.category === 'self')) });
        return net;
    };

“attributes” are the rectangular box attached to the nodes. “self” are edges whose source and target are the same.

After triggering a new diagram to be laid out for the first time, I got a rather unpleasant result:

Each time I trigger the layout, the nodes goes a little further apart. After triggering the force-directed layout about 20 times, I got a fairly reasonable layout:

Why is force-directed doing considerably better each time I trigger it? More importantly, how do I get it to finish the first time? I realize “finish” for force-directed layout may not mean an aesthetically pleasing diagram, but it is hard to believe that bulk of nodes in the the first iteration has locally minimum energy.

I played with force-directed layout properties for a bit. While they do improve the initial layout, they do not change the behavior that continuously running force-directed layout leads to considerably better layout.

Thanks in advance.

Each ForceDirectedLayout starts with the nodes at their current positions.

If you are going to have a lot of highly connected clusters of nodes, it might help to use a larger electrical charge. The default value is 150, so your setting it to 50 would cause the nodes not to spread out as fast.

I’d start with the default values, but increase the maxIterations, which defaults to 100, to 1000. Or maybe increasing it to 10000 isn’t too bad if it doesn’t take too long.

Thanks Walter. I got a better initial layout when I increased the electrical charge. I also got to a good layout in 5 iterations of triggering force-directed layout.

It sounds like multiple force-directed layouts ran one after the other is essentially one long run of force-directed. I experimented with 1000 and 10000 as maxIterations and the initial run looks similar. This leads me to believe the layout was terminated by epsilonDistance constraint instead of maxIterations. If the diagram already has locally minimum energy, why does running force-directed layout again moves nodes further apart?

For reference: top picture is 1000 and bottom is 10000.

Iterations stop either when it reaches the maxIterations count or no vertex has moved by at least the epsilonDistance.

Yes. Since the layout with 1000 as maxIterations looks similar to the layout with 10000 as maxIterations, it is likely the algorithm is not reaching maxIterations and is terminating because no vertex moved by at least the epsilonDistance. Given that all the force-directed layout properties are constants, how is running force-directed layout again able to move nodes by more than epsilonDistance again? Shouldn’t it terminate right away?

You can look at the value of ForceDirectedLayout.currentIteration to see if it got to the maxIterations count. My guess is that it did not settle down by that time – there were still some nodes moving a bit. I suppose you could try increasing the value of ForceDirectedLayout.epsilonDistance.

hmm I am reaching maxIterations every run. Weird to see that 10000 iterations is worse than 2 sets of 1000 iterations. I think you mean decreasing epsilonDistance? That does help a bit. Thanks.