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