How can I cross out a node in Graphviz? - 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}
}

Related

Graphviz enforce ordering between nodes of different levels

I have the following graph:
digraph {
stylesheet = "..."
subgraph cluster {
b; c; g;
{rank=same; b; g;}
}
a -> b;
b -> c;
c -> d;
c -> e;
f -> c;
{rank=same; a; f;}
}
Is there any way to force/encourage the edge f -> c to pass between nodes b and g? I've tried a number of different strategies and graphviz refuses to both:
keep b and g within the border, and
allow g to appear of to the side and not interfere with the rest of the graph.
Any suggestions would be much appreciated!
Indeed, the dot algorithm does not want to route the f->c edge as you want. However, the neato edge routing algorithm produces a closer result. So we use dot to position the nodes and neato -n to route the edges. Like so:
dot -Tdot myfile.gv >out.dot
neato -n -Tpng out.dot >myfile.png
Using this input:
digraph {
stylesheet = "https://g3doc.corp.google.com/frameworks/g3doc/includes/graphviz-style.css"
nodesep=.5 // optional
subgraph cluster {
b
c; g
{rank=same; b; g;}
}
f -> g [style=invis]
f:se -> c:nw [constraint=false]
a -> b;
b -> c;
c -> d;
c -> e;
}
Giving:
See https://graphviz.org/faq/#FaqDotWithNodeCoords
And https://graphviz.org/docs/outputs/canon/
(Close enough?)

In graphviz, can you bring two vertices closer together?

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:

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: How to add label to arrows if nodes already have a label

I am making a graph using graphviz dot.
like this
digraph protocol {
label="Protocol workflow";
node[shape=box, style=rounded];
a[label="Byte received"];
b[label="Is start delimiter?", shape=diamond];
c[label="Recv length [n, 1B]"]; c1[label="Set count=n"];
d[label="Recv 1 payload byte and decrement count"];
e[label="count > 0", shape=diamond];
f[label="Recv CRC [2B]"];
g[label="Is CRC valid?", shape=diamond];
a -> b -> c -> c1 -> d -> e -> f -> g;
e -> d;
}
Problem is that to some arrows I need to add yes no labels but I don't know how I could do that.
You may add labels to edges like this:
e -> f [label="Yes"];

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

Resources