Search code examples
graphviz

Graphviz: create non overlapping edges from a struct to itself


I am trying to recreate in graphviz a diagram similar to the one taken from the CLR book: enter image description here

I am using the following code in Python:

s = Digraph(node_attr={'shape': 'record'})
s.node('struct', '<f0> 3|<f1> 13|<f2> 1|<f3> 2|<f4> 8|<f5> 5')
s.edge("struct:f0", "struct:f1")
s.edge("struct:f0", "struct:f2")
s.edge("struct:f1", "struct:f3")
s.edge("struct:f1", "struct:f4")
s

The result is: enter image description here

Which is very close to what I want, except the edges overlap the nodes instead of going above/below them.

I couldn't find a way to modify edge behavior. Also, I have a feeling I am misusing the struct feature of graphviz, but I don't know how to force the graph elements to stick together in one row otherwise.

Can I avoid edge overlaps (except by modifying the SVG myself, of course) or use a different approach altogether?


Solution

  • You can use "ports" to set the points of connection.

    I tried your graph on the online service, it seems your rendering is with dot http://dreampuf.github.io/GraphvizOnline

    You can test with the others, Circo and the rest do not cross the cells, but by default they all draw on one side (bottom) and the edges are messy.

    However there are "ports" properties which allow to solve that by specifying suffixes ":s", ":n" etc. n, s, e, w, nw, ne, sw, s. - i.e. south, north etc.... .

    It seems that Dot has different default ports than Circo and Neato, the latter are fine with that:

    digraph Structs {
        node [shape=record];        
        s [label="<f0> 3|<f1> 13|<f2> 1|<f3>2|<f4>8|<f5>5"];
        s:f0 -> s:f1;
        s:f0 -> s:f2;
        s:f1:n -> s:f3:n;
        s:f1:n -> s:f4:n;
    }
    

    enter image description here

    Dot needs to specify the ports of the first two nodes, too:

    digraph Structs {
        node [shape=record];       
        s [label="<f0> 3|<f1> 13|<f2> 1|<f3>2|<f4>8|<f5>5"];
        s:f0:s -> s:f1:s;
        s:f0:s -> s:f2:s;
        s:f1:n -> s:f3:n;
        s:f1:n -> s:f4:n;
    }
    

    So I guess, in your code:

    s.edge("struct:f0:s", "struct:f1:s")
    s.edge("struct:f0:s", "struct:f2:s")
    s.edge("struct:f1:n", "struct:f3:n")
    s.edge("struct:f1:n", "struct:f4:n")
    

    See: How to prevent edges in graphviz to overlap each other

    Graphviz Dot Edge Ports for Family Tree