| #include <u.h> |
| #include <libc.h> |
| #include <draw.h> |
| #include <cursor.h> |
| #include <event.h> |
| #include <bio.h> |
| #include "proof.h" |
| |
| int res; |
| int hpos; |
| int vpos; |
| int DIV = 11; |
| |
| Point offset; |
| Point xyoffset = { 0,0 }; |
| |
| Rectangle view[MAXVIEW]; |
| Rectangle bound[MAXVIEW]; /* extreme points */ |
| int nview = 1; |
| |
| int lastp; /* last page number we were on */ |
| |
| #define NPAGENUMS 200 |
| struct pagenum { |
| int num; |
| long adr; |
| } pagenums[NPAGENUMS]; |
| int npagenums; |
| |
| int curfont, cursize; |
| |
| char *getcmdstr(void); |
| |
| static void initpage(void); |
| static void view_setup(int); |
| static Point scale(Point); |
| static void clearview(Rectangle); |
| static int addpage(int); |
| static void spline(Image *, int, Point *); |
| static int skipto(int, int); |
| static void wiggly(int); |
| static void devcntrl(void); |
| static void eatline(void); |
| static int getn(void); |
| static int botpage(int); |
| static void getstr(char *); |
| /* |
| static void getutf(char *); |
| */ |
| |
| #define Do screen->r.min |
| #define Dc screen->r.max |
| |
| /* declarations and definitions of font stuff are in font.c and main.c */ |
| |
| static void |
| initpage(void) |
| { |
| int i; |
| |
| view_setup(nview); |
| for (i = 0; i < nview-1; i++) |
| draw(screen, view[i], screen, nil, view[i+1].min); |
| clearview(view[nview-1]); |
| offset = view[nview-1].min; |
| vpos = 0; |
| } |
| |
| static void |
| view_setup(int n) |
| { |
| int i, j, v, dx, dy, r, c; |
| |
| switch (n) { |
| case 1: r = 1; c = 1; break; |
| case 2: r = 1; c = 2; break; |
| case 3: r = 1; c = 3; break; |
| case 4: r = 2; c = 2; break; |
| case 5: case 6: r = 2; c = 3; break; |
| case 7: case 8: case 9: r = 3; c = 3; break; |
| default: r = (n+2)/3; c = 3; break; /* finking out */ |
| } |
| dx = (Dc.x - Do.x) / c; |
| dy = (Dc.y - Do.y) / r; |
| v = 0; |
| for (i = 0; i < r && v < n; i++) |
| for (j = 0; j < c && v < n; j++) { |
| view[v] = screen->r; |
| view[v].min.x = Do.x + j * dx; |
| view[v].max.x = Do.x + (j+1) * dx; |
| view[v].min.y = Do.y + i * dy; |
| view[v].max.y = Do.y + (i+1) * dy; |
| v++; |
| } |
| } |
| |
| static void |
| clearview(Rectangle r) |
| { |
| draw(screen, r, display->white, nil, r.min); |
| } |
| |
| int resized; |
| void eresized(int new) |
| { |
| /* this is called if we are resized */ |
| if(new && getwindow(display, Refnone) < 0) |
| drawerror(display, "can't reattach to window"); |
| initpage(); |
| resized = 1; |
| } |
| |
| static Point |
| scale(Point p) |
| { |
| p.x /= DIV; |
| p.y /= DIV; |
| return addpt(xyoffset, addpt(offset,p)); |
| } |
| |
| static int |
| addpage(int n) |
| { |
| int i; |
| |
| for (i = 0; i < npagenums; i++) |
| if (n == pagenums[i].num) |
| return i; |
| if (npagenums < NPAGENUMS-1) { |
| pagenums[npagenums].num = n; |
| pagenums[npagenums].adr = offsetc(); |
| npagenums++; |
| } |
| return npagenums; |
| } |
| |
| void |
| readpage(void) |
| { |
| int c, i, a, alpha, phi; |
| static int first = 0; |
| int m, n, gonow = 1; |
| Rune r[32], t; |
| Point p,q,qq; |
| |
| offset = screen->clipr.min; |
| esetcursor(&deadmouse); |
| while (gonow) |
| { |
| c = getc(); |
| switch (c) |
| { |
| case -1: |
| esetcursor(0); |
| if (botpage(lastp+1)) { |
| initpage(); |
| break; |
| } |
| exits(0); |
| case 'p': /* new page */ |
| lastp = getn(); |
| addpage(lastp); |
| if (first++ > 0) { |
| esetcursor(0); |
| botpage(lastp); |
| esetcursor(&deadmouse); |
| } |
| initpage(); |
| break; |
| case '\n': /* when input is text */ |
| case ' ': |
| case 0: /* occasional noise creeps in */ |
| break; |
| case '0': case '1': case '2': case '3': case '4': |
| case '5': case '6': case '7': case '8': case '9': |
| /* two motion digits plus a character */ |
| hpos += (c-'0')*10 + getc()-'0'; |
| |
| /* FALLS THROUGH */ |
| case 'c': /* single ascii character */ |
| r[0] = getrune(); |
| r[1] = 0; |
| dochar(r); |
| break; |
| |
| case 'C': |
| for(i=0; ; i++){ |
| t = getrune(); |
| if(isspace(t)) |
| break; |
| r[i] = t; |
| } |
| r[i] = 0; |
| dochar(r); |
| break; |
| |
| case 'N': |
| r[0] = getn(); |
| r[1] = 0; |
| dochar(r); |
| break; |
| |
| case 'D': /* draw function */ |
| switch (getc()) |
| { |
| case 'l': /* draw a line */ |
| n = getn(); |
| m = getn(); |
| p = Pt(hpos,vpos); |
| q = addpt(p, Pt(n,m)); |
| hpos += n; |
| vpos += m; |
| line(screen, scale(p), scale(q), 0, 0, 0, display->black, ZP); |
| break; |
| case 'c': /* circle */ |
| /*nop*/ |
| m = getn()/2; |
| p = Pt(hpos+m,vpos); |
| hpos += 2*m; |
| ellipse(screen, scale(p), m/DIV, m/DIV, 0, display->black, ZP); |
| /* p=currentpt; p.x+=dmap(m/2);circle bp,p,a,ONES,Mode*/ |
| break; |
| case 'e': /* ellipse */ |
| /*nop*/ |
| m = getn()/2; |
| n = getn()/2; |
| p = Pt(hpos+m,vpos); |
| hpos += 2*m; |
| ellipse(screen, scale(p), m/DIV, n/DIV, 0, display->black, ZP); |
| break; |
| case 'a': /* arc */ |
| p = scale(Pt(hpos,vpos)); |
| n = getn(); |
| m = getn(); |
| hpos += n; |
| vpos += m; |
| q = scale(Pt(hpos,vpos)); |
| n = getn(); |
| m = getn(); |
| hpos += n; |
| vpos += m; |
| qq = scale(Pt(hpos,vpos)); |
| /* |
| * tricky: convert from 3-point clockwise to |
| * center, angle1, delta-angle counterclockwise. |
| */ |
| a = hypot(qq.x-q.x, qq.y-q.y); |
| phi = atan2(q.y-p.y, p.x-q.x)*180./PI; |
| alpha = atan2(q.y-qq.y, qq.x-q.x)*180./PI - phi; |
| if(alpha < 0) |
| alpha += 360; |
| arc(screen, q, a, a, 0, display->black, ZP, phi, alpha); |
| break; |
| case '~': /* wiggly line */ |
| wiggly(0); |
| break; |
| default: |
| break; |
| } |
| eatline(); |
| break; |
| case 's': |
| n = getn(); /* ignore fractional sizes */ |
| if (cursize == n) |
| break; |
| cursize = n; |
| if (cursize >= NFONT) |
| cursize = NFONT-1; |
| break; |
| case 'f': |
| curfont = getn(); |
| break; |
| case 'H': /* absolute horizontal motion */ |
| hpos = getn(); |
| break; |
| case 'h': /* relative horizontal motion */ |
| hpos += getn(); |
| break; |
| case 'w': /* word space */ |
| break; |
| case 'V': |
| vpos = getn(); |
| break; |
| case 'v': |
| vpos += getn(); |
| break; |
| case '#': /* comment */ |
| case 'n': /* end of line */ |
| eatline(); |
| break; |
| case 'x': /* device control */ |
| devcntrl(); |
| break; |
| default: |
| fprint(2, "unknown input character %o %c at offset %lud\n", c, c, offsetc()); |
| exits("bad char"); |
| } |
| } |
| esetcursor(0); |
| } |
| |
| static void |
| spline(Image *b, int n, Point *pp) |
| { |
| long w, t1, t2, t3, fac=1000; |
| int i, j, steps=10; |
| Point p, q; |
| |
| for (i = n; i > 0; i--) |
| pp[i] = pp[i-1]; |
| pp[n+1] = pp[n]; |
| n += 2; |
| p = pp[0]; |
| for(i = 0; i < n-2; i++) |
| { |
| for(j = 0; j < steps; j++) |
| { |
| w = fac * j / steps; |
| t1 = w * w / (2 * fac); |
| w = w - fac/2; |
| t2 = 3*fac/4 - w * w / fac; |
| w = w - fac/2; |
| t3 = w * w / (2*fac); |
| q.x = (t1*pp[i+2].x + t2*pp[i+1].x + |
| t3*pp[i].x + fac/2) / fac; |
| q.y = (t1*pp[i+2].y + t2*pp[i+1].y + |
| t3*pp[i].y + fac/2) / fac; |
| line(b, p, q, 0, 0, 0, display->black, ZP); |
| p = q; |
| } |
| } |
| } |
| |
| /* Have to parse skipped pages, to find out what fonts are loaded. */ |
| static int |
| skipto(int gotop, int curp) |
| { |
| char *p; |
| int i; |
| |
| if (gotop == curp) |
| return 1; |
| for (i = 0; i < npagenums; i++) |
| if (pagenums[i].num == gotop) { |
| if (seekc(pagenums[i].adr) == Beof) { |
| fprint(2, "can't rewind input\n"); |
| return 0; |
| } |
| return 1; |
| } |
| if (gotop <= curp) { |
| restart: |
| if (seekc(0) == Beof) { |
| fprint(2, "can't rewind input\n"); |
| return 0; |
| } |
| } |
| for(;;){ |
| p = rdlinec(); |
| if (p == 0) { |
| if(gotop>curp){ |
| gotop = curp; |
| goto restart; |
| } |
| return 0; |
| } else if (*p == 'p') { |
| lastp = curp = atoi(p+1); |
| addpage(lastp); /* maybe 1 too high */ |
| if (curp>=gotop) |
| return 1; |
| } |
| } |
| } |
| |
| static void |
| wiggly(int skip) |
| { |
| Point p[300]; |
| int c,i,n; |
| for (n = 1; (c = getc()) != '\n' && c>=0; n++) { |
| ungetc(); |
| p[n].x = getn(); |
| p[n].y = getn(); |
| } |
| p[0] = Pt(hpos, vpos); |
| for (i = 1; i < n; i++) |
| p[i] = addpt(p[i],p[i-1]); |
| hpos = p[n-1].x; |
| vpos = p[n-1].y; |
| for (i = 0; i < n; i++) |
| p[i] = scale(p[i]); |
| if (!skip) |
| spline(screen,n,p); |
| } |
| |
| static void |
| devcntrl(void) /* interpret device control functions */ |
| { |
| char str[80]; |
| int n; |
| |
| getstr(str); |
| switch (str[0]) { /* crude for now */ |
| case 'i': /* initialize */ |
| break; |
| case 'T': /* device name */ |
| getstr(devname); |
| break; |
| case 't': /* trailer */ |
| break; |
| case 'p': /* pause -- can restart */ |
| break; |
| case 's': /* stop */ |
| break; |
| case 'r': /* resolution assumed when prepared */ |
| res=getn(); |
| DIV = floor(.5 + res/(100.0*mag)); |
| if (DIV < 1) |
| DIV = 1; |
| mag = res/(100.0*DIV); /* adjust mag according to DIV coarseness */ |
| break; |
| case 'f': /* font used */ |
| n = getn(); |
| getstr(str); |
| loadfontname(n, str); |
| break; |
| /* these don't belong here... */ |
| case 'H': /* char height */ |
| break; |
| case 'S': /* slant */ |
| break; |
| case 'X': |
| break; |
| } |
| eatline(); |
| } |
| |
| int |
| isspace(int c) |
| { |
| return c==' ' || c=='\t' || c=='\n'; |
| } |
| |
| static void |
| getstr(char *is) |
| { |
| uchar *s = (uchar *) is; |
| |
| for (*s = getc(); isspace(*s); *s = getc()) |
| ; |
| for (; !isspace(*s); *++s = getc()) |
| ; |
| ungetc(); |
| *s = 0; |
| } |
| |
| #if 0 |
| static void |
| getutf(char *s) /* get next utf char, as bytes */ |
| { |
| int c, i; |
| |
| for (i=0;;) { |
| c = getc(); |
| if (c < 0) |
| return; |
| s[i++] = c; |
| |
| if (fullrune(s, i)) { |
| s[i] = 0; |
| return; |
| } |
| } |
| } |
| #endif |
| |
| static void |
| eatline(void) |
| { |
| int c; |
| |
| while ((c=getc()) != '\n' && c >= 0) |
| ; |
| } |
| |
| static int |
| getn(void) |
| { |
| int n, c, sign; |
| |
| while (c = getc()) |
| if (!isspace(c)) |
| break; |
| if(c == '-'){ |
| sign = -1; |
| c = getc(); |
| }else |
| sign = 1; |
| for (n = 0; '0'<=c && c<='9'; c = getc()) |
| n = n*10 + c - '0'; |
| while (c == ' ') |
| c = getc(); |
| ungetc(); |
| return(n*sign); |
| } |
| |
| static int |
| botpage(int np) /* called at bottom of page np-1 == top of page np */ |
| { |
| char *p; |
| int n; |
| |
| while (p = getcmdstr()) { |
| if (*p == '\0') |
| return 0; |
| if (*p == 'q') |
| exits(p); |
| if (*p == 'c') /* nop */ |
| continue; |
| if (*p == 'm') { |
| mag = atof(p+1); |
| if (mag <= .1 || mag >= 10) |
| mag = DEFMAG; |
| allfree(); /* zap fonts */ |
| DIV = floor(.5 + res/(100.0*mag)); |
| if (DIV < 1) |
| DIV = 1; |
| mag = res/(100.0*DIV); |
| return skipto(np-1, np); /* reprint the page */ |
| } |
| if (*p == 'x') { |
| xyoffset.x += atoi(p+1)*100; |
| skipto(np-1, np); |
| return 1; |
| } |
| if (*p == 'y') { |
| xyoffset.y += atoi(p+1)*100; |
| skipto(np-1, np); |
| return 1; |
| } |
| if (*p == '/') { /* divide into n pieces */ |
| nview = atoi(p+1); |
| if (nview < 1) |
| nview = 1; |
| else if (nview > MAXVIEW) |
| nview = MAXVIEW; |
| return skipto(np-1, np); |
| } |
| if (*p == 'p') { |
| if (p[1] == '\0'){ /* bare 'p' */ |
| if(skipto(np-1, np)) |
| return 1; |
| continue; |
| } |
| p++; |
| } |
| if ('0'<=*p && *p<='9') { |
| n = atoi(p); |
| if(skipto(n, np)) |
| return 1; |
| continue; |
| } |
| if (*p == '-' || *p == '+') { |
| n = atoi(p); |
| if (n == 0) |
| n = *p == '-' ? -1 : 1; |
| if(skipto(np - 1 + n, np)) |
| return 1; |
| continue; |
| } |
| if (*p == 'd') { |
| dbg = 1 - dbg; |
| continue; |
| } |
| |
| fprint(2, "illegal; try q, 17, +2, -1, p, m.7, /2, x1, y-.5 or return\n"); |
| } |
| return 0; |
| } |