xref: /plan9/sys/src/cmd/graph/graph.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
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