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 
arrow_match_name_frag(char * name,arrowname_t * arrownames,int * flag)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 
arrow_match_shape(char * name,int * flag)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 
arrow_match_name(char * name,int * flag)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 
arrow_flags(Agedge_t * e,int * sflag,int * eflag)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 
arrow_length(edge_t * e,int flag)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 */
inside(inside_t * inside_context,pointf p)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 
arrowEndClip(edge_t * e,pointf * ps,int startp,int endp,bezier * spl,int eflag)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 
arrowStartClip(edge_t * e,pointf * ps,int startp,int endp,bezier * spl,int sflag)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  */
arrowOrthoClip(edge_t * e,pointf * ps,int startp,int endp,bezier * spl,int sflag,int eflag)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 
arrow_type_normal(GVJ_t * job,pointf p,pointf u,double arrowsize,double penwidth,int flag)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 
arrow_type_crow(GVJ_t * job,pointf p,pointf u,double arrowsize,double penwidth,int flag)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 
arrow_type_gap(GVJ_t * job,pointf p,pointf u,double arrowsize,double penwidth,int flag)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 
arrow_type_tee(GVJ_t * job,pointf p,pointf u,double arrowsize,double penwidth,int flag)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 
arrow_type_box(GVJ_t * job,pointf p,pointf u,double arrowsize,double penwidth,int flag)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 
arrow_type_diamond(GVJ_t * job,pointf p,pointf u,double arrowsize,double penwidth,int flag)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 
arrow_type_dot(GVJ_t * job,pointf p,pointf u,double arrowsize,double penwidth,int flag)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  */
arrow_type_curve(GVJ_t * job,pointf p,pointf u,double arrowsize,double penwidth,int flag)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 
arrow_gen_type(GVJ_t * job,pointf p,pointf u,double arrowsize,double penwidth,int flag)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 
arrow_bb(pointf p,pointf u,double arrowsize,int flag)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 
arrow_gen(GVJ_t * job,emit_state_t emit_state,pointf p,pointf u,double arrowsize,double penwidth,int flag)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