Search code examples
graphviz

Graphviz enforce edge start and stop position


I have a GraphViz directed graph, that looks in a simplified version like this

digraph g {
  rankdir=LR
  node [shape=record]

  subgraph "cluster_R" {
    label="R"
    "R_in" [label="in|<in_0> 0"]
    "R_out" [label="out|<out_0> 0"]
  }
  "R_in" -> "R_out" [style=invis]

  subgraph "cluster_D" {
    label="D"
    "D_in" [label="in|<in_0> 0"]
    "D_out" [label="out|<out_0> 0|<out_1> 1"]
  }
  "D_in" -> "D_out" [style=invis]

  subgraph "cluster_P" {
    label="P"
    "P_in" [label="in|<in_0> 0|<in_1> 1"]
    "P_out" [label="out|<out_0> 0"]
  }
  "P_in" -> "P_out" [style=invis]


  "R_out":"out_0" -> "D_in":"in_0"  [weight=10]
  "P_out":"out_0" -> "R_in":"in_0" [weight=10]
  "D_out":"out_0" -> "P_in":"in_0"
}

This renders OK (see) but I would like to start the unweighted edge at the right side of the source node and end at the left side of the destination node. So I modified the last edge like this

  "D_out":"out_0":e -> "P_in":"in_0":w

Now the edge starts end ends at the right place but takes an extra route at the edge of the enclosing nodes D_out and P_in. How can I prevent that and make a smooth edge that goes all around the graph?


Solution

  • It is quite difficult to get Graphviz to re-think its edges, but today we're lucky. Unfortunately it took a total rewrite of the nodes, changing them from record to HTML, to allow adding invisible cells. This in-turn "convinced" Graphviz to draw the edge below the nodes. Whew.
    But a nice looking result.

    digraph g {
      rankdir=LR
      //node [shape=record]
    
      subgraph "cluster_R" {
        label="R"
        R [shape=none label=<<table BORDER="0" CELLBORDER="1" CELLSPACING="0">
        <tr><td style="invis" width="55"></td>    <td style="invis" width="34"></td> <td style="invis" width="55"></td></tr>
        <tr><td>in</td>     <td style="invis"></td> <td>out</td></tr>
        <tr><td port="in_0">0</td> <td style="invis"></td> <td port="out_0">0</td></tr>
        </table>>]
      }
    
      subgraph "cluster_D" {
        label="D"
        D [shape=none label=<<table BORDER="0" CELLBORDER="1" CELLSPACING="0">
        <tr><td style="invis" width="55"></td>  <td style="invis" width="34"></td> <td style="invis" width="55"></td></tr>
        <tr><td>in</td>            <td style="invis" ></td> <td>out</td></tr>    
        <tr><td port="in_0">0</td> <td style="invis" ></td> <td port="out_0">0</td></tr>
        <tr><td style="invis"></td> <td style="invis" ></td> <td>1</td></tr>
        </table>>]
      }
     
      subgraph "cluster_P" {
        label="P"
        P [shape=none label=<<table BORDER="0" CELLBORDER="1" CELLSPACING="0">
        <tr><td style="invis" width="55"></td>   <td style="invis" width="34"></td> <td style="invis" width="55"></td></tr>
        <tr><td>in</td>            <td style="invis" ></td> <td>out</td></tr>
        <tr><td port="in_0">0</td> <td style="invis" ></td> <td port="out_0">0</td></tr>
        <tr><td port="in_1">1</td> <td style="invis" ></td> </tr>
        </table>>]
      }
    
       R:"out_0" -> D:"in_0"  [weight=10]
       P:"out_0" -> R:"in_0"  [weight=10]
       P:"in_0":w  ->  D:"out_0":e [dir=back] // this makes all the difference!
     }
    

    Giving:
    enter image description here