]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/fplot.c
cc: use 7 octal digits for 21 bit runes
[plan9front.git] / sys / src / cmd / fplot.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <draw.h>
5 #include <event.h>
6
7 typedef struct Op Op;
8 typedef struct Operator Operator;
9 typedef struct Token Token;
10 typedef struct Constant Constant;
11 typedef struct Code Code;
12
13 enum {
14         ONONE,
15         ONUMBER,
16         OVAR,
17         OUNARY,
18         OBINARY,
19 };
20
21 struct Op {
22         int type;
23         union {
24                 void (*f)(void);
25                 double val;
26         };
27 };
28
29 enum {
30         TNONE,
31         TNUMBER,
32         TVAR,
33         TOP,
34         TPARENL,
35         TPARENR,
36 };
37
38 struct Token {
39         int type;
40         union {
41                 Operator *op;
42                 double val;
43         };
44         Token *next;
45 };
46
47 double *stack, *sp;
48 void omax(void) { sp--; if(sp[1]>*sp) *sp = sp[1]; }
49 void omin(void) { sp--; if(sp[1]<*sp) *sp = sp[1]; }
50 void add(void) { sp--; *sp += *(sp+1); }
51 void sub(void) { sp--; *sp -= *(sp+1); }
52 void mul(void) { sp--; *sp *= *(sp+1); }
53 void div(void) { sp--; *sp /= *(sp+1); }
54 void mod(void) { sp--; *sp = fmod(*sp, *(sp+1)); }
55 void pot(void) { sp--; *sp = pow(*sp, *(sp+1)); }
56 void osin(void) { *sp = sin(*sp); }
57 void ocos(void) { *sp = cos(*sp); }
58 void otan(void) { *sp = tan(*sp); }
59 void oasin(void) { *sp = asin(*sp); }
60 void oacos(void) { *sp = acos(*sp); }
61 void oatan(void) { *sp = atan(*sp); }
62 void osqrt(void) { *sp = sqrt(*sp); }
63 void oexp(void) { *sp = exp(*sp); }
64 void olog(void) { *sp = log10(*sp); }
65 void oln(void) { *sp = log(*sp); }
66
67 struct Operator {
68         char *s;
69         char type;
70         char rassoc;
71         short prec;
72         void (*f)(void);
73 } ops[] = {
74         "max",  OBINARY,        0,      0,      omax,
75         "min",  OBINARY,        0,      0,      omax,
76         "+",    OBINARY,        0,      100,    add,
77         "-",    OBINARY,        0,      100,    sub,
78         "*",    OBINARY,        0,      200,    mul,
79         "/",    OBINARY,        0,      200,    div,
80         "%",    OBINARY,        0,      200,    mod,
81         "^",    OBINARY,        1,      300,    pot,
82         "sin",  OUNARY,         0,      400,    osin,
83         "cos",  OUNARY,         0,      400,    ocos,
84         "tan",  OUNARY,         0,      400,    otan,
85         "asin", OUNARY,         0,      400,    oasin,
86         "acos", OUNARY,         0,      400,    oacos,
87         "atan", OUNARY,         0,      400,    oatan,
88         "sqrt", OUNARY,         0,      400,    osqrt,
89         "exp",  OUNARY,         0,      400,    oexp,
90         "log",  OUNARY,         0,      400,    olog,
91         "ln",   OUNARY,         0,      400,    oln,
92 };
93
94 struct Constant {
95         char *s;
96         double val;
97 } consts[] = {
98         "pi",   3.14159265359,
99         "π",   3.14159265359,
100         "e",    2.71828182846,
101 };
102
103 struct Code {
104         Op* p;
105         int len, cap;
106 } *fns;
107 int nfns;
108
109 Token *opstackbot;
110 double xmin = -10, xmax = 10;
111 double ymin = -10, ymax = 10;
112 double gymin, gymax;
113 int icolors[] = {
114         DBlack,
115         0xCC0000FF,
116         0x00CC00FF,
117         0x0000CCFF,
118         0xFF00CCFF,
119         0xFFAA00FF,
120         0xCCCC00FF,
121 };
122 Image *colors[nelem(icolors)];
123 int cflag, aflag;
124 char *imagedata;
125 char *pixels;
126 int picx = 640, picy = 480;
127
128 typedef struct FRectangle FRectangle;
129 struct FRectangle {
130         double xmin, xmax, ymin, ymax;
131 } *zoomst;
132 int nzoomst;
133
134 void *
135 emalloc(int size)
136 {
137         void *v;
138         
139         v = mallocz(size, 1);
140         if(v == nil)
141                 sysfatal("emalloc: %r");
142         setmalloctag(v, getcallerpc(&size));
143         return v;
144 }
145
146 void
147 addop(Code *c, Op o)
148 {
149         if(c->len >= c->cap) {
150                 c->cap += 32;
151                 c->p = realloc(c->p, sizeof(Op) * c->cap);
152                 if(c->p == nil)
153                         sysfatal("realloc: %r");
154         }
155         c->p[c->len++] = o;
156 }
157
158 void
159 push(Token *t)
160 {
161         t->next = opstackbot;
162         opstackbot = t;
163 }
164
165 void
166 pop(Code *c)
167 {
168         Token *t;
169         Op o;
170         
171         t = opstackbot;
172         if(t == nil)
173                 sysfatal("stack underflow");
174         opstackbot = t->next;
175         if(t->type != TOP)
176                 sysfatal("non-operator pop");
177         o.type = t->op->type;
178         o.f = t->op->f;
179         addop(c, o);
180         free(t);
181 }
182
183 void
184 popdel(void)
185 {
186         Token *t;
187         
188         t = opstackbot;
189         opstackbot = t->next;
190         free(t);
191 }
192
193 Token *
194 lex(char **s)
195 {
196         Token *t;
197         Operator *o;
198         Constant *c;
199
200         while(isspace(**s))
201                 (*s)++;
202         if(**s == 0)
203                 return nil;
204         
205         t = emalloc(sizeof(*t));
206         if(isdigit(**s)) {
207                 t->type = TNUMBER;
208                 t->val = strtod(*s, s);
209                 return t;
210         }
211         if(**s == '(') {
212                 t->type = TPARENL;
213                 (*s)++;
214                 return t;
215         }
216         if(**s == ')') {
217                 t->type = TPARENR;
218                 (*s)++;
219                 return t;
220         }
221         if(**s == 'x') {
222                 t->type = TVAR;
223                 (*s)++;
224                 return t;
225         }
226         for(o = ops; o < ops + nelem(ops); o++)
227                 if(strncmp(*s, o->s, strlen(o->s)) == 0) {
228                         t->type = TOP;
229                         t->op = o;
230                         *s += strlen(o->s);
231                         return t;
232                 }
233         for(c = consts; c < consts + nelem(consts); c++)
234                 if(strncmp(*s, c->s, strlen(c->s)) == 0) {
235                         t->type = TNUMBER;
236                         t->val = c->val;
237                         *s += strlen(c->s);
238                         return t;
239                 }
240         sysfatal("syntax error at %s", *s);
241         return nil;
242 }
243
244 void
245 parse(Code *c, char *s)
246 {
247         Token *t;
248         Op o;
249         
250         while(t = lex(&s)) {
251                 switch(t->type) {
252                 case TNUMBER:
253                         o.type = ONUMBER;
254                         o.val = t->val;
255                         addop(c, o);
256                         free(t);
257                         break;
258                 case TVAR:
259                         o.type = OVAR;
260                         addop(c, o);
261                         free(t);
262                         break;
263                 case TOP:
264                         if(t->op->type == OBINARY)
265                                 while(opstackbot != nil && opstackbot->type == TOP &&
266                                         (opstackbot->op->prec > t->op->prec ||
267                                         !t->op->rassoc && opstackbot->op->prec == t->op->prec))
268                                         pop(c);
269                         push(t);
270                         break;
271                 case TPARENL:
272                         push(t);
273                         break;
274                 case TPARENR:
275                         while(opstackbot != nil && opstackbot->type == TOP)
276                                 pop(c);
277                         if(opstackbot == nil)
278                                 sysfatal("mismatched parentheses");
279                         popdel();
280                         free(t);
281                         break;
282                 default:
283                         sysfatal("unknown token type %d", t->type);
284                 }
285         }
286         while(opstackbot != nil)
287                 switch(opstackbot->type) {
288                 case TOP:
289                         pop(c);
290                         break;
291                 case TPARENL:
292                         sysfatal("mismatched parentheses");
293                 default:
294                         sysfatal("syntax error");
295                 }
296 }
297
298 int
299 calcstack(Code *c)
300 {
301         int cur, max;
302         Op *o;
303         
304         cur = 0;
305         max = 0;
306         for(o = c->p; o < c->p + c->len; o++)
307                 switch(o->type) {
308                 case ONUMBER: case OVAR:
309                         if(++cur > max)
310                                 max = cur;
311                         break;
312                 case OUNARY:
313                         if(cur == 0)
314                                 sysfatal("syntax error");
315                         break;
316                 case OBINARY:
317                         if(cur <= 1)
318                                 sysfatal("syntax error");
319                         cur--;
320                         break;
321                 }
322
323         if(cur != 1)
324                 sysfatal("syntax error");
325         return max;
326 }
327
328 double
329 calc(Code *c, double x)
330 {
331         Op *o;
332         
333         sp = stack - 1;
334         for(o = c->p; o < c->p + c->len; o++)
335                 switch(o->type) {
336                 case ONUMBER:
337                         *++sp = o->val;
338                         break;
339                 case OVAR:
340                         *++sp = x;
341                         break;
342                 case OUNARY: case OBINARY:
343                         o->f();
344                 }
345         if(*sp < gymin) gymin = *sp;
346         if(*sp > gymax) gymax = *sp;
347         return *sp;
348 }
349
350 double
351 convx(Rectangle *r, int x)
352 {
353         return (xmax - xmin) * (x - r->min.x) / (r->max.x - r->min.x) + xmin;
354 }
355
356 int
357 deconvx(Rectangle *r, double dx)
358 {
359         return (dx - xmin) * (r->max.x - r->min.x) / (xmax - xmin) + r->min.x + 0.5;
360 }
361
362 double
363 convy(Rectangle *r, int y)
364 {
365         return (ymax - ymin) * (r->max.y - y) / (r->max.y - r->min.y) + ymin;
366 }
367
368 int
369 deconvy(Rectangle *r, double dy)
370 {
371         return (ymax - dy) * (r->max.y - r->min.y) / (ymax - ymin) + r->min.y + 0.5;
372 }
373
374 void
375 pixel(int x, int y, int c)
376 {
377         char *p;
378
379         if(cflag) {
380                 if(x >= picx || y >= picy || x < 0 || y < 0)
381                         return;
382                 p = imagedata + (picx * y + x) * 3;
383                 p[0] = icolors[c] >> 24;
384                 p[1] = icolors[c] >> 16;
385                 p[2] = icolors[c] >> 8;
386         }else{
387                 draw(screen, Rect(x, y, x + 1, y + 1), colors[c], nil, ZP);
388                 if(ptinrect(Pt(x, y), screen->r))
389                         pixels[picx * (y - screen->r.min.y) + (x - screen->r.min.x)] = 1;
390         }
391 }
392
393 void
394 drawinter(Code *co, Rectangle *r, double x1, double x2, int n, int c)
395 {
396         double y1, y2;
397         int iy1, iy2;
398         int ix1, ix2;
399
400         ix1 = deconvx(r, x1);
401         ix2 = deconvx(r, x2);
402         iy1 = iy2 = 0;
403         y1 = calc(co, x1);
404         if(!isNaN(y1)) {
405                 iy1 = deconvy(r, y1);
406                 pixel(ix1, iy1, c);
407         }
408         y2 = calc(co, x2);
409         if(!isNaN(y2)) {
410                 iy2 = deconvy(r, y2);
411                 pixel(ix2, iy2, c);
412         }
413         if(isNaN(y1) || isNaN(y2))
414                 return;
415         if(n >= 10)
416                 return;
417         if(iy2 >= iy1 - 1 && iy2 <= iy1 + 1)
418                 return;
419         if(iy1 > r->max.y && iy2 > r->max.y)
420                 return;
421         if(iy1 < r->min.y && iy2 < r->min.y)
422                 return;
423         drawinter(co, r, x1, (x1 + x2) / 2, n + 1, c);
424         drawinter(co, r, (x1 + x2) / 2, x2, n + 1, c);
425 }
426
427 void
428 drawgraph(Code *co, Rectangle *r, int c)
429 {
430         int x;
431         
432         for(x = r->min.x; x < r->max.x; x++)
433                 drawinter(co, r, convx(r, x), convx(r, x + 1), 0, c);
434 }
435
436 void
437 tickfmt(double d, double m, int n, char *fmt)
438 {
439         double e1, e2;
440         int x;
441         
442         e1 = log10(fabs(m));
443         e2 = log10(fabs(m + n * d));
444         if(e2 > e1) e1 = e2;
445         if(e2 >= 4 || e2 < -3){
446                 x = ceil(e1-log10(d)-1);
447                 snprint(fmt, 32, "%%.%de", x);
448         }else{
449                 x = ceil(-log10(d));
450                 snprint(fmt, 32, "%%.%df", x);
451         }
452 }
453
454 int
455 xticklabel(char *fmt, double g, int p, int x, int y)
456 {
457         char buf[32];
458         Rectangle lr;
459         int ny;
460
461         snprint(buf, sizeof(buf), fmt, g);
462         lr.min = Pt(p, y+2);
463         lr.max = addpt(lr.min, stringsize(display->defaultfont, buf));
464         lr = rectsubpt(lr, Pt(Dx(lr) / 2-1, 0));
465         if(lr.max.y >= screen->r.max.y){
466                 ny = y - 7 - Dy(lr);
467                 lr = rectsubpt(lr, Pt(0, lr.min.y - ny));
468         }
469         if(rectinrect(lr, screen->r) && (lr.min.x > x || lr.max.x <= x)){
470                 string(screen, lr.min, display->black, ZP, display->defaultfont, buf);
471                 return 1;
472         }
473         return 0;
474 }
475
476 int
477 yticklabel(char *fmt, double g, int p, int x, int y)
478 {
479         char buf[32];
480         Rectangle lr;
481         int nx;
482
483         snprint(buf, sizeof(buf), fmt, g);
484         lr.min = Pt(0, 0);
485         lr.max = stringsize(display->defaultfont, buf);
486         lr = rectaddpt(lr, Pt(x-Dx(lr)-2, p - Dy(lr) / 2));
487         if(lr.min.x < screen->r.min.x){
488                 nx = x + 7;
489                 lr = rectsubpt(lr, Pt(lr.min.x - nx, 0));
490         }
491         if(rectinrect(lr, screen->r) && (lr.min.y > y || lr.max.y <= y)){
492                 string(screen, lr.min, display->black, ZP, display->defaultfont, buf);
493                 return 1;
494         }
495         return 0;
496 }
497
498 int
499 calcm(double min, double max, int e, double *dp, double *mp)
500 {
501         double d, m, r;
502
503         d = pow(10, e>>1);
504         if((e & 1) != 0) d *= 5;
505         m = min;
506         if(min < 0 && max > 0)
507                 m += fmod(-m, d);
508         else{
509                 r = fmod(m, d);
510                 if(r < 0)
511                         m -= r;
512                 else
513                         m += d - r;
514         }
515         if(dp != nil) *dp = d;
516         if(mp != nil) *mp = m;
517         return (max-m)*0.999/d;
518 }
519
520 int
521 ticks(double min, double max, double *dp, double *mp)
522 {
523         int e, n;
524         double m;
525         int beste;
526         double bestm;
527         
528         e = 2 * ceil(log10(max - min));
529         beste = 0;
530         bestm = Inf(1);
531         for(;e>-100;e--){
532                 n = calcm(min, max, e, nil, nil);
533                 if(n <= 0) continue;
534                 if(n < 10) m = 10.0 / n;
535                 else m = n / 10.0;
536                 if(m < bestm){
537                         beste = e;
538                         bestm = m;
539                 }
540                 if(n > 10) break;
541         }
542         calcm(min, max, beste, dp, mp);
543         return (max - *mp) / *dp;
544 }
545
546 void
547 drawaxes(void)
548 {
549         int x, y, p;
550         double dx, dy, mx, my;
551         int nx, ny;
552         int i;
553         char fmt[32];
554
555         if(xmin < 0 && xmax > 0)
556                 x = deconvx(&screen->r, 0);
557         else
558                 x = screen->r.min.x+5;
559         line(screen, Pt(x, screen->r.min.y), Pt(x, screen->r.max.y), Endarrow, 0, 0, display->black, ZP);
560         if(ymin < 0 && ymax > 0)
561                 y = deconvy(&screen->r, 0);
562         else
563                 y = screen->r.max.y-5;
564         line(screen, Pt(screen->r.min.x, y), Pt(screen->r.max.x, y), 0, Endarrow, 0, display->black, ZP);
565         nx = ticks(xmin, xmax, &dx, &mx);
566         tickfmt(dx, mx, nx, fmt);
567         for(i = 0; i <= nx; i++){
568                 p = deconvx(&screen->r, dx*i+mx);
569                 if(xticklabel(fmt, dx*i+mx, p, x, y))
570                         line(screen, Pt(p, y), Pt(p, y-5), 0, 0, 0, display->black, ZP);
571         }
572         ny = ticks(ymin, ymax, &dy, &my);
573         tickfmt(dy, my, ny, fmt);
574         for(i = 0; i <= ny; i++){
575                 p = deconvy(&screen->r, dy*i+my);
576                 if(yticklabel(fmt, dy*i+my, p, x, y))
577                         line(screen, Pt(x, p), Pt(x+5, p), 0, 0, 0, display->black, ZP);
578         }
579 }
580
581 void
582 drawgraphs(void)
583 {
584         int i;
585
586         gymin = Inf(1);
587         gymax = Inf(-1);
588         memset(pixels, 0, picx * picy);
589         for(i = 0; i < nfns; i++)
590                 drawgraph(&fns[i], &screen->r, i % nelem(icolors));
591         if(!aflag)
592                 drawaxes();
593         flushimage(display, 1);
594 }
595
596 void
597 usage(void)
598 {
599         fprint(2, "usage: fplot [-a] [-c [-s size]] [-r range] functions ...\n");
600         exits("usage");
601 }
602
603 void
604 zoom(void)
605 {
606         Mouse m;
607         Rectangle r;
608         double xmin_, xmax_, ymin_, ymax_;
609         
610         m.buttons = 0;
611         r = egetrect(1, &m);
612         if(Dx(r) < 1 || Dy(r) < 1)
613                 return;
614         zoomst = realloc(zoomst, sizeof(FRectangle) * (nzoomst + 1));
615         if(zoomst == nil) sysfatal("realloc: %r");
616         zoomst[nzoomst++] = (FRectangle){xmin, xmax, ymin, ymax};
617         xmin_ = convx(&screen->r, r.min.x);
618         xmax_ = convx(&screen->r, r.max.x);
619         ymin_ = convy(&screen->r, r.max.y);
620         ymax_ = convy(&screen->r, r.min.y);
621         xmin = xmin_;
622         xmax = xmax_;
623         ymin = ymin_;
624         ymax = ymax_;
625         draw(screen, screen->r, display->white, nil, ZP);
626         drawgraphs();
627 }
628
629 void
630 unzoom(void)
631 {
632         if(nzoomst == 0) return;
633         xmin = zoomst[nzoomst - 1].xmin;
634         xmax = zoomst[nzoomst - 1].xmax;
635         ymin = zoomst[nzoomst - 1].ymin;
636         ymax = zoomst[nzoomst - 1].ymax;
637         zoomst = realloc(zoomst, sizeof(FRectangle) * --nzoomst);
638         if(zoomst == nil && nzoomst != 0) sysfatal("realloc: %r");
639         draw(screen, screen->r, display->white, nil, ZP);
640         drawgraphs();
641 }
642
643 void
644 parsefns(int n, char **s)
645 {
646         int i, max, cur;
647
648         max = 0;
649         nfns = n;
650         fns = emalloc(sizeof(*fns) * n);
651         for(i = 0; i < nfns; i++) {
652                 parse(&fns[i], s[i]);
653                 cur = calcstack(&fns[i]);
654                 if(cur > max)
655                         max = cur;
656         }
657         stack = emalloc(sizeof(*stack) * max);
658 }
659
660 void
661 parserange(char *s)
662 {
663         while(*s && !isdigit(*s) && *s != '-') s++;
664         if(*s == 0) return;
665         xmin = strtod(s, &s);
666         while(*s && !isdigit(*s) && *s != '-') s++;
667         if(*s == 0) return;
668         xmax = strtod(s, &s);
669         while(*s && !isdigit(*s) && *s != '-') s++;
670         if(*s == 0) return;
671         ymin = strtod(s, &s);
672         while(*s && !isdigit(*s) && *s != '-') s++;
673         if(*s == 0) return;
674         ymax = strtod(s, &s);
675 }
676
677 void
678 parsesize(char *s)
679 {
680         while(*s && !isdigit(*s)) s++;
681         if(*s == 0) return;
682         picx = strtol(s, &s, 0);
683         while(*s && !isdigit(*s)) s++;
684         if(*s == 0) return;
685         picy = strtol(s, &s, 0);
686 }
687
688 void
689 alloccolors(void)
690 {
691         int i;
692         
693         for(i = 0; i < nelem(icolors); i++){
694                 freeimage(colors[i]);
695                 colors[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, icolors[i]);
696         }
697 }
698
699 void
700 readout(Point p)
701 {
702         int i, j;
703         double x, y;
704         vlong d, best;
705         Point bestp;
706         double ny, besty;
707         char buf[64];
708
709         /*TODO: do something more intelligent*/
710         best = (uvlong)(-1)>>1;
711         for(j = screen->r.min.y; j < screen->r.max.y; j++)
712                 for(i = screen->r.min.x; i < screen->r.max.x; i++){
713                         if(!pixels[(j - screen->r.min.y) * picx + (i - screen->r.min.x)]) continue;
714                         d = (i - p.x) * (i - p.x) + (j - p.y) * (j - p.y);
715                         if(d < best){
716                                 best = d;
717                                 bestp = Pt(i, j);
718                         }
719                 }
720         ellipse(screen, bestp, 3, 3, 0, display->black, ZP);
721         x = convx(&screen->r, bestp.x);
722         y = convy(&screen->r, bestp.y);
723         besty = calc(&fns[0], x);
724         for(i = 1; i < nfns; i++){
725                 ny = calc(&fns[i], x);
726                 if(abs(ny - y) < abs(besty - y))
727                         besty = ny;
728         }
729         snprint(buf, sizeof(buf), "%#.4g %#.4g", x, besty);
730         string(screen, addpt(Pt(10, 10), screen->r.min), display->black, ZP, display->defaultfont, buf);
731 }
732
733 void
734 main(int argc, char **argv)
735 {
736         Event e;
737         Rectangle r;
738         int i;
739         static int lbut;
740
741         ARGBEGIN {
742         case 'a': aflag++; break;
743         case 'r': parserange(EARGF(usage())); break;
744         case 's': parsesize(EARGF(usage())); break;
745         case 'c': cflag++; break;
746         default: usage();
747         } ARGEND;
748         if(argc < 1)
749                 usage();
750         setfcr(getfcr() & ~(FPZDIV | FPINVAL));
751         parsefns(argc, argv);
752         if(cflag) {
753                 imagedata = emalloc(picx * picy * 3);
754                 memset(imagedata, 0xFF, picx * picy * 3);
755                 print("%11s %11d %11d %11d %11d ", "r8g8b8", 0, 0, picx, picy);
756                 r.min.x = r.min.y = 0;
757                 r.max.x = picx;
758                 r.max.y = picy;
759                 for(i = 0; i < nfns; i++)
760                         drawgraph(&fns[i], &r, i % nelem(icolors));
761                 if(write(1, imagedata, picx * picy * 3) < picx * picy * 3)
762                         sysfatal("write: %r");
763         } else {
764                 if(initdraw(nil, nil, "fplot") < 0)
765                         sysfatal("initdraw: %r");
766                 einit(Emouse | Ekeyboard);
767                 picx = Dx(screen->r);
768                 picy = Dy(screen->r);
769                 pixels = emalloc(picx * picy);
770                 alloccolors();
771                 drawgraphs();
772                 for(;;) {
773                         switch(event(&e)) {
774                         case Emouse:
775                                 if((e.mouse.buttons & 1) != 0)
776                                         zoom();
777                                 if(((lbut|e.mouse.buttons) & 2) != 0){
778                                         draw(screen, screen->r, display->white, nil, ZP);
779                                         drawgraphs();
780                                 }
781                                 if((e.mouse.buttons & 2) != 0)
782                                         readout(e.mouse.xy);
783                                 if((~e.mouse.buttons & lbut & 4) != 0)
784                                         unzoom();
785                                 lbut = e.mouse.buttons;
786                                 break;
787                         case Ekeyboard:
788                                 switch(e.kbdc) {
789                                 case 'q': case 127: exits(nil); break;
790                                 case 'y':
791                                         if(!isInf(ymin, 1) && !isInf(ymax, -1)){
792                                                 zoomst = realloc(zoomst, sizeof(FRectangle) * (nzoomst + 1));
793                                                 if(zoomst == nil) sysfatal("realloc: %r");
794                                                 zoomst[nzoomst++] = (FRectangle){xmin, xmax, ymin, ymax};
795                                                 ymin = gymin-0.05*(gymax-gymin);
796                                                 ymax = gymax+0.05*(gymax-gymin);
797                                                 draw(screen, screen->r, display->white, nil, ZP);
798                                                 drawgraphs();
799                                         }
800                                         break;
801                                 }
802                         }
803                 }
804         }
805 }
806
807 void
808 eresized(int new)
809 {
810         if(new) {
811                 if(getwindow(display, Refnone) < 0)
812                         sysfatal("getwindow: %r");
813                 picx = Dx(screen->r);
814                 picy = Dy(screen->r);
815                 pixels = realloc(pixels, picx * picy);
816                 if(pixels == nil) sysfatal("realloc: %r");
817                 alloccolors();
818                 drawgraphs();
819         }
820 }