UML Graph Layout - Andy Talk - March 2011
UML Graph Layout - Andy Talk - March 2011
UML Graph Layout - Andy Talk - March 2011
Andy Bulka
March 2011
Melbourne Patterns Group
www.andypatterns.com
Problem – Untangling UML
Topics
Spring Layout
Mapping layout to Real World
OGL
MVC
Overlap Removal
Unit tests and unit test diagrams
Memento Design Pattern
Blackboard Design Pattern
injecting sorting function
Future...
Terms
Nodes – these are the shapes/rectangles
Edges – these are the lines connecting the shapes
Summary 1
My UML tool is written in Python
Existing layout libraries for e.g. python PyGraphviz
has no windows port so I wrote my own
Layout is reasonably hard to implement – academic
papers are very complex and deal in a lot of math
DIRTY SECRET OF ACADEMIA - Layout algorithms
only deal with ‘points’ and don’t take into account real
width and height
Summary 2
Thus you must run an overlap removal algorithm after
the layout to remove shape overlaps for any real world
use (unless dealing with network and particle
visualisation where each node is the same size/shape)
Overlap removal algorithm needs to minimise shape
movement in order to respect the layout results
What I developed
I used a ‘spring layout’ adapted from java and
javascript
I developed my own overlap removal algorithm
Developed a GUI sandbox test app for development
Overlap Removal - Before and After
Unit Testing
Extensive unit tests were created to keep on top of the
layout algorithm results. A word document containing
annotated screenshots for each test helped me
enormously.
Unit Testing
Layout / persistence format was created for creating
layout scenarios
def test3_5InsertedVerticallyTwoPushedDown(self):
self._LoadScenario3()
# move m1 to the left
node = self.g.FindNodeById('m1')
node.left, node.top = (6, 4)
# assert m1 has been inserted vertically - two pushed down
were_all_overlaps_removed = self.overlap_remover.RemoveOverlaps()
self.assertTrue(were_all_overlaps_removed)
self.assertEqual(2, self.overlap_remover.GetStats()['total_overlaps_found'])
self.assertTrue(self._ensureYorder('m1', 'D25', 'D13'))
self.assertTrue(self._ensureXorder('m1', 'D97', 'D98'))
self.assertTrue(self._ensureXorder('D25', 'D97', 'D98'))
Tests helped in refactoring
Final Results were pretty good!
Design Patterns Used - Memento
Memento was used to remember graph layout
positions and then compare mementos to see if
anything had ‘changed’ and thus drop out of the
Spring layout algorithm early
Memento was used to save/restore layouts in my test
GUI – assigned to keys 0..9
Design Patterns Used - Blackboard
Blackboard pattern used to run layout several times
and figure out which was the best, cleanest result using
multiple criteria. Each run is a ‘snapshot’
Snapshot 1 [6] LL 0 NN pre rm overlaps 5 LN 0 scale 1.6 bounds 23 (500, 473) <---
Snapshot 2 [4] LL 0 NN pre rm overlaps 5 LN 1 scale 1.4 bounds 30 (570, 537)
Snapshot 3 [5] LL 0 NN pre rm overlaps 6 LN 2 scale 2.0 bounds 17 (444, 393)
Snapshot 4 [2] LL 0 NN pre rm overlaps 4 LN 2 scale 1.4 bounds 34 (648, 537)
Snapshot 5 [3] LL 0 NN pre rm overlaps 5 LN 4 scale 2.0 bounds 21 (427, 508)
Snapshot 6 [1] LL 0 NN pre rm overlaps 10 LN 5 scale 2.0 bounds 18 (485, 379)