How to dynamically increase the distance between two nodes in GraphViz? - graphviz

I'm using this Python project "Family Tree Maker" to generate a family tree. It works with GraphViz, creates a nice DOT file and then a PNG. "Minimum Viable Product" check. It works :D
Now I'd like to make it a bit nicer.
Is there a simple way (e.g. not having to increase the length of the edge per hand) to force the right-most vertical edge (between Father1-Mother2 and Franz) to be straight/vertical like displayed below?
Thanks!
Edit: as correctly stated by #albert in the comments, webgraphviz.com displays the correct output while viz-js.com doesn't.
PS: Here's the DOT code that generate this Graph:
digraph {
graph [splines=ortho];
node [shape=box, fontname = "calibri"];
edge [dir=none];
Father1[label="Father1",style=filled,fillcolor=azure2];
Mother1[label="Mother1",style=filled,fillcolor=bisque];
Child1[label="Child 1",style=filled,fillcolor=azure2];
Child2[label="Child 2",style=filled,fillcolor=bisque];
Child3[label="Child 3",style=filled,fillcolor=azure2];
Child4[label="Child 4",style=filled,fillcolor=azure2];
Mother2[label="Mother2",style=filled,fillcolor=bisque];
Franz[label="Franz",style=filled,fillcolor=azure2];
{ rank=same;
Mother1 -> h0 -> Father1;
h0[shape=circle,label="",height=0.01,width=0.01];
Father1 -> h1 -> Mother2;
h1[shape=circle,label="",height=0.01,width=0.01];
}
{ rank=same;
h0_0 -> h0_1 -> h0_2 -> h0_3 -> h0_4;
h0_0[shape=circle,label="",height=0.01,width=0.01];
h0_1[shape=circle,label="",height=0.01,width=0.01];
h0_2[shape=circle,label="",height=0.01,width=0.01];
h0_3[shape=circle,label="",height=0.01,width=0.01];
h0_4[shape=circle,label="",height=0.01,width=0.01];
h0_4 -> h1_0 [style=invis];
h1_0;
h1_0[shape=circle,label="",height=0.01,width=0.01];
}
h0 -> h0_2;
h0_0 -> Child1;
h0_1 -> Child2;
h0_3 -> Child3;
h0_4 -> Child4;
h1 -> h1_0;
h1_0 -> Franz;
{ rank=same;
Child1 -> Child2 [style=invis];
Child2 -> Child3 [style=invis];
Child3 -> Child4 [style=invis];
Child4 -> Franz [style=invis];
}
{ rank=same;
}
}

As expected after discussing with #Albert in the comments, using version 2.28 of GraphViz, I get the result I wanted.
I created an issue report on GraphViz's Gitlab: https://gitlab.com/graphviz/graphviz/issues/1627

Related

How can I cross out a node in Graphviz?

