Graphviz cluster vertical align

105 Views Asked by At

I've got a graph with clusters. In this example the graph is generated "tilted" to the left. The graph is generated automatically with a strategy: to combine clusters I draw an edge between the last node of previous subgraph to the first node of subsequent subgraph. How to make the subgraphs to be aligned vertically in this scenario?

digraph {
    compound=true
    fontname="Verdana"
    fontsize=12
    nodesep = 1
    ranksep = 1.5
    node[shape=folder, style="filled,solid", color=green4, fontsize=12]
    subgraph "cluster 1" {
        style=filled color=green4 fillcolor=lightgreen
        label = <l1>
        n0 [label=<n0> fillcolor=limegreen]
        n1 [label=<n1> fillcolor=limegreen]
        n2 [label=<n2> fillcolor=limegreen]
        n3 [label=<n3> fillcolor=limegreen]
        n4 [label=<n4> fillcolor=limegreen]
        n5 [label=<n5> fillcolor=limegreen]
        n0 -> n1
        n1 -> n2
        n2 -> n3
        n3 -> n4
        n4 -> n5
    }
    subgraph "cluster c1" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c1>
        n6 [label=<n6> fillcolor=limegreen]
    }
    subgraph "cluster c2" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c2>
        n7 [label=<n7> fillcolor=limegreen]
        n8 [label=<n8> fillcolor=limegreen]
        n9 [label=<n9> fillcolor=limegreen]
        n10 [label=<n10> fillcolor=limegreen]
    }
    subgraph "cluster c3" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c3>
        n11 [label=<n11> fillcolor=limegreen]
        n12 [label=<n12> fillcolor=limegreen]
        n13 [label=<n13> fillcolor=limegreen]
        n14 [label=<n14> fillcolor=limegreen]
    }
    subgraph "cluster c4" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c4>
        n15 [label=<n15> fillcolor=limegreen]
        n16 [label=<n16> fillcolor=limegreen]
    }
    subgraph "cluster c5" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c5>
        n17 [label=<n17> fillcolor=limegreen]
        n18 [label=<n18> fillcolor=limegreen]
        n17 -> n18
    }
    subgraph "cluster c6" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c6>
        n48 [label=<n48> fillcolor=limegreen]
        n49 [label=<n49> fillcolor=limegreen]
        n50 [label=<n50> fillcolor=limegreen]
        n51 [label=<n51> fillcolor=limegreen]
        n48 -> n49
        n49 -> n50
        n50 -> n51
    }
    subgraph "cluster c7" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c7>
        n52 [label=<n52> fillcolor=limegreen]
        n53 [label=<n53> fillcolor=limegreen]
    }
    subgraph "cluster c8" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c8>
        n54 [label=<n54> fillcolor=limegreen]
        n55 [label=<n55> fillcolor=limegreen]
    }
    n6 -> n7 [ltail="cluster c1", lhead="cluster c2"]
    n10 -> n11 [ltail="cluster c2", lhead="cluster c3"]
    n18 -> n15 [ltail="cluster c5", lhead="cluster c4"]
    n14 -> n17 [ltail="cluster c3", lhead="cluster c5"]
    n55 -> n48 [ltail="cluster c8", lhead="cluster c6"]
    n16 -> n52 [ltail="cluster c4", lhead="cluster c7"]
    n53 -> n54 [ltail="cluster c7", lhead="cluster c8"]
}

This is the genarated graph with strategy: last to first

enter image description here

This is something I would like to get:

enter image description here

Example with strategy last to last:

enter image description here

dot for last-to-last

