Graphviz 2.29.20120208.0545
lib/common/arrows.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 
00015 #include "render.h"
00016 
00017 #define EPSILON .0001
00018 
00019 /* standard arrow length in points */
00020 #define ARROW_LENGTH 10.
00021 
00022 #define NUMB_OF_ARROW_HEADS  4
00023 /* each arrow in 8 bits.  Room for NUMB_OF_ARROW_HEADS arrows in 32 bit int. */
00024 
00025 #define BITS_PER_ARROW 8
00026 
00027 #define BITS_PER_ARROW_TYPE 3
00028 /* arrow types (in BITS_PER_ARROW_TYPE bits) */
00029 #define ARR_TYPE_NONE     (ARR_NONE)
00030 #define ARR_TYPE_NORM     1
00031 #define ARR_TYPE_CROW     2
00032 #define ARR_TYPE_TEE      3
00033 #define ARR_TYPE_BOX      4
00034 #define ARR_TYPE_DIAMOND  5
00035 #define ARR_TYPE_DOT      6
00036 /* Spare:  #define ARR_TYPE_xxx      7 */
00037 
00038 /* arrow mods (in (BITS_PER_ARROW - BITS_PER_ARROW_TYPE) bits) */
00039 #define ARR_MOD_OPEN      (1<<(BITS_PER_ARROW_TYPE+0))
00040 #define ARR_MOD_INV       (1<<(BITS_PER_ARROW_TYPE+1))
00041 #define ARR_MOD_LEFT      (1<<(BITS_PER_ARROW_TYPE+2))
00042 #define ARR_MOD_RIGHT     (1<<(BITS_PER_ARROW_TYPE+3))
00043 /* Spare:  #define ARR_MOD_xxx       (1<<(BITS_PER_ARROW_TYPE+4))  */
00044 
00045 typedef struct arrowdir_t {
00046     char *dir;
00047     int sflag;
00048     int eflag;
00049 } arrowdir_t;
00050 
00051 static arrowdir_t Arrowdirs[] = {
00052     {"forward", ARR_TYPE_NONE, ARR_TYPE_NORM},
00053     {"back", ARR_TYPE_NORM, ARR_TYPE_NONE},
00054     {"both", ARR_TYPE_NORM, ARR_TYPE_NORM},
00055     {"none", ARR_TYPE_NONE, ARR_TYPE_NONE},
00056     {(char *) 0, ARR_TYPE_NONE, ARR_TYPE_NONE}
00057 };
00058 
00059 typedef struct arrowname_t {
00060     char *name;
00061     int type;
00062 } arrowname_t;
00063 
00064 static arrowname_t Arrowsynonyms[] = {
00065     /* synonyms for deprecated arrow names - included for backward compatibility */
00066     /*  evaluated before primary names else "invempty" would give different results */
00067     {"invempty", (ARR_TYPE_NORM | ARR_MOD_INV | ARR_MOD_OPEN)}, /* oinv     */
00068     {(char *) 0, (ARR_TYPE_NONE)}
00069 };
00070 
00071 static arrowname_t Arrowmods[] = {
00072     {"o", ARR_MOD_OPEN},
00073     {"r", ARR_MOD_RIGHT},
00074     {"l", ARR_MOD_LEFT},
00075     /* deprecated alternates for backward compat */
00076     {"e", ARR_MOD_OPEN},        /* o  - needed for "ediamond" */
00077     {"half", ARR_MOD_LEFT},     /* l  - needed for "halfopen" */
00078     {(char *) 0, ARR_TYPE_NONE}
00079 };
00080 
00081 static arrowname_t Arrownames[] = {
00082     {"normal", ARR_TYPE_NORM},
00083     {"crow", ARR_TYPE_CROW},
00084     {"tee", ARR_TYPE_TEE},
00085     {"box", ARR_TYPE_BOX},
00086     {"diamond", ARR_TYPE_DIAMOND},
00087     {"dot", ARR_TYPE_DOT},
00088     {"none", ARR_TYPE_NONE},
00089     /* ARR_MOD_INV is used only here to define two additional shapes
00090        since not all types can use it */
00091     {"inv", (ARR_TYPE_NORM | ARR_MOD_INV)},
00092     {"vee", (ARR_TYPE_CROW | ARR_MOD_INV)},
00093     /* WARNING ugly kludge to deal with "o" v "open" conflict */
00094     /* Define "open" as just "pen" since "o" already taken as ARR_MOD_OPEN */
00095     /* Note that ARR_MOD_OPEN has no meaning for ARR_TYPE_CROW shape */
00096     {"pen", (ARR_TYPE_CROW | ARR_MOD_INV)},
00097     /* WARNING ugly kludge to deal with "e" v "empty" conflict */
00098     /* Define "empty" as just "mpty" since "e" already taken as ARR_MOD_OPEN */
00099     /* Note that ARR_MOD_OPEN has expected meaning for ARR_TYPE_NORM shape */
00100     {"mpty", ARR_TYPE_NORM},
00101     {(char *) 0, ARR_TYPE_NONE}
00102 };
00103 
00104 typedef struct arrowtype_t {
00105     int type;
00106     double lenfact;             /* ratio of length of this arrow type to standard arrow */
00107     void (*gen) (GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag); /* generator function for type */
00108 } arrowtype_t;
00109 
00110 /* forward declaration of functions used in Arrowtypes[] */
00111 static void arrow_type_normal(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag);
00112 static void arrow_type_crow(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag);
00113 static void arrow_type_tee(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag);
00114 static void arrow_type_box(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag);
00115 static void arrow_type_diamond(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag);
00116 static void arrow_type_dot(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag);
00117 
00118 static arrowtype_t Arrowtypes[] = {
00119     {ARR_TYPE_NORM, 1.0, arrow_type_normal},
00120     {ARR_TYPE_CROW, 1.0, arrow_type_crow},
00121     {ARR_TYPE_TEE, 0.5, arrow_type_tee},
00122     {ARR_TYPE_BOX, 1.0, arrow_type_box},
00123     {ARR_TYPE_DIAMOND, 1.2, arrow_type_diamond},
00124     {ARR_TYPE_DOT, 0.8, arrow_type_dot},
00125     {ARR_TYPE_NONE, 0.0, NULL}
00126 };
00127 
00128 static char *arrow_match_name_frag(char *name, arrowname_t * arrownames, int *flag)
00129 {
00130     arrowname_t *arrowname;
00131     int namelen = 0;
00132     char *rest = name;
00133 
00134     for (arrowname = arrownames; arrowname->name; arrowname++) {
00135         namelen = strlen(arrowname->name);
00136         if (strncmp(name, arrowname->name, namelen) == 0) {
00137             *flag |= arrowname->type;
00138             rest += namelen;
00139             break;
00140         }
00141     }
00142     return rest;
00143 }
00144 
00145 static char *arrow_match_shape(char *name, int *flag)
00146 {
00147     char *next, *rest;
00148     int f = ARR_TYPE_NONE;
00149 
00150     rest = arrow_match_name_frag(name, Arrowsynonyms, &f);
00151     if (rest == name) {
00152         do {
00153             next = rest;
00154             rest = arrow_match_name_frag(next, Arrowmods, &f);
00155         } while (next != rest);
00156         rest = arrow_match_name_frag(rest, Arrownames, &f);
00157     }
00158     if (f && !(f & ((1 << BITS_PER_ARROW_TYPE) - 1)))
00159         f |= ARR_TYPE_NORM;
00160     *flag |= f;
00161     return rest;
00162 }
00163 
00164 static void arrow_match_name(char *name, int *flag)
00165 {
00166     char *rest = name;
00167     int i, f;
00168 
00169     *flag = 0;
00170     for (i = 0; *rest != '\0' && i < NUMB_OF_ARROW_HEADS; i++) {
00171         f = ARR_TYPE_NONE;
00172         rest = arrow_match_shape(rest, &f);
00173         *flag |= (f << (i * BITS_PER_ARROW));
00174     }
00175 }
00176 
00177 void arrow_flags(Agedge_t * e, int *sflag, int *eflag)
00178 {
00179     char *attr;
00180     arrowdir_t *arrowdir;
00181 
00182     *sflag = ARR_TYPE_NONE;
00183 #ifdef WITH_CGRAPH
00184     *eflag = agisdirected(agraphof(e)) ? ARR_TYPE_NORM : ARR_TYPE_NONE;
00185     if (E_dir && ((attr = agxget(e, E_dir)))[0]) {
00186 #else
00187     *eflag = AG_IS_DIRECTED(e->tail->graph) ? ARR_TYPE_NORM : ARR_TYPE_NONE;
00188     if (E_dir && ((attr = agxget(e, E_dir->index)))[0]) {
00189 #endif
00190         for (arrowdir = Arrowdirs; arrowdir->dir; arrowdir++) {
00191             if (streq(attr, arrowdir->dir)) {
00192                 *sflag = arrowdir->sflag;
00193                 *eflag = arrowdir->eflag;
00194                 break;
00195             }
00196         }
00197     }
00198 #ifdef WITH_CGRAPH
00199     if (E_arrowhead && (*eflag == ARR_TYPE_NORM) && ((attr = agxget(e, E_arrowhead)))[0])
00200         arrow_match_name(attr, eflag);
00201     if (E_arrowtail && (*sflag == ARR_TYPE_NORM) && ((attr = agxget(e, E_arrowtail)))[0])
00202         arrow_match_name(attr, sflag);
00203 #else
00204     if (E_arrowhead && (*eflag == ARR_TYPE_NORM) && ((attr = agxget(e, E_arrowhead->index)))[0])
00205         arrow_match_name(attr, eflag);
00206     if (E_arrowtail && (*sflag == ARR_TYPE_NORM) && ((attr = agxget(e, E_arrowtail->index)))[0])
00207         arrow_match_name(attr, sflag);
00208 #endif
00209     if (ED_conc_opp_flag(e)) {
00210         edge_t *f;
00211         int s0, e0;
00212         /* pick up arrowhead of opposing edge */
00213         f = agfindedge(agraphof(aghead(e)), aghead(e), agtail(e));
00214         arrow_flags(f, &s0, &e0);
00215         *eflag = *eflag | s0;
00216         *sflag = *sflag | e0;
00217     }
00218 }
00219 
00220 double arrow_length(edge_t * e, int flag)
00221 {
00222     arrowtype_t *arrowtype;
00223     double lenfact = 0.0;
00224     int f, i;
00225 
00226     for (i = 0; i < NUMB_OF_ARROW_HEADS; i++) {
00227         /* we don't simply index with flag because arrowtypes are not necessarily sorted */
00228         f = (flag >> (i * BITS_PER_ARROW)) & ((1 << BITS_PER_ARROW_TYPE) - 1);
00229         for (arrowtype = Arrowtypes; arrowtype->gen; arrowtype++) {
00230             if (f == arrowtype->type) {
00231                 lenfact += arrowtype->lenfact;
00232                 break;
00233             }
00234         }
00235     }
00236     /* The original was missing the factor E_arrowsz, but I believe it
00237        should be here for correct arrow clipping */
00238     return ARROW_LENGTH * lenfact * late_double(e, E_arrowsz, 1.0, 0.0);
00239 }
00240 
00241 /* inside function for calls to bezier_clip */
00242 static boolean inside(inside_t * inside_context, pointf p)
00243 {
00244     return DIST2(p, inside_context->a.p[0]) <= inside_context->a.r[0];
00245 }
00246 
00247 int arrowEndClip(edge_t* e, pointf * ps, int startp,
00248                  int endp, bezier * spl, int eflag)
00249 {
00250     inside_t inside_context;
00251     pointf sp[4];
00252     double elen, elen2;
00253 
00254     elen = arrow_length(e, eflag);
00255     elen2 = elen * elen;
00256     spl->eflag = eflag, spl->ep = ps[endp + 3];
00257     if (endp > startp && DIST2(ps[endp], ps[endp + 3]) < elen2) {
00258         endp -= 3;
00259     }
00260     sp[3] = ps[endp];
00261     sp[2] = ps[endp + 1];
00262     sp[1] = ps[endp + 2];
00263     sp[0] = spl->ep;    /* ensure endpoint starts inside */
00264 
00265     inside_context.a.p = &sp[0];
00266     inside_context.a.r = &elen2;
00267     bezier_clip(&inside_context, inside, sp, TRUE);
00268 
00269     ps[endp] = sp[3];
00270     ps[endp + 1] = sp[2];
00271     ps[endp + 2] = sp[1];
00272     ps[endp + 3] = sp[0];
00273     return endp;
00274 }
00275 
00276 int arrowStartClip(edge_t* e, pointf * ps, int startp,
00277                    int endp, bezier * spl, int sflag)
00278 {
00279     inside_t inside_context;
00280     pointf sp[4];
00281     double slen, slen2;
00282 
00283     slen = arrow_length(e, sflag);
00284     slen2 = slen * slen;
00285     spl->sflag = sflag, spl->sp = ps[startp];
00286     if (endp > startp && DIST2(ps[startp], ps[startp + 3]) < slen2) {
00287         startp += 3;
00288     }
00289     sp[0] = ps[startp + 3];
00290     sp[1] = ps[startp + 2];
00291     sp[2] = ps[startp + 1];
00292     sp[3] = spl->sp;    /* ensure endpoint starts inside */
00293 
00294     inside_context.a.p = &sp[3];
00295     inside_context.a.r = &slen2;
00296     bezier_clip(&inside_context, inside, sp, FALSE);
00297 
00298     ps[startp] = sp[3];
00299     ps[startp + 1] = sp[2];
00300     ps[startp + 2] = sp[1];
00301     ps[startp + 3] = sp[0];
00302     return startp;
00303 }
00304 
00305 /* arrowOrthoClip:
00306  * For orthogonal routing, we know each Bezier of spl is a horizontal or vertical
00307  * line segment. We need to guarantee the B-spline stays this way. At present, we shrink
00308  * the arrows if necessary to fit the last segment at either end. Alternatively, we could
00309  * maintain the arrow size by dropping the 3 points of spl, and adding a new spl encoding
00310  * 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
00311  * 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
00312  * that the truncated spl clips to the arrow shape.
00313  */
00314 void arrowOrthoClip(edge_t* e, pointf* ps, int startp, int endp, bezier* spl, int sflag, int eflag)
00315 {
00316     pointf p, q, r, s, t;
00317     double d, tlen, hlen, maxd;
00318 
00319     if (sflag && eflag && (endp == startp)) { /* handle special case of two arrows on a single segment */
00320         p = ps[endp];
00321         q = ps[endp+3];
00322         tlen = arrow_length (e, sflag);
00323         hlen = arrow_length (e, eflag);
00324         d = DIST(p, q);
00325         if (hlen + tlen >= d) {
00326             hlen = tlen = d/3.0;
00327         }
00328         if (p.y == q.y) { /* horz segment */
00329             s.y = t.y = p.y;
00330             if (p.x < q.x) {
00331                 t.x = q.x - hlen;
00332                 s.x = p.x + tlen;
00333             }
00334             else {
00335                 t.x = q.x + hlen;
00336                 s.x = p.x - tlen;
00337             }
00338         }
00339         else {            /* vert segment */
00340             s.x = t.x = p.x;
00341             if (p.y < q.y) {
00342                 t.y = q.y - hlen;
00343                 s.y = p.y + tlen;
00344             }
00345             else {
00346                 t.y = q.y + hlen;
00347                 s.y = p.y - tlen;
00348             }
00349         }
00350         ps[endp] = ps[endp + 1] = s;
00351         ps[endp + 2] = ps[endp + 3] = t;
00352         spl->eflag = eflag, spl->ep = p;
00353         spl->sflag = sflag, spl->sp = q;
00354         return;
00355     }
00356     if (eflag) {
00357         hlen = arrow_length(e, eflag);
00358         p = ps[endp];
00359         q = ps[endp+3];
00360         d = DIST(p, q);
00361         maxd = 0.9*d;
00362         if (hlen >= maxd) {   /* arrow too long */
00363             hlen = maxd;
00364         }
00365         if (p.y == q.y) { /* horz segment */
00366             r.y = p.y;
00367             if (p.x < q.x) r.x = q.x - hlen;
00368             else r.x = q.x + hlen;
00369         }
00370         else {            /* vert segment */
00371             r.x = p.x;
00372             if (p.y < q.y) r.y = q.y - hlen;
00373             else r.y = q.y + hlen;
00374         }
00375         ps[endp + 1] = p;
00376         ps[endp + 2] = ps[endp + 3] = r;
00377         spl->eflag = eflag;
00378         spl->ep = q;
00379     }
00380     if (sflag) {
00381         tlen = arrow_length(e, sflag);
00382         p = ps[startp];
00383         q = ps[startp+3];
00384         d = DIST(p, q);
00385         maxd = 0.9*d;
00386         if (tlen >= maxd) {   /* arrow too long */
00387             tlen = maxd;
00388         }
00389         if (p.y == q.y) { /* horz segment */
00390             r.y = p.y;
00391             if (p.x < q.x) r.x = p.x + tlen;
00392             else r.x = p.x - tlen;
00393         }
00394         else {            /* vert segment */
00395             r.x = p.x;
00396             if (p.y < q.y) r.y = p.y + tlen;
00397             else r.y = p.y - tlen;
00398         }
00399         ps[startp] = ps[startp + 1] = r;
00400         ps[startp + 2] = q;
00401         spl->sflag = sflag;
00402         spl->sp = p;
00403     }
00404 }
00405 
00406 static void arrow_type_normal(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag)
00407 {
00408     pointf q, v, a[5];
00409     double arrowwidth;
00410 
00411     arrowwidth = 0.35;
00412     if (penwidth > 4)
00413         arrowwidth *= penwidth / 4;
00414 
00415     v.x = -u.y * arrowwidth;
00416     v.y = u.x * arrowwidth;
00417     q.x = p.x + u.x;
00418     q.y = p.y + u.y;
00419     if (flag & ARR_MOD_INV) {
00420         a[0] = a[4] = p;
00421         a[1].x = p.x - v.x;
00422         a[1].y = p.y - v.y;
00423         a[2] = q;
00424         a[3].x = p.x + v.x;
00425         a[3].y = p.y + v.y;
00426     } else {
00427         a[0] = a[4] = q;
00428         a[1].x = q.x - v.x;
00429         a[1].y = q.y - v.y;
00430         a[2] = p;
00431         a[3].x = q.x + v.x;
00432         a[3].y = q.y + v.y;
00433     }
00434     if (flag & ARR_MOD_LEFT)
00435         gvrender_polygon(job, a, 3, !(flag & ARR_MOD_OPEN));
00436     else if (flag & ARR_MOD_RIGHT)
00437         gvrender_polygon(job, &a[2], 3, !(flag & ARR_MOD_OPEN));
00438     else
00439         gvrender_polygon(job, &a[1], 3, !(flag & ARR_MOD_OPEN));
00440 }
00441 
00442 static void arrow_type_crow(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag)
00443 {
00444     pointf m, q, v, w, a[9];
00445     double arrowwidth, shaftwidth;
00446 
00447     arrowwidth = 0.45;
00448     if (penwidth > (4 * arrowsize) && (flag & ARR_MOD_INV))
00449         arrowwidth *= penwidth / (4 * arrowsize);
00450 
00451     shaftwidth = 0;
00452     if (penwidth > 1 && (flag & ARR_MOD_INV))
00453         shaftwidth = 0.05 * (penwidth - 1) / arrowsize;   /* arrowsize to cancel the arrowsize term already in u */
00454 
00455     v.x = -u.y * arrowwidth;
00456     v.y = u.x * arrowwidth;
00457     w.x = -u.y * shaftwidth;
00458     w.y = u.x * shaftwidth;
00459     q.x = p.x + u.x;
00460     q.y = p.y + u.y;
00461     m.x = p.x + u.x * 0.5;
00462     m.y = p.y + u.y * 0.5;
00463     if (flag & ARR_MOD_INV) {  /* vee */
00464         a[0] = a[8] = p;
00465         a[1].x = q.x - v.x;
00466         a[1].y = q.y - v.y;
00467         a[2].x = m.x - w.x;
00468         a[2].y = m.y - w.y;
00469         a[3].x = q.x - w.x;
00470         a[3].y = q.y - w.y;
00471         a[4] = q;
00472         a[5].x = q.x + w.x;
00473         a[5].y = q.y + w.y;
00474         a[6].x = m.x + w.x;
00475         a[6].y = m.y + w.y;
00476         a[7].x = q.x + v.x;
00477         a[7].y = q.y + v.y;
00478     } else {                     /* crow */
00479         a[0] = a[8] = q;
00480         a[1].x = p.x - v.x;
00481         a[1].y = p.y - v.y;
00482         a[2].x = m.x - w.x;
00483         a[2].y = m.y - w.y;
00484         a[3].x = p.x;
00485         a[3].y = p.y;
00486         a[4] = p;
00487         a[5].x = p.x;
00488         a[5].y = p.y;
00489         a[6].x = m.x + w.x;
00490         a[6].y = m.y + w.y;
00491         a[7].x = p.x + v.x;
00492         a[7].y = p.y + v.y;
00493     }
00494     if (flag & ARR_MOD_LEFT)
00495         gvrender_polygon(job, a, 6, 1);
00496     else if (flag & ARR_MOD_RIGHT)
00497         gvrender_polygon(job, &a[3], 6, 1);
00498     else
00499         gvrender_polygon(job, a, 9, 1);
00500 }
00501 
00502 static void arrow_type_tee(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag)
00503 {
00504     pointf m, n, q, v, a[4];
00505 
00506     v.x = -u.y;
00507     v.y = u.x;
00508     q.x = p.x + u.x;
00509     q.y = p.y + u.y;
00510     m.x = p.x + u.x * 0.2;
00511     m.y = p.y + u.y * 0.2;
00512     n.x = p.x + u.x * 0.6;
00513     n.y = p.y + u.y * 0.6;
00514     a[0].x = m.x + v.x;
00515     a[0].y = m.y + v.y;
00516     a[1].x = m.x - v.x;
00517     a[1].y = m.y - v.y;
00518     a[2].x = n.x - v.x;
00519     a[2].y = n.y - v.y;
00520     a[3].x = n.x + v.x;
00521     a[3].y = n.y + v.y;
00522     if (flag & ARR_MOD_LEFT) {
00523         a[0] = m;
00524         a[3] = n;
00525     } else if (flag & ARR_MOD_RIGHT) {
00526         a[1] = m;
00527         a[2] = n;
00528     }
00529     gvrender_polygon(job, a, 4, 1);
00530     a[0] = p;
00531     a[1] = q;
00532     gvrender_polyline(job, a, 2);
00533 }
00534 
00535 static void arrow_type_box(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag)
00536 {
00537     pointf m, q, v, a[4];
00538 
00539     v.x = -u.y * 0.4;
00540     v.y = u.x * 0.4;
00541     m.x = p.x + u.x * 0.8;
00542     m.y = p.y + u.y * 0.8;
00543     q.x = p.x + u.x;
00544     q.y = p.y + u.y;
00545     a[0].x = p.x + v.x;
00546     a[0].y = p.y + v.y;
00547     a[1].x = p.x - v.x;
00548     a[1].y = p.y - v.y;
00549     a[2].x = m.x - v.x;
00550     a[2].y = m.y - v.y;
00551     a[3].x = m.x + v.x;
00552     a[3].y = m.y + v.y;
00553     if (flag & ARR_MOD_LEFT) {
00554         a[0] = p;
00555         a[3] = m;
00556     } else if (flag & ARR_MOD_RIGHT) {
00557         a[1] = p;
00558         a[2] = m;
00559     }
00560     gvrender_polygon(job, a, 4, !(flag & ARR_MOD_OPEN));
00561     a[0] = m;
00562     a[1] = q;
00563     gvrender_polyline(job, a, 2);
00564 }
00565 
00566 static void arrow_type_diamond(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag)
00567 {
00568     pointf q, r, v, a[5];
00569 
00570     v.x = -u.y / 3.;
00571     v.y = u.x / 3.;
00572     r.x = p.x + u.x / 2.;
00573     r.y = p.y + u.y / 2.;
00574     q.x = p.x + u.x;
00575     q.y = p.y + u.y;
00576     a[0] = a[4] = q;
00577     a[1].x = r.x + v.x;
00578     a[1].y = r.y + v.y;
00579     a[2] = p;
00580     a[3].x = r.x - v.x;
00581     a[3].y = r.y - v.y;
00582     if (flag & ARR_MOD_LEFT)
00583         gvrender_polygon(job, &a[2], 3, !(flag & ARR_MOD_OPEN));
00584     else if (flag & ARR_MOD_RIGHT)
00585         gvrender_polygon(job, a, 3, !(flag & ARR_MOD_OPEN));
00586     else
00587         gvrender_polygon(job, a, 4, !(flag & ARR_MOD_OPEN));
00588 }
00589 
00590 static void arrow_type_dot(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag)
00591 {
00592     double r;
00593     pointf AF[2];
00594 
00595     r = sqrt(u.x * u.x + u.y * u.y) / 2.;
00596     AF[0].x = p.x + u.x / 2. - r;
00597     AF[0].y = p.y + u.y / 2. - r;
00598     AF[1].x = p.x + u.x / 2. + r;
00599     AF[1].y = p.y + u.y / 2. + r;
00600     gvrender_ellipse(job, AF, 2, !(flag & ARR_MOD_OPEN));
00601 }
00602 
00603 static pointf arrow_gen_type(GVJ_t * job, pointf p, pointf u, double arrowsize, double penwidth, int flag)
00604 {
00605     int f;
00606     arrowtype_t *arrowtype;
00607 
00608     f = flag & ((1 << BITS_PER_ARROW_TYPE) - 1);
00609     for (arrowtype = Arrowtypes; arrowtype->type; arrowtype++) {
00610         if (f == arrowtype->type) {
00611             u.x *= arrowtype->lenfact * arrowsize;
00612             u.y *= arrowtype->lenfact * arrowsize;
00613             (arrowtype->gen) (job, p, u, arrowsize, penwidth, flag);
00614             p.x = p.x + u.x;
00615             p.y = p.y + u.y;
00616             break;
00617         }
00618     }
00619     return p;
00620 }
00621 
00622 boxf arrow_bb(pointf p, pointf u, double arrowsize, int flag)
00623 {
00624     double s;
00625     boxf bb;
00626     double ax,ay,bx,by,cx,cy,dx,dy;
00627     double ux2, uy2;
00628 
00629     /* generate arrowhead vector */
00630     u.x -= p.x;
00631     u.y -= p.y;
00632     /* the EPSILONs are to keep this stable as length of u approaches 0.0 */
00633     s = ARROW_LENGTH * arrowsize / (sqrt(u.x * u.x + u.y * u.y) + EPSILON);
00634     u.x += (u.x >= 0.0) ? EPSILON : -EPSILON;
00635     u.y += (u.y >= 0.0) ? EPSILON : -EPSILON;
00636     u.x *= s;
00637     u.y *= s;
00638 
00639     /* compute all 4 corners of rotated arrowhead bounding box */
00640     ux2 = u.x / 2.;
00641     uy2 = u.y / 2.;
00642     ax = p.x - uy2;
00643     ay = p.y - ux2;
00644     bx = p.x + uy2;
00645     by = p.y + ux2;
00646     cx = ax + u.x;
00647     cy = ay + u.y;
00648     dx = bx + u.x;
00649     dy = by + u.y;
00650 
00651     /* compute a right bb */
00652     bb.UR.x = MAX(ax, MAX(bx, MAX(cx, dx)));
00653     bb.UR.y = MAX(ay, MAX(by, MAX(cy, dy)));
00654     bb.LL.x = MIN(ax, MIN(bx, MIN(cx, dx)));
00655     bb.LL.y = MIN(ay, MIN(by, MIN(cy, dy)));
00656  
00657     return bb;
00658 }
00659 
00660 void arrow_gen(GVJ_t * job, emit_state_t emit_state, pointf p, pointf u, double arrowsize, double penwidth, int flag)
00661 {
00662     obj_state_t *obj = job->obj;
00663     double s;
00664     int i, f;
00665     emit_state_t old_emit_state;
00666 
00667     old_emit_state = obj->emit_state;
00668     obj->emit_state = emit_state;
00669 
00670     /* Dotted and dashed styles on the arrowhead are ugly (dds) */
00671     /* linewidth needs to be reset */
00672     gvrender_set_style(job, job->gvc->defaultlinestyle);
00673 
00674     /* generate arrowhead vector */
00675     u.x -= p.x;
00676     u.y -= p.y;
00677     /* the EPSILONs are to keep this stable as length of u approaches 0.0 */
00678     s = ARROW_LENGTH / (sqrt(u.x * u.x + u.y * u.y) + EPSILON);
00679     u.x += (u.x >= 0.0) ? EPSILON : -EPSILON;
00680     u.y += (u.y >= 0.0) ? EPSILON : -EPSILON;
00681     u.x *= s;
00682     u.y *= s;
00683 
00684     /* the first arrow head - closest to node */
00685     for (i = 0; i < NUMB_OF_ARROW_HEADS; i++) {
00686         f = (flag >> (i * BITS_PER_ARROW)) & ((1 << BITS_PER_ARROW) - 1);
00687         if (f == ARR_TYPE_NONE)
00688             break;
00689         p = arrow_gen_type(job, p, u, arrowsize, penwidth, f);
00690     }
00691 
00692     obj->emit_state = old_emit_state;
00693 }