1 #include	<stdio.h>
2 #include	<math.h>
3 #include	"pic.h"
4 #include	"y.tab.h"
5 
6 obj*
arcgen(int type)7 arcgen(int type)	/* handles circular and (eventually) elliptical arcs */
8 {
9 	static double prevw = HT10;
10 	static double prevh = HT5;
11 	static double prevrad = HT2;
12 	static int dtox[2][4] ={ 1, -1, -1, 1, 1, 1, -1, -1 };
13 	static int dtoy[2][4] ={ 1, 1, -1, -1, -1, 1, 1, -1 };
14 	static int dctrx[2][4] ={ 0, -1, 0, 1, 0, 1, 0, -1 };
15 	static int dctry[2][4] ={ 1, 0, -1, 0, -1, 0, 1, 0 };
16 	static int nexthv[2][4] ={ U_DIR, L_DIR, D_DIR, R_DIR, D_DIR, R_DIR, U_DIR, L_DIR };
17 	double dx2, dy2, ht, phi, r, d;
18 	int i, head, to, at, cw, invis, ddtype;
19 	obj *p, *ppos;
20 	double fromx, fromy, tox, toy;
21 	Attr *ap;
22 
23 	tox = 0;
24 	toy = 0;
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 = 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 		}
88 	}
89 	if (!at && !to) {	/* the defaults are mostly OK */
90 		curx = fromx + prevrad * dctrx[cw][hvmode];
91 		cury = fromy + prevrad * dctry[cw][hvmode];
92 		tox = fromx + prevrad * dtox[cw][hvmode];
93 		toy = fromy + prevrad * dtoy[cw][hvmode];
94 		hvmode = nexthv[cw][hvmode];
95 	}
96 	else if (!at) {
97 		dx2 = (tox - fromx) / 2;
98 		dy2 = (toy - fromy) / 2;
99 		phi = atan2(dy2, dx2) + (cw ? -PI/2 : PI/2);
100 		if (prevrad <= 0.0)
101 			prevrad = dx2*dx2+dy2*dy2;
102 		for (r=prevrad; (d = r*r - (dx2*dx2+dy2*dy2)) <= 0.0; r *= 2)
103 			;	/* this kludge gets around too-small radii */
104 		prevrad = r;
105 		ht = sqrt(d);
106 		curx = fromx + dx2 + ht * cos(phi);
107 		cury = fromy + dy2 + ht * sin(phi);
108 		dprintf("dx2,dy2=%g,%g, phi=%g, r,ht=%g,%g\n",
109 			dx2, dy2, phi, r, ht);
110 	}
111 	else if (at && !to) {	/* do we have all the cases??? */
112 		tox = fromx + prevrad * dtox[cw][hvmode];
113 		toy = fromy + prevrad * dtoy[cw][hvmode];
114 		hvmode = nexthv[cw][hvmode];
115 	}
116 	if (cw) {	/* interchange roles of from-to and heads */
117 		double temp;
118 		temp = fromx; fromx = tox; tox = temp;
119 		temp = fromy; fromy = toy; toy = temp;
120 		if (head == HEAD1)
121 			head = HEAD2;
122 		else if (head == HEAD2)
123 			head = HEAD1;
124 	}
125 	p = makenode(type, 7);
126 	arc_extreme(fromx, fromy, tox, toy, curx, cury);
127 	p->o_val[0] = fromx;
128 	p->o_val[1] = fromy;
129 	p->o_val[2] = tox;
130 	p->o_val[3] = toy;
131 	if (cw) {
132 		curx = fromx;
133 		cury = fromy;
134 	} else {
135 		curx = tox;
136 		cury = toy;
137 	}
138 	p->o_val[4] = prevw;
139 	p->o_val[5] = prevh;
140 	p->o_val[6] = prevrad;
141 	p->o_attr = head | (cw ? CW_ARC : 0) | invis | ddtype;
142 	if (head)
143 		p->o_nhead = getfval("arrowhead");
144 	dprintf("arc rad %g at %g %g from %g %g to %g %g head %g %g\n",
145 		prevrad, p->o_x, p->o_y,
146 		p->o_val[0], p->o_val[1], p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5]);
147 	return(p);
148 }
149 
150 /***************************************************************************
151    bounding box of a circular arc             Eric Grosse  24 May 84
152 
153 Conceptually, this routine generates a list consisting of the start,
154 end, and whichever north, east, south, and west points lie on the arc.
155 The bounding box is then the range of this list.
156     list = {start,end}
157     j = quadrant(start)
158     k = quadrant(end)
159     if( j==k && long way 'round )  append north,west,south,east
160     else
161       while( j != k )
162          append center+radius*[j-th of north,west,south,east unit vectors]
163          j += 1  (mod 4)
164     return( bounding box of list )
165 The following code implements this, with simple optimizations.
166 ***********************************************************************/
167 
168 
169 void
arc_extreme(double x0,double y0,double x1,double y1,double xc,double yc)170 arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc)
171 {
172 	/* assumes center isn't too far out */
173 	double r, xmin, ymin, xmax, ymax;
174 	int j, k;
175 	x0 -= xc; y0 -= yc;	/* move to center */
176 	x1 -= xc; y1 -= yc;
177 	xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1;
178 	xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1;
179 	r = sqrt(x0*x0 + y0*y0);
180 	if (r > 0.0) {
181 		j = quadrant(x0,y0);
182 		k = quadrant(x1,y1);
183 		if (j == k && y1*x0 < x1*y0) {
184 			/* viewed as complex numbers, if Im(z1/z0)<0, arc is big */
185 			if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r;
186 			if( xmax <  r) xmax =  r; if( ymax <  r) ymax =  r;
187 		} else {
188 			while (j != k) {
189 				switch (j) {
190 					case 1: if( ymax <  r) ymax =  r; break; /* north */
191 					case 2: if( xmin > -r) xmin = -r; break; /* west */
192 					case 3: if( ymin > -r) ymin = -r; break; /* south */
193 					case 4: if( xmax <  r) xmax =  r; break; /* east */
194 				}
195 				j = j%4 + 1;
196 			}
197 		}
198 	}
199 	xmin += xc; ymin += yc;
200 	xmax += xc; ymax += yc;
201 	extreme(xmin, ymin);
202 	extreme(xmax, ymax);
203 }
204 
205 int
quadrant(double x,double y)206 quadrant(double x, double y)
207 {
208 	if (     x>=0.0 && y> 0.0) return(1);
209 	else if( x< 0.0 && y>=0.0) return(2);
210 	else if( x<=0.0 && y< 0.0) return(3);
211 	else if( x> 0.0 && y<=0.0) return(4);
212 	else			   return 0;	/* shut up lint */
213 }
214