Graphviz  2.31.20130521.0446
lib/fdpgen/xlayout.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 /* 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 }