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