Graphviz: Nodes with custom Shapes - graphviz

I'm basically doing the same thing as in this question: How to generate nodes with customized shape?, but I have problems with using eps / svg / other vector-based formats as images. I searched for the last two hours if there is any possibilty to scale eps file to the size that I wish, so that the nodes are about the same size as the normal shapes (box, rounded etc.).
All attempts to scale the eps files with the width/height attribute failed and it seems like graphviz is ignoring that when using vector-based images.
fixedsize=shape did also fail and the result is really strange with regards to where an edge's head ends up (somewhere in in the custom shape).
The only workaround (which sucks) is to set the size externally with tools like inkscape, or changing the fontsize to a value that all generated nodes scale more or less to the size of the custom shape. Is there any better way to do this, that I overlooked?
This is what I have so far:
digraph G {
rankdir="LR";
size="8.3,11.7";
page="8.3,11.7";
ratio="compress";
ranksep=0;
nodesep=0.5;
compound=true;
graph [ color=white, penwidth=0, labelloc="b" , margin=0];
node [fontsize=30];
bgcolor=white;
edge [arrowsize=1, color=black];
/* Nodes */
subgraph cluster_Computers {
label="Comput"; Computers_icon [label="", image="gitrepo.eps", shape=none,margin=0] ;};
Computers_icon -> c;
asdf -> jklö;
jklö -> Computers_icon;
c->f->g;
d->a->b;
d->g;
d->c;
}

Related

Graphviz drawing - make a box that starts at one node and goes to the end of another one

