1 #include <u.h>
2 #include <libc.h>
3 #include <stdio.h>
4 #include "iplot.h"
5 #define INF 1.e+37
6 #define F .25
7
8 struct xy {
9 int xlbf; /*flag:explicit lower bound*/
10 int xubf; /*flag:explicit upper bound*/
11 int xqf; /*flag:explicit quantum*/
12 double (*xf)(double); /*transform function, e.g. log*/
13 float xa,xb; /*scaling coefficients*/
14 float xlb,xub; /*lower and upper bound*/
15 float xquant; /*quantum*/
16 float xoff; /*screen offset fraction*/
17 float xsize; /*screen fraction*/
18 int xbot,xtop; /*screen coords of border*/
19 float xmult; /*scaling constant*/
20 } xd,yd;
21 struct val {
22 float xv;
23 float yv;
24 int lblptr;
25 } *xx;
26
27 char *labels;
28 int labelsiz;
29
30 int tick = 50;
31 int top = 4000;
32 int bot = 200;
33 float absbot;
34 int n;
35 int erasf = 1;
36 int gridf = 2;
37 int symbf = 0;
38 int absf = 0;
39 int transf;
40 int equf;
41 int brkf;
42 int ovlay = 1;
43 float dx;
44 char *plotsymb;
45
46 #define BSIZ 80
47 char labbuf[BSIZ];
48 char titlebuf[BSIZ];
49
50 char *modes[] = {
51 "disconnected",
52 "solid",
53 "dotted",
54 "dotdashed",
55 "shortdashed",
56 "longdashed"
57 };
58 int mode = 1;
ident(double x)59 double ident(double x){
60 return(x);
61 }
62
63 struct z {
64 float lb,ub,mult,quant;
65 };
66
67 struct {
68 char *name;
69 int next;
70 } palette[] = {
71 ['b'] { "blue", 'b' },
72 ['c'] { "cyan", 'c' },
73 ['g'] { "green", 'g' },
74 ['k'] { "kblack", 'k' },
75 ['m'] { "magenta", 'm' },
76 ['r'] { "red", 'r' },
77 ['w'] { "white", 'w' },
78 ['y'] { "yellow", 'y' }
79 };
80 int pencolor = 'k';
81
82 void init(struct xy *);
83 void setopt(int, char *[]);
84 void readin(void);
85 void transpose(void);
86 void getlim(struct xy *, struct val *);
87 void equilibrate(struct xy *, struct xy *);
88 void scale(struct xy *);
89 void limread(struct xy *, int *, char ***);
90 int numb(float *, int *, char ***);
91 void colread(int *, char ***);
92 int copystring(int);
93 struct z setloglim(int, int, float, float);
94 struct z setlinlim(int, int, float, float);
95 void axes(void);
96 int setmark(int *, struct xy *);
97 void submark(int *, int *, float, struct xy *);
98 void plot(void);
99 int getfloat(float *);
100 int getstring(void);
101 void title(void);
102 void badarg(void);
103 int conv(float, struct xy *, int *);
104 int symbol(int, int, int);
105 void axlab(char, struct xy *, char *);
106
main(int argc,char * argv[])107 void main(int argc,char *argv[]){
108
109 openpl();
110 range(0,0,4096,4096);
111 init(&xd);
112 init(&yd);
113 xd.xsize = yd.xsize = 1.;
114 xx = (struct val *)malloc((unsigned)sizeof(struct val));
115 labels = malloc(1);
116 labels[labelsiz++] = 0;
117 setopt(argc,argv);
118 if(erasf)
119 erase();
120 readin();
121 transpose();
122 getlim(&xd,(struct val *)&xx->xv);
123 getlim(&yd,(struct val *)&xx->yv);
124 if(equf) {
125 equilibrate(&xd,&yd);
126 equilibrate(&yd,&xd);
127 }
128 scale(&xd);
129 scale(&yd);
130 axes();
131 title();
132 plot();
133 closepl();
134 exits(0);
135 }
136
init(struct xy * p)137 void init(struct xy *p){
138 p->xf = ident;
139 p->xmult = 1;
140 }
141
setopt(int argc,char * argv[])142 void setopt(int argc, char *argv[]){
143 char *p1, *p2;
144 float temp;
145
146 xd.xlb = yd.xlb = INF;
147 xd.xub = yd.xub = -INF;
148 while(--argc > 0) {
149 argv++;
150 again: switch(argv[0][0]) {
151 case '-':
152 argv[0]++;
153 goto again;
154 case 'l': /* label for plot */
155 p1 = titlebuf;
156 if (argc>=2) {
157 argv++;
158 argc--;
159 p2 = argv[0];
160 while (*p1++ = *p2++);
161 }
162 break;
163
164 case 'd': /*disconnected,obsolete option*/
165 case 'm': /*line mode*/
166 mode = 0;
167 if(!numb(&temp,&argc,&argv))
168 break;
169 if(temp>=sizeof(modes)/sizeof(*modes))
170 mode = 1;
171 else if(temp>=-1)
172 mode = temp;
173 break;
174
175 case 'o':
176 if(numb(&temp,&argc,&argv) && temp>=1)
177 ovlay = temp;
178 break;
179 case 'a': /*automatic abscissas*/
180 absf = 1;
181 dx = 1;
182 if(!numb(&dx,&argc,&argv))
183 break;
184 if(numb(&absbot,&argc,&argv))
185 absf = 2;
186 break;
187
188 case 's': /*save screen, overlay plot*/
189 erasf = 0;
190 break;
191
192 case 'g': /*grid style 0 none, 1 ticks, 2 full*/
193 gridf = 0;
194 if(!numb(&temp,&argc,&argv))
195 temp = argv[0][1]-'0'; /*for caompatibility*/
196 if(temp>=0&&temp<=2)
197 gridf = temp;
198 break;
199
200 case 'c': /*character(s) for plotting*/
201 if(argc >= 2) {
202 symbf = 1;
203 plotsymb = argv[1];
204 argv++;
205 argc--;
206 }
207 break;
208
209 case 't': /*transpose*/
210 transf = 1;
211 break;
212 case 'e': /*equal scales*/
213 equf = 1;
214 break;
215 case 'b': /*breaks*/
216 brkf = 1;
217 break;
218 case 'x': /*x limits */
219 limread(&xd,&argc,&argv);
220 break;
221 case 'y':
222 limread(&yd,&argc,&argv);
223 break;
224 case 'h': /*set height of plot */
225 if(!numb(&yd.xsize, &argc,&argv))
226 badarg();
227 break;
228 case 'w': /*set width of plot */
229 if(!numb(&xd.xsize, &argc, &argv))
230 badarg();
231 break;
232 case 'r': /* set offset to right */
233 if(!numb(&xd.xoff, &argc, &argv))
234 badarg();
235 break;
236 case 'u': /*set offset up the screen*/
237 if(!numb(&yd.xoff,&argc,&argv))
238 badarg();
239 break;
240 case 'p': /*pen color*/
241 colread(&argc, &argv);
242 break;
243 default:
244 badarg();
245 }
246 }
247 }
248
limread(struct xy * p,int * argcp,char *** argvp)249 void limread(struct xy *p, int *argcp, char ***argvp){
250 if(*argcp>1 && (*argvp)[1][0]=='l') {
251 (*argcp)--;
252 (*argvp)++;
253 p->xf = log10;
254 }
255 if(!numb(&p->xlb,argcp,argvp))
256 return;
257 p->xlbf = 1;
258 if(!numb(&p->xub,argcp,argvp))
259 return;
260 p->xubf = 1;
261 if(!numb(&p->xquant,argcp,argvp))
262 return;
263 p->xqf = 1;
264 }
265
isdigit(char c)266 isdigit(char c){
267 return '0'<=c && c<='9';
268 }
numb(float * np,int * argcp,char *** argvp)269 numb(float *np, int *argcp, char ***argvp){
270 char c;
271
272 if(*argcp <= 1)
273 return(0);
274 while((c=(*argvp)[1][0]) == '+')
275 (*argvp)[1]++;
276 if(!(isdigit(c) || c=='-'&&(*argvp)[1][1]<'A' || c=='.'))
277 return(0);
278 *np = atof((*argvp)[1]);
279 (*argcp)--;
280 (*argvp)++;
281 return(1);
282 }
283
colread(int * argcp,char *** argvp)284 void colread(int *argcp, char ***argvp){
285 int c, cnext;
286 int i, n;
287
288 if(*argcp<=1)
289 return;
290 n = strlen((*argvp)[1]);
291 if(strspn((*argvp)[1], "bcgkmrwy")!=n)
292 return;
293 pencolor = cnext = (*argvp)[1][0];
294 for(i=0; i<n-1; i++){
295 c = (unsigned char)(*argvp)[1][i];
296 cnext = (unsigned char)(*argvp)[1][i+1];
297 palette[c].next = cnext;
298 }
299 palette[cnext].next = pencolor;
300 (*argcp)--;
301 (*argvp)++;
302 }
303
readin(void)304 void readin(void){
305 int i, t;
306 struct val *temp;
307
308 if(absf==1) {
309 if(xd.xlbf)
310 absbot = xd.xlb;
311 else if(xd.xf==log10)
312 absbot = 1;
313 }
314 for(;;) {
315 temp = (struct val *)realloc((char*)xx,
316 (unsigned)(n+ovlay)*sizeof(struct val));
317 if(temp==0)
318 return;
319 xx = temp;
320 if(absf)
321 xx[n].xv = n*dx/ovlay + absbot;
322 else
323 if(!getfloat(&xx[n].xv))
324 return;
325 t = 0; /* silence compiler */
326 for(i=0;i<ovlay;i++) {
327 xx[n+i].xv = xx[n].xv;
328 if(!getfloat(&xx[n+i].yv))
329 return;
330 xx[n+i].lblptr = -1;
331 t = getstring();
332 if(t>0)
333 xx[n+i].lblptr = copystring(t);
334 if(t<0 && i+1<ovlay)
335 return;
336 }
337 n += ovlay;
338 if(t<0)
339 return;
340 }
341 }
342
transpose(void)343 void transpose(void){
344 int i;
345 float f;
346 struct xy t;
347 if(!transf)
348 return;
349 t = xd; xd = yd; yd = t;
350 for(i= 0;i<n;i++) {
351 f = xx[i].xv; xx[i].xv = xx[i].yv; xx[i].yv = f;
352 }
353 }
354
copystring(int k)355 int copystring(int k){
356 char *temp;
357 int i;
358 int q;
359
360 temp = realloc(labels,(unsigned)(labelsiz+1+k));
361 if(temp==0)
362 return(0);
363 labels = temp;
364 q = labelsiz;
365 for(i=0;i<=k;i++)
366 labels[labelsiz++] = labbuf[i];
367 return(q);
368 }
369
modceil(float f,float t)370 float modceil(float f, float t){
371
372 t = fabs(t);
373 return(ceil(f/t)*t);
374 }
375
376 float
modfloor(float f,float t)377 modfloor(float f, float t){
378 t = fabs(t);
379 return(floor(f/t)*t);
380 }
381
getlim(struct xy * p,struct val * v)382 void getlim(struct xy *p, struct val *v){
383 int i;
384
385 i = 0;
386 do {
387 if(!p->xlbf && p->xlb>v[i].xv)
388 p->xlb = v[i].xv;
389 if(!p->xubf && p->xub<v[i].xv)
390 p->xub = v[i].xv;
391 i++;
392 } while(i < n);
393 }
394
setlim(struct xy * p)395 void setlim(struct xy *p){
396 float t,delta,sign;
397 struct z z;
398 int mark[50];
399 float lb,ub;
400 int lbf,ubf;
401
402 lb = p->xlb;
403 ub = p->xub;
404 delta = ub-lb;
405 if(p->xqf) {
406 if(delta*p->xquant <=0 )
407 badarg();
408 return;
409 }
410 sign = 1;
411 lbf = p->xlbf;
412 ubf = p->xubf;
413 if(delta < 0) {
414 sign = -1;
415 t = lb;
416 lb = ub;
417 ub = t;
418 t = lbf;
419 lbf = ubf;
420 ubf = t;
421 }
422 else if(delta == 0) {
423 if(ub > 0) {
424 ub = 2*ub;
425 lb = 0;
426 }
427 else
428 if(lb < 0) {
429 lb = 2*lb;
430 ub = 0;
431 }
432 else {
433 ub = 1;
434 lb = -1;
435 }
436 }
437 if(p->xf==log10 && lb>0 && ub>lb) {
438 z = setloglim(lbf,ubf,lb,ub);
439 p->xlb = z.lb;
440 p->xub = z.ub;
441 p->xmult *= z.mult;
442 p->xquant = z.quant;
443 if(setmark(mark,p)<2) {
444 p->xqf = lbf = ubf = 1;
445 lb = z.lb; ub = z.ub;
446 } else
447 return;
448 }
449 z = setlinlim(lbf,ubf,lb,ub);
450 if(sign > 0) {
451 p->xlb = z.lb;
452 p->xub = z.ub;
453 } else {
454 p->xlb = z.ub;
455 p->xub = z.lb;
456 }
457 p->xmult *= z.mult;
458 p->xquant = sign*z.quant;
459 }
460
461 struct z
setloglim(int lbf,int ubf,float lb,float ub)462 setloglim(int lbf, int ubf, float lb, float ub){
463 float r,s,t;
464 struct z z;
465
466 for(s=1; lb*s<1; s*=10) ;
467 lb *= s;
468 ub *= s;
469 for(r=1; 10*r<=lb; r*=10) ;
470 for(t=1; t<ub; t*=10) ;
471 z.lb = !lbf ? r : lb;
472 z.ub = !ubf ? t : ub;
473 if(ub/lb<100) {
474 if(!lbf) {
475 if(lb >= 5*z.lb)
476 z.lb *= 5;
477 else if(lb >= 2*z.lb)
478 z.lb *= 2;
479 }
480 if(!ubf) {
481 if(ub*5 <= z.ub)
482 z.ub /= 5;
483 else if(ub*2 <= z.ub)
484 z.ub /= 2;
485 }
486 }
487 z.mult = s;
488 z.quant = r;
489 return(z);
490 }
491
492 struct z
setlinlim(int lbf,int ubf,float xlb,float xub)493 setlinlim(int lbf, int ubf, float xlb, float xub){
494 struct z z;
495 float r,s,delta;
496 float ub,lb;
497
498 loop:
499 ub = xub;
500 lb = xlb;
501 delta = ub - lb;
502 /*scale up by s, a power of 10, so range (delta) exceeds 1*/
503 /*find power of 10 quantum, r, such that delta/10<=r<delta*/
504 r = s = 1;
505 while(delta*s < 10)
506 s *= 10;
507 delta *= s;
508 while(10*r < delta)
509 r *= 10;
510 lb *= s;
511 ub *= s;
512 /*set r=(1,2,5)*10**n so that 3-5 quanta cover range*/
513 if(r>=delta/2)
514 r /= 2;
515 else if(r<delta/5)
516 r *= 2;
517 z.ub = ubf? ub: modceil(ub,r);
518 z.lb = lbf? lb: modfloor(lb,r);
519 if(!lbf && z.lb<=r && z.lb>0) {
520 xlb = 0;
521 goto loop;
522 }
523 else if(!ubf && z.ub>=-r && z.ub<0) {
524 xub = 0;
525 goto loop;
526 }
527 z.quant = r;
528 z.mult = s;
529 return(z);
530 }
531
scale(struct xy * p)532 void scale(struct xy *p){
533 float edge;
534
535 setlim(p);
536 edge = top-bot;
537 p->xa = p->xsize*edge/((*p->xf)(p->xub) - (*p->xf)(p->xlb));
538 p->xbot = bot + edge*p->xoff;
539 p->xtop = p->xbot + (top-bot)*p->xsize;
540 p->xb = p->xbot - (*p->xf)(p->xlb)*p->xa + .5;
541 }
542
equilibrate(struct xy * p,struct xy * q)543 void equilibrate(struct xy *p, struct xy *q){
544 if(p->xlbf|| /* needn't test xubf; it implies xlbf*/
545 q->xubf&&q->xlb>q->xub)
546 return;
547 if(p->xlb>q->xlb) {
548 p->xlb = q->xlb;
549 p->xlbf = q->xlbf;
550 }
551 if(p->xub<q->xub) {
552 p->xub = q->xub;
553 p->xubf = q->xubf;
554 }
555 }
556
axes(void)557 void axes(void){
558 int i;
559 int mark[50];
560 int xn, yn;
561 if(gridf==0)
562 return;
563
564 line(xd.xbot,yd.xbot,xd.xtop,yd.xbot);
565 vec(xd.xtop,yd.xtop);
566 vec(xd.xbot,yd.xtop);
567 vec(xd.xbot,yd.xbot);
568
569 xn = setmark(mark,&xd);
570 for(i=0; i<xn; i++) {
571 if(gridf==2)
572 line(mark[i],yd.xbot,mark[i],yd.xtop);
573 if(gridf==1) {
574 line(mark[i],yd.xbot,mark[i],yd.xbot+tick);
575 line(mark[i],yd.xtop-tick,mark[i],yd.xtop);
576 }
577 }
578 yn = setmark(mark,&yd);
579 for(i=0; i<yn; i++) {
580 if(gridf==2)
581 line(xd.xbot,mark[i],xd.xtop,mark[i]);
582 if(gridf==1) {
583 line(xd.xbot,mark[i],xd.xbot+tick,mark[i]);
584 line(xd.xtop-tick,mark[i],xd.xtop,mark[i]);
585 }
586 }
587 }
588
589 int
setmark(int * xmark,struct xy * p)590 setmark(int *xmark, struct xy *p){
591 int xn = 0;
592 float x,xl,xu;
593 float q;
594 if(p->xf==log10&&!p->xqf) {
595 for(x=p->xquant; x<p->xub; x*=10) {
596 submark(xmark,&xn,x,p);
597 if(p->xub/p->xlb<=100) {
598 submark(xmark,&xn,2*x,p);
599 submark(xmark,&xn,5*x,p);
600 }
601 }
602 } else {
603 xn = 0;
604 q = p->xquant;
605 if(q>0) {
606 xl = modceil(p->xlb+q/6,q);
607 xu = modfloor(p->xub-q/6,q)+q/2;
608 } else {
609 xl = modceil(p->xub-q/6,q);
610 xu = modfloor(p->xlb+q/6,q)-q/2;
611 }
612 for(x=xl; x<=xu; x+=fabs(p->xquant))
613 xmark[xn++] = (*p->xf)(x)*p->xa + p->xb;
614 }
615 return(xn);
616 }
submark(int * xmark,int * pxn,float x,struct xy * p)617 void submark(int *xmark, int *pxn, float x, struct xy *p){
618 if(1.001*p->xlb < x && .999*p->xub > x)
619 xmark[(*pxn)++] = log10(x)*p->xa + p->xb;
620 }
621
plot(void)622 void plot(void){
623 int ix,iy;
624 int i,j;
625 int conn;
626
627 for(j=0;j<ovlay;j++) {
628 switch(mode) {
629 case -1:
630 pen(modes[j%(sizeof modes/sizeof *modes-1)+1]);
631 break;
632 case 0:
633 break;
634 default:
635 pen(modes[mode]);
636 }
637 color(palette[pencolor].name);
638 conn = 0;
639 for(i=j; i<n; i+=ovlay) {
640 if(!conv(xx[i].xv,&xd,&ix) ||
641 !conv(xx[i].yv,&yd,&iy)) {
642 conn = 0;
643 continue;
644 }
645 if(mode!=0) {
646 if(conn != 0)
647 vec(ix,iy);
648 else
649 move(ix,iy);
650 conn = 1;
651 }
652 conn &= symbol(ix,iy,xx[i].lblptr);
653 }
654 pencolor = palette[pencolor].next;
655 }
656 pen(modes[1]);
657 }
658
659 int
conv(float xv,struct xy * p,int * ip)660 conv(float xv, struct xy *p, int *ip){
661 long ix;
662 ix = p->xa*(*p->xf)(xv*p->xmult) + p->xb;
663 if(ix<p->xbot || ix>p->xtop)
664 return(0);
665 *ip = ix;
666 return(1);
667 }
668
669 int
getfloat(float * p)670 getfloat(float *p){
671 int i;
672
673 i = scanf("%f",p);
674 return(i==1);
675 }
676
677 int
getstring(void)678 getstring(void){
679 int i;
680 char junk[20];
681 i = scanf("%1s",labbuf);
682 if(i==-1)
683 return(-1);
684 switch(*labbuf) {
685 default:
686 if(!isdigit(*labbuf)) {
687 ungetc(*labbuf,stdin);
688 i = scanf("%s",labbuf);
689 break;
690 }
691 case '.':
692 case '+':
693 case '-':
694 ungetc(*labbuf,stdin);
695 return(0);
696 case '"':
697 i = scanf("%[^\"\n]",labbuf);
698 scanf("%[\"]",junk);
699 break;
700 }
701 if(i==-1)
702 return(-1);
703 return(strlen(labbuf));
704 }
705
706 int
symbol(int ix,int iy,int k)707 symbol(int ix, int iy, int k){
708
709 if(symbf==0&&k<0) {
710 if(mode==0)
711 point(ix,iy);
712 return(1);
713 }
714 else {
715 move(ix,iy);
716 text(k>=0?labels+k:plotsymb);
717 move(ix,iy);
718 return(!brkf|k<0);
719 }
720 }
721
title(void)722 void title(void){
723 char buf[BSIZ+100];
724 buf[0] = ' ';
725 buf[1] = ' ';
726 buf[2] = ' ';
727 strcpy(buf+3,titlebuf);
728 if(erasf&&gridf) {
729 axlab('x',&xd,buf);
730 strcat(buf,",");
731 axlab('y',&yd,buf);
732 }
733 move(xd.xbot,yd.xbot-60);
734 text(buf);
735 }
736
axlab(char c,struct xy * p,char * b)737 void axlab(char c, struct xy *p, char *b){
738 char *dir;
739 dir = p->xlb<p->xub? "<=": ">=";
740 sprintf(b+strlen(b), " %g %s %c%s %s %g", p->xlb/p->xmult,
741 dir, c, p->xf==log10?" (log)":"", dir, p->xub/p->xmult);
742 }
743
badarg(void)744 void badarg(void){
745 fprintf(stderr,"graph: error in arguments\n");
746 closepl();
747 exits("bad arg");
748 }
749