8 typedef struct Operator Operator;
9 typedef struct Token Token;
10 typedef struct Constant Constant;
11 typedef struct Code Code;
48 void omax(void) { sp--; if(sp[1]>*sp) *sp = sp[1]; }
49 void omin(void) { sp--; if(sp[1]<*sp) *sp = sp[1]; }
50 void add(void) { sp--; *sp += *(sp+1); }
51 void sub(void) { sp--; *sp -= *(sp+1); }
52 void mul(void) { sp--; *sp *= *(sp+1); }
53 void div(void) { sp--; *sp /= *(sp+1); }
54 void mod(void) { sp--; *sp = fmod(*sp, *(sp+1)); }
55 void pot(void) { sp--; *sp = pow(*sp, *(sp+1)); }
56 void oabs(void) { *sp = fabs(*sp); }
57 void osin(void) { *sp = sin(*sp); }
58 void ocos(void) { *sp = cos(*sp); }
59 void otan(void) { *sp = tan(*sp); }
60 void osinh(void) { *sp = sinh(*sp); }
61 void ocosh(void) { *sp = cosh(*sp); }
62 void otanh(void) { *sp = tanh(*sp); }
63 void oasin(void) { *sp = asin(*sp); }
64 void oacos(void) { *sp = acos(*sp); }
65 void oatan(void) { *sp = atan(*sp); }
66 void osqrt(void) { *sp = sqrt(*sp); }
67 void oexp(void) { *sp = exp(*sp); }
68 void olog(void) { *sp = log10(*sp); }
69 void oln(void) { *sp = log(*sp); }
78 "max", OBINARY, 0, 0, omax,
79 "min", OBINARY, 0, 0, omax,
80 "+", OBINARY, 0, 100, add,
81 "-", OBINARY, 0, 100, sub,
82 "*", OBINARY, 0, 200, mul,
83 "/", OBINARY, 0, 200, div,
84 "%", OBINARY, 0, 200, mod,
85 "^", OBINARY, 1, 300, pot,
86 "sinh", OUNARY, 0, 400, osinh,
87 "cosh", OUNARY, 0, 400, ocosh,
88 "tanh", OUNARY, 0, 400, otanh,
89 "abs", OUNARY, 0, 400, oabs,
90 "sin", OUNARY, 0, 400, osin,
91 "cos", OUNARY, 0, 400, ocos,
92 "tan", OUNARY, 0, 400, otan,
93 "asin", OUNARY, 0, 400, oasin,
94 "acos", OUNARY, 0, 400, oacos,
95 "atan", OUNARY, 0, 400, oatan,
96 "sqrt", OUNARY, 0, 400, osqrt,
97 "exp", OUNARY, 0, 400, oexp,
98 "log", OUNARY, 0, 400, olog,
99 "ln", OUNARY, 0, 400, oln,
118 double xmin = -10, xmax = 10;
119 double ymin = -10, ymax = 10;
130 Image *colors[nelem(icolors)];
134 int picx = 640, picy = 480;
136 typedef struct FRectangle FRectangle;
138 double xmin, xmax, ymin, ymax;
147 v = mallocz(size, 1);
149 sysfatal("emalloc: %r");
150 setmalloctag(v, getcallerpc(&size));
157 if(c->len >= c->cap) {
159 c->p = realloc(c->p, sizeof(Op) * c->cap);
161 sysfatal("realloc: %r");
169 t->next = opstackbot;
181 sysfatal("stack underflow");
182 opstackbot = t->next;
184 sysfatal("non-operator pop");
185 o.type = t->op->type;
197 opstackbot = t->next;
213 t = emalloc(sizeof(*t));
216 t->val = strtod(*s, s);
234 for(o = ops; o < ops + nelem(ops); o++)
235 if(strncmp(*s, o->s, strlen(o->s)) == 0) {
241 for(c = consts; c < consts + nelem(consts); c++)
242 if(strncmp(*s, c->s, strlen(c->s)) == 0) {
248 sysfatal("syntax error at %s", *s);
253 parse(Code *c, char *s)
272 if(t->op->type == OBINARY)
273 while(opstackbot != nil && opstackbot->type == TOP &&
274 (opstackbot->op->prec > t->op->prec ||
275 !t->op->rassoc && opstackbot->op->prec == t->op->prec))
283 while(opstackbot != nil && opstackbot->type == TOP)
285 if(opstackbot == nil)
286 sysfatal("mismatched parentheses");
291 sysfatal("unknown token type %d", t->type);
294 while(opstackbot != nil)
295 switch(opstackbot->type) {
300 sysfatal("mismatched parentheses");
302 sysfatal("syntax error");
314 for(o = c->p; o < c->p + c->len; o++)
316 case ONUMBER: case OVAR:
322 sysfatal("syntax error");
326 sysfatal("syntax error");
332 sysfatal("syntax error");
337 calc(Code *c, double x)
342 for(o = c->p; o < c->p + c->len; o++)
350 case OUNARY: case OBINARY:
353 if(*sp < gymin) gymin = *sp;
354 if(*sp > gymax) gymax = *sp;
359 convx(Rectangle *r, int x)
361 return (xmax - xmin) * (x - r->min.x) / (r->max.x - r->min.x) + xmin;
365 deconvx(Rectangle *r, double dx)
367 return (dx - xmin) * (r->max.x - r->min.x) / (xmax - xmin) + r->min.x + 0.5;
371 convy(Rectangle *r, int y)
373 return (ymax - ymin) * (r->max.y - y) / (r->max.y - r->min.y) + ymin;
377 deconvy(Rectangle *r, double dy)
379 return (ymax - dy) * (r->max.y - r->min.y) / (ymax - ymin) + r->min.y + 0.5;
383 pixel(int x, int y, int c)
388 if(x >= picx || y >= picy || x < 0 || y < 0)
390 p = imagedata + (picx * y + x) * 3;
391 p[0] = icolors[c] >> 24;
392 p[1] = icolors[c] >> 16;
393 p[2] = icolors[c] >> 8;
395 draw(screen, Rect(x, y, x + 1, y + 1), colors[c], nil, ZP);
396 if(ptinrect(Pt(x, y), screen->r))
397 pixels[picx * (y - screen->r.min.y) + (x - screen->r.min.x)] = 1;
402 drawinter(Code *co, Rectangle *r, double x1, double x2, int n, int c)
408 ix1 = deconvx(r, x1);
409 ix2 = deconvx(r, x2);
413 iy1 = deconvy(r, y1);
418 iy2 = deconvy(r, y2);
421 if(isNaN(y1) || isNaN(y2))
425 if(iy2 >= iy1 - 1 && iy2 <= iy1 + 1)
427 if(iy1 > r->max.y && iy2 > r->max.y)
429 if(iy1 < r->min.y && iy2 < r->min.y)
431 drawinter(co, r, x1, (x1 + x2) / 2, n + 1, c);
432 drawinter(co, r, (x1 + x2) / 2, x2, n + 1, c);
436 drawgraph(Code *co, Rectangle *r, int c)
440 for(x = r->min.x; x < r->max.x; x++)
441 drawinter(co, r, convx(r, x), convx(r, x + 1), 0, c);
445 tickfmt(double d, double m, int n, char *fmt)
451 e2 = log10(fabs(m + n * d));
453 if(e2 >= 4 || e2 < -3){
454 x = ceil(e1-log10(d)-1);
455 snprint(fmt, 32, "%%.%de", x);
458 snprint(fmt, 32, "%%.%df", x);
463 xticklabel(char *fmt, double g, int p, int x, int y)
469 snprint(buf, sizeof(buf), fmt, g);
471 lr.max = addpt(lr.min, stringsize(display->defaultfont, buf));
472 lr = rectsubpt(lr, Pt(Dx(lr) / 2-1, 0));
473 if(lr.max.y >= screen->r.max.y){
475 lr = rectsubpt(lr, Pt(0, lr.min.y - ny));
477 if(rectinrect(lr, screen->r) && (lr.min.x > x || lr.max.x <= x)){
478 string(screen, lr.min, display->black, ZP, display->defaultfont, buf);
485 yticklabel(char *fmt, double g, int p, int x, int y)
491 snprint(buf, sizeof(buf), fmt, g);
493 lr.max = stringsize(display->defaultfont, buf);
494 lr = rectaddpt(lr, Pt(x-Dx(lr)-2, p - Dy(lr) / 2));
495 if(lr.min.x < screen->r.min.x){
497 lr = rectsubpt(lr, Pt(lr.min.x - nx, 0));
499 if(rectinrect(lr, screen->r) && (lr.min.y > y || lr.max.y <= y)){
500 string(screen, lr.min, display->black, ZP, display->defaultfont, buf);
507 calcm(double min, double max, int e, double *dp, double *mp)
512 if((e & 1) != 0) d *= 5;
514 if(min < 0 && max > 0)
523 if(dp != nil) *dp = d;
524 if(mp != nil) *mp = m;
525 return (max-m)*0.999/d;
529 ticks(double min, double max, double *dp, double *mp)
536 e = 2 * ceil(log10(max - min));
540 n = calcm(min, max, e, nil, nil);
542 if(n < 10) m = 10.0 / n;
550 calcm(min, max, beste, dp, mp);
551 return (max - *mp) / *dp;
558 double dx, dy, mx, my;
563 if(xmin < 0 && xmax > 0)
564 x = deconvx(&screen->r, 0);
566 x = screen->r.min.x+5;
567 line(screen, Pt(x, screen->r.min.y), Pt(x, screen->r.max.y), Endarrow, 0, 0, display->black, ZP);
568 if(ymin < 0 && ymax > 0)
569 y = deconvy(&screen->r, 0);
571 y = screen->r.max.y-5;
572 line(screen, Pt(screen->r.min.x, y), Pt(screen->r.max.x, y), 0, Endarrow, 0, display->black, ZP);
573 nx = ticks(xmin, xmax, &dx, &mx);
574 tickfmt(dx, mx, nx, fmt);
575 for(i = 0; i <= nx; i++){
576 p = deconvx(&screen->r, dx*i+mx);
577 if(xticklabel(fmt, dx*i+mx, p, x, y))
578 line(screen, Pt(p, y), Pt(p, y-5), 0, 0, 0, display->black, ZP);
580 ny = ticks(ymin, ymax, &dy, &my);
581 tickfmt(dy, my, ny, fmt);
582 for(i = 0; i <= ny; i++){
583 p = deconvy(&screen->r, dy*i+my);
584 if(yticklabel(fmt, dy*i+my, p, x, y))
585 line(screen, Pt(x, p), Pt(x+5, p), 0, 0, 0, display->black, ZP);
596 memset(pixels, 0, picx * picy);
597 for(i = 0; i < nfns; i++)
598 drawgraph(&fns[i], &screen->r, i % nelem(icolors));
601 flushimage(display, 1);
607 fprint(2, "usage: fplot [-a] [-c [-s size]] [-r range] functions ...\n");
616 double xmin_, xmax_, ymin_, ymax_;
620 if(Dx(r) < 1 || Dy(r) < 1)
622 zoomst = realloc(zoomst, sizeof(FRectangle) * (nzoomst + 1));
623 if(zoomst == nil) sysfatal("realloc: %r");
624 zoomst[nzoomst++] = (FRectangle){xmin, xmax, ymin, ymax};
625 xmin_ = convx(&screen->r, r.min.x);
626 xmax_ = convx(&screen->r, r.max.x);
627 ymin_ = convy(&screen->r, r.max.y);
628 ymax_ = convy(&screen->r, r.min.y);
633 draw(screen, screen->r, display->white, nil, ZP);
640 if(nzoomst == 0) return;
641 xmin = zoomst[nzoomst - 1].xmin;
642 xmax = zoomst[nzoomst - 1].xmax;
643 ymin = zoomst[nzoomst - 1].ymin;
644 ymax = zoomst[nzoomst - 1].ymax;
645 zoomst = realloc(zoomst, sizeof(FRectangle) * --nzoomst);
646 if(zoomst == nil && nzoomst != 0) sysfatal("realloc: %r");
647 draw(screen, screen->r, display->white, nil, ZP);
652 parsefns(int n, char **s)
658 fns = emalloc(sizeof(*fns) * n);
659 for(i = 0; i < nfns; i++) {
660 parse(&fns[i], s[i]);
661 cur = calcstack(&fns[i]);
665 stack = emalloc(sizeof(*stack) * max);
671 while(*s && !isdigit(*s) && *s != '-') s++;
673 xmin = strtod(s, &s);
674 while(*s && !isdigit(*s) && *s != '-') s++;
676 xmax = strtod(s, &s);
677 while(*s && !isdigit(*s) && *s != '-') s++;
679 ymin = strtod(s, &s);
680 while(*s && !isdigit(*s) && *s != '-') s++;
682 ymax = strtod(s, &s);
688 while(*s && !isdigit(*s)) s++;
690 picx = strtol(s, &s, 0);
691 while(*s && !isdigit(*s)) s++;
693 picy = strtol(s, &s, 0);
701 for(i = 0; i < nelem(icolors); i++){
702 freeimage(colors[i]);
703 colors[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, icolors[i]);
717 /*TODO: do something more intelligent*/
718 best = (uvlong)(-1)>>1;
719 for(j = screen->r.min.y; j < screen->r.max.y; j++)
720 for(i = screen->r.min.x; i < screen->r.max.x; i++){
721 if(!pixels[(j - screen->r.min.y) * picx + (i - screen->r.min.x)]) continue;
722 d = (i - p.x) * (i - p.x) + (j - p.y) * (j - p.y);
728 ellipse(screen, bestp, 3, 3, 0, display->black, ZP);
729 x = convx(&screen->r, bestp.x);
730 y = convy(&screen->r, bestp.y);
731 besty = calc(&fns[0], x);
732 for(i = 1; i < nfns; i++){
733 ny = calc(&fns[i], x);
734 if(abs(ny - y) < abs(besty - y))
737 snprint(buf, sizeof(buf), "%#.4g %#.4g", x, besty);
738 string(screen, addpt(Pt(10, 10), screen->r.min), display->black, ZP, display->defaultfont, buf);
742 main(int argc, char **argv)
750 case 'a': aflag++; break;
751 case 'r': parserange(EARGF(usage())); break;
752 case 's': parsesize(EARGF(usage())); break;
753 case 'c': cflag++; break;
758 setfcr(getfcr() & ~(FPZDIV | FPINVAL));
759 parsefns(argc, argv);
761 imagedata = emalloc(picx * picy * 3);
762 memset(imagedata, 0xFF, picx * picy * 3);
763 print("%11s %11d %11d %11d %11d ", "r8g8b8", 0, 0, picx, picy);
764 r.min.x = r.min.y = 0;
767 for(i = 0; i < nfns; i++)
768 drawgraph(&fns[i], &r, i % nelem(icolors));
769 if(write(1, imagedata, picx * picy * 3) < picx * picy * 3)
770 sysfatal("write: %r");
772 if(initdraw(nil, nil, "fplot") < 0)
773 sysfatal("initdraw: %r");
774 einit(Emouse | Ekeyboard);
775 picx = Dx(screen->r);
776 picy = Dy(screen->r);
777 pixels = emalloc(picx * picy);
783 if((e.mouse.buttons & 1) != 0)
785 if(((lbut|e.mouse.buttons) & 2) != 0){
786 draw(screen, screen->r, display->white, nil, ZP);
789 if((e.mouse.buttons & 2) != 0)
791 if((~e.mouse.buttons & lbut & 4) != 0)
793 lbut = e.mouse.buttons;
797 case 'q': case 127: exits(nil); break;
799 if(!isInf(ymin, 1) && !isInf(ymax, -1)){
800 zoomst = realloc(zoomst, sizeof(FRectangle) * (nzoomst + 1));
801 if(zoomst == nil) sysfatal("realloc: %r");
802 zoomst[nzoomst++] = (FRectangle){xmin, xmax, ymin, ymax};
803 ymin = gymin-0.05*(gymax-gymin);
804 ymax = gymax+0.05*(gymax-gymin);
805 draw(screen, screen->r, display->white, nil, ZP);
819 if(getwindow(display, Refnone) < 0)
820 sysfatal("getwindow: %r");
821 picx = Dx(screen->r);
822 picy = Dy(screen->r);
823 pixels = realloc(pixels, picx * picy);
824 if(pixels == nil) sysfatal("realloc: %r");