# Is it possible to achieve this type of design?

Hi again,

I am trying to design a very specific type of graph with certain colors and shapes but I am unable to tell if this is achievable with Graphviz.

My goal is to draw a directed graph where each node:

• is composed of 3 smaller boxes aligned horizontally (`striped` `rectangle` or `record`, `rounded`)
• contains 3 `labels` accordingly
• is filled with a gradient
• has a black square node located on top of the middle box

Example (all black boxes have been placed manually) :

I have made it to the third requirement but I am struggling implementing the last one. How would you place square nodes (`rect`, `square`, `box`, etc) on top of the first set of nodes (colored rectangles) so as they perfectly fit the dimensions of the middle boxes they are covering ? Is that even possible with Graphviz ?

Example code (Python):

``````# Dictionary storing relations between nodes
d = {0: set([1, 2, 3]),
1: set([4, 5, 6]),
2: set([7, 8, 9, 10]),
3: set([0]),
4: set([]),
5: set([]),
6: set([]),
7: set([]),
8: set([0]),
9: set([]),
10: set([])}

# List of node labels (3 labels per node)
P = [('S', 'M', 'S'),
('M', 'S', 'L'),
('M', 'S', 'S'),
('M', 'S', 'M'),
('S', 'L', 'L'),
('S', 'L', 'M'),
('S', 'L', 'X'),
('S', 'S', 'S'),
('S', 'S', 'M'),
('S', 'S', 'L'),
('S', 'S', 'X')]

# Dictionary storing the colors corresponding to each label
c = {'S':'olivedrab1',
'M':'mediumturquoise',
'L':'deepskyblue',
'X':'palevioletred1'}

# Create a "directed graph" with general node and edge attributes
G = Digraph(node_attr={'shape':'record',
'style':'rounded, filled',
'color':'white',
'height':'0.1',
'fontcolor':'white'},

edge_attr={'color':'grey',
)

G.attr('graph', bgcolor='transparent')

# 1st pass: create all nodes (0 to 10)
for k in d:
l1, l2, l3 = P[k]

# set specific attribute to each node (label & colors)
G.attr('node', label='{} | {} | {}'.format(l1, ' ', l2), fillcolor='{}:{}'.format(c[l1], c[l2]))
G.node(str(k))

# 2nd pass: create edges between nodes
for k in d:
l1, l2, l3 = P[k]
for i in d[k]:
if i in d:
G.edge(str(k), str(i))

# Then, how to overlap black square nodes ?
``````
``````digraph G {
node [shape=plaintext]
t [label=<
<TABLE border="1" cellborder="1" cellspacing="0" cellpadding="0" style="rounded">
<TR>
<TD WIDTH="50" BGCOLOR="red:blue" ><FONT COLOR="white">00</FONT></TD>
<TD WIDTH="50" BGCOLOR="black"><FONT COLOR="white">02</FONT></TD>
<TD WIDTH="50" BGCOLOR="pink:green"><FONT COLOR="white">03</FONT></TD>
</TR>
</TABLE>>];
}
``````

Giving:

1 Like

Hi, thank you for the pointer !

The problem with this approach is that:

• there is 1 gradient per cell instead of 1 filling all three at once
• gradients are filling the inner cells, not the larger node.
• the overall outline is not sharp/clear because of the overlapping of multiple cell borders (inner+outer)

Illustration:

When removing the inner cellsâ€™ borders for a cleaner outline, it becomes apparent that the gradients are not filling the main node.

Instead, is there a way to place a `rect` on top of the middle cell of a `record` node, just like in my previous example picture ?

Very fiddly, but seemingly what you want.

• Output to dot output format (dot -Tdot) (see DOT | Graphviz)
• Use post-processing to add center node (see script below),
• Finally run through neato -n (FAQ | Graphviz)
Demo graph:
``````digraph G {
node [shape=Mrecord fontcolor="white"]
triple [style=filled fillcolor="red:blue" label="<f0>AA|<f1>BB|<f2>CC"];
}
``````

I used gvpr to add the center node overlay (see https://graphviz.org/pdf/gvpr.1.pdf), but seemingly python (or others) could be used

My script:

``````f=gradientOverlay.gv;
T=dot;
F=`basename \$f .gv`;
/usr/bin/dot -T\$T \$f |
/usr/bin/gvpr -c 'BEGIN{int Z=0;node_t aNode}N{Z++;aNode=node(\$G,"__top_" + (string)Z);aNode.width=.05+(float)\$.width/3.;aNode.height=\$.height;aNode.pos=\$.pos;aNode.shape="rect";aNode.style="filled";aNode.fillcolor="black";aNode.label=html(\$G,"<u>4&#960;</u><BR/>8");}'
|/usr/bin/neato -n -Tpng >O.png
``````

Giving:

The post-processing creates new, overlay nodes, using the pos of the original node. It sets attributes of the new node as needed, setting width to ~original width/3 and height to ~original height.

1 Like

Thank you so much! Iâ€™ll see if I can implement this in Python.

I am having a difficult time finding a way to do the same with Python. Am I correct that gv.3python is the right extension to load in this case ? If so, where can I find it ?

If not, would you mind sharing a transcription of your example in Graphviz Python ? That would be very helpful.

[Note: I am not a Python coder and have never used the PyGraphviz interface. Take my suggestions with a ton of salt]

My best guess:

• I donâ€™t think you need to worry about gv.3python
1. Add the labels for the â€śoverlayâ€ť nodes to the existing nodes using a new, made-up attribute name. I suggest something like centerLabel. It is completely legal to add new attribute name/value pairs to Graphviz files. Graphviz produces no error messages, but just passes them downstream.
[after the code presented above]
2. G.layout to add positions to the graph
3. iterate through all the nodes. For each node with a centerLabel attribute (see above):
3.1. use node command to create the overlay node (using a new, unique node name)
3.2 set the overlay node pos equal to the old node pos
3.3 set the overlay node shape to â€śrectâ€ť
3.4 set the overlay node style to â€śfilledâ€ť
3.5 set the overlay node fillcolor to â€śblackâ€ť
3.6 set the overlay node height to ~ old node height - .02
3.7 set the overlay node width to ~ .05 + (old node width/3)
3.8 set the overlay node label to the old node centerLabel value
4. G.layout (again) but using the neato engine and -n arg
5. G.draw - I assume this produces the output

This is â€śbest guessâ€ť. I hope it helps in some way.