shithub: riscv

Download patch

ref: e140aa0aeef3912d3d173b40603e86e5cf936299
parent: 2ebf18958eb21b5a2dcd7a1d5fa9e5c6f07179b0
author: aiju <[email protected]>
date: Sat Jul 16 14:57:18 EDT 2011

added fplot

--- /dev/null
+++ b/sys/src/cmd/fplot.c
@@ -1,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();
+	}
+}