1 /* @(#)graphics2.c 1.2 04/18/83 2 * 3 * Copyright -C- 1982 Barry S. Roitblat 4 * 5 * This file contains more routines for implementing the graphics primitives 6 * for the gremlin picture editor 7 * 8 * (Modified from software written by John Ousterhout for the caesar 9 * program) 10 */ 11 12 #include "gremlin.h" 13 #include "grem2.h" 14 15 /* imports from graphics1.c */ 16 17 extern GRsetlinestyle(), GRsetcharstyle(), GRsetcharsize(), 18 GRsetpos(), GRoutxy20(), GRSetRead(); 19 extern FILE *display; 20 extern int curx, cury, rmask, GrXMax, GrYMax, charysize, charxsize, descenders; 21 22 /* imports from main.c */ 23 24 extern error(); 25 26 /* The following is available to the outside world */ 27 28 int artmode; 29 30 GRVector(point1,point2,style) 31 POINT *point1,*point2; 32 int style; 33 { 34 GRsetlinestyle(style); 35 GRsetpos((int) point1->x,(int) point1->y); 36 putc('A',display); /* Draw vector absolute */ 37 GRoutxy20((int) point2->x,(int) point2->y); 38 curx = point2->x; 39 cury = point2->y; 40 (void) fflush(display); 41 } 42 43 44 static RelativeVector(x, y) 45 float x, y; 46 /* 47 * This routine outputs the proper character sequence to the AED 48 * to draw a vector in the current line style and from the current position 49 * to the point (x, y). 50 */ 51 52 { 53 int nextx, nexty; 54 55 nextx = (int) x; 56 nexty = (int) y; 57 58 putc('A', display); /* draw vector absolute */ 59 GRoutxy20(nextx, nexty); /* output coordinates */ 60 curx = nextx; 61 cury = nexty; 62 } /* end RelativeVector */ 63 64 65 #define pi 3.14159265357 66 #define log2_10 3.321915 67 68 int 69 GRArc(center,cpoint,angle,style) 70 POINT *center, *cpoint; 71 double angle; 72 int style; 73 { 74 double xs, ys, radius, resolution, t1, epsalon, fullcircle; 75 double degreesperpoint; 76 int i, extent; 77 POINT next; 78 79 xs = cpoint->x - center->x; 80 ys = cpoint->y - center->y; 81 if ((xs == 0) && (ys == 0)) /* radius = 0 */ 82 { 83 error("zero radius"); 84 return(-1); 85 } 86 87 /* calculate drawing parameters */ 88 89 radius = sqrt(xs * xs + ys * ys); 90 t1 = floor(log10(radius) * log2_10); 91 resolution = pow(2.0, t1); 92 epsalon = 1.0 / resolution; 93 fullcircle = ceil(2 * pi * resolution); 94 degreesperpoint = 360.0 / fullcircle; 95 96 if (angle == 0) 97 { 98 extent = fullcircle; 99 if ( (int) radius < 128) /* manageable by AED hardware */ 100 { 101 GRsetpos((int) center->x, (int) center->y); 102 GRsetlinestyle(style); 103 putc('O',display); /* draw circle */ 104 putc(((int) radius) &0377, display); 105 return(0); 106 } 107 } 108 else extent = angle/degreesperpoint; 109 110 GRsetpos((int) cpoint->x, (int) cpoint->y); 111 GRsetlinestyle(style); 112 for (i=0; i<extent; ++i) 113 { 114 xs -= epsalon * ys; 115 next.x = xs + center->x ; /* round */ 116 ys += epsalon * xs; 117 next.y = ys + center->y ; /* round */ 118 RelativeVector(next.x, next.y); 119 } /* end for */; 120 return(0); 121 } /* end GRArc */; 122 123 124 #define MAXPOINTS 200 125 126 static Paramaterize(x, y, h, n) 127 float x[MAXPOINTS], y[MAXPOINTS], h[MAXPOINTS]; 128 int n; 129 /* This routine calculates parameteric values for use in calculating 130 * curves. The parametric values are returned in the array u. The values 131 * are an approximation of cumulative arc lengths of the curve (uses cord 132 * length). For additional information, see paper cited below. 133 */ 134 135 { 136 int i,j; 137 float u[MAXPOINTS]; 138 139 for (i=1; i<=n; ++i) 140 { 141 u[i] = 0; 142 for (j=1; j<i; ++j) 143 { 144 u[i] += sqrt(pow((double) (x[j+1] - x[j]), (double) 2.0) 145 + pow((double) (y[j+1] - y[j]), (double) 2.0)); 146 } 147 } 148 for (i=1; i<n; ++i) 149 h[i] = u[i+1] - u[i]; 150 } /* end Paramaterize */ 151 152 static PeriodicSpline(h, z, dz, d2z, d3z, npoints) 153 float h[MAXPOINTS], z[MAXPOINTS]; /* Point list and paramaterization */ 154 float dz[MAXPOINTS]; /* to return the 1st derivative */ 155 float d2z[MAXPOINTS], d3z[MAXPOINTS]; /* 2nd and 3rd derivatives */ 156 int npoints; /* number of valid points */ 157 /* 158 * This routine solves for the cubic polynomial to fit a spline 159 * curve to the the points specified by the list of values. 160 * The Curve generated is periodic. The alogrithms for this 161 * curve are from the "Spline Curve Techniques" paper cited below. 162 */ 163 164 { 165 float d[MAXPOINTS]; 166 float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS]; 167 float c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS]; 168 int i; 169 170 /* step 1 */ 171 for (i=1; i<npoints; ++i) 172 { 173 if (h[i] != 0) 174 deltaz[i] = (z[i+1] - z[i]) / h[i]; 175 else 176 deltaz[i] = 0; 177 } 178 h[0] = h[npoints-1]; 179 deltaz[0] = deltaz[npoints-1]; 180 181 /* step 2 */ 182 for (i=1; i<npoints-1; ++i) 183 { 184 d[i] = deltaz[i+1] - deltaz[i]; 185 } 186 d[0] = deltaz[1] - deltaz[0]; 187 188 /* step 3a */ 189 a[1] = 2 * (h[0] + h[1]); 190 if (a[1] == 0) return(-1); /* 3 consecutive knots at same point */ 191 b[1] = d[0]; 192 c[1] = h[0]; 193 for (i=2; i<npoints-1; ++i) 194 { 195 a[i] = 2 * (h[i-1] + h[i]) - pow((double) h[i-1], (double) 2.0) 196 / a[i-1]; 197 if (a[i] == 0) return(-1); /* 3 consec knots at same point */ 198 b[i] = d[i-1] - h[i-1] * b[i-1]/a[i-1]; 199 c[i] = -h[i-1] * c[i-1]/a[i-1]; 200 } 201 202 /* step 3b */ 203 r[npoints-1] = 1; 204 s[npoints-1] = 0; 205 for (i=npoints-2; i>0; --i) 206 { 207 r[i] = -(h[i] * r[i+1] + c[i])/a[i]; 208 s[i] = (6 * b[i] - h[i] * s[i+1])/a[i]; 209 } 210 211 /* step 4 */ 212 d2z[npoints-1] = (6 * d[npoints-2] - h[0] * s[1] 213 - h[npoints-1] * s[npoints-2]) 214 / (h[0] * r[1] + h[npoints-1] * r[npoints-2] 215 + 2 * (h[npoints-2] + h[0])); 216 for (i=1; i<npoints-1; ++i) 217 { 218 d2z[i] = r[i] * d2z[npoints-1] + s[i]; 219 } 220 d2z[npoints] = d2z[1]; 221 222 /* step 5 */ 223 for (i=1; i<npoints; ++i) 224 { 225 dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i+1])/6; 226 if (h[i] != 0) 227 d3z[i] = (d2z[i+1] - d2z[i])/h[i]; 228 else 229 d3z[i] = 0; 230 } 231 return(0); 232 } /* end PeriodicSpline */ 233 234 235 static NaturalEndSpline(h, z, dz, d2z, d3z, npoints) 236 float h[MAXPOINTS], z[MAXPOINTS]; /* Point list and parameterization */ 237 float dz[MAXPOINTS]; /* to return the 1st derivative */ 238 float d2z[MAXPOINTS], d3z[MAXPOINTS]; /* 2nd and 3rd derivatives */ 239 int npoints; /* number of valid points */ 240 /* 241 * This routine solves for the cubic polynomial to fit a spline 242 * curve the the points specified by the list of values. The alogrithms for 243 * this curve are from the "Spline Curve Techniques" paper cited below. 244 */ 245 246 { 247 float d[MAXPOINTS]; 248 float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS]; 249 int i; 250 251 /* step 1 */ 252 for (i=1; i<npoints; ++i) 253 { 254 if (h[i] != 0) 255 deltaz[i] = (z[i+1] - z[i]) / h[i]; 256 else 257 deltaz[i] = 0; 258 } 259 deltaz[0] = deltaz[npoints-1]; 260 261 /* step 2 */ 262 for (i=1; i<npoints-1; ++i) 263 { 264 d[i] = deltaz[i+1] - deltaz[i]; 265 } 266 d[0] = deltaz[1] - deltaz[0]; 267 268 /* step 3 */ 269 a[0] = 2 * (h[2] + h[1]); 270 if (a[0] == 0) return(-1); /* 3 consec knots at same point */ 271 b[0] = d[1]; 272 for (i=1; i<npoints-2; ++i) 273 { 274 a[i] = 2 * (h[i+1] + h[i+2]) - pow((double) h[i+1],(double) 2.0) 275 / a[i-1]; 276 if (a[i] == 0) return(-1); /* 3 consec knots at same point */ 277 b[i] = d[i+1] - h[i+1] * b[i-1]/a[i-1]; 278 } 279 280 /* step 4 */ 281 d2z[npoints] = d2z[1] = 0; 282 for (i=npoints-1; i>1; --i) 283 { 284 d2z[i] = (6 * b[i-2] - h[i] *d2z[i+1])/a[i-2]; 285 } 286 287 /* step 5 */ 288 for (i=1; i<npoints; ++i) 289 { 290 dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i+1])/6; 291 if (h[i] != 0) 292 d3z[i] = (d2z[i+1] - d2z[i])/h[i]; 293 else 294 d3z[i] = 0; 295 } 296 return(0); 297 } /* end NaturalEndSpline */ 298 299 300 #define PointsPerInterval 16 301 302 GRCurve(pointlist,style) 303 POINT *pointlist; 304 int style; 305 /* 306 * This routine generates a smooth curve through a set of points. The 307 * method used is the parametric spline curve on unit knot mesh described 308 * in "Spline Curve Techniques" by Patrick Baudelaire, Robert Flegal, and 309 * Robert Sproull -- Xerox Parc. 310 */ 311 { 312 float h[MAXPOINTS]; 313 float x[MAXPOINTS], y[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS]; 314 float d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS]; 315 float t, t2, t3, xinter, yinter; 316 POINT *ptr; 317 int numpoints, i, j, k, stat; 318 319 GRsetlinestyle(style); 320 GRsetpos((int) pointlist->x, (int) pointlist->y); 321 322 /* Copy point list to array for easier access */ 323 324 ptr = pointlist; 325 for (i=1; (!Nullpoint(ptr)); ++i) 326 { 327 x[i] = ptr->x; 328 y[i] = ptr->y; 329 if (i >= MAXPOINTS - 1) break; 330 ptr = PTNextPoint(ptr); 331 } 332 333 /* Solve for derivatives of the curve at each point 334 * separately for x and y (parametric). 335 */ 336 numpoints = i - 1; 337 Paramaterize(x, y, h, numpoints); 338 stat = 0; 339 if ((x[1] == x[numpoints]) && (y[1] == y[numpoints]))/* closed curve */ 340 { 341 stat |= PeriodicSpline(h, x, dx, d2x, d3x, numpoints); 342 stat |= PeriodicSpline(h, y, dy, d2y, d3y, numpoints); 343 } 344 else 345 { 346 stat |= NaturalEndSpline(h, x, dx, d2x, d3x, numpoints); 347 stat |= NaturalEndSpline(h, y, dy, d2y, d3y, numpoints); 348 } 349 if (stat != 0) return(-1); /* error occurred */ 350 351 /* generate the curve using the above information and 352 * PointsPerInterval vectors between each specified knot. 353 */ 354 355 for (j=1; j<numpoints; ++j) 356 { 357 for (k=0; k<=PointsPerInterval; ++k) 358 { 359 t = (float) k * h[j] / (float) PointsPerInterval; 360 t2 = t * t; 361 t3 = t * t * t; 362 xinter = x[j] + t * dx[j] + t2 * d2x[j]/2.0 363 + t3 * d3x[j]/6.0; 364 yinter = y[j] + t * dy[j] + t2 * d2y[j]/2.0 365 + t3 * d3y[j]/6.0; 366 RelativeVector(xinter, yinter); 367 } /* end for k */ 368 } /* end for j */ 369 return(0); 370 } /* end GRCurve */ 371 372 373 374 GRPutText(justify,point1,font,size,string,pos) 375 int justify, font, size; 376 POINT *point1, *pos; 377 char string[]; 378 { 379 int length, nchars, i; 380 381 GRsetcharstyle(font); 382 GRsetcharsize(size); 383 nchars = strlen(string); 384 length = nchars * charxsize ; 385 switch (justify) 386 { 387 case BOTLEFT: pos->x = point1->x; 388 pos->y = point1->y; 389 break; 390 case BOTCENT: pos->x = point1->x - (length/2); 391 pos->y = point1->y; 392 break; 393 case BOTRIGHT: pos->x = point1->x - length; 394 pos->y = point1->y; 395 break; 396 case CENTLEFT: pos->x = point1->x; 397 pos->y = point1->y - (charysize/2); 398 break; 399 case CENTCENT: pos->x = point1->x - (length/2); 400 pos->y = point1->y - (charysize/2); 401 break; 402 case CENTRIGHT: pos->x = point1->x - length; 403 pos->y = point1->y - (charysize/2); 404 break; 405 case TOPLEFT: pos->x = point1->x; 406 pos->y = point1->y - charysize + descenders; 407 break; 408 case TOPCENT: pos->x = point1->x - (length/2); 409 pos->y = point1->y - charysize + descenders; 410 break; 411 case TOPRIGHT: pos->x = point1->x - length; 412 pos->y = point1->y - charysize + descenders; 413 break; 414 } 415 if ((pos->x < 0) || ((pos->x + length - charxsize) > GrXMax)) 416 { 417 TxPutMsg("\7string too long"); 418 } 419 if (( pos->y + charysize ) > GrYMax ) 420 pos->y = GrYMax - charysize; 421 if (pos->y < 0) pos->y = 0; 422 GRsetpos((int) pos->x, (int) pos->y); 423 putc('\6',display); /* enter text mode */ 424 for ( i=0; i<nchars; ++i ) 425 { 426 putc(string[i],display); 427 } 428 fputs("\33Q", display); /* re-enter graphics mode */ 429 GRoutxy20(curx,cury); 430 (void) fflush(display); 431 } /* end PutText */; 432 433 434 GRClear(mask) 435 int mask; 436 /* 437 * This routine clears the memory planes enabled by mask. 438 * The read mask is first set to avoid an annoying flash when the 439 * clear is performed. 440 */ 441 442 { 443 int savemask; 444 445 savemask = rmask; 446 GRSetRead(rmask & ~mask); 447 GRsetwmask(mask); 448 putc('~',display); 449 GRSetRead(savemask); 450 (void) fflush(display); 451 } /* end GRClear */ 452 453 GRDisplayPoint(x, y, number, mark) 454 int x, y, number, mark; 455 /* 456 * This routine displays a point on the screen. The point is 457 * displayed as a '+' centered about the point (x,y) and followed by 458 * number. 459 */ 460 461 { 462 POINT p1, p2; 463 int psize; 464 465 if (artmode) psize = 1; 466 else psize = halfpoint; 467 GRsetwmask(pointmask); 468 p1.y = p2.y = y; 469 p1.x = x - psize; 470 p2.x = x + psize; 471 GRVector(&p1, &p2, mark); 472 p1.x = p2.x = x; 473 p1.y = y - psize; 474 p2.y = y + psize; 475 GRVector(&p1, &p2, mark); 476 if ( !artmode ) 477 { 478 GRsetcharsize(pointchar); 479 GRsetpos( (x + numspace), (y - charysize/2 + 1) ); 480 fprintf(display,"\6%d\33",number); 481 } 482 GRsetpos(x, y); 483 (void) fflush(display); 484 } /* end DisplayPoint */ 485 486 487 488 GRErasePoint(x, y, number) 489 int x, y, number; 490 /* 491 * This routine erases the specified point by redrawing it in the 492 * background color 493 */ 494 495 { 496 GRDisplayPoint(x, y, number, eraseany); 497 } /* end ErasePoint */ 498 499 500 GRBlankPoints() 501 /* 502 * This routine clears all displayed points by clearing 503 * the memory plane they are written on. 504 */ 505 506 { 507 GRClear(pointmask); 508 } /* end BlankPoints */ 509