summaryrefslogtreecommitdiffstats
path: root/src/preproc/grn/hgraph.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:44:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:44:05 +0000
commitd318611dd6f23fcfedd50e9b9e24620b102ba96a (patch)
tree8b9eef82ca40fdd5a8deeabf07572074c236095d /src/preproc/grn/hgraph.cpp
parentInitial commit. (diff)
downloadgroff-upstream/1.23.0.tar.xz
groff-upstream/1.23.0.zip
Adding upstream version 1.23.0.upstream/1.23.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/preproc/grn/hgraph.cpp')
-rw-r--r--src/preproc/grn/hgraph.cpp1060
1 files changed, 1060 insertions, 0 deletions
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: