added fplot

This commit is contained in:
aiju 2011-07-16 18:57:18 +02:00
parent 2ebf18958e
commit e140aa0aee

467
sys/src/cmd/fplot.c Normal file
View file

@ -0,0 +1,467 @@
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <draw.h>
#include <event.h>
typedef struct Op Op;
typedef struct Operator Operator;
typedef struct Token Token;
typedef struct Constant Constant;
typedef struct Code Code;
enum {
ONONE,
ONUMBER,
OVAR,
OUNARY,
OBINARY,
};
struct Op {
int type;
union {
void (*f)(void);
double val;
};
};
enum {
TNONE,
TNUMBER,
TVAR,
TOP,
TPARENL,
TPARENR,
};
struct Token {
int type;
union {
Operator *op;
double val;
};
Token *next;
};
double *stack, *sp;
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 pot(void) { sp--; *sp = pow(*sp, *(sp+1)); }
void osin(void) { *sp = sin(*sp); }
void ocos(void) { *sp = cos(*sp); }
void otan(void) { *sp = tan(*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 olog(void) { *sp = log10(*sp); }
void oln(void) { *sp = log(*sp); }
struct Operator {
char *s;
char type;
char rassoc;
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,
"log", OUNARY, 0, 50, olog,
"ln", OUNARY, 0, 50, oln,
};
struct Constant {
char *s;
double val;
} consts[] = {
"pi", 3.14159265359,
"π", 3.14159265359,
"e", 2.71828182846,
};
struct Code {
Op* p;
int len, cap;
} *fns;
int nfns;
Token *opstackbot;
double xmin = -10, xmax = 10;
double ymin = -10, ymax = 10;
void *
emalloc(int size)
{
void *v;
v = mallocz(size, 1);
if(v == nil)
sysfatal("emalloc: %r");
setmalloctag(v, getcallerpc(&size));
return v;
}
void
addop(Code *c, Op o)
{
if(c->len >= c->cap) {
c->cap += 32;
c->p = realloc(c->p, sizeof(Op) * c->cap);
if(c->p == nil)
sysfatal("realloc: %r");
}
c->p[c->len++] = o;
}
void
push(Token *t)
{
t->next = opstackbot;
opstackbot = t;
}
void
pop(Code *c)
{
Token *t;
Op o;
t = opstackbot;
if(t == nil)
sysfatal("stack underflow");
opstackbot = t->next;
if(t->type != TOP)
sysfatal("non-operator pop");
o.type = t->op->type;
o.f = t->op->f;
addop(c, o);
free(t);
}
void
popdel(void)
{
Token *t;
t = opstackbot;
opstackbot = t->next;
free(t);
}
Token *
lex(char **s)
{
Token *t;
Operator *o;
Constant *c;
while(isspace(**s))
(*s)++;
if(**s == 0)
return nil;
t = emalloc(sizeof(*t));
if(isdigit(**s)) {
t->type = TNUMBER;
t->val = strtod(*s, s);
return t;
}
if(**s == '(') {
t->type = TPARENL;
(*s)++;
return t;
}
if(**s == ')') {
t->type = TPARENR;
(*s)++;
return t;
}
if(**s == 'x') {
t->type = TVAR;
(*s)++;
return t;
}
for(o = ops; o < ops + nelem(ops); o++)
if(strncmp(*s, o->s, strlen(o->s)) == 0) {
t->type = TOP;
t->op = o;
*s += strlen(o->s);
return t;
}
for(c = consts; c < consts + nelem(consts); c++)
if(strncmp(*s, c->s, strlen(c->s)) == 0) {
t->type = TNUMBER;
t->val = c->val;
*s += strlen(c->s);
return t;
}
sysfatal("syntax error at %s", *s);
return nil;
}
void
parse(Code *c, char *s)
{
Token *t;
Op o;
while(t = lex(&s)) {
switch(t->type) {
case TNUMBER:
o.type = ONUMBER;
o.val = t->val;
addop(c, o);
free(t);
break;
case TVAR:
o.type = OVAR;
addop(c, o);
free(t);
break;
case TOP:
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))
pop(c);
push(t);
break;
case TPARENL:
push(t);
break;
case TPARENR:
while(opstackbot != nil && opstackbot->type == TOP)
pop(c);
if(opstackbot == nil)
sysfatal("mismatched parentheses");
popdel();
free(t);
break;
default:
sysfatal("unknown token type %d", t->type);
}
}
while(opstackbot != nil)
switch(opstackbot->type) {
case TOP:
pop(c);
break;
case TPARENL:
sysfatal("mismatched parentheses");
default:
sysfatal("syntax error");
}
}
int
calcstack(Code *c)
{
int cur, max;
Op *o;
cur = 0;
max = 0;
for(o = c->p; o < c->p + c->len; o++)
switch(o->type) {
case ONUMBER: case OVAR:
if(++cur > max)
max = cur;
break;
case OUNARY:
if(cur == 0)
sysfatal("syntax error");
break;
case OBINARY:
if(cur <= 1)
sysfatal("syntax error");
cur--;
break;
}
if(cur != 1)
sysfatal("syntax error");
return max;
}
double
calc(Code *c, double x)
{
Op *o;
sp = stack - 1;
for(o = c->p; o < c->p + c->len; o++)
switch(o->type) {
case ONUMBER:
*++sp = o->val;
break;
case OVAR:
*++sp = x;
break;
case OUNARY: case OBINARY:
o->f();
}
return *sp;
}
double
convx(Image *i, int x)
{
return (xmax - xmin) * (x - i->r.min.x) / (i->r.max.x - i->r.min.x) + xmin;
}
int
deconvx(Image *i, double dx)
{
return (dx - xmin) * (i->r.max.x - i->r.min.x) / (xmax - xmin) + i->r.min.x + 0.5;
}
double
convy(Image *i, int y)
{
return (ymax - ymin) * (i->r.max.y - y) / (i->r.max.y - i->r.min.y) + ymin;
}
int
deconvy(Image *i, double dy)
{
return (ymax - dy) * (i->r.max.y - i->r.min.y) / (ymax - ymin) + i->r.min.y + 0.5;
}
void
drawinter(Code *co, Image *i, Image *c, double x1, double x2, int n)
{
double y1, y2;
int iy1, iy2;
int ix1, ix2;
ix1 = deconvx(i, x1);
ix2 = deconvx(i, x2);
iy1 = iy2 = 0;
y1 = calc(co, x1);
if(!isNaN(y1)) {
iy1 = deconvy(i, y1);
draw(i, Rect(ix1, iy1, ix1 + 1, iy1 + 1), c, nil, ZP);
}
y2 = calc(co, x2);
if(!isNaN(y2)) {
iy2 = deconvy(i, y2);
draw(i, Rect(ix2, iy2, ix2 + 1, iy2 + 1), c, nil, ZP);
}
if(!isNaN(y1) && !isNaN(y2) && (iy2 < iy1 - 1 || iy2 > iy1 + 1) && n < 10) {
drawinter(co, i, c, x1, (x1 + x2) / 2, n + 1);
drawinter(co, i, c, (x1 + x2) / 2, x2, n + 1);
}
}
void
drawgraph(Code *co, Image *i, Image *c)
{
int x;
for(x = i->r.min.x; x < i->r.max.x; x++)
drawinter(co, i, c, convx(i, x), convx(i, x + 1), 0);
}
void
drawgraphs(void)
{
int i;
for(i = 0; i < nfns; i++)
drawgraph(&fns[i], screen, display->black);
flushimage(display, 1);
}
void
usage(void)
{
fprint(2, "usage: fplot function\n");
exits("usage");
}
void
zoom(void)
{
Mouse m;
Rectangle r;
double xmin_, xmax_, ymin_, ymax_;
m.buttons = 7;
r = egetrect(1, &m);
if(r.min.x == 0 && r.min.y == 0 && r.max.x == 0 && r.max.y == 0)
return;
xmin_ = convx(screen, r.min.x);
xmax_ = convx(screen, r.max.x);
ymin_ = convy(screen, r.max.y);
ymax_ = convy(screen, r.min.y);
xmin = xmin_;
xmax = xmax_;
ymin = ymin_;
ymax = ymax_;
draw(screen, screen->r, display->white, nil, ZP);
drawgraphs();
}
void
parsefns(int n, char **s)
{
int i, max, cur;
max = 0;
nfns = n;
fns = emalloc(sizeof(*fns) * n);
for(i = 0; i < nfns; i++) {
parse(&fns[i], s[i]);
cur = calcstack(&fns[i]);
if(cur > max)
max = cur;
}
stack = emalloc(sizeof(*stack) * max);
}
void
main(int argc, char **argv)
{
Event e;
if(argc < 2)
usage();
setfcr(getfcr() & ~(FPZDIV | FPINVAL));
parsefns(argc - 1, argv + 1);
if(initdraw(nil, nil, "fplot") < 0)
sysfatal("initdraw: %r");
einit(Emouse | Ekeyboard);
drawgraphs();
for(;;) {
switch(event(&e)) {
case Ekeyboard:
switch(e.kbdc) {
case 'q': exits(nil);
case 'z': zoom();
}
}
}
}
void
eresized(int new)
{
if(new) {
if(getwindow(display, Refnone) < 0)
sysfatal("getwindow: %r");
drawgraphs();
}
}