Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force graphviz to preserve node positions

I have a graph that changes over time (normally, new nodes are added). I need to regenerate the graph several times, and want all of the nodes to stick where they have been. This partially works when using graphviz with the fdp-algorithm, setting the pin-flag and specifying the positions with the pos-argument.

In most cases, this works pretty well. But especially when new nodes are added, the layout sometimes changes drastically - which I need to avoid. Is there an option to actually force these pinned positions? It could result in an ugly graph, that would be alright - but they just need to stick.

like image 874
Remo Avatar asked May 27 '13 15:05

Remo


1 Answers

Graphviz doesn't make it easy to keep noes in the same position:

  • Adding/removing a node can cause a completely different layout.
  • Adding/removing a node can change the dimension of the bounding box to change.

The following works if all nodes are known when creating the graphs:

  • Create a graph containing all possible nodes, and let graphviz lay it out. You may add nodes[pin=true]; at the beginning of the graph (then you won't have to add it later).
  • Layout the graph:

    fdp -Tdot input.gv -o input.pos.gv
    

    You now have a dot file containing all the nodes. You can use this as the base file for all the graphs to be created:

  • For every graph, create a copy of input.pos.gv and hide the unneeded nodes and edges by adding style=invis to their attributes. This makes sure they're not displayed, but also, the place they use in the layout will not be cropped away (for example if at the very top of the graph).
  • Lay them out using something like this (neato and option -n2 being the important parts):

    neato -n2 -Tpng input.pos.v1.gv -o output.v1.png
    

Example:

input.gv:

digraph g{
    node[pin=true];
    a -> b;
    a -> c;
    b -> d;
    b -> e;
    b -> f;
    c -> g;
}

input.pos.modified.gv:

digraph g {
    node [label="\N", pin=true];
    graph [bb="0,0,187,207"];
    a [pos="60.846,70.555", width="0.75", height="0.5", style=invis];
    b [pos="94.351,128.04", width="0.75", height="0.5"];
    c [pos="76.868,18.459", width="0.75", height="0.5"];
    d [pos="119.08,188.8", width="0.75", height="0.5",style=invis];
    e [pos="157.97,106.75", width="0.75", height="0.5"];
    f [pos="27.319,158.05", width="0.75", height="0.5"];
    g [pos="160.1,20.585", width="0.75", height="0.5"];
    a -> b [pos="e,84.434,111.03 70.717,87.493 73.42,92.13 76.411,97.263 79.332,102.27", style=invis];
    a -> c [pos="e,71.34,36.433 66.27,52.918 66.934,50.759 67.624,48.514 68.32,46.252", style=invis];
    b -> d [pos="e,111.86,171.05 101.5,145.62 103.54,150.61 105.8,156.17 108.01,161.59", style=invis];
    b -> e [pos="e,133.72,114.86 118.76,119.87 120.45,119.31 122.17,118.73 123.89,118.16"];
    b -> f [pos="e,49.99,147.9 71.657,138.2 67.726,139.96 63.572,141.82 59.447,143.67"];
    c -> g [pos="e,133.07,19.895 104.12,19.155 110.07,19.307 116.47,19.471 122.74,19.631"];
}

input.png without modifications:

output.png

hidden nodes:

output with invisible nodes

like image 85
marapet Avatar answered Oct 12 '22 01:10

marapet