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:
Related
When describing a graph with graphviz, I sometimes find I want two vertices to appear closer together than the layout engine I chose places them. Is there a way to hint that I want them closer?
I'm mostly interested in the case of two connected vertices, so an answer specific to that case is fine.
Concrete example:
digraph G {
node [shape="circle"];
Start [shape="none" label=""];
C [shape="doublecircle"];
Start -> A;
A -> B [label="0,1"];
B -> C [label="0,1"];
C -> D [label="0,1"];
D -> D [label="0,1"];
}
I want the vertices Start and A to be closer.
You can't do that, but you can make nearly everything else twice as big, here is a start.
(But you can't increase the size of an edge to self)
digraph G {
rankdir=LR
edge[minlen=2 fontsize=28 arrowsize=2 penwidth=2]
node[fontsize=28 height=1 penwidth=2]
graph[fontsize=28 penwidth=2]
node [shape="circle"];
Start [shape="none" label=""];
C [shape="doublecircle"];
Start -> A[minlen=1]; // not twice the size to get the requested effect
A -> B [label="0,1"];
B -> C [label="0,1"];
C -> D [label="0,1"];
D -> D [label="0,1"];
}
[this answer applies specifically to dot]
there is no edge-level attribute that explicitly sets or changes edge length
the graph-level nodesep attribute sets minimum distance between two nodes of same rank
so:
digraph G {
nodesep=.17
{
rank=same
node [shape="circle"];
Start [shape="none" label=""];
C [shape="doublecircle"];
Start -> A;
A -> B [label="0,1"];
B -> C [label="0,1"];
C -> D [label="0,1"];
D -> D [label="0,1"];
}
}
produces:
To increase the distance between the other nodes, you can add spaces to the labels.
I'm not wild about it either, but this change:
B -> C [label=" 0,1 "]; // pad to make label (and edge) longer
produced this:
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}
}
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:
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];
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.