]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/plot/plot.c
- use the double-buffer buffer to allow redrawing on resize events.
[plan9front.git] / sys / src / cmd / plot / plot.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include "plot.h"
5 #include <draw.h>
6 #include <thread.h>
7 #include <mouse.h>
8 #include <keyboard.h>
9
10 void    define(char*);
11 void    call(char*);
12 void    include(char*);
13 int     process(Biobuf*);
14 int     server(void);
15
16 enum{
17         ARC,
18         BOX,
19         CALL,
20         CFILL,
21         CIRC,
22         CLOSEPL,
23         COLOR,
24         CSPLINE,
25         DEFINE,
26         DISK,
27         DSPLINE,
28         ERASE,
29         FILL,
30         FRAME,
31         FSPLINE,
32         GRADE,
33         IDLE,
34         INCLUDE,
35         LINE,
36         LSPLINE,
37         MOVE,
38         OPENPL,
39         PARABOLA,
40         PEN,
41         PAUSE,
42         POINT,
43         POLY,
44         RANGE,
45         RESTORE,
46         RMOVE,
47         RVEC,
48         SAVE,
49         SBOX,
50         SPLINE,
51         TEXT,
52         VEC,
53         LAST
54 };
55
56 struct pcall {
57         char    *cc;
58         int     numc;
59 } plots[] = {
60         [ARC]           "a",    1,
61         [BOX]           "bo",   2,
62         [CALL]          "ca",   2,
63         [CFILL]         "cf",   2,
64         [CIRC]          "ci",   2,
65         [CLOSEPL]       "cl",   2,
66         [COLOR]         "co",   2,
67         [CSPLINE]       "cs",   2,
68         [DEFINE]        "de",   2,
69         [DISK]          "di",   2,
70         [DSPLINE]       "ds",   2,
71         [ERASE]         "e",    1,
72         [FILL]          "fi",   2,
73         [FRAME]         "fr",   2,
74         [FSPLINE]       "fs",   2,
75         [GRADE]         "g",    1,
76         [IDLE]          "id",   2,
77         [INCLUDE]       "in",   2,
78         [LINE]          "li",   2,
79         [LSPLINE]       "ls",   2,
80         [MOVE]          "m",    1,
81         [OPENPL]        "o",    1,
82         [PARABOLA]      "par",  3,
83         [PEN]           "pe",   2,
84         [PAUSE]         "pau",  3,
85         [POINT]         "poi",  3,
86         [POLY]          "pol",  3,
87         [RANGE]         "ra",   2,
88         [RESTORE]       "re",   2,
89         [RMOVE]         "rm",   2,
90         [RVEC]          "rv",   2,
91         [SAVE]          "sa",   2,
92         [SBOX]          "sb",   2,
93         [SPLINE]        "sp",   2,
94         [TEXT]          "t",    1,
95         [VEC]           "v",    1,
96         [LAST]          0,      0,
97 };
98
99 struct pcall *pplots;           /* last command read */
100
101 #define MAXL 16
102 struct fcall {
103         char *name;
104         char *stash;
105 } flibr[MAXL];                  /* define strings */
106
107 struct fcall *fptr = flibr;
108
109 #define NFSTACK 50
110 struct fstack{
111         char name[128];
112         int peekc;
113         int lineno;
114         char *corebuf;
115         Biobuf *fd;
116         double scale;
117 }fstack[NFSTACK];               /* stack of open input files & defines */
118 struct fstack *fsp=fstack;
119
120 #define NARGSTR 8192
121 char argstr[NARGSTR+1];         /* string arguments */
122
123 #define NX      8192
124 double x[NX];                   /* numeric arguments */
125
126 #define NPTS    256
127 int cnt[NPTS];                  /* control-polygon vertex counts */
128 double *pts[NPTS];              /* control-polygon vertex pointers */
129
130 extern void m_swapbuf(void);    /* reaching into implementation.  ick. */
131 extern Image *offscreen;
132
133 void
134 resize(Point p)
135 {
136         int fd;
137
138         fd = open("/dev/wctl", OWRITE);
139         if(fd >= 0){
140                 fprint(fd, "resize -dx %d -dy %d", p.x+4*2, p.y+4*2);
141                 close(fd);
142         }
143 }
144
145 void
146 resizeto(Point p)
147 {
148         Point s;
149
150         s = (Point){Dx(screen->r), Dy(screen->r)};
151         if(eqpt(p, s))
152                 return;
153         resize(p);
154 }
155
156 void
157 eresized(int new)
158 {
159         if(new && getwindow(display, Refnone) < 0)
160                 sysfatal("plot: can't reattach to window: %r\n");
161 //      resizeto((Point){Dx(offscreen->r)+4, Dy(offscreen->r)+4});
162         m_swapbuf();
163 }
164
165 char *items[]={
166         "exit",
167         0
168 };
169 Menu menu={items};
170
171 void
172 mouseproc(void*)
173 {
174         void *v;
175         Rune r;
176         Alt alts[4];
177         Keyboardctl *k;
178         Mousectl *m;
179         Mouse mc;
180         enum{Amouse, Akbd, Aresize, Aend};
181
182         m = initmouse(nil, screen);
183         k = initkeyboard(nil);
184
185         memset(alts, 0, sizeof alts);
186         alts[Amouse].c = m->c;
187         alts[Amouse].v = &mc;
188         alts[Amouse].op = CHANRCV;
189
190         alts[Akbd].c = k->c;
191         alts[Akbd].v = &r;
192         alts[Akbd].op = CHANRCV;
193
194         alts[Aresize].c = m->resizec;
195         alts[Aresize].v = &v;
196         alts[Aresize].op = CHANRCV;
197
198         alts[Aend].op = CHANEND;
199
200         for(;;)
201                 switch(alt(alts)){
202                 default:
203                         sysfatal("mouse!");
204                 case Amouse:
205                         if(mc.buttons & 4) {
206                                 if(menuhit(3, m, &menu, nil) == 0)
207                                         threadexitsall("");
208                         }
209                         break;
210                 case Akbd:
211                         switch(r){
212                         case 'q':
213                         case 0x7f:
214                         case 0x04:
215                                 threadexitsall("");
216                         }
217                         break;
218                 case Aresize:
219                         eresized(1);
220                         ;
221                 }
222 }
223
224 void
225 threadmain(int arc, char *arv[]){
226         char *ap;
227         Biobuf *bp;
228         int fd;
229         int i;
230         int dflag;
231         char *oflag;
232
233         bp = 0;
234         fd = dup(0, -1);                /* because openpl will close 0! */
235         dflag=0;
236         oflag="";
237         argv0 = arv[0];
238         for(i=1;i!=arc;i++) if(arv[i][0]=='-') switch(arv[i][1]){
239         case 'd': dflag=1; break;
240         case 'o': oflag=arv[i]+2; break;
241         case 's': fd=server(); break;
242         }
243         openpl(oflag);
244         proccreate(mouseproc, nil, 32*1024);
245         if(dflag)
246                 doublebuffer();
247         for (; arc > 1; arc--, arv++) {
248                 if (arv[1][0] == '-') {
249                         ap = arv[1];
250                         ap++;
251                         switch (*ap) {
252                         default:
253                                 fprint(2, "%s not allowed as argument\n", ap);
254                                 exits("usage");
255                         case 'T': break;
256                         case 'D': break;
257                         case 'd': break;
258                         case 'o': break;
259                         case 'W': break;
260                         case 's': break;
261                         case 'e': erase(); break;
262                         case 'C': closepl(); break;
263                         case 'w': ppause(); break;
264                         case 'c': color(ap+1); break;
265                         case 'f': cfill(ap+1); break;
266                         case 'p': pen(ap+1); break;
267                         case 'g': grade(atof(ap+1)); break;
268                         }
269                 }
270                 else if ((bp = Bopen(arv[1], OREAD)) == 0) {
271                         perror(arv[1]);
272                         fprint(2, "Cannot find file %s\n", arv[1]);
273                 }
274                 else if(process(bp)) Bterm(fsp->fd);
275                 else break;
276         }
277         if (bp == 0){
278                 bp = malloc(sizeof *bp);
279                 Binit(bp, fd, OREAD);
280                 process(bp);
281         }
282         closepl();
283         flushimage(display, 1);
284         for(;;)
285                 sleep(1000);
286 }
287 int isalpha(int c)
288 {
289         return ('a'<=c && c<='z') || ('A'<=c && c<='Z');
290 }
291 int isupper(int c)
292 {
293         return 'A'<=c && c<='Z';
294 }
295 int isdigit(int c)
296 {
297         return '0'<=c && c<='9';
298 }
299 int ispunct(int c)
300 {
301         return strchr("!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~", c)!=0;
302 }
303 int isspace(int c)
304 {
305         return strchr(" \t\n\v\f\r", c)!=0;
306 }
307 int nextc(void){
308         int c;
309         Rune r;
310         for(;;){
311                 if(fsp->peekc!=Beof){
312                         c=fsp->peekc;
313                         fsp->peekc=Beof;
314                         return c;
315                 }
316                 if(fsp->fd)
317                         c=Bgetrune(fsp->fd);
318                 else if(*fsp->corebuf){
319                         fsp->corebuf+=chartorune(&r, fsp->corebuf);
320                         c=r;
321                 }else
322                         c=Beof;
323                 if(c!=Beof || fsp==fstack) break;
324                 if(fsp->fd) Bterm(fsp->fd);
325                 --fsp;
326         }
327         if(c=='\n') fsp->lineno++;
328         return c;
329 }
330 /*
331  * Read a string into argstr -- ignores leading spaces
332  * and an optional leading quote-mark
333  */
334 void
335 strarg(void){
336         int c;
337         Rune r;
338         int quote=0;
339         char *s=argstr;
340         do
341                 c=nextc();
342         while(c==' ' || c=='\t');
343         if(c=='\'' || c=='"'){
344                 quote=c;
345                 c=nextc();
346         }
347         r = 0;
348         while(c!='\n' && c!=Beof){
349                 r=c;
350                 s+=runetochar(s, &r);
351                 c=nextc();
352         }
353         if(quote && s!=argstr && r==quote) --s;
354         *s='\0';
355 }
356 /*
357  * Read a floating point number into argstr
358  */
359 numstring(void){
360         int ndp=0;
361         int ndig=0;
362         char *s=argstr;
363         int c=nextc();
364         if(c=='+' || c=='-'){
365                 *s++=c;
366                 c=nextc();
367         }
368         while(isdigit(c) || c=='.'){
369                 if(s!=&argstr[NARGSTR]) *s++=c;
370                 if(c=='.') ndp++;
371                 else ndig++;
372                 c=nextc();
373         }
374         if(ndp>1 || ndig==0){
375                 fsp->peekc=c;
376                 return 0;
377         }
378         if(c=='e' || c=='E'){
379                 if(s!=&argstr[NARGSTR]) *s++=c;
380                 c=nextc();
381                 if(c=='+' || c=='-'){
382                         if(s!=&argstr[NARGSTR]) *s++=c;
383                         c=nextc();
384                 }
385                 if(!isdigit(c)){
386                         fsp->peekc=c;
387                         return 0;
388                 }
389                 while(isdigit(c)){
390                         if(s!=&argstr[NARGSTR]) *s++=c;
391                         c=nextc();
392                 }
393         }
394         fsp->peekc=c;
395         *s='\0';
396         return 1;
397 }
398 /*
399  * Read n numeric arguments, storing them in
400  * x[0], ..., x[n-1]
401  */
402 void
403 numargs(int n){
404         int i, c;
405         for(i=0;i!=n;i++){
406                 do{
407                         c=nextc();
408                 }while(strchr(" \t\n", c) || c!='.' && c!='+' && c!='-' && ispunct(c));
409                 fsp->peekc=c;
410                 if(!numstring())
411                         sysfatal("%s:%d: number expected\n", fsp->name, fsp->lineno);
412                 x[i]=atof(argstr)*fsp->scale;
413         }
414 }
415 /*
416  * Read a list of lists of control vertices, storing points in x[.],
417  * pointers in pts[.] and counts in cnt[.]
418  */
419 void
420 polyarg(void){
421         int nleft, l, r, c;
422         double **ptsp=pts, *xp=x;
423         int *cntp=cnt;
424         do{
425                 c=nextc();
426         }while(c==' ' || c=='\t');
427         if(c=='{'){
428                 l='{';
429                 r='}';
430         }
431         else{
432                 l=r='\n';
433                 fsp->peekc=c;
434         }
435         nleft=1;
436         *cntp=0;
437         *ptsp=xp;
438         for(;;){
439                 c=nextc();
440                 if(c==r){
441                         if(*cntp){
442                                 if(*cntp&1)
443                                         sysfatal("%s:%d: phase error", fsp->name, fsp->lineno);
444                                 *cntp/=2;
445                                 if(ptsp==&pts[NPTS])
446                                         sysfatal("%s:%d: out of polygons", fsp->name, fsp->lineno);
447                                 *++ptsp=xp;
448                                 *++cntp=0;
449                         }
450                         if(--nleft==0) return;
451                 }
452                 else switch(c){
453                 case Beof:  return;
454                 case ' ':  break;
455                 case '\t': break;
456                 case '\n': break;
457                 case '.': case '+': case '-':
458                 case '0': case '1': case '2': case '3': case '4':
459                 case '5': case '6': case '7': case '8': case '9':
460                         fsp->peekc=c;
461                         if(!numstring())
462                                 sysfatal("%s:%d: expected number", fsp->name, fsp->lineno);
463                         if(xp==&x[NX])
464                                 sysfatal("%s:%d: out of space", fsp->name, fsp->lineno);
465                         *xp++=atof(argstr);
466                         ++*cntp;
467                         break;
468                 default:
469                         if(c==l) nleft++;
470                         else if(!ispunct(c)){
471                                 fsp->peekc=c;
472                                 return;
473                         }
474                 }
475         }
476 }
477
478 process(Biobuf *fd){
479         char *s;
480         int c;
481         fsp=fstack;
482         fsp->fd=fd;
483         fsp->corebuf=0;
484         fsp->peekc=Beof;
485         fsp->lineno=1;
486         fsp->scale=1.;
487         for(;;){
488                 do
489                         c=nextc();
490                 while(c==' ' || c=='\t');
491                 if(c==':'){
492                         do
493                                 c=nextc();
494                         while(c!='\n' && c!=Beof);
495                         if(c==Beof) break;
496                         continue;
497                 }
498                 while(c=='.'){
499                         c=nextc();
500                         if(isdigit(c)){
501                                 if(fsp->fd) Bungetc(fsp->fd);
502                                 else --fsp->corebuf;
503                                 c='.';
504                                 break;
505                         }
506                 }
507                 if(c==Beof) break;
508                 if(c=='\n') continue;
509                 if(isalpha(c)){
510                         s=argstr;
511                         do{
512                                 if(isupper(c)) c=tolower(c);
513                                 if(s!=&argstr[NARGSTR]) *s++=c;
514                                 c=nextc();
515                         }while(isalpha(c));
516                         fsp->peekc=c;
517                         *s='\0';
518                         for(pplots=plots;pplots->cc;pplots++)
519                                 if(strncmp(argstr, pplots->cc, pplots->numc)==0)
520                                         break;
521                         if(pplots->cc==0)
522                                 sysfatal("%s:%d: %s unknown", fsp->name, fsp->lineno, argstr);
523                 }
524                 else{
525                         fsp->peekc=c;
526                 }
527                 if(!pplots)
528                         sysfatal("%s:%d: no command\n", fsp->name, fsp->lineno);
529                 switch(pplots-plots){
530                 case ARC:       numargs(7); rarc(x[0],x[1],x[2],x[3],x[4],x[5],x[6]); break;
531                 case BOX:       numargs(4); box(x[0], x[1], x[2], x[3]); break;
532                 case CALL:      strarg();   call(argstr); pplots=0; break;
533                 case CFILL:     strarg();   cfill(argstr); pplots=0; break;
534                 case CIRC:      numargs(3); circ(x[0], x[1], x[2]); break;
535                 case CLOSEPL:   strarg();   closepl(); pplots=0; break;
536                 case COLOR:     strarg();   color(argstr); pplots=0; break;
537                 case CSPLINE:   polyarg();  splin(4, cnt, pts); break;
538                 case DEFINE:    strarg();   define(argstr); pplots=0; break;
539                 case DISK:      numargs(3); plotdisc(x[0], x[1], x[2]); break;
540                 case DSPLINE:   polyarg();  splin(3, cnt, pts); break;
541                 case ERASE:     strarg();   erase(); pplots=0; break;
542                 case FILL:      polyarg();  fill(cnt, pts); break;
543                 case FRAME:     numargs(4); frame(x[0], x[1], x[2], x[3]); break;
544                 case FSPLINE:   polyarg();  splin(1, cnt, pts); break;
545                 case GRADE:     numargs(1); grade(x[0]); break;
546                 case IDLE:      strarg();   idle(); pplots=0; break;
547                 case INCLUDE:   strarg();   include(argstr); pplots=0; break;
548                 case LINE:      numargs(4); plotline(x[0], x[1], x[2], x[3]); break;
549                 case LSPLINE:   polyarg();  splin(2, cnt, pts); break;
550                 case MOVE:      numargs(2); move(x[0], x[1]); break;
551                 case OPENPL:    strarg();   openpl(argstr); pplots=0; break;
552                 case PARABOLA:  numargs(6); parabola(x[0],x[1],x[2],x[3],x[4],x[5]); break;
553                 case PAUSE:     strarg();   ppause(); pplots=0; break;
554                 case PEN:       strarg();   pen(argstr); pplots=0; break;
555                 case POINT:     numargs(2); dpoint(x[0], x[1]); break;
556                 case POLY:      polyarg();  plotpoly(cnt, pts); break;
557                 case RANGE:     numargs(4); range(x[0], x[1], x[2], x[3]); break;
558                 case RESTORE:   strarg();   restore(); pplots=0; break;
559                 case RMOVE:     numargs(2); rmove(x[0], x[1]); break;
560                 case RVEC:      numargs(2); rvec(x[0], x[1]); break;
561                 case SAVE:      strarg();   save(); pplots=0; break;
562                 case SBOX:      numargs(4); sbox(x[0], x[1], x[2], x[3]); break;
563                 case SPLINE:    polyarg();  splin(0, cnt, pts); break;
564                 case TEXT:      strarg();   text(argstr); pplots=0; break;
565                 case VEC:       numargs(2); vec(x[0], x[1]); break;
566                 default:
567                         sysfatal("%s:%d: plot: missing case %ld\n", fsp->name, fsp->lineno, pplots-plots);
568                 }
569         }
570         return 1;
571 }
572 char *names = 0;
573 char *enames = 0;
574 char *bstash = 0;
575 char *estash = 0;
576 unsigned size = 1024;
577 char *nstash = 0;
578 void define(char *a){
579         char    *ap;
580         short   i, j;
581         int curly = 0;
582         ap = a;
583         while(isalpha(*ap))ap++;
584         if(ap == a)
585                 sysfatal("plot: no name with define\n");
586         i = ap - a;
587         if(names+i+1 > enames){
588                 names = malloc((unsigned)512);
589                 enames = names + 512;
590         }
591         fptr->name = names;
592         strncpy(names, a,i);
593         names += i;
594         *names++ = '\0';
595         if(!bstash){
596                 bstash = nstash = malloc(size);
597                 estash = bstash + size;
598         }
599         fptr->stash = nstash;
600         while(*ap != '{')
601                 if(*ap == '\n'){
602                         if((ap=Brdline(fsp->fd, '\n'))==0)
603                                 sysfatal("plot: unexpected eof");
604                 }
605                 else ap++;
606         while((j=Bgetc(fsp->fd))!= Beof){
607                 if(j == '{')curly++;
608                 else if(j == '}'){
609                         if(curly == 0)break;
610                         else curly--;
611                 }
612                 *nstash++ = j;
613                 if(nstash == estash){
614                         free(bstash);
615                         size += 1024;
616                         bstash = realloc(bstash,size);
617                         if(bstash == nil)
618                                 sysfatal("plot: realloc: %r");
619                         estash = bstash+size;
620                 }
621         }
622         *nstash++ = '\0';
623         if(fptr++ >= &flibr[MAXL])
624                 sysfatal("too many objects");
625 }
626 void call(char *a){
627         char *ap;
628         struct fcall *f;
629         char sav;
630         double SC;
631         ap = a;
632         while(isalpha(*ap))ap++;
633         sav = *ap;
634         *ap = '\0';
635         for(f=flibr;f<fptr;f++){
636                 if (!(strcmp(a, f->name)))
637                         break;
638         }
639         if(f == fptr)
640                 sysfatal("plot: object %s not defined",a);
641         *ap = sav;
642         while (isspace(*ap) || *ap == ',') 
643                 ap++;
644         if (*ap != '\0')
645                 SC = atof(ap);
646         else SC = 1.;
647         if(++fsp==&fstack[NFSTACK])
648                 sysfatal("plot: input stack overflow");
649         snprint(fsp->name, sizeof fsp->name, "call %s", f->name);
650         fsp->peekc=Beof;
651         fsp->lineno=1;
652         fsp->corebuf=f->stash;
653         fsp->fd=0;
654         fsp->scale=fsp[-1].scale*SC;
655 }
656 void include(char *a){
657         Biobuf *fd;
658         fd=Bopen(a, OREAD);
659         if(fd==0)
660                 sysfatal("plot: cant include %s: %r", a);
661         if(++fsp==&fstack[NFSTACK])
662                 sysfatal("plot: input stack overflow");
663         snprint(fsp->name, sizeof fsp->name, "%s", a);
664         fsp->peekc=Beof;
665         fsp->lineno=1;
666         fsp->corebuf=0;
667         fsp->fd=fd;
668 }
669 /*
670  * Doesn't work.  Why?
671  */
672 int server(void){
673         int fd, p[2];
674         char buf[32];
675         pipe(p);
676         fd = create("/srv/plot", 1, 0666);
677         sprint(buf, "%d", p[1]);
678         write(fd, buf, strlen(buf));
679         close(fd);
680         close(p[1]);
681         return p[0];
682 }