pack.c 33 KB

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