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 pot(void) { sp--; *sp = pow(*sp, *(sp+1)); }
53 void osin(void) { *sp = sin(*sp); }
54 void ocos(void) { *sp = cos(*sp); }
55 void otan(void) { *sp = tan(*sp); }
56 void oasin(void) { *sp = asin(*sp); }
57 void oacos(void) { *sp = acos(*sp); }
58 void oatan(void) { *sp = atan(*sp); }
59 void osqrt(void) { *sp = sqrt(*sp); }
60 void oexp(void) { *sp = exp(*sp); }
61 void olog(void) { *sp = log10(*sp); }
62 void oln(void) { *sp = log(*sp); }
71 "+", OBINARY, 0, 0, add,
72 "-", OBINARY, 0, 0, sub,
73 "*", OBINARY, 0, 100, mul,
74 "/", OBINARY, 0, 100, div,
75 "^", OBINARY, 1, 200, pot,
76 "sin", OUNARY, 0, 50, osin,
77 "cos", OUNARY, 0, 50, ocos,
78 "tan", OUNARY, 0, 50, otan,
79 "asin", OUNARY, 0, 50, oasin,
80 "acos", OUNARY, 0, 50, oacos,
81 "atan", OUNARY, 0, 50, oatan,
82 "sqrt", OUNARY, 0, 50, osqrt,
83 "exp", OUNARY, 0, 50, oexp,
84 "log", OUNARY, 0, 50, olog,
85 "ln", OUNARY, 0, 50, oln,
104 double xmin = -10, xmax = 10;
105 double ymin = -10, ymax = 10;
109 int picx = 640, picy = 480;
116 v = mallocz(size, 1);
118 sysfatal("emalloc: %r");
119 setmalloctag(v, getcallerpc(&size));
126 if(c->len >= c->cap) {
128 c->p = realloc(c->p, sizeof(Op) * c->cap);
130 sysfatal("realloc: %r");
138 t->next = opstackbot;
150 sysfatal("stack underflow");
151 opstackbot = t->next;
153 sysfatal("non-operator pop");
154 o.type = t->op->type;
166 opstackbot = t->next;
182 t = emalloc(sizeof(*t));
185 t->val = strtod(*s, s);
203 for(o = ops; o < ops + nelem(ops); o++)
204 if(strncmp(*s, o->s, strlen(o->s)) == 0) {
210 for(c = consts; c < consts + nelem(consts); c++)
211 if(strncmp(*s, c->s, strlen(c->s)) == 0) {
217 sysfatal("syntax error at %s", *s);
222 parse(Code *c, char *s)
241 if(t->op->type == OBINARY)
242 while(opstackbot != nil && opstackbot->type == TOP &&
243 (opstackbot->op->prec > t->op->prec ||
244 t->op->rassoc && opstackbot->op->prec == t->op->prec))
252 while(opstackbot != nil && opstackbot->type == TOP)
254 if(opstackbot == nil)
255 sysfatal("mismatched parentheses");
260 sysfatal("unknown token type %d", t->type);
263 while(opstackbot != nil)
264 switch(opstackbot->type) {
269 sysfatal("mismatched parentheses");
271 sysfatal("syntax error");
283 for(o = c->p; o < c->p + c->len; o++)
285 case ONUMBER: case OVAR:
291 sysfatal("syntax error");
295 sysfatal("syntax error");
301 sysfatal("syntax error");
306 calc(Code *c, double x)
311 for(o = c->p; o < c->p + c->len; o++)
319 case OUNARY: case OBINARY:
326 convx(Rectangle *r, int x)
328 return (xmax - xmin) * (x - r->min.x) / (r->max.x - r->min.x) + xmin;
332 deconvx(Rectangle *r, double dx)
334 return (dx - xmin) * (r->max.x - r->min.x) / (xmax - xmin) + r->min.x + 0.5;
338 convy(Rectangle *r, int y)
340 return (ymax - ymin) * (r->max.y - y) / (r->max.y - r->min.y) + ymin;
344 deconvy(Rectangle *r, double dy)
346 return (ymax - dy) * (r->max.y - r->min.y) / (ymax - ymin) + r->min.y + 0.5;
355 if(x >= picx || y >= picy || x < 0 || y < 0)
357 p = imagedata + (picx * y + x) * 3;
358 p[0] = p[1] = p[2] = 0;
360 draw(screen, Rect(x, y, x + 1, y + 1), color, nil, ZP);
364 drawinter(Code *co, Rectangle *r, double x1, double x2, int n)
370 ix1 = deconvx(r, x1);
371 ix2 = deconvx(r, x2);
375 iy1 = deconvy(r, y1);
380 iy2 = deconvy(r, y2);
383 if(isNaN(y1) || isNaN(y2))
387 if(iy2 >= iy1 - 1 && iy2 <= iy1 + 1)
389 if(iy1 > r->max.y && iy2 > r->max.y)
391 if(iy1 < r->min.y && iy2 < r->min.y)
393 drawinter(co, r, x1, (x1 + x2) / 2, n + 1);
394 drawinter(co, r, (x1 + x2) / 2, x2, n + 1);
398 drawgraph(Code *co, Rectangle *r)
402 for(x = r->min.x; x < r->max.x; x++)
403 drawinter(co, r, convx(r, x), convx(r, x + 1), 0);
411 color = display->black;
412 for(i = 0; i < nfns; i++)
413 drawgraph(&fns[i], &screen->r);
414 flushimage(display, 1);
420 fprint(2, "usage: fplot [-c [-s size]] [-r range] functions ...\n");
429 double xmin_, xmax_, ymin_, ymax_;
433 if(r.min.x == 0 && r.min.y == 0 && r.max.x == 0 && r.max.y == 0)
435 xmin_ = convx(&screen->r, r.min.x);
436 xmax_ = convx(&screen->r, r.max.x);
437 ymin_ = convy(&screen->r, r.max.y);
438 ymax_ = convy(&screen->r, r.min.y);
443 draw(screen, screen->r, display->white, nil, ZP);
448 parsefns(int n, char **s)
454 fns = emalloc(sizeof(*fns) * n);
455 for(i = 0; i < nfns; i++) {
456 parse(&fns[i], s[i]);
457 cur = calcstack(&fns[i]);
461 stack = emalloc(sizeof(*stack) * max);
467 while(*s && !isdigit(*s)) s++;
469 xmin = strtod(s, &s);
470 while(*s && !isdigit(*s)) s++;
472 xmax = strtod(s, &s);
473 while(*s && !isdigit(*s)) s++;
475 ymin = strtod(s, &s);
476 while(*s && !isdigit(*s)) s++;
478 ymax = strtod(s, &s);
484 while(*s && !isdigit(*s)) s++;
486 picx = strtol(s, &s, 0);
487 while(*s && !isdigit(*s)) s++;
489 picy = strtol(s, &s, 0);
493 main(int argc, char **argv)
500 case 'r': parserange(EARGF(usage())); break;
501 case 's': parsesize(EARGF(usage())); break;
502 case 'c': cflag++; break;
507 setfcr(getfcr() & ~(FPZDIV | FPINVAL));
508 parsefns(argc, argv);
510 imagedata = emalloc(picx * picy * 3);
511 memset(imagedata, 0xFF, picx * picy * 3);
512 print("%11s %11d %11d %11d %11d ", "r8g8b8", 0, 0, picx, picy);
513 r.min.x = r.min.y = 0;
516 for(i = 0; i < nfns; i++)
517 drawgraph(&fns[i], &r);
518 if(write(1, imagedata, picx * picy * 3) < picx * picy * 3)
519 sysfatal("write: %r");
521 if(initdraw(nil, nil, "fplot") < 0)
522 sysfatal("initdraw: %r");
523 einit(Emouse | Ekeyboard);
529 case 'q': exits(nil);
541 if(getwindow(display, Refnone) < 0)
542 sysfatal("getwindow: %r");