xref: /plan9/sys/src/cmd/pic/arcgen.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
13e12c5d1SDavid du Colombier #include	<stdio.h>
23e12c5d1SDavid du Colombier #include	<math.h>
33e12c5d1SDavid du Colombier #include	"pic.h"
43e12c5d1SDavid du Colombier #include	"y.tab.h"
53e12c5d1SDavid du Colombier 
63e12c5d1SDavid du Colombier void arc_extreme(double, double, double, double, double, double);
73e12c5d1SDavid du Colombier int quadrant(double x, double y);
83e12c5d1SDavid du Colombier 
arcgen(int type)93e12c5d1SDavid du Colombier obj *arcgen(int type)	/* handles circular and (eventually) elliptical arcs */
103e12c5d1SDavid du Colombier {
113e12c5d1SDavid du Colombier 	static double prevw = HT10;
123e12c5d1SDavid du Colombier 	static double prevh = HT5;
133e12c5d1SDavid du Colombier 	static double prevrad = HT2;
143e12c5d1SDavid du Colombier 	static int dtox[2][4] ={ 1, -1, -1, 1, 1, 1, -1, -1 };
153e12c5d1SDavid du Colombier 	static int dtoy[2][4] ={ 1, 1, -1, -1, -1, 1, 1, -1 };
163e12c5d1SDavid du Colombier 	static int dctrx[2][4] ={ 0, -1, 0, 1, 0, 1, 0, -1 };
173e12c5d1SDavid du Colombier 	static int dctry[2][4] ={ 1, 0, -1, 0, -1, 0, 1, 0 };
183e12c5d1SDavid du Colombier 	static int nexthv[2][4] ={ U_DIR, L_DIR, D_DIR, R_DIR, D_DIR, R_DIR, U_DIR, L_DIR };
193e12c5d1SDavid du Colombier 	double dx2, dy2, ht, phi, r, d;
20*7dd7cddfSDavid du Colombier 	int i, head, to, at, cw, invis, ddtype, battr;
213e12c5d1SDavid du Colombier 	obj *p, *ppos;
22*7dd7cddfSDavid du Colombier 	double fromx, fromy, tox, toy, fillval = 0;
233e12c5d1SDavid du Colombier 	Attr *ap;
243e12c5d1SDavid du Colombier 
253e12c5d1SDavid du Colombier 	prevrad = getfval("arcrad");
263e12c5d1SDavid du Colombier 	prevh = getfval("arrowht");
273e12c5d1SDavid du Colombier 	prevw = getfval("arrowwid");
283e12c5d1SDavid du Colombier 	fromx = curx;
293e12c5d1SDavid du Colombier 	fromy = cury;
30*7dd7cddfSDavid du Colombier 	head = to = at = cw = invis = ddtype = battr = 0;
313e12c5d1SDavid du Colombier 	for (i = 0; i < nattr; i++) {
323e12c5d1SDavid du Colombier 		ap = &attr[i];
333e12c5d1SDavid du Colombier 		switch (ap->a_type) {
343e12c5d1SDavid du Colombier 		case TEXTATTR:
353e12c5d1SDavid du Colombier 			savetext(ap->a_sub, ap->a_val.p);
363e12c5d1SDavid du Colombier 			break;
373e12c5d1SDavid du Colombier 		case HEAD:
383e12c5d1SDavid du Colombier 			head += ap->a_val.i;
393e12c5d1SDavid du Colombier 			break;
403e12c5d1SDavid du Colombier 		case INVIS:
413e12c5d1SDavid du Colombier 			invis = INVIS;
423e12c5d1SDavid du Colombier 			break;
433e12c5d1SDavid du Colombier 		case HEIGHT:	/* length of arrowhead */
443e12c5d1SDavid du Colombier 			prevh = ap->a_val.f;
453e12c5d1SDavid du Colombier 			break;
463e12c5d1SDavid du Colombier 		case WIDTH:	/* width of arrowhead */
473e12c5d1SDavid du Colombier 			prevw = ap->a_val.f;
483e12c5d1SDavid du Colombier 			break;
493e12c5d1SDavid du Colombier 		case RADIUS:
503e12c5d1SDavid du Colombier 			prevrad = ap->a_val.f;
513e12c5d1SDavid du Colombier 			break;
523e12c5d1SDavid du Colombier 		case DIAMETER:
533e12c5d1SDavid du Colombier 			prevrad = ap->a_val.f / 2;
543e12c5d1SDavid du Colombier 			break;
553e12c5d1SDavid du Colombier 		case CW:
563e12c5d1SDavid du Colombier 			cw = 1;
573e12c5d1SDavid du Colombier 			break;
583e12c5d1SDavid du Colombier 		case FROM:	/* start point of arc */
593e12c5d1SDavid du Colombier 			ppos = ap->a_val.o;
603e12c5d1SDavid du Colombier 			fromx = ppos->o_x;
613e12c5d1SDavid du Colombier 			fromy = ppos->o_y;
623e12c5d1SDavid du Colombier 			break;
633e12c5d1SDavid du Colombier 		case TO:	/* end point of arc */
643e12c5d1SDavid du Colombier 			ppos = ap->a_val.o;
653e12c5d1SDavid du Colombier 			tox = ppos->o_x;
663e12c5d1SDavid du Colombier 			toy = ppos->o_y;
673e12c5d1SDavid du Colombier 			to++;
683e12c5d1SDavid du Colombier 			break;
693e12c5d1SDavid du Colombier 		case AT:	/* center of arc */
703e12c5d1SDavid du Colombier 			ppos = ap->a_val.o;
713e12c5d1SDavid du Colombier 			curx = ppos->o_x;
723e12c5d1SDavid du Colombier 			cury = ppos->o_y;
733e12c5d1SDavid du Colombier 			at = 1;
743e12c5d1SDavid du Colombier 			break;
753e12c5d1SDavid du Colombier 		case UP:
763e12c5d1SDavid du Colombier 			hvmode = U_DIR;
773e12c5d1SDavid du Colombier 			break;
783e12c5d1SDavid du Colombier 		case DOWN:
793e12c5d1SDavid du Colombier 			hvmode = D_DIR;
803e12c5d1SDavid du Colombier 			break;
813e12c5d1SDavid du Colombier 		case RIGHT:
823e12c5d1SDavid du Colombier 			hvmode = R_DIR;
833e12c5d1SDavid du Colombier 			break;
843e12c5d1SDavid du Colombier 		case LEFT:
853e12c5d1SDavid du Colombier 			hvmode = L_DIR;
863e12c5d1SDavid du Colombier 			break;
87*7dd7cddfSDavid du Colombier 		case FILL:
88*7dd7cddfSDavid du Colombier 			battr |= FILLBIT;
89*7dd7cddfSDavid du Colombier 			if (ap->a_sub == DEFAULT)
90*7dd7cddfSDavid du Colombier 				fillval = getfval("fillval");
91*7dd7cddfSDavid du Colombier 			else
92*7dd7cddfSDavid du Colombier 				fillval = ap->a_val.f;
93*7dd7cddfSDavid du Colombier 			break;
943e12c5d1SDavid du Colombier 		}
953e12c5d1SDavid du Colombier 	}
963e12c5d1SDavid du Colombier 	if (!at && !to) {	/* the defaults are mostly OK */
973e12c5d1SDavid du Colombier 		curx = fromx + prevrad * dctrx[cw][hvmode];
983e12c5d1SDavid du Colombier 		cury = fromy + prevrad * dctry[cw][hvmode];
993e12c5d1SDavid du Colombier 		tox = fromx + prevrad * dtox[cw][hvmode];
1003e12c5d1SDavid du Colombier 		toy = fromy + prevrad * dtoy[cw][hvmode];
1013e12c5d1SDavid du Colombier 		hvmode = nexthv[cw][hvmode];
1023e12c5d1SDavid du Colombier 	}
1033e12c5d1SDavid du Colombier 	else if (!at) {
1043e12c5d1SDavid du Colombier 		dx2 = (tox - fromx) / 2;
1053e12c5d1SDavid du Colombier 		dy2 = (toy - fromy) / 2;
1063e12c5d1SDavid du Colombier 		phi = atan2(dy2, dx2) + (cw ? -PI/2 : PI/2);
1073e12c5d1SDavid du Colombier 		if (prevrad <= 0.0)
1083e12c5d1SDavid du Colombier 			prevrad = dx2*dx2+dy2*dy2;
1093e12c5d1SDavid du Colombier 		for (r=prevrad; (d = r*r - (dx2*dx2+dy2*dy2)) <= 0.0; r *= 2)
1103e12c5d1SDavid du Colombier 			;	/* this kludge gets around too-small radii */
1113e12c5d1SDavid du Colombier 		prevrad = r;
1123e12c5d1SDavid du Colombier 		ht = sqrt(d);
1133e12c5d1SDavid du Colombier 		curx = fromx + dx2 + ht * cos(phi);
1143e12c5d1SDavid du Colombier 		cury = fromy + dy2 + ht * sin(phi);
1153e12c5d1SDavid du Colombier 		dprintf("dx2,dy2=%g,%g, phi=%g, r,ht=%g,%g\n",
1163e12c5d1SDavid du Colombier 			dx2, dy2, phi, r, ht);
1173e12c5d1SDavid du Colombier 	}
1183e12c5d1SDavid du Colombier 	else if (at && !to) {	/* do we have all the cases??? */
1193e12c5d1SDavid du Colombier 		tox = fromx + prevrad * dtox[cw][hvmode];
1203e12c5d1SDavid du Colombier 		toy = fromy + prevrad * dtoy[cw][hvmode];
1213e12c5d1SDavid du Colombier 		hvmode = nexthv[cw][hvmode];
1223e12c5d1SDavid du Colombier 	}
1233e12c5d1SDavid du Colombier 	if (cw) {	/* interchange roles of from-to and heads */
1243e12c5d1SDavid du Colombier 		double temp;
1253e12c5d1SDavid du Colombier 		temp = fromx; fromx = tox; tox = temp;
1263e12c5d1SDavid du Colombier 		temp = fromy; fromy = toy; toy = temp;
1273e12c5d1SDavid du Colombier 		if (head == HEAD1)
1283e12c5d1SDavid du Colombier 			head = HEAD2;
1293e12c5d1SDavid du Colombier 		else if (head == HEAD2)
1303e12c5d1SDavid du Colombier 			head = HEAD1;
1313e12c5d1SDavid du Colombier 	}
1323e12c5d1SDavid du Colombier 	p = makenode(type, 7);
1333e12c5d1SDavid du Colombier 	arc_extreme(fromx, fromy, tox, toy, curx, cury);
1343e12c5d1SDavid du Colombier 	p->o_val[0] = fromx;
1353e12c5d1SDavid du Colombier 	p->o_val[1] = fromy;
1363e12c5d1SDavid du Colombier 	p->o_val[2] = tox;
1373e12c5d1SDavid du Colombier 	p->o_val[3] = toy;
1383e12c5d1SDavid du Colombier 	if (cw) {
1393e12c5d1SDavid du Colombier 		curx = fromx;
1403e12c5d1SDavid du Colombier 		cury = fromy;
1413e12c5d1SDavid du Colombier 	} else {
1423e12c5d1SDavid du Colombier 		curx = tox;
1433e12c5d1SDavid du Colombier 		cury = toy;
1443e12c5d1SDavid du Colombier 	}
1453e12c5d1SDavid du Colombier 	p->o_val[4] = prevw;
1463e12c5d1SDavid du Colombier 	p->o_val[5] = prevh;
1473e12c5d1SDavid du Colombier 	p->o_val[6] = prevrad;
148*7dd7cddfSDavid du Colombier 	p->o_attr = head | (cw ? CW_ARC : 0) | invis | ddtype | battr;
149*7dd7cddfSDavid du Colombier 	p->o_fillval = fillval;
1503e12c5d1SDavid du Colombier 	if (head)
1513e12c5d1SDavid du Colombier 		p->o_nhead = getfval("arrowhead");
1523e12c5d1SDavid du Colombier 	dprintf("arc rad %g at %g %g from %g %g to %g %g head %g %g\n",
1533e12c5d1SDavid du Colombier 		prevrad, p->o_x, p->o_y,
1543e12c5d1SDavid du Colombier 		p->o_val[0], p->o_val[1], p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5]);
1553e12c5d1SDavid du Colombier 	return(p);
1563e12c5d1SDavid du Colombier }
1573e12c5d1SDavid du Colombier 
1583e12c5d1SDavid du Colombier /***************************************************************************
1593e12c5d1SDavid du Colombier    bounding box of a circular arc             Eric Grosse  24 May 84
1603e12c5d1SDavid du Colombier 
1613e12c5d1SDavid du Colombier Conceptually, this routine generates a list consisting of the start,
1623e12c5d1SDavid du Colombier end, and whichever north, east, south, and west points lie on the arc.
1633e12c5d1SDavid du Colombier The bounding box is then the range of this list.
1643e12c5d1SDavid du Colombier     list = {start,end}
1653e12c5d1SDavid du Colombier     j = quadrant(start)
1663e12c5d1SDavid du Colombier     k = quadrant(end)
1673e12c5d1SDavid du Colombier     if( j==k && long way 'round )  append north,west,south,east
1683e12c5d1SDavid du Colombier     else
1693e12c5d1SDavid du Colombier       while( j != k )
1703e12c5d1SDavid du Colombier          append center+radius*[j-th of north,west,south,east unit vectors]
1713e12c5d1SDavid du Colombier          j += 1  (mod 4)
1723e12c5d1SDavid du Colombier     return( bounding box of list )
1733e12c5d1SDavid du Colombier The following code implements this, with simple optimizations.
1743e12c5d1SDavid du Colombier ***********************************************************************/
1753e12c5d1SDavid du Colombier 
1763e12c5d1SDavid du Colombier 
arc_extreme(double x0,double y0,double x1,double y1,double xc,double yc)1773e12c5d1SDavid du Colombier void arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc)
1783e12c5d1SDavid du Colombier 			  /* start, end, center */
1793e12c5d1SDavid du Colombier {
1803e12c5d1SDavid du Colombier 	/* assumes center isn't too far out */
1813e12c5d1SDavid du Colombier 	double r, xmin, ymin, xmax, ymax;
1823e12c5d1SDavid du Colombier 	int j, k;
1833e12c5d1SDavid du Colombier 	x0 -= xc; y0 -= yc;	/* move to center */
1843e12c5d1SDavid du Colombier 	x1 -= xc; y1 -= yc;
1853e12c5d1SDavid du Colombier 	xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1;
1863e12c5d1SDavid du Colombier 	xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1;
1873e12c5d1SDavid du Colombier 	r = sqrt(x0*x0 + y0*y0);
1883e12c5d1SDavid du Colombier 	if (r > 0.0) {
1893e12c5d1SDavid du Colombier 		j = quadrant(x0,y0);
1903e12c5d1SDavid du Colombier 		k = quadrant(x1,y1);
1913e12c5d1SDavid du Colombier 		if (j == k && y1*x0 < x1*y0) {
1923e12c5d1SDavid du Colombier 			/* viewed as complex numbers, if Im(z1/z0)<0, arc is big */
1933e12c5d1SDavid du Colombier 			if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r;
1943e12c5d1SDavid du Colombier 			if( xmax <  r) xmax =  r; if( ymax <  r) ymax =  r;
1953e12c5d1SDavid du Colombier 		} else {
1963e12c5d1SDavid du Colombier 			while (j != k) {
1973e12c5d1SDavid du Colombier 				switch (j) {
1983e12c5d1SDavid du Colombier 					case 1: if( ymax <  r) ymax =  r; break; /* north */
1993e12c5d1SDavid du Colombier 					case 2: if( xmin > -r) xmin = -r; break; /* west */
2003e12c5d1SDavid du Colombier 					case 3: if( ymin > -r) ymin = -r; break; /* south */
2013e12c5d1SDavid du Colombier 					case 4: if( xmax <  r) xmax =  r; break; /* east */
2023e12c5d1SDavid du Colombier 				}
2033e12c5d1SDavid du Colombier 				j = j%4 + 1;
2043e12c5d1SDavid du Colombier 			}
2053e12c5d1SDavid du Colombier 		}
2063e12c5d1SDavid du Colombier 	}
2073e12c5d1SDavid du Colombier 	xmin += xc; ymin += yc;
2083e12c5d1SDavid du Colombier 	xmax += xc; ymax += yc;
2093e12c5d1SDavid du Colombier 	extreme(xmin, ymin);
2103e12c5d1SDavid du Colombier 	extreme(xmax, ymax);
2113e12c5d1SDavid du Colombier }
2123e12c5d1SDavid du Colombier 
quadrant(double x,double y)2133e12c5d1SDavid du Colombier quadrant(double x, double y)
2143e12c5d1SDavid du Colombier {
2153e12c5d1SDavid du Colombier 	if (     x>=0.0 && y> 0.0) return(1);
2163e12c5d1SDavid du Colombier 	else if( x< 0.0 && y>=0.0) return(2);
2173e12c5d1SDavid du Colombier 	else if( x<=0.0 && y< 0.0) return(3);
2183e12c5d1SDavid du Colombier 	else if( x> 0.0 && y<=0.0) return(4);
2193e12c5d1SDavid du Colombier 	else			   return 0;	/* shut up lint */
2203e12c5d1SDavid du Colombier }
2213e12c5d1SDavid du Colombier 
222