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