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