The split solver in Cannon.js

As I had recently implemented some graph traversing code, I was keen on using it in Cannon.js. You may think, why use graph algorithms in a physics engine? The idea is a bit tricky but I’ll try to explain.

In the usual contact solving case, we iterate over all contacts in the system. For each iteration, we transfer impulses from one body to another. In the example case of stacked boxes, iterating like this will make the top box “feel” impulses from the bottom box as the impulse travel through the stack (this depends on how many times we iterate over the system and what iteration order we use, though that’s another problem).

Many solvers have a “tolerance” parameter and it is used to check when to stop iterating. If the total error (read: contact overlap) is small, then the solution is good enough and we can stop. The tolerance is compared to the *sum* of all errors in the system.

Say we have one stack of boxes on a static plane and a sphere on the same plane. The total error will include both the errors from the stack and from the sphere. We will stop iterating over all the contacts when the total error is below the tolerance limit. Say we reach M iterations. This means we compute stuff M times on each of the N contacts in the system (a total of N*M computations).

Now what if we split the system into two independent systems, one for the stack+plane and one for the sphere+plane, and run the solver once for each of the two systems? The stack will probably still need M iterations, but the interesting thing is that the sphere will only need one. Why? Because the case of a single contact does not need to propagate impulses, and it can directly report the exact solution.

So, in the big system we need M*N computations and in the split system we need M*(N-1). That’s great! And this strategy works for many other systems too. In most cases, we can get away cheaper by using a split solver.

But what about the graph algorithm, you may say. It is used to find the independent sets in the system. Yes, it will add some complexity. However, that computation needed is not as hard as the solve part, and it has linear complexity (with respect to the number of contacts and bodies). The solving complexity depends on the number of contacts times the number of iterations. The number of iterations should be linearly dependent on the number of contacts to be able to propagate all impulses across the system, and so we end up with a quadratic solving complexity.

There is another advantage with the split solver: the subsystems can be solved in parallel. But that is another story!