1 /*	hgraph.c	1.4	83/05/05
2  *
3  * Copyright -C- 1982 Barry S. Roitblat
4  *
5  *     This file contains the graphics routines for hard copy (gprint)
6  * production of gremlin files.
7  *
8  */
9 
10 #include "gprint.h"
11 #include <vfont.h>
12 
13 
14 /* line and character styles */
15 
16 int style[STYLES] = { DOTTED, DOTDASHED, SOLID, DASHED, SOLID, SOLID };
17 int thick[STYLES] = { 1, 1, 5, 1, 1, 3 };
18 char *tfont[FONTS] = { "R", "I", "B", "S" };
19 char *tsize[SIZES] = { "10", "16", "24", "36" };
20 
21 
22 /* variables used to print from font file */
23 
24 extern Orientation;
25 extern cfont;
26 extern csize;
27 extern struct header header;
28 extern struct dispatch dispatch[256];
29 extern char *bits;
30 extern char *fontdir ;
31 
32 /* imports from main.c */
33 
34 extern double scale;
35 extern point();
36 extern int    linethickness;
37 extern int    linmod;
38 extern int    lastx;
39 extern int    lasty;
40 extern double    orgx;
41 extern double    orgy;
42 extern int DevRange;
43 
44 
45 line(x0, y0, x1, y1)
46 int x0, y0, x1, y1;
47 
48 /* This routine is called to draw a line from the point at (x0, y0) to (x1, y1).
49  * The line is drawn using a variation of
50  */
51 
52 {
53     int dx, dy;
54     int xinc, yinc;
55     int res1;
56     int res2;
57     int slope;
58 
59     xinc = 1;
60     yinc = 1;
61     if ((dx = x1-x0) < 0)
62     {
63         xinc = -1;
64         dx = -dx;
65     }
66     if ((dy = y1-y0) < 0)
67     {
68         yinc = -1;
69         dy = -dy;
70     }
71     slope = xinc*yinc;
72     res1 = 0;
73     res2 = 0;
74     if (dx >= dy)
75         while (x0 != x1)
76         {
77             if((x0+slope*y0)&linmod) point(x0, y0);
78             if (res1 > res2)
79             {
80                 res2 += dx - res1;
81                 res1 = 0;
82                 y0 += yinc;
83             }
84             res1 += dy;
85             x0 += xinc;
86         }
87     else
88         while (y0 != y1)
89         {
90             if((x0+slope*y0)&linmod) point(x0, y0);
91             if (res1 > res2)
92             {
93                 res2 += dy - res1;
94                 res1 = 0;
95                 x0 += xinc;
96             }
97             res1 += dx;
98             y0 += yinc;
99         }
100     if((x1+slope*y1)&linmod) point(x1, y1);
101 }
102 
103 
104 HGPrintElt(element)
105 ELT *element;
106 
107 /* This routine examines the picture elements and calls the appropriate
108  * routine(s) to print them according to their type.
109  */
110 
111 {
112     POINT *p1, *p2, pt1, pt2;
113     int x1, y1, i, dx, dy;
114     char *txt, c;
115 
116     if ( !DBNullelt(element) )
117     {
118         if (TEXT(element->type))
119         {
120             p1 = element->ptlist;
121             HGSetFont(element->brushf, element->size);
122             pt1.x = mapx(xorn(p1->x, p1->y));
123             pt1.y = mapy(yorn(p1->x, p1->y));
124             txt = element->textpt;
125             HGPutText(element->type, pt1, txt);
126         }
127         else
128         {
129             switch (element->type)
130             {
131                  case ARC:  p1 = element->ptlist;
132                             p2 = PTNextPoint(p1);
133                             HGSetBrush(element->brushf);
134                             pt1.x = mapx(xorn(p1->x, p1->y));
135                             pt1.y = mapy(yorn(p1->x, p1->y));
136                             pt2.x = mapx(xorn(p2->x, p2->y));
137                             pt2.y = mapy(yorn(p2->x, p2->y));
138                             HGArc(&pt1, &pt2, element->size);
139                             break;
140 
141                case CURVE:  HGSetBrush(element->brushf);
142                             HGCurve(element->ptlist);
143                             break;
144 
145               case VECTOR:  p1 = element->ptlist;
146                             p2 = PTNextPoint(p1);
147                             HGSetBrush(element->brushf);
148                             while ( !Nullpoint(p2) )
149                             {
150                                 x1 = mapx(xorn(p1->x, p1->y));
151                                 y1 = mapy(yorn(p1->x, p1->y));
152                                 lastx = mapx(xorn(p2->x, p2->y));
153                                 lasty = mapy(yorn(p2->x, p2->y));
154                                 HGtline(x1, y1, lastx, lasty);
155                                 p1 = p2;
156                                 p2 = PTNextPoint(p2);
157                             }  /* end while */;
158                             break;
159             }  /* end switch */;
160         }  /* end else Text */
161     }  /* end if */
162 }  /* end PrintElt */
163 
164 
165 HGPutText(justify,pnt,string)
166 int justify;
167 POINT pnt;
168 char string[];
169 
170 /* This routine is used to calculate the proper starting position for a
171  * text string (based on justification, size and font), and prints it
172  * character by character.
173  */
174 
175 {
176     int length, height, nchars, i;
177     POINT pos;
178 
179     height = dispatch['T'].up + dispatch['y'].down;
180     length = 0;
181     for (i=0; string[i] != '\0'; ++i)
182         length += dispatch[(string[i] == ' ') ? 'a' : string[i]].width;
183     nchars = i;
184         switch (justify)
185 	{
186 		  case BOTLEFT:	pos.x = pnt.x;
187 				pos.y = pnt.y;
188 				break;
189 		  case BOTCENT:	if (Orientation == 0)
190                                 {
191                                    pos.x = pnt.x - (length/2);
192 				   pos.y = pnt.y;
193                                 }
194                                 else
195                                 {
196                                    pos.x = pnt.x;
197 				   pos.y = pnt.y - (length/2);
198                                 }
199 				break;
200 		 case BOTRIGHT:	if (Orientation == 0)
201                                 {
202                                    pos.x = pnt.x - length;
203 				   pos.y = pnt.y;
204                                 }
205                                 else
206                                 {
207                                    pos.x = pnt.x;
208 				   pos.y = pnt.y - length;
209                                 }
210 				break;
211 		 case CENTLEFT:	if (Orientation == 0)
212                                 {
213                                    pos.x = pnt.x;
214 				   pos.y = pnt.y + (height/2);
215                                 }
216                                 else
217                                 {
218                                    pos.x = pnt.x - (height/2);
219 				   pos.y = pnt.y;
220                                 }
221 				break;
222 		 case CENTCENT:	if (Orientation == 0)
223                                 {
224                                    pos.x = pnt.x - (length/2);
225 				   pos.y = pnt.y + (height/2);
226                                 }
227                                 else
228                                 {
229                                    pos.x = pnt.x - (height/2);
230 				   pos.y = pnt.y - (length/2);
231                                 }
232 				break;
233 		case CENTRIGHT:	if (Orientation == 0)
234                                 {
235                                    pos.x = pnt.x - length;
236 				   pos.y = pnt.y + (height/2);
237                                 }
238                                 else
239                                 {
240                                    pos.x = pnt.x - (height/2);
241 				   pos.y = pnt.y - length;
242                                 }
243 				break;
244 		  case TOPLEFT:	if (Orientation == 0)
245                                 {
246                                    pos.x = pnt.x;
247 				   pos.y = pnt.y + height;
248 
249                                 }
250                                 else
251                                 {
252                                    pos.x = pnt.x - height;
253 				   pos.y = pnt.y;
254                                 }
255 				break;
256 		  case TOPCENT:	if (Orientation == 0)
257                                 {
258                                    pos.x = pnt.x - (length/2);
259 				   pos.y = pnt.y + height;
260                                 }
261                                 else
262                                 {
263                                    pos.x = pnt.x - height;
264 				   pos.y = pnt.y - (length/2);
265                                 }
266 				break;
267 		 case TOPRIGHT:	if (Orientation == 0)
268                                 {
269                                    pos.x = pnt.x - length;
270 				   pos.y = pnt.y + height;
271                                 }
272                                 else
273                                 {
274                                    pos.x = pnt.x - height;
275 				   pos.y = pnt.y - length;
276                                 }
277 				break;
278 	}
279 
280     HGMove(pos);
281     for ( i=0; i<nchars; ++i )
282     {
283         HGplotch(string[i]);
284     }
285 } /* end HGPutText */;
286 
287 
288 #define pi 3.14159265357
289 #define log2_10 3.321915
290 
291 HGArc(center,cpoint,angle)
292 POINT *center, *cpoint;
293 int angle;
294 
295 /* This routine plots an arc centered about 'center' counter clockwise for
296  * the point 'cpoint' through 'angle' degrees.  If angle is 0, a full circle
297  * is drawn.
298  */
299 
300 {
301     double xs, ys, resolution, epsalon, degreesperpoint, fullcircle;
302     double t1, t2;
303     int i, extent, nx, ny;
304 
305     xs = cpoint->x - center->x;
306     ys = cpoint->y - center->y;
307     lastx = (int) cpoint->x;
308     lasty = (int) cpoint->y;
309 
310 /* calculate drawing parameters */
311 
312     t1 = log10(sqrt( xs * xs + ys * ys)) * log2_10;
313     t1 = ceil(t1);
314     resolution = pow(2.0, t1);
315     epsalon = 1.0 / resolution;
316     fullcircle = 2 * pi * resolution;
317     fullcircle = ceil(fullcircle);
318     degreesperpoint = 360.0 / fullcircle;
319 
320     if (angle == 0) extent = fullcircle;
321     else extent = angle/degreesperpoint;
322 
323     for (i=0; i<extent; ++i)
324     {
325         xs += epsalon * ys;
326         nx = (int) (xs + center->x + 0.5);
327         ys -= epsalon * xs;
328         ny = (int) (ys + center->y + 0.5);
329         RoundEnd(nx, ny, (int) (linethickness/2), FALSE);
330         lastx = nx;
331         lasty = ny;
332     }   /* end for */;
333 }  /* end HGArc */;
334 
335 
336 RoundEnd(x, y, radius, filled)
337 int x, y, radius;
338 int filled;                /* indicates whether the circle is filled */
339 
340 /* This routine plots a filled circle of the specified radius centered
341  * about (x, y).
342  */
343 
344 {
345     double xs, ys, epsalon;
346     int i, j, k, extent, nx, ny;
347     int cx, cy;
348 
349     if (radius < 1)    /* too small to notice */
350     {
351         point(x, y);
352         return;
353     }
354     xs = 0;
355     ys = radius;
356     epsalon = 1.0 / radius;
357     extent = pi * radius / 2;    /* 1/4 the circumference */
358 
359         /* Calculate the trajectory of the circle for 1/4 the circumference
360          * and mirror appropriately to get the other three quadrants.
361          */
362 
363     point(x, y+((int) ys));    /* take care if end of arc missed by */
364     point(x, y-((int) ys));    /* below formulation                 */
365     for (i=0; i<extent; ++i)
366     {
367              /* generate circumference */
368         xs += epsalon * ys;
369         nx = (int) (xs + x + 0.5);
370         if (nx < x) nx = x;  /* 1st quadrant, should be positive */
371         ys -= epsalon * xs;
372         ny = (int) (ys + y + 0.5);
373         if (ny < y) ny = y;  /* 1st quadrant, should be positive */
374 
375         if (filled == TRUE)
376         {       /* fill from center */
377             cx = x;
378             cy = y;
379         }
380         else
381         {       /* fill from perimeter only (no fill) */
382             cx = nx;
383             cy = ny;
384         }
385         for (j=cx; j<=nx; ++j)
386         {
387             for (k=cy; k<=ny; ++k)
388             {
389                 point(j, k);
390                 point(j, 2*y-k);
391                 point(2*x-j, k);
392                 point(2*x-j, 2*y-k);
393             }  /* end for k */
394         }  /* end for j */;
395     }  /* end for i */;
396 }  /* end RoundEnd */;
397 
398 
399 #define MAXPOINTS 200
400 
401 static Paramaterize(x, y, h, n)
402 float x[MAXPOINTS], y[MAXPOINTS], h[MAXPOINTS];
403 int n;
404 /*     This routine calculates parameteric values for use in calculating
405  * curves.  The parametric values are returned in the array u.  The values
406  * are an approximation of cumulative arc lengths of the curve (uses cord
407  * length).  For additional information, see paper cited below.
408  */
409 
410 {
411 	int i,j;
412 	float u[MAXPOINTS];
413 
414 	for (i=1; i<=n; ++i)
415 	{
416 		u[i] = 0;
417 		for (j=1; j<i; ++j)
418 		{
419 			u[i] += sqrt(pow((double) (x[j+1] - x[j]),(double) 2.0)
420 			         + pow((double) (y[j+1] - y[j]), (double) 2.0));
421 		}
422 	}
423 	for (i=1; i<n; ++i)
424 		h[i] = u[i+1] - u[i];
425 }  /* end Paramaterize */
426 
427 static PeriodicSpline(h, z, dz, d2z, d3z, npoints)
428 float h[MAXPOINTS], z[MAXPOINTS];	/* Point list and paramaterization  */
429 float dz[MAXPOINTS];			/* to return the 1st derivative */
430 float d2z[MAXPOINTS], d3z[MAXPOINTS];	/* 2nd and 3rd derivatives */
431 int npoints;				/* number of valid points */
432 /*
433  *     This routine solves for the cubic polynomial to fit a spline
434  * curve to the the points  specified by the list of values.
435  * The Curve generated is periodic.  The alogrithms for this
436  * curve are from the "Spline Curve Techniques" paper cited below.
437  */
438 
439 {
440 	float d[MAXPOINTS];
441 	float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
442 	float c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS];
443 	int i;
444 
445 	            /* step 1 */
446 	for (i=1; i<npoints; ++i)
447 	{
448 		if (h[i] != 0)
449 			deltaz[i] = (z[i+1] - z[i]) / h[i];
450 		else
451 			deltaz[i] = 0;
452 	}
453 	h[0] = h[npoints-1];
454 	deltaz[0] = deltaz[npoints-1];
455 
456 	            /* step 2 */
457 	for (i=1; i<npoints-1; ++i)
458 	{
459 		d[i] = deltaz[i+1] - deltaz[i];
460 	}
461 	d[0] = deltaz[1] - deltaz[0];
462 
463 	            /* step 3a */
464 	a[1] = 2 * (h[0] + h[1]);
465 	b[1] = d[0];
466 	c[1] = h[0];
467 	for (i=2; i<npoints-1; ++i)
468 	{
469 		a[i] = 2 * (h[i-1] + h[i]) - pow((double) h[i-1], (double) 2.0)
470 		            / a[i-1];
471 		b[i] = d[i-1] - h[i-1] * b[i-1]/a[i-1];
472 		c[i] = -h[i-1] * c[i-1]/a[i-1];
473 	}
474 
475 	            /* step 3b */
476 	r[npoints-1] = 1;
477 	s[npoints-1] = 0;
478 	for (i=npoints-2; i>0; --i)
479 	{
480 		r[i] = -(h[i] * r[i+1] + c[i])/a[i];
481 		s[i] = (6 * b[i] - h[i] * s[i+1])/a[i];
482 	}
483 
484 	            /* step 4 */
485 	d2z[npoints-1] = (6 * d[npoints-2] - h[0] * s[1]
486 	                   - h[npoints-1] * s[npoints-2])
487 	                 / (h[0] * r[1] + h[npoints-1] * r[npoints-2]
488 	                    + 2 * (h[npoints-2] + h[0]));
489 	for (i=1; i<npoints-1; ++i)
490 	{
491 		d2z[i] = r[i] * d2z[npoints-1] + s[i];
492 	}
493 	d2z[npoints] = d2z[1];
494 
495 	            /* step 5 */
496 	for (i=1; i<npoints; ++i)
497 	{
498 		dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i+1])/6;
499 		if (h[i] != 0)
500 			d3z[i] = (d2z[i+1] - d2z[i])/h[i];
501 		else
502 			d3z[i] = 0;
503 	}
504 }  /* end PeriodicSpline */
505 
506 
507 static NaturalEndSpline(h, z, dz, d2z, d3z, npoints)
508 float h[MAXPOINTS], z[MAXPOINTS];	/* Point list and parameterization */
509 float dz[MAXPOINTS];			/* to return the 1st derivative */
510 float d2z[MAXPOINTS], d3z[MAXPOINTS];	/* 2nd and 3rd derivatives */
511 int npoints;				/* number of valid points */
512 /*
513  *     This routine solves for the cubic polynomial to fit a spline
514  * curve the the points  specified by the list of values.  The alogrithms for
515  * this curve are from the "Spline Curve Techniques" paper cited below.
516  */
517 
518 {
519 	float d[MAXPOINTS];
520 	float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
521 	int i;
522 
523 	            /* step 1 */
524 	for (i=1; i<npoints; ++i)
525 	{
526 		if (h[i] != 0)
527 			deltaz[i] = (z[i+1] - z[i]) / h[i];
528 		else
529 			deltaz[i] = 0;
530 	}
531 	deltaz[0] = deltaz[npoints-1];
532 
533 	            /* step 2 */
534 	for (i=1; i<npoints-1; ++i)
535 	{
536 		d[i] = deltaz[i+1] - deltaz[i];
537 	}
538 	d[0] = deltaz[1] - deltaz[0];
539 
540 	            /* step 3 */
541 	a[0] = 2 * (h[2] + h[1]);
542 	b[0] = d[1];
543 	for (i=1; i<npoints-2; ++i)
544 	{
545 		a[i] = 2 * (h[i+1] + h[i+2]) - pow((double) h[i+1],(double) 2.0)
546 		             / a[i-1];
547 		b[i] = d[i+1] - h[i+1] * b[i-1]/a[i-1];
548 	}
549 
550 	            /* step 4 */
551 	d2z[npoints] = d2z[1] = 0;
552 	for (i=npoints-1; i>1; --i)
553 	{
554 		d2z[i] = (6 * b[i-2] - h[i] *d2z[i+1])/a[i-2];
555 	}
556 
557 	            /* step 5 */
558 	for (i=1; i<npoints; ++i)
559 	{
560 		dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i+1])/6;
561 		if (h[i] != 0)
562 			d3z[i] = (d2z[i+1] - d2z[i])/h[i];
563 		else
564 			d3z[i] = 0;
565 	}
566 }  /* end NaturalEndSpline */
567 
568 
569 #define PointsPerInterval 32
570 
571 HGCurve(pointlist,style)
572 POINT *pointlist;
573 int   style;
574 /*
575  *    This routine generates a smooth curve through a set of points.  The
576  * method used is the parametric spline curve on unit knot mesh described
577  * in "Spline Curve Techniques" by Patrick Baudelaire, Robert Flegal, and
578  * Robert Sproull -- Xerox Parc.
579  */
580 {
581 	float h[MAXPOINTS];
582 	float x[MAXPOINTS], y[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS];
583 	float d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS];
584 	float t, t2, t3, xinter, yinter;
585 	POINT *ptr;
586 	int numpoints, i, j, k, lx, ly, nx, ny;
587 
588               /* Copy point list to array for easier access */
589 
590 	ptr = pointlist;
591 	for (i=1; (!Nullpoint(ptr)); ++i)
592 	{
593 		x[i] = mapx(xorn(ptr->x, ptr->y));
594 		y[i] = mapy(yorn(ptr->x, ptr->y));
595 		if (i >= MAXPOINTS - 1) break;
596 		ptr = PTNextPoint(ptr);
597 	}
598 	lx = (int) x[1];
599 	ly = (int) y[1];
600 
601 	     /* Solve for derivatives of the curve at each point
602               * separately for x and y (parametric).
603 	      */
604 	numpoints = i - 1;
605 	Paramaterize(x, y, h, numpoints);
606 	if ((x[1] == x[numpoints]) && (y[1] == y[numpoints]))/* closed curve */
607 	{
608 		PeriodicSpline(h, x, dx, d2x, d3x, numpoints);
609 		PeriodicSpline(h, y, dy, d2y, d3y, numpoints);
610 	}
611 	else
612 	{
613 		NaturalEndSpline(h, x, dx, d2x, d3x, numpoints);
614 		NaturalEndSpline(h, y, dy, d2y, d3y, numpoints);
615 	}
616 
617 	      /* generate the curve using the above information and
618 	       * PointsPerInterval vectors between each specified knot.
619 	       */
620 
621 	for (j=1; j<numpoints; ++j)
622 	{
623 		if ((x[j] == x[j+1]) && (y[j] == y[j+1])) continue;
624 		for (k=0; k<=PointsPerInterval; ++k)
625 		{
626 			t = (float) k * h[j] / (float) PointsPerInterval;
627 			t2 = t * t;
628 			t3 = t * t * t;
629 			xinter = x[j] + t * dx[j] + t2 * d2x[j]/2
630 			       + t3 * d3x[j]/6;
631 			nx = (int) xinter;
632 			yinter = y[j] + t * dy[j] + t2 * d2y[j]/2
633 			       + t3 * d3y[j]/6;
634 			ny = (int) yinter;
635 			HGtline(lx, ly, nx, ny);
636 			lx = nx;
637 			ly = ny;
638 		}  /* end for k */
639 	}  /* end for j */
640 }  /* end HGCurve */
641 
642 
643 
644 HGplotch(ch)
645 char ch;
646 
647 /* This routine prints a single character using the current (bit mapped)
648  * vtroff font
649  */
650 
651 {
652     int i,j,k;
653     char *ptr,c;
654     int nbytes;
655 
656     ptr = bits + dispatch[ch].addr;
657 
658     for(i = dispatch[ch].up; i > -dispatch[ch].down; --i)
659     {
660         nbytes = (dispatch[ch].right + dispatch[ch].left + 7)/8;
661         for(j = 0; j < nbytes; j++)
662         {
663             c = *ptr++;
664             for(k=7; k>=0; --k)
665                 if((c>>k) & 1)
666                     if (Orientation == 0 )
667                            point(lastx+7-k+j*8-dispatch[ch].left, lasty-i);
668                     else
669                            point(lastx+i, lasty+7-k+j*8-dispatch[ch].left);
670         }
671     }
672     if (Orientation == 0)
673           lastx += dispatch[ (ch == ' ') ? 'a' : ch ].width;
674     else
675           lasty += dispatch[ (ch == ' ') ? 'a' : ch ].width;
676 }
677 
678 
679 HGInitFont(fontFile)
680 char *fontFile;
681 
682 /* This routine reads in the appropriate font file */
683 
684 {
685     char *s;
686     int fonts;
687     int i;
688 
689     /* Get the font file */
690     s = fontFile;
691     if((fonts = open(s,0)) == -1)
692     {
693         perror(s);
694         fprintf(stderr,"Can't get font file\n");
695         exit(1);
696     }
697     /* Get the header and check magic number */
698     if(read(fonts,&header,sizeof(header)) != sizeof(header))
699     {
700         perror(s);
701         fprintf(stderr,"Bad read in font file\n");
702         exit(1);
703     }
704     if(header.magic != 0436)
705     {
706         fprintf(stderr,"Bad magic numer in font file\n");
707         exit(1);
708     }
709     /* Get dispatches */
710     if(read(fonts,dispatch,sizeof(dispatch)) != sizeof(dispatch))
711     {
712         perror(s);
713         fprintf(stderr,"Bad read in font file\n");
714         exit(1);
715     }
716     /* Allocate space for bit map and read in bits */
717     if (bits != NULL) free(bits);
718     bits = (char *) malloc(header.size);
719 
720     if(read(fonts,bits,header.size) != header.size)
721     {
722         perror(s);
723         fprintf(stderr,"Can't read bit map in font file\n");
724         exit(1);
725     }
726     /* Close font file */
727     if(close(fonts) != 0)
728     {
729         perror(s);
730         fprintf(stderr,"Can't close font file\n");
731         exit(1);
732     }
733 }
734 
735 
736 HGtline(x0, y0, x1, y1)
737 int x0, y0, x1, y1;
738 /*
739  *      This routine calls line repeatedly until the line is
740  * of the proper thickness.
741  */
742 
743 {
744         double morelen, theta, wx, wy, xx, xy;
745         int xs, xe, ys, ye;
746         int addln, j, xdir, ydir, dx, dy;
747 
748         xdir = ydir = 1;
749         dx = x1 - x0;   /* calculate direction to move to  */
750         dy = y1 - y0;   /* move to draw additional lines if needed */
751         if (dx < 0 )       /* for extra thickness */
752         {
753             dx = -dx;
754             xdir = -1;
755         }
756         if (dy < 0 )
757         {
758             dy = -dy;
759             ydir = -1;
760         }
761 
762         morelen = linethickness / 2;
763 	addln = (int) morelen;
764         RoundEnd(x0, y0, (int) morelen, TRUE);    /* add rounded end */
765         for (j=(-addln); j<=addln; ++j)
766         {
767                 if (dy == 0)
768                 {
769                         xs = x0;
770                         xe = x1;
771                         ys = ye = y0 + j;
772                 }
773                 if (dx == 0)
774                 {
775                        ys = y0;
776                        ye = y1;
777                        xs = xe = x0 + j;
778                 }
779                 if ((dx != 0) && (dy != 0))
780                 {
781                        theta =  pi / 2.0 - atan( ((double) dx)/((double) dy) );
782                        wx = j * sin(theta);
783                        wy = j * cos(theta);
784                        xs = x0 + wx * xdir;
785                        ys = y0 - wy * ydir;
786                        xe = x1 + wx * xdir;
787                        ye = y1 - wy * ydir;
788                 }
789                 line(xs, ys, xe, ye);
790         }  /* end for */
791         RoundEnd(x1, y1, (int) morelen, TRUE);    /* add rounded end */
792 }  /* end HGtline */
793 
794 
795 HGMove(p)
796 POINT p;
797 {
798         lastx = p.x;
799         lasty = p.y;
800 }
801 
802 HGSetFont(font, size)
803 int font, size;
804 {
805     int i;
806     char c, string[100];
807 
808     if ((font == cfont) && (size == csize)) return;
809     cfont = font;
810     csize = size;
811     strcpy(string, fontdir);
812     strcat(string, tfont[font-1]);
813     strcat(string, ".");
814     strcat(string, tsize[size-1]);
815     HGInitFont(string);
816 }
817 
818 HGSetBrush(mode)
819 int mode;
820 {
821     linmod = style[mode - 1];
822     linethickness = thick[mode - 1];
823 
824 }
825