Search code examples
graphgraphvizdot

Tighten the dot graph making it more symmetric


I compile the following graph with: dot graph.dot -Tpdf -ograph.pdf, producing:

enter image description here

The result is okay, but the state transition look a lot like a spagetthi monster and I have no clue what I could do to approach this problem. I tried other layouts: twopi, neato, etc. Are there parameters which allow it to force the graph to look a little bit more symmetric? Because the overall picture is okay.

To me it seems like the edges use the minimal space available to frame the edge descriptions, maybe this is the problem?

Is my graph design flawed? Should I write the different state transitions on one edge, using \n to separate the different transitions?

digraph finite_state_machine {
    rankdir=LR;
    edge [fontsize=26];
    node [shape = doublecircle, width=2.0, fontsize=24, fixedsize=true,style=filled, colorscheme=spectral5]; New  [fillcolor=3] Terminated [fillcolor=5];
    node [shape = circle, width=2.0, fontsize=24, fixedsize=true, colorscheme=spectral5]; Runnable [fillcolor=4] Waiting [fillcolor=2] "Timed\nWaiting" [fillcolor=2] Blocked [fillcolor=1];
    New -> Runnable [ label = "Thread.start" ];
    Runnable -> Waiting [ label = "Object.wait" ];
    Runnable -> Waiting [ label = "Thread.sleep" ];
    Runnable -> Waiting [ label = "LockSupport.park" ];
    Waiting -> Blocked [ label = "Reacquire monitor lock after\nleaving Object.wait" ]
    Waiting -> Blocked [label = "Spurious wake-up"]
    "Timed\nWaiting" -> Blocked [ label = "Reaquire monitor lock after\n leaving Object.wait" ]
    "Timed\nWaiting" -> Terminated [ label = "Exception" ]
    "Timed\nWaiting" -> Blocked [ label = "Spurious wake-up" ]
    Runnable -> "Timed\nWaiting" [ label = "Object.wait" ];
    Runnable -> Blocked [ label = "Contended Monitor\nEnter" ];
    Blocked -> Runnable [ label = "Contended Monitor\nEntered" ];
    Runnable -> Terminated [ label = "Thread finishes\nexecution" ]
    Runnable -> Terminated [ label = "Exception" ]
    Waiting -> Runnable [ label = "Object.notify\nObject.notifyAll" ]
        Waiting -> Terminated [ label = "Exception" ]
    "Timed\nWaiting" -> Runnable [ label = "Object.notify\nObject.notifyAll" ]
}

Solution

  • I don't think your design is flawed, I think it's ok. The dot syntax is readable and therefore maintainable, and the result is what an automatically generated graph looks typically alike.

    Of course you can add small corrections to make this particular graph better (or at least different). Some of them may prove difficult to implement if for example the source for your graph is generated by an application. Here are some ideas:


    To get the layout to be more symmetric, you may try to align the nodes Waiting and Terminated as well as Timed Waiting and Blocked by setting their group attribute to the same value (group=a and group=b).

    grouped nodes

    It worked fine for Waiting and Teminated, but not so well for Timed Waiting and Blocked - probably because there are two edges between those nodes.

    You may try to straighten them out by picking one of the edges which links them and set its weight attribute to a high value.

    Other than that, I think the graph looks nicer over all because the edges are smoother and have less unnecessary curves, especially - but not only - between Runnable and Waiting.


    The spaghetti effect is due to the splines - maybe it looks less spaghetti without splines? I tried by adding splines=compound or splines=ortho (same result):

    group and compound

    The graph uses slightly less vertical space. It's not spaghetti, but it's not better in my opinion...


    You may also try splines=compound without the group attributes, this should make the graph a little more compact (bug not necessarily prettier). Or simply play around with the weight of the edges to straighten out particularly unpleasant edges.

    In some cases, concentrate can clear up graphs with lots of parallel edges - in this case it doesn't really help.