Arrange nodes between ranks correctly

*here in the topic I may use terms incorrectly, but I try to explain in my words, so feel free to suggest me how to use it correctly.

I have graphs like these:



digraph {
    rankdir=LR;
  
    A1 
    A2
    A3 
 
    B1 
    B2 
    B3 

    C1 
    C2 
    C3 

    A1 -> B1
    B1 -> C1
    
    //A1 -> B2
    A1 -> A2
    A1 -> A3
    A2 -> A3
    
    B1 -> B2
    B2 -> B3
    B1 -> B3
    B2 -> A2
    //B3 -> C2
    
    C1 -> C2
    C2 -> C3
    

  //B1 -> C2
  C3 -> B2
}


I like that arrows are constraints here (I mean, node A2 goes right of the B2).
But I don’t like that A,B,C-nodes are mixed between each other vertically.
I want that all A-nodes are placed on its own “line” (rank?) and only A-nodes are placed in this horizontal area.
All B-nodes = in B-line, under A-line…
All C-nodes = in C-line, under B-line…
In the certain order, defined manually and strictly for each node.
Now B and C nodes are mixed in one “line”.

I tried clusters with the code below.
I like that A-nodes are placed on the same “level” and A-nodes are not mixed with B-nodes vertically, B-nodes not mixed with C-nodes.
But I don’t like that links between nodes of the different clusters don’t work as constraints…
I don’t like that B2 is placed under A2.
I want A2 to be placed right of the B2 due to the corresponding arrow.
I also would like to have control over placing B1 under C1, or C1 right of the B1, or C1 under B2, but it is somehow shown under B3.


digraph {
    rankdir=LR;
    
    subgraph cluster_1 {
        A1 
        A2
        A3
    }
    subgraph cluster_2 {
        B1 
        B2
        B3
    }
    
    subgraph cluster_3 {
        C1 
        C2
        C3
    } 
    
    A1 -> B1
    B1 -> C1
    
    A1 -> B2
    A1 -> A2
    A1 -> A3
    A2 -> A3
    
    B1 -> B2
    B2 -> B3
    B1 -> B3
    B2 -> A2
    B3 -> C2
    
    C1 -> C2
    C2 -> C3
    
}


I also tried using rank=same AND groups and it looks better, but I don’t understand how to ensure that the edges work as constraints so that B2 goes right after C3, not on the same level.


digraph {
    rankdir=TB;

  {rank=same; A1; A2; A3}
  {rank=same; B1; B2; B3}
  {rank=same; C1; C2; C3}
  
    A1 [group=1]
    A2 [group=2]
    A3 
 
    B1 [group=1]
    B2 [group=2]
    B3 

    C1 [group=1]
    C2 
    C3 [group=2]

    A1 -> B1
    B1 -> C1
    
    //A1 -> B2
    A1 -> A2
    A1 -> A3
    A2 -> A3
    
    B1 -> B2
    B2 -> B3
    B1 -> B3
    B2 -> A2
    //B3 -> C2
    
    C1 -> C2
    C2 -> C3
    

  //B1 -> C2
  C3 -> B2
}

If I remove the group, the arrow from C3 goes left to B2, but I want this arrow to go right to B2 and B2 should be placed right, on a new rank(?) to obey constraint of the edge.
By the way, sometimes I will need this backward arrows, and sometimes I don’t need them.
I expected to do it using constraint=true/false.
But even with default value, it doesn’t take place.

Add weight attributes to the edges which you wish to straighten out.

strict digraph "Graphviz Forum"
{
    rankdir=LR;
    A1;
    A2;
    A3;
    B1;
    B2;
    B3;
    C1;
    C2;
    C3;
    A1 -> B1;
    B1 -> C1;
    A1 -> A2[ weight=100 ];
    A1 -> A3;
    A2 -> A3[ weight=100 ];
    B1 -> B2[ weight=100 ];
    B2 -> B3[ weight=100 ];
    B1 -> B3;
    B2 -> A2;
    C1 -> C2;
    C2 -> C3;
    C3 -> B2;
}

yields the following graph:

1 Like

Another way to produce ~ same result as @jjlong, using group and constraint=false

/******************************************************************************
  forum - https://forum.graphviz.org/t/arrange-nodes-between-ranks-correctly/2503
  - now
    - rankdir=LR
    - removed rank=same - not needed
    - changed grouping A1, A2, A3 ...
    - added constraint=false keeps the groups linear
******************************************************************************/
digraph {

  rankdir=LR;  // changed

    A1  [group=1]
    A2  [group=1]
    A3  [group=1]
 
    B1  [group=2]
    B2  [group=2]
    B3  [group=2]

    C1  [group=3]
    C2  [group=3]
    C3  [group=3]

    A1 -> B1
    B1 -> C1
    
    //A1 -> B2
    A1 -> A2
    A1 -> A3   [constraint=false]  // constraint=false keeps the group linear
    A2 -> A3 
    
    B1 -> B2
    B2 -> B3
    B1 -> B3  [constraint=false]  // constraint=false keeps the group linear
    B2 -> A2   
    //B3 -> C2
    
    C1 -> C2
    C2 -> C3

  //B1 -> C2
  C3 -> B2
}

Giving:

1 Like

Thanks.

Could you please explain how you choose which edges should have [constraint=false] ?

Without the two constraint=false additions, the A1, A2, A3 and B1, B2, B3 nodes were not linear (in a line), even with the group attributes. I saw that the two “long” edges (multi-hop) were not needed to position A3 and B3 in either the X or Y direction and guessed that the two edges were “pulling” the nodes out of line.
(Does that make sense?)

1 Like

If I will want to place C3 under B2, how can I achieve it?
Click the gif-file below to see my explanation:

Animation (1)

The answer should be: B2 → A2 [minlen=0] (minlen | Graphviz). It kind of works, except it also flips the graph upside-down. Dang!

Back to the drawing-board. By adding clusters and minlen, we get the desired graph.

/******************************************************************************
  forum - https://forum.graphviz.org/t/arrange-nodes-between-ranks-correctly/2503
  - now
    -rankdir=LR
    - removed rank=same - not needed
    - changed grouping A1, A2, A3 ...
    - added clusters
    - added constraint=false keeps the groups linear
******************************************************************************/
digraph {

  rankdir=LR;  // changed
{
  cluster=true  peripheries=0
    A1  [group=1]
    A2  [group=1]
    A3  [group=1]
    A1 -> A2 [minlen=5]
    A1 -> A3   [constraint=false]  // constraint=false keeps the group linear
    A2 -> A3
}

{
  cluster=true peripheries=0
    B1  [group=2]
    B2  [group=2]
    B3  [group=2]
    B1 -> B2 [minlen=4]
    B2 -> B3
    B1 -> B3  [constraint=false]  // constraint=false keeps the group linear 
}
{
  cluster=true peripheries=0
    C1  [group=3]
    C2  [group=3]
    C3  [group=3]
    C1 -> C2
    C2 -> C3
}
   C3 -> B2
    A1 -> B1
    B1 -> C1
    
    //A1 -> B2
    B2 -> A2    [minlen=0]
    //B3 -> C2
  //B1 -> C2   
}

2 Likes