| 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 |  | 
|---|