1 /*	draw.c	1.3	83/11/01
2  *
3  *	This file contains the functions for producing the graphics
4  *   images in the canon/imagen driver for ditroff.
5  */
6 
7 
8 #include <stdio.h>
9 #include <ctype.h>
10 #include <math.h>
11 #include "canon.h"
12 
13 
14 #define TRUE	1
15 #define FALSE	0
16 				/* imports from dip.c */
17 #define  hmot(n)	hpos += n;
18 #define  hgoto(n)	hpos = n;
19 #define  vmot(n)	vpos += n;
20 #define  vgoto(n)	vpos = n;
21 
22 extern int hpos;
23 extern int vpos;
24 extern int MAXX;
25 extern int MAXY;
26 extern FILE *tf;
27 extern putint();
28 
29 #define word(x)	putint(x,tf)
30 #define byte(x)	putc(x,tf)
31 #define MAXPOINTS 200	/* number of points legal for a curve */
32 
33 #define SOLID -1	/* line styles:  these are used as bit masks to */
34 #define DOTTED 004	/* create the right style lines. */
35 #define DASHED 020
36 #define DOTDASHED 024
37 #define LONGDASHED 074
38 				/* constants... */
39 #define  pi		3.14159265358979324
40 #define  log2_10	3.3219280948873623
41 
42 #define START	1
43 #define POINT	0
44 /* the imagen complains if a path is drawn at < 1, or > limit, so truncate. */
45 #define xbound(x)	(x < 1 ? 1 : x > MAXX ? MAXX : x)
46 #define ybound(y)	(y < 1 ? 1 : y > MAXY ? MAXY : y)
47 
48 
49 int	linethickness = -1;	/* number of pixels wide to make lines */
50 int	linmod = SOLID;		/* type of line (SOLID, DOTTED, DASHED...) */
51 
52 
53 
54 /*----------------------------------------------------------------------------
55  | Routine:	drawline (horizontal_motion, vertical_motion)
56  |
57  | Results:	Draws a line of "linethickness" width and "linmod" style
58  |		from current (hpos, vpos) to (hpos + dh, vpos + dv).
59  |
60  | Side Efct:	Resulting position is at end of line (hpos + dh, vpos + dv)
61  *----------------------------------------------------------------------------*/
62 
63 drawline(dh, dv)
64 register int dh;
65 register int dv;
66 {
67     HGtline (hpos, vpos, hpos + dh, vpos + dv);
68     hmot (dh);					/* new position is at */
69     vmot (dv);					/* the end of the line */
70 }
71 
72 
73 /*----------------------------------------------------------------------------
74  | Routine:	drawcirc (diameter)
75  |
76  | Results:	Draws a circle with leftmost point at current (hpos, vpos)
77  |		with the given diameter d.
78  |
79  | Side Efct:	Resulting position is at (hpos + diameter, vpos)
80  *----------------------------------------------------------------------------*/
81 
82 drawcirc(d)
83 register int d;
84 {			/* 0.0 is the angle to sweep the arc: = full circle */
85     HGArc (hpos + d/2, vpos, hpos, vpos, 0.0);
86     hmot (d);			/* new postion is the right of the circle */
87 }
88 
89 
90 /*----------------------------------------------------------------------------
91  | Routine:	drawellip (horizontal_diameter, vertical_diameter)
92  |
93  | Results:	Draws regular ellipses given the major "diameters."  It does
94  |		so by drawing many small lines, every other pixel.  The ellipse
95  |		formula:  ((x-x0)/hrad)**2 + ((y-y0)/vrad)**2 = 1 is used,
96  |		converting to:  y = y0 +- vrad * sqrt(1 - ((x-x0)/hrad)**2).
97  |		The line segments are duplicated (mirrored) on either side of
98  |		the horizontal "diameter".
99  |
100  | Side Efct:	Resulting position is at (hpos + hd, vpos).
101  |
102  | Bugs:	Odd numbered horizontal axes are rounded up to even numbers.
103  *----------------------------------------------------------------------------*/
104 
105 drawellip(hd, vd)
106 register int hd;
107 int vd;
108 {
109     register int bx;		/* multiplicative x factor */
110     register int x;		/* x position drawing to */
111     register int k2;		/* the square-root term */
112     register int y;		/* y position drawing to */
113     double k1;			/* k? are constants depending on parameters */
114     int bxsave, xsave, hdsave;	/* places to save things to be used over */
115 
116 
117     hd = 2 * ((hd + 1) / 2);	/* don't accept odd diameters */
118     if (hd < 2) hd = 2;		/* or dinky ones */
119 
120     bx = 4 * (hpos + hd);
121     x = hpos;
122     k1 = vd / (2.0 * hd);
123     k2 = hd * hd - 4 * (hpos + hd/2) * (hpos + hd/2);
124 
125     bxsave = bx;	/* remember the parameters that will change through */
126     xsave = x;		/*    the top half of the elipse, so the bottom half */
127     hdsave = hd;	/*    can be drawn later. */
128 
129     byte(ASPATH);		/* define drawing path */
130     word(hd / 2 + 1);
131     word(xbound(hpos));	/* start out at current position */
132     word(ybound(vpos));
133     do {
134 	x += 2;
135 	word(xbound(x));
136 	y = vpos + (int) (k1 * sqrt((double) (k2 + (bx -= 8) * x)));
137 	word(ybound(y));
138     } while (hd -= 2);
139     byte(ADRAW);		/* now draw the top half */
140     byte(15);
141 
142     bx = bxsave;	/* get back the parameters for bottom half */
143     x = xsave;
144     hd = hdsave;
145 
146     byte(ASPATH);		/* define drawing path */
147     word(hd / 2 + 1);
148     word(xbound(hpos));	/* start out at current position */
149     word(ybound(vpos));
150     do {
151 	x += 2;
152 	word(xbound(x));
153 	y = vpos - (int) (k1 * sqrt((double) (k2 + (bx -= 8) * x)));
154 	word(ybound(y));
155     } while (hd -= 2);
156     byte(ADRAW);		/* now draw the bottom half */
157     byte(15);
158 
159     hmot (hdsave);	/* end position is the right-hand side of the ellipse */
160 }
161 
162 
163 /*----------------------------------------------------------------------------
164  | Routine:	drawarc (xcenter, ycenter, xpoint, ypoint)
165  |
166  | Results:	Draws an arc starting at current (hpos, vpos).  Center is
167  |		at (hpos + cdh, vpos + cdv) and the terminating point is
168  |		at <center> + (pdh, pdv).  The angle between the lines
169  |		formed by the starting, ending, and center points is figured
170  |		first, then the points and angle are sent to HGArc for the
171  |		drawing.
172  |
173  | Side Efct:	Resulting position is at the last point of the arc.
174  *----------------------------------------------------------------------------*/
175 
176 drawarc (cdh, cdv, pdh, pdv)
177 register int cdh;
178 register int cdv;
179 register int pdh;
180 register int pdv;
181 {
182     register double angle;
183 				/* figure angle from the three points...*/
184 				/* and convert (and round) to degrees */
185     angle = (atan2((double) pdh, (double) pdv)
186 		- atan2 ((double) -cdh, (double) -cdv)) * 180.0 / pi;
187 				/* "normalize" and round */
188     angle += (angle < 0.0)  ?  360.5 : 0.5;
189 
190     HGArc(hpos + cdh, vpos + cdv, hpos, vpos, (int) angle);
191     hmot(cdh + pdh);
192     vmot(cdv + pdv);
193 }
194 
195 
196 /*----------------------------------------------------------------------------
197  | Routine:	drawwig (character_buffer, file_pointer, type_flag)
198  |
199  | Results:	Given the starting position, the motion list in buf, and any
200  |		extra characters from fp (terminated by a \n), drawwig sets
201  |		up a point list to make a spline from.  If "pic" is set picurve
202  |		is called to draw the curve in pic style; else it calls HGCurve
203  |		for the gremlin-style curve.
204  |
205  | Side Efct:	Resulting position is reached from adding successive motions
206  |		to the current position.
207  *----------------------------------------------------------------------------*/
208 
209 drawwig (buf, fp, pic)
210 char *buf;
211 FILE *fp;
212 int pic;
213 {
214     register int len = strlen(buf);	/* length of the string in "buf" */
215     register int npts = 2;		/* point list index */
216     register char *ptr = buf;		/* "walking" pointer into buf */
217     int x[MAXPOINTS], y[MAXPOINTS];	/* point list */
218 
219     while (*ptr == ' ') ptr++;		/* skip any leading spaces */
220     x[1] = hpos;		/* the curve starts at the */
221     y[1] = vpos;		/* current position */
222 
223     while (*ptr != '\n') {		/* curve commands end with a '\n' */
224 	hmot(atoi(ptr));		/* convert motion to curve points */
225 	x[npts] = hpos;			/* and remember them */
226 	while (isdigit(*++ptr));		/* skip number*/
227 	while (*++ptr == ' ');		/* skip spaces 'tween numbers */
228 	vmot(atoi(ptr));
229 	y[npts] = vpos;
230 	while (isdigit(*++ptr));
231 	while (*ptr == ' ') ptr++;
232 				/* if the amount we read wasn't the */
233 		 		/*    whole thing, read some more in */
234 	if (len - (ptr - buf) < 15 && *(buf + len - 1) != '\n') {
235 	    char *cop = buf;
236 
237 	    while (*cop++ = *ptr++);	/* copy what's left to the beginning */
238 	    fgets ((cop - 1), len - (cop - buf), fp);
239 	    ptr = buf;
240 	}
241 	if (npts < MAXPOINTS - 1)	/* if too many points, forget some */
242 	    npts++;
243     }
244     npts--;	/* npts must point to the last coordinate in x and y */
245 				/* now, actually DO the curve */
246     if (pic)
247 	picurve(x, y, npts);
248     else
249 	HGCurve(x, y, npts);
250 }
251 
252 
253 /*----------------------------------------------------------------------------*
254  | Routine:	drawthick (thickness)
255  |
256  | Results:	sets the variable "linethickness" to the given size.  If this
257  |		is different than previous thiknesses, informs Imagen of the
258  |		change.  NO motion is involved.
259  *----------------------------------------------------------------------------*/
260 
261 drawthick(s)
262 int s;
263 {
264     if (linethickness != s) {
265 	byte(ASPEN);
266 	byte((linethickness = s) < 1 ? 1 : linethickness > MAXPENW ?
267 					MAXPENW : linethickness);
268     }
269 }
270 
271 
272 /*----------------------------------------------------------------------------*
273  | Routine:	drawstyle (style_bit_map)
274  |
275  | Results:	sets the variable "linmod" to the given bit map.
276  |		NO motion is involved.
277  *----------------------------------------------------------------------------*/
278 
279 drawstyle(s)
280 int s;
281 {
282     linmod = s;
283 }
284 
285 
286 /*----------------------------------------------------------------------------
287  | Routine:	picurve (xpoints, ypoints, num_of_points)
288  |
289  | Results:	Draws a curve delimited by (not through) the line segments
290  |		traced by (xpoints, ypoints) point list.  This is the "Pic"
291  |		style curve.
292  *----------------------------------------------------------------------------*/
293 
294 picurve (x, y, npts)
295 int x[MAXPOINTS];
296 int y[MAXPOINTS];
297 int npts;
298 {
299     register int i;		/* line segment traverser */
300     register float w;		/* position factor */
301     register int xp;		/* current point (and intermediary) */
302     register int yp;
303     register int j;		/* inner curve segment traverser */
304     register int nseg;		/* effective resolution for each curve */
305     float t1, t2, t3;		/* calculation temps */
306 
307 
308     if (x[1] == x[npts] && y[1] == y[npts]) {
309 	x[0] = x[npts - 1];		/* if the lines' ends meet, make */
310 	y[0] = y[npts - 1];		/* sure the curve meets */
311 	x[npts + 1] = x[2];
312 	y[npts + 1] = y[2];
313     } else {				/* otherwise, make the ends of the */
314 	x[0] = x[1];			/* curve touch the ending points of */
315 	y[0] = y[1];			/* the line segments */
316 	x[npts + 1] = x[npts];
317 	y[npts + 1] = y[npts];
318     }
319 
320     for (i = 0; i < npts; i++) {	/* traverse the line segments */
321 	xp = x[i] - x[i+1];
322 	yp = y[i] - y[i+1];
323 	nseg = (int) sqrt((double)(xp * xp + yp * yp));
324 	xp = x[i+1] - x[i+2];
325 	yp = y[i+1] - y[i+2];		/* "nseg" is the number of line */
326 					/* segments that will be drawn for */
327 					/* each curve segment.  ">> 3" is */
328 					/* dropping the resolution ( == / 8) */
329 	nseg = (nseg + (int) sqrt((double)(xp * xp + yp * yp))) >> 3;
330 
331 	byte(ASPATH);
332 	if (nseg)
333 	    word(nseg + 1);
334 	else
335 	    word(2);
336 	for (j = 0; j <= nseg; j++) {
337 	    w = (float) j / (float) nseg;
338 	    t1 = 0.5 * w * w;
339 	    w -= 0.5;
340 	    t2 = 0.75 - w * w ;
341 	    w -= 0.5;
342 	    t3 = 0.5 * w * w;
343 	    xp = t1 * x[i+2] + t2 * x[i+1] + t3 * x[i] + 0.5;
344 	    yp = t1 * y[i+2] + t2 * y[i+1] + t3 * y[i] + 0.5;
345 	    word(xbound(xp));
346 	    word(ybound(yp));
347 	}
348 	if (nseg == 0) {
349 	    word(xbound(xp));
350 	    word(ybound(yp));
351 	}
352 	byte(ADRAW);
353 	byte(15);
354     }
355 }
356 
357 
358 /*----------------------------------------------------------------------------
359  | Routine:	HGArc (xcenter, ycenter, xstart, ystart, angle)
360  |
361  | Results:	This routine plots an arc centered about (cx, cy) counter
362  |		clockwise starting from the point (px, py) through 'angle'
363  |		degrees.  If angle is 0, a full circle is drawn. It does so
364  |		by creating a draw-path around the arc whose density of
365  |		points depends on the size of the arc.
366  *----------------------------------------------------------------------------*/
367 
368 HGArc(cx,cy,px,py,angle)
369 int cx, cy, px, py, angle;
370 {
371     double xs, ys, resolution, fullcircle;
372     register int i;
373     register int extent;
374     register int nx;
375     register int ny;
376     register double epsilon;
377 
378     xs = px - cx;
379     ys = py - cy;
380 
381 /* calculate drawing parameters */
382 
383     resolution = pow(2.0, floor(log10(sqrt(xs * xs + ys * ys)) * log2_10));
384     epsilon = 1.0 / resolution;
385     fullcircle = ceil(2 * pi * resolution);
386     if (angle == 0)
387 	extent = fullcircle;
388     else
389 	extent = angle * fullcircle / 360.0;
390 
391     byte(ASPATH);			/* start path definition */
392     if (extent > 1) {
393 	word(extent);			/* number of points */
394 	for (i=0; i<extent; ++i) {
395 	    xs += epsilon * ys;
396 	    nx = (int) (xs + cx + 0.5);
397 	    ys -= epsilon * xs;
398 	    ny = (int) (ys + cy + 0.5);
399 	    word(xbound(nx));	/* put out a point on circle */
400 	    word(ybound(ny));
401 	}   /* end for */
402     } else {			/* arc is too small: put out point */
403 	word(2);
404 	word(xbound(cx));
405 	word(ybound(cy));
406 	word(xbound(cx));
407 	word(ybound(cy));
408     }
409     byte(ADRAW);		/* now draw the arc */
410     byte(15);
411 }  /* end HGArc */
412 
413 
414 /*----------------------------------------------------------------------------
415  | Routine:	Paramaterize (xpoints, ypoints, hparams, num_points)
416  |
417  | Results:	This routine calculates parameteric values for use in
418  |		calculating curves.  The parametric values are returned
419  |		in the array h.  The values are an approximation of
420  |		cumulative arc lengths of the curve (uses cord length).
421  |		For additional information, see paper cited below.
422  *----------------------------------------------------------------------------*/
423 
424 static Paramaterize(x, y, h, n)
425 int x[MAXPOINTS];
426 int y[MAXPOINTS];
427 float h[MAXPOINTS];
428 int n;
429 {
430 	register int dx;
431 	register int dy;
432 	register int i;
433 	register int j;
434 	float u[MAXPOINTS];
435 
436 
437 	for (i=1; i<=n; ++i) {
438 	    u[i] = 0;
439 	    for (j=1; j<i; j++) {
440 		dx = x[j+1] - x[j];
441 		dy = y[j+1] - y[j];
442 		u[i] += sqrt ((double) (dx * dx + dy * dy));
443 	    }
444 	}
445 	for (i=1; i<n; ++i)  h[i] = u[i+1] - u[i];
446 }  /* end Paramaterize */
447 
448 
449 /*----------------------------------------------------------------------------
450  | Routine:	PeriodicSpline (h, z, dz, d2z, d3z, npoints)
451  |
452  | Results:	This routine solves for the cubic polynomial to fit a
453  |		spline curve to the the points  specified by the list
454  |		of values.  The Curve generated is periodic.  The algorithms
455  |		for this curve are from the "Spline Curve Techniques" paper
456  |		cited below.
457  *----------------------------------------------------------------------------*/
458 
459 static PeriodicSpline(h, z, dz, d2z, d3z, npoints)
460 float h[MAXPOINTS];		/* paramaterization  */
461 int z[MAXPOINTS];		/* point list */
462 float dz[MAXPOINTS];			/* to return the 1st derivative */
463 float d2z[MAXPOINTS], d3z[MAXPOINTS];	/* 2nd and 3rd derivatives */
464 int npoints;				/* number of valid points */
465 {
466 	float d[MAXPOINTS];
467 	float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
468 	float c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS];
469 	int i;
470 
471 						/* step 1 */
472 	for (i=1; i<npoints; ++i) {
473 	    deltaz[i] = h[i] ? ((double) (z[i+1] - z[i])) / h[i] : 0;
474 	}
475 	h[0] = h[npoints-1];
476 	deltaz[0] = deltaz[npoints-1];
477 
478 						/* step 2 */
479 	for (i=1; i<npoints-1; ++i) {
480 	    d[i] = deltaz[i+1] - deltaz[i];
481 	}
482 	d[0] = deltaz[1] - deltaz[0];
483 
484 						/* step 3a */
485 	a[1] = 2 * (h[0] + h[1]);
486 	b[1] = d[0];
487 	c[1] = h[0];
488 	for (i=2; i<npoints-1; ++i) {
489 	    a[i] = 2*(h[i-1]+h[i]) - pow ((double) h[i-1],(double)2.0) / a[i-1];
490 	    b[i] = d[i-1] - h[i-1] * b[i-1]/a[i-1];
491 	    c[i] = -h[i-1] * c[i-1]/a[i-1];
492 	}
493 
494 						/* step 3b */
495 	r[npoints-1] = 1;
496 	s[npoints-1] = 0;
497 	for (i=npoints-2; i>0; --i) {
498 	    r[i] = -(h[i] * r[i+1] + c[i])/a[i];
499 	    s[i] = (6 * b[i] - h[i] * s[i+1])/a[i];
500 	}
501 
502 						/* step 4 */
503 	d2z[npoints-1] = (6 * d[npoints-2] - h[0] * s[1]
504 	                   - h[npoints-1] * s[npoints-2])
505 	                 / (h[0] * r[1] + h[npoints-1] * r[npoints-2]
506 	                    + 2 * (h[npoints-2] + h[0]));
507 	for (i=1; i<npoints-1; ++i) {
508 	    d2z[i] = r[i] * d2z[npoints-1] + s[i];
509 	}
510 	d2z[npoints] = d2z[1];
511 
512 						/* step 5 */
513 	for (i=1; i<npoints; ++i) {
514 	    dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i+1])/6;
515 	    d3z[i] = h[i] ? (d2z[i+1] - d2z[i])/h[i] : 0;
516 	}
517 }  /* end PeriodicSpline */
518 
519 
520 /*----------------------------------------------------------------------------
521  | Routine:	NaturalEndSpline (h, z, dz, d2z, d3z, npoints)
522  |
523  | Results:	This routine solves for the cubic polynomial to fit a
524  |		spline curve the the points  specified by the list of
525  |		values.  The alogrithms for this curve are from the
526  |		"Spline Curve Techniques" paper cited below.
527  *----------------------------------------------------------------------------*/
528 
529 static NaturalEndSpline(h, z, dz, d2z, d3z, npoints)
530 float h[MAXPOINTS];		/* parameterization */
531 int z[MAXPOINTS];		/* Point list */
532 float dz[MAXPOINTS];			/* to return the 1st derivative */
533 float d2z[MAXPOINTS], d3z[MAXPOINTS];	/* 2nd and 3rd derivatives */
534 int npoints;				/* number of valid points */
535 {
536 	float d[MAXPOINTS];
537 	float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
538 	int i;
539 
540 						/* step 1 */
541 	for (i=1; i<npoints; ++i) {
542 	    deltaz[i] = h[i] ? ((double) (z[i+1] - z[i])) / h[i] : 0;
543 	}
544 	deltaz[0] = deltaz[npoints-1];
545 
546 						/* step 2 */
547 	for (i=1; i<npoints-1; ++i) {
548 	    d[i] = deltaz[i+1] - deltaz[i];
549 	}
550 	d[0] = deltaz[1] - deltaz[0];
551 
552 						/* step 3 */
553 	a[0] = 2 * (h[2] + h[1]);
554 	b[0] = d[1];
555 	for (i=1; i<npoints-2; ++i) {
556 	    a[i] = 2*(h[i+1]+h[i+2]) - pow((double) h[i+1],(double) 2.0)/a[i-1];
557 	    b[i] = d[i+1] - h[i+1] * b[i-1]/a[i-1];
558 	}
559 
560 						/* step 4 */
561 	d2z[npoints] = d2z[1] = 0;
562 	for (i=npoints-1; i>1; --i) {
563 	    d2z[i] = (6 * b[i-2] - h[i] *d2z[i+1])/a[i-2];
564 	}
565 
566 						/* step 5 */
567 	for (i=1; i<npoints; ++i) {
568 	    dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i+1])/6;
569 	    d3z[i] = h[i] ? (d2z[i+1] - d2z[i])/h[i] : 0;
570 	}
571 }  /* end NaturalEndSpline */
572 
573 
574 /*----------------------------------------------------------------------------
575  | Routine:	HGCurve(xpoints, ypoints, num_points)
576  |
577  | Results:	This routine generates a smooth curve through a set of points.
578  |		The method used is the parametric spline curve on unit knot
579  |		mesh described in "Spline Curve Techniques" by Patrick
580  |		Baudelaire, Robert Flegal, and Robert Sproull -- Xerox Parc.
581  *----------------------------------------------------------------------------*/
582 
583 #define PointsPerInterval 32
584 
585 HGCurve(x, y, numpoints)
586 int x[MAXPOINTS];
587 int y[MAXPOINTS];
588 int numpoints;
589 {
590 	float h[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS];
591 	float d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS];
592 	float t, t2, t3;
593 	register int j;
594 	register int k;
595 	register int nx;
596 	register int ny;
597 	int lx, ly;
598 
599 
600 	lx = x[1];
601 	ly = y[1];
602 
603 	     /* Solve for derivatives of the curve at each point
604               * separately for x and y (parametric).
605 	      */
606 	Paramaterize(x, y, h, numpoints);
607 							/* closed curve */
608 	if ((x[1] == x[numpoints]) && (y[1] == y[numpoints])) {
609 	    PeriodicSpline(h, x, dx, d2x, d3x, numpoints);
610 	    PeriodicSpline(h, y, dy, d2y, d3y, numpoints);
611 	} else {
612 	    NaturalEndSpline(h, x, dx, d2x, d3x, numpoints);
613 	    NaturalEndSpline(h, y, dy, d2y, d3y, numpoints);
614 	}
615 
616 	      /* generate the curve using the above information and
617 	       * PointsPerInterval vectors between each specified knot.
618 	       */
619 
620 	for (j=1; j<numpoints; ++j) {
621 	    if ((x[j] == x[j+1]) && (y[j] == y[j+1])) continue;
622 	    for (k=0; k<=PointsPerInterval; ++k) {
623 		t = (float) k * h[j] / (float) PointsPerInterval;
624 		t2 = t * t;
625 		t3 = t * t * t;
626 		nx = x[j] + (int) (t * dx[j] + t2 * d2x[j]/2 + t3 * d3x[j]/6);
627 		ny = y[j] + (int) (t * dy[j] + t2 * d2y[j]/2 + t3 * d3y[j]/6);
628 		HGtline(lx, ly, nx, ny);
629 		lx = nx;
630 		ly = ny;
631 	    }  /* end for k */
632 	}  /* end for j */
633 }  /* end HGCurve */
634 
635 
636 /*----------------------------------------------------------------------------
637  | Routine:	line(xstart, ystart, xend, yend)
638  |
639  | Results:	Creates a drawing path and draws the line.  If the line falls
640  |		off the end of the page, a crude clipping is done:  truncating
641  |		the offending ordinate.
642  *----------------------------------------------------------------------------*/
643 
644 line(x0, y0, x1, y1)
645 int x0, y0, x1, y1;
646 {
647     byte(ASPATH);		/* send the coordinates first */
648     word(2);		/* only two */
649     word(xbound(x0));
650     word(ybound(y0));
651     word(xbound(x1));
652     word(ybound(y1));
653     byte(ADRAW);		/* now draw it */
654     byte(15);		/* black */
655 }  /* end line */
656 
657 
658 /*----------------------------------------------------------------------------*
659  | Routine:	change (x_position, y_position, visible_flag)
660  |
661  | Results:	As HGtline passes from the invisible to visible (or vice
662  |		versa) portion of a line, change is called to either draw
663  |		the line, or initialize the beginning of the next one.
664  |		Change calls line to draw segments if visible_flag is set
665  |		(which means we're leaving a visible area).
666  *----------------------------------------------------------------------------*/
667 
668 change (x, y, vis)
669 register int x;
670 register int y;
671 register int vis;
672 {
673     static int xorg;
674     static int yorg;
675 
676     if (vis)		/* leaving a visible area, draw it. */
677 	line (xorg, yorg, x, y);
678     else {		/* otherwise, we're entering one, remember beginning */
679 	xorg = x;
680 	yorg = y;
681     }
682 }
683 
684 
685 /*----------------------------------------------------------------------------
686  | Routine:	HGtline (xstart, ystart, xend, yend)
687  |
688  | Results:	Draws a line from (x0,y0) to (x1,y1) using line(x0,y0,x1,y1)
689  |		to place individual segments of dotted or dashed lines.
690  *----------------------------------------------------------------------------*/
691 
692 HGtline(x0, y0, x1, y1)
693 register int x0;
694 register int y0;
695 int x1;
696 int y1;
697 {
698     register int dx;
699     register int dy;
700     register int res1;
701     register int visible;
702     int res2;
703     int xinc;
704     int yinc;
705     int slope;
706 
707 
708     if (linmod == -1) {
709 	line(x0, y0, x1, y1);
710 	return;
711     }
712     xinc = 1;
713     yinc = 1;
714     if ((dx = x1-x0) < 0) {
715         xinc = -1;
716         dx = -dx;
717     }
718     if ((dy = y1-y0) < 0) {
719         yinc = -1;
720         dy = -dy;
721     }
722     slope = xinc*yinc;
723     res1 = 0;
724     res2 = 0;
725     visible = 0;
726     if (dx >= dy)
727         while (x0 != x1) {
728             if((((x0+slope*y0)&linmod)&&1)^visible) {
729 	    	change(x0, y0, visible);
730 		visible = !visible;
731 	    }
732             if (res1 > res2) {
733                 res2 += dx - res1;
734                 res1 = 0;
735                 y0 += yinc;
736             }
737             res1 += dy;
738             x0 += xinc;
739         }
740     else
741         while (y0 != y1) {
742             if((((x0+slope*y0)&linmod)&&1)^visible) {
743 		change(x0, y0, visible);
744 		visible = !visible;
745 	    }
746             if (res1 > res2) {
747                 res2 += dy - res1;
748                 res1 = 0;
749                 x0 += xinc;
750             }
751             res1 += dx;
752             y0 += yinc;
753         }
754     if(visible) change(x1, y1, 1);
755 }
756