/* This software may only be used by you under license from AT&T Corp. ("AT&T"). A copy of AT&T's Source Code Agreement is available at AT&T's Internet website having the URL: If you received this software without first entering into a license with AT&T, you have an infringing copy of this software and cannot use it without violating AT&T's intellectual property rights. */ #pragma prototyped #include "circle.h" #define DEF_RANKSEP 1.00 /* dfs to set distance from a particular leaf. * Note that termination is implicit in the test * for reduced number of steps. Proof? */ static void setNStepsToLeaf(Agraph_t* g, Agnode_t* n, Agnode_t* prev) { Agnode_t* next; Agedge_t* ep; int nsteps = SLEAF(n) + 1; for (ep = agfstedge(g,n); ep; ep = agnxtedge(g,ep,n)) { if ((next = ep->tail) == n) next = ep->head; if (prev == next) continue; if (nsteps < SLEAF(next)) { SLEAF(next) = nsteps; setNStepsToLeaf(g, next, n); } } } static int isLeaf (Agraph_t* g, Agnode_t* n) { Agedge_t* ep; int cnt = 0; for (ep = agfstedge(g,n); ep; ep = agnxtedge(g,ep,n)) { cnt++; if (cnt == 2) break; } return (cnt == 1); } static void initLayout (Agraph_t* g) { Agnode_t* n; int nnodes = agnnodes (g); int INF = nnodes*nnodes; for (n = agfstnode(g); n; n = agnxtnode (g,n)) { STSIZE(n) = 0; NCHILD(n) = 0; SCENTER(n) = INF; if (isLeaf(g,n)) SLEAF(n) = 0; else SLEAF(n) = INF; } } /* * Working recursively in from each leaf node (ie, each node * with nStepsToLeaf == 0; see initLayout), set the * minimum value of nStepsToLeaf for each node. Using * that information, assign some node to be the centerNode. */ static Agnode_t* findCenterNode(Agraph_t* g) { Agnode_t* n; Agnode_t* center=NULL; int maxNStepsToLeaf = 0; /* With just 1 or 2 nodes, return anything. */ if (agnnodes(g) <= 2) return (agfstnode(g)); /* dfs from each leaf node */ for (n = agfstnode(g); n; n = agnxtnode(g,n)) { if (SLEAF(n) == 0) setNStepsToLeaf(g, n, 0); } for (n = agfstnode(g); n; n = agnxtnode(g,n)) { if (SLEAF(n) > maxNStepsToLeaf) { maxNStepsToLeaf = SLEAF(n); center = n; } } return center; } /* dfs to set distance from center * Note that termination is implicit in the test * for reduced number of steps. Proof? */ static void setNStepsToCenter(Agraph_t* g, Agnode_t* n, Agnode_t* prev) { Agnode_t* next; Agedge_t* ep; int nsteps = SCENTER(n) + 1; for (ep = agfstedge(g,n); ep; ep = agnxtedge(g,ep,n)) { if ((next = ep->tail) == n) next = ep->head; if (prev == next) continue; if (nsteps < SCENTER(next)) { SCENTER(next) = nsteps; if (PARENT(next)) NCHILD(PARENT(next))--; PARENT(next) = n; NCHILD(n)++; setNStepsToCenter(g, next, n); } } } /* * Work out from the center and determine the value of * nStepsToCenter and parent node for each node. */ static int setParentNodes(Agraph_t* sg, Agnode_t* center) { Agnode_t* n; int maxn = 0; SCENTER(center) = 0; PARENT(center) = 0; setNStepsToCenter(sg, center, 0); /* find the maximum number of steps from the center */ for (n = agfstnode(sg); n; n = agnxtnode(sg,n)) { if (SCENTER(n) > maxn) { maxn = SCENTER(n); } } return maxn; } /* Sets each node's subtreeSize, which counts the number of * leaves in subtree rooted at the node. * At present, this is done bottom-up. */ static void setSubtreeSize(Agraph_t* g, Agnode_t* center) { Agnode_t* n; Agnode_t* parent; for (n = agfstnode(g); n; n = agnxtnode (g,n)) { if (NCHILD(n) > 0) continue; STSIZE(n)++; parent = PARENT(n); while (parent) { STSIZE(parent)++; parent = PARENT(parent); } } } static void setChildSubtreeSpans(Agraph_t* g, Agnode_t* n) { Agedge_t* ep; Agnode_t* next; double ratio; ratio = SPAN(n) / STSIZE(n); for (ep = agfstedge(g,n); ep; ep = agnxtedge(g,ep,n)) { if ((next = ep->tail) == n) next = ep->head; if (PARENT(next) != n) continue; SPAN(next) = ratio * STSIZE(next); if (NCHILD(next) > 0) { setChildSubtreeSpans(g, next); } } } static void setSubtreeSpans(Agraph_t* sg, Agnode_t* center) { SPAN(center) = 2*PI; setChildSubtreeSpans(sg, center); } /* Set the node positions for the 2nd and later rings. */ static void setChildPositions(Agraph_t* sg, Agnode_t* n) { Agnode_t* next; Agedge_t* ep; double theta; /* theta is the lower boundary radius of the fan */ if (PARENT(n) == 0) /* center */ theta = 0; else theta = THETA(n) - SPAN(n)/2; for (ep = agfstedge(sg,n); ep; ep = agnxtedge(sg,ep,n)) { if ((next = ep->tail) == n) next = ep->head; if (PARENT(next) != n) continue; THETA(next) = theta + SPAN(next)/2.0 ; theta += SPAN(next); if (NCHILD(next) > 0) setChildPositions(sg, next); } } static void setPositions(Agraph_t* sg, Agnode_t* center) { THETA(center) = 0; setChildPositions(sg, center); } static void setAbsolutePos(Agraph_t* g) { char* p; Agnode_t* n; double xf; double hyp; p = late_string(g,agfindattr(g->root,"ranksep"),NULL); if (p) { if (sscanf(p,"%lf",&xf) == 0) xf = DEF_RANKSEP; else {if (xf < MIN_RANKSEP) xf = MIN_RANKSEP;} } else xf = DEF_RANKSEP; if (Verbose) fprintf (stderr, "Rank separation = %f\n", xf); /* Convert circular to cartesian coordinates */ for (n = agfstnode(g); n; n = agnxtnode (g,n)) { hyp = xf*(SCENTER(n)); ND_pos(n)[0] = hyp*cos(THETA(n)); ND_pos(n)[1] = hyp*sin(THETA(n)); } } #if 0 /* not used */ static void dumpGraph (Agraph_t* g) { Agnode_t* n; char* p; fprintf (stderr, " : leaf stsz nkids cntr parent span theta\n"); for (n = agfstnode(g); n; n = agnxtnode (g,n)) { if (PARENT(n)) p = PARENT(n)->name; else p = ""; fprintf (stderr, "%4s :%6d%6d%6d%6d%7s%7.3f%7.3f%8.3f%8.3f\n", n->name, SLEAF(n), STSIZE(n), NCHILD(n), SCENTER(n), p, SPAN(n), THETA(n), ND_pos(n)[0], ND_pos(n)[1]); } } #endif /* circleLayout: * We assume sg is is connected and non-empty. * Also, if center != 0, we are guaranteed that center is * in the graph. */ void circleLayout(Agraph_t* sg, Agnode_t* center) { /* int maxNStepsToCenter; */ if (agnnodes(sg) == 1) { Agnode_t* n = agfstnode(sg); ND_pos(n)[0] = 0; ND_pos(n)[1] = 0; return; } initLayout(sg); if (!center) center = findCenterNode(sg); if (Verbose) fprintf (stderr, "Center = %s\n", center->name); /* maxNStepsToCenter = setParentNodes(sg,center); */ setParentNodes(sg,center); setSubtreeSize(sg, center); setSubtreeSpans(sg, center); setPositions(sg, center); setAbsolutePos (sg); /* dumpGraph (sg); */ }