graph.c 13 KB

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