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