1114402Sru/* Last non-groff version: hgraph.c 1.14 (Berkeley) 84/11/27 2114402Sru * 3114402Sru * This file contains the graphics routines for converting gremlin pictures 4114402Sru * to troff input. 5114402Sru */ 6114402Sru 7114402Sru#include "lib.h" 8114402Sru 9114402Sru#include "gprint.h" 10114402Sru 11114402Sru#define MAXVECT 40 12114402Sru#define MAXPOINTS 200 13114402Sru#define LINELENGTH 1 14114402Sru#define PointsPerInterval 64 15114402Sru#define pi 3.14159265358979324 16114402Sru#define twopi (2.0 * pi) 17151497Sru#define len(a, b) groff_hypot((double)(b.x-a.x), (double)(b.y-a.y)) 18114402Sru 19114402Sru 20114402Sruextern int dotshifter; /* for the length of dotted curves */ 21114402Sru 22114402Sruextern int style[]; /* line and character styles */ 23114402Sruextern double thick[]; 24114402Sruextern char *tfont[]; 25114402Sruextern int tsize[]; 26114402Sruextern int stipple_index[]; /* stipple font index for stipples 0 - 16 */ 27114402Sruextern char *stipple; /* stipple type (cf or ug) */ 28114402Sru 29114402Sru 30114402Sruextern double troffscale; /* imports from main.c */ 31114402Sruextern double linethickness; 32114402Sruextern int linmod; 33114402Sruextern int lastx; 34114402Sruextern int lasty; 35114402Sruextern int lastyline; 36114402Sruextern int ytop; 37114402Sruextern int ybottom; 38114402Sruextern int xleft; 39114402Sruextern int xright; 40151497Sruextern enum E { 41114402Sru OUTLINE, FILL, BOTH 42114402Sru} polyfill; 43114402Sru 44114402Sruextern double adj1; 45114402Sruextern double adj2; 46114402Sruextern double adj3; 47114402Sruextern double adj4; 48114402Sruextern int res; 49114402Sru 50114402Sruvoid HGSetFont(int font, int size); 51114402Sruvoid HGPutText(int justify, POINT pnt, register char *string); 52114402Sruvoid HGSetBrush(int mode); 53114402Sruvoid tmove2(int px, int py); 54114402Sruvoid doarc(POINT cp, POINT sp, int angle); 55114402Sruvoid tmove(POINT * ptr); 56114402Sruvoid cr(); 57114402Sruvoid drawwig(POINT * ptr, int type); 58114402Sruvoid HGtline(int x1, int y1); 59151497Sruvoid deltax(double x); 60151497Sruvoid deltay(double y); 61114402Sruvoid HGArc(register int cx, register int cy, int px, int py, int angle); 62114402Sruvoid picurve(register int *x, register int *y, int npts); 63114402Sruvoid HGCurve(int *x, int *y, int numpoints); 64151497Sruvoid Paramaterize(int x[], int y[], double h[], int n); 65151497Sruvoid PeriodicSpline(double h[], int z[], 66151497Sru double dz[], double d2z[], double d3z[], 67114402Sru int npoints); 68151497Sruvoid NaturalEndSpline(double h[], int z[], 69151497Sru double dz[], double d2z[], double d3z[], 70114402Sru int npoints); 71114402Sru 72114402Sru 73114402Sru 74114402Sru/*----------------------------------------------------------------------------* 75114402Sru | Routine: HGPrintElt (element_pointer, baseline) 76114402Sru | 77114402Sru | Results: Examines a picture element and calls the appropriate 78114402Sru | routine(s) to print them according to their type. After the 79114402Sru | picture is drawn, current position is (lastx, lasty). 80114402Sru *----------------------------------------------------------------------------*/ 81114402Sru 82114402Sruvoid 83114402SruHGPrintElt(ELT *element, 84114402Sru int /* baseline */) 85114402Sru{ 86114402Sru register POINT *p1; 87114402Sru register POINT *p2; 88114402Sru register int length; 89114402Sru register int graylevel; 90114402Sru 91114402Sru if (!DBNullelt(element) && !Nullpoint((p1 = element->ptlist))) { 92114402Sru /* p1 always has first point */ 93114402Sru if (TEXT(element->type)) { 94114402Sru HGSetFont(element->brushf, element->size); 95114402Sru switch (element->size) { 96114402Sru case 1: 97114402Sru p1->y += adj1; 98114402Sru break; 99114402Sru case 2: 100114402Sru p1->y += adj2; 101114402Sru break; 102114402Sru case 3: 103114402Sru p1->y += adj3; 104114402Sru break; 105114402Sru case 4: 106114402Sru p1->y += adj4; 107114402Sru break; 108114402Sru default: 109114402Sru break; 110114402Sru } 111114402Sru HGPutText(element->type, *p1, element->textpt); 112114402Sru } else { 113114402Sru if (element->brushf) /* if there is a brush, the */ 114114402Sru HGSetBrush(element->brushf); /* graphics need it set */ 115114402Sru 116114402Sru switch (element->type) { 117114402Sru 118114402Sru case ARC: 119114402Sru p2 = PTNextPoint(p1); 120114402Sru tmove(p2); 121114402Sru doarc(*p1, *p2, element->size); 122114402Sru cr(); 123114402Sru break; 124114402Sru 125114402Sru case CURVE: 126114402Sru length = 0; /* keep track of line length */ 127114402Sru drawwig(p1, CURVE); 128114402Sru cr(); 129114402Sru break; 130114402Sru 131114402Sru case BSPLINE: 132114402Sru length = 0; /* keep track of line length */ 133114402Sru drawwig(p1, BSPLINE); 134114402Sru cr(); 135114402Sru break; 136114402Sru 137114402Sru case VECTOR: 138114402Sru length = 0; /* keep track of line length so */ 139114402Sru tmove(p1); /* single lines don't get long */ 140114402Sru while (!Nullpoint((p1 = PTNextPoint(p1)))) { 141114402Sru HGtline((int) (p1->x * troffscale), 142114402Sru (int) (p1->y * troffscale)); 143114402Sru if (length++ > LINELENGTH) { 144114402Sru length = 0; 145114402Sru printf("\\\n"); 146114402Sru } 147114402Sru } /* end while */ 148114402Sru cr(); 149114402Sru break; 150114402Sru 151114402Sru case POLYGON: 152114402Sru { 153114402Sru /* brushf = style of outline; size = color of fill: 154114402Sru * on first pass (polyfill=FILL), do the interior using 'P' 155114402Sru * unless size=0 156114402Sru * on second pass (polyfill=OUTLINE), do the outline using a series 157114402Sru * of vectors. It might make more sense to use \D'p ...', 158114402Sru * but there is no uniform way to specify a 'fill character' 159114402Sru * that prints as 'no fill' on all output devices (and 160114402Sru * stipple fonts). 161114402Sru * If polyfill=BOTH, just use the \D'p ...' command. 162114402Sru */ 163151497Sru double firstx = p1->x; 164151497Sru double firsty = p1->y; 165114402Sru 166114402Sru length = 0; /* keep track of line length so */ 167114402Sru /* single lines don't get long */ 168114402Sru 169114402Sru if (polyfill == FILL || polyfill == BOTH) { 170114402Sru /* do the interior */ 171114402Sru char command = (polyfill == BOTH && element->brushf) ? 'p' : 'P'; 172114402Sru 173114402Sru /* include outline, if there is one and */ 174114402Sru /* the -p flag was set */ 175114402Sru 176114402Sru /* switch based on what gremlin gives */ 177114402Sru switch (element->size) { 178114402Sru case 1: 179114402Sru graylevel = 1; 180114402Sru break; 181114402Sru case 3: 182114402Sru graylevel = 2; 183114402Sru break; 184114402Sru case 12: 185114402Sru graylevel = 3; 186114402Sru break; 187114402Sru case 14: 188114402Sru graylevel = 4; 189114402Sru break; 190114402Sru case 16: 191114402Sru graylevel = 5; 192114402Sru break; 193114402Sru case 19: 194114402Sru graylevel = 6; 195114402Sru break; 196114402Sru case 21: 197114402Sru graylevel = 7; 198114402Sru break; 199114402Sru case 23: 200114402Sru graylevel = 8; 201114402Sru break; 202114402Sru default: /* who's giving something else? */ 203114402Sru graylevel = NSTIPPLES; 204114402Sru break; 205114402Sru } 206114402Sru /* int graylevel = element->size; */ 207114402Sru 208114402Sru if (graylevel < 0) 209114402Sru break; 210114402Sru if (graylevel > NSTIPPLES) 211114402Sru graylevel = NSTIPPLES; 212114402Sru printf("\\D'Fg %.3f'", 213114402Sru double(1000 - stipple_index[graylevel]) / 1000.0); 214114402Sru cr(); 215114402Sru tmove(p1); 216114402Sru printf("\\D'%c", command); 217114402Sru 218114402Sru while (!Nullpoint((PTNextPoint(p1)))) { 219114402Sru p1 = PTNextPoint(p1); 220151497Sru deltax((double) p1->x); 221151497Sru deltay((double) p1->y); 222114402Sru if (length++ > LINELENGTH) { 223114402Sru length = 0; 224114402Sru printf("\\\n"); 225114402Sru } 226114402Sru } /* end while */ 227114402Sru 228114402Sru /* close polygon if not done so by user */ 229114402Sru if ((firstx != p1->x) || (firsty != p1->y)) { 230151497Sru deltax((double) firstx); 231151497Sru deltay((double) firsty); 232114402Sru } 233114402Sru putchar('\''); 234114402Sru cr(); 235114402Sru break; 236114402Sru } 237114402Sru /* else polyfill == OUTLINE; only draw the outline */ 238114402Sru if (!(element->brushf)) 239114402Sru break; 240114402Sru length = 0; /* keep track of line length */ 241114402Sru tmove(p1); 242114402Sru 243114402Sru while (!Nullpoint((PTNextPoint(p1)))) { 244114402Sru p1 = PTNextPoint(p1); 245114402Sru HGtline((int) (p1->x * troffscale), 246114402Sru (int) (p1->y * troffscale)); 247114402Sru if (length++ > LINELENGTH) { 248114402Sru length = 0; 249114402Sru printf("\\\n"); 250114402Sru } 251114402Sru } /* end while */ 252114402Sru 253114402Sru /* close polygon if not done so by user */ 254114402Sru if ((firstx != p1->x) || (firsty != p1->y)) { 255114402Sru HGtline((int) (firstx * troffscale), 256114402Sru (int) (firsty * troffscale)); 257114402Sru } 258114402Sru cr(); 259114402Sru break; 260114402Sru } /* end case POLYGON */ 261114402Sru } /* end switch */ 262114402Sru } /* end else Text */ 263114402Sru } /* end if */ 264114402Sru} /* end PrintElt */ 265114402Sru 266114402Sru 267114402Sru/*----------------------------------------------------------------------------* 268114402Sru | Routine: HGPutText (justification, position_point, string) 269114402Sru | 270114402Sru | Results: Given the justification, a point to position with, and a 271114402Sru | string to put, HGPutText first sends the string into a 272114402Sru | diversion, moves to the positioning point, then outputs 273114402Sru | local vertical and horizontal motions as needed to justify 274114402Sru | the text. After all motions are done, the diversion is 275114402Sru | printed out. 276114402Sru *----------------------------------------------------------------------------*/ 277114402Sru 278114402Sruvoid 279114402SruHGPutText(int justify, 280114402Sru POINT pnt, 281114402Sru register char *string) 282114402Sru{ 283114402Sru int savelasty = lasty; /* vertical motion for text is to be */ 284114402Sru /* ignored. Save current y here */ 285114402Sru 286114402Sru printf(".nr g8 \\n(.d\n"); /* save current vertical position. */ 287114402Sru printf(".ds g9 \""); /* define string containing the text. */ 288114402Sru while (*string) { /* put out the string */ 289114402Sru if (*string == '\\' && 290114402Sru *(string + 1) == '\\') { /* one character at a */ 291114402Sru printf("\\\\\\"); /* time replacing // */ 292114402Sru string++; /* by //// to prevent */ 293114402Sru } /* interpretation at */ 294114402Sru printf("%c", *(string++)); /* printout time */ 295114402Sru } 296114402Sru printf("\n"); 297114402Sru 298114402Sru tmove(&pnt); /* move to positioning point */ 299114402Sru 300114402Sru switch (justify) { 301114402Sru /* local vertical motions */ 302114402Sru /* (the numbers here are used to be somewhat compatible with gprint) */ 303114402Sru case CENTLEFT: 304114402Sru case CENTCENT: 305114402Sru case CENTRIGHT: 306114402Sru printf("\\v'0.85n'"); /* down half */ 307114402Sru break; 308114402Sru 309114402Sru case TOPLEFT: 310114402Sru case TOPCENT: 311114402Sru case TOPRIGHT: 312114402Sru printf("\\v'1.7n'"); /* down whole */ 313114402Sru } 314114402Sru 315114402Sru switch (justify) { 316114402Sru /* local horizontal motions */ 317114402Sru case BOTCENT: 318114402Sru case CENTCENT: 319114402Sru case TOPCENT: 320114402Sru printf("\\h'-\\w'\\*(g9'u/2u'"); /* back half */ 321114402Sru break; 322114402Sru 323114402Sru case BOTRIGHT: 324114402Sru case CENTRIGHT: 325114402Sru case TOPRIGHT: 326114402Sru printf("\\h'-\\w'\\*(g9'u'"); /* back whole */ 327114402Sru } 328114402Sru 329114402Sru printf("\\&\\*(g9\n"); /* now print the text. */ 330114402Sru printf(".sp |\\n(g8u\n"); /* restore vertical position */ 331114402Sru lasty = savelasty; /* vertical position restored to where it */ 332114402Sru lastx = xleft; /* was before text, also horizontal is at */ 333114402Sru /* left */ 334114402Sru} /* end HGPutText */ 335114402Sru 336114402Sru 337114402Sru/*----------------------------------------------------------------------------* 338114402Sru | Routine: doarc (center_point, start_point, angle) 339114402Sru | 340114402Sru | Results: Produces either drawarc command or a drawcircle command 341114402Sru | depending on the angle needed to draw through. 342114402Sru *----------------------------------------------------------------------------*/ 343114402Sru 344114402Sruvoid 345114402Srudoarc(POINT cp, 346114402Sru POINT sp, 347114402Sru int angle) 348114402Sru{ 349114402Sru if (angle) /* arc with angle */ 350114402Sru HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale), 351114402Sru (int) (sp.x * troffscale), (int) (sp.y * troffscale), angle); 352114402Sru else /* a full circle (angle == 0) */ 353114402Sru HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale), 354114402Sru (int) (sp.x * troffscale), (int) (sp.y * troffscale), 0); 355114402Sru} 356114402Sru 357114402Sru 358114402Sru/*----------------------------------------------------------------------------* 359114402Sru | Routine: HGSetFont (font_number, Point_size) 360114402Sru | 361114402Sru | Results: ALWAYS outputs a .ft and .ps directive to troff. This is 362114402Sru | done because someone may change stuff inside a text string. 363114402Sru | Changes thickness back to default thickness. Default 364114402Sru | thickness depends on font and pointsize. 365114402Sru *----------------------------------------------------------------------------*/ 366114402Sru 367114402Sruvoid 368114402SruHGSetFont(int font, 369114402Sru int size) 370114402Sru{ 371114402Sru printf(".ft %s\n" 372114402Sru ".ps %d\n", tfont[font - 1], tsize[size - 1]); 373114402Sru linethickness = DEFTHICK; 374114402Sru} 375114402Sru 376114402Sru 377114402Sru/*----------------------------------------------------------------------------* 378114402Sru | Routine: HGSetBrush (line_mode) 379114402Sru | 380114402Sru | Results: Generates the troff commands to set up the line width and 381114402Sru | style of subsequent lines. Does nothing if no change is 382114402Sru | needed. 383114402Sru | 384114402Sru | Side Efct: Sets `linmode' and `linethicknes'. 385114402Sru *----------------------------------------------------------------------------*/ 386114402Sru 387114402Sruvoid 388114402SruHGSetBrush(int mode) 389114402Sru{ 390114402Sru register int printed = 0; 391114402Sru 392114402Sru if (linmod != style[--mode]) { 393114402Sru /* Groff doesn't understand \Ds, so we take it out */ 394114402Sru /* printf ("\\D's %du'", linmod = style[mode]); */ 395114402Sru linmod = style[mode]; 396114402Sru printed = 1; 397114402Sru } 398114402Sru if (linethickness != thick[mode]) { 399114402Sru linethickness = thick[mode]; 400114402Sru printf("\\h'-%.2fp'\\D't %.2fp'", linethickness, linethickness); 401114402Sru printed = 1; 402114402Sru } 403114402Sru if (printed) 404114402Sru cr(); 405114402Sru} 406114402Sru 407114402Sru 408114402Sru/*----------------------------------------------------------------------------* 409151497Sru | Routine: deltax (x_destination) 410114402Sru | 411114402Sru | Results: Scales and outputs a number for delta x (with a leading 412114402Sru | space) given `lastx' and x_destination. 413114402Sru | 414114402Sru | Side Efct: Resets `lastx' to x_destination. 415114402Sru *----------------------------------------------------------------------------*/ 416114402Sru 417114402Sruvoid 418151497Srudeltax(double x) 419114402Sru{ 420114402Sru register int ix = (int) (x * troffscale); 421114402Sru 422114402Sru printf(" %du", ix - lastx); 423114402Sru lastx = ix; 424114402Sru} 425114402Sru 426114402Sru 427114402Sru/*----------------------------------------------------------------------------* 428151497Sru | Routine: deltay (y_destination) 429114402Sru | 430114402Sru | Results: Scales and outputs a number for delta y (with a leading 431114402Sru | space) given `lastyline' and y_destination. 432114402Sru | 433114402Sru | Side Efct: Resets `lastyline' to y_destination. Since `line' vertical 434114402Sru | motions don't affect `page' ones, `lasty' isn't updated. 435114402Sru *----------------------------------------------------------------------------*/ 436114402Sru 437114402Sruvoid 438151497Srudeltay(double y) 439114402Sru{ 440114402Sru register int iy = (int) (y * troffscale); 441114402Sru 442114402Sru printf(" %du", iy - lastyline); 443114402Sru lastyline = iy; 444114402Sru} 445114402Sru 446114402Sru 447114402Sru/*----------------------------------------------------------------------------* 448114402Sru | Routine: tmove2 (px, py) 449114402Sru | 450114402Sru | Results: Produces horizontal and vertical moves for troff given the 451114402Sru | pair of points to move to and knowing the current position. 452114402Sru | Also puts out a horizontal move to start the line. This is 453114402Sru | a variation without the .sp command. 454114402Sru *----------------------------------------------------------------------------*/ 455114402Sru 456114402Sruvoid 457114402Srutmove2(int px, 458114402Sru int py) 459114402Sru{ 460114402Sru register int dx; 461114402Sru register int dy; 462114402Sru 463114402Sru if ((dy = py - lasty)) { 464114402Sru printf("\\v'%du'", dy); 465114402Sru } 466114402Sru lastyline = lasty = py; /* lasty is always set to current */ 467114402Sru if ((dx = px - lastx)) { 468114402Sru printf("\\h'%du'", dx); 469114402Sru lastx = px; 470114402Sru } 471114402Sru} 472114402Sru 473114402Sru 474114402Sru/*----------------------------------------------------------------------------* 475114402Sru | Routine: tmove (point_pointer) 476114402Sru | 477114402Sru | Results: Produces horizontal and vertical moves for troff given the 478114402Sru | pointer of a point to move to and knowing the current 479114402Sru | position. Also puts out a horizontal move to start the 480114402Sru | line. 481114402Sru *----------------------------------------------------------------------------*/ 482114402Sru 483114402Sruvoid 484114402Srutmove(POINT * ptr) 485114402Sru{ 486114402Sru register int ix = (int) (ptr->x * troffscale); 487114402Sru register int iy = (int) (ptr->y * troffscale); 488114402Sru register int dx; 489114402Sru register int dy; 490114402Sru 491114402Sru if ((dy = iy - lasty)) { 492114402Sru printf(".sp %du\n", dy); 493114402Sru } 494114402Sru lastyline = lasty = iy; /* lasty is always set to current */ 495114402Sru if ((dx = ix - lastx)) { 496114402Sru printf("\\h'%du'", dx); 497114402Sru lastx = ix; 498114402Sru } 499114402Sru} 500114402Sru 501114402Sru 502114402Sru/*----------------------------------------------------------------------------* 503114402Sru | Routine: cr ( ) 504114402Sru | 505114402Sru | Results: Ends off an input line. `.sp -1' is also added to counteract 506114402Sru | the vertical move done at the end of text lines. 507114402Sru | 508114402Sru | Side Efct: Sets `lastx' to `xleft' for troff's return to left margin. 509114402Sru *----------------------------------------------------------------------------*/ 510114402Sru 511114402Sruvoid 512114402Srucr() 513114402Sru{ 514114402Sru printf("\n.sp -1\n"); 515114402Sru lastx = xleft; 516114402Sru} 517114402Sru 518114402Sru 519114402Sru/*----------------------------------------------------------------------------* 520114402Sru | Routine: line ( ) 521114402Sru | 522114402Sru | Results: Draws a single solid line to (x,y). 523114402Sru *----------------------------------------------------------------------------*/ 524114402Sru 525114402Sruvoid 526114402Sruline(int px, 527114402Sru int py) 528114402Sru{ 529114402Sru printf("\\D'l"); 530114402Sru printf(" %du", px - lastx); 531114402Sru printf(" %du'", py - lastyline); 532114402Sru lastx = px; 533114402Sru lastyline = lasty = py; 534114402Sru} 535114402Sru 536114402Sru 537114402Sru/*---------------------------------------------------------------------------- 538114402Sru | Routine: drawwig (ptr, type) 539114402Sru | 540114402Sru | Results: The point sequence found in the structure pointed by ptr is 541114402Sru | placed in integer arrays for further manipulation by the 542114402Sru | existing routing. With the corresponding type parameter, 543114402Sru | either picurve or HGCurve are called. 544114402Sru *----------------------------------------------------------------------------*/ 545114402Sru 546114402Sruvoid 547114402Srudrawwig(POINT * ptr, 548114402Sru int type) 549114402Sru{ 550114402Sru register int npts; /* point list index */ 551114402Sru int x[MAXPOINTS], y[MAXPOINTS]; /* point list */ 552114402Sru 553114402Sru for (npts = 1; !Nullpoint(ptr); ptr = PTNextPoint(ptr), npts++) { 554114402Sru x[npts] = (int) (ptr->x * troffscale); 555114402Sru y[npts] = (int) (ptr->y * troffscale); 556114402Sru } 557114402Sru if (--npts) { 558114402Sru if (type == CURVE) /* Use the 2 different types of curves */ 559114402Sru HGCurve(&x[0], &y[0], npts); 560114402Sru else 561114402Sru picurve(&x[0], &y[0], npts); 562114402Sru } 563114402Sru} 564114402Sru 565114402Sru 566114402Sru/*---------------------------------------------------------------------------- 567114402Sru | Routine: HGArc (xcenter, ycenter, xstart, ystart, angle) 568114402Sru | 569114402Sru | Results: This routine plots an arc centered about (cx, cy) counter 570114402Sru | clockwise starting from the point (px, py) through `angle' 571114402Sru | degrees. If angle is 0, a full circle is drawn. It does so 572114402Sru | by creating a draw-path around the arc whose density of 573114402Sru | points depends on the size of the arc. 574114402Sru *----------------------------------------------------------------------------*/ 575114402Sru 576114402Sruvoid 577114402SruHGArc(register int cx, 578114402Sru register int cy, 579114402Sru int px, 580114402Sru int py, 581114402Sru int angle) 582114402Sru{ 583114402Sru double xs, ys, resolution, fullcircle; 584114402Sru int m; 585114402Sru register int mask; 586114402Sru register int extent; 587114402Sru register int nx; 588114402Sru register int ny; 589114402Sru register int length; 590114402Sru register double epsilon; 591114402Sru 592114402Sru xs = px - cx; 593114402Sru ys = py - cy; 594114402Sru 595114402Sru length = 0; 596114402Sru 597151497Sru resolution = (1.0 + groff_hypot(xs, ys) / res) * PointsPerInterval; 598114402Sru /* mask = (1 << (int) log10(resolution + 1.0)) - 1; */ 599114402Sru (void) frexp(resolution, &m); /* A bit more elegant than log10 */ 600114402Sru for (mask = 1; mask < m; mask = mask << 1); 601114402Sru mask -= 1; 602114402Sru 603114402Sru epsilon = 1.0 / resolution; 604114402Sru fullcircle = (2.0 * pi) * resolution; 605114402Sru if (angle == 0) 606114402Sru extent = (int) fullcircle; 607114402Sru else 608114402Sru extent = (int) (angle * fullcircle / 360.0); 609114402Sru 610114402Sru HGtline(px, py); 611114402Sru while (--extent >= 0) { 612114402Sru xs += epsilon * ys; 613114402Sru nx = cx + (int) (xs + 0.5); 614114402Sru ys -= epsilon * xs; 615114402Sru ny = cy + (int) (ys + 0.5); 616114402Sru if (!(extent & mask)) { 617114402Sru HGtline(nx, ny); /* put out a point on circle */ 618114402Sru if (length++ > LINELENGTH) { 619114402Sru length = 0; 620114402Sru printf("\\\n"); 621114402Sru } 622114402Sru } 623114402Sru } /* end for */ 624114402Sru} /* end HGArc */ 625114402Sru 626114402Sru 627114402Sru/*---------------------------------------------------------------------------- 628114402Sru | Routine: picurve (xpoints, ypoints, num_of_points) 629114402Sru | 630114402Sru | Results: Draws a curve delimited by (not through) the line segments 631114402Sru | traced by (xpoints, ypoints) point list. This is the `Pic' 632114402Sru | style curve. 633114402Sru *----------------------------------------------------------------------------*/ 634114402Sru 635114402Sruvoid 636114402Srupicurve(register int *x, 637114402Sru register int *y, 638114402Sru int npts) 639114402Sru{ 640114402Sru register int nseg; /* effective resolution for each curve */ 641114402Sru register int xp; /* current point (and temporary) */ 642114402Sru register int yp; 643114402Sru int pxp, pyp; /* previous point (to make lines from) */ 644114402Sru int i; /* inner curve segment traverser */ 645114402Sru int length = 0; 646114402Sru double w; /* position factor */ 647114402Sru double t1, t2, t3; /* calculation temps */ 648114402Sru 649114402Sru if (x[1] == x[npts] && y[1] == y[npts]) { 650114402Sru x[0] = x[npts - 1]; /* if the lines' ends meet, make */ 651114402Sru y[0] = y[npts - 1]; /* sure the curve meets */ 652114402Sru x[npts + 1] = x[2]; 653114402Sru y[npts + 1] = y[2]; 654114402Sru } else { /* otherwise, make the ends of the */ 655114402Sru x[0] = x[1]; /* curve touch the ending points of */ 656114402Sru y[0] = y[1]; /* the line segments */ 657114402Sru x[npts + 1] = x[npts]; 658114402Sru y[npts + 1] = y[npts]; 659114402Sru } 660114402Sru 661114402Sru pxp = (x[0] + x[1]) / 2; /* make the last point pointers */ 662114402Sru pyp = (y[0] + y[1]) / 2; /* point to the start of the 1st line */ 663114402Sru tmove2(pxp, pyp); 664114402Sru 665114402Sru for (; npts--; x++, y++) { /* traverse the line segments */ 666114402Sru xp = x[0] - x[1]; 667114402Sru yp = y[0] - y[1]; 668151497Sru nseg = (int) groff_hypot((double) xp, (double) yp); 669114402Sru xp = x[1] - x[2]; 670114402Sru yp = y[1] - y[2]; 671114402Sru /* `nseg' is the number of line */ 672114402Sru /* segments that will be drawn for */ 673114402Sru /* each curve segment. */ 674151497Sru nseg = (int) ((double) (nseg + (int) groff_hypot((double) xp, (double) yp)) / 675114402Sru res * PointsPerInterval); 676114402Sru 677114402Sru for (i = 1; i < nseg; i++) { 678114402Sru w = (double) i / (double) nseg; 679114402Sru t1 = w * w; 680114402Sru t3 = t1 + 1.0 - (w + w); 681114402Sru t2 = 2.0 - (t3 + t1); 682114402Sru xp = (((int) (t1 * x[2] + t2 * x[1] + t3 * x[0])) + 1) / 2; 683114402Sru yp = (((int) (t1 * y[2] + t2 * y[1] + t3 * y[0])) + 1) / 2; 684114402Sru 685114402Sru HGtline(xp, yp); 686114402Sru if (length++ > LINELENGTH) { 687114402Sru length = 0; 688114402Sru printf("\\\n"); 689114402Sru } 690114402Sru } 691114402Sru } 692114402Sru} 693114402Sru 694114402Sru 695114402Sru/*---------------------------------------------------------------------------- 696114402Sru | Routine: HGCurve(xpoints, ypoints, num_points) 697114402Sru | 698114402Sru | Results: This routine generates a smooth curve through a set of 699114402Sru | points. The method used is the parametric spline curve on 700114402Sru | unit knot mesh described in `Spline Curve Techniques' by 701114402Sru | Patrick Baudelaire, Robert Flegal, and Robert Sproull -- 702114402Sru | Xerox Parc. 703114402Sru *----------------------------------------------------------------------------*/ 704114402Sru 705114402Sruvoid 706114402SruHGCurve(int *x, 707114402Sru int *y, 708114402Sru int numpoints) 709114402Sru{ 710151497Sru double h[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS]; 711151497Sru double d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS]; 712151497Sru double t, t2, t3; 713114402Sru register int j; 714114402Sru register int k; 715114402Sru register int nx; 716114402Sru register int ny; 717114402Sru int lx, ly; 718114402Sru int length = 0; 719114402Sru 720114402Sru lx = x[1]; 721114402Sru ly = y[1]; 722114402Sru tmove2(lx, ly); 723114402Sru 724114402Sru /* 725114402Sru * Solve for derivatives of the curve at each point separately for x and y 726114402Sru * (parametric). 727114402Sru */ 728114402Sru Paramaterize(x, y, h, numpoints); 729114402Sru 730114402Sru /* closed curve */ 731114402Sru if ((x[1] == x[numpoints]) && (y[1] == y[numpoints])) { 732114402Sru PeriodicSpline(h, x, dx, d2x, d3x, numpoints); 733114402Sru PeriodicSpline(h, y, dy, d2y, d3y, numpoints); 734114402Sru } else { 735114402Sru NaturalEndSpline(h, x, dx, d2x, d3x, numpoints); 736114402Sru NaturalEndSpline(h, y, dy, d2y, d3y, numpoints); 737114402Sru } 738114402Sru 739114402Sru /* 740114402Sru * generate the curve using the above information and PointsPerInterval 741114402Sru * vectors between each specified knot. 742114402Sru */ 743114402Sru 744114402Sru for (j = 1; j < numpoints; ++j) { 745114402Sru if ((x[j] == x[j + 1]) && (y[j] == y[j + 1])) 746114402Sru continue; 747114402Sru for (k = 0; k <= PointsPerInterval; ++k) { 748151497Sru t = (double) k *h[j] / (double) PointsPerInterval; 749114402Sru t2 = t * t; 750114402Sru t3 = t * t * t; 751114402Sru nx = x[j] + (int) (t * dx[j] + t2 * d2x[j] / 2 + t3 * d3x[j] / 6); 752114402Sru ny = y[j] + (int) (t * dy[j] + t2 * d2y[j] / 2 + t3 * d3y[j] / 6); 753114402Sru HGtline(nx, ny); 754114402Sru if (length++ > LINELENGTH) { 755114402Sru length = 0; 756114402Sru printf("\\\n"); 757114402Sru } 758114402Sru } /* end for k */ 759114402Sru } /* end for j */ 760114402Sru} /* end HGCurve */ 761114402Sru 762114402Sru 763114402Sru/*---------------------------------------------------------------------------- 764114402Sru | Routine: Paramaterize (xpoints, ypoints, hparams, num_points) 765114402Sru | 766114402Sru | Results: This routine calculates parameteric values for use in 767114402Sru | calculating curves. The parametric values are returned 768114402Sru | in the array h. The values are an approximation of 769114402Sru | cumulative arc lengths of the curve (uses cord length). 770114402Sru | For additional information, see paper cited below. 771114402Sru *----------------------------------------------------------------------------*/ 772114402Sru 773114402Sruvoid 774114402SruParamaterize(int x[], 775114402Sru int y[], 776151497Sru double h[], 777114402Sru int n) 778114402Sru{ 779114402Sru register int dx; 780114402Sru register int dy; 781114402Sru register int i; 782114402Sru register int j; 783151497Sru double u[MAXPOINTS]; 784114402Sru 785114402Sru for (i = 1; i <= n; ++i) { 786114402Sru u[i] = 0; 787114402Sru for (j = 1; j < i; j++) { 788114402Sru dx = x[j + 1] - x[j]; 789114402Sru dy = y[j + 1] - y[j]; 790114402Sru /* Here was overflowing, so I changed it. */ 791114402Sru /* u[i] += sqrt ((double) (dx * dx + dy * dy)); */ 792151497Sru u[i] += groff_hypot((double) dx, (double) dy); 793114402Sru } 794114402Sru } 795114402Sru for (i = 1; i < n; ++i) 796114402Sru h[i] = u[i + 1] - u[i]; 797114402Sru} /* end Paramaterize */ 798114402Sru 799114402Sru 800114402Sru/*---------------------------------------------------------------------------- 801114402Sru | Routine: PeriodicSpline (h, z, dz, d2z, d3z, npoints) 802114402Sru | 803114402Sru | Results: This routine solves for the cubic polynomial to fit a spline 804114402Sru | curve to the the points specified by the list of values. 805114402Sru | The Curve generated is periodic. The algorithms for this 806114402Sru | curve are from the `Spline Curve Techniques' paper cited 807114402Sru | above. 808114402Sru *----------------------------------------------------------------------------*/ 809114402Sru 810114402Sruvoid 811151497SruPeriodicSpline(double h[], /* paramaterization */ 812114402Sru int z[], /* point list */ 813151497Sru double dz[], /* to return the 1st derivative */ 814151497Sru double d2z[], /* 2nd derivative */ 815151497Sru double d3z[], /* 3rd derivative */ 816114402Sru int npoints) /* number of valid points */ 817114402Sru{ 818151497Sru double d[MAXPOINTS]; 819151497Sru double deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS]; 820151497Sru double c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS]; 821114402Sru int i; 822114402Sru 823114402Sru /* step 1 */ 824114402Sru for (i = 1; i < npoints; ++i) { 825114402Sru deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0; 826114402Sru } 827114402Sru h[0] = h[npoints - 1]; 828114402Sru deltaz[0] = deltaz[npoints - 1]; 829114402Sru 830114402Sru /* step 2 */ 831114402Sru for (i = 1; i < npoints - 1; ++i) { 832114402Sru d[i] = deltaz[i + 1] - deltaz[i]; 833114402Sru } 834114402Sru d[0] = deltaz[1] - deltaz[0]; 835114402Sru 836114402Sru /* step 3a */ 837114402Sru a[1] = 2 * (h[0] + h[1]); 838114402Sru b[1] = d[0]; 839114402Sru c[1] = h[0]; 840114402Sru for (i = 2; i < npoints - 1; ++i) { 841114402Sru a[i] = 2 * (h[i - 1] + h[i]) - 842114402Sru pow((double) h[i - 1], (double) 2.0) / a[i - 1]; 843114402Sru b[i] = d[i - 1] - h[i - 1] * b[i - 1] / a[i - 1]; 844114402Sru c[i] = -h[i - 1] * c[i - 1] / a[i - 1]; 845114402Sru } 846114402Sru 847114402Sru /* step 3b */ 848114402Sru r[npoints - 1] = 1; 849114402Sru s[npoints - 1] = 0; 850114402Sru for (i = npoints - 2; i > 0; --i) { 851114402Sru r[i] = -(h[i] * r[i + 1] + c[i]) / a[i]; 852114402Sru s[i] = (6 * b[i] - h[i] * s[i + 1]) / a[i]; 853114402Sru } 854114402Sru 855114402Sru /* step 4 */ 856114402Sru d2z[npoints - 1] = (6 * d[npoints - 2] - h[0] * s[1] 857114402Sru - h[npoints - 1] * s[npoints - 2]) 858114402Sru / (h[0] * r[1] + h[npoints - 1] * r[npoints - 2] 859114402Sru + 2 * (h[npoints - 2] + h[0])); 860114402Sru for (i = 1; i < npoints - 1; ++i) { 861114402Sru d2z[i] = r[i] * d2z[npoints - 1] + s[i]; 862114402Sru } 863114402Sru d2z[npoints] = d2z[1]; 864114402Sru 865114402Sru /* step 5 */ 866114402Sru for (i = 1; i < npoints; ++i) { 867114402Sru dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6; 868114402Sru d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0; 869114402Sru } 870114402Sru} /* end PeriodicSpline */ 871114402Sru 872114402Sru 873114402Sru/*---------------------------------------------------------------------------- 874114402Sru | Routine: NaturalEndSpline (h, z, dz, d2z, d3z, npoints) 875114402Sru | 876114402Sru | Results: This routine solves for the cubic polynomial to fit a spline 877114402Sru | curve the the points specified by the list of values. The 878114402Sru | alogrithms for this curve are from the `Spline Curve 879114402Sru | Techniques' paper cited above. 880114402Sru *----------------------------------------------------------------------------*/ 881114402Sru 882114402Sruvoid 883151497SruNaturalEndSpline(double h[], /* parameterization */ 884114402Sru int z[], /* Point list */ 885151497Sru double dz[], /* to return the 1st derivative */ 886151497Sru double d2z[], /* 2nd derivative */ 887151497Sru double d3z[], /* 3rd derivative */ 888114402Sru int npoints) /* number of valid points */ 889114402Sru{ 890151497Sru double d[MAXPOINTS]; 891151497Sru double deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS]; 892114402Sru int i; 893114402Sru 894114402Sru /* step 1 */ 895114402Sru for (i = 1; i < npoints; ++i) { 896114402Sru deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0; 897114402Sru } 898114402Sru deltaz[0] = deltaz[npoints - 1]; 899114402Sru 900114402Sru /* step 2 */ 901114402Sru for (i = 1; i < npoints - 1; ++i) { 902114402Sru d[i] = deltaz[i + 1] - deltaz[i]; 903114402Sru } 904114402Sru d[0] = deltaz[1] - deltaz[0]; 905114402Sru 906114402Sru /* step 3 */ 907114402Sru a[0] = 2 * (h[2] + h[1]); 908114402Sru b[0] = d[1]; 909114402Sru for (i = 1; i < npoints - 2; ++i) { 910114402Sru a[i] = 2 * (h[i + 1] + h[i + 2]) - 911114402Sru pow((double) h[i + 1], (double) 2.0) / a[i - 1]; 912114402Sru b[i] = d[i + 1] - h[i + 1] * b[i - 1] / a[i - 1]; 913114402Sru } 914114402Sru 915114402Sru /* step 4 */ 916114402Sru d2z[npoints] = d2z[1] = 0; 917114402Sru for (i = npoints - 1; i > 1; --i) { 918114402Sru d2z[i] = (6 * b[i - 2] - h[i] * d2z[i + 1]) / a[i - 2]; 919114402Sru } 920114402Sru 921114402Sru /* step 5 */ 922114402Sru for (i = 1; i < npoints; ++i) { 923114402Sru dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6; 924114402Sru d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0; 925114402Sru } 926114402Sru} /* end NaturalEndSpline */ 927114402Sru 928114402Sru 929114402Sru/*----------------------------------------------------------------------------* 930114402Sru | Routine: change (x_position, y_position, visible_flag) 931114402Sru | 932114402Sru | Results: As HGtline passes from the invisible to visible (or vice 933114402Sru | versa) portion of a line, change is called to either draw 934114402Sru | the line, or initialize the beginning of the next one. 935114402Sru | Change calls line to draw segments if visible_flag is set 936114402Sru | (which means we're leaving a visible area). 937114402Sru *----------------------------------------------------------------------------*/ 938114402Sru 939114402Sruvoid 940114402Sruchange(register int x, 941114402Sru register int y, 942114402Sru register int vis) 943114402Sru{ 944114402Sru static int length = 0; 945114402Sru 946114402Sru if (vis) { /* leaving a visible area, draw it. */ 947114402Sru line(x, y); 948114402Sru if (length++ > LINELENGTH) { 949114402Sru length = 0; 950114402Sru printf("\\\n"); 951114402Sru } 952114402Sru } else { /* otherwise, we're entering one, remember */ 953114402Sru /* beginning */ 954114402Sru tmove2(x, y); 955114402Sru } 956114402Sru} 957114402Sru 958114402Sru 959114402Sru/*---------------------------------------------------------------------------- 960114402Sru | Routine: HGtline (xstart, ystart, xend, yend) 961114402Sru | 962114402Sru | Results: Draws a line from current position to (x1,y1) using line(x1, 963114402Sru | y1) to place individual segments of dotted or dashed lines. 964114402Sru *----------------------------------------------------------------------------*/ 965114402Sru 966114402Sruvoid 967151497SruHGtline(int x_1, 968151497Sru int y_1) 969114402Sru{ 970151497Sru register int x_0 = lastx; 971151497Sru register int y_0 = lasty; 972114402Sru register int dx; 973114402Sru register int dy; 974114402Sru register int oldcoord; 975114402Sru register int res1; 976114402Sru register int visible; 977114402Sru register int res2; 978114402Sru register int xinc; 979114402Sru register int yinc; 980114402Sru register int dotcounter; 981114402Sru 982114402Sru if (linmod == SOLID) { 983151497Sru line(x_1, y_1); 984114402Sru return; 985114402Sru } 986114402Sru 987114402Sru /* for handling different resolutions */ 988114402Sru dotcounter = linmod << dotshifter; 989114402Sru 990114402Sru xinc = 1; 991114402Sru yinc = 1; 992151497Sru if ((dx = x_1 - x_0) < 0) { 993114402Sru xinc = -xinc; 994114402Sru dx = -dx; 995114402Sru } 996151497Sru if ((dy = y_1 - y_0) < 0) { 997114402Sru yinc = -yinc; 998114402Sru dy = -dy; 999114402Sru } 1000114402Sru res1 = 0; 1001114402Sru res2 = 0; 1002114402Sru visible = 0; 1003114402Sru if (dx >= dy) { 1004151497Sru oldcoord = y_0; 1005151497Sru while (x_0 != x_1) { 1006151497Sru if ((x_0 & dotcounter) && !visible) { 1007151497Sru change(x_0, y_0, 0); 1008114402Sru visible = 1; 1009151497Sru } else if (visible && !(x_0 & dotcounter)) { 1010151497Sru change(x_0 - xinc, oldcoord, 1); 1011114402Sru visible = 0; 1012114402Sru } 1013114402Sru if (res1 > res2) { 1014151497Sru oldcoord = y_0; 1015114402Sru res2 += dx - res1; 1016114402Sru res1 = 0; 1017151497Sru y_0 += yinc; 1018114402Sru } 1019114402Sru res1 += dy; 1020151497Sru x_0 += xinc; 1021114402Sru } 1022114402Sru } else { 1023151497Sru oldcoord = x_0; 1024151497Sru while (y_0 != y_1) { 1025151497Sru if ((y_0 & dotcounter) && !visible) { 1026151497Sru change(x_0, y_0, 0); 1027114402Sru visible = 1; 1028151497Sru } else if (visible && !(y_0 & dotcounter)) { 1029151497Sru change(oldcoord, y_0 - yinc, 1); 1030114402Sru visible = 0; 1031114402Sru } 1032114402Sru if (res1 > res2) { 1033151497Sru oldcoord = x_0; 1034114402Sru res2 += dy - res1; 1035114402Sru res1 = 0; 1036151497Sru x_0 += xinc; 1037114402Sru } 1038114402Sru res1 += dx; 1039151497Sru y_0 += yinc; 1040114402Sru } 1041114402Sru } 1042114402Sru if (visible) 1043151497Sru change(x_1, y_1, 1); 1044114402Sru else 1045151497Sru change(x_1, y_1, 0); 1046114402Sru} 1047114402Sru 1048114402Sru/* EOF */ 1049