Graphviz  2.29.20120524.0446
lib/pack/pack.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 /* Module for packing disconnected graphs together.
00016  * Based on "Disconnected Graph Layout and the Polyomino Packing Approach", 
00017  * K. Freivalds et al., GD0'01, LNCS 2265, pp. 378-391.
00018  */
00019 
00020 #include <math.h>
00021 #include <assert.h>
00022 #include "render.h"
00023 #include "pack.h"
00024 #include "pointset.h"
00025 #include <assert.h>
00026 
00027 #define strneq(a,b,n)           (!strncmp(a,b,n))
00028 
00029 #define C 100                   /* Max. avg. polyomino size */
00030 
00031 #define MOVEPT(p) ((p).x += dx, (p).y += dy)
00032 /* Given cell size s, GRID(x:double,s:int) returns how many cells are required by size x */
00033 #define GRID(x,s) ((int)ceil((x)/(s)))
00034 /* Given grid cell size s, CVAL(v:int,s:int) returns index of cell containing point v */
00035 #define CVAL(v,s) ((v) >= 0 ? (v)/(s) : (((v)+1)/(s))-1)
00036 /* Given grid cell size s, CELL(p:point,s:int) sets p to cell containing point p */
00037 #define CELL(p,s) ((p).x = CVAL((p).x,s), (p).y = CVAL((p).y,(s)))
00038 
00039 typedef struct {
00040     int perim;                  /* half size of bounding rectangle perimeter */
00041     point *cells;               /* cells in covering polyomino */
00042     int nc;                     /* no. of cells */
00043     int index;                  /* index in original array */
00044 } ginfo;
00045 
00046 typedef struct {
00047     double width, height;
00048     int index;                  /* index in original array */
00049 } ainfo;
00050 
00051 /* computeStep:
00052  * Compute grid step size. This is a root of the
00053  * quadratic equation al^2 +bl + c, where a, b and
00054  * c are defined below.
00055  */
00056 static int computeStep(int ng, boxf* bbs, int margin)
00057 {
00058     double l1, l2;
00059     double a, b, c, d, r;
00060     double W, H;                /* width and height of graph, with margin */
00061     int i, root;
00062 
00063     a = C * ng - 1;
00064     c = 0;
00065     b = 0;
00066     for (i = 0; i < ng; i++) {
00067         boxf bb = bbs[i];
00068         W = bb.UR.x - bb.LL.x + 2 * margin;
00069         H = bb.UR.y - bb.LL.y + 2 * margin;
00070         b -= (W + H);
00071         c -= (W * H);
00072     }
00073     d = b * b - 4.0 * a * c;
00074     if (d < 0) {
00075         agerr(AGERR, "libpack: disc = %f ( < 0)\n", d);
00076         return -1;
00077     }
00078     r = sqrt(d);
00079     l1 = (-b + r) / (2 * a);
00080     l2 = (-b - r) / (2 * a);
00081     root = (int) l1;
00082     if (root == 0) root = 1;
00083     if (Verbose > 2) {
00084         fprintf(stderr, "Packing: compute grid size\n");
00085         fprintf(stderr, "a %f b %f c %f d %f r %f\n", a, b, c, d, r);
00086         fprintf(stderr, "root %d (%f) %d (%f)\n", root, l1, (int) l2,
00087                 l2);
00088         fprintf(stderr, " r1 %f r2 %f\n", a * l1 * l1 + b * l1 + c,
00089                 a * l2 * l2 + b * l2 + c);
00090     }
00091 
00092     return root;
00093 }
00094 
00095 /* cmpf;
00096  * Comparison function for polyominoes.
00097  * Size is determined by perimeter.
00098  */
00099 static int cmpf(const void *X, const void *Y)
00100 {
00101     ginfo *x = *(ginfo **) X;
00102     ginfo *y = *(ginfo **) Y;
00103     /* flip order to get descending array */
00104     return (y->perim - x->perim);
00105 }
00106 
00107 /* fillLine:
00108  * Mark cells crossed by line from cell p to cell q.
00109  * Bresenham's algorithm, from Graphics Gems I, pp. 99-100.
00110  */
00111 /* static  */
00112 void fillLine(pointf p, pointf q, PointSet * ps)
00113 {
00114     int x1 = ROUND(p.x);
00115     int y1 = ROUND(p.y);
00116     int x2 = ROUND(q.x);
00117     int y2 = ROUND(q.y);
00118     int d, x, y, ax, ay, sx, sy, dx, dy;
00119 
00120     dx = x2 - x1;
00121     ax = ABS(dx) << 1;
00122     sx = SGN(dx);
00123     dy = y2 - y1;
00124     ay = ABS(dy) << 1;
00125     sy = SGN(dy);
00126 
00127 /* fprintf (stderr, "fillLine %d %d - %d %d\n", x1,y1,x2,y2); */
00128     x = x1;
00129     y = y1;
00130     if (ax > ay) {              /* x dominant */
00131         d = ay - (ax >> 1);
00132         for (;;) {
00133 /* fprintf (stderr, "  addPS %d %d\n", x,y); */
00134             addPS(ps, x, y);
00135             if (x == x2)
00136                 return;
00137             if (d >= 0) {
00138                 y += sy;
00139                 d -= ax;
00140             }
00141             x += sx;
00142             d += ay;
00143         }
00144     } else {                    /* y dominant */
00145         d = ax - (ay >> 1);
00146         for (;;) {
00147 /* fprintf (stderr, "  addPS %d %d\n", x,y); */
00148             addPS(ps, x, y);
00149             if (y == y2)
00150                 return;
00151             if (d >= 0) {
00152                 x += sx;
00153                 d -= ay;
00154             }
00155             y += sy;
00156             d += ax;
00157         }
00158     }
00159 }
00160 
00161 /* fillEdge:
00162  * It appears that spline_edges always have the start point at the
00163  * beginning and the end point at the end.
00164  */
00165 static void
00166 fillEdge(Agedge_t * e, point p, PointSet * ps, int dx, int dy,
00167          int ssize, int doS)
00168 {
00169     int j, k;
00170     bezier bz;
00171     pointf pt, hpt;
00172     Agnode_t *h;
00173 
00174     P2PF(p, pt);
00175 
00176     /* If doS is false or the edge has not splines, use line segment */
00177     if (!doS || !ED_spl(e)) {
00178         h = aghead(e);
00179         hpt = coord(h);
00180         MOVEPT(hpt);
00181         CELL(hpt, ssize);
00182         fillLine(pt, hpt, ps);
00183         return;
00184     }
00185 
00186     for (j = 0; j < ED_spl(e)->size; j++) {
00187         bz = ED_spl(e)->list[j];
00188         if (bz.sflag) {
00189             pt = bz.sp;
00190             hpt = bz.list[0];
00191             k = 1;
00192         } else {
00193             pt = bz.list[0];
00194             hpt = bz.list[1];
00195             k = 2;
00196         }
00197         MOVEPT(pt);
00198         CELL(pt, ssize);
00199         MOVEPT(hpt);
00200         CELL(hpt, ssize);
00201         fillLine(pt, hpt, ps);
00202 
00203         for (; k < bz.size; k++) {
00204             pt = hpt;
00205             hpt = bz.list[k];
00206             MOVEPT(hpt);
00207             CELL(hpt, ssize);
00208             fillLine(pt, hpt, ps);
00209         }
00210 
00211         if (bz.eflag) {
00212             pt = hpt;
00213             hpt = bz.ep;
00214             MOVEPT(hpt);
00215             CELL(hpt, ssize);
00216             fillLine(pt, hpt, ps);
00217         }
00218     }
00219 
00220 }
00221 
00222 /* genBox:
00223  * Generate polyomino info from graph using the bounding box of
00224  * the graph.
00225  */
00226 static void
00227 genBox(boxf bb0, ginfo * info, int ssize, int margin, point center, char* s)
00228 {
00229     PointSet *ps;
00230     int W, H;
00231     point UR, LL;
00232     box bb;
00233     int x, y;
00234 
00235     BF2B(bb0, bb);
00236     ps = newPS();
00237 
00238     LL.x = center.x - margin;
00239     LL.y = center.y - margin;
00240     UR.x = center.x + bb.UR.x - bb.LL.x + margin;
00241     UR.y = center.y + bb.UR.y - bb.LL.y + margin;
00242     CELL(LL, ssize);
00243     CELL(UR, ssize);
00244 
00245     for (x = LL.x; x <= UR.x; x++)
00246         for (y = LL.y; y <= UR.y; y++)
00247             addPS(ps, x, y);
00248 
00249     info->cells = pointsOf(ps);
00250     info->nc = sizeOf(ps);
00251     W = GRID(bb0.UR.x - bb0.LL.x + 2 * margin, ssize);
00252     H = GRID(bb0.UR.y - bb0.LL.y + 2 * margin, ssize);
00253     info->perim = W + H;
00254 
00255     if (Verbose > 2) {
00256         int i;
00257         fprintf(stderr, "%s no. cells %d W %d H %d\n",
00258                 s, info->nc, W, H);
00259         for (i = 0; i < info->nc; i++)
00260             fprintf(stderr, "  %d %d cell\n", info->cells[i].x,
00261                     info->cells[i].y);
00262     }
00263 
00264     freePS(ps);
00265 }
00266 
00267 /* genPoly:
00268  * Generate polyomino info from graph.
00269  * We add all cells covered partially by the bounding box of the 
00270  * node. If doSplines is true and an edge has a spline, we use the 
00271  * polyline determined by the control point. Otherwise,
00272  * we use each cell crossed by a straight edge between the head and tail.
00273  * If mode = l_clust, we use the graph's GD_clust array to treat the
00274  * top level clusters like large nodes.
00275  * Returns 0 if okay.
00276  */
00277 static int
00278 genPoly(Agraph_t * root, Agraph_t * g, ginfo * info,
00279         int ssize, pack_info * pinfo, point center)
00280 {
00281     PointSet *ps;
00282     int W, H;
00283     point LL, UR;
00284     point pt, s2;
00285     pointf ptf;
00286     Agraph_t *eg;               /* graph containing edges */
00287     Agnode_t *n;
00288     Agedge_t *e;
00289     int x, y;
00290     int dx, dy;
00291     graph_t *subg;
00292     int margin = pinfo->margin;
00293     int doSplines = pinfo->doSplines;
00294     box bb;
00295 
00296     if (root)
00297         eg = root;
00298     else
00299         eg = g;
00300 
00301     ps = newPS();
00302     dx = center.x - ROUND(GD_bb(g).LL.x);
00303     dy = center.y - ROUND(GD_bb(g).LL.y);
00304 
00305     if (pinfo->mode == l_clust) {
00306         int i;
00307         void **alg;
00308 
00309         /* backup the alg data */
00310         alg = N_GNEW(agnnodes(g), void *);
00311         for (i = 0, n = agfstnode(g); n; n = agnxtnode(g, n)) {
00312             alg[i++] = ND_alg(n);
00313             ND_alg(n) = 0;
00314         }
00315 
00316         /* do bbox of top clusters */
00317         for (i = 1; i <= GD_n_cluster(g); i++) {
00318             subg = GD_clust(g)[i];
00319             BF2B(GD_bb(subg), bb);
00320             if ((bb.UR.x > bb.LL.x) && (bb.UR.y > bb.LL.y)) {
00321                 MOVEPT(bb.LL);
00322                 MOVEPT(bb.UR);
00323                 bb.LL.x -= margin;
00324                 bb.LL.y -= margin;
00325                 bb.UR.x += margin;
00326                 bb.UR.y += margin;
00327                 CELL(bb.LL, ssize);
00328                 CELL(bb.UR, ssize);
00329 
00330                 for (x = bb.LL.x; x <= bb.UR.x; x++)
00331                     for (y = bb.LL.y; y <= bb.UR.y; y++)
00332                         addPS(ps, x, y);
00333 
00334                 /* note which nodes are in clusters */
00335                 for (n = agfstnode(subg); n; n = agnxtnode(subg, n))
00336                     ND_clust(n) = subg;
00337             }
00338         }
00339 
00340         /* now do remaining nodes and edges */
00341         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00342             ptf = coord(n);
00343             PF2P(ptf, pt);
00344             MOVEPT(pt);
00345             if (!ND_clust(n)) { /* n is not in a top-level cluster */
00346                 s2.x = margin + ND_xsize(n) / 2;
00347                 s2.y = margin + ND_ysize(n) / 2;
00348                 LL = sub_point(pt, s2);
00349                 UR = add_point(pt, s2);
00350                 CELL(LL, ssize);
00351                 CELL(UR, ssize);
00352 
00353                 for (x = LL.x; x <= UR.x; x++)
00354                     for (y = LL.y; y <= UR.y; y++)
00355                         addPS(ps, x, y);
00356 
00357                 CELL(pt, ssize);
00358                 for (e = agfstout(eg, n); e; e = agnxtout(eg, e)) {
00359                     fillEdge(e, pt, ps, dx, dy, ssize, doSplines);
00360                 }
00361             } else {
00362                 CELL(pt, ssize);
00363                 for (e = agfstout(eg, n); e; e = agnxtout(eg, e)) {
00364                     if (ND_clust(n) == ND_clust(aghead(e)))
00365                         continue;
00366                     fillEdge(e, pt, ps, dx, dy, ssize, doSplines);
00367                 }
00368             }
00369         }
00370 
00371         /* restore the alg data */
00372         for (i = 0, n = agfstnode(g); n; n = agnxtnode(g, n)) {
00373             ND_alg(n)= alg[i++];
00374         }
00375         free(alg);
00376 
00377     } else
00378         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00379             ptf = coord(n);
00380             PF2P(ptf, pt);
00381             MOVEPT(pt);
00382             s2.x = margin + ND_xsize(n) / 2;
00383             s2.y = margin + ND_ysize(n) / 2;
00384             LL = sub_point(pt, s2);
00385             UR = add_point(pt, s2);
00386             CELL(LL, ssize);
00387             CELL(UR, ssize);
00388 
00389             for (x = LL.x; x <= UR.x; x++)
00390                 for (y = LL.y; y <= UR.y; y++)
00391                     addPS(ps, x, y);
00392 
00393             CELL(pt, ssize);
00394             for (e = agfstout(eg, n); e; e = agnxtout(eg, e)) {
00395                 fillEdge(e, pt, ps, dx, dy, ssize, doSplines);
00396             }
00397         }
00398 
00399     info->cells = pointsOf(ps);
00400     info->nc = sizeOf(ps);
00401     W = GRID(GD_bb(g).UR.x - GD_bb(g).LL.x + 2 * margin, ssize);
00402     H = GRID(GD_bb(g).UR.y - GD_bb(g).LL.y + 2 * margin, ssize);
00403     info->perim = W + H;
00404 
00405     if (Verbose > 2) {
00406         int i;
00407         fprintf(stderr, "%s no. cells %d W %d H %d\n",
00408                 agnameof(g), info->nc, W, H);
00409         for (i = 0; i < info->nc; i++)
00410             fprintf(stderr, "  %d %d cell\n", info->cells[i].x,
00411                     info->cells[i].y);
00412     }
00413 
00414     freePS(ps);
00415     return 0;
00416 }
00417 
00418 /* fits:
00419  * Check if polyomino fits at given point.
00420  * If so, add cells to pointset, store point in place and return true.
00421  */
00422 static int
00423 fits(int x, int y, ginfo * info, PointSet * ps, point * place, int step, boxf* bbs)
00424 {
00425     point *cells = info->cells;
00426     int n = info->nc;
00427     point cell;
00428     int i;
00429     point LL;
00430 
00431     for (i = 0; i < n; i++) {
00432         cell = *cells;
00433         cell.x += x;
00434         cell.y += y;
00435         if (inPS(ps, cell))
00436             return 0;
00437         cells++;
00438     }
00439 
00440     PF2P(bbs[info->index].LL, LL);
00441     place->x = step * x - LL.x;
00442     place->y = step * y - LL.y;
00443 
00444     cells = info->cells;
00445     for (i = 0; i < n; i++) {
00446         cell = *cells;
00447         cell.x += x;
00448         cell.y += y;
00449         insertPS(ps, cell);
00450         cells++;
00451     }
00452 
00453     if (Verbose >= 2)
00454         fprintf(stderr, "cc (%d cells) at (%d,%d) (%d,%d)\n", n, x, y,
00455                 place->x, place->y);
00456     return 1;
00457 }
00458 
00459 /* placeFixed:
00460  * Position fixed graph. Store final translation and
00461  * fill polyomino set. Note that polyomino set for the
00462  * graph is constructed where it will be.
00463  */
00464 static void
00465 placeFixed(ginfo * info, PointSet * ps, point * place, point center)
00466 {
00467     point *cells = info->cells;
00468     int n = info->nc;
00469     int i;
00470 
00471     place->x = -center.x;
00472     place->y = -center.y;
00473 
00474     for (i = 0; i < n; i++) {
00475         insertPS(ps, *cells++);
00476     }
00477 
00478     if (Verbose >= 2)
00479         fprintf(stderr, "cc (%d cells) at (%d,%d)\n", n, place->x,
00480                 place->y);
00481 }
00482 
00483 /* placeGraph:
00484  * Search for points on concentric "circles" out
00485  * from the origin. Check if polyomino can be placed
00486  * with bounding box origin at point.
00487  * First graph (i == 0) is centered on the origin if possible.
00488  */
00489 static void
00490 placeGraph(int i, ginfo * info, PointSet * ps, point * place, int step,
00491            int margin, boxf* bbs)
00492 {
00493     int x, y;
00494     int W, H;
00495     int bnd;
00496     boxf bb = bbs[info->index];
00497 
00498     if (i == 0) {
00499         W = GRID(bb.UR.x - bb.LL.x + 2 * margin, step);
00500         H = GRID(bb.UR.y - bb.LL.y + 2 * margin, step);
00501         if (fits(-W / 2, -H / 2, info, ps, place, step, bbs))
00502             return;
00503     }
00504 
00505     if (fits(0, 0, info, ps, place, step, bbs))
00506         return;
00507     W = ceil(bb.UR.x - bb.LL.x);
00508     H = ceil(bb.UR.y - bb.LL.y);
00509     if (W >= H) {
00510         for (bnd = 1;; bnd++) {
00511             x = 0;
00512             y = -bnd;
00513             for (; x < bnd; x++)
00514                 if (fits(x, y, info, ps, place, step, bbs))
00515                     return;
00516             for (; y < bnd; y++)
00517                 if (fits(x, y, info, ps, place, step, bbs))
00518                     return;
00519             for (; x > -bnd; x--)
00520                 if (fits(x, y, info, ps, place, step, bbs))
00521                     return;
00522             for (; y > -bnd; y--)
00523                 if (fits(x, y, info, ps, place, step, bbs))
00524                     return;
00525             for (; x < 0; x++)
00526                 if (fits(x, y, info, ps, place, step, bbs))
00527                     return;
00528         }
00529     } else {
00530         for (bnd = 1;; bnd++) {
00531             y = 0;
00532             x = -bnd;
00533             for (; y > -bnd; y--)
00534                 if (fits(x, y, info, ps, place, step, bbs))
00535                     return;
00536             for (; x < bnd; x++)
00537                 if (fits(x, y, info, ps, place, step, bbs))
00538                     return;
00539             for (; y < bnd; y++)
00540                 if (fits(x, y, info, ps, place, step, bbs))
00541                     return;
00542             for (; x > -bnd; x--)
00543                 if (fits(x, y, info, ps, place, step, bbs))
00544                     return;
00545             for (; y > 0; y--)
00546                 if (fits(x, y, info, ps, place, step, bbs))
00547                     return;
00548         }
00549     }
00550 }
00551 
00552 #ifdef DEBUG
00553 void dumpp(ginfo * info, char *pfx)
00554 {
00555     point *cells = info->cells;
00556     int i, c_cnt = info->nc;
00557 
00558     fprintf(stderr, "%s\n", pfx);
00559     for (i = 0; i < c_cnt; i++) {
00560         fprintf(stderr, "%d %d box\n", cells[i].x, cells[i].y);
00561     }
00562 }
00563 #endif
00564 
00565 static packval_t* userVals;
00566 
00567 /* ucmpf;
00568  * Sort by user values.
00569  */
00570 static int ucmpf(const void *X, const void *Y)
00571 {
00572     ainfo* x = *(ainfo **) X;
00573     ainfo* y = *(ainfo **) Y;
00574 
00575     int dX = userVals[x->index];
00576     int dY = userVals[y->index];
00577     if (dX > dY) return 1;
00578     else if (dX < dY) return -1;
00579     else return 0;
00580 }
00581 
00582 /* acmpf;
00583  * Sort by height + width
00584  */
00585 static int acmpf(const void *X, const void *Y)
00586 {
00587     ainfo* x = *(ainfo **) X;
00588     ainfo* y = *(ainfo **) Y;
00589 #if 0
00590     if (x->height < y->height) return 1;
00591     else if (x->height > y->height) return -1;
00592     else if (x->width < y->width) return 1;
00593     else if (x->width > y->width) return -1;
00594     else return 0;
00595 #endif
00596     double dX = x->height + x->width; 
00597     double dY = y->height + y->width; 
00598     if (dX < dY) return 1;
00599     else if (dX > dY) return -1;
00600     else return 0;
00601 }
00602 
00603 #define INC(m,c,r) \
00604     if (m){ c++; if (c == nc) { c = 0; r++; } } \
00605     else { r++; if (r == nr) { r = 0; c++; } }
00606 
00607 /* arrayRects:
00608  */
00609 static point *
00610 arrayRects (int ng, boxf* gs, pack_info* pinfo)
00611 {
00612     int i;
00613     int nr = 0, nc;
00614     int r, c;
00615     ainfo *info;
00616     ainfo *ip;
00617     ainfo **sinfo;
00618     double* widths;
00619     double* heights;
00620     double v, wd, ht;
00621     point* places = N_NEW(ng, point);
00622     boxf bb;
00623     int sz, rowMajor;
00624 
00625     /* set up no. of rows and columns */
00626     sz = pinfo->sz;
00627     if (pinfo->flags & PK_COL_MAJOR) {
00628         rowMajor = 0;
00629         if (sz > 0) {
00630             nr = sz;
00631             nc = (ng + (nr-1))/nr;
00632         }
00633         else {
00634             nr = ceil(sqrt(ng));
00635             nc = (ng + (nr-1))/nr;
00636         }
00637     }
00638     else {
00639         rowMajor = 1;
00640         if (sz > 0) {
00641             nc = sz;
00642             nr = (ng + (nc-1))/nc;
00643         }
00644         else {
00645             nc = ceil(sqrt(ng));
00646             nr = (ng + (nc-1))/nc;
00647         }
00648     }
00649     widths = N_NEW(nc+1, double);
00650     heights = N_NEW(nr+1, double);
00651 
00652     ip = info = N_NEW(ng, ainfo);
00653     for (i = 0; i < ng; i++, ip++) {
00654         bb = gs[i];
00655         ip->width = bb.UR.x - bb.LL.x + pinfo->margin;
00656         ip->height = bb.UR.y - bb.LL.y + pinfo->margin;
00657         ip->index = i;
00658     }
00659 
00660     sinfo = N_NEW(ng, ainfo*);
00661     for (i = 0; i < ng; i++) {
00662         sinfo[i] = info + i;
00663     }
00664 
00665     if (pinfo->vals) {
00666         userVals = pinfo->vals;
00667         qsort(sinfo, ng, sizeof(ainfo *), ucmpf);
00668     }
00669     else {
00670         qsort(sinfo, ng, sizeof(ainfo *), acmpf);
00671     }
00672 
00673     /* compute column widths and row heights */
00674     r = c = 0;
00675     for (i = 0; i < ng; i++, ip++) {
00676         ip = sinfo[i];
00677         widths[c] = MAX(widths[c],ip->width);
00678         heights[r] = MAX(heights[r],ip->height);
00679         INC(rowMajor,c,r);
00680     }
00681 
00682     /* convert widths and heights to positions */
00683     wd = 0;
00684     for (i = 0; i <= nc; i++) {
00685         v = widths[i];
00686         widths[i] = wd;
00687         wd += v;
00688     }
00689 
00690     ht = 0;
00691     for (i = nr; 0 < i; i--) {
00692         v = heights[i-1];
00693         heights[i] = ht;
00694         ht += v;
00695     }
00696     heights[0] = ht;
00697 
00698     /* position rects */
00699     r = c = 0;
00700     for (i = 0; i < ng; i++, ip++) {
00701         int idx;
00702         ip = sinfo[i];
00703         idx = ip->index;
00704         bb = gs[idx];
00705         if (pinfo->flags & PK_LEFT_ALIGN)
00706             places[idx].x = widths[c];
00707         else if (pinfo->flags & PK_RIGHT_ALIGN)
00708             places[idx].x = widths[c+1] - (bb.UR.x - bb.LL.x);
00709         else
00710             places[idx].x = (widths[c] + widths[c+1] - bb.UR.x - bb.LL.x)/2.0;
00711         if (pinfo->flags & PK_TOP_ALIGN)
00712             places[idx].y = heights[r] - (bb.UR.y - bb.LL.y);
00713         else if (pinfo->flags & PK_BOT_ALIGN)
00714             places[idx].y = heights[r+1];
00715         else
00716             places[idx].y = (heights[r] + heights[r+1] - bb.UR.y - bb.LL.y)/2.0;
00717         INC(rowMajor,c,r);
00718     }
00719 
00720     free (info);
00721     free (sinfo);
00722     free (widths);
00723     free (heights);
00724     return places;
00725 }
00726 
00727 static point*
00728 polyRects(int ng, boxf* gs, pack_info * pinfo)
00729 {
00730     int stepSize;
00731     ginfo *info;
00732     ginfo **sinfo;
00733     point *places;
00734     Dict_t *ps;
00735     int i;
00736     point center;
00737 
00738     /* calculate grid size */
00739     stepSize = computeStep(ng, gs, pinfo->margin);
00740     if (Verbose)
00741         fprintf(stderr, "step size = %d\n", stepSize);
00742     if (stepSize <= 0)
00743         return 0;
00744 
00745     /* generate polyomino cover for the rectangles */
00746     center.x = center.y = 0;
00747     info = N_NEW(ng, ginfo);
00748     for (i = 0; i < ng; i++) {
00749         info[i].index = i;
00750         genBox(gs[i], info + i, stepSize, pinfo->margin, center, "");
00751     }
00752 
00753     /* sort */
00754     sinfo = N_NEW(ng, ginfo *);
00755     for (i = 0; i < ng; i++) {
00756         sinfo[i] = info + i;
00757     }
00758     qsort(sinfo, ng, sizeof(ginfo *), cmpf);
00759 
00760     ps = newPS();
00761     places = N_NEW(ng, point);
00762     for (i = 0; i < ng; i++)
00763         placeGraph(i, sinfo[i], ps, places + (sinfo[i]->index),
00764                        stepSize, pinfo->margin, gs);
00765 
00766     free(sinfo);
00767     for (i = 0; i < ng; i++)
00768         free(info[i].cells);
00769     free(info);
00770     freePS(ps);
00771 
00772     if (Verbose > 1)
00773         for (i = 0; i < ng; i++)
00774             fprintf(stderr, "pos[%d] %d %d\n", i, places[i].x,
00775                     places[i].y);
00776 
00777     return places;
00778 }
00779 
00780 /* polyGraphs:
00781  *  Given a collection of graphs, reposition them in the plane
00782  *  to not overlap but pack "nicely".
00783  *   ng is the number of graphs
00784  *   gs is a pointer to an array of graph pointers
00785  *   root gives the graph containing the edges; if null, the function
00786  *     looks in each graph in gs for its edges
00787  *   pinfo->margin gives the amount of extra space left around nodes in points
00788  *   If pinfo->doSplines is true, use edge splines, if computed,
00789  *     in calculating polyomino.
00790  *   pinfo->mode specifies the packing granularity and technique:
00791  *     l_node : pack at the node/cluster level
00792  *     l_graph : pack at the bounding box level
00793  *  Returns array of points to which graphs should be translated;
00794  *  the array needs to be freed;
00795  * Returns NULL if problem occur or if ng == 0.
00796  * 
00797  * Depends on graph fields bb, node fields pos, xsize and ysize, and
00798  * edge field spl.
00799  *
00800  * FIX: fixed mode does not always work. The fixed ones get translated
00801  * back to be centered on the origin.
00802  * FIX: Check CELL and GRID macros for negative coordinates 
00803  * FIX: Check width and height computation
00804  */
00805 static point*
00806 polyGraphs(int ng, Agraph_t ** gs, Agraph_t * root, pack_info * pinfo)
00807 {
00808     int stepSize;
00809     ginfo *info;
00810     ginfo **sinfo;
00811     point *places;
00812     Dict_t *ps;
00813     int i;
00814     boolean *fixed = pinfo->fixed;
00815     int fixed_cnt = 0;
00816     box bb, fixed_bb = { {0, 0}, {0, 0} };
00817     point center;
00818     boxf* bbs;
00819 
00820     if (ng <= 0)
00821         return 0;
00822 
00823     /* update bounding box info for each graph */
00824     /* If fixed, compute bbox of fixed graphs */
00825     for (i = 0; i < ng; i++) {
00826         Agraph_t *g = gs[i];
00827         compute_bb(g);
00828         if (fixed && fixed[i]) {
00829             BF2B(GD_bb(g), bb);
00830             if (fixed_cnt) {
00831                 fixed_bb.LL.x = MIN(bb.LL.x, fixed_bb.LL.x);
00832                 fixed_bb.LL.y = MIN(bb.LL.y, fixed_bb.LL.y);
00833                 fixed_bb.UR.x = MAX(bb.UR.x, fixed_bb.UR.x);
00834                 fixed_bb.UR.y = MAX(bb.UR.y, fixed_bb.UR.y);
00835             } else
00836                 fixed_bb = bb;
00837             fixed_cnt++;
00838         }
00839         if (Verbose > 2) {
00840             fprintf(stderr, "bb[%s] %.5g %.5g %.5g %.5g\n", agnameof(g),
00841                 GD_bb(g).LL.x, GD_bb(g).LL.y,
00842                 GD_bb(g).UR.x, GD_bb(g).UR.y);
00843         }
00844     }
00845 
00846     /* calculate grid size */
00847     bbs = N_GNEW(ng, boxf);
00848     for (i = 0; i < ng; i++)
00849         bbs[i] = GD_bb(gs[i]);
00850     stepSize = computeStep(ng, bbs, pinfo->margin);
00851     if (Verbose)
00852         fprintf(stderr, "step size = %d\n", stepSize);
00853     if (stepSize <= 0)
00854         return 0;
00855 
00856     /* generate polyomino cover for the graphs */
00857     if (fixed) {
00858         center.x = (fixed_bb.LL.x + fixed_bb.UR.x) / 2;
00859         center.y = (fixed_bb.LL.y + fixed_bb.UR.y) / 2;
00860     } else
00861         center.x = center.y = 0;
00862     info = N_NEW(ng, ginfo);
00863     for (i = 0; i < ng; i++) {
00864         Agraph_t *g = gs[i];
00865         info[i].index = i;
00866         if (pinfo->mode == l_graph)
00867             genBox(GD_bb(g), info + i, stepSize, pinfo->margin, center, agnameof(g));
00868         else if (genPoly(root, gs[i], info + i, stepSize, pinfo, center)) {
00869             return 0;
00870         }
00871     }
00872 
00873     /* sort */
00874     sinfo = N_NEW(ng, ginfo *);
00875     for (i = 0; i < ng; i++) {
00876         sinfo[i] = info + i;
00877     }
00878     qsort(sinfo, ng, sizeof(ginfo *), cmpf);
00879 
00880     ps = newPS();
00881     places = N_NEW(ng, point);
00882     if (fixed) {
00883         for (i = 0; i < ng; i++) {
00884             if (fixed[i])
00885                 placeFixed(sinfo[i], ps, places + (sinfo[i]->index),
00886                            center);
00887         }
00888         for (i = 0; i < ng; i++) {
00889             if (!fixed[i])
00890                 placeGraph(i, sinfo[i], ps, places + (sinfo[i]->index),
00891                            stepSize, pinfo->margin, bbs);
00892         }
00893     } else {
00894         for (i = 0; i < ng; i++)
00895             placeGraph(i, sinfo[i], ps, places + (sinfo[i]->index),
00896                        stepSize, pinfo->margin, bbs);
00897     }
00898 
00899     free(sinfo);
00900     for (i = 0; i < ng; i++)
00901         free(info[i].cells);
00902     free(info);
00903     freePS(ps);
00904     free (bbs);
00905 
00906     if (Verbose > 1)
00907         for (i = 0; i < ng; i++)
00908             fprintf(stderr, "pos[%d] %d %d\n", i, places[i].x,
00909                     places[i].y);
00910 
00911     return places;
00912 }
00913 
00914 point *putGraphs(int ng, Agraph_t ** gs, Agraph_t * root,
00915                  pack_info * pinfo)
00916 {
00917     int i, v;
00918     boxf* bbs;
00919     Agraph_t* g;
00920     point* pts;
00921     char* s;
00922 
00923     if (ng <= 0) return NULL;
00924 
00925     if (pinfo->mode <= l_graph)
00926         return polyGraphs (ng, gs, root, pinfo);
00927     
00928     bbs = N_GNEW(ng, boxf);
00929 
00930     for (i = 0; i < ng; i++) {
00931         g = gs[i];
00932         compute_bb(g);
00933         bbs[i] = GD_bb(g);
00934     }
00935 
00936     if (pinfo->mode == l_array) {
00937         if (pinfo->flags & PK_USER_VALS) {
00938             pinfo->vals = N_NEW(ng, unsigned char);
00939             for (i = 0; i < ng; i++) {
00940                 s = agget (gs[i], "sortv");
00941                 if (s && (sscanf (s, "%d", &v) > 0) && (v >= 0))
00942                     pinfo->vals[i] = v;
00943             }
00944 
00945         }
00946         pts = arrayRects (ng, bbs, pinfo);
00947         if (pinfo->flags & PK_USER_VALS)
00948             free (pinfo->vals);
00949     }
00950 
00951     free (bbs);
00952 
00953     return pts;
00954 }
00955 
00956 point *
00957 putRects(int ng, boxf* bbs, pack_info* pinfo)
00958 {
00959     if (ng <= 0) return NULL;
00960     if ((pinfo->mode == l_node) || (pinfo->mode == l_clust)) return NULL;
00961     if (pinfo->mode == l_graph)
00962         return polyRects (ng, bbs, pinfo);
00963     else if (pinfo->mode == l_array)
00964         return arrayRects (ng, bbs, pinfo);
00965     else
00966         return NULL;
00967 }
00968 
00969 /* packRects:
00970  * Packs rectangles.
00971  *  ng - number of rectangles
00972  *  bbs - array of rectangles
00973  *  info - parameters used in packing
00974  * This decides where to layout the rectangles and repositions 
00975  * the bounding boxes.
00976  *
00977  * Returns 0 on success.
00978  */
00979 int 
00980 packRects(int ng, boxf* bbs, pack_info* pinfo)
00981 {
00982     int i;
00983     point *pp;
00984     boxf bb;
00985     point p;
00986 
00987     if (ng < 0) return -1;
00988     if (ng <= 1) return 0;
00989 
00990     pp = putRects(ng, bbs, pinfo);
00991     if (!pp)
00992         return 1;
00993 
00994     for (i = 0; i < ng; i++) { 
00995         bb = bbs[i];
00996         p = pp[i];
00997         bb.LL.x += p.x;
00998         bb.UR.x += p.x;
00999         bb.LL.y += p.y;
01000         bb.UR.y += p.y;
01001         bbs[i] = bb;
01002     }
01003     free(pp);
01004     return 0;
01005 }
01006 
01007 /* shiftEdge:
01008  * Translate all of the edge components by the given offset.
01009  */
01010 static void shiftEdge(Agedge_t * e, int dx, int dy)
01011 {
01012     int j, k;
01013     bezier bz;
01014 
01015     if (ED_label(e))
01016         MOVEPT(ED_label(e)->pos);
01017     if (ED_head_label(e))
01018         MOVEPT(ED_head_label(e)->pos);
01019     if (ED_tail_label(e))
01020         MOVEPT(ED_tail_label(e)->pos);
01021 
01022     if (ED_spl(e) == NULL)
01023         return;
01024 
01025     for (j = 0; j < ED_spl(e)->size; j++) {
01026         bz = ED_spl(e)->list[j];
01027         for (k = 0; k < bz.size; k++)
01028             MOVEPT(bz.list[k]);
01029         if (bz.sflag)
01030             MOVEPT(ED_spl(e)->list[j].sp);
01031         if (bz.eflag)
01032             MOVEPT(ED_spl(e)->list[j].ep);
01033     }
01034 }
01035 
01036 /* shiftGraph:
01037  */
01038 static void shiftGraph(Agraph_t * g, int dx, int dy)
01039 {
01040     graph_t *subg;
01041     boxf bb = GD_bb(g);
01042     int i;
01043 
01044     bb = GD_bb(g);
01045     bb.LL.x += dx;
01046     bb.UR.x += dx;
01047     bb.LL.y += dy;
01048     bb.UR.y += dy;
01049     GD_bb(g) = bb;
01050 
01051     if (GD_label(g))
01052         MOVEPT(GD_label(g)->pos);
01053 
01054     for (i = 1; i <= GD_n_cluster(g); i++) {
01055         subg = GD_clust(g)[i];
01056         shiftGraph(subg, dx, dy);
01057     }
01058 }
01059 
01060 /* shiftGraphs:
01061  * The function takes ng graphs gs and a similar
01062  * number of points pp and translates each graph so
01063  * that the lower left corner of the bounding box of graph gs[i] is at
01064  * point ps[i]. To do this, it assumes the bb field in
01065  * Agraphinfo_t accurately reflects the current graph layout.
01066  * The graph is repositioned by translating the pos and coord fields of 
01067  * each node appropriately.
01068  * 
01069  * If doSplines is non-zero, the function also translates splines coordinates
01070  * of each edge, if they have been calculated. In addition, edge labels are
01071  * repositioned. 
01072  * 
01073  * If root is non-NULL, it is taken as the root graph of
01074  * the graphs in gs and is used to find the edges. Otherwise, the function
01075  * uses the edges found in each graph gs[i].
01076  * 
01077  * It returns 0 on success.
01078  * 
01079  * This function uses the bb field in Agraphinfo_t,
01080  * the pos and coord fields in nodehinfo_t and
01081  * the spl field in Aedgeinfo_t.
01082  */
01083 int
01084 shiftGraphs(int ng, Agraph_t ** gs, point * pp, Agraph_t * root,
01085             int doSplines)
01086 {
01087     int i;
01088     int dx, dy;
01089     double fx, fy;
01090     point p;
01091     Agraph_t *g;
01092     Agraph_t *eg;
01093     Agnode_t *n;
01094     Agedge_t *e;
01095 
01096     if (ng <= 0)
01097         return abs(ng);
01098 
01099     for (i = 0; i < ng; i++) {
01100         g = gs[i];
01101         if (root)
01102             eg = root;
01103         else
01104             eg = g;
01105         p = pp[i];
01106         dx = p.x;
01107         dy = p.y;
01108         fx = PS2INCH(dx);
01109         fy = PS2INCH(dy);
01110 
01111         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
01112             ND_pos(n)[0] += fx;
01113             ND_pos(n)[1] += fy;
01114             MOVEPT(ND_coord(n));
01115             if (doSplines) {
01116                 for (e = agfstout(eg, n); e; e = agnxtout(eg, e))
01117                     shiftEdge(e, dx, dy);
01118             }
01119         }
01120         shiftGraph(g, dx, dy);
01121     }
01122 
01123     return 0;
01124 }
01125 
01126 /* packGraphs:
01127  * Packs graphs.
01128  *  ng - number of graphs
01129  *  gs - pointer to array of graphs
01130  *  root - graph used to find edges
01131  *  info - parameters used in packing
01132  *  info->doSplines - if true, use already computed spline control points
01133  * This decides where to layout the graphs and repositions the graph's
01134  * position info.
01135  *
01136  * Returns 0 on success.
01137  */
01138 int packGraphs(int ng, Agraph_t ** gs, Agraph_t * root, pack_info * info)
01139 {
01140     int ret;
01141     point *pp = putGraphs(ng, gs, root, info);
01142 
01143     if (!pp)
01144         return 1;
01145     ret = shiftGraphs(ng, gs, pp, root, info->doSplines);
01146     free(pp);
01147     return ret;
01148 }
01149 
01150 /* packSubgraphs:
01151  * Packs subgraphs of given root graph, then recalculates root's bounding box.
01152  * Note that it does not recompute subgraph bounding boxes.
01153  * Cluster bounding boxes are recomputed in shiftGraphs.
01154  */
01155 int
01156 packSubgraphs(int ng, Agraph_t ** gs, Agraph_t * root, pack_info * info)
01157 {
01158     int ret;
01159 
01160     ret = packGraphs(ng, gs, root, info);
01161     if (ret == 0) {
01162         int i, j;
01163         boxf bb;
01164         graph_t* g;
01165 
01166         compute_bb(root);
01167         bb = GD_bb(root);
01168         for (i = 0; i < ng; i++) {
01169             g = gs[i];
01170             for (j = 1; j <= GD_n_cluster(g); j++) {
01171                 EXPANDBB(bb,GD_bb(GD_clust(g)[j]));
01172             }
01173         }
01174         GD_bb(root) = bb;
01175     }
01176     return ret;
01177 }
01178 
01179 /* pack_graph:
01180  * Pack subgraphs followed by postprocessing.
01181  */
01182 int 
01183 pack_graph(int ng, Agraph_t** gs, Agraph_t* root, boolean* fixed)
01184 {
01185     int ret;
01186     pack_info info;
01187 
01188     getPackInfo(root, l_graph, CL_OFFSET, &info);
01189     info.doSplines = 1;
01190     info.fixed = fixed;
01191     ret = packSubgraphs(ng, gs, root, &info);
01192     if (ret == 0) dotneato_postprocess (root);
01193     return ret;
01194 }
01195 
01196 #define ARRAY  "array"
01197 #define ASPECT "aspect"
01198 #define SLEN(s) (sizeof(s)/sizeof(char) - 1)
01199 
01200 static char*
01201 chkFlags (char* p, pack_info* pinfo)
01202 {
01203     int c, more;
01204 
01205     if (*p != '_') return p;
01206     p++;
01207     more = 1;
01208     while (more && (c = *p)) {
01209         switch (c) {
01210         case 'c' :
01211             pinfo->flags |= PK_COL_MAJOR;
01212             p++;
01213             break;
01214         case 'u' :
01215             pinfo->flags |= PK_USER_VALS;
01216             p++;
01217             break;
01218         case 't' :
01219             pinfo->flags |= PK_TOP_ALIGN;
01220             p++;
01221             break;
01222         case 'b' :
01223             pinfo->flags |= PK_BOT_ALIGN;
01224             p++;
01225             break;
01226         case 'l' :
01227             pinfo->flags |= PK_LEFT_ALIGN;
01228             p++;
01229             break;
01230         case 'r' :
01231             pinfo->flags |= PK_RIGHT_ALIGN;
01232             p++;
01233             break;
01234         default :
01235             more = 0;
01236             break;
01237         }
01238     }
01239     return p;
01240 }
01241 
01242 /* parsePackModeInfo;
01243  * Return pack_mode of graph using "packmode" attribute.
01244  * If not defined, return dflt
01245  */
01246 pack_mode 
01247 parsePackModeInfo(char* p, pack_mode dflt, pack_info* pinfo)
01248 {
01249     float v;
01250     int i;
01251 
01252     assert (pinfo);
01253     pinfo->flags = 0;
01254     pinfo->mode = dflt;
01255     pinfo->sz = 0;
01256     pinfo->vals = NULL;
01257     if (p && *p) {
01258         switch (*p) {
01259         case 'a':
01260             if (strneq(p, ARRAY, SLEN(ARRAY))) {
01261                 pinfo->mode = l_array;
01262                 p += SLEN(ARRAY);
01263                 p = chkFlags (p, pinfo);
01264                 if ((sscanf (p, "%d", &i)>0) && (i > 0))
01265                     pinfo->sz = i;
01266             }
01267             else if (strneq(p, ASPECT, SLEN(ASPECT))) {
01268                 pinfo->mode = l_aspect;
01269                 if ((sscanf (p + SLEN(ARRAY), "%f", &v)>0) && (v > 0))
01270                     pinfo->aspect = v;
01271                 else
01272                     pinfo->aspect = 1;
01273             }
01274             break;
01275 #ifdef NOT_IMPLEMENTED
01276         case 'b':
01277             if (streq(p, "bisect"))
01278                 pinfo->mode = l_bisect;
01279             break;
01280 #endif
01281         case 'c':
01282             if (streq(p, "cluster"))
01283                 pinfo->mode = l_clust;
01284             break;
01285         case 'g':
01286             if (streq(p, "graph"))
01287                 pinfo->mode = l_graph;
01288             break;
01289 #ifdef NOT_IMPLEMENTED
01290         case 'h':
01291             if (streq(p, "hull"))
01292                 pinfo->mode = l_hull;
01293             break;
01294 #endif
01295         case 'n':
01296             if (streq(p, "node"))
01297                 pinfo->mode = l_node;
01298             break;
01299 #ifdef NOT_IMPLEMENTED
01300         case 't':
01301             if (streq(p, "tile"))
01302                 pinfo->mode = l_tile;
01303             break;
01304 #endif
01305         }
01306     }
01307 
01308     if (Verbose) {
01309         fprintf (stderr, "pack info:\n");
01310         fprintf (stderr, "  mode   %d\n", pinfo->mode);
01311         if (pinfo->mode == l_aspect)
01312             fprintf (stderr, "  aspect %f\n", pinfo->aspect);
01313         fprintf (stderr, "  size   %d\n", pinfo->sz);
01314         fprintf (stderr, "  flags  %d\n", pinfo->flags);
01315     }
01316     return pinfo->mode;
01317 }
01318 
01319 /* getPackModeInfo;
01320  * Return pack_mode of graph using "packmode" attribute.
01321  * If not defined, return dflt
01322  */
01323 pack_mode 
01324 getPackModeInfo(Agraph_t * g, pack_mode dflt, pack_info* pinfo)
01325 {
01326     return parsePackModeInfo (agget(g, "packmode"), dflt, pinfo);
01327 }
01328 
01329 pack_mode 
01330 getPackMode(Agraph_t * g, pack_mode dflt)
01331 {
01332   pack_info info;
01333   return getPackModeInfo (g, dflt, &info);
01334 }
01335 
01336 /* getPack:
01337  * Return "pack" attribute of g.
01338  * If not defined or negative, return not_def.
01339  * If defined but not specified, return dflt.
01340  */
01341 int getPack(Agraph_t * g, int not_def, int dflt)
01342 {
01343     char *p;
01344     int i;
01345     int v = not_def;
01346 
01347     if ((p = agget(g, "pack"))) {
01348         if ((sscanf(p, "%d", &i) == 1) && (i >= 0))
01349             v = i;
01350         else if ((*p == 't') || (*p == 'T'))
01351             v = dflt;
01352     }
01353 
01354     return v;
01355 }
01356 
01357 pack_mode 
01358 getPackInfo(Agraph_t * g, pack_mode dflt, int dfltMargin, pack_info* pinfo)
01359 {
01360     assert (pinfo);
01361 
01362     pinfo->margin = getPack(g, dfltMargin, dfltMargin);
01363     if (Verbose) {
01364         fprintf (stderr, "  margin %d\n", pinfo->margin);
01365     }
01366     pinfo->doSplines = 0;
01367     pinfo->fixed = 0;
01368     getPackModeInfo(g, dflt, pinfo);
01369 
01370     return pinfo->mode;
01371 }
01372 
01373