I would like to indicate that a node should be there, but is currently lacking in the process.
Intuitively I would like to cross it out as shown in below image (now done manually in Paint):
Is there a node attribute in Graphviz that I can use for this?
I can't find an attribute or node shape to do what you want, but here are two ways to do it:
build an image outside of Graphviz (with the text and the X) and use the image attribute to use the image as the node (yes, a pain if you want to do this frequently):
b [image="myB.png"]
For every X'd out node, add 2 new edges from .ne to .sw and .nw to .se (see below) Each with this (new) attribute: straightline=1. Then run this command:
dot -Tdot Xout2.gv |gvpr -f straightline.gvpr -c | neato -n2 -Tpng >out.png
Where this is straightline.gvpr:
E[straightline==1]{
int i, n;
string pt[int];
double x1, y1, x2, y2, xI1, yI1, xI2, yI2;
n=split($.pos, pt, " ");
for (i=0;i<=1;i++){
if (match(pt[i],"e,")>=0){
print ("// BEFORE: ", pt[i]);
pt[n-1]=substr(pt[i],2);
print ("// AFTER: ", pt[i]);
pt[i]=pt[i+1];
}
}
for (i=0;i<=1;i++){
if (match(pt[i],"s,")>=0){
pt[0]=substr(pt[i],2);
}
}
sscanf (pt[0], "%f,%f", &x1, &y1);
sscanf (pt[n-1], "%f,%f", &x2, &y2);
xI1=x1+(x2-x1)*.3;
yI1=y1+(y2-y1)*.3;
xI2=x1+(x2-x1)*.7;
yI2=y1+(y2-y1)*.7;
$.pos=sprintf("%s %.3f,%.3f %.3f,%.3f %s", pt[0], xI1, yI1, xI2, yI2, pt[n-1]);
}
From this input:
digraph X{
graph [outputorder=edgefirst]
b [ label="X me"]
a -> b -> c
a -> d
d -> c
e -> f
g -> i -> k
edge [color="#ff000080" penwidth=2] // note translucent color
b:ne -> b:sw [straightline=1]
b:se -> b:nw [straightline=1]
edge [color="green" penwidth=2]
e:n -> e:s [straightline=1]
f:w -> f:se [straightline=1]
edge [color="orange" penwidth=2]
g:n -> g:se [dir=back straightline=1]
edge [color="blue" penwidth=2]
g:n -> g:sw [dir=back straightline=1]
i:e -> i:w [dir=none straightline=1]
k -> k:s [dir=both straightline=1]
}
Sorry, convoluted, but it works.
While the answer of sroush gives me the exact output I need, it requires that I understand how to introduce gvpr in my workflow which will take a bit of time.
In the meantime I came up with a dot only approach, which approximates crossing out a node sufficiently for my purpose.
In below graph I would like to cross out the node Some process:
digraph graphname {
rankdir=LR
node [fillcolor="lightblue3", style="filled"]
a
c
d
b [label="Some\nprocess"]
a -> b -> c
a -> d -> c
{rank=same a;d}
}
To do so I change:
the nodestyle of the Some process node to have a diagonal hard gradient
use a HTML-like label to strikethrough the text
Make the fontcolor and node outline a shade of gray
digraph graphname {
rankdir=LR
node [fillcolor="lightblue3", style="filled"]
a
c
d
node [fillcolor="lightblue3;0.5:white", style="filled", fontcolor="gray50", color="gray50", gradientangle=100]
b [label=<<s>Some<br/>process</s>>]
a -> b -> c
a -> d -> c
{rank=same a;d}
}

Can DOT produce a more structured graph?

