/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/fdpgen/xlayout.c

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

Generated on Mon Mar 31 19:03:26 2008 for Graphviz by  doxygen 1.5.1