I have a timeline and would like a box to be drawn next to a particular number of years
digraph timeline {
node [fontsize=24, shape = plaintext];
1940 -> 1950;
1950 -> 1955;
1955 -> 1960;
node [fontsize=20, shape = box];
{ rank=same; 1940 test; }
}
This places a timeline on the left hand side going from 1940 to 1950 and so forth. I would like draw a box next to the numbers that starts at 1940 - which is what I do now with { rank=same; 1940 test; } and that ends with 1955.
Here is an example of the box drawn at 1940
Here is an example of the box drawn at 1955 with code { rank=same; 1955 test; }
I would like to have the box drawn from the start of 1940 position to the end of 1955 position, so encompassing these two boxes right now.
Tricky solution is to draw a cluster and place invisible nodes inside it using style=invis. Then align the cluster with the timeline using newrank.
Script:
digraph timeline {
newrank=true;
node [fontsize=24, shape = plaintext];
1940 -> 1950 -> 1955 -> 1960;
subgraph cluster_1 {
test [fontsize=20]
cl_start [shape=none style=invis]
cl_end [shape=none style=invis]
cl_start -> test -> cl_end [style=invis]
}
{rank=same;1940;cl_start}
{rank=same;1955;cl_end}
}
Result:
No easy way to do this with Graphviz.
dot does not allow nodes to span ranks. It centers all nodes in the same rank on the same line (horizontal, with rankdir=TB).
if you set rankdir=LR, you can get nodes to span years, but positioning & node sizing become difficult
you could put a node on every year you want the final node to span and the post-process the result to overwrite the several nodes with a single large node.
you can us the neato -n (https://graphviz.org/faq/#FaqDotWithNodeCoords) capability and position & size all the nodes yourself
or you could try another language entirely (maybe pikchr https://pikchr.org/home/doc/trunk/homepage.md)

Graph in Graph Extraction

I want to draw a graph with "dot" format. The graph I want is a (sub)graph in a graph style. The subgraph (a child) has internal graph, and the (parent) graphs are connected among the parents, and does not connect to children that are connected only in the subgraph.
Could you please guide how writing such style with the dot-format, with an example?
Good news:
creating your "parent nodes" is pretty easy. Graphviz, (dot) calls them "cluster subgraphs" (or clusters). (well documented on p. 19 of https://www.graphviz.org/pdf/dotguide.pdf) see below for an example.
Bad news:
explicitly sizing clusters is very difficult. Dot sizes a cluster to be just big enough to contain its nodes.
explicitly positioning clusters is also quite difficult. Dot positions clusters based on the "best" positioning of the component nodes, specifically the edges that connect nodes within the various clusters.
drawing edges from one cluster to another is a kludge. You define an edge from a node contained within one cluster to another node contained within a different cluster and instruct dot to clip that edge so it appears to be from cluster to cluster. This uses the attribute compound=true. (again, read the documentation listed above)
Finally: the fdp engine allows cluster-to-cluster edges, but you lose the directionality of the edges
Drawn with dot:
digraph C {
graph [compound=true] // allows edges to/from clusters
// create an extra cluster to try to keep clusterA above clusterB
subgraph clusterWrapper {
graph [peripheries=0] // no box around
subgraph clusterA {
graph [peripheries=1] // box around
a -> c
b->c
c->d
}
subgraph clusterB {
graph [peripheries=1] // box around
f->g
e->g
}
} // end of wrapper
subgraph clusterC {
edge [dir=none ] // no arrowheads
h->k
i->k
i->l
j->l
{rank=same h->i->j [style=invis]}
}
b->f [ltail=clusterA, lhead=clusterB];
l:s->b:n [ltail=clusterC, lhead=clusterA];
l->f [style=invis weight=50]
}
Giving:

Increase curvature of connecting arcs

Consider:
digraph D {
0[pos="0,0!"]
1[pos="0,5!"]
0 -> 1[label=0.25]
1 -> 0[label=0.50]
}
Rendered under neato engine, this gives:
Is there a way to increase/control the curvature of the connecting arcs so that the rendering is something like the hand-drawn annotations below:
Edited to add: While I don't have to stick with neato, it is important in my application that the node pos coordinate values be upheld.
[there may be better ways, but edges are pretty hard to wrangle]
This solution requires neato -n (see https://graphviz.org/faq/#FaqDotWithNodeCoords) and (https://graphviz.org/docs/attrs/pos/)
Different versions of neato seem to produce different results when previous answer is input. This input file should work with older & newer versions of neato.
digraph D {
// next lines from the Graphviz FAQ
overlap=false
splines=true
0[pos="0,0!"]
1[pos="0,5!"]
// stick an invisible node in the middle, to force the edges out
half [pos="0,2.5!" width=1.1 height=1 style=invis]
0:nw -> 1:sw [label=0.25]
1:se -> 0:ne [label=0.50]
}
Giving:

Bisecting dashed curved lines ala Threat Model

Is there a way to add a curved dash line similar to the one present here for Privilege Boundaries? I'd like to have a threat model checked into GIT that is procedurally generated as part of my CI/CD builds as apposed to drawing it online or in some tool like OmniGraffle and exporting image only to loose the original docs.
You can get fairly close by adding some extra edges and invisible nodes and using attribute for extra edge arrowhead=icurve, but it will not be a dashed line. Graphviz allows you to draw dashed cluster boundaries, or dashed edges from node to node, but the perpendicular line to the one or two edges will be impossible to draw without manual positioning, as you drawing it online, and as I assume you need to draw without human involvement.
Also with some extra effort with PostScript you can create half-drawn nodes with dashed border like A -> Internet Boundary [shape=yourshape color=red] -> B, but this is also not actually a line crossing.
Image:
and script for arrowhead=icurve variant:
digraph threat_model {
graph [
splines=false
ranksep=0
nodesep=2]
User [shape=box xlabel=<<FONT COLOR="ORANGE"><B>Actor</B></FONT>>]
a [shape=circle label=<<FONT><B>a. Static<BR/>front-end<BR/>files</B></FONT>> xlabel=<<FONT COLOR="ORANGE"><B>Process</B></FONT>>]
b [shape=circle label=<<FONT><B>b. App<BR/>back-end</B></FONT>> xlabel=<<FONT COLOR="ORANGE"><B>Data<BR/>Flow</B></FONT>>]
// Two extra nodes:
boundary_User_b_1 [shape=point height=.01]
boundary_User_b_2 [shape=point height=.01]
User -> a [minlen=3 dir=both xlabel="1. Retrieve static\nfront-end files"]
// Three extra edges:
User -> boundary_User_b_1 [minlen=3 dir=back xlabel="2. Modify report"]
boundary_User_b_1 -> boundary_User_b_2 [arrowhead=icurve arrowsize=6 color=red headlabel=<<FONT COLOR="RED">Internet<BR/>Boundary</FONT>>]
boundary_User_b_2 -> b [minlen=3]
}
Another fairly close variant using cluster, note that the arrow can rest against boundary, with the compound=true attribute for graph and lhead attribute for arrow:
digraph {
graph[
ranksep=1
compound=true
]
A
B
subgraph cluster_IB {
graph [
label="Internet Boundary"
fontcolor=red
margin=20
style="dashed, rounded"
color=red
]
C
}
A -> C
B -> C [lhead="cluster_IB"]
}

How to specify the length of an edge in graphviz?

In a directed graph, if there is a cycle, the graphviz makes that edge really short.
Is there a parameter that would let me change the length of the cyclic edge, so that
the graph looks a bit uniform.
digraph ER {
rankdir="LR";
//orientation=landscape;
node [shape=ellipse, fontsize=30];
{node [label="Original"] old;}
{node [label="Final"] new;}
{node [label="Intermediate"] ir;}
old -> ir [label="suggest", fontsize=30];
ir -> ir [label="validate", fontsize=30, len=f];
ir -> new [label = "finalize", fontsize=30];
}
Edit: Sorry, my answer will make edges longer, but not the self referencing edges you need.
len doesn't work in dot, but minlen does.
https://www.graphviz.org/doc/info/attrs.html#d:minlen
x->y
[minlen=5]
len doesn't works in dot, but you can try this trick:
digraph G {
rankdir=LR
a->b[dir=both]
b->c[dir=both,label=" "]// Just use the space to increase the edge length
}
If rankdir=TB, use label="\n" (repeat the \n as necessary) to increase the length.
I found that the following attribute nodesep worked to solve this problem with sfdp.
From nodesep | Graphviz:
For layouts other than dot
nodesep affects the spacing between loops on a single node, or multiedges between a pair of nodes.
Note that this is a graph attribute, so the value is the same for all edges in the graph.
From dot(1):
len=f sets the optimal length of an edge. The default is 1.0.
You can make the cyclic edge longer by adding a bunch of invisible cyclic edges before your visible one, like this:
digraph ER {
rankdir="LR";
//orientation=landscape;
node [shape=ellipse, fontsize=30];
{node [label="Original"] old;}
{node [label="Final"] new;}
{node [label="Intermediate"] ir;}
old -> ir [label="suggest", fontsize=30];
ir -> ir [style="invis"]
ir -> ir [style="invis"]
ir -> ir [style="invis"]
ir -> ir [style="invis"]
ir -> ir [label="validate", fontsize=30, len=f];
ir -> new [label = "finalize", fontsize=30];
}
In .dot language, the edge connects two notes with different ranks.
The length of edge is equal to (difference in ranks)*ranksep
default ranksep (in graph attribute) is 0.75 inch, so edge of adjacent nodes will be 0.75 inch.
To reduce the edge length, you set ranksep into a smaller value in graph atrribute
There's another approach that I now use for this:
I use graphviz to output the file in dot format
dot -T dot -Kneato -o ./positioned.dot ./input.dot
This file will contain the Bezier curve definitions for every edge.
I manually change the points to curve the way I want it to draw.
This may look a little daunting at first but once you figure out, how they work this isn't hard, I'm inching towards a script that will do this for me automatically
then re-run dot with your edited positioned file as the input
dot -T png -Kneato -O ./positioned.dot
With this approach I've pretty much turned dot into my text based visio replacement

Resources