Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
layout.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
12/* layout.c:
13 * Written by Emden R. Gansner
14 *
15 * This module provides the main bookkeeping for the fdp layout.
16 * In particular, it handles the recursion and the creation of
17 * ports and auxiliary graphs.
18 *
19 * TODO : can we use ports to aid in layout of edges? Note that
20 * at present, they are deleted.
21 *
22 * Can we delay all repositioning of nodes until evalPositions, so
23 * finalCC only sets the bounding boxes?
24 *
25 * Make sure multiple edges have an effect.
26 */
27
28/* uses PRIVATE interface */
29#define FDP_PRIVATE 1
30
31#include "config.h"
32#include <assert.h>
33#include <float.h>
34#include <limits.h>
35#include <inttypes.h>
36#include <assert.h>
37#include <cgraph/alloc.h>
38#include <cgraph/list.h>
39#include <cgraph/startswith.h>
40#include <fdpgen/tlayout.h>
41#include <math.h>
42#include <neatogen/neatoprocs.h>
43#include <neatogen/adjust.h>
44#include <fdpgen/comp.h>
45#include <pack/pack.h>
46#include <fdpgen/clusteredges.h>
47#include <fdpgen/dbg.h>
48#include <stddef.h>
49#include <stdbool.h>
50
51typedef struct {
52 graph_t* rootg; /* logical root; graph passed in to fdp_layout */
56 int gid;
59
60typedef struct {
62 double alpha;
63 double dist2;
64} erec;
65
66#define NEW_EDGE(e) (ED_to_virt(e) == 0)
67
68/* finalCC:
69 * Set graph bounding box given list of connected
70 * components, each with its bounding box set.
71 * If c_cnt > 1, then pts != NULL and gives translations for components.
72 * Add margin about whole graph unless isRoot is true.
73 * Reposition nodes based on final position of
74 * node's connected component.
75 * Also, entire layout is translated to origin.
76 */
77static void finalCC(graph_t *g, size_t c_cnt, graph_t **cc, pointf *pts,
78 graph_t *rg, layout_info* infop) {
79 attrsym_t * G_width = infop->G_width;
80 attrsym_t * G_height = infop->G_height;
81 graph_t *cg;
82 boxf bb;
83 boxf bbf;
84 pointf pt;
85 int margin;
86 graph_t **cp = cc;
87 pointf *pp = pts;
88 int isRoot = rg == infop->rootg;
89 int isEmpty = 0;
90
91 /* compute graph bounding box in points */
92 if (c_cnt) {
93 cg = *cp++;
94 bb = GD_bb(cg);
95 if (c_cnt > 1) {
96 pt = *pp++;
97 bb.LL.x += pt.x;
98 bb.LL.y += pt.y;
99 bb.UR.x += pt.x;
100 bb.UR.y += pt.y;
101 while ((cg = *cp++)) {
102 boxf b = GD_bb(cg);
103 pt = *pp++;
104 b.LL.x += pt.x;
105 b.LL.y += pt.y;
106 b.UR.x += pt.x;
107 b.UR.y += pt.y;
108 bb.LL.x = fmin(bb.LL.x, b.LL.x);
109 bb.LL.y = fmin(bb.LL.y, b.LL.y);
110 bb.UR.x = fmax(bb.UR.x, b.UR.x);
111 bb.UR.y = fmax(bb.UR.y, b.UR.y);
112 }
113 }
114 } else { /* empty graph */
115 bb.LL.x = 0;
116 bb.LL.y = 0;
117 bb.UR.x = late_int(rg, G_width, POINTS(DEFAULT_NODEWIDTH), 3);
118 bb.UR.y = late_int(rg, G_height, POINTS(DEFAULT_NODEHEIGHT), 3);
119 isEmpty = 1;
120 }
121
122 if (GD_label(rg)) {
123 isEmpty = 0;
124 double d = round(GD_label(rg)->dimen.x) - (bb.UR.x - bb.LL.x);
125 if (d > 0) { /* height of label added below */
126 d /= 2;
127 bb.LL.x -= d;
128 bb.UR.x += d;
129 }
130 }
131
132 if (isRoot || isEmpty)
133 margin = 0;
134 else
135 margin = late_int (rg, G_margin, CL_OFFSET, 0);
136 pt.x = -bb.LL.x + margin;
137 pt.y = -bb.LL.y + margin + GD_border(rg)[BOTTOM_IX].y;
138 bb.LL.x = 0;
139 bb.LL.y = 0;
140 bb.UR.x += pt.x + margin;
141 bb.UR.y += pt.y + margin + GD_border(rg)[TOP_IX].y;
142
143 /* translate nodes */
144 if (c_cnt) {
145 cp = cc;
146 pp = pts;
147 while ((cg = *cp++)) {
148 pointf p;
149 node_t *n;
150 pointf del;
151
152 if (pp) {
153 p = *pp++;
154 p.x += pt.x;
155 p.y += pt.y;
156 } else {
157 p = pt;
158 }
159 del.x = PS2INCH(p.x);
160 del.y = PS2INCH(p.y);
161 for (n = agfstnode(cg); n; n = agnxtnode(cg, n)) {
162 ND_pos(n)[0] += del.x;
163 ND_pos(n)[1] += del.y;
164 }
165 }
166 }
167
168 bbf.LL.x = PS2INCH(bb.LL.x);
169 bbf.LL.y = PS2INCH(bb.LL.y);
170 bbf.UR.x = PS2INCH(bb.UR.x);
171 bbf.UR.y = PS2INCH(bb.UR.y);
172 BB(g) = bbf;
173
174}
175
176/* mkDeriveNode:
177 * Constructor for a node in a derived graph.
178 * Allocates dndata.
179 */
180static node_t *mkDeriveNode(graph_t * dg, char *name)
181{
182 node_t *dn;
183
184 dn = agnode(dg, name,1);
185 agbindrec(dn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true); //node custom data
186 ND_alg(dn) = gv_alloc(sizeof(dndata)); // free in freeDeriveNode
187 ND_pos(dn) = gv_calloc(GD_ndim(dg), sizeof(double));
188 /* fprintf (stderr, "Creating %s\n", dn->name); */
189 return dn;
190}
191
192static void freeDeriveNode(node_t * n)
193{
194 free(ND_alg(n));
195 free(ND_pos(n));
196 agdelrec(n, "Agnodeinfo_t");
197}
198
199static void freeGData(graph_t * g)
200{
201 free(GD_alg(g));
202}
203
204static void freeDerivedGraph(graph_t * g, graph_t ** cc)
205{
206 graph_t *cg;
207 node_t *dn;
208 node_t *dnxt;
209 edge_t *e;
210
211 while ((cg = *cc++)) {
212 freeGData(cg);
213 agdelrec(cg, "Agraphinfo_t");
214 }
215 if (PORTS(g))
216 free(PORTS(g));
217 freeGData(g);
218 agdelrec(g, "Agraphinfo_t");
219 for (dn = agfstnode(g); dn; dn = dnxt) {
220 dnxt = agnxtnode(g, dn);
221 for (e = agfstout(g, dn); e; e = agnxtout(g, e)) {
222 free (ED_to_virt(e));
223 agdelrec(e, "Agedgeinfo_t");
224 }
225 freeDeriveNode(dn);
226 }
227 agclose(g);
228}
229
230/* evalPositions:
231 * The input is laid out, but node coordinates
232 * are relative to smallest containing cluster.
233 * Walk through all nodes and clusters, translating
234 * the positions to absolute coordinates.
235 * Assume that when called, g's bounding box is
236 * in absolute coordinates and that box of root graph
237 * has LL at origin.
238 */
239static void evalPositions(graph_t * g, graph_t* rootg)
240{
241 int i;
242 graph_t *subg;
243 node_t *n;
244 boxf bb;
245 boxf sbb;
246
247 bb = BB(g);
248
249 /* translate nodes in g */
250 if (g != rootg) {
251 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
252 if (PARENT(n) != g)
253 continue;
254 ND_pos(n)[0] += bb.LL.x;
255 ND_pos(n)[1] += bb.LL.y;
256 }
257 }
258
259 /* translate top-level clusters and recurse */
260 for (i = 1; i <= GD_n_cluster(g); i++) {
261 subg = GD_clust(g)[i];
262 if (g != rootg) {
263 sbb = BB(subg);
264 sbb.LL.x += bb.LL.x;
265 sbb.LL.y += bb.LL.y;
266 sbb.UR.x += bb.LL.x;
267 sbb.UR.y += bb.LL.y;
268 BB(subg) = sbb;
269 }
270 evalPositions(subg, rootg);
271 }
272}
273
274DEFINE_LIST(clist, graph_t*)
275
276#define BSZ 1000
277
278/* portName:
279 * Generate a name for a port.
280 * We use the ids of the nodes.
281 * This is for debugging. For production, just use edge id and some
282 * id for the graph. Note that all the graphs are subgraphs of the
283 * root graph.
284 */
285static char *portName(graph_t * g, bport_t * p)
286{
287 edge_t *e = p->e;
288 node_t *h = aghead(e);
289 node_t *t = agtail(e);
290 static char buf[BSZ + 1];
291
292 snprintf(buf, sizeof(buf), "_port_%s_(%d)_(%d)_%u",agnameof(g),
293 ND_id(t), ND_id(h), AGSEQ(e));
294 return buf;
295}
296
297/* chkPos:
298 * If cluster has coords attribute, use to supply initial position
299 * of derived node.
300 * Only called if G_coord is defined.
301 * We also look at the parent graph's G_coord attribute. If this
302 * is identical to the child graph, we have to assume the child
303 * inherited it.
304 */
305static void chkPos(graph_t* g, node_t* n, layout_info* infop, boxf* bbp)
306{
307 char *p;
308 char *pp;
309 boxf bb;
310 char c;
312 attrsym_t *G_coord = infop->G_coord;
313
314 p = agxget(g, G_coord);
315 if (p[0]) {
316 if (g != infop->rootg) {
317 parent =agparent(g);
318 pp = agxget(parent, G_coord);
319 if (!strcmp(p, pp))
320 return;
321 }
322 c = '\0';
323 if (sscanf(p, "%lf,%lf,%lf,%lf%c",
324 &bb.LL.x, &bb.LL.y, &bb.UR.x, &bb.UR.y, &c) >= 4) {
325 if (PSinputscale > 0.0) {
326 bb.LL.x /= PSinputscale;
327 bb.LL.y /= PSinputscale;
328 bb.UR.x /= PSinputscale;
329 bb.UR.y /= PSinputscale;
330 }
331 if (c == '!')
332 ND_pinned(n) = P_PIN;
333 else if (c == '?')
334 ND_pinned(n) = P_FIX;
335 else
336 ND_pinned(n) = P_SET;
337 *bbp = bb;
338 } else
339 agwarningf("graph %s, coord %s, expected four doubles\n",
340 agnameof(g), p);
341 }
342}
343
344/* addEdge:
345 * Add real edge e to its image de in the derived graph.
346 * We use the to_virt and count fields to store the list.
347 */
348static void addEdge(edge_t * de, edge_t * e)
349{
350 short cnt = ED_count(de);
351 edge_t **el;
352
353 el = (edge_t**)ED_to_virt(de);
354 el = gv_recalloc(el, cnt, cnt + 1, sizeof(edge_t*));
355 el[cnt] = e;
356 ED_to_virt(de) = (edge_t *) el;
357 ED_count(de)++;
358}
359
360/* copyAttr:
361 * Copy given attribute from g to dg.
362 */
363static void
364copyAttr (graph_t* g, graph_t* dg, char* attr)
365{
366 char* ov_val;
367 Agsym_t* ov;
368
369 if ((ov = agattr(g,AGRAPH, attr, NULL))) {
370 ov_val = agxget(g,ov);
371 ov = agattr(dg,AGRAPH, attr, NULL);
372 if (ov)
373 agxset (dg, ov, ov_val);
374 else
375 agattr(dg, AGRAPH, attr, ov_val);
376 }
377}
378
379/* deriveGraph:
380 * Create derived graph of g by collapsing clusters into
381 * nodes. An edge is created between nodes if there is
382 * an edge between two nodes in the clusters of the base graph.
383 * Such edges record all corresponding real edges.
384 * In addition, we add a node and edge for each port.
385 */
387{
388 graph_t *dg;
389 node_t *dn;
390 graph_t *subg;
391 bport_t *pp;
392 node_t *n;
393 edge_t *de;
394 int i, id = 0;
395
396 if (Verbose >= 2)
397 fprintf(stderr, "derive graph _dg_%d of %s\n", infop->gid, agnameof(g));
398 infop->gid++;
399
400 dg = agopen("derived", Agstrictdirected,NULL);
401 agbindrec(dg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
402 GD_alg(dg) = gv_alloc(sizeof(gdata)); // freed in freeDeriveGraph
403#ifdef DEBUG
404 GORIG(dg) = g;
405#endif
406 GD_ndim(dg) = GD_ndim(agroot(g));
407
408 /* Copy attributes from g.
409 */
410 copyAttr(g,dg,"overlap");
411 copyAttr(g,dg,"sep");
412 copyAttr(g,dg,"K");
413
414 /* create derived nodes from clusters */
415 for (i = 1; i <= GD_n_cluster(g); i++) {
416 boxf fix_bb = {{DBL_MAX, DBL_MAX}, {-DBL_MAX, -DBL_MAX}};
417 subg = GD_clust(g)[i];
418
419 do_graph_label(subg);
420 dn = mkDeriveNode(dg, agnameof(subg));
421 ND_clust(dn) = subg;
422 ND_id(dn) = id++;
423 if (infop->G_coord)
424 chkPos(subg, dn, infop, &fix_bb);
425 for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
426 DNODE(n) = dn;
427 }
428 if (ND_pinned(dn)) {
429 ND_pos(dn)[0] = (fix_bb.LL.x + fix_bb.UR.x) / 2;
430 ND_pos(dn)[1] = (fix_bb.LL.y + fix_bb.UR.y) / 2;
431 }
432 }
433
434 /* create derived nodes from remaining nodes */
435 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
436 if (!DNODE(n)) {
437 if (PARENT(n) && PARENT(n) != GPARENT(g)) {
438 agerrorf("node \"%s\" is contained in two non-comparable clusters \"%s\" and \"%s\"\n", agnameof(n), agnameof(g), agnameof(PARENT(n)));
439 return NULL;
440 }
441 PARENT(n) = g;
442 if (IS_CLUST_NODE(n))
443 continue;
444 dn = mkDeriveNode(dg, agnameof(n));
445 DNODE(n) = dn;
446 ND_id(dn) = id++;
447 ND_width(dn) = ND_width(n);
448 ND_height(dn) = ND_height(n);
449 ND_lw(dn) = ND_lw(n);
450 ND_rw(dn) = ND_rw(n);
451 ND_ht(dn) = ND_ht(n);
452 ND_shape(dn) = ND_shape(n);
454 if (ND_pinned(n)) {
455 ND_pos(dn)[0] = ND_pos(n)[0];
456 ND_pos(dn)[1] = ND_pos(n)[1];
457 ND_pinned(dn) = ND_pinned(n);
458 }
459 ANODE(dn) = n;
460 }
461 }
462
463 /* add edges */
464 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
465 edge_t *e;
466 node_t *hd;
467 node_t *tl = DNODE(n);
468 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
469 hd = DNODE(aghead(e));
470 if (hd == tl)
471 continue;
472 if (hd > tl)
473 de = agedge(dg, tl, hd, NULL,1);
474 else
475 de = agedge(dg, hd, tl, NULL,1);
476 agbindrec(de, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true);
477 ED_dist(de) = ED_dist(e);
478 ED_factor(de) = ED_factor(e);
479 /* fprintf (stderr, "edge %s -- %s\n", tl->name, hd->name); */
480 WDEG(hd)++;
481 WDEG(tl)++;
482 if (NEW_EDGE(de)) {
483 DEG(hd)++;
484 DEG(tl)++;
485 }
486 addEdge(de, e);
487 }
488 }
489
490 /* transform ports */
491 if ((pp = PORTS(g))) {
492 bport_t *pq;
493 node_t *m;
494 int sz = NPORTS(g);
495
496 /* freed in freeDeriveGraph */
497 PORTS(dg) = pq = gv_calloc(sz + 1, sizeof(bport_t));
498 sz = 0;
499 while (pp->e) {
500 m = DNODE(pp->n);
501 /* Create port in derived graph only if hooks to internal node */
502 if (m) {
503 dn = mkDeriveNode(dg, portName(g, pp));
504 sz++;
505 ND_id(dn) = id++;
506 if (dn > m)
507 de = agedge(dg, m, dn, NULL,1);
508 else
509 de = agedge(dg, dn, m, NULL,1);
510 agbindrec(de, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true);
511 ED_dist(de) = ED_dist(pp->e);
512 ED_factor(de) = ED_factor(pp->e);
513 addEdge(de, pp->e);
514 WDEG(dn)++;
515 WDEG(m)++;
516 DEG(dn)++; /* ports are unique, so this will be the first and */
517 DEG(m)++; /* only time the edge is touched. */
518 pq->n = dn;
519 pq->alpha = pp->alpha;
520 pq->e = de;
521 pq++;
522 }
523 pp++;
524 }
525 NPORTS(dg) = sz;
526 }
527
528 return dg;
529}
530
531/* ecmp:
532 * Sort edges by angle, then distance.
533 */
534static int ecmp(const void *v1, const void *v2)
535{
536 const erec *e1 = v1;
537 const erec *e2 = v2;
538 if (e1->alpha > e2->alpha)
539 return 1;
540 else if (e1->alpha < e2->alpha)
541 return -1;
542 else if (e1->dist2 > e2->dist2)
543 return 1;
544 else if (e1->dist2 < e2->dist2)
545 return -1;
546 else
547 return 0;
548}
549
550#define ANG (M_PI/90) /* Maximum angular change: 2 degrees */
551
552/* getEdgeList:
553 * Generate list of edges in derived graph g using
554 * node n. The list is in counterclockwise order.
555 * This, of course, assumes we have an initial layout for g.
556 */
558{
559 int deg = DEG(n);
560 int i;
561 double dx, dy;
562 edge_t *e;
563 node_t *m;
564
565 /* freed in expandCluster */
566 erec *erecs = gv_calloc(deg + 1, sizeof(erec));
567 i = 0;
568 for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
569 if (aghead(e) == n)
570 m = agtail(e);
571 else
572 m = aghead(e);
573 dx = ND_pos(m)[0] - ND_pos(n)[0];
574 dy = ND_pos(m)[1] - ND_pos(n)[1];
575 erecs[i].e = e;
576 erecs[i].alpha = atan2(dy, dx);
577 erecs[i].dist2 = dx * dx + dy * dy;
578 i++;
579 }
580 assert(i == deg);
581 qsort(erecs, deg, sizeof(erec), ecmp);
582
583 /* ensure no two angles are equal */
584 if (deg >= 2) {
585 int j;
586 double a, inc, delta, bnd;
587
588 i = 0;
589 while (i < deg - 1) {
590 a = erecs[i].alpha;
591 j = i + 1;
592 while (j < deg && erecs[j].alpha == a)
593 j++;
594 if (j == i + 1)
595 i = j;
596 else {
597 if (j == deg)
598 bnd = M_PI; /* all values equal up to end */
599 else
600 bnd = erecs[j].alpha;
601 delta = fmin((bnd - a) / (j - i), ANG);
602 inc = 0;
603 for (; i < j; i++) {
604 erecs[i].alpha += inc;
605 inc += delta;
606 }
607 }
608 }
609 }
610
611 return erecs;
612}
613
614/* genPorts:
615 * Given list of edges with node n in derived graph, add corresponding
616 * ports to port list pp, starting at index idx. Return next index.
617 * If an edge in the derived graph corresponds to multiple real edges,
618 * add them in order if address of n is smaller than other node address.
619 * Otherwise, reverse order.
620 * Attach angles. The value bnd gives next angle after er->alpha.
621 */
622static int
623genPorts(node_t * n, erec * er, bport_t * pp, int idx, double bnd)
624{
625 node_t *other;
626 int cnt;
627 edge_t *e = er->e;
628 edge_t *el;
629 edge_t **ep;
630 double angle, delta;
631 int i, j, inc;
632
633 cnt = ED_count(e);
634
635 if (aghead(e) == n)
636 other = agtail(e);
637 else
638 other = aghead(e);
639
640 delta = fmin((bnd - er->alpha) / cnt, ANG);
641 angle = er->alpha;
642
643 if (n < other) {
644 i = idx;
645 inc = 1;
646 } else {
647 i = idx + cnt - 1;
648 inc = -1;
649 angle += delta * (cnt - 1);
650 delta = -delta;
651 }
652
653 ep = (edge_t **)ED_to_virt(e);
654 for (j = 0; j < ED_count(e); j++, ep++) {
655 el = *ep;
656 pp[i].e = el;
657 pp[i].n = DNODE(agtail(el)) == n ? agtail(el) : aghead(el);
658 pp[i].alpha = angle;
659 i += inc;
660 angle += delta;
661 }
662 return (idx + cnt);
663}
664
665/* expandCluster;
666 * Given positioned derived graph cg with node n which corresponds
667 * to a cluster, generate a graph containing the interior of the
668 * cluster, plus port information induced by the layout of cg.
669 * Basically, we use the cluster subgraph to which n corresponds,
670 * attached with port information.
671 */
673{
674 erec *es;
675 erec *ep;
676 erec *next;
677 graph_t *sg = ND_clust(n);
678 int sz = WDEG(n);
679 int idx = 0;
680 double bnd;
681
682 if (sz != 0) {
683 /* freed in cleanup_subgs */
684 bport_t *pp = gv_calloc(sz + 1, sizeof(bport_t));
685
686 /* create sorted list of edges of n */
687 es = ep = getEdgeList(n, cg);
688
689 /* generate ports from edges */
690 while (ep->e) {
691 next = ep + 1;
692 if (next->e)
693 bnd = next->alpha;
694 else
695 bnd = 2 * M_PI + es->alpha;
696 idx = genPorts(n, ep, pp, idx, bnd);
697 ep = next;
698 }
699 assert(idx == sz);
700
701 PORTS(sg) = pp;
702 NPORTS(sg) = sz;
703 free(es);
704 }
705 return sg;
706}
707
708/* setClustNodes:
709 * At present, cluster nodes are not assigned a position during layout,
710 * but positioned in the center of its associated cluster. Because the
711 * dummy edge associated with a cluster node may not occur at a sufficient
712 * level of cluster, the edge may not be used during layout and we cannot
713 * therefore rely find these nodes via ports.
714 *
715 * In this implementation, we just do a linear pass over all nodes in the
716 * root graph. At some point, we may use a better method, like having each
717 * cluster contain its list of cluster nodes, or have the graph keep a list.
718 *
719 * As nodes, we need to assign cluster nodes the coordinates in the
720 * coordinates of its cluster p. Note that p's bbox is in its parent's
721 * coordinates.
722 *
723 * If routing, we may decide to place on cluster boundary,
724 * and use polyline.
725 */
726static void
728{
729 boxf bb;
730 graph_t* p;
731 pointf ctr;
732 node_t *n;
733 double w, h, h_pts;
734 double h2, w2;
735 pointf *vertices;
736
737 for (n = agfstnode(root); n; n = agnxtnode(root, n)) {
738 if (!IS_CLUST_NODE(n)) continue;
739
740 p = PARENT(n);
741 bb = BB(p); /* bbox in parent cluster's coordinates */
742 w = bb.UR.x - bb.LL.x;
743 h = bb.UR.y - bb.LL.y;
744 ctr.x = w / 2.0;
745 ctr.y = h / 2.0;
746 w2 = INCH2PS(w / 2.0);
747 h2 = INCH2PS(h / 2.0);
748 h_pts = INCH2PS(h);
749 ND_pos(n)[0] = ctr.x;
750 ND_pos(n)[1] = ctr.y;
751 ND_width(n) = w;
752 ND_height(n) = h;
754 ND_outline_width(n) = w + penwidth;
756 /* ND_xsize(n) = POINTS(w); */
757 ND_lw(n) = ND_rw(n) = w2;
758 ND_ht(n) = h_pts;
759
760 vertices = ((polygon_t *) ND_shape_info(n))->vertices;
761 vertices[0].x = ND_rw(n);
762 vertices[0].y = h2;
763 vertices[1].x = -ND_lw(n);
764 vertices[1].y = h2;
765 vertices[2].x = -ND_lw(n);
766 vertices[2].y = -h2;
767 vertices[3].x = ND_rw(n);
768 vertices[3].y = -h2;
769 // allocate extra vertices representing the outline, i.e., the outermost
770 // periphery with penwidth taken into account
771 vertices[4].x = ND_rw(n) + penwidth / 2;
772 vertices[4].y = h2 + penwidth / 2;
773 vertices[5].x = -ND_lw(n) - penwidth / 2;
774 vertices[5].y = h2 + penwidth / 2;
775 vertices[6].x = -ND_lw(n) - penwidth / 2;
776 vertices[6].y = -h2 - penwidth / 2;
777 vertices[7].x = ND_rw(n) + penwidth / 2;
778 vertices[7].y = -h2 - penwidth / 2;
779 }
780}
781
782/* layout:
783 * Given g with ports:
784 * Derive g' from g by reducing clusters to points (deriveGraph)
785 * Compute connected components of g' (findCComp)
786 * For each cc of g':
787 * Layout cc (tLayout)
788 * For each node n in cc of g' <-> cluster c in g:
789 * Add ports based on layout of cc to get c' (expandCluster)
790 * Layout c' (recursion)
791 * Remove ports from cc
792 * Expand nodes of cc to reflect size of c' (xLayout)
793 * Pack connected components to get layout of g (putGraphs)
794 * Translate layout so that bounding box of layout + margin
795 * has the origin as LL corner.
796 * Set position of top level clusters and real nodes.
797 * Set bounding box of graph
798 *
799 * TODO:
800 *
801 * Possibly should modify so that only do connected components
802 * on top-level derived graph. Unconnected parts of a cluster
803 * could just rattle within cluster boundaries. This may mix
804 * up components but give a tighter packing.
805 *
806 * Add edges per components to get better packing, rather than
807 * wait until the end.
808 */
809static int layout(graph_t * g, layout_info * infop)
810{
811 pointf *pts = NULL;
812 graph_t *dg;
813 node_t *dn;
814 node_t *n;
815 graph_t *cg;
816 graph_t *sg;
817 graph_t **cc;
818 graph_t **pg;
819 int pinned;
820 xparams xpms;
821
822#ifdef DEBUG
823 incInd();
824#endif
825 if (Verbose) {
826#ifdef DEBUG
827 prIndent();
828#endif
829 fprintf (stderr, "layout %s\n", agnameof(g));
830 }
831 /* initialize derived node pointers */
832 for (n = agfstnode(g); n; n = agnxtnode(g, n))
833 DNODE(n) = 0;
834
835 dg = deriveGraph(g, infop);
836 if (dg == NULL) {
837 return -1;
838 }
839 size_t c_cnt;
840 cc = pg = findCComp(dg, &c_cnt, &pinned);
841
842 while ((cg = *pg++)) {
843 node_t* nxtnode;
844 fdp_tLayout(cg, &xpms);
845 for (n = agfstnode(cg); n; n = nxtnode) {
846 nxtnode = agnxtnode(cg, n);
847 if (ND_clust(n)) {
848 pointf pt;
849 sg = expandCluster(n, cg); /* attach ports to sg */
850 int r = layout(sg, infop);
851 if (r != 0) {
852 return r;
853 }
854 ND_width(n) = BB(sg).UR.x;
855 ND_height(n) = BB(sg).UR.y;
856 pt.x = POINTS_PER_INCH * BB(sg).UR.x;
857 pt.y = POINTS_PER_INCH * BB(sg).UR.y;
858 ND_rw(n) = ND_lw(n) = pt.x/2;
859 ND_ht(n) = pt.y;
860 } else if (IS_PORT(n))
861 agdelete(cg, n); /* remove ports from component */
862 }
863
864 /* Remove overlaps */
865 if (agnnodes(cg) >= 2) {
866 if (g == infop->rootg)
867 normalize (cg);
868 fdp_xLayout(cg, &xpms);
869 }
870 }
871
872 /* At this point, each connected component has its nodes correctly
873 * positioned. If we have multiple components, we pack them together.
874 * All nodes will be moved to their new positions.
875 * NOTE: packGraphs uses nodes in components, so if port nodes are
876 * not removed, it won't work.
877 */
878 /* Handle special cases well: no ports to real internal nodes
879 * Place cluster edges separately, after layout.
880 * How to combine parts, especially with disparate components?
881 */
882 if (c_cnt > 1) {
883 bool *bp;
884 if (pinned) {
885 bp = gv_calloc(c_cnt, sizeof(bool));
886 bp[0] = true;
887 } else
888 bp = NULL;
889 infop->pack.fixed = bp;
890 pts = putGraphs(c_cnt, cc, NULL, &infop->pack);
891 free(bp);
892 } else {
893 pts = NULL;
894 if (c_cnt == 1)
895 compute_bb(cc[0]);
896 }
897
898 /* set bounding box of dg and reposition nodes */
899 finalCC(dg, c_cnt, cc, pts, g, infop);
900 free (pts);
901
902 /* record positions from derived graph to input graph */
903 /* At present, this does not record port node info */
904 /* In fact, as noted above, we have removed port nodes */
905 for (dn = agfstnode(dg); dn; dn = agnxtnode(dg, dn)) {
906 if ((sg = ND_clust(dn))) {
907 BB(sg).LL.x = ND_pos(dn)[0] - ND_width(dn) / 2;
908 BB(sg).LL.y = ND_pos(dn)[1] - ND_height(dn) / 2;
909 BB(sg).UR.x = BB(sg).LL.x + ND_width(dn);
910 BB(sg).UR.y = BB(sg).LL.y + ND_height(dn);
911 } else if ((n = ANODE(dn))) {
912 ND_pos(n)[0] = ND_pos(dn)[0];
913 ND_pos(n)[1] = ND_pos(dn)[1];
914 }
915 }
916 BB(g) = BB(dg);
917#ifdef DEBUG
918 if (g == infop->rootg)
919 dump(g, 1);
920#endif
921
922 /* clean up temp graphs */
923 freeDerivedGraph(dg, cc);
924 free(cc);
925 if (Verbose) {
926#ifdef DEBUG
927 prIndent ();
928#endif
929 fprintf (stderr, "end %s\n", agnameof(g));
930 }
931#ifdef DEBUG
932 decInd();
933#endif
934
935 return 0;
936}
937
938/* setBB;
939 * Set point box g->bb from inch box BB(g).
940 */
941static void setBB(graph_t * g)
942{
943 int i;
944 boxf bb;
945
946 bb.LL.x = POINTS_PER_INCH * BB(g).LL.x;
947 bb.LL.y = POINTS_PER_INCH * BB(g).LL.y;
948 bb.UR.x = POINTS_PER_INCH * BB(g).UR.x;
949 bb.UR.y = POINTS_PER_INCH * BB(g).UR.y;
950 GD_bb(g) = bb;
951 for (i = 1; i <= GD_n_cluster(g); i++) {
952 setBB(GD_clust(g)[i]);
953 }
954}
955
956/* init_info:
957 * Initialize graph-dependent information and
958 * state variable.s
959 */
960static void init_info(graph_t * g, layout_info * infop)
961{
962 infop->G_coord = agattr(g, AGRAPH, "coords", NULL);
963 infop->G_width = agattr(g, AGRAPH, "width", NULL);
964 infop->G_height = agattr(g, AGRAPH, "height", NULL);
965 infop->rootg = g;
966 infop->gid = 0;
967 infop->pack.mode = getPackInfo(g, l_node, CL_OFFSET / 2, &infop->pack);
968}
969
970/* mkClusters:
971 * Attach list of immediate child clusters.
972 * NB: By convention, the indexing starts at 1.
973 * If pclist is NULL, the graph is the root graph or a cluster
974 * If pclist is non-NULL, we are recursively scanning a non-cluster
975 * subgraph for cluster children.
976 */
977static void
978mkClusters (graph_t * g, clist_t* pclist, graph_t* parent)
979{
980 graph_t* subg;
981 clist_t list = {0};
982 clist_t* clist;
983
984 if (pclist == NULL) {
985 // [0] is empty. The clusters are in [1..cnt].
986 clist_append(&list, NULL);
987 clist = &list;
988 }
989 else
990 clist = pclist;
991
992 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg))
993 {
994 if (startswith(agnameof(subg), "cluster")) {
995 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
996 GD_alg(subg) = gv_alloc(sizeof(gdata)); // freed in cleanup_subgs
997 GD_ndim(subg) = GD_ndim(agroot(parent));
998 LEVEL(subg) = LEVEL(parent) + 1;
999 GPARENT(subg) = parent;
1000 clist_append(clist, subg);
1001 mkClusters(subg, NULL, subg);
1002 }
1003 else {
1004 mkClusters(subg, clist, parent);
1005 }
1006 }
1007 if (pclist == NULL) {
1008 assert(clist_size(&list) - 1 <= INT_MAX);
1009 GD_n_cluster(g) = (int)(clist_size(&list) - 1);
1010 if (clist_size(&list) > 1) {
1011 clist_shrink_to_fit(&list);
1012 GD_clust(g) = clist_detach(&list);
1013 } else {
1014 clist_free(&list);
1015 }
1016 }
1017}
1018
1019static void fdp_init_graph(Agraph_t * g)
1020{
1022 GD_alg(g) = gv_alloc(sizeof(gdata)); // freed in cleanup_graph
1023 GD_ndim(agroot(g)) = late_int(g, agattr(g,AGRAPH, "dim", NULL), 2, 2);
1024 Ndim = GD_ndim(agroot(g)) = MIN(GD_ndim(agroot(g)), MAXDIM);
1025
1026 mkClusters (g, NULL, g);
1027 fdp_initParams(g);
1029}
1030
1031static int fdpLayout(graph_t * g)
1032{
1034
1035 init_info(g, &info);
1036 int r = layout(g, &info);
1037 if (r != 0) {
1038 return r;
1039 }
1040 setClustNodes(g);
1041 evalPositions(g,g);
1042
1043 /* Set bbox info for g and all clusters. This is needed for
1044 * spline drawing. We already know the graph bbox is at the origin.
1045 * On return from spline drawing, all bounding boxes should be correct.
1046 */
1047 setBB(g);
1048
1049 return 0;
1050}
1051
1052static void
1054{
1055 int trySplines = 0;
1056 int et = EDGE_TYPE(g);
1057
1058 if (et > EDGETYPE_ORTHO) {
1059 if (et == EDGETYPE_COMPOUND) {
1060 trySplines = splineEdges(g, compoundEdges, EDGETYPE_SPLINE);
1061 /* When doing the edges again, accept edges done by compoundEdges */
1062 if (trySplines)
1063 Nop = 2;
1064 }
1065 if (trySplines || et != EDGETYPE_COMPOUND) {
1066 if (HAS_CLUST_EDGE(g)) {
1067 agwarningf(
1068 "splines and cluster edges not supported - using line segments\n");
1069 et = EDGETYPE_LINE;
1070 } else {
1071 spline_edges1(g, et);
1072 }
1073 }
1074 Nop = 0;
1075 }
1076 if (State < GVSPLINES)
1077 spline_edges1(g, et);
1078}
1079
1081{
1082 double save_scale = PSinputscale;
1083
1085 fdp_init_graph(g);
1086 if (fdpLayout(g) != 0) {
1087 return;
1088 }
1090
1091 if (EDGE_TYPE(g) != EDGETYPE_NONE) fdpSplines (g);
1092
1093 gv_postprocess(g, 0);
1094 PSinputscale = save_scale;
1095}
int normalize(graph_t *g)
Definition adjust.c:744
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define MIN(a, b)
Definition arith.h:28
#define M_PI
Definition arith.h:41
#define DNODE(n)
Definition circular.h:74
#define PARENT(n)
Definition circular.h:83
#define parent(i)
Definition closest.c:78
int compoundEdges(graph_t *g, expand_t *pm, int edgetype)
void setEdgeType(graph_t *g, int defaultValue)
Definition utils.c:1443
int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum)
Definition utils.c:33
double get_inputscale(graph_t *g)
Definition utils.c:71
void compute_bb(graph_t *g)
Definition utils.c:629
graph_t ** findCComp(graph_t *g, size_t *cnt, int *pinned)
Definition comp.c:61
#define P_PIN
Definition const.h:263
#define BOTTOM_IX
Definition const.h:111
#define P_SET
Definition const.h:261
#define TOP_IX
Definition const.h:113
#define EDGETYPE_SPLINE
Definition const.h:253
#define CL_OFFSET
Definition const.h:151
#define DEFAULT_NODEHEIGHT
Definition const.h:72
#define DEFAULT_NODEPENWIDTH
Definition const.h:77
#define DEFAULT_NODEWIDTH
Definition const.h:74
#define MAXDIM
Definition const.h:169
#define P_FIX
Definition const.h:262
#define EDGETYPE_ORTHO
Definition const.h:252
#define MIN_NODEPENWIDTH
Definition const.h:78
#define EDGETYPE_LINE
Definition const.h:249
#define EDGETYPE_NONE
Definition const.h:248
#define GVSPLINES
Definition const.h:173
#define EDGETYPE_COMPOUND
Definition const.h:254
static float dy
Definition draw.c:38
static float dx
Definition draw.c:37
static void del(Dict_t *d, Dtlink_t **set, Agedge_t *e)
Definition edge.c:156
static snode ** pq
Definition fPQ.c:19
void fdp_init_node_edge(Agraph_t *g)
Definition fdpinit.c:87
#define PS2INCH(a_points)
Definition geom.h:70
#define POINTS(a_inches)
Definition geom.h:68
#define POINTS_PER_INCH
Definition geom.h:64
#define INCH2PS(a_inches)
Definition geom.h:69
int State
Definition globals.h:65
int Nop
Definition globals.h:57
Agsym_t * G_margin
Definition globals.h:77
Agsym_t * N_penwidth
Definition globals.h:89
double PSinputscale
Definition globals.h:58
unsigned short Ndim
Definition globals.h:64
static int Verbose
Definition gml2gv.c:22
void free(void *)
node NULL
Definition grammar.y:149
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:199
int agnnodes(Agraph_t *g)
Definition graph.c:158
Agsym_t * agattr(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up attributes of a graph
Definition attr.c:341
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:481
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:458
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:260
#define ED_dist(e)
Definition types.h:602
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:23
#define ED_count(e)
Definition types.h:580
#define agtail(e)
Definition cgraph.h:889
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:93
#define ED_factor(e)
Definition types.h:585
#define aghead(e)
Definition cgraph.h:890
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:38
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:84
#define ED_to_virt(e)
Definition types.h:599
void agwarningf(const char *fmt,...)
Definition agerror.c:173
void agerrorf(const char *fmt,...)
Definition agerror.c:165
#define GD_border(g)
Definition types.h:359
#define GD_clust(g)
Definition types.h:360
#define GD_alg(g)
Definition types.h:358
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:96
#define GD_bb(g)
Definition types.h:354
Agdesc_t Agstrictdirected
strict directed. A strict graph cannot have multi-edges or self-arcs.
Definition graph.c:274
#define GD_n_cluster(g)
Definition types.h:389
Agraph_t * agopen(char *name, Agdesc_t desc, Agdisc_t *disc)
creates a new graph with the given name and kind
Definition graph.c:44
#define GD_ndim(g)
Definition types.h:390
#define GD_label(g)
Definition types.h:374
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:147
#define ND_outline_width(n)
Definition types.h:516
#define ND_outline_height(n)
Definition types.h:517
#define ND_ht(n)
Definition types.h:500
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_pinned(n)
Definition types.h:519
#define ND_clust(n)
Definition types.h:489
#define ND_alg(n)
Definition types.h:484
#define ND_rw(n)
Definition types.h:525
#define ND_height(n)
Definition types.h:498
#define ND_width(n)
Definition types.h:536
#define ND_lw(n)
Definition types.h:506
#define ND_shape_info(n)
Definition types.h:529
#define ND_pos(n)
Definition types.h:520
#define ND_shape(n)
Definition types.h:528
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
int agdelete(Agraph_t *g, void *obj)
deletes object. Equivalent to agclose, agdelnode, and agdeledge for obj being a graph,...
Definition obj.c:19
Agraph_t * agroot(void *obj)
Definition obj.c:167
#define AGSEQ(obj)
Definition cgraph.h:225
@ AGRAPH
Definition cgraph.h:207
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:88
int agdelrec(void *obj, const char *name)
deletes a named record from one object
Definition rec.c:136
Agraph_t * agparent(Agraph_t *g)
Definition subg.c:90
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:77
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:82
static double penwidth[]
void do_graph_label(graph_t *sg)
Set characteristics of graph label if it exists.
Definition input.c:836
static erec * getEdgeList(node_t *n, graph_t *g)
Definition layout.c:557
static void fdpSplines(graph_t *g)
Definition layout.c:1053
static void setBB(graph_t *g)
Definition layout.c:941
static void init_info(graph_t *g, layout_info *infop)
Definition layout.c:960
static void fdp_init_graph(Agraph_t *g)
Definition layout.c:1019
static char * portName(graph_t *g, bport_t *p)
Definition layout.c:285
void fdp_layout(graph_t *g)
Definition layout.c:1080
static void evalPositions(graph_t *g, graph_t *rootg)
Definition layout.c:239
static int genPorts(node_t *n, erec *er, bport_t *pp, int idx, double bnd)
Definition layout.c:623
static int ecmp(const void *v1, const void *v2)
Definition layout.c:534
static void addEdge(edge_t *de, edge_t *e)
Definition layout.c:348
static void chkPos(graph_t *g, node_t *n, layout_info *infop, boxf *bbp)
Definition layout.c:305
static node_t * mkDeriveNode(graph_t *dg, char *name)
Definition layout.c:180
static graph_t * expandCluster(node_t *n, graph_t *cg)
Definition layout.c:672
static void freeDerivedGraph(graph_t *g, graph_t **cc)
Definition layout.c:204
static void freeDeriveNode(node_t *n)
Definition layout.c:192
static int fdpLayout(graph_t *g)
Definition layout.c:1031
#define ANG
Definition layout.c:550
static void copyAttr(graph_t *g, graph_t *dg, char *attr)
Definition layout.c:364
static void freeGData(graph_t *g)
Definition layout.c:199
#define NEW_EDGE(e)
Definition layout.c:66
static void setClustNodes(graph_t *root)
Definition layout.c:727
static void mkClusters(graph_t *g, clist_t *pclist, graph_t *parent)
Definition layout.c:978
static void finalCC(graph_t *g, size_t c_cnt, graph_t **cc, pointf *pts, graph_t *rg, layout_info *infop)
Definition layout.c:77
#define BSZ
Definition layout.c:276
static int layout(graph_t *g, layout_info *infop)
Definition layout.c:809
static graph_t * deriveGraph(graph_t *g, layout_info *infop)
Definition layout.c:386
#define DEFINE_LIST(name, type)
Definition list.h:26
#define IS_CLUST_NODE(n)
Definition macros.h:23
#define EDGE_TYPE(g)
Definition macros.h:25
#define HAS_CLUST_EDGE(g)
Definition macros.h:24
#define delta
Definition maze.c:133
#define ND_id(n)
Definition mm2gv.c:39
NEATOPROCS_API bool neato_set_aspect(graph_t *g)
NEATOPROCS_API int splineEdges(graph_t *, int(*edgefn)(graph_t *, expand_t *, int), int)
NEATOPROCS_API int spline_edges1(graph_t *g, int)
pack_mode getPackInfo(Agraph_t *g, pack_mode dflt, int dfltMargin, pack_info *pinfo)
Definition pack.c:1253
pointf * putGraphs(size_t ng, Agraph_t **gs, Agraph_t *root, pack_info *pinfo)
Definition pack.c:856
support for connected components
@ l_node
Definition pack.h:55
void gv_postprocess(Agraph_t *g, int allowTranslation)
Definition postproc.c:601
#define alpha
Definition shapes.c:4068
static double cg(SparseMatrix A, const double *precond, int n, int dim, double *x0, double *rhs, double tol, double maxit)
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
graph or subgraph
Definition cgraph.h:425
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:639
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
Definition layout.c:60
double dist2
Definition layout.c:63
edge_t * e
Definition layout.c:61
double alpha
Definition layout.c:62
attrsym_t * G_coord
Definition layout.c:53
pack_info pack
Definition layout.c:57
attrsym_t * G_width
Definition layout.c:54
int gid
Definition layout.c:56
graph_t * rootg
Definition layout.c:52
attrsym_t * G_height
Definition layout.c:55
pack_mode mode
Definition pack.h:72
bool * fixed
Definition pack.h:73
double x
Definition geom.h:29
double y
Definition geom.h:29
void fdp_initParams(graph_t *g)
Definition tlayout.c:168
void fdp_tLayout(graph_t *g, xparams *xpms)
Definition tlayout.c:614
void fdp_xLayout(graph_t *g, xparams *xpms)
Definition xlayout.c:515