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