Graphviz 16.1.1~dev.20261104.1411
Loading...
Searching...
No Matches
position.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11/*
12 * position(g): set ND_coord(n) (x and y) for all nodes n of g, using
13 * GD_rank(g). (the graph may be modified by merging certain edges with a common
14 * endpoint.) the coordinates are computed by constructing and ranking an
15 * auxiliary graph. then leaf nodes are inserted in the fast graph. cluster
16 * boundary nodes are created and correctly separated.
17 */
18
19#include "config.h"
20
21#include <common/geomprocs.h>
22#include <dotgen/aspect.h>
23#include <dotgen/dot.h>
24#include <math.h>
25#include <stdbool.h>
26#include <stdlib.h>
27#include <util/alloc.h>
28#include <util/gv_math.h>
29#include <util/unused.h>
30
31static int nsiter2(graph_t *g);
32
34static WUR int create_aux_edges(graph_t *g);
35
36static void remove_aux_edges(graph_t *g);
37static void set_xcoords(graph_t *g);
38static void set_ycoords(graph_t *g);
39static void set_aspect(graph_t *g);
40static void expand_leaves(graph_t *g);
41static void make_lrvn(graph_t *g);
42static void contain_nodes(graph_t *g);
43static bool idealsize(graph_t *g, double);
44
45#if defined(DEBUG) && DEBUG > 1
46static void dumpNS(graph_t *g) {
47 node_t *n = GD_nlist(g);
48 elist el;
49 edge_t *e;
50
51 while (n) {
52 el = ND_out(n);
53 for (size_t i = 0; i < el.size; i++) {
54 e = el.list[i];
55 fprintf(stderr, "%s(%x) -> ", agnameof(agtail(e)), agtail(e));
56 fprintf(stderr, "%s(%x) : %d\n", agnameof(aghead(e)), aghead(e),
57 ED_minlen(e));
58 }
59 n = ND_next(n);
60 }
61}
62#endif
63
64/* When source and/or sink nodes are defined, it is possible that
65 * after the auxiliary edges are added, the graph may still have 2 or
66 * 3 components. To fix this, we put trivial constraints connecting the
67 * first items of each rank.
68 */
69static void connectGraph(graph_t *g) {
70 int i, j, r;
71 bool found;
72 node_t *tp;
73 node_t *hp;
74 node_t *sn;
75 edge_t *e;
76 rank_t *rp;
77
78 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
79 rp = GD_rank(g) + r;
80 found = false;
81 tp = NULL;
82 for (i = 0; i < rp->n; i++) {
83 tp = rp->v[i];
84 if (ND_save_out(tp).list) {
85 for (j = 0; (e = ND_save_out(tp).list[j]); j++) {
86 if (ND_rank(aghead(e)) > r || ND_rank(agtail(e)) > r) {
87 found = true;
88 break;
89 }
90 }
91 if (found)
92 break;
93 }
94 if (ND_save_in(tp).list) {
95 for (j = 0; (e = ND_save_in(tp).list[j]); j++) {
96 if (ND_rank(agtail(e)) > r || ND_rank(aghead(e)) > r) {
97 found = true;
98 break;
99 }
100 }
101 if (found)
102 break;
103 }
104 }
105 if (found || !tp)
106 continue;
107 tp = rp->v[0];
108 if (r < GD_maxrank(g))
109 hp = (rp + 1)->v[0];
110 else
111 hp = (rp - 1)->v[0];
112 assert(hp);
113 sn = virtual_node(g);
115 make_aux_edge(sn, tp, 0, 0);
116 make_aux_edge(sn, hp, 0, 0);
117 ND_rank(sn) = MIN(ND_rank(tp), ND_rank(hp));
118 }
119}
120
122 if (GD_nlist(g) == NULL)
123 return 0; // ignore empty graph
124 mark_lowclusters(g); /* we could remove from splines.c now */
125 set_ycoords(g);
126 if (Concentrate) {
127 const int rc = dot_concentrate(g);
128 if (rc != 0) {
129 return rc;
130 }
131 }
132 expand_leaves(g);
133 if (flat_edges(g))
134 set_ycoords(g);
135 {
136 const int rc = create_aux_edges(g);
137 if (rc != 0) {
138 return rc;
139 }
140 }
141 if (rank(g, 2, nsiter2(g))) { /* LR balance == 2 */
142 connectGraph(g);
143 const int rank_result = rank(g, 2, nsiter2(g));
144 assert(rank_result == 0);
145 (void)rank_result;
146 }
147 set_xcoords(g);
148 set_aspect(g);
149 remove_aux_edges(g); /* must come after set_aspect since we now
150 * use GD_ln and GD_rn for bbox width.
151 */
152 return 0;
153}
154
155static int nsiter2(graph_t *g) {
156 int maxiter = INT_MAX;
157 char *s;
158
159 if ((s = agget(g, "nslimit")))
160 maxiter = scale_clamp(agnnodes(g), atof(s));
161 return maxiter;
162}
163
164static bool go(node_t *u, node_t *v) {
165 int i;
166 edge_t *e;
167
168 if (u == v)
169 return true;
170 for (i = 0; (e = ND_out(u).list[i]); i++) {
171 if (go(aghead(e), v))
172 return true;
173 }
174 return false;
175}
176
177static bool canreach(node_t *u, node_t *v) { return go(u, v); }
178
179edge_t *make_aux_edge(node_t *u, node_t *v, double len, int wt) {
180 Agedgepair_t *e2 = gv_alloc(sizeof(Agedgepair_t));
181 AGTYPE(&e2->in) = AGINEDGE;
182 AGTYPE(&e2->out) = AGOUTEDGE;
183 e2->out.base.data = gv_alloc(sizeof(Agedgeinfo_t));
184 edge_t *const e = &e2->out;
185
186 agtail(e) = u;
187 aghead(e) = v;
188 if (len > INT_MAX) {
189 agerrorf(
190 "Edge length %f larger than maximum %d allowed.\nCheck for overwide "
191 "node(s).\n",
192 len, INT_MAX);
193 free(e2->out.base.data);
194 free(e2);
195 return NULL;
196 }
197 ED_minlen(e) = ROUND(len);
198 ED_weight(e) = wt;
199 fast_edge(e);
200 return e;
201}
202
204 int i, j, n_in;
205 node_t *n;
206
207 /* allocate space for aux edge lists */
208 for (n = GD_nlist(g); n; n = ND_next(n)) {
209 ND_save_in(n) = ND_in(n);
210 ND_save_out(n) = ND_out(n);
211 for (i = 0; ND_out(n).list[i]; i++)
212 ;
213 for (j = 0; ND_in(n).list[j]; j++)
214 ;
215 n_in = i + j;
216 alloc_elist(n_in + 3, ND_in(n));
217 alloc_elist(3, ND_out(n));
218 }
219}
220
223 int i, j;
224 int m0;
225 double width;
226 int sep[2];
227 int nodesep; /* separation between nodes on same rank */
228 edge_t *e, *e0, *e1;
229 node_t *u, *v, *t0, *h0;
230 rank_t *rank = GD_rank(g);
231
232 /* Use smaller separation on odd ranks if g has edge labels */
233 if (GD_has_labels(g->root) & EDGE_LABEL) {
234 sep[0] = GD_nodesep(g);
235 sep[1] = 5;
236 } else {
237 sep[1] = sep[0] = GD_nodesep(g);
238 }
239 /* make edges to constrain left-to-right ordering */
240 for (i = GD_minrank(g); i <= GD_maxrank(g); i++) {
241 if (rank[i].v[0] == NULL) {
242 continue;
243 }
244 double last = ND_rank(rank[i].v[0]) = 0;
245 nodesep = sep[i & 1];
246 for (j = 0; j < rank[i].n; j++) {
247 u = rank[i].v[j];
248 ND_mval(u) = ND_rw(u); /* keep it somewhere safe */
249 if (ND_other(u).size > 0) { /* compute self size */
250 /* FIX: dot assumes all self-edges go to the right. This
251 * is no longer true, though makeSelfEdge still attempts to
252 * put as many as reasonable on the right. The dot code
253 * should be modified to allow a box reflecting the placement
254 * of all self-edges, and use that to reposition the nodes.
255 * Note that this would not only affect left and right
256 * positioning but may also affect interrank spacing.
257 */
258 double sw = 0; // self width
259 for (size_t k = 0; (e = ND_other(u).list[k]); k++) {
260 if (agtail(e) == aghead(e)) {
261 sw += selfRightSpace(e);
262 }
263 }
264 ND_rw(u) += sw; /* increment to include self edges */
265 }
266 v = rank[i].v[j + 1];
267 if (v) {
268 width = ND_rw(u) + ND_lw(v) + nodesep;
269 e0 = make_aux_edge(u, v, width, 0);
270 if (e0 == NULL) {
271 return -1;
272 }
273 last = (ND_rank(v) = last + width);
274 }
275
276 /* constraints from labels of flat edges on previous rank */
277 if ((e = ND_alg(u))) {
278 e0 = ND_save_out(u).list[0];
279 e1 = ND_save_out(u).list[1];
280 if (ND_order(aghead(e0)) > ND_order(aghead(e1))) {
281 SWAP(&e0, &e1);
282 }
283 m0 = ED_minlen(e) * GD_nodesep(g) / 2;
284 double m1 = m0 + ND_rw(aghead(e0)) + ND_lw(agtail(e0));
285 /* these guards are needed because the flat edges
286 * work very poorly with cluster layout */
287 if (!canreach(agtail(e0), aghead(e0)))
288 if (make_aux_edge(aghead(e0), agtail(e0), m1, ED_weight(e)) == NULL) {
289 return -1;
290 }
291 m1 = m0 + ND_rw(agtail(e1)) + ND_lw(aghead(e1));
292 if (!canreach(aghead(e1), agtail(e1)))
293 if (make_aux_edge(agtail(e1), aghead(e1), m1, ED_weight(e)) == NULL) {
294 return -1;
295 }
296 }
297
298 /* position flat edge endpoints */
299 for (size_t k = 0; k < ND_flat_out(u).size; k++) {
300 e = ND_flat_out(u).list[k];
301 if (ND_order(agtail(e)) < ND_order(aghead(e))) {
302 t0 = agtail(e);
303 h0 = aghead(e);
304 } else {
305 t0 = aghead(e);
306 h0 = agtail(e);
307 }
308
309 width = ND_rw(t0) + ND_lw(h0);
310 m0 = ED_minlen(e) * GD_nodesep(g) + width;
311
312 if ((e0 = find_fast_edge(t0, h0))) {
313 /* flat edge between adjacent neighbors
314 * ED_dist contains the largest label width.
315 */
316 m0 = MAX(m0, width + GD_nodesep(g) + ROUND(ED_dist(e)));
317 ED_minlen(e0) = MAX(ED_minlen(e0), m0);
318 ED_weight(e0) = MAX(ED_weight(e0), ED_weight(e));
319 } else if (!ED_label(e)) {
320 /* unlabeled flat edge between non-neighbors
321 * ED_minlen(e) is max of ED_minlen of all equivalent
322 * edges.
323 */
324 if (make_aux_edge(t0, h0, m0, ED_weight(e)) == NULL) {
325 return -1;
326 }
327 }
328 /* labeled flat edges between non-neighbors have already
329 * been constrained by the label above.
330 */
331 }
332 }
333 }
334 return 0;
335}
336
341 int i, m0, m1;
342 node_t *n, *sn;
343 edge_t *e;
344
345 for (n = GD_nlist(g); n; n = ND_next(n)) {
346 if (ND_save_out(n).list)
347 for (i = 0; (e = ND_save_out(n).list[i]); i++) {
348 sn = virtual_node(g);
350 m0 = (ED_head_port(e).p.x - ED_tail_port(e).p.x);
351 if (m0 > 0)
352 m1 = 0;
353 else {
354 m1 = -m0;
355 m0 = 0;
356 }
357 if (make_aux_edge(sn, agtail(e), m0 + 1, ED_weight(e)) == NULL) {
358 return -1;
359 }
360 if (make_aux_edge(sn, aghead(e), m1 + 1, ED_weight(e)) == NULL) {
361 return -1;
362 }
363 ND_rank(sn) =
364 MIN(ND_rank(agtail(e)) - m0 - 1, ND_rank(aghead(e)) - m1 - 1);
365 }
366 }
367 return 0;
368}
369
371 int c;
372 edge_t *e;
373
374 if (g != dot_root(g)) {
375 contain_nodes(g);
376 if ((e = find_fast_edge(GD_ln(g), GD_rn(g)))) /* maybe from lrvn()?*/
377 ED_weight(e) += 128;
378 else
379 make_aux_edge(GD_ln(g), GD_rn(g), 1, 128); /* clust compaction edge */
380 }
381 for (c = 1; c <= GD_n_cluster(g); c++)
383}
384
386 edge_t *e;
387
388 if (ND_node_type(v) != VIRTUAL)
389 return false;
390 for (e = ND_save_out(v).list[0]; ED_to_orig(e); e = ED_to_orig(e))
391 ;
392 if (agcontains(g, agtail(e)))
393 return false;
394 if (agcontains(g, aghead(e)))
395 return false;
396 return true;
397}
398
399/* Guarantee nodes outside the cluster g are placed outside of it.
400 * This is done by adding constraints to make sure such nodes have
401 * a gap of margin from the left or right bounding box node ln or rn.
402 *
403 * We could probably reduce some of these constraints by checking if
404 * the node is in a cluster, since elsewhere we make constrain a
405 * separate between clusters. Also, we should be able to skip the
406 * first loop if g is the root graph.
407 */
409 int i, c, r, margin;
410 node_t *u, *v;
411
412 margin = late_int(g, G_margin, CL_OFFSET, 0);
413 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
414 if (GD_rank(g)[r].n == 0)
415 continue;
416 v = GD_rank(g)[r].v[0];
417 if (v == NULL)
418 continue;
419 for (i = ND_order(v) - 1; i >= 0; i--) {
420 u = GD_rank(dot_root(g))[r].v[i];
421 /* can't use "is_a_vnode_of" because elists are swapped */
422 if (ND_node_type(u) == NORMAL || vnode_not_related_to(g, u)) {
423 make_aux_edge(u, GD_ln(g), margin + ND_rw(u), 0);
424 break;
425 }
426 }
427 for (i = ND_order(v) + GD_rank(g)[r].n; i < GD_rank(dot_root(g))[r].n;
428 i++) {
429 u = GD_rank(dot_root(g))[r].v[i];
430 if (ND_node_type(u) == NORMAL || vnode_not_related_to(g, u)) {
431 make_aux_edge(GD_rn(g), u, margin + ND_lw(u), 0);
432 break;
433 }
434 }
435 }
436
437 for (c = 1; c <= GD_n_cluster(g); c++)
439}
440
441/* Make sure boxes of subclusters of g are offset from the
442 * box of g. This is done by a constraint between the left and
443 * right bounding box nodes ln and rn of g and a subcluster.
444 * The gap needs to include any left or right labels.
445 */
446static void contain_subclust(graph_t *g) {
447 int margin, c;
448 graph_t *subg;
449
450 margin = late_int(g, G_margin, CL_OFFSET, 0);
451 make_lrvn(g);
452 for (c = 1; c <= GD_n_cluster(g); c++) {
453 subg = GD_clust(g)[c];
454 make_lrvn(subg);
455 make_aux_edge(GD_ln(g), GD_ln(subg), margin + GD_border(g)[LEFT_IX].x, 0);
456 make_aux_edge(GD_rn(subg), GD_rn(g), margin + GD_border(g)[RIGHT_IX].x, 0);
457 contain_subclust(subg);
458 }
459}
460
461/* Guarantee space between subcluster of g.
462 * This is done by adding a constraint between the right bbox node rn
463 * of the left cluster and the left bbox node ln of the right cluster.
464 * This is only done if the two clusters overlap in some rank.
465 */
466static void separate_subclust(graph_t *g) {
467 int i, j, margin;
468 graph_t *low, *high;
469 graph_t *left, *right;
470
471 margin = late_int(g, G_margin, CL_OFFSET, 0);
472 for (i = 1; i <= GD_n_cluster(g); i++)
473 make_lrvn(GD_clust(g)[i]);
474 for (i = 1; i <= GD_n_cluster(g); i++) {
475 for (j = i + 1; j <= GD_n_cluster(g); j++) {
476 low = GD_clust(g)[i];
477 high = GD_clust(g)[j];
478 if (GD_minrank(low) > GD_minrank(high)) {
479 SWAP(&low, &high);
480 }
481 if (GD_maxrank(low) < GD_minrank(high))
482 continue;
483 if (ND_order(GD_rank(low)[GD_minrank(high)].v[0]) <
484 ND_order(GD_rank(high)[GD_minrank(high)].v[0])) {
485 left = low;
486 right = high;
487 } else {
488 left = high;
489 right = low;
490 }
491 make_aux_edge(GD_rn(left), GD_ln(right), margin, 0);
492 }
494 }
495}
496
497/* create constraints for:
498 * node containment in clusters,
499 * cluster containment in clusters,
500 * separation of sibling clusters.
501 */
502static void pos_clusters(graph_t *g) {
503 if (GD_n_cluster(g) > 0) {
508 }
509}
510
511static void compress_graph(graph_t *g) {
512 double x;
513 pointf p;
514
515 if (GD_drawing(g)->ratio_kind != R_COMPRESS)
516 return;
517 p = GD_drawing(g)->size;
518 if (p.x * p.y <= 1)
519 return;
520 contain_nodes(g);
521 if (!GD_flip(g))
522 x = p.x;
523 else
524 x = p.y;
525
526 /* Guard against huge size attribute since max. edge length is USHRT_MAX
527 * A warning might be called for. Also, one could check that the graph
528 * already fits GD_drawing(g)->size and return immediately.
529 */
530 x = MIN(x, USHRT_MAX);
531 make_aux_edge(GD_ln(g), GD_rn(g), x, 1000);
532}
533
534static int create_aux_edges(graph_t *g) {
536 {
537 const int rc = make_LR_constraints(g);
538 if (rc != 0) {
539 return rc;
540 }
541 }
542 {
543 const int rc = make_edge_pairs(g);
544 if (rc != 0) {
545 return rc;
546 }
547 }
548 pos_clusters(g);
550 return 0;
551}
552
553static void remove_aux_edges(graph_t *g) {
554 int i;
555 node_t *n, *nnext, *nprev;
556 edge_t *e;
557
558 for (n = GD_nlist(g); n; n = ND_next(n)) {
559 for (i = 0; (e = ND_out(n).list[i]); i++) {
560 free(e->base.data);
561 free(e);
562 }
563 free_list(ND_out(n));
564 free_list(ND_in(n));
565 ND_out(n) = ND_save_out(n);
566 ND_in(n) = ND_save_in(n);
567 }
568 /* cannot be merged with previous loop */
569 nprev = NULL;
570 for (n = GD_nlist(g); n; n = nnext) {
571 nnext = ND_next(n);
572 if (ND_node_type(n) == SLACKNODE) {
573 if (nprev)
574 ND_next(nprev) = nnext;
575 else
576 GD_nlist(g) = nnext;
577 if (nnext != NULL) {
578 ND_prev(nnext) = nprev;
579 }
580 free(n->base.data);
581 free(n);
582 } else
583 nprev = n;
584 }
585}
586
588static void set_xcoords(graph_t *g) {
589 rank_t *rank = GD_rank(g);
590
591 for (int i = GD_minrank(g); i <= GD_maxrank(g); i++) {
592 for (int j = 0; j < rank[i].n; j++) {
593 node_t *const v = rank[i].v[j];
594 ND_coord(v).x = ND_rank(v);
595 ND_rank(v) = i;
596 }
597 }
598}
599
600/* Expand cluster height by delta, adding half to top
601 * and half to bottom. If the bottom expansion exceeds the
602 * ht1 of the rank, shift the ranks in the cluster up.
603 * If the top expansion, including any shift from the bottom
604 * expansion, exceeds to ht2 of the rank, shift the ranks above
605 * the cluster up.
606 *
607 * FIX: There can be excess space between ranks. Not sure where this is
608 * coming from but it could be cleaned up.
609 */
610static void adjustSimple(graph_t *g, double delta, int margin_total) {
611 int r;
612 double deltop;
613 graph_t *root = dot_root(g);
614 rank_t *rank = GD_rank(root);
615 int maxr = GD_maxrank(g);
616 int minr = GD_minrank(g);
617
618 const double bottom = (delta + 1) / 2;
619 const double delbottom = GD_ht1(g) + bottom - (rank[maxr].ht1 - margin_total);
620 if (delbottom > 0) {
621 for (r = maxr; r >= minr; r--) {
622 if (rank[r].n > 0)
623 ND_coord(rank[r].v[0]).y += delbottom;
624 }
625 deltop = GD_ht2(g) + (delta - bottom) + delbottom -
626 (rank[minr].ht2 - margin_total);
627 } else
628 deltop = GD_ht2(g) + (delta - bottom) - (rank[minr].ht2 - margin_total);
629 if (deltop > 0) {
630 for (r = minr - 1; r >= GD_minrank(root); r--) {
631 if (rank[r].n > 0)
632 ND_coord(rank[r].v[0]).y += deltop;
633 }
634 }
635 GD_ht2(g) += delta - bottom;
636 GD_ht1(g) += bottom;
637}
638
639/* Recursively adjust ranks to take into account
640 * wide cluster labels when rankdir=LR.
641 * We divide the extra space between the top and bottom.
642 * Adjust the ht1 and ht2 values in the process.
643 */
644static void adjustRanks(graph_t *g, int margin_total) {
645 double lht; /* label height */
646 double rht; /* height between top and bottom ranks */
647 int maxr, minr, margin;
648 int c;
649 double delta, ht1, ht2;
650
652 if (g == dot_root(g))
653 margin = 0;
654 else
655 margin = late_int(g, G_margin, CL_OFFSET, 0);
656
657 ht1 = GD_ht1(g);
658 ht2 = GD_ht2(g);
659
660 for (c = 1; c <= GD_n_cluster(g); c++) {
661 graph_t *subg = GD_clust(g)[c];
662 adjustRanks(subg, margin + margin_total);
663 if (GD_maxrank(subg) == GD_maxrank(g))
664 ht1 = fmax(ht1, GD_ht1(subg) + margin);
665 if (GD_minrank(subg) == GD_minrank(g))
666 ht2 = fmax(ht2, GD_ht2(subg) + margin);
667 }
668
669 GD_ht1(g) = ht1;
670 GD_ht2(g) = ht2;
671
672 if (g != dot_root(g) && GD_label(g)) {
673 lht = MAX(GD_border(g)[LEFT_IX].y, GD_border(g)[RIGHT_IX].y);
674 maxr = GD_maxrank(g);
675 minr = GD_minrank(g);
676 rht = ND_coord(rank[minr].v[0]).y - ND_coord(rank[maxr].v[0]).y;
677 delta = lht - (rht + ht1 + ht2);
678 if (delta > 0) {
679 adjustSimple(g, delta, margin_total);
680 }
681 }
682
683 /* update the global ranks */
684 if (g != dot_root(g)) {
685 rank[GD_minrank(g)].ht2 = fmax(rank[GD_minrank(g)].ht2, GD_ht2(g));
686 rank[GD_maxrank(g)].ht1 = fmax(rank[GD_maxrank(g)].ht1, GD_ht1(g));
687 }
688}
689
690/* recursively compute cluster ht requirements. assumes GD_ht1(subg) and ht2
691 * are computed from primitive nodes only. updates ht1 and ht2 to reflect
692 * cluster nesting and labels. also maintains global rank ht1 and ht2.
693 * Return true if some cluster has a label.
694 */
695static int clust_ht(Agraph_t *g) {
696 int c;
697 double ht1, ht2;
698 graph_t *subg;
700 int margin, haveClustLabel = 0;
701
702 if (g == dot_root(g))
703 margin = CL_OFFSET;
704 else
705 margin = late_int(g, G_margin, CL_OFFSET, 0);
706
707 ht1 = GD_ht1(g);
708 ht2 = GD_ht2(g);
709
710 /* account for sub-clusters */
711 for (c = 1; c <= GD_n_cluster(g); c++) {
712 subg = GD_clust(g)[c];
713 haveClustLabel |= clust_ht(subg);
714 if (GD_maxrank(subg) == GD_maxrank(g))
715 ht1 = MAX(ht1, GD_ht1(subg) + margin);
716 if (GD_minrank(subg) == GD_minrank(g))
717 ht2 = MAX(ht2, GD_ht2(subg) + margin);
718 }
719
720 /* account for a possible cluster label in clusters */
721 /* room for root graph label is handled in dotneato_postprocess */
722 if (g != dot_root(g) && GD_label(g)) {
723 haveClustLabel = 1;
724 if (!GD_flip(agroot(g))) {
725 ht1 += GD_border(g)[BOTTOM_IX].y;
726 ht2 += GD_border(g)[TOP_IX].y;
727 }
728 }
729 GD_ht1(g) = ht1;
730 GD_ht2(g) = ht2;
731
732 /* update the global ranks */
733 if (g != dot_root(g)) {
734 rank[GD_minrank(g)].ht2 = MAX(rank[GD_minrank(g)].ht2, ht2);
735 rank[GD_maxrank(g)].ht1 = MAX(rank[GD_maxrank(g)].ht1, ht1);
736 }
737
738 return haveClustLabel;
739}
740
741/* set y coordinates of nodes, a rank at a time */
742static void set_ycoords(graph_t *g) {
743 edge_t *e;
744 rank_t *rank = GD_rank(g);
745 graph_t *clust;
746
747 /* scan ranks for tallest nodes. */
748 for (int r = GD_minrank(g); r <= GD_maxrank(g); r++) {
749 for (int i = 0; i < rank[r].n; i++) {
750 node_t *const n = rank[r].v[i];
751
752 /* assumes symmetry, ht1 = ht2 */
753 double ht2 = ND_ht(n) / 2;
754
755 /* have to look for high self-edge labels, too */
756 if (ND_other(n).list)
757 for (int j = 0; (e = ND_other(n).list[j]); j++) {
758 if (agtail(e) == aghead(e)) {
759 if (ED_label(e))
760 ht2 = fmax(ht2, ED_label(e)->dimen.y / 2);
761 }
762 }
763
764 /* update global rank ht */
765 if (rank[r].pht2 < ht2)
766 rank[r].pht2 = rank[r].ht2 = ht2;
767 if (rank[r].pht1 < ht2)
768 rank[r].pht1 = rank[r].ht1 = ht2;
769
770 /* update nearest enclosing cluster rank ht */
771 if ((clust = ND_clust(n))) {
772 int yoff = clust == g ? 0 : late_int(clust, G_margin, CL_OFFSET, 0);
773 if (ND_rank(n) == GD_minrank(clust))
774 GD_ht2(clust) = fmax(GD_ht2(clust), ht2 + yoff);
775 if (ND_rank(n) == GD_maxrank(clust))
776 GD_ht1(clust) = fmax(GD_ht1(clust), ht2 + yoff);
777 }
778 }
779 }
780
781 /* scan sub-clusters */
782 const int lbl = clust_ht(g);
783
784 /* make the initial assignment of ycoords to leftmost nodes by ranks */
785 double maxht = 0;
786 int r = GD_maxrank(g);
787 ND_coord(rank[r].v[0]).y = rank[r].ht1;
788 while (--r >= GD_minrank(g)) {
789 const double d0 =
790 rank[r + 1].pht2 + rank[r].pht1 + GD_ranksep(g); // prim node sep
791 const double d1 = rank[r + 1].ht2 + rank[r].ht1 + CL_OFFSET; // cluster sep
792 const double delta = fmax(d0, d1);
793 if (rank[r].n > 0) { // this may reflect some problem
794 if (rank[r + 1].v[0] != NULL) {
795 ND_coord(rank[r].v[0]).y = ND_coord(rank[r + 1].v[0]).y + delta;
796 }
797 }
798#ifdef DEBUG
799 else
800 fprintf(stderr, "dot set_ycoords: rank %d is empty\n", rank[r].n);
801#endif
802 maxht = fmax(maxht, delta);
803 }
804
805 /* If there are cluster labels and the drawing is rotated, we need special
806 * processing to allocate enough room. We use adjustRanks for this, and then
807 * recompute the maxht if the ranks are to be equally spaced. This seems
808 * simpler and appears to work better than handling equal spacing as a special
809 * case.
810 */
811 if (lbl && GD_flip(g)) {
812 adjustRanks(g, 0);
813 if (GD_exact_ranksep(g)) { /* recompute maxht */
814 maxht = 0;
815 r = GD_maxrank(g);
816 double d0 = ND_coord(rank[r].v[0]).y;
817 while (--r >= GD_minrank(g)) {
818 const double d1 = ND_coord(rank[r].v[0]).y;
819 const double delta = d1 - d0;
820 maxht = fmax(maxht, delta);
821 d0 = d1;
822 }
823 }
824 }
825
826 /* re-assign if ranks are equally spaced */
827 if (GD_exact_ranksep(g)) {
828 for (r = GD_maxrank(g) - 1; r >= GD_minrank(g); r--)
829 if (rank[r].n > 0) /* this may reflect the same problem :-() */
830 ND_coord(rank[r].v[0]).y = ND_coord(rank[r + 1].v[0]).y + maxht;
831 }
832
833 /* copy ycoord assignment from leftmost nodes to others */
834 for (node_t *n = GD_nlist(g); n; n = ND_next(n))
835 ND_coord(n).y = ND_coord(rank[ND_rank(n)].v[0]).y;
836}
837
838/* Compute bounding box of g.
839 * The x limits of clusters are given by the x positions of ln and rn.
840 * This information is stored in the rank field, since it was calculated
841 * using network simplex.
842 * For the root graph, we don't enforce all the constraints on lr and
843 * rn, so we traverse the nodes and subclusters.
844 */
845static void dot_compute_bb(graph_t *g, graph_t *root) {
846 node_t *v;
847 pointf LL, UR;
848
849 if (g == dot_root(g)) {
850 LL.x = INT_MAX;
851 UR.x = -INT_MAX;
852 for (int r = GD_minrank(g); r <= GD_maxrank(g); r++) {
853 int rnkn = GD_rank(g)[r].n;
854 if (rnkn == 0)
855 continue;
856 if ((v = GD_rank(g)[r].v[0]) == NULL)
857 continue;
858 for (int c = 1; ND_node_type(v) != NORMAL && c < rnkn; c++)
859 v = GD_rank(g)[r].v[c];
860 if (ND_node_type(v) == NORMAL) {
861 const double x = ND_coord(v).x - ND_lw(v);
862 LL.x = MIN(LL.x, x);
863 } else
864 continue;
865 /* At this point, we know the rank contains a NORMAL node */
866 v = GD_rank(g)[r].v[rnkn - 1];
867 for (int c = rnkn - 2; ND_node_type(v) != NORMAL; c--)
868 v = GD_rank(g)[r].v[c];
869 const double x = ND_coord(v).x + ND_rw(v);
870 UR.x = MAX(UR.x, x);
871 }
872 const double offset = CL_OFFSET;
873 for (int c = 1; c <= GD_n_cluster(g); c++) {
874 double x = GD_bb(GD_clust(g)[c]).LL.x - offset;
875 LL.x = MIN(LL.x, x);
876 x = GD_bb(GD_clust(g)[c]).UR.x + offset;
877 UR.x = MAX(UR.x, x);
878 }
879 } else {
880 LL.x = ND_rank(GD_ln(g));
881 UR.x = ND_rank(GD_rn(g));
882 }
883 LL.y = ND_coord(GD_rank(root)[GD_maxrank(g)].v[0]).y - GD_ht1(g);
884
885 for (int r = GD_minrank(g); r <= GD_maxrank(g); ++r) {
886 if (GD_rank(root)[r].n == 0) {
887 continue;
888 }
889 if (GD_rank(root)[r].v[0] == NULL) {
890 continue;
891 }
892 UR.y = ND_coord(GD_rank(root)[r].v[0]).y + GD_ht2(g);
893 break;
894 }
895
896 GD_bb(g).LL = LL;
897 GD_bb(g).UR = UR;
898}
899
900static void rec_bb(graph_t *g, graph_t *root) {
901 int c;
902 for (c = 1; c <= GD_n_cluster(g); c++)
903 rec_bb(GD_clust(g)[c], root);
904 dot_compute_bb(g, root);
905}
906
907/* Recursively rescale all bounding boxes using scale factors
908 * xf and yf. We assume all the bboxes have been computed.
909 */
910static void scale_bb(graph_t *g, double xf, double yf) {
911 int c;
912
913 for (c = 1; c <= GD_n_cluster(g); c++)
914 scale_bb(GD_clust(g)[c], xf, yf);
915 GD_bb(g).LL.x *= xf;
916 GD_bb(g).LL.y *= yf;
917 GD_bb(g).UR.x *= xf;
918 GD_bb(g).UR.y *= yf;
919}
920
921/* Set bounding boxes and, if ratio is set, rescale graph.
922 * Note that if some dimension shrinks, there may be problems
923 * with labels.
924 */
925static void set_aspect(graph_t *g) {
926 double xf = 0.0, yf = 0.0, actual, desired;
927 node_t *n;
928 bool filled;
929
930 rec_bb(g, g);
931 if (GD_maxrank(g) > 0 && GD_drawing(g)->ratio_kind) {
932 pointf sz = sub_pointf(GD_bb(g).UR, GD_bb(g).LL); // normalize
933 if (GD_flip(g)) {
934 sz = exch_xyf(sz);
935 }
936 bool scale_it = true;
937 if (GD_drawing(g)->ratio_kind == R_AUTO)
938 filled = idealsize(g, .5);
939 else
940 filled = GD_drawing(g)->ratio_kind == R_FILL;
941 if (filled) {
942 /* fill is weird because both X and Y can stretch */
943 if (GD_drawing(g)->size.x <= 0)
944 scale_it = false;
945 else {
946 xf = GD_drawing(g)->size.x / sz.x;
947 yf = GD_drawing(g)->size.y / sz.y;
948 if (xf < 1.0 || yf < 1.0) {
949 if (xf < yf) {
950 yf /= xf;
951 xf = 1.0;
952 } else {
953 xf /= yf;
954 yf = 1.0;
955 }
956 }
957 }
958 } else if (GD_drawing(g)->ratio_kind == R_EXPAND) {
959 if (GD_drawing(g)->size.x <= 0)
960 scale_it = false;
961 else {
962 xf = GD_drawing(g)->size.x / GD_bb(g).UR.x;
963 yf = GD_drawing(g)->size.y / GD_bb(g).UR.y;
964 if (xf > 1.0 && yf > 1.0) {
965 const double scale = fmin(xf, yf);
966 xf = yf = scale;
967 } else
968 scale_it = false;
969 }
970 } else if (GD_drawing(g)->ratio_kind == R_VALUE) {
971 desired = GD_drawing(g)->ratio;
972 actual = sz.y / sz.x;
973 if (actual < desired) {
974 yf = desired / actual;
975 xf = 1.0;
976 } else {
977 xf = actual / desired;
978 yf = 1.0;
979 }
980 } else
981 scale_it = false;
982 if (scale_it) {
983 if (GD_flip(g)) {
984 SWAP(&xf, &yf);
985 }
986 for (n = GD_nlist(g); n; n = ND_next(n)) {
987 ND_coord(n).x = round(ND_coord(n).x * xf);
988 ND_coord(n).y = round(ND_coord(n).y * yf);
989 }
990 scale_bb(g, xf, yf);
991 }
992 }
993}
994
995/* make space for the leaf nodes of each rank */
996static void make_leafslots(graph_t *g) {
997 int i, j, r;
998 node_t *v;
999
1000 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
1001 j = 0;
1002 for (i = 0; i < GD_rank(g)[r].n; i++) {
1003 v = GD_rank(g)[r].v[i];
1004 ND_order(v) = j;
1005 if (ND_ranktype(v) == LEAFSET)
1006 j = j + ND_UF_size(v);
1007 else
1008 j++;
1009 }
1010 if (j <= GD_rank(g)[r].n)
1011 continue;
1012 node_t **new_v = gv_calloc(j + 1, sizeof(node_t *));
1013 for (i = GD_rank(g)[r].n - 1; i >= 0; i--) {
1014 v = GD_rank(g)[r].v[i];
1015 new_v[ND_order(v)] = v;
1016 }
1017 GD_rank(g)[r].n = j;
1018 new_v[j] = NULL;
1019 free(GD_rank(g)[r].v);
1020 GD_rank(g)[r].v = new_v;
1021 }
1022}
1023
1025 return ED_head_port(e).defined == ED_head_port(f).defined &&
1026 ((ED_head_port(e).p.x == ED_head_port(f).p.x &&
1027 ED_head_port(e).p.y == ED_head_port(f).p.y) ||
1028 !ED_head_port(e).defined) &&
1029 ((ED_tail_port(e).p.x == ED_tail_port(f).p.x &&
1030 ED_tail_port(e).p.y == ED_tail_port(f).p.y) ||
1031 !ED_tail_port(e).defined);
1032}
1033
1034static void expand_leaves(graph_t *g) {
1035 int i, d;
1036 node_t *n;
1037 edge_t *e, *f;
1038
1039 make_leafslots(g);
1040 for (n = GD_nlist(g); n; n = ND_next(n)) {
1041 if (ND_other(n).list)
1042 for (i = 0; (e = ND_other(n).list[i]); i++) {
1043 if ((d = ND_rank(aghead(e)) - ND_rank(aghead(e))) == 0)
1044 continue;
1045 f = ED_to_orig(e);
1046 if (!ports_eq(e, f)) {
1047 zapinlist(&(ND_other(n)), e);
1048 if (d == 1)
1049 fast_edge(e);
1050 /*else unitize(e); ### */
1051 i--;
1052 }
1053 }
1054 }
1055}
1056
1057/* Add left and right slacknodes to a cluster which
1058 * are used in the LP to constrain nodes not in g but
1059 * sharing its ranks to be to the left or right of g
1060 * by a specified amount.
1061 * The slacknodes ln and rn give the x position of the
1062 * left and right side of the cluster's bounding box. In
1063 * particular, any cluster labels on the left or right side
1064 * are inside.
1065 * If a cluster has a label, and we have rankdir!=LR, we make
1066 * sure the cluster is wide enough for the label. Note that
1067 * if the label is wider than the cluster, the nodes in the
1068 * cluster may not be centered.
1069 */
1070static void make_lrvn(graph_t *g) {
1071 node_t *ln, *rn;
1072
1073 if (GD_ln(g))
1074 return;
1075 ln = virtual_node(dot_root(g));
1076 ND_node_type(ln) = SLACKNODE;
1077 rn = virtual_node(dot_root(g));
1078 ND_node_type(rn) = SLACKNODE;
1079
1080 if (GD_label(g) && g != dot_root(g) && !GD_flip(agroot(g))) {
1081 const double w = fmax(GD_border(g)[BOTTOM_IX].x, GD_border(g)[TOP_IX].x);
1082 make_aux_edge(ln, rn, w, 0);
1083 }
1084
1085 GD_ln(g) = ln;
1086 GD_rn(g) = rn;
1087}
1088
1089/* make left and right bounding box virtual nodes ln and rn
1090 * constrain interior nodes
1091 */
1092static void contain_nodes(graph_t *g) {
1093 int margin, r;
1094 node_t *ln, *rn, *v;
1095
1096 margin = late_int(g, G_margin, CL_OFFSET, 0);
1097 make_lrvn(g);
1098 ln = GD_ln(g);
1099 rn = GD_rn(g);
1100 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
1101 if (GD_rank(g)[r].n == 0)
1102 continue;
1103 v = GD_rank(g)[r].v[0];
1104 if (v == NULL) {
1105 agerrorf("contain_nodes clust %s rank %d missing node\n", agnameof(g), r);
1106 continue;
1107 }
1108 make_aux_edge(ln, v, ND_lw(v) + margin + GD_border(g)[LEFT_IX].x, 0);
1109 v = GD_rank(g)[r].v[GD_rank(g)[r].n - 1];
1110 make_aux_edge(v, rn, ND_rw(v) + margin + GD_border(g)[RIGHT_IX].x, 0);
1111 }
1112}
1113
1114/* set g->drawing->size to a reasonable default.
1115 * returns a boolean to indicate if drawing is to
1116 * be scaled and filled */
1117static bool idealsize(graph_t *g, double minallowed) {
1118 double xf, yf, f, R;
1119 pointf b, relpage, margin;
1120
1121 /* try for one page */
1122 relpage = GD_drawing(g)->page;
1123 if (relpage.x < 0.001 || relpage.y < 0.001)
1124 return false; /* no page was specified */
1125 margin = GD_drawing(g)->margin;
1126 relpage = sub_pointf(relpage, margin);
1127 relpage = sub_pointf(relpage, margin);
1128 b.x = GD_bb(g).UR.x;
1129 b.y = GD_bb(g).UR.y;
1130 xf = relpage.x / b.x;
1131 yf = relpage.y / b.y;
1132 if (xf >= 1.0 && yf >= 1.0)
1133 return false; /* fits on one page */
1134
1135 f = MIN(xf, yf);
1136 xf = yf = MAX(f, minallowed);
1137
1138 R = ceil(xf * b.x / relpage.x);
1139 xf = R * relpage.x / b.x;
1140 R = ceil(yf * b.y / relpage.y);
1141 yf = R * relpage.y / b.y;
1142 GD_drawing(g)->size.x = b.x * xf;
1143 GD_drawing(g)->size.y = b.y * yf;
1144 return true;
1145}
static agxbuf last
last message
Definition agerror.c:31
Memory allocation wrappers that exit on failure.
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 ROUND(f)
Definition arith.h:48
#define MAX(a, b)
Definition arith.h:33
#define right(i)
Definition closest.c:74
int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum)
Definition utils.c:40
int dot_concentrate(graph_t *g)
Definition conc.c:202
#define BOTTOM_IX
Definition const.h:111
#define TOP_IX
Definition const.h:113
#define NORMAL
Definition const.h:24
#define CL_OFFSET
Definition const.h:142
#define EDGE_LABEL
Definition const.h:167
#define LEFT_IX
Definition const.h:114
#define SLACKNODE
Definition const.h:26
#define VIRTUAL
Definition const.h:25
#define LEAFSET
Definition const.h:39
#define RIGHT_IX
Definition const.h:112
Agraph_t * dot_root(void *p)
Definition dotinit.c:483
int flat_edges(Agraph_t *)
Definition flat.c:257
void zapinlist(elist *, Agedge_t *)
remove e from list and fill hole with last member of list
Definition fastgr.c:96
Agedge_t * fast_edge(Agedge_t *)
Definition fastgr.c:71
Agedge_t * find_fast_edge(Agnode_t *, Agnode_t *)
Definition fastgr.c:43
Agnode_t * virtual_node(Agraph_t *)
Definition fastgr.c:200
#define left
Definition dthdr.h:12
geometric functions (e.g. on points and boxes)
static WUR pointf sub_pointf(pointf p, pointf q)
Definition geomprocs.h:96
static WUR pointf exch_xyf(pointf p)
Definition geomprocs.h:128
static WUR pointf scale(double c, pointf p)
Definition geomprocs.h:148
bool Concentrate
Definition globals.h:62
Agsym_t * G_margin
Definition globals.h:76
static double len(glCompPoint p)
Definition glutils.c:138
void free(void *)
node NULL
Definition grammar.y:181
int agnnodes(Agraph_t *g)
Definition graph.c:163
char * agget(void *obj, char *name)
Definition attr.c:447
#define ED_to_orig(e)
Definition types.h:598
#define ED_dist(e)
Definition types.h:602
#define ED_minlen(e)
Definition types.h:592
#define agtail(e)
Definition cgraph.h:982
#define ED_weight(e)
Definition types.h:603
#define aghead(e)
Definition cgraph.h:983
#define ED_head_port(e)
Definition types.h:588
#define ED_label(e)
Definition types.h:589
#define ED_tail_port(e)
Definition types.h:597
void agerrorf(const char *fmt,...)
Definition agerror.c:167
#define GD_minrank(g)
Definition types.h:384
#define GD_maxrank(g)
Definition types.h:382
#define GD_drawing(g)
Definition types.h:353
#define GD_border(g)
Definition types.h:359
#define GD_has_labels(g)
Definition types.h:368
#define GD_clust(g)
Definition types.h:360
#define GD_rn(g)
Definition types.h:398
#define GD_rank(g)
Definition types.h:395
#define GD_bb(g)
Definition types.h:354
#define GD_nlist(g)
Definition types.h:393
#define GD_n_cluster(g)
Definition types.h:389
#define GD_ht2(g)
Definition types.h:372
#define GD_label(g)
Definition types.h:374
#define GD_nodesep(g)
Definition types.h:394
#define GD_ln(g)
Definition types.h:381
#define GD_ht1(g)
Definition types.h:371
#define GD_flip(g)
Definition types.h:378
#define GD_exact_ranksep(g)
Definition types.h:363
#define GD_ranksep(g)
Definition types.h:397
#define ND_rank(n)
Definition types.h:523
#define ND_prev(n)
Definition types.h:521
#define ND_ht(n)
Definition types.h:500
#define ND_next(n)
Definition types.h:510
#define ND_clust(n)
Definition types.h:489
#define ND_other(n)
Definition types.h:514
#define ND_save_in(n)
Definition types.h:526
#define ND_alg(n)
Definition types.h:484
#define ND_flat_out(n)
Definition types.h:493
#define ND_rw(n)
Definition types.h:525
#define ND_node_type(n)
Definition types.h:511
#define ND_lw(n)
Definition types.h:506
#define ND_save_out(n)
Definition types.h:527
#define ND_mval(n)
Definition types.h:508
#define ND_order(n)
Definition types.h:513
#define ND_UF_size(n)
Definition types.h:487
#define ND_ranktype(n)
Definition types.h:524
#define ND_coord(n)
Definition types.h:490
#define ND_in(n)
Definition types.h:501
#define ND_out(n)
Definition types.h:515
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
#define AGTYPE(obj)
returns AGRAPH, AGNODE, or AGEDGE depending on the type of the object
Definition cgraph.h:216
int agcontains(Agraph_t *, void *obj)
returns non-zero if obj is a member of (sub)graph
Definition obj.c:235
Agraph_t * agroot(void *obj)
Definition obj.c:170
@ AGOUTEDGE
Definition cgraph.h:207
@ AGINEDGE
Definition cgraph.h:207
Arithmetic helper functions.
static int scale_clamp(int original, double scale)
scale up or down a non-negative integer, clamping to [0, INT_MAX]
Definition gv_math.h:75
#define SWAP(a, b)
Definition gv_math.h:133
void mark_lowclusters(Agraph_t *root)
Definition cluster.c:400
#define delta
Definition maze.c:138
int rank(graph_t *g, int balance, int maxiter)
Definition ns.c:1104
static WUR int make_LR_constraints(graph_t *g)
Definition position.c:222
static void set_ycoords(graph_t *g)
Definition position.c:742
static bool vnode_not_related_to(graph_t *g, node_t *v)
Definition position.c:385
static void separate_subclust(graph_t *g)
Definition position.c:466
static void scale_bb(graph_t *g, double xf, double yf)
Definition position.c:910
static void contain_subclust(graph_t *g)
Definition position.c:446
static void set_aspect(graph_t *g)
Definition position.c:925
static void rec_bb(graph_t *g, graph_t *root)
Definition position.c:900
static void expand_leaves(graph_t *g)
Definition position.c:1034
static WUR int create_aux_edges(graph_t *g)
Definition position.c:534
static void connectGraph(graph_t *g)
Definition position.c:69
static void compress_graph(graph_t *g)
Definition position.c:511
static bool idealsize(graph_t *g, double)
Definition position.c:1117
static void contain_clustnodes(graph_t *g)
Definition position.c:370
static void dot_compute_bb(graph_t *g, graph_t *root)
Definition position.c:845
static void make_lrvn(graph_t *g)
Definition position.c:1070
static WUR int make_edge_pairs(graph_t *g)
Definition position.c:340
static void make_leafslots(graph_t *g)
Definition position.c:996
static bool canreach(node_t *u, node_t *v)
Definition position.c:177
static void set_xcoords(graph_t *g)
Set x coords of nodes.
Definition position.c:588
static bool go(node_t *u, node_t *v)
Definition position.c:164
static void adjustSimple(graph_t *g, double delta, int margin_total)
Definition position.c:610
static int nsiter2(graph_t *g)
Definition position.c:155
static int clust_ht(Agraph_t *g)
Definition position.c:695
static void remove_aux_edges(graph_t *g)
Definition position.c:553
static void contain_nodes(graph_t *g)
Definition position.c:1092
edge_t * make_aux_edge(node_t *u, node_t *v, double len, int wt)
Definition position.c:179
static void adjustRanks(graph_t *g, int margin_total)
Definition position.c:644
static void pos_clusters(graph_t *g)
Definition position.c:502
static void allocate_aux_edges(graph_t *g)
Definition position.c:203
int ports_eq(edge_t *e, edge_t *f)
Definition position.c:1024
static void keepout_othernodes(graph_t *g)
Definition position.c:408
int dot_position(graph_t *g)
Definition position.c:121
double selfRightSpace(edge_t *e)
Definition splines.c:1105
Agobj_t base
Definition cgraph.h:269
Agedge_t in
Definition cgraph.h:276
Agedge_t out
Definition cgraph.h:276
Agobj_t base
Definition cgraph.h:260
Agrec_t * data
stores programmer-defined data, access with AGDATA
Definition cgraph.h:212
graph or subgraph
Definition cgraph.h:424
Agraph_t * root
subgraphs - ancestors
Definition cgraph.h:433
Definition types.h:251
edge_t ** list
Definition types.h:252
size_t size
Definition types.h:253
double x
Definition geom.h:29
double y
Definition geom.h:29
node_t ** v
Definition types.h:202
int n
Definition types.h:201
#define free_list(L)
Definition types.h:272
#define alloc_elist(n, L)
Definition types.h:267
@ R_AUTO
Definition types.h:216
@ R_COMPRESS
Definition types.h:216
@ R_VALUE
Definition types.h:216
@ R_FILL
Definition types.h:216
@ R_EXPAND
Definition types.h:216
Definition grammar.c:90
abstraction for squashing compiler warnings for unused symbols
#define WUR
Definition unused.h:43