do not need pseudo-INF, just set bounds sensibly instead
[unix-history] / usr / src / usr.bin / graph / graph.c
CommitLineData
2791ff57
KB
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
9char copyright[] =
10"@(#) Copyright (c) 1991 The Regents of the University of California.\n\
11 All rights reserved.\n";
12#endif /* not lint */
13
3457b4e6 14#ifndef lint
ac8a1a73 15static char sccsid[] = "@(#)graph.c 4.4 (Berkeley) %G%";
2791ff57 16#endif /* not lint */
3457b4e6 17
78ffd996
SL
18#include <stdio.h>
19#include <ctype.h>
20#include <math.h>
78ffd996
SL
21#define F .25
22
23struct xy {
24 int xlbf; /*flag:explicit lower bound*/
25 int xubf; /*flag:explicit upper bound*/
26 int xqf; /*flag:explicit quantum*/
27 double (*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;
36struct val {
37 float xv;
38 float yv;
39 int lblptr;
40} *xx;
41
42char *labs;
43int labsiz;
44
45int tick = 50;
46int top = 4000;
47int bot = 200;
48float absbot;
49int n;
50int erasf = 1;
51int gridf = 2;
52int symbf = 0;
53int absf = 0;
54int transf;
55int brkf;
56float dx;
57char *plotsymb;
58
59double atof();
60#define BSIZ 80
61char labbuf[BSIZ];
62char titlebuf[BSIZ];
63
64char *modes[] = {
65 "disconnected",
66 "solid",
67 "dotted",
68 "dotdashed",
69 "shortdashed",
70 "longdashed"
71};
72int mode = 1;
73char *realloc();
74char *malloc();
75
76double ident(x)
77double x;
78{
79 return(x);
80}
81
82main(argc,argv)
83char *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 labs = malloc(1);
92 labs[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
108init(p)
109struct xy *p;
110{
111 p->xf = ident;
112 p->xmult = 1;
113}
114
115setopt(argc,argv)
116char *argv[];
117{
118 char *p1, *p2;
119 float temp;
120
ac8a1a73
CT
121 xd.xlb = yd.xlb = 0;
122 xd.xub = yd.xub = 0;
78ffd996
SL
123 while(--argc > 0) {
124 argv++;
125again: 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
214limread(p, argcp, argvp)
215register struct xy *p;
216int *argcp;
217char ***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
235numb(np, argcp, argvp)
236int *argcp;
237float *np;
238register 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
254readin()
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
288transpose()
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
301copystring(k)
302{
303 register char *temp;
304 register i;
305 int q;
306
307 temp = realloc(labs,(unsigned)(labsiz+1+k));
308 if(temp==0)
309 return(0);
310 labs = temp;
311 q = labsiz;
312 for(i=0;i<=k;i++)
313 labs[labsiz++] = labbuf[i];
314 return(q);
315}
316
317float
318modceil(f,t)
319float f,t;
320{
321
322 t = fabs(t);
323 return(ceil(f/t)*t);
324}
325
326float
327modfloor(f,t)
328float f,t;
329{
330 t = fabs(t);
331 return(floor(f/t)*t);
332}
333
ac8a1a73
CT
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 */
78ffd996
SL
339getlim(p,v)
340register struct xy *p;
341struct val *v;
342{
343 register i;
344
ac8a1a73
CT
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 }
78ffd996
SL
357}
358
359struct z {
360 float lb,ub,mult,quant;
361} setloglim(), setlinlim();
362
363setlim(p)
364register 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
431struct z
432setloglim(lbf,ubf,lb,ub)
433float 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
464struct z
465setlinlim(lbf,ubf,xlb,xub)
466int lbf,ubf;
467float xlb,xub;
468{
469 struct z z;
470 float r,s,delta;
471 float ub,lb;
472
473loop:
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
507scale(p,v)
508register struct xy *p;
509struct 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
522axes()
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
555setmark(xmark,p)
556int *xmark;
557register 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}
585submark(xmark,pxn,x,p)
586int *xmark;
587int *pxn;
588float x;
589struct 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
595plot()
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
622conv(xv,p,ip)
623float xv;
624register struct xy *p;
625int *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
635getfloat(p)
636float *p;
637{
638 register i;
639
640 i = scanf("%f",p);
641 return(i==1);
642}
643
644getstring()
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
674symbol(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?labs+k:plotsymb);
685 move(ix,iy);
686 return(!brkf|k<0);
687 }
688}
689
690title()
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
704axlab(c,p)
705char c;
706struct 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
714badarg()
715{
716 fprintf(stderr,"graph: error in arguments\n");
717 exit(1);
718}