--- /dev/null
+/* Takes a graph laid out by twopi and adds rings.
+ * Assumes ARGV[] = "root" "=" <rootname>, as output by twopi -v.
+ * Usage:
+ * twopi -v foo.dot > out 2> log
+ * gvpr -f addrings.g -a"`grep root log`" out | neato -n2 ...
+ */
+BEG_G {
+ graph_t og;
+ edge_t e;
+ node_t ctr = node($, ARGV[0]);
+ double rs = 1.0; /* min. slack between the squares of two consecutive radii */
+ int cx, cy;
+ int x, y;
+ node_t n;
+ int i, n_r;
+ int d;
+ int rads[int];
+ char* ctr_s = ctr.pos;
+ sscanf (ctr_s, "%d,%d", &cx, &cy);
+ if (hasAttr($, "ranksep")) {
+ sscanf ($.ranksep, "%f", &rs);
+ if (rs == 0.0) rs = 1.0;
+ }
+ rs *= 72;
+ rs = 1.5*rs*rs;
+}
+N [$ != ctr] {
+ sscanf ($.pos, "%d,%d", &x, &y);
+ d = (x-cx)*(x-cx) + (y-cy)*(y-cy);
+ for (rads[i]) {
+ if ((rads[i]-rs <= d) && (d <= rads[i]+rs)) return;
+ }
+ n_r++;
+ rads[n_r] = d;
+}
+END_G {
+ og = copy (NULL, $);
+ og.outputorder = "nodesfirst";
+ setDflt (og, "N", "label", "\\N");
+ for (rads[i]) {
+ n = node(og, "ring_"+((string)i));
+ n.shape = "circle";
+ n.pos = ctr_s;
+ n.style = "";
+ n.label = "";
+ d = rads[i];
+ n.width = sprintf("%f", sqrt(d)/36.0);
+ }
+ for (n=fstnode($);n;n = nxtnode(n))
+ clone (og, n);
+ for (n=fstnode($);n;n = nxtnode(n))
+ for (e=fstedge(n);e;e = nxtedge(e,n))
+ clone (og, e);
+ write(og);
+
+}
--- /dev/null
+/* anonymize the graph */
+BEG_G {
+ node_t map[node_t];
+ graph_t dup;
+ int id = 0;
+ char* gtype;
+ node_t n;
+ edge_t e;
+ char* l;
+
+ if ($.directed) gtype = "D";
+ else gtype = "U";
+ if ($.strict) gtype = gtype + "S";
+ dup = graph ($.name, gtype);
+ $tvtype = TV_ne;
+}
+
+N {
+ n = node(dup, (char*)id);
+ n.label=$.name;
+ map[$] = n;
+ id++;
+}
+
+E {
+ edge (map[$.tail],map[$.head],"");
+}
+
+END_G {
+ write (dup);
+ delete (dup,dup);
+}
--- /dev/null
+/* List known node attributes */
+BEG_G {
+ char* attr;
+ for (attr = fstAttr($,"N"); attr != ""; attr = nxtAttr($,"N",attr)) {
+ print (attr);
+ }
+}
--- /dev/null
+/* computes the bounding box of a graph based on its nodes
+ taking into account clusters and node sizes.
+ */
+BEGIN {
+ double x, y, w2, h2;
+ double llx, lly, urx, ury;
+ double llx0, lly0, urx0, ury0;
+
+ graph_t clustBB (graph_t G) {
+ graph_t sg;
+ for (sg = fstsubg(G); sg; sg = nxtsubg(sg)) {
+ sg = clustBB(sg);
+ }
+ if (G.name == "cluster*") {
+ sscanf (G.bb, "%lf,%lf,%lf,%lf", &llx0, &lly0, &urx0, &ury0);
+ if (llx0 < llx) llx = llx0;
+ if (lly0 < lly) lly = lly0;
+ if (urx0 > urx) urx = urx0;
+ if (ury0 > ury) ury = ury0;
+ }
+ return G;
+ }
+}
+BEG_G {
+ llx = 1000000; lly = 1000000; urx = -1000000; ury = -1000000;
+}
+N {
+ sscanf ($.pos, "%lf,%lf", &x, &y);
+ w2 = (36.0*(double)$.width);
+ h2 = (36.0*(double)$.height);
+ if ((x - w2) < llx) llx = x - w2;
+ if ((x + w2) > urx) urx = x + w2;
+ if ((y - h2) < lly) lly = y - h2;
+ if ((y + h2) > ury) ury = y + h2;
+}
+END_G {
+ clustBB ($);
+ $.bb = sprintf ("%lf,%lf,%lf,%lf", llx, lly, urx, ury);
+}
--- /dev/null
+/* Construct subgraph reachable from node ARGV[0] by forward edges */
+BEG_G {
+ node_t r = node($,ARGV[0]);
+
+ $tvroot = r;
+ $tvtype = TV_fwd;
+}
+N{$tvroot=NULL; subnode($T,$);}
--- /dev/null
+# test
+/* color nodes using output of dijkstra */
+BEG_G {
+ double h, hn, hf, d, sat, md = maxdist;
+ if (hue_near =="") hue_near = 0.8;
+ if (hue_far =="") hue_far = 1.2;
+ hn = hue_near;
+ hf = hue_far;
+}
+# test
+N {
+ d = dist;
+ sat = (md - d + 1.0) /( md + 1.0);
+ h = hn + ((hf - hn) * d)/md;
+ while (h < 0.0) h = h + 1.0;
+ while (h > 1.0) h = h - 1.0;
+ color = sprintf("%lf %lf %lf",hue,sat,1.0);
+ /* make sure the shape is filled */
+ if (!match(style,"filled")>=0) {
+ if (style != "") style=sprintf("%s,filled",style);
+ else style="filled";
+ }
+}
+
--- /dev/null
+/* color edges based on normalized length alpha.
+ * If we have lim[i-1] <= alpha <= lim[i], the color
+ * is linearly interpolated between color[i-1] and color[i].
+ * Edges already having a color attribute are left unchanged.
+ */
+BEGIN {
+ double lens [edge_t];
+ double maxlen, len, minlen=1.7976931348623157e+308;
+ double alpha, x0, y0, x1, y1;
+ double h0, s0, v0;
+ int i;
+
+ int ncolors; /* number of colors: ncolors >= 2 */
+ /* start of color i.
+ * lim[0]=0 < lim[1] < ... < lim[ncolors-1]=1
+ */
+ double lim[int];
+ /* HSV values for color i */
+ double h[int];
+ double s[int];
+ double v[int];
+
+ /* simple default */
+ ncolors = 2;
+ lim[0] = 0;
+ lim[1] = 1;
+ h[0] = 0;
+ h[1] = 0.67;
+ s[0] = 1;
+ s[1] = 1;
+ v[0] = 1;
+ v[1] = 1;
+}
+E{
+ sscanf ($.tail.pos, "%f,%f", &x0, &y0);
+ sscanf ($.head.pos, "%f,%f", &x1, &y1);
+
+ len = sqrt ((x0-x1)*(x0-x1) + (y0-y1)*(y0-y1));
+
+ lens[$] = len;
+ if (len > maxlen) maxlen = len;
+ if (len < minlen) minlen = len;
+}
+BEG_G {
+ if (!isAttr($,"E","color"))
+ setDflt($,"E","color","");
+}
+E{
+ if ($.color != "") return;
+ alpha = (lens[$]-minlen)/(maxlen-minlen);
+ for (i = 1; i < ncolors; i++) {
+ if (alpha < lim[i]) break;
+ }
+ alpha = (alpha - lim[i-1])/(lim[i] - lim[i-1]);
+ h0 = (1-alpha)*h[i-1] + alpha*h[i];
+ s0 = (1-alpha)*s[i-1] + alpha*s[i];
+ v0 = (1-alpha)*v[i-1] + alpha*v[i];
+ $.color = sprintf ("%.02f %.02f %.02f", h0, s0, v0);
+}
--- /dev/null
+/* delete all edges */
+BEGIN {
+ int map[edge_t];
+ edge_t e;
+}
+E {map[$]=1}
+END_G {
+ for (map[e]) delete ($,e);
+}
--- /dev/null
+/* Delete nodes whose names are given in ARGV */
+BEG_G {
+ int names[char*];
+ int nodes[node_t];
+ node_t n;
+ int i;
+
+ for (i = 0; i < ARGC; i++)
+ names[ARGV[i]] = 1;
+}
+N[names[name]]{nodes[$] = 1}
+END_G {
+ for (nodes[n])
+ delete ($,n);
+}
--- /dev/null
+/* Given graph processed by dijkstra, and node,
+ * color shortest path
+ * Assumes path has been computed by dijkstra
+ */
+BEG_G {
+ node_t n = isNode($,ARGV[0]);
+ node_t nxt;
+ edge_t e;
+ double d, totd = 0;
+
+ if (n == NULL) {
+ printf(2, "no node named \"%s\"\n", ARGV[0]);
+ exit(1);
+ }
+ while (n.prev != "") {
+ nxt = isNode($,n.prev);
+ /* printf(2, "nxt \"%s\"\n", nxt.name); */
+ e = isEdge (n, nxt, "");
+ if (e == NULL) {
+ printf(2, "no edge between %s and %s\n", n.name, nxt.name);
+ }
+ e.color = "blue";
+ /* printf(2, "len %s\n", e.len); */
+ /* sscanf (e.len, "%f", &d); */
+ /* totd += d; */
+ n = nxt;
+ }
+}
--- /dev/null
+/* Report the distance from src = ARGV[0] to dst = ARGV[1]
+ */
+BEG_G {
+ int dist[node_t];
+ node_t n, curn;
+ node_t src = node($G, ARGV[0]);
+ node_t dst = node($G, ARGV[1]);
+ $tvroot = src;
+ $tvtype = TV_bfs;
+}
+
+N {
+ curn = $;
+ if ($ == dst) {
+ printf ("dist from %s to %s is %d\n", src.name, dst.name, dist[dst]);
+ exit(0);
+ }
+}
+
+E {
+ if ($.head == curn) n = $.tail;
+ else n = $.head;
+ if (dist[n] == 0) dist[n] = dist[curn]+1;
+}
--- /dev/null
+/* finds node n with root attribute
+ * finds distance minr of closest node
+ * the layout is then scaled out from n so that
+ * a node is put on the smallest circle of radius x*minr
+ * containing n
+ */
+BEG_G {
+ node_t ctr;
+ int cx, cy;
+ int x, y;
+ double delx, dely;
+ int newx, newy;
+ node_t n;
+ edge_t e;
+ int i, sc, d, mind = -1;
+ double fact, newr, ang, minr;
+
+ ctr = node($,aget($,"root"));
+ sscanf (ctr.pos, "%d,%d", &cx, &cy);
+ for (e = fstedge(ctr); e; e = nxtedge(e, ctr)) {
+ if (e.head == ctr) n = e.tail;
+ else n = e.head;
+ sscanf (n.pos, "%d,%d", &x, &y);
+ d = (x-cx)*(x-cx) + (y-cy)*(y-cy);
+ if ((mind == -1) || (d < mind)) mind = d;
+ }
+ minr = (int)sqrt((double)mind);
+}
+
+N [$ != ctr] {
+
+ sscanf ($.pos, "%d,%d", &x, &y);
+ dely = y - cy;
+ delx = x - cx;
+ d = delx*delx + dely*dely;
+ sc = (int)sqrt((double)(d/mind));
+ if (sc > 1) {
+ fact = 2.0;
+ for (i=1; i<sc-1;i++) fact *= 2.0;
+ newr = minr*(2.0 - (1.0/fact));
+ ang = atan2 (dely, delx);
+ newx = newr*cos(ang) + cx;
+ newy = newr*sin(ang) + cy;
+ $.pos = sprintf ("%d,%d", newx, newy);
+ }
+}
--- /dev/null
+/* finds root node of graph.
+ * scales the x and y position of all other nodes using, first,
+ * ARGV[0], or $G.scale.
+ *
+ * The expected syntax is "x,y" where at least one of x or y must
+ * be given. If only one is given, the other is taken as 1.
+ */
+BEGIN {
+ double scalex, scaley;
+ int r, done;
+
+ int setScale (char* s)
+ {
+ if ((sscanf (s, ",%f",&scaley))) {
+ scalex = 1;
+ return 1;
+ }
+ else {
+ r = sscanf (s, "%f,%f",&scalex,&scaley);
+ if (r) {
+ if (r == 1) scaley = 1;
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+}
+
+BEG_G {
+ node_t ctr = node($,aget($,"root"));
+ double cx, cy, x, y, delx;
+
+ /* get scale argument */
+ done = 0;
+ if (ARGC == 1)
+ done = setScale (ARGV[0]);
+ if (!done && isAttr($,"G","scale"))
+ done = setScale ($.scale);
+ if (!done)
+ scalex = scaley = 1.0;
+
+ if ((scalex == 1.0) && (scaley == 1.0))
+ exit (0);
+
+ $.bb = "";
+ sscanf (ctr.pos, "%f,%f", &cx, &cy);
+}
+N [$ != ctr] {
+ sscanf ($.pos, "%f,%f", &x, &y);
+ delx = scalex*(x - cx) + cx;
+ dely = scaley*(y - cy) + cy;
+ $.pos = sprintf ("%f,%f", delx, dely);
+}
--- /dev/null
+/* Generate copy of topology of input graph
+ * Replace names with numbers
+ */
+BEGIN {
+int id = 0;
+char* names[char*];
+
+char* mapn (char* inname)
+{
+ char* s = names[inname];
+ if (s == "") {
+ s = id++;
+ names[inname] = s;
+ }
+ return s;
+}
+
+}
+
+BEG_G {
+ graph_t g = graph ($.name, "U");
+}
+N { node (g, mapn($.name)); }
+E { edge (node (g, mapn($.tail.name)), node (g, mapn($.head.name)), ""); }
+END_G {
+ write (g);
+}
--- /dev/null
+/* Convert a rooted tree to a hierarchy of clusters for patchwork.
+ * ARGV[0] is desired root
+ */
+BEG_G {
+ node_t rt;
+ node_t n;
+ graph_t cg;
+ graph_t sg;
+ int depth;
+ int mark[node_t];
+ graph_t stk[int];
+
+ if (! $.directed) {
+ printf(2,"Input graph is not directed\n");
+ exit (1);
+ }
+ rt = isNode($,ARGV[0]);
+ if (rt == NULL) {
+ printf(2,"Root node \"%s\" not found\n", ARGV[0]);
+ exit (1);
+ }
+ $tvroot = rt;
+ $tvtype = TV_prepostfwd;
+ cg = graph(rt.name,"U");
+}
+
+N {
+ if (mark[$]) {
+ depth--;
+ }
+ else {
+ mark[$] = 1;
+ if (depth > 0) {
+ if (fstout($)) {
+ sg = subg(stk[depth-1], "cluster_" + $.name);
+ if (($.style == "filled") && ($.fillcolor != ""))
+ sg.bgcolor = $.fillcolor;
+ }
+ else {
+ sg = NULL;
+ n = node(stk[depth-1], $.name);
+ n.style = "filled";
+ n.fillcolor = $.fillcolor;
+ }
+ }
+ else sg = cg;
+ stk[depth] = sg;
+ depth++;
+ }
+}
+END_G {
+ write(cg);
+}
+