xref: /plan9/sys/src/cmd/pic/arcgen.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
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 	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;
94 		}
95 	}
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];
102 	}
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);
117 	}
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];
122 	}
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;
131 	}
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;
144 	}
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);
156 }
157 
158 /***************************************************************************
159    bounding box of a circular arc             Eric Grosse  24 May 84
160 
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 ***********************************************************************/
175 
176 
arc_extreme(double x0,double y0,double x1,double y1,double xc,double yc)177 void arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc)
178 			  /* start, end, center */
179 {
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 */
202 				}
203 				j = j%4 + 1;
204 			}
205 		}
206 	}
207 	xmin += xc; ymin += yc;
208 	xmax += xc; ymax += yc;
209 	extreme(xmin, ymin);
210 	extreme(xmax, ymax);
211 }
212 
quadrant(double x,double y)213 quadrant(double x, double y)
214 {
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 */
220 }
221 
222