How can gvpr clone a graph's edges? - graphviz

I would expect that the following GraphViz gvpr command would copy the graph's edges, but it only copies its nodes. What is the correct incantation to clone a graph's edges?
$ echo 'digraph { john -> mary }' | gvpr 'E {clone($O, $)}'
digraph gvpr_result {
john;
mary;
}

Edges can be correctly cloned into a new graph.
BEGIN { graph_t g = graph("new", "D") } // Initialize new directional graph
E { clone(g, $) } // Clone edge into new graph
END { write(g) } // Write new graph

Related

AWK recursive tree structure

I'm trying to parse a file that contains lines in a hierarchical structure. For example the file:
a b c
a b d
a B C
A B C
indicates that a contains b and B, that b contains c and d, that B contains C. A contains a different B which contains its own C.
This is much like a list of files.
I want to format this in a hierarchical bracketed way like:
a {
b {
c
d
}
B {
C
}
}
A {
B {
C
}
}
I couldn't come up with a decent way to do this. I thought that AWK would be my best bet, but came up short with how to actually implement it.
Context
My input is actually a list of files. I can of course separate the fields by spaces if needed, or keep them with /. The files are unordered and generated from a code-base during compile-time via inspection. My desired output is going to be a graphviz DOT file containing each file in its own subgraph.
Thus for the input:
a/b/c
a/b/d
a/B/C
A/B/C
the output would be
digraph {
subgraph cluster_a {
label = a
subgraph cluster_b {
label = b
node_1 [label=c]
node_2 [label=d]
}
subgraph cluster_B {
label = B
node_3 [label=C]
}
}
subgraph cluster_A {
label = A
subgraph cluster_B {
label = B
node_4 [label=C]
}
}
}
Does anybody know how I could get this processing done? I'm open to other tools as well, not just AWK.
NOTE: Depth is not fixed, though I could pre-compute the maximum depth if necessary. Not all leaves will be at the same depth either.
I'm open to other tools as well, not just AWK.
I offer this Python solution:
import sys
INDENT = ' '
NODE_COUNT = 1
def build(node, l):
x = l[0]
if x not in node:
node[x] = {}
if len(l) > 1:
build(node[x], l[1:])
def indent(s, depth):
print('%s%s' % (INDENT * depth, s))
def print_node(label, value, depth):
if len(value.keys()) > 0:
indent('subgraph cluster_%s {' % label, depth)
indent(' label = %s' % label, depth)
for child in value:
print_node(child, value[child], depth+1)
indent('}', depth)
else:
global NODE_COUNT
indent('node_%d [label=%s]' % (NODE_COUNT, label), depth)
NODE_COUNT += 1
def main():
d = {}
for line in sys.stdin:
build(d, [x.strip() for x in line.split()])
print('digraph {')
for k in d.keys():
print_node(k, d[k], 1)
print('}')
if __name__ == '__main__':
main()
Result:
$ cat rels.txt
a b c
a b d
a B C
A B C
$ cat rels.txt | python3 make_rels.py
digraph {
subgraph cluster_a {
label = a
subgraph cluster_b {
label = b
node_1 [label=c]
node_2 [label=d]
}
subgraph cluster_B {
label = B
node_3 [label=C]
}
}
subgraph cluster_A {
label = A
subgraph cluster_B {
label = B
node_4 [label=C]
}
}
}
If the depth is fixed at 3 levels
gawk -F/ '
{f[$1][$2][$3] = 1}
END {
n = 0
print "digraph {"
for (a in f) {
print " subgraph cluster_" a " {"
print " label = " a
for (b in f[a]) {
print " subgraph cluster_" b " {"
print " label = " b
for (c in f[a][b]) {
printf " node_%d [label=%s]\n", ++n, c
}
print " }"
}
print " }"
}
print "}"
}
' file
digraph {
subgraph cluster_A {
label = A
subgraph cluster_B {
label = B
node_1 [label=C]
}
}
subgraph cluster_a {
label = a
subgraph cluster_B {
label = B
node_2 [label=C]
}
subgraph cluster_b {
label = b
node_3 [label=c]
node_4 [label=d]
}
}
}
If the depth is arbitrary, things get complicated.

How can I direct dot to use a shorter edge path?