I'm not really sure how to describe what my client wants, so I'll let a picture do most of the talking. I'm using DOT to produce graphs for what is more or less the bill of materials problem. (Show an incoming lot and all the outgoing lots, at all levels, that were created from the material in the incoming lot.) I've got the code to create a graph that contains the data structured appropriately. For example, I generate this GV file:
digraph LotTrc {
rankdir=LR;
graph[label="Lot #AD626", labelloc=top, labeljust=left, fontsize=24];
PO_AD626_0000003333[shape=triangle,color=greenyellow,style=filled,label=AD626];
AJ_AD626_SJ00000099[shape=circle,color=red2,style=filled,label=AD626];
PO_AD626_0000003333 -> AJ_AD626_SJ00000099;
AJ_AD626_SJ00000103[shape=circle,color=red2,style=filled,label=AD626];
PO_AD626_0000003333 -> AJ_AD626_SJ00000103;
WO_AD627_RE00002230[shape=ellipse,color=lemonchiffon,style=filled,label=AD627];
PO_AD626_0000003333 -> WO_AD627_RE00002230;
SO_AD627_OZ00025429[shape=box,color=cyan3,style=filled,label=AD627];
WO_AD627_RE00002230 -> SO_AD627_OZ00025429;
SO_AD627_OZ00025434[shape=box,color=cyan3,style=filled,label=AD627];
WO_AD627_RE00002230 -> SO_AD627_OZ00025434;
SO_AD627_OZ00025439[shape=box,color=cyan3,style=filled,label=AD627];
WO_AD627_RE00002230 -> SO_AD627_OZ00025439;
SO_AD627_OZ00025444[shape=box,color=cyan3,style=filled,label=AD627];
WO_AD627_RE00002230 -> SO_AD627_OZ00025444;
WO_AD628_RE00002231[shape=ellipse,color=lemonchiffon,style=filled,label=AD628];
PO_AD626_0000003333 -> WO_AD628_RE00002231;
SO_AD628_OZ00025430[shape=box,color=cyan3,style=filled,label=AD628];
WO_AD628_RE00002231 -> SO_AD628_OZ00025430;
SO_AD628_OZ00025435[shape=box,color=cyan3,style=filled,label=AD628];
WO_AD628_RE00002231 -> SO_AD628_OZ00025435;
SO_AD628_OZ00025440[shape=box,color=cyan3,style=filled,label=AD628];
WO_AD628_RE00002231 -> SO_AD628_OZ00025440;
SO_AD628_OZ00025445[shape=box,color=cyan3,style=filled,label=AD628];
WO_AD628_RE00002231 -> SO_AD628_OZ00025445;
WO_AD629_RE00002232[shape=ellipse,color=lemonchiffon,style=filled,label=AD629];
PO_AD626_0000003333 -> WO_AD629_RE00002232;
SO_AD629_OZ00025431[shape=box,color=cyan3,style=filled,label=AD629];
WO_AD629_RE00002232 -> SO_AD629_OZ00025431;
SO_AD629_OZ00025436[shape=box,color=cyan3,style=filled,label=AD629];
WO_AD629_RE00002232 -> SO_AD629_OZ00025436;
SO_AD629_OZ00025441[shape=box,color=cyan3,style=filled,label=AD629];
WO_AD629_RE00002232 -> SO_AD629_OZ00025441;
SO_AD629_OZ00025446[shape=box,color=cyan3,style=filled,label=AD629];
WO_AD629_RE00002232 -> SO_AD629_OZ00025446;
WO_AD630_RE00002233[shape=ellipse,color=lemonchiffon,style=filled,label=AD630];
PO_AD626_0000003333 -> WO_AD630_RE00002233;
SO_AD630_OZ00025432[shape=box,color=cyan3,style=filled,label=AD630];
WO_AD630_RE00002233 -> SO_AD630_OZ00025432;
SO_AD630_OZ00025437[shape=box,color=cyan3,style=filled,label=AD630];
WO_AD630_RE00002233 -> SO_AD630_OZ00025437;
SO_AD630_OZ00025442[shape=box,color=cyan3,style=filled,label=AD630];
WO_AD630_RE00002233 -> SO_AD630_OZ00025442;
SO_AD630_OZ00025447[shape=box,color=cyan3,style=filled,label=AD630];
WO_AD630_RE00002233 -> SO_AD630_OZ00025447;
WO_AD631_RE00002234[shape=ellipse,color=lemonchiffon,style=filled,label=AD631];
PO_AD626_0000003333 -> WO_AD631_RE00002234;
SO_AD631_OZ00025433[shape=box,color=cyan3,style=filled,label=AD631];
WO_AD631_RE00002234 -> SO_AD631_OZ00025433;
SO_AD631_OZ00025438[shape=box,color=cyan3,style=filled,label=AD631];
WO_AD631_RE00002234 -> SO_AD631_OZ00025438;
SO_AD631_OZ00025443[shape=box,color=cyan3,style=filled,label=AD631];
WO_AD631_RE00002234 -> SO_AD631_OZ00025443;
SO_AD631_OZ00025448[shape=box,color=cyan3,style=filled,label=AD631];
WO_AD631_RE00002234 -> SO_AD631_OZ00025448;
}
and it produces this graph:
But what my client really wants is something that looks more like this, where the edges are straight lines, using 90 degree angles as needed. (Note that this is generic, not based on the example above.)
Is there a way to use DOT to produce something like that?
You can experiment with splines=ortho graph attibute. It makes the very straight connections with 90 degree angles.
But I won't recommend it. It's almost impossible to control them, port specification often doesn't work with them, and also, ortho splines may eat up some of the edge lables.
Possible solution would be using dummy nodes with point shape (this shape is convenient because it removes node lable by default) and width=0. Use these dummy nodes in places where the 90 degree turn is needed. You will have to group them with main nodes in subgraphs and add rank=same attribute to force these nodes to stay at the same level.
You would probably also need to add weight to some edges to prevent them from being bent (edges with higher weight tend to be straight).
Example
I've implemented part of your example graph using mentioned techniques, the code and image are below:
digraph {
rankdir=LR
ranksep=1
nodesep=0.5
LOT1 [shape=rect]
LOT2 [shape=rect]
LOT3 [shape=rect]
LOT4 [shape=rect]
LOT5 [shape=rect]
{rank=same
PO
dot1 [shape=point width=0]
dot2 [shape=point width=0]
PO -> dot1 -> dot2 [arrowhead=none]
}
dot1 -> WO1 [weight=20]
{
rank=same
WO1
dot21 [shape=point width=0]
dot22 [shape=point width=0]
WO1 -> dot21 -> dot22 [arrowhead=none]
}
dot21 -> LOT1 [weight=20]
dot22 -> LOT2 [weight=20]
{
rank=same
dot31 [shape=point width=0]
dot32 [shape=point width=0]
dot33 [shape=point width=0]
dot31 -> dot32 -> dot33 [arrowhead=none]
}
dot2 -> WO2 [weight=20]
{
WO2
rank=same
dot23 [shape=point width=0]
dot24 [shape=point width=0]
dot25 [shape=point width=0]
WO2 -> dot23 -> dot24 -> dot25 [arrowhead=none]
}
dot23 -> LOT3 [weight=20]
dot24 -> LOT4 [weight=20]
dot25 -> LOT5 [weight=20]
dot31 -> SO1
dot33 -> SO2
LOT1 -> dot32
}
Result:

