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