| #include <u.h> |
| #include <libc.h> |
| #include <stdio.h> |
| #include "iplot.h" |
| #define INF 1.e+37 |
| #define F .25 |
| |
| struct xy { |
| int xlbf; /*flag:explicit lower bound*/ |
| int xubf; /*flag:explicit upper bound*/ |
| int xqf; /*flag:explicit quantum*/ |
| double (*xf)(double); /*transform function, e.g. log*/ |
| float xa,xb; /*scaling coefficients*/ |
| float xlb,xub; /*lower and upper bound*/ |
| float xquant; /*quantum*/ |
| float xoff; /*screen offset fraction*/ |
| float xsize; /*screen fraction*/ |
| int xbot,xtop; /*screen coords of border*/ |
| float xmult; /*scaling constant*/ |
| } xd,yd; |
| struct val { |
| float xv; |
| float yv; |
| int lblptr; |
| } *xx; |
| |
| char *labels; |
| int labelsiz; |
| |
| int tick = 50; |
| int top = 4000; |
| int bot = 200; |
| float absbot; |
| int n; |
| int erasf = 1; |
| int gridf = 2; |
| int symbf = 0; |
| int absf = 0; |
| int transf; |
| int equf; |
| int brkf; |
| int ovlay = 1; |
| float dx; |
| char *plotsymb; |
| |
| #define BSIZ 80 |
| char labbuf[BSIZ]; |
| char titlebuf[BSIZ]; |
| |
| char *modes[] = { |
| "disconnected", |
| "solid", |
| "dotted", |
| "dotdashed", |
| "shortdashed", |
| "longdashed" |
| }; |
| int mode = 1; |
| double ident(double x){ |
| return(x); |
| } |
| |
| struct z { |
| float lb,ub,mult,quant; |
| }; |
| |
| struct { |
| char *name; |
| int next; |
| } palette[256]; |
| |
| static char* colors[] = { |
| "blue", |
| "cyan", |
| "green", |
| "kblack", |
| "magenta", |
| "red", |
| "white", |
| "yellow" |
| }; |
| static void |
| initpalette(void) |
| { |
| int i; |
| |
| for(i=0; i<nelem(colors); i++){ |
| palette[(uchar)colors[i][0]].name = colors[i]; |
| palette[(uchar)colors[i][0]].next = colors[i][0]; |
| } |
| } |
| |
| int pencolor = 'k'; |
| |
| void init(struct xy *); |
| void setopt(int, char *[]); |
| void readin(void); |
| void transpose(void); |
| void getlim(struct xy *, struct val *); |
| void equilibrate(struct xy *, struct xy *); |
| void scale(struct xy *); |
| void limread(struct xy *, int *, char ***); |
| int numb(float *, int *, char ***); |
| void colread(int *, char ***); |
| int copystring(int); |
| struct z setloglim(int, int, float, float); |
| struct z setlinlim(int, int, float, float); |
| void axes(void); |
| int setmark(int *, struct xy *); |
| void submark(int *, int *, float, struct xy *); |
| void plot(void); |
| int getfloat(float *); |
| int getstring(void); |
| void title(void); |
| void badarg(void); |
| int conv(float, struct xy *, int *); |
| int symbol(int, int, int); |
| void axlab(char, struct xy *, char *); |
| |
| int main(int argc,char *argv[]){ |
| |
| initpalette(); |
| openpl(); |
| range(0,0,4096,4096); |
| init(&xd); |
| init(&yd); |
| xd.xsize = yd.xsize = 1.; |
| xx = (struct val *)malloc((unsigned)sizeof(struct val)); |
| labels = malloc(1); |
| labels[labelsiz++] = 0; |
| setopt(argc,argv); |
| if(erasf) |
| erase(); |
| readin(); |
| transpose(); |
| getlim(&xd,(struct val *)&xx->xv); |
| getlim(&yd,(struct val *)&xx->yv); |
| if(equf) { |
| equilibrate(&xd,&yd); |
| equilibrate(&yd,&xd); |
| } |
| scale(&xd); |
| scale(&yd); |
| axes(); |
| title(); |
| plot(); |
| closepl(); |
| exits(0); |
| return 0; /* gcc */ |
| } |
| |
| void init(struct xy *p){ |
| p->xf = ident; |
| p->xmult = 1; |
| } |
| |
| void setopt(int argc, char *argv[]){ |
| char *p1, *p2; |
| float temp; |
| |
| xd.xlb = yd.xlb = INF; |
| xd.xub = yd.xub = -INF; |
| while(--argc > 0) { |
| argv++; |
| again: switch(argv[0][0]) { |
| case '-': |
| argv[0]++; |
| goto again; |
| case 'l': /* label for plot */ |
| p1 = titlebuf; |
| if (argc>=2) { |
| argv++; |
| argc--; |
| p2 = argv[0]; |
| while (*p1++ = *p2++); |
| } |
| break; |
| |
| case 'd': /*disconnected,obsolete option*/ |
| case 'm': /*line mode*/ |
| mode = 0; |
| if(!numb(&temp,&argc,&argv)) |
| break; |
| if(temp>=sizeof(modes)/sizeof(*modes)) |
| mode = 1; |
| else if(temp>=-1) |
| mode = temp; |
| break; |
| |
| case 'o': |
| if(numb(&temp,&argc,&argv) && temp>=1) |
| ovlay = temp; |
| break; |
| case 'a': /*automatic abscissas*/ |
| absf = 1; |
| dx = 1; |
| if(!numb(&dx,&argc,&argv)) |
| break; |
| if(numb(&absbot,&argc,&argv)) |
| absf = 2; |
| break; |
| |
| case 's': /*save screen, overlay plot*/ |
| erasf = 0; |
| break; |
| |
| case 'g': /*grid style 0 none, 1 ticks, 2 full*/ |
| gridf = 0; |
| if(!numb(&temp,&argc,&argv)) |
| temp = argv[0][1]-'0'; /*for caompatibility*/ |
| if(temp>=0&&temp<=2) |
| gridf = temp; |
| break; |
| |
| case 'c': /*character(s) for plotting*/ |
| if(argc >= 2) { |
| symbf = 1; |
| plotsymb = argv[1]; |
| argv++; |
| argc--; |
| } |
| break; |
| |
| case 't': /*transpose*/ |
| transf = 1; |
| break; |
| case 'e': /*equal scales*/ |
| equf = 1; |
| break; |
| case 'b': /*breaks*/ |
| brkf = 1; |
| break; |
| case 'x': /*x limits */ |
| limread(&xd,&argc,&argv); |
| break; |
| case 'y': |
| limread(&yd,&argc,&argv); |
| break; |
| case 'h': /*set height of plot */ |
| if(!numb(&yd.xsize, &argc,&argv)) |
| badarg(); |
| break; |
| case 'w': /*set width of plot */ |
| if(!numb(&xd.xsize, &argc, &argv)) |
| badarg(); |
| break; |
| case 'r': /* set offset to right */ |
| if(!numb(&xd.xoff, &argc, &argv)) |
| badarg(); |
| break; |
| case 'u': /*set offset up the screen*/ |
| if(!numb(&yd.xoff,&argc,&argv)) |
| badarg(); |
| break; |
| case 'p': /*pen color*/ |
| colread(&argc, &argv); |
| break; |
| default: |
| badarg(); |
| } |
| } |
| } |
| |
| void limread(struct xy *p, int *argcp, char ***argvp){ |
| if(*argcp>1 && (*argvp)[1][0]=='l') { |
| (*argcp)--; |
| (*argvp)++; |
| p->xf = log10; |
| } |
| if(!numb(&p->xlb,argcp,argvp)) |
| return; |
| p->xlbf = 1; |
| if(!numb(&p->xub,argcp,argvp)) |
| return; |
| p->xubf = 1; |
| if(!numb(&p->xquant,argcp,argvp)) |
| return; |
| p->xqf = 1; |
| } |
| |
| #ifdef NOTDEF |
| isdigit(char c){ |
| return '0'<=c && c<='9'; |
| } |
| #endif |
| |
| int |
| numb(float *np, int *argcp, char ***argvp){ |
| char c; |
| |
| if(*argcp <= 1) |
| return(0); |
| while((c=(*argvp)[1][0]) == '+') |
| (*argvp)[1]++; |
| if(!(isdigit((uchar)c) || c=='-'&&(*argvp)[1][1]<'A' || c=='.')) |
| return(0); |
| *np = atof((*argvp)[1]); |
| (*argcp)--; |
| (*argvp)++; |
| return(1); |
| } |
| |
| void colread(int *argcp, char ***argvp){ |
| int c, cnext; |
| int i, n; |
| |
| if(*argcp<=1) |
| return; |
| n = strlen((*argvp)[1]); |
| if(strspn((*argvp)[1], "bcgkmrwy")!=n) |
| return; |
| pencolor = cnext = (*argvp)[1][0]; |
| for(i=0; i<n-1; i++){ |
| c = (unsigned char)(*argvp)[1][i]; |
| cnext = (unsigned char)(*argvp)[1][i+1]; |
| palette[c].next = cnext; |
| } |
| palette[cnext].next = pencolor; |
| (*argcp)--; |
| (*argvp)++; |
| } |
| |
| void readin(void){ |
| int i, t; |
| struct val *temp; |
| |
| if(absf==1) { |
| if(xd.xlbf) |
| absbot = xd.xlb; |
| else if(xd.xf==log10) |
| absbot = 1; |
| } |
| for(;;) { |
| temp = (struct val *)realloc((char*)xx, |
| (unsigned)(n+ovlay)*sizeof(struct val)); |
| if(temp==0) |
| return; |
| xx = temp; |
| if(absf) |
| xx[n].xv = n*dx/ovlay + absbot; |
| else |
| if(!getfloat(&xx[n].xv)) |
| return; |
| t = 0; /* silence compiler */ |
| for(i=0;i<ovlay;i++) { |
| xx[n+i].xv = xx[n].xv; |
| if(!getfloat(&xx[n+i].yv)) |
| return; |
| xx[n+i].lblptr = -1; |
| t = getstring(); |
| if(t>0) |
| xx[n+i].lblptr = copystring(t); |
| if(t<0 && i+1<ovlay) |
| return; |
| } |
| n += ovlay; |
| if(t<0) |
| return; |
| } |
| } |
| |
| void transpose(void){ |
| int i; |
| float f; |
| struct xy t; |
| if(!transf) |
| return; |
| t = xd; xd = yd; yd = t; |
| for(i= 0;i<n;i++) { |
| f = xx[i].xv; xx[i].xv = xx[i].yv; xx[i].yv = f; |
| } |
| } |
| |
| int copystring(int k){ |
| char *temp; |
| int i; |
| int q; |
| |
| temp = realloc(labels,(unsigned)(labelsiz+1+k)); |
| if(temp==0) |
| return(0); |
| labels = temp; |
| q = labelsiz; |
| for(i=0;i<=k;i++) |
| labels[labelsiz++] = labbuf[i]; |
| return(q); |
| } |
| |
| float modceil(float f, float t){ |
| |
| t = fabs(t); |
| return(ceil(f/t)*t); |
| } |
| |
| float |
| modfloor(float f, float t){ |
| t = fabs(t); |
| return(floor(f/t)*t); |
| } |
| |
| void getlim(struct xy *p, struct val *v){ |
| int i; |
| |
| i = 0; |
| do { |
| if(!p->xlbf && p->xlb>v[i].xv) |
| p->xlb = v[i].xv; |
| if(!p->xubf && p->xub<v[i].xv) |
| p->xub = v[i].xv; |
| i++; |
| } while(i < n); |
| } |
| |
| void setlim(struct xy *p){ |
| float t,delta,sign; |
| struct z z; |
| int mark[50]; |
| float lb,ub; |
| int lbf,ubf; |
| |
| lb = p->xlb; |
| ub = p->xub; |
| delta = ub-lb; |
| if(p->xqf) { |
| if(delta*p->xquant <=0 ) |
| badarg(); |
| return; |
| } |
| sign = 1; |
| lbf = p->xlbf; |
| ubf = p->xubf; |
| if(delta < 0) { |
| sign = -1; |
| t = lb; |
| lb = ub; |
| ub = t; |
| t = lbf; |
| lbf = ubf; |
| ubf = t; |
| } |
| else if(delta == 0) { |
| if(ub > 0) { |
| ub = 2*ub; |
| lb = 0; |
| } |
| else |
| if(lb < 0) { |
| lb = 2*lb; |
| ub = 0; |
| } |
| else { |
| ub = 1; |
| lb = -1; |
| } |
| } |
| if(p->xf==log10 && lb>0 && ub>lb) { |
| z = setloglim(lbf,ubf,lb,ub); |
| p->xlb = z.lb; |
| p->xub = z.ub; |
| p->xmult *= z.mult; |
| p->xquant = z.quant; |
| if(setmark(mark,p)<2) { |
| p->xqf = lbf = ubf = 1; |
| lb = z.lb; ub = z.ub; |
| } else |
| return; |
| } |
| z = setlinlim(lbf,ubf,lb,ub); |
| if(sign > 0) { |
| p->xlb = z.lb; |
| p->xub = z.ub; |
| } else { |
| p->xlb = z.ub; |
| p->xub = z.lb; |
| } |
| p->xmult *= z.mult; |
| p->xquant = sign*z.quant; |
| } |
| |
| struct z |
| setloglim(int lbf, int ubf, float lb, float ub){ |
| float r,s,t; |
| struct z z; |
| |
| for(s=1; lb*s<1; s*=10) ; |
| lb *= s; |
| ub *= s; |
| for(r=1; 10*r<=lb; r*=10) ; |
| for(t=1; t<ub; t*=10) ; |
| z.lb = !lbf ? r : lb; |
| z.ub = !ubf ? t : ub; |
| if(ub/lb<100) { |
| if(!lbf) { |
| if(lb >= 5*z.lb) |
| z.lb *= 5; |
| else if(lb >= 2*z.lb) |
| z.lb *= 2; |
| } |
| if(!ubf) { |
| if(ub*5 <= z.ub) |
| z.ub /= 5; |
| else if(ub*2 <= z.ub) |
| z.ub /= 2; |
| } |
| } |
| z.mult = s; |
| z.quant = r; |
| return(z); |
| } |
| |
| struct z |
| setlinlim(int lbf, int ubf, float xlb, float xub){ |
| struct z z; |
| float r,s,delta; |
| float ub,lb; |
| |
| loop: |
| ub = xub; |
| lb = xlb; |
| delta = ub - lb; |
| /*scale up by s, a power of 10, so range (delta) exceeds 1*/ |
| /*find power of 10 quantum, r, such that delta/10<=r<delta*/ |
| r = s = 1; |
| while(delta*s < 10) |
| s *= 10; |
| delta *= s; |
| while(10*r < delta) |
| r *= 10; |
| lb *= s; |
| ub *= s; |
| /*set r=(1,2,5)*10**n so that 3-5 quanta cover range*/ |
| if(r>=delta/2) |
| r /= 2; |
| else if(r<delta/5) |
| r *= 2; |
| z.ub = ubf? ub: modceil(ub,r); |
| z.lb = lbf? lb: modfloor(lb,r); |
| if(!lbf && z.lb<=r && z.lb>0) { |
| xlb = 0; |
| goto loop; |
| } |
| else if(!ubf && z.ub>=-r && z.ub<0) { |
| xub = 0; |
| goto loop; |
| } |
| z.quant = r; |
| z.mult = s; |
| return(z); |
| } |
| |
| void scale(struct xy *p){ |
| float edge; |
| |
| setlim(p); |
| edge = top-bot; |
| p->xa = p->xsize*edge/((*p->xf)(p->xub) - (*p->xf)(p->xlb)); |
| p->xbot = bot + edge*p->xoff; |
| p->xtop = p->xbot + (top-bot)*p->xsize; |
| p->xb = p->xbot - (*p->xf)(p->xlb)*p->xa + .5; |
| } |
| |
| void equilibrate(struct xy *p, struct xy *q){ |
| if(p->xlbf|| /* needn't test xubf; it implies xlbf*/ |
| q->xubf&&q->xlb>q->xub) |
| return; |
| if(p->xlb>q->xlb) { |
| p->xlb = q->xlb; |
| p->xlbf = q->xlbf; |
| } |
| if(p->xub<q->xub) { |
| p->xub = q->xub; |
| p->xubf = q->xubf; |
| } |
| } |
| |
| void axes(void){ |
| int i; |
| int mark[50]; |
| int xn, yn; |
| if(gridf==0) |
| return; |
| |
| line(xd.xbot,yd.xbot,xd.xtop,yd.xbot); |
| vec(xd.xtop,yd.xtop); |
| vec(xd.xbot,yd.xtop); |
| vec(xd.xbot,yd.xbot); |
| |
| xn = setmark(mark,&xd); |
| for(i=0; i<xn; i++) { |
| if(gridf==2) |
| line(mark[i],yd.xbot,mark[i],yd.xtop); |
| if(gridf==1) { |
| line(mark[i],yd.xbot,mark[i],yd.xbot+tick); |
| line(mark[i],yd.xtop-tick,mark[i],yd.xtop); |
| } |
| } |
| yn = setmark(mark,&yd); |
| for(i=0; i<yn; i++) { |
| if(gridf==2) |
| line(xd.xbot,mark[i],xd.xtop,mark[i]); |
| if(gridf==1) { |
| line(xd.xbot,mark[i],xd.xbot+tick,mark[i]); |
| line(xd.xtop-tick,mark[i],xd.xtop,mark[i]); |
| } |
| } |
| } |
| |
| int |
| setmark(int *xmark, struct xy *p){ |
| int xn = 0; |
| float x,xl,xu; |
| float q; |
| if(p->xf==log10&&!p->xqf) { |
| for(x=p->xquant; x<p->xub; x*=10) { |
| submark(xmark,&xn,x,p); |
| if(p->xub/p->xlb<=100) { |
| submark(xmark,&xn,2*x,p); |
| submark(xmark,&xn,5*x,p); |
| } |
| } |
| } else { |
| xn = 0; |
| q = p->xquant; |
| if(q>0) { |
| xl = modceil(p->xlb+q/6,q); |
| xu = modfloor(p->xub-q/6,q)+q/2; |
| } else { |
| xl = modceil(p->xub-q/6,q); |
| xu = modfloor(p->xlb+q/6,q)-q/2; |
| } |
| for(x=xl; x<=xu; x+=fabs(p->xquant)) |
| xmark[xn++] = (*p->xf)(x)*p->xa + p->xb; |
| } |
| return(xn); |
| } |
| void submark(int *xmark, int *pxn, float x, struct xy *p){ |
| if(1.001*p->xlb < x && .999*p->xub > x) |
| xmark[(*pxn)++] = log10(x)*p->xa + p->xb; |
| } |
| |
| void plot(void){ |
| int ix,iy; |
| int i,j; |
| int conn; |
| |
| for(j=0;j<ovlay;j++) { |
| switch(mode) { |
| case -1: |
| pen(modes[j%(sizeof modes/sizeof *modes-1)+1]); |
| break; |
| case 0: |
| break; |
| default: |
| pen(modes[mode]); |
| } |
| color(palette[pencolor].name); |
| conn = 0; |
| for(i=j; i<n; i+=ovlay) { |
| if(!conv(xx[i].xv,&xd,&ix) || |
| !conv(xx[i].yv,&yd,&iy)) { |
| conn = 0; |
| continue; |
| } |
| if(mode!=0) { |
| if(conn != 0) |
| vec(ix,iy); |
| else |
| move(ix,iy); |
| conn = 1; |
| } |
| conn &= symbol(ix,iy,xx[i].lblptr); |
| } |
| pencolor = palette[pencolor].next; |
| } |
| pen(modes[1]); |
| } |
| |
| int |
| conv(float xv, struct xy *p, int *ip){ |
| long ix; |
| ix = p->xa*(*p->xf)(xv*p->xmult) + p->xb; |
| if(ix<p->xbot || ix>p->xtop) |
| return(0); |
| *ip = ix; |
| return(1); |
| } |
| |
| int |
| getfloat(float *p){ |
| int i; |
| |
| i = scanf("%f",p); |
| return(i==1); |
| } |
| |
| int |
| getstring(void){ |
| int i; |
| char junk[20]; |
| i = scanf("%1s",labbuf); |
| if(i==-1) |
| return(-1); |
| switch(*labbuf) { |
| default: |
| if(!isdigit((uchar)*labbuf)) { |
| ungetc(*labbuf,stdin); |
| i = scanf("%s",labbuf); |
| break; |
| } |
| case '.': |
| case '+': |
| case '-': |
| ungetc(*labbuf,stdin); |
| return(0); |
| case '"': |
| i = scanf("%[^\"\n]",labbuf); |
| scanf("%[\"]",junk); |
| break; |
| } |
| if(i==-1) |
| return(-1); |
| return(strlen(labbuf)); |
| } |
| |
| int |
| symbol(int ix, int iy, int k){ |
| |
| if(symbf==0&&k<0) { |
| if(mode==0) |
| point(ix,iy); |
| return(1); |
| } |
| else { |
| move(ix,iy); |
| text(k>=0?labels+k:plotsymb); |
| move(ix,iy); |
| return(!brkf|k<0); |
| } |
| } |
| |
| void title(void){ |
| char buf[BSIZ+100]; |
| buf[0] = ' '; |
| buf[1] = ' '; |
| buf[2] = ' '; |
| strcpy(buf+3,titlebuf); |
| if(erasf&&gridf) { |
| axlab('x',&xd,buf); |
| strcat(buf,","); |
| axlab('y',&yd,buf); |
| } |
| move(xd.xbot,yd.xbot-60); |
| text(buf); |
| } |
| |
| void axlab(char c, struct xy *p, char *b){ |
| char *dir; |
| dir = p->xlb<p->xub? "<=": ">="; |
| sprintf(b+strlen(b), " %g %s %c%s %s %g", p->xlb/p->xmult, |
| dir, c, p->xf==log10?" (log)":"", dir, p->xub/p->xmult); |
| } |
| |
| void badarg(void){ |
| fprintf(stderr,"graph: error in arguments\n"); |
| closepl(); |
| exits("bad arg"); |
| } |