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