Search code examples
graphvizdot

DOT: How to place clusters at same level without ranking equally the nodes in those clusters?


I have below DOT code. It describes five subgraphs (clusters). I am happy with the rendering of the two lower clusters and also with the relative position of the clusters (three on the top at the same level and two at the bottom in the respective order). However, I would like that the nods in the upper three clusters are ranked according to the internal relationships of their nodes, similar to what I get in the lower two clusters (As you see, the nodes in the upper clusters are rendered all in a single row despite the presence of hidden edges between some of them).

I understand that the reason they are not is the rank=same; command. However, if I remove that I am losing the positioning of the clusters. I tried a couple of variants using different combinations of the commands and additional ones such as clusterrank=local;, but the result below stayed the closest. Also subgraph cluster ranking in dot did not get me what I want.

digraph G {

    splines=line;
    size=1;
    ranksep=2;
    newrank=true;
    rankdir=BT

    subgraph cluster_z {
      label="Z";
      rank=same;

      "ZSF" [fillcolor = red];
      "ZTA" [fillcolor = red]; 
      "ZSS" [fillcolor = red];
      "ZIN" [fillcolor = red];
      "ZOW" [fillcolor = red];
      "ZNT" [fillcolor = red];
      "ZSS" [fillcolor = red];
      "ZCE" [fillcolor = red];
      "ZAY" [fillcolor = red];
      "ZNT" [fillcolor = red];
      "ZTA" [fillcolor = red];
      "ZTA" [fillcolor = red];
      "ZST" [fillcolor = red];
      "ZTO" [fillcolor = red];
      "ZON" [fillcolor = red];
      "ZPP" [fillcolor = red];
      "TNT" [fillcolor = red];
      "TCE" [fillcolor = red];
      "TNT" [fillcolor = red];


      "ZNT" -> "ZTA" [style=invis]
      "ZTA" -> "ZSF" [style=invis]
      "ZSF" -> "ZNT" [style=invis]

      "ZIN" -> "ZTA" [style=invis]
      "ZTA" -> "ZON" [style=invis]
      "ZON" -> "ZTA" [style=invis]
      "ZTA" -> "ZSF" [style=invis]
      "ZSF" -> "ZNT" [style=invis]
      "ZNT" -> "ZIN" [style=invis]

      "ZPP" -> "ZTA" [style=invis]
      "ZTA" -> "ZSF" [style=invis]
      "ZSF" -> "ZPP" [style=invis]

      "ZSF" -> "ZTA" [style=invis]
      "ZTA" -> "ZSF" [style=invis]

      "ZOW" -> "ZSS" [style=invis]
      "ZSS" -> "ZOW" [style=invis]

      "ZAY" -> "ZCE" [style=invis]
      "ZCE" -> "ZTA" [style=invis]
      "ZTA" -> "ZAY" [style=invis]

      "ZSF" -> "ZTA" [style=invis]
      "ZAY" -> "ZTA" [style=invis]
      "ZTA" -> "ZSF" [style=invis]

      "ZAY" -> "ZTA" [style=invis]
      "ZTA" -> "ZTA" [style=invis]
      "ZTA" -> "ZCE" [style=invis]
      "ZCE" -> "ZTA" [style=invis]
      "ZTA" -> "ZSF" [style=invis]
      "ZSF" -> "ZAY" [style=invis]

    }

    subgraph cluster_y {
      rank=same;
      label="Y";

      "YCY" [fillcolor = blue];
      "YES" [fillcolor = blue];
    }

    subgraph cluster_w {

      rank=same;
      label="W";

      "WER" [fillcolor = green];
      "WRT" [fillcolor = green];
    }

    subgraph cluster_o {

      label="O";
      "OOL" [fillcolor = white];
      "OOL" [fillcolor = white];
      "OIT" [fillcolor = white];
      "ONT" [fillcolor = white];
      "OGE" [fillcolor = white];
      "OTA" [fillcolor = white];
      "OTA" [fillcolor = white];
      "OTS" [fillcolor = white];
      "OTS" [fillcolor = white];
      "OCE" [fillcolor = white];
      "ORT" [fillcolor = white];
      "ORT" [fillcolor = white];
      "OON" [fillcolor = white];
      "OCT" [fillcolor = white];
      "OOL" [fillcolor = white];
      "OTO" [fillcolor = white];
      "OPE" [fillcolor = white];
      "OPY" [fillcolor = white];

      "OIT" -> "ORT" [style=invis]
      "ORT" -> "OON" [style=invis]
      "OON" -> "OPE" [style=invis]
      "OPE" -> "OIT" [style=invis]

      "OON" -> "OPE" [style=invis]
      "OPE" -> "OON" [style=invis]

      "OOL" -> "OOL" [style=invis]
      "OOL" -> "OOL" [style=invis]

      "OCE" -> "OON" [style=invis]
      "OON" -> "OCE" [style=invis]

      "OTA" -> "OON" [style=invis]
      "OON" -> "OTA" [style=invis]

      "OIT" -> "OTA" [style=invis]
      "OTA" -> "ORT" [style=invis]
      "ORT" -> "ORT" [style=invis]
      "ORT" -> "OON" [style=invis]
      "OON" -> "OIT" [style=invis]

      "OIT" -> "OTA" [style=invis]
      "OTA" -> "OTS" [style=invis]
      "OTS" -> "OON" [style=invis]
      "OON" -> "OPE" [style=invis]
      "OPE" -> "OIT" [style=invis]

    }

    subgraph cluster_e {
      label="E";

      "EUT" [fillcolor = grey];
      "EON" [fillcolor = grey];
      "ERT" [fillcolor = grey];
      "ERT" [fillcolor = grey];
      "EST" [fillcolor = grey];
      "EON" [fillcolor = grey];
      "EER" [fillcolor = grey];
      "ERE" [fillcolor = grey];
      "ETO" [fillcolor = grey];

    }

    "OIT" -> "ZSF";
    "ORT" -> "ZSF";
    "OON" -> "ZSF";
    "OPE" -> "ZSF";
    "EON" -> "ZSF";
    "EER" -> "ZSF";

    "OON" -> "ZTA";
    "OPE" -> "ZTA";
    "EER" -> "ZTA";

    "OOL" -> "ZSS";
    "OOL" -> "ZSS";

    "OGE" -> "ZIN";
    "EON" -> "ZIN";

    "OOL" -> "ZOW";

    "OTS" -> "ZNT";

    "OCT" -> "ZSS";

    "OCE" -> "ZCE";
    "OON" -> "ZCE";

    "OTA" -> "ZAY";
    "OON" -> "ZAY";

    "OIT" -> "ZNT";
    "EON" -> "ZNT";

    "OIT" -> "ZTA";
    "OTA" -> "ZTA";
    "ORT" -> "ZTA";
    "ORT" -> "ZTA";
    "OON" -> "ZTA";
    "EON" -> "ZTA";
    "ERE" -> "ZTA";

    "OIT" -> "ZTA";
    "OTA" -> "ZTA";
    "OTS" -> "ZTA";
    "OON" -> "ZTA";
    "OPE" -> "ZTA";
    "EON" -> "ZTA";
    "ERE" -> "ZTA";

    "OOL" -> "ZST";

    "OTO" -> "ZTO";

    "ONT" -> "ZON";
    "EON" -> "ZON";

    "OPY" -> "ZPP";
    "EER" -> "ZPP";


  { rank=same; "ZSF"; "YCY"; "WER" }
  { rank=same; "OPY"; "EER"}


}

