How to get balanced diagrams from graphviz? - graphviz

Is there a setting in graphviz to generate balanced diagrams like this:
correct diagram http://img3.imageshack.us/img3/6423/testaah.png
When diagram is more complex like below - it isn't balanced like that above (4 is below **).
not correctly balanced http://img51.imageshack.us/img51/6632/test2b.png
Code to generate second diagram:
graph
{
n1 [label="+"];
n1 -- n2;
n2 [label="/"];
n2 -- n3;
n3 [label="*"];
n3 -- n4;
n4 [label="1"];
n3 -- n5;
n5 [label="2"];
n2 -- n6;
n6 [label="3"];
n1 -- n7;
n7 [label="**"];
n7 -- n8;
n8 [label="4"];
n7 -- n9;
n9 [label="5"];
}

You can "introduce new, invisible nodes to re-balance the layout." (see https://graphviz.org/faq/#FaqBalanceTree). So your code becomes :
graph
{
n1 [label="+"];
n2 [label="/"];
n1 -- n2;
n1b1 [label="", width=.1, style=invis]
n1 -- n1b1 [style=invis]
n1b2 [label="", width=.1, style=invis]
n1 -- n1b2 [style=invis]
n1b3 [label="", width=.1, style=invis]
n1 -- n1b3 [style=invis]
n7 [label="**"];
n1 -- n7;
{ rank=same n2 -- n1b1 -- n1b2 -- n1b3 -- n7 [style=invis] }
n3 [label="*"];
n2 -- n3;
n2b1 [label="", width=.1, style=invis]
n2 -- n2b1 [style=invis]
n6 [label="3"];
n2 -- n6;
{ rank=same n3 -- n2b1 -- n6 [style=invis] }
n8 [label="4"];
n7 -- n8;
n7b1 [label="", width=.1, style=invis]
n7 -- n7b1 [style=invis]
n9 [label="5"];
n7 -- n9;
{ rank=same n8 -- n7b1 -- n9 [style=invis] }
n3 -- n4;
n4 [label="1"];
n3 -- n5;
n5 [label="2"];
}
With this output :

A variation on #greg's answer, that's a bit more compact and less dependent on the order of statements in the graph.
Upsides:
It doesn't use rank=same at all
Generally the source organization is a bit more 'tree-like', which makes edits easy.
Only 2 minor changes to show/hide the all nodes if needed.
It doesn't matter what order any of the edges are declared in, as long as the parent and children are all on the same line.
Downside:
Like the FAQ, it still depends on hidden nodes to balance the tree.
graph calc {
graph[nodesep=0.25, ranksep=0.3, splines=line];
node [fontname = "Bitstream Vera Sans", fontsize=14,
style=filled, fillcolor=lightblue,
shape=circle, fixedsize=true, width=0.3];
// layout all nodes 1 row at a time
// order matters on each line, but not the order of lines
"+";
"/", am, "**";
// or can be more spread out if you need to . . .
n1 [label="1"];
dm;
n2 [label="2"];
"*", bm, "3", "4", cm, "5";
// make 'mid' nodes invisible
am, bm, cm, dm [style=dotted, label=""];
// layout all visible edges as parent -> left_child, right_child
"+" -- "/","**";
"/" -- "*","3"
"**"-- "4","5";
"*" -- n1, n2;
// link mid nodes with a larger weight:
edge [style=dotted, weight=10];
"+" -- am;
"/" -- bm;
"**"-- cm;
"*" -- dm;
}
Which results in:
This is the technique I usually use when I need to draw a tree.
I don't often use gvpr because it doesn't play well in environments where I often want to use a graph (sphinx, doxygen, etc.).
But if you can use a standard pipeline to make your graph and you don't care what the resulting graph source looks like, then gvpr is your friend.

For this particular example, all you need is to loosen up the weight of the edges between the top 3 edges:
graph g{
n1 [label="+"];
n1 -- n2 [weight=0];
n2 [label="/"];
n2 -- n3;
n3 [label="*"];
n3 -- n4;
n4 [label="1"];
n3 -- n5;
n5 [label="2"];
n2 -- n6;
n6 [label="3"];
n1 -- n7 [weight=0];
n7 [label="**"];
n7 -- n8;
n8 [label="4"];
n7 -- n9;
n9 [label="5"];
}
Output:
In more complex cases where invisible nodes and edges and setting weights gets difficult to manage, you may try Emden R. Gansner's gvpr script to layout binary trees as described in an answer to this question.

Related

Make a Graphviz plot more square

I am trying to make plot of several disconnected graphs.
digraph {
// rankdir=RL
subgraph template {
node [shape=square]
edge [color=black]
subgraph top {
node [group=1]
A
B
C
D
E
}
subgraph bottom {
node [group=2]
F
G
H
}
}
C -> c
F -> f
subgraph s1 {
edge [color=red]
A -> a
B -> b1
D -> d1
E -> e1
G -> g1
H -> h1
}
subgraph s2 {
edge [color=blue]
A -> b1
B -> a
D -> d2
E -> e2
G -> g2
H -> h2
}
subgraph s3 {
edge [color=green]
A -> a
B -> b1
D -> d2
E -> e3
G -> g3
H -> h1
}
subgraph s4 {
edge [color=purple]
A -> b1
B -> a
D -> e1
E -> e2
G -> g4
H -> h1
}
subgraph s5 {
edge [ color=orange]
A -> b1
B -> a
D -> d5
E -> e1
G -> g5
H -> h1
}
subgraph s6 {
edge [ color=brown]
A -> a
B -> b1
D -> d1
E -> e6
G -> g6
H -> h1
}
subgraph s6 {
edge [ color=tan]
A -> a
B -> b2
D -> d2
E -> e6
G -> g6
H -> h1
}
}
This creates a short, wide graph.
I would like to have a taller, narrower graph. For example, moving the F, G and H trees under the A-E nodes would be good.
I tried size, which just made the nodes bigger or smaller.
I tried ratio, which stretched the graph but did not move nodes around.
I tried using group and/or rankdir, but neither did what I wanted.
I have mostly tried using fdp, but also tried dot.
I am happy with a solution that either automatically moves the nodes around or requires me to manually move them.
Any suggestions on how to achieve this?
Here are two ways:
(easiest)
make each top-level cluster a stand-alone graph.
run each graph through dot -Tdot myfileX.gv >myfileX.dot
use gvpack (https://graphviz.org/pdf/gvpack.1.pdf) to combine the individual files into one combo graph
run the combo graph through neato -n2 -Tpng >mycombo.png (see https://www.graphviz.org/faq/#FaqDotWithCoords)
gvpack -array_ib1 myfiles*.dot |neato -Tpng -n2 >oooo.png
OR
use invisible edges to connect the nodes from one cluster to another a->C. Unfortunately, this quickly becomes tedious, trying to get all the nodes to line up as you want.

Graphviz unwanted subgraph node separation stretch

I have two graphs being rendered by dot:
graph G {
graph[rankdir=LR]
node[shape=circle, fontname="Courier-Bold", fontsize=10, width=0.4, height=0.4, fixedsize=true]
edge[arrowsize=0.6, fontname="Courier-Bold", fontsize=10, arrowhead=vee]
v0 -- i0 [label=11]
v1 -- i0 [label=2]
v2 -- i0 [label=10]
i0 -- i1 [label=4]
i1 -- i2 [label=3]
i2 -- v3 [label=3]
i2 -- v4 [label=4]
i1 -- v5 [label=0, style=dashed]
}
graph G {
graph[rankdir=LR]
node[shape=circle, fontname="Courier-Bold", fontsize=10, width=0.4, height=0.4, fixedsize=true]
edge[arrowsize=0.6, fontname="Courier-Bold", fontsize=10, arrowhead=vee]
subgraph cluster_two {
fontname="Courier-Bold"
fontsize=10
v0 -- i0 [label=11]
v1 -- i0 [label=2]
v2 -- i0 [label=10]
i0 -- i1 [label=4]
i1 -- i2 [label=3]
i2 -- v3 [label=3]
i2 -- v4 [label=4]
i1 -- v5 [label=0, style=dashed]
}
}
The 2nd graph is exactly the same as the first, except that it's wrapped in subgraph. For whatever reason, the subgraph wrapping makes it so that node separation is much wider than normal:
The nodesep attribute doesn't control the nodes under a subgraph. Is there anything to get node placement to unstretch back to normal (as in the 1st picture)?
Ranksep is the attribute that sets the distance from one rank to the next. Ranksep is a graph-level attribute. When it is set, it applies to the entire graph. This is also true for nodesep, except nodesep sets the distance between two adjacent nodes on the same rank.
p.s. 2.40.1 is a rather old release. Many fixes have been applied over the last 18 months or so (I take no credit).

How do I order subgraph clusters when using dot?

I have a dot file in which I create subgraph clusters which I want to appear in a specific order, let's say I have this:
digraph G {
splines=true;
sep="+25,25";
overlap=scalexy;
nodesep=0.6;
subgraph cluster_2 {
label="ADD_MORE_PROBLEMS";
subgraph cluster_3 {
label="pattern";
N1 [label="problem"];
}
subgraph cluster_4 {
label="replacement";
N2 [label="problem"];
N3 [label="problem"];
}
}
}
Which creates:
How do I ensure that "pattern" appears to the left of "replacement" (I may have an arbitrary number of subgraphs).
Clusters are one of the odd cases where simply the ordering in the code makes most (if not quite all) of the difference. If we simply reorder your code like this:
digraph G {
splines=true;
sep="+25,25";
overlap=scalexy;
nodesep=0.6;
subgraph cluster_2 {
label="ADD_MORE_PROBLEMS";
subgraph cluster_4 {
label="replacement";
N2 [label="problem"];
N3 [label="problem"];
}
subgraph cluster_3 {
label="pattern";
N1 [label="problem"];
}
}
}
that makes all the difference.
Now, that can fail, in which case setting up invisible edges is one of the more common solutions.
I can't give and answer, but can provide some clarification. The usual approach to force layout is to introduce hidden edges. In this case, it does not work.
Without the nested clusters, you can use rank=same to force connected edges onto the same level. Then, an invisible edge N1 -> N2 [style = invis] would force the nodes into the correct ordering.
However, constraining nodes with rank breaks the cluster membership and prevents the scheme from working.
The modified graph shows the result. There may not be a general solution.
digraph G {
splines=true;
sep="+25,25";
overlap=scalexy;
nodesep=0.6;
subgraph cluster_2 {
label="ADD_MORE_PROBLEMS";
subgraph cluster_3 {
label="pattern";
N1 [label="problem 1"];
}
subgraph cluster_4 {
label="replacement";
N2 [label="problem 2"];
N3 [label="problem 3"];
}
// Introduce hidden edge (shown dashed)
N1 -> N2 [style = dashed];
// Force nodes to remain at same rank
{ rank = same; N1; N2; }
}
}

Random change of node order in GraphViz/dot

I am trying to paint a little graph in GraphViz/dot.
The boxes "Team 1", "Turnier32" and "Team 2" should be on one horizontal level.
"Team 1" points right to "Turnier32", "Team 2" to the left.
I want the ellipses "Name1", "2015-11-14" and "Name2" below the corresponding nodes. There should only be straight horizontal and vertical edges.
My current approach is this:
digraph turnier{
ordering=out ;
subgraph {
rank=same;
T1 [shape="box", label="Team 1"];
TOURNAMENT [shape="box", label="Turnier32"];
T2 [shape="box", label="Team 2"];
}
subgraph {
rank=same;
N1 [shape="ellipse", label="Name1"];
DATE [shape="ellipse", label="2015-11-14"];
N2 [shape="ellipse", label="Name2"];
}
T1 -> N1 [label="hasName"];
TOURNAMENT -> DATE [label="occuredOnDate"];
T2 -> N2 [label="hasName"];
T1 -> TOURNAMENT [label="attended"];
T2 -> TOURNAMENT [label="attended"];
}
GraphViz reordered T1, TOURNAMENT and T2 to T1, T2, TOURNAMENT, which is not, what I want. If i strike out the last two edges, the order is correct.
Simply add to your last line constraint=false:
T2 -> TOURNAMENT [label="attended", constraint=false];

complicated graphviz tree structure

I am trying to create a tree structure with graphviz. I am open to either writing the graphviz code by hand or using the ruby-graphviz gem for ruby. Given the below picture can anyone provide any insight on the necessary code? Ignore that the lines are not straight...they should be when graphviz builds the graph. I am open to having dots/points when lines intersect as well.
I have played with ruby-graphviz and the family tree class...this is getting me part of the way there but I really need all lines to be straight and intersect at right angles and the out-of-the-box code doesn't seem to do that.
The code should be generic enough to allow for the "C" box to have children as well and for there also to be more children under "A".
The colors are irrelevant...the examples can exclude any coloring.
http://docs.google.com/drawings/pub?id=1lUTfgKP_LN0x7C3ItbsFjfLBuDTL84AtmoaW7YFn32Y&w=1036&h=713
A little late, I know, but I just wanted to show an other version without having to figure out the exact positions of each node.
digraph {
splines=false;
ranksep=0.05;
node[shape=box, color=lightblue, style=filled];
A;B;C;D;E;
node[shape=none, color=none, style=solid];
i1[label="Item 1"];
i2[label="Item 2"];
i3[label="Item 3"];
node[label="", width=0, height=0];
edge[arrowhead=none, color=blue];
{rank=same; n2; n1; n3;}
n2; n1; n3;
A -> n1;
n2 -> n1 -> n3;
{rank=same; B; C;}
n2 -> B;
n3 -> C;
{rank=same; n4; D;}
B -> n4 -> D;
{rank=same; n6; n5; i1;}
D -> n5 -> i1;
n4 -> n6;
{rank=same; n7; E;}
n6 -> n7 -> E;
{rank=same; n8; i2;}
E -> n8 -> i2;
{rank=same; n9; i3;}
i2 -> n9 -> i3;
}
Straight lines are enforced by:
splines=false - say no to splines
invisible nodes (nodes n1, n2,... n9)
placing nodes on the same rank with rank=same
It's still some work to get the dot file right, but it beats imho calculating yourself the position of each node.
Output looks like this:
As long as C has no child nodes, you'd have to apply some more trickery (invisible nodes) to display it all the way to the right.
In order to get a more general solution for different graphs, some further adaptations will probably be needed (apply weight to vertical edges, or group nodes which have to be vertically aligned, or use subgraphs, ...).
As far as I know this requires a little work-around; I will only do it in Graphviz DOT language. I give you the solution first, then provide some explanations of how you can extend it.
This is the resulting figure:
This is the Graphviz code producing the figure:
graph atree {
Item1 [shape=none,label="Item 1",pos="2.2,1.1!"];
Item2 [shape=none,label="Item 2",pos="2.2,0.1!"];
Item3 [shape=none,label="Item 3",pos="2.9,-0.3!"];
A [shape=box,color=lightblue,style=filled,pos="2,3!"];
B [shape=box,color=lightblue,style=filled,pos="1,2.1!"];
C [shape=box,color=lightblue,style=filled,pos="3,2.1!"];
D [shape=box,color=lightblue,style=filled,pos="1.5,1.5!"];
E [shape=box,color=lightblue,style=filled,pos="1.5,0.5!"];
D0 [style=invisible,fixedsize=true,width=0,height=0,pos="2,2.5!",label=""];
D1 [style=invisible,fixedsize=true,width=0,height=0,pos="1,2.5!",label=""];
D2 [style=invisible,fixedsize=true,width=0,height=0,pos="3,2.5!",label=""];
D3 [style=invisible,fixedsize=true,width=0,height=0,pos="1,1.5!",label=""];
D4 [style=invisible,fixedsize=true,width=0,height=0,pos="1,0.5!",label=""];
D5 [style=invisible,fixedsize=true,width=0,height=0,pos="1.5,1.1!",label=""];
D6 [style=invisible,fixedsize=true,width=0,height=0,pos="1.5,0.1!",label=""];
D7 [style=invisible,fixedsize=true,width=0,height=0,pos="2.2,-0.3!",label=""];
A -- D0 -- D1 -- B -- D3 -- D4 -- E [color=blue];
E -- D6 -- Item2 -- D7 -- Item3 [color=blue];
D0 -- D2 -- C [color=blue];
D3 -- D -- D5 -- Item1 [color=blue];
}
If you put it in a file named inputfile.dot you can get the resulting image file by using the command neato -Tpng inputfile.dot > outfile.png.
Now a couple of comments on how it works: The code building the tree with A, B, C, D, E, Item1, Item2, Item3 is straightforward (the attributes merely set the colors and box styles). The trick to get the lines straight and orthogonal consists of 1) adding invisible nodes with zero size to the graph, and 2) positioning all objects in absolute coordinates on the canvas. The auxiliary nodes D1, D2, D3, D4, D5, D6, D7 are needed for step 1) and the pos="x,y!" options are needed for step 2). Note, that you need the ! sign at the end of the pos command since otherwise the positions would not be considered final and the layout would still get changed.
You can add additional nodes by first positioning a new node (by using the code for the nodes A ... Item3 as a template), adding an invisible, auxiliary node (with pos such that all connections to and from it are orthogonal) and then adding the connection to the graph via <StartingNode> -- <AuxiliaryNode> -- <NewNode>.
Still another version using splines=ortho, which needs less hidden nodes and gives similar visual result.
digraph example {
splines=ortho;
ranksep=0.05;
node[shape=box, color=lightblue, style=filled];
A;B;C;D;E;
node[shape=none, color=none, style=solid];
i1[label="Item 1"];
i2[label="Item 2"];
i3[label="Item 3"];
node[label="", width=0, height=0];
edge[arrowhead=none, color=blue];
n1; n2; n3; n4; n5;
{rank=same; B; C;}
A -> n1;
n1 -> B;
n1 -> C;
{rank=same; n2; D;}
B -> n2;
n2 -> D;
{rank=same; n3; i1;}
D -> n3;
n3 -> i1;
{rank=same; n4; E;}
n2 -> n4;
n4 -> E;
{rank=same; n5; i2;}
E -> n5;
n5 -> i2;
{rank=same; n6; i3;}
i2 -> n6;
n6 -> i3;
}

Resources