X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=sys%2Fsrc%2Fcmd%2Ffplot.c;h=8fba63a282ac741f3434188d062d028325b46f99;hb=cea9e2267a70d17173c2b0fcdc421ee11b66ee79;hp=c51dc86422fcbf5b742e277215840ae5733a400d;hpb=7755561ae133f1313b1f1e61a0baf77f51c31bd9;p=plan9front.git diff --git a/sys/src/cmd/fplot.c b/sys/src/cmd/fplot.c index c51dc8642..8fba63a28 100644 --- a/sys/src/cmd/fplot.c +++ b/sys/src/cmd/fplot.c @@ -45,6 +45,8 @@ struct Token { }; double *stack, *sp; +void omax(void) { sp--; if(sp[1]>*sp) *sp = sp[1]; } +void omin(void) { sp--; if(sp[1]<*sp) *sp = sp[1]; } void add(void) { sp--; *sp += *(sp+1); } void sub(void) { sp--; *sp -= *(sp+1); } void mul(void) { sp--; *sp *= *(sp+1); } @@ -69,22 +71,24 @@ struct Operator { short prec; void (*f)(void); } ops[] = { - "+", OBINARY, 0, 0, add, - "-", OBINARY, 0, 0, sub, - "*", OBINARY, 0, 100, mul, - "/", OBINARY, 0, 100, div, - "%", OBINARY, 0, 100, mod, - "^", OBINARY, 1, 200, pot, - "sin", OUNARY, 0, 50, osin, - "cos", OUNARY, 0, 50, ocos, - "tan", OUNARY, 0, 50, otan, - "asin", OUNARY, 0, 50, oasin, - "acos", OUNARY, 0, 50, oacos, - "atan", OUNARY, 0, 50, oatan, - "sqrt", OUNARY, 0, 50, osqrt, - "exp", OUNARY, 0, 50, oexp, - "log", OUNARY, 0, 50, olog, - "ln", OUNARY, 0, 50, oln, + "max", OBINARY, 0, 0, omax, + "min", OBINARY, 0, 0, omax, + "+", OBINARY, 0, 100, add, + "-", OBINARY, 0, 100, sub, + "*", OBINARY, 0, 200, mul, + "/", OBINARY, 0, 200, div, + "%", OBINARY, 0, 200, mod, + "^", OBINARY, 1, 300, pot, + "sin", OUNARY, 0, 400, osin, + "cos", OUNARY, 0, 400, ocos, + "tan", OUNARY, 0, 400, otan, + "asin", OUNARY, 0, 400, oasin, + "acos", OUNARY, 0, 400, oacos, + "atan", OUNARY, 0, 400, oatan, + "sqrt", OUNARY, 0, 400, osqrt, + "exp", OUNARY, 0, 400, oexp, + "log", OUNARY, 0, 400, olog, + "ln", OUNARY, 0, 400, oln, }; struct Constant { @@ -105,11 +109,28 @@ int nfns; Token *opstackbot; double xmin = -10, xmax = 10; double ymin = -10, ymax = 10; -Image *color; -int cflag; +double gymin, gymax; +int icolors[] = { + DBlack, + 0xCC0000FF, + 0x00CC00FF, + 0x0000CCFF, + 0xFF00CCFF, + 0xFFAA00FF, + 0xCCCC00FF, +}; +Image *colors[nelem(icolors)]; +int cflag, aflag; char *imagedata; +char *pixels; int picx = 640, picy = 480; +typedef struct FRectangle FRectangle; +struct FRectangle { + double xmin, xmax, ymin, ymax; +} *zoomst; +int nzoomst; + void * emalloc(int size) { @@ -243,7 +264,7 @@ parse(Code *c, char *s) if(t->op->type == OBINARY) while(opstackbot != nil && opstackbot->type == TOP && (opstackbot->op->prec > t->op->prec || - t->op->rassoc && opstackbot->op->prec == t->op->prec)) + !t->op->rassoc && opstackbot->op->prec == t->op->prec)) pop(c); push(t); break; @@ -321,6 +342,8 @@ calc(Code *c, double x) case OUNARY: case OBINARY: o->f(); } + if(*sp < gymin) gymin = *sp; + if(*sp > gymax) gymax = *sp; return *sp; } @@ -349,7 +372,7 @@ deconvy(Rectangle *r, double dy) } void -pixel(int x, int y) +pixel(int x, int y, int c) { char *p; @@ -357,13 +380,18 @@ pixel(int x, int y) if(x >= picx || y >= picy || x < 0 || y < 0) return; p = imagedata + (picx * y + x) * 3; - p[0] = p[1] = p[2] = 0; - } else - draw(screen, Rect(x, y, x + 1, y + 1), color, nil, ZP); + p[0] = icolors[c] >> 24; + p[1] = icolors[c] >> 16; + p[2] = icolors[c] >> 8; + }else{ + draw(screen, Rect(x, y, x + 1, y + 1), colors[c], nil, ZP); + if(ptinrect(Pt(x, y), screen->r)) + pixels[picx * (y - screen->r.min.y) + (x - screen->r.min.x)] = 1; + } } void -drawinter(Code *co, Rectangle *r, double x1, double x2, int n) +drawinter(Code *co, Rectangle *r, double x1, double x2, int n, int c) { double y1, y2; int iy1, iy2; @@ -375,12 +403,12 @@ drawinter(Code *co, Rectangle *r, double x1, double x2, int n) y1 = calc(co, x1); if(!isNaN(y1)) { iy1 = deconvy(r, y1); - pixel(ix1, iy1); + pixel(ix1, iy1, c); } y2 = calc(co, x2); if(!isNaN(y2)) { iy2 = deconvy(r, y2); - pixel(ix2, iy2); + pixel(ix2, iy2, c); } if(isNaN(y1) || isNaN(y2)) return; @@ -392,34 +420,183 @@ drawinter(Code *co, Rectangle *r, double x1, double x2, int n) return; if(iy1 < r->min.y && iy2 < r->min.y) return; - drawinter(co, r, x1, (x1 + x2) / 2, n + 1); - drawinter(co, r, (x1 + x2) / 2, x2, n + 1); + drawinter(co, r, x1, (x1 + x2) / 2, n + 1, c); + drawinter(co, r, (x1 + x2) / 2, x2, n + 1, c); } void -drawgraph(Code *co, Rectangle *r) +drawgraph(Code *co, Rectangle *r, int c) { int x; for(x = r->min.x; x < r->max.x; x++) - drawinter(co, r, convx(r, x), convx(r, x + 1), 0); + drawinter(co, r, convx(r, x), convx(r, x + 1), 0, c); +} + +void +tickfmt(double d, double m, int n, char *fmt) +{ + double e1, e2; + int x; + + e1 = log10(fabs(m)); + e2 = log10(fabs(m + n * d)); + if(e2 > e1) e1 = e2; + if(e2 >= 4 || e2 < -3){ + x = ceil(e1-log10(d)-1); + snprint(fmt, 32, "%%.%de", x); + }else{ + x = ceil(-log10(d)); + snprint(fmt, 32, "%%.%df", x); + } +} + +int +xticklabel(char *fmt, double g, int p, int x, int y) +{ + char buf[32]; + Rectangle lr; + int ny; + + snprint(buf, sizeof(buf), fmt, g); + lr.min = Pt(p, y+2); + lr.max = addpt(lr.min, stringsize(display->defaultfont, buf)); + lr = rectsubpt(lr, Pt(Dx(lr) / 2-1, 0)); + if(lr.max.y >= screen->r.max.y){ + ny = y - 7 - Dy(lr); + lr = rectsubpt(lr, Pt(0, lr.min.y - ny)); + } + if(rectinrect(lr, screen->r) && (lr.min.x > x || lr.max.x <= x)){ + string(screen, lr.min, display->black, ZP, display->defaultfont, buf); + return 1; + } + return 0; +} + +int +yticklabel(char *fmt, double g, int p, int x, int y) +{ + char buf[32]; + Rectangle lr; + int nx; + + snprint(buf, sizeof(buf), fmt, g); + lr.min = Pt(0, 0); + lr.max = stringsize(display->defaultfont, buf); + lr = rectaddpt(lr, Pt(x-Dx(lr)-2, p - Dy(lr) / 2)); + if(lr.min.x < screen->r.min.x){ + nx = x + 7; + lr = rectsubpt(lr, Pt(lr.min.x - nx, 0)); + } + if(rectinrect(lr, screen->r) && (lr.min.y > y || lr.max.y <= y)){ + string(screen, lr.min, display->black, ZP, display->defaultfont, buf); + return 1; + } + return 0; +} + +int +calcm(double min, double max, int e, double *dp, double *mp) +{ + double d, m, r; + + d = pow(10, e>>1); + if((e & 1) != 0) d *= 5; + m = min; + if(min < 0 && max > 0) + m += fmod(-m, d); + else{ + r = fmod(m, d); + if(r < 0) + m -= r; + else + m += d - r; + } + if(dp != nil) *dp = d; + if(mp != nil) *mp = m; + return (max-m)*0.999/d; +} + +int +ticks(double min, double max, double *dp, double *mp) +{ + int e, n; + double m; + int beste; + double bestm; + + e = 2 * ceil(log10(max - min)); + beste = 0; + bestm = Inf(1); + for(;e>-100;e--){ + n = calcm(min, max, e, nil, nil); + if(n <= 0) continue; + if(n < 10) m = 10.0 / n; + else m = n / 10.0; + if(m < bestm){ + beste = e; + bestm = m; + } + if(n > 10) break; + } + calcm(min, max, beste, dp, mp); + return (max - *mp) / *dp; +} + +void +drawaxes(void) +{ + int x, y, p; + double dx, dy, mx, my; + int nx, ny; + int i; + char fmt[32]; + + if(xmin < 0 && xmax > 0) + x = deconvx(&screen->r, 0); + else + x = screen->r.min.x+5; + line(screen, Pt(x, screen->r.min.y), Pt(x, screen->r.max.y), Endarrow, 0, 0, display->black, ZP); + if(ymin < 0 && ymax > 0) + y = deconvy(&screen->r, 0); + else + y = screen->r.max.y-5; + line(screen, Pt(screen->r.min.x, y), Pt(screen->r.max.x, y), 0, Endarrow, 0, display->black, ZP); + nx = ticks(xmin, xmax, &dx, &mx); + tickfmt(dx, mx, nx, fmt); + for(i = 0; i <= nx; i++){ + p = deconvx(&screen->r, dx*i+mx); + if(xticklabel(fmt, dx*i+mx, p, x, y)) + line(screen, Pt(p, y), Pt(p, y-5), 0, 0, 0, display->black, ZP); + } + ny = ticks(ymin, ymax, &dy, &my); + tickfmt(dy, my, ny, fmt); + for(i = 0; i <= ny; i++){ + p = deconvy(&screen->r, dy*i+my); + if(yticklabel(fmt, dy*i+my, p, x, y)) + line(screen, Pt(x, p), Pt(x+5, p), 0, 0, 0, display->black, ZP); + } } void drawgraphs(void) { int i; - - color = display->black; + + gymin = Inf(1); + gymax = Inf(-1); + memset(pixels, 0, picx * picy); for(i = 0; i < nfns; i++) - drawgraph(&fns[i], &screen->r); + drawgraph(&fns[i], &screen->r, i % nelem(icolors)); + if(!aflag) + drawaxes(); flushimage(display, 1); } void usage(void) { - fprint(2, "usage: fplot [-c [-s size]] [-r range] functions ...\n"); + fprint(2, "usage: fplot [-a] [-c [-s size]] [-r range] functions ...\n"); exits("usage"); } @@ -430,10 +607,13 @@ zoom(void) Rectangle r; double xmin_, xmax_, ymin_, ymax_; - m.buttons = 7; + m.buttons = 0; r = egetrect(1, &m); if(Dx(r) < 1 || Dy(r) < 1) return; + zoomst = realloc(zoomst, sizeof(FRectangle) * (nzoomst + 1)); + if(zoomst == nil) sysfatal("realloc: %r"); + zoomst[nzoomst++] = (FRectangle){xmin, xmax, ymin, ymax}; xmin_ = convx(&screen->r, r.min.x); xmax_ = convx(&screen->r, r.max.x); ymin_ = convy(&screen->r, r.max.y); @@ -446,6 +626,20 @@ zoom(void) drawgraphs(); } +void +unzoom(void) +{ + if(nzoomst == 0) return; + xmin = zoomst[nzoomst - 1].xmin; + xmax = zoomst[nzoomst - 1].xmax; + ymin = zoomst[nzoomst - 1].ymin; + ymax = zoomst[nzoomst - 1].ymax; + zoomst = realloc(zoomst, sizeof(FRectangle) * --nzoomst); + if(zoomst == nil && nzoomst != 0) sysfatal("realloc: %r"); + draw(screen, screen->r, display->white, nil, ZP); + drawgraphs(); +} + void parsefns(int n, char **s) { @@ -491,14 +685,61 @@ parsesize(char *s) picy = strtol(s, &s, 0); } +void +alloccolors(void) +{ + int i; + + for(i = 0; i < nelem(icolors); i++){ + freeimage(colors[i]); + colors[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, icolors[i]); + } +} + +void +readout(Point p) +{ + int i, j; + double x, y; + vlong d, best; + Point bestp; + double ny, besty; + char buf[64]; + + /*TODO: do something more intelligent*/ + best = (uvlong)(-1)>>1; + for(j = screen->r.min.y; j < screen->r.max.y; j++) + for(i = screen->r.min.x; i < screen->r.max.x; i++){ + if(!pixels[(j - screen->r.min.y) * picx + (i - screen->r.min.x)]) continue; + d = (i - p.x) * (i - p.x) + (j - p.y) * (j - p.y); + if(d < best){ + best = d; + bestp = Pt(i, j); + } + } + ellipse(screen, bestp, 3, 3, 0, display->black, ZP); + x = convx(&screen->r, bestp.x); + y = convy(&screen->r, bestp.y); + besty = calc(&fns[0], x); + for(i = 1; i < nfns; i++){ + ny = calc(&fns[i], x); + if(abs(ny - y) < abs(besty - y)) + besty = ny; + } + snprint(buf, sizeof(buf), "%#.4g %#.4g", x, besty); + string(screen, addpt(Pt(10, 10), screen->r.min), display->black, ZP, display->defaultfont, buf); +} + void main(int argc, char **argv) { Event e; Rectangle r; int i; + static int lbut; ARGBEGIN { + case 'a': aflag++; break; case 'r': parserange(EARGF(usage())); break; case 's': parsesize(EARGF(usage())); break; case 'c': cflag++; break; @@ -516,20 +757,47 @@ main(int argc, char **argv) r.max.x = picx; r.max.y = picy; for(i = 0; i < nfns; i++) - drawgraph(&fns[i], &r); + drawgraph(&fns[i], &r, i % nelem(icolors)); if(write(1, imagedata, picx * picy * 3) < picx * picy * 3) sysfatal("write: %r"); } else { if(initdraw(nil, nil, "fplot") < 0) sysfatal("initdraw: %r"); einit(Emouse | Ekeyboard); + picx = Dx(screen->r); + picy = Dy(screen->r); + pixels = emalloc(picx * picy); + alloccolors(); drawgraphs(); for(;;) { switch(event(&e)) { + case Emouse: + if((e.mouse.buttons & 1) != 0) + zoom(); + if(((lbut|e.mouse.buttons) & 2) != 0){ + draw(screen, screen->r, display->white, nil, ZP); + drawgraphs(); + } + if((e.mouse.buttons & 2) != 0) + readout(e.mouse.xy); + if((~e.mouse.buttons & lbut & 4) != 0) + unzoom(); + lbut = e.mouse.buttons; + break; case Ekeyboard: switch(e.kbdc) { - case 'q': exits(nil); - case 'z': zoom(); + case 'q': case 127: exits(nil); break; + case 'y': + if(!isInf(ymin, 1) && !isInf(ymax, -1)){ + zoomst = realloc(zoomst, sizeof(FRectangle) * (nzoomst + 1)); + if(zoomst == nil) sysfatal("realloc: %r"); + zoomst[nzoomst++] = (FRectangle){xmin, xmax, ymin, ymax}; + ymin = gymin-0.05*(gymax-gymin); + ymax = gymax+0.05*(gymax-gymin); + draw(screen, screen->r, display->white, nil, ZP); + drawgraphs(); + } + break; } } } @@ -542,6 +810,11 @@ eresized(int new) if(new) { if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); + picx = Dx(screen->r); + picy = Dy(screen->r); + pixels = realloc(pixels, picx * picy); + if(pixels == nil) sysfatal("realloc: %r"); + alloccolors(); drawgraphs(); } }