# Dynamic minlen to account for plot scaling

**URL:** <https://forum.graphviz.org/t/dynamic-minlen-to-account-for-plot-scaling/2220>\
**Category:** Help\
**Created:** [May 29, 2024, 5:42pm UTC](https://forum.graphviz.org/t/dynamic-minlen-to-account-for-plot-scaling/2220 "2024-05-29T17:42:41Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![alim](https://avatars.discourse-cdn.com/v4/letter/a/bbe5ce/32.png) [@alim](https://forum.graphviz.org/u/alim)\
**Post date:** [May 29, 2024, 5:42pm UTC](https://forum.graphviz.org/t/dynamic-minlen-to-account-for-plot-scaling/2220/1 "2024-05-29T17:42:41Z")

</div>

Hi,

I am developing an R package to generate a DOT plot based on user input. The text might have different length or the number of nodes in parallel will be different. I have managed to calculate the `minlen` for some cases. I calculated the width of the text in each node and multiplied by some values. This method works if there are only a few nodes in parallel or the text is not too wide, but it fails when the text in the node is too long or there are too many nodes in parallel. The Graphviz scaled down the figure to fit the window but the `minlen` is calculated without scaling. So I have wide distance between nodes. I can manually decrease the `minlen` and get it working, but I am trying to find a solution, where the `minlen` is calculated based accounting for the scaling.

[dot verbose=true]  
digraph consort\_diagram {  
graph [layout = dot, splines=ortho, overlap=prism]

# node definitions with substituted label text

node [shape = rectangle, fillcolor = Biege, style=“”, fillcolor = “”, color = “”]

node1 [label = “Cohort 1 (n=6)”]  
node2 [label = “Cohort 2 (n=6)”]  
node3 [label = “Cohort 3 (n=6)”]  
node4 [label = “Excluded (n=1)\l”]  
node5 [label = “Excluded (n=3)\l”]  
node7 [label = “Cohort 1 (n=5)”]  
node8 [label = “Cohort 2 (n=3)”]  
node10 [label = “Total (n=14)”]

## Invisible point node for joints

node [shape = point, width = 0, style=invis]

P1 P2 P3 P4 P5

subgraph {  
rank = same; rankdir = LR; node1; node2; node3;  
}  
subgraph {  
rank = same; rankdir = LR; P1; node4;  
}  
subgraph {  
rank = same; rankdir = LR; node7; node8; node9;  
}  
subgraph {  
rank = same; rankdir = LR; node4; node5; node6;  
}  
subgraph {  
rank = same; rankdir = LR; P2; node5;  
}  
subgraph {  
rank = same; rankdir = LR; node4; node5;  
}  
subgraph {  
rank = same; rankdir = LR; P3; P4; P5;  
}

edge[style=“”];

node1 → P1 [arrowhead = none];  
P1 → node4;  
P1 → node7;  
node2 → P2 [arrowhead = none];  
P2 → node5;  
P2 → node8;  
P4 → node10;  
node7 → P3 [arrowhead = none];  
node8 → P4 [arrowhead = none];  
node9 → P5 [arrowhead = none];  
P3 → P4 → P5 [arrowhead = none, minlen = 7.0];

node3 → node9 [arrowhead = none];  
}  
[/dot]

But the distance between nodes are increased in the example below:

[dot verbose=true]  
digraph consort\_diagram {  
graph [layout = dot, splines=ortho, overlap=prism]

# node definitions with substituted label text

node [shape = rectangle, fillcolor = Biege, style=“”, fillcolor = “”, color = “”]

node1 [label = “Cohort 1 (n=6)”]  
node2 [label = “Cohort 2 (n=6)”]  
node3 [label = “Cohort 3 (n=6)”]  
node4 [label = “Cohort 4 (n=6)”]  
node5 [label = “Cohort 5 (n=6)”]  
node6 [label = “Excluded (n=1)\l”]  
node7 [label = “Excluded (n=3)\lPatient died before surgery\l”]  
node9 [label = “Excluded (n=3)\l”]  
node10 [label = “Excluded (n=3)\lPatient declined to participate\l”]  
node11 [label = “Cohort 1 (n=5)”]  
node12 [label = “Cohort 2 (n=3)”]  
node14 [label = “Cohort 5 (n=6)”]  
node15 [label = “Cohort 5 (n=6)”]  
node16 [label = “Total (n=14)”]

## Invisible point node for joints

node [shape = point, width = 0, style=invis]

P1 P2 P3 P4 P5 P6 P7 P8 P9

subgraph {  
rank = same; rankdir = LR; node1; node2; node3; node4; node5;  
}  
subgraph {  
rank = same; rankdir = LR; P1; node6;  
}  
subgraph {  
rank = same; rankdir = LR; node11; node12; node13; node14; node15;  
}  
subgraph {  
rank = same; rankdir = LR; node6; node7; node8; node9; node10;  
}  
subgraph {  
rank = same; rankdir = LR; P2; node7;  
}  
subgraph {  
rank = same; rankdir = LR; node6; node7; node9; node10;  
}  
subgraph {  
rank = same; rankdir = LR; P3; node9;  
}  
subgraph {  
rank = same; rankdir = LR; P4; node10;  
}  
subgraph {  
rank = same; rankdir = LR; P5; P6; P7; P8; P9;  
}

edge[style=“”];

node1 → P1 [arrowhead = none];  
P1 → node6;  
P1 → node11;  
node2 → P2 [arrowhead = none];  
P2 → node7;  
P2 → node12;  
node4 → P3 [arrowhead = none];  
P3 → node9;  
P3 → node14;  
node5 → P4 [arrowhead = none];  
P4 → node10;  
P4 → node15;  
P7 → node16;  
node11 → P5 [arrowhead = none];  
node12 → P6 [arrowhead = none];  
node13 → P7 [arrowhead = none];  
node14 → P8 [arrowhead = none];  
node15 → P9 [arrowhead = none];  
P5 → P6 → P7 → P8 → P9 [arrowhead = none, minlen = 16.0];

node3 → node13 [arrowhead = none];  
}

[/dot]

I don’t want the nodes to have large space but want to keep the layout as it is.

So my question is: how to account for potential scaling when calculating the `minlen` based on the text width within the nodes? Or is there a way to draw a plot without specifying the `minlen` but keep the layout as in the example?

Much appreciate any help.

---

<div class="post-metadata">

**Author:** ![steveroush](https://avatars.discourse-cdn.com/v4/letter/s/a9adbd/32.png) [@steveroush](https://forum.graphviz.org/u/steveroush)\
**Post date:** [May 29, 2024, 6:12pm UTC](https://forum.graphviz.org/t/dynamic-minlen-to-account-for-plot-scaling/2220/2 "2024-05-29T18:12:43Z")

</div>

I’m confused (once again).

- **minlen** is in units of rank (integer) ([minlen | Graphviz](https://graphviz.org/docs/attrs/minlen/)).
- Your examples seem to be ranked TB.
- The poorly scaled distance is LR - within ranks.
- This is me looking confused.  
Could you provide input files to test with?

---

<div class="post-metadata">

**Author:** ![alim](https://avatars.discourse-cdn.com/v4/letter/a/bbe5ce/32.png) [@alim](https://forum.graphviz.org/u/alim)\
**Post date:** [May 29, 2024, 6:40pm UTC](https://forum.graphviz.org/t/dynamic-minlen-to-account-for-plot-scaling/2220/3 "2024-05-29T18:40:31Z")

</div>

Ah, sorry. I thought the code would display. I have added the code.

Yes, it is poorly scaled distance and I want to fix the problem with the distance. If I don’t `minlen`, the arrows connected to the nodes with various ways.

---

<div class="post-metadata">

**Author:** ![magjac](https://sea2.discourse-cdn.com/graphviz/user_avatar/forum.graphviz.org/magjac/32/16_2.png) [@magjac](https://forum.graphviz.org/u/magjac)\
**Post date:** [May 29, 2024, 7:11pm UTC](https://forum.graphviz.org/t/dynamic-minlen-to-account-for-plot-scaling/2220/4 "2024-05-29T19:11:08Z")

</div>

If you use [dot verbose=true] the code will display automatically.

---

<div class="post-metadata">

**Author:** ![alim](https://avatars.discourse-cdn.com/v4/letter/a/bbe5ce/32.png) [@alim](https://forum.graphviz.org/u/alim)\
**Post date:** [May 29, 2024, 7:26pm UTC](https://forum.graphviz.org/t/dynamic-minlen-to-account-for-plot-scaling/2220/5 "2024-05-29T19:26:34Z")

</div>

Perfect, thanks. The code is displayed now.

---

<div class="post-metadata">

**Author:** ![scnorth](https://sea2.discourse-cdn.com/graphviz/user_avatar/forum.graphviz.org/scnorth/32/89_2.png) [@scnorth](https://forum.graphviz.org/u/scnorth)\
**Post date:** [May 29, 2024, 9:47pm UTC](https://forum.graphviz.org/t/dynamic-minlen-to-account-for-plot-scaling/2220/6 "2024-05-29T21:47:40Z")

</div>

I don’t understand the specifics, but if the general idea is that some layout attribute (like minlen) is to be calculated at runtime using some other layout properties, I don’t see any way around just running two full layout passes.

It might be kind of cool if Graphviz had a richer way of defining attributes, allowing information like node sizes to be passed downstream, but we don’t have that. The closest thing are the, uh, escapes? macros? for text labels, like \N for the name of the current node, and \G, \T, \H etc. for current graph name, tail node name, head node name of an edge. See [strdup\_and\_subst\_obj0](https://gitlab.com/graphviz/graphviz/-/blob/main/lib/common/labels.c?ref_type=heads#L278) for complete documentation. We already have bugs involving this notation and I wouldn’t try to push it any further, besides, those escapes only work in text labels. I admire this idea but it would be considerable work to interface to the current world that mixes graphviz attributes, runtime object, and state written in C that’s floating around inside Graphviz programs.

---

<div class="post-metadata">

**Author:** ![steveroush](https://avatars.discourse-cdn.com/v4/letter/s/a9adbd/32.png) [@steveroush](https://forum.graphviz.org/u/steveroush)\
**Post date:** [May 30, 2024, 12:08am UTC](https://forum.graphviz.org/t/dynamic-minlen-to-account-for-plot-scaling/2220/7 "2024-05-30T00:08:34Z")

</div>

Some bugs:

- **overlap** does not apply to **dot**. It will be ignored.
- **rankdir** only applies to the root graph. All other references will be ignored.
- **minlen** is used to set the number of _ranks_ between _tail_ & _head_. e.g. setting **minlen=16** means that you want 16 (or more) ranks between tail & head. (i.e. not inches nor points)

Here is a modified version of your second graph. It uses the **group** attribute to keep nodes in columns. (It needs some **headclip** and **tailclip** fixes)

```auto
digraph consort_diagram {
graph [layout = dot, splines=ortho]

node [shape = rectangle, fillcolor = Biege, style="", fillcolor = "", color = ""]

node1 [label = "Cohort 1 (n=6)" group=C1]
node2 [label = "Cohort 2 (n=6)" group=C2]
node3 [label = "Cohort 3 (n=6)" group=C3]
node4 [label = "Cohort 4 (n=6)" group=C4]
node5 [label = "Cohort 5 (n=6)" group=C5]
node6 [label = "Excluded (n=1)\l"]
node7 [label = "Excluded (n=3)\lPatient died before surgery\l"]
node9 [label = "Excluded (n=3)\l"]
node10 [label = "Excluded (n=3)\lPatient declined to participate\l"]
node11 [label = "Cohort 1 (n=5)" group=C1]
node12 [label = "Cohort 2 (n=3)" group=C2]
node14 [label = "Cohort 5 (n=6)" group=C4]
node15 [label = "Cohort 5 (n=6)" group=C5]
node16 [label = "Total (n=14)" group=C3]

node [shape = point, width = 0, style=invis]

P1 [group=C1]
P2 [group=C2]
P3 [group=C4]
P4 [group=C5]
P5 [group=C1]
P6 [group=C2]
P7 [group=C3]
P8 [group=C4]
P9 [group=C5]

subgraph {
rank = same; node1; node2; node3; node4; node5;
}
subgraph {
rank = same; P1; node6;
}
subgraph {
rank = same; node11; node12; node13; node14; node15;
}
subgraph {
rank = same; node6; node7; node9; node10;
}
subgraph {
rank = same; P2; node7;
}
subgraph {
rank = same; node6; node7; node9; node10;
}
subgraph {
rank = same; P3; node9;
}
subgraph {
rank = same; P4; node10;
}
subgraph {
rank = same; P5; P6; P7; P8; P9;
}

edge[style=""];

node1 -> P1 [arrowhead = none];
P1 -> node6;
P1 -> node11;
node2 -> P2 [arrowhead = none];
P2 -> node7;
P2 -> node12;
node4 -> P3 [arrowhead = none];
P3 -> node9;
P3 -> node14;
node5 -> P4 [arrowhead = none];
P4 -> node10;
P4 -> node15;
P7 -> node16;

edge[headclip=false]
node11 -> P5 [arrowhead = none];
node12 -> P6 [arrowhead = none];
node13 -> P7 [arrowhead = none];
node14 -> P8 [arrowhead = none];
node15 -> P9 [arrowhead = none];
edge[headclip=false tailclip=false]
P5 -> P6 -> P7 -> P8 -> P9 [arrowhead = none]

node3 -> node13 [arrowhead = none];
}

```

Giving:

 ![minlenQb10](https://global.discourse-cdn.com/graphviz/original/2X/3/346ce810ccf81535437f7b4ce4413ad76d3dcdf5.png)

---

<div class="post-metadata">

**Author:** ![alim](https://avatars.discourse-cdn.com/v4/letter/a/bbe5ce/32.png) [@alim](https://forum.graphviz.org/u/alim)\
**Post date:** [May 30, 2024, 7:29am UTC](https://forum.graphviz.org/t/dynamic-minlen-to-account-for-plot-scaling/2220/8 "2024-05-30T07:29:17Z")

</div>

This is amazing. Thank you very much. It looks like I was wrong about the `minlen` and I don’t need to do any numerical calculation. This is super helpful.

Cheers.
