I would like to recreate the petroleum product tree with graphviz.
prototype
For this I'm glad with graphviz possibility to create a graph structured in columns.
But how can I force a box to be rendered in the 3rd instead of the 2nd column without using an empty box as bypass?
I did a small experiment, important part is rank=same:
digraph g {
rankdir=LR
node [shape=box ]
Petroleum
{rank=same Benzene;Xylene;Butanes}
{rank=same Ethylbenzene;Cyclobenzene;Cumene;"p-Xylene";Toluene}
Petroleum -> Benzene
Petroleum -> Xylene
Petroleum -> Butanes
Petroleum -> Toluene
Benzene -> Ethylbenzene
Benzene -> Cyclobenzene
Benzene -> Cumene
Xylene -> "p-Xylene"
}
Related
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"]
}
I'm looking to have a series of nodes in a row, joined by an edge. This works fine when the graph's rankdir is set to TB or BT, but it rearranges the nodes when I set it to LR or RL so they're no longer next to each other. Example images are included.
I've taken my code and stripped it down to it's minimum point for demonstration. The code is the same for both of the following graphs, aside from line 2 (which specifies rankdir):
digraph{
rankdir=LR;
node[shape=box,fontcolor=white,color=black,fillcolor=black,style=filled];
edge[dir=none,color=black];
Josh -> JoshParent;
JoshParent -> Hero;
JoshParent[shape=circle,label="",height=0.0001,width=0.0001];
{
rank=same;
Kae[label="Kae"];
Hero[label="Hero"];
Kae -> Hero;
}
Kae -> KaeParent;
Hero -> HeroParent;
KaeParent -> Liz;
KaeParent[shape=circle,label="",height=0.0001,width=0.0001];
HeroParent -> George;
HeroParent[shape=circle,label="",height=0.0001,width=0.0001];
{
rank=same;
George[label="George"];
Liz[label="Liz"];
Ocean[label="Ocean"];
Egg[label="Egg"];
Liz -> Ocean -> Egg;
}
}
This is what's shown with rankdir=TB:
This is what's shown with rankdir=LR:
As you can see, from the LR image, the nodes have been drawn in the order "Ocean, George, Egg", rather than "Ocean, Egg, George" as it is with the TB image.
You can force the order by adding an explicit but invisible edge from Egg to George:
Liz -> Ocean -> Egg; // last line of your code
Egg -> George[ style = invis ]; // additional edge
This produces
I don't have an explanation for the different behaviour between TB and LR, though.
My ultimate aim is to write a python script that will automatically generate graphviz graphs based on some input data. However, I'm first focusing just on the graphviz.
This is currently what I have:
digraph G {
compound=true;
node [shape=box];
edge [dir=none];
subgraph cluster_overall{
subgraph cluster_top{
apple;
banana;
}
subgraph clustermsc{
basket1;
basket2;
label="Baskets";
}
subgraph cluster_bottom{
orange;
kiwi;
}
label="Test";
}
apple -> basket1;
banana -> basket2;
orange -> basket1;
kiwi -> basket2;
}
Current: https://i.imgur.com/76GCx0f.png
This is what I want my final graph to look like. With the number of fruits evenly distributed between the top and bottom section (based on the input date):
Final: https://i.imgur.com/OHFsAdd.png
How do I structure the page to have 3 static, separate sections. Currently the fruit's placement constantly changes based on node they point to.
Please let me know if I wasn't clear in my explanation, I'll try better explaining it. Thanks.
Don't have enough repuation to directly post images.
In graphviz, it is important to produce the hierarchy as the tool sees it, not reproducing the logic that is on your mind. simply reversing the edges from your baskets to the "lower" fruits does the job:
digraph G {
compound=true;
node [shape=box];
edge [dir=none];
subgraph cluster_overall{
subgraph cluster_top{
apple;
banana;
}
subgraph clustermsc{
basket1;
basket2;
label="Baskets";
}
subgraph cluster_bottom{
orange;
kiwi;
}
label="Test";
}
apple -> basket1;
banana -> basket2;
basket1 -> orange; // !!!
basket2-> kiwi; // !!!
}
gives you
If you want to force a certain order of items (such as apple being to the left of banana), you can do so by replacing your definition with
subgraph cluster_top{
{ rank = same; apple -> banana[ style = invis ] }
}
If I correctly understood you, you need to connect clusters with invisible edges to force their position.
Here I've added a dummy node into each cluster (because to connect clusters you have to connect nodes in these clusters and then add lhead and ltail attributes).
Then I've connected these clusters in the correct order with invisible edges. Also I've added an extremely big weight to these edges to give them priority over other edges.
Is that what you need?
digraph G {
compound=true;
node [shape=box];
edge [dir=none];
subgraph cluster_overall{
subgraph cluster_top{
dummy_top [shape=point width=0 style=invis]
apple;
banana;
}
subgraph clustermsc{
dummy_msc [shape=point width=0 style=invis]
basket1;
basket2;
label="Baskets";
}
subgraph cluster_bottom{
dummy_bottom [shape=point width=0 style=invis]
orange;
kiwi;
}
label="Test";
}
dummy_top -> dummy_msc [
style=invis
weight=100
lhead="clustermsc"
ltail="cluster_top"
]
dummy_msc -> dummy_bottom [
style=invis
weight=100
lhead="cluster_bottom"
ltail="clustermsc"
]
apple -> basket1;
banana -> basket2;
orange -> basket1;
kiwi -> basket2;
}
I'd like to draw a diagram like this.
But the only diagram I can draw is:
The code I used :
graph [rankdir = LR]
node [shape=box]
x1;x2;x3;y1;y2;y3;y4;y5;y6;y7;y8;
node [shape=oval]
ind60;dem60;dem65
{x1,x2,x3} -> ind60[arrowhead=none arrowtail=normal dir=both]
{y1,y2,y3,y4} -> dem60[arrowhead=none arrowtail=normal dir=both]
dem65 -> {y5,y6,y7,y8}
ind60->dem60 dem60->dem65 ind60->dem65
How can I draw the desired plot?
A first step in what you want to achieve, using rank=same, invisible edges, groups, and constraint=false:
digraph {
node [shape=box]
{
rank=same;
y1;y2;y3;y4;
}
dem60[shape=oval];
{y1;y2;y3;y4} -> dem60 [dir=back];
{
rank=same;
x2 [group=left];
ind60[shape=oval];
dem65[shape=oval];
y6 [group=right];
x2 -> ind60 [dir=back];
ind60 -> dem65
dem65 -> y6;
}
// Invisible edges to order vertically node groups
edge[style=invis];
x1[group=left];
x3[group=left];
x1 -> x2 -> x3;
node[group=right];
y5 -> y6 -> y7 -> y8;
node[group=""]
edge[style=solid]
ind60->dem60
dem60->dem65
edge[constraint=false];
ind60 -> x1;
ind60 -> x3;
dem65 -> y5;
dem65 -> y7;
dem65 -> y8;
}
group enforces vertical alignement of nodes (of the same group).
rank=same makes nodes stay on the same rank.
Invisible edges enforce rank order within a vertical group.
constraint=false removes constraint calculation for some edges.
dir=back reverses displayed edge direction.
I'm trying to draw bigraphs in GraphViz (something like this)
Is it possible to nest nodes directly in GraphViz?
If not is it possible to change the shape of a subgraph/cluster?
I have found a post about how to import images of graphs into nodes but that seems like more effort than drawing them in powerpoint.
I can also make clusters visible, but they will include nodes from outside the cluster and as far as I can tell have a fixed shape.
Thanks for your help.
Nest nodes is impossible, but nest clusters is possible.
Subgraph/cluster has some predefined styles. Also nodes has predefines shapes. I don't know about manual editing path of border shapes for nodes in graphviz in different layouts (or alternatives like representing the border as a chain of nodes), maybe someone else can tell us.
You can get nested nodes in neato layout engine, but in fact they will be nested only visually, because you just set their coordinates with pos attribute, which means they overlap each other.
Example image:
and script:
graph {
layout=neato
node [shape=circle pin=true ]
foo [height=0.6 pos="0.7,1.5" ]
bar [height=2 pos="1,2" ]
baz [height=1 pos="2,2" ]
}
You can import images as backgrounds into nodes as described in this answer. For example for Windows you can use this official archive, it comes with a cairo.dll, which will avoid errors like Warning: No loadimage plugin for "jpeg:cairo" when you run the command $ dot -Tpng input.dot -o output.png.
The image from your question can be drawn like this:
Script:
digraph {
graph [
layout=dot,
ranksep=1,
overlap=false,
compound=true, // to enable `lhead`, `ltail` features
newrank=true, // to enable ranking between nodes in clusters
splines=true,
splines=curved]
edge[color=lime arrowhead=none]
node[shape=plaintext]
y0 [label=<<FONT><I>y</I><SUB>0</SUB></FONT>>]
y1 [label=<<FONT><I>y</I><SUB>1</SUB></FONT>>]
y2 [label=<<FONT><I>y</I><SUB>2</SUB></FONT>>]
x0 [label=<<FONT><I>x</I><SUB>0</SUB></FONT>>]
x1 [label=<<FONT><I>x</I><SUB>1</SUB></FONT>>]
subgraph cluster_r0{
style="rounded, dashed"
margin=20
label=<<FONT><I>r</I><SUB>0</SUB></FONT>>
v2 [shape=circle]
noname_dot[shape=point height=0]
subgraph cluster_v0{
style="rounded"
label=<<FONT><I>v</I><SUB>0</SUB></FONT>>
invis_dot_1 [style=invis shape=point]
subgraph cluster_v1{
style="rounded";
label=<<FONT><I>v</I><SUB>1</SUB></FONT>>
subgraph cluster_s0{
label=<<FONT><I>s</I><SUB>0</SUB></FONT>>
fillcolor=lightgrey
style="filled, rounded"
invis_dot_2 [style=invis shape=point]
}
}
}
}
y0 -> noname_dot
noname_dot -> invis_dot_2 [lhead="cluster_v1"]
noname_dot -> invis_dot_1 [lhead="cluster_v0"]
noname_dot -> v2 [constraint=false]
y1 -> v2
subgraph cluster_r1{
style="rounded, dashed"
margin=20
label=<<FONT><I>r</I><SUB>1</SUB></FONT>>
noname_dot_2 [shape=point height=0]
subgraph cluster_s3{
label=<<FONT><I>s</I><SUB>3</SUB></FONT>>
fillcolor=lightgrey
style="filled, rounded"
invis_dot_s3 [style=invis shape=point]
}
subgraph cluster_v3{
style="rounded"
label=<<FONT><I>v</I><SUB>3</SUB></FONT>>
subgraph cluster_s2{
label=<<FONT><I>s</I><SUB>2</SUB></FONT>>
fillcolor=lightgrey
style="filled, rounded"
invis_dot_s2 [style=invis shape=point]
}
}
}
y2 -> noname_dot_2
noname_dot_2 -> x0
v2 -> noname_dot_2 [weight=0]
noname_dot_2 -> invis_dot_s2 [lhead="cluster_v3"]
invis_dot_s2 -> x1 [ltail="cluster_v3"]
// Vertical alignment (`rank`) of the desired nodes:
{rank=same; noname_dot; noname_dot_2; v2 }
{rank=same; invis_dot_s3; invis_dot_s2 }
{rank=same; x0; x1;}
{rank=same; y0; y1; y2;}
}