| 1 | /* $Id$ $Revision$ */ |
| 2 | /* vim:set shiftwidth=4 ts=8: */ |
| 3 | |
| 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 | * http://www.eclipse.org/legal/epl-v10.html |
| 10 | * |
| 11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
| 12 | *************************************************************************/ |
| 13 | |
| 14 | |
| 15 | #include "render.h" |
| 16 | |
| 17 | #define EPSILON .0001 |
| 18 | |
| 19 | /* standard arrow length in points */ |
| 20 | #define ARROW_LENGTH 10. |
| 21 | |
| 22 | #define NUMB_OF_ARROW_HEADS 4 |
| 23 | /* each arrow in 8 bits. Room for NUMB_OF_ARROW_HEADS arrows in 32 bit int. */ |
| 24 | |
| 25 | #define BITS_PER_ARROW 8 |
| 26 | |
| 27 | #define BITS_PER_ARROW_TYPE 4 |
| 28 | /* arrow types (in BITS_PER_ARROW_TYPE bits) */ |
| 29 | #define ARR_TYPE_NONE (ARR_NONE) |
| 30 | #define ARR_TYPE_NORM 1 |
| 31 | #define ARR_TYPE_CROW 2 |
| 32 | #define ARR_TYPE_TEE 3 |
| 33 | #define ARR_TYPE_BOX 4 |
| 34 | #define ARR_TYPE_DIAMOND 5 |
| 35 | #define ARR_TYPE_DOT 6 |
| 36 | #define ARR_TYPE_CURVE 7 |
| 37 | #define ARR_TYPE_GAP 8 |
| 38 | /* Spare: 9-15 */ |
| 39 | |
| 40 | /* arrow mods (in (BITS_PER_ARROW - BITS_PER_ARROW_TYPE) bits) */ |
| 41 | #define ARR_MOD_OPEN (1<<(BITS_PER_ARROW_TYPE+0)) |
| 42 | #define ARR_MOD_INV (1<<(BITS_PER_ARROW_TYPE+1)) |
| 43 | #define ARR_MOD_LEFT (1<<(BITS_PER_ARROW_TYPE+2)) |
| 44 | #define ARR_MOD_RIGHT (1<<(BITS_PER_ARROW_TYPE+3)) |
| 45 | /* No spares */ |
| 46 | |
| 47 | typedef struct arrowdir_t { |
| 48 | char *dir; |
| 49 | int sflag; |
| 50 | int eflag; |
| 51 | } arrowdir_t; |
| 52 | |
| 53 | static arrowdir_t Arrowdirs[] = { |
| 54 | {"forward" , ARR_TYPE_NONE, ARR_TYPE_NORM}, |
| 55 | {"back" , ARR_TYPE_NORM, ARR_TYPE_NONE}, |
| 56 | {"both" , ARR_TYPE_NORM, ARR_TYPE_NORM}, |
| 57 | {"none" , ARR_TYPE_NONE, ARR_TYPE_NONE}, |
| 58 | {(char *) 0, ARR_TYPE_NONE, ARR_TYPE_NONE} |
| 59 | }; |
| 60 | |
| 61 | typedef struct arrowname_t { |
| 62 | char *name; |
| 63 | int type; |
| 64 | } arrowname_t; |
| 65 | |
| 66 | static arrowname_t Arrowsynonyms[] = { |
| 67 | /* synonyms for deprecated arrow names - included for backward compatibility */ |
| 68 | /* evaluated before primary names else "invempty" would give different results */ |
| 69 | {"invempty" , (ARR_TYPE_NORM | ARR_MOD_INV | ARR_MOD_OPEN)}, /* oinv */ |
| 70 | {(char *) 0, (ARR_TYPE_NONE)} |
| 71 | }; |
| 72 | |
| 73 | static arrowname_t Arrowmods[] = { |
| 74 | {"o" , ARR_MOD_OPEN}, |
| 75 | {"r" , ARR_MOD_RIGHT}, |
| 76 | {"l" , ARR_MOD_LEFT}, |
| 77 | /* deprecated alternates for backward compat */ |
| 78 | {"e" , ARR_MOD_OPEN}, /* o - needed for "ediamond" */ |
| 79 | {"half" , ARR_MOD_LEFT}, /* l - needed for "halfopen" */ |
| 80 | {(char *) 0, ARR_TYPE_NONE} |
| 81 | }; |
| 82 | |
| 83 | static arrowname_t Arrownames[] = { |
| 84 | {"normal" , ARR_TYPE_NORM}, |
| 85 | {"crow" , ARR_TYPE_CROW}, |
| 86 | {"tee" , ARR_TYPE_TEE}, |
| 87 | {"box" , ARR_TYPE_BOX}, |
| 88 | {"diamond" , ARR_TYPE_DIAMOND}, |
| 89 | {"dot" , ARR_TYPE_DOT}, |
| 90 | // {"none", ARR_TYPE_NONE}, |
| 91 | {"none" , ARR_TYPE_GAP}, |
| 92 | // {"gap", ARR_TYPE_GAP}, |
| 93 | /* ARR_MOD_INV is used only here to define two additional shapes |
| 94 | since not all types can use it */ |
| 95 | {"inv" , (ARR_TYPE_NORM | ARR_MOD_INV)}, |
| 96 | {"vee" , (ARR_TYPE_CROW | ARR_MOD_INV)}, |
| 97 | /* WARNING ugly kludge to deal with "o" v "open" conflict */ |
| 98 | /* Define "open" as just "pen" since "o" already taken as ARR_MOD_OPEN */ |
| 99 | /* Note that ARR_MOD_OPEN has no meaning for ARR_TYPE_CROW shape */ |
| 100 | {"pen" , (ARR_TYPE_CROW | ARR_MOD_INV)}, |
| 101 | /* WARNING ugly kludge to deal with "e" v "empty" conflict */ |
| 102 | /* Define "empty" as just "mpty" since "e" already taken as ARR_MOD_OPEN */ |
| 103 | /* Note that ARR_MOD_OPEN has expected meaning for ARR_TYPE_NORM shape */ |
| 104 | {"mpty" , ARR_TYPE_NORM}, |
| 105 | {"curve" , ARR_TYPE_CURVE}, |
| 106 | {"icurve" , (ARR_TYPE_CURVE | ARR_MOD_INV)}, |
| 107 | {(char *) 0, ARR_TYPE_NONE} |
| 108 | }; |
| 109 | |
| 110 | typedef struct arrowtype_t { |
| 111 | int type; |
| 112 | double lenfact; /* ratio of length of this arrow type to standard arrow */ |
| 113 | void (*gen) (GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag); /* generator function for type */ |
| 114 | } arrowtype_t; |
| 115 | |
| 116 | /* forward declaration of functions used in Arrowtypes[] */ |
| 117 | static void arrow_type_normal(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag); |
| 118 | static void arrow_type_crow(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag); |
| 119 | static void arrow_type_tee(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag); |
| 120 | static void arrow_type_box(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag); |
| 121 | static void arrow_type_diamond(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag); |
| 122 | static void arrow_type_dot(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag); |
| 123 | static void arrow_type_curve(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag); |
| 124 | static void arrow_type_gap(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag); |
| 125 | |
| 126 | static arrowtype_t Arrowtypes[] = { |
| 127 | {ARR_TYPE_NORM, 1.0, arrow_type_normal}, |
| 128 | {ARR_TYPE_CROW, 1.0, arrow_type_crow}, |
| 129 | {ARR_TYPE_TEE, 0.5, arrow_type_tee}, |
| 130 | {ARR_TYPE_BOX, 1.0, arrow_type_box}, |
| 131 | {ARR_TYPE_DIAMOND, 1.2, arrow_type_diamond}, |
| 132 | {ARR_TYPE_DOT, 0.8, arrow_type_dot}, |
| 133 | {ARR_TYPE_CURVE, 1.0, arrow_type_curve}, |
| 134 | {ARR_TYPE_GAP, 0.5, arrow_type_gap}, |
| 135 | {ARR_TYPE_NONE, 0.0, NULL} |
| 136 | }; |
| 137 | |
| 138 | static char *arrow_match_name_frag(char *name, arrowname_t * arrownames, int *flag) |
| 139 | { |
| 140 | arrowname_t *arrowname; |
| 141 | size_t namelen = 0; |
| 142 | char *rest = name; |
| 143 | |
| 144 | for (arrowname = arrownames; arrowname->name; arrowname++) { |
| 145 | namelen = strlen(arrowname->name); |
| 146 | if (strncmp(name, arrowname->name, namelen) == 0) { |
| 147 | *flag |= arrowname->type; |
| 148 | rest += namelen; |
| 149 | break; |
| 150 | } |
| 151 | } |
| 152 | return rest; |
| 153 | } |
| 154 | |
| 155 | static char *arrow_match_shape(char *name, int *flag) |
| 156 | { |
| 157 | char *next, *rest; |
| 158 | int f = ARR_TYPE_NONE; |
| 159 | |
| 160 | rest = arrow_match_name_frag(name, Arrowsynonyms, &f); |
| 161 | if (rest == name) { |
| 162 | do { |
| 163 | next = rest; |
| 164 | rest = arrow_match_name_frag(next, Arrowmods, &f); |
| 165 | } while (next != rest); |
| 166 | rest = arrow_match_name_frag(rest, Arrownames, &f); |
| 167 | } |
| 168 | if (f && !(f & ((1 << BITS_PER_ARROW_TYPE) - 1))) |
| 169 | f |= ARR_TYPE_NORM; |
| 170 | *flag |= f; |
| 171 | return rest; |
| 172 | } |
| 173 | |
| 174 | static void arrow_match_name(char *name, int *flag) |
| 175 | { |
| 176 | char *rest = name; |
| 177 | char *next; |
| 178 | int i, f; |
| 179 | |
| 180 | *flag = 0; |
| 181 | for (i = 0; *rest != '\0' && i < NUMB_OF_ARROW_HEADS; ) { |
| 182 | f = ARR_TYPE_NONE; |
| 183 | next = rest; |
| 184 | rest = arrow_match_shape(next, &f); |
| 185 | if (f == ARR_TYPE_NONE) { |
| 186 | agerr(AGWARN, "Arrow type \"%s\" unknown - ignoring\n" , next); |
| 187 | return; |
| 188 | } |
| 189 | if (f == ARR_TYPE_GAP && i == (NUMB_OF_ARROW_HEADS -1)) |
| 190 | f = ARR_TYPE_NONE; |
| 191 | if ((f == ARR_TYPE_GAP) && (i == 0) && (*rest == '\0')) |
| 192 | f = ARR_TYPE_NONE; |
| 193 | if (f != ARR_TYPE_NONE) |
| 194 | *flag |= (f << (i++ * BITS_PER_ARROW)); |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | void arrow_flags(Agedge_t * e, int *sflag, int *eflag) |
| 199 | { |
| 200 | char *attr; |
| 201 | arrowdir_t *arrowdir; |
| 202 | |
| 203 | *sflag = ARR_TYPE_NONE; |
| 204 | *eflag = agisdirected(agraphof(e)) ? ARR_TYPE_NORM : ARR_TYPE_NONE; |
| 205 | if (E_dir && ((attr = agxget(e, E_dir)))[0]) { |
| 206 | for (arrowdir = Arrowdirs; arrowdir->dir; arrowdir++) { |
| 207 | if (streq(attr, arrowdir->dir)) { |
| 208 | *sflag = arrowdir->sflag; |
| 209 | *eflag = arrowdir->eflag; |
| 210 | break; |
| 211 | } |
| 212 | } |
| 213 | } |
| 214 | if (E_arrowhead && (*eflag == ARR_TYPE_NORM) && ((attr = agxget(e, E_arrowhead)))[0]) |
| 215 | arrow_match_name(attr, eflag); |
| 216 | if (E_arrowtail && (*sflag == ARR_TYPE_NORM) && ((attr = agxget(e, E_arrowtail)))[0]) |
| 217 | arrow_match_name(attr, sflag); |
| 218 | if (ED_conc_opp_flag(e)) { |
| 219 | edge_t *f; |
| 220 | int s0, e0; |
| 221 | /* pick up arrowhead of opposing edge */ |
| 222 | f = agfindedge(agraphof(aghead(e)), aghead(e), agtail(e)); |
| 223 | arrow_flags(f, &s0, &e0); |
| 224 | *eflag = *eflag | s0; |
| 225 | *sflag = *sflag | e0; |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | double arrow_length(edge_t * e, int flag) |
| 230 | { |
| 231 | arrowtype_t *arrowtype; |
| 232 | double lenfact = 0.0; |
| 233 | int f, i; |
| 234 | |
| 235 | for (i = 0; i < NUMB_OF_ARROW_HEADS; i++) { |
| 236 | /* we don't simply index with flag because arrowtypes are not necessarily sorted */ |
| 237 | f = (flag >> (i * BITS_PER_ARROW)) & ((1 << BITS_PER_ARROW_TYPE) - 1); |
| 238 | for (arrowtype = Arrowtypes; arrowtype->gen; arrowtype++) { |
| 239 | if (f == arrowtype->type) { |
| 240 | lenfact += arrowtype->lenfact; |
| 241 | break; |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | /* The original was missing the factor E_arrowsz, but I believe it |
| 246 | should be here for correct arrow clipping */ |
| 247 | return ARROW_LENGTH * lenfact * late_double(e, E_arrowsz, 1.0, 0.0); |
| 248 | } |
| 249 | |
| 250 | /* inside function for calls to bezier_clip */ |
| 251 | static boolean inside(inside_t * inside_context, pointf p) |
| 252 | { |
| 253 | return DIST2(p, inside_context->a.p[0]) <= inside_context->a.r[0]; |
| 254 | } |
| 255 | |
| 256 | int arrowEndClip(edge_t* e, pointf * ps, int startp, |
| 257 | int endp, bezier * spl, int eflag) |
| 258 | { |
| 259 | inside_t inside_context; |
| 260 | pointf sp[4]; |
| 261 | double elen, elen2; |
| 262 | |
| 263 | elen = arrow_length(e, eflag); |
| 264 | elen2 = elen * elen; |
| 265 | spl->eflag = eflag, spl->ep = ps[endp + 3]; |
| 266 | if (endp > startp && DIST2(ps[endp], ps[endp + 3]) < elen2) { |
| 267 | endp -= 3; |
| 268 | } |
| 269 | sp[3] = ps[endp]; |
| 270 | sp[2] = ps[endp + 1]; |
| 271 | sp[1] = ps[endp + 2]; |
| 272 | sp[0] = spl->ep; /* ensure endpoint starts inside */ |
| 273 | |
| 274 | inside_context.a.p = &sp[0]; |
| 275 | inside_context.a.r = &elen2; |
| 276 | bezier_clip(&inside_context, inside, sp, TRUE); |
| 277 | |
| 278 | ps[endp] = sp[3]; |
| 279 | ps[endp + 1] = sp[2]; |
| 280 | ps[endp + 2] = sp[1]; |
| 281 | ps[endp + 3] = sp[0]; |
| 282 | return endp; |
| 283 | } |
| 284 | |
| 285 | int arrowStartClip(edge_t* e, pointf * ps, int startp, |
| 286 | int endp, bezier * spl, int sflag) |
| 287 | { |
| 288 | inside_t inside_context; |
| 289 | pointf sp[4]; |
| 290 | double slen, slen2; |
| 291 | |
| 292 | slen = arrow_length(e, sflag); |
| 293 | slen2 = slen * slen; |
| 294 | spl->sflag = sflag, spl->sp = ps[startp]; |
| 295 | if (endp > startp && DIST2(ps[startp], ps[startp + 3]) < slen2) { |
| 296 | startp += 3; |
| 297 | } |
| 298 | sp[0] = ps[startp + 3]; |
| 299 | sp[1] = ps[startp + 2]; |
| 300 | sp[2] = ps[startp + 1]; |
| 301 | sp[3] = spl->sp; /* ensure endpoint starts inside */ |
| 302 | |
| 303 | inside_context.a.p = &sp[3]; |
| 304 | inside_context.a.r = &slen2; |
| 305 | bezier_clip(&inside_context, inside, sp, FALSE); |
| 306 | |
| 307 | ps[startp] = sp[3]; |
| 308 | ps[startp + 1] = sp[2]; |
| 309 | ps[startp + 2] = sp[1]; |
| 310 | ps[startp + 3] = sp[0]; |
| 311 | return startp; |
| 312 | } |
| 313 | |
| 314 | /* arrowOrthoClip: |
| 315 | * For orthogonal routing, we know each Bezier of spl is a horizontal or vertical |
| 316 | * line segment. We need to guarantee the B-spline stays this way. At present, we shrink |
| 317 | * the arrows if necessary to fit the last segment at either end. Alternatively, we could |
| 318 | * maintain the arrow size by dropping the 3 points of spl, and adding a new spl encoding |
| 319 | * 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 |
| 320 | * 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 |
| 321 | * that the truncated spl clips to the arrow shape. |
| 322 | */ |
| 323 | void arrowOrthoClip(edge_t* e, pointf* ps, int startp, int endp, bezier* spl, int sflag, int eflag) |
| 324 | { |
| 325 | pointf p, q, r, s, t; |
| 326 | double d, tlen, hlen, maxd; |
| 327 | |
| 328 | if (sflag && eflag && (endp == startp)) { /* handle special case of two arrows on a single segment */ |
| 329 | p = ps[endp]; |
| 330 | q = ps[endp+3]; |
| 331 | tlen = arrow_length (e, sflag); |
| 332 | hlen = arrow_length (e, eflag); |
| 333 | d = DIST(p, q); |
| 334 | if (hlen + tlen >= d) { |
| 335 | hlen = tlen = d/3.0; |
| 336 | } |
| 337 | if (p.y == q.y) { /* horz segment */ |
| 338 | s.y = t.y = p.y; |
| 339 | if (p.x < q.x) { |
| 340 | t.x = q.x - hlen; |
| 341 | s.x = p.x + tlen; |
| 342 | } |
| 343 | else { |
| 344 | t.x = q.x + hlen; |
| 345 | s.x = p.x - tlen; |
| 346 | } |
| 347 | } |
| 348 | else { /* vert segment */ |
| 349 | s.x = t.x = p.x; |
| 350 | if (p.y < q.y) { |
| 351 | t.y = q.y - hlen; |
| 352 | s.y = p.y + tlen; |
| 353 | } |
| 354 | else { |
| 355 | t.y = q.y + hlen; |
| 356 | s.y = p.y - tlen; |
| 357 | } |
| 358 | } |
| 359 | ps[endp] = ps[endp + 1] = s; |
| 360 | ps[endp + 2] = ps[endp + 3] = t; |
| 361 | spl->eflag = eflag, spl->ep = p; |
| 362 | spl->sflag = sflag, spl->sp = q; |
| 363 | return; |
| 364 | } |
| 365 | if (eflag) { |
| 366 | hlen = arrow_length(e, eflag); |
| 367 | p = ps[endp]; |
| 368 | q = ps[endp+3]; |
| 369 | d = DIST(p, q); |
| 370 | maxd = 0.9*d; |
| 371 | if (hlen >= maxd) { /* arrow too long */ |
| 372 | hlen = maxd; |
| 373 | } |
| 374 | if (p.y == q.y) { /* horz segment */ |
| 375 | r.y = p.y; |
| 376 | if (p.x < q.x) r.x = q.x - hlen; |
| 377 | else r.x = q.x + hlen; |
| 378 | } |
| 379 | else { /* vert segment */ |
| 380 | r.x = p.x; |
| 381 | if (p.y < q.y) r.y = q.y - hlen; |
| 382 | else r.y = q.y + hlen; |
| 383 | } |
| 384 | ps[endp + 1] = p; |
| 385 | ps[endp + 2] = ps[endp + 3] = r; |
| 386 | spl->eflag = eflag; |
| 387 | spl->ep = q; |
| 388 | } |
| 389 | if (sflag) { |
| 390 | tlen = arrow_length(e, sflag); |
| 391 | p = ps[startp]; |
| 392 | q = ps[startp+3]; |
| 393 | d = DIST(p, q); |
| 394 | maxd = 0.9*d; |
| 395 | if (tlen >= maxd) { /* arrow too long */ |
| 396 | tlen = maxd; |
| 397 | } |
| 398 | if (p.y == q.y) { /* horz segment */ |
| 399 | r.y = p.y; |
| 400 | if (p.x < q.x) r.x = p.x + tlen; |
| 401 | else r.x = p.x - tlen; |
| 402 | } |
| 403 | else { /* vert segment */ |
| 404 | r.x = p.x; |
| 405 | if (p.y < q.y) r.y = p.y + tlen; |
| 406 | else r.y = p.y - tlen; |
| 407 | } |
| 408 | ps[startp] = ps[startp + 1] = r; |
| 409 | ps[startp + 2] = q; |
| 410 | spl->sflag = sflag; |
| 411 | spl->sp = p; |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | static void arrow_type_normal(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag) |
| 416 | { |
| 417 | pointf q, v, a[5]; |
| 418 | double arrowwidth; |
| 419 | |
| 420 | arrowwidth = 0.35; |
| 421 | if (penwidth > 4) |
| 422 | arrowwidth *= penwidth / 4; |
| 423 | |
| 424 | v.x = -u.y * arrowwidth; |
| 425 | v.y = u.x * arrowwidth; |
| 426 | q.x = p.x + u.x; |
| 427 | q.y = p.y + u.y; |
| 428 | if (flag & ARR_MOD_INV) { |
| 429 | a[0] = a[4] = p; |
| 430 | a[1].x = p.x - v.x; |
| 431 | a[1].y = p.y - v.y; |
| 432 | a[2] = q; |
| 433 | a[3].x = p.x + v.x; |
| 434 | a[3].y = p.y + v.y; |
| 435 | } else { |
| 436 | a[0] = a[4] = q; |
| 437 | a[1].x = q.x - v.x; |
| 438 | a[1].y = q.y - v.y; |
| 439 | a[2] = p; |
| 440 | a[3].x = q.x + v.x; |
| 441 | a[3].y = q.y + v.y; |
| 442 | } |
| 443 | if (flag & ARR_MOD_LEFT) |
| 444 | gvrender_polygon(job, a, 3, !(flag & ARR_MOD_OPEN)); |
| 445 | else if (flag & ARR_MOD_RIGHT) |
| 446 | gvrender_polygon(job, &a[2], 3, !(flag & ARR_MOD_OPEN)); |
| 447 | else |
| 448 | gvrender_polygon(job, &a[1], 3, !(flag & ARR_MOD_OPEN)); |
| 449 | } |
| 450 | |
| 451 | static void arrow_type_crow(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag) |
| 452 | { |
| 453 | pointf m, q, v, w, a[9]; |
| 454 | double arrowwidth, shaftwidth; |
| 455 | |
| 456 | arrowwidth = 0.45; |
| 457 | if (penwidth > (4 * arrowsize) && (flag & ARR_MOD_INV)) |
| 458 | arrowwidth *= penwidth / (4 * arrowsize); |
| 459 | |
| 460 | shaftwidth = 0; |
| 461 | if (penwidth > 1 && (flag & ARR_MOD_INV)) |
| 462 | shaftwidth = 0.05 * (penwidth - 1) / arrowsize; /* arrowsize to cancel the arrowsize term already in u */ |
| 463 | |
| 464 | v.x = -u.y * arrowwidth; |
| 465 | v.y = u.x * arrowwidth; |
| 466 | w.x = -u.y * shaftwidth; |
| 467 | w.y = u.x * shaftwidth; |
| 468 | q.x = p.x + u.x; |
| 469 | q.y = p.y + u.y; |
| 470 | m.x = p.x + u.x * 0.5; |
| 471 | m.y = p.y + u.y * 0.5; |
| 472 | if (flag & ARR_MOD_INV) { /* vee */ |
| 473 | a[0] = a[8] = p; |
| 474 | a[1].x = q.x - v.x; |
| 475 | a[1].y = q.y - v.y; |
| 476 | a[2].x = m.x - w.x; |
| 477 | a[2].y = m.y - w.y; |
| 478 | a[3].x = q.x - w.x; |
| 479 | a[3].y = q.y - w.y; |
| 480 | a[4] = q; |
| 481 | a[5].x = q.x + w.x; |
| 482 | a[5].y = q.y + w.y; |
| 483 | a[6].x = m.x + w.x; |
| 484 | a[6].y = m.y + w.y; |
| 485 | a[7].x = q.x + v.x; |
| 486 | a[7].y = q.y + v.y; |
| 487 | } else { /* crow */ |
| 488 | a[0] = a[8] = q; |
| 489 | a[1].x = p.x - v.x; |
| 490 | a[1].y = p.y - v.y; |
| 491 | a[2].x = m.x - w.x; |
| 492 | a[2].y = m.y - w.y; |
| 493 | a[3].x = p.x; |
| 494 | a[3].y = p.y; |
| 495 | a[4] = p; |
| 496 | a[5].x = p.x; |
| 497 | a[5].y = p.y; |
| 498 | a[6].x = m.x + w.x; |
| 499 | a[6].y = m.y + w.y; |
| 500 | a[7].x = p.x + v.x; |
| 501 | a[7].y = p.y + v.y; |
| 502 | } |
| 503 | if (flag & ARR_MOD_LEFT) |
| 504 | gvrender_polygon(job, a, 6, 1); |
| 505 | else if (flag & ARR_MOD_RIGHT) |
| 506 | gvrender_polygon(job, &a[3], 6, 1); |
| 507 | else |
| 508 | gvrender_polygon(job, a, 9, 1); |
| 509 | } |
| 510 | |
| 511 | static void arrow_type_gap(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag) |
| 512 | { |
| 513 | pointf q, a[2]; |
| 514 | |
| 515 | q.x = p.x + u.x; |
| 516 | q.y = p.y + u.y; |
| 517 | a[0] = p; |
| 518 | a[1] = q; |
| 519 | gvrender_polyline(job, a, 2); |
| 520 | } |
| 521 | |
| 522 | static void arrow_type_tee(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag) |
| 523 | { |
| 524 | pointf m, n, q, v, a[4]; |
| 525 | |
| 526 | v.x = -u.y; |
| 527 | v.y = u.x; |
| 528 | q.x = p.x + u.x; |
| 529 | q.y = p.y + u.y; |
| 530 | m.x = p.x + u.x * 0.2; |
| 531 | m.y = p.y + u.y * 0.2; |
| 532 | n.x = p.x + u.x * 0.6; |
| 533 | n.y = p.y + u.y * 0.6; |
| 534 | a[0].x = m.x + v.x; |
| 535 | a[0].y = m.y + v.y; |
| 536 | a[1].x = m.x - v.x; |
| 537 | a[1].y = m.y - v.y; |
| 538 | a[2].x = n.x - v.x; |
| 539 | a[2].y = n.y - v.y; |
| 540 | a[3].x = n.x + v.x; |
| 541 | a[3].y = n.y + v.y; |
| 542 | if (flag & ARR_MOD_LEFT) { |
| 543 | a[0] = m; |
| 544 | a[3] = n; |
| 545 | } else if (flag & ARR_MOD_RIGHT) { |
| 546 | a[1] = m; |
| 547 | a[2] = n; |
| 548 | } |
| 549 | gvrender_polygon(job, a, 4, 1); |
| 550 | a[0] = p; |
| 551 | a[1] = q; |
| 552 | gvrender_polyline(job, a, 2); |
| 553 | } |
| 554 | |
| 555 | static void arrow_type_box(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag) |
| 556 | { |
| 557 | pointf m, q, v, a[4]; |
| 558 | |
| 559 | v.x = -u.y * 0.4; |
| 560 | v.y = u.x * 0.4; |
| 561 | m.x = p.x + u.x * 0.8; |
| 562 | m.y = p.y + u.y * 0.8; |
| 563 | q.x = p.x + u.x; |
| 564 | q.y = p.y + u.y; |
| 565 | a[0].x = p.x + v.x; |
| 566 | a[0].y = p.y + v.y; |
| 567 | a[1].x = p.x - v.x; |
| 568 | a[1].y = p.y - v.y; |
| 569 | a[2].x = m.x - v.x; |
| 570 | a[2].y = m.y - v.y; |
| 571 | a[3].x = m.x + v.x; |
| 572 | a[3].y = m.y + v.y; |
| 573 | if (flag & ARR_MOD_LEFT) { |
| 574 | a[0] = p; |
| 575 | a[3] = m; |
| 576 | } else if (flag & ARR_MOD_RIGHT) { |
| 577 | a[1] = p; |
| 578 | a[2] = m; |
| 579 | } |
| 580 | gvrender_polygon(job, a, 4, !(flag & ARR_MOD_OPEN)); |
| 581 | a[0] = m; |
| 582 | a[1] = q; |
| 583 | gvrender_polyline(job, a, 2); |
| 584 | } |
| 585 | |
| 586 | static void arrow_type_diamond(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag) |
| 587 | { |
| 588 | pointf q, r, v, a[5]; |
| 589 | |
| 590 | v.x = -u.y / 3.; |
| 591 | v.y = u.x / 3.; |
| 592 | r.x = p.x + u.x / 2.; |
| 593 | r.y = p.y + u.y / 2.; |
| 594 | q.x = p.x + u.x; |
| 595 | q.y = p.y + u.y; |
| 596 | a[0] = a[4] = q; |
| 597 | a[1].x = r.x + v.x; |
| 598 | a[1].y = r.y + v.y; |
| 599 | a[2] = p; |
| 600 | a[3].x = r.x - v.x; |
| 601 | a[3].y = r.y - v.y; |
| 602 | if (flag & ARR_MOD_LEFT) |
| 603 | gvrender_polygon(job, &a[2], 3, !(flag & ARR_MOD_OPEN)); |
| 604 | else if (flag & ARR_MOD_RIGHT) |
| 605 | gvrender_polygon(job, a, 3, !(flag & ARR_MOD_OPEN)); |
| 606 | else |
| 607 | gvrender_polygon(job, a, 4, !(flag & ARR_MOD_OPEN)); |
| 608 | } |
| 609 | |
| 610 | static void arrow_type_dot(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag) |
| 611 | { |
| 612 | double r; |
| 613 | pointf AF[2]; |
| 614 | |
| 615 | r = sqrt(u.x * u.x + u.y * u.y) / 2.; |
| 616 | AF[0].x = p.x + u.x / 2. - r; |
| 617 | AF[0].y = p.y + u.y / 2. - r; |
| 618 | AF[1].x = p.x + u.x / 2. + r; |
| 619 | AF[1].y = p.y + u.y / 2. + r; |
| 620 | gvrender_ellipse(job, AF, 2, !(flag & ARR_MOD_OPEN)); |
| 621 | } |
| 622 | |
| 623 | |
| 624 | /* Draw a concave semicircle using a single cubic bezier curve that touches p at its midpoint. |
| 625 | * See http://digerati-illuminatus.blogspot.com.au/2008/05/approximating-semicircle-with-cubic.html for details. |
| 626 | */ |
| 627 | static void arrow_type_curve(GVJ_t* job, pointf p, pointf u, double arrowsize, double penwidth, int flag) |
| 628 | { |
| 629 | double arrowwidth = penwidth > 4 ? 0.5 * penwidth / 4 : 0.5; |
| 630 | pointf q, v, w; |
| 631 | pointf AF[4], a[2]; |
| 632 | |
| 633 | q.x = p.x + u.x; |
| 634 | q.y = p.y + u.y; |
| 635 | v.x = -u.y * arrowwidth; |
| 636 | v.y = u.x * arrowwidth; |
| 637 | w.x = v.y; // same direction as u, same magnitude as v. |
| 638 | w.y = -v.x; |
| 639 | a[0] = p; |
| 640 | a[1] = q; |
| 641 | |
| 642 | AF[0].x = p.x + v.x + w.x; |
| 643 | AF[0].y = p.y + v.y + w.y; |
| 644 | |
| 645 | AF[3].x = p.x - v.x + w.x; |
| 646 | AF[3].y = p.y - v.y + w.y; |
| 647 | |
| 648 | if (flag & ARR_MOD_INV) { /* ----(-| */ |
| 649 | AF[1].x = p.x + 0.95 * v.x + w.x + w.x * 4.0 / 3.0; |
| 650 | AF[1].y = AF[0].y + w.y * 4.0 / 3.0; |
| 651 | |
| 652 | AF[2].x = p.x - 0.95 * v.x + w.x + w.x * 4.0 / 3.0; |
| 653 | AF[2].y = AF[3].y + w.y * 4.0 / 3.0; |
| 654 | } |
| 655 | else { /* ----)-| */ |
| 656 | AF[1].x = p.x + 0.95 * v.x + w.x - w.x * 4.0 / 3.0; |
| 657 | AF[1].y = AF[0].y - w.y * 4.0 / 3.0; |
| 658 | |
| 659 | AF[2].x = p.x - 0.95 * v.x + w.x - w.x * 4.0 / 3.0; |
| 660 | AF[2].y = AF[3].y - w.y * 4.0 / 3.0; |
| 661 | } |
| 662 | |
| 663 | gvrender_polyline(job, a, 2); |
| 664 | if (flag & ARR_MOD_LEFT) |
| 665 | Bezier(AF, 3, 0.5, NULL, AF); |
| 666 | else if (flag & ARR_MOD_RIGHT) |
| 667 | Bezier(AF, 3, 0.5, AF, NULL); |
| 668 | gvrender_beziercurve(job, AF, sizeof(AF) / sizeof(pointf), FALSE, FALSE, FALSE); |
| 669 | } |
| 670 | |
| 671 | |
| 672 | static pointf arrow_gen_type(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag) |
| 673 | { |
| 674 | int f; |
| 675 | arrowtype_t *arrowtype; |
| 676 | |
| 677 | f = flag & ((1 << BITS_PER_ARROW_TYPE) - 1); |
| 678 | for (arrowtype = Arrowtypes; arrowtype->type; arrowtype++) { |
| 679 | if (f == arrowtype->type) { |
| 680 | u.x *= arrowtype->lenfact * arrowsize; |
| 681 | u.y *= arrowtype->lenfact * arrowsize; |
| 682 | (arrowtype->gen) (job, p, u, arrowsize, penwidth, flag); |
| 683 | p.x = p.x + u.x; |
| 684 | p.y = p.y + u.y; |
| 685 | break; |
| 686 | } |
| 687 | } |
| 688 | return p; |
| 689 | } |
| 690 | |
| 691 | boxf arrow_bb(pointf p, pointf u, double arrowsize, int flag) |
| 692 | { |
| 693 | double s; |
| 694 | boxf bb; |
| 695 | double ax,ay,bx,by,cx,cy,dx,dy; |
| 696 | double ux2, uy2; |
| 697 | |
| 698 | /* generate arrowhead vector */ |
| 699 | u.x -= p.x; |
| 700 | u.y -= p.y; |
| 701 | /* the EPSILONs are to keep this stable as length of u approaches 0.0 */ |
| 702 | s = ARROW_LENGTH * arrowsize / (sqrt(u.x * u.x + u.y * u.y) + EPSILON); |
| 703 | u.x += (u.x >= 0.0) ? EPSILON : -EPSILON; |
| 704 | u.y += (u.y >= 0.0) ? EPSILON : -EPSILON; |
| 705 | u.x *= s; |
| 706 | u.y *= s; |
| 707 | |
| 708 | /* compute all 4 corners of rotated arrowhead bounding box */ |
| 709 | ux2 = u.x / 2.; |
| 710 | uy2 = u.y / 2.; |
| 711 | ax = p.x - uy2; |
| 712 | ay = p.y - ux2; |
| 713 | bx = p.x + uy2; |
| 714 | by = p.y + ux2; |
| 715 | cx = ax + u.x; |
| 716 | cy = ay + u.y; |
| 717 | dx = bx + u.x; |
| 718 | dy = by + u.y; |
| 719 | |
| 720 | /* compute a right bb */ |
| 721 | bb.UR.x = MAX(ax, MAX(bx, MAX(cx, dx))); |
| 722 | bb.UR.y = MAX(ay, MAX(by, MAX(cy, dy))); |
| 723 | bb.LL.x = MIN(ax, MIN(bx, MIN(cx, dx))); |
| 724 | bb.LL.y = MIN(ay, MIN(by, MIN(cy, dy))); |
| 725 | |
| 726 | return bb; |
| 727 | } |
| 728 | |
| 729 | void arrow_gen(GVJ_t * job, emit_state_t emit_state, pointf p, pointf u, double arrowsize, double penwidth, int flag) |
| 730 | { |
| 731 | obj_state_t *obj = job->obj; |
| 732 | double s; |
| 733 | int i, f; |
| 734 | emit_state_t old_emit_state; |
| 735 | |
| 736 | old_emit_state = obj->emit_state; |
| 737 | obj->emit_state = emit_state; |
| 738 | |
| 739 | /* Dotted and dashed styles on the arrowhead are ugly (dds) */ |
| 740 | /* linewidth needs to be reset */ |
| 741 | gvrender_set_style(job, job->gvc->defaultlinestyle); |
| 742 | |
| 743 | gvrender_set_penwidth(job, penwidth); |
| 744 | |
| 745 | /* generate arrowhead vector */ |
| 746 | u.x -= p.x; |
| 747 | u.y -= p.y; |
| 748 | /* the EPSILONs are to keep this stable as length of u approaches 0.0 */ |
| 749 | s = ARROW_LENGTH / (sqrt(u.x * u.x + u.y * u.y) + EPSILON); |
| 750 | u.x += (u.x >= 0.0) ? EPSILON : -EPSILON; |
| 751 | u.y += (u.y >= 0.0) ? EPSILON : -EPSILON; |
| 752 | u.x *= s; |
| 753 | u.y *= s; |
| 754 | |
| 755 | /* the first arrow head - closest to node */ |
| 756 | for (i = 0; i < NUMB_OF_ARROW_HEADS; i++) { |
| 757 | f = (flag >> (i * BITS_PER_ARROW)) & ((1 << BITS_PER_ARROW) - 1); |
| 758 | if (f == ARR_TYPE_NONE) |
| 759 | break; |
| 760 | p = arrow_gen_type(job, p, u, arrowsize, penwidth, f); |
| 761 | } |
| 762 | |
| 763 | obj->emit_state = old_emit_state; |
| 764 | } |
| 765 | |