# How can I center nodes in a Graphviz cluster?

I have the follow dot definition of a graph:

[dot verbose=true]
digraph G {
bgcolor=“gray”

``````node [
style=filled
shape="circle"
fillcolor="green4"
penwidth="2"
color="green1"
fontcolor="white"
]

root [
shape="box"
color="white"
fontcolor="white"
label="hello"
]

AAA_1 [
label="AAA"
]

BBB_2 [
label="BBB"
]

subgraph cluster_ccc {
color=gray
fillcolor="blue"
style=filled
label="ccccc"
labelloc=b
fontcolor="white"
labeljust=l

subgraph cluster_south_a {
label="aaaaaaaaaaa"

5 [ label="" fillcolor="red" ]
6 [ label="" fillcolor="red" ]

5 -> 6
}

subgraph cluster_south_b {
label="bbbbbbbbbbb"

7 [ label="" fillcolor="red" ]
8 [ label="" fillcolor="red" ]

7 -> 8
}
}

root -> AAA_1
root -> BBB_2

AAA_1 -> 7
BBB_2 -> 5
``````

}
[/dot]

The red nodes are not horizontally centered in their cluster.

Is it possible to have them horizontally centered? If so, how?

No simple binary attribute, but nodesep can do it. Tried various values until .75 seemed “close enough”.

``````digraph G {
bgcolor="gray"
nodesep=.75  // inches
node [
style=filled
shape="circle"
fillcolor="green4"
penwidth="2"
color="green1"
fontcolor="white"
]

root [
shape="box"
color="white"
fontcolor="white"
label="hello"
]

AAA_1 [
label="AAA"
]

BBB_2 [
label="BBB"
]

subgraph cluster_ccc {
color=gray
fillcolor="blue"
style=filled
label="ccccc"
labelloc=b
fontcolor="white"
labeljust=l

subgraph cluster_south_a {
label="aaaaaaaaaaa"

5 [ label="" fillcolor="red" ]
6 [ label="" fillcolor="red" ]

5 -> 6
}

subgraph cluster_south_b {
label="bbbbbbbbbbb"

7 [ label="" fillcolor="red" ]
8 [ label="" fillcolor="red" ]

7 -> 8
}
}

root -> AAA_1
root -> BBB_2

AAA_1 -> 7
BBB_2 -> 5
}
``````

Giving:

Thank you. An interesting solution. Any idea why that works?

I do worry, as my graph adds nodes, etc., if that attribute will have unintended side effects leaving me no better off.

Might it be worth filing a feature request for something like this?

It looks like `nodesep=.75` works for my initial case, but doesn’t work in this one.

I need a generic solution which doesn’t require manual fine-tuning. I guess there isn’t one. How can a feature request be submitted?

[dot verbose=true]
digraph G {
bgcolor=“gray”
nodesep=.75 // inches
node [
style=filled
shape=“circle”
fillcolor=“green4”
penwidth=“2”
color=“green1”
fontcolor=“white”
]

``````root [
shape="box"
color="white"
fontcolor="white"
label="hello"
]

AAA_1 [
label="AAA"
]

BBB_2 [
label="BBB"
]

subgraph cluster_ccc {
color=gray
fillcolor="blue"
style=filled
label="ccccc"
labelloc=b
fontcolor="white"
labeljust=l

subgraph cluster_south_a {
label="aaaaaaaaaaaaaaaaaaaaaa"

5 [ label="" fillcolor="red" ]
6 [ label="" fillcolor="red" ]

5 -> 6
}

subgraph cluster_south_b {
label="bbbbbbbbbbbbbbbbbbbbbb"

7 [ label="" fillcolor="red" ]
8 [ label="" fillcolor="red" ]
9 [ label="" fillcolor="red" ]
10 [ label="" fillcolor="red" ]

7 -> 8
7 -> 8
7 -> 9
8 -> 9
8 -> 10
9 -> 10
}
}

root -> AAA_1
root -> BBB_2

AAA_1 -> 7
BBB_2 -> 5
``````

}
[/dot]

