Graphviz 16.1.1~dev.20261104.1411
Loading...
Searching...
No Matches
dotsplines.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 * set edge splines.
13 */
14
15#include "config.h"
16
17#include <assert.h>
18#include <common/boxes.h>
19#include <dotgen/dot.h>
20#include <math.h>
21#include <stdatomic.h>
22#include <stdbool.h>
23#include <stddef.h>
24#include <stdint.h>
25#include <stdlib.h>
26#include <string.h>
27#include <util/agxbuf.h>
28#include <util/alloc.h>
29#include <util/gv_math.h>
30#include <util/list.h>
31
32#ifdef ORTHO
33#include <ortho/ortho.h>
34#endif
35
36#define NSUB 9 /* number of subdivisions, re-aiming splines */
37
38#define MINW 16 /* minimum width of a box in the edge path */
39#define HALFMINW 8
40
41#define FWDEDGE 16
42#define BWDEDGE 32
43
44#define MAINGRAPH 64
45#define AUXGRAPH 128
46#define GRAPHTYPEMASK 192 /* the OR of the above */
47
48static void makefwdedge(edge_t *new, edge_t *old) {
49 Agedgeinfo_t *const info =
50 (Agedgeinfo_t *)((uintptr_t)new->base.data - offsetof(Agedgeinfo_t, hdr));
51 const Agedgeinfo_t *const old_info =
52 (Agedgeinfo_t *)((uintptr_t)old->base.data - offsetof(Agedgeinfo_t, hdr));
53 *info = *old_info;
54 *new = *old;
55 new->base.data = &info->hdr;
56 AGTAIL(new) = AGHEAD(old);
57 AGHEAD(new) = AGTAIL(old);
58 ED_tail_port(new) = ED_head_port(old);
59 ED_head_port(new) = ED_tail_port(old);
60 ED_edge_type(new) = VIRTUAL;
61 ED_to_orig(new) = old;
62}
63
64typedef struct {
65 double LeftBound;
66 double RightBound;
67 double Splinesep;
68 double Multisep;
71
72typedef LIST(pointf) points_t;
73
74static void adjustregularpath(path *, size_t, size_t);
75static Agedge_t *bot_bound(Agedge_t *, int);
76static bool pathscross(Agnode_t *, Agnode_t *, Agedge_t *, Agedge_t *);
78static bool cl_vninside(Agraph_t *, Agnode_t *);
80 pathend_t *, const boxes_t *);
81static int edgecmp(const void *, const void *);
82static int make_flat_edge(graph_t *, const spline_info_t, path *, Agedge_t **,
83 unsigned, int);
84static void make_regular_edge(graph_t *g, spline_info_t *, path *, Agedge_t **,
85 unsigned, int);
86static boxf makeregularend(boxf, int, double);
88 Agedge_t *, Agedge_t *);
89static Agnode_t *neighbor(graph_t *, Agnode_t *, Agedge_t *, Agedge_t *, int);
90static void place_vnlabel(Agnode_t *);
91static boxf rank_box(spline_info_t *sp, Agraph_t *, int);
92static void recover_slack(Agedge_t *, path *);
93static void resize_vn(Agnode_t *, double, double, double);
94static void setflags(Agedge_t *, int, int, int);
95static int straight_len(Agnode_t *);
96static Agedge_t *straight_path(Agedge_t *, int, points_t *);
97static Agedge_t *top_bound(Agedge_t *, int);
98
99static edge_t *getmainedge(edge_t *e) {
100 edge_t *le = e;
101 while (ED_to_virt(le))
102 le = ED_to_virt(le);
103 while (ED_to_orig(le))
104 le = ED_to_orig(le);
105 return le;
106}
107
108static bool spline_merge(node_t *n) {
109 return ND_node_type(n) == VIRTUAL &&
110 (ND_in(n).size > 1 || ND_out(n).size > 1);
111}
112
113static bool swap_ends_p(edge_t *e) {
114 while (ED_to_orig(e))
115 e = ED_to_orig(e);
116 if (ND_rank(aghead(e)) > ND_rank(agtail(e)))
117 return false;
118 if (ND_rank(aghead(e)) < ND_rank(agtail(e)))
119 return true;
120 if (ND_order(aghead(e)) >= ND_order(agtail(e)))
121 return false;
122 return true;
123}
124
126 .splineMerge = spline_merge};
127
128int portcmp(port p0, port p1) {
129 if (!p1.defined)
130 return p0.defined ? 1 : 0;
131 if (!p0.defined)
132 return -1;
133 if (p0.p.x < p1.p.x)
134 return -1;
135 if (p0.p.x > p1.p.x)
136 return 1;
137 if (p0.p.y < p1.p.y)
138 return -1;
139 if (p0.p.y > p1.p.y)
140 return 1;
141 return 0;
142}
143
144static void swap_bezier(bezier *b) {
145 const size_t sz = b->size;
146 for (size_t i = 0; i < sz / 2; ++i) { // reverse list of points
147 SWAP(&b->list[i], &b->list[sz - 1 - i]);
148 }
149
150 SWAP(&b->sflag, &b->eflag);
151 SWAP(&b->sp, &b->ep);
152}
153
154static void swap_spline(splines *s) {
155 const size_t sz = s->size;
156
157 // reverse list
158 for (size_t i = 0; i < sz / 2; ++i) {
159 SWAP(&s->list[i], &s->list[sz - 1 - i]);
160 }
161
162 // swap Béziers
163 for (size_t i = 0; i < sz; ++i) {
164 swap_bezier(&s->list[i]);
165 }
166}
167
168/* Some back edges are reversed during layout and the reversed edge
169 * is used to compute the spline. We would like to guarantee that
170 * the order of control points always goes from tail to head, so
171 * we reverse them if necessary.
172 */
173static void edge_normalize(graph_t *g) {
174 for (node_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
175 for (edge_t *e = agfstout(g, n); e; e = agnxtout(g, e)) {
176 if (sinfo.swapEnds(e) && ED_spl(e))
178 }
179 }
180}
181
182/* In position, each node has its rw stored in mval and,
183 * if a node is part of a loop, rw may be increased to
184 * reflect the loops and associated labels. We restore
185 * the original value here.
186 */
187static void resetRW(graph_t *g) {
188 for (node_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
189 if (ND_other(n).list) {
190 SWAP(&ND_rw(n), &ND_mval(n));
191 }
192 }
193}
194
195/* Set edge label position information for regular and non-adjacent flat edges.
196 * Dot has allocated space and position for these labels. This info will be
197 * used when routing orthogonal edges.
198 */
199static void setEdgeLabelPos(graph_t *g) {
200 textlabel_t *l;
201
202 /* place regular edge labels */
203 for (node_t *n = GD_nlist(g); n; n = ND_next(n)) {
204 if (ND_node_type(n) == VIRTUAL) {
205 if (ND_alg(n)) { // label of non-adjacent flat edge
206 edge_t *fe = ND_alg(n);
207 l = ED_label(fe);
208 assert(l);
209 l->pos = ND_coord(n);
210 l->set = true;
211 } else if ((l = ND_label(n))) { // label of regular edge
212 place_vnlabel(n);
213 }
214 if (l)
215 updateBB(g, l);
216 }
217 }
218}
219
228static int dot_splines_(graph_t *g, int normalize) {
229 int i, j, n_nodes;
230 node_t *n;
231 Agedgeinfo_t fwdedgeai, fwdedgebi;
232 Agedgepair_t fwdedgea, fwdedgeb;
233 edge_t *e, *e0, *e1, *ea, *eb, *le1;
234 path P = {0};
235 int et = EDGE_TYPE(g);
236 fwdedgea.out.base.data = &fwdedgeai.hdr;
237 fwdedgeb.out.base.data = &fwdedgebi.hdr;
238
239 if (et == EDGETYPE_NONE)
240 return 0;
241 if (et == EDGETYPE_CURVED) {
242 resetRW(g);
243 if (GD_has_labels(g->root) & EDGE_LABEL) {
244 agwarningf("edge labels with splines=curved not supported in dot - use "
245 "xlabels\n");
246 }
247 }
248 spline_info_t sd = {0};
249 LIST(edge_t *) edges = {0};
250#ifdef ORTHO
251 if (et == EDGETYPE_ORTHO) {
252 resetRW(g);
253 if (GD_has_labels(g->root) & EDGE_LABEL) {
255 const int rc = orthoEdges(g, true);
256 if (rc != 0) {
257 return rc;
258 }
259 } else {
260 const int rc = orthoEdges(g, false);
261 if (rc != 0) {
262 return rc;
263 }
264 }
265 goto finish;
266 }
267#else
268 (void)setEdgeLabelPos;
269#endif
270
272 if (routesplinesinit())
273 return 0;
274 sd = (spline_info_t){.Splinesep = GD_nodesep(g) / 4,
275 .Multisep = GD_nodesep(g)};
276
277 /* compute boundaries and list of splines */
278 n_nodes = 0;
279 for (i = GD_minrank(g); i <= GD_maxrank(g); i++) {
280 n_nodes += GD_rank(g)[i].n;
281 if ((n = GD_rank(g)[i].v[0]))
282 sd.LeftBound = MIN(sd.LeftBound, ND_coord(n).x - ND_lw(n));
283 if (GD_rank(g)[i].n && (n = GD_rank(g)[i].v[GD_rank(g)[i].n - 1]))
284 sd.RightBound = MAX(sd.RightBound, ND_coord(n).x + ND_rw(n));
285 sd.LeftBound -= MINW;
286 sd.RightBound += MINW;
287
288 for (j = 0; j < GD_rank(g)[i].n; j++) {
289 n = GD_rank(g)[i].v[j];
290 /* if n is the label of a flat edge, copy its position to
291 * the label.
292 */
293 if (ND_alg(n)) {
294 edge_t *fe = ND_alg(n);
295 assert(ED_label(fe));
296 ED_label(fe)->pos = ND_coord(n);
297 ED_label(fe)->set = true;
298 }
299 if (ND_node_type(n) != NORMAL && !sinfo.splineMerge(n))
300 continue;
301 for (int k = 0; (e = ND_out(n).list[k]); k++) {
302 if (ED_edge_type(e) == FLATORDER || ED_edge_type(e) == IGNORED)
303 continue;
305 LIST_APPEND(&edges, e);
306 }
307 if (ND_flat_out(n).list)
308 for (int k = 0; (e = ND_flat_out(n).list[k]); k++) {
309 setflags(e, FLATEDGE, 0, AUXGRAPH);
310 LIST_APPEND(&edges, e);
311 }
312 if (ND_other(n).list) {
313 /* In position, each node has its rw stored in mval and,
314 * if a node is part of a loop, rw may be increased to
315 * reflect the loops and associated labels. We restore
316 * the original value here.
317 */
318 if (ND_node_type(n) == NORMAL) {
319 SWAP(&ND_rw(n), &ND_mval(n));
320 }
321 for (int k = 0; (e = ND_other(n).list[k]); k++) {
322 setflags(e, 0, 0, AUXGRAPH);
323 LIST_APPEND(&edges, e);
324 }
325 }
326 }
327 }
328
329 /* Sort so that equivalent edges are contiguous.
330 * Equivalence should basically mean that 2 edges have the
331 * same set {(tailnode,tailport),(headnode,headport)}, or
332 * alternatively, the edges would be routed identically if
333 * routed separately.
334 */
335 LIST_SORT(&edges, edgecmp);
336
337 /* FIXME: just how many boxes can there be? */
338 P.boxes = gv_calloc(n_nodes + 20 * 2 * NSUB, sizeof(boxf));
339 sd.Rank_box = gv_calloc(i, sizeof(boxf));
340
341 if (et == EDGETYPE_LINE) {
342 /* place regular edge labels */
343 for (n = GD_nlist(g); n; n = ND_next(n)) {
344 if (ND_node_type(n) == VIRTUAL && ND_label(n)) {
345 place_vnlabel(n);
346 }
347 }
348 }
349
350 for (unsigned l = 0; l < LIST_SIZE(&edges);) {
351 const unsigned ind = l;
352 edge_t *le0 = getmainedge((e0 = LIST_GET(&edges, l++)));
353 if (ED_tail_port(e0).defined || ED_head_port(e0).defined) {
354 ea = e0;
355 } else {
356 ea = le0;
357 }
358 if (ED_tree_index(ea) & BWDEDGE) {
359 makefwdedge(&fwdedgea.out, ea);
360 ea = &fwdedgea.out;
361 }
362 unsigned cnt;
363 for (cnt = 1; l < LIST_SIZE(&edges); cnt++, l++) {
364 if (le0 != (le1 = getmainedge((e1 = LIST_GET(&edges, l)))))
365 break;
366 if (ED_adjacent(e0))
367 continue; /* all flat adjacent edges at once */
368 if (ED_tail_port(e1).defined || ED_head_port(e1).defined) {
369 eb = e1;
370 } else {
371 eb = le1;
372 }
373 if (ED_tree_index(eb) & BWDEDGE) {
374 makefwdedge(&fwdedgeb.out, eb);
375 eb = &fwdedgeb.out;
376 }
377 if (portcmp(ED_tail_port(ea), ED_tail_port(eb)))
378 break;
379 if (portcmp(ED_head_port(ea), ED_head_port(eb)))
380 break;
381 if ((ED_tree_index(e0) & EDGETYPEMASK) == FLATEDGE &&
382 ED_label(e0) != ED_label(e1))
383 break;
384 if (ED_tree_index(LIST_GET(&edges, l)) & MAINGRAPH) /* Aha! -C is on */
385 break;
386 }
387
388 if (et == EDGETYPE_CURVED) {
389 edge_t **edgelist = gv_calloc(cnt, sizeof(edge_t *));
390 edgelist[0] = getmainedge(LIST_GET(&edges, ind));
391 for (unsigned ii = 1; ii < cnt; ii++)
392 edgelist[ii] = LIST_GET(&edges, ind + ii);
394 free(edgelist);
395 } else if (agtail(e0) == aghead(e0)) {
396 double sizey;
397 n = agtail(e0);
398 const int r = ND_rank(n);
399 if (r == GD_maxrank(g) || GD_rank(g)[r + 1].v[0] == NULL) {
400 if (r > 0 && GD_rank(g)[r - 1].v[0] != NULL) {
401 sizey = ND_coord(GD_rank(g)[r - 1].v[0]).y - ND_coord(n).y;
402 } else {
403 sizey = ND_ht(n);
404 }
405 } else if (r == GD_minrank(g) || GD_rank(g)[r - 1].v[0] == NULL) {
406 sizey = ND_coord(n).y - ND_coord(GD_rank(g)[r + 1].v[0]).y;
407 } else {
408 double upy = ND_coord(GD_rank(g)[r - 1].v[0]).y - ND_coord(n).y;
409 double dwny = ND_coord(n).y - ND_coord(GD_rank(g)[r + 1].v[0]).y;
410 sizey = fmin(upy, dwny);
411 }
412 makeSelfEdge(LIST_AT(&edges, ind), cnt, sd.Multisep, sizey / 2, &sinfo);
413 for (unsigned b = 0; b < cnt; b++) {
414 e = LIST_GET(&edges, ind + b);
415 if (ED_label(e))
416 updateBB(g, ED_label(e));
417 }
418 } else if (ND_rank(agtail(e0)) == ND_rank(aghead(e0))) {
419 const int rc = make_flat_edge(g, sd, &P, LIST_AT(&edges, ind), cnt, et);
420 if (rc != 0) {
421 free(sd.Rank_box);
422 LIST_FREE(&edges);
423 free(P.boxes);
424 return rc;
425 }
426 } else
427 make_regular_edge(g, &sd, &P, LIST_AT(&edges, ind), cnt, et);
428 }
429
430 /* place regular edge labels */
431 for (n = GD_nlist(g); n; n = ND_next(n)) {
432 if (ND_node_type(n) == VIRTUAL && ND_label(n)) {
433 place_vnlabel(n);
434 updateBB(g, ND_label(n));
435 }
436 }
437
438 /* normalize splines so they always go from tail to head */
439 /* place_portlabel relies on this being done first */
440 if (normalize)
442
443#ifdef ORTHO
444finish:
445#endif
446 /* place port labels */
447 /* FIX: head and tail labels are not part of cluster bbox */
449 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
450 if (E_headlabel) {
451 for (e = agfstin(g, n); e; e = agnxtin(g, e))
452 if (ED_head_label(AGMKOUT(e))) {
453 place_portlabel(AGMKOUT(e), true);
455 }
456 }
457 if (E_taillabel) {
458 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
459 if (ED_tail_label(e)) {
460 if (place_portlabel(e, false))
461 updateBB(g, ED_tail_label(e));
462 }
463 }
464 }
465 }
466 }
467
468#ifdef ORTHO
469 if (et != EDGETYPE_ORTHO && et != EDGETYPE_CURVED) {
470#else
471 if (et != EDGETYPE_CURVED) {
472#endif
474 }
475 free(sd.Rank_box);
476 LIST_FREE(&edges);
477 free(P.boxes);
479 EdgeLabelsDone = 1;
480 return 0;
481}
482
483/* If the splines attribute is defined but equal to "", skip edge routing.
484 *
485 * @return 0 on success
486 */
487int dot_splines(graph_t *g) { return dot_splines_(g, 1); }
488
489/* assign position of an edge label from its virtual node
490 * This is for regular edges only.
491 */
492static void place_vnlabel(node_t *n) {
493 edge_t *e;
494 if (ND_in(n).size == 0)
495 return; /* skip flat edge labels here */
496 for (e = ND_out(n).list[0]; ED_edge_type(e) != NORMAL; e = ED_to_orig(e))
497 ;
498 const pointf dimen = ED_label(e)->dimen;
499 const double width = GD_flip(agraphof(n)) ? dimen.y : dimen.x;
500 ED_label(e)->pos.x = ND_coord(n).x + width / 2.0;
501 ED_label(e)->pos.y = ND_coord(n).y;
502 ED_label(e)->set = true;
503}
504
505static void setflags(edge_t *e, int hint1, int hint2, int f3) {
506 int f1, f2;
507 if (hint1 != 0)
508 f1 = hint1;
509 else {
510 if (agtail(e) == aghead(e))
511 if (ED_tail_port(e).defined || ED_head_port(e).defined)
512 f1 = SELFWPEDGE;
513 else
514 f1 = SELFNPEDGE;
515 else if (ND_rank(agtail(e)) == ND_rank(aghead(e)))
516 f1 = FLATEDGE;
517 else
518 f1 = REGULAREDGE;
519 }
520 if (hint2 != 0)
521 f2 = hint2;
522 else {
523 if (f1 == REGULAREDGE)
524 f2 = ND_rank(agtail(e)) < ND_rank(aghead(e)) ? FWDEDGE : BWDEDGE;
525 else if (f1 == FLATEDGE)
526 f2 = ND_order(agtail(e)) < ND_order(aghead(e)) ? FWDEDGE : BWDEDGE;
527 else /* f1 == SELF*EDGE */
528 f2 = FWDEDGE;
529 }
530 ED_tree_index(e) = f1 | f2 | f3;
531}
532
533/* lexicographically order edges by
534 * - edge type
535 * - |rank difference of nodes|
536 * - |x difference of nodes|
537 * - id of witness edge for equivalence class
538 * - port comparison
539 * - graph type
540 * - labels if flat edges
541 * - edge id
542 */
543static int edgecmp(const void *p0, const void *p1) {
544 edge_t *const *ptr0 = p0;
545 edge_t *const *ptr1 = p1;
546 Agedgeinfo_t fwdedgeai, fwdedgebi;
547 Agedgepair_t fwdedgea, fwdedgeb;
548 int rv;
549
550 fwdedgea.out.base.data = &fwdedgeai.hdr;
551 fwdedgeb.out.base.data = &fwdedgebi.hdr;
552 edge_t *const e0 = *ptr0;
553 edge_t *const e1 = *ptr1;
554 int et0 = ED_tree_index(e0) & EDGETYPEMASK;
555 int et1 = ED_tree_index(e1) & EDGETYPEMASK;
556 if (et0 < et1) {
557 return 1;
558 }
559 if (et0 > et1) {
560 return -1;
561 }
562
563 edge_t *le0 = getmainedge(e0);
564 edge_t *le1 = getmainedge(e1);
565
566 {
567 const int rank_diff0 = ND_rank(agtail(le0)) - ND_rank(aghead(le0));
568 const int rank_diff1 = ND_rank(agtail(le1)) - ND_rank(aghead(le1));
569 const int v0 = abs(rank_diff0);
570 const int v1 = abs(rank_diff1);
571 if (v0 < v1) {
572 return -1;
573 }
574 if (v0 > v1) {
575 return 1;
576 }
577 }
578
579 {
580 const double t0 = ND_coord(agtail(le0)).x - ND_coord(aghead(le0)).x;
581 const double t1 = ND_coord(agtail(le1)).x - ND_coord(aghead(le1)).x;
582 const double v0 = fabs(t0);
583 const double v1 = fabs(t1);
584 if (v0 < v1) {
585 return -1;
586 }
587 if (v0 > v1) {
588 return 1;
589 }
590 }
591
592 /* This provides a cheap test for edges having the same set of endpoints. */
593 if (AGSEQ(le0) < AGSEQ(le1)) {
594 return -1;
595 }
596 if (AGSEQ(le0) > AGSEQ(le1)) {
597 return 1;
598 }
599
600 edge_t *ea =
601 (ED_tail_port(e0).defined || ED_head_port(e0).defined) ? e0 : le0;
602 if (ED_tree_index(ea) & BWDEDGE) {
603 makefwdedge(&fwdedgea.out, ea);
604 ea = &fwdedgea.out;
605 }
606 edge_t *eb =
607 (ED_tail_port(e1).defined || ED_head_port(e1).defined) ? e1 : le1;
608 if (ED_tree_index(eb) & BWDEDGE) {
609 makefwdedge(&fwdedgeb.out, eb);
610 eb = &fwdedgeb.out;
611 }
612 if ((rv = portcmp(ED_tail_port(ea), ED_tail_port(eb))))
613 return rv;
614 if ((rv = portcmp(ED_head_port(ea), ED_head_port(eb))))
615 return rv;
616
617 et0 = ED_tree_index(e0) & GRAPHTYPEMASK;
618 et1 = ED_tree_index(e1) & GRAPHTYPEMASK;
619 if (et0 < et1) {
620 return -1;
621 }
622 if (et0 > et1) {
623 return 1;
624 }
625
626 if (et0 == FLATEDGE) {
627 if (ED_label(e0) < ED_label(e1)) {
628 return -1;
629 }
630 if (ED_label(e0) > ED_label(e1)) {
631 return 1;
632 }
633 }
634
635 if (AGSEQ(e0) < AGSEQ(e1)) {
636 return -1;
637 }
638 if (AGSEQ(e0) > AGSEQ(e1)) {
639 return 1;
640 }
641 return 0;
642}
643
688
689static void setState(graph_t *auxg, attr_state_t *attr_state) {
690 /* save state */
691 attr_state->E_constr = E_constr;
692 attr_state->E_dir = E_dir;
693 attr_state->E_samehead = E_samehead;
694 attr_state->E_sametail = E_sametail;
695 attr_state->E_weight = E_weight;
696 attr_state->E_minlen = E_minlen;
697 attr_state->E_fontcolor = E_fontcolor;
698 attr_state->E_fontname = E_fontname;
699 attr_state->E_fontsize = E_fontsize;
700 attr_state->E_headclip = E_headclip;
701 attr_state->E_headlabel = E_headlabel;
702 attr_state->E_label = E_label;
703 attr_state->E_label_float = E_label_float;
705 attr_state->E_labelfontname = E_labelfontname;
706 attr_state->E_labelfontsize = E_labelfontsize;
707 attr_state->E_tailclip = E_tailclip;
708 attr_state->E_taillabel = E_taillabel;
709 attr_state->E_xlabel = E_xlabel;
710 attr_state->N_height = N_height;
711 attr_state->N_width = N_width;
712 attr_state->N_shape = N_shape;
713 attr_state->N_style = N_style;
714 attr_state->N_fontsize = N_fontsize;
715 attr_state->N_fontname = N_fontname;
716 attr_state->N_fontcolor = N_fontcolor;
717 attr_state->N_label = N_label;
718 attr_state->N_xlabel = N_xlabel;
719 attr_state->N_showboxes = N_showboxes;
720 attr_state->N_ordering = N_ordering;
721 attr_state->N_sides = N_sides;
722 attr_state->N_peripheries = N_peripheries;
723 attr_state->N_skew = N_skew;
724 attr_state->N_orientation = N_orientation;
725 attr_state->N_distortion = N_distortion;
726 attr_state->N_fixed = N_fixed;
727 attr_state->N_nojustify = N_nojustify;
728 attr_state->N_group = N_group;
729 attr_state->State = State;
730 attr_state->G_ordering = G_ordering;
731
732 E_constr = NULL;
733 E_dir = agattr_text(auxg, AGEDGE, "dir", NULL);
734 E_samehead = agattr_text(auxg, AGEDGE, "samehead", NULL);
735 E_sametail = agattr_text(auxg, AGEDGE, "sametail", NULL);
736 E_weight = agattr_text(auxg, AGEDGE, "weight", NULL);
737 if (!E_weight)
738 E_weight = agattr_text(auxg, AGEDGE, "weight", "");
739 E_minlen = NULL;
741 E_fontname = agfindedgeattr(auxg, "fontname");
742 E_fontsize = agfindedgeattr(auxg, "fontsize");
743 E_headclip = agfindedgeattr(auxg, "headclip");
745 E_label = agfindedgeattr(auxg, "label");
746 E_label_float = agfindedgeattr(auxg, "label_float");
748 E_labelfontname = agfindedgeattr(auxg, "labelfontname");
749 E_labelfontsize = agfindedgeattr(auxg, "labelfontsize");
750 E_tailclip = agfindedgeattr(auxg, "tailclip");
752 E_xlabel = NULL;
753 N_height = agfindnodeattr(auxg, "height");
754 N_width = agfindnodeattr(auxg, "width");
755 N_shape = agfindnodeattr(auxg, "shape");
756 N_style = NULL;
757 N_fontsize = agfindnodeattr(auxg, "fontsize");
758 N_fontname = agfindnodeattr(auxg, "fontname");
760 N_label = agfindnodeattr(auxg, "label");
761 N_xlabel = NULL;
763 N_ordering = agfindnodeattr(auxg, "ordering");
764 N_sides = agfindnodeattr(auxg, "sides");
765 N_peripheries = agfindnodeattr(auxg, "peripheries");
766 N_skew = agfindnodeattr(auxg, "skew");
767 N_orientation = agfindnodeattr(auxg, "orientation");
768 N_distortion = agfindnodeattr(auxg, "distortion");
769 N_fixed = agfindnodeattr(auxg, "fixed");
771 N_group = NULL;
772 G_ordering = agfindgraphattr(auxg, "ordering");
773}
774
775/* Create clone graph. It stores the global Agsyms, to be
776 * restored in cleanupCloneGraph. The graph uses the main
777 * graph's settings for certain geometry parameters, and
778 * declares all node and edge attributes used in the original
779 * graph.
780 */
781static graph_t *cloneGraph(graph_t *g, attr_state_t *attr_state) {
782 Agsym_t *sym;
783 graph_t *auxg;
784 if (agisdirected(g))
785 auxg = agopen("auxg", Agdirected, NULL);
786 else
787 auxg = agopen("auxg", Agundirected, NULL);
788 agbindrec(auxg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
789 agattr_text(auxg, AGRAPH, "rank", "");
790 GD_drawing(auxg) = gv_alloc(sizeof(layout_t));
791 GD_drawing(auxg)->quantum = GD_drawing(g)->quantum;
792 GD_drawing(auxg)->dpi = GD_drawing(g)->dpi;
793
794 GD_charset(auxg) = GD_charset(g);
795 if (GD_flip(g))
796 SET_RANKDIR(auxg, RANKDIR_TB);
797 else
798 SET_RANKDIR(auxg, RANKDIR_LR);
799 GD_nodesep(auxg) = GD_nodesep(g);
800 GD_ranksep(auxg) = GD_ranksep(g);
801
802 // copy node attrs to auxg
803 sym = agnxtattr(agroot(g), AGNODE, NULL); // get the first attr.
804 for (; sym; sym = agnxtattr(agroot(g), AGNODE, sym)) {
805 const bool is_html = aghtmlstr(sym->defval);
806 is_html ? agattr_html(auxg, AGNODE, sym->name, sym->defval)
807 : agattr_text(auxg, AGNODE, sym->name, sym->defval);
808 }
809
810 // copy edge attributes
811 sym = agnxtattr(agroot(g), AGEDGE, NULL); // get the first attr.
812 for (; sym; sym = agnxtattr(agroot(g), AGEDGE, sym)) {
813 const bool is_html = aghtmlstr(sym->defval);
814 is_html ? agattr_html(auxg, AGEDGE, sym->name, sym->defval)
815 : agattr_text(auxg, AGEDGE, sym->name, sym->defval);
816 }
817
818 if (!agattr_text(auxg, AGEDGE, "headport", NULL))
819 agattr_text(auxg, AGEDGE, "headport", "");
820 if (!agattr_text(auxg, AGEDGE, "tailport", NULL))
821 agattr_text(auxg, AGEDGE, "tailport", "");
822
823 setState(auxg, attr_state);
824
825 return auxg;
826}
827
828static void cleanupCloneGraph(graph_t *g, attr_state_t *attr_state) {
829 /* restore main graph syms */
830 E_constr = attr_state->E_constr;
831 E_dir = attr_state->E_dir;
832 E_samehead = attr_state->E_samehead;
833 E_sametail = attr_state->E_sametail;
834 E_weight = attr_state->E_weight;
835 E_minlen = attr_state->E_minlen;
836 E_fontcolor = attr_state->E_fontcolor;
837 E_fontname = attr_state->E_fontname;
838 E_fontsize = attr_state->E_fontsize;
839 E_headclip = attr_state->E_headclip;
840 E_headlabel = attr_state->E_headlabel;
841 E_label = attr_state->E_label;
842 E_label_float = attr_state->E_label_float;
844 E_labelfontname = attr_state->E_labelfontname;
845 E_labelfontsize = attr_state->E_labelfontsize;
846 E_tailclip = attr_state->E_tailclip;
847 E_taillabel = attr_state->E_taillabel;
848 E_xlabel = attr_state->E_xlabel;
849 N_height = attr_state->N_height;
850 N_width = attr_state->N_width;
851 N_shape = attr_state->N_shape;
852 N_style = attr_state->N_style;
853 N_fontsize = attr_state->N_fontsize;
854 N_fontname = attr_state->N_fontname;
855 N_fontcolor = attr_state->N_fontcolor;
856 N_label = attr_state->N_label;
857 N_xlabel = attr_state->N_xlabel;
858 N_showboxes = attr_state->N_showboxes;
859 N_ordering = attr_state->N_ordering;
860 N_sides = attr_state->N_sides;
861 N_peripheries = attr_state->N_peripheries;
862 N_skew = attr_state->N_skew;
863 N_orientation = attr_state->N_orientation;
864 N_distortion = attr_state->N_distortion;
865 N_fixed = attr_state->N_fixed;
866 N_nojustify = attr_state->N_nojustify;
867 N_group = attr_state->N_group;
868 G_ordering = attr_state->G_ordering;
869 State = attr_state->State;
870
871 dot_cleanup(g);
872 agclose(g);
873}
874
875/* If original graph has rankdir=LR or RL, records change shape,
876 * so we wrap a record node's label in "{...}" to prevent this.
877 */
878static node_t *cloneNode(graph_t *g, node_t *orign) {
879 node_t *n = agnode(g, agnameof(orign), 1);
880 agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
881 agcopyattr(orign, n);
882 if (shapeOf(orign) == SH_RECORD) {
883 agxbuf buf = {0};
884 agxbprint(&buf, "{%s}", ND_label(orign)->text);
885 agset(n, "label", agxbuse(&buf));
886 agxbfree(&buf);
887 }
888
889 return n;
890}
891
892static edge_t *cloneEdge(graph_t *g, node_t *tn, node_t *hn, edge_t *orig) {
893 edge_t *e = agedge(g, tn, hn, NULL, 1);
894 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true);
895 agcopyattr(orig, e);
896
897 return e;
898}
899
901static pointf transformf(pointf p, pointf del, int flip) {
902 if (flip) {
903 double i = p.x;
904 p.x = p.y;
905 p.y = -i;
906 }
907 return add_pointf(p, del);
908}
909
910/* lexicographically order edges by
911 * - has label
912 * - label is wider
913 * - label is higher
914 */
915static int edgelblcmpfn(const void *x, const void *y) {
916 edge_t *const *const ptr0 = x;
917 edge_t *const *const ptr1 = y;
918 pointf sz0, sz1;
919
920 edge_t *e0 = *ptr0;
921 edge_t *e1 = *ptr1;
922
923 if (ED_label(e0)) {
924 if (ED_label(e1)) {
925 sz0 = ED_label(e0)->dimen;
926 sz1 = ED_label(e1)->dimen;
927 if (sz0.x > sz1.x)
928 return -1;
929 if (sz0.x < sz1.x)
930 return 1;
931 if (sz0.y > sz1.y)
932 return -1;
933 if (sz0.y < sz1.y)
934 return 1;
935 return 0;
936 }
937 return -1;
938 }
939 if (ED_label(e1)) {
940 return 1;
941 }
942 return 0;
943}
944
945#define LBL_SPACE 6 /* space between labels, in points */
946
947/* This handles the second simplest case for flat edges between
948 * two adjacent nodes. We still invoke a dot on a rotated problem
949 * to handle edges with ports. This usually works, but fails for
950 * records because of their weird nature.
951 */
952static void makeSimpleFlatLabels(node_t *tn, node_t *hn, edge_t **edges,
953 unsigned cnt, int et, unsigned n_lbls) {
955 edge_t *e = *edges;
956 pointf points[10], tp, hp;
957 double leftend, rightend, ctrx, ctry, miny, maxy;
958 double uminx, umaxx;
959 double lminx = 0.0, lmaxx = 0.0;
960
961 edge_t **earray = gv_calloc(cnt, sizeof(edge_t *));
962
963 for (unsigned i = 0; i < cnt; i++) {
964 earray[i] = edges[i];
965 }
966
967 qsort(earray, cnt, sizeof(edge_t *), edgelblcmpfn);
968
969 tp = add_pointf(ND_coord(tn), ED_tail_port(e).p);
970 hp = add_pointf(ND_coord(hn), ED_head_port(e).p);
971
972 leftend = tp.x + ND_rw(tn);
973 rightend = hp.x - ND_lw(hn);
974 ctrx = (leftend + rightend) / 2.0;
975
976 /* do first edge */
977 e = earray[0];
978 size_t pointn = 0;
979 points[pointn++] = tp;
980 points[pointn++] = tp;
981 points[pointn++] = hp;
982 points[pointn++] = hp;
983 clip_and_install(e, aghead(e), points, pointn, &sinfo);
984 ED_label(e)->pos.x = ctrx;
985 ED_label(e)->pos.y = tp.y + (ED_label(e)->dimen.y + LBL_SPACE) / 2.0;
986 ED_label(e)->set = true;
987
988 miny = tp.y + LBL_SPACE / 2.0;
989 maxy = miny + ED_label(e)->dimen.y;
990 uminx = ctrx - ED_label(e)->dimen.x / 2.0;
991 umaxx = ctrx + ED_label(e)->dimen.x / 2.0;
992
993 unsigned i;
994 for (i = 1; i < n_lbls; i++) {
995 e = earray[i];
996 if (i % 2) { /* down */
997 if (i == 1) {
998 lminx = ctrx - ED_label(e)->dimen.x / 2.0;
999 lmaxx = ctrx + ED_label(e)->dimen.x / 2.0;
1000 }
1001 miny -= LBL_SPACE + ED_label(e)->dimen.y;
1002 points[0] = tp;
1003 points[1] = (pointf){.x = tp.x, .y = miny - LBL_SPACE};
1004 points[2] = (pointf){.x = hp.x, .y = points[1].y};
1005 points[3] = hp;
1006 points[4] = (pointf){.x = lmaxx, .y = hp.y};
1007 points[5] = (pointf){.x = lmaxx, .y = miny};
1008 points[6] = (pointf){.x = lminx, .y = miny};
1009 points[7] = (pointf){.x = lminx, .y = tp.y};
1010 ctry = miny + ED_label(e)->dimen.y / 2.0;
1011 } else { /* up */
1012 points[0] = tp;
1013 points[1] = (pointf){.x = uminx, .y = tp.y};
1014 points[2] = (pointf){.x = uminx, .y = maxy};
1015 points[3] = (pointf){.x = umaxx, .y = maxy};
1016 points[4] = (pointf){.x = umaxx, .y = hp.y};
1017 points[5] = hp;
1018 points[6] = (pointf){.x = hp.x, .y = maxy + LBL_SPACE};
1019 points[7] = (pointf){.x = tp.x, .y = maxy + LBL_SPACE};
1020 ctry = maxy + ED_label(e)->dimen.y / 2.0 + LBL_SPACE;
1021 maxy += ED_label(e)->dimen.y + LBL_SPACE;
1022 }
1023 poly.pn = 8;
1024 poly.ps = (Ppoint_t *)points;
1025 size_t pn;
1026 pointf *ps = simpleSplineRoute(tp, hp, poly, &pn, et == EDGETYPE_PLINE);
1027 if (ps == NULL || pn == 0) {
1028 free(ps);
1029 free(earray);
1030 return;
1031 }
1032 ED_label(e)->pos.x = ctrx;
1033 ED_label(e)->pos.y = ctry;
1034 ED_label(e)->set = true;
1035 clip_and_install(e, aghead(e), ps, pn, &sinfo);
1036 free(ps);
1037 }
1038
1039 /* edges with no labels */
1040 for (; i < cnt; i++) {
1041 e = earray[i];
1042 if (i % 2) { /* down */
1043 if (i == 1) {
1044 lminx = (2 * leftend + rightend) / 3.0;
1045 lmaxx = (leftend + 2 * rightend) / 3.0;
1046 }
1047 miny -= LBL_SPACE;
1048 points[0] = tp;
1049 points[1] = (pointf){.x = tp.x, .y = miny - LBL_SPACE};
1050 points[2] = (pointf){.x = hp.x, .y = points[1].y};
1051 points[3] = hp;
1052 points[4] = (pointf){.x = lmaxx, .y = hp.y};
1053 points[5] = (pointf){.x = lmaxx, .y = miny};
1054 points[6] = (pointf){.x = lminx, .y = miny};
1055 points[7] = (pointf){.x = lminx, .y = tp.y};
1056 } else { /* up */
1057 points[0] = tp;
1058 points[1] = (pointf){.x = uminx, .y = tp.y};
1059 points[2] = (pointf){.x = uminx, .y = maxy};
1060 points[3] = (pointf){.x = umaxx, .y = maxy};
1061 points[4] = (pointf){.x = umaxx, .y = hp.y};
1062 points[5] = hp;
1063 points[6] = (pointf){.x = hp.x, .y = maxy + LBL_SPACE};
1064 points[7] = (pointf){.x = tp.x, .y = maxy + LBL_SPACE};
1065 maxy += +LBL_SPACE;
1066 }
1067 poly.pn = 8;
1068 poly.ps = (Ppoint_t *)points;
1069 size_t pn;
1070 pointf *ps = simpleSplineRoute(tp, hp, poly, &pn, et == EDGETYPE_PLINE);
1071 if (ps == NULL || pn == 0) {
1072 free(ps);
1073 free(earray);
1074 return;
1075 }
1076 clip_and_install(e, aghead(e), ps, pn, &sinfo);
1077 free(ps);
1078 }
1079
1080 free(earray);
1081}
1082
1083static void makeSimpleFlat(node_t *tn, node_t *hn, edge_t **edges, unsigned cnt,
1084 int et) {
1085 edge_t *e = *edges;
1086 pointf points[10], tp, hp;
1087 double stepy, dy;
1088
1089 tp = add_pointf(ND_coord(tn), ED_tail_port(e).p);
1090 hp = add_pointf(ND_coord(hn), ED_head_port(e).p);
1091
1092 stepy = cnt > 1 ? ND_ht(tn) / (double)(cnt - 1) : 0.;
1093 dy = tp.y - (cnt > 1 ? ND_ht(tn) / 2. : 0.);
1094
1095 for (unsigned i = 0; i < cnt; i++) {
1096 e = edges[i];
1097 size_t pointn = 0;
1098 if (et == EDGETYPE_SPLINE || et == EDGETYPE_LINE) {
1099 points[pointn++] = tp;
1100 points[pointn++] = (pointf){(2 * tp.x + hp.x) / 3, dy};
1101 points[pointn++] = (pointf){(2 * hp.x + tp.x) / 3, dy};
1102 points[pointn++] = hp;
1103 } else { /* EDGETYPE_PLINE */
1104 points[pointn++] = tp;
1105 points[pointn++] = tp;
1106 points[pointn++] = (pointf){(2 * tp.x + hp.x) / 3, dy};
1107 points[pointn++] = (pointf){(2 * tp.x + hp.x) / 3, dy};
1108 points[pointn++] = (pointf){(2 * tp.x + hp.x) / 3, dy};
1109 points[pointn++] = (pointf){(2 * hp.x + tp.x) / 3, dy};
1110 points[pointn++] = (pointf){(2 * hp.x + tp.x) / 3, dy};
1111 points[pointn++] = (pointf){(2 * hp.x + tp.x) / 3, dy};
1112 points[pointn++] = hp;
1113 points[pointn++] = hp;
1114 }
1115 dy += stepy;
1116 clip_and_install(e, aghead(e), points, pointn, &sinfo);
1117 }
1118}
1119
1120/* In the simple case, with no labels or ports, this creates a simple
1121 * spindle of splines.
1122 * If there are only labels, cobble something together.
1123 * Otherwise, we run dot recursively on the 2 nodes and the edges,
1124 * essentially using rankdir=LR, to get the needed spline info.
1125 * This is probably to cute and fragile, and should be rewritten in a
1126 * more straightforward and laborious fashion.
1127 *
1128 * @return 0 on success
1129 */
1130static int make_flat_adj_edges(graph_t *g, edge_t **edges, unsigned cnt,
1131 edge_t *e0, int et) {
1132 node_t *n;
1133 node_t *tn, *hn;
1134 edge_t *e;
1135 graph_t *auxg;
1136 graph_t *subg;
1137 node_t *auxt, *auxh;
1138 edge_t *auxe;
1139 double midx, midy, leftx, rightx;
1140 pointf del;
1141 edge_t *hvye = NULL;
1142 static atomic_flag warned;
1143
1144 tn = agtail(e0), hn = aghead(e0);
1145 if (shapeOf(tn) == SH_RECORD || shapeOf(hn) == SH_RECORD) {
1146 if (!atomic_flag_test_and_set(&warned)) {
1147 agwarningf("flat edge between adjacent nodes one of which has a record "
1148 "shape - replace records with HTML-like labels\n");
1149 agerr(AGPREV, " Edge %s %s %s\n", agnameof(tn),
1150 agisdirected(g) ? "->" : "--", agnameof(hn));
1151 }
1152 return 0;
1153 }
1154 unsigned labels = 0;
1155 bool ports = false;
1156 for (unsigned i = 0; i < cnt; i++) {
1157 e = edges[i];
1158 if (ED_label(e))
1159 labels++;
1160 if (ED_tail_port(e).defined || ED_head_port(e).defined)
1161 ports = true;
1162 }
1163
1164 if (!ports) {
1165 /* flat edges without ports and labels can go straight left to right */
1166 if (labels == 0) {
1167 makeSimpleFlat(tn, hn, edges, cnt, et);
1168 }
1169 /* flat edges without ports but with labels take more work */
1170 else {
1171 makeSimpleFlatLabels(tn, hn, edges, cnt, et, labels);
1172 }
1173 return 0;
1174 }
1175
1176 attr_state_t attrs = {0};
1177 auxg = cloneGraph(g, &attrs);
1178 subg = agsubg(auxg, "xxx", 1);
1179 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
1180 agset(subg, "rank", "source");
1181 rightx = ND_coord(hn).x;
1182 leftx = ND_coord(tn).x;
1183 if (GD_flip(g)) {
1184 SWAP(&tn, &hn);
1185 }
1186 auxt = cloneNode(subg, tn);
1187 auxh = cloneNode(auxg, hn);
1188 for (unsigned i = 0; i < cnt; i++) {
1189 e = edges[i];
1190 for (; ED_edge_type(e) != NORMAL; e = ED_to_orig(e))
1191 ;
1192 if (agtail(e) == tn)
1193 auxe = cloneEdge(auxg, auxt, auxh, e);
1194 else
1195 auxe = cloneEdge(auxg, auxh, auxt, e);
1196 ED_alg(e) = auxe;
1197 if (!hvye && !ED_tail_port(e).defined && !ED_head_port(e).defined) {
1198 hvye = auxe;
1199 ED_alg(hvye) = e;
1200 }
1201 }
1202 if (!hvye) {
1203 hvye = agedge(auxg, auxt, auxh, NULL, 1);
1204 }
1205 agxset(hvye, E_weight, "10000");
1206 GD_gvc(auxg) = GD_gvc(g);
1207 GD_dotroot(auxg) = auxg;
1208 setEdgeType(auxg, et);
1209 dot_init_node_edge(auxg);
1210
1211 dot_rank(auxg);
1212 const int r = dot_mincross(auxg);
1213 if (r != 0) {
1214 return r;
1215 }
1216 {
1217 const int rc = dot_position(auxg);
1218 if (rc != 0) {
1219 return rc;
1220 }
1221 }
1222
1223 /* reposition */
1224 midx = (ND_coord(tn).x - ND_rw(tn) + ND_coord(hn).x + ND_lw(hn)) / 2;
1225 midy = (ND_coord(auxt).x + ND_coord(auxh).x) / 2;
1226 for (n = GD_nlist(auxg); n; n = ND_next(n)) {
1227 if (n == auxt) {
1228 ND_coord(n).y = rightx;
1229 ND_coord(n).x = midy;
1230 } else if (n == auxh) {
1231 ND_coord(n).y = leftx;
1232 ND_coord(n).x = midy;
1233 } else
1234 ND_coord(n).y = midx;
1235 }
1236 dot_sameports(auxg);
1237 const int rc = dot_splines_(auxg, 0);
1238 if (rc != 0) {
1239 return rc;
1240 }
1242
1243 /* copy splines */
1244 if (GD_flip(g)) {
1245 del.x = ND_coord(tn).x - ND_coord(auxt).y;
1246 del.y = ND_coord(tn).y + ND_coord(auxt).x;
1247 } else {
1248 del.x = ND_coord(tn).x - ND_coord(auxt).x;
1249 del.y = ND_coord(tn).y - ND_coord(auxt).y;
1250 }
1251 for (unsigned i = 0; i < cnt; i++) {
1252 bezier *auxbz;
1253 bezier *bz;
1254
1255 e = edges[i];
1256 for (; ED_edge_type(e) != NORMAL; e = ED_to_orig(e))
1257 ;
1258 auxe = ED_alg(e);
1259 if ((auxe == hvye) & !ED_alg(auxe))
1260 continue; /* pseudo-edge */
1261 auxbz = ED_spl(auxe)->list;
1262 bz = new_spline(e, auxbz->size);
1263 bz->sflag = auxbz->sflag;
1264 bz->sp = transformf(auxbz->sp, del, GD_flip(g));
1265 bz->eflag = auxbz->eflag;
1266 bz->ep = transformf(auxbz->ep, del, GD_flip(g));
1267 for (size_t j = 0; j < auxbz->size;) {
1268 pointf cp[4];
1269 cp[0] = bz->list[j] = transformf(auxbz->list[j], del, GD_flip(g));
1270 j++;
1271 if (j >= auxbz->size)
1272 break;
1273 cp[1] = bz->list[j] = transformf(auxbz->list[j], del, GD_flip(g));
1274 j++;
1275 cp[2] = bz->list[j] = transformf(auxbz->list[j], del, GD_flip(g));
1276 j++;
1277 cp[3] = transformf(auxbz->list[j], del, GD_flip(g));
1278 update_bb_bz(&GD_bb(g), cp);
1279 }
1280 if (ED_label(e)) {
1281 ED_label(e)->pos = transformf(ED_label(auxe)->pos, del, GD_flip(g));
1282 ED_label(e)->set = true;
1283 updateBB(g, ED_label(e));
1284 }
1285 }
1286
1287 cleanupCloneGraph(auxg, &attrs);
1288 return 0;
1289}
1290
1291static void makeFlatEnd(graph_t *g, const spline_info_t sp, path *P, node_t *n,
1292 edge_t *e, pathend_t *endp, bool isBegin) {
1293 boxf b = endp->nb = maximal_bbox(g, sp, n, NULL, e);
1294 endp->sidemask = TOP;
1295 if (isBegin)
1296 beginpath(P, e, FLATEDGE, endp, false);
1297 else
1298 endpath(P, e, FLATEDGE, endp, false);
1299 b.UR.y = endp->boxes[endp->boxn - 1].UR.y;
1300 b.LL.y = endp->boxes[endp->boxn - 1].LL.y;
1301 b = makeregularend(b, TOP, ND_coord(n).y + GD_rank(g)[ND_rank(n)].ht2);
1302 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
1303 endp->boxes[endp->boxn++] = b;
1304}
1305
1306static void makeBottomFlatEnd(graph_t *g, const spline_info_t sp, path *P,
1307 node_t *n, edge_t *e, pathend_t *endp,
1308 bool isBegin) {
1309 boxf b = endp->nb = maximal_bbox(g, sp, n, NULL, e);
1310 endp->sidemask = BOTTOM;
1311 if (isBegin)
1312 beginpath(P, e, FLATEDGE, endp, false);
1313 else
1314 endpath(P, e, FLATEDGE, endp, false);
1315 b.UR.y = endp->boxes[endp->boxn - 1].UR.y;
1316 b.LL.y = endp->boxes[endp->boxn - 1].LL.y;
1317 b = makeregularend(b, BOTTOM, ND_coord(n).y - GD_rank(g)[ND_rank(n)].ht2);
1318 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
1319 endp->boxes[endp->boxn++] = b;
1320}
1321
1323 edge_t *e, int et) {
1324 node_t *tn, *hn, *ln;
1325 pointf *ps;
1326 bool ps_needs_free = false;
1327 pathend_t tend, hend;
1328 boxf lb;
1329 int i;
1330 edge_t *f;
1331 pointf points[7];
1332
1333 tn = agtail(e);
1334 hn = aghead(e);
1335
1336 for (f = ED_to_virt(e); ED_to_virt(f); f = ED_to_virt(f))
1337 ;
1338 ln = agtail(f);
1339 ED_label(e)->pos = ND_coord(ln);
1340 ED_label(e)->set = true;
1341
1342 size_t pn;
1343 if (et == EDGETYPE_LINE) {
1344 pointf startp, endp, lp;
1345
1346 startp = add_pointf(ND_coord(tn), ED_tail_port(e).p);
1347 endp = add_pointf(ND_coord(hn), ED_head_port(e).p);
1348
1349 lp = ED_label(e)->pos;
1350 lp.y -= ED_label(e)->dimen.y / 2.0;
1351 points[1] = points[0] = startp;
1352 points[2] = points[3] = points[4] = lp;
1353 points[5] = points[6] = endp;
1354 ps = points;
1355 pn = 7;
1356 } else {
1357 lb.LL.x = ND_coord(ln).x - ND_lw(ln);
1358 lb.UR.x = ND_coord(ln).x + ND_rw(ln);
1359 lb.UR.y = ND_coord(ln).y + ND_ht(ln) / 2;
1360 double ydelta = ND_coord(ln).y - GD_rank(g)[ND_rank(tn)].ht1 -
1361 ND_coord(tn).y + GD_rank(g)[ND_rank(tn)].ht2;
1362 ydelta /= 6;
1363 lb.LL.y = lb.UR.y - MAX(5, ydelta);
1364
1365 makeFlatEnd(g, sp, P, tn, e, &tend, true);
1366 makeFlatEnd(g, sp, P, hn, e, &hend, false);
1367
1368 boxf boxes[] = {
1369 {
1370 .LL =
1371 {
1372 .x = tend.boxes[tend.boxn - 1].LL.x,
1373 .y = tend.boxes[tend.boxn - 1].UR.y,
1374 },
1375 .UR = lb.LL,
1376 },
1377 {
1378 .LL =
1379 {
1380 .x = tend.boxes[tend.boxn - 1].LL.x,
1381 .y = lb.LL.y,
1382 },
1383 .UR =
1384 {
1385 .x = hend.boxes[hend.boxn - 1].UR.x,
1386 .y = lb.UR.y,
1387 },
1388 },
1389 {
1390 .LL =
1391 {
1392 .x = lb.UR.x,
1393 .y = hend.boxes[hend.boxn - 1].UR.y,
1394 },
1395 .UR =
1396 {
1397 .x = hend.boxes[hend.boxn - 1].UR.x,
1398 .y = lb.LL.y,
1399 },
1400 },
1401 };
1402 const size_t boxn = sizeof(boxes) / sizeof(boxes[0]);
1403
1404 for (i = 0; i < tend.boxn; i++)
1405 add_box(P, tend.boxes[i]);
1406 for (size_t j = 0; j < boxn; j++)
1407 add_box(P, boxes[j]);
1408 for (i = hend.boxn - 1; i >= 0; i--)
1409 add_box(P, hend.boxes[i]);
1410
1411 ps_needs_free = true;
1412 if (et == EDGETYPE_SPLINE)
1413 ps = routesplines(P, &pn);
1414 else
1415 ps = routepolylines(P, &pn);
1416 if (pn == 0) {
1417 free(ps);
1418 return;
1419 }
1420 }
1421 clip_and_install(e, aghead(e), ps, pn, &sinfo);
1422 if (ps_needs_free)
1423 free(ps);
1424}
1425
1427 edge_t **edges, unsigned cnt, edge_t *e,
1428 bool use_splines) {
1429 node_t *tn, *hn;
1430 int j, r;
1431 double stepx, stepy, vspace;
1432 rank_t *nextr;
1433 pathend_t tend, hend;
1434
1435 tn = agtail(e);
1436 hn = aghead(e);
1437 r = ND_rank(tn);
1438 if (r < GD_maxrank(g)) {
1439 nextr = GD_rank(g) + (r + 1);
1440 vspace = ND_coord(tn).y - GD_rank(g)[r].pht1 -
1441 (ND_coord(nextr->v[0]).y + nextr->pht2);
1442 } else {
1443 vspace = GD_ranksep(g);
1444 }
1445 stepx = sp.Multisep / (cnt + 1);
1446 stepy = vspace / (cnt + 1);
1447
1448 makeBottomFlatEnd(g, sp, P, tn, e, &tend, true);
1449 makeBottomFlatEnd(g, sp, P, hn, e, &hend, false);
1450
1451 for (unsigned i = 0; i < cnt; i++) {
1452 boxf b;
1453 e = edges[i];
1454 size_t boxn = 0;
1455
1456 boxf boxes[3];
1457
1458 b = tend.boxes[tend.boxn - 1];
1459 boxes[boxn].LL.x = b.LL.x;
1460 boxes[boxn].UR.y = b.LL.y;
1461 boxes[boxn].UR.x = b.UR.x + (i + 1) * stepx;
1462 boxes[boxn].LL.y = b.LL.y - (i + 1) * stepy;
1463 boxn++;
1464 boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x;
1465 boxes[boxn].UR.y = boxes[boxn - 1].LL.y;
1466 boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x;
1467 boxes[boxn].LL.y = boxes[boxn].UR.y - stepy;
1468 boxn++;
1469 b = hend.boxes[hend.boxn - 1];
1470 boxes[boxn].UR.x = b.UR.x;
1471 boxes[boxn].UR.y = b.LL.y;
1472 boxes[boxn].LL.x = b.LL.x - (i + 1) * stepx;
1473 boxes[boxn].LL.y = boxes[boxn - 1].UR.y;
1474 boxn++;
1475 assert(boxn == sizeof(boxes) / sizeof(boxes[0]));
1476
1477 for (j = 0; j < tend.boxn; j++)
1478 add_box(P, tend.boxes[j]);
1479 for (size_t k = 0; k < boxn; k++)
1480 add_box(P, boxes[k]);
1481 for (j = hend.boxn - 1; j >= 0; j--)
1482 add_box(P, hend.boxes[j]);
1483
1484 pointf *ps = NULL;
1485 size_t pn = 0;
1486 if (use_splines)
1487 ps = routesplines(P, &pn);
1488 else
1489 ps = routepolylines(P, &pn);
1490 if (pn == 0) {
1491 free(ps);
1492 return;
1493 }
1494 clip_and_install(e, aghead(e), ps, pn, &sinfo);
1495 free(ps);
1496 P->nbox = 0;
1497 }
1498}
1499
1500/* Construct flat edges edges[ind...ind+cnt-1]
1501 * There are 4 main cases:
1502 * - all edges between a and b where a and b are adjacent
1503 * - one labeled edge
1504 * - all non-labeled edges with identical ports between non-adjacent a and b
1505 * = connecting bottom to bottom/left/right - route along bottom
1506 * = the rest - route along top
1507 *
1508 * @return 0 on success
1509 */
1510static int make_flat_edge(graph_t *g, const spline_info_t sp, path *P,
1511 edge_t **edges, unsigned cnt, int et) {
1512 Agedgeinfo_t fwdedgei;
1513 Agedgepair_t fwdedge;
1514 int j;
1515 double stepx, stepy, vspace;
1516 pathend_t tend, hend;
1517
1518 fwdedge.out.base.data = &fwdedgei.hdr;
1519
1520 /* Get sample edge; normalize to go from left to right */
1521 edge_t *e = *edges;
1522 bool isAdjacent = ED_adjacent(e) != 0;
1523 if (ED_tree_index(e) & BWDEDGE) {
1524 makefwdedge(&fwdedge.out, e);
1525 e = &fwdedge.out;
1526 }
1527 for (unsigned i = 1; i < cnt; i++) {
1528 if (ED_adjacent(edges[i])) {
1529 isAdjacent = true;
1530 break;
1531 }
1532 }
1533 // The lead edge edges[0] might not have been marked earlier as adjacent, so
1534 // check them all.
1535 if (isAdjacent) {
1536 return make_flat_adj_edges(g, edges, cnt, e, et);
1537 }
1538 if (ED_label(e)) { /* edges with labels aren't multi-edges */
1539 make_flat_labeled_edge(g, sp, P, e, et);
1540 return 0;
1541 }
1542
1543 if (et == EDGETYPE_LINE) {
1544 makeSimpleFlat(agtail(e), aghead(e), edges, cnt, et);
1545 return 0;
1546 }
1547
1548 const int tside = ED_tail_port(e).side;
1549 const int hside = ED_head_port(e).side;
1550 if ((tside == BOTTOM && hside != TOP) || (hside == BOTTOM && tside != TOP)) {
1551 make_flat_bottom_edges(g, sp, P, edges, cnt, e, et == EDGETYPE_SPLINE);
1552 return 0;
1553 }
1554
1555 node_t *tn = agtail(e);
1556 node_t *hn = aghead(e);
1557 const int r = ND_rank(tn);
1558 if (r > 0) {
1559 rank_t *prevr;
1560 if (GD_has_labels(g->root) & EDGE_LABEL)
1561 prevr = GD_rank(g) + (r - 2);
1562 else
1563 prevr = GD_rank(g) + (r - 1);
1564 vspace = ND_coord(prevr->v[0]).y - prevr->ht1 - ND_coord(tn).y -
1565 GD_rank(g)[r].ht2;
1566 } else {
1567 vspace = GD_ranksep(g);
1568 }
1569 stepx = sp.Multisep / (cnt + 1);
1570 stepy = vspace / (cnt + 1);
1571
1572 makeFlatEnd(g, sp, P, tn, e, &tend, true);
1573 makeFlatEnd(g, sp, P, hn, e, &hend, false);
1574
1575 for (unsigned i = 0; i < cnt; i++) {
1576 boxf b;
1577 e = edges[i];
1578 size_t boxn = 0;
1579
1580 boxf boxes[3];
1581
1582 b = tend.boxes[tend.boxn - 1];
1583 boxes[boxn].LL.x = b.LL.x;
1584 boxes[boxn].LL.y = b.UR.y;
1585 boxes[boxn].UR.x = b.UR.x + (i + 1) * stepx;
1586 boxes[boxn].UR.y = b.UR.y + (i + 1) * stepy;
1587 boxn++;
1588 boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x;
1589 boxes[boxn].LL.y = boxes[boxn - 1].UR.y;
1590 boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x;
1591 boxes[boxn].UR.y = boxes[boxn].LL.y + stepy;
1592 boxn++;
1593 b = hend.boxes[hend.boxn - 1];
1594 boxes[boxn].UR.x = b.UR.x;
1595 boxes[boxn].LL.y = b.UR.y;
1596 boxes[boxn].LL.x = b.LL.x - (i + 1) * stepx;
1597 boxes[boxn].UR.y = boxes[boxn - 1].LL.y;
1598 boxn++;
1599 assert(boxn == sizeof(boxes) / sizeof(boxes[0]));
1600
1601 for (j = 0; j < tend.boxn; j++)
1602 add_box(P, tend.boxes[j]);
1603 for (size_t k = 0; k < boxn; k++)
1604 add_box(P, boxes[k]);
1605 for (j = hend.boxn - 1; j >= 0; j--)
1606 add_box(P, hend.boxes[j]);
1607
1608 pointf *ps = NULL;
1609 size_t pn = 0;
1610 if (et == EDGETYPE_SPLINE)
1611 ps = routesplines(P, &pn);
1612 else
1613 ps = routepolylines(P, &pn);
1614 if (pn == 0) {
1615 free(ps);
1616 return 0;
1617 }
1618 clip_and_install(e, aghead(e), ps, pn, &sinfo);
1619 free(ps);
1620 P->nbox = 0;
1621 }
1622 return 0;
1623}
1624
1626static bool leftOf(pointf p1, pointf p2, pointf p3) {
1627 return (p1.y - p2.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p1.x - p2.x) > 0;
1628}
1629
1630/* Create an edge as line segment. We guarantee that the points
1631 * are always drawn downwards. This means that for flipped edges,
1632 * we draw from the head to the tail. The routine returns the
1633 * end node of the edge in *hp. The points are stored in the
1634 * given array of points, and the number of points is returned.
1635 *
1636 * If the edge has a label, the edge is draw as two segments, with
1637 * the bend near the label.
1638 *
1639 * If the endpoints are on adjacent ranks, revert to usual code by
1640 * returning 0.
1641 * This is done because the usual code handles the interaction of
1642 * multiple edges better.
1643 */
1644static int makeLineEdge(graph_t *g, edge_t *fe, points_t *points, node_t **hp) {
1645 int delr, pn;
1646 node_t *hn;
1647 node_t *tn;
1648 edge_t *e = fe;
1649 pointf startp, endp, lp;
1650 pointf dimen;
1651 double width, height;
1652
1653 while (ED_edge_type(e) != NORMAL)
1654 e = ED_to_orig(e);
1655 hn = aghead(e);
1656 tn = agtail(e);
1657 delr = abs(ND_rank(hn) - ND_rank(tn));
1658 if (delr == 1 || (delr == 2 && (GD_has_labels(g->root) & EDGE_LABEL)))
1659 return 0;
1660 if (agtail(fe) == agtail(e)) {
1661 *hp = hn;
1662 startp = add_pointf(ND_coord(tn), ED_tail_port(e).p);
1663 endp = add_pointf(ND_coord(hn), ED_head_port(e).p);
1664 } else {
1665 *hp = tn;
1666 startp = add_pointf(ND_coord(hn), ED_head_port(e).p);
1667 endp = add_pointf(ND_coord(tn), ED_tail_port(e).p);
1668 }
1669
1670 if (ED_label(e)) {
1671 dimen = ED_label(e)->dimen;
1672 if (GD_flip(agraphof(hn))) {
1673 width = dimen.y;
1674 height = dimen.x;
1675 } else {
1676 width = dimen.x;
1677 height = dimen.y;
1678 }
1679
1680 lp = ED_label(e)->pos;
1681 if (leftOf(endp, startp, lp)) {
1682 lp.x += width / 2.0;
1683 lp.y -= height / 2.0;
1684 } else {
1685 lp.x -= width / 2.0;
1686 lp.y += height / 2.0;
1687 }
1688
1689 LIST_APPEND(points, startp);
1690 LIST_APPEND(points, startp);
1691 LIST_APPEND(points, lp);
1692 LIST_APPEND(points, lp);
1693 LIST_APPEND(points, lp);
1694 LIST_APPEND(points, endp);
1695 LIST_APPEND(points, endp);
1696 pn = 7;
1697 } else {
1698 LIST_APPEND(points, startp);
1699 LIST_APPEND(points, startp);
1700 LIST_APPEND(points, endp);
1701 LIST_APPEND(points, endp);
1702 pn = 4;
1703 }
1704
1705 return pn;
1706}
1707
1709 edge_t **edges, unsigned cnt, int et) {
1710 node_t *tn, *hn;
1711 Agedgeinfo_t fwdedgeai, fwdedgebi, fwdedgei;
1712 Agedgepair_t fwdedgea, fwdedgeb, fwdedge;
1713 edge_t *e, *fe, *le, *segfirst;
1714 pathend_t tend, hend;
1715 boxf b;
1716 int sl;
1717 points_t pointfs = {0};
1718 points_t pointfs2 = {0};
1719
1720 fwdedgea.out.base.data = &fwdedgeai.hdr;
1721 fwdedgeb.out.base.data = &fwdedgebi.hdr;
1722 fwdedge.out.base.data = &fwdedgei.hdr;
1723
1724 sl = 0;
1725 e = *edges;
1726 bool hackflag = false;
1727 if (abs(ND_rank(agtail(e)) - ND_rank(aghead(e))) > 1) {
1728 fwdedgeai = *(Agedgeinfo_t *)((uintptr_t)e->base.data -
1729 offsetof(Agedgeinfo_t, hdr));
1730 fwdedgea.out = *e;
1731 fwdedgea.in = *AGOUT2IN(e);
1732 fwdedgea.out.base.data = &fwdedgeai.hdr;
1733 if (ED_tree_index(e) & BWDEDGE) {
1734 makefwdedge(&fwdedgeb.out, e);
1735 agtail(&fwdedgea.out) = aghead(e);
1736 ED_tail_port(&fwdedgea.out) = ED_head_port(e);
1737 } else {
1738 fwdedgebi = *(Agedgeinfo_t *)((uintptr_t)e->base.data -
1739 offsetof(Agedgeinfo_t, hdr));
1740 fwdedgeb.out = *e;
1741 fwdedgeb.out.base.data = &fwdedgebi.hdr;
1742 agtail(&fwdedgea.out) = agtail(e);
1743 fwdedgeb.in = *AGOUT2IN(e);
1744 }
1745 le = getmainedge(e);
1746 while (ED_to_virt(le))
1747 le = ED_to_virt(le);
1748 aghead(&fwdedgea.out) = aghead(le);
1749 ED_head_port(&fwdedgea.out).defined = false;
1750 ED_edge_type(&fwdedgea.out) = VIRTUAL;
1751 ED_head_port(&fwdedgea.out).p.x = ED_head_port(&fwdedgea.out).p.y = 0;
1752 ED_to_orig(&fwdedgea.out) = e;
1753 e = &fwdedgea.out;
1754 hackflag = true;
1755 } else {
1756 if (ED_tree_index(e) & BWDEDGE) {
1757 makefwdedge(&fwdedgea.out, e);
1758 e = &fwdedgea.out;
1759 }
1760 }
1761 fe = e;
1762
1763 /* compute the spline points for the edge */
1764
1765 if (et == EDGETYPE_LINE && makeLineEdge(g, fe, &pointfs, &hn)) {
1766 } else {
1767 bool is_spline = et == EDGETYPE_SPLINE;
1768 boxes_t boxes = {0};
1769 segfirst = e;
1770 tn = agtail(e);
1771 hn = aghead(e);
1772 b = tend.nb = maximal_bbox(g, *sp, tn, NULL, e);
1773 beginpath(P, e, REGULAREDGE, &tend, spline_merge(tn));
1774 b.UR.y = tend.boxes[tend.boxn - 1].UR.y;
1775 b.LL.y = tend.boxes[tend.boxn - 1].LL.y;
1776 b = makeregularend(b, BOTTOM, ND_coord(tn).y - GD_rank(g)[ND_rank(tn)].ht1);
1777 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
1778 tend.boxes[tend.boxn++] = b;
1779 bool smode = false;
1780 bool si = false;
1781 while (ND_node_type(hn) == VIRTUAL && !sinfo.splineMerge(hn)) {
1782 LIST_APPEND(&boxes, rank_box(sp, g, ND_rank(tn)));
1783 if (!smode && ((sl = straight_len(hn)) >=
1784 ((GD_has_labels(g->root) & EDGE_LABEL) ? 4 + 1 : 2 + 1))) {
1785 smode = true;
1786 si = true;
1787 sl -= 2;
1788 }
1789 if ((!smode || si) && ND_out(hn).size > 0) {
1790 si = false;
1791 LIST_APPEND(&boxes, maximal_bbox(g, *sp, hn, e, ND_out(hn).list[0]));
1792 e = ND_out(hn).list[0];
1793 tn = agtail(e);
1794 hn = aghead(e);
1795 continue;
1796 }
1797 hend.nb = maximal_bbox(g, *sp, hn, e, ND_out(hn).list[0]);
1798 endpath(P, e, REGULAREDGE, &hend, spline_merge(aghead(e)));
1799 b = makeregularend(hend.boxes[hend.boxn - 1], TOP,
1800 ND_coord(hn).y + GD_rank(g)[ND_rank(hn)].ht2);
1801 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
1802 hend.boxes[hend.boxn++] = b;
1803 P->end.theta = M_PI / 2, P->end.constrained = true;
1804 completeregularpath(P, segfirst, e, &tend, &hend, &boxes);
1805 pointf *ps = NULL;
1806 size_t pn = 0;
1807 if (is_spline)
1808 ps = routesplines(P, &pn);
1809 else {
1810 ps = routepolylines(P, &pn);
1811 if (et == EDGETYPE_LINE && pn > 4) {
1812 ps[1] = ps[0];
1813 ps[3] = ps[2] = ps[pn - 1];
1814 pn = 4;
1815 }
1816 }
1817 if (pn == 0) {
1818 free(ps);
1819 LIST_FREE(&boxes);
1820 LIST_FREE(&pointfs);
1821 LIST_FREE(&pointfs2);
1822 return;
1823 }
1824
1825 for (size_t i = 0; i < pn; i++) {
1826 LIST_APPEND(&pointfs, ps[i]);
1827 }
1828 free(ps);
1829 e = straight_path(ND_out(hn).list[0], sl, &pointfs);
1830 recover_slack(segfirst, P);
1831 if (e == NULL) {
1832 LIST_FREE(&boxes);
1833 LIST_FREE(&pointfs);
1834 LIST_FREE(&pointfs2);
1835 return;
1836 }
1837 segfirst = e;
1838 tn = agtail(e);
1839 hn = aghead(e);
1840 LIST_CLEAR(&boxes);
1841 tend.nb = maximal_bbox(g, *sp, tn, ND_in(tn).list[0], e);
1842 beginpath(P, e, REGULAREDGE, &tend, spline_merge(tn));
1843 b = makeregularend(tend.boxes[tend.boxn - 1], BOTTOM,
1844 ND_coord(tn).y - GD_rank(g)[ND_rank(tn)].ht1);
1845 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
1846 tend.boxes[tend.boxn++] = b;
1847 P->start.theta = -M_PI / 2, P->start.constrained = true;
1848 smode = false;
1849 }
1850 LIST_APPEND(&boxes, rank_box(sp, g, ND_rank(tn)));
1851 b = hend.nb = maximal_bbox(g, *sp, hn, e, NULL);
1852 endpath(P, hackflag ? &fwdedgeb.out : e, REGULAREDGE, &hend,
1853 spline_merge(aghead(e)));
1854 b.UR.y = hend.boxes[hend.boxn - 1].UR.y;
1855 b.LL.y = hend.boxes[hend.boxn - 1].LL.y;
1856 b = makeregularend(b, TOP, ND_coord(hn).y + GD_rank(g)[ND_rank(hn)].ht2);
1857 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
1858 hend.boxes[hend.boxn++] = b;
1859 completeregularpath(P, segfirst, e, &tend, &hend, &boxes);
1860 LIST_FREE(&boxes);
1861 pointf *ps = NULL;
1862 size_t pn = 0;
1863 if (is_spline)
1864 ps = routesplines(P, &pn);
1865 else
1866 ps = routepolylines(P, &pn);
1867 if (et == EDGETYPE_LINE && pn > 4) {
1868 /* Here we have used the polyline case to handle
1869 * an edge between two nodes on adjacent ranks. If the
1870 * results really is a polyline, straighten it.
1871 */
1872 ps[1] = ps[0];
1873 ps[3] = ps[2] = ps[pn - 1];
1874 pn = 4;
1875 }
1876 if (pn == 0) {
1877 free(ps);
1878 LIST_FREE(&pointfs);
1879 LIST_FREE(&pointfs2);
1880 return;
1881 }
1882 for (size_t i = 0; i < pn; i++) {
1883 LIST_APPEND(&pointfs, ps[i]);
1884 }
1885 free(ps);
1886 recover_slack(segfirst, P);
1887 hn = hackflag ? aghead(&fwdedgeb.out) : aghead(e);
1888 }
1889
1890 /* make copies of the spline points, one per multi-edge */
1891
1892 if (cnt == 1) {
1893 LIST_SYNC(&pointfs);
1894 clip_and_install(fe, hn, LIST_FRONT(&pointfs), LIST_SIZE(&pointfs), &sinfo);
1895 LIST_FREE(&pointfs);
1896 LIST_FREE(&pointfs2);
1897 return;
1898 }
1899 const double dx = sp->Multisep * (cnt - 1) / 2;
1900 for (size_t k = 1; k + 1 < LIST_SIZE(&pointfs); k++)
1901 LIST_AT(&pointfs, k)->x -= dx;
1902
1903 for (size_t k = 0; k < LIST_SIZE(&pointfs); k++)
1904 LIST_APPEND(&pointfs2, LIST_GET(&pointfs, k));
1905 LIST_SYNC(&pointfs2);
1906 clip_and_install(fe, hn, LIST_FRONT(&pointfs2), LIST_SIZE(&pointfs2), &sinfo);
1907 for (unsigned j = 1; j < cnt; j++) {
1908 e = edges[j];
1909 if (ED_tree_index(e) & BWDEDGE) {
1910 makefwdedge(&fwdedge.out, e);
1911 e = &fwdedge.out;
1912 }
1913 for (size_t k = 1; k + 1 < LIST_SIZE(&pointfs); k++)
1914 LIST_AT(&pointfs, k)->x += sp->Multisep;
1915 LIST_CLEAR(&pointfs2);
1916 for (size_t k = 0; k < LIST_SIZE(&pointfs); k++)
1917 LIST_APPEND(&pointfs2, LIST_GET(&pointfs, k));
1918 LIST_SYNC(&pointfs2);
1919 clip_and_install(e, aghead(e), LIST_FRONT(&pointfs2), LIST_SIZE(&pointfs2),
1920 &sinfo);
1921 }
1922 LIST_FREE(&pointfs);
1923 LIST_FREE(&pointfs2);
1924}
1925
1926/* regular edges */
1927
1928static void completeregularpath(path *P, edge_t *first, edge_t *last,
1929 pathend_t *tendp, pathend_t *hendp,
1930 const boxes_t *boxes) {
1931 edge_t *uleft = top_bound(first, -1);
1932 edge_t *uright = top_bound(first, 1);
1933 if (uleft) {
1934 if (getsplinepoints(uleft) == NULL)
1935 return;
1936 }
1937 if (uright) {
1938 if (getsplinepoints(uright) == NULL)
1939 return;
1940 }
1941 edge_t *lleft = bot_bound(last, -1);
1942 edge_t *lright = bot_bound(last, 1);
1943 if (lleft) {
1944 if (getsplinepoints(lleft) == NULL)
1945 return;
1946 }
1947 if (lright) {
1948 if (getsplinepoints(lright) == NULL)
1949 return;
1950 }
1951 for (int i = 0; i < tendp->boxn; i++)
1952 add_box(P, tendp->boxes[i]);
1953 const size_t fb = P->nbox + 1;
1954 const size_t lb = fb + LIST_SIZE(boxes) - 3;
1955 for (size_t i = 0; i < LIST_SIZE(boxes); i++)
1956 add_box(P, LIST_GET(boxes, i));
1957 for (int i = hendp->boxn - 1; i >= 0; i--)
1958 add_box(P, hendp->boxes[i]);
1959 adjustregularpath(P, fb, lb);
1960}
1961
1962/* Add box to fill between node and interrank space. Needed because
1963 * nodes in a given rank can differ in height.
1964 * for now, regular edges always go from top to bottom
1965 */
1966static boxf makeregularend(boxf b, int side, double y) {
1967 assert(side == BOTTOM || side == TOP);
1968 if (side == BOTTOM) {
1969 return (boxf){{b.LL.x, y}, {b.UR.x, b.LL.y}};
1970 }
1971 return (boxf){{b.LL.x, b.UR.y}, {b.UR.x, y}};
1972}
1973
1974/* make sure the path is wide enough.
1975 * the % 2 was so that in rank boxes would only be grown if
1976 * they were == 0 while inter-rank boxes could be stretched to a min
1977 * width.
1978 * The list of boxes has three parts: tail boxes, path boxes, and head
1979 * boxes. (Note that because of back edges, the tail boxes might actually
1980 * belong to the head node, and vice versa.) fb is the index of the
1981 * first interrank path box and lb is the last interrank path box.
1982 * If fb > lb, there are none.
1983 *
1984 * The second for loop was added by ek long ago, and apparently is intended
1985 * to guarantee an overlap between adjacent boxes of at least MINW.
1986 * It doesn't do this.
1987 */
1988static void adjustregularpath(path *P, size_t fb, size_t lb) {
1989 boxf *bp1, *bp2;
1990
1991 for (size_t i = fb - 1; i < lb + 1; i++) {
1992 bp1 = &P->boxes[i];
1993 if ((i - fb) % 2 == 0) {
1994 if (bp1->LL.x >= bp1->UR.x) {
1995 double x = (bp1->LL.x + bp1->UR.x) / 2;
1996 bp1->LL.x = x - HALFMINW;
1997 bp1->UR.x = x + HALFMINW;
1998 }
1999 } else {
2000 if (bp1->LL.x + MINW > bp1->UR.x) {
2001 double x = (bp1->LL.x + bp1->UR.x) / 2;
2002 bp1->LL.x = x - HALFMINW;
2003 bp1->UR.x = x + HALFMINW;
2004 }
2005 }
2006 }
2007 for (size_t i = 0; i + 1 < P->nbox; i++) {
2008 bp1 = &P->boxes[i], bp2 = &P->boxes[i + 1];
2009 if (i >= fb && i <= lb && (i - fb) % 2 == 0) {
2010 if (bp1->LL.x + MINW > bp2->UR.x)
2011 bp2->UR.x = bp1->LL.x + MINW;
2012 if (bp1->UR.x - MINW < bp2->LL.x)
2013 bp2->LL.x = bp1->UR.x - MINW;
2014 } else if (i + 1 >= fb && i < lb && (i + 1 - fb) % 2 == 0) {
2015 if (bp1->LL.x + MINW > bp2->UR.x)
2016 bp1->LL.x = bp2->UR.x - MINW;
2017 if (bp1->UR.x - MINW < bp2->LL.x)
2018 bp1->UR.x = bp2->LL.x + MINW;
2019 }
2020 }
2021}
2022
2023static boxf rank_box(spline_info_t *sp, graph_t *g, int r) {
2024 boxf b = sp->Rank_box[r];
2025 if (b.LL.x == b.UR.x) {
2026 node_t *const left0 = GD_rank(g)[r].v[0];
2027 node_t *const left1 = GD_rank(g)[r + 1].v[0];
2028 b.LL.x = sp->LeftBound;
2029 b.LL.y = ND_coord(left1).y + GD_rank(g)[r + 1].ht2;
2030 b.UR.x = sp->RightBound;
2031 b.UR.y = ND_coord(left0).y - GD_rank(g)[r].ht1;
2032 sp->Rank_box[r] = b;
2033 }
2034 return b;
2035}
2036
2037/* returns count of vertically aligned edges starting at n */
2038static int straight_len(node_t *n) {
2039 int cnt = 0;
2040 node_t *v;
2041
2042 v = n;
2043 while (ND_out(v).size > 0) {
2044 v = aghead(ND_out(v).list[0]);
2045 if (ND_node_type(v) != VIRTUAL)
2046 break;
2047 if (ND_out(v).size != 1 || ND_in(v).size != 1)
2048 break;
2049 if (ND_coord(v).x != ND_coord(n).x)
2050 break;
2051 cnt++;
2052 }
2053 return cnt;
2054}
2055
2056static edge_t *straight_path(edge_t *e, int cnt, points_t *plist) {
2057 edge_t *f = e;
2058
2059 while (cnt--)
2060 f = ND_out(aghead(f)).list[0];
2061 assert(!LIST_IS_EMPTY(plist));
2062 LIST_APPEND(plist, LIST_GET(plist, LIST_SIZE(plist) - 1));
2063 LIST_APPEND(plist, LIST_GET(plist, LIST_SIZE(plist) - 1));
2064
2065 return f;
2066}
2067
2068static void recover_slack(edge_t *e, path *p) {
2069 node_t *vn;
2070
2071 size_t b = 0; // skip first rank box
2072 for (vn = aghead(e); ND_node_type(vn) == VIRTUAL && !sinfo.splineMerge(vn);
2073 vn = aghead(ND_out(vn).list[0])) {
2074 while (b < p->nbox && p->boxes[b].LL.y > ND_coord(vn).y)
2075 b++;
2076 if (b >= p->nbox)
2077 break;
2078 if (p->boxes[b].UR.y < ND_coord(vn).y) {
2079 if (ND_out(vn).size == 0) {
2080 break;
2081 }
2082 continue;
2083 }
2084 if (ND_label(vn))
2085 resize_vn(vn, p->boxes[b].LL.x, p->boxes[b].UR.x,
2086 p->boxes[b].UR.x + ND_rw(vn));
2087 else
2088 resize_vn(vn, p->boxes[b].LL.x, (p->boxes[b].LL.x + p->boxes[b].UR.x) / 2,
2089 p->boxes[b].UR.x);
2090 if (ND_out(vn).size == 0) {
2091 break;
2092 }
2093 }
2094}
2095
2096static void resize_vn(node_t *vn, double lx, double cx, double rx) {
2097 ND_coord(vn).x = cx;
2098 ND_lw(vn) = cx - lx, ND_rw(vn) = rx - cx;
2099}
2100
2101/* side > 0 means right. side < 0 means left */
2102static edge_t *top_bound(edge_t *e, int side) {
2103 edge_t *f, *ans = NULL;
2104 int i;
2105
2106 for (i = 0; (f = ND_out(agtail(e)).list[i]); i++) {
2107 if (side * (ND_order(aghead(f)) - ND_order(aghead(e))) <= 0)
2108 continue;
2109 if (ED_spl(f) == NULL &&
2110 (ED_to_orig(f) == NULL || ED_spl(ED_to_orig(f)) == NULL))
2111 continue;
2112 if (ans == NULL || side * (ND_order(aghead(ans)) - ND_order(aghead(f))) > 0)
2113 ans = f;
2114 }
2115 return ans;
2116}
2117
2118static edge_t *bot_bound(edge_t *e, int side) {
2119 edge_t *f, *ans = NULL;
2120 int i;
2121
2122 for (i = 0; (f = ND_in(aghead(e)).list[i]); i++) {
2123 if (side * (ND_order(agtail(f)) - ND_order(agtail(e))) <= 0)
2124 continue;
2125 if (ED_spl(f) == NULL &&
2126 (ED_to_orig(f) == NULL || ED_spl(ED_to_orig(f)) == NULL))
2127 continue;
2128 if (ans == NULL || side * (ND_order(agtail(ans)) - ND_order(agtail(f))) > 0)
2129 ans = f;
2130 }
2131 return ans;
2132}
2133
2134/* common routines */
2135
2136static bool cl_vninside(graph_t *cl, node_t *n) {
2137 return BETWEEN(GD_bb(cl).LL.x, ND_coord(n).x, GD_bb(cl).UR.x) &&
2138 BETWEEN(GD_bb(cl).LL.y, ND_coord(n).y, GD_bb(cl).UR.y);
2139}
2140
2141/* All nodes belong to some cluster, which may be the root graph.
2142 * For the following, we only want a cluster if it is a real cluster
2143 * It is not clear this will handle all potential problems. It seems one
2144 * could have hcl and tcl contained in cl, which would also cause problems.
2145 */
2146#define REAL_CLUSTER(n) (ND_clust(n) == g ? NULL : ND_clust(n))
2147
2148/* returns the cluster of (adj) that interferes with n,
2149 */
2150static Agraph_t *cl_bound(graph_t *g, node_t *n, node_t *adj) {
2151 graph_t *rv, *cl, *tcl, *hcl;
2152 edge_t *orig;
2153
2154 rv = NULL;
2155 if (ND_node_type(n) == NORMAL || ND_out(n).size == 0)
2156 tcl = hcl = ND_clust(n);
2157 else {
2158 orig = ED_to_orig(ND_out(n).list[0]);
2159 tcl = ND_clust(agtail(orig));
2160 hcl = ND_clust(aghead(orig));
2161 }
2162 if (ND_node_type(adj) == NORMAL) {
2163 cl = REAL_CLUSTER(adj);
2164 if (cl && cl != tcl && cl != hcl)
2165 rv = cl;
2166 } else {
2167 orig = ED_to_orig(ND_out(adj).list[0]);
2168 cl = REAL_CLUSTER(agtail(orig));
2169 if (cl && cl != tcl && cl != hcl && cl_vninside(cl, adj))
2170 rv = cl;
2171 else {
2172 cl = REAL_CLUSTER(aghead(orig));
2173 if (cl && cl != tcl && cl != hcl && cl_vninside(cl, adj))
2174 rv = cl;
2175 }
2176 }
2177 return rv;
2178}
2179
2180/* Return an initial bounding box to be used for building the
2181 * beginning or ending of the path of boxes.
2182 * Height reflects height of tallest node on rank.
2183 * The extra space provided by FUDGE allows begin/endpath to create a box
2184 * FUDGE-2 away from the node, so the routing can avoid the node and the
2185 * box is at least 2 wide.
2186 */
2187#define FUDGE 4
2188
2190 edge_t *ie, edge_t *oe) {
2191 double b, nb;
2192 graph_t *left_cl, *right_cl;
2193 node_t *left, *right;
2194 boxf rv;
2195
2196 left_cl = right_cl = NULL;
2197
2198 /* give this node all the available space up to its neighbors */
2199 b = (double)(ND_coord(vn).x - ND_lw(vn) - FUDGE);
2200 if ((left = neighbor(g, vn, ie, oe, -1))) {
2201 if ((left_cl = cl_bound(g, vn, left)))
2202 nb = GD_bb(left_cl).UR.x + sp.Splinesep;
2203 else {
2204 nb = (double)(ND_coord(left).x + ND_mval(left));
2205 if (ND_node_type(left) == NORMAL)
2206 nb += GD_nodesep(g) / 2.;
2207 else
2208 nb += sp.Splinesep;
2209 }
2210 if (nb < b)
2211 b = nb;
2212 rv.LL.x = round(b);
2213 } else
2214 rv.LL.x = fmin(round(b), sp.LeftBound);
2215
2216 /* we have to leave room for our own label! */
2217 if (ND_node_type(vn) == VIRTUAL && ND_label(vn))
2218 b = (double)(ND_coord(vn).x + 10);
2219 else
2220 b = (double)(ND_coord(vn).x + ND_rw(vn) + FUDGE);
2221 if ((right = neighbor(g, vn, ie, oe, 1))) {
2222 if ((right_cl = cl_bound(g, vn, right)))
2223 nb = GD_bb(right_cl).LL.x - sp.Splinesep;
2224 else {
2225 nb = ND_coord(right).x - ND_lw(right);
2226 if (ND_node_type(right) == NORMAL)
2227 nb -= GD_nodesep(g) / 2.;
2228 else
2229 nb -= sp.Splinesep;
2230 }
2231 if (nb > b)
2232 b = nb;
2233 rv.UR.x = round(b);
2234 } else
2235 rv.UR.x = fmax(round(b), sp.RightBound);
2236
2237 if (ND_node_type(vn) == VIRTUAL && ND_label(vn)) {
2238 rv.UR.x -= ND_rw(vn);
2239 if (rv.UR.x < rv.LL.x)
2240 rv.UR.x = ND_coord(vn).x;
2241 }
2242
2243 rv.LL.y = ND_coord(vn).y - GD_rank(g)[ND_rank(vn)].ht1;
2244 rv.UR.y = ND_coord(vn).y + GD_rank(g)[ND_rank(vn)].ht2;
2245 return rv;
2246}
2247
2248static node_t *neighbor(graph_t *g, node_t *vn, edge_t *ie, edge_t *oe,
2249 int dir) {
2250 int i;
2251 node_t *n, *rv = NULL;
2252 rank_t *rank = &GD_rank(g)[ND_rank(vn)];
2253
2254 for (i = ND_order(vn) + dir; i >= 0 && i < rank->n; i += dir) {
2255 n = rank->v[i];
2256 if (ND_node_type(n) == VIRTUAL && ND_label(n)) {
2257 rv = n;
2258 break;
2259 }
2260 if (ND_node_type(n) == NORMAL) {
2261 rv = n;
2262 break;
2263 }
2264 if (!pathscross(n, vn, ie, oe)) {
2265 rv = n;
2266 break;
2267 }
2268 }
2269 return rv;
2270}
2271
2272static bool pathscross(node_t *n0, node_t *n1, edge_t *ie1, edge_t *oe1) {
2273 edge_t *e0, *e1;
2274 node_t *na, *nb;
2275 int order, cnt;
2276
2277 order = ND_order(n0) > ND_order(n1);
2278 if (ND_out(n0).size != 1 && ND_out(n1).size != 1)
2279 return false;
2280 e1 = oe1;
2281 if (ND_out(n0).size == 1 && e1) {
2282 e0 = ND_out(n0).list[0];
2283 for (cnt = 0; cnt < 2; cnt++) {
2284 if ((na = aghead(e0)) == (nb = aghead(e1)))
2285 break;
2286 if (order != (ND_order(na) > ND_order(nb)))
2287 return true;
2288 if (ND_out(na).size != 1 || ND_node_type(na) == NORMAL)
2289 break;
2290 e0 = ND_out(na).list[0];
2291 if (ND_out(nb).size != 1 || ND_node_type(nb) == NORMAL)
2292 break;
2293 e1 = ND_out(nb).list[0];
2294 }
2295 }
2296 e1 = ie1;
2297 if (ND_in(n0).size == 1 && e1) {
2298 e0 = ND_in(n0).list[0];
2299 for (cnt = 0; cnt < 2; cnt++) {
2300 if ((na = agtail(e0)) == (nb = agtail(e1)))
2301 break;
2302 if (order != (ND_order(na) > ND_order(nb)))
2303 return true;
2304 if (ND_in(na).size != 1 || ND_node_type(na) == NORMAL)
2305 break;
2306 e0 = ND_in(na).list[0];
2307 if (ND_in(nb).size != 1 || ND_node_type(nb) == NORMAL)
2308 break;
2309 e1 = ND_in(nb).list[0];
2310 }
2311 }
2312 return false;
2313}
2314
2315#ifdef DEBUG
2316void showpath(path *p) {
2317 pointf LL, UR;
2318
2319 fprintf(stderr, "%%!PS\n");
2320 for (size_t i = 0; i < p->nbox; i++) {
2321 LL = p->boxes[i].LL;
2322 UR = p->boxes[i].UR;
2323 fprintf(stderr,
2324 "newpath %.04f %.04f moveto %.04f %.04f lineto %.04f %.04f lineto "
2325 "%.04f %.04f lineto closepath stroke\n",
2326 LL.x, LL.y, UR.x, LL.y, UR.x, UR.y, LL.x, UR.y);
2327 }
2328 fprintf(stderr, "showpage\n");
2329}
2330#endif
int normalize(graph_t *g)
Definition adjust.c:722
static agxbuf last
last message
Definition agerror.c:31
Dynamically expanding string buffers.
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:97
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:252
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:325
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 BETWEEN(a, b, c)
Definition arith.h:38
#define MIN(a, b)
Definition arith.h:28
#define M_PI
Definition arith.h:41
#define MAX(a, b)
Definition arith.h:33
#define right(i)
Definition closest.c:74
void setEdgeType(graph_t *g, int defaultValue)
Definition utils.c:1358
void updateBB(graph_t *g, textlabel_t *lp)
Definition utils.c:576
#define SELFWPEDGE
Definition const.h:152
#define NORMAL
Definition const.h:24
#define EDGETYPE_SPLINE
Definition const.h:239
#define FLATORDER
Definition const.h:28
#define REGULAREDGE
Definition const.h:150
#define EDGE_LABEL
Definition const.h:167
#define IGNORED
Definition const.h:30
#define RANKDIR_TB
Definition const.h:181
#define EDGETYPE_CURVED
Definition const.h:236
#define VIRTUAL
Definition const.h:25
#define RANKDIR_LR
Definition const.h:182
#define EDGETYPE_ORTHO
Definition const.h:238
#define EDGETYPE_PLINE
Definition const.h:237
#define EDGETYPE_LINE
Definition const.h:235
#define EDGETYPEMASK
Definition const.h:155
#define EDGETYPE_NONE
Definition const.h:234
#define GVSPLINES
Definition const.h:164
#define BOTTOM
Definition const.h:117
#define FLATEDGE
Definition const.h:151
#define TOP
Definition const.h:119
#define SELFNPEDGE
Definition const.h:153
void dot_init_node_edge(graph_t *g)
Definition dotinit.c:89
void dot_cleanup(graph_t *g)
Definition dotinit.c:181
int dot_mincross(Agraph_t *)
Definition mincross.c:332
void dot_sameports(Agraph_t *)
void dot_rank(Agraph_t *)
Definition rank.c:528
WUR int dot_position(Agraph_t *)
Definition position.c:121
static bool leftOf(pointf p1, pointf p2, pointf p3)
Return true if p3 is to left of ray p1->p2.
static edge_t * top_bound(edge_t *e, int side)
static void makeBottomFlatEnd(graph_t *g, const spline_info_t sp, path *P, node_t *n, edge_t *e, pathend_t *endp, bool isBegin)
static void make_flat_labeled_edge(graph_t *g, const spline_info_t sp, path *P, edge_t *e, int et)
#define HALFMINW
Definition dotsplines.c:39
static void recover_slack(edge_t *e, path *p)
static void swap_bezier(bezier *b)
Definition dotsplines.c:144
static void make_regular_edge(graph_t *g, spline_info_t *sp, path *P, edge_t **edges, unsigned cnt, int et)
static bool cl_vninside(graph_t *cl, node_t *n)
static Agraph_t * cl_bound(graph_t *g, node_t *n, node_t *adj)
#define MINW
Definition dotsplines.c:38
static edge_t * bot_bound(edge_t *e, int side)
int dot_splines(graph_t *g)
Definition dotsplines.c:487
#define FWDEDGE
Definition dotsplines.c:41
static void makeSimpleFlatLabels(node_t *tn, node_t *hn, edge_t **edges, unsigned cnt, int et, unsigned n_lbls)
Definition dotsplines.c:952
static int make_flat_edge(graph_t *g, const spline_info_t sp, path *P, edge_t **edges, unsigned cnt, int et)
static void makeSimpleFlat(node_t *tn, node_t *hn, edge_t **edges, unsigned cnt, int et)
#define NSUB
Definition dotsplines.c:36
static edge_t * cloneEdge(graph_t *g, node_t *tn, node_t *hn, edge_t *orig)
Definition dotsplines.c:892
static void setState(graph_t *auxg, attr_state_t *attr_state)
Definition dotsplines.c:689
static void edge_normalize(graph_t *g)
Definition dotsplines.c:173
static int makeLineEdge(graph_t *g, edge_t *fe, points_t *points, node_t **hp)
#define BWDEDGE
Definition dotsplines.c:42
static bool swap_ends_p(edge_t *e)
Definition dotsplines.c:113
int portcmp(port p0, port p1)
Definition dotsplines.c:128
static bool spline_merge(node_t *n)
Definition dotsplines.c:108
static void makeFlatEnd(graph_t *g, const spline_info_t sp, path *P, node_t *n, edge_t *e, pathend_t *endp, bool isBegin)
#define AUXGRAPH
Definition dotsplines.c:45
static void place_vnlabel(node_t *n)
Definition dotsplines.c:492
static void setEdgeLabelPos(graph_t *g)
Definition dotsplines.c:199
#define MAINGRAPH
Definition dotsplines.c:44
static void completeregularpath(path *P, edge_t *first, edge_t *last, pathend_t *tendp, pathend_t *hendp, const boxes_t *boxes)
static int edgecmp(const void *p0, const void *p1)
Definition dotsplines.c:543
static graph_t * cloneGraph(graph_t *g, attr_state_t *attr_state)
Definition dotsplines.c:781
static void adjustregularpath(path *P, size_t fb, size_t lb)
static void makefwdedge(edge_t *new, edge_t *old)
Definition dotsplines.c:48
static int make_flat_adj_edges(graph_t *g, edge_t **edges, unsigned cnt, edge_t *e0, int et)
#define LBL_SPACE
Definition dotsplines.c:945
static bool pathscross(node_t *n0, node_t *n1, edge_t *ie1, edge_t *oe1)
static void cleanupCloneGraph(graph_t *g, attr_state_t *attr_state)
Definition dotsplines.c:828
static int straight_len(node_t *n)
#define FUDGE
#define GRAPHTYPEMASK
Definition dotsplines.c:46
#define REAL_CLUSTER(n)
static splineInfo sinfo
Definition dotsplines.c:125
static int edgelblcmpfn(const void *x, const void *y)
Definition dotsplines.c:915
static boxf rank_box(spline_info_t *sp, graph_t *g, int r)
static void resetRW(graph_t *g)
Definition dotsplines.c:187
static pointf transformf(pointf p, pointf del, int flip)
rotate, if necessary, then translate points
Definition dotsplines.c:901
static int dot_splines_(graph_t *g, int normalize)
Definition dotsplines.c:228
static boxf makeregularend(boxf b, int side, double y)
static void swap_spline(splines *s)
Definition dotsplines.c:154
static void setflags(edge_t *e, int hint1, int hint2, int f3)
Definition dotsplines.c:505
static boxf maximal_bbox(graph_t *g, const spline_info_t sp, node_t *vn, edge_t *ie, edge_t *oe)
static void resize_vn(node_t *vn, double lx, double cx, double rx)
static void make_flat_bottom_edges(graph_t *g, const spline_info_t sp, path *P, edge_t **edges, unsigned cnt, edge_t *e, bool use_splines)
static edge_t * straight_path(edge_t *e, int cnt, points_t *plist)
static node_t * cloneNode(graph_t *g, node_t *orign)
Definition dotsplines.c:878
static float dy
Definition draw.c:43
static float dx
Definition draw.c:42
#define left
Definition dthdr.h:12
static void del(Dict_t *d, Dtlink_t **set, Agedge_t *e)
Definition edge.c:158
#define le
Definition edges.h:29
void update_bb_bz(boxf *bb, const pointf *cp)
Definition emit.c:770
struct pointf_s pointf
static WUR pointf add_pointf(pointf p, pointf q)
Definition geomprocs.h:88
Agsym_t * E_sametail
Definition globals.h:86
Agsym_t * N_fontsize
Definition globals.h:78
Agsym_t * E_labelfontsize
Definition globals.h:87
Agsym_t * E_fontcolor
Definition globals.h:84
Agsym_t * N_group
Definition globals.h:81
Agsym_t * N_width
Definition globals.h:77
Agsym_t * E_weight
Definition globals.h:83
int State
Definition globals.h:66
Agsym_t * N_orientation
Definition globals.h:80
Agsym_t * N_sides
Definition globals.h:79
Agsym_t * E_headclip
Definition globals.h:88
Agsym_t * G_ordering
Definition globals.h:75
Agsym_t * E_headlabel
Definition globals.h:86
Agsym_t * N_showboxes
Definition globals.h:79
Agsym_t * N_fontname
Definition globals.h:78
Agsym_t * E_fontname
Definition globals.h:84
Agsym_t * N_style
Definition globals.h:79
int EdgeLabelsDone
Definition globals.h:68
Agsym_t * E_dir
Definition globals.h:84
Agsym_t * E_label
Definition globals.h:84
Agsym_t * N_skew
Definition globals.h:80
Agsym_t * E_minlen
Definition globals.h:83
Agsym_t * N_shape
Definition globals.h:77
Agsym_t * N_xlabel
Definition globals.h:78
Agsym_t * E_label_float
Definition globals.h:86
Agsym_t * E_samehead
Definition globals.h:86
Agsym_t * N_nojustify
Definition globals.h:79
Agsym_t * E_taillabel
Definition globals.h:87
Agsym_t * N_label
Definition globals.h:78
Agsym_t * E_labelangle
Definition globals.h:88
Agsym_t * E_fontsize
Definition globals.h:84
Agsym_t * N_fixed
Definition globals.h:80
Agsym_t * N_peripheries
Definition globals.h:79
Agsym_t * E_labelfontname
Definition globals.h:87
Agsym_t * N_distortion
Definition globals.h:80
Agsym_t * E_xlabel
Definition globals.h:84
Agsym_t * E_constr
Definition globals.h:85
Agsym_t * N_fontcolor
Definition globals.h:78
Agsym_t * E_labelfontcolor
Definition globals.h:87
Agsym_t * E_tailclip
Definition globals.h:88
Agsym_t * E_labeldistance
Definition globals.h:88
Agsym_t * N_ordering
Definition globals.h:79
Agsym_t * N_height
Definition globals.h:77
void free(void *)
node NULL
Definition grammar.y:181
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:204
Agsym_t * agattr_text(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up text attributes of a graph
Definition attr.c:333
int agset(void *obj, char *name, const char *value)
Definition attr.c:474
Agsym_t * agnxtattr(Agraph_t *g, int kind, Agsym_t *attr)
permits traversing the list of attributes of a given type
Definition attr.c:362
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:521
int agcopyattr(void *oldobj, void *newobj)
copies all of the attributes from one object to another
Definition attr.c:632
Agsym_t * agattr_html(Agraph_t *g, int kind, char *name, const char *value)
agattr_text, but creates HTML-like values
Definition attr.c:337
#define ED_to_orig(e)
Definition types.h:598
#define ED_tree_index(e)
Definition types.h:600
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:252
#define AGMKOUT(e)
Definition cgraph.h:976
Agedge_t * agnxtin(Agraph_t *g, Agedge_t *e)
Definition edge.c:73
#define ED_head_label(e)
Definition types.h:587
#define agfindedgeattr(g, a)
Definition types.h:617
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:28
#define ED_spl(e)
Definition types.h:595
#define agtail(e)
Definition cgraph.h:982
#define ED_edge_type(e)
Definition types.h:582
#define ED_alg(e)
Definition types.h:578
#define ED_tail_label(e)
Definition types.h:596
#define ED_adjacent(e)
Definition types.h:584
#define aghead(e)
Definition cgraph.h:983
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:43
#define AGTAIL(e)
Definition cgraph.h:978
#define ED_head_port(e)
Definition types.h:588
Agedge_t * agfstin(Agraph_t *g, Agnode_t *n)
Definition edge.c:59
#define ED_label(e)
Definition types.h:589
#define ED_tail_port(e)
Definition types.h:597
#define AGHEAD(e)
Definition cgraph.h:979
#define ED_to_virt(e)
Definition types.h:599
#define AGOUT2IN(outedge)
Agedgepair_s.out -> Agedgepair_s.in/*#end#*‍/.
Definition cgraph.h:972
void agwarningf(const char *fmt,...)
Definition agerror.c:175
int agerr(agerrlevel_t level, const char *fmt,...)
Definition agerror.c:157
@ AGPREV
Definition cgraph.h:951
#define GD_minrank(g)
Definition types.h:384
#define agfindgraphattr(g, a)
Definition types.h:613
#define GD_maxrank(g)
Definition types.h:382
#define GD_drawing(g)
Definition types.h:353
Agdesc_t Agundirected
undirected
Definition graph.c:280
#define GD_has_labels(g)
Definition types.h:368
int agisdirected(Agraph_t *g)
Definition graph.c:184
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:97
#define GD_rank(g)
Definition types.h:395
#define GD_bb(g)
Definition types.h:354
#define GD_nlist(g)
Definition types.h:393
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_dotroot(g)
Definition types.h:361
#define GD_nodesep(g)
Definition types.h:394
#define GD_charset(g)
Definition types.h:367
#define GD_gvc(g)
Definition types.h:355
#define GD_flip(g)
Definition types.h:378
#define GD_ranksep(g)
Definition types.h:397
Agdesc_t Agdirected
directed
Definition graph.c:278
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:143
#define ND_rank(n)
Definition types.h:523
#define ND_ht(n)
Definition types.h:500
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:50
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:43
#define ND_next(n)
Definition types.h:510
#define ND_clust(n)
Definition types.h:489
#define ND_other(n)
Definition types.h:514
#define agfindnodeattr(g, a)
Definition types.h:615
#define ND_label(n)
Definition types.h:502
#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_mval(n)
Definition types.h:508
#define ND_order(n)
Definition types.h:513
#define ND_coord(n)
Definition types.h:490
#define ND_in(n)
Definition types.h:501
#define ND_out(n)
Definition types.h:515
Agraph_t * agraphof(void *obj)
Definition obj.c:187
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
Agraph_t * agroot(void *obj)
Definition obj.c:170
#define AGSEQ(obj)
Definition cgraph.h:225
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ 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:91
int aghtmlstr(const char *)
Definition refstr.c:440
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:52
Arithmetic helper functions.
#define SWAP(a, b)
Definition gv_math.h:133
static gdPoint * points
void mark_lowclusters(Agraph_t *root)
Definition cluster.c:400
type-generic dynamically expanding list
#define LIST_AT(list, index)
Definition list.h:210
#define LIST_APPEND(list,...)
Definition list.h:151
#define LIST(type)
Definition list.h:66
#define LIST_SIZE(list)
Definition list.h:92
#define LIST_CLEAR(list)
Definition list.h:307
#define LIST_FREE(list)
Definition list.h:413
#define LIST_SORT(list, cmp)
Definition list.h:381
#define LIST_FRONT(list)
Definition list.h:222
#define LIST_IS_EMPTY(list)
Definition list.h:102
#define LIST_SYNC(list)
Definition list.h:370
#define LIST_GET(list, index)
Definition list.h:197
#define EDGE_TYPE(g)
Definition macros.h:25
#define neighbor(t, i, edim, elist)
Definition make_map.h:41
int rank(graph_t *g, int balance, int maxiter)
Definition ns.c:1104
int orthoEdges(Agraph_t *g, bool useLbls)
Definition ortho.c:1161
void dotneato_postprocess(Agraph_t *g)
Definition postproc.c:691
bezier * new_spline(edge_t *e, size_t sz)
create and attach a new Bézier of size sz to the edge d
Definition splines.c:200
splines * getsplinepoints(edge_t *e)
Definition splines.c:1316
void clip_and_install(edge_t *fe, node_t *hn, pointf *ps, size_t pn, splineInfo *info)
Definition splines.c:221
pointf * simpleSplineRoute(pointf, pointf, Ppoly_t, size_t *, int)
Given a simple (ccw) polygon, route an edge from tp to hp.
Definition routespl.c:169
int routesplinesinit(void)
Definition routespl.c:212
shape_kind shapeOf(node_t *)
Definition shapes.c:1909
void add_box(path *, boxf)
Definition splines.c:314
void beginpath(path *, Agedge_t *, int, pathend_t *, bool)
Definition splines.c:352
pointf * routesplines(path *, size_t *)
Definition routespl.c:595
void makeSelfEdge(edge_t *edges[], size_t cnt, double sizex, double sizey, splineInfo *sinfo)
Definition splines.c:1129
void routesplinesterm(void)
Definition routespl.c:225
void endpath(path *, Agedge_t *, int, pathend_t *, bool)
Definition splines.c:543
pointf * routepolylines(path *pp, size_t *npoints)
Definition routespl.c:599
int place_portlabel(edge_t *e, bool head_p)
Definition splines.c:1267
void makeStraightEdges(graph_t *g, edge_t **edges, size_t e_cnt, int et, splineInfo *sinfo)
Definition routespl.c:973
Agobj_t base
Definition cgraph.h:269
Agrec_t hdr
Definition types.h:544
Agedge_t in
Definition cgraph.h:276
Agedge_t out
Definition cgraph.h:276
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
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:641
char * name
Definition cgraph.h:643
char * defval
Definition cgraph.h:644
attrsym_t * N_style
Definition dotsplines.c:668
attrsym_t * E_sametail
Definition dotsplines.c:648
attrsym_t * E_fontsize
Definition dotsplines.c:653
attrsym_t * N_shape
Definition dotsplines.c:667
attrsym_t * E_minlen
Definition dotsplines.c:650
attrsym_t * E_tailclip
Definition dotsplines.c:661
attrsym_t * E_taillabel
Definition dotsplines.c:662
attrsym_t * E_label
Definition dotsplines.c:656
attrsym_t * E_weight
Definition dotsplines.c:649
attrsym_t * N_nojustify
Definition dotsplines.c:682
attrsym_t * N_fontcolor
Definition dotsplines.c:671
attrsym_t * N_peripheries
Definition dotsplines.c:677
attrsym_t * E_xlabel
Definition dotsplines.c:663
attrsym_t * E_samehead
Definition dotsplines.c:647
attrsym_t * N_xlabel
Definition dotsplines.c:673
attrsym_t * E_label_float
Definition dotsplines.c:657
attrsym_t * N_fontname
Definition dotsplines.c:670
attrsym_t * E_headclip
Definition dotsplines.c:654
attrsym_t * N_sides
Definition dotsplines.c:676
attrsym_t * N_group
Definition dotsplines.c:683
attrsym_t * N_ordering
Definition dotsplines.c:675
attrsym_t * N_width
Definition dotsplines.c:666
attrsym_t * N_fixed
Definition dotsplines.c:681
attrsym_t * E_labelfontname
Definition dotsplines.c:659
attrsym_t * E_dir
Definition dotsplines.c:646
attrsym_t * E_labelfontcolor
Definition dotsplines.c:658
attrsym_t * N_height
Definition dotsplines.c:665
attrsym_t * G_ordering
Definition dotsplines.c:685
attrsym_t * E_constr
Definition dotsplines.c:645
attrsym_t * N_label
Definition dotsplines.c:672
attrsym_t * E_fontname
Definition dotsplines.c:652
attrsym_t * N_skew
Definition dotsplines.c:678
attrsym_t * N_distortion
Definition dotsplines.c:680
attrsym_t * E_fontcolor
Definition dotsplines.c:651
attrsym_t * E_headlabel
Definition dotsplines.c:655
attrsym_t * N_showboxes
Definition dotsplines.c:674
attrsym_t * N_orientation
Definition dotsplines.c:679
attrsym_t * N_fontsize
Definition dotsplines.c:669
attrsym_t * E_labelfontsize
Definition dotsplines.c:660
Definition types.h:89
size_t size
Definition types.h:91
pointf sp
Definition types.h:94
pointf * list
Definition types.h:90
uint32_t eflag
Definition types.h:93
pointf ep
Definition types.h:95
uint32_t sflag
Definition types.h:92
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
Definition cdt.h:98
Definition types.h:81
port start
Definition types.h:82
boxf * boxes
Definition types.h:85
port end
Definition types.h:83
size_t nbox
number of subdivisions
Definition types.h:84
boxf nb
Definition types.h:74
int boxn
Definition types.h:77
int sidemask
Definition types.h:76
boxf boxes[20]
Definition types.h:78
double x
Definition geom.h:29
double y
Definition geom.h:29
Definition types.h:48
pointf p
Definition types.h:49
double theta
Definition types.h:50
bool constrained
Definition types.h:55
bool defined
Definition types.h:54
double pht1
Definition types.h:207
node_t ** v
Definition types.h:202
double ht1
Definition types.h:205
double pht2
Definition types.h:208
bool(* swapEnds)(edge_t *e)
Definition types.h:67
bool(* splineMerge)(node_t *n)
Definition types.h:68
boxf * Rank_box
Definition dotsplines.c:69
double RightBound
Definition dotsplines.c:66
double LeftBound
Definition dotsplines.c:65
double Multisep
Definition dotsplines.c:68
double Splinesep
Definition dotsplines.c:67
pointf pos
Definition types.h:114
bool set
Definition types.h:123
struct poly_s poly
@ SH_RECORD
Definition types.h:187
#define SET_RANKDIR(g, rd)
Definition types.h:607
Definition grammar.c:90
struct item_s * list
Definition grammar.c:96