| 1 | /* $Id$ $Revision$ */ |
| 2 | /* vim:set shiftwidth=4 ts=8: */ |
| 3 | |
| 4 | /************************************************************************* |
| 5 | * Copyright (c) 2011 AT&T Intellectual Property |
| 6 | * All rights reserved. This program and the accompanying materials |
| 7 | * are made available under the terms of the Eclipse Public License v1.0 |
| 8 | * which accompanies this distribution, and is available at |
| 9 | * http://www.eclipse.org/legal/epl-v10.html |
| 10 | * |
| 11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
| 12 | *************************************************************************/ |
| 13 | |
| 14 | |
| 15 | /* Module for packing disconnected graphs together. |
| 16 | * Based on "Disconnected Graph Layout and the Polyomino Packing Approach", |
| 17 | * K. Freivalds et al., GD0'01, LNCS 2265, pp. 378-391. |
| 18 | */ |
| 19 | |
| 20 | #include <math.h> |
| 21 | #include <assert.h> |
| 22 | #include "render.h" |
| 23 | #include "pack.h" |
| 24 | #include "pointset.h" |
| 25 | #include <assert.h> |
| 26 | |
| 27 | #define strneq(a,b,n) (!strncmp(a,b,n)) |
| 28 | |
| 29 | #define C 100 /* Max. avg. polyomino size */ |
| 30 | |
| 31 | #define MOVEPT(p) ((p).x += dx, (p).y += dy) |
| 32 | /* Given cell size s, GRID(x:double,s:int) returns how many cells are required by size x */ |
| 33 | #define GRID(x,s) ((int)ceil((x)/(s))) |
| 34 | /* Given grid cell size s, CVAL(v:int,s:int) returns index of cell containing point v */ |
| 35 | #define CVAL(v,s) ((v) >= 0 ? (v)/(s) : (((v)+1)/(s))-1) |
| 36 | /* Given grid cell size s, CELL(p:point,s:int) sets p to cell containing point p */ |
| 37 | #define CELL(p,s) ((p).x = CVAL((p).x,s), (p).y = CVAL((p).y,(s))) |
| 38 | |
| 39 | typedef struct { |
| 40 | int perim; /* half size of bounding rectangle perimeter */ |
| 41 | point *cells; /* cells in covering polyomino */ |
| 42 | int nc; /* no. of cells */ |
| 43 | int index; /* index in original array */ |
| 44 | } ginfo; |
| 45 | |
| 46 | typedef struct { |
| 47 | double width, height; |
| 48 | int index; /* index in original array */ |
| 49 | } ainfo; |
| 50 | |
| 51 | /* computeStep: |
| 52 | * Compute grid step size. This is a root of the |
| 53 | * quadratic equation al^2 +bl + c, where a, b and |
| 54 | * c are defined below. |
| 55 | */ |
| 56 | static int computeStep(int ng, boxf* bbs, int margin) |
| 57 | { |
| 58 | double l1, l2; |
| 59 | double a, b, c, d, r; |
| 60 | double W, H; /* width and height of graph, with margin */ |
| 61 | int i, root; |
| 62 | |
| 63 | a = C * ng - 1; |
| 64 | c = 0; |
| 65 | b = 0; |
| 66 | for (i = 0; i < ng; i++) { |
| 67 | boxf bb = bbs[i]; |
| 68 | W = bb.UR.x - bb.LL.x + 2 * margin; |
| 69 | H = bb.UR.y - bb.LL.y + 2 * margin; |
| 70 | b -= (W + H); |
| 71 | c -= (W * H); |
| 72 | } |
| 73 | d = b * b - 4.0 * a * c; |
| 74 | if (d < 0) { |
| 75 | agerr(AGERR, "libpack: disc = %f ( < 0)\n" , d); |
| 76 | return -1; |
| 77 | } |
| 78 | r = sqrt(d); |
| 79 | l1 = (-b + r) / (2 * a); |
| 80 | l2 = (-b - r) / (2 * a); |
| 81 | root = (int) l1; |
| 82 | if (root == 0) root = 1; |
| 83 | if (Verbose > 2) { |
| 84 | fprintf(stderr, "Packing: compute grid size\n" ); |
| 85 | fprintf(stderr, "a %f b %f c %f d %f r %f\n" , a, b, c, d, r); |
| 86 | fprintf(stderr, "root %d (%f) %d (%f)\n" , root, l1, (int) l2, |
| 87 | l2); |
| 88 | fprintf(stderr, " r1 %f r2 %f\n" , a * l1 * l1 + b * l1 + c, |
| 89 | a * l2 * l2 + b * l2 + c); |
| 90 | } |
| 91 | |
| 92 | return root; |
| 93 | } |
| 94 | |
| 95 | /* cmpf; |
| 96 | * Comparison function for polyominoes. |
| 97 | * Size is determined by perimeter. |
| 98 | */ |
| 99 | static int cmpf(const void *X, const void *Y) |
| 100 | { |
| 101 | ginfo *x = *(ginfo **) X; |
| 102 | ginfo *y = *(ginfo **) Y; |
| 103 | /* flip order to get descending array */ |
| 104 | return (y->perim - x->perim); |
| 105 | } |
| 106 | |
| 107 | /* fillLine: |
| 108 | * Mark cells crossed by line from cell p to cell q. |
| 109 | * Bresenham's algorithm, from Graphics Gems I, pp. 99-100. |
| 110 | */ |
| 111 | /* static */ |
| 112 | void fillLine(pointf p, pointf q, PointSet * ps) |
| 113 | { |
| 114 | int x1 = ROUND(p.x); |
| 115 | int y1 = ROUND(p.y); |
| 116 | int x2 = ROUND(q.x); |
| 117 | int y2 = ROUND(q.y); |
| 118 | int d, x, y, ax, ay, sx, sy, dx, dy; |
| 119 | |
| 120 | dx = x2 - x1; |
| 121 | ax = ABS(dx) << 1; |
| 122 | sx = SGN(dx); |
| 123 | dy = y2 - y1; |
| 124 | ay = ABS(dy) << 1; |
| 125 | sy = SGN(dy); |
| 126 | |
| 127 | /* fprintf (stderr, "fillLine %d %d - %d %d\n", x1,y1,x2,y2); */ |
| 128 | x = x1; |
| 129 | y = y1; |
| 130 | if (ax > ay) { /* x dominant */ |
| 131 | d = ay - (ax >> 1); |
| 132 | for (;;) { |
| 133 | /* fprintf (stderr, " addPS %d %d\n", x,y); */ |
| 134 | addPS(ps, x, y); |
| 135 | if (x == x2) |
| 136 | return; |
| 137 | if (d >= 0) { |
| 138 | y += sy; |
| 139 | d -= ax; |
| 140 | } |
| 141 | x += sx; |
| 142 | d += ay; |
| 143 | } |
| 144 | } else { /* y dominant */ |
| 145 | d = ax - (ay >> 1); |
| 146 | for (;;) { |
| 147 | /* fprintf (stderr, " addPS %d %d\n", x,y); */ |
| 148 | addPS(ps, x, y); |
| 149 | if (y == y2) |
| 150 | return; |
| 151 | if (d >= 0) { |
| 152 | x += sx; |
| 153 | d -= ay; |
| 154 | } |
| 155 | y += sy; |
| 156 | d += ax; |
| 157 | } |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | /* fillEdge: |
| 162 | * It appears that spline_edges always have the start point at the |
| 163 | * beginning and the end point at the end. |
| 164 | */ |
| 165 | static void |
| 166 | fillEdge(Agedge_t * e, point p, PointSet * ps, int dx, int dy, |
| 167 | int ssize, int doS) |
| 168 | { |
| 169 | int j, k; |
| 170 | bezier bz; |
| 171 | pointf pt, hpt; |
| 172 | Agnode_t *h; |
| 173 | |
| 174 | P2PF(p, pt); |
| 175 | |
| 176 | /* If doS is false or the edge has not splines, use line segment */ |
| 177 | if (!doS || !ED_spl(e)) { |
| 178 | h = aghead(e); |
| 179 | hpt = coord(h); |
| 180 | MOVEPT(hpt); |
| 181 | CELL(hpt, ssize); |
| 182 | fillLine(pt, hpt, ps); |
| 183 | return; |
| 184 | } |
| 185 | |
| 186 | for (j = 0; j < ED_spl(e)->size; j++) { |
| 187 | bz = ED_spl(e)->list[j]; |
| 188 | if (bz.sflag) { |
| 189 | pt = bz.sp; |
| 190 | hpt = bz.list[0]; |
| 191 | k = 1; |
| 192 | } else { |
| 193 | pt = bz.list[0]; |
| 194 | hpt = bz.list[1]; |
| 195 | k = 2; |
| 196 | } |
| 197 | MOVEPT(pt); |
| 198 | CELL(pt, ssize); |
| 199 | MOVEPT(hpt); |
| 200 | CELL(hpt, ssize); |
| 201 | fillLine(pt, hpt, ps); |
| 202 | |
| 203 | for (; k < bz.size; k++) { |
| 204 | pt = hpt; |
| 205 | hpt = bz.list[k]; |
| 206 | MOVEPT(hpt); |
| 207 | CELL(hpt, ssize); |
| 208 | fillLine(pt, hpt, ps); |
| 209 | } |
| 210 | |
| 211 | if (bz.eflag) { |
| 212 | pt = hpt; |
| 213 | hpt = bz.ep; |
| 214 | MOVEPT(hpt); |
| 215 | CELL(hpt, ssize); |
| 216 | fillLine(pt, hpt, ps); |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | } |
| 221 | |
| 222 | /* genBox: |
| 223 | * Generate polyomino info from graph using the bounding box of |
| 224 | * the graph. |
| 225 | */ |
| 226 | static void |
| 227 | genBox(boxf bb0, ginfo * info, int ssize, int margin, point center, char* s) |
| 228 | { |
| 229 | PointSet *ps; |
| 230 | int W, H; |
| 231 | point UR, LL; |
| 232 | box bb; |
| 233 | int x, y; |
| 234 | |
| 235 | BF2B(bb0, bb); |
| 236 | ps = newPS(); |
| 237 | |
| 238 | LL.x = center.x - margin; |
| 239 | LL.y = center.y - margin; |
| 240 | UR.x = center.x + bb.UR.x - bb.LL.x + margin; |
| 241 | UR.y = center.y + bb.UR.y - bb.LL.y + margin; |
| 242 | CELL(LL, ssize); |
| 243 | CELL(UR, ssize); |
| 244 | |
| 245 | for (x = LL.x; x <= UR.x; x++) |
| 246 | for (y = LL.y; y <= UR.y; y++) |
| 247 | addPS(ps, x, y); |
| 248 | |
| 249 | info->cells = pointsOf(ps); |
| 250 | info->nc = sizeOf(ps); |
| 251 | W = GRID(bb0.UR.x - bb0.LL.x + 2 * margin, ssize); |
| 252 | H = GRID(bb0.UR.y - bb0.LL.y + 2 * margin, ssize); |
| 253 | info->perim = W + H; |
| 254 | |
| 255 | if (Verbose > 2) { |
| 256 | int i; |
| 257 | fprintf(stderr, "%s no. cells %d W %d H %d\n" , |
| 258 | s, info->nc, W, H); |
| 259 | for (i = 0; i < info->nc; i++) |
| 260 | fprintf(stderr, " %d %d cell\n" , info->cells[i].x, |
| 261 | info->cells[i].y); |
| 262 | } |
| 263 | |
| 264 | freePS(ps); |
| 265 | } |
| 266 | |
| 267 | /* genPoly: |
| 268 | * Generate polyomino info from graph. |
| 269 | * We add all cells covered partially by the bounding box of the |
| 270 | * node. If doSplines is true and an edge has a spline, we use the |
| 271 | * polyline determined by the control point. Otherwise, |
| 272 | * we use each cell crossed by a straight edge between the head and tail. |
| 273 | * If mode = l_clust, we use the graph's GD_clust array to treat the |
| 274 | * top level clusters like large nodes. |
| 275 | * Returns 0 if okay. |
| 276 | */ |
| 277 | static int |
| 278 | genPoly(Agraph_t * root, Agraph_t * g, ginfo * info, |
| 279 | int ssize, pack_info * pinfo, point center) |
| 280 | { |
| 281 | PointSet *ps; |
| 282 | int W, H; |
| 283 | point LL, UR; |
| 284 | point pt, s2; |
| 285 | pointf ptf; |
| 286 | Agraph_t *eg; /* graph containing edges */ |
| 287 | Agnode_t *n; |
| 288 | Agedge_t *e; |
| 289 | int x, y; |
| 290 | int dx, dy; |
| 291 | graph_t *subg; |
| 292 | int margin = pinfo->margin; |
| 293 | int doSplines = pinfo->doSplines; |
| 294 | box bb; |
| 295 | |
| 296 | if (root) |
| 297 | eg = root; |
| 298 | else |
| 299 | eg = g; |
| 300 | |
| 301 | ps = newPS(); |
| 302 | dx = center.x - ROUND(GD_bb(g).LL.x); |
| 303 | dy = center.y - ROUND(GD_bb(g).LL.y); |
| 304 | |
| 305 | if (pinfo->mode == l_clust) { |
| 306 | int i; |
| 307 | void **alg; |
| 308 | |
| 309 | /* backup the alg data */ |
| 310 | alg = N_GNEW(agnnodes(g), void *); |
| 311 | for (i = 0, n = agfstnode(g); n; n = agnxtnode(g, n)) { |
| 312 | alg[i++] = ND_alg(n); |
| 313 | ND_alg(n) = 0; |
| 314 | } |
| 315 | |
| 316 | /* do bbox of top clusters */ |
| 317 | for (i = 1; i <= GD_n_cluster(g); i++) { |
| 318 | subg = GD_clust(g)[i]; |
| 319 | BF2B(GD_bb(subg), bb); |
| 320 | if ((bb.UR.x > bb.LL.x) && (bb.UR.y > bb.LL.y)) { |
| 321 | MOVEPT(bb.LL); |
| 322 | MOVEPT(bb.UR); |
| 323 | bb.LL.x -= margin; |
| 324 | bb.LL.y -= margin; |
| 325 | bb.UR.x += margin; |
| 326 | bb.UR.y += margin; |
| 327 | CELL(bb.LL, ssize); |
| 328 | CELL(bb.UR, ssize); |
| 329 | |
| 330 | for (x = bb.LL.x; x <= bb.UR.x; x++) |
| 331 | for (y = bb.LL.y; y <= bb.UR.y; y++) |
| 332 | addPS(ps, x, y); |
| 333 | |
| 334 | /* note which nodes are in clusters */ |
| 335 | for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) |
| 336 | ND_clust(n) = subg; |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | /* now do remaining nodes and edges */ |
| 341 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { |
| 342 | ptf = coord(n); |
| 343 | PF2P(ptf, pt); |
| 344 | MOVEPT(pt); |
| 345 | if (!ND_clust(n)) { /* n is not in a top-level cluster */ |
| 346 | s2.x = margin + ND_xsize(n) / 2; |
| 347 | s2.y = margin + ND_ysize(n) / 2; |
| 348 | LL = sub_point(pt, s2); |
| 349 | UR = add_point(pt, s2); |
| 350 | CELL(LL, ssize); |
| 351 | CELL(UR, ssize); |
| 352 | |
| 353 | for (x = LL.x; x <= UR.x; x++) |
| 354 | for (y = LL.y; y <= UR.y; y++) |
| 355 | addPS(ps, x, y); |
| 356 | |
| 357 | CELL(pt, ssize); |
| 358 | for (e = agfstout(eg, n); e; e = agnxtout(eg, e)) { |
| 359 | fillEdge(e, pt, ps, dx, dy, ssize, doSplines); |
| 360 | } |
| 361 | } else { |
| 362 | CELL(pt, ssize); |
| 363 | for (e = agfstout(eg, n); e; e = agnxtout(eg, e)) { |
| 364 | if (ND_clust(n) == ND_clust(aghead(e))) |
| 365 | continue; |
| 366 | fillEdge(e, pt, ps, dx, dy, ssize, doSplines); |
| 367 | } |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | /* restore the alg data */ |
| 372 | for (i = 0, n = agfstnode(g); n; n = agnxtnode(g, n)) { |
| 373 | ND_alg(n)= alg[i++]; |
| 374 | } |
| 375 | free(alg); |
| 376 | |
| 377 | } else |
| 378 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { |
| 379 | ptf = coord(n); |
| 380 | PF2P(ptf, pt); |
| 381 | MOVEPT(pt); |
| 382 | s2.x = margin + ND_xsize(n) / 2; |
| 383 | s2.y = margin + ND_ysize(n) / 2; |
| 384 | LL = sub_point(pt, s2); |
| 385 | UR = add_point(pt, s2); |
| 386 | CELL(LL, ssize); |
| 387 | CELL(UR, ssize); |
| 388 | |
| 389 | for (x = LL.x; x <= UR.x; x++) |
| 390 | for (y = LL.y; y <= UR.y; y++) |
| 391 | addPS(ps, x, y); |
| 392 | |
| 393 | CELL(pt, ssize); |
| 394 | for (e = agfstout(eg, n); e; e = agnxtout(eg, e)) { |
| 395 | fillEdge(e, pt, ps, dx, dy, ssize, doSplines); |
| 396 | } |
| 397 | } |
| 398 | |
| 399 | info->cells = pointsOf(ps); |
| 400 | info->nc = sizeOf(ps); |
| 401 | W = GRID(GD_bb(g).UR.x - GD_bb(g).LL.x + 2 * margin, ssize); |
| 402 | H = GRID(GD_bb(g).UR.y - GD_bb(g).LL.y + 2 * margin, ssize); |
| 403 | info->perim = W + H; |
| 404 | |
| 405 | if (Verbose > 2) { |
| 406 | int i; |
| 407 | fprintf(stderr, "%s no. cells %d W %d H %d\n" , |
| 408 | agnameof(g), info->nc, W, H); |
| 409 | for (i = 0; i < info->nc; i++) |
| 410 | fprintf(stderr, " %d %d cell\n" , info->cells[i].x, |
| 411 | info->cells[i].y); |
| 412 | } |
| 413 | |
| 414 | freePS(ps); |
| 415 | return 0; |
| 416 | } |
| 417 | |
| 418 | /* fits: |
| 419 | * Check if polyomino fits at given point. |
| 420 | * If so, add cells to pointset, store point in place and return true. |
| 421 | */ |
| 422 | static int |
| 423 | fits(int x, int y, ginfo * info, PointSet * ps, point * place, int step, boxf* bbs) |
| 424 | { |
| 425 | point *cells = info->cells; |
| 426 | int n = info->nc; |
| 427 | point cell; |
| 428 | int i; |
| 429 | point LL; |
| 430 | |
| 431 | for (i = 0; i < n; i++) { |
| 432 | cell = *cells; |
| 433 | cell.x += x; |
| 434 | cell.y += y; |
| 435 | if (inPS(ps, cell)) |
| 436 | return 0; |
| 437 | cells++; |
| 438 | } |
| 439 | |
| 440 | PF2P(bbs[info->index].LL, LL); |
| 441 | place->x = step * x - LL.x; |
| 442 | place->y = step * y - LL.y; |
| 443 | |
| 444 | cells = info->cells; |
| 445 | for (i = 0; i < n; i++) { |
| 446 | cell = *cells; |
| 447 | cell.x += x; |
| 448 | cell.y += y; |
| 449 | insertPS(ps, cell); |
| 450 | cells++; |
| 451 | } |
| 452 | |
| 453 | if (Verbose >= 2) |
| 454 | fprintf(stderr, "cc (%d cells) at (%d,%d) (%d,%d)\n" , n, x, y, |
| 455 | place->x, place->y); |
| 456 | return 1; |
| 457 | } |
| 458 | |
| 459 | /* placeFixed: |
| 460 | * Position fixed graph. Store final translation and |
| 461 | * fill polyomino set. Note that polyomino set for the |
| 462 | * graph is constructed where it will be. |
| 463 | */ |
| 464 | static void |
| 465 | placeFixed(ginfo * info, PointSet * ps, point * place, point center) |
| 466 | { |
| 467 | point *cells = info->cells; |
| 468 | int n = info->nc; |
| 469 | int i; |
| 470 | |
| 471 | place->x = -center.x; |
| 472 | place->y = -center.y; |
| 473 | |
| 474 | for (i = 0; i < n; i++) { |
| 475 | insertPS(ps, *cells++); |
| 476 | } |
| 477 | |
| 478 | if (Verbose >= 2) |
| 479 | fprintf(stderr, "cc (%d cells) at (%d,%d)\n" , n, place->x, |
| 480 | place->y); |
| 481 | } |
| 482 | |
| 483 | /* placeGraph: |
| 484 | * Search for points on concentric "circles" out |
| 485 | * from the origin. Check if polyomino can be placed |
| 486 | * with bounding box origin at point. |
| 487 | * First graph (i == 0) is centered on the origin if possible. |
| 488 | */ |
| 489 | static void |
| 490 | placeGraph(int i, ginfo * info, PointSet * ps, point * place, int step, |
| 491 | int margin, boxf* bbs) |
| 492 | { |
| 493 | int x, y; |
| 494 | int W, H; |
| 495 | int bnd; |
| 496 | boxf bb = bbs[info->index]; |
| 497 | |
| 498 | if (i == 0) { |
| 499 | W = GRID(bb.UR.x - bb.LL.x + 2 * margin, step); |
| 500 | H = GRID(bb.UR.y - bb.LL.y + 2 * margin, step); |
| 501 | if (fits(-W / 2, -H / 2, info, ps, place, step, bbs)) |
| 502 | return; |
| 503 | } |
| 504 | |
| 505 | if (fits(0, 0, info, ps, place, step, bbs)) |
| 506 | return; |
| 507 | W = ceil(bb.UR.x - bb.LL.x); |
| 508 | H = ceil(bb.UR.y - bb.LL.y); |
| 509 | if (W >= H) { |
| 510 | for (bnd = 1;; bnd++) { |
| 511 | x = 0; |
| 512 | y = -bnd; |
| 513 | for (; x < bnd; x++) |
| 514 | if (fits(x, y, info, ps, place, step, bbs)) |
| 515 | return; |
| 516 | for (; y < bnd; y++) |
| 517 | if (fits(x, y, info, ps, place, step, bbs)) |
| 518 | return; |
| 519 | for (; x > -bnd; x--) |
| 520 | if (fits(x, y, info, ps, place, step, bbs)) |
| 521 | return; |
| 522 | for (; y > -bnd; y--) |
| 523 | if (fits(x, y, info, ps, place, step, bbs)) |
| 524 | return; |
| 525 | for (; x < 0; x++) |
| 526 | if (fits(x, y, info, ps, place, step, bbs)) |
| 527 | return; |
| 528 | } |
| 529 | } else { |
| 530 | for (bnd = 1;; bnd++) { |
| 531 | y = 0; |
| 532 | x = -bnd; |
| 533 | for (; y > -bnd; y--) |
| 534 | if (fits(x, y, info, ps, place, step, bbs)) |
| 535 | return; |
| 536 | for (; x < bnd; x++) |
| 537 | if (fits(x, y, info, ps, place, step, bbs)) |
| 538 | return; |
| 539 | for (; y < bnd; y++) |
| 540 | if (fits(x, y, info, ps, place, step, bbs)) |
| 541 | return; |
| 542 | for (; x > -bnd; x--) |
| 543 | if (fits(x, y, info, ps, place, step, bbs)) |
| 544 | return; |
| 545 | for (; y > 0; y--) |
| 546 | if (fits(x, y, info, ps, place, step, bbs)) |
| 547 | return; |
| 548 | } |
| 549 | } |
| 550 | } |
| 551 | |
| 552 | #ifdef DEBUG |
| 553 | void dumpp(ginfo * info, char *pfx) |
| 554 | { |
| 555 | point *cells = info->cells; |
| 556 | int i, c_cnt = info->nc; |
| 557 | |
| 558 | fprintf(stderr, "%s\n" , pfx); |
| 559 | for (i = 0; i < c_cnt; i++) { |
| 560 | fprintf(stderr, "%d %d box\n" , cells[i].x, cells[i].y); |
| 561 | } |
| 562 | } |
| 563 | #endif |
| 564 | |
| 565 | static packval_t* userVals; |
| 566 | |
| 567 | /* ucmpf; |
| 568 | * Sort by user values. |
| 569 | */ |
| 570 | static int ucmpf(const void *X, const void *Y) |
| 571 | { |
| 572 | ainfo* x = *(ainfo **) X; |
| 573 | ainfo* y = *(ainfo **) Y; |
| 574 | |
| 575 | int dX = userVals[x->index]; |
| 576 | int dY = userVals[y->index]; |
| 577 | if (dX > dY) return 1; |
| 578 | else if (dX < dY) return -1; |
| 579 | else return 0; |
| 580 | } |
| 581 | |
| 582 | /* acmpf; |
| 583 | * Sort by height + width |
| 584 | */ |
| 585 | static int acmpf(const void *X, const void *Y) |
| 586 | { |
| 587 | ainfo* x = *(ainfo **) X; |
| 588 | ainfo* y = *(ainfo **) Y; |
| 589 | #if 0 |
| 590 | if (x->height < y->height) return 1; |
| 591 | else if (x->height > y->height) return -1; |
| 592 | else if (x->width < y->width) return 1; |
| 593 | else if (x->width > y->width) return -1; |
| 594 | else return 0; |
| 595 | #endif |
| 596 | double dX = x->height + x->width; |
| 597 | double dY = y->height + y->width; |
| 598 | if (dX < dY) return 1; |
| 599 | else if (dX > dY) return -1; |
| 600 | else return 0; |
| 601 | } |
| 602 | |
| 603 | #define INC(m,c,r) \ |
| 604 | if (m){ c++; if (c == nc) { c = 0; r++; } } \ |
| 605 | else { r++; if (r == nr) { r = 0; c++; } } |
| 606 | |
| 607 | /* arrayRects: |
| 608 | */ |
| 609 | static point * |
| 610 | arrayRects (int ng, boxf* gs, pack_info* pinfo) |
| 611 | { |
| 612 | int i; |
| 613 | int nr = 0, nc; |
| 614 | int r, c; |
| 615 | ainfo *info; |
| 616 | ainfo *ip; |
| 617 | ainfo **sinfo; |
| 618 | double* widths; |
| 619 | double* heights; |
| 620 | double v, wd, ht; |
| 621 | point* places = N_NEW(ng, point); |
| 622 | boxf bb; |
| 623 | int sz, rowMajor; |
| 624 | |
| 625 | /* set up no. of rows and columns */ |
| 626 | sz = pinfo->sz; |
| 627 | if (pinfo->flags & PK_COL_MAJOR) { |
| 628 | rowMajor = 0; |
| 629 | if (sz > 0) { |
| 630 | nr = sz; |
| 631 | nc = (ng + (nr-1))/nr; |
| 632 | } |
| 633 | else { |
| 634 | nr = ceil(sqrt(ng)); |
| 635 | nc = (ng + (nr-1))/nr; |
| 636 | } |
| 637 | } |
| 638 | else { |
| 639 | rowMajor = 1; |
| 640 | if (sz > 0) { |
| 641 | nc = sz; |
| 642 | nr = (ng + (nc-1))/nc; |
| 643 | } |
| 644 | else { |
| 645 | nc = ceil(sqrt(ng)); |
| 646 | nr = (ng + (nc-1))/nc; |
| 647 | } |
| 648 | } |
| 649 | if (Verbose) |
| 650 | fprintf (stderr, "array packing: %s %d rows %d columns\n" , (rowMajor?"row major" :"column major" ), nr, nc); |
| 651 | widths = N_NEW(nc+1, double); |
| 652 | heights = N_NEW(nr+1, double); |
| 653 | |
| 654 | ip = info = N_NEW(ng, ainfo); |
| 655 | for (i = 0; i < ng; i++, ip++) { |
| 656 | bb = gs[i]; |
| 657 | ip->width = bb.UR.x - bb.LL.x + pinfo->margin; |
| 658 | ip->height = bb.UR.y - bb.LL.y + pinfo->margin; |
| 659 | ip->index = i; |
| 660 | } |
| 661 | |
| 662 | sinfo = N_NEW(ng, ainfo*); |
| 663 | for (i = 0; i < ng; i++) { |
| 664 | sinfo[i] = info + i; |
| 665 | } |
| 666 | |
| 667 | if (pinfo->vals) { |
| 668 | userVals = pinfo->vals; |
| 669 | qsort(sinfo, ng, sizeof(ainfo *), ucmpf); |
| 670 | } |
| 671 | else if (!(pinfo->flags & PK_INPUT_ORDER)) { |
| 672 | qsort(sinfo, ng, sizeof(ainfo *), acmpf); |
| 673 | } |
| 674 | |
| 675 | /* compute column widths and row heights */ |
| 676 | r = c = 0; |
| 677 | for (i = 0; i < ng; i++, ip++) { |
| 678 | ip = sinfo[i]; |
| 679 | widths[c] = MAX(widths[c],ip->width); |
| 680 | heights[r] = MAX(heights[r],ip->height); |
| 681 | INC(rowMajor,c,r); |
| 682 | } |
| 683 | |
| 684 | /* convert widths and heights to positions */ |
| 685 | wd = 0; |
| 686 | for (i = 0; i <= nc; i++) { |
| 687 | v = widths[i]; |
| 688 | widths[i] = wd; |
| 689 | wd += v; |
| 690 | } |
| 691 | |
| 692 | ht = 0; |
| 693 | for (i = nr; 0 < i; i--) { |
| 694 | v = heights[i-1]; |
| 695 | heights[i] = ht; |
| 696 | ht += v; |
| 697 | } |
| 698 | heights[0] = ht; |
| 699 | |
| 700 | /* position rects */ |
| 701 | r = c = 0; |
| 702 | for (i = 0; i < ng; i++, ip++) { |
| 703 | int idx; |
| 704 | ip = sinfo[i]; |
| 705 | idx = ip->index; |
| 706 | bb = gs[idx]; |
| 707 | if (pinfo->flags & PK_LEFT_ALIGN) |
| 708 | places[idx].x = widths[c]; |
| 709 | else if (pinfo->flags & PK_RIGHT_ALIGN) |
| 710 | places[idx].x = widths[c+1] - (bb.UR.x - bb.LL.x); |
| 711 | else |
| 712 | places[idx].x = (widths[c] + widths[c+1] - bb.UR.x - bb.LL.x)/2.0; |
| 713 | if (pinfo->flags & PK_TOP_ALIGN) |
| 714 | places[idx].y = heights[r] - (bb.UR.y - bb.LL.y); |
| 715 | else if (pinfo->flags & PK_BOT_ALIGN) |
| 716 | places[idx].y = heights[r+1]; |
| 717 | else |
| 718 | places[idx].y = (heights[r] + heights[r+1] - bb.UR.y - bb.LL.y)/2.0; |
| 719 | INC(rowMajor,c,r); |
| 720 | } |
| 721 | |
| 722 | free (info); |
| 723 | free (sinfo); |
| 724 | free (widths); |
| 725 | free (heights); |
| 726 | return places; |
| 727 | } |
| 728 | |
| 729 | static point* |
| 730 | polyRects(int ng, boxf* gs, pack_info * pinfo) |
| 731 | { |
| 732 | int stepSize; |
| 733 | ginfo *info; |
| 734 | ginfo **sinfo; |
| 735 | point *places; |
| 736 | Dict_t *ps; |
| 737 | int i; |
| 738 | point center; |
| 739 | |
| 740 | /* calculate grid size */ |
| 741 | stepSize = computeStep(ng, gs, pinfo->margin); |
| 742 | if (Verbose) |
| 743 | fprintf(stderr, "step size = %d\n" , stepSize); |
| 744 | if (stepSize <= 0) |
| 745 | return 0; |
| 746 | |
| 747 | /* generate polyomino cover for the rectangles */ |
| 748 | center.x = center.y = 0; |
| 749 | info = N_NEW(ng, ginfo); |
| 750 | for (i = 0; i < ng; i++) { |
| 751 | info[i].index = i; |
| 752 | genBox(gs[i], info + i, stepSize, pinfo->margin, center, "" ); |
| 753 | } |
| 754 | |
| 755 | /* sort */ |
| 756 | sinfo = N_NEW(ng, ginfo *); |
| 757 | for (i = 0; i < ng; i++) { |
| 758 | sinfo[i] = info + i; |
| 759 | } |
| 760 | qsort(sinfo, ng, sizeof(ginfo *), cmpf); |
| 761 | |
| 762 | ps = newPS(); |
| 763 | places = N_NEW(ng, point); |
| 764 | for (i = 0; i < ng; i++) |
| 765 | placeGraph(i, sinfo[i], ps, places + (sinfo[i]->index), |
| 766 | stepSize, pinfo->margin, gs); |
| 767 | |
| 768 | free(sinfo); |
| 769 | for (i = 0; i < ng; i++) |
| 770 | free(info[i].cells); |
| 771 | free(info); |
| 772 | freePS(ps); |
| 773 | |
| 774 | if (Verbose > 1) |
| 775 | for (i = 0; i < ng; i++) |
| 776 | fprintf(stderr, "pos[%d] %d %d\n" , i, places[i].x, |
| 777 | places[i].y); |
| 778 | |
| 779 | return places; |
| 780 | } |
| 781 | |
| 782 | /* polyGraphs: |
| 783 | * Given a collection of graphs, reposition them in the plane |
| 784 | * to not overlap but pack "nicely". |
| 785 | * ng is the number of graphs |
| 786 | * gs is a pointer to an array of graph pointers |
| 787 | * root gives the graph containing the edges; if null, the function |
| 788 | * looks in each graph in gs for its edges |
| 789 | * pinfo->margin gives the amount of extra space left around nodes in points |
| 790 | * If pinfo->doSplines is true, use edge splines, if computed, |
| 791 | * in calculating polyomino. |
| 792 | * pinfo->mode specifies the packing granularity and technique: |
| 793 | * l_node : pack at the node/cluster level |
| 794 | * l_graph : pack at the bounding box level |
| 795 | * Returns array of points to which graphs should be translated; |
| 796 | * the array needs to be freed; |
| 797 | * Returns NULL if problem occur or if ng == 0. |
| 798 | * |
| 799 | * Depends on graph fields GD_bb, node fields ND_pos(inches), ND_xsize and |
| 800 | * ND_ysize, and edge field ED_spl. |
| 801 | * |
| 802 | * FIX: fixed mode does not always work. The fixed ones get translated |
| 803 | * back to be centered on the origin. |
| 804 | * FIX: Check CELL and GRID macros for negative coordinates |
| 805 | * FIX: Check width and height computation |
| 806 | */ |
| 807 | static point* |
| 808 | polyGraphs(int ng, Agraph_t ** gs, Agraph_t * root, pack_info * pinfo) |
| 809 | { |
| 810 | int stepSize; |
| 811 | ginfo *info; |
| 812 | ginfo **sinfo; |
| 813 | point *places; |
| 814 | Dict_t *ps; |
| 815 | int i; |
| 816 | boolean *fixed = pinfo->fixed; |
| 817 | int fixed_cnt = 0; |
| 818 | box bb, fixed_bb = { {0, 0}, {0, 0} }; |
| 819 | point center; |
| 820 | boxf* bbs; |
| 821 | |
| 822 | if (ng <= 0) |
| 823 | return 0; |
| 824 | |
| 825 | /* update bounding box info for each graph */ |
| 826 | /* If fixed, compute bbox of fixed graphs */ |
| 827 | for (i = 0; i < ng; i++) { |
| 828 | Agraph_t *g = gs[i]; |
| 829 | compute_bb(g); |
| 830 | if (fixed && fixed[i]) { |
| 831 | BF2B(GD_bb(g), bb); |
| 832 | if (fixed_cnt) { |
| 833 | fixed_bb.LL.x = MIN(bb.LL.x, fixed_bb.LL.x); |
| 834 | fixed_bb.LL.y = MIN(bb.LL.y, fixed_bb.LL.y); |
| 835 | fixed_bb.UR.x = MAX(bb.UR.x, fixed_bb.UR.x); |
| 836 | fixed_bb.UR.y = MAX(bb.UR.y, fixed_bb.UR.y); |
| 837 | } else |
| 838 | fixed_bb = bb; |
| 839 | fixed_cnt++; |
| 840 | } |
| 841 | if (Verbose > 2) { |
| 842 | fprintf(stderr, "bb[%s] %.5g %.5g %.5g %.5g\n" , agnameof(g), |
| 843 | GD_bb(g).LL.x, GD_bb(g).LL.y, |
| 844 | GD_bb(g).UR.x, GD_bb(g).UR.y); |
| 845 | } |
| 846 | } |
| 847 | |
| 848 | /* calculate grid size */ |
| 849 | bbs = N_GNEW(ng, boxf); |
| 850 | for (i = 0; i < ng; i++) |
| 851 | bbs[i] = GD_bb(gs[i]); |
| 852 | stepSize = computeStep(ng, bbs, pinfo->margin); |
| 853 | if (Verbose) |
| 854 | fprintf(stderr, "step size = %d\n" , stepSize); |
| 855 | if (stepSize <= 0) |
| 856 | return 0; |
| 857 | |
| 858 | /* generate polyomino cover for the graphs */ |
| 859 | if (fixed) { |
| 860 | center.x = (fixed_bb.LL.x + fixed_bb.UR.x) / 2; |
| 861 | center.y = (fixed_bb.LL.y + fixed_bb.UR.y) / 2; |
| 862 | } else |
| 863 | center.x = center.y = 0; |
| 864 | info = N_NEW(ng, ginfo); |
| 865 | for (i = 0; i < ng; i++) { |
| 866 | Agraph_t *g = gs[i]; |
| 867 | info[i].index = i; |
| 868 | if (pinfo->mode == l_graph) |
| 869 | genBox(GD_bb(g), info + i, stepSize, pinfo->margin, center, agnameof(g)); |
| 870 | else if (genPoly(root, gs[i], info + i, stepSize, pinfo, center)) { |
| 871 | return 0; |
| 872 | } |
| 873 | } |
| 874 | |
| 875 | /* sort */ |
| 876 | sinfo = N_NEW(ng, ginfo *); |
| 877 | for (i = 0; i < ng; i++) { |
| 878 | sinfo[i] = info + i; |
| 879 | } |
| 880 | qsort(sinfo, ng, sizeof(ginfo *), cmpf); |
| 881 | |
| 882 | ps = newPS(); |
| 883 | places = N_NEW(ng, point); |
| 884 | if (fixed) { |
| 885 | for (i = 0; i < ng; i++) { |
| 886 | if (fixed[i]) |
| 887 | placeFixed(sinfo[i], ps, places + (sinfo[i]->index), |
| 888 | center); |
| 889 | } |
| 890 | for (i = 0; i < ng; i++) { |
| 891 | if (!fixed[i]) |
| 892 | placeGraph(i, sinfo[i], ps, places + (sinfo[i]->index), |
| 893 | stepSize, pinfo->margin, bbs); |
| 894 | } |
| 895 | } else { |
| 896 | for (i = 0; i < ng; i++) |
| 897 | placeGraph(i, sinfo[i], ps, places + (sinfo[i]->index), |
| 898 | stepSize, pinfo->margin, bbs); |
| 899 | } |
| 900 | |
| 901 | free(sinfo); |
| 902 | for (i = 0; i < ng; i++) |
| 903 | free(info[i].cells); |
| 904 | free(info); |
| 905 | freePS(ps); |
| 906 | free (bbs); |
| 907 | |
| 908 | if (Verbose > 1) |
| 909 | for (i = 0; i < ng; i++) |
| 910 | fprintf(stderr, "pos[%d] %d %d\n" , i, places[i].x, |
| 911 | places[i].y); |
| 912 | |
| 913 | return places; |
| 914 | } |
| 915 | |
| 916 | point *putGraphs(int ng, Agraph_t ** gs, Agraph_t * root, |
| 917 | pack_info * pinfo) |
| 918 | { |
| 919 | int i, v; |
| 920 | boxf* bbs; |
| 921 | Agraph_t* g; |
| 922 | point* pts = NULL; |
| 923 | char* s; |
| 924 | |
| 925 | if (ng <= 0) return NULL; |
| 926 | |
| 927 | if (pinfo->mode <= l_graph) |
| 928 | return polyGraphs (ng, gs, root, pinfo); |
| 929 | |
| 930 | bbs = N_GNEW(ng, boxf); |
| 931 | |
| 932 | for (i = 0; i < ng; i++) { |
| 933 | g = gs[i]; |
| 934 | compute_bb(g); |
| 935 | bbs[i] = GD_bb(g); |
| 936 | } |
| 937 | |
| 938 | if (pinfo->mode == l_array) { |
| 939 | if (pinfo->flags & PK_USER_VALS) { |
| 940 | pinfo->vals = N_NEW(ng, packval_t); |
| 941 | for (i = 0; i < ng; i++) { |
| 942 | s = agget (gs[i], "sortv" ); |
| 943 | if (s && (sscanf (s, "%d" , &v) > 0) && (v >= 0)) |
| 944 | pinfo->vals[i] = v; |
| 945 | } |
| 946 | |
| 947 | } |
| 948 | pts = arrayRects (ng, bbs, pinfo); |
| 949 | if (pinfo->flags & PK_USER_VALS) |
| 950 | free (pinfo->vals); |
| 951 | } |
| 952 | |
| 953 | free (bbs); |
| 954 | |
| 955 | return pts; |
| 956 | } |
| 957 | |
| 958 | point * |
| 959 | putRects(int ng, boxf* bbs, pack_info* pinfo) |
| 960 | { |
| 961 | if (ng <= 0) return NULL; |
| 962 | if ((pinfo->mode == l_node) || (pinfo->mode == l_clust)) return NULL; |
| 963 | if (pinfo->mode == l_graph) |
| 964 | return polyRects (ng, bbs, pinfo); |
| 965 | else if (pinfo->mode == l_array) |
| 966 | return arrayRects (ng, bbs, pinfo); |
| 967 | else |
| 968 | return NULL; |
| 969 | } |
| 970 | |
| 971 | /* packRects: |
| 972 | * Packs rectangles. |
| 973 | * ng - number of rectangles |
| 974 | * bbs - array of rectangles |
| 975 | * info - parameters used in packing |
| 976 | * This decides where to layout the rectangles and repositions |
| 977 | * the bounding boxes. |
| 978 | * |
| 979 | * Returns 0 on success. |
| 980 | */ |
| 981 | int |
| 982 | packRects(int ng, boxf* bbs, pack_info* pinfo) |
| 983 | { |
| 984 | int i; |
| 985 | point *pp; |
| 986 | boxf bb; |
| 987 | point p; |
| 988 | |
| 989 | if (ng < 0) return -1; |
| 990 | if (ng <= 1) return 0; |
| 991 | |
| 992 | pp = putRects(ng, bbs, pinfo); |
| 993 | if (!pp) |
| 994 | return 1; |
| 995 | |
| 996 | for (i = 0; i < ng; i++) { |
| 997 | bb = bbs[i]; |
| 998 | p = pp[i]; |
| 999 | bb.LL.x += p.x; |
| 1000 | bb.UR.x += p.x; |
| 1001 | bb.LL.y += p.y; |
| 1002 | bb.UR.y += p.y; |
| 1003 | bbs[i] = bb; |
| 1004 | } |
| 1005 | free(pp); |
| 1006 | return 0; |
| 1007 | } |
| 1008 | |
| 1009 | /* shiftEdge: |
| 1010 | * Translate all of the edge components by the given offset. |
| 1011 | */ |
| 1012 | static void shiftEdge(Agedge_t * e, int dx, int dy) |
| 1013 | { |
| 1014 | int j, k; |
| 1015 | bezier bz; |
| 1016 | |
| 1017 | if (ED_label(e)) |
| 1018 | MOVEPT(ED_label(e)->pos); |
| 1019 | if (ED_xlabel(e)) |
| 1020 | MOVEPT(ED_xlabel(e)->pos); |
| 1021 | if (ED_head_label(e)) |
| 1022 | MOVEPT(ED_head_label(e)->pos); |
| 1023 | if (ED_tail_label(e)) |
| 1024 | MOVEPT(ED_tail_label(e)->pos); |
| 1025 | |
| 1026 | if (ED_spl(e) == NULL) |
| 1027 | return; |
| 1028 | |
| 1029 | for (j = 0; j < ED_spl(e)->size; j++) { |
| 1030 | bz = ED_spl(e)->list[j]; |
| 1031 | for (k = 0; k < bz.size; k++) |
| 1032 | MOVEPT(bz.list[k]); |
| 1033 | if (bz.sflag) |
| 1034 | MOVEPT(ED_spl(e)->list[j].sp); |
| 1035 | if (bz.eflag) |
| 1036 | MOVEPT(ED_spl(e)->list[j].ep); |
| 1037 | } |
| 1038 | } |
| 1039 | |
| 1040 | /* shiftGraph: |
| 1041 | */ |
| 1042 | static void shiftGraph(Agraph_t * g, int dx, int dy) |
| 1043 | { |
| 1044 | graph_t *subg; |
| 1045 | boxf bb = GD_bb(g); |
| 1046 | int i; |
| 1047 | |
| 1048 | bb = GD_bb(g); |
| 1049 | bb.LL.x += dx; |
| 1050 | bb.UR.x += dx; |
| 1051 | bb.LL.y += dy; |
| 1052 | bb.UR.y += dy; |
| 1053 | GD_bb(g) = bb; |
| 1054 | |
| 1055 | if (GD_label(g) && GD_label(g)->set) |
| 1056 | MOVEPT(GD_label(g)->pos); |
| 1057 | |
| 1058 | for (i = 1; i <= GD_n_cluster(g); i++) { |
| 1059 | subg = GD_clust(g)[i]; |
| 1060 | shiftGraph(subg, dx, dy); |
| 1061 | } |
| 1062 | } |
| 1063 | |
| 1064 | /* shiftGraphs: |
| 1065 | * The function takes ng graphs gs and a similar |
| 1066 | * number of points pp and translates each graph so |
| 1067 | * that the lower left corner of the bounding box of graph gs[i] is at |
| 1068 | * point ps[i]. To do this, it assumes the bb field in |
| 1069 | * Agraphinfo_t accurately reflects the current graph layout. |
| 1070 | * The graph is repositioned by translating the pos and coord fields of |
| 1071 | * each node appropriately. |
| 1072 | * |
| 1073 | * If doSplines is non-zero, the function also translates splines coordinates |
| 1074 | * of each edge, if they have been calculated. In addition, edge labels are |
| 1075 | * repositioned. |
| 1076 | * |
| 1077 | * If root is non-NULL, it is taken as the root graph of |
| 1078 | * the graphs in gs and is used to find the edges. Otherwise, the function |
| 1079 | * uses the edges found in each graph gs[i]. |
| 1080 | * |
| 1081 | * It returns 0 on success. |
| 1082 | * |
| 1083 | * This function uses the bb field in Agraphinfo_t, |
| 1084 | * the pos and coord fields in nodehinfo_t and |
| 1085 | * the spl field in Aedgeinfo_t. |
| 1086 | */ |
| 1087 | int |
| 1088 | shiftGraphs(int ng, Agraph_t ** gs, point * pp, Agraph_t * root, |
| 1089 | int doSplines) |
| 1090 | { |
| 1091 | int i; |
| 1092 | int dx, dy; |
| 1093 | double fx, fy; |
| 1094 | point p; |
| 1095 | Agraph_t *g; |
| 1096 | Agraph_t *eg; |
| 1097 | Agnode_t *n; |
| 1098 | Agedge_t *e; |
| 1099 | |
| 1100 | if (ng <= 0) |
| 1101 | return abs(ng); |
| 1102 | |
| 1103 | for (i = 0; i < ng; i++) { |
| 1104 | g = gs[i]; |
| 1105 | if (root) |
| 1106 | eg = root; |
| 1107 | else |
| 1108 | eg = g; |
| 1109 | p = pp[i]; |
| 1110 | dx = p.x; |
| 1111 | dy = p.y; |
| 1112 | fx = PS2INCH(dx); |
| 1113 | fy = PS2INCH(dy); |
| 1114 | |
| 1115 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { |
| 1116 | ND_pos(n)[0] += fx; |
| 1117 | ND_pos(n)[1] += fy; |
| 1118 | MOVEPT(ND_coord(n)); |
| 1119 | if (ND_xlabel(n)) { |
| 1120 | MOVEPT(ND_xlabel(n)->pos); |
| 1121 | } |
| 1122 | if (doSplines) { |
| 1123 | for (e = agfstout(eg, n); e; e = agnxtout(eg, e)) |
| 1124 | shiftEdge(e, dx, dy); |
| 1125 | } |
| 1126 | } |
| 1127 | shiftGraph(g, dx, dy); |
| 1128 | } |
| 1129 | |
| 1130 | return 0; |
| 1131 | } |
| 1132 | |
| 1133 | /* packGraphs: |
| 1134 | * Packs graphs. |
| 1135 | * ng - number of graphs |
| 1136 | * gs - pointer to array of graphs |
| 1137 | * root - graph used to find edges |
| 1138 | * info - parameters used in packing |
| 1139 | * info->doSplines - if true, use already computed spline control points |
| 1140 | * This decides where to layout the graphs and repositions the graph's |
| 1141 | * position info. |
| 1142 | * |
| 1143 | * Returns 0 on success. |
| 1144 | */ |
| 1145 | int packGraphs(int ng, Agraph_t ** gs, Agraph_t * root, pack_info * info) |
| 1146 | { |
| 1147 | int ret; |
| 1148 | point *pp = putGraphs(ng, gs, root, info); |
| 1149 | |
| 1150 | if (!pp) |
| 1151 | return 1; |
| 1152 | ret = shiftGraphs(ng, gs, pp, root, info->doSplines); |
| 1153 | free(pp); |
| 1154 | return ret; |
| 1155 | } |
| 1156 | |
| 1157 | /* packSubgraphs: |
| 1158 | * Packs subgraphs of given root graph, then recalculates root's bounding box. |
| 1159 | * Note that it does not recompute subgraph bounding boxes. |
| 1160 | * Cluster bounding boxes are recomputed in shiftGraphs. |
| 1161 | */ |
| 1162 | int |
| 1163 | packSubgraphs(int ng, Agraph_t ** gs, Agraph_t * root, pack_info * info) |
| 1164 | { |
| 1165 | int ret; |
| 1166 | |
| 1167 | ret = packGraphs(ng, gs, root, info); |
| 1168 | if (ret == 0) { |
| 1169 | int i, j; |
| 1170 | boxf bb; |
| 1171 | graph_t* g; |
| 1172 | |
| 1173 | compute_bb(root); |
| 1174 | bb = GD_bb(root); |
| 1175 | for (i = 0; i < ng; i++) { |
| 1176 | g = gs[i]; |
| 1177 | for (j = 1; j <= GD_n_cluster(g); j++) { |
| 1178 | EXPANDBB(bb,GD_bb(GD_clust(g)[j])); |
| 1179 | } |
| 1180 | } |
| 1181 | GD_bb(root) = bb; |
| 1182 | } |
| 1183 | return ret; |
| 1184 | } |
| 1185 | |
| 1186 | /* pack_graph: |
| 1187 | * Pack subgraphs followed by postprocessing. |
| 1188 | */ |
| 1189 | int |
| 1190 | pack_graph(int ng, Agraph_t** gs, Agraph_t* root, boolean* fixed) |
| 1191 | { |
| 1192 | int ret; |
| 1193 | pack_info info; |
| 1194 | |
| 1195 | getPackInfo(root, l_graph, CL_OFFSET, &info); |
| 1196 | info.doSplines = 1; |
| 1197 | info.fixed = fixed; |
| 1198 | ret = packSubgraphs(ng, gs, root, &info); |
| 1199 | if (ret == 0) dotneato_postprocess (root); |
| 1200 | return ret; |
| 1201 | } |
| 1202 | |
| 1203 | #define ARRAY "array" |
| 1204 | #define ASPECT "aspect" |
| 1205 | #define SLEN(s) (sizeof(s)/sizeof(char) - 1) |
| 1206 | |
| 1207 | static char* |
| 1208 | chkFlags (char* p, pack_info* pinfo) |
| 1209 | { |
| 1210 | int c, more; |
| 1211 | |
| 1212 | if (*p != '_') return p; |
| 1213 | p++; |
| 1214 | more = 1; |
| 1215 | while (more && (c = *p)) { |
| 1216 | switch (c) { |
| 1217 | case 'c' : |
| 1218 | pinfo->flags |= PK_COL_MAJOR; |
| 1219 | p++; |
| 1220 | break; |
| 1221 | case 'i' : |
| 1222 | pinfo->flags |= PK_INPUT_ORDER; |
| 1223 | p++; |
| 1224 | break; |
| 1225 | case 'u' : |
| 1226 | pinfo->flags |= PK_USER_VALS; |
| 1227 | p++; |
| 1228 | break; |
| 1229 | case 't' : |
| 1230 | pinfo->flags |= PK_TOP_ALIGN; |
| 1231 | p++; |
| 1232 | break; |
| 1233 | case 'b' : |
| 1234 | pinfo->flags |= PK_BOT_ALIGN; |
| 1235 | p++; |
| 1236 | break; |
| 1237 | case 'l' : |
| 1238 | pinfo->flags |= PK_LEFT_ALIGN; |
| 1239 | p++; |
| 1240 | break; |
| 1241 | case 'r' : |
| 1242 | pinfo->flags |= PK_RIGHT_ALIGN; |
| 1243 | p++; |
| 1244 | break; |
| 1245 | default : |
| 1246 | more = 0; |
| 1247 | break; |
| 1248 | } |
| 1249 | } |
| 1250 | return p; |
| 1251 | } |
| 1252 | |
| 1253 | static char* |
| 1254 | mode2Str (pack_mode m) |
| 1255 | { |
| 1256 | char *s; |
| 1257 | |
| 1258 | switch (m) { |
| 1259 | case l_clust: |
| 1260 | s = "cluster" ; |
| 1261 | break; |
| 1262 | case l_node: |
| 1263 | s = "node" ; |
| 1264 | break; |
| 1265 | case l_graph: |
| 1266 | s = "graph" ; |
| 1267 | break; |
| 1268 | case l_array: |
| 1269 | s = "array" ; |
| 1270 | break; |
| 1271 | case l_aspect: |
| 1272 | s = "aspect" ; |
| 1273 | break; |
| 1274 | case l_undef: |
| 1275 | default: |
| 1276 | s = "undefined" ; |
| 1277 | break; |
| 1278 | } |
| 1279 | return s; |
| 1280 | } |
| 1281 | |
| 1282 | /* parsePackModeInfo; |
| 1283 | * Return pack_mode of graph using "packmode" attribute. |
| 1284 | * If not defined, return dflt |
| 1285 | */ |
| 1286 | pack_mode |
| 1287 | parsePackModeInfo(char* p, pack_mode dflt, pack_info* pinfo) |
| 1288 | { |
| 1289 | float v; |
| 1290 | int i; |
| 1291 | |
| 1292 | assert (pinfo); |
| 1293 | pinfo->flags = 0; |
| 1294 | pinfo->mode = dflt; |
| 1295 | pinfo->sz = 0; |
| 1296 | pinfo->vals = NULL; |
| 1297 | if (p && *p) { |
| 1298 | switch (*p) { |
| 1299 | case 'a': |
| 1300 | if (strneq(p, ARRAY, SLEN(ARRAY))) { |
| 1301 | pinfo->mode = l_array; |
| 1302 | p += SLEN(ARRAY); |
| 1303 | p = chkFlags (p, pinfo); |
| 1304 | if ((sscanf (p, "%d" , &i)>0) && (i > 0)) |
| 1305 | pinfo->sz = i; |
| 1306 | } |
| 1307 | else if (strneq(p, ASPECT, SLEN(ASPECT))) { |
| 1308 | pinfo->mode = l_aspect; |
| 1309 | if ((sscanf (p + SLEN(ARRAY), "%f" , &v)>0) && (v > 0)) |
| 1310 | pinfo->aspect = v; |
| 1311 | else |
| 1312 | pinfo->aspect = 1; |
| 1313 | } |
| 1314 | break; |
| 1315 | #ifdef NOT_IMPLEMENTED |
| 1316 | case 'b': |
| 1317 | if (streq(p, "bisect" )) |
| 1318 | pinfo->mode = l_bisect; |
| 1319 | break; |
| 1320 | #endif |
| 1321 | case 'c': |
| 1322 | if (streq(p, "cluster" )) |
| 1323 | pinfo->mode = l_clust; |
| 1324 | break; |
| 1325 | case 'g': |
| 1326 | if (streq(p, "graph" )) |
| 1327 | pinfo->mode = l_graph; |
| 1328 | break; |
| 1329 | #ifdef NOT_IMPLEMENTED |
| 1330 | case 'h': |
| 1331 | if (streq(p, "hull" )) |
| 1332 | pinfo->mode = l_hull; |
| 1333 | break; |
| 1334 | #endif |
| 1335 | case 'n': |
| 1336 | if (streq(p, "node" )) |
| 1337 | pinfo->mode = l_node; |
| 1338 | break; |
| 1339 | #ifdef NOT_IMPLEMENTED |
| 1340 | case 't': |
| 1341 | if (streq(p, "tile" )) |
| 1342 | pinfo->mode = l_tile; |
| 1343 | break; |
| 1344 | #endif |
| 1345 | } |
| 1346 | } |
| 1347 | |
| 1348 | if (Verbose) { |
| 1349 | fprintf (stderr, "pack info:\n" ); |
| 1350 | fprintf (stderr, " mode %s\n" , mode2Str(pinfo->mode)); |
| 1351 | if (pinfo->mode == l_aspect) |
| 1352 | fprintf (stderr, " aspect %f\n" , pinfo->aspect); |
| 1353 | fprintf (stderr, " size %d\n" , pinfo->sz); |
| 1354 | fprintf (stderr, " flags %d\n" , pinfo->flags); |
| 1355 | } |
| 1356 | return pinfo->mode; |
| 1357 | } |
| 1358 | |
| 1359 | /* getPackModeInfo; |
| 1360 | * Return pack_mode of graph using "packmode" attribute. |
| 1361 | * If not defined, return dflt |
| 1362 | */ |
| 1363 | pack_mode |
| 1364 | getPackModeInfo(Agraph_t * g, pack_mode dflt, pack_info* pinfo) |
| 1365 | { |
| 1366 | return parsePackModeInfo (agget(g, "packmode" ), dflt, pinfo); |
| 1367 | } |
| 1368 | |
| 1369 | pack_mode |
| 1370 | getPackMode(Agraph_t * g, pack_mode dflt) |
| 1371 | { |
| 1372 | pack_info info; |
| 1373 | return getPackModeInfo (g, dflt, &info); |
| 1374 | } |
| 1375 | |
| 1376 | /* getPack: |
| 1377 | * Return "pack" attribute of g. |
| 1378 | * If not defined or negative, return not_def. |
| 1379 | * If defined but not specified, return dflt. |
| 1380 | */ |
| 1381 | int getPack(Agraph_t * g, int not_def, int dflt) |
| 1382 | { |
| 1383 | char *p; |
| 1384 | int i; |
| 1385 | int v = not_def; |
| 1386 | |
| 1387 | if ((p = agget(g, "pack" ))) { |
| 1388 | if ((sscanf(p, "%d" , &i) == 1) && (i >= 0)) |
| 1389 | v = i; |
| 1390 | else if ((*p == 't') || (*p == 'T')) |
| 1391 | v = dflt; |
| 1392 | } |
| 1393 | |
| 1394 | return v; |
| 1395 | } |
| 1396 | |
| 1397 | pack_mode |
| 1398 | getPackInfo(Agraph_t * g, pack_mode dflt, int dfltMargin, pack_info* pinfo) |
| 1399 | { |
| 1400 | assert (pinfo); |
| 1401 | |
| 1402 | pinfo->margin = getPack(g, dfltMargin, dfltMargin); |
| 1403 | if (Verbose) { |
| 1404 | fprintf (stderr, " margin %d\n" , pinfo->margin); |
| 1405 | } |
| 1406 | pinfo->doSplines = 0; |
| 1407 | pinfo->fixed = 0; |
| 1408 | getPackModeInfo(g, dflt, pinfo); |
| 1409 | |
| 1410 | return pinfo->mode; |
| 1411 | } |
| 1412 | |
| 1413 | |
| 1414 | |