From 4314729ddef28cb619ce97d50f0968ca24c93803 Mon Sep 17 00:00:00 2001 From: rsc Date: Wed, 14 Apr 2004 19:54:10 +0000 Subject: Add graph, plot --- src/cmd/graph/graph.c | 708 +++++++++++++++++++++++++++++++++++++++++++++++++ src/cmd/graph/iplot.h | 39 +++ src/cmd/graph/mkfile | 9 + src/cmd/graph/subr.c | 19 ++ src/cmd/graph/whoami.c | 5 + 5 files changed, 780 insertions(+) create mode 100644 src/cmd/graph/graph.c create mode 100644 src/cmd/graph/iplot.h create mode 100644 src/cmd/graph/mkfile create mode 100644 src/cmd/graph/subr.c create mode 100644 src/cmd/graph/whoami.c (limited to 'src/cmd/graph') diff --git a/src/cmd/graph/graph.c b/src/cmd/graph/graph.c new file mode 100644 index 00000000..d1b6938f --- /dev/null +++ b/src/cmd/graph/graph.c @@ -0,0 +1,708 @@ +#include +#include +#include +#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; +}; +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 ***); +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 *); + +void main(int argc,char *argv[]){ + + 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); +} + +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; + 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; +} + +int +isdigit(char c){ + return '0'<=c && c<='9'; +} + +int +numb(float *np, int *argcp, char ***argvp){ + char c; + + if(*argcp <= 1) + return(0); + while((c=(*argvp)[1][0]) == '+') + (*argvp)[1]++; + if(!(isdigit(c) || c=='-'&&(*argvp)[1][1]<'A' || c=='.')) + return(0); + *np = atof((*argvp)[1]); + (*argcp)--; + (*argvp)++; + return(1); +} + +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;i0) + xx[n+i].lblptr = copystring(t); + if(t<0 && i+1xlbf && p->xlb>v[i].xv) + p->xlb = v[i].xv; + if(!p->xubf && p->xubxub = 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= 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/2) + r /= 2; + else if(r0) { + 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->xubxub) { + 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; ixf==log10&&!p->xqf) { + for(x=p->xquant; xxub; 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;jxa*(*p->xf)(xv*p->xmult) + p->xb; + if(ixxbot || 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(*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->xlbxub? "<=": ">="; + 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"); +} diff --git a/src/cmd/graph/iplot.h b/src/cmd/graph/iplot.h new file mode 100644 index 00000000..b6cd50bc --- /dev/null +++ b/src/cmd/graph/iplot.h @@ -0,0 +1,39 @@ +#define arc(_x,_y,_X,_Y,_u,_v,_r) printf("a %d %d %d %d %d %d %d\n",_x,_y,_X,_Y,_u,_v,_r) +#define box(_x,_y,_X,_Y) printf("bo %d %d %d %d\n", _x,_y,_X,_Y) +#define bspline(_num,_ff) {printf("bs {\n"); putnum(_num,_ff); printf("}\n");} +#define call(_sname,_x) printf("ca %s %d\n", _sname,_x) +#define cfill(_s) printf("cf %s\n", _s) +#define circle(_x,_y,_r) printf("ci %d %d %d\n", _x,_y,_r) +#define closepl() printf("cl\n") +#define color(_s) printf("co %s\n", _s) +#define cspline(_num,_ff) {printf("cs {\n"); putnum(_num,_ff); printf("}\n");} +#define pdefine(_sname, _str) printf("de %s {\n%s\n",_sname, _str) +#define dspline(_num,_ff) {printf("ds {\n");putnum(_num,_ff);printf("}\n");} +#define erase() printf("e\n") +#define ffill(_s) printf("ff %s\n", _s) +#define fill(_num,_ff) {printf("fi {\n"); putnum(_num,_ff); printf("}\n");} +#define fpoly(_s) printf("fp %s\n", _s) +#define frame(_x,_y,_X,_Y) printf("fr %g %g %g %g\n", _x,_y,_X,_Y) +#define fspline(_s) printf("fs %s\n", _s) +#define grade(_x) printf("g %d\n", _x) +#define idle() +#define line(_x1,_y1,_x2,_y2) printf("li %d %d %d %d\n", _x1,_y1,_x2,_y2) +#define lspline(_num,_ff) {printf("ls {\n"); putnum(_num,_ff); printf("}\n");} +#define move(_x, _y) printf("m %d %d\n", _x, _y) +#define openpl() printf("o\n") +#define parabola(_x,_y,_X,_Y,_u,_v) printf("\npa %d %d %d %d %d %d\n", _x,_y,_X,_Y,_u,_v) +#define pen(_s) printf("pe %s\n", _s) +#define point(_x,_y) printf("poi %d %d\n", _x,_y) +#define poly(_num,_ff) {printf("pol {\n"); putnum(_num,_ff); printf("}\n");} +#define ppause() printf("pp\n") +#define range(_x,_y,_X,_Y) printf("ra %d %d %d %d\n", _x,_y,_X,_Y) +#define restore() printf("re\n") +#define rmove(_x,_y) printf("rm %d %d\n", _x,_y) +#define rvec(_x,_y) printf("rv %d %d\n", _x,_y) +#define save() printf( "sa\n") +#define sbox(_x,_y,_X,_Y) printf("sb %d %d %d %d\n", _x,_y,_X,_Y) +#define spline(_num,_ff) {printf("sp {\n"); putnum(_num,_ff); printf("}\n");} +#define text(_s) {if(*(_s) == ' ')printf("t \"%s\"\n",_s); else printf("t %s\n", _s); } +#define vec(_x,_y) printf("v %d %d\n", _x,_y) +void putnum(int [], double *[]); +char *whoami(void); diff --git a/src/cmd/graph/mkfile b/src/cmd/graph/mkfile new file mode 100644 index 00000000..4a45add1 --- /dev/null +++ b/src/cmd/graph/mkfile @@ -0,0 +1,9 @@ +PLAN9=../../.. +<$PLAN9/src/mkhdr + +TARG=graph +OFILES=graph.$O\ + subr.$O\ + whoami.$O\ + +<$PLAN9/src/mkone diff --git a/src/cmd/graph/subr.c b/src/cmd/graph/subr.c new file mode 100644 index 00000000..9531523d --- /dev/null +++ b/src/cmd/graph/subr.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include "iplot.h" +void putnum(int num[], double *ff[]){ + double **fp, *xp; + int *np, n,i; + np = num; + fp = ff; + while( (n = *np++)){ + xp = *fp++; + printf("{ "); + for(i=0; i