1 #ifndef lint
2 static char sccsid[] = "@(#)arcgen.c	3.1 (CWI) 85/07/30";
3 #endif lint
4 
5 #include	<stdio.h>
6 #include	<math.h>
7 #include	"pic.h"
8 #include	"y.tab.h"
9 
10 obj *arcgen(type)	/* handles circular and (eventually) elliptical arcs */
11 {
12 	static float prevw = HT10;
13 	static float prevh = HT5;
14 	static float prevrad = HT2;
15 	static int dtox[2][4] ={ 1, -1, -1, 1, 1, 1, -1, -1 };
16 	static int dtoy[2][4] ={ 1, 1, -1, -1, -1, 1, 1, -1 };
17 	static int dctrx[2][4] ={ 0, -1, 0, 1, 0, 1, 0, -1 };
18 	static int dctry[2][4] ={ 1, 0, -1, 0, -1, 0, 1, 0 };
19 	static int nexthv[2][4] ={ U_DIR, L_DIR, D_DIR, R_DIR, D_DIR, R_DIR, U_DIR, L_DIR };
20 	float dx2, dy2, ht, phi, r, d, ddval;
21 	int i, head, to, at, cw, invis, ddtype;
22 	obj *p, *ppos;
23 	float fromx, fromy, tox, toy;
24 	Attr *ap;
25 
26 	prevrad = getfval("arcrad");
27 	prevh = getfval("arrowht");
28 	prevw = getfval("arrowwid");
29 	fromx = curx;
30 	fromy = cury;
31 	head = to = at = cw = invis = ddtype = 0;
32 	for (i = 0; i < nattr; i++) {
33 		ap = &attr[i];
34 		switch (ap->a_type) {
35 		case TEXTATTR:
36 			savetext(ap->a_sub, ap->a_val.p);
37 			break;
38 		case HEAD:
39 			head += ap->a_val.i;
40 			break;
41 		case INVIS:
42 			invis = INVIS;
43 			break;
44 		case DOT:
45 		case DASH:
46 			ddtype = ap->a_type==DOT ? DOTBIT : DASHBIT;
47 			if (ap->a_sub == DEFAULT)
48 				ddval = getfval("dashwid");
49 			else
50 				ddval = ap->a_val.f;
51 			break;
52 		case HEIGHT:	/* length of arrowhead */
53 			prevh = ap->a_val.f;
54 			break;
55 		case WIDTH:	/* width of arrowhead */
56 			prevw = ap->a_val.f;
57 			break;
58 		case RADIUS:
59 			prevrad = ap->a_val.f;
60 			break;
61 		case DIAMETER:
62 			prevrad = ap->a_val.f / 2;
63 			break;
64 		case CW:
65 			cw = 1;
66 			break;
67 		case FROM:	/* start point of arc */
68 			ppos = ap->a_val.o;
69 			fromx = ppos->o_x;
70 			fromy = ppos->o_y;
71 			break;
72 		case TO:	/* end point of arc */
73 			ppos = ap->a_val.o;
74 			tox = ppos->o_x;
75 			toy = ppos->o_y;
76 			to++;
77 			break;
78 		case AT:	/* center of arc */
79 			ppos = ap->a_val.o;
80 			curx = ppos->o_x;
81 			cury = ppos->o_y;
82 			at = 1;
83 			break;
84 		case UP:
85 			hvmode = U_DIR;
86 			break;
87 		case DOWN:
88 			hvmode = D_DIR;
89 			break;
90 		case RIGHT:
91 			hvmode = R_DIR;
92 			break;
93 		case LEFT:
94 			hvmode = L_DIR;
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 		float 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;
151 	if (head)
152 		p->o_nhead = getfval("arrowhead");
153 	dprintf("arc rad %g at %g %g from %g %g to %g %g head %g %g\n",
154 		prevrad, p->o_x, p->o_y,
155 		p->o_val[0], p->o_val[1], p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5]);
156 	return(p);
157 }
158 
159 /***************************************************************************
160    bounding box of a circular arc             Eric Grosse  24 May 84
161 
162 Conceptually, this routine generates a list consisting of the start,
163 end, and whichever north, east, south, and west points lie on the arc.
164 The bounding box is then the range of this list.
165     list = {start,end}
166     j = quadrant(start)
167     k = quadrant(end)
168     if( j==k && long way 'round )  append north,west,south,east
169     else
170       while( j != k )
171          append center+radius*[j-th of north,west,south,east unit vectors]
172          j += 1  (mod 4)
173     return( bounding box of list )
174 The following code implements this, with simple optimizations.
175 ***********************************************************************/
176 
177 
178 arc_extreme(x0, y0, x1, y1, xc, yc)
179 	double x0, y0, x1, y1, xc, yc;  /* start, end, center */
180 {
181 	/* assumes center isn't too far out */
182 	double r, x, y, xmin, ymin, xmax, ymax;
183 	int j, k;
184 	x0 -= xc; y0 -= yc;	/* move to center */
185 	x1 -= xc; y1 -= yc;
186 	xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1;
187 	xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1;
188 	r = sqrt(x0*x0 + y0*y0);
189 	if (r > 0.0) {
190 		j = quadrant(x0,y0);
191 		k = quadrant(x1,y1);
192 		if (j == k && y1*x0 < x1*y0) {
193 			/* viewed as complex numbers, if Im(z1/z0)<0, arc is big */
194 			if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r;
195 			if( xmax <  r) xmax =  r; if( ymax <  r) ymax =  r;
196 		} else {
197 			while (j != k) {
198 				switch (j) {
199 					case 1: if( ymax <  r) ymax =  r; break; /* north */
200 					case 2: if( xmin > -r) xmin = -r; break; /* west */
201 					case 3: if( ymin > -r) ymin = -r; break; /* south */
202 					case 4: if( xmax <  r) xmax =  r; break; /* east */
203 				}
204 				j = j%4 + 1;
205 			}
206 		}
207 	}
208 	xmin += xc; ymin += yc;
209 	xmax += xc; ymax += yc;
210 	extreme(xmin, ymin);
211 	extreme(xmax, ymax);
212 }
213 
214 quadrant(x,y)
215 	double x, y;
216 {
217 	if (     x>=0.0 && y> 0.0) return(1);
218 	else if( x< 0.0 && y>=0.0) return(2);
219 	else if( x<=0.0 && y< 0.0) return(3);
220 	else if( x> 0.0 && y<=0.0) return(4);
221 	else
222 		{ fprintf(stderr,"can't happen: x,y=%g,%g",x,y); exit(1);}
223 }
224 
225