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