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