1*25858Sslatteng /*	draw.c	1.11	86/01/12
214924Sslatteng  *
314924Sslatteng  *	This file contains the functions for producing the graphics
415347Sslatteng  *   images in the canon/imagen driver for ditroff.
514924Sslatteng  */
614924Sslatteng 
714924Sslatteng 
814924Sslatteng #include <stdio.h>
914924Sslatteng #include <ctype.h>
1014924Sslatteng #include <math.h>
1114924Sslatteng #include "canon.h"
1214924Sslatteng 
1314924Sslatteng 				/* imports from dip.c */
1416187Sslatteng #define  FATAL		1
1514924Sslatteng #define  hmot(n)	hpos += n;
1614924Sslatteng #define  hgoto(n)	hpos = n;
1714924Sslatteng #define  vmot(n)	vpos += n;
1814924Sslatteng #define  vgoto(n)	vpos = n;
1914924Sslatteng 
2015622Sslatteng extern int output;
2114924Sslatteng extern int hpos;
2214924Sslatteng extern int vpos;
2314924Sslatteng extern int MAXX;
2414924Sslatteng extern int MAXY;
2514924Sslatteng extern FILE *tf;
2614924Sslatteng extern putint();
2714924Sslatteng 
2814924Sslatteng #define word(x)	putint(x,tf)
2914924Sslatteng #define byte(x)	putc(x,tf)
3014924Sslatteng #define MAXPOINTS 200	/* number of points legal for a curve */
3114924Sslatteng 
3214924Sslatteng #define SOLID -1	/* line styles:  these are used as bit masks to */
3314924Sslatteng #define DOTTED 004	/* create the right style lines. */
3414924Sslatteng #define DASHED 020
3514924Sslatteng #define DOTDASHED 024
3614924Sslatteng #define LONGDASHED 074
3714924Sslatteng 				/* constants... */
3814924Sslatteng #define  pi		3.14159265358979324
3914924Sslatteng 
4014924Sslatteng #define START	1
4114924Sslatteng #define POINT	0
4215347Sslatteng /* the imagen complains if a path is drawn at < 1, or > limit, so truncate. */
4316030Sslatteng #define xbound(x)	((x) < 1 ? 1 : (x) > MAXX ? MAXX : (x))
4416030Sslatteng #define ybound(y)	((y) < 1 ? 1 : (y) > MAXY ? MAXY : (y))
4514924Sslatteng 
4614924Sslatteng 
4714924Sslatteng int	linethickness = -1;	/* number of pixels wide to make lines */
4814924Sslatteng int	linmod = SOLID;		/* type of line (SOLID, DOTTED, DASHED...) */
4916461Sslatteng int	polyborder = 1;		/* flag for whether or not to draw a border */
5014924Sslatteng 
5114924Sslatteng 
5214924Sslatteng 
5314924Sslatteng /*----------------------------------------------------------------------------
5414924Sslatteng  | Routine:	drawline (horizontal_motion, vertical_motion)
5514924Sslatteng  |
5614924Sslatteng  | Results:	Draws a line of "linethickness" width and "linmod" style
5714924Sslatteng  |		from current (hpos, vpos) to (hpos + dh, vpos + dv).
5814924Sslatteng  |
5914924Sslatteng  | Side Efct:	Resulting position is at end of line (hpos + dh, vpos + dv)
6014924Sslatteng  *----------------------------------------------------------------------------*/
6114924Sslatteng 
drawline(dh,dv)6214924Sslatteng drawline(dh, dv)
6314924Sslatteng register int dh;
6414924Sslatteng register int dv;
6514924Sslatteng {
6615622Sslatteng     if (output) HGtline (hpos, vpos, hpos + dh, vpos + dv);
6714924Sslatteng     hmot (dh);					/* new position is at */
6814924Sslatteng     vmot (dv);					/* the end of the line */
6914924Sslatteng }
7014924Sslatteng 
7114924Sslatteng 
7214924Sslatteng /*----------------------------------------------------------------------------
7314924Sslatteng  | Routine:	drawcirc (diameter)
7414924Sslatteng  |
7514924Sslatteng  | Results:	Draws a circle with leftmost point at current (hpos, vpos)
7614924Sslatteng  |		with the given diameter d.
7714924Sslatteng  |
7814924Sslatteng  | Side Efct:	Resulting position is at (hpos + diameter, vpos)
7914924Sslatteng  *----------------------------------------------------------------------------*/
8014924Sslatteng 
drawcirc(d)8114924Sslatteng drawcirc(d)
8214924Sslatteng register int d;
8314924Sslatteng {			/* 0.0 is the angle to sweep the arc: = full circle */
8415622Sslatteng     if (output) HGArc (hpos + d/2, vpos, hpos, vpos, 0.0);
8514924Sslatteng     hmot (d);			/* new postion is the right of the circle */
8614924Sslatteng }
8714924Sslatteng 
8814924Sslatteng 
8914924Sslatteng /*----------------------------------------------------------------------------
9014924Sslatteng  | Routine:	drawellip (horizontal_diameter, vertical_diameter)
9114924Sslatteng  |
9214924Sslatteng  | Results:	Draws regular ellipses given the major "diameters."  It does
9316388Sslatteng  |		so by drawing many small lines along the ellipses perimeter.
9416388Sslatteng  |		The algorithm is a modified (bresenham-like) circle algorithm
9514924Sslatteng  |
9614924Sslatteng  | Side Efct:	Resulting position is at (hpos + hd, vpos).
9714924Sslatteng  |
9814924Sslatteng  | Bugs:	Odd numbered horizontal axes are rounded up to even numbers.
9914924Sslatteng  *----------------------------------------------------------------------------*/
10014924Sslatteng 
drawellip(hd,vd)10114924Sslatteng drawellip(hd, vd)
10225608Sslatteng register int hd;
10325608Sslatteng register int vd;
10414924Sslatteng {
10516388Sslatteng     double xs, ys, xepsilon, yepsilon;	/* ellipse-calculation vairables */
10616388Sslatteng     register int basex;			/* center of the ellipse */
10716388Sslatteng     register int basey;
10816388Sslatteng     register int extent;		/* number of points to produce */
10914924Sslatteng 
11014924Sslatteng 
11125608Sslatteng     basex = hpos;		/* set the center of the ellipse */
11216388Sslatteng     basey = vpos;
11316388Sslatteng     hmot (hd);			/* troff motion done here, once. */
11425608Sslatteng     if (!output) return;
11516388Sslatteng     if ((hd = hd >> 1) < 1) hd = 1;	/* hd and vd are like radii */
11625608Sslatteng     basex += ++hd;
11716388Sslatteng     if ((vd = vd >> 1) < 1) vd = 1;
11825608Sslatteng     ys = (double) ++vd;		/* initial distances from center to perimeter */
11916388Sslatteng     xs = 0.0;
12016388Sslatteng 				/* calculate drawing parameters */
12116388Sslatteng     if (vd > hd) {
12225608Sslatteng 	xepsilon = 4.0 * (double) hd / (double) (vd * vd);
12325608Sslatteng 	yepsilon = 4.0 / (double) hd;
12425608Sslatteng 	extent = (int) (1.575 * (double) vd);
12516388Sslatteng     } else {
12625608Sslatteng 	xepsilon = 4.0 / (double) vd;
12725608Sslatteng 	yepsilon = 4.0 * (double) vd / (double) (hd * hd);
12825608Sslatteng 	extent = (int) (1.575 * (double) hd);
12915622Sslatteng     }
13014924Sslatteng 
13116388Sslatteng     byte(ASPATH);			/* start path definition */
13225608Sslatteng     word(1 + extent);			/* number of points */
13316388Sslatteng     word(xbound(basex));
13425608Sslatteng     vd += basey;
13525608Sslatteng     word(ybound(vd));
13625608Sslatteng     while (extent--) {
13716388Sslatteng 	xs += xepsilon * ys;
13816388Sslatteng 	ys -= yepsilon * xs;
13925608Sslatteng 	hd = basex + (int) xs;
14025608Sslatteng 	vd = basey + (int) ys;
14125608Sslatteng 	word(xbound(hd));	/* put out a point on ellipse */
142*25858Sslatteng 	word(ybound(vd));
14315622Sslatteng     }
14416388Sslatteng     byte(ADRAW);		/* now draw the arc */
14514924Sslatteng     byte(15);
14614924Sslatteng }
14714924Sslatteng 
14814924Sslatteng 
14914924Sslatteng /*----------------------------------------------------------------------------
15014924Sslatteng  | Routine:	drawarc (xcenter, ycenter, xpoint, ypoint)
15114924Sslatteng  |
15214924Sslatteng  | Results:	Draws an arc starting at current (hpos, vpos).  Center is
15314924Sslatteng  |		at (hpos + cdh, vpos + cdv) and the terminating point is
15414924Sslatteng  |		at <center> + (pdh, pdv).  The angle between the lines
15514924Sslatteng  |		formed by the starting, ending, and center points is figured
15614924Sslatteng  |		first, then the points and angle are sent to HGArc for the
15714924Sslatteng  |		drawing.
15814924Sslatteng  |
15914924Sslatteng  | Side Efct:	Resulting position is at the last point of the arc.
16014924Sslatteng  *----------------------------------------------------------------------------*/
16114924Sslatteng 
drawarc(cdh,cdv,pdh,pdv)16214924Sslatteng drawarc (cdh, cdv, pdh, pdv)
16314924Sslatteng register int cdh;
16414924Sslatteng register int cdv;
16514924Sslatteng register int pdh;
16614924Sslatteng register int pdv;
16714924Sslatteng {
16814924Sslatteng     register double angle;
16914924Sslatteng 				/* figure angle from the three points...*/
17014924Sslatteng 				/* and convert (and round) to degrees */
17114924Sslatteng     angle = (atan2((double) pdh, (double) pdv)
17214924Sslatteng 		- atan2 ((double) -cdh, (double) -cdv)) * 180.0 / pi;
17314924Sslatteng 				/* "normalize" and round */
17414924Sslatteng     angle += (angle < 0.0)  ?  360.5 : 0.5;
17514924Sslatteng 
17615622Sslatteng     if (output) HGArc(hpos + cdh, vpos + cdv, hpos, vpos, (int) angle);
17714924Sslatteng     hmot(cdh + pdh);
17814924Sslatteng     vmot(cdv + pdv);
17914924Sslatteng }
18014924Sslatteng 
18114924Sslatteng 
18214924Sslatteng /*----------------------------------------------------------------------------
18314924Sslatteng  | Routine:	drawwig (character_buffer, file_pointer, type_flag)
18414924Sslatteng  |
18514924Sslatteng  | Results:	Given the starting position, the motion list in buf, and any
18614924Sslatteng  |		extra characters from fp (terminated by a \n), drawwig sets
18717428Sslatteng  |		up a point list to make a spline or polygon from.  If "pic" is
18817428Sslatteng  |		zero, a gremlin curve is drawn with HGCurve; if less than zero
18917428Sslatteng  |		a polygon is drawn, else (pic > 0) a pic style spline is drawn
19017428Sslatteng  |		using picurve.
19114924Sslatteng  |
19214924Sslatteng  | Side Efct:	Resulting position is reached from adding successive motions
19314924Sslatteng  |		to the current position.
19414924Sslatteng  *----------------------------------------------------------------------------*/
19514924Sslatteng 
drawwig(buf,fp,pic)19614924Sslatteng drawwig (buf, fp, pic)
19714924Sslatteng char *buf;
19814924Sslatteng FILE *fp;
19914924Sslatteng int pic;
20014924Sslatteng {
20114924Sslatteng     register int len = strlen(buf);	/* length of the string in "buf" */
20214924Sslatteng     register int npts = 2;		/* point list index */
20314924Sslatteng     register char *ptr = buf;		/* "walking" pointer into buf */
20414924Sslatteng     int x[MAXPOINTS], y[MAXPOINTS];	/* point list */
20514924Sslatteng 
20614924Sslatteng     while (*ptr == ' ') ptr++;		/* skip any leading spaces */
20714924Sslatteng     x[1] = hpos;		/* the curve starts at the */
20814924Sslatteng     y[1] = vpos;		/* current position */
20914924Sslatteng 
21014924Sslatteng     while (*ptr != '\n') {		/* curve commands end with a '\n' */
21114924Sslatteng 	hmot(atoi(ptr));		/* convert motion to curve points */
21214924Sslatteng 	x[npts] = hpos;			/* and remember them */
21314924Sslatteng 	while (isdigit(*++ptr));		/* skip number*/
21414924Sslatteng 	while (*++ptr == ' ');		/* skip spaces 'tween numbers */
21514924Sslatteng 	vmot(atoi(ptr));
21614924Sslatteng 	y[npts] = vpos;
21714924Sslatteng 	while (isdigit(*++ptr));
21814924Sslatteng 	while (*ptr == ' ') ptr++;
21914924Sslatteng 				/* if the amount we read wasn't the */
22014924Sslatteng 		 		/*    whole thing, read some more in */
22114924Sslatteng 	if (len - (ptr - buf) < 15 && *(buf + len - 1) != '\n') {
22214924Sslatteng 	    char *cop = buf;
22314924Sslatteng 
22414924Sslatteng 	    while (*cop++ = *ptr++);	/* copy what's left to the beginning */
22516187Sslatteng 	    if (fgets ((cop - 1), len - (cop - buf), fp) == NULL)
22616187Sslatteng 		error (FATAL, "unexpected end of input");
22714924Sslatteng 	    ptr = buf;
22814924Sslatteng 	}
22914924Sslatteng 	if (npts < MAXPOINTS - 1)	/* if too many points, forget some */
23014924Sslatteng 	    npts++;
23114924Sslatteng     }
23214924Sslatteng     npts--;	/* npts must point to the last coordinate in x and y */
23314924Sslatteng 				/* now, actually DO the curve */
23415622Sslatteng     if (output) {
23516388Sslatteng 	if (pic > 0)
23625608Sslatteng 	    picurve(&x[0], &y[0], npts);
23716388Sslatteng 	else if (pic < 0)
23825608Sslatteng 	    polygon(&x[0], &y[0], npts);
23915622Sslatteng 	else
24025608Sslatteng 	    HGCurve(&x[0], &y[0], npts);
24115622Sslatteng     }
24214924Sslatteng }
24314924Sslatteng 
24414924Sslatteng 
24514924Sslatteng /*----------------------------------------------------------------------------*
24614924Sslatteng  | Routine:	drawthick (thickness)
24714924Sslatteng  |
24814924Sslatteng  | Results:	sets the variable "linethickness" to the given size.  If this
24914924Sslatteng  |		is different than previous thiknesses, informs Imagen of the
25014924Sslatteng  |		change.  NO motion is involved.
25114924Sslatteng  *----------------------------------------------------------------------------*/
25214924Sslatteng 
drawthick(s)25314924Sslatteng drawthick(s)
25414924Sslatteng int s;
25514924Sslatteng {
25614924Sslatteng     if (linethickness != s) {
25714924Sslatteng 	byte(ASPEN);
25814924Sslatteng 	byte((linethickness = s) < 1 ? 1 : linethickness > MAXPENW ?
25914924Sslatteng 					MAXPENW : linethickness);
26014924Sslatteng     }
26114924Sslatteng }
26214924Sslatteng 
26314924Sslatteng 
26414924Sslatteng /*----------------------------------------------------------------------------*
26514924Sslatteng  | Routine:	drawstyle (style_bit_map)
26614924Sslatteng  |
26714924Sslatteng  | Results:	sets the variable "linmod" to the given bit map.
26814924Sslatteng  |		NO motion is involved.
26914924Sslatteng  *----------------------------------------------------------------------------*/
27014924Sslatteng 
drawstyle(s)27114924Sslatteng drawstyle(s)
27214924Sslatteng int s;
27314924Sslatteng {
27414924Sslatteng     linmod = s;
27514924Sslatteng }
27614924Sslatteng 
27714924Sslatteng 
27816388Sslatteng /*----------------------------------------------------------------------------*
27916388Sslatteng  | Routine:	polygon (xpoints, ypoints, num_of_points)
28016388Sslatteng  |
28116388Sslatteng  | Results:	draws a polygon through the points (xpoints, ypoints).
28216388Sslatteng  |		The polygon has a raster fill associated with it.  The
28316388Sslatteng  |		fill is already set up from conv(), but if the stipple
28416388Sslatteng  |		pattern "laststipmem" is zero, polygon draws a "clear"
28516388Sslatteng  |		polygon.
28616388Sslatteng  |
28716388Sslatteng  | Bugs:	If the path is not closed, polygon will NOT close it.
28816388Sslatteng  |		(or is that a feature?)
28917428Sslatteng  |		self-interseting polygons can choke the Imagen - tough luck
29016388Sslatteng  |		if the path is "counterclockwise", it'll slow down the
29117428Sslatteng  |		rendering.  This is not checked for here.
29216388Sslatteng  *----------------------------------------------------------------------------*/
29316388Sslatteng 
29416388Sslatteng extern int laststipmem;		/* this is set, before this routine, to the */
29516388Sslatteng 				/* stipple member number to be printed.  If */
29616388Sslatteng 				/* it's zero, the path should not be filled */
polygon(x,y,npts)29716388Sslatteng polygon(x, y, npts)
29825608Sslatteng register int *x;
29925608Sslatteng register int *y;
30025608Sslatteng register int npts;
30116388Sslatteng {
30216388Sslatteng 	register int i;
30316388Sslatteng 
30417428Sslatteng 	if (polyborder && linmod != SOLID) {	/* if the border isn't solid */
30517428Sslatteng 		for (i = 2; i <= npts; i++)	/*    have HGtline draw it */
30617428Sslatteng 			HGtline(x[i-1], y[i-1], x[i], y[i]);
30717428Sslatteng 	}
30816388Sslatteng 	byte(ASPATH);		/* set up to send the path */
30916388Sslatteng 	word(npts);
31025608Sslatteng 	while (npts--) {	/* send the path */
31125608Sslatteng 		x++;
31225608Sslatteng 		y++;
31325608Sslatteng 		word(xbound(*x));
31425608Sslatteng 		word(ybound(*y));
31516388Sslatteng 	}
31617428Sslatteng 	if (polyborder && linmod == SOLID) {
31716461Sslatteng 		byte(ADRAW);	/* draw the border, if requested */
31816461Sslatteng 		byte(15);
31916461Sslatteng 	}
32016388Sslatteng 	if (laststipmem) {	/* draw a filled path, if requested */
32116388Sslatteng 		byte(AFPATH);
32216388Sslatteng 		byte(7);
32316388Sslatteng 	}
32416388Sslatteng }
32516388Sslatteng 
32616388Sslatteng 
32714924Sslatteng /*----------------------------------------------------------------------------
32814924Sslatteng  | Routine:	picurve (xpoints, ypoints, num_of_points)
32914924Sslatteng  |
33014924Sslatteng  | Results:	Draws a curve delimited by (not through) the line segments
33114924Sslatteng  |		traced by (xpoints, ypoints) point list.  This is the "Pic"
33214924Sslatteng  |		style curve.
33314924Sslatteng  *----------------------------------------------------------------------------*/
33414924Sslatteng 
picurve(x,y,npts)33514924Sslatteng picurve (x, y, npts)
33625608Sslatteng register int *x;
33725608Sslatteng register int *y;
33814924Sslatteng int npts;
33914924Sslatteng {
34025608Sslatteng     register int nseg;		/* effective resolution for each curve */
34125608Sslatteng     register int xp;		/* current point (and temporary) */
34214924Sslatteng     register int yp;
34325608Sslatteng     int pxp, pyp;		/* previous point (to make lines from) */
34425608Sslatteng     int i;			/* inner curve segment traverser */
34525608Sslatteng     double w;			/* position factor */
34625608Sslatteng     double t1, t2, t3;		/* calculation temps */
34714924Sslatteng 
34814924Sslatteng 
34914924Sslatteng     if (x[1] == x[npts] && y[1] == y[npts]) {
35014924Sslatteng 	x[0] = x[npts - 1];		/* if the lines' ends meet, make */
35114924Sslatteng 	y[0] = y[npts - 1];		/* sure the curve meets */
35214924Sslatteng 	x[npts + 1] = x[2];
35314924Sslatteng 	y[npts + 1] = y[2];
35414924Sslatteng     } else {				/* otherwise, make the ends of the */
35514924Sslatteng 	x[0] = x[1];			/* curve touch the ending points of */
35614924Sslatteng 	y[0] = y[1];			/* the line segments */
35714924Sslatteng 	x[npts + 1] = x[npts];
35814924Sslatteng 	y[npts + 1] = y[npts];
35914924Sslatteng     }
36014924Sslatteng 
36125608Sslatteng     pxp = (x[0] + x[1]) / 2;		/* make the last point pointers */
36225608Sslatteng     pyp = (y[0] + y[1]) / 2;		/* point to the start of the 1st line */
36325608Sslatteng 
36425608Sslatteng     for (; npts--; x++, y++) {		/* traverse the line segments */
36525608Sslatteng 	xp = x[0] - x[1];
36625608Sslatteng 	yp = y[0] - y[1];
36714924Sslatteng 	nseg = (int) sqrt((double)(xp * xp + yp * yp));
36825608Sslatteng 	xp = x[1] - x[2];
36925608Sslatteng 	yp = y[1] - y[2];		/* "nseg" is the number of line */
37014924Sslatteng 					/* segments that will be drawn for */
37125608Sslatteng 					/* each curve segment.  ">> 4" is */
37225608Sslatteng 					/* dropping the resolution ( == / 16) */
37325608Sslatteng 	nseg = (nseg + (int) sqrt((double)(xp * xp + yp * yp))) >> 4;
37414924Sslatteng 
37525608Sslatteng 	for (i = 1; i < nseg; i++) {
37625608Sslatteng 	    w = (double) i / (double) nseg;
37725608Sslatteng 	    t1 = w * w;
37825608Sslatteng 	    t3 = t1 + 1.0 - (w + w);
37925608Sslatteng 	    t2 = 2.0 - (t3 + t1);
38025608Sslatteng 	    xp = (((int) (t1 * x[2] + t2 * x[1] + t3 * x[0])) + 1) / 2;
38125608Sslatteng 	    yp = (((int) (t1 * y[2] + t2 * y[1] + t3 * y[0])) + 1) / 2;
38225608Sslatteng 
38325608Sslatteng 	    HGtline(pxp, pyp, xp, yp);
38416030Sslatteng 	    pxp = xp;
38516030Sslatteng 	    pyp = yp;
38614924Sslatteng 	}
38714924Sslatteng     }
38814924Sslatteng }
38914924Sslatteng 
39014924Sslatteng 
39114924Sslatteng /*----------------------------------------------------------------------------
39214924Sslatteng  | Routine:	HGArc (xcenter, ycenter, xstart, ystart, angle)
39314924Sslatteng  |
39414924Sslatteng  | Results:	This routine plots an arc centered about (cx, cy) counter
39514924Sslatteng  |		clockwise starting from the point (px, py) through 'angle'
39614924Sslatteng  |		degrees.  If angle is 0, a full circle is drawn. It does so
39714924Sslatteng  |		by creating a draw-path around the arc whose density of
39814924Sslatteng  |		points depends on the size of the arc.
39914924Sslatteng  *----------------------------------------------------------------------------*/
40014924Sslatteng 
HGArc(cx,cy,px,py,angle)40114924Sslatteng HGArc(cx,cy,px,py,angle)
40216030Sslatteng register int cx;
40316030Sslatteng register int cy;
40416030Sslatteng int px, py, angle;
40514924Sslatteng {
40614924Sslatteng     double xs, ys, resolution, fullcircle;
40716030Sslatteng     register int mask;
40814924Sslatteng     register int extent;
40914924Sslatteng     register int nx;
41014924Sslatteng     register int ny;
41114924Sslatteng     register double epsilon;
41214924Sslatteng 
41314924Sslatteng     xs = px - cx;
41414924Sslatteng     ys = py - cy;
41514924Sslatteng 
41616030Sslatteng 		/* calculate how fine to make the lines that build
41716030Sslatteng 		   the circle.  Resolution cannot be dropped, but
41816030Sslatteng 		   mask is used to skip some points for larger
41916030Sslatteng 		   arcs due to Imagen's path length limitations */
42014924Sslatteng 
42116030Sslatteng     resolution = sqrt(xs * xs + ys * ys);
42216030Sslatteng     mask = (1 << (int) log10(resolution + 1.0)) - 1;
42316030Sslatteng 
42414924Sslatteng     epsilon = 1.0 / resolution;
42516030Sslatteng     fullcircle = (2.0 * pi) * resolution;
42614924Sslatteng     if (angle == 0)
42714924Sslatteng 	extent = fullcircle;
42814924Sslatteng     else
42914924Sslatteng 	extent = angle * fullcircle / 360.0;
43014924Sslatteng 
43114924Sslatteng     byte(ASPATH);			/* start path definition */
43214924Sslatteng     if (extent > 1) {
43316030Sslatteng 	word(2 + (extent-1) / (mask+1));	/* number of points */
43416030Sslatteng 	word(xbound(px));
43516030Sslatteng 	word(ybound(py));
43616030Sslatteng 	while (--extent >= 0) {
43714924Sslatteng 	    xs += epsilon * ys;
43816030Sslatteng 	    nx = cx + (int) (xs + 0.5);
43914924Sslatteng 	    ys -= epsilon * xs;
44016030Sslatteng 	    ny = cy + (int) (ys + 0.5);
44116030Sslatteng 	    if (!(extent&mask)) {
44216030Sslatteng 		word(xbound(nx));	/* put out a point on circle */
44316030Sslatteng 		word(ybound(ny));
44416030Sslatteng 	    }
44514924Sslatteng 	}   /* end for */
44614924Sslatteng     } else {			/* arc is too small: put out point */
44714924Sslatteng 	word(2);
44816030Sslatteng 	word(xbound(px));
44916030Sslatteng 	word(ybound(py));
45016030Sslatteng 	word(xbound(px));
45116030Sslatteng 	word(ybound(py));
45214924Sslatteng     }
45314924Sslatteng     byte(ADRAW);		/* now draw the arc */
45414924Sslatteng     byte(15);
45514924Sslatteng }  /* end HGArc */
45614924Sslatteng 
45714924Sslatteng 
45814924Sslatteng /*----------------------------------------------------------------------------
45914924Sslatteng  | Routine:	Paramaterize (xpoints, ypoints, hparams, num_points)
46014924Sslatteng  |
46114924Sslatteng  | Results:	This routine calculates parameteric values for use in
46214924Sslatteng  |		calculating curves.  The parametric values are returned
46314924Sslatteng  |		in the array h.  The values are an approximation of
46414924Sslatteng  |		cumulative arc lengths of the curve (uses cord length).
46514924Sslatteng  |		For additional information, see paper cited below.
46614924Sslatteng  *----------------------------------------------------------------------------*/
46714924Sslatteng 
Paramaterize(x,y,h,n)46814924Sslatteng static Paramaterize(x, y, h, n)
46914924Sslatteng int x[MAXPOINTS];
47014924Sslatteng int y[MAXPOINTS];
47114924Sslatteng float h[MAXPOINTS];
47214924Sslatteng int n;
47314924Sslatteng {
47414924Sslatteng 	register int dx;
47514924Sslatteng 	register int dy;
47614924Sslatteng 	register int i;
47714924Sslatteng 	register int j;
47814924Sslatteng 	float u[MAXPOINTS];
47914924Sslatteng 
48014924Sslatteng 
48114924Sslatteng 	for (i=1; i<=n; ++i) {
48214924Sslatteng 	    u[i] = 0;
48314924Sslatteng 	    for (j=1; j<i; j++) {
48414924Sslatteng 		dx = x[j+1] - x[j];
48514924Sslatteng 		dy = y[j+1] - y[j];
48614924Sslatteng 		u[i] += sqrt ((double) (dx * dx + dy * dy));
48714924Sslatteng 	    }
48814924Sslatteng 	}
48914924Sslatteng 	for (i=1; i<n; ++i)  h[i] = u[i+1] - u[i];
49014924Sslatteng }  /* end Paramaterize */
49114924Sslatteng 
49214924Sslatteng 
49314924Sslatteng /*----------------------------------------------------------------------------
49414924Sslatteng  | Routine:	PeriodicSpline (h, z, dz, d2z, d3z, npoints)
49514924Sslatteng  |
49614924Sslatteng  | Results:	This routine solves for the cubic polynomial to fit a
49714924Sslatteng  |		spline curve to the the points  specified by the list
49814924Sslatteng  |		of values.  The Curve generated is periodic.  The algorithms
49914924Sslatteng  |		for this curve are from the "Spline Curve Techniques" paper
50014924Sslatteng  |		cited below.
50114924Sslatteng  *----------------------------------------------------------------------------*/
50214924Sslatteng 
PeriodicSpline(h,z,dz,d2z,d3z,npoints)50314924Sslatteng static PeriodicSpline(h, z, dz, d2z, d3z, npoints)
50414924Sslatteng float h[MAXPOINTS];		/* paramaterization  */
50514924Sslatteng int z[MAXPOINTS];		/* point list */
50614924Sslatteng float dz[MAXPOINTS];			/* to return the 1st derivative */
50714924Sslatteng float d2z[MAXPOINTS], d3z[MAXPOINTS];	/* 2nd and 3rd derivatives */
50814924Sslatteng int npoints;				/* number of valid points */
50914924Sslatteng {
51014924Sslatteng 	float d[MAXPOINTS];
51114924Sslatteng 	float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
51214924Sslatteng 	float c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS];
51314924Sslatteng 	int i;
51414924Sslatteng 
51514924Sslatteng 						/* step 1 */
51614924Sslatteng 	for (i=1; i<npoints; ++i) {
51714924Sslatteng 	    deltaz[i] = h[i] ? ((double) (z[i+1] - z[i])) / h[i] : 0;
51814924Sslatteng 	}
51914924Sslatteng 	h[0] = h[npoints-1];
52014924Sslatteng 	deltaz[0] = deltaz[npoints-1];
52114924Sslatteng 
52214924Sslatteng 						/* step 2 */
52314924Sslatteng 	for (i=1; i<npoints-1; ++i) {
52414924Sslatteng 	    d[i] = deltaz[i+1] - deltaz[i];
52514924Sslatteng 	}
52614924Sslatteng 	d[0] = deltaz[1] - deltaz[0];
52714924Sslatteng 
52814924Sslatteng 						/* step 3a */
52914924Sslatteng 	a[1] = 2 * (h[0] + h[1]);
53014924Sslatteng 	b[1] = d[0];
53114924Sslatteng 	c[1] = h[0];
53214924Sslatteng 	for (i=2; i<npoints-1; ++i) {
53314924Sslatteng 	    a[i] = 2*(h[i-1]+h[i]) - pow ((double) h[i-1],(double)2.0) / a[i-1];
53414924Sslatteng 	    b[i] = d[i-1] - h[i-1] * b[i-1]/a[i-1];
53514924Sslatteng 	    c[i] = -h[i-1] * c[i-1]/a[i-1];
53614924Sslatteng 	}
53714924Sslatteng 
53814924Sslatteng 						/* step 3b */
53914924Sslatteng 	r[npoints-1] = 1;
54014924Sslatteng 	s[npoints-1] = 0;
54114924Sslatteng 	for (i=npoints-2; i>0; --i) {
54214924Sslatteng 	    r[i] = -(h[i] * r[i+1] + c[i])/a[i];
54314924Sslatteng 	    s[i] = (6 * b[i] - h[i] * s[i+1])/a[i];
54414924Sslatteng 	}
54514924Sslatteng 
54614924Sslatteng 						/* step 4 */
54714924Sslatteng 	d2z[npoints-1] = (6 * d[npoints-2] - h[0] * s[1]
54814924Sslatteng 	                   - h[npoints-1] * s[npoints-2])
54914924Sslatteng 	                 / (h[0] * r[1] + h[npoints-1] * r[npoints-2]
55014924Sslatteng 	                    + 2 * (h[npoints-2] + h[0]));
55114924Sslatteng 	for (i=1; i<npoints-1; ++i) {
55214924Sslatteng 	    d2z[i] = r[i] * d2z[npoints-1] + s[i];
55314924Sslatteng 	}
55414924Sslatteng 	d2z[npoints] = d2z[1];
55514924Sslatteng 
55614924Sslatteng 						/* step 5 */
55714924Sslatteng 	for (i=1; i<npoints; ++i) {
55814924Sslatteng 	    dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i+1])/6;
55914924Sslatteng 	    d3z[i] = h[i] ? (d2z[i+1] - d2z[i])/h[i] : 0;
56014924Sslatteng 	}
56114924Sslatteng }  /* end PeriodicSpline */
56214924Sslatteng 
56314924Sslatteng 
56414924Sslatteng /*----------------------------------------------------------------------------
56514924Sslatteng  | Routine:	NaturalEndSpline (h, z, dz, d2z, d3z, npoints)
56614924Sslatteng  |
56714924Sslatteng  | Results:	This routine solves for the cubic polynomial to fit a
56814924Sslatteng  |		spline curve the the points  specified by the list of
56914924Sslatteng  |		values.  The alogrithms for this curve are from the
57014924Sslatteng  |		"Spline Curve Techniques" paper cited below.
57114924Sslatteng  *----------------------------------------------------------------------------*/
57214924Sslatteng 
NaturalEndSpline(h,z,dz,d2z,d3z,npoints)57314924Sslatteng static NaturalEndSpline(h, z, dz, d2z, d3z, npoints)
57414924Sslatteng float h[MAXPOINTS];		/* parameterization */
57514924Sslatteng int z[MAXPOINTS];		/* Point list */
57614924Sslatteng float dz[MAXPOINTS];			/* to return the 1st derivative */
57714924Sslatteng float d2z[MAXPOINTS], d3z[MAXPOINTS];	/* 2nd and 3rd derivatives */
57814924Sslatteng int npoints;				/* number of valid points */
57914924Sslatteng {
58014924Sslatteng 	float d[MAXPOINTS];
58114924Sslatteng 	float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
58214924Sslatteng 	int i;
58314924Sslatteng 
58414924Sslatteng 						/* step 1 */
58514924Sslatteng 	for (i=1; i<npoints; ++i) {
58614924Sslatteng 	    deltaz[i] = h[i] ? ((double) (z[i+1] - z[i])) / h[i] : 0;
58714924Sslatteng 	}
58814924Sslatteng 	deltaz[0] = deltaz[npoints-1];
58914924Sslatteng 
59014924Sslatteng 						/* step 2 */
59114924Sslatteng 	for (i=1; i<npoints-1; ++i) {
59214924Sslatteng 	    d[i] = deltaz[i+1] - deltaz[i];
59314924Sslatteng 	}
59414924Sslatteng 	d[0] = deltaz[1] - deltaz[0];
59514924Sslatteng 
59614924Sslatteng 						/* step 3 */
59714924Sslatteng 	a[0] = 2 * (h[2] + h[1]);
59814924Sslatteng 	b[0] = d[1];
59914924Sslatteng 	for (i=1; i<npoints-2; ++i) {
60014924Sslatteng 	    a[i] = 2*(h[i+1]+h[i+2]) - pow((double) h[i+1],(double) 2.0)/a[i-1];
60114924Sslatteng 	    b[i] = d[i+1] - h[i+1] * b[i-1]/a[i-1];
60214924Sslatteng 	}
60314924Sslatteng 
60414924Sslatteng 						/* step 4 */
60514924Sslatteng 	d2z[npoints] = d2z[1] = 0;
60614924Sslatteng 	for (i=npoints-1; i>1; --i) {
60714924Sslatteng 	    d2z[i] = (6 * b[i-2] - h[i] *d2z[i+1])/a[i-2];
60814924Sslatteng 	}
60914924Sslatteng 
61014924Sslatteng 						/* step 5 */
61114924Sslatteng 	for (i=1; i<npoints; ++i) {
61214924Sslatteng 	    dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i+1])/6;
61314924Sslatteng 	    d3z[i] = h[i] ? (d2z[i+1] - d2z[i])/h[i] : 0;
61414924Sslatteng 	}
61514924Sslatteng }  /* end NaturalEndSpline */
61614924Sslatteng 
61714924Sslatteng 
61814924Sslatteng /*----------------------------------------------------------------------------
61914924Sslatteng  | Routine:	HGCurve(xpoints, ypoints, num_points)
62014924Sslatteng  |
62114924Sslatteng  | Results:	This routine generates a smooth curve through a set of points.
62214924Sslatteng  |		The method used is the parametric spline curve on unit knot
62314924Sslatteng  |		mesh described in "Spline Curve Techniques" by Patrick
62414924Sslatteng  |		Baudelaire, Robert Flegal, and Robert Sproull -- Xerox Parc.
62514924Sslatteng  *----------------------------------------------------------------------------*/
62614924Sslatteng 
62714924Sslatteng #define PointsPerInterval 32
62814924Sslatteng 
HGCurve(x,y,numpoints)62914924Sslatteng HGCurve(x, y, numpoints)
63025608Sslatteng int *x;
63125608Sslatteng int *y;
63214924Sslatteng int numpoints;
63314924Sslatteng {
63414924Sslatteng 	float h[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS];
63514924Sslatteng 	float d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS];
63614924Sslatteng 	float t, t2, t3;
63714924Sslatteng 	register int j;
63814924Sslatteng 	register int k;
63914924Sslatteng 	register int nx;
64014924Sslatteng 	register int ny;
64114924Sslatteng 	int lx, ly;
64214924Sslatteng 
64314924Sslatteng 
64414924Sslatteng 	lx = x[1];
64514924Sslatteng 	ly = y[1];
64614924Sslatteng 
64714924Sslatteng 	     /* Solve for derivatives of the curve at each point
64814924Sslatteng               * separately for x and y (parametric).
64914924Sslatteng 	      */
65014924Sslatteng 	Paramaterize(x, y, h, numpoints);
65114924Sslatteng 							/* closed curve */
65214924Sslatteng 	if ((x[1] == x[numpoints]) && (y[1] == y[numpoints])) {
65314924Sslatteng 	    PeriodicSpline(h, x, dx, d2x, d3x, numpoints);
65414924Sslatteng 	    PeriodicSpline(h, y, dy, d2y, d3y, numpoints);
65514924Sslatteng 	} else {
65614924Sslatteng 	    NaturalEndSpline(h, x, dx, d2x, d3x, numpoints);
65714924Sslatteng 	    NaturalEndSpline(h, y, dy, d2y, d3y, numpoints);
65814924Sslatteng 	}
65914924Sslatteng 
66014924Sslatteng 	      /* generate the curve using the above information and
66114924Sslatteng 	       * PointsPerInterval vectors between each specified knot.
66214924Sslatteng 	       */
66314924Sslatteng 
66414924Sslatteng 	for (j=1; j<numpoints; ++j) {
66514924Sslatteng 	    if ((x[j] == x[j+1]) && (y[j] == y[j+1])) continue;
66614924Sslatteng 	    for (k=0; k<=PointsPerInterval; ++k) {
66714924Sslatteng 		t = (float) k * h[j] / (float) PointsPerInterval;
66814924Sslatteng 		t2 = t * t;
66914924Sslatteng 		t3 = t * t * t;
67014924Sslatteng 		nx = x[j] + (int) (t * dx[j] + t2 * d2x[j]/2 + t3 * d3x[j]/6);
67114924Sslatteng 		ny = y[j] + (int) (t * dy[j] + t2 * d2y[j]/2 + t3 * d3y[j]/6);
67214924Sslatteng 		HGtline(lx, ly, nx, ny);
67314924Sslatteng 		lx = nx;
67414924Sslatteng 		ly = ny;
67514924Sslatteng 	    }  /* end for k */
67614924Sslatteng 	}  /* end for j */
67714924Sslatteng }  /* end HGCurve */
67814924Sslatteng 
67914924Sslatteng 
68014924Sslatteng /*----------------------------------------------------------------------------
68115347Sslatteng  | Routine:	line(xstart, ystart, xend, yend)
68214924Sslatteng  |
68314924Sslatteng  | Results:	Creates a drawing path and draws the line.  If the line falls
68414924Sslatteng  |		off the end of the page, a crude clipping is done:  truncating
68514924Sslatteng  |		the offending ordinate.
68614924Sslatteng  *----------------------------------------------------------------------------*/
68714924Sslatteng 
line(x0,y0,x1,y1)68815347Sslatteng line(x0, y0, x1, y1)
68914924Sslatteng int x0, y0, x1, y1;
69014924Sslatteng {
69114924Sslatteng     byte(ASPATH);		/* send the coordinates first */
69214924Sslatteng     word(2);		/* only two */
69314924Sslatteng     word(xbound(x0));
69414924Sslatteng     word(ybound(y0));
69514924Sslatteng     word(xbound(x1));
69614924Sslatteng     word(ybound(y1));
69714924Sslatteng     byte(ADRAW);		/* now draw it */
69814924Sslatteng     byte(15);		/* black */
69915347Sslatteng }  /* end line */
70015347Sslatteng 
70115347Sslatteng 
70215347Sslatteng /*----------------------------------------------------------------------------*
70315347Sslatteng  | Routine:	change (x_position, y_position, visible_flag)
70415347Sslatteng  |
70515347Sslatteng  | Results:	As HGtline passes from the invisible to visible (or vice
70615347Sslatteng  |		versa) portion of a line, change is called to either draw
70715347Sslatteng  |		the line, or initialize the beginning of the next one.
70815347Sslatteng  |		Change calls line to draw segments if visible_flag is set
70915347Sslatteng  |		(which means we're leaving a visible area).
71015347Sslatteng  *----------------------------------------------------------------------------*/
71115347Sslatteng 
change(x,y,vis)71215347Sslatteng change (x, y, vis)
71315347Sslatteng register int x;
71415347Sslatteng register int y;
71515347Sslatteng register int vis;
71615347Sslatteng {
71715347Sslatteng     static int xorg;
71815347Sslatteng     static int yorg;
71915347Sslatteng 
72015347Sslatteng     if (vis)		/* leaving a visible area, draw it. */
72115347Sslatteng 	line (xorg, yorg, x, y);
72215347Sslatteng     else {		/* otherwise, we're entering one, remember beginning */
72315347Sslatteng 	xorg = x;
72415347Sslatteng 	yorg = y;
72515347Sslatteng     }
72615347Sslatteng }
72715347Sslatteng 
72815347Sslatteng 
72915347Sslatteng /*----------------------------------------------------------------------------
73015347Sslatteng  | Routine:	HGtline (xstart, ystart, xend, yend)
73115347Sslatteng  |
73215347Sslatteng  | Results:	Draws a line from (x0,y0) to (x1,y1) using line(x0,y0,x1,y1)
73315347Sslatteng  |		to place individual segments of dotted or dashed lines.
73415347Sslatteng  *----------------------------------------------------------------------------*/
73515347Sslatteng 
HGtline(x0,y0,x1,y1)73615347Sslatteng HGtline(x0, y0, x1, y1)
73716030Sslatteng int x0, y0, x1, y1;
73815347Sslatteng {
73915347Sslatteng     register int dx;
74015347Sslatteng     register int dy;
74116030Sslatteng     register int oldcoord;
74215347Sslatteng     register int res1;
74315347Sslatteng     register int visible;
74416030Sslatteng     register int res2;
74516030Sslatteng     register int xinc;
74616030Sslatteng     register int yinc;
74715347Sslatteng 
74815347Sslatteng 
74917428Sslatteng     if (linmod == SOLID) {
75015347Sslatteng 	line(x0, y0, x1, y1);
75115347Sslatteng 	return;
75215347Sslatteng     }
75315347Sslatteng     xinc = 1;
75415347Sslatteng     yinc = 1;
75515347Sslatteng     if ((dx = x1-x0) < 0) {
75615347Sslatteng         xinc = -1;
75715347Sslatteng         dx = -dx;
75815347Sslatteng     }
75915347Sslatteng     if ((dy = y1-y0) < 0) {
76015347Sslatteng         yinc = -1;
76115347Sslatteng         dy = -dy;
76215347Sslatteng     }
76315347Sslatteng     res1 = 0;
76415347Sslatteng     res2 = 0;
76515347Sslatteng     visible = 0;
76616030Sslatteng     if (dx >= dy) {
76716030Sslatteng 	oldcoord = y0;
76815347Sslatteng         while (x0 != x1) {
76916030Sslatteng             if((x0&linmod) && !visible) {
77016030Sslatteng 		change(x0, y0, 0);
77116030Sslatteng 		visible = 1;
77216030Sslatteng 	    } else if(visible && !(x0&linmod)) {
77316030Sslatteng 		change(x0 - xinc, oldcoord, 1);
77416030Sslatteng 		visible = 0;
77515347Sslatteng 	    }
77615347Sslatteng             if (res1 > res2) {
77716030Sslatteng 		oldcoord = y0;
77815347Sslatteng                 res2 += dx - res1;
77915347Sslatteng                 res1 = 0;
78015347Sslatteng                 y0 += yinc;
78115347Sslatteng             }
78215347Sslatteng             res1 += dy;
78315347Sslatteng             x0 += xinc;
78415347Sslatteng         }
78516030Sslatteng     } else {
78616030Sslatteng 	oldcoord = x0;
78715347Sslatteng         while (y0 != y1) {
78816030Sslatteng             if((y0&linmod) && !visible) {
78916030Sslatteng 		change(x0, y0, 0);
79016030Sslatteng 		visible = 1;
79116030Sslatteng 	    } else if(visible && !(y0&linmod)) {
79216030Sslatteng 		change(oldcoord, y0 - yinc, 1);
79316030Sslatteng 		visible = 0;
79415347Sslatteng 	    }
79515347Sslatteng             if (res1 > res2) {
79616030Sslatteng 		oldcoord = x0;
79715347Sslatteng                 res2 += dy - res1;
79815347Sslatteng                 res1 = 0;
79915347Sslatteng                 x0 += xinc;
80015347Sslatteng             }
80115347Sslatteng             res1 += dx;
80215347Sslatteng             y0 += yinc;
80315347Sslatteng         }
80416030Sslatteng     }
80515347Sslatteng     if(visible) change(x1, y1, 1);
80615347Sslatteng }
807