|
Graphviz
2.29.20120523.0446
|
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 }
1.7.5