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