|
Graphviz
2.29.20120524.0446
|
00001 /* $Id$ $Revision$ */ 00002 /* vim:set shiftwidth=4 ts=8: */ 00003 00004 /************************************************************************* 00005 * Copyright (c) 2011 AT&T Intellectual Property 00006 * All rights reserved. This program and the accompanying materials 00007 * are made available under the terms of the Eclipse Public License v1.0 00008 * which accompanies this distribution, and is available at 00009 * http://www.eclipse.org/legal/epl-v10.html 00010 * 00011 * Contributors: See CVS logs. Details at http://www.graphviz.org/ 00012 *************************************************************************/ 00013 00014 00015 /* 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
1.7.5