digraph {
    compound=true
    fontname="Verdana"
    fontsize=12
    nodesep = 1
    ranksep = 1.5
    node[shape=folder, style="filled,solid", color=green4, fontsize=12]
    subgraph "cluster c0" {
        style=filled color=green4 fillcolor=lightgreen
        label = <l1>
        n0 [label=<n0> fillcolor=limegreen]
        n1 [label=<n1> fillcolor=limegreen]
        n2 [label=<n2> fillcolor=limegreen]
        n3 [label=<n3> fillcolor=limegreen]
        n4 [label=<n4> fillcolor=limegreen]
        n5 [label=<n5> fillcolor=limegreen]
        n0 -> n1
        n1 -> n2
        n2 -> n3
        n3 -> n4
        n4 -> n5
    }
    subgraph "cluster c1" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c1>
        n6 [label=<n6> fillcolor=limegreen]
    }
    subgraph "cluster c2" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c2>
        n7 [label=<n7> fillcolor=limegreen]
        n8 [label=<n8> fillcolor=limegreen]
        n9 [label=<n9> fillcolor=limegreen]
        n10 [label=<n10> fillcolor=limegreen]
    }
    subgraph "cluster c3" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c3>
        n11 [label=<n11> fillcolor=limegreen]
        n12 [label=<n12> fillcolor=limegreen]
        n13 [label=<n13> fillcolor=limegreen]
        n14 [label=<n14> fillcolor=limegreen]
    }
    subgraph "cluster c4" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c4>
        n15 [label=<n15> fillcolor=limegreen]
        n16 [label=<n16> fillcolor=limegreen]
    }
    subgraph "cluster c5" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c5>
        n17 [label=<n17> fillcolor=limegreen]
        n18 [label=<n18> fillcolor=limegreen]
        n17 -> n18
    }
    subgraph "cluster c6" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c6>
        n48 [label=<n48> fillcolor=limegreen]
        n49 [label=<n49> fillcolor=limegreen]
        n50 [label=<n50> fillcolor=limegreen]
        n51 [label=<n51> fillcolor=limegreen]
        n48 -> n49
        n49 -> n50
        n50 -> n51
    }
    subgraph "cluster c7" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c7>
        n52 [label=<n52> fillcolor=limegreen]
        n53 [label=<n53> fillcolor=limegreen]
    }
    subgraph "cluster c8" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c8>
        n54 [label=<n54> fillcolor=limegreen]
        n55 [label=<n55> fillcolor=limegreen]
    }
    n6 -> n10 [ltail="cluster c1", lhead="cluster c2"]
    n10 -> n14 [ltail="cluster c2", lhead="cluster c3"]
    n18 -> n16 [ltail="cluster c5", lhead="cluster c4"]
    n14 -> n18 [ltail="cluster c3", lhead="cluster c5"]
    n55 -> n51 [ltail="cluster c8", lhead="cluster c6"]
    n16 -> n53 [ltail="cluster c4", lhead="cluster c7"]
    n53 -> n55 [ltail="cluster c7", lhead="cluster c8"]
}
2

There are 2 best solutions below

0
sroush On

To get the nodes in the "correct" sequence (left-to-right), add a subgraph inside each cluster. If the cluster turns out to be horizontal - no internal edges - add the rank=same attribute to the subgraph and invisible edges connecting the cluster's nodes.
Programmatically, this can be done at the end of building the cluster (see attached file).
This allows you to correctly identify first and last nodes.
To connect the clusters with edges, I agree that a "hybrid" method is needed.

  • first-to-first if the tail cluster is horizontal (see above)
  • last-to-first if the tail cluster is vertical
digraph {
    compound=true
    fontname="Verdana"
    fontsize=12
    nodesep = 1
    ranksep = 1.5
    node[shape=folder, style="filled,solid", color=green4, fontsize=12]
    subgraph "cluster 1" {
        style=filled color=green4 fillcolor=lightgreen
        label = <l1>
        n0 [label=<n0> fillcolor=limegreen]
        n1 [label=<n1> fillcolor=limegreen]
        n2 [label=<n2> fillcolor=limegreen]
        n3 [label=<n3> fillcolor=limegreen]
        n4 [label=<n4> fillcolor=limegreen]
        n5 [label=<n5> fillcolor=limegreen]
        n0 -> n1
        n1 -> n2
        n2 -> n3
        n3 -> n4
        n4 -> n5
    }
    subgraph "cluster c1" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c1>
      {
        n6 [label=<n6> fillcolor=limegreen]
      rank=same}  // added
    }
    subgraph "cluster c2" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c2>
      {  // added
        n7 [label=<n7> fillcolor=limegreen]
        n8 [label=<n8> fillcolor=limegreen]
        n9 [label=<n9> fillcolor=limegreen]
        n10 [label=<n10> fillcolor=limegreen]
    // added next 3 lines
    edge[style=invis]
    n7->n8->n9->n10
      rank=same}
    }
    subgraph "cluster c3" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c3>
      {
        n11 [label=<n11> fillcolor=limegreen]
        n12 [label=<n12> fillcolor=limegreen]
        n13 [label=<n13> fillcolor=limegreen]
        n14 [label=<n14> fillcolor=limegreen]
    edge[style=invis]
    n11->n12->n13->n14
      rank=same}
    }
    subgraph "cluster c4" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c4>
      {
        n15 [label=<n15> fillcolor=limegreen]
        n16 [label=<n16> fillcolor=limegreen]
    edge[style=invis]
    n15->n16
      rank=same}
    }
    subgraph "cluster c5" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c5>
      {
        n17 [label=<n17> fillcolor=limegreen]
        n18 [label=<n18> fillcolor=limegreen]
        n17 -> n18
      }  // note: did not add rank=same
    }
    subgraph "cluster c6" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c6>
      {
        n48 [label=<n48> fillcolor=limegreen]
        n49 [label=<n49> fillcolor=limegreen]
        n50 [label=<n50> fillcolor=limegreen]
        n51 [label=<n51> fillcolor=limegreen]
        n48 -> n49
        n49 -> n50
        n50 -> n51
      }  // note: did not add rank=same
    }
    subgraph "cluster c7" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c7>
      {
        n52 [label=<n52> fillcolor=limegreen]
        n53 [label=<n53> fillcolor=limegreen]
    edge[style=invis]
    n52->n53
      rank=same}
    }
    subgraph "cluster c8" {
        style=filled color=green4 fillcolor=lightgreen
        label = <c8>
      {
        n54 [label=<n54> fillcolor=limegreen]
        n55 [label=<n55> fillcolor=limegreen]
    edge[style=invis]
    n54->n55
      rank=same}
    }
    n6 -> n7 [ltail="cluster c1", lhead="cluster c2"]
    n7 -> n11 [ltail="cluster c2", lhead="cluster c3"]
    // last-to-first
    n18 -> n15 [ltail="cluster c5", lhead="cluster c4"]
