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