Graphviz 14.1.3~dev.20260227.0545
Loading...
Searching...
No Matches
arrows.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#include "config.h"
15
16#include <assert.h>
17#include <common/geomprocs.h>
18#include <common/render.h>
19#include <math.h>
20#include <stdbool.h>
21#include <stddef.h>
22#include <stdint.h>
23#include <util/startswith.h>
24#include <util/streq.h>
25
26#define EPSILON .0001
27
28/* standard arrow length in points */
29#define ARROW_LENGTH 10.
30
31#define NUMB_OF_ARROW_HEADS 4
32/* each arrow in 8 bits. Room for NUMB_OF_ARROW_HEADS arrows in 32 bit int. */
33
34#define BITS_PER_ARROW 8
35
36#define BITS_PER_ARROW_TYPE 4
37/* arrow types (in BITS_PER_ARROW_TYPE bits) */
38#define ARR_TYPE_NONE (ARR_NONE)
39#define ARR_TYPE_NORM 1
40#define ARR_TYPE_CROW 2
41#define ARR_TYPE_TEE 3
42#define ARR_TYPE_BOX 4
43#define ARR_TYPE_DIAMOND 5
44#define ARR_TYPE_DOT 6
45#define ARR_TYPE_CURVE 7
46#define ARR_TYPE_GAP 8
47/* Spare: 9-15 */
48
49/* arrow mods (in (BITS_PER_ARROW - BITS_PER_ARROW_TYPE) bits) */
50#define ARR_MOD_OPEN (1<<(BITS_PER_ARROW_TYPE+0))
51#define ARR_MOD_INV (1<<(BITS_PER_ARROW_TYPE+1))
52#define ARR_MOD_LEFT (1<<(BITS_PER_ARROW_TYPE+2))
53#define ARR_MOD_RIGHT (1<<(BITS_PER_ARROW_TYPE+3))
54/* No spares */
55
56typedef struct {
57 char *dir;
58 uint32_t sflag;
59 uint32_t eflag;
61
62static const arrowdir_t Arrowdirs[] = {
63 {"forward", ARR_TYPE_NONE, ARR_TYPE_NORM},
67 {0}
68};
69
70typedef struct {
71 char *name;
72 uint32_t type;
74
75static const arrowname_t Arrowsynonyms[] = {
76 /* synonyms for deprecated arrow names - included for backward compatibility */
77 /* evaluated before primary names else "invempty" would give different results */
78 {"invempty", (ARR_TYPE_NORM | ARR_MOD_INV | ARR_MOD_OPEN)}, /* oinv */
79 {0}
80};
81
82static const arrowname_t Arrowmods[] = {
83 {"o", ARR_MOD_OPEN},
84 {"r", ARR_MOD_RIGHT},
85 {"l", ARR_MOD_LEFT},
86 /* deprecated alternates for backward compat */
87 {"e", ARR_MOD_OPEN}, /* o - needed for "ediamond" */
88 {"half", ARR_MOD_LEFT}, /* l - needed for "halfopen" */
89 {0}
90};
91
92static const arrowname_t Arrownames[] = {
93 {"normal", ARR_TYPE_NORM},
94 {"crow", ARR_TYPE_CROW},
95 {"tee", ARR_TYPE_TEE},
96 {"box", ARR_TYPE_BOX},
97 {"diamond", ARR_TYPE_DIAMOND},
98 {"dot", ARR_TYPE_DOT},
99 {"none", ARR_TYPE_GAP},
100 /* ARR_MOD_INV is used only here to define two additional shapes
101 since not all types can use it */
102 {"inv", (ARR_TYPE_NORM | ARR_MOD_INV)},
103 {"vee", (ARR_TYPE_CROW | ARR_MOD_INV)},
104 /* WARNING ugly kludge to deal with "o" v "open" conflict */
105 /* Define "open" as just "pen" since "o" already taken as ARR_MOD_OPEN */
106 /* Note that ARR_MOD_OPEN has no meaning for ARR_TYPE_CROW shape */
107 {"pen", (ARR_TYPE_CROW | ARR_MOD_INV)},
108 /* WARNING ugly kludge to deal with "e" v "empty" conflict */
109 /* Define "empty" as just "mpty" since "e" already taken as ARR_MOD_OPEN */
110 /* Note that ARR_MOD_OPEN has expected meaning for ARR_TYPE_NORM shape */
111 {"mpty", ARR_TYPE_NORM},
112 {"curve", ARR_TYPE_CURVE},
113 {"icurve", (ARR_TYPE_CURVE | ARR_MOD_INV)},
114 {0}
115};
116
117typedef struct {
118 uint32_t type;
119 double lenfact; /* ratio of length of this arrow type to standard arrow */
120 pointf (*gen)(GVJ_t *job, pointf p, pointf u, double arrowsize,
121 double penwidth, uint32_t flag);
123 double (*len)(double lenfact, double arrowsize, double penwidth,
124 uint32_t flag);
126
127/* forward declaration of functions used in Arrowtypes[] */
128static pointf arrow_type_normal(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag);
129static pointf arrow_type_crow(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag);
130static pointf arrow_type_tee(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag);
131static pointf arrow_type_box(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag);
132static pointf arrow_type_diamond(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag);
133static pointf arrow_type_dot(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag);
134static pointf arrow_type_curve(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag);
135static pointf arrow_type_gap(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag);
136
137static double arrow_length_generic(double lenfact, double arrowsize, double penwidth, uint32_t flag);
138static double arrow_length_crow(double lenfact, double arrowsize, double penwidth, uint32_t flag);
139static double arrow_length_normal(double lenfact, double arrowsize, double penwidth, uint32_t flag);
140static double arrow_length_tee(double lenfact, double arrowsize, double penwidth, uint32_t flag);
141static double arrow_length_box(double lenfact, double arrowsize, double penwidth, uint32_t flag);
142static double arrow_length_diamond(double lenfact, double arrowsize, double penwidth, uint32_t flag);
143static double arrow_length_curve(double lenfact, double arrowsize, double penwidth, uint32_t flag);
144static double arrow_length_dot(double lenfact, double arrowsize, double penwidth, uint32_t flag);
145
156
157static const size_t Arrowtypes_size =
158 sizeof(Arrowtypes) / sizeof(Arrowtypes[0]);
159
160static char *arrow_match_name_frag(char *name, const arrowname_t *arrownames,
161 uint32_t *flag) {
162 size_t namelen = 0;
163 char *rest = name;
164
165 for (const arrowname_t *arrowname = arrownames; arrowname->name;
166 arrowname++) {
167 namelen = strlen(arrowname->name);
168 if (startswith(name, arrowname->name)) {
169 *flag |= arrowname->type;
170 rest += namelen;
171 break;
172 }
173 }
174 return rest;
175}
176
177static char *arrow_match_shape(char *name, uint32_t *flag) {
178 char *next, *rest;
179 uint32_t f = ARR_TYPE_NONE;
180
181 rest = arrow_match_name_frag(name, Arrowsynonyms, &f);
182 if (rest == name) {
183 do {
184 next = rest;
185 rest = arrow_match_name_frag(next, Arrowmods, &f);
186 } while (next != rest);
187 rest = arrow_match_name_frag(rest, Arrownames, &f);
188 }
189 if (f && !(f & ((1 << BITS_PER_ARROW_TYPE) - 1)))
190 f |= ARR_TYPE_NORM;
191 *flag = f;
192 return rest;
193}
194
195static void arrow_match_name(char *name, uint32_t *flag) {
196 char *rest = name;
197 char *next;
198 int i;
199
200 *flag = 0;
201 for (i = 0; *rest != '\0' && i < NUMB_OF_ARROW_HEADS; ) {
202 uint32_t f = ARR_TYPE_NONE;
203 next = rest;
204 rest = arrow_match_shape(next, &f);
205 if (f == ARR_TYPE_NONE) {
206 agwarningf("Arrow type \"%s\" unknown - ignoring\n", next);
207 return;
208 }
209 if (f == ARR_TYPE_GAP && i == NUMB_OF_ARROW_HEADS - 1)
210 f = ARR_TYPE_NONE;
211 if (f == ARR_TYPE_GAP && i == 0 && *rest == '\0')
212 f = ARR_TYPE_NONE;
213 if (f != ARR_TYPE_NONE)
214 *flag |= (f << (i++ * BITS_PER_ARROW));
215 }
216}
217
218void arrow_flags(Agedge_t *e, uint32_t *sflag, uint32_t *eflag) {
219 char *attr;
220
221 *sflag = ARR_TYPE_NONE;
223 if (E_dir && ((attr = agxget(e, E_dir)))[0]) {
224 for (const arrowdir_t *arrowdir = Arrowdirs; arrowdir->dir; arrowdir++) {
225 if (streq(attr, arrowdir->dir)) {
226 *sflag = arrowdir->sflag;
227 *eflag = arrowdir->eflag;
228 break;
229 }
230 }
231 }
232 if (*eflag == ARR_TYPE_NORM) {
233 Agsym_t *arrowhead = agfindedgeattr(agraphof(e), "arrowhead");
234 if (arrowhead != NULL && ((attr = agxget(e, arrowhead)))[0])
235 arrow_match_name(attr, eflag);
236 }
237 if (*sflag == ARR_TYPE_NORM) {
238 Agsym_t *arrowtail = agfindedgeattr(agraphof(e), "arrowtail");
239 if (arrowtail != NULL && ((attr = agxget(e, arrowtail)))[0])
240 arrow_match_name(attr, sflag);
241 }
242 if (ED_conc_opp_flag(e)) {
243 edge_t *f;
244 uint32_t s0, e0;
245 /* pick up arrowhead of opposing edge */
246 f = agfindedge(agraphof(aghead(e)), aghead(e), agtail(e));
247 arrow_flags(f, &s0, &e0);
248 *eflag |= s0;
249 *sflag |= e0;
250 }
251}
252
253static double arrow_length(edge_t * e, uint32_t flag) {
254 double length = 0.0;
255 int i;
256
257 const double penwidth = late_double(e, E_penwidth, 1.0, 0.0);
258 const double arrowsize = late_double(e, E_arrowsz, 1.0, 0.0);
259
260 if (arrowsize == 0) {
261 return 0;
262 }
263
264 for (i = 0; i < NUMB_OF_ARROW_HEADS; i++) {
265 /* we don't simply index with flag because arrowtypes are not necessarily sorted */
266 uint32_t f = (flag >> (i * BITS_PER_ARROW)) & ((1 << BITS_PER_ARROW_TYPE) - 1);
267 for (size_t j = 0; j < Arrowtypes_size; ++j) {
268 const arrowtype_t *arrowtype = &Arrowtypes[j];
269 if (f == arrowtype->type) {
270 const uint32_t arrow_flag = (flag >> (i * BITS_PER_ARROW)) & ((1 << BITS_PER_ARROW) - 1);
271 length += (arrowtype->len)(arrowtype->lenfact, arrowsize, penwidth, arrow_flag);
272 break;
273 }
274 }
275 }
276 return length;
277}
278
279/* inside function for calls to bezier_clip */
280static bool inside(inside_t * inside_context, pointf p)
281{
282 return DIST(p, inside_context->a.p[0]) <= inside_context->a.r[0];
283}
284
285size_t arrowEndClip(edge_t* e, pointf * ps, size_t startp,
286 size_t endp, bezier *spl, uint32_t eflag) {
287 inside_t inside_context;
288 pointf sp[4];
289 double elen = arrow_length(e, eflag);
290 spl->eflag = eflag;
291 spl->ep = ps[endp + 3];
292 if (endp > startp && DIST(ps[endp], ps[endp + 3]) < elen) {
293 endp -= 3;
294 }
295 sp[3] = ps[endp];
296 sp[2] = ps[endp + 1];
297 sp[1] = ps[endp + 2];
298 sp[0] = spl->ep; /* ensure endpoint starts inside */
299
300 if (elen > 0) {
301 inside_context.a.p = &sp[0];
302 inside_context.a.r = &elen;
303 bezier_clip(&inside_context, inside, sp, true);
304 }
305
306 ps[endp] = sp[3];
307 ps[endp + 1] = sp[2];
308 ps[endp + 2] = sp[1];
309 ps[endp + 3] = sp[0];
310 return endp;
311}
312
313size_t arrowStartClip(edge_t* e, pointf * ps, size_t startp,
314 size_t endp, bezier *spl, uint32_t sflag) {
315 inside_t inside_context;
316 pointf sp[4];
317 double slen = arrow_length(e, sflag);
318 spl->sflag = sflag;
319 spl->sp = ps[startp];
320 if (endp > startp && DIST(ps[startp], ps[startp + 3]) < slen) {
321 startp += 3;
322 }
323 sp[0] = ps[startp + 3];
324 sp[1] = ps[startp + 2];
325 sp[2] = ps[startp + 1];
326 sp[3] = spl->sp; /* ensure endpoint starts inside */
327
328 if (slen > 0) {
329 inside_context.a.p = &sp[3];
330 inside_context.a.r = &slen;
331 bezier_clip(&inside_context, inside, sp, false);
332 }
333
334 ps[startp] = sp[3];
335 ps[startp + 1] = sp[2];
336 ps[startp + 2] = sp[1];
337 ps[startp + 3] = sp[0];
338 return startp;
339}
340
341/* arrowOrthoClip:
342 * For orthogonal routing, we know each Bézier of spl is a horizontal or vertical
343 * line segment. We need to guarantee the B-spline stays this way. At present, we shrink
344 * the arrows if necessary to fit the last segment at either end. Alternatively, we could
345 * maintain the arrow size by dropping the 3 points of spl, and adding a new spl encoding
346 * the arrow, something "ex_0,y_0 x_1,y_1 x_1,y_1 x_1,y_1 x_1,y_1", when the last line
347 * segment is x_1,y_1 x_2,y_2 x_3,y_3 x_0,y_0. With a good deal more work, we could guarantee
348 * that the truncated spl clips to the arrow shape.
349 */
350void arrowOrthoClip(edge_t *e, pointf *ps, size_t startp, size_t endp,
351 bezier *spl, uint32_t sflag, uint32_t eflag) {
352 pointf p, q, r, s, t;
353 double d, tlen, hlen, maxd;
354
355 if (sflag && eflag && endp == startp) { /* handle special case of two arrows on a single segment */
356 p = ps[endp];
357 q = ps[endp+3];
358 tlen = arrow_length (e, sflag);
359 hlen = arrow_length (e, eflag);
360 d = DIST(p, q);
361 if (hlen + tlen >= d) {
362 hlen = tlen = d/3.0;
363 }
364 if (p.y == q.y) { // horizontal segment
365 s.y = t.y = p.y;
366 if (p.x < q.x) {
367 t.x = q.x - hlen;
368 s.x = p.x + tlen;
369 }
370 else {
371 t.x = q.x + hlen;
372 s.x = p.x - tlen;
373 }
374 }
375 else { // vertical segment
376 s.x = t.x = p.x;
377 if (p.y < q.y) {
378 t.y = q.y - hlen;
379 s.y = p.y + tlen;
380 }
381 else {
382 t.y = q.y + hlen;
383 s.y = p.y - tlen;
384 }
385 }
386 ps[endp] = ps[endp + 1] = s;
387 ps[endp + 2] = ps[endp + 3] = t;
388 spl->sflag = sflag, spl->sp = p;
389 spl->eflag = eflag, spl->ep = q;
390 return;
391 }
392 if (eflag) {
393 hlen = arrow_length(e, eflag);
394 p = ps[endp];
395 q = ps[endp+3];
396 d = DIST(p, q);
397 maxd = 0.9*d;
398 if (hlen >= maxd) { /* arrow too long */
399 hlen = maxd;
400 }
401 if (p.y == q.y) { // horizontal segment
402 r.y = p.y;
403 if (p.x < q.x) r.x = q.x - hlen;
404 else r.x = q.x + hlen;
405 }
406 else { // vertical segment
407 r.x = p.x;
408 if (p.y < q.y) r.y = q.y - hlen;
409 else r.y = q.y + hlen;
410 }
411 ps[endp + 1] = p;
412 ps[endp + 2] = ps[endp + 3] = r;
413 spl->eflag = eflag;
414 spl->ep = q;
415 }
416 if (sflag) {
417 tlen = arrow_length(e, sflag);
418 p = ps[startp];
419 q = ps[startp+3];
420 d = DIST(p, q);
421 maxd = 0.9*d;
422 if (tlen >= maxd) { /* arrow too long */
423 tlen = maxd;
424 }
425 if (p.y == q.y) { // horizontal segment
426 r.y = p.y;
427 if (p.x < q.x) r.x = p.x + tlen;
428 else r.x = p.x - tlen;
429 }
430 else { // vertical segment
431 r.x = p.x;
432 if (p.y < q.y) r.y = p.y + tlen;
433 else r.y = p.y - tlen;
434 }
435 ps[startp] = ps[startp + 1] = r;
436 ps[startp + 2] = q;
437 spl->sflag = sflag;
438 spl->sp = p;
439 }
440}
441
442// See https://www.w3.org/TR/SVG2/painting.html#TermLineJoinShape for the
443// terminology
444
445typedef struct {
447} triangle;
448
449static triangle
450miter_shape(pointf base_left, pointf P, pointf base_right, double penwidth) {
451 if ((base_left.x == P.x && base_left.y == P.y) ||
452 (base_right.x == P.x && base_right.y == P.y)) {
453 // the stroke shape is really a point so we just return this point without
454 // extending it with penwidth in any direction, which seems to be the way
455 // SVG renderers render this.
456 const triangle line_join_shape = {{P, P, P}};
457 return line_join_shape;
458 }
459 const pointf A[] = {base_left, P};
460 const double dxA = A[1].x - A[0].x;
461 const double dyA = A[1].y - A[0].y;
462 const double hypotA = hypot(dxA, dyA);
463 const double cosAlpha = dxA / hypotA;
464 const double sinAlpha = dyA / hypotA;
465 const double alpha = dyA > 0 ? acos(cosAlpha) : -acos(cosAlpha);
466
467 const pointf P1 = {P.x - penwidth / 2.0 * sinAlpha,
468 P.y + penwidth / 2.0 * cosAlpha};
469
470 const pointf B[] = {P, base_right};
471 const double dxB = B[1].x - B[0].x;
472 const double dyB = B[1].y - B[0].y;
473 const double hypotB = hypot(dxB, dyB);
474 const double cosBeta = dxB / hypotB;
475 const double beta = dyB > 0 ? acos(cosBeta) : -acos(cosBeta);
476
477 // angle between the A segment and the B segment in the reverse direction
478 const double beta_rev = beta - M_PI;
479 const double theta = beta_rev - alpha + (beta_rev - alpha <= -M_PI ? 2 * M_PI : 0);
480 assert(theta >= 0 && theta <= M_PI && "theta out of range");
481
482 // check if the miter limit is exceeded according to
483 // https://www.w3.org/TR/SVG2/painting.html#StrokeMiterlimitProperty
484 const double stroke_miterlimit = 4.0;
485 const double normalized_miter_length = 1.0 / sin(theta / 2.0);
486
487 const double sinBeta = dyB / hypotB;
488 const double sinBetaMinusPi = -sinBeta;
489 const double cosBetaMinusPi = -cosBeta;
490 const pointf P2 = {P.x + penwidth / 2.0 * sinBetaMinusPi,
491 P.y - penwidth / 2.0 * cosBetaMinusPi};
492
493 if (normalized_miter_length > stroke_miterlimit) {
494 // fall back to bevel
495
496 // the bevel is the triangle formed from the three points P, P1 and P2 so
497 // a good enough approximation of the miter point in this case is the
498 // crossing of P-P3 with P1-P2 which is the same as the midpoint between
499 // P1 and P2
500 const pointf Pbevel = {(P1.x + P2.x) / 2, (P1.y + P2.y) / 2};
501 const triangle line_join_shape = {{Pbevel, P1, P2}};
502
503 return line_join_shape;
504 }
505
506 // length between P1 and P3 (and between P2 and P3)
507 const double l = penwidth / 2.0 / tan(theta / 2.0);
508
509 const pointf P3 = {P1.x + l * cosAlpha,
510 P1.y + l * sinAlpha};
511 const triangle line_join_shape = {{P3, P1, P2}};
512
513 return line_join_shape;
514}
515
517 uint32_t flag, pointf *a) {
518 pointf q, v;
519 double arrowwidth;
520
521 arrowwidth = 0.35;
522 if (penwidth > 4)
523 arrowwidth *= penwidth / 4;
524
525 v.x = -u.y * arrowwidth;
526 v.y = u.x * arrowwidth;
527 q.x = p.x + u.x;
528 q.y = p.y + u.y;
529
530 pointf delta_base = {0, 0};
531
532 const pointf origin = {0, 0};
533 const pointf v_inv = {-v.x, -v.y};
534 const pointf normal_left = flag & ARR_MOD_RIGHT ? origin : v_inv;
535 const pointf normal_right = flag & ARR_MOD_LEFT ? origin : v;
536 const pointf base_left = flag & ARR_MOD_INV ? normal_right : normal_left;
537 const pointf base_right = flag & ARR_MOD_INV ? normal_left : normal_right;
538 const pointf normal_tip = {-u.x, -u.y};
539 const pointf inv_tip = u;
540 const pointf P = flag & ARR_MOD_INV ? inv_tip : normal_tip ;
541
542 pointf delta_tip = {0, 0};
543
544 if (u.x != 0 || u.y != 0) {
545 // phi = angle of arrow
546 const double cosPhi = P.x / hypot(P.x, P.y);
547 const double sinPhi = P.y / hypot(P.x, P.y);
548 const double phi = P.y > 0 ? acos(cosPhi) : -acos(cosPhi);
549
550 if (flag & ARR_MOD_LEFT) {
551 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
552 const pointf P1 = line_join_shape.points[1];
553 // alpha = angle of P -> P1
554 const double dx_P_P1 = P1.x - P.x;
555 const double dy_P_P1 = P1.y - P.y;
556 const double hypot_P_P1 = hypot(dx_P_P1, dy_P_P1);
557 const double cosAlpha = dx_P_P1 / hypot_P_P1;
558 const double alpha = dy_P_P1 > 0 ? acos(cosAlpha) : -acos(cosAlpha);
559 const double gamma = alpha - phi;
560 const double delta_tip_length = hypot_P_P1 * cos(gamma);
561 delta_tip = (pointf){delta_tip_length * cosPhi, delta_tip_length * sinPhi};
562 } else if (flag & ARR_MOD_RIGHT) {
563 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
564 const pointf P2 = line_join_shape.points[2];
565 // alpha = angle of P -> P2
566 const double dx_P_P2 = P2.x - P.x;
567 const double dy_P_P2 = P2.y - P.y;
568 const double hypot_P_P2 = hypot(dx_P_P2, dy_P_P2);
569 const double cosAlpha = dx_P_P2 / hypot_P_P2;
570 const double alpha = dy_P_P2 > 0 ? acos(cosAlpha) : -acos(cosAlpha);
571 const double gamma = alpha - phi;
572 const double delta_tip_length = hypot_P_P2 * cos(gamma);
573 delta_tip = (pointf){delta_tip_length * cosPhi, delta_tip_length * sinPhi};
574 } else {
575 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
576 const pointf P3 = line_join_shape.points[0];
577 delta_tip = sub_pointf(P3, P);
578 }
579 delta_base = (pointf) {penwidth / 2.0 * cosPhi, penwidth / 2.0 * sinPhi};
580 }
581
582 if (flag & ARR_MOD_INV) {
583 p.x += delta_base.x;
584 p.y += delta_base.y;
585 q.x += delta_base.x;
586 q.y += delta_base.y;
587 a[0] = a[4] = p;
588 a[1].x = p.x - v.x;
589 a[1].y = p.y - v.y;
590 a[2] = q;
591 a[3].x = p.x + v.x;
592 a[3].y = p.y + v.y;
593 q.x += delta_tip.x;
594 q.y += delta_tip.y;
595 } else {
596 p.x -= delta_tip.x;
597 p.y -= delta_tip.y;
598 q.x -= delta_tip.x;
599 q.y -= delta_tip.y;
600 a[0] = a[4] = q;
601 a[1].x = q.x - v.x;
602 a[1].y = q.y - v.y;
603 a[2] = p;
604 a[3].x = q.x + v.x;
605 a[3].y = q.y + v.y;
606 q.x -= delta_base.x;
607 q.y -= delta_base.y;
608 }
609
610 return q;
611}
612
614 double arrowsize, double penwidth,
615 uint32_t flag) {
616 (void)arrowsize;
617
618 pointf a[5];
619
620 pointf q = arrow_type_normal0(p, u, penwidth, flag, a);
621
622 if (flag & ARR_MOD_LEFT)
623 gvrender_polygon(job, a, 3, !(flag & ARR_MOD_OPEN));
624 else if (flag & ARR_MOD_RIGHT)
625 gvrender_polygon(job, &a[2], 3, !(flag & ARR_MOD_OPEN));
626 else
627 gvrender_polygon(job, &a[1], 3, !(flag & ARR_MOD_OPEN));
628
629 return q;
630}
631
632static pointf arrow_type_crow0(pointf p, pointf u, double arrowsize,
633 double penwidth, uint32_t flag, pointf *a) {
634 pointf m, q, v, w;
635 double arrowwidth, shaftwidth;
636
637 arrowwidth = 0.45;
638 if (penwidth > 4 * arrowsize && (flag & ARR_MOD_INV))
639 arrowwidth *= penwidth / (4 * arrowsize);
640
641 shaftwidth = 0;
642 if (penwidth > 1 && (flag & ARR_MOD_INV))
643 shaftwidth = 0.05 * (penwidth - 1) / arrowsize; /* arrowsize to cancel the arrowsize term already in u */
644
645 v.x = -u.y * arrowwidth;
646 v.y = u.x * arrowwidth;
647 w.x = -u.y * shaftwidth;
648 w.y = u.x * shaftwidth;
649 q.x = p.x + u.x;
650 q.y = p.y + u.y;
651 m.x = p.x + u.x * 0.5;
652 m.y = p.y + u.y * 0.5;
653
654 pointf delta_base = {0, 0};
655
656 const pointf origin = {0, 0};
657 const pointf v_inv = {-v.x, -v.y};
658 const pointf normal_left = flag & ARR_MOD_RIGHT ? origin : v;
659 const pointf normal_right = flag & ARR_MOD_LEFT ? origin : v_inv;
660 const pointf base_left = flag & ARR_MOD_INV ? normal_right : normal_left;
661 const pointf base_right = flag & ARR_MOD_INV ? normal_left : normal_right;
662 const pointf normal_tip = u;
663 const pointf inv_tip = {-u.x, -u.y};
664 const pointf P = flag & ARR_MOD_INV ? inv_tip : normal_tip ;
665
666 pointf delta_tip = {0, 0};
667
668 if (u.x != 0 || u.y != 0) {
669 // phi = angle of arrow
670 const double cosPhi = P.x / hypot(P.x, P.y);
671 const double sinPhi = P.y / hypot(P.x, P.y);
672 const double phi = P.y > 0 ? acos(cosPhi) : -acos(cosPhi);
673
674 if ((flag & ARR_MOD_LEFT && flag & ARR_MOD_INV) || (flag & ARR_MOD_RIGHT && !(flag & ARR_MOD_INV))) {
675 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
676 const pointf P2 = line_join_shape.points[2];
677 // alpha = angle of P -> P2
678 const double dx_P_P2 = P2.x - P.x;
679 const double dy_P_P2 = P2.y - P.y;
680 const double hypot_P_P2 = hypot(dx_P_P2, dy_P_P2);
681 const double cosAlpha = dx_P_P2 / hypot_P_P2;
682 const double alpha = dy_P_P2 > 0 ? acos(cosAlpha) : -acos(cosAlpha);
683 const double gamma = alpha - phi;
684 const double delta_tip_length = hypot_P_P2 * cos(gamma);
685 delta_tip = (pointf){delta_tip_length * cosPhi, delta_tip_length * sinPhi};
686 } else if ((flag & ARR_MOD_LEFT && !(flag & ARR_MOD_INV)) || (flag & ARR_MOD_RIGHT && flag & ARR_MOD_INV)) {
687 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
688 const pointf P1 = line_join_shape.points[1];
689 // alpha = angle of P -> P1
690 const double dx_P_P1 = P1.x - P.x;
691 const double dy_P_P1 = P1.y - P.y;
692 const double hypot_P_P1 = hypot(dx_P_P1, dy_P_P1);
693 const double cosAlpha = dx_P_P1 / hypot_P_P1;
694 const double alpha = dy_P_P1 > 0 ? acos(cosAlpha) : -acos(cosAlpha);
695 const double gamma = alpha - phi;
696 const double delta_tip_length = hypot_P_P1 * cos(gamma);
697 delta_tip = (pointf){delta_tip_length * cosPhi, delta_tip_length * sinPhi};
698 } else {
699 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
700 const pointf P3 = line_join_shape.points[0];
701 delta_tip = sub_pointf(P3, P);
702 }
703 if (flag & ARR_MOD_INV) { /* vee */
704 delta_base = (pointf) {penwidth / 2.0 * cosPhi, penwidth / 2.0 * sinPhi};
705 } else {
706 // the left and right "toes" of the crow extend in the direction of
707 // the arrow by the same amount. Their shape is not affected by the
708 // 'l' or 'r' modifiers. Here we use the right "toe" to calculate
709 // the extension. This is ok even if it's not actually rendered when
710 // the 'l' modifier is used.
711 const pointf toe_base_left = add_pointf(sub_pointf(m, q), w);
712 const pointf toe_base_right = origin;
713 const pointf toe_P = sub_pointf(v, u);
714 const triangle toe_line_join_shape = miter_shape(toe_base_left, toe_P, toe_base_right, penwidth);
715 const pointf P1 = toe_line_join_shape.points[1];
716 // alpha = angle of toe_P -> P1
717 const double dx_P_P1 = P1.x - toe_P.x;
718 const double dy_P_P1 = P1.y - toe_P.y;
719 const double hypot_P_P1 = hypot(dx_P_P1, dy_P_P1);
720 const double cosAlpha = dx_P_P1 / hypot_P_P1;
721 const double alpha = dy_P_P1 > 0 ? acos(cosAlpha) : -acos(cosAlpha);
722 const double gamma = alpha - phi;
723 const double delta_tip_length = -hypot_P_P1 * cos(gamma);
724 delta_base = (pointf){delta_tip_length * cosPhi, delta_tip_length * sinPhi};
725 }
726 }
727
728 if (flag & ARR_MOD_INV) { /* vee */
729 p.x -= delta_tip.x;
730 p.y -= delta_tip.y;
731 q.x -= delta_tip.x;
732 q.y -= delta_tip.y;
733 a[0] = a[8] = p;
734 a[1].x = q.x - v.x;
735 a[1].y = q.y - v.y;
736 a[2].x = m.x - w.x;
737 a[2].y = m.y - w.y;
738 a[3].x = q.x - w.x;
739 a[3].y = q.y - w.y;
740 a[4] = q;
741 a[5].x = q.x + w.x;
742 a[5].y = q.y + w.y;
743 a[6].x = m.x + w.x;
744 a[6].y = m.y + w.y;
745 a[7].x = q.x + v.x;
746 a[7].y = q.y + v.y;
747 q.x -= delta_base.x;
748 q.y -= delta_base.y;
749 } else { /* crow */
750 p.x += delta_base.x;
751 p.y += delta_base.y;
752 q.x += delta_base.x;
753 q.y += delta_base.y;
754 a[0] = a[8] = q;
755 a[1].x = p.x - v.x;
756 a[1].y = p.y - v.y;
757 a[2].x = m.x - w.x;
758 a[2].y = m.y - w.y;
759 a[3].x = p.x + delta_base.x;
760 a[3].y = p.y + delta_base.y;
761 a[4] = add_pointf(p, delta_base);
762 a[5].x = p.x + delta_base.x;
763 a[5].y = p.y + delta_base.y;
764 a[6].x = m.x + w.x;
765 a[6].y = m.y + w.y;
766 a[7].x = p.x + v.x;
767 a[7].y = p.y + v.y;
768 q.x += delta_tip.x;
769 q.y += delta_tip.y;
770 }
771 return q;
772}
773
774static pointf arrow_type_crow(GVJ_t *job, pointf p, pointf u, double arrowsize,
775 double penwidth, uint32_t flag) {
776 (void)arrowsize;
777
778 pointf a[9];
779
780 pointf q = arrow_type_crow0(p, u, arrowsize, penwidth, flag, a);
781 if (flag & ARR_MOD_LEFT)
782 gvrender_polygon(job, a, 5, 1);
783 else if (flag & ARR_MOD_RIGHT)
784 gvrender_polygon(job, &a[4], 5, 1);
785 else
786 gvrender_polygon(job, a, 8, 1);
787
788 return q;
789}
790
791static pointf arrow_type_gap(GVJ_t *job, pointf p, pointf u, double arrowsize,
792 double penwidth, uint32_t flag) {
793 (void)arrowsize;
794 (void)penwidth;
795 (void)flag;
796
797 pointf q, a[2];
798
799 q.x = p.x + u.x;
800 q.y = p.y + u.y;
801 a[0] = p;
802 a[1] = q;
803 gvrender_polyline(job, a, 2);
804
805 return q;
806}
807
808static pointf arrow_type_tee(GVJ_t *job, pointf p, pointf u, double arrowsize,
809 double penwidth, uint32_t flag) {
810 (void)arrowsize;
811
812 pointf m, n, q, v, a[4];
813
814 v.x = -u.y;
815 v.y = u.x;
816 q.x = p.x + u.x;
817 q.y = p.y + u.y;
818 m.x = p.x + u.x * 0.2;
819 m.y = p.y + u.y * 0.2;
820 n.x = p.x + u.x * 0.6;
821 n.y = p.y + u.y * 0.6;
822
823 const double length = hypot(u.x, u.y);
824 const double polygon_extend_over_polyline = penwidth / 2 - 0.2 * length;
825 if (length > 0 && polygon_extend_over_polyline > 0) {
826 // the polygon part of the 'tee' arrow will visually overlap the
827 // 'polyline' part so we need to move the whole arrow in order not to
828 // overlap the node
829 const pointf P = {-u.x, -u.y};
830 // phi = angle of arrow
831 const double cosPhi = P.x / hypot(P.x, P.y);
832 const double sinPhi = P.y / hypot(P.x, P.y);
833 const pointf delta = {polygon_extend_over_polyline * cosPhi, polygon_extend_over_polyline * sinPhi};
834
835 // move the arrow backwards to not visually overlap the node
836 p = sub_pointf(p, delta);
837 m = sub_pointf(m, delta);
838 n = sub_pointf(n, delta);
839 q = sub_pointf(q, delta);
840 }
841
842 a[0].x = m.x + v.x;
843 a[0].y = m.y + v.y;
844 a[1].x = m.x - v.x;
845 a[1].y = m.y - v.y;
846 a[2].x = n.x - v.x;
847 a[2].y = n.y - v.y;
848 a[3].x = n.x + v.x;
849 a[3].y = n.y + v.y;
850 if (flag & ARR_MOD_LEFT) {
851 a[0] = m;
852 a[3] = n;
853 } else if (flag & ARR_MOD_RIGHT) {
854 a[1] = m;
855 a[2] = n;
856 }
857 gvrender_polygon(job, a, 4, 1);
858 a[0] = p;
859 a[1] = q;
860 gvrender_polyline(job, a, 2);
861
862 // A polyline doesn't extend visually beyond its starting point, so we
863 // return the starting point as it is, without taking penwidth into account
864
865 return q;
866}
867
868static pointf arrow_type_box(GVJ_t *job, pointf p, pointf u, double arrowsize,
869 double penwidth, uint32_t flag) {
870 (void)arrowsize;
871 (void)penwidth;
872
873 pointf m, q, v, a[4];
874
875 v.x = -u.y * 0.4;
876 v.y = u.x * 0.4;
877 m.x = p.x + u.x * 0.8;
878 m.y = p.y + u.y * 0.8;
879 q.x = p.x + u.x;
880 q.y = p.y + u.y;
881
882 pointf delta = {0, 0};
883
884 if (u.x != 0 || u.y != 0) {
885 const pointf P = {-u.x, -u.y};
886 // phi = angle of arrow
887 const double cosPhi = P.x / hypot(P.x, P.y);
888 const double sinPhi = P.y / hypot(P.x, P.y);
889 delta = (pointf) {penwidth / 2.0 * cosPhi, penwidth / 2.0 * sinPhi};
890 }
891
892 // move the arrow backwards to not visually overlap the node
893 p.x -= delta.x;
894 p.y -= delta.y;
895 m.x -= delta.x;
896 m.y -= delta.y;
897 q.x -= delta.x;
898 q.y -= delta.y;
899
900 a[0].x = p.x + v.x;
901 a[0].y = p.y + v.y;
902 a[1].x = p.x - v.x;
903 a[1].y = p.y - v.y;
904 a[2].x = m.x - v.x;
905 a[2].y = m.y - v.y;
906 a[3].x = m.x + v.x;
907 a[3].y = m.y + v.y;
908 if (flag & ARR_MOD_LEFT) {
909 a[0] = p;
910 a[3] = m;
911 } else if (flag & ARR_MOD_RIGHT) {
912 a[1] = p;
913 a[2] = m;
914 }
915 gvrender_polygon(job, a, 4, !(flag & ARR_MOD_OPEN));
916 a[0] = m;
917 a[1] = q;
918 gvrender_polyline(job, a, 2);
919
920 // A polyline doesn't extend visually beyond its starting point, so we
921 // return the starting point as it is, without taking penwidth into account
922
923 return q;
924}
925
927 uint32_t flag, pointf *a) {
928 pointf q, r, v;
929
930 v.x = -u.y / 3.;
931 v.y = u.x / 3.;
932 r.x = p.x + u.x / 2.;
933 r.y = p.y + u.y / 2.;
934 q.x = p.x + u.x;
935 q.y = p.y + u.y;
936
937 const pointf origin = {0, 0};
938 const pointf unmod_left = sub_pointf(scale(-0.5, u), v);
939 const pointf unmod_right = add_pointf(scale(-0.5, u), v);
940 const pointf base_left = flag & ARR_MOD_RIGHT ? origin : unmod_left;
941 const pointf base_right = flag & ARR_MOD_LEFT ? origin : unmod_right;
942 const pointf tip = scale(-1, u);
943 const pointf P = tip;
944
945 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
946 const pointf P3 = line_join_shape.points[0];
947
948 const pointf delta = sub_pointf(P3, P);
949
950 // move the arrow backwards to not visually overlap the node
951 p = sub_pointf(p, delta);
952 r = sub_pointf(r, delta);
953 q = sub_pointf(q, delta);
954
955 a[0] = a[4] = q;
956 a[1].x = r.x + v.x;
957 a[1].y = r.y + v.y;
958 a[2] = p;
959 a[3].x = r.x - v.x;
960 a[3].y = r.y - v.y;
961
962 // return the visual starting point of the arrow outline
963 q = sub_pointf(q, delta);
964
965 return q;
966}
967
969 double arrowsize, double penwidth,
970 uint32_t flag) {
971 (void)arrowsize;
972
973 pointf a[5];
974
975 pointf q = arrow_type_diamond0(p, u, penwidth, flag, a);
976
977 if (flag & ARR_MOD_LEFT)
978 gvrender_polygon(job, &a[2], 3, !(flag & ARR_MOD_OPEN));
979 else if (flag & ARR_MOD_RIGHT)
980 gvrender_polygon(job, a, 3, !(flag & ARR_MOD_OPEN));
981 else
982 gvrender_polygon(job, a, 4, !(flag & ARR_MOD_OPEN));
983
984 return q;
985}
986
987static pointf arrow_type_dot(GVJ_t *job, pointf p, pointf u, double arrowsize,
988 double penwidth, uint32_t flag) {
989 (void)arrowsize;
990 (void)penwidth;
991
992 double r;
993 pointf AF[2];
994
995 r = hypot(u.x, u.y) / 2.;
996
997 pointf delta = {0, 0};
998
999 if (u.x != 0 || u.y != 0) {
1000 const pointf P = {-u.x, -u.y};
1001 // phi = angle of arrow
1002 const double cosPhi = P.x / hypot(P.x, P.y);
1003 const double sinPhi = P.y / hypot(P.x, P.y);
1004 delta = (pointf) {penwidth / 2.0 * cosPhi, penwidth / 2.0 * sinPhi};
1005
1006 // move the arrow backwards to not visually overlap the node
1007 p.x -= delta.x;
1008 p.y -= delta.y;
1009 }
1010
1011
1012 AF[0].x = p.x + u.x / 2. - r;
1013 AF[0].y = p.y + u.y / 2. - r;
1014 AF[1].x = p.x + u.x / 2. + r;
1015 AF[1].y = p.y + u.y / 2. + r;
1016 gvrender_ellipse(job, AF, !(flag & ARR_MOD_OPEN));
1017
1018 pointf q = {p.x + u.x, p.y + u.y};
1019
1020 // return the visual starting point of the arrow outline
1021 q.x -= delta.x;
1022 q.y -= delta.y;
1023
1024 return q;
1025}
1026
1027
1028/* Draw a concave semicircle using a single cubic Bézier curve that touches p at its midpoint.
1029 * See http://digerati-illuminatus.blogspot.com.au/2008/05/approximating-semicircle-with-cubic.html for details.
1030 */
1031static pointf arrow_type_curve(GVJ_t *job, pointf p, pointf u, double arrowsize,
1032 double penwidth, uint32_t flag) {
1033 (void)arrowsize;
1034
1035 double arrowwidth = penwidth > 4 ? 0.5 * penwidth / 4 : 0.5;
1036 pointf q, v, w;
1037 pointf AF[4], a[2];
1038
1039 a[0] = p;
1040 if (!(flag & ARR_MOD_INV) && (u.x != 0 || u.y != 0)) {
1041 const pointf P = {-u.x, -u.y};
1042 // phi = angle of arrow
1043 const double cosPhi = P.x / hypot(P.x, P.y);
1044 const double sinPhi = P.y / hypot(P.x, P.y);
1045 const pointf delta = {penwidth / 2.0 * cosPhi, penwidth / 2.0 * sinPhi};
1046
1047 // move the arrow backwards to not visually overlap the node
1048 p.x -= delta.x;
1049 p.y -= delta.y;
1050 }
1051
1052 q.x = p.x + u.x;
1053 q.y = p.y + u.y;
1054 v.x = -u.y * arrowwidth;
1055 v.y = u.x * arrowwidth;
1056 w.x = v.y; // same direction as u, same magnitude as v.
1057 w.y = -v.x;
1058 a[1] = q;
1059
1060 AF[0].x = p.x + v.x + w.x;
1061 AF[0].y = p.y + v.y + w.y;
1062
1063 AF[3].x = p.x - v.x + w.x;
1064 AF[3].y = p.y - v.y + w.y;
1065
1066 if (flag & ARR_MOD_INV) { /* ----(-| */
1067 AF[1].x = p.x + 0.95 * v.x + w.x + w.x * 4.0 / 3.0;
1068 AF[1].y = AF[0].y + w.y * 4.0 / 3.0;
1069
1070 AF[2].x = p.x - 0.95 * v.x + w.x + w.x * 4.0 / 3.0;
1071 AF[2].y = AF[3].y + w.y * 4.0 / 3.0;
1072 }
1073 else { /* ----)-| */
1074 AF[1].x = p.x + 0.95 * v.x + w.x - w.x * 4.0 / 3.0;
1075 AF[1].y = AF[0].y - w.y * 4.0 / 3.0;
1076
1077 AF[2].x = p.x - 0.95 * v.x + w.x - w.x * 4.0 / 3.0;
1078 AF[2].y = AF[3].y - w.y * 4.0 / 3.0;
1079 }
1080
1081 gvrender_polyline(job, a, 2);
1082 if (flag & ARR_MOD_LEFT)
1083 Bezier(AF, 0.5, NULL, AF);
1084 else if (flag & ARR_MOD_RIGHT)
1085 Bezier(AF, 0.5, AF, NULL);
1086 gvrender_beziercurve(job, AF, sizeof(AF) / sizeof(pointf), 0);
1087
1088 return q;
1089}
1090
1091
1092static pointf arrow_gen_type(GVJ_t *job, pointf p, pointf u, double arrowsize,
1093 double penwidth, uint32_t flag) {
1094 uint32_t f = flag & ((1 << BITS_PER_ARROW_TYPE) - 1);
1095 for (size_t i = 0; i < Arrowtypes_size; ++i) {
1096 const arrowtype_t *arrowtype = &Arrowtypes[i];
1097 if (f == arrowtype->type) {
1098 u.x *= arrowtype->lenfact * arrowsize;
1099 u.y *= arrowtype->lenfact * arrowsize;
1100 p = arrowtype->gen(job, p, u, arrowsize, penwidth, flag);
1101 break;
1102 }
1103 }
1104 return p;
1105}
1106
1107boxf arrow_bb(pointf p, pointf u, double arrowsize)
1108{
1109 double s;
1110 boxf bb;
1111 double ax,ay,bx,by,cx,cy,dx,dy;
1112 double ux2, uy2;
1113
1114 /* generate arrowhead vector */
1115 u.x -= p.x;
1116 u.y -= p.y;
1117 /* the EPSILONs are to keep this stable as length of u approaches 0.0 */
1118 s = ARROW_LENGTH * arrowsize / (hypot(u.x, u.y) + EPSILON);
1119 u.x += (u.x >= 0.0) ? EPSILON : -EPSILON;
1120 u.y += (u.y >= 0.0) ? EPSILON : -EPSILON;
1121 u.x *= s;
1122 u.y *= s;
1123
1124 /* compute all 4 corners of rotated arrowhead bounding box */
1125 ux2 = u.x / 2.;
1126 uy2 = u.y / 2.;
1127 ax = p.x - uy2;
1128 ay = p.y - ux2;
1129 bx = p.x + uy2;
1130 by = p.y + ux2;
1131 cx = ax + u.x;
1132 cy = ay + u.y;
1133 dx = bx + u.x;
1134 dy = by + u.y;
1135
1136 /* compute a right bb */
1137 bb.UR.x = fmax(ax, fmax(bx, fmax(cx, dx)));
1138 bb.UR.y = fmax(ay, fmax(by, fmax(cy, dy)));
1139 bb.LL.x = fmin(ax, fmin(bx, fmin(cx, dx)));
1140 bb.LL.y = fmin(ay, fmin(by, fmin(cy, dy)));
1141
1142 return bb;
1143}
1144
1145void arrow_gen(GVJ_t *job, emit_state_t emit_state, pointf p, pointf u,
1146 double arrowsize, double penwidth, uint32_t flag) {
1147 obj_state_t *obj = job->obj;
1148 double s;
1149 int i;
1150 emit_state_t old_emit_state;
1151
1152 old_emit_state = obj->emit_state;
1153 obj->emit_state = emit_state;
1154
1155 /* Dotted and dashed styles on the arrowhead are ugly (dds) */
1156 /* linewidth needs to be reset */
1158
1160
1161 /* generate arrowhead vector */
1162 u.x -= p.x;
1163 u.y -= p.y;
1164 /* the EPSILONs are to keep this stable as length of u approaches 0.0 */
1165 s = ARROW_LENGTH / (hypot(u.x, u.y) + EPSILON);
1166 u.x += (u.x >= 0.0) ? EPSILON : -EPSILON;
1167 u.y += (u.y >= 0.0) ? EPSILON : -EPSILON;
1168 u.x *= s;
1169 u.y *= s;
1170
1171 /* the first arrow head - closest to node */
1172 for (i = 0; i < NUMB_OF_ARROW_HEADS; i++) {
1173 uint32_t f = (flag >> (i * BITS_PER_ARROW)) & ((1 << BITS_PER_ARROW) - 1);
1174 if (f == ARR_TYPE_NONE)
1175 break;
1176 p = arrow_gen_type(job, p, u, arrowsize, penwidth, f);
1177 }
1178
1179 obj->emit_state = old_emit_state;
1180}
1181
1182static double arrow_length_generic(double lenfact, double arrowsize,
1183 double penwidth, uint32_t flag) {
1184 (void)penwidth;
1185 (void)flag;
1186
1187 return lenfact * arrowsize * ARROW_LENGTH;
1188}
1189
1190static double arrow_length_normal(double lenfact, double arrowsize,
1191 double penwidth, uint32_t flag) {
1192 pointf a[5];
1193 // set arrow end point at origin
1194 const pointf p = {0, 0};
1195 // generate an arrowhead vector along x-axis
1196 const pointf u = {lenfact * arrowsize * ARROW_LENGTH, 0};
1197
1198 // arrow start point
1199 pointf q = arrow_type_normal0(p, u, penwidth, flag, a);
1200
1201 const pointf base1 = a[1];
1202 const pointf base2 = a[3];
1203 const pointf tip = a[2];
1204 const double full_length = q.x;
1205 assert(full_length > 0 && "non-positive full length");
1206 const double nominal_length = fabs(base1.x - tip.x);
1207 const double nominal_base_width = base2.y - base1.y;
1208 assert(nominal_base_width > 0 && "non-positive nominal base width");
1209 // the full base width is proportionally scaled with the length
1210 const double full_base_width =
1211 nominal_base_width * full_length / nominal_length;
1212 assert(full_base_width > 0 && "non-positive full base width");
1213
1214 // we want a small overlap between the edge path (stem) and the arrow to avoid
1215 // gaps between them in case the arrow has a corner towards the edge path
1216 const double overlap_at_base = penwidth / 2;
1217 // overlap the tip to a point where its width is equal to the penwidth.
1218 const double length_where_width_is_penwidth =
1219 full_length * penwidth / full_base_width;
1220 const double overlap_at_tip = length_where_width_is_penwidth;
1221
1222 const double overlap = flag & ARR_MOD_INV ? overlap_at_tip : overlap_at_base;
1223
1224 // arrow length is the x value of the start point since the arrow points along
1225 // the positive x axis and ends at origin
1226 return full_length - overlap;
1227}
1228
1229static double arrow_length_tee(double lenfact, double arrowsize,
1230 double penwidth, uint32_t flag) {
1231 (void)flag;
1232
1233 // The `tee` arrow shape normally begins and ends with a polyline which
1234 // doesn't extend visually beyond its starting point, so we only have to
1235 // take penwidth into account if the polygon part visually extends the
1236 // polyline part at the start or end points.
1237
1238 const double nominal_length = lenfact * arrowsize * ARROW_LENGTH;
1239 double length = nominal_length;
1240
1241 // see the 'arrow_type_tee' function for the magical constants used below
1242
1243 const double polygon_extend_over_polyline_at_start = penwidth / 2 - (1 - 0.6) * nominal_length;
1244 if (polygon_extend_over_polyline_at_start > 0) {
1245 length += polygon_extend_over_polyline_at_start;
1246 }
1247
1248 const double polygon_extend_over_polyline_at_end = penwidth / 2 - 0.2 * nominal_length;
1249 if (polygon_extend_over_polyline_at_start > 0) {
1250 length += polygon_extend_over_polyline_at_end;
1251 }
1252
1253 return length;
1254}
1255
1256static double arrow_length_box(double lenfact, double arrowsize,
1257 double penwidth, uint32_t flag) {
1258 (void)flag;
1259
1260 // The `box` arrow shape begins with a polyline which doesn't extend
1261 // visually beyond its starting point, so we only have to take penwidth
1262 // into account at the end point.
1263
1264 return lenfact * arrowsize * ARROW_LENGTH + penwidth / 2;
1265}
1266
1267static double arrow_length_diamond(double lenfact, double arrowsize,
1268 double penwidth, uint32_t flag) {
1269 pointf a[5];
1270 // set arrow end point at origin
1271 const pointf p = {0, 0};
1272 // generate an arrowhead vector along x-axis
1273 const pointf u = {lenfact * arrowsize * ARROW_LENGTH, 0};
1274
1275 // arrow start point
1276 pointf q = arrow_type_diamond0(p, u, penwidth, flag, a);
1277
1278 // calculate overlap using a triangle with its base at the left and right
1279 // corners of the diamond and its tip at the end point
1280 const pointf base1 = a[3];
1281 const pointf base2 = a[1];
1282 const pointf tip = a[2];
1283 const double full_length = q.x / 2;
1284 assert(full_length > 0 && "non-positive full length");
1285 const double nominal_length = fabs(base1.x - tip.x);
1286 const double nominal_base_width = base2.y - base1.y;
1287 assert(nominal_base_width > 0 && "non-positive nominal base width");
1288 // the full base width is proportionally scaled with the length
1289 const double full_base_width =
1290 nominal_base_width * full_length / nominal_length;
1291 assert(full_base_width > 0 && "non-positive full base width");
1292
1293 // we want a small overlap between the edge path (stem) and the arrow to avoid
1294 // gaps between them in case the arrow has a corner towards the edge path
1295
1296 // overlap the tip to a point where its width is equal to the penwidth
1297 const double length_where_width_is_penwidth =
1298 full_length * penwidth / full_base_width;
1299 const double overlap_at_tip = length_where_width_is_penwidth;
1300
1301 const double overlap = overlap_at_tip;
1302
1303 // arrow length is the x value of the start point since the arrow points along
1304 // the positive x axis and ends at origin
1305 return 2 * full_length - overlap;
1306}
1307
1308static double arrow_length_dot(double lenfact, double arrowsize,
1309 double penwidth, uint32_t flag) {
1310 (void)flag;
1311
1312 return lenfact * arrowsize * ARROW_LENGTH + penwidth;
1313}
1314
1315static double arrow_length_curve(double lenfact, double arrowsize,
1316 double penwidth, uint32_t flag) {
1317 (void)flag;
1318
1319 return lenfact * arrowsize * ARROW_LENGTH + penwidth / 2;
1320}
1321
1322static double arrow_length_crow(double lenfact, double arrowsize,
1323 double penwidth, uint32_t flag) {
1324 pointf a[9];
1325 // set arrow end point at origin
1326 const pointf p = {0, 0};
1327 // generate an arrowhead vector along x-axis
1328 const pointf u = {lenfact * arrowsize * ARROW_LENGTH, 0};
1329
1330 // arrow start point
1331 pointf q = arrow_type_crow0(p, u, arrowsize, penwidth, flag, a);
1332
1333 const pointf base1 = a[1];
1334 const pointf base2 = a[7];
1335 const pointf tip = a[0];
1336 const pointf shaft1 = a[3];
1337 const double full_length = q.x;
1338 assert(full_length > 0 && "non-positive full length");
1339 const double full_length_without_shaft = full_length - (base1.x - shaft1.x);
1340 assert(full_length_without_shaft > 0 && "non-positive full length without shaft");
1341 const double nominal_length = fabs(base1.x - tip.x);
1342 const double nominal_base_width = base2.y - base1.y;
1343 assert(nominal_base_width > 0 && "non-positive nominal base width");
1344 // the full base width is proportionally scaled with the length
1345 const double full_base_width =
1346 nominal_base_width * full_length_without_shaft / nominal_length;
1347 assert(full_base_width > 0 && "non-positive full base width");
1348
1349 // we want a small overlap between the edge path (stem) and the arrow to avoid
1350 // gaps between them in case the arrow has a corner towards the edge path
1351 const double overlap_at_base = penwidth / 2;
1352 // overlap the tip to a point where its width is equal to the penwidth.
1353 const double length_where_width_is_penwidth =
1354 full_length_without_shaft * penwidth / full_base_width;
1355 const double overlap_at_tip = length_where_width_is_penwidth;
1356
1357 const double overlap = flag & ARR_MOD_INV ? overlap_at_base : overlap_at_tip;
1358 // arrow length is the x value of the start point since the arrow points along
1359 // the positive x axis and ends at origin
1360 return full_length - overlap;
1361}
#define M_PI
Definition arith.h:41
#define EPSILON
Definition arrows.c:26
static const arrowtype_t Arrowtypes[]
Definition arrows.c:146
#define ARR_TYPE_NORM
Definition arrows.c:39
static pointf arrow_type_diamond(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:968
static triangle miter_shape(pointf base_left, pointf P, pointf base_right, double penwidth)
Definition arrows.c:450
#define ARR_MOD_LEFT
Definition arrows.c:52
static double arrow_length_curve(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1315
static double arrow_length_tee(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1229
#define ARR_TYPE_TEE
Definition arrows.c:41
static double arrow_length(edge_t *e, uint32_t flag)
Definition arrows.c:253
static double arrow_length_crow(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1322
static pointf arrow_gen_type(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1092
boxf arrow_bb(pointf p, pointf u, double arrowsize)
Definition arrows.c:1107
#define ARR_TYPE_NONE
Definition arrows.c:38
#define BITS_PER_ARROW
Definition arrows.c:34
static double arrow_length_dot(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1308
#define ARR_TYPE_DOT
Definition arrows.c:44
size_t arrowStartClip(edge_t *e, pointf *ps, size_t startp, size_t endp, bezier *spl, uint32_t sflag)
Definition arrows.c:313
size_t arrowEndClip(edge_t *e, pointf *ps, size_t startp, size_t endp, bezier *spl, uint32_t eflag)
Definition arrows.c:285
static pointf arrow_type_diamond0(pointf p, pointf u, double penwidth, uint32_t flag, pointf *a)
Definition arrows.c:926
static const arrowname_t Arrownames[]
Definition arrows.c:92
static char * arrow_match_shape(char *name, uint32_t *flag)
Definition arrows.c:177
#define ARR_TYPE_GAP
Definition arrows.c:46
static const arrowname_t Arrowmods[]
Definition arrows.c:82
#define ARR_TYPE_CURVE
Definition arrows.c:45
#define ARR_TYPE_BOX
Definition arrows.c:42
static pointf arrow_type_dot(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:987
static const arrowname_t Arrowsynonyms[]
Definition arrows.c:75
void arrowOrthoClip(edge_t *e, pointf *ps, size_t startp, size_t endp, bezier *spl, uint32_t sflag, uint32_t eflag)
Definition arrows.c:350
static double arrow_length_generic(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1182
static double arrow_length_diamond(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1267
static pointf arrow_type_box(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:868
#define ARR_MOD_OPEN
Definition arrows.c:50
#define ARR_MOD_INV
Definition arrows.c:51
static void arrow_match_name(char *name, uint32_t *flag)
Definition arrows.c:195
static const size_t Arrowtypes_size
Definition arrows.c:157
static bool inside(inside_t *inside_context, pointf p)
Definition arrows.c:280
static char * arrow_match_name_frag(char *name, const arrowname_t *arrownames, uint32_t *flag)
Definition arrows.c:160
void arrow_flags(Agedge_t *e, uint32_t *sflag, uint32_t *eflag)
Definition arrows.c:218
#define NUMB_OF_ARROW_HEADS
Definition arrows.c:31
static pointf arrow_type_normal0(pointf p, pointf u, double penwidth, uint32_t flag, pointf *a)
Definition arrows.c:516
static const arrowdir_t Arrowdirs[]
Definition arrows.c:62
#define ARR_TYPE_CROW
Definition arrows.c:40
static pointf arrow_type_gap(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:791
#define ARR_TYPE_DIAMOND
Definition arrows.c:43
static pointf arrow_type_crow0(pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag, pointf *a)
Definition arrows.c:632
#define BITS_PER_ARROW_TYPE
Definition arrows.c:36
static pointf arrow_type_curve(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1031
static pointf arrow_type_tee(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:808
#define ARR_MOD_RIGHT
Definition arrows.c:53
void arrow_gen(GVJ_t *job, emit_state_t emit_state, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1145
static pointf arrow_type_crow(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:774
#define ARROW_LENGTH
Definition arrows.c:29
static double arrow_length_normal(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1190
static pointf arrow_type_normal(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:613
static double arrow_length_box(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1256
pointf Bezier(const pointf *V, double t, pointf *Left, pointf *Right)
Definition utils.c:175
double late_double(void *obj, attrsym_t *attr, double defaultValue, double minimum)
Definition utils.c:55
static Extype_t length(Exid_t *rhs, Exdisc_t *disc)
Definition compile.c:1615
static float dy
Definition draw.c:43
static float dx
Definition draw.c:42
static void gen(Excc_t *, Exnode_t *)
Definition excc.c:183
#define A(n, t)
Definition expr.h:76
struct pointf_s pointf
#define DIST(p, q)
Definition geom.h:56
geometric functions (e.g. on points and boxes)
static WUR pointf sub_pointf(pointf p, pointf q)
Definition geomprocs.h:96
static WUR pointf add_pointf(pointf p, pointf q)
Definition geomprocs.h:88
static WUR pointf scale(double c, pointf p)
Definition geomprocs.h:148
Agsym_t * E_dir
Definition globals.h:84
Agsym_t * E_arrowsz
Definition globals.h:85
Agsym_t * E_penwidth
Definition globals.h:92
static double len(glCompPoint p)
Definition glutils.c:138
node NULL
Definition grammar.y:181
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:457
#define ED_conc_opp_flag(e)
Definition types.h:579
#define agfindedgeattr(g, a)
Definition types.h:617
#define agtail(e)
Definition cgraph.h:977
#define agfindedge(g, t, h)
Definition types.h:609
#define aghead(e)
Definition cgraph.h:978
void agwarningf(const char *fmt,...)
Definition agerror.c:175
int agisdirected(Agraph_t *g)
Definition graph.c:178
Agraph_t * agraphof(void *obj)
Definition obj.c:187
emit_state_t
Definition gvcjob.h:173
void gvrender_beziercurve(GVJ_t *job, pointf *AF, size_t n, int filled)
Definition gvrender.c:579
void gvrender_set_style(GVJ_t *job, char **s)
Definition gvrender.c:481
void gvrender_polyline(GVJ_t *job, pointf *AF, size_t n)
Definition gvrender.c:596
void gvrender_polygon(GVJ_t *job, pointf *af, size_t n, int filled)
Definition gvrender.c:537
void gvrender_ellipse(GVJ_t *job, pointf *AF, int filled)
Definition gvrender.c:520
void gvrender_set_penwidth(GVJ_t *job, double penwidth)
Definition gvrender.c:798
static double penwidth[]
static gdPoint * points
#define B
Definition hierarchy.c:120
#define delta
Definition maze.c:136
void bezier_clip(inside_t *inside_context, bool(*insidefn)(inside_t *inside_context, pointf p), pointf *sp, bool left_inside)
Definition splines.c:109
static double overlap(double i0, double i1, double j0, double j1)
Definition routespl.c:606
#define alpha
Definition shapes.c:4058
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:640
char ** defaultlinestyle
Definition gvcint.h:149
obj_state_t * obj
Definition gvcjob.h:269
GVC_t * gvc
Definition gvcjob.h:263
uint32_t eflag
Definition arrows.c:59
uint32_t sflag
Definition arrows.c:58
char * dir
Definition arrows.c:57
uint32_t type
Definition arrows.c:72
char * name
Definition arrows.c:71
pointf(* gen)(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:120
double lenfact
Definition arrows.c:119
double(* len)(double lenfact, double arrowsize, double penwidth, uint32_t flag)
penwidth dependent length
Definition arrows.c:123
uint32_t type
Definition arrows.c:118
Definition types.h:89
pointf sp
Definition types.h:94
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
emit_state_t emit_state
Definition gvcjob.h:192
double x
Definition geom.h:29
double y
Definition geom.h:29
pointf points[3]
Definition arrows.c:446
pointf * p
Definition types.h:156
double * r
Definition types.h:157
struct inside_t::@56 a
Definition grammar.c:90