Search code examples
graphviz

fdp/graphviz: vertical misalignment at the beginning of rows even with forced positions


I'm exploring using fdp to do grid (rows, and columns) based diagram using fdp with forced positions. But to my surprise, the forced positions sometimes are still just suggestion, not really enforced. I wonder how I can have the positions completely enforced with graphviz, some layout engine, and some parameter combination?

Here are the illustration of my findings: (I'm using emacs/org-mode/graphviz code block to generate the diagrams)

Case 1: single column, perfect alignment:

#+begin_src dot :file ./column1.png :cmdline -Kfdp -Tpng
graph dfd {
node[shape=box]
"(0.0, 0.0)" [pos="0.0, 0.0!"]
"(0.0, 0.1)" [pos="0.0, 0.1!"]
"(0.0, 0.2)" [pos="0.0, 0.2!"]
"(0.0, 0.3)" [pos="0.0, 0.3!"]
}
#+end_src

Resulting: enter image description here

Case 2: 2 columns: even the first column is no longer aligned

#+begin_src dot :file ./column2.png :cmdline -Kfdp -Tpng
graph dfd {
node[shape=box]
"(0.0, 0.0)" [pos="0.0, 0.0!"]; "(0.1, 0.0)" [pos="0.1, 0.0!"]
"(0.0, 0.1)" [pos="0.0, 0.1!"]; "(0.1, 0.1)" [pos="0.1, 0.1!"]
"(0.0, 0.2)" [pos="0.0, 0.2!"]; "(0.1, 0.2)" [pos="0.1, 0.2!"]
"(0.0, 0.3)" [pos="0.0, 0.3!"]; "(0.1, 0.3)" [pos="0.1, 0.3!"]
}
#+end_src

resulting: enter image description here

I also found that the more columns, the worse misalignment is. I found that fdp's layout algorithm is somehow elastic to avoid overlapping but I still don't understand for the first column, there should not be any overlapping yet.


Solution

  • Well, I'm not sure whether graphviz is really the tool of choice for what you want to achieve. But since I don't know, here some bits to get you going:

    dot arranges the nodes you define in a hierarchical fashion that depends upon the order they are defined, upon the direction of the edges and upon a specific instruction rank = same for nodes that are on the same hierarchical level. dot automatically sets the width and height of the nodes according to the label, so if your labels have different length, you need to take that into consideration.

    A very basic sample:

    digraph so 
    {
        // default/initial node style
        node[ shape = box, width = 2, height = 1.5 ];
        // default/initial edge style
        edge[ style = invis ];
    
        {rank = same; a1 -> a2 -> a3 -> a4 }
        {rank = same; b1 -> b2 -> b3 -> b4 }
        {rank = same; c1 -> c2 -> c3 -> c4 }
        {rank = same; d1 -> d2 -> d3 -> d4 }
        a1 -> b1 -> c1 -> d1;  
    
        a1[ label = "bla" ];
        c1[ label = "long label\nwith new line" ];
    
    }
    

    which gives you

    enter image description here

    In this simple example, the rank = same instruction is not really necessary, you would achieve the same with

    a1 -> b1 -> c1 -> d1;
    a2 -> b2 -> c2 -> d2;
    a3 -> b3 -> c3 -> d3;
    a4 -> b4 -> c4 -> d4;
    

    but the more elaborate your structure gets, the more the explicit ranking will help.