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.
Related
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.
I'm using DOT to visualize a lisp AST, and the picture that is generated currently looks like this:
Currently, the vertical lines are specified normally like parent -> child;, and the skewed ones are specified using constraint like so: parent -> child [constraint=false];.
This kind of works, but what I'm really looking for is a way to make the vertical connections stay the same where each connection puts the child one row downwards, but make the horizontal connections be actually horizontal. This would create something that looks more like this:
Is this possible?
You may be making it too complicated - this simple basic code does the job:
digraph so
{
# nodes
A[ label = "list" ];
B[ label = "ident: +" ];
C[ label = "literal: 1" ];
D[ label = "list" ];
E[ label = "ident: *" ];
F[ label = "literal: 3" ];
G[ label = "literal: 2" ];
# layout
{ rank = same; B C D }
{ rank = same; E F G }
# edges
A -> B;
B -> C -> D;
D -> E;
E -> F -> G;
}
compiled with dot -T png -o so.png so.dot yields what you want:
Let's take this map, where '#' illustrates a taken square and '.' illustrates a free square:
1 . # # # . .
2 . # . . # .
3 # . . . . #
4 . # # # . .
5 . . . . . .
6 . . . . . .
- 1 2 3 4 5 6
Now, if I put a '#' in the square 4,5 the area would be "filled" like this:
1 . # # # . .
2 . # # # # .
3 # # # # # #
4 . # # # # .
5 . . . . . .
6 . . . . . .
- 1 2 3 4 5 6
So, what is the best way to find "a limited square", where I can start flood fill or other filling algorithm that fills the limited area?
If you can convert your problem to a graph, what you are looking for is to identify connected components. And if a connected component does not contain an edge that is the boundary edge, then you have found the region that needs to be filled.
If I define the graph like this:
G = (V, E)
V = {r1, r2, r3, r4, r5, r6, c1, c2, c3, c4, c5, c6}
E = {(u, v) | u, v are elements of V && the cell(u, v) is not taken}
Now run DFS to find all disconnected trees. Algorithm:
for each u in V:
color[u] = white
for each u in V:
if color[u] == white:
contains_boundary_edge = False
DFS-visit( u, contains_boundary_edge )
if not contains_boundary_edge:
Flood-fill( u )
DFS-visit( u, contains_boundary_edge ):
color[u] = gray
for each v in adjacent( u ):
if color[v] == white:
if edge(u, v) is a boundary edge: // Can be easily identified if one of u, v is start or end row/col.
contains_boundary_edge = True
DFS-visit( v, contains_boundary_edge )
color[u] = black
I think this question can be reduced to a convex hull problem if we consider each # as point x,y then convex hull be give us the x,y of all the # which are absent
http://en.wikipedia.org/wiki/Convex_hull
I will try to code it in leisure ..
You could attack this by processing each '.' node.
Definition: A '.' node is enclosed if there does not exist a path from the node to the boundary of the map.
If you agree with the above definition, the algorithm would be to maintain a graph of '.' nodes, where adjacent nodes are connected.
Every time a node is changed to '#', remove it from this graph, and check each remaining '.' node to see if a path exists from it to one of the nodes on the map border.
Depending on the size of your map, you made need to attempt various optimizations to limit the number of path searches performed each turn.
If you model this map as a graph, and each square is connected to its four neighbours, you can use a bridge finding algorithm to find the square you need.
Note this model gives you several subgraphs to work with sometimes, so it might produce a number of false positives around the border, since adding a # there would certainly separate some nodes from the rest. To get around this, you could pad two levels of squares around the graph, so that no single # could separate a border node from the rest.
#svick's comment inspired this method.
I would start from each neighbor of the picked square, and try to 'escape' to the boundary of the grid. Meanwhile, mark the path followed by 'X'. If you can escape: undo every 'X'. If you cannot escape, replace every 'X' by '#'. I made an example in Java, as shown below.
int W, H;
char[][] input;
final int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public void handle(int x, int y) {
// try each neihgbor
for (int[] d : directions) {
if (canEscape(input, x, y)) {
// if we can escape, the path found shouldn't be filled
// so replace the Xes by '.';
handleXes(input, false);
} else {
// if we cannot escape, this is a closed shape, so
// fill with '#'
handleXes(input, true);
}
// note that this can be written more concisely as
// handleXes(input, !canEscape(input, x, y));
}
}
public boolean canEscape(char[][] grid, int x, int y) {
if (isEscape(grid, x, y))
return true
if (isValid(grid, x, y)) {
// mark as visited
grid[x][y] = 'X';
// try each neighbor
for (int[] d : directions) {
if (canEscape(grid, x+d[0], y+d[1]))
return true;
}
}
return false;
}
public boolean isValid(char[][] grid, int x, int y) {
return 0 <= x && x < W && 0 <= y && y < H && grid[x][y] == '.';
}
public boolean isEscape(char[][] grid, int x, int y) {
return (0 == x || x == W-1 || 0 == y || y == H-1) && grid[x][y] == '.';
}
public void handleXes(char[][] grid, boolean fill) {
for (int x = 0; x < W; x++)
for (int y = 0; y < H; y++)
if (grid[x][y] == 'X')
grid[x][y] = fill ? '#' : '.';
}
I'm using Graphviz (namely Dot) to draw up a state machine for a Hypermedia API I'm planning on building. In my graph, nodes represent states, while edges represent links. What I'm trying to do is have edges (links) of the same "type" (ie, use the same verb or same rel) to share attributes like color.
I know you can define "global" attributes that apply to all nodes/edges, but I need something I can apply more generally to several different "types". The closest analogy I can come up with for what I want is HTML classes. I don't need multiple "classes" for my edges (although that would be nice) but repeating attributes like color=red, style=bold is cumbersome.
Is there a way in Dot to declare something like this? Or at least some way I don't have to repeat myself so often?
I've done this in two different ways:
Option (A): Write the dot file from another script. This is particularly useful when I'm using a script (in, say, Python or Perl) to rework the input data into dot format for drawing. In that case, as well as having the Python script write the data into dot format I can also have it write the attributes for each node and edge into the dot file. An example is shown below (not runnable because I've extracted it from a larger script that interprets the input data but you can see how the Perl is writing the dot code).
print "graph G {\n graph [overlap = scale, size = \"10,10\"]; node [fontname = \"Helvetica\", fontsize = 9]\n";
for ($j = 0; $j <= $#sectionList; $j++) {
print "n$j [label = \"$sectionList[$j]\", style = filled, fillcolor = $groupColour{$group{$sectionList[$j]}} ]\n";
}
for ($j = 0; $j <= $#sectionList; $j++) {
for ($i = $j+1; $i <= $#sectionList; $i++) {
$wt = ($collab{$sectionList[$j]}{$sectionList[$i]}+0)/
($collab{$sectionList[$j]}{$sectionList[$j]}+0);
if ($wt > 0.01) {
print "n$j -- n$i [weight = $wt, ";
if ($wt > 0.15) {
print "style = bold]\n";
}
elsif ($wt > 0.04) {
print "]\n";
} else {
print "style = dotted]\n";
}
}
}
print "\n";
}
print "}\n";
Option (B): If I'm writing the dot script by hand, I'll use a macro processor to define common elements. For example given the file polygon.dot.m4 containing the m4 macro define() as follows:
define(SHAPE1,square)
define(SHAPE2,triangle)
digraph G {
a -> b -> c;
b -> d;
a [shape=SHAPE1];
b [shape=SHAPE2];
d [shape=SHAPE1];
e [shape=SHAPE2];
}
... the command m4 <polygon.dot.m4 | dot -Tjpg -opolygon.jpg produces:
Changing the definitions of SHAPE1 and SHAPE2 at the top of the file will change the shapes drawn for each of the relevant nodes.
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]