8 typedef struct Operator Operator;
9 typedef struct Token Token;
10 typedef struct Constant Constant;
11 typedef struct Code Code;
48 void add(void) { sp--; *sp += *(sp+1); }
49 void sub(void) { sp--; *sp -= *(sp+1); }
50 void mul(void) { sp--; *sp *= *(sp+1); }
51 void div(void) { sp--; *sp /= *(sp+1); }
52 void mod(void) { sp--; *sp = fmod(*sp, *(sp+1)); }
53 void pot(void) { sp--; *sp = pow(*sp, *(sp+1)); }
54 void osin(void) { *sp = sin(*sp); }
55 void ocos(void) { *sp = cos(*sp); }
56 void otan(void) { *sp = tan(*sp); }
57 void oasin(void) { *sp = asin(*sp); }
58 void oacos(void) { *sp = acos(*sp); }
59 void oatan(void) { *sp = atan(*sp); }
60 void osqrt(void) { *sp = sqrt(*sp); }
61 void oexp(void) { *sp = exp(*sp); }
62 void olog(void) { *sp = log10(*sp); }
63 void oln(void) { *sp = log(*sp); }
72 "+", OBINARY, 0, 0, add,
73 "-", OBINARY, 0, 0, sub,
74 "*", OBINARY, 0, 100, mul,
75 "/", OBINARY, 0, 100, div,
76 "%", OBINARY, 0, 100, mod,
77 "^", OBINARY, 1, 200, pot,
78 "sin", OUNARY, 0, 300, osin,
79 "cos", OUNARY, 0, 300, ocos,
80 "tan", OUNARY, 0, 300, otan,
81 "asin", OUNARY, 0, 300, oasin,
82 "acos", OUNARY, 0, 300, oacos,
83 "atan", OUNARY, 0, 300, oatan,
84 "sqrt", OUNARY, 0, 300, osqrt,
85 "exp", OUNARY, 0, 300, oexp,
86 "log", OUNARY, 0, 300, olog,
87 "ln", OUNARY, 0, 300, oln,
106 double xmin = -10, xmax = 10;
107 double ymin = -10, ymax = 10;
112 int picx = 640, picy = 480;
114 typedef struct FRectangle FRectangle;
116 double xmin, xmax, ymin, ymax;
125 v = mallocz(size, 1);
127 sysfatal("emalloc: %r");
128 setmalloctag(v, getcallerpc(&size));
135 if(c->len >= c->cap) {
137 c->p = realloc(c->p, sizeof(Op) * c->cap);
139 sysfatal("realloc: %r");
147 t->next = opstackbot;
159 sysfatal("stack underflow");
160 opstackbot = t->next;
162 sysfatal("non-operator pop");
163 o.type = t->op->type;
175 opstackbot = t->next;
191 t = emalloc(sizeof(*t));
194 t->val = strtod(*s, s);
212 for(o = ops; o < ops + nelem(ops); o++)
213 if(strncmp(*s, o->s, strlen(o->s)) == 0) {
219 for(c = consts; c < consts + nelem(consts); c++)
220 if(strncmp(*s, c->s, strlen(c->s)) == 0) {
226 sysfatal("syntax error at %s", *s);
231 parse(Code *c, char *s)
250 if(t->op->type == OBINARY)
251 while(opstackbot != nil && opstackbot->type == TOP &&
252 (opstackbot->op->prec > t->op->prec ||
253 t->op->rassoc && opstackbot->op->prec == t->op->prec))
261 while(opstackbot != nil && opstackbot->type == TOP)
263 if(opstackbot == nil)
264 sysfatal("mismatched parentheses");
269 sysfatal("unknown token type %d", t->type);
272 while(opstackbot != nil)
273 switch(opstackbot->type) {
278 sysfatal("mismatched parentheses");
280 sysfatal("syntax error");
292 for(o = c->p; o < c->p + c->len; o++)
294 case ONUMBER: case OVAR:
300 sysfatal("syntax error");
304 sysfatal("syntax error");
310 sysfatal("syntax error");
315 calc(Code *c, double x)
320 for(o = c->p; o < c->p + c->len; o++)
328 case OUNARY: case OBINARY:
331 if(*sp < gymin) gymin = *sp;
332 if(*sp > gymax) gymax = *sp;
337 convx(Rectangle *r, int x)
339 return (xmax - xmin) * (x - r->min.x) / (r->max.x - r->min.x) + xmin;
343 deconvx(Rectangle *r, double dx)
345 return (dx - xmin) * (r->max.x - r->min.x) / (xmax - xmin) + r->min.x + 0.5;
349 convy(Rectangle *r, int y)
351 return (ymax - ymin) * (r->max.y - y) / (r->max.y - r->min.y) + ymin;
355 deconvy(Rectangle *r, double dy)
357 return (ymax - dy) * (r->max.y - r->min.y) / (ymax - ymin) + r->min.y + 0.5;
366 if(x >= picx || y >= picy || x < 0 || y < 0)
368 p = imagedata + (picx * y + x) * 3;
369 p[0] = p[1] = p[2] = 0;
371 draw(screen, Rect(x, y, x + 1, y + 1), color, nil, ZP);
375 drawinter(Code *co, Rectangle *r, double x1, double x2, int n)
381 ix1 = deconvx(r, x1);
382 ix2 = deconvx(r, x2);
386 iy1 = deconvy(r, y1);
391 iy2 = deconvy(r, y2);
394 if(isNaN(y1) || isNaN(y2))
398 if(iy2 >= iy1 - 1 && iy2 <= iy1 + 1)
400 if(iy1 > r->max.y && iy2 > r->max.y)
402 if(iy1 < r->min.y && iy2 < r->min.y)
404 drawinter(co, r, x1, (x1 + x2) / 2, n + 1);
405 drawinter(co, r, (x1 + x2) / 2, x2, n + 1);
409 drawgraph(Code *co, Rectangle *r)
415 for(x = r->min.x; x < r->max.x; x++)
416 drawinter(co, r, convx(r, x), convx(r, x + 1), 0);
420 tickfmt(double d, double m, int n, char *fmt)
426 e2 = log10(fabs(m + n * d));
428 if(e2 >= 4 || e2 < -3){
429 x = ceil(e1-log10(d)-1);
430 snprint(fmt, 32, "%%.%de", x);
433 snprint(fmt, 32, "%%.%df", x);
438 xticklabel(char *fmt, double g, int p, int x, int y)
444 snprint(buf, sizeof(buf), fmt, g);
446 lr.max = addpt(lr.min, stringsize(display->defaultfont, buf));
447 lr = rectsubpt(lr, Pt(Dx(lr) / 2-1, 0));
448 if(lr.max.y >= screen->r.max.y){
450 lr = rectsubpt(lr, Pt(0, lr.min.y - ny));
452 if(rectinrect(lr, screen->r) && (lr.min.x > x || lr.max.x <= x)){
453 string(screen, lr.min, display->black, ZP, display->defaultfont, buf);
460 yticklabel(char *fmt, double g, int p, int x, int y)
466 snprint(buf, sizeof(buf), fmt, g);
468 lr.max = stringsize(display->defaultfont, buf);
469 lr = rectaddpt(lr, Pt(x-Dx(lr)-2, p - Dy(lr) / 2));
470 if(lr.min.x < screen->r.min.x){
472 lr = rectsubpt(lr, Pt(lr.min.x - nx, 0));
474 if(rectinrect(lr, screen->r) && (lr.min.y > y || lr.max.y <= y)){
475 string(screen, lr.min, display->black, ZP, display->defaultfont, buf);
482 calcm(double min, double max, int e, double *dp, double *mp)
487 if((e & 1) != 0) d *= 5;
489 if(min < 0 && max > 0)
498 if(dp != nil) *dp = d;
499 if(mp != nil) *mp = m;
500 return (max-m)*0.999/d;
504 ticks(double min, double max, double *dp, double *mp)
511 e = 2 * ceil(log10(max - min));
515 n = calcm(min, max, e, nil, nil);
517 if(n < 10) m = 10.0 / n;
525 calcm(min, max, beste, dp, mp);
526 return (max - *mp) / *dp;
533 double dx, dy, mx, my;
538 if(xmin < 0 && xmax > 0)
539 x = deconvx(&screen->r, 0);
541 x = screen->r.min.x+5;
542 line(screen, Pt(x, screen->r.min.y), Pt(x, screen->r.max.y), Endarrow, 0, 0, display->black, ZP);
543 if(ymin < 0 && ymax > 0)
544 y = deconvy(&screen->r, 0);
546 y = screen->r.max.y-5;
547 line(screen, Pt(screen->r.min.x, y), Pt(screen->r.max.x, y), 0, Endarrow, 0, display->black, ZP);
548 nx = ticks(xmin, xmax, &dx, &mx);
549 tickfmt(dx, mx, nx, fmt);
550 for(i = 0; i <= nx; i++){
551 p = deconvx(&screen->r, dx*i+mx);
552 if(xticklabel(fmt, dx*i+mx, p, x, y))
553 line(screen, Pt(p, y), Pt(p, y-5), 0, 0, 0, display->black, ZP);
555 ny = ticks(ymin, ymax, &dy, &my);
556 tickfmt(dy, my, ny, fmt);
557 for(i = 0; i <= ny; i++){
558 p = deconvy(&screen->r, dy*i+my);
559 if(yticklabel(fmt, dy*i+my, p, x, y))
560 line(screen, Pt(x, p), Pt(x+5, p), 0, 0, 0, display->black, ZP);
569 color = display->black;
570 for(i = 0; i < nfns; i++)
571 drawgraph(&fns[i], &screen->r);
574 flushimage(display, 1);
580 fprint(2, "usage: fplot [-a] [-c [-s size]] [-r range] functions ...\n");
589 double xmin_, xmax_, ymin_, ymax_;
593 if(Dx(r) < 1 || Dy(r) < 1)
595 zoomst = realloc(zoomst, sizeof(FRectangle) * (nzoomst + 1));
596 if(zoomst == nil) sysfatal("realloc: %r");
597 zoomst[nzoomst++] = (FRectangle){xmin, xmax, ymin, ymax};
598 xmin_ = convx(&screen->r, r.min.x);
599 xmax_ = convx(&screen->r, r.max.x);
600 ymin_ = convy(&screen->r, r.max.y);
601 ymax_ = convy(&screen->r, r.min.y);
606 draw(screen, screen->r, display->white, nil, ZP);
613 if(nzoomst == 0) return;
614 xmin = zoomst[nzoomst - 1].xmin;
615 xmax = zoomst[nzoomst - 1].xmax;
616 ymin = zoomst[nzoomst - 1].ymin;
617 ymax = zoomst[nzoomst - 1].ymax;
618 zoomst = realloc(zoomst, sizeof(FRectangle) * --nzoomst);
619 if(zoomst == nil && nzoomst != 0) sysfatal("realloc: %r");
620 draw(screen, screen->r, display->white, nil, ZP);
625 parsefns(int n, char **s)
631 fns = emalloc(sizeof(*fns) * n);
632 for(i = 0; i < nfns; i++) {
633 parse(&fns[i], s[i]);
634 cur = calcstack(&fns[i]);
638 stack = emalloc(sizeof(*stack) * max);
644 while(*s && !isdigit(*s) && *s != '-') s++;
646 xmin = strtod(s, &s);
647 while(*s && !isdigit(*s) && *s != '-') s++;
649 xmax = strtod(s, &s);
650 while(*s && !isdigit(*s) && *s != '-') s++;
652 ymin = strtod(s, &s);
653 while(*s && !isdigit(*s) && *s != '-') s++;
655 ymax = strtod(s, &s);
661 while(*s && !isdigit(*s)) s++;
663 picx = strtol(s, &s, 0);
664 while(*s && !isdigit(*s)) s++;
666 picy = strtol(s, &s, 0);
670 main(int argc, char **argv)
678 case 'a': aflag++; break;
679 case 'r': parserange(EARGF(usage())); break;
680 case 's': parsesize(EARGF(usage())); break;
681 case 'c': cflag++; break;
686 setfcr(getfcr() & ~(FPZDIV | FPINVAL));
687 parsefns(argc, argv);
689 imagedata = emalloc(picx * picy * 3);
690 memset(imagedata, 0xFF, picx * picy * 3);
691 print("%11s %11d %11d %11d %11d ", "r8g8b8", 0, 0, picx, picy);
692 r.min.x = r.min.y = 0;
695 for(i = 0; i < nfns; i++)
696 drawgraph(&fns[i], &r);
697 if(write(1, imagedata, picx * picy * 3) < picx * picy * 3)
698 sysfatal("write: %r");
700 if(initdraw(nil, nil, "fplot") < 0)
701 sysfatal("initdraw: %r");
702 einit(Emouse | Ekeyboard);
707 if((e.mouse.buttons & 1) != 0)
709 if((~e.mouse.buttons & lbut & 4) != 0)
711 lbut = e.mouse.buttons;
715 case 'q': case 127: exits(nil); break;
717 if(!isInf(ymin, 1) && !isInf(ymax, -1)){
718 zoomst = realloc(zoomst, sizeof(FRectangle) * (nzoomst + 1));
719 if(zoomst == nil) sysfatal("realloc: %r");
720 zoomst[nzoomst++] = (FRectangle){xmin, xmax, ymin, ymax};
721 ymin = gymin-0.05*(gymax-gymin);
722 ymax = gymax+0.05*(gymax-gymin);
723 draw(screen, screen->r, display->white, nil, ZP);
737 if(getwindow(display, Refnone) < 0)
738 sysfatal("getwindow: %r");