Graphviz 14.0.3~dev.20251104.0241
Loading...
Searching...
No Matches
splines.c
Go to the documentation of this file.
1
4/*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * https://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: Details at https://graphviz.org
12 *************************************************************************/
13
14
15/* Functions related to creating a spline and attaching it to
16 * an edge, starting from a list of control points.
17 */
18
19#include <math.h>
20#include <common/geomprocs.h>
21#include <common/render.h>
22#include <stdbool.h>
23#include <stddef.h>
24#include <stdint.h>
25#include <util/agxbuf.h>
26#include <util/alloc.h>
27#include <util/gv_math.h>
28#include <util/streq.h>
29#include <util/unreachable.h>
30
31#ifdef DEBUG
32static int debugleveln(edge_t* e, int i)
33{
34 return GD_showboxes(agraphof(aghead(e))) == i ||
35 GD_showboxes(agraphof(agtail(e))) == i ||
36 ED_showboxes(e) == i ||
37 ND_showboxes(aghead(e)) == i ||
38 ND_showboxes(agtail(e)) == i;
39}
40
41static void showPoints(pointf ps[], int pn)
42{
43 int bi;
44
45 LIST_APPEND(&Show_boxes, gv_strdup("%% self list"));
46 LIST_APPEND(&Show_boxes, gv_strdup("dbgstart"));
47 for (bi = 0; bi < pn; bi++) {
48 agxbuf buf = {0};
49 agxbprint(&buf, "%.5g %.5g point", ps[bi].x, ps[bi].y);
51 }
52 LIST_APPEND(&Show_boxes, gv_strdup("grestore"));
53}
54#endif
55
56/* Clip arrow to node boundary.
57 * The real work is done elsewhere. Here we get the real edge,
58 * check that the edge has arrowheads, and that an endpoint
59 * isn't a merge point where several parts of an edge meet.
60 * (e.g., with edge concentrators).
61 */
62static void
64 pointf * ps, size_t *startp, size_t *endp,
65 bezier * spl, splineInfo * info)
66{
67 edge_t *e;
68 bool j;
69
70 for (e = fe; ED_to_orig(e); e = ED_to_orig(e));
71
72 if (info->ignoreSwap)
73 j = false;
74 else
75 j = info->swapEnds(e);
76 uint32_t sflag, eflag;
77 arrow_flags(e, &sflag, &eflag);
78 if (info->splineMerge(hn))
79 eflag = ARR_NONE;
80 if (info->splineMerge(agtail(fe)))
81 sflag = ARR_NONE;
82 /* swap the two ends */
83 if (j) {
84 uint32_t i = sflag;
85 sflag = eflag;
86 eflag = i;
87 }
88 if (info->isOrtho) {
89 if (eflag || sflag)
90 arrowOrthoClip(e, ps, *startp, *endp, spl, sflag, eflag);
91 }
92 else {
93 if (sflag)
94 *startp = arrowStartClip(e, ps, *startp, *endp, spl, sflag);
95 if (eflag)
96 *endp = arrowEndClip(e, ps, *startp, *endp, spl, eflag);
97 }
98}
99
100/* Clip bezier to shape using binary search.
101 * The details of the shape are passed in the inside_context;
102 * The function providing the inside test is passed as a parameter.
103 * left_inside specifies that sp[0] is inside the node,
104 * else sp[3] is taken as inside.
105 * The points p are in node coordinates.
106 */
107void bezier_clip(inside_t * inside_context,
108 bool(*inside) (inside_t * inside_context, pointf p),
109 pointf * sp, bool left_inside)
110{
111 pointf seg[4], best[4], pt, opt, *left, *right;
112 double low, high, t, *idir, *odir;
113 bool found;
114 int i;
115
116 if (left_inside) {
117 left = NULL;
118 right = seg;
119 pt = sp[0];
120 idir = &low;
121 odir = &high;
122 } else {
123 left = seg;
124 right = NULL;
125 pt = sp[3];
126 idir = &high;
127 odir = &low;
128 }
129 found = false;
130 low = 0.0;
131 high = 1.0;
132 do {
133 opt = pt;
134 t = (high + low) / 2.0;
135 pt = Bezier(sp, t, left, right);
136 if (inside(inside_context, pt)) {
137 *idir = t;
138 for (i = 0; i < 4; i++)
139 best[i] = seg[i];
140 found = true;
141 } else {
142 *odir = t;
143 }
144 } while (fabs(opt.x - pt.x) > .5 || fabs(opt.y - pt.y) > .5);
145 if (found)
146 for (i = 0; i < 4; i++)
147 sp[i] = best[i];
148 else
149 for (i = 0; i < 4; i++)
150 sp[i] = seg[i];
151}
152
153/* Clip Bezier to node shape using binary search.
154 * left_inside specifies that curve[0] is inside the node, else
155 * curve[3] is taken as inside.
156 * Assumes ND_shape(n) and ND_shape(n)->fns->insidefn are non-NULL.
157 * See note on shape_clip.
158 */
159static void
160shape_clip0(inside_t * inside_context, node_t * n, pointf curve[4],
161 bool left_inside)
162{
163 int i;
164 double save_real_size;
165 pointf c[4];
166
167 save_real_size = ND_rw(n);
168 for (i = 0; i < 4; i++) {
169 c[i].x = curve[i].x - ND_coord(n).x;
170 c[i].y = curve[i].y - ND_coord(n).y;
171 }
172
173 bezier_clip(inside_context, ND_shape(n)->fns->insidefn, c, left_inside);
174
175 for (i = 0; i < 4; i++) {
176 curve[i].x = c[i].x + ND_coord(n).x;
177 curve[i].y = c[i].y + ND_coord(n).y;
178 }
179 ND_rw(n) = save_real_size;
180}
181
182/* Clip Bezier to node shape
183 * Uses curve[0] to determine which which side is inside the node.
184 * NOTE: This test is bad. It is possible for previous call to
185 * shape_clip to produce a Bezier with curve[0] moved to the boundary
186 * for which insidefn(curve[0]) is true. Thus, if the new Bezier is
187 * fed back to shape_clip, it will again assume left_inside is true.
188 * To be safe, shape_clip0 should guarantee that the computed boundary
189 * point fails insidefn.
190 * The edge e is used to provide a port box. If NULL, the spline is
191 * clipped to the node shape.
192 */
193void shape_clip(node_t * n, pointf curve[4])
194{
195 double save_real_size;
196 bool left_inside;
197 pointf c;
198
199 if (ND_shape(n) == NULL || ND_shape(n)->fns->insidefn == NULL)
200 return;
201
202 inside_t inside_context = {.s = {.n = n}};
203 save_real_size = ND_rw(n);
204 c.x = curve[0].x - ND_coord(n).x;
205 c.y = curve[0].y - ND_coord(n).y;
206 left_inside = ND_shape(n)->fns->insidefn(&inside_context, c);
207 ND_rw(n) = save_real_size;
208 shape_clip0(&inside_context, n, curve, left_inside);
209}
210
212bezier *new_spline(edge_t *e, size_t sz) {
213 bezier *rv;
214 while (ED_to_orig(e) != NULL && ED_edge_type(e) != NORMAL)
215 e = ED_to_orig(e);
216 if (ED_spl(e) == NULL)
217 ED_spl(e) = gv_alloc(sizeof(splines));
218 ED_spl(e)->list = gv_recalloc(ED_spl(e)->list, ED_spl(e)->size,
219 ED_spl(e)->size + 1, sizeof(bezier));
220 rv = &(ED_spl(e)->list[ED_spl(e)->size++]);
221 rv->list = gv_calloc(sz, sizeof(pointf));
222 rv->size = sz;
223 rv->sflag = rv->eflag = 0;
224 rv->sp.x = rv->sp.y = rv->ep.x = rv->ep.y = 0;
225 return rv;
226}
227
228/* Given a raw spline (pn control points in ps), representing
229 * a path from edge agtail(fe) ending in node hn, clip the ends to
230 * the node boundaries and attach the resulting spline to the
231 * edge.
232 */
233void
234clip_and_install(edge_t *fe, node_t *hn, pointf *ps, size_t pn,
236{
237 int clipTail, clipHead;
238 size_t start, end;
239 edge_t *orig;
240 boxf *tbox, *hbox;
241
242 node_t *tn = agtail(fe);
243 graph_t *const g = agraphof(tn);
244 bezier *const newspl = new_spline(fe, pn);
245
246 for (orig = fe; ED_to_orig(orig) != NULL && ED_edge_type(orig) != NORMAL;
247 orig = ED_to_orig(orig));
248
249 /* may be a reversed flat edge */
250 if (!info->ignoreSwap && ND_rank(tn) == ND_rank(hn) && ND_order(tn) > ND_order(hn)) {
251 SWAP(&hn, &tn);
252 }
253 if (tn == agtail(orig)) {
254 clipTail = ED_tail_port(orig).clip;
255 clipHead = ED_head_port(orig).clip;
256 tbox = ED_tail_port(orig).bp;
257 hbox = ED_head_port(orig).bp;
258 }
259 else { /* fe and orig are reversed */
260 clipTail = ED_head_port(orig).clip;
261 clipHead = ED_tail_port(orig).clip;
262 hbox = ED_tail_port(orig).bp;
263 tbox = ED_head_port(orig).bp;
264 }
265
266 /* spline may be interior to node */
267 if(clipTail && ND_shape(tn) && ND_shape(tn)->fns->insidefn) {
268 inside_t inside_context = {.s = {.n = tn, .bp = tbox}};
269 for (start = 0; start < pn - 4; start += 3) {
270 const pointf p2 = {.x = ps[start + 3].x - ND_coord(tn).x,
271 .y = ps[start + 3].y - ND_coord(tn).y};
272 if (!ND_shape(tn)->fns->insidefn(&inside_context, p2))
273 break;
274 }
275 shape_clip0(&inside_context, tn, &ps[start], true);
276 } else
277 start = 0;
278 if(clipHead && ND_shape(hn) && ND_shape(hn)->fns->insidefn) {
279 inside_t inside_context = {.s = {.n = hn, .bp = hbox}};
280 for (end = pn - 4; end > 0; end -= 3) {
281 const pointf p2 = {.x = ps[end].x - ND_coord(hn).x,
282 .y = ps[end].y - ND_coord(hn).y};
283 if (!ND_shape(hn)->fns->insidefn(&inside_context, p2))
284 break;
285 }
286 shape_clip0(&inside_context, hn, &ps[end], false);
287 } else
288 end = pn - 4;
289 for (; start < pn - 4; start += 3)
290 if (! APPROXEQPT(ps[start], ps[start + 3], MILLIPOINT))
291 break;
292 for (; end > 0; end -= 3)
293 if (! APPROXEQPT(ps[end], ps[end + 3], MILLIPOINT))
294 break;
295 arrow_clip(fe, hn, ps, &start, &end, newspl, info);
296 for (size_t i = start; i < end + 4; ) {
297 pointf cp[4];
298 newspl->list[i - start] = ps[i];
299 cp[0] = ps[i];
300 i++;
301 if ( i >= end + 4)
302 break;
303 newspl->list[i - start] = ps[i];
304 cp[1] = ps[i];
305 i++;
306 newspl->list[i - start] = ps[i];
307 cp[2] = ps[i];
308 i++;
309 cp[3] = ps[i];
310 update_bb_bz(&GD_bb(g), cp);
311 }
312 newspl->size = end - start + 4;
313}
314
315static double
317{
318 double s_in, s_out, m_in, m_out;
319 int cnt_in, cnt_out;
320 edge_t *e;
321
322 s_in = s_out = 0.0;
323 for (cnt_in = 0; (e = ND_in(n).list[cnt_in]); cnt_in++)
324 s_in += ND_coord(agtail(e)).x;
325 for (cnt_out = 0; (e = ND_out(n).list[cnt_out]); cnt_out++)
326 s_out += ND_coord(aghead(e)).x;
327 const double x1 = ND_coord(n).x - s_in / cnt_in;
328 const double y1 = ND_coord(n).y - ND_coord(agtail(ND_in(n).list[0])).y;
329 m_in = atan2(y1, x1);
330 const double x2 = s_out / cnt_out - ND_coord(n).x;
331 const double y2 = ND_coord(aghead(ND_out(n).list[0])).y - ND_coord(n).y;
332 m_out = atan2(y2, x2);
333 return (m_in + m_out) / 2.0;
334}
335
336void add_box(path * P, boxf b)
337{
338 if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
339 P->boxes[P->nbox++] = b;
340}
341
342/* Set up boxes near the tail node.
343 * For regular nodes, the result should be a list of contiguous rectangles
344 * such that the last one has the smallest LL.y and its LL.y is above
345 * the bottom of the rank (rank.ht1).
346 *
347 * For flat edges, we assume endp->sidemask has been set. For regular
348 * edges, we set this, but it doesn't appear to be needed any more.
349 *
350 * In many cases, we tweak the x or y coordinate of P->start.p by 1.
351 * This is because of a problem in the path routing code. If the starting
352 * point actually lies on the polygon, in some cases, the router gets
353 * confused and routes the path outside the polygon. So, the offset ensures
354 * the starting point is in the polygon.
355 *
356 * FIX: Creating the initial boxes only really works for rankdir=TB and
357 * rankdir=LR. For the others, we rely on compassPort flipping the side
358 * and then assume that the node shape has top-bottom symmetry. Since we
359 * at present only put compass points on the bounding box, this works.
360 * If we attempt to implement compass points on actual node perimeters,
361 * something major will probably be necessary. Doing the coordinate
362 * flip (postprocess) before spline routing will be too disruptive. The
363 * correct solution is probably to have beginpath/endpath create the
364 * boxes assuming an inverted node. Note that compassPort already does
365 * some flipping. Even better would be to allow the *_path function
366 * to provide a polygon.
367 *
368 * The extra space provided by FUDGE-2 prevents the edge from getting
369 * too close the side of the node.
370 *
371 */
372#define FUDGE 2
373#define HT2(n) (ND_ht(n)/2)
374
375void
376beginpath(path * P, edge_t * e, int et, pathend_t * endp, bool merge)
377{
378 int side, mask;
379 node_t *n;
380 int (*pboxfn) (node_t*, port*, int, boxf*, int*);
381
382 n = agtail(e);
383
384 if (ED_tail_port(e).dyna)
386 if (ND_shape(n))
387 pboxfn = ND_shape(n)->fns->pboxfn;
388 else
389 pboxfn = NULL;
390 P->start.p = add_pointf(ND_coord(n), ED_tail_port(e).p);
391 if (merge) {
392 /*P->start.theta = - M_PI / 2; */
393 P->start.theta = conc_slope(agtail(e));
394 P->start.constrained = true;
395 } else {
396 if (ED_tail_port(e).constrained) {
397 P->start.theta = ED_tail_port(e).theta;
398 P->start.constrained = true;
399 } else
400 P->start.constrained = false;
401 }
402 P->nbox = 0;
403 P->data = e;
404 endp->np = P->start.p;
405 if (et == REGULAREDGE && ND_node_type(n) == NORMAL && (side = ED_tail_port(e).side)) {
406 edge_t* orig;
407 boxf b0, b = endp->nb;
408 if (side & TOP) {
409 endp->sidemask = TOP;
410 if (P->start.p.x < ND_coord(n).x) { /* go left */
411 b0.LL.x = b.LL.x - 1;
412 /* b0.LL.y = ND_coord(n).y + HT2(n); */
413 b0.LL.y = P->start.p.y;
414 b0.UR.x = b.UR.x;
415 b0.UR.y = ND_coord(n).y + HT2(n) + GD_ranksep(agraphof(n))/2;
416 b.UR.x = ND_coord(n).x - ND_lw(n) - (FUDGE-2);
417 b.UR.y = b0.LL.y;
418 b.LL.y = ND_coord(n).y - HT2(n);
419 --b.LL.x;
420 endp->boxes[0] = b0;
421 endp->boxes[1] = b;
422 }
423 else {
424 b0.LL.x = b.LL.x;
425 b0.LL.y = P->start.p.y;
426 /* b0.LL.y = ND_coord(n).y + HT2(n); */
427 b0.UR.x = b.UR.x+1;
428 b0.UR.y = ND_coord(n).y + HT2(n) + GD_ranksep(agraphof(n))/2;
429 b.LL.x = ND_coord(n).x + ND_rw(n) + (FUDGE-2);
430 b.UR.y = b0.LL.y;
431 b.LL.y = ND_coord(n).y - HT2(n);
432 ++b.UR.x;
433 endp->boxes[0] = b0;
434 endp->boxes[1] = b;
435 }
436 ++P->start.p.y;
437 endp->boxn = 2;
438 }
439 else if (side & BOTTOM) {
440 endp->sidemask = BOTTOM;
441 b.UR.y = MAX(b.UR.y,P->start.p.y);
442 endp->boxes[0] = b;
443 endp->boxn = 1;
444 --P->start.p.y;
445 }
446 else if (side & LEFT) {
447 endp->sidemask = LEFT;
448 b.UR.x = P->start.p.x;
449 b.LL.y = ND_coord(n).y - HT2(n);
450 b.UR.y = P->start.p.y;
451 endp->boxes[0] = b;
452 endp->boxn = 1;
453 --P->start.p.x;
454 }
455 else {
456 endp->sidemask = RIGHT;
457 b.LL.x = P->start.p.x;
458 b.LL.y = ND_coord(n).y - HT2(n);
459 b.UR.y = P->start.p.y;
460 endp->boxes[0] = b;
461 endp->boxn = 1;
462 ++P->start.p.x;
463 }
464 for (orig = e; ED_to_orig(orig) != NULL && ED_edge_type(orig) != NORMAL;
465 orig = ED_to_orig(orig));
466 if (n == agtail(orig))
467 ED_tail_port(orig).clip = false;
468 else
469 ED_head_port(orig).clip = false;
470 return;
471 }
472 if (et == FLATEDGE && (side = ED_tail_port(e).side)) {
473 boxf b0, b = endp->nb;
474 edge_t* orig;
475 if (side & TOP) {
476 b.LL.y = MIN(b.LL.y,P->start.p.y);
477 endp->boxes[0] = b;
478 endp->boxn = 1;
479 ++P->start.p.y;
480 }
481 else if (side & BOTTOM) {
482 if (endp->sidemask == TOP) {
483 b0.UR.y = ND_coord(n).y - HT2(n);
484 b0.UR.x = b.UR.x+1;
485 b0.LL.x = P->start.p.x;
486 b0.LL.y = b0.UR.y - GD_ranksep(agraphof(n))/2;
487 b.LL.x = ND_coord(n).x + ND_rw(n) + (FUDGE-2);
488 b.LL.y = b0.UR.y;
489 b.UR.y = ND_coord(n).y + HT2(n);
490 ++b.UR.x;
491 endp->boxes[0] = b0;
492 endp->boxes[1] = b;
493 endp->boxn = 2;
494 }
495 else {
496 b.UR.y = MAX(b.UR.y,P->start.p.y);
497 endp->boxes[0] = b;
498 endp->boxn = 1;
499 }
500 --P->start.p.y;
501 }
502 else if (side & LEFT) {
503 b.UR.x = P->start.p.x+1;
504 if (endp->sidemask == TOP) {
505 b.UR.y = ND_coord(n).y + HT2(n);
506 b.LL.y = P->start.p.y-1;
507 }
508 else {
509 b.LL.y = ND_coord(n).y - HT2(n);
510 b.UR.y = P->start.p.y+1;
511 }
512 endp->boxes[0] = b;
513 endp->boxn = 1;
514 --P->start.p.x;
515 }
516 else {
517 b.LL.x = P->start.p.x;
518 if (endp->sidemask == TOP) {
519 b.UR.y = ND_coord(n).y + HT2(n);
520 b.LL.y = P->start.p.y;
521 }
522 else {
523 b.LL.y = ND_coord(n).y - HT2(n);
524 b.UR.y = P->start.p.y+1;
525 }
526 endp->boxes[0] = b;
527 endp->boxn = 1;
528 ++P->start.p.x;
529 }
530 for (orig = e; ED_to_orig(orig) != NULL && ED_edge_type(orig) != NORMAL;
531 orig = ED_to_orig(orig));
532 if (n == agtail(orig))
533 ED_tail_port(orig).clip = false;
534 else
535 ED_head_port(orig).clip = false;
536 endp->sidemask = side;
537 return;
538 }
539
540 if (et == REGULAREDGE) side = BOTTOM;
541 else side = endp->sidemask; /* for flat edges */
542 if (pboxfn
543 && (mask = pboxfn(n, &ED_tail_port(e), side, &endp->boxes[0], &endp->boxn)))
544 endp->sidemask = mask;
545 else {
546 endp->boxes[0] = endp->nb;
547 endp->boxn = 1;
548
549 switch (et) {
550 case SELFEDGE:
551 /* moving the box UR.y by + 1 avoids colinearity between
552 port point and box that confuses Proutespline(). it's
553 a bug in Proutespline() but this is the easiest fix. */
554 assert(0); /* at present, we don't use beginpath for selfedges */
555 endp->boxes[0].UR.y = P->start.p.y - 1;
556 endp->sidemask = BOTTOM;
557 break;
558 case FLATEDGE:
559 if (endp->sidemask == TOP)
560 endp->boxes[0].LL.y = P->start.p.y;
561 else
562 endp->boxes[0].UR.y = P->start.p.y;
563 break;
564 case REGULAREDGE:
565 endp->boxes[0].UR.y = P->start.p.y;
566 endp->sidemask = BOTTOM;
567 --P->start.p.y;
568 break;
569 }
570 }
571}
572
573void endpath(path * P, edge_t * e, int et, pathend_t * endp, bool merge)
574{
575 int side, mask;
576 node_t *n;
577 int (*pboxfn) (node_t* n, port*, int, boxf*, int*);
578
579 n = aghead(e);
580
581 if (ED_head_port(e).dyna)
583 if (ND_shape(n))
584 pboxfn = ND_shape(n)->fns->pboxfn;
585 else
586 pboxfn = NULL;
587 P->end.p = add_pointf(ND_coord(n), ED_head_port(e).p);
588 if (merge) {
589 /*P->end.theta = M_PI / 2; */
590 P->end.theta = conc_slope(aghead(e)) + M_PI;
591 assert(P->end.theta < 2 * M_PI);
592 P->end.constrained = true;
593 } else {
594 if (ED_head_port(e).constrained) {
595 P->end.theta = ED_head_port(e).theta;
596 P->end.constrained = true;
597 } else
598 P->end.constrained = false;
599 }
600 endp->np = P->end.p;
601 if (et == REGULAREDGE && ND_node_type(n) == NORMAL && (side = ED_head_port(e).side)) {
602 edge_t* orig;
603 boxf b0, b = endp->nb;
604 if (side & TOP) {
605 endp->sidemask = TOP;
606 b.LL.y = MIN(b.LL.y,P->end.p.y);
607 endp->boxes[0] = b;
608 endp->boxn = 1;
609 ++P->end.p.y;
610 }
611 else if (side & BOTTOM) {
612 endp->sidemask = BOTTOM;
613 if (P->end.p.x < ND_coord(n).x) { /* go left */
614 b0.LL.x = b.LL.x-1;
615 /* b0.UR.y = ND_coord(n).y - HT2(n); */
616 b0.UR.y = P->end.p.y;
617 b0.UR.x = b.UR.x;
618 b0.LL.y = ND_coord(n).y - HT2(n) - GD_ranksep(agraphof(n))/2;
619 b.UR.x = ND_coord(n).x - ND_lw(n) - (FUDGE-2);
620 b.LL.y = b0.UR.y;
621 b.UR.y = ND_coord(n).y + HT2(n);
622 --b.LL.x;
623 endp->boxes[0] = b0;
624 endp->boxes[1] = b;
625 }
626 else {
627 b0.LL.x = b.LL.x;
628 b0.UR.y = P->end.p.y;
629 /* b0.UR.y = ND_coord(n).y - HT2(n); */
630 b0.UR.x = b.UR.x+1;
631 b0.LL.y = ND_coord(n).y - HT2(n) - GD_ranksep(agraphof(n))/2;
632 b.LL.x = ND_coord(n).x + ND_rw(n) + (FUDGE-2);
633 b.LL.y = b0.UR.y;
634 b.UR.y = ND_coord(n).y + HT2(n);
635 ++b.UR.x;
636 endp->boxes[0] = b0;
637 endp->boxes[1] = b;
638 }
639 endp->boxn = 2;
640 --P->end.p.y;
641 }
642 else if (side & LEFT) {
643 endp->sidemask = LEFT;
644 b.UR.x = P->end.p.x;
645 b.UR.y = ND_coord(n).y + HT2(n);
646 b.LL.y = P->end.p.y;
647 endp->boxes[0] = b;
648 endp->boxn = 1;
649 --P->end.p.x;
650 }
651 else {
652 endp->sidemask = RIGHT;
653 b.LL.x = P->end.p.x;
654 b.UR.y = ND_coord(n).y + HT2(n);
655 b.LL.y = P->end.p.y;
656 endp->boxes[0] = b;
657 endp->boxn = 1;
658 ++P->end.p.x;
659 }
660 for (orig = e; ED_to_orig(orig) != NULL && ED_edge_type(orig) != NORMAL;
661 orig = ED_to_orig(orig));
662 if (n == aghead(orig))
663 ED_head_port(orig).clip = false;
664 else
665 ED_tail_port(orig).clip = false;
666 endp->sidemask = side;
667 return;
668 }
669
670 if (et == FLATEDGE && (side = ED_head_port(e).side)) {
671 boxf b0, b = endp->nb;
672 edge_t* orig;
673 if (side & TOP) {
674 b.LL.y = MIN(b.LL.y,P->end.p.y);
675 endp->boxes[0] = b;
676 endp->boxn = 1;
677 ++P->end.p.y;
678 }
679 else if (side & BOTTOM) {
680 if (endp->sidemask == TOP) {
681 b0.LL.x = b.LL.x-1;
682 b0.UR.y = ND_coord(n).y - HT2(n);
683 b0.UR.x = P->end.p.x;
684 b0.LL.y = b0.UR.y - GD_ranksep(agraphof(n))/2;
685 b.UR.x = ND_coord(n).x - ND_lw(n) - 2;
686 b.LL.y = b0.UR.y;
687 b.UR.y = ND_coord(n).y + HT2(n);
688 --b.LL.x;
689 endp->boxes[0] = b0;
690 endp->boxes[1] = b;
691 endp->boxn = 2;
692 }
693 else {
694 b.UR.y = MAX(b.UR.y,P->start.p.y);
695 endp->boxes[0] = b;
696 endp->boxn = 1;
697 }
698 --P->end.p.y;
699 }
700 else if (side & LEFT) {
701 b.UR.x = P->end.p.x+1;
702 if (endp->sidemask == TOP) {
703 b.UR.y = ND_coord(n).y + HT2(n);
704 b.LL.y = P->end.p.y-1;
705 }
706 else {
707 b.LL.y = ND_coord(n).y - HT2(n);
708 b.UR.y = P->end.p.y+1;
709 }
710 endp->boxes[0] = b;
711 endp->boxn = 1;
712 --P->end.p.x;
713 }
714 else {
715 b.LL.x = P->end.p.x-1;
716 if (endp->sidemask == TOP) {
717 b.UR.y = ND_coord(n).y + HT2(n);
718 b.LL.y = P->end.p.y-1;
719 }
720 else {
721 b.LL.y = ND_coord(n).y - HT2(n);
722 b.UR.y = P->end.p.y;
723 }
724 endp->boxes[0] = b;
725 endp->boxn = 1;
726 ++P->end.p.x;
727 }
728 for (orig = e; ED_to_orig(orig) != NULL && ED_edge_type(orig) != NORMAL;
729 orig = ED_to_orig(orig));
730 if (n == aghead(orig))
731 ED_head_port(orig).clip = false;
732 else
733 ED_tail_port(orig).clip = false;
734 endp->sidemask = side;
735 return;
736 }
737
738 if (et == REGULAREDGE) side = TOP;
739 else side = endp->sidemask; /* for flat edges */
740 if (pboxfn
741 && (mask = pboxfn(n, &ED_head_port(e), side, &endp->boxes[0], &endp->boxn)))
742 endp->sidemask = mask;
743 else {
744 endp->boxes[0] = endp->nb;
745 endp->boxn = 1;
746
747 switch (et) {
748 case SELFEDGE:
749 /* offset of -1 is symmetric w.r.t. beginpath()
750 * FIXME: is any of this right? what if self-edge
751 * doesn't connect from BOTTOM to TOP??? */
752 assert(0); /* at present, we don't use endpath for selfedges */
753 endp->boxes[0].LL.y = P->end.p.y + 1;
754 endp->sidemask = TOP;
755 break;
756 case FLATEDGE:
757 if (endp->sidemask == TOP)
758 endp->boxes[0].LL.y = P->end.p.y;
759 else
760 endp->boxes[0].UR.y = P->end.p.y;
761 break;
762 case REGULAREDGE:
763 endp->boxes[0].LL.y = P->end.p.y;
764 endp->sidemask = TOP;
765 ++P->end.p.y;
766 break;
767 }
768 }
769}
770
771
772static int convert_sides_to_points(int tail_side, int head_side)
773{
774int vertices[] = {12,4,6,2,3,1,9,8}; //the cumulative side value of each node point
775int i, tail_i, head_i;
776int pair_a[8][8] = { //array of possible node point pairs
777{11,12,13,14,15,16,17,18},
778{21,22,23,24,25,26,27,28},
779{31,32,33,34,35,36,37,38},
780{41,42,43,44,45,46,47,48},
781{51,52,53,54,55,56,57,58},
782{61,62,63,64,65,66,67,68},
783{71,72,73,74,75,76,77,78},
784{81,82,83,84,85,86,87,88}
785};
786
787 tail_i = head_i = -1;
788 for(i=0;i< 8; i++){
789 if(head_side == vertices[i]){
790 head_i = i;
791 break;
792 }
793 }
794 for(i=0;i< 8; i++){
795 if(tail_side == vertices[i]){
796 tail_i = i;
797 break;
798 }
799 }
800
801if( tail_i < 0 || head_i < 0)
802 return 0;
803else
804 return pair_a[tail_i][head_i];
805}
806
807static void selfBottom(edge_t *edges[], size_t ind, size_t cnt, double sizex,
808 double stepy, splineInfo *sinfo) {
809 pointf tp, hp, np;
810 node_t *n;
811 edge_t *e;
812 int sgn, point_pair;
813 double hy, ty, stepx, dx, dy, height;
814
815 e = edges[ind];
816 n = agtail(e);
817
818 stepx = fmax(sizex / 2.0 / (double)cnt, 2.0);
819 np = ND_coord(n);
820 tp = ED_tail_port(e).p;
821 tp.x += np.x;
822 tp.y += np.y;
823 hp = ED_head_port(e).p;
824 hp.x += np.x;
825 hp.y += np.y;
826 if (tp.x >= hp.x) sgn = 1;
827 else sgn = -1;
828 dy = ND_ht(n) / 2.0;
829 dx = 0.0;
830 // certain adjustments are required for some point_pairs in order to improve the
831 // display of the edge path between them
832 point_pair = convert_sides_to_points(ED_tail_port(e).side,ED_head_port(e).side);
833 switch(point_pair){
834 case 67: sgn = -sgn;
835 break;
836 default:
837 break;
838 }
839 ty = fmin(dy, 3 * (tp.y + dy - np.y));
840 hy = fmin(dy, 3 * (hp.y + dy - np.y));
841 for (size_t i = 0; i < cnt; i++) {
842 e = edges[ind++];
843 dy += stepy;
844 ty += stepy;
845 hy += stepy;
846 dx += sgn * stepx;
847 pointf points[] = {
848 tp,
849 {tp.x + dx, tp.y - ty / 3},
850 {tp.x + dx, np.y - dy},
851 {(tp.x + hp.x) / 2, np.y - dy},
852 {hp.x - dx, np.y - dy},
853 {hp.x - dx, hp.y - hy / 3},
854 hp
855 };
856 if (ED_label(e)) {
857 if (GD_flip(agraphof(agtail(e)))) {
858 height = ED_label(e)->dimen.x;
859 } else {
860 height = ED_label(e)->dimen.y;
861 }
862 ED_label(e)->pos.y = ND_coord(n).y - dy - height / 2.0;
863 ED_label(e)->pos.x = ND_coord(n).x;
864 ED_label(e)->set = true;
865 if (height > stepy)
866 dy += height - stepy;
867 }
868 const size_t pointn = sizeof(points) / sizeof(points[0]);
869 clip_and_install(e, aghead(e), points, pointn, sinfo);
870#ifdef DEBUG
871 if (debugleveln(e,1))
872 showPoints (points, pointn);
873#endif
874 }
875}
876
877static void selfTop(edge_t *edges[], size_t ind, size_t cnt, double sizex,
878 double stepy, splineInfo *sinfo) {
879 int sgn, point_pair;
880 double hy, ty, stepx, dx, dy, height;
881 pointf tp, hp, np;
882 node_t *n;
883 edge_t *e;
884
885 e = edges[ind];
886 n = agtail(e);
887
888 stepx = fmax(sizex / 2.0 / (double)cnt, 2.0);
889 np = ND_coord(n);
890 tp = ED_tail_port(e).p;
891 tp.x += np.x;
892 tp.y += np.y;
893 hp = ED_head_port(e).p;
894 hp.x += np.x;
895 hp.y += np.y;
896 if (tp.x >= hp.x) sgn = 1;
897 else sgn = -1;
898 dy = ND_ht(n)/2., dx = 0.;
899 // certain adjustments are required for some point_pairs in order to improve the
900 // display of the edge path between them
901 point_pair = convert_sides_to_points(ED_tail_port(e).side,ED_head_port(e).side);
902 switch(point_pair){
903 case 15:
904 dx = sgn*(ND_rw(n) - (hp.x-np.x) + stepx);
905 break;
906
907 case 38:
908 dx = sgn*(ND_lw(n)-(np.x-hp.x) + stepx);
909 break;
910 case 41:
911 dx = sgn*(ND_rw(n)-(tp.x-np.x) + stepx);
912 break;
913 case 48:
914 dx = sgn*(ND_rw(n)-(tp.x-np.x) + stepx);
915 break;
916
917 case 14:
918 case 37:
919 case 47:
920 case 51:
921 case 57:
922 case 58:
923 dx = sgn * ((ND_lw(n) - (np.x - tp.x) + (ND_rw(n) - (hp.x - np.x))) / 3.0);
924 break;
925 case 73:
926 dx = sgn*(ND_lw(n)-(np.x-tp.x) + stepx);
927 break;
928 case 83:
929 dx = sgn*(ND_lw(n)-(np.x-tp.x));
930 break;
931 case 84:
932 dx = sgn *
933 ((ND_lw(n) - (np.x - tp.x) + (ND_rw(n) - (hp.x - np.x))) / 2.0 +
934 stepx);
935 break;
936 case 74:
937 case 75:
938 case 85:
939 dx = sgn *
940 ((ND_lw(n) - (np.x - tp.x) + (ND_rw(n) - (hp.x - np.x))) / 2.0 +
941 2 * stepx);
942 break;
943 default:
944 break;
945 }
946 ty = fmin(dy, 3 * (np.y + dy - tp.y));
947 hy = fmin(dy, 3 * (np.y + dy - hp.y));
948 for (size_t i = 0; i < cnt; i++) {
949 e = edges[ind++];
950 dy += stepy;
951 ty += stepy;
952 hy += stepy;
953 dx += sgn * stepx;
954 pointf points[] = {
955 tp,
956 {tp.x + dx, tp.y + ty / 3},
957 {tp.x + dx, np.y + dy},
958 {(tp.x + hp.x) / 2, np.y + dy},
959 {hp.x - dx, np.y + dy},
960 {hp.x - dx, hp.y + hy / 3},
961 hp,
962 };
963 if (ED_label(e)) {
964 if (GD_flip(agraphof(agtail(e)))) {
965 height = ED_label(e)->dimen.x;
966 } else {
967 height = ED_label(e)->dimen.y;
968 }
969 ED_label(e)->pos.y = ND_coord(n).y + dy + height / 2.0;
970 ED_label(e)->pos.x = ND_coord(n).x;
971 ED_label(e)->set = true;
972 if (height > stepy)
973 dy += height - stepy;
974 }
975 const size_t pointn = sizeof(points) / sizeof(points[0]);
976 clip_and_install(e, aghead(e), points, pointn, sinfo);
977#ifdef DEBUG
978 if (debugleveln(e,1))
979 showPoints (points, pointn);
980#endif
981 }
982}
983
984static void selfRight(edge_t *edges[], size_t ind, size_t cnt, double stepx,
985 double sizey, splineInfo *sinfo) {
986 int sgn, point_pair;
987 double hx, tx, stepy, dx, dy, width;
988 pointf tp, hp, np;
989 node_t *n;
990 edge_t *e;
991
992 e = edges[ind];
993 n = agtail(e);
994
995 stepy = fmax(sizey / 2.0 / (double)cnt, 2.0);
996 np = ND_coord(n);
997 tp = ED_tail_port(e).p;
998 tp.x += np.x;
999 tp.y += np.y;
1000 hp = ED_head_port(e).p;
1001 hp.x += np.x;
1002 hp.y += np.y;
1003 if (tp.y >= hp.y) sgn = 1;
1004 else sgn = -1;
1005 dx = ND_rw(n), dy = 0;
1006 // certain adjustments are required for some point_pairs in order to improve the
1007 // display of the edge path between them
1008 point_pair = convert_sides_to_points(ED_tail_port(e).side,ED_head_port(e).side);
1009 switch(point_pair){
1010 case 32:
1011 case 65: if(tp.y == hp.y)
1012 sgn = -sgn;
1013 break;
1014 default:
1015 break;
1016 }
1017 tx = fmin(dx, 3 * (np.x + dx - tp.x));
1018 hx = fmin(dx, 3 * (np.x + dx - hp.x));
1019 for (size_t i = 0; i < cnt; i++) {
1020 e = edges[ind++];
1021 dx += stepx;
1022 tx += stepx;
1023 hx += stepx;
1024 dy += sgn * stepy;
1025 pointf points[] = {
1026 tp,
1027 {tp.x + tx / 3, tp.y + dy},
1028 {np.x + dx, tp.y + dy},
1029 {np.x + dx, (tp.y + hp.y) / 2},
1030 {np.x + dx, hp.y - dy},
1031 {hp.x + hx / 3, hp.y - dy},
1032 hp,
1033 };
1034 if (ED_label(e)) {
1035 if (GD_flip(agraphof(agtail(e)))) {
1036 width = ED_label(e)->dimen.y;
1037 } else {
1038 width = ED_label(e)->dimen.x;
1039 }
1040 ED_label(e)->pos.x = ND_coord(n).x + dx + width / 2.0;
1041 ED_label(e)->pos.y = ND_coord(n).y;
1042 ED_label(e)->set = true;
1043 if (width > stepx)
1044 dx += width - stepx;
1045 }
1046 const size_t pointn = sizeof(points) / sizeof(points[0]);
1047 clip_and_install(e, aghead(e), points, pointn, sinfo);
1048#ifdef DEBUG
1049 if (debugleveln(e,1))
1050 showPoints (points, pointn);
1051#endif
1052 }
1053}
1054
1055static void selfLeft(edge_t *edges[], size_t ind, size_t cnt, double stepx,
1056 double sizey, splineInfo *sinfo) {
1057 int sgn,point_pair;
1058 double hx, tx, stepy, dx, dy, width;
1059 pointf tp, hp, np;
1060 node_t *n;
1061 edge_t *e;
1062
1063 e = edges[ind];
1064 n = agtail(e);
1065
1066 stepy = fmax(sizey / 2.0 / (double)cnt, 2.0);
1067 np = ND_coord(n);
1068 tp = ED_tail_port(e).p;
1069 tp.x += np.x;
1070 tp.y += np.y;
1071 hp = ED_head_port(e).p;
1072 hp.x += np.x;
1073 hp.y += np.y;
1074
1075
1076 if (tp.y >= hp.y) sgn = 1;
1077 else sgn = -1;
1078 dx = ND_lw(n), dy = 0.;
1079 // certain adjustments are required for some point_pairs in order to improve the
1080 // display of the edge path between them
1081 point_pair = convert_sides_to_points(ED_tail_port(e).side,ED_head_port(e).side);
1082 switch(point_pair){
1083 case 12:
1084 case 67:
1085 if(tp.y == hp.y)
1086 sgn = -sgn;
1087 break;
1088 default:
1089 break;
1090 }
1091 tx = fmin(dx, 3 * (tp.x + dx - np.x));
1092 hx = fmin(dx, 3 * (hp.x + dx - np.x));
1093 for (size_t i = 0; i < cnt; i++) {
1094 e = edges[ind++];
1095 dx += stepx;
1096 tx += stepx;
1097 hx += stepx;
1098 dy += sgn * stepy;
1099 pointf points[] = {
1100 tp,
1101 {tp.x - tx / 3, tp.y + dy},
1102 {np.x - dx, tp.y + dy},
1103 {np.x - dx, (tp.y + hp.y) / 2},
1104 {np.x - dx, hp.y - dy},
1105 {hp.x - hx / 3, hp.y - dy},
1106 hp,
1107 };
1108 if (ED_label(e)) {
1109 if (GD_flip(agraphof(agtail(e)))) {
1110 width = ED_label(e)->dimen.y;
1111 } else {
1112 width = ED_label(e)->dimen.x;
1113 }
1114 ED_label(e)->pos.x = ND_coord(n).x - dx - width / 2.0;
1115 ED_label(e)->pos.y = ND_coord(n).y;
1116 ED_label(e)->set = true;
1117 if (width > stepx)
1118 dx += width - stepx;
1119 }
1120
1121 const size_t pointn = sizeof(points) / sizeof(points[0]);
1122 clip_and_install(e, aghead(e), points, pointn, sinfo);
1123#ifdef DEBUG
1124 if (debugleveln(e,1))
1125 showPoints (points, pointn);
1126#endif
1127 }
1128}
1129
1130/* Assume e is self-edge.
1131 * Return extra space necessary on the right for this edge.
1132 * If the edge does not go on the right, return 0.
1133 * NOTE: the actual space is determined dynamically by GD_nodesep,
1134 * so using the constant SELF_EDGE_SIZE is going to be wrong.
1135 * Fortunately, the default nodesep is the same as SELF_EDGE_SIZE.
1136 */
1138 double sw;
1139 double label_width;
1140 textlabel_t* l = ED_label(e);
1141
1142 if ((!ED_tail_port(e).defined && !ED_head_port(e).defined) ||
1143 (!(ED_tail_port(e).side & LEFT) &&
1144 !(ED_head_port(e).side & LEFT) &&
1145 (ED_tail_port(e).side != ED_head_port(e).side ||
1146 !(ED_tail_port(e).side & (TOP|BOTTOM))))) {
1147 sw = SELF_EDGE_SIZE;
1148 if (l) {
1149 label_width = GD_flip(agraphof(aghead(e))) ? l->dimen.y : l->dimen.x;
1150 sw += label_width;
1151 }
1152 }
1153 else sw = 0;
1154 return sw;
1155}
1156
1157/* The routing is biased towards the right side because this is what
1158 * dot supports, and leaves room for.
1159 * FIX: With this bias, labels tend to be placed on top of each other.
1160 * Perhaps for self-edges, the label should be centered.
1161 */
1162void makeSelfEdge(edge_t *edges[], size_t ind, size_t cnt, double sizex,
1163 double sizey, splineInfo * sinfo) {
1164 edge_t *e = edges[ind];
1165
1166 /* self edge without ports or
1167 * self edge with all ports inside, on the right, or at most 1 on top
1168 * and at most 1 on bottom
1169 */
1170
1171 if ((!ED_tail_port(e).defined && !ED_head_port(e).defined) ||
1172 (!(ED_tail_port(e).side & LEFT) &&
1173 !(ED_head_port(e).side & LEFT) &&
1174 (ED_tail_port(e).side != ED_head_port(e).side ||
1175 !(ED_tail_port(e).side & (TOP|BOTTOM))))) {
1176 selfRight(edges, ind, cnt, sizex, sizey, sinfo);
1177 }
1178
1179 /* self edge with port on left side */
1180 else if ((ED_tail_port(e).side & LEFT) || (ED_head_port(e).side & LEFT)) {
1181
1182 /* handle L-R specially */
1183 if ((ED_tail_port(e).side & RIGHT) || (ED_head_port(e).side & RIGHT)) {
1184 selfTop(edges, ind, cnt, sizex, sizey, sinfo);
1185 }
1186 else {
1187 selfLeft(edges, ind, cnt, sizex, sizey, sinfo);
1188 }
1189 }
1190
1191 /* self edge with both ports on top side */
1192 else if (ED_tail_port(e).side & TOP) {
1193 selfTop(edges, ind, cnt, sizex, sizey, sinfo);
1194 }
1195 else if (ED_tail_port(e).side & BOTTOM) {
1196 selfBottom(edges, ind, cnt, sizex, sizey, sinfo);
1197 }
1198
1199 else assert(0);
1200}
1201
1204{
1205 /* Only use this if labelangle or labeldistance is set for the edge;
1206 * otherwise, handle with external labels.
1207 */
1208 if (!E_labelangle && !E_labeldistance) return;
1209
1210 if (ED_head_label(e) && !ED_head_label(e)->set) {
1211 if (place_portlabel(e, true))
1213 }
1214 if (ED_tail_label(e) && !ED_tail_label(e)->set) {
1215 if (place_portlabel(e, false))
1217 }
1218}
1219
1221static void endPoints(splines * spl, pointf * p, pointf * q)
1222{
1223 bezier bz;
1224
1225 bz = spl->list[0];
1226 if (bz.sflag) {
1227 *p = bz.sp;
1228 }
1229 else {
1230 *p = bz.list[0];
1231 }
1232 bz = spl->list[spl->size - 1];
1233 if (bz.eflag) {
1234 *q = bz.ep;
1235 }
1236 else {
1237 *q = bz.list[bz.size - 1];
1238 }
1239}
1240
1241/* Find midpoint of polyline.
1242 * pp and pq are set to the endpoints of the line segment containing it.
1243 */
1244static pointf
1246{
1247 bezier bz;
1248 double d, dist = 0;
1249 pointf pf, qf, mf;
1250
1251 for (size_t i = 0; i < spl->size; i++) {
1252 bz = spl->list[i];
1253 for (size_t j = 0, k = 3; k < bz.size; j += 3, k += 3) {
1254 pf = bz.list[j];
1255 qf = bz.list[k];
1256 dist += DIST(pf, qf);
1257 }
1258 }
1259 dist /= 2;
1260 for (size_t i = 0; i < spl->size; i++) {
1261 bz = spl->list[i];
1262 for (size_t j = 0, k = 3; k < bz.size; j += 3, k += 3) {
1263 pf = bz.list[j];
1264 qf = bz.list[k];
1265 d = DIST(pf,qf);
1266 if (d >= dist) {
1267 *pp = pf;
1268 *pq = qf;
1269 mf.x = (qf.x * dist + pf.x * (d - dist)) / d;
1270 mf.y = (qf.y * dist + pf.y * (d - dist)) / d;
1271 return mf;
1272 }
1273 else
1274 dist -= d;
1275 }
1276 }
1277 UNREACHABLE();
1278}
1279
1280pointf
1282{
1283 int et = EDGE_TYPE (g);
1284 pointf spf, p, q;
1285
1286 endPoints(ED_spl(e), &p, &q);
1287 if (APPROXEQPT(p, q, MILLIPOINT)) { /* degenerate spline */
1288 spf = p;
1289 }
1290 else if (et == EDGETYPE_SPLINE || et == EDGETYPE_CURVED) {
1291 const pointf d = mid_pointf(p, q);
1292 spf = dotneato_closest(ED_spl(e), d);
1293 }
1294 else { /* EDGETYPE_PLINE, EDGETYPE_ORTHO or EDGETYPE_LINE */
1295 spf = polylineMidpoint (ED_spl(e), &p, &q);
1296 }
1297
1298 return spf;
1299}
1300
1301/* Adds label, headlabel and taillabel.
1302 * Updates bounding box.
1303 * We use the endpoints of the spline.
1304 */
1306 makePortLabels(e);
1307}
1308
1309/* place the {head,tail}label (depending on HEAD_P) of edge E
1310 * N.B. Assume edges are normalized, so tail is at spl->list[0].list[0]
1311 * and head is at spl->list[spl->size-l].list[bez->size-1]
1312 * Return 1 on success
1313 */
1314int place_portlabel(edge_t * e, bool head_p)
1315{
1316 splines *spl;
1317 pointf pe, pf;
1318
1319 if (ED_edge_type(e) == IGNORED)
1320 return 0;
1321 /* add label here only if labelangle or labeldistance is defined; else, use external label */
1322 if ((!E_labelangle || streq(agxget(e, E_labelangle), "")) &&
1324 return 0;
1325 }
1326
1327 textlabel_t *const l = head_p ? ED_head_label(e) : ED_tail_label(e);
1328 if ((spl = getsplinepoints(e)) == NULL) return 0;
1329 if (!head_p) {
1330 const bezier *const bez = &spl->list[0];
1331 if (bez->sflag) {
1332 pe = bez->sp;
1333 pf = bez->list[0];
1334 } else {
1335 pe = bez->list[0];
1336 const pointf *const c = bez->list; // slice of the first 4 points
1337 pf = Bezier(c, 0.1, NULL, NULL);
1338 }
1339 } else {
1340 const bezier *const bez = &spl->list[spl->size - 1];
1341 if (bez->eflag) {
1342 pe = bez->ep;
1343 pf = bez->list[bez->size - 1];
1344 } else {
1345 pe = bez->list[bez->size - 1];
1346 // slice of the last 4 points
1347 const pointf *const c = &bez->list[bez->size - 4];
1348 pf = Bezier(c, 0.9, NULL, NULL);
1349 }
1350 }
1351 const double angle = atan2(pf.y - pe.y, pf.x - pe.x) +
1353 const double dist =
1355 l->pos.x = pe.x + dist * cos(angle);
1356 l->pos.y = pe.y + dist * sin(angle);
1357 l->set = true;
1358 return 1;
1359}
1360
1362{
1363 edge_t *le;
1364 splines *sp;
1365
1366 for (le = e; !(sp = ED_spl(le)) && ED_edge_type(le) != NORMAL;
1367 le = ED_to_orig(le));
1368 if (sp == NULL)
1369 agerrorf("getsplinepoints: no spline points available for edge (%s,%s)\n",
1370 agnameof(agtail(e)), agnameof(aghead(e)));
1371 return sp;
1372}
1373
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:232
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:325
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static char * gv_strdup(const char *original)
Definition alloc.h:101
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define MIN(a, b)
Definition arith.h:28
#define RADIANS(deg)
Definition arith.h:49
#define M_PI
Definition arith.h:41
size_t arrowStartClip(edge_t *e, pointf *ps, size_t startp, size_t endp, bezier *spl, uint32_t sflag)
Definition arrows.c:315
size_t arrowEndClip(edge_t *e, pointf *ps, size_t startp, size_t endp, bezier *spl, uint32_t eflag)
Definition arrows.c:284
void arrowOrthoClip(edge_t *e, pointf *ps, size_t startp, size_t endp, bezier *spl, uint32_t sflag, uint32_t eflag)
Definition arrows.c:355
static bool inside(inside_t *inside_context, pointf p)
Definition arrows.c:279
void arrow_flags(Agedge_t *e, uint32_t *sflag, uint32_t *eflag)
Definition arrows.c:217
#define right(i)
Definition closest.c:72
pointf Bezier(const pointf *V, double t, pointf *Left, pointf *Right)
Definition utils.c:171
double late_double(void *obj, attrsym_t *attr, double defaultValue, double minimum)
Definition utils.c:51
pointf dotneato_closest(splines *spl, pointf pt)
Definition utils.c:344
void updateBB(graph_t *g, textlabel_t *lp)
Definition utils.c:622
#define NORMAL
Definition const.h:24
#define EDGETYPE_SPLINE
Definition const.h:239
#define LEFT
Definition const.h:120
#define REGULAREDGE
Definition const.h:150
#define IGNORED
Definition const.h:30
#define PORT_LABEL_DISTANCE
Definition const.h:101
#define EDGETYPE_CURVED
Definition const.h:236
#define RIGHT
Definition const.h:118
#define SELF_EDGE_SIZE
Definition const.h:98
#define ARR_NONE
Definition const.h:108
#define SELFEDGE
Definition const.h:154
#define PORT_LABEL_ANGLE
Definition const.h:102
#define BOTTOM
Definition const.h:117
#define FLATEDGE
Definition const.h:151
#define TOP
Definition const.h:119
static splineInfo sinfo
Definition dotsplines.c:123
static float dy
Definition draw.c:40
static float dx
Definition draw.c:39
#define left
Definition dthdr.h:12
#define le
Definition edges.h:29
void update_bb_bz(boxf *bb, pointf *cp)
Definition emit.c:752
static double dist(int dim, double *x, double *y)
#define MILLIPOINT
Definition geom.h:74
#define APPROXEQPT(p, q, tol)
Definition geom.h:71
#define DIST(p, q)
Definition geom.h:56
geometric functions (e.g. on points and boxes)
static WUR pointf mid_pointf(pointf p, pointf q)
Definition geomprocs.h:104
static WUR pointf add_pointf(pointf p, pointf q)
Definition geomprocs.h:88
show_boxes_t Show_boxes
Definition globals.c:22
Agsym_t * E_labelangle
Definition globals.h:90
Agsym_t * E_labeldistance
Definition globals.h:90
node NULL
Definition grammar.y:181
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:196
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:458
#define ED_to_orig(e)
Definition types.h:598
#define ED_showboxes(e)
Definition types.h:594
#define ED_head_label(e)
Definition types.h:587
#define ED_spl(e)
Definition types.h:595
#define agtail(e)
Definition cgraph.h:977
#define ED_edge_type(e)
Definition types.h:582
#define ED_tail_label(e)
Definition types.h:596
#define aghead(e)
Definition cgraph.h:978
#define ED_head_port(e)
Definition types.h:588
#define ED_label(e)
Definition types.h:589
#define ED_tail_port(e)
Definition types.h:597
void agerrorf(const char *fmt,...)
Definition agerror.c:165
#define GD_bb(g)
Definition types.h:354
#define GD_showboxes(g)
Definition types.h:401
#define GD_flip(g)
Definition types.h:378
#define GD_ranksep(g)
Definition types.h:397
#define ND_rank(n)
Definition types.h:523
#define ND_ht(n)
Definition types.h:500
#define ND_showboxes(n)
Definition types.h:530
#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_order(n)
Definition types.h:513
#define ND_coord(n)
Definition types.h:490
#define ND_in(n)
Definition types.h:501
#define ND_shape(n)
Definition types.h:528
#define ND_out(n)
Definition types.h:515
Agraph_t * agraphof(void *obj)
Definition obj.c:185
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:143
Arithmetic helper functions.
#define SWAP(a, b)
Definition gv_math.h:134
static gdPoint * points
#define LIST_APPEND(list, item)
Definition list.h:120
static int * ps
Definition lu.c:51
#define EDGE_TYPE(g)
Definition macros.h:25
static int sgn(int x)
sgn, as defined in Graphics Gems I, ยง11.8, pp. 99
Definition pack.c:115
static void merge(edge_t *e, int minlen, int weight)
Definition rank.c:763
port resolvePort(node_t *n, node_t *other, port *oldport)
Definition shapes.c:4344
bezier * new_spline(edge_t *e, size_t sz)
create and attach a new bezier of size sz to the edge d
Definition splines.c:212
void endpath(path *P, edge_t *e, int et, pathend_t *endp, bool merge)
Definition splines.c:573
void makePortLabels(edge_t *e)
add head and tail labels if necessary and update bounding box
Definition splines.c:1203
splines * getsplinepoints(edge_t *e)
Definition splines.c:1361
static double conc_slope(node_t *n)
Definition splines.c:316
void clip_and_install(edge_t *fe, node_t *hn, pointf *ps, size_t pn, splineInfo *info)
Definition splines.c:234
double selfRightSpace(edge_t *e)
Definition splines.c:1137
static void arrow_clip(edge_t *fe, node_t *hn, pointf *ps, size_t *startp, size_t *endp, bezier *spl, splineInfo *info)
Definition splines.c:63
static void selfLeft(edge_t *edges[], size_t ind, size_t cnt, double stepx, double sizey, splineInfo *sinfo)
Definition splines.c:1055
void shape_clip(node_t *n, pointf curve[4])
Definition splines.c:193
static int convert_sides_to_points(int tail_side, int head_side)
Definition splines.c:772
static void selfRight(edge_t *edges[], size_t ind, size_t cnt, double stepx, double sizey, splineInfo *sinfo)
Definition splines.c:984
static void shape_clip0(inside_t *inside_context, node_t *n, pointf curve[4], bool left_inside)
Definition splines.c:160
#define HT2(n)
Definition splines.c:373
static void selfBottom(edge_t *edges[], size_t ind, size_t cnt, double sizex, double stepy, splineInfo *sinfo)
Definition splines.c:807
void addEdgeLabels(edge_t *e)
Definition splines.c:1305
pointf edgeMidpoint(graph_t *g, edge_t *e)
Definition splines.c:1281
#define FUDGE
Definition splines.c:372
void beginpath(path *P, edge_t *e, int et, pathend_t *endp, bool merge)
Definition splines.c:376
void makeSelfEdge(edge_t *edges[], size_t ind, size_t cnt, double sizex, double sizey, splineInfo *sinfo)
Definition splines.c:1162
static pointf polylineMidpoint(splines *spl, pointf *pp, pointf *pq)
Definition splines.c:1245
void add_box(path *P, boxf b)
Definition splines.c:336
static void endPoints(splines *spl, pointf *p, pointf *q)
extract the actual end points of the spline, where they touch the node
Definition splines.c:1221
void bezier_clip(inside_t *inside_context, bool(*inside)(inside_t *inside_context, pointf p), pointf *sp, bool left_inside)
Definition splines.c:107
static void selfTop(edge_t *edges[], size_t ind, size_t cnt, double sizex, double stepy, splineInfo *sinfo)
Definition splines.c:877
int place_portlabel(edge_t *e, bool head_p)
Definition splines.c:1314
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
graph or subgraph
Definition cgraph.h:424
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
void * data
Definition types.h:86
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
pointf np
Definition types.h:75
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
Definition heap.c:18
bezier * list
Definition types.h:99
size_t size
Definition types.h:100
pointf pos
Definition types.h:114
bool set
Definition types.h:123
pointf dimen
Definition types.h:110
struct inside_t::@57 s
node_t * n
Definition types.h:160
#define UNREACHABLE()
Definition unreachable.h:30
#define MAX(a, b)
Definition write.c:32
int(* pf)(void *, char *,...)
Definition xdot.c:396