Well, I've made a decent amount of progress since my last post, along with a little story :)
So I've been reading a variety of papers on graph constraints, but none of them really gave me what I needed. In my last post I mentioned a series of conditions under which constraints could be applied, but the conditions were quite restrictive, and hardly lent themselves to angle constraints at all. Anyways, after scouring the web for better ideas, I could find none. I temporarily gave up on constraints and focused on improving the run time of the algorithm.
After caching a variety of common variables, eliminating unnecessary loops, decreasing the amount of heap allocation, and parallelizing the algorithm I have achieved over an order of magnitude increase in speed. Running the algorithm to completion over a graph of 400 nodes and ~900-1000 edges previously took 90-120 seconds. It now completes the same ~3000 iterations in ~3 seconds. This does not even address the worst N^2 part of the algorithm yet which calculates node/node repulsive forces. There are techniques for cutting this down to N log(N) or even simply N (octrees and the fast multipole method), which I hope could result in another order of magnitude increase if I implement them.
As part of the optimization, I was looking at different position integration methods, which have been studied quite a bit in N-body simulations and game design. Verlet integration is the most common, and while not as flexible as the Euler's integration I was using previously, it is more accurate and does not depend directly on velocity, meaning I could remove the velocity derivation from my loop.
This is where things get interesting for me :) One feature of Verlet integration is that it works extremely well when combined with rigid body dynamics. I spent the day reading up on rigid body dynamics, and was convinced I could use this to implement exactly the generic kinds of constraints I wanted. Rigid body dynamics are not as provably convergent as the previous constraint systems, but function much better in practice. Further, researchers in the graph layout field have been looking for this type of generic and flexible constraint model for a while, but I hadn't found a single paper mentioning anything like this. So, images of Noble Peace Prizes and knighthood flashing through my head, I wrote down some of my thoughts. 30 seconds later, I found a paper detailing everything I had just thought of, written by the same guy as many of the other papers I'd been reading! In addition, he apparently now works at the same company as me in a nearby building (small world).
So, the downside is no Nobel Peace Prize for me, the upside is I was only a year late (the paper was from 2009), and I shot him an email which helped me improve the code greatly.
I have now implemented an angle based constraint system in my code. I was originally running it after every step, as most games and simulation code does, but I found this doesn't allow for enough expansion in the graph. The problem is 99% of systems usually only implement distance constraints. Distance constraints either have a large set of values that satisfy the constraint, or if they have only one valid value, they are usually used as angle constraints as well, meaning the layout doesn't care that the angle of an edge is very difficult to change with a distance constraint. I wanted angle constraints, but I also wanted a large degree of freedom for the distance to change, which just was not happening.
The solution was not run the constraints after every step instead of angle forces, but to run the simulation with angle forces and no constraints, which allows for good expansion and a decent ending layout, and then run several constraint iterations to force all angles to be correct.
Final Layout: http://i.imgur.com/73vgP.png
I might throw in some extra layout passes during the final constraints pass to see if that makes things a little more symmetric.
The largest remaining problem is what to do about edge crossings. Applying this to MUD maps is somewhat unique as there are certain edge crossings you want, and certain edge crossing that you don't want. Further, the undesirable ones cannot be solved in the very generic ways most research so far has pointed to. I have some vague ideas around this, but nothing solid as of yet. |