Draw B-tree vertically - graphviz

Is there a way to draw B-Trees on Graphviz? suggests alternating link, data, link, data, link, ... However, I have, potentially, lots of data in a node such that I can't really space it out horizontally. I am using a vertical node with the arrows from one corner to simulate having the arrows between data.
digraph {
graph [rankdir=LR, truecolor=true, bgcolor=transparent];
node [shape=none];
node1 [label = <
<table>
<tr><td port="0">Ugargeeon</td></tr>
<tr><td port="1">103</td></tr>
</table>>];
node1:0:se -> node2;
node1:1:se -> node3;
node2 [label = <
<table>
<tr><td>Rutorshar</td></tr>
<tr><td>17</td></tr>
<tr><td>69</td></tr>
</table>>];
node3 [label = <
<table>
<tr><td>Nashmokum</td></tr>
<tr><td>117</td></tr>
<tr><td>135</td></tr>
</table>>];
}
Gives.
The first image is pretty close to what I want as the initial starting point, except, the initial spline is pointing to the south-east. The second image I've edited to show what I think the ideal output should be. That is, roughly symmetrical. Globally setting splines=line works, but with more edges, it becomes easier to tell them apart when they are the default splines. Is there a way to position the arrows between the nodes without affecting the initial spline state?

Close? Major change is to use ports on the head end of the edge - either to West side of entire node or a specific cell. Pick your spline type, Also twiddled with drawing the table, strictly aesthetic.
// https://stackoverflow.com/questions/72428350/draw-b-tree-vertically
digraph {
graph [rankdir=LR, truecolor=true, bgcolor=transparent];
splines=false // made a guess, should work fine true or false
node [shape=none];
node1 [label = <
<table>
<tr><td port="p0">Ugargeeon</td></tr>
<tr><td port="p1">103</td></tr>
</table>>];
node1:p0:se -> node2:w;
//node1:p1:se -> node3:w; // does not actually touch the node
node1:p1:se -> node3:p1:w; // looks a bit better?
node2 [label = <
<table>
<tr><td port="p0">Rutorshar</td></tr>
<tr><td port="p1">17</td></tr>
<tr><td port="p2">69</td></tr>
</table>>];
node3 [label = <
<table BORDER="0" CELLBORDER="1" cellspacing="0" >
<tr><td port="p0">Nashmokum</td></tr>
<tr><td port="p1">117</td></tr>
<tr><td port="p2">135</td></tr>
</table>>];
}

Related

Graphviz: edges between cells in the same html table are too long

I want to draw edges between cells in the same table node in graphviz, but the edges are way too long, which may overlap other nodes. How to make these edges shorter?
Here is an example.
digraph graphviz {
graph [rankdir = "LR"];
node [shape = "plaintext"];
"A" [label=<
<TABLE>
<TR><TD PORT="a">cell a</TD></TR>
<TR><TD PORT="b">cell b</TD></TR>
<TR><TD PORT="c">cell c</TD></TR>
</TABLE>
>];
"B" [label=<
<TABLE>
<TR><TD PORT="a">cell a</TD></TR>
<TR><TD PORT="b">cell b</TD></TR>
</TABLE>
>];
"A":"c" -> "A":"a";
"A":"c" -> "A":"b";
"B":"a" -> "A":"a";
}
I found a similar issue Loopy edge too long, but there is no answer.
This seems to produce smaller edges (less loopy):
dot -Tdot myfile.gv |neato -n -Tpng >$myfile.png
It uses dot to position the nodes and neato to position the edges.
See: https://graphviz.org/faq/#FaqDotWithNodeCoords and https://graphviz.org/docs/outputs/canon/
Before:
After:
I just found that simply adding a label on arbitrary edge solves the problem. If you don't want any text on edges, use spaces.
digraph graphviz {
graph [rankdir = "LR"];
node [shape = "plaintext"];
"A" [label=<
<TABLE>
<TR><TD PORT="a">cell a</TD></TR>
<TR><TD PORT="b">cell b</TD></TR>
<TR><TD PORT="c">cell c</TD></TR>
</TABLE>
>];
"B" [label=<
<TABLE>
<TR><TD PORT="a">cell a</TD></TR>
<TR><TD PORT="b">cell b</TD></TR>
</TABLE>
>];
"A":"c" -> "A":"a" [label=" "];
"A":"c" -> "A":"b";
"B":"a" -> "A":"a";
}

How to structure multiple subgraphs in Graphviz?

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;
}

Graphviz: Force / leapfrog "column" for box

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"
}

Nesting nodes in GraphViz

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;}
}

Distance between edges and nodes in graphviz

How do I configure the distance between edges and nodes (red), i.e. the outer shape of a node(blue)?
Here is a technique that you can consider that avoids the need to create a custom node shape. There may be some matters of taste that you may need to address further in order to get exactly what you want.
The advantage of this technique, using HTML-like labels, is that varying the space inside and outside the rectangle becomes a simple matter of changing the inner number of points (here 4) and outer number of points (here 16) respectively.
digraph {
node [shape=none]
O [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="16" CELLPADDING="4">
<TR><TD WIDTH="70">\N</TD></TR>
</TABLE>
>]
{a,b,c,d} -> O
}
Result:
You can change the BORDER and CELLBORDER parameters to show or hide the rectangles. You can adjust their thicknesses although only in multiples of 1 point. I have used the WIDTH parameter to force a width-to-height ratio that causes the aligning of all the arrow tips. Otherwise some of the tips would meet the sides of the invisible outer rectangle.
Like the other answer said, this isn't really an easy feat. Using a combination of height, width, fixedsize, labelloc, and margin node parameters, you can probably get any desired effect you'd like. Margin is best is you're looking to expand that distance, but to minimize it you need to use the other params. For example, this graph would have the arrowheads almost touching the 'O' node's text.
digraph {
node [shape="none" width=.2 height=.2 fixedsize="true" labelloc="top"];
a -> O;
b -> O;
c -> O;
d -> O;
}
Alternatively, if you really want to put in effort, you could create a custom node shape, and do whatever you'd like to it.
This is not possible AFAIK.
The only option available is using headclip to have the edge go to the center of the node or to the edge of the outer shape (as you did in your example).
You may try to add some padding to a node by putting it in a cluster and have the edges clip at the (invisible) border of the cluster:
digraph {
compound=true;
subgraph cluster1 {
style=invis;
2;
}
1 -> 2[lhead=cluster1];
}
Hopefully somebody knows a better hack for this...

Resources