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