Search code examples
graphviztriedotsubgraph

Forcing the edge between subgraphs to go left or right


I have a PATRICA trie for which I'm generating a GraphViz file. The internal nodes are the skip values and the edges are dotted 0 and solid 1. It is preferred that 0 is to the left of 1, giving an alphabetical order of the leaves. I re-arranged the order I visit the graph so dot gives this result. However, when I group them in trees in a forest using subgraphs, I can't seem to force dot to reliably respect the order for inter-subgraph edges.

digraph {
    rankdir=TB;
    node [shape = box, style = filled, fillcolor = lightsteelblue];
    // forest size 2.

    subgraph cluster_tree0 {
        style = filled; fillcolor = lightgray; label = "tree 0";
        // branches
        branch0_0 [label = "3", shape = none, fillcolor = none];
        branch0_0 -> branch0_1;
        branch0_1 [label = "0", shape = none, fillcolor = none];
        branch0_1 -> branch0_2 [style = dashed];
        branch0_2 [label = "1", shape = none, fillcolor = none];
        branch0_2 -> leaf0_1 [style = dashed, color = royalblue];
        branch0_2 -> leaf0_2 [color = royalblue];
        branch0_1 -> branch0_3;
        branch0_3 [label = "2", shape = none, fillcolor = none];
        branch0_3 -> leaf0_3 [style = dashed, color = royalblue];
        branch0_3 -> leaf0_4 [color = royalblue];
        // leaves
        leaf0_1 [label = "u"];
        leaf0_2 [label = "v"];
        leaf0_3 [label = "x"];
        leaf0_4 [label = "y"];
    }
    branch0_0 -> branch1_0 [lhead = cluster_tree0, ltail = cluster_tree1, color = firebrick, style = dashed];

    subgraph cluster_tree1 {
        style = filled; fillcolor = lightgray; label = "tree 1";
        // branches
        branch1_0 [label = "0", shape = none, fillcolor = none];
        branch1_0 -> leaf1_0 [style = dashed, color = royalblue];
        branch1_0 -> branch1_1;
        branch1_1 [label = "1", shape = none, fillcolor = none];
        branch1_1 -> leaf1_1 [style = dashed, color = royalblue];
        branch1_1 -> branch1_2;
        branch1_2 [label = "0", shape = none, fillcolor = none];
        branch1_2 -> leaf1_2 [style = dashed, color = royalblue];
        branch1_2 -> leaf1_3 [color = royalblue];
        // leaves
        leaf1_0 [label = "f"];
        leaf1_1 [label = "m"];
        leaf1_2 [label = "n"];
        leaf1_3 [label = "o"];
    }

}

On one graph it works fine, but it the subgraphs are reversed to the order I want them.

Patrica tree.

I reversed the order in the file and it still looks the same. I played around with it and I could get it turned around by rank=same, ordering=out, and invis edges somehow, but I want it to be programmatic. Is there any easy way to draw the red dotted line, representing 0, to the left, instead of to the right, of the solid line, representing 1?


Solution

  • enter image description here

    OK, a total kludge, but probably fully scriptable.

    First, your input somewhat reworked:

    digraph {
        rankdir=TB;
        newrank=true  // helps
        graph [splines=false]
        node [shape = box, style = filled, fillcolor = lightsteelblue];
        // forest size 2.
    
        subgraph cluster_tree1 {
            style = filled; fillcolor = lightgray; label = "tree 1";
            // branches
            branch1_0 [label = "0", shape = none, fillcolor = none];
            branch1_0 -> leaf1_0 [style = dashed, color = royalblue];
            branch1_0 -> branch1_1;
            branch1_1 [label = "1", shape = none, fillcolor = none];
            branch1_1 -> leaf1_1 [style = dashed, color = royalblue];
            branch1_1 -> branch1_2;
            branch1_2 [label = "0", shape = none, fillcolor = none];
            branch1_2 -> leaf1_2 [style = dashed, color = royalblue];
            branch1_2 -> leaf1_3 [color = royalblue];
            // leaves
            leaf1_0 [label = "f"];
            leaf1_1 [label = "m"];
            leaf1_2 [label = "n"];
            leaf1_3 [label = "o"];
        }
      subgraph cluster_tree0 {
            style = filled; fillcolor = lightgray; label = "tree 0";
            // branches
            branch0_0 [label = "3", shape = none, fillcolor = none];
            branch0_0 -> branch0_1;
            branch0_1 [label = "0", shape = none, fillcolor = none];
            branch0_1 -> branch0_2 [style = dashed];
            branch0_2 [label = "1", shape = none, fillcolor = none];
            branch0_2 -> leaf0_1 [style = dashed, color = royalblue];
            branch0_2 -> leaf0_2 [color = royalblue];
            branch0_1 -> branch0_3;
            branch0_3 [label = "2", shape = none, fillcolor = none];
            branch0_3 -> leaf0_3 [style = dashed, color = royalblue];
            branch0_3 -> leaf0_4 [color = royalblue];
            // leaves
            leaf0_1 [label = "u"];
            leaf0_2 [label = "v"];
            leaf0_3 [label = "x"];
            leaf0_4 [label = "y"];
        }
        // position the clusters (trees)
        {rank=same    branch1_0 ->  branch0_1 [style=invis weight=0]} 
      
         // a kludge, we'll add this edge in later
         graph [comment="branch0_0 -> branch1_0 [color = firebrick, style = dashed  constraint=false weight=0 ];"]
    }
    

    The "problem" is the cluster-to-cluster branch, so we remove it (for the first dot pass). And we add the invisible edge to position the clusters where we want them.
    Run this input into dot -Tdot >myfile.dot. This sets positions for all nodes & edges.
    Run that thru gawk (any language) to un-comment the commented branch(s) and insert into the file.
    Finally, neato -n2 -Tpng fixedfile >fixed.png
    (yuck, but it works)

    f=atrie4.gv;

    T=png; F=`basename $f .gv`;dot -Tdot $f >$F.dot;
    gawk '
    $1~/comment/{
      sub(/[\t ]*comment="/,"")
      sub(/"[\],;]?[\t ]*$/,"")
      add[++a]=$0
      next
    }
    {oline[++o]=$0}
    END{
      for (i=1;i<o;i++)print oline[i]
      for (i=1;i<=a;i++)print add[i]
      print oline[o]
    }' $F.dot|
    neato -n2 -Tpng >$F.$T
    firefox $F.$T