Graphviz  2.29.20120523.0446
lib/twopigen/circle.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 
00015 #include    "circle.h"
00016 #include    <ctype.h>
00017 #include    <stdlib.h>
00018 #define DEF_RANKSEP 1.00
00019 #define UNSET 10.00
00020 
00021 /* dfs to set distance from a particular leaf.
00022  * Note that termination is implicit in the test
00023  * for reduced number of steps. Proof?
00024  */
00025 static void setNStepsToLeaf(Agraph_t * g, Agnode_t * n, Agnode_t * prev)
00026 {
00027     Agnode_t *next;
00028     Agedge_t *ep;
00029 
00030     int nsteps = SLEAF(n) + 1;
00031 
00032     for (ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
00033         if ((next = agtail(ep)) == n)
00034             next = aghead(ep);
00035 
00036         if (prev == next)
00037             continue;
00038 
00039         if (nsteps < SLEAF(next)) {     /* handles loops and multiedges */
00040             SLEAF(next) = nsteps;
00041             setNStepsToLeaf(g, next, n);
00042         }
00043     }
00044 }
00045 
00046 /* isLeaf:
00047  * Return true if n is a leaf node.
00048  */
00049 static int isLeaf(Agraph_t * g, Agnode_t * n)
00050 {
00051     Agedge_t *ep;
00052     Agnode_t *neighp = 0;
00053     Agnode_t *np;
00054 
00055     for (ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
00056         if ((np = agtail(ep)) == n)
00057             np = aghead(ep);
00058         if (n == np)
00059             continue;           /* loop */
00060         if (neighp) {
00061             if (neighp != np)
00062                 return 0;       /* two different neighbors */
00063         } else
00064             neighp = np;
00065     }
00066     return 1;
00067 }
00068 
00069 static void initLayout(Agraph_t * g)
00070 {
00071     Agnode_t *n;
00072     int nnodes = agnnodes(g);
00073     int INF = nnodes * nnodes;
00074 
00075     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00076         /* STSIZE(n) = 0; */
00077         /* NCHILD(n) = 0; */
00078         SCENTER(n) = INF;
00079         THETA(n) = UNSET;       /* marks theta as unset, since 0 <= theta <= 2PI */
00080         if (isLeaf(g, n))
00081             SLEAF(n) = 0;
00082         else
00083             SLEAF(n) = INF;
00084     }
00085 }
00086 
00087 /*
00088  * Working recursively in from each leaf node (ie, each node
00089  * with nStepsToLeaf == 0; see initLayout), set the
00090  * minimum value of nStepsToLeaf for each node.  Using
00091  * that information, assign some node to be the centerNode.
00092 */
00093 static Agnode_t *findCenterNode(Agraph_t * g)
00094 {
00095     Agnode_t *n;
00096     Agnode_t *center = NULL;
00097     int maxNStepsToLeaf = 0;
00098 
00099     /* With just 1 or 2 nodes, return anything. */
00100     if (agnnodes(g) <= 2)
00101         return (agfstnode(g));
00102 
00103     /* dfs from each leaf node */
00104     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00105         if (SLEAF(n) == 0)
00106             setNStepsToLeaf(g, n, 0);
00107     }
00108 
00109     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00110         if (SLEAF(n) > maxNStepsToLeaf) {
00111             maxNStepsToLeaf = SLEAF(n);
00112             center = n;
00113         }
00114     }
00115     return center;
00116 }
00117 
00118 /* dfs to set distance from center
00119  * Note that termination is implicit in the test
00120  * for reduced number of steps. Proof?
00121  */
00122 static void setNStepsToCenter(Agraph_t * g, Agnode_t * n, Agnode_t * prev)
00123 {
00124     Agnode_t *next;
00125     Agedge_t *ep;
00126     int nsteps = SCENTER(n) + 1;
00127 
00128     for (ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
00129         if ((next = agtail(ep)) == n)
00130             next = aghead(ep);
00131 
00132         if (prev == next)
00133             continue;
00134 
00135         if (nsteps < SCENTER(next)) {   /* handles loops and multiedges */
00136             SCENTER(next) = nsteps;
00137             if (SPARENT(next))
00138                 NCHILD(SPARENT(next))--;
00139             SPARENT(next) = n;
00140             NCHILD(n)++;
00141             setNStepsToCenter(g, next, n);
00142         }
00143     }
00144 }
00145 
00146 
00147 /*
00148  * Work out from the center and determine the value of
00149  * nStepsToCenter and parent node for each node.
00150  */
00151 static int setParentNodes(Agraph_t * sg, Agnode_t * center)
00152 {
00153     Agnode_t *n;
00154     int maxn = 0;
00155 
00156     SCENTER(center) = 0;
00157     SPARENT(center) = 0;
00158     setNStepsToCenter(sg, center, 0);
00159 
00160     /* find the maximum number of steps from the center */
00161     for (n = agfstnode(sg); n; n = agnxtnode(sg, n)) {
00162         if (SCENTER(n) > maxn) {
00163             maxn = SCENTER(n);
00164         }
00165     }
00166     return maxn;
00167 }
00168 
00169 /* Sets each node's subtreeSize, which counts the number of 
00170  * leaves in subtree rooted at the node.
00171  * At present, this is done bottom-up.
00172  */
00173 static void setSubtreeSize(Agraph_t * g)
00174 {
00175     Agnode_t *n;
00176     Agnode_t *parent;
00177 
00178     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00179         if (NCHILD(n) > 0)
00180             continue;
00181         STSIZE(n)++;
00182         parent = SPARENT(n);
00183         while (parent) {
00184             STSIZE(parent)++;
00185             parent = SPARENT(parent);
00186         }
00187     }
00188 }
00189 
00190 static void setChildSubtreeSpans(Agraph_t * g, Agnode_t * n)
00191 {
00192     Agedge_t *ep;
00193     Agnode_t *next;
00194     double ratio;
00195 
00196     ratio = SPAN(n) / STSIZE(n);
00197     for (ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
00198         if ((next = agtail(ep)) == n)
00199             next = aghead(ep);
00200         if (SPARENT(next) != n)
00201             continue;           /* handles loops */
00202 
00203         if (SPAN(next) != 0.0)
00204             continue;           /* multiedges */
00205         (SPAN(next) = ratio * STSIZE(next));
00206 
00207         if (NCHILD(next) > 0) {
00208             setChildSubtreeSpans(g, next);
00209         }
00210     }
00211 }
00212 
00213 static void setSubtreeSpans(Agraph_t * sg, Agnode_t * center)
00214 {
00215     SPAN(center) = 2 * M_PI;
00216     setChildSubtreeSpans(sg, center);
00217 }
00218 
00219  /* Set the node positions for the 2nd and later rings. */
00220 static void setChildPositions(Agraph_t * sg, Agnode_t * n)
00221 {
00222     Agnode_t *next;
00223     Agedge_t *ep;
00224     double theta;               /* theta is the lower boundary radius of the fan */
00225 
00226     if (SPARENT(n) == 0)        /* center */
00227         theta = 0;
00228     else
00229         theta = THETA(n) - SPAN(n) / 2;
00230 
00231     for (ep = agfstedge(sg, n); ep; ep = agnxtedge(sg, ep, n)) {
00232         if ((next = agtail(ep)) == n)
00233             next = aghead(ep);
00234         if (SPARENT(next) != n)
00235             continue;           /* handles loops */
00236         if (THETA(next) != UNSET)
00237             continue;           /* handles multiedges */
00238 
00239         THETA(next) = theta + SPAN(next) / 2.0;
00240         theta += SPAN(next);
00241 
00242         if (NCHILD(next) > 0)
00243             setChildPositions(sg, next);
00244     }
00245 }
00246 
00247 static void setPositions(Agraph_t * sg, Agnode_t * center)
00248 {
00249     THETA(center) = 0;
00250     setChildPositions(sg, center);
00251 }
00252 
00253 /* getRankseps:
00254  * Return array of doubles of size maxrank+1 containing the radius of each
00255  * rank.  Position 0 always contains 0. Use the colon-separated list of 
00256  * doubles provided by ranksep to get the deltas for each additional rank.
00257  * If not enough values are provided, the last value is repeated.
00258  * If the ranksep attribute is not provided, use DEF_RANKSEP for all values. 
00259  */ 
00260 static double*
00261 getRankseps (Agraph_t* g, int maxrank)
00262 {
00263     char *p;
00264     char *endp;
00265     char c;
00266     int i, rk = 1;
00267     double* ranks = N_NEW(maxrank+1, double);
00268     double xf = 0, delx, d;
00269 
00270     if ((p = late_string(g, agfindgraphattr(g->root, "ranksep"), NULL))) {
00271         while ((rk <= maxrank) && ((d = strtod (p, &endp)) > 0)) {
00272             delx = MAX(d, MIN_RANKSEP);
00273             xf += delx;
00274             ranks[rk++] = xf;
00275             p = endp;
00276             while ((c = *p) && (isspace(c) || (c == ':')))
00277                 p++;
00278         }
00279     }
00280     else {
00281         delx = DEF_RANKSEP;
00282     }
00283 
00284     for (i = rk; i <= maxrank; i++) {
00285         xf += delx;
00286         ranks[i] = xf;
00287     }
00288 
00289     return ranks;
00290 }
00291 
00292 static void setAbsolutePos(Agraph_t * g, int maxrank)
00293 {
00294     Agnode_t *n;
00295     double hyp;
00296     double* ranksep;
00297     int i;
00298 
00299     ranksep = getRankseps (g, maxrank);
00300     if (Verbose) {
00301         fputs ("Rank separation = ", stderr);
00302         for (i = 0; i <= maxrank; i++)
00303             fprintf (stderr, "%.03lf ", ranksep[i]);
00304         fputs ("\n", stderr);
00305     }
00306 
00307     /* Convert circular to cartesian coordinates */
00308     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00309         hyp = ranksep[SCENTER(n)];
00310         ND_pos(n)[0] = hyp * cos(THETA(n));
00311         ND_pos(n)[1] = hyp * sin(THETA(n));
00312     }
00313     free (ranksep);
00314 }
00315 
00316 #if 0                           /* not used */
00317 static void dumpGraph(Agraph_t * g)
00318 {
00319     Agnode_t *n;
00320     char *p;
00321 
00322     fprintf(stderr,
00323             "     :  leaf  stsz nkids  cntr parent   span  theta\n");
00324     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00325         if (SPARENT(n))
00326             p = SPARENT(n)->name;
00327         else
00328             p = "<C>";
00329         fprintf(stderr, "%4s :%6d%6d%6d%6d%7s%7.3f%7.3f%8.3f%8.3f\n",
00330                 n->name, SLEAF(n), STSIZE(n), NCHILD(n),
00331                 SCENTER(n), p, SPAN(n), THETA(n), ND_pos(n)[0],
00332                 ND_pos(n)[1]);
00333     }
00334 }
00335 #endif
00336 
00337 /* circleLayout:
00338  *  We assume sg is is connected and non-empty.
00339  *  Also, if center != 0, we are guaranteed that center is
00340  *  in the graph.
00341  */
00342 Agnode_t* circleLayout(Agraph_t * sg, Agnode_t * center)
00343 {
00344     int maxNStepsToCenter;
00345 
00346     if (agnnodes(sg) == 1) {
00347         Agnode_t *n = agfstnode(sg);
00348         ND_pos(n)[0] = 0;
00349         ND_pos(n)[1] = 0;
00350         return center;
00351     }
00352 
00353     initLayout(sg);
00354 
00355     if (!center)
00356         center = findCenterNode(sg);
00357     if (Verbose)
00358         fprintf(stderr, "root = %s\n", agnameof(center));
00359 
00360     maxNStepsToCenter = setParentNodes(sg,center);
00361 
00362     setSubtreeSize(sg);
00363 
00364     setSubtreeSpans(sg, center);
00365 
00366     setPositions(sg, center);
00367 
00368     setAbsolutePos(sg, maxNStepsToCenter);
00369     /* dumpGraph (sg); */
00370     return center;
00371 }