Timeline of historic events in Dot

I am trying to create a timeline that associates years with historical events in DOT, but somehow don't get along with the DOT User's Guide.
Here is my first take:
digraph {
ratio=0.70 ranksep=.75
{
node [shape=plaintext, fontsize=16];
past -> 1933 -> 1943 -> 1988 -> 2001 -> 2015 -> future;
node [shape=plaintext, fontsize=14];
a -> b -> c -> d -> e -> f -> g;
{rank=same past -> a }
{rank=same 1943 -> c }
{rank=same future g }
}
}
The timeline on the left is taken from the User's Guide. The line on the right was meant to contain the corresponding information on the historic event, e.g. for the year 1943 the info is c.
The output looks like this:
However, I would like to have the line on the right much closer to the left timeline. And the historic events on right should not be connected by an arrow with each other.
Somehow the connection between f and g is messed up, too.
Both the ratio and the ranksep influence the distance between the nodes. I would advise using one or the other to see what the difference is. Removing both gives a shorter distance.
To remove the arrows between the events on the right, edge style can be set to "invis".
Running the posted DOT (see the resulting image) does not show any weird connection between f and g...
Using the following DOT:
digraph {
node [shape=plaintext, fontsize=16];
past -> 1933 -> 1943 -> 1988 -> 2001 -> 2015 -> future;
{
node [fontsize=14];
edge [style=invis];
a -> b -> c -> d -> e -> f -> g;
}
{rank=same past -> a }
{rank=same 1943 -> c }
{rank=same future g }
}
Yields this result:

Graphviz removes node from cluster

