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