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