]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/pic/arcgen.c
merge
[plan9front.git] / sys / src / cmd / pic / arcgen.c
1 #include        <stdio.h>
2 #include        <math.h>
3 #include        "pic.h"
4 #include        "y.tab.h"
5
6 void arc_extreme(double, double, double, double, double, double);
7 int quadrant(double x, double y);
8
9 obj *arcgen(int type)   /* handles circular and (eventually) elliptical arcs */
10 {
11         static double prevw = HT10;
12         static double prevh = HT5;
13         static double prevrad = HT2;
14         static int dtox[2][4] ={ 1, -1, -1, 1, 1, 1, -1, -1 };
15         static int dtoy[2][4] ={ 1, 1, -1, -1, -1, 1, 1, -1 };
16         static int dctrx[2][4] ={ 0, -1, 0, 1, 0, 1, 0, -1 };
17         static int dctry[2][4] ={ 1, 0, -1, 0, -1, 0, 1, 0 };
18         static int nexthv[2][4] ={ U_DIR, L_DIR, D_DIR, R_DIR, D_DIR, R_DIR, U_DIR, L_DIR };
19         double dx2, dy2, ht, phi, r, d;
20         int i, head, to, at, cw, invis, ddtype, battr;
21         obj *p, *ppos;
22         double fromx, fromy, tox, toy, fillval = 0;
23         Attr *ap;
24
25         prevrad = getfval("arcrad");
26         prevh = getfval("arrowht");
27         prevw = getfval("arrowwid");
28         fromx = curx;
29         fromy = cury;
30         head = to = at = cw = invis = ddtype = battr = 0;
31         for (i = 0; i < nattr; i++) {
32                 ap = &attr[i];
33                 switch (ap->a_type) {
34                 case TEXTATTR:
35                         savetext(ap->a_sub, ap->a_val.p);
36                         break;
37                 case HEAD:
38                         head += ap->a_val.i;
39                         break;
40                 case INVIS:
41                         invis = INVIS;
42                         break;
43                 case HEIGHT:    /* length of arrowhead */
44                         prevh = ap->a_val.f;
45                         break;
46                 case WIDTH:     /* width of arrowhead */
47                         prevw = ap->a_val.f;
48                         break;
49                 case RADIUS:
50                         prevrad = ap->a_val.f;
51                         break;
52                 case DIAMETER:
53                         prevrad = ap->a_val.f / 2;
54                         break;
55                 case CW:
56                         cw = 1;
57                         break;
58                 case FROM:      /* start point of arc */
59                         ppos = ap->a_val.o;
60                         fromx = ppos->o_x;
61                         fromy = ppos->o_y;
62                         break;
63                 case TO:        /* end point of arc */
64                         ppos = ap->a_val.o;
65                         tox = ppos->o_x;
66                         toy = ppos->o_y;
67                         to++;
68                         break;
69                 case AT:        /* center of arc */
70                         ppos = ap->a_val.o;
71                         curx = ppos->o_x;
72                         cury = ppos->o_y;
73                         at = 1;
74                         break;
75                 case UP:
76                         hvmode = U_DIR;
77                         break;
78                 case DOWN:
79                         hvmode = D_DIR;
80                         break;
81                 case RIGHT:
82                         hvmode = R_DIR;
83                         break;
84                 case LEFT:
85                         hvmode = L_DIR;
86                         break;
87                 case FILL:
88                         battr |= FILLBIT;
89                         if (ap->a_sub == DEFAULT)
90                                 fillval = getfval("fillval");
91                         else
92                                 fillval = ap->a_val.f;
93                         break;
94                 }
95         }
96         if (!at && !to) {       /* the defaults are mostly OK */
97                 curx = fromx + prevrad * dctrx[cw][hvmode];
98                 cury = fromy + prevrad * dctry[cw][hvmode];
99                 tox = fromx + prevrad * dtox[cw][hvmode];
100                 toy = fromy + prevrad * dtoy[cw][hvmode];
101                 hvmode = nexthv[cw][hvmode];
102         }
103         else if (!at) {
104                 dx2 = (tox - fromx) / 2;
105                 dy2 = (toy - fromy) / 2;
106                 phi = atan2(dy2, dx2) + (cw ? -PI/2 : PI/2);
107                 if (prevrad <= 0.0)
108                         prevrad = dx2*dx2+dy2*dy2;
109                 for (r=prevrad; (d = r*r - (dx2*dx2+dy2*dy2)) <= 0.0; r *= 2)
110                         ;       /* this kludge gets around too-small radii */
111                 prevrad = r;
112                 ht = sqrt(d);
113                 curx = fromx + dx2 + ht * cos(phi);
114                 cury = fromy + dy2 + ht * sin(phi);
115                 dprintf("dx2,dy2=%g,%g, phi=%g, r,ht=%g,%g\n",
116                         dx2, dy2, phi, r, ht);
117         }
118         else if (at && !to) {   /* do we have all the cases??? */
119                 tox = fromx + prevrad * dtox[cw][hvmode];
120                 toy = fromy + prevrad * dtoy[cw][hvmode];
121                 hvmode = nexthv[cw][hvmode];
122         }
123         if (cw) {       /* interchange roles of from-to and heads */
124                 double temp;
125                 temp = fromx; fromx = tox; tox = temp;
126                 temp = fromy; fromy = toy; toy = temp;
127                 if (head == HEAD1)
128                         head = HEAD2;
129                 else if (head == HEAD2)
130                         head = HEAD1;
131         }
132         p = makenode(type, 7);
133         arc_extreme(fromx, fromy, tox, toy, curx, cury);
134         p->o_val[0] = fromx;
135         p->o_val[1] = fromy;
136         p->o_val[2] = tox;
137         p->o_val[3] = toy;
138         if (cw) {
139                 curx = fromx;
140                 cury = fromy;
141         } else {
142                 curx = tox;
143                 cury = toy;
144         }
145         p->o_val[4] = prevw;
146         p->o_val[5] = prevh;
147         p->o_val[6] = prevrad;
148         p->o_attr = head | (cw ? CW_ARC : 0) | invis | ddtype | battr;
149         p->o_fillval = fillval;
150         if (head)
151                 p->o_nhead = getfval("arrowhead");
152         dprintf("arc rad %g at %g %g from %g %g to %g %g head %g %g\n",
153                 prevrad, p->o_x, p->o_y,
154                 p->o_val[0], p->o_val[1], p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5]);
155         return(p);
156 }
157
158 /***************************************************************************
159    bounding box of a circular arc             Eric Grosse  24 May 84
160
161 Conceptually, this routine generates a list consisting of the start,
162 end, and whichever north, east, south, and west points lie on the arc.
163 The bounding box is then the range of this list.
164     list = {start,end}
165     j = quadrant(start)
166     k = quadrant(end)
167     if( j==k && long way 'round )  append north,west,south,east
168     else
169       while( j != k )
170          append center+radius*[j-th of north,west,south,east unit vectors]
171          j += 1  (mod 4)
172     return( bounding box of list )
173 The following code implements this, with simple optimizations.
174 ***********************************************************************/
175
176
177 void arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc)
178                           /* start, end, center */
179 {
180         /* assumes center isn't too far out */
181         double r, xmin, ymin, xmax, ymax;
182         int j, k;
183         x0 -= xc; y0 -= yc;     /* move to center */
184         x1 -= xc; y1 -= yc;
185         xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1;
186         xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1;
187         r = sqrt(x0*x0 + y0*y0);
188         if (r > 0.0) {
189                 j = quadrant(x0,y0);
190                 k = quadrant(x1,y1);
191                 if (j == k && y1*x0 < x1*y0) {
192                         /* viewed as complex numbers, if Im(z1/z0)<0, arc is big */
193                         if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r;
194                         if( xmax <  r) xmax =  r; if( ymax <  r) ymax =  r;
195                 } else {
196                         while (j != k) {
197                                 switch (j) {
198                                         case 1: if( ymax <  r) ymax =  r; break; /* north */
199                                         case 2: if( xmin > -r) xmin = -r; break; /* west */
200                                         case 3: if( ymin > -r) ymin = -r; break; /* south */
201                                         case 4: if( xmax <  r) xmax =  r; break; /* east */
202                                 }
203                                 j = j%4 + 1;
204                         }
205                 }
206         }
207         xmin += xc; ymin += yc;
208         xmax += xc; ymax += yc;
209         extreme(xmin, ymin);
210         extreme(xmax, ymax);
211 }
212
213 quadrant(double x, double y)
214 {
215         if (     x>=0.0 && y> 0.0) return(1);
216         else if( x< 0.0 && y>=0.0) return(2);
217         else if( x<=0.0 && y< 0.0) return(3);
218         else if( x> 0.0 && y<=0.0) return(4);
219         else                       return 0;    /* shut up lint */
220 }
221