I'm using dot to compile. So I have two nodes in cluster0 (MATH1036 and MATH1034). When I try to make an edge from MATH1034 to a node outside the cluster (n1), it freaks out and removes MATH1034 from cluster0.
digraph G {
labelloc="t";
label="";
graph [splines=spline, nodesep=1]
compound=true;
subgraph cluster0{
label="Math 1";
MATH1034[label="MATH1034\nAlgebra"];
MATH1036[label="MATH1036\nCalculus"];
{rank=same;MATH1036->MATH1034;}
}
COMS1015[label="COMS1015\nBCO"];
COMS1017[label="COMS1017\nALG"];
COMS1016[label="COMS1016\nDCS"];
COMS1018[label="COMS1018\nADS"];
subgraph cluster1{
label="Math 2";
MATH2007[label="MATH2007\nMC"];
MATH2018[label="MATH2018\nGT"];
MATH2019[label="MATH2019\nLA"];
STAT2XXX[label="STAT2XXX\nIntro to MS\nor\nSTAT1003\nStats 1"];
}
COMS2003[label="COMS2003\nAAA"];
COMS2XXX[label="COMS2XXX\nMC"];
COMS2002[label="COMS2002\nDBF"];
COMS2001[label="COMS2001\nOS"];
COMS3000[label="COMS3000\nAAI"];
COMS3003[label="COMS3003\nFLA"];
COMS3004[label="COMS3004\nAN"];
COMS3002[label="COMS3002\nSE"];
// This line will hide the formatting nodes.
//node[shape=none,width=0,height=0, label=""];
// THIS NEXT LINE CAUSES THE PROBLEM
// If I remove MATH1034 from this line, things go normal.
{rank=same;MATH1034->n1[ltail=cluster0,dir=none ]; n1->n2->n3->n4->n5[dir=none];}
n1->COMS1015[style=dotted];
n2->COMS1016[style=dotted];
n4->COMS1017[style=dotted];
n5->COMS1018[style=dotted];
MATH1034 -> MATH2007[lhead=cluster1, ltail=cluster0];
MATH2018 -> STAT2XXX[style=invis];
MATH2007 -> MATH2019[style=invis];
//edge[dir=none];
n3->n6->n7[arrowhead=none];
{rank=same; COMS1016->n6->COMS1017[style=invis];}
{rank=same; COMS2001->n7[style=invis]; n7->COMS2003;}
COMS1015 -> COMS2001;
//{rank=same; COMS1017 -> p1 -> COMS1018;}
//p1 -> COMS2003;
//p1 -> COMS2XXX;
COMS1017 -> COMS2XXX;
COMS1017 -> COMS2001;
COMS1017 -> COMS2003;
COMS1018 -> COMS2003;
COMS1018 -> COMS2XXX;
COMS1018 -> COMS2002;
COMS1016 -> COMS2003;
COMS1016 -> COMS2001[weight=100,style=invis];
MATH2007 -> COMS2001[ltail=cluster1,style=dotted]
MATH2007 -> COMS2003[ltail=cluster1,style=dotted]
{rank=same;COMS2XXX -> COMS2002[dir=back, style=dotted]}
{rank=same;COMS2003 -> COMS2XXX[dir=back]}
subgraph cluster5{
label="";
{rank=same;COMS3004 -> COMS3003 -> COMS3000 -> COMS3002[style=invis];}
}
COMS2003 -> COMS3000[weight=1000];
COMS1016 -> COMS3003;
COMS2001 -> COMS3004[weight=1000];
COMS2002 -> COMS3002[weight=1000];
MATH2007 -> COMS3004[ltail=cluster1,lhead=cluster5];
}
The MATH modules should be next to each other and in a box.
Here is the very broken one:
Here is the correct layout, but without the edge between MATH1034 and n1:
Any help would really be appreciated I've looked everywhere and nothing seems to work.
The node Math1034 is in two different subgraphs which isn't allowed. dot actually emits the following warning:
Warning: MATH1034 was already in a rankset, deleted from cluster G
Warning: MATH1034 -> MATH2007: tail not inside tail cluster cluster0
Warning: MATH1034 -> n1: tail not inside tail cluster cluster0
The solution is to remove MATH1034 from the second cluster, and add the edge without constraining ranks:
{rank=same; n1[ltail=cluster0,dir=none ]; n1->n2->n3->n4->n5[dir=none];}
MATH1034 -> n1[constraint=false];

Change edge placement from beneath to above nodes in Graphviz

It took me some time to make the graph below look like it does right now, and I'm almost satisfied. The one thing that still bothers me is that the connection between D and B should be above all nodes for the sake of aesthetics.
The funny thing is, that supplying the ports for the edge doesn't impress dot which just makes the edge cross the connected nodes.
Do you have an idea on how to avoid this?
digraph {
graph [splines=ortho, nodesep=0.2, fontname="DejaVu Sans", rankdir=LR]
node [shape=box, fontsize=8]
edge [arrowsize=0.5]
subgraph cluster {
style=invis;
A -> B -> C;
A -> B -> C;
A -> B -> C -> D;
D -> E;
D:nw -> B:ne;
}
{
D -> F -> { C; E };
}
}
PS: You need the latest Graphviz version in order to get orthogonal edges.
It may be a function of the version of the engine you use. I'm not sure what version of dot the GraphViz Workspace http://graphviz-dev.appspot.com/ uses but it does run your problem connector across the top.

Resources