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