]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/fplot.c
/sys/src/cmd/ndb/dns.h:
[plan9front.git] / sys / src / cmd / fplot.c
index 7a302d733007c7a4edd6066f52ee6634677e33e4..67e06394ee437ce130ec89fbe9499870fe1acadd 100644 (file)
@@ -45,19 +45,26 @@ 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); }
 void div(void) { sp--; *sp /= *(sp+1); }
+void mod(void) { sp--; *sp = fmod(*sp, *(sp+1)); }
 void pot(void) { sp--; *sp = pow(*sp, *(sp+1)); }
+void oabs(void) { *sp = fabs(*sp); }
 void osin(void) { *sp = sin(*sp); }
 void ocos(void) { *sp = cos(*sp); }
 void otan(void) { *sp = tan(*sp); }
+void osinh(void) { *sp = sinh(*sp); }
+void ocosh(void) { *sp = cosh(*sp); }
+void otanh(void) { *sp = tanh(*sp); }
 void oasin(void) { *sp = asin(*sp); }
 void oacos(void) { *sp = acos(*sp); }
 void oatan(void) { *sp = atan(*sp); }
 void osqrt(void) { *sp = sqrt(*sp); }
-void oexp(void) { *sp = sqrt(*sp); }
+void oexp(void) { *sp = exp(*sp); }
 void olog(void) { *sp = log10(*sp); }
 void oln(void) { *sp = log(*sp); }
 
@@ -68,21 +75,28 @@ 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,        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,
+       "sinh", OUNARY,         0,      400,    osinh,
+       "cosh", OUNARY,         0,      400,    ocosh,
+       "tanh", OUNARY,         0,      400,    otanh,
+       "abs",  OUNARY,         0,      400,    oabs,
+       "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 {
@@ -103,11 +117,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)
 {
@@ -241,7 +272,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;
@@ -319,6 +350,8 @@ calc(Code *c, double x)
                case OUNARY: case OBINARY:
                        o->f();
                }
+       if(*sp < gymin) gymin = *sp;
+       if(*sp > gymax) gymax = *sp;
        return *sp;
 }
 
@@ -347,7 +380,7 @@ deconvy(Rectangle *r, double dy)
 }
 
 void
-pixel(int x, int y)
+pixel(int x, int y, int c)
 {
        char *p;
 
@@ -355,13 +388,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;
@@ -373,12 +411,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;
@@ -390,34 +428,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");
 }
 
@@ -428,10 +615,13 @@ zoom(void)
        Rectangle r;
        double xmin_, xmax_, ymin_, ymax_;
        
-       m.buttons = 7;
+       m.buttons = 0;
        r = egetrect(1, &m);
-       if(r.min.x == 0 && r.min.y == 0 && r.max.x == 0 && r.max.y == 0)
+       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);
@@ -444,6 +634,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)
 {
@@ -464,16 +668,16 @@ parsefns(int n, char **s)
 void
 parserange(char *s)
 {
-       while(*s && !isdigit(*s)) s++;
+       while(*s && !isdigit(*s) && *s != '-') s++;
        if(*s == 0) return;
        xmin = strtod(s, &s);
-       while(*s && !isdigit(*s)) s++;
+       while(*s && !isdigit(*s) && *s != '-') s++;
        if(*s == 0) return;
        xmax = strtod(s, &s);
-       while(*s && !isdigit(*s)) s++;
+       while(*s && !isdigit(*s) && *s != '-') s++;
        if(*s == 0) return;
        ymin = strtod(s, &s);
-       while(*s && !isdigit(*s)) s++;
+       while(*s && !isdigit(*s) && *s != '-') s++;
        if(*s == 0) return;
        ymax = strtod(s, &s);
 }
@@ -489,14 +693,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;
@@ -514,20 +765,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;
                                }
                        }
                }
@@ -540,6 +818,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();
        }
 }