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