1114402Sru/* Last non-groff version: hgraph.c  1.14 (Berkeley) 84/11/27
2114402Sru *
3114402Sru * This file contains the graphics routines for converting gremlin pictures
4114402Sru * to troff input.
5114402Sru */
6114402Sru
7114402Sru#include "lib.h"
8114402Sru
9114402Sru#include "gprint.h"
10114402Sru
11114402Sru#define MAXVECT	40
12114402Sru#define MAXPOINTS	200
13114402Sru#define LINELENGTH	1
14114402Sru#define PointsPerInterval 64
15114402Sru#define pi		3.14159265358979324
16114402Sru#define twopi		(2.0 * pi)
17151497Sru#define len(a, b)	groff_hypot((double)(b.x-a.x), (double)(b.y-a.y))
18114402Sru
19114402Sru
20114402Sruextern int dotshifter;		/* for the length of dotted curves */
21114402Sru
22114402Sruextern int style[];		/* line and character styles */
23114402Sruextern double thick[];
24114402Sruextern char *tfont[];
25114402Sruextern int tsize[];
26114402Sruextern int stipple_index[];	/* stipple font index for stipples 0 - 16 */
27114402Sruextern char *stipple;		/* stipple type (cf or ug) */
28114402Sru
29114402Sru
30114402Sruextern double troffscale;	/* imports from main.c */
31114402Sruextern double linethickness;
32114402Sruextern int linmod;
33114402Sruextern int lastx;
34114402Sruextern int lasty;
35114402Sruextern int lastyline;
36114402Sruextern int ytop;
37114402Sruextern int ybottom;
38114402Sruextern int xleft;
39114402Sruextern int xright;
40151497Sruextern enum E {
41114402Sru  OUTLINE, FILL, BOTH
42114402Sru} polyfill;
43114402Sru
44114402Sruextern double adj1;
45114402Sruextern double adj2;
46114402Sruextern double adj3;
47114402Sruextern double adj4;
48114402Sruextern int res;
49114402Sru
50114402Sruvoid HGSetFont(int font, int size);
51114402Sruvoid HGPutText(int justify, POINT pnt, register char *string);
52114402Sruvoid HGSetBrush(int mode);
53114402Sruvoid tmove2(int px, int py);
54114402Sruvoid doarc(POINT cp, POINT sp, int angle);
55114402Sruvoid tmove(POINT * ptr);
56114402Sruvoid cr();
57114402Sruvoid drawwig(POINT * ptr, int type);
58114402Sruvoid HGtline(int x1, int y1);
59151497Sruvoid deltax(double x);
60151497Sruvoid deltay(double y);
61114402Sruvoid HGArc(register int cx, register int cy, int px, int py, int angle);
62114402Sruvoid picurve(register int *x, register int *y, int npts);
63114402Sruvoid HGCurve(int *x, int *y, int numpoints);
64151497Sruvoid Paramaterize(int x[], int y[], double h[], int n);
65151497Sruvoid PeriodicSpline(double h[], int z[],
66151497Sru		    double dz[], double d2z[], double d3z[],
67114402Sru		    int npoints);
68151497Sruvoid NaturalEndSpline(double h[], int z[],
69151497Sru		      double dz[], double d2z[], double d3z[],
70114402Sru		      int npoints);
71114402Sru
72114402Sru
73114402Sru
74114402Sru/*----------------------------------------------------------------------------*
75114402Sru | Routine:	HGPrintElt (element_pointer, baseline)
76114402Sru |
77114402Sru | Results:	Examines a picture element and calls the appropriate
78114402Sru |		routine(s) to print them according to their type.  After the
79114402Sru |		picture is drawn, current position is (lastx, lasty).
80114402Sru *----------------------------------------------------------------------------*/
81114402Sru
82114402Sruvoid
83114402SruHGPrintElt(ELT *element,
84114402Sru	   int /* baseline */)
85114402Sru{
86114402Sru  register POINT *p1;
87114402Sru  register POINT *p2;
88114402Sru  register int length;
89114402Sru  register int graylevel;
90114402Sru
91114402Sru  if (!DBNullelt(element) && !Nullpoint((p1 = element->ptlist))) {
92114402Sru    /* p1 always has first point */
93114402Sru    if (TEXT(element->type)) {
94114402Sru      HGSetFont(element->brushf, element->size);
95114402Sru      switch (element->size) {
96114402Sru      case 1:
97114402Sru	p1->y += adj1;
98114402Sru	break;
99114402Sru      case 2:
100114402Sru	p1->y += adj2;
101114402Sru	break;
102114402Sru      case 3:
103114402Sru	p1->y += adj3;
104114402Sru	break;
105114402Sru      case 4:
106114402Sru	p1->y += adj4;
107114402Sru	break;
108114402Sru      default:
109114402Sru	break;
110114402Sru      }
111114402Sru      HGPutText(element->type, *p1, element->textpt);
112114402Sru    } else {
113114402Sru      if (element->brushf)		/* if there is a brush, the */
114114402Sru	HGSetBrush(element->brushf);	/* graphics need it set     */
115114402Sru
116114402Sru      switch (element->type) {
117114402Sru
118114402Sru      case ARC:
119114402Sru	p2 = PTNextPoint(p1);
120114402Sru	tmove(p2);
121114402Sru	doarc(*p1, *p2, element->size);
122114402Sru	cr();
123114402Sru	break;
124114402Sru
125114402Sru      case CURVE:
126114402Sru	length = 0;	/* keep track of line length */
127114402Sru	drawwig(p1, CURVE);
128114402Sru	cr();
129114402Sru	break;
130114402Sru
131114402Sru      case BSPLINE:
132114402Sru	length = 0;	/* keep track of line length */
133114402Sru	drawwig(p1, BSPLINE);
134114402Sru	cr();
135114402Sru	break;
136114402Sru
137114402Sru      case VECTOR:
138114402Sru	length = 0;		/* keep track of line length so */
139114402Sru	tmove(p1);		/* single lines don't get long  */
140114402Sru	while (!Nullpoint((p1 = PTNextPoint(p1)))) {
141114402Sru	  HGtline((int) (p1->x * troffscale),
142114402Sru		  (int) (p1->y * troffscale));
143114402Sru	  if (length++ > LINELENGTH) {
144114402Sru	    length = 0;
145114402Sru	    printf("\\\n");
146114402Sru	  }
147114402Sru	}			/* end while */
148114402Sru	cr();
149114402Sru	break;
150114402Sru
151114402Sru      case POLYGON:
152114402Sru	{
153114402Sru	  /* brushf = style of outline; size = color of fill:
154114402Sru	   * on first pass (polyfill=FILL), do the interior using 'P'
155114402Sru	   *    unless size=0
156114402Sru	   * on second pass (polyfill=OUTLINE), do the outline using a series
157114402Sru	   *    of vectors. It might make more sense to use \D'p ...',
158114402Sru	   *    but there is no uniform way to specify a 'fill character'
159114402Sru	   *    that prints as 'no fill' on all output devices (and
160114402Sru	   *    stipple fonts).
161114402Sru	   * If polyfill=BOTH, just use the \D'p ...' command.
162114402Sru	   */
163151497Sru	  double firstx = p1->x;
164151497Sru	  double firsty = p1->y;
165114402Sru
166114402Sru	  length = 0;		/* keep track of line length so */
167114402Sru				/* single lines don't get long  */
168114402Sru
169114402Sru	  if (polyfill == FILL || polyfill == BOTH) {
170114402Sru	    /* do the interior */
171114402Sru	    char command = (polyfill == BOTH && element->brushf) ? 'p' : 'P';
172114402Sru
173114402Sru	    /* include outline, if there is one and */
174114402Sru	    /* the -p flag was set                  */
175114402Sru
176114402Sru	    /* switch based on what gremlin gives */
177114402Sru	    switch (element->size) {
178114402Sru	    case 1:
179114402Sru	      graylevel = 1;
180114402Sru	      break;
181114402Sru	    case 3:
182114402Sru	      graylevel = 2;
183114402Sru	      break;
184114402Sru	    case 12:
185114402Sru	      graylevel = 3;
186114402Sru	      break;
187114402Sru	    case 14:
188114402Sru	      graylevel = 4;
189114402Sru	      break;
190114402Sru	    case 16:
191114402Sru	      graylevel = 5;
192114402Sru	      break;
193114402Sru	    case 19:
194114402Sru	      graylevel = 6;
195114402Sru	      break;
196114402Sru	    case 21:
197114402Sru	      graylevel = 7;
198114402Sru	      break;
199114402Sru	    case 23:
200114402Sru	      graylevel = 8;
201114402Sru	      break;
202114402Sru	    default:		/* who's giving something else? */
203114402Sru	      graylevel = NSTIPPLES;
204114402Sru	      break;
205114402Sru	    }
206114402Sru	    /* int graylevel = element->size; */
207114402Sru
208114402Sru	    if (graylevel < 0)
209114402Sru	      break;
210114402Sru	    if (graylevel > NSTIPPLES)
211114402Sru	      graylevel = NSTIPPLES;
212114402Sru	    printf("\\D'Fg %.3f'",
213114402Sru		   double(1000 - stipple_index[graylevel]) / 1000.0);
214114402Sru	    cr();
215114402Sru	    tmove(p1);
216114402Sru	    printf("\\D'%c", command);
217114402Sru
218114402Sru	    while (!Nullpoint((PTNextPoint(p1)))) {
219114402Sru	      p1 = PTNextPoint(p1);
220151497Sru	      deltax((double) p1->x);
221151497Sru	      deltay((double) p1->y);
222114402Sru	      if (length++ > LINELENGTH) {
223114402Sru		length = 0;
224114402Sru		printf("\\\n");
225114402Sru	      }
226114402Sru	    } /* end while */
227114402Sru
228114402Sru	    /* close polygon if not done so by user */
229114402Sru	    if ((firstx != p1->x) || (firsty != p1->y)) {
230151497Sru	      deltax((double) firstx);
231151497Sru	      deltay((double) firsty);
232114402Sru	    }
233114402Sru	    putchar('\'');
234114402Sru	    cr();
235114402Sru	    break;
236114402Sru	  }
237114402Sru	  /* else polyfill == OUTLINE; only draw the outline */
238114402Sru	  if (!(element->brushf))
239114402Sru	    break;
240114402Sru	  length = 0;		/* keep track of line length */
241114402Sru	  tmove(p1);
242114402Sru
243114402Sru	  while (!Nullpoint((PTNextPoint(p1)))) {
244114402Sru	    p1 = PTNextPoint(p1);
245114402Sru	    HGtline((int) (p1->x * troffscale),
246114402Sru		    (int) (p1->y * troffscale));
247114402Sru	    if (length++ > LINELENGTH) {
248114402Sru	      length = 0;
249114402Sru	      printf("\\\n");
250114402Sru	    }
251114402Sru	  }			/* end while */
252114402Sru
253114402Sru	  /* close polygon if not done so by user */
254114402Sru	  if ((firstx != p1->x) || (firsty != p1->y)) {
255114402Sru	    HGtline((int) (firstx * troffscale),
256114402Sru		    (int) (firsty * troffscale));
257114402Sru	  }
258114402Sru	  cr();
259114402Sru	  break;
260114402Sru	}			/* end case POLYGON */
261114402Sru      }				/* end switch */
262114402Sru    }				/* end else Text */
263114402Sru  }				/* end if */
264114402Sru}				/* end PrintElt */
265114402Sru
266114402Sru
267114402Sru/*----------------------------------------------------------------------------*
268114402Sru | Routine:	HGPutText (justification, position_point, string)
269114402Sru |
270114402Sru | Results:	Given the justification, a point to position with, and a
271114402Sru |		string to put, HGPutText first sends the string into a
272114402Sru |		diversion, moves to the positioning point, then outputs
273114402Sru |		local vertical and horizontal motions as needed to justify
274114402Sru |		the text.  After all motions are done, the diversion is
275114402Sru |		printed out.
276114402Sru *----------------------------------------------------------------------------*/
277114402Sru
278114402Sruvoid
279114402SruHGPutText(int justify,
280114402Sru	  POINT pnt,
281114402Sru	  register char *string)
282114402Sru{
283114402Sru  int savelasty = lasty;	/* vertical motion for text is to be */
284114402Sru				/* ignored.  Save current y here     */
285114402Sru
286114402Sru  printf(".nr g8 \\n(.d\n");	/* save current vertical position. */
287114402Sru  printf(".ds g9 \"");		/* define string containing the text. */
288114402Sru  while (*string) {		/* put out the string */
289114402Sru    if (*string == '\\' &&
290114402Sru	*(string + 1) == '\\') {	/* one character at a */
291114402Sru      printf("\\\\\\");			/* time replacing //  */
292114402Sru      string++;				/* by //// to prevent */
293114402Sru    }					/* interpretation at  */
294114402Sru    printf("%c", *(string++));		/* printout time      */
295114402Sru  }
296114402Sru  printf("\n");
297114402Sru
298114402Sru  tmove(&pnt);			/* move to positioning point */
299114402Sru
300114402Sru  switch (justify) {
301114402Sru    /* local vertical motions                                            */
302114402Sru    /* (the numbers here are used to be somewhat compatible with gprint) */
303114402Sru  case CENTLEFT:
304114402Sru  case CENTCENT:
305114402Sru  case CENTRIGHT:
306114402Sru    printf("\\v'0.85n'");	/* down half */
307114402Sru    break;
308114402Sru
309114402Sru  case TOPLEFT:
310114402Sru  case TOPCENT:
311114402Sru  case TOPRIGHT:
312114402Sru    printf("\\v'1.7n'");	/* down whole */
313114402Sru  }
314114402Sru
315114402Sru  switch (justify) {
316114402Sru    /* local horizontal motions */
317114402Sru  case BOTCENT:
318114402Sru  case CENTCENT:
319114402Sru  case TOPCENT:
320114402Sru    printf("\\h'-\\w'\\*(g9'u/2u'");	/* back half */
321114402Sru    break;
322114402Sru
323114402Sru  case BOTRIGHT:
324114402Sru  case CENTRIGHT:
325114402Sru  case TOPRIGHT:
326114402Sru    printf("\\h'-\\w'\\*(g9'u'");	/* back whole */
327114402Sru  }
328114402Sru
329114402Sru  printf("\\&\\*(g9\n");	/* now print the text. */
330114402Sru  printf(".sp |\\n(g8u\n");	/* restore vertical position */
331114402Sru  lasty = savelasty;		/* vertical position restored to where it */
332114402Sru  lastx = xleft;		/* was before text, also horizontal is at */
333114402Sru				/* left                                   */
334114402Sru}				/* end HGPutText */
335114402Sru
336114402Sru
337114402Sru/*----------------------------------------------------------------------------*
338114402Sru | Routine:	doarc (center_point, start_point, angle)
339114402Sru |
340114402Sru | Results:	Produces either drawarc command or a drawcircle command
341114402Sru |		depending on the angle needed to draw through.
342114402Sru *----------------------------------------------------------------------------*/
343114402Sru
344114402Sruvoid
345114402Srudoarc(POINT cp,
346114402Sru      POINT sp,
347114402Sru      int angle)
348114402Sru{
349114402Sru  if (angle)			/* arc with angle */
350114402Sru    HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale),
351114402Sru	  (int) (sp.x * troffscale), (int) (sp.y * troffscale), angle);
352114402Sru  else				/* a full circle (angle == 0) */
353114402Sru    HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale),
354114402Sru	  (int) (sp.x * troffscale), (int) (sp.y * troffscale), 0);
355114402Sru}
356114402Sru
357114402Sru
358114402Sru/*----------------------------------------------------------------------------*
359114402Sru | Routine:	HGSetFont (font_number, Point_size)
360114402Sru |
361114402Sru | Results:	ALWAYS outputs a .ft and .ps directive to troff.  This is
362114402Sru |		done because someone may change stuff inside a text string.
363114402Sru |		Changes thickness back to default thickness.  Default
364114402Sru |		thickness depends on font and pointsize.
365114402Sru *----------------------------------------------------------------------------*/
366114402Sru
367114402Sruvoid
368114402SruHGSetFont(int font,
369114402Sru	  int size)
370114402Sru{
371114402Sru  printf(".ft %s\n"
372114402Sru	 ".ps %d\n", tfont[font - 1], tsize[size - 1]);
373114402Sru  linethickness = DEFTHICK;
374114402Sru}
375114402Sru
376114402Sru
377114402Sru/*----------------------------------------------------------------------------*
378114402Sru | Routine:	HGSetBrush (line_mode)
379114402Sru |
380114402Sru | Results:	Generates the troff commands to set up the line width and
381114402Sru |		style of subsequent lines.  Does nothing if no change is
382114402Sru |              needed.
383114402Sru |
384114402Sru | Side Efct:	Sets `linmode' and `linethicknes'.
385114402Sru *----------------------------------------------------------------------------*/
386114402Sru
387114402Sruvoid
388114402SruHGSetBrush(int mode)
389114402Sru{
390114402Sru  register int printed = 0;
391114402Sru
392114402Sru  if (linmod != style[--mode]) {
393114402Sru    /* Groff doesn't understand \Ds, so we take it out */
394114402Sru    /* printf ("\\D's %du'", linmod = style[mode]); */
395114402Sru    linmod = style[mode];
396114402Sru    printed = 1;
397114402Sru  }
398114402Sru  if (linethickness != thick[mode]) {
399114402Sru    linethickness = thick[mode];
400114402Sru    printf("\\h'-%.2fp'\\D't %.2fp'", linethickness, linethickness);
401114402Sru    printed = 1;
402114402Sru  }
403114402Sru  if (printed)
404114402Sru    cr();
405114402Sru}
406114402Sru
407114402Sru
408114402Sru/*----------------------------------------------------------------------------*
409151497Sru | Routine:	deltax (x_destination)
410114402Sru |
411114402Sru | Results:	Scales and outputs a number for delta x (with a leading
412114402Sru |		space) given `lastx' and x_destination.
413114402Sru |
414114402Sru | Side Efct:	Resets `lastx' to x_destination.
415114402Sru *----------------------------------------------------------------------------*/
416114402Sru
417114402Sruvoid
418151497Srudeltax(double x)
419114402Sru{
420114402Sru  register int ix = (int) (x * troffscale);
421114402Sru
422114402Sru  printf(" %du", ix - lastx);
423114402Sru  lastx = ix;
424114402Sru}
425114402Sru
426114402Sru
427114402Sru/*----------------------------------------------------------------------------*
428151497Sru | Routine:	deltay (y_destination)
429114402Sru |
430114402Sru | Results:	Scales and outputs a number for delta y (with a leading
431114402Sru |		space) given `lastyline' and y_destination.
432114402Sru |
433114402Sru | Side Efct:	Resets `lastyline' to y_destination.  Since `line' vertical
434114402Sru |		motions don't affect `page' ones, `lasty' isn't updated.
435114402Sru *----------------------------------------------------------------------------*/
436114402Sru
437114402Sruvoid
438151497Srudeltay(double y)
439114402Sru{
440114402Sru  register int iy = (int) (y * troffscale);
441114402Sru
442114402Sru  printf(" %du", iy - lastyline);
443114402Sru  lastyline = iy;
444114402Sru}
445114402Sru
446114402Sru
447114402Sru/*----------------------------------------------------------------------------*
448114402Sru | Routine:	tmove2 (px, py)
449114402Sru |
450114402Sru | Results:	Produces horizontal and vertical moves for troff given the
451114402Sru |		pair of points to move to and knowing the current position.
452114402Sru |		Also puts out a horizontal move to start the line.  This is
453114402Sru |		a variation without the .sp command.
454114402Sru *----------------------------------------------------------------------------*/
455114402Sru
456114402Sruvoid
457114402Srutmove2(int px,
458114402Sru       int py)
459114402Sru{
460114402Sru  register int dx;
461114402Sru  register int dy;
462114402Sru
463114402Sru  if ((dy = py - lasty)) {
464114402Sru    printf("\\v'%du'", dy);
465114402Sru  }
466114402Sru  lastyline = lasty = py;	/* lasty is always set to current */
467114402Sru  if ((dx = px - lastx)) {
468114402Sru    printf("\\h'%du'", dx);
469114402Sru    lastx = px;
470114402Sru  }
471114402Sru}
472114402Sru
473114402Sru
474114402Sru/*----------------------------------------------------------------------------*
475114402Sru | Routine:	tmove (point_pointer)
476114402Sru |
477114402Sru | Results:	Produces horizontal and vertical moves for troff given the
478114402Sru |		pointer of a point to move to and knowing the current
479114402Sru |		position.  Also puts out a horizontal move to start the
480114402Sru |		line.
481114402Sru *----------------------------------------------------------------------------*/
482114402Sru
483114402Sruvoid
484114402Srutmove(POINT * ptr)
485114402Sru{
486114402Sru  register int ix = (int) (ptr->x * troffscale);
487114402Sru  register int iy = (int) (ptr->y * troffscale);
488114402Sru  register int dx;
489114402Sru  register int dy;
490114402Sru
491114402Sru  if ((dy = iy - lasty)) {
492114402Sru    printf(".sp %du\n", dy);
493114402Sru  }
494114402Sru  lastyline = lasty = iy;	/* lasty is always set to current */
495114402Sru  if ((dx = ix - lastx)) {
496114402Sru    printf("\\h'%du'", dx);
497114402Sru    lastx = ix;
498114402Sru  }
499114402Sru}
500114402Sru
501114402Sru
502114402Sru/*----------------------------------------------------------------------------*
503114402Sru | Routine:	cr ( )
504114402Sru |
505114402Sru | Results:	Ends off an input line.  `.sp -1' is also added to counteract
506114402Sru |		the vertical move done at the end of text lines.
507114402Sru |
508114402Sru | Side Efct:	Sets `lastx' to `xleft' for troff's return to left margin.
509114402Sru *----------------------------------------------------------------------------*/
510114402Sru
511114402Sruvoid
512114402Srucr()
513114402Sru{
514114402Sru  printf("\n.sp -1\n");
515114402Sru  lastx = xleft;
516114402Sru}
517114402Sru
518114402Sru
519114402Sru/*----------------------------------------------------------------------------*
520114402Sru | Routine:	line ( )
521114402Sru |
522114402Sru | Results:	Draws a single solid line to (x,y).
523114402Sru *----------------------------------------------------------------------------*/
524114402Sru
525114402Sruvoid
526114402Sruline(int px,
527114402Sru     int py)
528114402Sru{
529114402Sru  printf("\\D'l");
530114402Sru  printf(" %du", px - lastx);
531114402Sru  printf(" %du'", py - lastyline);
532114402Sru  lastx = px;
533114402Sru  lastyline = lasty = py;
534114402Sru}
535114402Sru
536114402Sru
537114402Sru/*----------------------------------------------------------------------------
538114402Sru | Routine:	drawwig (ptr, type)
539114402Sru |
540114402Sru | Results:	The point sequence found in the structure pointed by ptr is
541114402Sru |		placed in integer arrays for further manipulation by the
542114402Sru |		existing routing.  With the corresponding type parameter,
543114402Sru |		either picurve or HGCurve are called.
544114402Sru *----------------------------------------------------------------------------*/
545114402Sru
546114402Sruvoid
547114402Srudrawwig(POINT * ptr,
548114402Sru	int type)
549114402Sru{
550114402Sru  register int npts;			/* point list index */
551114402Sru  int x[MAXPOINTS], y[MAXPOINTS];	/* point list */
552114402Sru
553114402Sru  for (npts = 1; !Nullpoint(ptr); ptr = PTNextPoint(ptr), npts++) {
554114402Sru    x[npts] = (int) (ptr->x * troffscale);
555114402Sru    y[npts] = (int) (ptr->y * troffscale);
556114402Sru  }
557114402Sru  if (--npts) {
558114402Sru    if (type == CURVE) /* Use the 2 different types of curves */
559114402Sru      HGCurve(&x[0], &y[0], npts);
560114402Sru    else
561114402Sru      picurve(&x[0], &y[0], npts);
562114402Sru  }
563114402Sru}
564114402Sru
565114402Sru
566114402Sru/*----------------------------------------------------------------------------
567114402Sru | Routine:	HGArc (xcenter, ycenter, xstart, ystart, angle)
568114402Sru |
569114402Sru | Results:	This routine plots an arc centered about (cx, cy) counter
570114402Sru |		clockwise starting from the point (px, py) through `angle'
571114402Sru |		degrees.  If angle is 0, a full circle is drawn.  It does so
572114402Sru |		by creating a draw-path around the arc whose density of
573114402Sru |		points depends on the size of the arc.
574114402Sru *----------------------------------------------------------------------------*/
575114402Sru
576114402Sruvoid
577114402SruHGArc(register int cx,
578114402Sru      register int cy,
579114402Sru      int px,
580114402Sru      int py,
581114402Sru      int angle)
582114402Sru{
583114402Sru  double xs, ys, resolution, fullcircle;
584114402Sru  int m;
585114402Sru  register int mask;
586114402Sru  register int extent;
587114402Sru  register int nx;
588114402Sru  register int ny;
589114402Sru  register int length;
590114402Sru  register double epsilon;
591114402Sru
592114402Sru  xs = px - cx;
593114402Sru  ys = py - cy;
594114402Sru
595114402Sru  length = 0;
596114402Sru
597151497Sru  resolution = (1.0 + groff_hypot(xs, ys) / res) * PointsPerInterval;
598114402Sru  /* mask = (1 << (int) log10(resolution + 1.0)) - 1; */
599114402Sru  (void) frexp(resolution, &m);		/* A bit more elegant than log10 */
600114402Sru  for (mask = 1; mask < m; mask = mask << 1);
601114402Sru  mask -= 1;
602114402Sru
603114402Sru  epsilon = 1.0 / resolution;
604114402Sru  fullcircle = (2.0 * pi) * resolution;
605114402Sru  if (angle == 0)
606114402Sru    extent = (int) fullcircle;
607114402Sru  else
608114402Sru    extent = (int) (angle * fullcircle / 360.0);
609114402Sru
610114402Sru  HGtline(px, py);
611114402Sru  while (--extent >= 0) {
612114402Sru    xs += epsilon * ys;
613114402Sru    nx = cx + (int) (xs + 0.5);
614114402Sru    ys -= epsilon * xs;
615114402Sru    ny = cy + (int) (ys + 0.5);
616114402Sru    if (!(extent & mask)) {
617114402Sru      HGtline(nx, ny);		/* put out a point on circle */
618114402Sru      if (length++ > LINELENGTH) {
619114402Sru	length = 0;
620114402Sru	printf("\\\n");
621114402Sru      }
622114402Sru    }
623114402Sru  }				/* end for */
624114402Sru}				/* end HGArc */
625114402Sru
626114402Sru
627114402Sru/*----------------------------------------------------------------------------
628114402Sru | Routine:	picurve (xpoints, ypoints, num_of_points)
629114402Sru |
630114402Sru | Results:	Draws a curve delimited by (not through) the line segments
631114402Sru |		traced by (xpoints, ypoints) point list.  This is the `Pic'
632114402Sru |		style curve.
633114402Sru *----------------------------------------------------------------------------*/
634114402Sru
635114402Sruvoid
636114402Srupicurve(register int *x,
637114402Sru	register int *y,
638114402Sru	int npts)
639114402Sru{
640114402Sru  register int nseg;		/* effective resolution for each curve */
641114402Sru  register int xp;		/* current point (and temporary) */
642114402Sru  register int yp;
643114402Sru  int pxp, pyp;			/* previous point (to make lines from) */
644114402Sru  int i;			/* inner curve segment traverser */
645114402Sru  int length = 0;
646114402Sru  double w;			/* position factor */
647114402Sru  double t1, t2, t3;		/* calculation temps */
648114402Sru
649114402Sru  if (x[1] == x[npts] && y[1] == y[npts]) {
650114402Sru    x[0] = x[npts - 1];		/* if the lines' ends meet, make */
651114402Sru    y[0] = y[npts - 1];		/* sure the curve meets          */
652114402Sru    x[npts + 1] = x[2];
653114402Sru    y[npts + 1] = y[2];
654114402Sru  } else {			/* otherwise, make the ends of the  */
655114402Sru    x[0] = x[1];		/* curve touch the ending points of */
656114402Sru    y[0] = y[1];		/* the line segments                */
657114402Sru    x[npts + 1] = x[npts];
658114402Sru    y[npts + 1] = y[npts];
659114402Sru  }
660114402Sru
661114402Sru  pxp = (x[0] + x[1]) / 2;	/* make the last point pointers       */
662114402Sru  pyp = (y[0] + y[1]) / 2;	/* point to the start of the 1st line */
663114402Sru  tmove2(pxp, pyp);
664114402Sru
665114402Sru  for (; npts--; x++, y++) {	/* traverse the line segments */
666114402Sru    xp = x[0] - x[1];
667114402Sru    yp = y[0] - y[1];
668151497Sru    nseg = (int) groff_hypot((double) xp, (double) yp);
669114402Sru    xp = x[1] - x[2];
670114402Sru    yp = y[1] - y[2];
671114402Sru				/* `nseg' is the number of line    */
672114402Sru				/* segments that will be drawn for */
673114402Sru				/* each curve segment.             */
674151497Sru    nseg = (int) ((double) (nseg + (int) groff_hypot((double) xp, (double) yp)) /
675114402Sru		  res * PointsPerInterval);
676114402Sru
677114402Sru    for (i = 1; i < nseg; i++) {
678114402Sru      w = (double) i / (double) nseg;
679114402Sru      t1 = w * w;
680114402Sru      t3 = t1 + 1.0 - (w + w);
681114402Sru      t2 = 2.0 - (t3 + t1);
682114402Sru      xp = (((int) (t1 * x[2] + t2 * x[1] + t3 * x[0])) + 1) / 2;
683114402Sru      yp = (((int) (t1 * y[2] + t2 * y[1] + t3 * y[0])) + 1) / 2;
684114402Sru
685114402Sru      HGtline(xp, yp);
686114402Sru      if (length++ > LINELENGTH) {
687114402Sru	length = 0;
688114402Sru	printf("\\\n");
689114402Sru      }
690114402Sru    }
691114402Sru  }
692114402Sru}
693114402Sru
694114402Sru
695114402Sru/*----------------------------------------------------------------------------
696114402Sru | Routine:	HGCurve(xpoints, ypoints, num_points)
697114402Sru |
698114402Sru | Results:	This routine generates a smooth curve through a set of
699114402Sru |		points.  The method used is the parametric spline curve on
700114402Sru |		unit knot mesh described in `Spline Curve Techniques' by
701114402Sru |		Patrick Baudelaire, Robert Flegal, and Robert Sproull --
702114402Sru |		Xerox Parc.
703114402Sru *----------------------------------------------------------------------------*/
704114402Sru
705114402Sruvoid
706114402SruHGCurve(int *x,
707114402Sru	int *y,
708114402Sru	int numpoints)
709114402Sru{
710151497Sru  double h[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS];
711151497Sru  double d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS];
712151497Sru  double t, t2, t3;
713114402Sru  register int j;
714114402Sru  register int k;
715114402Sru  register int nx;
716114402Sru  register int ny;
717114402Sru  int lx, ly;
718114402Sru  int length = 0;
719114402Sru
720114402Sru  lx = x[1];
721114402Sru  ly = y[1];
722114402Sru  tmove2(lx, ly);
723114402Sru
724114402Sru  /*
725114402Sru   * Solve for derivatives of the curve at each point separately for x and y
726114402Sru   * (parametric).
727114402Sru   */
728114402Sru  Paramaterize(x, y, h, numpoints);
729114402Sru
730114402Sru  /* closed curve */
731114402Sru  if ((x[1] == x[numpoints]) && (y[1] == y[numpoints])) {
732114402Sru    PeriodicSpline(h, x, dx, d2x, d3x, numpoints);
733114402Sru    PeriodicSpline(h, y, dy, d2y, d3y, numpoints);
734114402Sru  } else {
735114402Sru    NaturalEndSpline(h, x, dx, d2x, d3x, numpoints);
736114402Sru    NaturalEndSpline(h, y, dy, d2y, d3y, numpoints);
737114402Sru  }
738114402Sru
739114402Sru  /*
740114402Sru   * generate the curve using the above information and PointsPerInterval
741114402Sru   * vectors between each specified knot.
742114402Sru   */
743114402Sru
744114402Sru  for (j = 1; j < numpoints; ++j) {
745114402Sru    if ((x[j] == x[j + 1]) && (y[j] == y[j + 1]))
746114402Sru      continue;
747114402Sru    for (k = 0; k <= PointsPerInterval; ++k) {
748151497Sru      t = (double) k *h[j] / (double) PointsPerInterval;
749114402Sru      t2 = t * t;
750114402Sru      t3 = t * t * t;
751114402Sru      nx = x[j] + (int) (t * dx[j] + t2 * d2x[j] / 2 + t3 * d3x[j] / 6);
752114402Sru      ny = y[j] + (int) (t * dy[j] + t2 * d2y[j] / 2 + t3 * d3y[j] / 6);
753114402Sru      HGtline(nx, ny);
754114402Sru      if (length++ > LINELENGTH) {
755114402Sru	length = 0;
756114402Sru	printf("\\\n");
757114402Sru      }
758114402Sru    }				/* end for k */
759114402Sru  }				/* end for j */
760114402Sru}				/* end HGCurve */
761114402Sru
762114402Sru
763114402Sru/*----------------------------------------------------------------------------
764114402Sru | Routine:	Paramaterize (xpoints, ypoints, hparams, num_points)
765114402Sru |
766114402Sru | Results:	This routine calculates parameteric values for use in
767114402Sru |		calculating curves.  The parametric values are returned
768114402Sru |		in the array h.  The values are an approximation of
769114402Sru |		cumulative arc lengths of the curve (uses cord length).
770114402Sru |		For additional information, see paper cited below.
771114402Sru *----------------------------------------------------------------------------*/
772114402Sru
773114402Sruvoid
774114402SruParamaterize(int x[],
775114402Sru	     int y[],
776151497Sru	     double h[],
777114402Sru	     int n)
778114402Sru{
779114402Sru  register int dx;
780114402Sru  register int dy;
781114402Sru  register int i;
782114402Sru  register int j;
783151497Sru  double u[MAXPOINTS];
784114402Sru
785114402Sru  for (i = 1; i <= n; ++i) {
786114402Sru    u[i] = 0;
787114402Sru    for (j = 1; j < i; j++) {
788114402Sru      dx = x[j + 1] - x[j];
789114402Sru      dy = y[j + 1] - y[j];
790114402Sru      /* Here was overflowing, so I changed it.       */
791114402Sru      /* u[i] += sqrt ((double) (dx * dx + dy * dy)); */
792151497Sru      u[i] += groff_hypot((double) dx, (double) dy);
793114402Sru    }
794114402Sru  }
795114402Sru  for (i = 1; i < n; ++i)
796114402Sru    h[i] = u[i + 1] - u[i];
797114402Sru}				/* end Paramaterize */
798114402Sru
799114402Sru
800114402Sru/*----------------------------------------------------------------------------
801114402Sru | Routine:	PeriodicSpline (h, z, dz, d2z, d3z, npoints)
802114402Sru |
803114402Sru | Results:	This routine solves for the cubic polynomial to fit a spline
804114402Sru |		curve to the the points specified by the list of values.
805114402Sru |		The Curve generated is periodic.  The algorithms for this
806114402Sru |		curve are from the `Spline Curve Techniques' paper cited
807114402Sru |		above.
808114402Sru *----------------------------------------------------------------------------*/
809114402Sru
810114402Sruvoid
811151497SruPeriodicSpline(double h[],	/* paramaterization  */
812114402Sru	       int z[],		/* point list */
813151497Sru	       double dz[],	/* to return the 1st derivative */
814151497Sru	       double d2z[],	/* 2nd derivative */
815151497Sru	       double d3z[],	/* 3rd derivative */
816114402Sru	       int npoints)	/* number of valid points */
817114402Sru{
818151497Sru  double d[MAXPOINTS];
819151497Sru  double deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
820151497Sru  double c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS];
821114402Sru  int i;
822114402Sru
823114402Sru  /* step 1 */
824114402Sru  for (i = 1; i < npoints; ++i) {
825114402Sru    deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0;
826114402Sru  }
827114402Sru  h[0] = h[npoints - 1];
828114402Sru  deltaz[0] = deltaz[npoints - 1];
829114402Sru
830114402Sru  /* step 2 */
831114402Sru  for (i = 1; i < npoints - 1; ++i) {
832114402Sru    d[i] = deltaz[i + 1] - deltaz[i];
833114402Sru  }
834114402Sru  d[0] = deltaz[1] - deltaz[0];
835114402Sru
836114402Sru  /* step 3a */
837114402Sru  a[1] = 2 * (h[0] + h[1]);
838114402Sru  b[1] = d[0];
839114402Sru  c[1] = h[0];
840114402Sru  for (i = 2; i < npoints - 1; ++i) {
841114402Sru    a[i] = 2 * (h[i - 1] + h[i]) -
842114402Sru	   pow((double) h[i - 1], (double) 2.0) / a[i - 1];
843114402Sru    b[i] = d[i - 1] - h[i - 1] * b[i - 1] / a[i - 1];
844114402Sru    c[i] = -h[i - 1] * c[i - 1] / a[i - 1];
845114402Sru  }
846114402Sru
847114402Sru  /* step 3b */
848114402Sru  r[npoints - 1] = 1;
849114402Sru  s[npoints - 1] = 0;
850114402Sru  for (i = npoints - 2; i > 0; --i) {
851114402Sru    r[i] = -(h[i] * r[i + 1] + c[i]) / a[i];
852114402Sru    s[i] = (6 * b[i] - h[i] * s[i + 1]) / a[i];
853114402Sru  }
854114402Sru
855114402Sru  /* step 4 */
856114402Sru  d2z[npoints - 1] = (6 * d[npoints - 2] - h[0] * s[1]
857114402Sru		      - h[npoints - 1] * s[npoints - 2])
858114402Sru		     / (h[0] * r[1] + h[npoints - 1] * r[npoints - 2]
859114402Sru		      + 2 * (h[npoints - 2] + h[0]));
860114402Sru  for (i = 1; i < npoints - 1; ++i) {
861114402Sru    d2z[i] = r[i] * d2z[npoints - 1] + s[i];
862114402Sru  }
863114402Sru  d2z[npoints] = d2z[1];
864114402Sru
865114402Sru  /* step 5 */
866114402Sru  for (i = 1; i < npoints; ++i) {
867114402Sru    dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6;
868114402Sru    d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0;
869114402Sru  }
870114402Sru}				/* end PeriodicSpline */
871114402Sru
872114402Sru
873114402Sru/*----------------------------------------------------------------------------
874114402Sru | Routine:	NaturalEndSpline (h, z, dz, d2z, d3z, npoints)
875114402Sru |
876114402Sru | Results:	This routine solves for the cubic polynomial to fit a spline
877114402Sru |		curve the the points specified by the list of values.  The
878114402Sru |		alogrithms for this curve are from the `Spline Curve
879114402Sru |		Techniques' paper cited above.
880114402Sru *----------------------------------------------------------------------------*/
881114402Sru
882114402Sruvoid
883151497SruNaturalEndSpline(double h[],	/* parameterization */
884114402Sru		 int z[],	/* Point list */
885151497Sru		 double dz[],	/* to return the 1st derivative */
886151497Sru		 double d2z[],	/* 2nd derivative */
887151497Sru		 double d3z[],	/* 3rd derivative */
888114402Sru		 int npoints)	/* number of valid points */
889114402Sru{
890151497Sru  double d[MAXPOINTS];
891151497Sru  double deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
892114402Sru  int i;
893114402Sru
894114402Sru  /* step 1 */
895114402Sru  for (i = 1; i < npoints; ++i) {
896114402Sru    deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0;
897114402Sru  }
898114402Sru  deltaz[0] = deltaz[npoints - 1];
899114402Sru
900114402Sru  /* step 2 */
901114402Sru  for (i = 1; i < npoints - 1; ++i) {
902114402Sru    d[i] = deltaz[i + 1] - deltaz[i];
903114402Sru  }
904114402Sru  d[0] = deltaz[1] - deltaz[0];
905114402Sru
906114402Sru  /* step 3 */
907114402Sru  a[0] = 2 * (h[2] + h[1]);
908114402Sru  b[0] = d[1];
909114402Sru  for (i = 1; i < npoints - 2; ++i) {
910114402Sru    a[i] = 2 * (h[i + 1] + h[i + 2]) -
911114402Sru	    pow((double) h[i + 1], (double) 2.0) / a[i - 1];
912114402Sru    b[i] = d[i + 1] - h[i + 1] * b[i - 1] / a[i - 1];
913114402Sru  }
914114402Sru
915114402Sru  /* step 4 */
916114402Sru  d2z[npoints] = d2z[1] = 0;
917114402Sru  for (i = npoints - 1; i > 1; --i) {
918114402Sru    d2z[i] = (6 * b[i - 2] - h[i] * d2z[i + 1]) / a[i - 2];
919114402Sru  }
920114402Sru
921114402Sru  /* step 5 */
922114402Sru  for (i = 1; i < npoints; ++i) {
923114402Sru    dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6;
924114402Sru    d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0;
925114402Sru  }
926114402Sru}				/* end NaturalEndSpline */
927114402Sru
928114402Sru
929114402Sru/*----------------------------------------------------------------------------*
930114402Sru | Routine:	change (x_position, y_position, visible_flag)
931114402Sru |
932114402Sru | Results:	As HGtline passes from the invisible to visible (or vice
933114402Sru |		versa) portion of a line, change is called to either draw
934114402Sru |		the line, or initialize the beginning of the next one.
935114402Sru |		Change calls line to draw segments if visible_flag is set
936114402Sru |		(which means we're leaving a visible area).
937114402Sru *----------------------------------------------------------------------------*/
938114402Sru
939114402Sruvoid
940114402Sruchange(register int x,
941114402Sru       register int y,
942114402Sru       register int vis)
943114402Sru{
944114402Sru  static int length = 0;
945114402Sru
946114402Sru  if (vis) {			/* leaving a visible area, draw it. */
947114402Sru    line(x, y);
948114402Sru    if (length++ > LINELENGTH) {
949114402Sru      length = 0;
950114402Sru      printf("\\\n");
951114402Sru    }
952114402Sru  } else {			/* otherwise, we're entering one, remember */
953114402Sru				/* beginning                               */
954114402Sru    tmove2(x, y);
955114402Sru  }
956114402Sru}
957114402Sru
958114402Sru
959114402Sru/*----------------------------------------------------------------------------
960114402Sru | Routine:	HGtline (xstart, ystart, xend, yend)
961114402Sru |
962114402Sru | Results:	Draws a line from current position to (x1,y1) using line(x1,
963114402Sru |		y1) to place individual segments of dotted or dashed lines.
964114402Sru *----------------------------------------------------------------------------*/
965114402Sru
966114402Sruvoid
967151497SruHGtline(int x_1,
968151497Sru	int y_1)
969114402Sru{
970151497Sru  register int x_0 = lastx;
971151497Sru  register int y_0 = lasty;
972114402Sru  register int dx;
973114402Sru  register int dy;
974114402Sru  register int oldcoord;
975114402Sru  register int res1;
976114402Sru  register int visible;
977114402Sru  register int res2;
978114402Sru  register int xinc;
979114402Sru  register int yinc;
980114402Sru  register int dotcounter;
981114402Sru
982114402Sru  if (linmod == SOLID) {
983151497Sru    line(x_1, y_1);
984114402Sru    return;
985114402Sru  }
986114402Sru
987114402Sru  /* for handling different resolutions */
988114402Sru  dotcounter = linmod << dotshifter;
989114402Sru
990114402Sru  xinc = 1;
991114402Sru  yinc = 1;
992151497Sru  if ((dx = x_1 - x_0) < 0) {
993114402Sru    xinc = -xinc;
994114402Sru    dx = -dx;
995114402Sru  }
996151497Sru  if ((dy = y_1 - y_0) < 0) {
997114402Sru    yinc = -yinc;
998114402Sru    dy = -dy;
999114402Sru  }
1000114402Sru  res1 = 0;
1001114402Sru  res2 = 0;
1002114402Sru  visible = 0;
1003114402Sru  if (dx >= dy) {
1004151497Sru    oldcoord = y_0;
1005151497Sru    while (x_0 != x_1) {
1006151497Sru      if ((x_0 & dotcounter) && !visible) {
1007151497Sru	change(x_0, y_0, 0);
1008114402Sru	visible = 1;
1009151497Sru      } else if (visible && !(x_0 & dotcounter)) {
1010151497Sru	change(x_0 - xinc, oldcoord, 1);
1011114402Sru	visible = 0;
1012114402Sru      }
1013114402Sru      if (res1 > res2) {
1014151497Sru	oldcoord = y_0;
1015114402Sru	res2 += dx - res1;
1016114402Sru	res1 = 0;
1017151497Sru	y_0 += yinc;
1018114402Sru      }
1019114402Sru      res1 += dy;
1020151497Sru      x_0 += xinc;
1021114402Sru    }
1022114402Sru  } else {
1023151497Sru    oldcoord = x_0;
1024151497Sru    while (y_0 != y_1) {
1025151497Sru      if ((y_0 & dotcounter) && !visible) {
1026151497Sru	change(x_0, y_0, 0);
1027114402Sru	visible = 1;
1028151497Sru      } else if (visible && !(y_0 & dotcounter)) {
1029151497Sru	change(oldcoord, y_0 - yinc, 1);
1030114402Sru	visible = 0;
1031114402Sru      }
1032114402Sru      if (res1 > res2) {
1033151497Sru	oldcoord = x_0;
1034114402Sru	res2 += dy - res1;
1035114402Sru	res1 = 0;
1036151497Sru	x_0 += xinc;
1037114402Sru      }
1038114402Sru      res1 += dx;
1039151497Sru      y_0 += yinc;
1040114402Sru    }
1041114402Sru  }
1042114402Sru  if (visible)
1043151497Sru    change(x_1, y_1, 1);
1044114402Sru  else
1045151497Sru    change(x_1, y_1, 0);
1046114402Sru}
1047114402Sru
1048114402Sru/* EOF */
1049