initial port of plan9 troff to linux
[troff.git] / pic / arcgen.c
blobd1b64224d24862cd717d3ebe74eb15864ba2a3f4
1 #include <stdio.h>
2 #include <math.h>
3 #include "pic.h"
4 #include "y.tab.h"
6 void arc_extreme(double, double, double, double, double, double);
7 int quadrant(double x, double y);
9 obj *arcgen(int type) /* handles circular and (eventually) elliptical arcs */
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;
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;
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];
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);
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];
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;
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;
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);
158 /***************************************************************************
159 bounding box of a circular arc Eric Grosse 24 May 84
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 ***********************************************************************/
177 void arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc)
178 /* start, end, center */
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 */
203 j = j%4 + 1;
207 xmin += xc; ymin += yc;
208 xmax += xc; ymax += yc;
209 extreme(xmin, ymin);
210 extreme(xmax, ymax);
213 quadrant(double x, double y)
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 */