Output


Solution

  • I removed all the duplicate lines, invisible edges, and colour attributes.

    I then gave it some colour to make visible what I've done.

    The source is like this:

    digraph G {
    
    newrank=true;
    rankdir=BT;
    ranksep=2;
    splines=line;
    
    subgraph cluster_z {
        label="Z";
        "TCE";
        "TNT";
        "ZAY";
        "ZCE";
        "ZIN";
        "ZNT";
        "ZON";
        "ZOW";
        "ZPP";
        "ZSF";
        "ZSS";
        "ZST";
        "ZTA";
        "ZTO";
    }
    
    subgraph cluster_y {
        rank=same;
        label="Y";
        "YCY";
        "YES";
    }
    
    subgraph cluster_w {
        rank=same;
        label="W";
        "WER";
        "WRT";
    }
    
    subgraph cluster_o {
        label="O";
        "OCE";
        "OCT";
        "OGE";
        "OIT";
        "ONT";
        "OOL";
        "OON";
        "OPE";
        "OPY";
        "ORT";
        "OTA";
        "OTO";
        "OTS";
    }
    
    subgraph cluster_e {
      label="E";
      "EUT";
      "ERT";
      "EST";
      "EON";
      "EER";
      "ERE";
      "ETO";
    }
    
    /* All the edges */
    "EER" -> "ZPP";
    "EER" -> "ZSF";
    "EER" -> "ZTA";
    "EON" -> "ZIN";
    "EON" -> "ZNT";
    "EON" -> "ZON";
    "EON" -> "ZSF";
    "EON" -> "ZTA";
    "ERE" -> "ZTA";
    "OCE" -> "ZCE";
    "OCT" -> "ZSS";
    "OGE" -> "ZIN";
    "OIT" -> "ZNT";
    "OIT" -> "ZSF";
    "OIT" -> "ZTA";
    "ONT" -> "ZON";
    "OOL" -> "ZOW";
    "OOL" -> "ZSS";
    "OOL" -> "ZST";
    "OON" -> "ZAY";
    "OON" -> "ZCE";
    "OON" -> "ZSF";
    "OON" -> "ZTA";
    "OPE" -> "ZSF";
    "OPE" -> "ZTA";
    "OPY" -> "ZPP";
    "ORT" -> "ZSF";
    "ORT" -> "ZTA";
    "OTA" -> "ZAY";
    "OTA" -> "ZTA";
    "OTO" -> "ZTO";
    "OTS" -> "ZNT";
    "OTS" -> "ZTA";
    
    /* 15 nodes from cluster_z and cluster_y and cluster_w */
    { rank = same;
        "TCE" [style = filled; color = green;]
        "TNT" [style = filled; color = green;]
        "WER" [style = filled; color = green;]
        "WRT" [style = filled; color = green;]
        "YCY" [style = filled; color = green;]
        "YES" [style = filled; color = green;]
        "ZAY" [style = filled; color = green;]
        "ZCE" [style = filled; color = green;]
        "ZIN" [style = filled; color = green;]
        "ZON" [style = filled; color = green;]
        "ZOW" [style = filled; color = green;]
        "ZPP" [style = filled; color = green;]
        "ZSS" [style = filled; color = green;]
        "ZST" [style = filled; color = green;]
        "ZTO" [style = filled; color = green;]
    }
    
    /* 10 nodes from cluster_o and cluster_e */
    { rank = same;
        "OCE" [style = filled; color = yellow;]
        "OCT" [style = filled; color = yellow;]
        "OGE" [style = filled; color = yellow;]
        "ONT" [style = filled; color = yellow;]
        "OOL" [style = filled; color = yellow;]
        "OPY" [style = filled; color = yellow;]
        "OTO" [style = filled; color = yellow;]
        "EON" [style = filled; color = yellow;]
        "EER" [style = filled; color = yellow;]
        "ERE" [style = filled; color = yellow;]
    }
    
    /* Gives cluster_z 2 rows of nodes. */
    "ZSF" -> "ZTO" [color = red];
    
    /* Gives cluster_o 4 rows of nodes. */
    "OIT" -> "OON" -> "OPE" -> "ORT" [color = red];
    
    }
    

    while the image looks like this: enter image description here so you can fiddle with it as you like.