|
Graphviz
2.31.20130521.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 /* xlayout.c: 00016 * Written by Emden R. Gansner 00017 * 00018 * Layout routine to expand initial layout to accommodate node 00019 * sizes. 00020 */ 00021 00022 #ifdef FIX 00023 Allow sep to be absolute additive (margin of n points) 00024 Increase less between tries 00025 #endif 00026 00027 /* uses PRIVATE interface */ 00028 #define FDP_PRIVATE 1 00029 00030 #include <xlayout.h> 00031 #include <adjust.h> 00032 #include <dbg.h> 00033 #include <ctype.h> 00034 00035 /* Use bbox based force function */ 00036 /* #define MS */ 00037 /* Use alternate force function */ 00038 /* #define ALT */ 00039 /* Add repulsive force even if nodes don't overlap */ 00040 /* #define ORIG */ 00041 #define BOX /* Use bbox to determine overlap, else use circles */ 00042 00043 #define DFLT_overlap "9:prism" /* default overlap value */ 00044 00045 #define WD2(n) (X_marg.doAdd ? (ND_width(n)/2.0 + X_marg.x): ND_width(n)*X_marg.x/2.0) 00046 #define HT2(n) (X_marg.doAdd ? (ND_height(n)/2.0 + X_marg.y): ND_height(n)*X_marg.y/2.0) 00047 00048 static xparams xParams = { 00049 60, /* numIters */ 00050 0.0, /* T0 */ 00051 0.3, /* K */ 00052 1.5, /* C */ 00053 0 /* loopcnt */ 00054 }; 00055 static double K2; 00056 static expand_t X_marg; 00057 static double X_nonov; 00058 static double X_ov; 00059 00060 void pr2graphs(Agraph_t *g0, Agraph_t *g1) 00061 { 00062 fprintf(stderr,"%s",agnameof(g0)); 00063 fprintf(stderr,"(%s)",agnameof(g1)); 00064 } 00065 00066 static double RAD(Agnode_t * n) 00067 { 00068 double w = WD2(n); 00069 double h = HT2(n); 00070 return sqrt(w * w + h * h); 00071 } 00072 00073 /* xinit_params: 00074 * Initialize local parameters 00075 */ 00076 static void xinit_params(graph_t* g, int n, xparams * xpms) 00077 { 00078 xParams.K = xpms->K; 00079 xParams.numIters = xpms->numIters; 00080 xParams.T0 = xpms->T0; 00081 xParams.loopcnt = xpms->loopcnt; 00082 if (xpms->C > 0.0) 00083 xParams.C = xpms->C; 00084 K2 = xParams.K * xParams.K; 00085 if (xParams.T0 == 0.0) 00086 xParams.T0 = xParams.K * sqrt(n) / 5; 00087 #ifdef DEBUG 00088 if (Verbose) { 00089 prIndent(); 00090 fprintf(stderr, "xLayout "); 00091 pr2graphs(g,GORIG(agroot(g))); 00092 fprintf(stderr, " : n = %d K = %f T0 = %f loop %d C %f\n", 00093 xParams.numIters, xParams.K, xParams.T0, xParams.loopcnt, 00094 xParams.C); 00095 } 00096 #endif 00097 } 00098 00099 #define X_T0 xParams.T0 00100 #define X_K xParams.K 00101 #define X_numIters xParams.numIters 00102 #define X_loopcnt xParams.loopcnt 00103 #define X_C xParams.C 00104 00105 00106 static double cool(int t) 00107 { 00108 return (X_T0 * (X_numIters - t)) / X_numIters; 00109 } 00110 00111 #define EPSILON 0.01 00112 00113 #ifdef MS 00114 /* dist: 00115 * Distance between two points 00116 */ 00117 static double dist(pointf p, pointf q) 00118 { 00119 double dx, dy; 00120 00121 dx = p.x - q.x; 00122 dy = p.y - q.y; 00123 return (sqrt(dx * dx + dy * dy)); 00124 } 00125 00126 /* bBox: 00127 * Compute bounding box of point 00128 */ 00129 static void bBox(node_t * p, pointf * ll, pointf * ur) 00130 { 00131 double w2 = WD2(p); 00132 double h2 = HT2(p); 00133 00134 ur->x = ND_pos(p)[0] + w2; 00135 ur->y = ND_pos(p)[1] + h2; 00136 ll->x = ND_pos(p)[0] - w2; 00137 ll->y = ND_pos(p)[1] - h2; 00138 } 00139 00140 /* boxDist: 00141 * Return the distance between two boxes; 0 if they overlap 00142 */ 00143 static double boxDist(node_t * p, node_t * q) 00144 { 00145 pointf p_ll, p_ur; 00146 pointf q_ll, q_ur; 00147 00148 bBox(p, &p_ll, &p_ur); 00149 bBox(q, &q_ll, &q_ur); 00150 00151 if (q_ll.x > p_ur.x) { 00152 if (q_ll.y > p_ur.y) { 00153 return (dist(p_ur, q_ll)); 00154 } else if (q_ll.y >= p_ll.y) { 00155 return (q_ll.x - p_ur.x); 00156 } else { 00157 if (q_ur.y >= p_ll.y) 00158 return (q_ll.x - p_ur.x); 00159 else { 00160 p_ur.y = p_ll.y; /* p_ur is now lower right */ 00161 q_ll.y = q_ur.y; /* q_ll is now upper left */ 00162 return (dist(p_ur, q_ll)); 00163 } 00164 } 00165 } else if (q_ll.x >= p_ll.x) { 00166 if (q_ll.y > p_ur.y) { 00167 return (q_ll.y - p_ur.x); 00168 } else if (q_ll.y >= p_ll.y) { 00169 return 0.0; 00170 } else { 00171 if (q_ur.y >= p_ll.y) 00172 return 0.0; 00173 else 00174 return (p_ll.y - q_ur.y); 00175 } 00176 } else { 00177 if (q_ll.y > p_ur.y) { 00178 if (q_ur.x >= p_ll.x) 00179 return (q_ll.y - p_ur.y); 00180 else { 00181 p_ur.x = p_ll.x; /* p_ur is now upper left */ 00182 q_ll.x = q_ur.x; /* q_ll is now lower right */ 00183 return (dist(p_ur, q_ll)); 00184 } 00185 } else if (q_ll.y >= p_ll.y) { 00186 if (q_ur.x >= p_ll.x) 00187 return 0.0; 00188 else 00189 return (p_ll.x - q_ur.x); 00190 } else { 00191 if (q_ur.x >= p_ll.x) { 00192 if (q_ur.y >= p_ll.y) 00193 return 0.0; 00194 else 00195 return (p_ll.y - q_ur.y); 00196 } else { 00197 if (q_ur.y >= p_ll.y) 00198 return (p_ll.x - q_ur.x); 00199 else 00200 return (dist(p_ll, q_ur)); 00201 } 00202 } 00203 } 00204 } 00205 #endif /* MS */ 00206 00207 /* overlap: 00208 * Return true if nodes overlap 00209 */ 00210 static int overlap(node_t * p, node_t * q) 00211 { 00212 #if defined(BOX) 00213 double xdelta, ydelta; 00214 int ret; 00215 00216 xdelta = ND_pos(q)[0] - ND_pos(p)[0]; 00217 if (xdelta < 0) 00218 xdelta = -xdelta; 00219 ydelta = ND_pos(q)[1] - ND_pos(p)[1]; 00220 if (ydelta < 0) 00221 ydelta = -ydelta; 00222 ret = ((xdelta <= (WD2(p) + WD2(q))) && (ydelta <= (HT2(p) + HT2(q)))); 00223 return ret; 00224 #else 00225 double dist2, xdelta, ydelta; 00226 double din; 00227 00228 din = RAD(p) + RAD(q); 00229 xdelta = ND_pos(q)[0] - ND_pos(p)[0]; 00230 ydelta = ND_pos(q)[1] - ND_pos(p)[1]; 00231 dist2 = xdelta * xdelta + ydelta * ydelta; 00232 return (dist2 <= (din * din)); 00233 #endif 00234 } 00235 00236 /* cntOverlaps: 00237 * Return number of overlaps. 00238 */ 00239 static int cntOverlaps(graph_t * g) 00240 { 00241 node_t *p; 00242 node_t *q; 00243 int cnt = 0; 00244 00245 for (p = agfstnode(g); p; p = agnxtnode(g, p)) { 00246 for (q = agnxtnode(g, p); q; q = agnxtnode(g, q)) { 00247 cnt += overlap(p, q); 00248 } 00249 } 00250 return cnt; 00251 } 00252 00253 /* doRep: 00254 * Return 1 if nodes overlap 00255 */ 00256 static int 00257 doRep(node_t * p, node_t * q, double xdelta, double ydelta, double dist2) 00258 { 00259 int ov; 00260 double force; 00261 /* double dout, din; */ 00262 #if defined(DEBUG) || defined(MS) || defined(ALT) 00263 double dist; 00264 #endif 00265 /* double factor; */ 00266 00267 while (dist2 == 0.0) { 00268 xdelta = 5 - rand() % 10; 00269 ydelta = 5 - rand() % 10; 00270 dist2 = xdelta * xdelta + ydelta * ydelta; 00271 } 00272 #if defined(MS) 00273 dout = boxDist(p, q); 00274 if (dout < EPSILON) 00275 dout = EPSILON; 00276 dist = sqrt(dist2); 00277 force = K2 / (dout * dist); 00278 #elif defined(ALT) 00279 force = K2 / dist2; 00280 dist = sqrt(dist2); 00281 din = RAD(p) + RAD(q); 00282 if (ov = overlap(p, q)) { 00283 factor = X_C; 00284 } else { 00285 ov = 0; 00286 if (dist <= din + X_K) 00287 factor = X_C * (X_K - (dist - din)) / X_K; 00288 else 00289 factor = 0.0; 00290 } 00291 force *= factor; 00292 #elif defined(ORIG) 00293 force = K2 / dist2; 00294 if ((ov = overlap(p, q))) 00295 force *= X_C; 00296 #else 00297 if ((ov = overlap(p, q))) 00298 force = X_ov / dist2; 00299 else 00300 force = X_nonov / dist2; 00301 #endif 00302 #ifdef DEBUG 00303 if (Verbose == 4) { 00304 prIndent(); 00305 dist = sqrt(dist2); 00306 fprintf(stderr, " ov Fr %f dist %f\n", force * dist, dist); 00307 } 00308 #endif 00309 DISP(q)[0] += xdelta * force; 00310 DISP(q)[1] += ydelta * force; 00311 DISP(p)[0] -= xdelta * force; 00312 DISP(p)[1] -= ydelta * force; 00313 return ov; 00314 } 00315 00316 /* applyRep: 00317 * Repulsive force = (K*K)/d 00318 * Return 1 if nodes overlap 00319 */ 00320 static int applyRep(Agnode_t * p, Agnode_t * q) 00321 { 00322 double xdelta, ydelta; 00323 00324 xdelta = ND_pos(q)[0] - ND_pos(p)[0]; 00325 ydelta = ND_pos(q)[1] - ND_pos(p)[1]; 00326 return doRep(p, q, xdelta, ydelta, xdelta * xdelta + ydelta * ydelta); 00327 } 00328 00329 static void applyAttr(Agnode_t * p, Agnode_t * q) 00330 { 00331 double xdelta, ydelta; 00332 double force; 00333 double dist; 00334 double dout; 00335 double din; 00336 00337 #if defined(MS) 00338 dout = boxDist(p, q); 00339 if (dout == 0.0) 00340 return; 00341 xdelta = ND_pos(q)[0] - ND_pos(p)[0]; 00342 ydelta = ND_pos(q)[1] - ND_pos(p)[1]; 00343 dist = sqrt(xdelta * xdelta + ydelta * ydelta); 00344 force = (dout * dout) / (X_K * dist); 00345 #elif defined(ALT) 00346 xdelta = ND_pos(q)[0] - ND_pos(p)[0]; 00347 ydelta = ND_pos(q)[1] - ND_pos(p)[1]; 00348 dist = sqrt(xdelta * xdelta + ydelta * ydelta); 00349 din = RAD(p) + RAD(q); 00350 if (dist < X_K + din) 00351 return; 00352 dout = dist - din; 00353 force = (dout * dout) / ((X_K + din) * dist); 00354 #else 00355 if (overlap(p, q)) { 00356 #ifdef DEBUG 00357 if (Verbose == 4) { 00358 prIndent(); 00359 fprintf(stderr, "ov 1 Fa 0 din %f\n", RAD(p) + RAD(q)); 00360 } 00361 #endif 00362 return; 00363 } 00364 xdelta = ND_pos(q)[0] - ND_pos(p)[0]; 00365 ydelta = ND_pos(q)[1] - ND_pos(p)[1]; 00366 dist = sqrt(xdelta * xdelta + ydelta * ydelta); 00367 din = RAD(p) + RAD(q); 00368 dout = dist - din; 00369 force = (dout * dout) / ((X_K + din) * dist); 00370 #endif 00371 #ifdef DEBUG 00372 if (Verbose == 4) { 00373 prIndent(); 00374 fprintf(stderr, " ov 0 Fa %f din %f \n", force * dist, din); 00375 } 00376 #endif 00377 DISP(q)[0] -= xdelta * force; 00378 DISP(q)[1] -= ydelta * force; 00379 DISP(p)[0] += xdelta * force; 00380 DISP(p)[1] += ydelta * force; 00381 } 00382 00383 /* adjust: 00384 * Return 0 if definitely no overlaps. 00385 * Return non-zero if we had overlaps before most recent move. 00386 */ 00387 static int adjust(Agraph_t * g, double temp) 00388 { 00389 Agnode_t *n; 00390 Agnode_t *n1; 00391 Agedge_t *e; 00392 double temp2; 00393 double len; 00394 double len2; 00395 double disp[NDIM]; /* incremental displacement */ 00396 int overlaps = 0; 00397 00398 #ifdef DEBUG 00399 if (Verbose == 4) 00400 fprintf(stderr, "=================\n"); 00401 #endif 00402 00403 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00404 DISP(n)[0] = DISP(n)[1] = 0; 00405 } 00406 00407 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00408 int ov; 00409 for (n1 = agnxtnode(g, n); n1; n1 = agnxtnode(g, n1)) { 00410 ov = applyRep(n, n1); 00411 /* if (V && ov) */ 00412 /* fprintf (stderr,"%s ov %s\n", n->name, n1->name); */ 00413 overlaps += ov; 00414 } 00415 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00416 applyAttr(n,aghead(e)); 00417 } 00418 } 00419 if (overlaps == 0) 00420 return 0; 00421 00422 temp2 = temp * temp; 00423 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00424 if (ND_pinned(n) == P_PIN) 00425 continue; 00426 disp[0] = DISP(n)[0]; 00427 disp[1] = DISP(n)[1]; 00428 len2 = disp[0] * disp[0] + disp[1] * disp[1]; 00429 00430 if (len2 < temp2) { 00431 ND_pos(n)[0] += disp[0]; 00432 ND_pos(n)[1] += disp[1]; 00433 } else { 00434 /* to avoid sqrt, consider abs(x) + abs(y) */ 00435 len = sqrt(len2); 00436 ND_pos(n)[0] += (disp[0] * temp) / len; 00437 ND_pos(n)[1] += (disp[1] * temp) / len; 00438 } 00439 } 00440 return overlaps; 00441 } 00442 00443 /* x_layout: 00444 * Given graph g with initial layout, adjust g so that nodes 00445 * do not overlap. 00446 * Assume g is connected. 00447 * g may have ports. At present, we do not use ports in the layout 00448 * at this stage. 00449 * Returns non-zero if overlaps still exist. 00450 * TODO (possible): 00451 * Allow X_T0 independent of T_TO or percentage of, so the cooling would 00452 * be piecewise linear. This would allow longer, cooler expansion. 00453 * In tries > 1, increase X_T0 and/or lengthen cooling 00454 */ 00455 static int x_layout(graph_t * g, xparams * pxpms, int tries) 00456 { 00457 int i; 00458 int try; 00459 int ov; 00460 double temp; 00461 int nnodes = agnnodes(g); 00462 int nedges = agnedges(g); 00463 double K; 00464 xparams xpms; 00465 00466 X_marg = sepFactor (g); 00467 if (X_marg.doAdd) { 00468 X_marg.x = PS2INCH(X_marg.x); /* sepFactor is in points */ 00469 X_marg.y = PS2INCH(X_marg.y); 00470 } 00471 ov = cntOverlaps(g); 00472 if (ov == 0) 00473 return 0; 00474 00475 try = 0; 00476 xpms = *pxpms; 00477 K = xpms.K; 00478 while (ov && (try < tries)) { 00479 xinit_params(g, nnodes, &xpms); 00480 X_ov = X_C * K2; 00481 X_nonov = (nedges*X_ov*2.0)/(nnodes*(nnodes-1)); 00482 #ifdef DEBUG 00483 if (Verbose) { 00484 prIndent(); 00485 fprintf(stderr, "try %d (%d): %d overlaps on ", try, tries, ov); 00486 pr2graphs(g,GORIG(agroot(g))); 00487 fprintf(stderr," \n"); 00488 } 00489 #endif 00490 00491 for (i = 0; i < X_loopcnt; i++) { 00492 temp = cool(i); 00493 if (temp <= 0.0) 00494 break; 00495 ov = adjust(g, temp); 00496 if (ov == 0) 00497 break; 00498 } 00499 try++; 00500 xpms.K += K; /* increase distance */ 00501 } 00502 #ifdef DEBUG 00503 if (Verbose && ov) 00504 fprintf(stderr, "Warning: %d overlaps remain on ", ov); 00505 pr2graphs(g,GORIG(agroot(g))); 00506 fprintf(stderr,"\n"); 00507 #endif 00508 00509 return ov; 00510 } 00511 00512 /* fdp_xLayout: 00513 * Use overlap parameter to determine if and how to remove overlaps. 00514 * In addition to the usual values accepted by removeOverlap, overlap 00515 * can begin with "n:" to indicate the given number of tries using 00516 * x_layout to remove overlaps. 00517 * Thus, 00518 * NULL or "" => dflt overlap 00519 * "mode" => "0:mode", i.e. removeOverlap with mode only 00520 * "true" => "0:true", i.e., no overlap removal 00521 * "n:" => n tries only 00522 * "n:mode" => n tries, then removeOverlap with mode 00523 * "0:" => no overlap removal 00524 */ 00525 void fdp_xLayout(graph_t * g, xparams * xpms) 00526 { 00527 int tries; 00528 char* ovlp = agget (g, "overlap"); 00529 char* cp; 00530 char* rest; 00531 00532 if (Verbose) { 00533 #ifdef DEBUG 00534 prIndent(); 00535 #endif 00536 fprintf (stderr, "xLayout "); 00537 } 00538 if (!ovlp || (*ovlp == '\0')) { 00539 ovlp = DFLT_overlap; 00540 } 00541 /* look for optional ":" or "number:" */ 00542 if ((cp = strchr(ovlp, ':')) && ((cp == ovlp) || isdigit(*ovlp))) { 00543 cp++; 00544 rest = cp; 00545 tries = atoi (ovlp); 00546 if (tries < 0) tries = 0; 00547 } 00548 else { 00549 tries = 0; 00550 rest = ovlp; 00551 } 00552 if (Verbose) { 00553 #ifdef DEBUG 00554 prIndent(); 00555 #endif 00556 fprintf (stderr, "tries = %d, mode = %s\n", tries, rest); 00557 } 00558 if (tries && !x_layout(g, xpms, tries)) 00559 return; 00560 removeOverlapAs(g, rest); 00561 00562 }
1.7.5