• here is the place to make enhancement requests & bug reports Issues · graphviz / graphviz · GitLab. But there is quite a backlog, do not hold your breath for an enhancement
• nodesep is probably the worst way to produce a “good” graph, all the nodes are affected
• margin applied to the two clusters might be a bit better, but not much
• the underlying problem is caused by the wide cluster labels. They cause cluster bounding-boxes that are much wider that required by the enclosed graph. Quite “legal”, but Graphviz does not have a “center graph within the cluster bounding box” option.
• Below is a “pretty good” solution that :
• adds 2 new levels of clusters
• explicitly sets rank for a new subgraph containing root
• sets constraint=false for some edges
Note that the two side-by-side clusters have flipped left & right. If that is a problem, try reversing the order within the file or adding another enclosing cluster
``````digraph G {
bgcolor="gray"

node [
style=filled
shape="circle"
fillcolor="green4"
penwidth="2"
color="green1"
fontcolor="white"
]

{
rank=source
root [
shape="box"
color="white"
fontcolor="white"
label="hello"
]
}

subgraph clusterXXX{
peripheries=0
AAA_1 [
label="AAA"
]

BBB_2 [
label="BBB"
]

subgraph cluster_ccc {
color=gray
fillcolor="blue"
style=filled
label="ccccc"
labelloc=b
fontcolor="white"
labeljust=l
peripheries=1

subgraph cluster_south_a {
label="aaaaaaaaaaaaaaaaaaaaaa"
subgraph cluster_south_a1 {
label="" peripheries=0
5 [ label="" fillcolor="red" ]
6 [ label="" fillcolor="red" ]

5 -> 6
}
}

subgraph cluster_south_b {
label="bbbbbbbbbbbbbbbbbbbbbb"
subgraph cluster_south_b1 {
label="" peripheries=0
7 [ label="" fillcolor="red" ]
8 [ label="" fillcolor="red" ]
9 [ label="" fillcolor="red" ]
10 [ label="" fillcolor="red" ]

7 -> 8
7 -> 8
7 -> 9
8 -> 9
8 -> 10
9 -> 10
}
}
}
}

{
edge [constraint=false]
root -> AAA_1
root -> BBB_2
}
AAA_1 -> 7
BBB_2 -> 5
}
``````

Giving:

You might also checkout the osage layout it centers nodes in clusters by default, and allows other ways of aligning as well. As the ranking is not as strict as in dot i did need to define some additional clusters for ordering the graph, but you can get to something like this:

example:

``````digraph G {
layout="osage";
splines="spline";
packmode="array_ut1";
pack=20;
bgcolor="gray"node [
style=filled
shape="circle"
fillcolor="green4"
penwidth="2"
color="green1"
fontcolor="white"

]
subgraph cluster_top{ sortv=1 packmode="array_ut1"
root [
shape="box"
color="white"
fontcolor="white"
label="hello"
]
}
subgraph cluster_middle{ sortv=2 packmode="array_ut2"
AAA_1 [ sortv=2
label="AAA"
]

BBB_2 [sortv=1
label="BBB"
]
}
subgraph cluster_ccc { sortv=3 packmode="array_ut2"
color=gray
fillcolor="blue"
style=filled
label="ccccc"
labelloc=b
fontcolor="white"
labeljust=l;

subgraph cluster_south_a {
label="aaaaaaaaaaaaaa";
pack=20; packmode="array_ut1"; sortv="1";

5 [ label="" fillcolor="red" ]
6 [ label="" fillcolor="red" ]

5 -> 6
}

subgraph cluster_south_b {
label="bbbbbbbbbbbbbbbbbbbbbb" pack=20 packmode="array_ut1" sortv="2"

7 [ label="" fillcolor="red" ]
8 [ label="" fillcolor="red" ]
9 [ label="" fillcolor="red" ]
10 [ label="" fillcolor="red" ]

7 -> 8
7 -> 8
7 -> 9
8 -> 9
8 -> 10
9 -> 10
}
}

root -> AAA_1
root -> BBB_2

AAA_1 -> 7
BBB_2 -> 5
}``````

[dot]
digraph G {
layout=“osage”;
splines=“spline”;
packmode=“array_ut1”;
pack=20;
bgcolor="gray"node [
style=filled
shape=“circle”
fillcolor=“green4”
penwidth=“2”
color=“green1”
fontcolor=“white”

]
subgraph cluster_top{ sortv=1 packmode=“array_ut1”
root [
shape=“box”
color=“white”
fontcolor=“white”
label=“hello”
]
}
subgraph cluster_middle{ sortv=2 packmode=“array_ut2”
AAA_1 [ sortv=2
label=“AAA”
]

BBB_2 [sortv=1
label=“BBB”
]
}
subgraph cluster_ccc { sortv=3 packmode=“array_ut2”
color=gray
fillcolor=“blue”
style=filled
label=“ccccc”
labelloc=b
fontcolor=“white”
labeljust=l;

``````subgraph cluster_south_a {
label="aaaaaaaaaaaaaa";
pack=20; packmode="array_ut1"; sortv="1";

5 [ label="" fillcolor="red" ]
6 [ label="" fillcolor="red" ]

5 -> 6
}

subgraph cluster_south_b {
label="bbbbbbbbbbbbbbbbbbbbbb" pack=20 packmode="array_ut1" sortv="2"

7 [ label="" fillcolor="red" ]
8 [ label="" fillcolor="red" ]
9 [ label="" fillcolor="red" ]
10 [ label="" fillcolor="red" ]

7 -> 8
7 -> 8
7 -> 9
8 -> 9
8 -> 10
9 -> 10
}
``````

}

root → AAA_1
root → BBB_2

AAA_1 → 7
BBB_2 → 5
}
[/dot]

