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