Graphviz unwanted subgraph node separation stretch - graphviz

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).

Related

Graphviz: Edge orientation to and from same node

I'm creating some multilevel SEM graphs and I'm running into a small problem. The latent variable at the bottom of the graph (labeled "WF") is supposed to have a double-headed edge to and from it but the edge should go under the node. Note the correct orientation of the topmost node. Seems and easy fix but I can find it. Thanks in advance. (BTW, I've provided only a snippet of the original model, but this is sufficient for the purpose.)
digraph stackex {
{rank=min;
bf [shape=ellipse]
bf:nw -> bf:ne [dir = both]}
{node[shape=square]
bf -> i1
i1[label=X1]
i1 -> wf [dir=back]}
{wf [shape=ellipse]
wf:sw -> wf:se [dir = both]}
}
And here's what it produces:
The double-headed arrow should go under the node labled "WF".
Based on experimentation, it appears that the only way to get
wf:sw -> wf:se [dir = both]
to do what you want is to add
graph [rankdir=TB]
Unfortunately, rankdir affects the entire graph (not just a subgraph or cluster). So you can fix one loop, but you break the other.
The only way I've found to accomplish your goal is to hand-modify the pos of the offending edge (spline). This:
digraph stackex {
graph [bb="0,0,82.583,216"];
node [label="\N",
shape=square
];
bf [height=0.5,
pos="41.292,162",
shape=ellipse,
width=0.75];
bf:nw -> bf:ne [dir=both,
pos="s,26.292,177 e,56.292,177 22.277,186.17 18.135,200.16 21.792,216 41.292,216 60.792,216 64.448,200.16 60.306,186.17"];
i1 [height=0.5,
label=X1,
pos="41.292,90",
width=0.5];
bf -> i1 [pos="e,41.292,108.1 41.292,143.7 41.292,135.98 41.292,126.71 41.292,118.11"];
wf [height=0.5,
pos="41.292,18",
shape=ellipse,
width=0.75];
i1 -> wf [dir=back,
pos="s,41.292,71.697 41.292,61.665 41.292,53.054 41.292,43.791 41.292,36.104"];
wf:se -> wf:sw [dir=both,
pos="s,56.292,3 e,26.292,3
65.002,8.3185
92.908,0.389
88.823,-20
41.292,-20
-6.2395,-20
-10.324,0.389
17.582,8.3185"];
}
And this command
neato -n2 -Tpng doubleheaded3.fixed.dot >doubleheaded3.fixed.png
Gives this:
All in all, I'd suggest the pic/gpic/dpic language. Lower-level, but probably easier to use in the long run.

In a graphviz dot digraph, how can I break a wide layout (rankdir LR)

With python I'm trying to generate a long graph where always one node points to the next. This ends up in having a long snail of nodes (rankdir LR). However I want to break it after a certain width or number or nodes. How can this be achived?
graph = gv.Digraph(format='svg')
graph.graph_attr.update({'rankdir': 'LR'})
graph.node('a', 'A')
graph.node('b', 'B')
graph.node('c', 'C')
graph.node('d', 'D')
graph.node('e', 'E')
graph.node('f', 'F')
...
graph.edges(['ab', 'bc', 'cd', 'de', 'ef', ...])
Output:
However I want (or similar):
I tried to use size, but that only zooms the whole graph.
As a workarround I tried to reduce ranksep, but that only makes it better for a few more items.
I also searched a lot but could not find an appropriate answer.
An unanswered question that goes into a similar direction is:
graphviz plot too wide.
For other related questions suggested answer was to use invisible elements but that does not work here either.
Update:
I've altered the code for edges according to the comment of #vaettchen:
graph.edge('a', 'b', None, {'weight':'5'})
graph.edge('b', 'c', None, {'weight':'5'})
graph.edge('d', 'e', None, {'weight':'5'})
graph.edge('e', 'f', None, {'weight':'5'})
graph.edge('c', 'd', None, {'weight':'1'})
graph.edge('a', 'd', None, {'style':'dashed', 'rank':'same'})
Unfortunately the result now looks like this (style 'dashed' instead of 'invis' for better visibility):
'rank': 'same' seems not change anything. Also when applied to nodes A and D.
This should be a comment rather than an answer as it doesn't address the python issue and I guess you are also looking for something more "automatic" - but maybe it gives some ideas; and as nobody else is picking it up, here a pure graphviz suggestion:
digraph so
{
// graph attributes
rankdir = LR; // horizontal graph
splines = ortho // edges with "corners"
// default/initial node style
node[ shape = box ];
// nodes where the "new lines" begin
// connected invisibly to keep them in order
{ rank = same; A -> E -> I[ style = invis ] }
// nodes that are to be in one line
// extra weight needed to keep the edges straight
edge[ weight = 5 ];
A -> B -> C -> D;
E -> F -> G -> H;
I -> J -> K -> etc;
// edges connecting the graph elements over the lines
edge[ weight = 1 ];
D -> E;
H -> I;
}
yields
There are several ways to make this "snake".
First, to create right-angle edge bends, apply to all edges attribute splines=ortho.
Variant 1
Use edge attributes such as constraint=false or weight=0 for C -> D edge to create "soft" edge and rank=same for A, D nodes to create "strong" alignment between these nodes.
DOT script:
digraph {
graph[rankdir=LR;splines=ortho]
node[shape=box]
A -> B -> C
D -> E -> F
C -> D [constraint=false]
{rank=same;A;D}
}
Variant 2
Use group attribute to create "strong" alignment between A, B, C nodes and between D, E. F nodes; and rank=same for A, D nodes to create "strong" alignment between these nodes.
DOT script:
digraph {
graph[rankdir=LR;splines=ortho]
node[shape=box]
A [group=g1]
B [group=g1]
C [group=g1]
D [group=g2]
E [group=g2]
F [group=g2]
A -> B -> C -> D -> E -> F
{rank=same;A;D}
}
Both variant give the same result, I suppose that you can also use the neato engine to set the exact coordinates of the nodes, but it looks overcomplicated.
Minimal code example (for variant 1) with comments:
import graphviz as gv
nodes = ['A','B','C','D','E','F']
# Count of nodes in row,
# can be changed for the desired graph width
columns = 3
graph = gv.Digraph(format='svg', filename = "output/mygraph.gv",
graph_attr=dict(rankdir='LR', splines='ortho'),
node_attr=dict(shape='box'))
# Set constraint=false only for desired edges, for example
# only for every 3rd edges, where `3` is set by `columns` variable
for i in range(1, len(nodes)):
if i % columns == 0 :
graph.edge(nodes[i-1], nodes[i], constraint='false')
else:
graph.edge(nodes[i-1], nodes[i])
# Add desired nodes to `rank=same` subgraph
with graph.subgraph() as s:
s.attr(rank='same')
for i in range(0, len(nodes)):
if i % columns == 0 :
s.node(nodes[i])
graph.view()
Result image:
Result mygraph.gv:
digraph {
graph [rankdir=LR splines=ortho]
node [shape=box]
A -> B
B -> C
C -> D [constraint=false]
D -> E
E -> F
{
rank=same
A
D
}
}
Possible improvements
If there is one node on the line, creates a non-consistent last arrow:
This can be corrected by creating an invisible node inv2 between the nodes F and G:
digraph {
graph [rankdir=LR splines=ortho nodesep=.2]
node [shape=box]
A -> B
B -> C
C -> inv1 [constraint=false arrowhead=none]
inv1 -> D [constraint=false ]
D -> E
E -> F
F -> inv2 [constraint=false arrowhead=none]
inv2 -> G [constraint=false]
{
rank=same
A
inv1 [shape=point width=.01]
D
inv2 [shape=point width=.01]
G
}
}
Result:

Graphviz Vertical Ordering

I have a set of GraphViz nodes such that:
digraph {
A->B;
A->C;
A->D;
}
But B, C, and D happen sequentially in time!
It would be great if there was some way to indicate the vertical level each node should appear upon (where the number of levels may be unknown beforehand).
Does anyone have thoughts on how to accomplish this?
One option to have a node display on a different rank (vertical level) than an other node is to add invisible edges.
Assigning those nodes the same group indicates graphviz to lay them out in a straight line if possible.
For example:
digraph g{
A;
node[group=a];
B;C;D;
A -> B;
A -> C;
A -> D;
edge[style=invis];
B->C->D;
}
An other option is to have one vertical line of (invisible) nodes, then force the same rank by defining the nodes of the same rank within the same subgraph with rank=same:
digraph g{
{rank=same; l1[style=invis, shape=point]; A;}
{rank=same; l2[style=invis, shape=point]; B;}
{rank=same; l3[style=invis, shape=point]; C;}
{rank=same; l4[style=invis, shape=point]; D;E;F;}
A -> B;
A -> C;
A -> D;
edge[style=invis];
l1->l2->l3->l4;
}

simple "T shaped" graph in graphviz

Need draw a graph with dot/graphviz like this image:
The texts can be above arrows, like graphviz does it. But how to achieve the T-layout? Need make a cluster for the top row?
This is one possibility using rank=same for a subgraph:
digraph g {
node[shape=point, width=0.2];
{
rank=same;
p1 -> n [label="text1"];
n -> p2 [label="text2"];
}
n -> p3 [label="text3", dir=back];
n[label="node", shape=rect, style=rounded];
}
You could also use a left-right layout instead of top-down.
An other possibility is to disable the effect of some edges using constraint=false:
digraph g {
node[shape=point, width=0.2];
p1 -> n [label="text1", constraint=false];
n -> p2 [label="text2", constraint=false];
n -> p3 [label="text3", dir=back];
n[label="node", shape=rect, style=rounded];
}
The result is the same.
dot usually layouts trees in layers. To force an edge to not be a layer separation you can add the constraint=false option. So something like:
digraph {
A [shape=point]
B [shape=point]
C [shape=point]
N [label="node"]
A -> N [label="text1", constraint=false]
N -> B [label="text2", constraint=false]
N -> C [label="text3", dir=back]
}
should work.
Note that the edge from the lower node to "node" has to be backwards, since dot layouts trees from top to bottom. Therefore the logical edge direction has to be from top to bottom, even though the display direction might be the other way round (which is the case here).

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