Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / groff / src / preproc / grn / hgraph.cc
1 /* Last non-groff version: hgraph.c  1.14 (Berkeley) 84/11/27
2  *
3  * This file contains the graphics routines for converting gremlin pictures
4  * to troff input.
5  */
6
7 #include "lib.h"
8
9 #include "gprint.h"
10
11 #ifdef NEED_DECLARATION_HYPOT
12 extern "C" {
13   double hypot(double, double);
14 }
15 #endif /* NEED_DECLARATION_HYPOT */
16
17 #define MAXVECT 40
18 #define MAXPOINTS       200
19 #define LINELENGTH      1
20 #define PointsPerInterval 64
21 #define pi              3.14159265358979324
22 #define twopi           (2.0 * pi)
23 #define len(a, b)       hypot((double)(b.x-a.x), (double)(b.y-a.y))
24
25
26 extern int dotshifter;          /* for the length of dotted curves */
27
28 extern int style[];             /* line and character styles */
29 extern double thick[];
30 extern char *tfont[];
31 extern int tsize[];
32 extern int stipple_index[];     /* stipple font index for stipples 0 - 16 */
33 extern char *stipple;           /* stipple type (cf or ug) */
34
35
36 extern double troffscale;       /* imports from main.c */
37 extern double linethickness;
38 extern int linmod;
39 extern int lastx;
40 extern int lasty;
41 extern int lastyline;
42 extern int ytop;
43 extern int ybottom;
44 extern int xleft;
45 extern int xright;
46 extern enum {
47   OUTLINE, FILL, BOTH
48 } polyfill;
49
50 extern double adj1;
51 extern double adj2;
52 extern double adj3;
53 extern double adj4;
54 extern int res;
55
56 void HGSetFont(int font, int size);
57 void HGPutText(int justify, POINT pnt, register char *string);
58 void HGSetBrush(int mode);
59 void tmove2(int px, int py);
60 void doarc(POINT cp, POINT sp, int angle);
61 void tmove(POINT * ptr);
62 void cr();
63 void drawwig(POINT * ptr, int type);
64 void HGtline(int x1, int y1);
65 void dx(double x);
66 void dy(double y);
67 void HGArc(register int cx, register int cy, int px, int py, int angle);
68 void picurve(register int *x, register int *y, int npts);
69 void HGCurve(int *x, int *y, int numpoints);
70 void Paramaterize(int x[], int y[], float h[], int n);
71 void PeriodicSpline(float h[], int z[],
72                     float dz[], float d2z[], float d3z[],
73                     int npoints);
74 void NaturalEndSpline(float h[], int z[],
75                       float dz[], float d2z[], float d3z[],
76                       int npoints);
77
78
79
80 /*----------------------------------------------------------------------------*
81  | Routine:     HGPrintElt (element_pointer, baseline)
82  |
83  | Results:     Examines a picture element and calls the appropriate
84  |              routine(s) to print them according to their type.  After the
85  |              picture is drawn, current position is (lastx, lasty).
86  *----------------------------------------------------------------------------*/
87
88 void
89 HGPrintElt(ELT *element,
90            int baseline)
91 {
92   register POINT *p1;
93   register POINT *p2;
94   register int length;
95   register int graylevel;
96
97   if (!DBNullelt(element) && !Nullpoint((p1 = element->ptlist))) {
98     /* p1 always has first point */
99     if (TEXT(element->type)) {
100       HGSetFont(element->brushf, element->size);
101       switch (element->size) {
102       case 1:
103         p1->y += adj1;
104         break;
105       case 2:
106         p1->y += adj2;
107         break;
108       case 3:
109         p1->y += adj3;
110         break;
111       case 4:
112         p1->y += adj4;
113         break;
114       default:
115         break;
116       }
117       HGPutText(element->type, *p1, element->textpt);
118     } else {
119       if (element->brushf)              /* if there is a brush, the */
120         HGSetBrush(element->brushf);    /* graphics need it set     */
121
122       switch (element->type) {
123
124       case ARC:
125         p2 = PTNextPoint(p1);
126         tmove(p2);
127         doarc(*p1, *p2, element->size);
128         cr();
129         break;
130
131       case CURVE:
132         length = 0;     /* keep track of line length */
133         drawwig(p1, CURVE);
134         cr();
135         break;
136
137       case BSPLINE:
138         length = 0;     /* keep track of line length */
139         drawwig(p1, BSPLINE);
140         cr();
141         break;
142
143       case VECTOR:
144         length = 0;             /* keep track of line length so */
145         tmove(p1);              /* single lines don't get long  */
146         while (!Nullpoint((p1 = PTNextPoint(p1)))) {
147           HGtline((int) (p1->x * troffscale),
148                   (int) (p1->y * troffscale));
149           if (length++ > LINELENGTH) {
150             length = 0;
151             printf("\\\n");
152           }
153         }                       /* end while */
154         cr();
155         break;
156
157       case POLYGON:
158         {
159           /* brushf = style of outline; size = color of fill:
160            * on first pass (polyfill=FILL), do the interior using 'P'
161            *    unless size=0
162            * on second pass (polyfill=OUTLINE), do the outline using a series
163            *    of vectors. It might make more sense to use \D'p ...',
164            *    but there is no uniform way to specify a 'fill character'
165            *    that prints as 'no fill' on all output devices (and
166            *    stipple fonts).
167            * If polyfill=BOTH, just use the \D'p ...' command.
168            */
169           float firstx = p1->x;
170           float firsty = p1->y;
171
172           length = 0;           /* keep track of line length so */
173                                 /* single lines don't get long  */
174
175           if (polyfill == FILL || polyfill == BOTH) {
176             /* do the interior */
177             char command = (polyfill == BOTH && element->brushf) ? 'p' : 'P';
178
179             /* include outline, if there is one and */
180             /* the -p flag was set                  */
181
182             /* switch based on what gremlin gives */
183             switch (element->size) {
184             case 1:
185               graylevel = 1;
186               break;
187             case 3:
188               graylevel = 2;
189               break;
190             case 12:
191               graylevel = 3;
192               break;
193             case 14:
194               graylevel = 4;
195               break;
196             case 16:
197               graylevel = 5;
198               break;
199             case 19:
200               graylevel = 6;
201               break;
202             case 21:
203               graylevel = 7;
204               break;
205             case 23:
206               graylevel = 8;
207               break;
208             default:            /* who's giving something else? */
209               graylevel = NSTIPPLES;
210               break;
211             }
212             /* int graylevel = element->size; */
213
214             if (graylevel < 0)
215               break;
216             if (graylevel > NSTIPPLES)
217               graylevel = NSTIPPLES;
218             printf("\\h'-%du'\\D'f %du'",
219                    stipple_index[graylevel],
220                    stipple_index[graylevel]);
221             cr();
222             tmove(p1);
223             printf("\\D'%c", command);
224
225             while (!Nullpoint((PTNextPoint(p1)))) {
226               p1 = PTNextPoint(p1);
227               dx((double) p1->x);
228               dy((double) p1->y);
229               if (length++ > LINELENGTH) {
230                 length = 0;
231                 printf("\\\n");
232               }
233             } /* end while */
234
235             /* close polygon if not done so by user */
236             if ((firstx != p1->x) || (firsty != p1->y)) {
237               dx((double) firstx);
238               dy((double) firsty);
239             }
240             putchar('\'');
241             cr();
242             break;
243           }
244           /* else polyfill == OUTLINE; only draw the outline */
245           if (!(element->brushf))
246             break;
247           length = 0;           /* keep track of line length */
248           tmove(p1);
249
250           while (!Nullpoint((PTNextPoint(p1)))) {
251             p1 = PTNextPoint(p1);
252             HGtline((int) (p1->x * troffscale),
253                     (int) (p1->y * troffscale));
254             if (length++ > LINELENGTH) {
255               length = 0;
256               printf("\\\n");
257             }
258           }                     /* end while */
259
260           /* close polygon if not done so by user */
261           if ((firstx != p1->x) || (firsty != p1->y)) {
262             HGtline((int) (firstx * troffscale),
263                     (int) (firsty * troffscale));
264           }
265           cr();
266           break;
267         }                       /* end case POLYGON */
268       }                         /* end switch */
269     }                           /* end else Text */
270   }                             /* end if */
271 }                               /* end PrintElt */
272
273
274 /*----------------------------------------------------------------------------*
275  | Routine:     HGPutText (justification, position_point, string)
276  |
277  | Results:     Given the justification, a point to position with, and a
278  |              string to put, HGPutText first sends the string into a
279  |              diversion, moves to the positioning point, then outputs
280  |              local vertical and horizontal motions as needed to justify
281  |              the text.  After all motions are done, the diversion is
282  |              printed out.
283  *----------------------------------------------------------------------------*/
284
285 void
286 HGPutText(int justify,
287           POINT pnt,
288           register char *string)
289 {
290   int savelasty = lasty;        /* vertical motion for text is to be */
291                                 /* ignored.  Save current y here     */
292
293   printf(".nr g8 \\n(.d\n");    /* save current vertical position. */
294   printf(".ds g9 \"");          /* define string containing the text. */
295   while (*string) {             /* put out the string */
296     if (*string == '\\' &&
297         *(string + 1) == '\\') {        /* one character at a */
298       printf("\\\\\\");                 /* time replacing //  */
299       string++;                         /* by //// to prevent */
300     }                                   /* interpretation at  */
301     printf("%c", *(string++));          /* printout time      */
302   }
303   printf("\n");
304
305   tmove(&pnt);                  /* move to positioning point */
306
307   switch (justify) {
308     /* local vertical motions                                            */
309     /* (the numbers here are used to be somewhat compatible with gprint) */
310   case CENTLEFT:
311   case CENTCENT:
312   case CENTRIGHT:
313     printf("\\v'0.85n'");       /* down half */
314     break;
315
316   case TOPLEFT:
317   case TOPCENT:
318   case TOPRIGHT:
319     printf("\\v'1.7n'");        /* down whole */
320   }
321
322   switch (justify) {
323     /* local horizontal motions */
324   case BOTCENT:
325   case CENTCENT:
326   case TOPCENT:
327     printf("\\h'-\\w'\\*(g9'u/2u'");    /* back half */
328     break;
329
330   case BOTRIGHT:
331   case CENTRIGHT:
332   case TOPRIGHT:
333     printf("\\h'-\\w'\\*(g9'u'");       /* back whole */
334   }
335
336   printf("\\&\\*(g9\n");        /* now print the text. */
337   printf(".sp |\\n(g8u\n");     /* restore vertical position */
338   lasty = savelasty;            /* vertical position restored to where it */
339   lastx = xleft;                /* was before text, also horizontal is at */
340                                 /* left                                   */
341 }                               /* end HGPutText */
342
343
344 /*----------------------------------------------------------------------------*
345  | Routine:     doarc (center_point, start_point, angle)
346  |
347  | Results:     Produces either drawarc command or a drawcircle command
348  |              depending on the angle needed to draw through.
349  *----------------------------------------------------------------------------*/
350
351 void
352 doarc(POINT cp,
353       POINT sp,
354       int angle)
355 {
356   if (angle)                    /* arc with angle */
357     HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale),
358           (int) (sp.x * troffscale), (int) (sp.y * troffscale), angle);
359   else                          /* a full circle (angle == 0) */
360     HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale),
361           (int) (sp.x * troffscale), (int) (sp.y * troffscale), 0);
362 }
363
364
365 /*----------------------------------------------------------------------------*
366  | Routine:     HGSetFont (font_number, Point_size)
367  |
368  | Results:     ALWAYS outputs a .ft and .ps directive to troff.  This is
369  |              done because someone may change stuff inside a text string. 
370  |              Changes thickness back to default thickness.  Default
371  |              thickness depends on font and pointsize.
372  *----------------------------------------------------------------------------*/
373
374 void
375 HGSetFont(int font,
376           int size)
377 {
378   printf(".ft %s\n"
379          ".ps %d\n", tfont[font - 1], tsize[size - 1]);
380   linethickness = DEFTHICK;
381 }
382
383
384 /*----------------------------------------------------------------------------*
385  | Routine:     HGSetBrush (line_mode)
386  |
387  | Results:     Generates the troff commands to set up the line width and
388  |              style of subsequent lines.  Does nothing if no change is
389  |              needed.
390  |
391  | Side Efct:   Sets `linmode' and `linethicknes'.
392  *----------------------------------------------------------------------------*/
393
394 void
395 HGSetBrush(int mode)
396 {
397   register int printed = 0;
398
399   if (linmod != style[--mode]) {
400     /* Groff doesn't understand \Ds, so we take it out */
401     /* printf ("\\D's %du'", linmod = style[mode]); */
402     linmod = style[mode];
403     printed = 1;
404   }
405   if (linethickness != thick[mode]) {
406     linethickness = thick[mode];
407     printf("\\h'-%.2fp'\\D't %.2fp'", linethickness, linethickness);
408     printed = 1;
409   }
410   if (printed)
411     cr();
412 }
413
414
415 /*----------------------------------------------------------------------------*
416  | Routine:     dx (x_destination)
417  |
418  | Results:     Scales and outputs a number for delta x (with a leading
419  |              space) given `lastx' and x_destination.
420  |
421  | Side Efct:   Resets `lastx' to x_destination.
422  *----------------------------------------------------------------------------*/
423
424 void
425 dx(double x)
426 {
427   register int ix = (int) (x * troffscale);
428
429   printf(" %du", ix - lastx);
430   lastx = ix;
431 }
432
433
434 /*----------------------------------------------------------------------------*
435  | Routine:     dy (y_destination)
436  |
437  | Results:     Scales and outputs a number for delta y (with a leading
438  |              space) given `lastyline' and y_destination.
439  |
440  | Side Efct:   Resets `lastyline' to y_destination.  Since `line' vertical
441  |              motions don't affect `page' ones, `lasty' isn't updated.
442  *----------------------------------------------------------------------------*/
443
444 void
445 dy(double y)
446 {
447   register int iy = (int) (y * troffscale);
448
449   printf(" %du", iy - lastyline);
450   lastyline = iy;
451 }
452
453
454 /*----------------------------------------------------------------------------*
455  | Routine:     tmove2 (px, py)
456  |
457  | Results:     Produces horizontal and vertical moves for troff given the
458  |              pair of points to move to and knowing the current position. 
459  |              Also puts out a horizontal move to start the line.  This is
460  |              a variation without the .sp command.
461  *----------------------------------------------------------------------------*/
462
463 void
464 tmove2(int px,
465        int py)
466 {
467   register int dx;
468   register int dy;
469
470   if ((dy = py - lasty)) {
471     printf("\\v'%du'", dy);
472   }
473   lastyline = lasty = py;       /* lasty is always set to current */
474   if ((dx = px - lastx)) {
475     printf("\\h'%du'", dx);
476     lastx = px;
477   }
478 }
479
480
481 /*----------------------------------------------------------------------------*
482  | Routine:     tmove (point_pointer)
483  |
484  | Results:     Produces horizontal and vertical moves for troff given the
485  |              pointer of a point to move to and knowing the current
486  |              position.  Also puts out a horizontal move to start the
487  |              line.
488  *----------------------------------------------------------------------------*/
489
490 void
491 tmove(POINT * ptr)
492 {
493   register int ix = (int) (ptr->x * troffscale);
494   register int iy = (int) (ptr->y * troffscale);
495   register int dx;
496   register int dy;
497
498   if ((dy = iy - lasty)) {
499     printf(".sp %du\n", dy);
500   }
501   lastyline = lasty = iy;       /* lasty is always set to current */
502   if ((dx = ix - lastx)) {
503     printf("\\h'%du'", dx);
504     lastx = ix;
505   }
506 }
507
508
509 /*----------------------------------------------------------------------------*
510  | Routine:     cr ( )
511  |
512  | Results:     Ends off an input line.  `.sp -1' is also added to counteract
513  |              the vertical move done at the end of text lines.
514  |
515  | Side Efct:   Sets `lastx' to `xleft' for troff's return to left margin.
516  *----------------------------------------------------------------------------*/
517
518 void
519 cr()
520 {
521   printf("\n.sp -1\n");
522   lastx = xleft;
523 }
524
525
526 /*----------------------------------------------------------------------------*
527  | Routine:     line ( )
528  |
529  | Results:     Draws a single solid line to (x,y).
530  *----------------------------------------------------------------------------*/
531
532 void
533 line(int px,
534      int py)
535 {
536   printf("\\D'l");
537   printf(" %du", px - lastx);
538   printf(" %du'", py - lastyline);
539   lastx = px;
540   lastyline = lasty = py;
541 }
542
543
544 /*----------------------------------------------------------------------------
545  | Routine:     drawwig (ptr, type)
546  |
547  | Results:     The point sequence found in the structure pointed by ptr is
548  |              placed in integer arrays for further manipulation by the
549  |              existing routing.  With the corresponding type parameter,
550  |              either picurve or HGCurve are called.
551  *----------------------------------------------------------------------------*/
552
553 void
554 drawwig(POINT * ptr,
555         int type)
556 {
557   register int npts;                    /* point list index */
558   int x[MAXPOINTS], y[MAXPOINTS];       /* point list */
559
560   for (npts = 1; !Nullpoint(ptr); ptr = PTNextPoint(ptr), npts++) {
561     x[npts] = (int) (ptr->x * troffscale);
562     y[npts] = (int) (ptr->y * troffscale);
563   }
564   if (--npts) {
565     if (type == CURVE) /* Use the 2 different types of curves */
566       HGCurve(&x[0], &y[0], npts);
567     else
568       picurve(&x[0], &y[0], npts);
569   }
570 }
571
572
573 /*----------------------------------------------------------------------------
574  | Routine:     HGArc (xcenter, ycenter, xstart, ystart, angle)
575  |
576  | Results:     This routine plots an arc centered about (cx, cy) counter
577  |              clockwise starting from the point (px, py) through `angle'
578  |              degrees.  If angle is 0, a full circle is drawn.  It does so
579  |              by creating a draw-path around the arc whose density of
580  |              points depends on the size of the arc.
581  *----------------------------------------------------------------------------*/
582
583 void
584 HGArc(register int cx,
585       register int cy,
586       int px,
587       int py,
588       int angle)
589 {
590   double xs, ys, resolution, fullcircle;
591   int m;
592   register int mask;
593   register int extent;
594   register int nx;
595   register int ny;
596   register int length;
597   register double epsilon;
598
599   xs = px - cx;
600   ys = py - cy;
601
602   length = 0;
603
604   resolution = (1.0 + hypot(xs, ys) / res) * PointsPerInterval;
605   /* mask = (1 << (int) log10(resolution + 1.0)) - 1; */
606   (void) frexp(resolution, &m);         /* A bit more elegant than log10 */
607   for (mask = 1; mask < m; mask = mask << 1);
608   mask -= 1;
609
610   epsilon = 1.0 / resolution;
611   fullcircle = (2.0 * pi) * resolution;
612   if (angle == 0)
613     extent = (int) fullcircle;
614   else
615     extent = (int) (angle * fullcircle / 360.0);
616
617   HGtline(px, py);
618   while (--extent >= 0) {
619     xs += epsilon * ys;
620     nx = cx + (int) (xs + 0.5);
621     ys -= epsilon * xs;
622     ny = cy + (int) (ys + 0.5);
623     if (!(extent & mask)) {
624       HGtline(nx, ny);          /* put out a point on circle */
625       if (length++ > LINELENGTH) {
626         length = 0;
627         printf("\\\n");
628       }
629     }
630   }                             /* end for */
631 }                               /* end HGArc */
632
633
634 /*----------------------------------------------------------------------------
635  | Routine:     picurve (xpoints, ypoints, num_of_points)
636  |
637  | Results:     Draws a curve delimited by (not through) the line segments
638  |              traced by (xpoints, ypoints) point list.  This is the `Pic'
639  |              style curve.
640  *----------------------------------------------------------------------------*/
641
642 void
643 picurve(register int *x,
644         register int *y,
645         int npts)
646 {
647   register int nseg;            /* effective resolution for each curve */
648   register int xp;              /* current point (and temporary) */
649   register int yp;
650   int pxp, pyp;                 /* previous point (to make lines from) */
651   int i;                        /* inner curve segment traverser */
652   int length = 0;
653   double w;                     /* position factor */
654   double t1, t2, t3;            /* calculation temps */
655
656   if (x[1] == x[npts] && y[1] == y[npts]) {
657     x[0] = x[npts - 1];         /* if the lines' ends meet, make */
658     y[0] = y[npts - 1];         /* sure the curve meets          */
659     x[npts + 1] = x[2];
660     y[npts + 1] = y[2];
661   } else {                      /* otherwise, make the ends of the  */
662     x[0] = x[1];                /* curve touch the ending points of */
663     y[0] = y[1];                /* the line segments                */
664     x[npts + 1] = x[npts];
665     y[npts + 1] = y[npts];
666   }
667
668   pxp = (x[0] + x[1]) / 2;      /* make the last point pointers       */
669   pyp = (y[0] + y[1]) / 2;      /* point to the start of the 1st line */
670   tmove2(pxp, pyp);
671
672   for (; npts--; x++, y++) {    /* traverse the line segments */
673     xp = x[0] - x[1];
674     yp = y[0] - y[1];
675     nseg = (int) hypot((double) xp, (double) yp);
676     xp = x[1] - x[2];
677     yp = y[1] - y[2];
678                                 /* `nseg' is the number of line    */
679                                 /* segments that will be drawn for */
680                                 /* each curve segment.             */
681     nseg = (int) ((double) (nseg + (int) hypot((double) xp, (double) yp)) /
682                   res * PointsPerInterval);
683
684     for (i = 1; i < nseg; i++) {
685       w = (double) i / (double) nseg;
686       t1 = w * w;
687       t3 = t1 + 1.0 - (w + w);
688       t2 = 2.0 - (t3 + t1);
689       xp = (((int) (t1 * x[2] + t2 * x[1] + t3 * x[0])) + 1) / 2;
690       yp = (((int) (t1 * y[2] + t2 * y[1] + t3 * y[0])) + 1) / 2;
691
692       HGtline(xp, yp);
693       if (length++ > LINELENGTH) {
694         length = 0;
695         printf("\\\n");
696       }
697     }
698   }
699 }
700
701
702 /*----------------------------------------------------------------------------
703  | Routine:     HGCurve(xpoints, ypoints, num_points)
704  |
705  | Results:     This routine generates a smooth curve through a set of
706  |              points.  The method used is the parametric spline curve on
707  |              unit knot mesh described in `Spline Curve Techniques' by
708  |              Patrick Baudelaire, Robert Flegal, and Robert Sproull --
709  |              Xerox Parc.
710  *----------------------------------------------------------------------------*/
711
712 void
713 HGCurve(int *x,
714         int *y,
715         int numpoints)
716 {
717   float h[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS];
718   float d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS];
719   float t, t2, t3;
720   register int j;
721   register int k;
722   register int nx;
723   register int ny;
724   int lx, ly;
725   int length = 0;
726
727   lx = x[1];
728   ly = y[1];
729   tmove2(lx, ly);
730
731   /*
732    * Solve for derivatives of the curve at each point separately for x and y
733    * (parametric).
734    */
735   Paramaterize(x, y, h, numpoints);
736
737   /* closed curve */
738   if ((x[1] == x[numpoints]) && (y[1] == y[numpoints])) {
739     PeriodicSpline(h, x, dx, d2x, d3x, numpoints);
740     PeriodicSpline(h, y, dy, d2y, d3y, numpoints);
741   } else {
742     NaturalEndSpline(h, x, dx, d2x, d3x, numpoints);
743     NaturalEndSpline(h, y, dy, d2y, d3y, numpoints);
744   }
745
746   /*
747    * generate the curve using the above information and PointsPerInterval
748    * vectors between each specified knot.
749    */
750
751   for (j = 1; j < numpoints; ++j) {
752     if ((x[j] == x[j + 1]) && (y[j] == y[j + 1]))
753       continue;
754     for (k = 0; k <= PointsPerInterval; ++k) {
755       t = (float) k *h[j] / (float) PointsPerInterval;
756       t2 = t * t;
757       t3 = t * t * t;
758       nx = x[j] + (int) (t * dx[j] + t2 * d2x[j] / 2 + t3 * d3x[j] / 6);
759       ny = y[j] + (int) (t * dy[j] + t2 * d2y[j] / 2 + t3 * d3y[j] / 6);
760       HGtline(nx, ny);
761       if (length++ > LINELENGTH) {
762         length = 0;
763         printf("\\\n");
764       }
765     }                           /* end for k */
766   }                             /* end for j */
767 }                               /* end HGCurve */
768
769
770 /*----------------------------------------------------------------------------
771  | Routine:     Paramaterize (xpoints, ypoints, hparams, num_points)
772  |
773  | Results:     This routine calculates parameteric values for use in
774  |              calculating curves.  The parametric values are returned
775  |              in the array h.  The values are an approximation of
776  |              cumulative arc lengths of the curve (uses cord length).
777  |              For additional information, see paper cited below.
778  *----------------------------------------------------------------------------*/
779
780 void
781 Paramaterize(int x[],
782              int y[],
783              float h[],
784              int n)
785 {
786   register int dx;
787   register int dy;
788   register int i;
789   register int j;
790   float u[MAXPOINTS];
791
792   for (i = 1; i <= n; ++i) {
793     u[i] = 0;
794     for (j = 1; j < i; j++) {
795       dx = x[j + 1] - x[j];
796       dy = y[j + 1] - y[j];
797       /* Here was overflowing, so I changed it.       */
798       /* u[i] += sqrt ((double) (dx * dx + dy * dy)); */
799       u[i] += hypot((double) dx, (double) dy);
800     }
801   }
802   for (i = 1; i < n; ++i)
803     h[i] = u[i + 1] - u[i];
804 }                               /* end Paramaterize */
805
806
807 /*----------------------------------------------------------------------------
808  | Routine:     PeriodicSpline (h, z, dz, d2z, d3z, npoints)
809  |
810  | Results:     This routine solves for the cubic polynomial to fit a spline
811  |              curve to the the points specified by the list of values. 
812  |              The Curve generated is periodic.  The algorithms for this
813  |              curve are from the `Spline Curve Techniques' paper cited
814  |              above.
815  *----------------------------------------------------------------------------*/
816
817 void
818 PeriodicSpline(float h[],       /* paramaterization  */
819                int z[],         /* point list */
820                float dz[],      /* to return the 1st derivative */
821                float d2z[],     /* 2nd derivative */
822                float d3z[],     /* 3rd derivative */
823                int npoints)     /* number of valid points */
824 {
825   float d[MAXPOINTS];
826   float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
827   float c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS];
828   int i;
829
830   /* step 1 */
831   for (i = 1; i < npoints; ++i) {
832     deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0;
833   }
834   h[0] = h[npoints - 1];
835   deltaz[0] = deltaz[npoints - 1];
836
837   /* step 2 */
838   for (i = 1; i < npoints - 1; ++i) {
839     d[i] = deltaz[i + 1] - deltaz[i];
840   }
841   d[0] = deltaz[1] - deltaz[0];
842
843   /* step 3a */
844   a[1] = 2 * (h[0] + h[1]);
845   b[1] = d[0];
846   c[1] = h[0];
847   for (i = 2; i < npoints - 1; ++i) {
848     a[i] = 2 * (h[i - 1] + h[i]) -
849            pow((double) h[i - 1], (double) 2.0) / a[i - 1];
850     b[i] = d[i - 1] - h[i - 1] * b[i - 1] / a[i - 1];
851     c[i] = -h[i - 1] * c[i - 1] / a[i - 1];
852   }
853
854   /* step 3b */
855   r[npoints - 1] = 1;
856   s[npoints - 1] = 0;
857   for (i = npoints - 2; i > 0; --i) {
858     r[i] = -(h[i] * r[i + 1] + c[i]) / a[i];
859     s[i] = (6 * b[i] - h[i] * s[i + 1]) / a[i];
860   }
861
862   /* step 4 */
863   d2z[npoints - 1] = (6 * d[npoints - 2] - h[0] * s[1]
864                       - h[npoints - 1] * s[npoints - 2])
865                      / (h[0] * r[1] + h[npoints - 1] * r[npoints - 2]
866                       + 2 * (h[npoints - 2] + h[0]));
867   for (i = 1; i < npoints - 1; ++i) {
868     d2z[i] = r[i] * d2z[npoints - 1] + s[i];
869   }
870   d2z[npoints] = d2z[1];
871
872   /* step 5 */
873   for (i = 1; i < npoints; ++i) {
874     dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6;
875     d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0;
876   }
877 }                               /* end PeriodicSpline */
878
879
880 /*----------------------------------------------------------------------------
881  | Routine:     NaturalEndSpline (h, z, dz, d2z, d3z, npoints)
882  |
883  | Results:     This routine solves for the cubic polynomial to fit a spline
884  |              curve the the points specified by the list of values.  The
885  |              alogrithms for this curve are from the `Spline Curve
886  |              Techniques' paper cited above.
887  *----------------------------------------------------------------------------*/
888
889 void
890 NaturalEndSpline(float h[],     /* parameterization */
891                  int z[],       /* Point list */
892                  float dz[],    /* to return the 1st derivative */
893                  float d2z[],   /* 2nd derivative */
894                  float d3z[],   /* 3rd derivative */
895                  int npoints)   /* number of valid points */
896 {
897   float d[MAXPOINTS];
898   float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
899   int i;
900
901   /* step 1 */
902   for (i = 1; i < npoints; ++i) {
903     deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0;
904   }
905   deltaz[0] = deltaz[npoints - 1];
906
907   /* step 2 */
908   for (i = 1; i < npoints - 1; ++i) {
909     d[i] = deltaz[i + 1] - deltaz[i];
910   }
911   d[0] = deltaz[1] - deltaz[0];
912
913   /* step 3 */
914   a[0] = 2 * (h[2] + h[1]);
915   b[0] = d[1];
916   for (i = 1; i < npoints - 2; ++i) {
917     a[i] = 2 * (h[i + 1] + h[i + 2]) -
918             pow((double) h[i + 1], (double) 2.0) / a[i - 1];
919     b[i] = d[i + 1] - h[i + 1] * b[i - 1] / a[i - 1];
920   }
921
922   /* step 4 */
923   d2z[npoints] = d2z[1] = 0;
924   for (i = npoints - 1; i > 1; --i) {
925     d2z[i] = (6 * b[i - 2] - h[i] * d2z[i + 1]) / a[i - 2];
926   }
927
928   /* step 5 */
929   for (i = 1; i < npoints; ++i) {
930     dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6;
931     d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0;
932   }
933 }                               /* end NaturalEndSpline */
934
935
936 /*----------------------------------------------------------------------------*
937  | Routine:     change (x_position, y_position, visible_flag)
938  |
939  | Results:     As HGtline passes from the invisible to visible (or vice
940  |              versa) portion of a line, change is called to either draw
941  |              the line, or initialize the beginning of the next one.
942  |              Change calls line to draw segments if visible_flag is set
943  |              (which means we're leaving a visible area).
944  *----------------------------------------------------------------------------*/
945
946 void
947 change(register int x,
948        register int y,
949        register int vis)
950 {
951   static int length = 0;
952
953   if (vis) {                    /* leaving a visible area, draw it. */
954     line(x, y);
955     if (length++ > LINELENGTH) {
956       length = 0;
957       printf("\\\n");
958     }
959   } else {                      /* otherwise, we're entering one, remember */
960                                 /* beginning                               */
961     tmove2(x, y);
962   }
963 }
964
965
966 /*----------------------------------------------------------------------------
967  | Routine:     HGtline (xstart, ystart, xend, yend)
968  |
969  | Results:     Draws a line from current position to (x1,y1) using line(x1,
970  |              y1) to place individual segments of dotted or dashed lines.
971  *----------------------------------------------------------------------------*/
972
973 void
974 HGtline(int x1,
975         int y1)
976 {
977   register int x0 = lastx;
978   register int y0 = lasty;
979   register int dx;
980   register int dy;
981   register int oldcoord;
982   register int res1;
983   register int visible;
984   register int res2;
985   register int xinc;
986   register int yinc;
987   register int dotcounter;
988
989   if (linmod == SOLID) {
990     line(x1, y1);
991     return;
992   }
993
994   /* for handling different resolutions */
995   dotcounter = linmod << dotshifter;
996
997   xinc = 1;
998   yinc = 1;
999   if ((dx = x1 - x0) < 0) {
1000     xinc = -xinc;
1001     dx = -dx;
1002   }
1003   if ((dy = y1 - y0) < 0) {
1004     yinc = -yinc;
1005     dy = -dy;
1006   }
1007   res1 = 0;
1008   res2 = 0;
1009   visible = 0;
1010   if (dx >= dy) {
1011     oldcoord = y0;
1012     while (x0 != x1) {
1013       if ((x0 & dotcounter) && !visible) {
1014         change(x0, y0, 0);
1015         visible = 1;
1016       } else if (visible && !(x0 & dotcounter)) {
1017         change(x0 - xinc, oldcoord, 1);
1018         visible = 0;
1019       }
1020       if (res1 > res2) {
1021         oldcoord = y0;
1022         res2 += dx - res1;
1023         res1 = 0;
1024         y0 += yinc;
1025       }
1026       res1 += dy;
1027       x0 += xinc;
1028     }
1029   } else {
1030     oldcoord = x0;
1031     while (y0 != y1) {
1032       if ((y0 & dotcounter) && !visible) {
1033         change(x0, y0, 0);
1034         visible = 1;
1035       } else if (visible && !(y0 & dotcounter)) {
1036         change(oldcoord, y0 - yinc, 1);
1037         visible = 0;
1038       }
1039       if (res1 > res2) {
1040         oldcoord = x0;
1041         res2 += dy - res1;
1042         res1 = 0;
1043         x0 += xinc;
1044       }
1045       res1 += dx;
1046       y0 += yinc;
1047     }
1048   }
1049   if (visible)
1050     change(x1, y1, 1);
1051   else
1052     change(x1, y1, 0);
1053 }
1054
1055 /* EOF */