//   n17 -> n15 [ltail="cluster c5", lhead="cluster c4"] 
    n11 -> n17 [ltail="cluster c3", lhead="cluster c5"]
    n54 -> n48 [ltail="cluster c8", lhead="cluster c6"]
    n15 -> n52 [ltail="cluster c4", lhead="cluster c7"]
    n52 -> n54  [ltail="cluster c7", lhead="cluster c8"]
}

Giving:
enter image description here

0
Artyum On

Thanks for advice. My final solution is mixed-strategy as follows:

  • In a cluster add a row: { rank=same ... [style=invis] } and list in it every node (in appearance order) that is not dependent of other node but only if there are at least 2 of them

  • Between clusters add an edge "first-to-first" if there are exactly 0 dependencies in a first cluster

  • Between clusters add an edge "last-to-first" if there are at least one dependency in a first cluster

      digraph {
          compound=true
          fontsize=12
          nodesep = 0.5
          ranksep = 1
          node[shape=folder, style="filled,solid"]
    
          subgraph "cluster c0" {
              label = <c0>
              n0 [label=<n0>]
              n1 [label=<n1>]
              n2 [label=<n2>]
              n3 [label=<n3>]
              n4 [label=<n4>]
              n5 [label=<n5>]
              n0 -> n1
              n1 -> n2
              n2 -> n3
              n3 -> n4
              n4 -> n5
          }
    
          subgraph "cluster c1" {
              label = <c1>
              n6 [label=<n6>]
          }
    
          subgraph "cluster c2" {
              label = <c2>
              n7 [label=<n7>]
              n8 [label=<n8>]
              n9 [label=<n9>]
              n10 [label=<n10>]
              { rank=same n7->n8->n9->n10 [style=invis] }
          }
    
          subgraph "cluster c3" {
              label = <c3>
              n11 [label=<n11>]
              n12 [label=<n12>]
              n13 [label=<n13>]
              n14 [label=<n14>]
              { rank=same n11->n12->n13->n14 [style=invis] }
          }
    
          subgraph "cluster c4" {
              label = <c4>
              n15 [label=<n15>]
              n16 [label=<n16>]
              { rank=same n15->n16 [style=invis] }
          }
    
          subgraph "cluster c5" {
              label = <c5>
              n17 [label=<n17>]
              n18 [label=<n18>]
              n181 [label=<n181>]
              n18 -> n181
              { rank=same n17->n18 [style=invis] }
          }
    
          subgraph "cluster c6" {
              label = <c6>
              n48 [label=<n48>]
              n49 [label=<n49>]
              n50 [label=<n50>]
              n51 [label=<n51>]
              n48 -> n49
              n49 -> n50
              n50 -> n51
          }
    
          subgraph "cluster c7" {
              label = <c7>
              n52 [label=<n52>]
              n53 [label=<n53>]
              { rank=same n52->n53 [style=invis] }
          }
    
          subgraph "cluster c8" {
              label = <c8>
              n54 [label=<n54>]
              n55 [label=<n55>]
              { rank=same n54->n55 [style=invis] }
          }
    
          n6 -> n7 [ltail="cluster c1", lhead="cluster c2"]
          n7 -> n11 [ltail="cluster c2", lhead="cluster c3"]
          n181 -> n15 [ltail="cluster c5", lhead="cluster c4"]
          n11 -> n17 [ltail="cluster c3", lhead="cluster c5"]
          n15 -> n52 [ltail="cluster c4", lhead="cluster c7"]
          n52 -> n54 [ltail="cluster c7", lhead="cluster c8"]
          n54 -> n48 [ltail="cluster c8", lhead="cluster c6"]
      }
    

The result:

enter image description here