1 /* @(#)dterm.c 1.12 (Berkeley) 05/17/84" 2 * 3 * Converts ditroff output to text on a terminal. It is NOT meant to 4 * produce readable output, but is to show one how one's paper is (in 5 * general) formatted - what will go where on which page. 6 * 7 * options: 8 * 9 * -hn set horizontal resolution to n (in characters per inch; 10 * default is 10.0). 11 * 12 * -vn set vertical resolution (default is 6.0). 13 * 14 * -ln set maximum output line-length to n (default is 79). 15 * 16 * -olist output page list - as in troff. 17 * 18 * -c continue at end of page. Default is to stop at the end 19 * of each page, print "dterm:" and wait for a command. 20 * Type ? to get a list of available commands. 21 * 22 * -m print margins. Default action is to cut printing area down 23 * to only the part of the page with information on it. 24 * 25 * -L put a form feed (^L) at the end of each page 26 * 27 * -w sets h = 20, v = 12, l = 131, also sets -c, -m and -L to allow 28 * for extra-wide printouts on the printer. 29 * 30 * -fxxx get special character definition file "xxx". Default is 31 * /usr/lib/font/devter/specfile. 32 */ 33 34 35 #include <stdio.h> 36 #include <ctype.h> 37 #include <math.h> 38 39 40 #define FATAL 1 41 #define PGWIDTH 266 /* WAY too big - for good measure */ 42 #define PGHEIGHT 220 43 #define LINELEN 78 44 #define SPECFILE "/usr/local/lib/font/devter/specfile" 45 46 #define hgoto(n) hpos = n 47 #define vgoto(n) vpos = n 48 #define hmot(n) hpos += n 49 #define vmot(n) vpos += n 50 51 #define sgn(n) ((n > 0) ? 1 : ((n < 0) ? -1 : 0)) 52 #define abs(n) ((n) >= 0 ? (n) : -(n)) 53 #define max(x,y) ((x) > (y) ? (x) : (y)) 54 #define min(x,y) ((x) < (y) ? (x) : (y)) 55 #define sqr(x) (long int)(x)*(x) 56 57 58 char SccsId [] = "@(#)dterm.c 1.12 (Berkeley) 05/17/84"; 59 60 char **spectab; /* here go the special characters */ 61 char *specfile = SPECFILE; /* place to look up special characters */ 62 char *malloc(); 63 char *operand(); 64 65 int keepon = 0; /* flags: Don't stop at the end of each page? */ 66 int clearsc = 0; /* Put out form feed at each page? */ 67 int output = 0; /* Do we do output at all? */ 68 int nolist = 0; /* Output page list if > 0 */ 69 int margin = 0; /* Print blank margins? */ 70 int olist[20]; /* pairs of page numbers */ 71 72 float hscale = 10.0; /* characters and lines per inch for output */ 73 float vscale = 6.0; /* device (defaults are for printer) */ 74 FILE *fp = stdin; /* input file pointer */ 75 76 char pagebuf[PGHEIGHT][PGWIDTH]; 77 int minh = PGWIDTH; 78 int maxh = 0; 79 int minv = PGHEIGHT; 80 int maxv = 0; 81 int linelen = LINELEN; 82 83 int hpos; /* horizontal position to be next (left = 0) */ 84 int vpos; /* current vertical position (down positive) */ 85 86 int np; /* number of pages seen */ 87 int npmax; /* high-water mark of np */ 88 int pgnum[40]; /* their actual numbers */ 89 long pgadr[40]; /* their seek addresses */ 90 91 int DP; /* step size for drawing */ 92 int maxdots = 3200; /* maximum number of dots in an object */ 93 94 95 96 main(argc, argv) 97 int argc; 98 char **argv; 99 { 100 while (--argc > 0 && **++argv == '-') { 101 switch ((*argv)[1]) { 102 case 'f': /* special character filepath */ 103 specfile = operand(&argc, &argv); 104 break; 105 case 'l': /* output line length */ 106 linelen = atoi(operand(&argc, &argv)) - 1; 107 break; 108 case 'h': /* horizontal scale (char/inch) */ 109 hscale = atof(operand(&argc, &argv)); 110 break; 111 case 'v': /* vertical scale (char/inch) */ 112 vscale = atof(operand(&argc, &argv)); 113 break; 114 case 'o': /* output list */ 115 outlist(operand(&argc, &argv)); 116 break; 117 case 'c': /* continue at endofpage */ 118 keepon = !keepon; 119 break; 120 case 'm': /* print margins */ 121 margin = !margin; 122 break; 123 case 'L': /* form feed after each page */ 124 clearsc = !clearsc; 125 break; 126 case 'w': /* "wide" printer format */ 127 hscale = 16.0; 128 vscale = 9.6; 129 linelen = 131; 130 keepon = 1; 131 clearsc = 1; 132 break; 133 } 134 } 135 136 if (argc < 1) 137 conv(stdin); 138 else 139 while (argc--) { 140 if (strcmp(*argv, "-") == 0) 141 fp = stdin; 142 else if ((fp = fopen(*argv, "r")) == NULL) 143 error(FATAL, "can't open %s", *argv); 144 conv(fp); 145 fclose(fp); 146 argv++; 147 } 148 done(); 149 } 150 151 152 /*----------------------------------------------------------------------------* 153 | Routine: char * operand (& argc, & argv) 154 | 155 | Results: returns address of the operand given with a command-line 156 | option. It uses either "-Xoperand" or "-X operand", whichever 157 | is present. The program is terminated if no option is present. 158 | 159 | Side Efct: argc and argv are updated as necessary. 160 *----------------------------------------------------------------------------*/ 161 162 char *operand(argcp, argvp) 163 int * argcp; 164 char ***argvp; 165 { 166 if ((**argvp)[2]) return(**argvp + 2); /* operand immediately follows */ 167 if ((--*argcp) <= 0) { /* operand next word */ 168 fprintf (stderr, "command-line option operand missing.\n"); 169 exit(1); 170 } 171 return(*(++(*argvp))); /* no operand */ 172 } 173 174 175 outlist(s) /* process list of page numbers to be printed */ 176 char *s; 177 { 178 int n1, n2, i; 179 180 nolist = 0; 181 while (*s) { 182 n1 = 0; 183 if (isdigit(*s)) 184 do 185 n1 = 10 * n1 + *s++ - '0'; 186 while (isdigit(*s)); 187 else 188 n1 = -9999; 189 n2 = n1; 190 if (*s == '-') { 191 s++; 192 n2 = 0; 193 if (isdigit(*s)) 194 do 195 n2 = 10 * n2 + *s++ - '0'; 196 while (isdigit(*s)); 197 else 198 n2 = 9999; 199 } 200 olist[nolist++] = n1; 201 olist[nolist++] = n2; 202 if (*s != '\0') 203 s++; 204 } 205 olist[nolist] = 0; 206 } 207 208 209 in_olist(n) /* is n in olist? */ 210 int n; 211 { 212 int i; 213 214 if (nolist == 0) 215 return(1); /* everything is included */ 216 for (i = 0; i < nolist; i += 2) 217 if (n >= olist[i] && n <= olist[i+1]) 218 return(1); 219 return(0); 220 } 221 222 223 conv(fp) 224 register FILE *fp; 225 { 226 register int c; 227 int m, n, i, n1, m1; 228 char str[100], buf[300]; 229 230 while ((c = getc(fp)) != EOF) { 231 switch (c) { 232 case '\n': /* when input is text */ 233 case '\t': 234 case ' ': 235 case 0: 236 break; 237 238 case '0': case '1': case '2': case '3': case '4': 239 case '5': case '6': case '7': case '8': case '9': 240 /* two motion digits plus a character */ 241 hmot((c-'0')*10 + getc(fp)-'0'); 242 put1(getc(fp)); 243 break; 244 245 case 'c': /* single ascii character */ 246 put1(getc(fp)); 247 break; 248 249 case 'C': /* funny character */ 250 fscanf(fp, "%s", str); 251 put1s(str); 252 break; 253 254 case 't': /* straight text */ 255 fgets(buf, sizeof(buf), fp); 256 t_text(buf); 257 break; 258 259 case 'D': /* draw function */ 260 fgets(buf, sizeof(buf), fp); 261 switch (buf[0]) { 262 case 'l': /* draw a line */ 263 sscanf(buf+1, "%d %d", &n, &m); 264 drawline(n, m); 265 break; 266 case 'c': /* circle */ 267 sscanf(buf+1, "%d", &n); 268 drawcirc(n); 269 break; 270 case 'e': /* ellipse */ 271 sscanf(buf+1, "%d %d", &m, &n); 272 drawellip(m, n); 273 break; 274 case 'a': /* arc */ 275 sscanf(buf+1, "%d %d %d %d", &n, &m, &n1, &m1); 276 drawarc(n, m, n1, m1); 277 break; 278 case 'q': /* versatec polygon - ignore */ 279 while (buf[strlen(buf) - 1] != '\n') 280 if (fgets(buf, sizeof(buf), fp) == NULL) 281 error(FATAL,"unexpected end of input"); 282 break; 283 case 'P': /* unbordered */ 284 case 'p': /* polygon */ 285 sscanf(buf+1, "%d", &n); 286 n = 1; 287 while(buf[n++] == ' '); 288 while(isdigit(buf[n])) n++; 289 drawwig(buf+n, 1); 290 break; 291 case 'g': /* "gremlin" curve */ 292 case '~': /* wiggly line */ 293 drawwig(buf+1, 0); 294 break; 295 case 't': /* thickness - not important */ 296 case 's': /* style - not important */ 297 break; 298 default: 299 error(FATAL,"unknown drawing command %s\n",buf); 300 break; 301 } 302 break; 303 case 'i': /* stipple pattern request - ignored */ 304 case 's': /* point size - ignored */ 305 fscanf(fp, "%d", &n); 306 break; 307 308 case 'f': /* font request - ignored */ 309 fscanf(fp, "%s", str); 310 break; 311 312 case 'H': /* absolute horizontal motion */ 313 fscanf(fp, "%d", &n); 314 hgoto(n); 315 break; 316 317 case 'h': /* relative horizontal motion */ 318 fscanf(fp, "%d", &n); 319 hmot(n); 320 break; 321 322 case 'w': /* word space */ 323 break; 324 325 case 'V': /* absolute vertical motion */ 326 fscanf(fp, "%d", &n); 327 vgoto(n); 328 break; 329 330 case 'v': /* relative vertical motion */ 331 fscanf(fp, "%d", &n); 332 vmot(n); 333 break; 334 335 case 'p': /* new page */ 336 fscanf(fp, "%d", &n); 337 t_page(n); 338 break; 339 340 case 'P': /* new span (ignored) */ 341 fscanf(fp, "%d", &n); 342 break; 343 344 case 'n': /* end of line */ 345 hpos = 0; 346 case '#': /* comment */ 347 while (getc(fp) != '\n') 348 ; 349 break; 350 351 case 'x': /* device control */ 352 devcntrl(fp); 353 break; 354 355 default: 356 error(!FATAL, "unknown input character %o %c\n", c, c); 357 done(); 358 } 359 } 360 } 361 362 363 devcntrl(fp) /* interpret device control functions */ 364 FILE *fp; 365 { 366 int c, n; 367 char str[20]; 368 369 fscanf(fp, "%s", str); 370 switch (str[0]) { /* crude for now */ 371 case 'i': /* initialize */ 372 t_init(0); 373 break; 374 case 'r': /* resolution assumed when prepared */ 375 fscanf(fp, "%d", &n); 376 hscale = (float) n / hscale; 377 vscale = (float) n / vscale; 378 DP = min (hscale, vscale); /* guess the drawing */ 379 DP = max (DP, 1); /* resolution */ 380 break; 381 case 'f': /* font used */ 382 case 'T': /* device name */ 383 case 't': /* trailer */ 384 case 'p': /* pause -- can restart */ 385 case 's': /* stop */ 386 break; 387 } 388 while (getc(fp) != '\n') /* skip rest of input line */ 389 ; 390 } 391 392 /* error printing routine - first argument is a "fatal" flag */ 393 error(f, s, a1, a2, a3, a4, a5, a6, a7) { 394 fprintf(stderr, "dterm: "); 395 fprintf(stderr, s, a1, a2, a3, a4, a5, a6, a7); 396 fprintf(stderr, "\n"); 397 if (f) exit(1); 398 } 399 400 401 t_init(reinit) /* initialize device */ 402 int reinit; 403 { 404 register int i; 405 register int j; 406 register FILE *fp; /* file to look up special characters */ 407 register char *charptr; /* string pointer to step through specials */ 408 register char *tabptr; /* string pointer for spectab setting */ 409 char specials[5000]; /* intermediate input buffer (made bigger */ 410 /* than we'll EVER use... */ 411 412 413 fflush(stdout); 414 hpos = vpos = 0; 415 for (i = 0; i < PGHEIGHT; i++) 416 for (j = 0; j < PGWIDTH; j++) 417 pagebuf[i][j] = ' '; 418 minh = PGWIDTH; 419 maxh = 0; 420 minv = PGHEIGHT; 421 maxv = 0; 422 423 if (reinit) return; /* if this is the first time, read */ 424 /* special character table file. */ 425 if ((fp = fopen (specfile, "r")) != NULL) { 426 charptr = &specials[0]; 427 for (i = 2; fscanf(fp, "%s", charptr) != EOF; i++) { 428 charptr += strlen(charptr) + 1; 429 } 430 fclose(fp); 431 *charptr++ = '\0'; /* ending strings */ 432 *charptr++ = '\0'; 433 /* allocate table */ 434 spectab = (char **) malloc(i * sizeof(char*)); 435 spectab[0] = tabptr = malloc(j = (int) (charptr - &specials[0])); 436 437 /* copy whole table */ 438 for (charptr = &specials[0]; j--; *tabptr++ = *charptr++); 439 440 tabptr = spectab[0]; /* set up pointers to table */ 441 for (j = 0; i--; j++) { 442 spectab[j] = tabptr; 443 tabptr += strlen(tabptr) + 1; 444 } 445 446 } else { /* didn't find table - allocate a null one */ 447 448 error (!FATAL, "Can't open special character file: %s", specfile); 449 spectab = (char **) malloc(2 * sizeof(char*)); 450 spectab[0] = malloc (2); 451 spectab[1] = spectab[0] + 1; 452 *spectab[0] = '\0'; 453 *spectab[1] = '\0'; 454 } 455 } 456 457 458 /* just got "p#" command. print the current page and */ 459 t_page(n) /* do whatever new page functions */ 460 { 461 long ftell(); 462 int c, m, i; 463 char buf[100], *bp; 464 465 pgnum[np++] = n; 466 pgadr[np] = ftell(fp); 467 if (np > npmax) 468 npmax = np; 469 if (output == 0) { 470 output = in_olist(n); 471 t_init(1); 472 return; 473 } 474 475 putpage(); 476 fflush(stdout); 477 478 if (clearsc) putchar(''); 479 if (keepon) { 480 t_init(1); 481 return; 482 } 483 next: 484 for (bp = buf; (*bp = readch()); ) 485 if (*bp++ == '\n') 486 break; 487 *bp = 0; 488 switch (buf[0]) { 489 case 0: 490 done(); 491 break; 492 case '\n': 493 output = in_olist(n); 494 t_init(1); 495 return; 496 case '-': 497 case 'p': 498 m = atoi(&buf[1]) + 1; 499 if (fp == stdin) { 500 fputs("you can't; it's not a file\n", stderr); 501 break; 502 } 503 if (np - m <= 0) { 504 fputs("too far back\n", stderr); 505 break; 506 } 507 np -= m; 508 fseek(fp, pgadr[np], 0); 509 output = 1; 510 t_init(1); 511 return; 512 case '0': case '1': case '2': case '3': case '4': 513 case '5': case '6': case '7': case '8': case '9': 514 m = atoi(&buf[0]); 515 for (i = 0; i < npmax; i++) 516 if (m == pgnum[i]) 517 break; 518 if (i >= npmax || fp == stdin) { 519 fputs("you can't\n", stderr); 520 break; 521 } 522 np = i + 1; 523 fseek(fp, pgadr[np], 0); 524 output = 1; 525 t_init(1); 526 return; 527 case 'o': 528 outlist(&buf[1]); 529 output = 0; 530 t_init(1); 531 return; 532 case '?': 533 fputs("p print this page again\n", stderr); 534 fputs("-n go back n pages\n", stderr); 535 fputs("n print page n (previously printed)\n", stderr); 536 fputs("o... set the -o output list to ...\n", stderr); 537 break; 538 default: 539 fputs("?\n", stderr); 540 break; 541 } 542 goto next; 543 } 544 545 /* print the contents of the current page. puts out */ 546 putpage() /* only the part of the page that's been written on */ 547 { /* unless "margin" is set. */ 548 int i, j, k; 549 550 fflush(stdout); 551 if (margin) minv = minh = 0; 552 for (i = minv; i <= maxv; i++) { 553 for (k = maxh; pagebuf[i][k] == ' '; k--) 554 ; 555 if (k > minh + linelen) 556 k = minh + linelen; 557 for (j = minh; j <= k; j++) 558 putchar(pagebuf[i][j]); 559 putchar('\n'); 560 } 561 fflush(stdout); 562 } 563 564 565 t_text(s) /* print string s as text */ 566 char *s; 567 { 568 int c; 569 char str[100]; 570 571 if (!output) 572 return; 573 while ((c = *s++) != '\n') { 574 if (c == '\\') { 575 switch (c = *s++) { 576 case '\\': 577 case 'e': 578 put1('\\'); 579 break; 580 case '(': 581 str[0] = *s++; 582 str[1] = *s++; 583 str[2] = '\0'; 584 put1s(str); 585 break; 586 } 587 } else { 588 put1(c); 589 } 590 hmot(1); 591 } 592 } 593 594 595 put1s(s) /* s is a funny char name */ 596 char *s; 597 { 598 int i; 599 char *p; 600 static char prev[10] = ""; 601 static int previ; 602 603 if (!output) 604 return; 605 if (strcmp(s, prev) != 0) { 606 previ = -1; 607 for (i = 0; *spectab[i] != '\0'; i += 2) 608 if (strcmp(spectab[i], s) == 0) { 609 strcpy(prev, s); 610 previ = i; 611 break; 612 } 613 } 614 if (previ >= 0) { 615 for (p = spectab[previ+1]; *p; p++) 616 store(*p); 617 } else 618 prev[0] = '\0'; 619 } 620 621 622 put1(c) /* output char c */ 623 int c; 624 { 625 if (!output) 626 return; 627 store(c); 628 } 629 630 631 done() 632 { 633 output = 1; 634 putpage(); 635 fflush(stdout); 636 exit(0); 637 } 638 639 640 readch () 641 { 642 int c; 643 static FILE *rcf; 644 static nbol; /* 0 if at beginning of a line */ 645 646 if (rcf == NULL) { 647 rcf = fopen ("/dev/tty", "r"); 648 setbuf (rcf, NULL); 649 } 650 651 if (!nbol) 652 fprintf (stderr, "dterm: "); /* issue prompt */ 653 if ((c = getc (rcf)) == EOF) 654 return 0; 655 nbol = (c != '\n'); 656 return c; 657 } 658 659 660 store(c) /* put 'c' in the page at (hpos, vpos) */ 661 { 662 register int i; 663 register int j; 664 665 666 i = hpos / hscale; /* scale the position to page coordinates */ 667 j = vpos / vscale; 668 669 if (i >= PGWIDTH) i = PGWIDTH - 1; /* don't go over the edge */ 670 else if (i < 0) i = 0; 671 if (j >= PGHEIGHT) j = PGHEIGHT - 1; 672 else if (j < 0) j = 0; 673 674 pagebuf[j][i] = c; /* write the character */ 675 676 if (i > maxh) maxh = i; /* update the page bounds */ 677 if (i < minh) minh = i; 678 if (j > maxv) maxv = j; 679 if (j < minv) minv = j; 680 } 681 682 683 drawline(dx, dy) /* draw line from here to dx, dy using s */ 684 int dx, dy; 685 { 686 register int xd; 687 register int yd; 688 register int i; 689 register int numdots; 690 int dirmot, perp; 691 int motincr, perpincr; 692 int ohpos, ovpos; 693 float val, slope; 694 float incrway; 695 696 ohpos = hpos; 697 ovpos = vpos; 698 xd = dx / DP; 699 yd = dy / DP; 700 if (xd == 0) { 701 numdots = abs (yd); 702 numdots = min(numdots, maxdots); 703 motincr = DP * sgn (yd); 704 put1('|'); 705 for (i = 0; i < numdots; i++) { 706 vmot(motincr); 707 put1('|'); 708 } 709 } else 710 if (yd == 0) { 711 numdots = abs (xd); 712 numdots = min(numdots, maxdots); 713 motincr = DP * sgn (xd); 714 put1('-'); 715 for (i = 0; i < numdots; i++) { 716 hmot(motincr); 717 put1('-'); 718 } 719 } else { 720 if (abs (xd) > abs (yd)) { 721 val = slope = (float) xd/yd; 722 numdots = abs (xd); 723 dirmot = 'h'; 724 perp = 'v'; 725 motincr = DP * sgn (xd); 726 perpincr = DP * sgn (yd); 727 } else { 728 val = slope = (float) yd/xd; 729 numdots = abs (yd); 730 dirmot = 'v'; 731 perp = 'h'; 732 motincr = DP * sgn (yd); 733 perpincr = DP * sgn (xd); 734 } 735 numdots = min(numdots, maxdots); 736 incrway = sgn ((int) slope); 737 put1('*'); 738 for (i = 0; i < numdots; i++) { 739 val -= incrway; 740 if (dirmot == 'h') 741 hmot(motincr); 742 else 743 vmot(motincr); 744 if (val * slope < 0) { 745 if (perp == 'h') 746 hmot(perpincr); 747 else 748 vmot(perpincr); 749 val += slope; 750 } 751 put1('*'); 752 } 753 } 754 hgoto(ohpos + dx); 755 vgoto(ovpos + dy); 756 } 757 758 759 drawwig(s, poly) /* draw wiggly line or polygon, if "poly" set */ 760 char *s; 761 int poly; 762 { 763 int x[50], y[50], xp, yp, pxp, pyp; 764 float t1, t2, t3, w; 765 int i, j, numdots, N; 766 char temp[50], *p, *getstr(); 767 768 p = s; 769 for (N = 2; (p=getstr(p,temp)) != NULL && N < sizeof(x)/sizeof(x[0]);) { 770 x[N] = atoi(temp); 771 p = getstr(p, temp); 772 y[N++] = atoi(temp); 773 } 774 if (poly) { 775 for (i = 2; i < N; i++) 776 drawline(x[i], y[i]); 777 return; 778 } 779 x[0] = x[1] = hpos; 780 y[0] = y[1] = vpos; 781 for (i = 1; i < N; i++) { 782 x[i+1] += x[i]; 783 y[i+1] += y[i]; 784 } 785 x[N] = x[N-1]; 786 y[N] = y[N-1]; 787 pxp = pyp = -9999; 788 for (i = 0; i < N-1; i++) { /* interval */ 789 numdots = (dist(x[i], y[i], x[i+1], y[i+1]) 790 + dist(x[i+1], y[i+1], x[i+2], y[i+2])) / 2; 791 numdots /= DP; 792 numdots = min(numdots, maxdots); 793 for (j = 0; j < numdots; j++) { /* points within */ 794 w = (float) j / numdots; 795 t1 = 0.5 * w * w; 796 w = w - 0.5; 797 t2 = 0.75 - w * w; 798 w = w - 0.5; 799 t3 = 0.5 * w * w; 800 xp = t1 * x[i+2] + t2 * x[i+1] + t3 * x[i] + 0.5; 801 yp = t1 * y[i+2] + t2 * y[i+1] + t3 * y[i] + 0.5; 802 if (xp != pxp || yp != pyp) { 803 hgoto(xp); 804 vgoto(yp); 805 put1('*'); 806 pxp = xp; 807 pyp = yp; 808 } 809 } 810 } 811 } 812 813 814 /* copy next non-blank string from p to temp, update p */ 815 816 char *getstr(p, temp) 817 char *p, *temp; 818 { 819 while (*p == ' ' || *p == '\t' || *p == '\n') 820 p++; 821 if (*p == '\0') { 822 temp[0] = 0; 823 return(NULL); 824 } 825 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 826 *temp++ = *p++; 827 *temp = '\0'; 828 return(p); 829 } 830 831 832 drawcirc(d) 833 { 834 int xc, yc; 835 836 xc = hpos; 837 yc = vpos; 838 conicarc(hpos + d/2, -vpos, hpos, -vpos, hpos, -vpos, d/2, d/2); 839 hgoto(xc + d); /* circle goes to right side */ 840 vgoto(yc); 841 } 842 843 844 dist(x1, y1, x2, y2) /* integer distance from x1,y1 to x2,y2 */ 845 { 846 float dx, dy; 847 848 dx = x2 - x1; 849 dy = y2 - y1; 850 return sqrt(dx*dx + dy*dy) + 0.5; 851 } 852 853 854 drawarc(dx1, dy1, dx2, dy2) 855 { 856 int x0, y0, x2, y2, r; 857 858 x0 = hpos + dx1; /* center */ 859 y0 = vpos + dy1; 860 x2 = x0 + dx2; /* "to" */ 861 y2 = y0 + dy2; 862 r = sqrt((float) dx1 * dx1 + (float) dy1 * dy1) + 0.5; 863 conicarc(x0, -y0, hpos, -vpos, x2, -y2, r, r); 864 } 865 866 867 drawellip(a, b) 868 { 869 int xc, yc; 870 871 xc = hpos; 872 yc = vpos; 873 conicarc(hpos + a/2, -vpos, hpos, -vpos, hpos, -vpos, a/2, b/2); 874 hgoto(xc + a); 875 vgoto(yc); 876 } 877 878 879 conicarc(x, y, x0, y0, x1, y1, a, b) 880 { 881 /* based on Bresenham, CACM Feb 77, pp 102-3 by Chris Van Wyk */ 882 /* capitalized vars are an internal reference frame */ 883 long dotcount = 0; 884 int xs, ys, xt, yt, Xs, Ys, qs, Xt, Yt, qt, 885 M1x, M1y, M2x, M2y, M3x, M3y, 886 Q, move, Xc, Yc; 887 int ox1, oy1; 888 long delta; 889 float xc, yc; 890 float radius, slope; 891 float xstep, ystep; 892 893 ox1 = x1; 894 oy1 = y1; 895 if (a != b) /* an arc of an ellipse; internally, think of circle */ 896 if (a > b) { 897 xstep = (float)a / b; 898 ystep = 1; 899 radius = b; 900 } else { 901 xstep = 1; 902 ystep = (float)b / a; 903 radius = a; 904 } 905 else { 906 /* a circular arc; radius computed from center and first point */ 907 xstep = ystep = 1; 908 radius = sqrt((float)(sqr(x0 - x) + sqr(y0 - y))); 909 } 910 911 xc = x0; 912 yc = y0; 913 /* now, use start and end point locations to figure out 914 the angle at which start and end happen; use these 915 angles with known radius to figure out where start 916 and end should be 917 */ 918 slope = atan2((double)(y0 - y), (double)(x0 - x) ); 919 if (slope == 0.0 && x0 < x) 920 slope = 3.14159265; 921 x0 = x + radius * cos(slope) + 0.5; 922 y0 = y + radius * sin(slope) + 0.5; 923 slope = atan2((double)(y1 - y), (double)(x1 - x)); 924 if (slope == 0.0 && x1 < x) 925 slope = 3.14159265; 926 x1 = x + radius * cos(slope) + 0.5; 927 y1 = y + radius * sin(slope) + 0.5; 928 /* step 2: translate to zero-centered circle */ 929 xs = x0 - x; 930 ys = y0 - y; 931 xt = x1 - x; 932 yt = y1 - y; 933 /* step 3: normalize to first quadrant */ 934 if (xs < 0) 935 if (ys < 0) { 936 Xs = abs(ys); 937 Ys = abs(xs); 938 qs = 3; 939 M1x = 0; 940 M1y = -1; 941 M2x = 1; 942 M2y = -1; 943 M3x = 1; 944 M3y = 0; 945 } else { 946 Xs = abs(xs); 947 Ys = abs(ys); 948 qs = 2; 949 M1x = -1; 950 M1y = 0; 951 M2x = -1; 952 M2y = -1; 953 M3x = 0; 954 M3y = -1; 955 } 956 else if (ys < 0) { 957 Xs = abs(xs); 958 Ys = abs(ys); 959 qs = 0; 960 M1x = 1; 961 M1y = 0; 962 M2x = 1; 963 M2y = 1; 964 M3x = 0; 965 M3y = 1; 966 } else { 967 Xs = abs(ys); 968 Ys = abs(xs); 969 qs = 1; 970 M1x = 0; 971 M1y = 1; 972 M2x = -1; 973 M2y = 1; 974 M3x = -1; 975 M3y = 0; 976 } 977 978 Xc = Xs; 979 Yc = Ys; 980 if (xt < 0) 981 if (yt < 0) { 982 Xt = abs(yt); 983 Yt = abs(xt); 984 qt = 3; 985 } else { 986 Xt = abs(xt); 987 Yt = abs(yt); 988 qt = 2; 989 } 990 else if (yt < 0) { 991 Xt = abs(xt); 992 Yt = abs(yt); 993 qt = 0; 994 } else { 995 Xt = abs(yt); 996 Yt = abs(xt); 997 qt = 1; 998 } 999 1000 /* step 4: calculate number of quadrant crossings */ 1001 if (((4 + qt - qs) % 4 == 0) && (Xt <= Xs) && (Yt >= Ys)) 1002 Q = 3; 1003 else 1004 Q = (4 + qt - qs) % 4 - 1; 1005 /* step 5: calculate initial decision difference */ 1006 delta = sqr(Xs + 1) + sqr(Ys - 1) - sqr(xs) - sqr(ys); 1007 /* here begins the work of drawing. */ 1008 while ((Q >= 0) || ((Q > -2) && ((Xt > Xc) && (Yt < Yc)))) { 1009 if (dotcount++ % DP == 0) { 1010 hgoto((int)xc); 1011 vmot(-vpos-((int)yc)); 1012 put1('*'); 1013 } 1014 if (Yc < 0.5) { 1015 /* reinitialize */ 1016 Xs = Xc = 0; 1017 Ys = Yc = sqrt((float)(sqr(xs) + sqr(ys))); 1018 delta = sqr(Xs + 1) + sqr(Ys - 1) - sqr(xs) - sqr(ys); 1019 Q--; 1020 M1x = M3x; 1021 M1y = M3y; 1022 { 1023 int T; 1024 T = M2y; 1025 M2y = M2x; 1026 M2x = -T; 1027 T = M3y; 1028 M3y = M3x; 1029 M3x = -T; 1030 } 1031 } else { 1032 if (delta <= 0) 1033 if (2 * delta + 2 * Yc - 1 <= 0) 1034 move = 1; 1035 else 1036 move = 2; 1037 else if (2 * delta - 2 * Xc - 1 <= 0) 1038 move = 2; 1039 else 1040 move = 3; 1041 switch (move) { 1042 case 1: 1043 Xc++; 1044 delta += 2 * Xc + 1; 1045 xc += M1x * xstep; 1046 yc += M1y * ystep; 1047 break; 1048 case 2: 1049 Xc++; 1050 Yc--; 1051 delta += 2 * Xc - 2 * Yc + 2; 1052 xc += M2x * xstep; 1053 yc += M2y * ystep; 1054 break; 1055 case 3: 1056 Yc--; 1057 delta -= 2 * Yc + 1; 1058 xc += M3x * xstep; 1059 yc += M3y * ystep; 1060 break; 1061 } 1062 } 1063 } 1064 drawline((int)xc-ox1,(int)yc-oy1); 1065 } 1066