From d318611dd6f23fcfedd50e9b9e24620b102ba96a Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 21:44:05 +0200 Subject: Adding upstream version 1.23.0. Signed-off-by: Daniel Baumann --- src/preproc/grn/hgraph.cpp | 1060 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1060 insertions(+) create mode 100644 src/preproc/grn/hgraph.cpp (limited to 'src/preproc/grn/hgraph.cpp') diff --git a/src/preproc/grn/hgraph.cpp b/src/preproc/grn/hgraph.cpp new file mode 100644 index 0000000..9ed81a4 --- /dev/null +++ b/src/preproc/grn/hgraph.cpp @@ -0,0 +1,1060 @@ +/* Last non-groff version: hgraph.c 1.14 (Berkeley) 84/11/27 + * + * This file contains the graphics routines for converting gremlin + * pictures to troff input. + */ + +#include "lib.h" + +#include "gprint.h" + +#define MAXVECT 40 +#define MAXPOINTS 200 +#define LINELENGTH 1 +#define PointsPerInterval 64 +#define pi 3.14159265358979324 +#define twopi (2.0 * pi) +#define len(a, b) groff_hypot((double)(b.x-a.x), \ + (double)(b.y-a.y)) + + +extern int dotshifter; /* for the length of dotted curves */ + +extern int style[]; /* line and character styles */ +extern double thick[]; +extern char *tfont[]; +extern int tsize[]; +extern int stipple_index[]; /* stipple font idx for stipples 0-16 */ +extern char *stipple; /* stipple type (cf or ug) */ + + +extern double troffscale; /* imports from main.c */ +extern double linethickness; +extern int linmod; +extern int lastx; +extern int lasty; +extern int lastyline; +extern int ytop; +extern int ybottom; +extern int xleft; +extern int xright; +extern enum E { + OUTLINE, FILL, BOTH +} polyfill; + +extern double adj1; +extern double adj2; +extern double adj3; +extern double adj4; +extern int res; + +void HGSetFont(int font, int size); +void HGPutText(int justify, POINT pnt, char *string); +void HGSetBrush(int mode); +void tmove2(int px, int py); +void doarc(POINT cp, POINT sp, int angle); +void tmove(POINT * ptr); +void cr(); +void drawwig(POINT * ptr, int type); +void HGtline(int x1, int y1); +void deltax(double x); +void deltay(double y); +void HGArc(int cx, int cy, int px, int py, int angle); +void picurve(int *x, int *y, int npts); +void HGCurve(int *x, int *y, int numpoints); +void Parameterize(int x[], int y[], double h[], int n); +void PeriodicSpline(double h[], int z[], + double dz[], double d2z[], double d3z[], + int npoints); +void NaturalEndSpline(double h[], int z[], + double dz[], double d2z[], double d3z[], + int npoints); + + + +/*--------------------------------------------------------------------* + | Routine: HGPrintElt (element_pointer, baseline) + | + | Results: Examines a picture element and calls the appropriate + | routine(s) to print them according to their type. After + | the picture is drawn, current position is (lastx,lasty). + *--------------------------------------------------------------------*/ + +void +HGPrintElt(ELT *element, + int /* baseline */) +{ + POINT *p1; + POINT *p2; + int length; + int graylevel; + + if (!DBNullelt(element) && !Nullpoint((p1 = element->ptlist))) { + /* p1 always has first point */ + if (TEXT(element->type)) { + HGSetFont(element->brushf, element->size); + switch (element->size) { + case 1: + p1->y += adj1; + break; + case 2: + p1->y += adj2; + break; + case 3: + p1->y += adj3; + break; + case 4: + p1->y += adj4; + break; + default: + break; + } + HGPutText(element->type, *p1, element->textpt); + } else { + if (element->brushf) /* if there is a brush, the */ + HGSetBrush(element->brushf); /* graphics need it set */ + + switch (element->type) { + + case ARC: + p2 = PTNextPoint(p1); + tmove(p2); + doarc(*p1, *p2, element->size); + cr(); + break; + + case CURVE: + length = 0; /* keep track of line length */ + drawwig(p1, CURVE); + cr(); + break; + + case BSPLINE: + length = 0; /* keep track of line length */ + drawwig(p1, BSPLINE); + cr(); + break; + + case VECTOR: + length = 0; /* keep track of line length so */ + tmove(p1); /* single lines don't get long */ + while (!Nullpoint((p1 = PTNextPoint(p1)))) { + HGtline((int) (p1->x * troffscale), + (int) (p1->y * troffscale)); + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } /* end while */ + cr(); + break; + + case POLYGON: + { + /* brushf = style of outline; size = color of fill: + * on first pass (polyfill=FILL), do the interior using 'P' + * unless size=0 + * on second pass (polyfill=OUTLINE), do the outline using a + * series of vectors. It might make more sense to use \D'p + * ...', but there is no uniform way to specify a 'fill + * character' that prints as 'no fill' on all output + * devices (and stipple fonts). + * If polyfill=BOTH, just use the \D'p ...' command. + */ + double firstx = p1->x; + double firsty = p1->y; + + length = 0; /* keep track of line length so */ + /* single lines don't get long */ + + if (polyfill == FILL || polyfill == BOTH) { + /* do the interior */ + char command = (polyfill == BOTH && element->brushf) + ? 'p' : 'P'; + + /* include outline, if there is one and */ + /* the -p flag was set */ + + /* switch based on what gremlin gives */ + switch (element->size) { + case 1: + graylevel = 1; + break; + case 3: + graylevel = 2; + break; + case 12: + graylevel = 3; + break; + case 14: + graylevel = 4; + break; + case 16: + graylevel = 5; + break; + case 19: + graylevel = 6; + break; + case 21: + graylevel = 7; + break; + case 23: + graylevel = 8; + break; + default: /* who's giving something else? */ + graylevel = NSTIPPLES; + break; + } + /* int graylevel = element->size; */ + + if (graylevel < 0) + break; + if (graylevel > NSTIPPLES) + graylevel = NSTIPPLES; + printf("\\D'Fg %.3f'", + double(1000 - stipple_index[graylevel]) / 1000.0); + cr(); + tmove(p1); + printf("\\D'%c", command); + + while (!Nullpoint((PTNextPoint(p1)))) { + p1 = PTNextPoint(p1); + deltax((double) p1->x); + deltay((double) p1->y); + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } /* end while */ + + /* close polygon if not done so by user */ + if ((firstx != p1->x) || (firsty != p1->y)) { + deltax((double) firstx); + deltay((double) firsty); + } + putchar('\''); + cr(); + break; + } + /* else polyfill == OUTLINE; only draw the outline */ + if (!(element->brushf)) + break; + length = 0; /* keep track of line length */ + tmove(p1); + + while (!Nullpoint((PTNextPoint(p1)))) { + p1 = PTNextPoint(p1); + HGtline((int) (p1->x * troffscale), + (int) (p1->y * troffscale)); + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } /* end while */ + + /* close polygon if not done so by user */ + if ((firstx != p1->x) || (firsty != p1->y)) { + HGtline((int) (firstx * troffscale), + (int) (firsty * troffscale)); + } + cr(); + break; + } /* end case POLYGON */ + } /* end switch */ + } /* end else Text */ + } /* end if */ +} /* end PrintElt */ + + +/*---------------------------------------------------------------------* + | Routine: HGPutText (justification, position_point, string) + | + | Results: Given the justification, a point to position with, and a + | string to put, HGPutText first sends the string into a + | diversion, moves to the positioning point, then outputs + | local vertical and horizontal motions as needed to + | justify the text. After all motions are done, the + | diversion is printed out. + *--------------------------------------------------------------------*/ + +void +HGPutText(int justify, + POINT pnt, + char *string) +{ + int savelasty = lasty; /* vertical motion for text is to be */ + /* ignored. Save current y here */ + + printf(".nr g8 \\n(.d\n"); /* save current vertical position. */ + printf(".ds g9 \""); /* define string containing the text. */ + while (*string) { /* put out the string */ + if (*string == '\\' && + *(string + 1) == '\\') { /* one character at a */ + printf("\\\\\\"); /* time replacing // */ + string++; /* by //// to prevent */ + } /* interpretation at */ + printf("%c", *(string++)); /* printout time */ + } + printf("\n"); + + tmove(&pnt); /* move to positioning point */ + + switch (justify) { + /* local vertical motions--the numbers here are used to be + somewhat compatible with gprint */ + case CENTLEFT: + case CENTCENT: + case CENTRIGHT: + printf("\\v'0.85n'"); /* down half */ + break; + + case TOPLEFT: + case TOPCENT: + case TOPRIGHT: + printf("\\v'1.7n'"); /* down whole */ + } + + switch (justify) { + /* local horizontal motions */ + case BOTCENT: + case CENTCENT: + case TOPCENT: + printf("\\h'-\\w'\\*(g9'u/2u'"); /* back half */ + break; + + case BOTRIGHT: + case CENTRIGHT: + case TOPRIGHT: + printf("\\h'-\\w'\\*(g9'u'"); /* back whole */ + } + + printf("\\&\\*(g9\n"); /* now print the text. */ + printf(".sp |\\n(g8u\n"); /* restore vertical position */ + lasty = savelasty; /* vertical position restored to */ + lastx = xleft; /* where it was before text, also */ + /* horizontal is at left */ +} /* end HGPutText */ + + +/*--------------------------------------------------------------------* + | Routine: doarc (center_point, start_point, angle) + | + | Results: Produces either drawarc command or a drawcircle command + | depending on the angle needed to draw through. + *--------------------------------------------------------------------*/ + +void +doarc(POINT cp, + POINT sp, + int angle) +{ + if (angle) /* arc with angle */ + HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale), + (int) (sp.x * troffscale), (int) (sp.y * troffscale), angle); + else /* a full circle (angle == 0) */ + HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale), + (int) (sp.x * troffscale), (int) (sp.y * troffscale), 0); +} + + +/*--------------------------------------------------------------------* + | Routine: HGSetFont (font_number, Point_size) + | + | Results: ALWAYS outputs a .ft and .ps directive to troff. This + | is done because someone may change stuff inside a text + | string. Changes thickness back to default thickness. + | Default thickness depends on font and point size. + *--------------------------------------------------------------------*/ + +void +HGSetFont(int font, + int size) +{ + printf(".ft %s\n" + ".ps %d\n", tfont[font - 1], tsize[size - 1]); + linethickness = DEFTHICK; +} + + +/*--------------------------------------------------------------------* + | Routine: HGSetBrush (line_mode) + | + | Results: Generates the troff commands to set up the line width + | and style of subsequent lines. Does nothing if no + | change is needed. + | + | Side Efct: Sets 'linmode' and 'linethickness'. + *--------------------------------------------------------------------*/ + +void +HGSetBrush(int mode) +{ + int printed = 0; + + if (linmod != style[--mode]) { + /* Groff doesn't understand \Ds, so we take it out */ + /* printf ("\\D's %du'", linmod = style[mode]); */ + linmod = style[mode]; + printed = 1; + } + if (linethickness != thick[mode]) { + linethickness = thick[mode]; + printf("\\h'-%.2fp'\\D't %.2fp'", linethickness, linethickness); + printed = 1; + } + if (printed) + cr(); +} + + +/*--------------------------------------------------------------------* + | Routine: deltax (x_destination) + | + | Results: Scales and outputs a number for delta x (with a leading + | space) given 'lastx' and x_destination. + | + | Side Efct: Resets 'lastx' to x_destination. + *--------------------------------------------------------------------*/ + +void +deltax(double x) +{ + int ix = (int) (x * troffscale); + + printf(" %du", ix - lastx); + lastx = ix; +} + + +/*--------------------------------------------------------------------* + | Routine: deltay (y_destination) + | + | Results: Scales and outputs a number for delta y (with a leading + | space) given 'lastyline' and y_destination. + | + | Side Efct: Resets 'lastyline' to y_destination. Since 'line' + | vertical motions don't affect 'page' ones, 'lasty' isn't + | updated. + *--------------------------------------------------------------------*/ + +void +deltay(double y) +{ + int iy = (int) (y * troffscale); + + printf(" %du", iy - lastyline); + lastyline = iy; +} + + +/*--------------------------------------------------------------------* + | Routine: tmove2 (px, py) + | + | Results: Produces horizontal and vertical moves for troff given + | the pair of points to move to and knowing the current + | position. Also puts out a horizontal move to start the + | line. This is a variation without the .sp command. + *--------------------------------------------------------------------*/ + +void +tmove2(int px, + int py) +{ + int dx; + int dy; + + if ((dy = py - lasty)) { + printf("\\v'%du'", dy); + } + lastyline = lasty = py; /* lasty is always set to current */ + if ((dx = px - lastx)) { + printf("\\h'%du'", dx); + lastx = px; + } +} + + +/*--------------------------------------------------------------------* + | Routine: tmove (point_pointer) + | + | Results: Produces horizontal and vertical moves for troff given + | the pointer of a point to move to and knowing the + | current position. Also puts out a horizontal move to + | start the line. + *--------------------------------------------------------------------*/ + +void +tmove(POINT * ptr) +{ + int ix = (int) (ptr->x * troffscale); + int iy = (int) (ptr->y * troffscale); + int dx; + int dy; + + if ((dy = iy - lasty)) { + printf(".sp %du\n", dy); + } + lastyline = lasty = iy; /* lasty is always set to current */ + if ((dx = ix - lastx)) { + printf("\\h'%du'", dx); + lastx = ix; + } +} + + +/*--------------------------------------------------------------------* + | Routine: cr ( ) + | + | Results: Ends off an input line. '.sp -1' is also added to + | counteract the vertical move done at the end of text + | lines. + | + | Side Efct: Sets 'lastx' to 'xleft' for troff's return to left + | margin. + *--------------------------------------------------------------------*/ + +void +cr() +{ + printf("\n.sp -1\n"); + lastx = xleft; +} + + +/*--------------------------------------------------------------------* + | Routine: line ( ) + | + | Results: Draws a single solid line to (x,y). + *--------------------------------------------------------------------*/ + +void +line(int px, + int py) +{ + printf("\\D'l"); + printf(" %du", px - lastx); + printf(" %du'", py - lastyline); + lastx = px; + lastyline = lasty = py; +} + + +/*--------------------------------------------------------------------* + | Routine: drawwig (ptr, type) + | + | Results: The point sequence found in the structure pointed by ptr + | is placed in integer arrays for further manipulation by + | the existing routing. With the corresponding type + | parameter, either picurve or HGCurve are called. + *--------------------------------------------------------------------*/ + +void +drawwig(POINT * ptr, + int type) +{ + int npts; /* point list index */ + int x[MAXPOINTS], y[MAXPOINTS]; /* point list */ + + for (npts = 1; !Nullpoint(ptr); ptr = PTNextPoint(ptr), npts++) { + x[npts] = (int) (ptr->x * troffscale); + y[npts] = (int) (ptr->y * troffscale); + } + if (--npts) { + if (type == CURVE) /* Use the 2 different types of curves */ + HGCurve(&x[0], &y[0], npts); + else + picurve(&x[0], &y[0], npts); + } +} + + +/*--------------------------------------------------------------------* + | Routine: HGArc (xcenter, ycenter, xstart, ystart, angle) + | + | Results: This routine plots an arc centered about (cx, cy) + | counter-clockwise starting from the point (px, py) + | through 'angle' degrees. If angle is 0, a full circle + | is drawn. It does so by creating a draw-path around the + | arc whose density of points depends on the size of the + | arc. + *--------------------------------------------------------------------*/ + +void +HGArc(int cx, + int cy, + int px, + int py, + int angle) +{ + double xs, ys, resolution, fullcircle; + int m; + int mask; + int extent; + int nx; + int ny; + int length; + double epsilon; + + xs = px - cx; + ys = py - cy; + + length = 0; + + resolution = (1.0 + groff_hypot(xs, ys) / res) * PointsPerInterval; + /* mask = (1 << (int) log10(resolution + 1.0)) - 1; */ + (void) frexp(resolution, &m); /* more elegant than log10 */ + for (mask = 1; mask < m; mask = mask << 1); + mask -= 1; + + epsilon = 1.0 / resolution; + fullcircle = (2.0 * pi) * resolution; + if (angle == 0) + extent = (int) fullcircle; + else + extent = (int) (angle * fullcircle / 360.0); + + HGtline(px, py); + while (--extent >= 0) { + xs += epsilon * ys; + nx = cx + (int) (xs + 0.5); + ys -= epsilon * xs; + ny = cy + (int) (ys + 0.5); + if (!(extent & mask)) { + HGtline(nx, ny); /* put out a point on circle */ + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } + } /* end for */ +} /* end HGArc */ + + +/*--------------------------------------------------------------------* + | Routine: picurve (xpoints, ypoints, num_of_points) + | + | Results: Draws a curve delimited by (not through) the line + | segments traced by (xpoints, ypoints) point list. This + | is the 'Pic'-style curve. + *--------------------------------------------------------------------*/ + +void +picurve(int *x, + int *y, + int npts) +{ + int nseg; /* effective resolution for each curve */ + int xp; /* current point (and temporary) */ + int yp; + int pxp, pyp; /* previous point (to make lines from) */ + int i; /* inner curve segment traverser */ + int length = 0; + double w; /* position factor */ + double t1, t2, t3; /* calculation temps */ + + if (x[1] == x[npts] && y[1] == y[npts]) { + x[0] = x[npts - 1]; /* if the lines' ends meet, make */ + y[0] = y[npts - 1]; /* sure the curve meets */ + x[npts + 1] = x[2]; + y[npts + 1] = y[2]; + } else { /* otherwise, make the ends of the */ + x[0] = x[1]; /* curve touch the ending points of */ + y[0] = y[1]; /* the line segments */ + x[npts + 1] = x[npts]; + y[npts + 1] = y[npts]; + } + + pxp = (x[0] + x[1]) / 2; /* make the last point pointers */ + pyp = (y[0] + y[1]) / 2; /* point to the start of the 1st line */ + tmove2(pxp, pyp); + + for (; npts--; x++, y++) { /* traverse the line segments */ + xp = x[0] - x[1]; + yp = y[0] - y[1]; + nseg = (int) groff_hypot((double) xp, (double) yp); + xp = x[1] - x[2]; + yp = y[1] - y[2]; + /* 'nseg' is the number of line */ + /* segments that will be drawn for */ + /* each curve segment. */ + nseg = (int) ((double) (nseg + (int) groff_hypot((double) xp, + (double) yp)) / + res * PointsPerInterval); + + for (i = 1; i < nseg; i++) { + w = (double) i / (double) nseg; + t1 = w * w; + t3 = t1 + 1.0 - (w + w); + t2 = 2.0 - (t3 + t1); + xp = (((int) (t1 * x[2] + t2 * x[1] + t3 * x[0])) + 1) / 2; + yp = (((int) (t1 * y[2] + t2 * y[1] + t3 * y[0])) + 1) / 2; + + HGtline(xp, yp); + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } + } +} + + +/*--------------------------------------------------------------------* + | Routine: HGCurve(xpoints, ypoints, num_points) + | + | Results: This routine generates a smooth curve through a set of + | points. The method used is the parametric spline curve + | on unit knot mesh described in 'Spline Curve Techniques' + | by Patrick Baudelaire, Robert Flegal, and Robert Sproull + | -- Xerox Parc. + *--------------------------------------------------------------------*/ + +void +HGCurve(int *x, + int *y, + int numpoints) +{ + double h[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS]; + double d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS]; + double t, t2, t3; + int j; + int k; + int nx; + int ny; + int lx, ly; + int length = 0; + + lx = x[1]; + ly = y[1]; + tmove2(lx, ly); + + /* + * Solve for derivatives of the curve at each point separately for x + * and y (parametric). + */ + Parameterize(x, y, h, numpoints); + + /* closed curve */ + if ((x[1] == x[numpoints]) && (y[1] == y[numpoints])) { + PeriodicSpline(h, x, dx, d2x, d3x, numpoints); + PeriodicSpline(h, y, dy, d2y, d3y, numpoints); + } else { + NaturalEndSpline(h, x, dx, d2x, d3x, numpoints); + NaturalEndSpline(h, y, dy, d2y, d3y, numpoints); + } + + /* + * Generate the curve using the above information and + * PointsPerInterval vectors between each specified knot. + */ + + for (j = 1; j < numpoints; ++j) { + if ((x[j] == x[j + 1]) && (y[j] == y[j + 1])) + continue; + for (k = 0; k <= PointsPerInterval; ++k) { + t = (double) k *h[j] / (double) PointsPerInterval; + t2 = t * t; + t3 = t * t * t; + nx = x[j] + (int) (t * dx[j] + t2 * d2x[j] / 2 + t3 * d3x[j] / 6); + ny = y[j] + (int) (t * dy[j] + t2 * d2y[j] / 2 + t3 * d3y[j] / 6); + HGtline(nx, ny); + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } /* end for k */ + } /* end for j */ +} /* end HGCurve */ + + +/*--------------------------------------------------------------------* + | Routine: Parameterize (xpoints, ypoints, hparams, num_points) + | + | Results: This routine calculates parametric values for use in + | calculating curves. The parametric values are returned + | in the array h. The values are an approximation of + | cumulative arc lengths of the curve (uses cord length). + | For additional information, see paper cited below. + *--------------------------------------------------------------------*/ + +void +Parameterize(int x[], + int y[], + double h[], + int n) +{ + int dx; + int dy; + int i; + int j; + double u[MAXPOINTS]; + + for (i = 1; i <= n; ++i) { + u[i] = 0; + for (j = 1; j < i; j++) { + dx = x[j + 1] - x[j]; + dy = y[j + 1] - y[j]; + /* Here was overflowing, so I changed it. */ + /* u[i] += sqrt ((double) (dx * dx + dy * dy)); */ + u[i] += groff_hypot((double) dx, (double) dy); + } + } + for (i = 1; i < n; ++i) + h[i] = u[i + 1] - u[i]; +} /* end Parameterize */ + + +/*--------------------------------------------------------------------* + | Routine: PeriodicSpline (h, z, dz, d2z, d3z, npoints) + | + | Results: This routine solves for the cubic polynomial to fit a + | spline curve to the points specified by the list of + | values. The curve generated is periodic. The + | algorithms for this curve are from the 'Spline Curve + | Techniques' paper cited above. + *--------------------------------------------------------------------*/ + +void +PeriodicSpline(double h[], /* parameterization */ + int z[], /* point list */ + double dz[], /* to return the 1st derivative */ + double d2z[], /* 2nd derivative */ + double d3z[], /* 3rd derivative */ + int npoints) /* number of valid points */ +{ + double d[MAXPOINTS]; + double deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS]; + double c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS]; + int i; + + /* step 1 */ + for (i = 1; i < npoints; ++i) { + deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0; + } + h[0] = h[npoints - 1]; + deltaz[0] = deltaz[npoints - 1]; + + /* step 2 */ + for (i = 1; i < npoints - 1; ++i) { + d[i] = deltaz[i + 1] - deltaz[i]; + } + d[0] = deltaz[1] - deltaz[0]; + + /* step 3a */ + a[1] = 2 * (h[0] + h[1]); + b[1] = d[0]; + c[1] = h[0]; + for (i = 2; i < npoints - 1; ++i) { + a[i] = 2 * (h[i - 1] + h[i]) - + pow((double) h[i - 1], (double) 2.0) / a[i - 1]; + b[i] = d[i - 1] - h[i - 1] * b[i - 1] / a[i - 1]; + c[i] = -h[i - 1] * c[i - 1] / a[i - 1]; + } + + /* step 3b */ + r[npoints - 1] = 1; + s[npoints - 1] = 0; + for (i = npoints - 2; i > 0; --i) { + r[i] = -(h[i] * r[i + 1] + c[i]) / a[i]; + s[i] = (6 * b[i] - h[i] * s[i + 1]) / a[i]; + } + + /* step 4 */ + d2z[npoints - 1] = (6 * d[npoints - 2] - h[0] * s[1] + - h[npoints - 1] * s[npoints - 2]) + / (h[0] * r[1] + h[npoints - 1] * r[npoints - 2] + + 2 * (h[npoints - 2] + h[0])); + for (i = 1; i < npoints - 1; ++i) { + d2z[i] = r[i] * d2z[npoints - 1] + s[i]; + } + d2z[npoints] = d2z[1]; + + /* step 5 */ + for (i = 1; i < npoints; ++i) { + dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6; + d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0; + } +} /* end PeriodicSpline */ + + +/*-------------------------------------------------------------------- + | Routine: NaturalEndSpline (h, z, dz, d2z, d3z, npoints) + | + | Results: This routine solves for the cubic polynomial to fit a + | spline curve the points specified by the list of values. + | The algorithms for this curve are from the 'Spline Curve + | Techniques' paper cited above. + *--------------------------------------------------------------------*/ + +void +NaturalEndSpline(double h[], /* parameterization */ + int z[], /* Point list */ + double dz[], /* to return the 1st derivative */ + double d2z[], /* 2nd derivative */ + double d3z[], /* 3rd derivative */ + int npoints) /* number of valid points */ +{ + double d[MAXPOINTS]; + double deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS]; + int i; + + /* step 1 */ + for (i = 1; i < npoints; ++i) { + deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0; + } + deltaz[0] = deltaz[npoints - 1]; + + /* step 2 */ + for (i = 1; i < npoints - 1; ++i) { + d[i] = deltaz[i + 1] - deltaz[i]; + } + d[0] = deltaz[1] - deltaz[0]; + + /* step 3 */ + a[0] = 2 * (h[2] + h[1]); + b[0] = d[1]; + for (i = 1; i < npoints - 2; ++i) { + a[i] = 2 * (h[i + 1] + h[i + 2]) - + pow((double) h[i + 1], (double) 2.0) / a[i - 1]; + b[i] = d[i + 1] - h[i + 1] * b[i - 1] / a[i - 1]; + } + + /* step 4 */ + d2z[npoints] = d2z[1] = 0; + for (i = npoints - 1; i > 1; --i) { + d2z[i] = (6 * b[i - 2] - h[i] * d2z[i + 1]) / a[i - 2]; + } + + /* step 5 */ + for (i = 1; i < npoints; ++i) { + dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6; + d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0; + } +} /* end NaturalEndSpline */ + + +/*--------------------------------------------------------------------* + | Routine: change (x_position, y_position, visible_flag) + | + | Results: As HGtline passes from the invisible to visible (or vice + | versa) portion of a line, change is called to either + | draw the line, or initialize the beginning of the next + | one. Change calls line to draw segments if visible_flag + | is set (which means we're leaving a visible area). + *--------------------------------------------------------------------*/ + +void +change(int x, + int y, + int vis) +{ + static int length = 0; + + if (vis) { /* leaving a visible area, draw it. */ + line(x, y); + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } else { /* otherwise entering one; remember */ + /* beginning */ + tmove2(x, y); + } +} + + +/*--------------------------------------------------------------------* + | Routine: HGtline (xstart, ystart, xend, yend) + | + | Results: Draws a line from current position to (x1,y1) using + | line(x1, y1) to place individual segments of dotted or + | dashed lines. + *--------------------------------------------------------------------*/ + +void +HGtline(int x_1, + int y_1) +{ + int x_0 = lastx; + int y_0 = lasty; + int dx; + int dy; + int oldcoord; + int res1; + int visible; + int res2; + int xinc; + int yinc; + int dotcounter; + + if (linmod == SOLID) { + line(x_1, y_1); + return; + } + + /* for handling different resolutions */ + dotcounter = linmod << dotshifter; + + xinc = 1; + yinc = 1; + if ((dx = x_1 - x_0) < 0) { + xinc = -xinc; + dx = -dx; + } + if ((dy = y_1 - y_0) < 0) { + yinc = -yinc; + dy = -dy; + } + res1 = 0; + res2 = 0; + visible = 0; + if (dx >= dy) { + oldcoord = y_0; + while (x_0 != x_1) { + if ((x_0 & dotcounter) && !visible) { + change(x_0, y_0, 0); + visible = 1; + } else if (visible && !(x_0 & dotcounter)) { + change(x_0 - xinc, oldcoord, 1); + visible = 0; + } + if (res1 > res2) { + oldcoord = y_0; + res2 += dx - res1; + res1 = 0; + y_0 += yinc; + } + res1 += dy; + x_0 += xinc; + } + } else { + oldcoord = x_0; + while (y_0 != y_1) { + if ((y_0 & dotcounter) && !visible) { + change(x_0, y_0, 0); + visible = 1; + } else if (visible && !(y_0 & dotcounter)) { + change(oldcoord, y_0 - yinc, 1); + visible = 0; + } + if (res1 > res2) { + oldcoord = x_0; + res2 += dy - res1; + res1 = 0; + x_0 += xinc; + } + res1 += dx; + y_0 += yinc; + } + } + if (visible) + change(x_1, y_1, 1); + else + change(x_1, y_1, 0); +} + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: -- cgit v1.2.3