* doc/groff.texinfo: Improve documentation of `ad'.
[s-roff.git] / src / preproc / grn / hgraph.cc
blob869b81c8519a7228448913a9b8e2e11aa71bc948
1 /* Last non-groff version: hgraph.c 1.14 (Berkeley) 84/11/27
3 * This file contains the graphics routines for converting gremlin pictures
4 * to troff input.
5 */
7 #include "lib.h"
9 #include "gprint.h"
11 #ifdef NEED_DECLARATION_HYPOT
12 extern "C" {
13 double hypot(double, double);
15 #endif /* NEED_DECLARATION_HYPOT */
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))
26 extern int dotshifter; /* for the length of dotted curves */
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) */
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;
50 extern double adj1;
51 extern double adj2;
52 extern double adj3;
53 extern double adj4;
54 extern int res;
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);
80 /*----------------------------------------------------------------------------*
81 | Routine: HGPrintElt (element_pointer, baseline)
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 *----------------------------------------------------------------------------*/
88 void
89 HGPrintElt(ELT *element,
90 int baseline)
92 register POINT *p1;
93 register POINT *p2;
94 register int length;
95 register int graylevel;
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;
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 */
122 switch (element->type) {
124 case ARC:
125 p2 = PTNextPoint(p1);
126 tmove(p2);
127 doarc(*p1, *p2, element->size);
128 cr();
129 break;
131 case CURVE:
132 length = 0; /* keep track of line length */
133 drawwig(p1, CURVE);
134 cr();
135 break;
137 case BSPLINE:
138 length = 0; /* keep track of line length */
139 drawwig(p1, BSPLINE);
140 cr();
141 break;
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");
153 } /* end while */
154 cr();
155 break;
157 case POLYGON:
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.
169 float firstx = p1->x;
170 float firsty = p1->y;
172 length = 0; /* keep track of line length so */
173 /* single lines don't get long */
175 if (polyfill == FILL || polyfill == BOTH) {
176 /* do the interior */
177 char command = (polyfill == BOTH && element->brushf) ? 'p' : 'P';
179 /* include outline, if there is one and */
180 /* the -p flag was set */
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;
212 /* int graylevel = element->size; */
214 if (graylevel < 0)
215 break;
216 if (graylevel > NSTIPPLES)
217 graylevel = NSTIPPLES;
218 printf("\\D'f %du'", stipple_index[graylevel])
219 cr();
220 tmove(p1);
221 printf("\\D'%c", command);
223 while (!Nullpoint((PTNextPoint(p1)))) {
224 p1 = PTNextPoint(p1);
225 dx((double) p1->x);
226 dy((double) p1->y);
227 if (length++ > LINELENGTH) {
228 length = 0;
229 printf("\\\n");
231 } /* end while */
233 /* close polygon if not done so by user */
234 if ((firstx != p1->x) || (firsty != p1->y)) {
235 dx((double) firstx);
236 dy((double) firsty);
238 putchar('\'');
239 cr();
240 break;
242 /* else polyfill == OUTLINE; only draw the outline */
243 if (!(element->brushf))
244 break;
245 length = 0; /* keep track of line length */
246 tmove(p1);
248 while (!Nullpoint((PTNextPoint(p1)))) {
249 p1 = PTNextPoint(p1);
250 HGtline((int) (p1->x * troffscale),
251 (int) (p1->y * troffscale));
252 if (length++ > LINELENGTH) {
253 length = 0;
254 printf("\\\n");
256 } /* end while */
258 /* close polygon if not done so by user */
259 if ((firstx != p1->x) || (firsty != p1->y)) {
260 HGtline((int) (firstx * troffscale),
261 (int) (firsty * troffscale));
263 cr();
264 break;
265 } /* end case POLYGON */
266 } /* end switch */
267 } /* end else Text */
268 } /* end if */
269 } /* end PrintElt */
272 /*----------------------------------------------------------------------------*
273 | Routine: HGPutText (justification, position_point, string)
275 | Results: Given the justification, a point to position with, and a
276 | string to put, HGPutText first sends the string into a
277 | diversion, moves to the positioning point, then outputs
278 | local vertical and horizontal motions as needed to justify
279 | the text. After all motions are done, the diversion is
280 | printed out.
281 *----------------------------------------------------------------------------*/
283 void
284 HGPutText(int justify,
285 POINT pnt,
286 register char *string)
288 int savelasty = lasty; /* vertical motion for text is to be */
289 /* ignored. Save current y here */
291 printf(".nr g8 \\n(.d\n"); /* save current vertical position. */
292 printf(".ds g9 \""); /* define string containing the text. */
293 while (*string) { /* put out the string */
294 if (*string == '\\' &&
295 *(string + 1) == '\\') { /* one character at a */
296 printf("\\\\\\"); /* time replacing // */
297 string++; /* by //// to prevent */
298 } /* interpretation at */
299 printf("%c", *(string++)); /* printout time */
301 printf("\n");
303 tmove(&pnt); /* move to positioning point */
305 switch (justify) {
306 /* local vertical motions */
307 /* (the numbers here are used to be somewhat compatible with gprint) */
308 case CENTLEFT:
309 case CENTCENT:
310 case CENTRIGHT:
311 printf("\\v'0.85n'"); /* down half */
312 break;
314 case TOPLEFT:
315 case TOPCENT:
316 case TOPRIGHT:
317 printf("\\v'1.7n'"); /* down whole */
320 switch (justify) {
321 /* local horizontal motions */
322 case BOTCENT:
323 case CENTCENT:
324 case TOPCENT:
325 printf("\\h'-\\w'\\*(g9'u/2u'"); /* back half */
326 break;
328 case BOTRIGHT:
329 case CENTRIGHT:
330 case TOPRIGHT:
331 printf("\\h'-\\w'\\*(g9'u'"); /* back whole */
334 printf("\\&\\*(g9\n"); /* now print the text. */
335 printf(".sp |\\n(g8u\n"); /* restore vertical position */
336 lasty = savelasty; /* vertical position restored to where it */
337 lastx = xleft; /* was before text, also horizontal is at */
338 /* left */
339 } /* end HGPutText */
342 /*----------------------------------------------------------------------------*
343 | Routine: doarc (center_point, start_point, angle)
345 | Results: Produces either drawarc command or a drawcircle command
346 | depending on the angle needed to draw through.
347 *----------------------------------------------------------------------------*/
349 void
350 doarc(POINT cp,
351 POINT sp,
352 int angle)
354 if (angle) /* arc with angle */
355 HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale),
356 (int) (sp.x * troffscale), (int) (sp.y * troffscale), angle);
357 else /* a full circle (angle == 0) */
358 HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale),
359 (int) (sp.x * troffscale), (int) (sp.y * troffscale), 0);
363 /*----------------------------------------------------------------------------*
364 | Routine: HGSetFont (font_number, Point_size)
366 | Results: ALWAYS outputs a .ft and .ps directive to troff. This is
367 | done because someone may change stuff inside a text string.
368 | Changes thickness back to default thickness. Default
369 | thickness depends on font and pointsize.
370 *----------------------------------------------------------------------------*/
372 void
373 HGSetFont(int font,
374 int size)
376 printf(".ft %s\n"
377 ".ps %d\n", tfont[font - 1], tsize[size - 1]);
378 linethickness = DEFTHICK;
382 /*----------------------------------------------------------------------------*
383 | Routine: HGSetBrush (line_mode)
385 | Results: Generates the troff commands to set up the line width and
386 | style of subsequent lines. Does nothing if no change is
387 | needed.
389 | Side Efct: Sets `linmode' and `linethicknes'.
390 *----------------------------------------------------------------------------*/
392 void
393 HGSetBrush(int mode)
395 register int printed = 0;
397 if (linmod != style[--mode]) {
398 /* Groff doesn't understand \Ds, so we take it out */
399 /* printf ("\\D's %du'", linmod = style[mode]); */
400 linmod = style[mode];
401 printed = 1;
403 if (linethickness != thick[mode]) {
404 linethickness = thick[mode];
405 printf("\\h'-%.2fp'\\D't %.2fp'", linethickness, linethickness);
406 printed = 1;
408 if (printed)
409 cr();
413 /*----------------------------------------------------------------------------*
414 | Routine: dx (x_destination)
416 | Results: Scales and outputs a number for delta x (with a leading
417 | space) given `lastx' and x_destination.
419 | Side Efct: Resets `lastx' to x_destination.
420 *----------------------------------------------------------------------------*/
422 void
423 dx(double x)
425 register int ix = (int) (x * troffscale);
427 printf(" %du", ix - lastx);
428 lastx = ix;
432 /*----------------------------------------------------------------------------*
433 | Routine: dy (y_destination)
435 | Results: Scales and outputs a number for delta y (with a leading
436 | space) given `lastyline' and y_destination.
438 | Side Efct: Resets `lastyline' to y_destination. Since `line' vertical
439 | motions don't affect `page' ones, `lasty' isn't updated.
440 *----------------------------------------------------------------------------*/
442 void
443 dy(double y)
445 register int iy = (int) (y * troffscale);
447 printf(" %du", iy - lastyline);
448 lastyline = iy;
452 /*----------------------------------------------------------------------------*
453 | Routine: tmove2 (px, py)
455 | Results: Produces horizontal and vertical moves for troff given the
456 | pair of points to move to and knowing the current position.
457 | Also puts out a horizontal move to start the line. This is
458 | a variation without the .sp command.
459 *----------------------------------------------------------------------------*/
461 void
462 tmove2(int px,
463 int py)
465 register int dx;
466 register int dy;
468 if ((dy = py - lasty)) {
469 printf("\\v'%du'", dy);
471 lastyline = lasty = py; /* lasty is always set to current */
472 if ((dx = px - lastx)) {
473 printf("\\h'%du'", dx);
474 lastx = px;
479 /*----------------------------------------------------------------------------*
480 | Routine: tmove (point_pointer)
482 | Results: Produces horizontal and vertical moves for troff given the
483 | pointer of a point to move to and knowing the current
484 | position. Also puts out a horizontal move to start the
485 | line.
486 *----------------------------------------------------------------------------*/
488 void
489 tmove(POINT * ptr)
491 register int ix = (int) (ptr->x * troffscale);
492 register int iy = (int) (ptr->y * troffscale);
493 register int dx;
494 register int dy;
496 if ((dy = iy - lasty)) {
497 printf(".sp %du\n", dy);
499 lastyline = lasty = iy; /* lasty is always set to current */
500 if ((dx = ix - lastx)) {
501 printf("\\h'%du'", dx);
502 lastx = ix;
507 /*----------------------------------------------------------------------------*
508 | Routine: cr ( )
510 | Results: Ends off an input line. `.sp -1' is also added to counteract
511 | the vertical move done at the end of text lines.
513 | Side Efct: Sets `lastx' to `xleft' for troff's return to left margin.
514 *----------------------------------------------------------------------------*/
516 void
517 cr()
519 printf("\n.sp -1\n");
520 lastx = xleft;
524 /*----------------------------------------------------------------------------*
525 | Routine: line ( )
527 | Results: Draws a single solid line to (x,y).
528 *----------------------------------------------------------------------------*/
530 void
531 line(int px,
532 int py)
534 printf("\\D'l");
535 printf(" %du", px - lastx);
536 printf(" %du'", py - lastyline);
537 lastx = px;
538 lastyline = lasty = py;
542 /*----------------------------------------------------------------------------
543 | Routine: drawwig (ptr, type)
545 | Results: The point sequence found in the structure pointed by ptr is
546 | placed in integer arrays for further manipulation by the
547 | existing routing. With the corresponding type parameter,
548 | either picurve or HGCurve are called.
549 *----------------------------------------------------------------------------*/
551 void
552 drawwig(POINT * ptr,
553 int type)
555 register int npts; /* point list index */
556 int x[MAXPOINTS], y[MAXPOINTS]; /* point list */
558 for (npts = 1; !Nullpoint(ptr); ptr = PTNextPoint(ptr), npts++) {
559 x[npts] = (int) (ptr->x * troffscale);
560 y[npts] = (int) (ptr->y * troffscale);
562 if (--npts) {
563 if (type == CURVE) /* Use the 2 different types of curves */
564 HGCurve(&x[0], &y[0], npts);
565 else
566 picurve(&x[0], &y[0], npts);
571 /*----------------------------------------------------------------------------
572 | Routine: HGArc (xcenter, ycenter, xstart, ystart, angle)
574 | Results: This routine plots an arc centered about (cx, cy) counter
575 | clockwise starting from the point (px, py) through `angle'
576 | degrees. If angle is 0, a full circle is drawn. It does so
577 | by creating a draw-path around the arc whose density of
578 | points depends on the size of the arc.
579 *----------------------------------------------------------------------------*/
581 void
582 HGArc(register int cx,
583 register int cy,
584 int px,
585 int py,
586 int angle)
588 double xs, ys, resolution, fullcircle;
589 int m;
590 register int mask;
591 register int extent;
592 register int nx;
593 register int ny;
594 register int length;
595 register double epsilon;
597 xs = px - cx;
598 ys = py - cy;
600 length = 0;
602 resolution = (1.0 + hypot(xs, ys) / res) * PointsPerInterval;
603 /* mask = (1 << (int) log10(resolution + 1.0)) - 1; */
604 (void) frexp(resolution, &m); /* A bit more elegant than log10 */
605 for (mask = 1; mask < m; mask = mask << 1);
606 mask -= 1;
608 epsilon = 1.0 / resolution;
609 fullcircle = (2.0 * pi) * resolution;
610 if (angle == 0)
611 extent = (int) fullcircle;
612 else
613 extent = (int) (angle * fullcircle / 360.0);
615 HGtline(px, py);
616 while (--extent >= 0) {
617 xs += epsilon * ys;
618 nx = cx + (int) (xs + 0.5);
619 ys -= epsilon * xs;
620 ny = cy + (int) (ys + 0.5);
621 if (!(extent & mask)) {
622 HGtline(nx, ny); /* put out a point on circle */
623 if (length++ > LINELENGTH) {
624 length = 0;
625 printf("\\\n");
628 } /* end for */
629 } /* end HGArc */
632 /*----------------------------------------------------------------------------
633 | Routine: picurve (xpoints, ypoints, num_of_points)
635 | Results: Draws a curve delimited by (not through) the line segments
636 | traced by (xpoints, ypoints) point list. This is the `Pic'
637 | style curve.
638 *----------------------------------------------------------------------------*/
640 void
641 picurve(register int *x,
642 register int *y,
643 int npts)
645 register int nseg; /* effective resolution for each curve */
646 register int xp; /* current point (and temporary) */
647 register int yp;
648 int pxp, pyp; /* previous point (to make lines from) */
649 int i; /* inner curve segment traverser */
650 int length = 0;
651 double w; /* position factor */
652 double t1, t2, t3; /* calculation temps */
654 if (x[1] == x[npts] && y[1] == y[npts]) {
655 x[0] = x[npts - 1]; /* if the lines' ends meet, make */
656 y[0] = y[npts - 1]; /* sure the curve meets */
657 x[npts + 1] = x[2];
658 y[npts + 1] = y[2];
659 } else { /* otherwise, make the ends of the */
660 x[0] = x[1]; /* curve touch the ending points of */
661 y[0] = y[1]; /* the line segments */
662 x[npts + 1] = x[npts];
663 y[npts + 1] = y[npts];
666 pxp = (x[0] + x[1]) / 2; /* make the last point pointers */
667 pyp = (y[0] + y[1]) / 2; /* point to the start of the 1st line */
668 tmove2(pxp, pyp);
670 for (; npts--; x++, y++) { /* traverse the line segments */
671 xp = x[0] - x[1];
672 yp = y[0] - y[1];
673 nseg = (int) hypot((double) xp, (double) yp);
674 xp = x[1] - x[2];
675 yp = y[1] - y[2];
676 /* `nseg' is the number of line */
677 /* segments that will be drawn for */
678 /* each curve segment. */
679 nseg = (int) ((double) (nseg + (int) hypot((double) xp, (double) yp)) /
680 res * PointsPerInterval);
682 for (i = 1; i < nseg; i++) {
683 w = (double) i / (double) nseg;
684 t1 = w * w;
685 t3 = t1 + 1.0 - (w + w);
686 t2 = 2.0 - (t3 + t1);
687 xp = (((int) (t1 * x[2] + t2 * x[1] + t3 * x[0])) + 1) / 2;
688 yp = (((int) (t1 * y[2] + t2 * y[1] + t3 * y[0])) + 1) / 2;
690 HGtline(xp, yp);
691 if (length++ > LINELENGTH) {
692 length = 0;
693 printf("\\\n");
700 /*----------------------------------------------------------------------------
701 | Routine: HGCurve(xpoints, ypoints, num_points)
703 | Results: This routine generates a smooth curve through a set of
704 | points. The method used is the parametric spline curve on
705 | unit knot mesh described in `Spline Curve Techniques' by
706 | Patrick Baudelaire, Robert Flegal, and Robert Sproull --
707 | Xerox Parc.
708 *----------------------------------------------------------------------------*/
710 void
711 HGCurve(int *x,
712 int *y,
713 int numpoints)
715 float h[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS];
716 float d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS];
717 float t, t2, t3;
718 register int j;
719 register int k;
720 register int nx;
721 register int ny;
722 int lx, ly;
723 int length = 0;
725 lx = x[1];
726 ly = y[1];
727 tmove2(lx, ly);
730 * Solve for derivatives of the curve at each point separately for x and y
731 * (parametric).
733 Paramaterize(x, y, h, numpoints);
735 /* closed curve */
736 if ((x[1] == x[numpoints]) && (y[1] == y[numpoints])) {
737 PeriodicSpline(h, x, dx, d2x, d3x, numpoints);
738 PeriodicSpline(h, y, dy, d2y, d3y, numpoints);
739 } else {
740 NaturalEndSpline(h, x, dx, d2x, d3x, numpoints);
741 NaturalEndSpline(h, y, dy, d2y, d3y, numpoints);
745 * generate the curve using the above information and PointsPerInterval
746 * vectors between each specified knot.
749 for (j = 1; j < numpoints; ++j) {
750 if ((x[j] == x[j + 1]) && (y[j] == y[j + 1]))
751 continue;
752 for (k = 0; k <= PointsPerInterval; ++k) {
753 t = (float) k *h[j] / (float) PointsPerInterval;
754 t2 = t * t;
755 t3 = t * t * t;
756 nx = x[j] + (int) (t * dx[j] + t2 * d2x[j] / 2 + t3 * d3x[j] / 6);
757 ny = y[j] + (int) (t * dy[j] + t2 * d2y[j] / 2 + t3 * d3y[j] / 6);
758 HGtline(nx, ny);
759 if (length++ > LINELENGTH) {
760 length = 0;
761 printf("\\\n");
763 } /* end for k */
764 } /* end for j */
765 } /* end HGCurve */
768 /*----------------------------------------------------------------------------
769 | Routine: Paramaterize (xpoints, ypoints, hparams, num_points)
771 | Results: This routine calculates parameteric values for use in
772 | calculating curves. The parametric values are returned
773 | in the array h. The values are an approximation of
774 | cumulative arc lengths of the curve (uses cord length).
775 | For additional information, see paper cited below.
776 *----------------------------------------------------------------------------*/
778 void
779 Paramaterize(int x[],
780 int y[],
781 float h[],
782 int n)
784 register int dx;
785 register int dy;
786 register int i;
787 register int j;
788 float u[MAXPOINTS];
790 for (i = 1; i <= n; ++i) {
791 u[i] = 0;
792 for (j = 1; j < i; j++) {
793 dx = x[j + 1] - x[j];
794 dy = y[j + 1] - y[j];
795 /* Here was overflowing, so I changed it. */
796 /* u[i] += sqrt ((double) (dx * dx + dy * dy)); */
797 u[i] += hypot((double) dx, (double) dy);
800 for (i = 1; i < n; ++i)
801 h[i] = u[i + 1] - u[i];
802 } /* end Paramaterize */
805 /*----------------------------------------------------------------------------
806 | Routine: PeriodicSpline (h, z, dz, d2z, d3z, npoints)
808 | Results: This routine solves for the cubic polynomial to fit a spline
809 | curve to the the points specified by the list of values.
810 | The Curve generated is periodic. The algorithms for this
811 | curve are from the `Spline Curve Techniques' paper cited
812 | above.
813 *----------------------------------------------------------------------------*/
815 void
816 PeriodicSpline(float h[], /* paramaterization */
817 int z[], /* point list */
818 float dz[], /* to return the 1st derivative */
819 float d2z[], /* 2nd derivative */
820 float d3z[], /* 3rd derivative */
821 int npoints) /* number of valid points */
823 float d[MAXPOINTS];
824 float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
825 float c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS];
826 int i;
828 /* step 1 */
829 for (i = 1; i < npoints; ++i) {
830 deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0;
832 h[0] = h[npoints - 1];
833 deltaz[0] = deltaz[npoints - 1];
835 /* step 2 */
836 for (i = 1; i < npoints - 1; ++i) {
837 d[i] = deltaz[i + 1] - deltaz[i];
839 d[0] = deltaz[1] - deltaz[0];
841 /* step 3a */
842 a[1] = 2 * (h[0] + h[1]);
843 b[1] = d[0];
844 c[1] = h[0];
845 for (i = 2; i < npoints - 1; ++i) {
846 a[i] = 2 * (h[i - 1] + h[i]) -
847 pow((double) h[i - 1], (double) 2.0) / a[i - 1];
848 b[i] = d[i - 1] - h[i - 1] * b[i - 1] / a[i - 1];
849 c[i] = -h[i - 1] * c[i - 1] / a[i - 1];
852 /* step 3b */
853 r[npoints - 1] = 1;
854 s[npoints - 1] = 0;
855 for (i = npoints - 2; i > 0; --i) {
856 r[i] = -(h[i] * r[i + 1] + c[i]) / a[i];
857 s[i] = (6 * b[i] - h[i] * s[i + 1]) / a[i];
860 /* step 4 */
861 d2z[npoints - 1] = (6 * d[npoints - 2] - h[0] * s[1]
862 - h[npoints - 1] * s[npoints - 2])
863 / (h[0] * r[1] + h[npoints - 1] * r[npoints - 2]
864 + 2 * (h[npoints - 2] + h[0]));
865 for (i = 1; i < npoints - 1; ++i) {
866 d2z[i] = r[i] * d2z[npoints - 1] + s[i];
868 d2z[npoints] = d2z[1];
870 /* step 5 */
871 for (i = 1; i < npoints; ++i) {
872 dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6;
873 d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0;
875 } /* end PeriodicSpline */
878 /*----------------------------------------------------------------------------
879 | Routine: NaturalEndSpline (h, z, dz, d2z, d3z, npoints)
881 | Results: This routine solves for the cubic polynomial to fit a spline
882 | curve the the points specified by the list of values. The
883 | alogrithms for this curve are from the `Spline Curve
884 | Techniques' paper cited above.
885 *----------------------------------------------------------------------------*/
887 void
888 NaturalEndSpline(float h[], /* parameterization */
889 int z[], /* Point list */
890 float dz[], /* to return the 1st derivative */
891 float d2z[], /* 2nd derivative */
892 float d3z[], /* 3rd derivative */
893 int npoints) /* number of valid points */
895 float d[MAXPOINTS];
896 float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
897 int i;
899 /* step 1 */
900 for (i = 1; i < npoints; ++i) {
901 deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0;
903 deltaz[0] = deltaz[npoints - 1];
905 /* step 2 */
906 for (i = 1; i < npoints - 1; ++i) {
907 d[i] = deltaz[i + 1] - deltaz[i];
909 d[0] = deltaz[1] - deltaz[0];
911 /* step 3 */
912 a[0] = 2 * (h[2] + h[1]);
913 b[0] = d[1];
914 for (i = 1; i < npoints - 2; ++i) {
915 a[i] = 2 * (h[i + 1] + h[i + 2]) -
916 pow((double) h[i + 1], (double) 2.0) / a[i - 1];
917 b[i] = d[i + 1] - h[i + 1] * b[i - 1] / a[i - 1];
920 /* step 4 */
921 d2z[npoints] = d2z[1] = 0;
922 for (i = npoints - 1; i > 1; --i) {
923 d2z[i] = (6 * b[i - 2] - h[i] * d2z[i + 1]) / a[i - 2];
926 /* step 5 */
927 for (i = 1; i < npoints; ++i) {
928 dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6;
929 d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0;
931 } /* end NaturalEndSpline */
934 /*----------------------------------------------------------------------------*
935 | Routine: change (x_position, y_position, visible_flag)
937 | Results: As HGtline passes from the invisible to visible (or vice
938 | versa) portion of a line, change is called to either draw
939 | the line, or initialize the beginning of the next one.
940 | Change calls line to draw segments if visible_flag is set
941 | (which means we're leaving a visible area).
942 *----------------------------------------------------------------------------*/
944 void
945 change(register int x,
946 register int y,
947 register int vis)
949 static int length = 0;
951 if (vis) { /* leaving a visible area, draw it. */
952 line(x, y);
953 if (length++ > LINELENGTH) {
954 length = 0;
955 printf("\\\n");
957 } else { /* otherwise, we're entering one, remember */
958 /* beginning */
959 tmove2(x, y);
964 /*----------------------------------------------------------------------------
965 | Routine: HGtline (xstart, ystart, xend, yend)
967 | Results: Draws a line from current position to (x1,y1) using line(x1,
968 | y1) to place individual segments of dotted or dashed lines.
969 *----------------------------------------------------------------------------*/
971 void
972 HGtline(int x1,
973 int y1)
975 register int x0 = lastx;
976 register int y0 = lasty;
977 register int dx;
978 register int dy;
979 register int oldcoord;
980 register int res1;
981 register int visible;
982 register int res2;
983 register int xinc;
984 register int yinc;
985 register int dotcounter;
987 if (linmod == SOLID) {
988 line(x1, y1);
989 return;
992 /* for handling different resolutions */
993 dotcounter = linmod << dotshifter;
995 xinc = 1;
996 yinc = 1;
997 if ((dx = x1 - x0) < 0) {
998 xinc = -xinc;
999 dx = -dx;
1001 if ((dy = y1 - y0) < 0) {
1002 yinc = -yinc;
1003 dy = -dy;
1005 res1 = 0;
1006 res2 = 0;
1007 visible = 0;
1008 if (dx >= dy) {
1009 oldcoord = y0;
1010 while (x0 != x1) {
1011 if ((x0 & dotcounter) && !visible) {
1012 change(x0, y0, 0);
1013 visible = 1;
1014 } else if (visible && !(x0 & dotcounter)) {
1015 change(x0 - xinc, oldcoord, 1);
1016 visible = 0;
1018 if (res1 > res2) {
1019 oldcoord = y0;
1020 res2 += dx - res1;
1021 res1 = 0;
1022 y0 += yinc;
1024 res1 += dy;
1025 x0 += xinc;
1027 } else {
1028 oldcoord = x0;
1029 while (y0 != y1) {
1030 if ((y0 & dotcounter) && !visible) {
1031 change(x0, y0, 0);
1032 visible = 1;
1033 } else if (visible && !(y0 & dotcounter)) {
1034 change(oldcoord, y0 - yinc, 1);
1035 visible = 0;
1037 if (res1 > res2) {
1038 oldcoord = x0;
1039 res2 += dy - res1;
1040 res1 = 0;
1041 x0 += xinc;
1043 res1 += dx;
1044 y0 += yinc;
1047 if (visible)
1048 change(x1, y1, 1);
1049 else
1050 change(x1, y1, 0);
1053 /* EOF */