/* 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 index 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, register 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(register int cx, register int cy, int px, int py, int angle); void picurve(register int *x, register int *y, int npts); void HGCurve(int *x, int *y, int numpoints); void Paramaterize(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 */) { register POINT *p1; register POINT *p2; register int length; register 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, register 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 where it */ lastx = xleft; /* 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 pointsize. *----------------------------------------------------------------------------*/ 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 `linethicknes'. *----------------------------------------------------------------------------*/ void HGSetBrush(int mode) { register 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) { register 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) { register 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) { register int dx; register 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) { register int ix = (int) (ptr->x * troffscale); register int iy = (int) (ptr->y * troffscale); register int dx; register 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) { register 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(register int cx, register int cy, int px, int py, int angle) { double xs, ys, resolution, fullcircle; int m; register int mask; register int extent; register int nx; register int ny; register int length; register 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); /* A bit 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(register int *x, register int *y, int npts) { register int nseg; /* effective resolution for each curve */ register int xp; /* current point (and temporary) */ register 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; register int j; register int k; register int nx; register 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). */ Paramaterize(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: Paramaterize (xpoints, ypoints, hparams, num_points) | | Results: This routine calculates parameteric 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 Paramaterize(int x[], int y[], double h[], int n) { register int dx; register int dy; register int i; register 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 Paramaterize */ /*---------------------------------------------------------------------------- | Routine: PeriodicSpline (h, z, dz, d2z, d3z, npoints) | | Results: This routine solves for the cubic polynomial to fit a spline | curve to the 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[], /* paramaterization */ 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 the points specified by the list of values. The | alogrithms 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(register int x, register int y, register 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, we're 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) { register int x_0 = lastx; register int y_0 = lasty; register int dx; register int dy; register int oldcoord; register int res1; register int visible; register int res2; register int xinc; register int yinc; register 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); } /* EOF */