/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/pack/pack.c

Go to the documentation of this file.
00001 /* $Id: pack.c,v 1.6 2006/12/07 22:49:37 erg Exp $ $Revision: 1.6 $ */
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 /* Module for packing disconnected graphs together.
00019  * Based on "Disconnected Graph Layout and the Polyomino Packing Approach", 
00020  * K. Freivalds et al., GD0'01, LNCS 2265, pp. 378-391.
00021  */
00022 
00023 #include <render.h>
00024 #include <pack.h>
00025 #include <pointset.h>
00026 #include <math.h>
00027 
00028 #define C 100                   /* Max. avg. polyomino size */
00029 
00030 #define MOVEPT(p) ((p).x += dx, (p).y += dy)
00031 #define GRID(x,s) (((x) + ((s)-1)) / (s))
00032 #define CELL(p,s) ((p).x = (p).x/(s), (p).y = ((p).y/(s)))
00033 #define SGN(a)    (((a)<0)? -1 : 1)
00034 
00035 typedef struct {
00036     Agraph_t *graph;            /* related graph */
00037     int perim;                  /* half size of bounding rectangle perimeter */
00038     point *cells;               /* cells in covering polyomino */
00039     int nc;                     /* no. of cells */
00040     int index;                  /* index in original array */
00041 
00042 } ginfo;
00043 
00044 /* computeStep:
00045  * Compute grid step size. This is a root of the
00046  * quadratic equation al^2 +bl + c, where a, b and
00047  * c are defined below.
00048  */
00049 static int computeStep(int ng, Agraph_t ** gs, int margin)
00050 {
00051     double l1, l2;
00052     double a, b, c, d, r;
00053     double W, H;                /* width and height of graph, with margin */
00054     Agraph_t *g;
00055     int i;
00056 
00057     a = C * ng - 1;
00058     c = 0;
00059     b = 0;
00060     for (i = 0; i < ng; i++) {
00061         g = gs[i];
00062         W = GD_bb(g).UR.x - GD_bb(g).LL.x + 2 * margin;
00063         H = GD_bb(g).UR.y - GD_bb(g).LL.y + 2 * margin;
00064         b -= (W + H);
00065         c -= (W * H);
00066     }
00067     d = b * b - 4.0 * a * c;
00068     if (d < 0) {
00069         agerr(AGERR, "libpack: disc = %f ( < 0)\n", d);
00070         return -1;
00071     }
00072     r = sqrt(d);
00073     l1 = (-b + r) / (2 * a);
00074     l2 = (-b - r) / (2 * a);
00075     if (Verbose > 2) {
00076         fprintf(stderr, "Packing: compute grid size\n");
00077         fprintf(stderr, "a %f b %f c %f d %f r %f\n", a, b, c, d, r);
00078         fprintf(stderr, "root %d (%f) %d (%f)\n", (int) l1, l1, (int) l2,
00079                 l2);
00080         fprintf(stderr, " r1 %f r2 %f\n", a * l1 * l1 + b * l1 + c,
00081                 a * l2 * l2 + b * l2 + c);
00082     }
00083 
00084     return (int) l1;
00085 }
00086 
00087 /* cmpf;
00088  * Comparison function for polyominoes.
00089  * Size is determined by perimeter.
00090  */
00091 static int cmpf(const void *X, const void *Y)
00092 {
00093     ginfo *x = *(ginfo **) X;
00094     ginfo *y = *(ginfo **) Y;
00095     /* flip order to get descending array */
00096     return (y->perim - x->perim);
00097 }
00098 
00099 /* fillLine:
00100  * Mark cells crossed by line from cell p to cell q.
00101  * Bresenham's algorithm, from Graphics Gems I, pp. 99-100.
00102  */
00103 /* static  */
00104 void fillLine(point p, point q, PointSet * ps)
00105 {
00106     int x1 = p.x;
00107     int y1 = p.y;
00108     int x2 = q.x;
00109     int y2 = q.y;
00110     int d, x, y, ax, ay, sx, sy, dx, dy;
00111 
00112     dx = x2 - x1;
00113     ax = ABS(dx) << 1;
00114     sx = SGN(dx);
00115     dy = y2 - y1;
00116     ay = ABS(dy) << 1;
00117     sy = SGN(dy);
00118 
00119 /* fprintf (stderr, "fillLine %d %d - %d %d\n", x1,y1,x2,y2); */
00120     x = x1;
00121     y = y1;
00122     if (ax > ay) {              /* x dominant */
00123         d = ay - (ax >> 1);
00124         for (;;) {
00125 /* fprintf (stderr, "  addPS %d %d\n", x,y); */
00126             addPS(ps, x, y);
00127             if (x == x2)
00128                 return;
00129             if (d >= 0) {
00130                 y += sy;
00131                 d -= ax;
00132             }
00133             x += sx;
00134             d += ay;
00135         }
00136     } else {                    /* y dominant */
00137         d = ax - (ay >> 1);
00138         for (;;) {
00139 /* fprintf (stderr, "  addPS %d %d\n", x,y); */
00140             addPS(ps, x, y);
00141             if (y == y2)
00142                 return;
00143             if (d >= 0) {
00144                 x += sx;
00145                 d -= ay;
00146             }
00147             y += sy;
00148             d += ax;
00149         }
00150     }
00151 }
00152 
00153 /* fillEdge:
00154  * It appears that spline_edges always have the start point at the
00155  * beginning and the end point at the end.
00156  */
00157 static void
00158 fillEdge(Agedge_t * e, point pt, PointSet * ps, int dx, int dy,
00159          int ssize, int doS)
00160 {
00161     int j, k;
00162     bezier bz;
00163     point hpt;
00164     Agnode_t *h;
00165 
00166     /* If doS is false or the edge has not splines, use line segment */
00167     if (!doS || !ED_spl(e)) {
00168         h = e->head;
00169         hpt = coord(h);
00170         MOVEPT(hpt);
00171         CELL(hpt, ssize);
00172         fillLine(pt, hpt, ps);
00173         return;
00174     }
00175 
00176     for (j = 0; j < ED_spl(e)->size; j++) {
00177         bz = ED_spl(e)->list[j];
00178         if (bz.sflag) {
00179             pt = bz.sp;
00180             hpt = bz.list[0];
00181             k = 1;
00182         } else {
00183             pt = bz.list[0];
00184             hpt = bz.list[1];
00185             k = 2;
00186         }
00187         MOVEPT(pt);
00188         CELL(pt, ssize);
00189         MOVEPT(hpt);
00190         CELL(hpt, ssize);
00191         fillLine(pt, hpt, ps);
00192 
00193         for (; k < bz.size; k++) {
00194             pt = hpt;
00195             hpt = bz.list[k];
00196             MOVEPT(hpt);
00197             CELL(hpt, ssize);
00198             fillLine(pt, hpt, ps);
00199         }
00200 
00201         if (bz.eflag) {
00202             pt = hpt;
00203             hpt = bz.ep;
00204             MOVEPT(hpt);
00205             CELL(hpt, ssize);
00206             fillLine(pt, hpt, ps);
00207         }
00208     }
00209 
00210 }
00211 
00212 /* genBox:
00213  * Generate polyomino info from graph using the bounding box of
00214  * the graph.
00215  */
00216 static void
00217 genBox(Agraph_t * g, ginfo * info, int ssize, int margin, point center)
00218 {
00219     PointSet *ps;
00220     int W, H;
00221     point UR, LL;
00222     box bb = GD_bb(g);
00223     int x, y;
00224 
00225     ps = newPS();
00226 
00227     LL.x = center.x - margin;
00228     LL.y = center.y - margin;
00229     UR.x = center.x + bb.UR.x - bb.LL.x + margin;
00230     UR.y = center.y + bb.UR.y - bb.LL.y + margin;
00231     CELL(LL, ssize);
00232     CELL(UR, ssize);
00233 
00234     for (x = LL.x; x <= UR.x; x++)
00235         for (y = LL.y; y <= UR.y; y++)
00236             addPS(ps, x, y);
00237 
00238     info->graph = g;
00239     info->cells = pointsOf(ps);
00240     info->nc = sizeOf(ps);
00241     W = GRID(bb.UR.x - bb.LL.x + 2 * margin, ssize);
00242     H = GRID(bb.UR.y - bb.LL.y + 2 * margin, ssize);
00243     info->perim = W + H;
00244 
00245     if (Verbose > 2) {
00246         int i;
00247         fprintf(stderr, "%s no. cells %d W %d H %d\n", g->name, info->nc,
00248                 W, H);
00249         for (i = 0; i < info->nc; i++)
00250             fprintf(stderr, "  %d %d cell\n", info->cells[i].x,
00251                     info->cells[i].y);
00252     }
00253 
00254     freePS(ps);
00255 }
00256 
00257 /* genPoly:
00258  * Generate polyomino info from graph.
00259  * We add all cells covered partially by the bounding box of the 
00260  * node. If doSplines is true and an edge has a spline, we use the 
00261  * polyline determined by the control point. Otherwise,
00262  * we use each cell crossed by a straight edge between the head and tail.
00263  * If mode = l_clust, we use the graph's GD_clust array to treat the
00264  * top level clusters like large nodes.
00265  * Returns 0 if okay.
00266  */
00267 static int
00268 genPoly(Agraph_t * root, Agraph_t * g, ginfo * info,
00269         int ssize, pack_info * pinfo, point center)
00270 {
00271     PointSet *ps;
00272     int W, H;
00273     point LL, UR;
00274     point pt, s2;
00275     Agraph_t *eg;               /* graph containing edges */
00276     Agnode_t *n;
00277     Agedge_t *e;
00278     int x, y;
00279     int dx, dy;
00280     graph_t *subg;
00281     int margin = pinfo->margin;
00282     int doSplines = pinfo->doSplines;
00283     box bb;
00284 
00285     if (root)
00286         eg = root;
00287     else
00288         eg = g;
00289 
00290     ps = newPS();
00291     dx = center.x - GD_bb(g).LL.x;
00292     dy = center.y - GD_bb(g).LL.y;
00293 
00294     if (pinfo->mode == l_clust) {
00295         int i;
00296         void **alg;
00297 
00298         /* backup the alg data */
00299         alg = N_GNEW(agnnodes(g), void *);
00300         for (i = 0, n = agfstnode(g); n; n = agnxtnode(g, n)) {
00301             alg[i++] = n->u.alg;
00302             n->u.alg = 0;
00303         }
00304 
00305         /* do bbox of top clusters */
00306         for (i = 1; i <= GD_n_cluster(g); i++) {
00307             subg = GD_clust(g)[i];
00308             bb = GD_bb(subg);
00309             if ((bb.UR.x > bb.LL.x) && (bb.UR.y > bb.LL.y)) {
00310                 MOVEPT(bb.LL);
00311                 MOVEPT(bb.UR);
00312                 bb.LL.x -= margin;
00313                 bb.LL.y -= margin;
00314                 bb.UR.x += margin;
00315                 bb.UR.y += margin;
00316                 CELL(bb.LL, ssize);
00317                 CELL(bb.UR, ssize);
00318 
00319                 for (x = bb.LL.x; x <= bb.UR.x; x++)
00320                     for (y = bb.LL.y; y <= bb.UR.y; y++)
00321                         addPS(ps, x, y);
00322 
00323                 /* note which nodes are in clusters */
00324                 for (n = agfstnode(subg); n; n = agnxtnode(subg, n))
00325                     ND_clust(n) = subg;
00326             }
00327         }
00328 
00329         /* now do remaining nodes and edges */
00330         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00331             pt = coord(n);
00332             MOVEPT(pt);
00333             if (!ND_clust(n)) { /* n is not in a top-level cluster */
00334                 s2.x = margin + ND_xsize(n) / 2;
00335                 s2.y = margin + ND_ysize(n) / 2;
00336                 LL = sub_points(pt, s2);
00337                 UR = add_points(pt, s2);
00338                 CELL(LL, ssize);
00339                 CELL(UR, ssize);
00340 
00341                 for (x = LL.x; x <= UR.x; x++)
00342                     for (y = LL.y; y <= UR.y; y++)
00343                         addPS(ps, x, y);
00344 
00345                 CELL(pt, ssize);
00346                 for (e = agfstout(eg, n); e; e = agnxtout(eg, e)) {
00347                     fillEdge(e, pt, ps, dx, dy, ssize, doSplines);
00348                 }
00349             } else {
00350                 CELL(pt, ssize);
00351                 for (e = agfstout(eg, n); e; e = agnxtout(eg, e)) {
00352                     if (ND_clust(n) == ND_clust(e->head))
00353                         continue;
00354                     fillEdge(e, pt, ps, dx, dy, ssize, doSplines);
00355                 }
00356             }
00357         }
00358 
00359         /* restore the alg data */
00360         for (i = 0, n = agfstnode(g); n; n = agnxtnode(g, n)) {
00361             n->u.alg = alg[i++];
00362         }
00363         free(alg);
00364 
00365     } else
00366         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00367             pt = coord(n);
00368             MOVEPT(pt);
00369             s2.x = margin + ND_xsize(n) / 2;
00370             s2.y = margin + ND_ysize(n) / 2;
00371             LL = sub_points(pt, s2);
00372             UR = add_points(pt, s2);
00373             CELL(LL, ssize);
00374             CELL(UR, ssize);
00375 
00376             for (x = LL.x; x <= UR.x; x++)
00377                 for (y = LL.y; y <= UR.y; y++)
00378                     addPS(ps, x, y);
00379 
00380             CELL(pt, ssize);
00381             for (e = agfstout(eg, n); e; e = agnxtout(eg, e)) {
00382                 fillEdge(e, pt, ps, dx, dy, ssize, doSplines);
00383             }
00384         }
00385 
00386     info->graph = g;
00387     info->cells = pointsOf(ps);
00388     info->nc = sizeOf(ps);
00389     W = GRID(GD_bb(g).UR.x - GD_bb(g).LL.x + 2 * margin, ssize);
00390     H = GRID(GD_bb(g).UR.y - GD_bb(g).LL.y + 2 * margin, ssize);
00391     info->perim = W + H;
00392 
00393     if (Verbose > 2) {
00394         int i;
00395         fprintf(stderr, "%s no. cells %d W %d H %d\n", g->name, info->nc,
00396                 W, H);
00397         for (i = 0; i < info->nc; i++)
00398             fprintf(stderr, "  %d %d cell\n", info->cells[i].x,
00399                     info->cells[i].y);
00400     }
00401 
00402     freePS(ps);
00403     return 0;
00404 }
00405 
00406 /* fits:
00407  * Check if polyomino fits at given point.
00408  * If so, add cells to pointset, store point in place and return true.
00409  */
00410 static int
00411 fits(int x, int y, ginfo * info, PointSet * ps, point * place, int step)
00412 {
00413     point *cells = info->cells;
00414     int n = info->nc;
00415     point cell;
00416     int i;
00417     point LL;
00418 
00419     for (i = 0; i < n; i++) {
00420         cell = *cells;
00421         cell.x += x;
00422         cell.y += y;
00423         if (inPS(ps, cell))
00424             return 0;
00425         cells++;
00426     }
00427 
00428     LL = GD_bb(info->graph).LL;
00429     place->x = step * x - LL.x;
00430     place->y = step * y - LL.y;
00431 
00432     cells = info->cells;
00433     for (i = 0; i < n; i++) {
00434         cell = *cells;
00435         cell.x += x;
00436         cell.y += y;
00437         insertPS(ps, cell);
00438         cells++;
00439     }
00440 
00441     if (Verbose >= 2)
00442         fprintf(stderr, "cc (%d cells) at (%d,%d) (%d,%d)\n", n, x, y,
00443                 place->x, place->y);
00444     return 1;
00445 }
00446 
00447 /* placeFixed:
00448  * Position fixed graph. Store final translation and
00449  * fill polyomino set. Note that polyomino set for the
00450  * graph is constructed where it will be.
00451  */
00452 static void
00453 placeFixed(ginfo * info, PointSet * ps, point * place, point center)
00454 {
00455     point *cells = info->cells;
00456     int n = info->nc;
00457     int i;
00458 
00459     place->x = -center.x;
00460     place->y = -center.y;
00461 
00462     for (i = 0; i < n; i++) {
00463         insertPS(ps, *cells++);
00464     }
00465 
00466     if (Verbose >= 2)
00467         fprintf(stderr, "cc (%d cells) at (%d,%d)\n", n, place->x,
00468                 place->y);
00469 }
00470 
00471 /* placeGraph:
00472  * Search for points on concentric "circles" out
00473  * from the origin. Check if polyomino can be placed
00474  * with bounding box origin at point.
00475  * First graph (i == 0) is centered on the origin if possible.
00476  */
00477 static void
00478 placeGraph(int i, ginfo * info, PointSet * ps, point * place, int step,
00479            int margin)
00480 {
00481     int x, y;
00482     int W, H;
00483     int bnd;
00484 
00485     if (i == 0) {
00486         Agraph_t *g = info->graph;
00487         W = GRID(GD_bb(g).UR.x - GD_bb(g).LL.x + 2 * margin, step);
00488         H = GRID(GD_bb(g).UR.y - GD_bb(g).LL.y + 2 * margin, step);
00489         if (fits(-W / 2, -H / 2, info, ps, place, step))
00490             return;
00491     }
00492 
00493     if (fits(0, 0, info, ps, place, step))
00494         return;
00495     W = GD_bb(info->graph).UR.x - GD_bb(info->graph).LL.x;
00496     H = GD_bb(info->graph).UR.y - GD_bb(info->graph).LL.y;
00497     if (W >= H) {
00498         for (bnd = 1;; bnd++) {
00499             x = 0;
00500             y = -bnd;
00501             for (; x < bnd; x++)
00502                 if (fits(x, y, info, ps, place, step))
00503                     return;
00504             for (; y < bnd; y++)
00505                 if (fits(x, y, info, ps, place, step))
00506                     return;
00507             for (; x > -bnd; x--)
00508                 if (fits(x, y, info, ps, place, step))
00509                     return;
00510             for (; y > -bnd; y--)
00511                 if (fits(x, y, info, ps, place, step))
00512                     return;
00513             for (; x < 0; x++)
00514                 if (fits(x, y, info, ps, place, step))
00515                     return;
00516         }
00517     } else {
00518         for (bnd = 1;; bnd++) {
00519             y = 0;
00520             x = -bnd;
00521             for (; y > -bnd; y--)
00522                 if (fits(x, y, info, ps, place, step))
00523                     return;
00524             for (; x < bnd; x++)
00525                 if (fits(x, y, info, ps, place, step))
00526                     return;
00527             for (; y < bnd; y++)
00528                 if (fits(x, y, info, ps, place, step))
00529                     return;
00530             for (; x > -bnd; x--)
00531                 if (fits(x, y, info, ps, place, step))
00532                     return;
00533             for (; y > 0; y--)
00534                 if (fits(x, y, info, ps, place, step))
00535                     return;
00536         }
00537     }
00538 }
00539 
00540 #ifdef DEBUG
00541 void dumpp(ginfo * info, char *pfx)
00542 {
00543     point *cells = info->cells;
00544     int i, c_cnt = info->nc;
00545 
00546     fprintf(stderr, "%s\n", pfx);
00547     for (i = 0; i < c_cnt; i++) {
00548         fprintf(stderr, "%d %d box\n", cells[i].x, cells[i].y);
00549     }
00550 }
00551 #endif
00552 
00553 /* putGraphs:
00554  *  Given a collection of graphs, reposition them in the plane
00555  *  to not overlap but pack "nicely".
00556  *   ng is the number of graphs
00557  *   gs is a pointer to an array of graph pointers
00558  *   root gives the graph containing the edges; if null, the function
00559  *     looks in each graph in gs for its edges
00560  *   pinfo->margin gives the amount of extra space left around nodes in points
00561  *   If pinfo->doSplines is true, use edge splines, if computed,
00562  *     in calculating polyomino.
00563  *   pinfo->mode specifies the packing granularity and technique:
00564  *     l_node : pack at the node/cluster level
00565  *     l_graph : pack at the bounding box level
00566  *  Returns array of points to which graphs should be translated;
00567  *  the array needs to be freed;
00568  * Returns NULL if problem occur or if ng == 0.
00569  * 
00570  * Depends on graph fields bb, node fields pos, xsize and ysize, and
00571  * edge field spl.
00572  */
00573 point *putGraphs(int ng, Agraph_t ** gs, Agraph_t * root,
00574                  pack_info * pinfo)
00575 {
00576     int stepSize;
00577     ginfo *info;
00578     ginfo **sinfo;
00579     point *places;
00580     Dict_t *ps;
00581     int i;
00582     boolean *fixed = pinfo->fixed;
00583     int fixed_cnt = 0;
00584     box fixed_bb = { {0, 0}, {0, 0} };
00585     point center;
00586 
00587     if (ng <= 0)
00588         return 0;
00589 
00590     /* update bounding box info for each graph */
00591     /* If fixed, compute bbox of fixed graphs */
00592     for (i = 0; i < ng; i++) {
00593         Agraph_t *g = gs[i];
00594         compute_bb(g);
00595         if (fixed && fixed[i]) {
00596             if (fixed_cnt) {
00597                 box bb = GD_bb(g);
00598                 fixed_bb.LL.x = MIN(bb.LL.x, fixed_bb.LL.x);
00599                 fixed_bb.LL.y = MIN(bb.LL.y, fixed_bb.LL.y);
00600                 fixed_bb.UR.x = MAX(bb.UR.x, fixed_bb.UR.x);
00601                 fixed_bb.UR.y = MAX(bb.UR.y, fixed_bb.UR.y);
00602             } else
00603                 fixed_bb = GD_bb(g);
00604             fixed_cnt++;
00605         }
00606         if (Verbose > 2) {
00607             fprintf(stderr, "bb[%s] %d %d %d %d\n", g->name, GD_bb(g).LL.x,
00608                     GD_bb(g).LL.y, GD_bb(g).UR.x, GD_bb(g).UR.y);
00609         }
00610     }
00611 
00612     /* calculate grid size */
00613     stepSize = computeStep(ng, gs, pinfo->margin);
00614     if (Verbose)
00615         fprintf(stderr, "step size = %d\n", stepSize);
00616     if (stepSize < 0)
00617         return 0;
00618 
00619     /* generate polyomino cover for the graphs */
00620     if (fixed) {
00621         center.x = (fixed_bb.LL.x + fixed_bb.UR.x) / 2;
00622         center.y = (fixed_bb.LL.y + fixed_bb.UR.y) / 2;
00623     } else
00624         center.x = center.y = 0;
00625     info = N_NEW(ng, ginfo);
00626     for (i = 0; i < ng; i++) {
00627         info[i].index = i;
00628         if (pinfo->mode == l_graph)
00629             genBox(gs[i], info + i, stepSize, pinfo->margin, center);
00630         else if (genPoly(root, gs[i], info + i, stepSize, pinfo, center)) {
00631             return 0;
00632         }
00633     }
00634 
00635     /* sort */
00636     sinfo = N_NEW(ng, ginfo *);
00637     for (i = 0; i < ng; i++) {
00638         sinfo[i] = info + i;
00639     }
00640     qsort(sinfo, ng, sizeof(ginfo *), cmpf);
00641 
00642     ps = newPS();
00643     places = N_NEW(ng, point);
00644     if (fixed) {
00645         for (i = 0; i < ng; i++) {
00646             if (fixed[i])
00647                 placeFixed(sinfo[i], ps, places + (sinfo[i]->index),
00648                            center);
00649         }
00650         for (i = 0; i < ng; i++) {
00651             if (!fixed[i])
00652                 placeGraph(i, sinfo[i], ps, places + (sinfo[i]->index),
00653                            stepSize, pinfo->margin);
00654         }
00655     } else {
00656         for (i = 0; i < ng; i++)
00657             placeGraph(i, sinfo[i], ps, places + (sinfo[i]->index),
00658                        stepSize, pinfo->margin);
00659     }
00660 
00661     free(sinfo);
00662     for (i = 0; i < ng; i++)
00663         free(info[i].cells);
00664     free(info);
00665     freePS(ps);
00666 
00667     if (Verbose > 1)
00668         for (i = 0; i < ng; i++)
00669             fprintf(stderr, "pos[%d] %d %d\n", i, places[i].x,
00670                     places[i].y);
00671 
00672     return places;
00673 }
00674 
00675 /* shiftEdge:
00676  * Translate all of the edge components by the given offset.
00677  */
00678 static void shiftEdge(Agedge_t * e, int dx, int dy)
00679 {
00680     int j, k;
00681     bezier bz;
00682 
00683     if (ED_label(e))
00684         MOVEPT(ED_label(e)->p);
00685     if (ED_head_label(e))
00686         MOVEPT(ED_head_label(e)->p);
00687     if (ED_tail_label(e))
00688         MOVEPT(ED_tail_label(e)->p);
00689 
00690     if (ED_spl(e) == NULL)
00691         return;
00692 
00693     for (j = 0; j < ED_spl(e)->size; j++) {
00694         bz = ED_spl(e)->list[j];
00695         for (k = 0; k < bz.size; k++)
00696             MOVEPT(bz.list[k]);
00697         if (bz.sflag)
00698             MOVEPT(ED_spl(e)->list[j].sp);
00699         if (bz.eflag)
00700             MOVEPT(ED_spl(e)->list[j].ep);
00701     }
00702 }
00703 
00704 /* shiftGraph:
00705  */
00706 static void shiftGraph(Agraph_t * g, int dx, int dy)
00707 {
00708     graph_t *subg;
00709     box bb = GD_bb(g);
00710     int i;
00711 
00712     bb.LL.x += dx;
00713     bb.UR.x += dx;
00714     bb.LL.y += dy;
00715     bb.UR.y += dy;
00716     GD_bb(g) = bb;
00717 
00718     if (GD_label(g))
00719         MOVEPT(GD_label(g)->p);
00720 
00721     for (i = 1; i <= GD_n_cluster(g); i++) {
00722         subg = GD_clust(g)[i];
00723         shiftGraph(subg, dx, dy);
00724     }
00725 }
00726 
00727 /* shiftGraphs:
00728  * The function takes ng graphs gs and a similar
00729  * number of points pp and translates each graph so
00730  * that the lower left corner of the bounding box of graph gs[i] is at
00731  * point ps[i]. To do this, it assumes the bb field in
00732  * Agraphinfo_t accurately reflects the current graph layout.
00733  * The graph is repositioned by translating the pos and coord fields of 
00734  * each node appropriately.
00735  * 
00736  * If doSplines is non-zero, the function also translates splines coordinates
00737  * of each edge, if they have been calculated. In addition, edge labels are
00738  * repositioned. 
00739  * 
00740  * If root is non-NULL, it is taken as the root graph of
00741  * the graphs in gs and is used to find the edges. Otherwise, the function
00742  * uses the edges found in each graph gs[i].
00743  * 
00744  * It returns 0 on success.
00745  * 
00746  * This function uses the bb field in Agraphinfo_t,
00747  * the pos and coord fields in nodehinfo_t and
00748  * the spl field in Aedgeinfo_t.
00749  */
00750 static int
00751 shiftGraphs(int ng, Agraph_t ** gs, point * pp, Agraph_t * root,
00752             int doSplines)
00753 {
00754     int i;
00755     int dx, dy;
00756     double fx, fy;
00757     point p;
00758     Agraph_t *g;
00759     Agraph_t *eg;
00760     Agnode_t *n;
00761     Agedge_t *e;
00762 
00763     if (ng <= 0)
00764         return abs(ng);
00765 
00766     for (i = 0; i < ng; i++) {
00767         g = gs[i];
00768         if (root)
00769             eg = root;
00770         else
00771             eg = g;
00772         p = pp[i];
00773         dx = p.x;
00774         dy = p.y;
00775         fx = PS2INCH(dx);
00776         fy = PS2INCH(dy);
00777 
00778         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00779             ND_pos(n)[0] += fx;
00780             ND_pos(n)[1] += fy;
00781             MOVEPT(ND_coord_i(n));
00782             if (doSplines) {
00783                 for (e = agfstout(eg, n); e; e = agnxtout(eg, e))
00784                     shiftEdge(e, dx, dy);
00785             }
00786         }
00787         shiftGraph(g, dx, dy);
00788     }
00789 
00790     return 0;
00791 }
00792 
00793 /* packGraphs:
00794  * Packs graphs.
00795  *  ng - number of graphs
00796  *  gs - pointer to array of graphs
00797  *  root - graph used to find edges
00798  *  info - parameters used in packing
00799  *  info->doSplines - if true, use already computed spline control points
00800  * This decides where to layout the graphs and repositions the graph's
00801  * position info.
00802  *
00803  * Returns 0 on success.
00804  */
00805 int packGraphs(int ng, Agraph_t ** gs, Agraph_t * root, pack_info * info)
00806 {
00807     int ret;
00808     point *pp = putGraphs(ng, gs, root, info);
00809 
00810     if (!pp)
00811         return 1;
00812     ret = shiftGraphs(ng, gs, pp, root, info->doSplines);
00813     free(pp);
00814     return ret;
00815 }
00816 
00817 /* packSubgraphs:
00818  * Packs subgraphs of given root graph, then recalculates root's bounding box.
00819  * Note that it does not recompute subgraph bounding boxes.
00820  * Cluster bounding boxes are recomputed in shiftGraphs.
00821  */
00822 int
00823 packSubgraphs(int ng, Agraph_t ** gs, Agraph_t * root, pack_info * info)
00824 {
00825     int ret;
00826 
00827     ret = packGraphs(ng, gs, root, info);
00828     if (ret == 0) {
00829         int i, j;
00830         box bb;
00831         graph_t* g;
00832 
00833         compute_bb(root);
00834         bb = GD_bb(root);
00835         for (i = 0; i < ng; i++) {
00836             g = gs[i];
00837             for (j = 1; j <= GD_n_cluster(g); j++) {
00838                 EXPANDBB(bb,GD_bb(GD_clust(g)[j]));
00839             }
00840         }
00841         GD_bb(root) = bb;
00842     }
00843     return ret;
00844 }
00845 
00846 /* pack_graph:
00847  * Pack subgraphs followed by postprocessing.
00848  */
00849 int 
00850 pack_graph(int ng, Agraph_t** gs, Agraph_t* root, boolean* fixed)
00851 {
00852     int ret;
00853     pack_info info;
00854 
00855     info.margin = getPack (root, CL_OFFSET, CL_OFFSET);;
00856     info.mode = getPackMode (root, l_graph);
00857     info.doSplines = 1;
00858     info.fixed = fixed;
00859     ret = packSubgraphs(ng, gs, root, &info);
00860     if (ret == 0) dotneato_postprocess (root);
00861     return ret;
00862 }
00863 
00864 /* getPackMode;
00865  * Return pack_mode of graph using "packmode" attribute.
00866  * If not defined, return dflt
00867  */
00868 pack_mode getPackMode(Agraph_t * g, pack_mode dflt)
00869 {
00870     char *p = agget(g, "packmode");
00871     pack_mode mode = dflt;
00872 
00873     if (p && *p) {
00874         switch (*p) {
00875 #ifdef NOT_IMPLEMENTED
00876         case 'b':
00877             if (streq(p, "bisect"))
00878                 mode = l_bisect;
00879             break;
00880 #endif
00881         case 'c':
00882             if (streq(p, "cluster"))
00883                 mode = l_clust;
00884             break;
00885         case 'g':
00886             if (streq(p, "graph"))
00887                 mode = l_graph;
00888             break;
00889 #ifdef NOT_IMPLEMENTED
00890         case 'h':
00891             if (streq(p, "hull"))
00892                 mode = l_hull;
00893             break;
00894 #endif
00895         case 'n':
00896             if (streq(p, "node"))
00897                 mode = l_node;
00898             break;
00899 #ifdef NOT_IMPLEMENTED
00900         case 't':
00901             if (streq(p, "tile"))
00902                 mode = l_tile;
00903             break;
00904 #endif
00905         }
00906     }
00907     return mode;
00908 }
00909 
00910 /* getPack;
00911  * Return "pack" attribute of g.
00912  * If not defined or negative, return not_def.
00913  * If defined but not specified, return dflt.
00914  */
00915 int getPack(Agraph_t * g, int not_def, int dflt)
00916 {
00917     char *p;
00918     int i;
00919     int v = not_def;
00920 
00921     if ((p = agget(g, "pack"))) {
00922         if ((sscanf(p, "%d", &i) == 1) && (i >= 0))
00923             v = i;
00924         else if ((*p == 't') || (*p == 'T'))
00925             v = dflt;
00926     }
00927 
00928     return v;
00929 }

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