The diagram below is laid out almost perfectly, apart from the edge from the left "named pipe" node to "cat", which takes a long circuitous route, instead of the obvious short one I've marked with red on the diagram below. Is there a way to direct dot to use the short edge path? Note that the sequence diagram on the diagram's bottom, must be rendered as it currently appears, i.e. in the left to right order.
This is the code that draws the diagram.
digraph D {
fontname="Arial";
subgraph cluster_async {
label="Asynchronous processes";
style=filled;
color=lightgrey;
node [shape=box, style=solid, fillcolor=white, fontname="Arial"];
{
rank=same;
npi_0_0_0 [label="named\npipe"];
npi_0_3_0 [label="named\npipe"];
npi_0_2_0 [label="named\npipe"];
}
node [shape=box, style=bold];
tee [label="sgsh-tee"];
"ls -l" -> tee;
tee -> npi_0_0_0;
tee -> npi_0_3_0;
tee -> npi_0_2_0;
NBYTES [label="sgsh-writeval -s NBYTES"];
npi_0_3_0 -> "awk '{s += $5} END {print s}'" -> NBYTES;
NDIRS [label="sgsh-writeval -s NDIRS"];
npi_0_2_0 -> "grep -c '^d'" -> NDIRS;
// Put some order in the appearance
{
rank=same;
NDIRS;
NBYTES;
}
}
subgraph clustersync {
label="Synchronous sequence";
style=dashed;
start [shape=circle, style=filled, label="", fillcolor=black, width=.2];
node [shape=box, style=bold, fontname="Arial"];
npi_0_0_0:sw -> cat:nw [constraint=false];
"sgsh-readval -s NDIRS" -> echo;
"sgsh-readval -s NBYTES" -> echo;
NBYTES -> "sgsh-readval -s NBYTES";
NDIRS -> "sgsh-readval -s NDIRS";
end [shape=doublecircle, style=filled, label="", fillcolor=black, width=.2];
{
rank=same;
edge [arrowhead=open];
start -> cat -> echo -> end;
}
}
}
(In case you're interested, the diagram illustrates the setup of an example from sgsh.)
For this graph, setting splines=ortho will produce a desirable result.

Graphviz: Layout multiple clusters

digraph G {
rankdir=LR;
subgraph cluster_one {
one_x -> one_y -> one_z;
}
subgraph cluster_two {
two_x -> two_y;
}
subgraph cluster_three {
three_x -> three_y;
}
}
The order of the clusters is reversed. They should be in the order they appear in the source file.
I want all clusters to be of the same width (determined by the largest sub-graph) and aligned.
The order of the clusters is reversed. They should be in the order they appear in the source file.
The following code should work:
digraph G {
rankdir=LR;
subgraph cluster_one {
shape=rect;
one_x -> one_y -> one_z;
}
subgraph cluster_two {
two_x -> two_y;
}
subgraph cluster_three {
three_x -> three_y;
}
one_x->two_y[style=invis];
two_x->three_y[style=invis];
}
I want all clusters to be of the same width (determined by the largest sub-graph) and aligned.
I have found this answer. It is a bad solution, but I can't give a better one.

How to darw a node with label is '*' using dot (graphviz)?

I want to draw a node that the node label only displays a char : '*' .
digraph {
node2[label="*"];
}
But when the image generated, the label displays a empty string. Is that possiable to draw a node only displays a '*' ?
digraph G {
a [shape="none", label="*"] ;
b [shape="box", label="*"] ;
a -> b ;
}
Works fine.

Graphviz: how to set 'default' arrow style?

Consider this dot language code:
digraph graphname {
subgraph clusterA {
node [shape=plaintext,style=filled];
1 -> 2 [arrowhead=normal,arrowtail=dot];
2 -> 3 -> X2 -> 5;
6;
7;
label = "A";
color=blue
}
}
In the above example, only the 1 -> 2 connection will have the arrowhead=normal,arrowtail=dot style applied; all the other arrows will be of the "default" style.
My question is - how do I set the arrow style (for the entire subgraph - or for the entire graph), without having to copy paste "[arrowhead=normal,arrowtail=dot];" next to each edge connection?
EDIT: Just for reference - the answer from Jesse didn't contain any code; I wrote that snippet and had it in this space here - for unknown reasons, a moderator cut it off from here and pasted it into Jesse's answer.
Use the edge attribute statement, as stated in the DOT Language documentation.
digraph graphname {
subgraph clusterA {
node [shape=plaintext,style=filled];
edge [arrowhead=normal,arrowtail=dot];
1 -> 2 ;
2 -> 3 -> X2 -> 5;
6;
7;
label = "A";
color=blue
}
}
Just like you did for nodes, but using edge, e.g. edge[style=dashed]

Resources