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