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