};
double *stack, *sp;
+void omax(void) { sp--; if(sp[1]>*sp) *sp = sp[1]; }
+void omin(void) { sp--; if(sp[1]<*sp) *sp = sp[1]; }
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 mod(void) { sp--; *sp = fmod(*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 oacos(void) { *sp = acos(*sp); }
void oatan(void) { *sp = atan(*sp); }
void osqrt(void) { *sp = sqrt(*sp); }
-void oexp(void) { *sp = sqrt(*sp); }
+void oexp(void) { *sp = exp(*sp); }
void olog(void) { *sp = log10(*sp); }
void oln(void) { *sp = log(*sp); }
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,
- "exp", OUNARY, 0, 50, oexp,
- "log", OUNARY, 0, 50, olog,
- "ln", OUNARY, 0, 50, oln,
+ "max", OBINARY, 0, 0, omax,
+ "min", OBINARY, 0, 0, omax,
+ "+", OBINARY, 0, 100, add,
+ "-", OBINARY, 0, 100, sub,
+ "*", OBINARY, 0, 200, mul,
+ "/", OBINARY, 0, 200, div,
+ "%", OBINARY, 0, 200, mod,
+ "^", OBINARY, 1, 300, pot,
+ "sin", OUNARY, 0, 400, osin,
+ "cos", OUNARY, 0, 400, ocos,
+ "tan", OUNARY, 0, 400, otan,
+ "asin", OUNARY, 0, 400, oasin,
+ "acos", OUNARY, 0, 400, oacos,
+ "atan", OUNARY, 0, 400, oatan,
+ "sqrt", OUNARY, 0, 400, osqrt,
+ "exp", OUNARY, 0, 400, oexp,
+ "log", OUNARY, 0, 400, olog,
+ "ln", OUNARY, 0, 400, oln,
};
struct Constant {
Token *opstackbot;
double xmin = -10, xmax = 10;
double ymin = -10, ymax = 10;
-Image *color;
-int cflag;
+double gymin, gymax;
+int icolors[] = {
+ DBlack,
+ 0xCC0000FF,
+ 0x00CC00FF,
+ 0x0000CCFF,
+ 0xFF00CCFF,
+ 0xFFAA00FF,
+ 0xCCCC00FF,
+};
+Image *colors[nelem(icolors)];
+int cflag, aflag;
char *imagedata;
+char *pixels;
int picx = 640, picy = 480;
+typedef struct FRectangle FRectangle;
+struct FRectangle {
+ double xmin, xmax, ymin, ymax;
+} *zoomst;
+int nzoomst;
+
void *
emalloc(int size)
{
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))
+ !t->op->rassoc && opstackbot->op->prec == t->op->prec))
pop(c);
push(t);
break;
case OUNARY: case OBINARY:
o->f();
}
+ if(*sp < gymin) gymin = *sp;
+ if(*sp > gymax) gymax = *sp;
return *sp;
}
}
void
-pixel(int x, int y)
+pixel(int x, int y, int c)
{
char *p;
if(x >= picx || y >= picy || x < 0 || y < 0)
return;
p = imagedata + (picx * y + x) * 3;
- p[0] = p[1] = p[2] = 0;
- } else
- draw(screen, Rect(x, y, x + 1, y + 1), color, nil, ZP);
+ p[0] = icolors[c] >> 24;
+ p[1] = icolors[c] >> 16;
+ p[2] = icolors[c] >> 8;
+ }else{
+ draw(screen, Rect(x, y, x + 1, y + 1), colors[c], nil, ZP);
+ if(ptinrect(Pt(x, y), screen->r))
+ pixels[picx * (y - screen->r.min.y) + (x - screen->r.min.x)] = 1;
+ }
}
void
-drawinter(Code *co, Rectangle *r, double x1, double x2, int n)
+drawinter(Code *co, Rectangle *r, double x1, double x2, int n, int c)
{
double y1, y2;
int iy1, iy2;
y1 = calc(co, x1);
if(!isNaN(y1)) {
iy1 = deconvy(r, y1);
- pixel(ix1, iy1);
+ pixel(ix1, iy1, c);
}
y2 = calc(co, x2);
if(!isNaN(y2)) {
iy2 = deconvy(r, y2);
- pixel(ix2, iy2);
+ pixel(ix2, iy2, c);
}
if(isNaN(y1) || isNaN(y2))
return;
return;
if(iy1 < r->min.y && iy2 < r->min.y)
return;
- drawinter(co, r, x1, (x1 + x2) / 2, n + 1);
- drawinter(co, r, (x1 + x2) / 2, x2, n + 1);
+ drawinter(co, r, x1, (x1 + x2) / 2, n + 1, c);
+ drawinter(co, r, (x1 + x2) / 2, x2, n + 1, c);
}
void
-drawgraph(Code *co, Rectangle *r)
+drawgraph(Code *co, Rectangle *r, int c)
{
int x;
for(x = r->min.x; x < r->max.x; x++)
- drawinter(co, r, convx(r, x), convx(r, x + 1), 0);
+ drawinter(co, r, convx(r, x), convx(r, x + 1), 0, c);
+}
+
+void
+tickfmt(double d, double m, int n, char *fmt)
+{
+ double e1, e2;
+ int x;
+
+ e1 = log10(fabs(m));
+ e2 = log10(fabs(m + n * d));
+ if(e2 > e1) e1 = e2;
+ if(e2 >= 4 || e2 < -3){
+ x = ceil(e1-log10(d)-1);
+ snprint(fmt, 32, "%%.%de", x);
+ }else{
+ x = ceil(-log10(d));
+ snprint(fmt, 32, "%%.%df", x);
+ }
+}
+
+int
+xticklabel(char *fmt, double g, int p, int x, int y)
+{
+ char buf[32];
+ Rectangle lr;
+ int ny;
+
+ snprint(buf, sizeof(buf), fmt, g);
+ lr.min = Pt(p, y+2);
+ lr.max = addpt(lr.min, stringsize(display->defaultfont, buf));
+ lr = rectsubpt(lr, Pt(Dx(lr) / 2-1, 0));
+ if(lr.max.y >= screen->r.max.y){
+ ny = y - 7 - Dy(lr);
+ lr = rectsubpt(lr, Pt(0, lr.min.y - ny));
+ }
+ if(rectinrect(lr, screen->r) && (lr.min.x > x || lr.max.x <= x)){
+ string(screen, lr.min, display->black, ZP, display->defaultfont, buf);
+ return 1;
+ }
+ return 0;
+}
+
+int
+yticklabel(char *fmt, double g, int p, int x, int y)
+{
+ char buf[32];
+ Rectangle lr;
+ int nx;
+
+ snprint(buf, sizeof(buf), fmt, g);
+ lr.min = Pt(0, 0);
+ lr.max = stringsize(display->defaultfont, buf);
+ lr = rectaddpt(lr, Pt(x-Dx(lr)-2, p - Dy(lr) / 2));
+ if(lr.min.x < screen->r.min.x){
+ nx = x + 7;
+ lr = rectsubpt(lr, Pt(lr.min.x - nx, 0));
+ }
+ if(rectinrect(lr, screen->r) && (lr.min.y > y || lr.max.y <= y)){
+ string(screen, lr.min, display->black, ZP, display->defaultfont, buf);
+ return 1;
+ }
+ return 0;
+}
+
+int
+calcm(double min, double max, int e, double *dp, double *mp)
+{
+ double d, m, r;
+
+ d = pow(10, e>>1);
+ if((e & 1) != 0) d *= 5;
+ m = min;
+ if(min < 0 && max > 0)
+ m += fmod(-m, d);
+ else{
+ r = fmod(m, d);
+ if(r < 0)
+ m -= r;
+ else
+ m += d - r;
+ }
+ if(dp != nil) *dp = d;
+ if(mp != nil) *mp = m;
+ return (max-m)*0.999/d;
+}
+
+int
+ticks(double min, double max, double *dp, double *mp)
+{
+ int e, n;
+ double m;
+ int beste;
+ double bestm;
+
+ e = 2 * ceil(log10(max - min));
+ beste = 0;
+ bestm = Inf(1);
+ for(;e>-100;e--){
+ n = calcm(min, max, e, nil, nil);
+ if(n <= 0) continue;
+ if(n < 10) m = 10.0 / n;
+ else m = n / 10.0;
+ if(m < bestm){
+ beste = e;
+ bestm = m;
+ }
+ if(n > 10) break;
+ }
+ calcm(min, max, beste, dp, mp);
+ return (max - *mp) / *dp;
+}
+
+void
+drawaxes(void)
+{
+ int x, y, p;
+ double dx, dy, mx, my;
+ int nx, ny;
+ int i;
+ char fmt[32];
+
+ if(xmin < 0 && xmax > 0)
+ x = deconvx(&screen->r, 0);
+ else
+ x = screen->r.min.x+5;
+ line(screen, Pt(x, screen->r.min.y), Pt(x, screen->r.max.y), Endarrow, 0, 0, display->black, ZP);
+ if(ymin < 0 && ymax > 0)
+ y = deconvy(&screen->r, 0);
+ else
+ y = screen->r.max.y-5;
+ line(screen, Pt(screen->r.min.x, y), Pt(screen->r.max.x, y), 0, Endarrow, 0, display->black, ZP);
+ nx = ticks(xmin, xmax, &dx, &mx);
+ tickfmt(dx, mx, nx, fmt);
+ for(i = 0; i <= nx; i++){
+ p = deconvx(&screen->r, dx*i+mx);
+ if(xticklabel(fmt, dx*i+mx, p, x, y))
+ line(screen, Pt(p, y), Pt(p, y-5), 0, 0, 0, display->black, ZP);
+ }
+ ny = ticks(ymin, ymax, &dy, &my);
+ tickfmt(dy, my, ny, fmt);
+ for(i = 0; i <= ny; i++){
+ p = deconvy(&screen->r, dy*i+my);
+ if(yticklabel(fmt, dy*i+my, p, x, y))
+ line(screen, Pt(x, p), Pt(x+5, p), 0, 0, 0, display->black, ZP);
+ }
}
void
drawgraphs(void)
{
int i;
-
- color = display->black;
+
+ gymin = Inf(1);
+ gymax = Inf(-1);
+ memset(pixels, 0, picx * picy);
for(i = 0; i < nfns; i++)
- drawgraph(&fns[i], &screen->r);
+ drawgraph(&fns[i], &screen->r, i % nelem(icolors));
+ if(!aflag)
+ drawaxes();
flushimage(display, 1);
}
void
usage(void)
{
- fprint(2, "usage: fplot [-c [-s size]] [-r range] functions ...\n");
+ fprint(2, "usage: fplot [-a] [-c [-s size]] [-r range] functions ...\n");
exits("usage");
}
Rectangle r;
double xmin_, xmax_, ymin_, ymax_;
- m.buttons = 7;
+ m.buttons = 0;
r = egetrect(1, &m);
- if(r.min.x == 0 && r.min.y == 0 && r.max.x == 0 && r.max.y == 0)
+ if(Dx(r) < 1 || Dy(r) < 1)
return;
+ zoomst = realloc(zoomst, sizeof(FRectangle) * (nzoomst + 1));
+ if(zoomst == nil) sysfatal("realloc: %r");
+ zoomst[nzoomst++] = (FRectangle){xmin, xmax, ymin, ymax};
xmin_ = convx(&screen->r, r.min.x);
xmax_ = convx(&screen->r, r.max.x);
ymin_ = convy(&screen->r, r.max.y);
drawgraphs();
}
+void
+unzoom(void)
+{
+ if(nzoomst == 0) return;
+ xmin = zoomst[nzoomst - 1].xmin;
+ xmax = zoomst[nzoomst - 1].xmax;
+ ymin = zoomst[nzoomst - 1].ymin;
+ ymax = zoomst[nzoomst - 1].ymax;
+ zoomst = realloc(zoomst, sizeof(FRectangle) * --nzoomst);
+ if(zoomst == nil && nzoomst != 0) sysfatal("realloc: %r");
+ draw(screen, screen->r, display->white, nil, ZP);
+ drawgraphs();
+}
+
void
parsefns(int n, char **s)
{
void
parserange(char *s)
{
- while(*s && !isdigit(*s)) s++;
+ while(*s && !isdigit(*s) && *s != '-') s++;
if(*s == 0) return;
xmin = strtod(s, &s);
- while(*s && !isdigit(*s)) s++;
+ while(*s && !isdigit(*s) && *s != '-') s++;
if(*s == 0) return;
xmax = strtod(s, &s);
- while(*s && !isdigit(*s)) s++;
+ while(*s && !isdigit(*s) && *s != '-') s++;
if(*s == 0) return;
ymin = strtod(s, &s);
- while(*s && !isdigit(*s)) s++;
+ while(*s && !isdigit(*s) && *s != '-') s++;
if(*s == 0) return;
ymax = strtod(s, &s);
}
picy = strtol(s, &s, 0);
}
+void
+alloccolors(void)
+{
+ int i;
+
+ for(i = 0; i < nelem(icolors); i++){
+ freeimage(colors[i]);
+ colors[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, icolors[i]);
+ }
+}
+
+void
+readout(Point p)
+{
+ int i, j;
+ double x, y;
+ vlong d, best;
+ Point bestp;
+ double ny, besty;
+ char buf[64];
+
+ /*TODO: do something more intelligent*/
+ best = (uvlong)(-1)>>1;
+ for(j = screen->r.min.y; j < screen->r.max.y; j++)
+ for(i = screen->r.min.x; i < screen->r.max.x; i++){
+ if(!pixels[(j - screen->r.min.y) * picx + (i - screen->r.min.x)]) continue;
+ d = (i - p.x) * (i - p.x) + (j - p.y) * (j - p.y);
+ if(d < best){
+ best = d;
+ bestp = Pt(i, j);
+ }
+ }
+ ellipse(screen, bestp, 3, 3, 0, display->black, ZP);
+ x = convx(&screen->r, bestp.x);
+ y = convy(&screen->r, bestp.y);
+ besty = calc(&fns[0], x);
+ for(i = 1; i < nfns; i++){
+ ny = calc(&fns[i], x);
+ if(abs(ny - y) < abs(besty - y))
+ besty = ny;
+ }
+ snprint(buf, sizeof(buf), "%#.4g %#.4g", x, besty);
+ string(screen, addpt(Pt(10, 10), screen->r.min), display->black, ZP, display->defaultfont, buf);
+}
+
void
main(int argc, char **argv)
{
Event e;
Rectangle r;
int i;
+ static int lbut;
ARGBEGIN {
+ case 'a': aflag++; break;
case 'r': parserange(EARGF(usage())); break;
case 's': parsesize(EARGF(usage())); break;
case 'c': cflag++; break;
r.max.x = picx;
r.max.y = picy;
for(i = 0; i < nfns; i++)
- drawgraph(&fns[i], &r);
+ drawgraph(&fns[i], &r, i % nelem(icolors));
if(write(1, imagedata, picx * picy * 3) < picx * picy * 3)
sysfatal("write: %r");
} else {
if(initdraw(nil, nil, "fplot") < 0)
sysfatal("initdraw: %r");
einit(Emouse | Ekeyboard);
+ picx = Dx(screen->r);
+ picy = Dy(screen->r);
+ pixels = emalloc(picx * picy);
+ alloccolors();
drawgraphs();
for(;;) {
switch(event(&e)) {
+ case Emouse:
+ if((e.mouse.buttons & 1) != 0)
+ zoom();
+ if(((lbut|e.mouse.buttons) & 2) != 0){
+ draw(screen, screen->r, display->white, nil, ZP);
+ drawgraphs();
+ }
+ if((e.mouse.buttons & 2) != 0)
+ readout(e.mouse.xy);
+ if((~e.mouse.buttons & lbut & 4) != 0)
+ unzoom();
+ lbut = e.mouse.buttons;
+ break;
case Ekeyboard:
switch(e.kbdc) {
- case 'q': exits(nil);
- case 'z': zoom();
+ case 'q': case 127: exits(nil); break;
+ case 'y':
+ if(!isInf(ymin, 1) && !isInf(ymax, -1)){
+ zoomst = realloc(zoomst, sizeof(FRectangle) * (nzoomst + 1));
+ if(zoomst == nil) sysfatal("realloc: %r");
+ zoomst[nzoomst++] = (FRectangle){xmin, xmax, ymin, ymax};
+ ymin = gymin-0.05*(gymax-gymin);
+ ymax = gymax+0.05*(gymax-gymin);
+ draw(screen, screen->r, display->white, nil, ZP);
+ drawgraphs();
+ }
+ break;
}
}
}
if(new) {
if(getwindow(display, Refnone) < 0)
sysfatal("getwindow: %r");
+ picx = Dx(screen->r);
+ picy = Dy(screen->r);
+ pixels = realloc(pixels, picx * picy);
+ if(pixels == nil) sysfatal("realloc: %r");
+ alloccolors();
drawgraphs();
}
}