1 Like

Thanks @steveroush for your potential solution. It was close again. However, if I add `BBB_2 -> 7`, the techniques used to have the nodes in the cluster centered no longer work. It seems a minimum requirement for my use case is that the layout system used supports explicit centering. I assume there will also be a manual fine-tuning technique for a specific layout, but use case does not lend itself to that. I am going to try the osage layout that @BartB suggested. I am hopeful it will work because it has explicit centering of nodes in a cluster.

@BartB Your solution to use the `osage` layout is working out well. However, I am seeing edges drawn over nodes.

With `splines` set to `spline`, the output is:

With `splines` set to `curved`, the output is:

With `splines` set to `ortho`, the output is:

The splines documentation says:

If `splines=true` , edges are drawn as splines routed around nodes

Only with `ortho` are the edges routed around nodes.

Is this a bug with the `osage` layout? Or, is there a way to use `curved` or `spline` with the `splines` attribute and not have the edges overlap nodes?

``````digraph G {
layout    = "osage"
splines   = spline
packmode  = "array_ut1"
pack      = 20
bgcolor   = "gray"

edge [ arrowhead = none ]

node [
style     = filled
shape     = "circle"
fillcolor = "green4"
penwidth  = "2"
color     = "green1"
fontcolor = "white"
]

subgraph cluster_root {
sortv     = 1
packmode  = "array_ut1"
color     = transparent

root [
shape     = "box"
color     = "white"
fontcolor = "white"
label     = "hello"
]
}

subgraph cluster_parents {
sortv     = 2
packmode  = array_rt3
color     = transparent

AAA_1 [
sortv = 1
label = "AAA"
]

BBB_2 [
sortv = 2
label = "BBB"
]

CCC_3 [
sortv = 3
label = "CCC"
]
}

subgraph cluster_children {
sortv     = 3
color     = transparent
packmode  = array_rt3

subgraph cluster_child_south {
sortv     = 3
packmode  = "array_ut2"
color     = gray
fillcolor = blue
style     = filled
label     = "South"
labelloc  = b
fontcolor = "white"
labeljust = l
style     = "rounded, filled"

subgraph cluster_child_south_a {
label = "aaaaaaaa"
sortv = 1
style = "rounded, filled"

5 [ label="" fillcolor="red" ]
6 [ label="" fillcolor="red" ]

5 -> 6
}

subgraph cluster_child_south_b {
label = "bbbbbbbbbbbbbbbbbbbbbb"
sortv = 2
style = "rounded, filled"

7 [ label = "" fillcolor = "red" ]
8 [ label = "" fillcolor = "red" ]
9 [ label = "" fillcolor = "red" ]
10 [ label = "" fillcolor = "red" ]

7 -> 8
7 -> 9
8 -> 9
8 -> 10
9 -> 10
}
}

subgraph cluster_child_east {
sortv     = 3
packmode  = "array_ut2"
color     = gray
fillcolor = blue
style     = filled
label     = "East"
labelloc  = b
fontcolor = "white"
labeljust = l
style     = "rounded, filled"

subgraph cluster_child_east_a {
label = "aaaaaaaa"
sortv = 1
style = "rounded, filled"

ea1 [ label="" fillcolor="red" ]
ea2 [ label="" fillcolor="red" ]

ea1 -> ea2
}

subgraph cluster_child_east_b {
label = "bbbbbbbbbbbbbbbbbbbbbb"
sortv = 2
style = "rounded, filled"

eb1 [ label = "" fillcolor = "red" ]
eb2 [ label = "" fillcolor = "red" ]
eb3 [ label = "" fillcolor = "red" ]

eb1 -> eb2
eb1 -> eb3
eb2 -> eb3
}
}
}

root -> AAA_1
root -> BBB_2
root -> CCC_3

AAA_1 -> 7

BBB_2 -> 5
BBB_2 -> 7
BBB_2 -> eb1

CCC_3 -> 5
CCC_3 -> ea1
}
``````

@eric_g_97477 , in my experience both splines=spline or ortho are respecting the fact that edges are not overlapping nodes. I think the problem starts when circular shapes are used with a larger penwidth. But even in that case they will only touch the circle on the border and not go through it completely, which, in my case, would be still good enough. When using rectangles , like in the below example, i see edges to be non node overlapping when using splines=spline.

I think you may be encountering Incorrect edge splines with fdp and neato (#2168) · Issues · graphviz / graphviz · GitLab.

• osage seems to use the neato spline-routing code
• neato spline-routing will route edges much closer to nodes that dot
• that code seems to have a bug, resulting in edges drawn over nodes

The good-ish news:

• add the attribute esep=“+18” to your Root graph (esep | Graphviz) (undocumented for osage), to get the edges routed further from the nodes.
• you might also increase pack a bit
• seemingly you can set pack at the cluster-level, not just Root-level

With esep=“+18”:

2 Likes