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