Graphviz 14.1.2~dev.20260118.1035
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 DIST2(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, elen2;
290
291 elen = arrow_length(e, eflag);
292 elen2 = elen * elen;
293 spl->eflag = eflag;
294 spl->ep = ps[endp + 3];
295 if (endp > startp && DIST2(ps[endp], ps[endp + 3]) < elen2) {
296 endp -= 3;
297 }
298 sp[3] = ps[endp];
299 sp[2] = ps[endp + 1];
300 sp[1] = ps[endp + 2];
301 sp[0] = spl->ep; /* ensure endpoint starts inside */
302
303 if (elen > 0) {
304 inside_context.a.p = &sp[0];
305 inside_context.a.r = &elen2;
306 bezier_clip(&inside_context, inside, sp, true);
307 }
308
309 ps[endp] = sp[3];
310 ps[endp + 1] = sp[2];
311 ps[endp + 2] = sp[1];
312 ps[endp + 3] = sp[0];
313 return endp;
314}
315
316size_t arrowStartClip(edge_t* e, pointf * ps, size_t startp,
317 size_t endp, bezier *spl, uint32_t sflag) {
318 inside_t inside_context;
319 pointf sp[4];
320 double slen, slen2;
321
322 slen = arrow_length(e, sflag);
323 slen2 = slen * slen;
324 spl->sflag = sflag;
325 spl->sp = ps[startp];
326 if (endp > startp && DIST2(ps[startp], ps[startp + 3]) < slen2) {
327 startp += 3;
328 }
329 sp[0] = ps[startp + 3];
330 sp[1] = ps[startp + 2];
331 sp[2] = ps[startp + 1];
332 sp[3] = spl->sp; /* ensure endpoint starts inside */
333
334 if (slen > 0) {
335 inside_context.a.p = &sp[3];
336 inside_context.a.r = &slen2;
337 bezier_clip(&inside_context, inside, sp, false);
338 }
339
340 ps[startp] = sp[3];
341 ps[startp + 1] = sp[2];
342 ps[startp + 2] = sp[1];
343 ps[startp + 3] = sp[0];
344 return startp;
345}
346
347/* arrowOrthoClip:
348 * For orthogonal routing, we know each Bézier of spl is a horizontal or vertical
349 * line segment. We need to guarantee the B-spline stays this way. At present, we shrink
350 * the arrows if necessary to fit the last segment at either end. Alternatively, we could
351 * maintain the arrow size by dropping the 3 points of spl, and adding a new spl encoding
352 * 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
353 * 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
354 * that the truncated spl clips to the arrow shape.
355 */
356void arrowOrthoClip(edge_t *e, pointf *ps, size_t startp, size_t endp,
357 bezier *spl, uint32_t sflag, uint32_t eflag) {
358 pointf p, q, r, s, t;
359 double d, tlen, hlen, maxd;
360
361 if (sflag && eflag && endp == startp) { /* handle special case of two arrows on a single segment */
362 p = ps[endp];
363 q = ps[endp+3];
364 tlen = arrow_length (e, sflag);
365 hlen = arrow_length (e, eflag);
366 d = DIST(p, q);
367 if (hlen + tlen >= d) {
368 hlen = tlen = d/3.0;
369 }
370 if (p.y == q.y) { // horizontal segment
371 s.y = t.y = p.y;
372 if (p.x < q.x) {
373 t.x = q.x - hlen;
374 s.x = p.x + tlen;
375 }
376 else {
377 t.x = q.x + hlen;
378 s.x = p.x - tlen;
379 }
380 }
381 else { // vertical segment
382 s.x = t.x = p.x;
383 if (p.y < q.y) {
384 t.y = q.y - hlen;
385 s.y = p.y + tlen;
386 }
387 else {
388 t.y = q.y + hlen;
389 s.y = p.y - tlen;
390 }
391 }
392 ps[endp] = ps[endp + 1] = s;
393 ps[endp + 2] = ps[endp + 3] = t;
394 spl->sflag = sflag, spl->sp = p;
395 spl->eflag = eflag, spl->ep = q;
396 return;
397 }
398 if (eflag) {
399 hlen = arrow_length(e, eflag);
400 p = ps[endp];
401 q = ps[endp+3];
402 d = DIST(p, q);
403 maxd = 0.9*d;
404 if (hlen >= maxd) { /* arrow too long */
405 hlen = maxd;
406 }
407 if (p.y == q.y) { // horizontal segment
408 r.y = p.y;
409 if (p.x < q.x) r.x = q.x - hlen;
410 else r.x = q.x + hlen;
411 }
412 else { // vertical segment
413 r.x = p.x;
414 if (p.y < q.y) r.y = q.y - hlen;
415 else r.y = q.y + hlen;
416 }
417 ps[endp + 1] = p;
418 ps[endp + 2] = ps[endp + 3] = r;
419 spl->eflag = eflag;
420 spl->ep = q;
421 }
422 if (sflag) {
423 tlen = arrow_length(e, sflag);
424 p = ps[startp];
425 q = ps[startp+3];
426 d = DIST(p, q);
427 maxd = 0.9*d;
428 if (tlen >= maxd) { /* arrow too long */
429 tlen = maxd;
430 }
431 if (p.y == q.y) { // horizontal segment
432 r.y = p.y;
433 if (p.x < q.x) r.x = p.x + tlen;
434 else r.x = p.x - tlen;
435 }
436 else { // vertical segment
437 r.x = p.x;
438 if (p.y < q.y) r.y = p.y + tlen;
439 else r.y = p.y - tlen;
440 }
441 ps[startp] = ps[startp + 1] = r;
442 ps[startp + 2] = q;
443 spl->sflag = sflag;
444 spl->sp = p;
445 }
446}
447
448// See https://www.w3.org/TR/SVG2/painting.html#TermLineJoinShape for the
449// terminology
450
451typedef struct {
453} triangle;
454
455static triangle
456miter_shape(pointf base_left, pointf P, pointf base_right, double penwidth) {
457 if ((base_left.x == P.x && base_left.y == P.y) ||
458 (base_right.x == P.x && base_right.y == P.y)) {
459 // the stroke shape is really a point so we just return this point without
460 // extending it with penwidth in any direction, which seems to be the way
461 // SVG renderers render this.
462 const triangle line_join_shape = {{P, P, P}};
463 return line_join_shape;
464 }
465 const pointf A[] = {base_left, P};
466 const double dxA = A[1].x - A[0].x;
467 const double dyA = A[1].y - A[0].y;
468 const double hypotA = hypot(dxA, dyA);
469 const double cosAlpha = dxA / hypotA;
470 const double sinAlpha = dyA / hypotA;
471 const double alpha = dyA > 0 ? acos(cosAlpha) : -acos(cosAlpha);
472
473 const pointf P1 = {P.x - penwidth / 2.0 * sinAlpha,
474 P.y + penwidth / 2.0 * cosAlpha};
475
476 const pointf B[] = {P, base_right};
477 const double dxB = B[1].x - B[0].x;
478 const double dyB = B[1].y - B[0].y;
479 const double hypotB = hypot(dxB, dyB);
480 const double cosBeta = dxB / hypotB;
481 const double beta = dyB > 0 ? acos(cosBeta) : -acos(cosBeta);
482
483 // angle between the A segment and the B segment in the reverse direction
484 const double beta_rev = beta - M_PI;
485 const double theta = beta_rev - alpha + (beta_rev - alpha <= -M_PI ? 2 * M_PI : 0);
486 assert(theta >= 0 && theta <= M_PI && "theta out of range");
487
488 // check if the miter limit is exceeded according to
489 // https://www.w3.org/TR/SVG2/painting.html#StrokeMiterlimitProperty
490 const double stroke_miterlimit = 4.0;
491 const double normalized_miter_length = 1.0 / sin(theta / 2.0);
492
493 const double sinBeta = dyB / hypotB;
494 const double sinBetaMinusPi = -sinBeta;
495 const double cosBetaMinusPi = -cosBeta;
496 const pointf P2 = {P.x + penwidth / 2.0 * sinBetaMinusPi,
497 P.y - penwidth / 2.0 * cosBetaMinusPi};
498
499 if (normalized_miter_length > stroke_miterlimit) {
500 // fall back to bevel
501
502 // the bevel is the triangle formed from the three points P, P1 and P2 so
503 // a good enough approximation of the miter point in this case is the
504 // crossing of P-P3 with P1-P2 which is the same as the midpoint between
505 // P1 and P2
506 const pointf Pbevel = {(P1.x + P2.x) / 2, (P1.y + P2.y) / 2};
507 const triangle line_join_shape = {{Pbevel, P1, P2}};
508
509 return line_join_shape;
510 }
511
512 // length between P1 and P3 (and between P2 and P3)
513 const double l = penwidth / 2.0 / tan(theta / 2.0);
514
515 const pointf P3 = {P1.x + l * cosAlpha,
516 P1.y + l * sinAlpha};
517 const triangle line_join_shape = {{P3, P1, P2}};
518
519 return line_join_shape;
520}
521
523 uint32_t flag, pointf *a) {
524 pointf q, v;
525 double arrowwidth;
526
527 arrowwidth = 0.35;
528 if (penwidth > 4)
529 arrowwidth *= penwidth / 4;
530
531 v.x = -u.y * arrowwidth;
532 v.y = u.x * arrowwidth;
533 q.x = p.x + u.x;
534 q.y = p.y + u.y;
535
536 pointf delta_base = {0, 0};
537
538 const pointf origin = {0, 0};
539 const pointf v_inv = {-v.x, -v.y};
540 const pointf normal_left = flag & ARR_MOD_RIGHT ? origin : v_inv;
541 const pointf normal_right = flag & ARR_MOD_LEFT ? origin : v;
542 const pointf base_left = flag & ARR_MOD_INV ? normal_right : normal_left;
543 const pointf base_right = flag & ARR_MOD_INV ? normal_left : normal_right;
544 const pointf normal_tip = {-u.x, -u.y};
545 const pointf inv_tip = u;
546 const pointf P = flag & ARR_MOD_INV ? inv_tip : normal_tip ;
547
548 pointf delta_tip = {0, 0};
549
550 if (u.x != 0 || u.y != 0) {
551 // phi = angle of arrow
552 const double cosPhi = P.x / hypot(P.x, P.y);
553 const double sinPhi = P.y / hypot(P.x, P.y);
554 const double phi = P.y > 0 ? acos(cosPhi) : -acos(cosPhi);
555
556 if (flag & ARR_MOD_LEFT) {
557 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
558 const pointf P1 = line_join_shape.points[1];
559 // alpha = angle of P -> P1
560 const double dx_P_P1 = P1.x - P.x;
561 const double dy_P_P1 = P1.y - P.y;
562 const double hypot_P_P1 = hypot(dx_P_P1, dy_P_P1);
563 const double cosAlpha = dx_P_P1 / hypot_P_P1;
564 const double alpha = dy_P_P1 > 0 ? acos(cosAlpha) : -acos(cosAlpha);
565 const double gamma = alpha - phi;
566 const double delta_tip_length = hypot_P_P1 * cos(gamma);
567 delta_tip = (pointf){delta_tip_length * cosPhi, delta_tip_length * sinPhi};
568 } else if (flag & ARR_MOD_RIGHT) {
569 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
570 const pointf P2 = line_join_shape.points[2];
571 // alpha = angle of P -> P2
572 const double dx_P_P2 = P2.x - P.x;
573 const double dy_P_P2 = P2.y - P.y;
574 const double hypot_P_P2 = hypot(dx_P_P2, dy_P_P2);
575 const double cosAlpha = dx_P_P2 / hypot_P_P2;
576 const double alpha = dy_P_P2 > 0 ? acos(cosAlpha) : -acos(cosAlpha);
577 const double gamma = alpha - phi;
578 const double delta_tip_length = hypot_P_P2 * cos(gamma);
579 delta_tip = (pointf){delta_tip_length * cosPhi, delta_tip_length * sinPhi};
580 } else {
581 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
582 const pointf P3 = line_join_shape.points[0];
583 delta_tip = sub_pointf(P3, P);
584 }
585 delta_base = (pointf) {penwidth / 2.0 * cosPhi, penwidth / 2.0 * sinPhi};
586 }
587
588 if (flag & ARR_MOD_INV) {
589 p.x += delta_base.x;
590 p.y += delta_base.y;
591 q.x += delta_base.x;
592 q.y += delta_base.y;
593 a[0] = a[4] = p;
594 a[1].x = p.x - v.x;
595 a[1].y = p.y - v.y;
596 a[2] = q;
597 a[3].x = p.x + v.x;
598 a[3].y = p.y + v.y;
599 q.x += delta_tip.x;
600 q.y += delta_tip.y;
601 } else {
602 p.x -= delta_tip.x;
603 p.y -= delta_tip.y;
604 q.x -= delta_tip.x;
605 q.y -= delta_tip.y;
606 a[0] = a[4] = q;
607 a[1].x = q.x - v.x;
608 a[1].y = q.y - v.y;
609 a[2] = p;
610 a[3].x = q.x + v.x;
611 a[3].y = q.y + v.y;
612 q.x -= delta_base.x;
613 q.y -= delta_base.y;
614 }
615
616 return q;
617}
618
620 double arrowsize, double penwidth,
621 uint32_t flag) {
622 (void)arrowsize;
623
624 pointf a[5];
625
626 pointf q = arrow_type_normal0(p, u, penwidth, flag, a);
627
628 if (flag & ARR_MOD_LEFT)
629 gvrender_polygon(job, a, 3, !(flag & ARR_MOD_OPEN));
630 else if (flag & ARR_MOD_RIGHT)
631 gvrender_polygon(job, &a[2], 3, !(flag & ARR_MOD_OPEN));
632 else
633 gvrender_polygon(job, &a[1], 3, !(flag & ARR_MOD_OPEN));
634
635 return q;
636}
637
638static pointf arrow_type_crow0(pointf p, pointf u, double arrowsize,
639 double penwidth, uint32_t flag, pointf *a) {
640 pointf m, q, v, w;
641 double arrowwidth, shaftwidth;
642
643 arrowwidth = 0.45;
644 if (penwidth > 4 * arrowsize && (flag & ARR_MOD_INV))
645 arrowwidth *= penwidth / (4 * arrowsize);
646
647 shaftwidth = 0;
648 if (penwidth > 1 && (flag & ARR_MOD_INV))
649 shaftwidth = 0.05 * (penwidth - 1) / arrowsize; /* arrowsize to cancel the arrowsize term already in u */
650
651 v.x = -u.y * arrowwidth;
652 v.y = u.x * arrowwidth;
653 w.x = -u.y * shaftwidth;
654 w.y = u.x * shaftwidth;
655 q.x = p.x + u.x;
656 q.y = p.y + u.y;
657 m.x = p.x + u.x * 0.5;
658 m.y = p.y + u.y * 0.5;
659
660 pointf delta_base = {0, 0};
661
662 const pointf origin = {0, 0};
663 const pointf v_inv = {-v.x, -v.y};
664 const pointf normal_left = flag & ARR_MOD_RIGHT ? origin : v;
665 const pointf normal_right = flag & ARR_MOD_LEFT ? origin : v_inv;
666 const pointf base_left = flag & ARR_MOD_INV ? normal_right : normal_left;
667 const pointf base_right = flag & ARR_MOD_INV ? normal_left : normal_right;
668 const pointf normal_tip = u;
669 const pointf inv_tip = {-u.x, -u.y};
670 const pointf P = flag & ARR_MOD_INV ? inv_tip : normal_tip ;
671
672 pointf delta_tip = {0, 0};
673
674 if (u.x != 0 || u.y != 0) {
675 // phi = angle of arrow
676 const double cosPhi = P.x / hypot(P.x, P.y);
677 const double sinPhi = P.y / hypot(P.x, P.y);
678 const double phi = P.y > 0 ? acos(cosPhi) : -acos(cosPhi);
679
680 if ((flag & ARR_MOD_LEFT && flag & ARR_MOD_INV) || (flag & ARR_MOD_RIGHT && !(flag & ARR_MOD_INV))) {
681 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
682 const pointf P2 = line_join_shape.points[2];
683 // alpha = angle of P -> P2
684 const double dx_P_P2 = P2.x - P.x;
685 const double dy_P_P2 = P2.y - P.y;
686 const double hypot_P_P2 = hypot(dx_P_P2, dy_P_P2);
687 const double cosAlpha = dx_P_P2 / hypot_P_P2;
688 const double alpha = dy_P_P2 > 0 ? acos(cosAlpha) : -acos(cosAlpha);
689 const double gamma = alpha - phi;
690 const double delta_tip_length = hypot_P_P2 * cos(gamma);
691 delta_tip = (pointf){delta_tip_length * cosPhi, delta_tip_length * sinPhi};
692 } else if ((flag & ARR_MOD_LEFT && !(flag & ARR_MOD_INV)) || (flag & ARR_MOD_RIGHT && flag & ARR_MOD_INV)) {
693 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
694 const pointf P1 = line_join_shape.points[1];
695 // alpha = angle of P -> P1
696 const double dx_P_P1 = P1.x - P.x;
697 const double dy_P_P1 = P1.y - P.y;
698 const double hypot_P_P1 = hypot(dx_P_P1, dy_P_P1);
699 const double cosAlpha = dx_P_P1 / hypot_P_P1;
700 const double alpha = dy_P_P1 > 0 ? acos(cosAlpha) : -acos(cosAlpha);
701 const double gamma = alpha - phi;
702 const double delta_tip_length = hypot_P_P1 * cos(gamma);
703 delta_tip = (pointf){delta_tip_length * cosPhi, delta_tip_length * sinPhi};
704 } else {
705 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
706 const pointf P3 = line_join_shape.points[0];
707 delta_tip = sub_pointf(P3, P);
708 }
709 if (flag & ARR_MOD_INV) { /* vee */
710 delta_base = (pointf) {penwidth / 2.0 * cosPhi, penwidth / 2.0 * sinPhi};
711 } else {
712 // the left and right "toes" of the crow extend in the direction of
713 // the arrow by the same amount. Their shape is not affected by the
714 // 'l' or 'r' modifiers. Here we use the right "toe" to calculate
715 // the extension. This is ok even if it's not actually rendered when
716 // the 'l' modifier is used.
717 const pointf toe_base_left = add_pointf(sub_pointf(m, q), w);
718 const pointf toe_base_right = origin;
719 const pointf toe_P = sub_pointf(v, u);
720 const triangle toe_line_join_shape = miter_shape(toe_base_left, toe_P, toe_base_right, penwidth);
721 const pointf P1 = toe_line_join_shape.points[1];
722 // alpha = angle of toe_P -> P1
723 const double dx_P_P1 = P1.x - toe_P.x;
724 const double dy_P_P1 = P1.y - toe_P.y;
725 const double hypot_P_P1 = hypot(dx_P_P1, dy_P_P1);
726 const double cosAlpha = dx_P_P1 / hypot_P_P1;
727 const double alpha = dy_P_P1 > 0 ? acos(cosAlpha) : -acos(cosAlpha);
728 const double gamma = alpha - phi;
729 const double delta_tip_length = -hypot_P_P1 * cos(gamma);
730 delta_base = (pointf){delta_tip_length * cosPhi, delta_tip_length * sinPhi};
731 }
732 }
733
734 if (flag & ARR_MOD_INV) { /* vee */
735 p.x -= delta_tip.x;
736 p.y -= delta_tip.y;
737 q.x -= delta_tip.x;
738 q.y -= delta_tip.y;
739 a[0] = a[8] = p;
740 a[1].x = q.x - v.x;
741 a[1].y = q.y - v.y;
742 a[2].x = m.x - w.x;
743 a[2].y = m.y - w.y;
744 a[3].x = q.x - w.x;
745 a[3].y = q.y - w.y;
746 a[4] = q;
747 a[5].x = q.x + w.x;
748 a[5].y = q.y + w.y;
749 a[6].x = m.x + w.x;
750 a[6].y = m.y + w.y;
751 a[7].x = q.x + v.x;
752 a[7].y = q.y + v.y;
753 q.x -= delta_base.x;
754 q.y -= delta_base.y;
755 } else { /* crow */
756 p.x += delta_base.x;
757 p.y += delta_base.y;
758 q.x += delta_base.x;
759 q.y += delta_base.y;
760 a[0] = a[8] = q;
761 a[1].x = p.x - v.x;
762 a[1].y = p.y - v.y;
763 a[2].x = m.x - w.x;
764 a[2].y = m.y - w.y;
765 a[3].x = p.x + delta_base.x;
766 a[3].y = p.y + delta_base.y;
767 a[4] = add_pointf(p, delta_base);
768 a[5].x = p.x + delta_base.x;
769 a[5].y = p.y + delta_base.y;
770 a[6].x = m.x + w.x;
771 a[6].y = m.y + w.y;
772 a[7].x = p.x + v.x;
773 a[7].y = p.y + v.y;
774 q.x += delta_tip.x;
775 q.y += delta_tip.y;
776 }
777 return q;
778}
779
780static pointf arrow_type_crow(GVJ_t *job, pointf p, pointf u, double arrowsize,
781 double penwidth, uint32_t flag) {
782 (void)arrowsize;
783
784 pointf a[9];
785
786 pointf q = arrow_type_crow0(p, u, arrowsize, penwidth, flag, a);
787 if (flag & ARR_MOD_LEFT)
788 gvrender_polygon(job, a, 5, 1);
789 else if (flag & ARR_MOD_RIGHT)
790 gvrender_polygon(job, &a[4], 5, 1);
791 else
792 gvrender_polygon(job, a, 8, 1);
793
794 return q;
795}
796
797static pointf arrow_type_gap(GVJ_t *job, pointf p, pointf u, double arrowsize,
798 double penwidth, uint32_t flag) {
799 (void)arrowsize;
800 (void)penwidth;
801 (void)flag;
802
803 pointf q, a[2];
804
805 q.x = p.x + u.x;
806 q.y = p.y + u.y;
807 a[0] = p;
808 a[1] = q;
809 gvrender_polyline(job, a, 2);
810
811 return q;
812}
813
814static pointf arrow_type_tee(GVJ_t *job, pointf p, pointf u, double arrowsize,
815 double penwidth, uint32_t flag) {
816 (void)arrowsize;
817
818 pointf m, n, q, v, a[4];
819
820 v.x = -u.y;
821 v.y = u.x;
822 q.x = p.x + u.x;
823 q.y = p.y + u.y;
824 m.x = p.x + u.x * 0.2;
825 m.y = p.y + u.y * 0.2;
826 n.x = p.x + u.x * 0.6;
827 n.y = p.y + u.y * 0.6;
828
829 const double length = hypot(u.x, u.y);
830 const double polygon_extend_over_polyline = penwidth / 2 - 0.2 * length;
831 if (length > 0 && polygon_extend_over_polyline > 0) {
832 // the polygon part of the 'tee' arrow will visually overlap the
833 // 'polyline' part so we need to move the whole arrow in order not to
834 // overlap the node
835 const pointf P = {-u.x, -u.y};
836 // phi = angle of arrow
837 const double cosPhi = P.x / hypot(P.x, P.y);
838 const double sinPhi = P.y / hypot(P.x, P.y);
839 const pointf delta = {polygon_extend_over_polyline * cosPhi, polygon_extend_over_polyline * sinPhi};
840
841 // move the arrow backwards to not visually overlap the node
842 p = sub_pointf(p, delta);
843 m = sub_pointf(m, delta);
844 n = sub_pointf(n, delta);
845 q = sub_pointf(q, delta);
846 }
847
848 a[0].x = m.x + v.x;
849 a[0].y = m.y + v.y;
850 a[1].x = m.x - v.x;
851 a[1].y = m.y - v.y;
852 a[2].x = n.x - v.x;
853 a[2].y = n.y - v.y;
854 a[3].x = n.x + v.x;
855 a[3].y = n.y + v.y;
856 if (flag & ARR_MOD_LEFT) {
857 a[0] = m;
858 a[3] = n;
859 } else if (flag & ARR_MOD_RIGHT) {
860 a[1] = m;
861 a[2] = n;
862 }
863 gvrender_polygon(job, a, 4, 1);
864 a[0] = p;
865 a[1] = q;
866 gvrender_polyline(job, a, 2);
867
868 // A polyline doesn't extend visually beyond its starting point, so we
869 // return the starting point as it is, without taking penwidth into account
870
871 return q;
872}
873
874static pointf arrow_type_box(GVJ_t *job, pointf p, pointf u, double arrowsize,
875 double penwidth, uint32_t flag) {
876 (void)arrowsize;
877 (void)penwidth;
878
879 pointf m, q, v, a[4];
880
881 v.x = -u.y * 0.4;
882 v.y = u.x * 0.4;
883 m.x = p.x + u.x * 0.8;
884 m.y = p.y + u.y * 0.8;
885 q.x = p.x + u.x;
886 q.y = p.y + u.y;
887
888 pointf delta = {0, 0};
889
890 if (u.x != 0 || u.y != 0) {
891 const pointf P = {-u.x, -u.y};
892 // phi = angle of arrow
893 const double cosPhi = P.x / hypot(P.x, P.y);
894 const double sinPhi = P.y / hypot(P.x, P.y);
895 delta = (pointf) {penwidth / 2.0 * cosPhi, penwidth / 2.0 * sinPhi};
896 }
897
898 // move the arrow backwards to not visually overlap the node
899 p.x -= delta.x;
900 p.y -= delta.y;
901 m.x -= delta.x;
902 m.y -= delta.y;
903 q.x -= delta.x;
904 q.y -= delta.y;
905
906 a[0].x = p.x + v.x;
907 a[0].y = p.y + v.y;
908 a[1].x = p.x - v.x;
909 a[1].y = p.y - v.y;
910 a[2].x = m.x - v.x;
911 a[2].y = m.y - v.y;
912 a[3].x = m.x + v.x;
913 a[3].y = m.y + v.y;
914 if (flag & ARR_MOD_LEFT) {
915 a[0] = p;
916 a[3] = m;
917 } else if (flag & ARR_MOD_RIGHT) {
918 a[1] = p;
919 a[2] = m;
920 }
921 gvrender_polygon(job, a, 4, !(flag & ARR_MOD_OPEN));
922 a[0] = m;
923 a[1] = q;
924 gvrender_polyline(job, a, 2);
925
926 // A polyline doesn't extend visually beyond its starting point, so we
927 // return the starting point as it is, without taking penwidth into account
928
929 return q;
930}
931
933 uint32_t flag, pointf *a) {
934 pointf q, r, v;
935
936 v.x = -u.y / 3.;
937 v.y = u.x / 3.;
938 r.x = p.x + u.x / 2.;
939 r.y = p.y + u.y / 2.;
940 q.x = p.x + u.x;
941 q.y = p.y + u.y;
942
943 const pointf origin = {0, 0};
944 const pointf unmod_left = sub_pointf(scale(-0.5, u), v);
945 const pointf unmod_right = add_pointf(scale(-0.5, u), v);
946 const pointf base_left = flag & ARR_MOD_RIGHT ? origin : unmod_left;
947 const pointf base_right = flag & ARR_MOD_LEFT ? origin : unmod_right;
948 const pointf tip = scale(-1, u);
949 const pointf P = tip;
950
951 const triangle line_join_shape = miter_shape(base_left, P, base_right, penwidth);
952 const pointf P3 = line_join_shape.points[0];
953
954 const pointf delta = sub_pointf(P3, P);
955
956 // move the arrow backwards to not visually overlap the node
957 p = sub_pointf(p, delta);
958 r = sub_pointf(r, delta);
959 q = sub_pointf(q, delta);
960
961 a[0] = a[4] = q;
962 a[1].x = r.x + v.x;
963 a[1].y = r.y + v.y;
964 a[2] = p;
965 a[3].x = r.x - v.x;
966 a[3].y = r.y - v.y;
967
968 // return the visual starting point of the arrow outline
969 q = sub_pointf(q, delta);
970
971 return q;
972}
973
975 double arrowsize, double penwidth,
976 uint32_t flag) {
977 (void)arrowsize;
978
979 pointf a[5];
980
981 pointf q = arrow_type_diamond0(p, u, penwidth, flag, a);
982
983 if (flag & ARR_MOD_LEFT)
984 gvrender_polygon(job, &a[2], 3, !(flag & ARR_MOD_OPEN));
985 else if (flag & ARR_MOD_RIGHT)
986 gvrender_polygon(job, a, 3, !(flag & ARR_MOD_OPEN));
987 else
988 gvrender_polygon(job, a, 4, !(flag & ARR_MOD_OPEN));
989
990 return q;
991}
992
993static pointf arrow_type_dot(GVJ_t *job, pointf p, pointf u, double arrowsize,
994 double penwidth, uint32_t flag) {
995 (void)arrowsize;
996 (void)penwidth;
997
998 double r;
999 pointf AF[2];
1000
1001 r = hypot(u.x, u.y) / 2.;
1002
1003 pointf delta = {0, 0};
1004
1005 if (u.x != 0 || u.y != 0) {
1006 const pointf P = {-u.x, -u.y};
1007 // phi = angle of arrow
1008 const double cosPhi = P.x / hypot(P.x, P.y);
1009 const double sinPhi = P.y / hypot(P.x, P.y);
1010 delta = (pointf) {penwidth / 2.0 * cosPhi, penwidth / 2.0 * sinPhi};
1011
1012 // move the arrow backwards to not visually overlap the node
1013 p.x -= delta.x;
1014 p.y -= delta.y;
1015 }
1016
1017
1018 AF[0].x = p.x + u.x / 2. - r;
1019 AF[0].y = p.y + u.y / 2. - r;
1020 AF[1].x = p.x + u.x / 2. + r;
1021 AF[1].y = p.y + u.y / 2. + r;
1022 gvrender_ellipse(job, AF, !(flag & ARR_MOD_OPEN));
1023
1024 pointf q = {p.x + u.x, p.y + u.y};
1025
1026 // return the visual starting point of the arrow outline
1027 q.x -= delta.x;
1028 q.y -= delta.y;
1029
1030 return q;
1031}
1032
1033
1034/* Draw a concave semicircle using a single cubic Bézier curve that touches p at its midpoint.
1035 * See http://digerati-illuminatus.blogspot.com.au/2008/05/approximating-semicircle-with-cubic.html for details.
1036 */
1037static pointf arrow_type_curve(GVJ_t *job, pointf p, pointf u, double arrowsize,
1038 double penwidth, uint32_t flag) {
1039 (void)arrowsize;
1040
1041 double arrowwidth = penwidth > 4 ? 0.5 * penwidth / 4 : 0.5;
1042 pointf q, v, w;
1043 pointf AF[4], a[2];
1044
1045 a[0] = p;
1046 if (!(flag & ARR_MOD_INV) && (u.x != 0 || u.y != 0)) {
1047 const pointf P = {-u.x, -u.y};
1048 // phi = angle of arrow
1049 const double cosPhi = P.x / hypot(P.x, P.y);
1050 const double sinPhi = P.y / hypot(P.x, P.y);
1051 const pointf delta = {penwidth / 2.0 * cosPhi, penwidth / 2.0 * sinPhi};
1052
1053 // move the arrow backwards to not visually overlap the node
1054 p.x -= delta.x;
1055 p.y -= delta.y;
1056 }
1057
1058 q.x = p.x + u.x;
1059 q.y = p.y + u.y;
1060 v.x = -u.y * arrowwidth;
1061 v.y = u.x * arrowwidth;
1062 w.x = v.y; // same direction as u, same magnitude as v.
1063 w.y = -v.x;
1064 a[1] = q;
1065
1066 AF[0].x = p.x + v.x + w.x;
1067 AF[0].y = p.y + v.y + w.y;
1068
1069 AF[3].x = p.x - v.x + w.x;
1070 AF[3].y = p.y - v.y + w.y;
1071
1072 if (flag & ARR_MOD_INV) { /* ----(-| */
1073 AF[1].x = p.x + 0.95 * v.x + w.x + w.x * 4.0 / 3.0;
1074 AF[1].y = AF[0].y + w.y * 4.0 / 3.0;
1075
1076 AF[2].x = p.x - 0.95 * v.x + w.x + w.x * 4.0 / 3.0;
1077 AF[2].y = AF[3].y + w.y * 4.0 / 3.0;
1078 }
1079 else { /* ----)-| */
1080 AF[1].x = p.x + 0.95 * v.x + w.x - w.x * 4.0 / 3.0;
1081 AF[1].y = AF[0].y - w.y * 4.0 / 3.0;
1082
1083 AF[2].x = p.x - 0.95 * v.x + w.x - w.x * 4.0 / 3.0;
1084 AF[2].y = AF[3].y - w.y * 4.0 / 3.0;
1085 }
1086
1087 gvrender_polyline(job, a, 2);
1088 if (flag & ARR_MOD_LEFT)
1089 Bezier(AF, 0.5, NULL, AF);
1090 else if (flag & ARR_MOD_RIGHT)
1091 Bezier(AF, 0.5, AF, NULL);
1092 gvrender_beziercurve(job, AF, sizeof(AF) / sizeof(pointf), 0);
1093
1094 return q;
1095}
1096
1097
1098static pointf arrow_gen_type(GVJ_t *job, pointf p, pointf u, double arrowsize,
1099 double penwidth, uint32_t flag) {
1100 uint32_t f = flag & ((1 << BITS_PER_ARROW_TYPE) - 1);
1101 for (size_t i = 0; i < Arrowtypes_size; ++i) {
1102 const arrowtype_t *arrowtype = &Arrowtypes[i];
1103 if (f == arrowtype->type) {
1104 u.x *= arrowtype->lenfact * arrowsize;
1105 u.y *= arrowtype->lenfact * arrowsize;
1106 p = arrowtype->gen(job, p, u, arrowsize, penwidth, flag);
1107 break;
1108 }
1109 }
1110 return p;
1111}
1112
1113boxf arrow_bb(pointf p, pointf u, double arrowsize)
1114{
1115 double s;
1116 boxf bb;
1117 double ax,ay,bx,by,cx,cy,dx,dy;
1118 double ux2, uy2;
1119
1120 /* generate arrowhead vector */
1121 u.x -= p.x;
1122 u.y -= p.y;
1123 /* the EPSILONs are to keep this stable as length of u approaches 0.0 */
1124 s = ARROW_LENGTH * arrowsize / (hypot(u.x, u.y) + EPSILON);
1125 u.x += (u.x >= 0.0) ? EPSILON : -EPSILON;
1126 u.y += (u.y >= 0.0) ? EPSILON : -EPSILON;
1127 u.x *= s;
1128 u.y *= s;
1129
1130 /* compute all 4 corners of rotated arrowhead bounding box */
1131 ux2 = u.x / 2.;
1132 uy2 = u.y / 2.;
1133 ax = p.x - uy2;
1134 ay = p.y - ux2;
1135 bx = p.x + uy2;
1136 by = p.y + ux2;
1137 cx = ax + u.x;
1138 cy = ay + u.y;
1139 dx = bx + u.x;
1140 dy = by + u.y;
1141
1142 /* compute a right bb */
1143 bb.UR.x = fmax(ax, fmax(bx, fmax(cx, dx)));
1144 bb.UR.y = fmax(ay, fmax(by, fmax(cy, dy)));
1145 bb.LL.x = fmin(ax, fmin(bx, fmin(cx, dx)));
1146 bb.LL.y = fmin(ay, fmin(by, fmin(cy, dy)));
1147
1148 return bb;
1149}
1150
1151void arrow_gen(GVJ_t *job, emit_state_t emit_state, pointf p, pointf u,
1152 double arrowsize, double penwidth, uint32_t flag) {
1153 obj_state_t *obj = job->obj;
1154 double s;
1155 int i;
1156 emit_state_t old_emit_state;
1157
1158 old_emit_state = obj->emit_state;
1159 obj->emit_state = emit_state;
1160
1161 /* Dotted and dashed styles on the arrowhead are ugly (dds) */
1162 /* linewidth needs to be reset */
1164
1166
1167 /* generate arrowhead vector */
1168 u.x -= p.x;
1169 u.y -= p.y;
1170 /* the EPSILONs are to keep this stable as length of u approaches 0.0 */
1171 s = ARROW_LENGTH / (hypot(u.x, u.y) + EPSILON);
1172 u.x += (u.x >= 0.0) ? EPSILON : -EPSILON;
1173 u.y += (u.y >= 0.0) ? EPSILON : -EPSILON;
1174 u.x *= s;
1175 u.y *= s;
1176
1177 /* the first arrow head - closest to node */
1178 for (i = 0; i < NUMB_OF_ARROW_HEADS; i++) {
1179 uint32_t f = (flag >> (i * BITS_PER_ARROW)) & ((1 << BITS_PER_ARROW) - 1);
1180 if (f == ARR_TYPE_NONE)
1181 break;
1182 p = arrow_gen_type(job, p, u, arrowsize, penwidth, f);
1183 }
1184
1185 obj->emit_state = old_emit_state;
1186}
1187
1188static double arrow_length_generic(double lenfact, double arrowsize,
1189 double penwidth, uint32_t flag) {
1190 (void)penwidth;
1191 (void)flag;
1192
1193 return lenfact * arrowsize * ARROW_LENGTH;
1194}
1195
1196static double arrow_length_normal(double lenfact, double arrowsize,
1197 double penwidth, uint32_t flag) {
1198 pointf a[5];
1199 // set arrow end point at origin
1200 const pointf p = {0, 0};
1201 // generate an arrowhead vector along x-axis
1202 const pointf u = {lenfact * arrowsize * ARROW_LENGTH, 0};
1203
1204 // arrow start point
1205 pointf q = arrow_type_normal0(p, u, penwidth, flag, a);
1206
1207 const pointf base1 = a[1];
1208 const pointf base2 = a[3];
1209 const pointf tip = a[2];
1210 const double full_length = q.x;
1211 assert(full_length > 0 && "non-positive full length");
1212 const double nominal_length = fabs(base1.x - tip.x);
1213 const double nominal_base_width = base2.y - base1.y;
1214 assert(nominal_base_width > 0 && "non-positive nominal base width");
1215 // the full base width is proportionally scaled with the length
1216 const double full_base_width =
1217 nominal_base_width * full_length / nominal_length;
1218 assert(full_base_width > 0 && "non-positive full base width");
1219
1220 // we want a small overlap between the edge path (stem) and the arrow to avoid
1221 // gaps between them in case the arrow has a corner towards the edge path
1222 const double overlap_at_base = penwidth / 2;
1223 // overlap the tip to a point where its width is equal to the penwidth.
1224 const double length_where_width_is_penwidth =
1225 full_length * penwidth / full_base_width;
1226 const double overlap_at_tip = length_where_width_is_penwidth;
1227
1228 const double overlap = flag & ARR_MOD_INV ? overlap_at_tip : overlap_at_base;
1229
1230 // arrow length is the x value of the start point since the arrow points along
1231 // the positive x axis and ends at origin
1232 return full_length - overlap;
1233}
1234
1235static double arrow_length_tee(double lenfact, double arrowsize,
1236 double penwidth, uint32_t flag) {
1237 (void)flag;
1238
1239 // The `tee` arrow shape normally begins and ends with a polyline which
1240 // doesn't extend visually beyond its starting point, so we only have to
1241 // take penwidth into account if the polygon part visually extends the
1242 // polyline part at the start or end points.
1243
1244 const double nominal_length = lenfact * arrowsize * ARROW_LENGTH;
1245 double length = nominal_length;
1246
1247 // see the 'arrow_type_tee' function for the magical constants used below
1248
1249 const double polygon_extend_over_polyline_at_start = penwidth / 2 - (1 - 0.6) * nominal_length;
1250 if (polygon_extend_over_polyline_at_start > 0) {
1251 length += polygon_extend_over_polyline_at_start;
1252 }
1253
1254 const double polygon_extend_over_polyline_at_end = penwidth / 2 - 0.2 * nominal_length;
1255 if (polygon_extend_over_polyline_at_start > 0) {
1256 length += polygon_extend_over_polyline_at_end;
1257 }
1258
1259 return length;
1260}
1261
1262static double arrow_length_box(double lenfact, double arrowsize,
1263 double penwidth, uint32_t flag) {
1264 (void)flag;
1265
1266 // The `box` arrow shape begins with a polyline which doesn't extend
1267 // visually beyond its starting point, so we only have to take penwidth
1268 // into account at the end point.
1269
1270 return lenfact * arrowsize * ARROW_LENGTH + penwidth / 2;
1271}
1272
1273static double arrow_length_diamond(double lenfact, double arrowsize,
1274 double penwidth, uint32_t flag) {
1275 pointf a[5];
1276 // set arrow end point at origin
1277 const pointf p = {0, 0};
1278 // generate an arrowhead vector along x-axis
1279 const pointf u = {lenfact * arrowsize * ARROW_LENGTH, 0};
1280
1281 // arrow start point
1282 pointf q = arrow_type_diamond0(p, u, penwidth, flag, a);
1283
1284 // calculate overlap using a triangle with its base at the left and right
1285 // corners of the diamond and its tip at the end point
1286 const pointf base1 = a[3];
1287 const pointf base2 = a[1];
1288 const pointf tip = a[2];
1289 const double full_length = q.x / 2;
1290 assert(full_length > 0 && "non-positive full length");
1291 const double nominal_length = fabs(base1.x - tip.x);
1292 const double nominal_base_width = base2.y - base1.y;
1293 assert(nominal_base_width > 0 && "non-positive nominal base width");
1294 // the full base width is proportionally scaled with the length
1295 const double full_base_width =
1296 nominal_base_width * full_length / nominal_length;
1297 assert(full_base_width > 0 && "non-positive full base width");
1298
1299 // we want a small overlap between the edge path (stem) and the arrow to avoid
1300 // gaps between them in case the arrow has a corner towards the edge path
1301
1302 // overlap the tip to a point where its width is equal to the penwidth
1303 const double length_where_width_is_penwidth =
1304 full_length * penwidth / full_base_width;
1305 const double overlap_at_tip = length_where_width_is_penwidth;
1306
1307 const double overlap = overlap_at_tip;
1308
1309 // arrow length is the x value of the start point since the arrow points along
1310 // the positive x axis and ends at origin
1311 return 2 * full_length - overlap;
1312}
1313
1314static double arrow_length_dot(double lenfact, double arrowsize,
1315 double penwidth, uint32_t flag) {
1316 (void)flag;
1317
1318 return lenfact * arrowsize * ARROW_LENGTH + penwidth;
1319}
1320
1321static double arrow_length_curve(double lenfact, double arrowsize,
1322 double penwidth, uint32_t flag) {
1323 (void)flag;
1324
1325 return lenfact * arrowsize * ARROW_LENGTH + penwidth / 2;
1326}
1327
1328static double arrow_length_crow(double lenfact, double arrowsize,
1329 double penwidth, uint32_t flag) {
1330 pointf a[9];
1331 // set arrow end point at origin
1332 const pointf p = {0, 0};
1333 // generate an arrowhead vector along x-axis
1334 const pointf u = {lenfact * arrowsize * ARROW_LENGTH, 0};
1335
1336 // arrow start point
1337 pointf q = arrow_type_crow0(p, u, arrowsize, penwidth, flag, a);
1338
1339 const pointf base1 = a[1];
1340 const pointf base2 = a[7];
1341 const pointf tip = a[0];
1342 const pointf shaft1 = a[3];
1343 const double full_length = q.x;
1344 assert(full_length > 0 && "non-positive full length");
1345 const double full_length_without_shaft = full_length - (base1.x - shaft1.x);
1346 assert(full_length_without_shaft > 0 && "non-positive full length without shaft");
1347 const double nominal_length = fabs(base1.x - tip.x);
1348 const double nominal_base_width = base2.y - base1.y;
1349 assert(nominal_base_width > 0 && "non-positive nominal base width");
1350 // the full base width is proportionally scaled with the length
1351 const double full_base_width =
1352 nominal_base_width * full_length_without_shaft / nominal_length;
1353 assert(full_base_width > 0 && "non-positive full base width");
1354
1355 // we want a small overlap between the edge path (stem) and the arrow to avoid
1356 // gaps between them in case the arrow has a corner towards the edge path
1357 const double overlap_at_base = penwidth / 2;
1358 // overlap the tip to a point where its width is equal to the penwidth.
1359 const double length_where_width_is_penwidth =
1360 full_length_without_shaft * penwidth / full_base_width;
1361 const double overlap_at_tip = length_where_width_is_penwidth;
1362
1363 const double overlap = flag & ARR_MOD_INV ? overlap_at_base : overlap_at_tip;
1364 // arrow length is the x value of the start point since the arrow points along
1365 // the positive x axis and ends at origin
1366 return full_length - overlap;
1367}
#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:974
static triangle miter_shape(pointf base_left, pointf P, pointf base_right, double penwidth)
Definition arrows.c:456
#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:1321
static double arrow_length_tee(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1235
#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:1328
static pointf arrow_gen_type(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1098
boxf arrow_bb(pointf p, pointf u, double arrowsize)
Definition arrows.c:1113
#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:1314
#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:316
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:932
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:993
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:356
static double arrow_length_generic(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1188
static double arrow_length_diamond(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1273
static pointf arrow_type_box(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:874
#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:522
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:797
#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:638
#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:1037
static pointf arrow_type_tee(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:814
#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:1151
static pointf arrow_type_crow(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:780
#define ARROW_LENGTH
Definition arrows.c:29
static double arrow_length_normal(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1196
static pointf arrow_type_normal(GVJ_t *job, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:619
static double arrow_length_box(double lenfact, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1262
pointf Bezier(const pointf *V, double t, pointf *Left, pointf *Right)
Definition utils.c:174
double late_double(void *obj, attrsym_t *attr, double defaultValue, double minimum)
Definition utils.c:54
static Extype_t length(Exid_t *rhs, Exdisc_t *disc)
Definition compile.c:1606
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 DIST2(p, q)
Definition geom.h:55
#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:460
#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
static int * ps
Definition lu.c:53
#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:452
pointf * p
Definition types.h:156
double * r
Definition types.h:157
struct inside_t::@56 a
Definition grammar.c:90