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