ticks.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  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 <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <math.h>
  13. #include "grap.h"
  14. #include "y.tab.h"
  15. #define MAXTICK 200
  16. int ntick = 0;
  17. double tickval[MAXTICK]; /* tick values (one axis at a time */
  18. char *tickstr[MAXTICK]; /* and labels */
  19. int tside = 0;
  20. int tlist = 0; /* 1 => explicit values given */
  21. int toffside = 0; /* no ticks on these sides */
  22. int goffside = 0; /* no ticks on grid on these sides */
  23. int tick_dir = OUT;
  24. double ticklen = TICKLEN; /* default tick length */
  25. int autoticks = LEFT|BOT;
  26. int autodir = 0; /* set LEFT, etc. if automatic ticks go in */
  27. void savetick(double f, char *s) /* remember tick location and label */
  28. {
  29. if (ntick >= MAXTICK)
  30. ERROR "too many ticks (%d)", MAXTICK FATAL;
  31. tickval[ntick] = f;
  32. tickstr[ntick] = s;
  33. ntick++;
  34. }
  35. void dflt_tick(double f)
  36. {
  37. if (f >= 0.0)
  38. savetick(f, tostring("%g"));
  39. else
  40. savetick(f, tostring("\\%g"));
  41. }
  42. void tickside(int n) /* remember which side these ticks/gridlines go on */
  43. {
  44. tside |= n;
  45. }
  46. void tickoff(int side) /* remember explicit sides */
  47. {
  48. toffside |= side;
  49. }
  50. void gridtickoff(void) /* turn grid ticks off on the side previously specified (ugh) */
  51. {
  52. goffside = tside;
  53. }
  54. void setlist(void) /* remember that there was an explicit list */
  55. {
  56. tlist = 1;
  57. }
  58. void tickdir(int dir, double val, int explicit) /* remember in/out [expr] */
  59. {
  60. tick_dir = dir;
  61. if (explicit)
  62. ticklen = val;
  63. }
  64. void ticks(void) /* set autoticks after ticks statement */
  65. {
  66. /* was there an explicit "ticks [side] off"? */
  67. if (toffside)
  68. autoticks &= ~toffside;
  69. /* was there an explicit list? (eg "ticks at ..." or "ticks from ...") */
  70. if (tlist) {
  71. if (tside & (BOT|TOP))
  72. autoticks &= ~(BOT|TOP);
  73. if (tside & (LEFT|RIGHT))
  74. autoticks &= ~(LEFT|RIGHT);
  75. }
  76. /* was there a side without a list? (eg "ticks left in") */
  77. if (tside && !tlist) {
  78. if (tick_dir == IN)
  79. autodir |= tside;
  80. if (tside & (BOT|TOP))
  81. autoticks = (autoticks & ~(BOT|TOP)) | (tside & (BOT|TOP));
  82. if (tside & (LEFT|RIGHT))
  83. autoticks = (autoticks & ~(LEFT|RIGHT)) | (tside & (LEFT|RIGHT));
  84. }
  85. tlist = tside = toffside = goffside = 0;
  86. tick_dir = OUT;
  87. }
  88. double modfloor(double f, double t)
  89. {
  90. t = fabs(t);
  91. return floor(f/t) * t;
  92. }
  93. double modceil(double f, double t)
  94. {
  95. t = fabs(t);
  96. return ceil(f/t) * t;
  97. }
  98. double xtmin, xtmax; /* range of ticks */
  99. double ytmin, ytmax;
  100. double xquant, xmult; /* quantization & scale for auto x ticks */
  101. double yquant, ymult;
  102. double lograt = 5;
  103. void do_autoticks(Obj *p) /* make set of ticks for default coord only */
  104. {
  105. double x, xl, xu, q;
  106. if (p == NULL)
  107. return;
  108. fprintf(tfd, "Autoticks:\t# x %g..%g, y %g..%g",
  109. p->pt.x, p->pt1.x, p->pt.y, p->pt1.y);
  110. fprintf(tfd, "; xt %g,%g, yt %g,%g, xq,xm = %g,%g, yq,ym = %g,%g\n",
  111. xtmin, xtmax, ytmin, ytmax, xquant, xmult, yquant, ymult);
  112. if ((autoticks & (BOT|TOP)) && p->pt1.x >= p->pt.x) { /* make x ticks */
  113. q = xquant;
  114. xl = p->pt.x;
  115. xu = p->pt1.x;
  116. if (xl >= xu)
  117. dflt_tick(xl);
  118. else if ((p->log & XFLAG) && xu/xl >= lograt) {
  119. for (x = q; x < xu; x *= 10) {
  120. logtick(x, xl, xu);
  121. if (xu/xl <= 100) {
  122. logtick(2*x, xl, xu);
  123. logtick(5*x, xl, xu);
  124. }
  125. }
  126. } else {
  127. xl = modceil(xtmin - q/100, q);
  128. xu = modfloor(xtmax + q/100, q) + q/2;
  129. for (x = xl; x <= xu; x += q)
  130. dflt_tick(x);
  131. }
  132. tside = autoticks & (BOT|TOP);
  133. ticklist(p, 0);
  134. }
  135. if ((autoticks & (LEFT|RIGHT)) && p->pt1.y >= p->pt.y) { /* make y ticks */
  136. q = yquant;
  137. xl = p->pt.y;
  138. xu = p->pt1.y;
  139. if (xl >= xu)
  140. dflt_tick(xl);
  141. else if ((p->log & YFLAG) && xu/xl >= lograt) {
  142. for (x = q; x < xu; x *= 10) {
  143. logtick(x, xl, xu);
  144. if (xu/xl <= 100) {
  145. logtick(2*x, xl, xu);
  146. logtick(5*x, xl, xu);
  147. }
  148. }
  149. } else {
  150. xl = modceil(ytmin - q/100, q);
  151. xu = modfloor(ytmax + q/100, q) + q/2;
  152. for (x = xl; x <= xu; x += q)
  153. dflt_tick(x);
  154. }
  155. tside = autoticks & (LEFT|RIGHT);
  156. ticklist(p, 0);
  157. }
  158. }
  159. void logtick(double v, double lb, double ub)
  160. {
  161. float slop = 1.0; /* was 1.001 */
  162. if (slop * lb <= v && ub >= slop * v)
  163. dflt_tick(v);
  164. }
  165. Obj *setauto(void) /* compute new min,max, and quant & mult */
  166. {
  167. Obj *p, *q;
  168. if ((q = lookup("lograt",0)) != NULL)
  169. lograt = q->fval;
  170. for (p = objlist; p; p = p->next)
  171. if (p->type == NAME && strcmp(p->name,dflt_coord) == 0)
  172. break;
  173. if (p) {
  174. if ((p->log & XFLAG) && p->pt1.x/p->pt.x >= lograt)
  175. autolog(p, 'x');
  176. else
  177. autoside(p, 'x');
  178. if ((p->log & YFLAG) && p->pt1.y/p->pt.y >= lograt)
  179. autolog(p, 'y');
  180. else
  181. autoside(p, 'y');
  182. }
  183. return p;
  184. }
  185. void autoside(Obj *p, int side)
  186. {
  187. double r, s, d, ub, lb;
  188. if (side == 'x') {
  189. xtmin = lb = p->pt.x;
  190. xtmax = ub = p->pt1.x;
  191. } else {
  192. ytmin = lb = p->pt.y;
  193. ytmax = ub = p->pt1.y;
  194. }
  195. if (ub <= lb)
  196. return; /* cop out on little ranges */
  197. d = ub - lb;
  198. r = s = 1;
  199. while (d * s < 10)
  200. s *= 10;
  201. d *= s;
  202. while (10 * r < d)
  203. r *= 10;
  204. if (r > d/3)
  205. r /= 2;
  206. else if (r <= d/6)
  207. r *= 2;
  208. if (side == 'x') {
  209. xquant = r / s;
  210. } else {
  211. yquant = r / s;
  212. }
  213. }
  214. void autolog(Obj *p, int side)
  215. {
  216. double r, s, t, ub, lb;
  217. int flg;
  218. if (side == 'x') {
  219. xtmin = lb = p->pt.x;
  220. xtmax = ub = p->pt1.x;
  221. flg = p->coord & XFLAG;
  222. } else {
  223. ytmin = lb = p->pt.y;
  224. ytmax = ub = p->pt1.y;
  225. flg = p->coord & YFLAG;
  226. }
  227. for (s = 1; lb * s < 1; s *= 10)
  228. ;
  229. lb *= s;
  230. ub *= s;
  231. for (r = 1; 10 * r < lb; r *= 10)
  232. ;
  233. for (t = 1; t < ub; t *= 10)
  234. ;
  235. if (side == 'x')
  236. xquant = r / s;
  237. else
  238. yquant = r / s;
  239. if (flg)
  240. return;
  241. if (ub / lb < 100) {
  242. if (lb >= 5 * r)
  243. r *= 5;
  244. else if (lb >= 2 * r)
  245. r *= 2;
  246. if (ub * 5 <= t)
  247. t /= 5;
  248. else if (ub * 2 <= t)
  249. t /= 2;
  250. if (side == 'x') {
  251. xtmin = r / s;
  252. xtmax = t / s;
  253. } else {
  254. ytmin = r / s;
  255. ytmax = t / s;
  256. }
  257. }
  258. }
  259. void iterator(double from, double to, int op, double by, char *fmt) /* create an iterator */
  260. {
  261. double x;
  262. /* should validate limits, etc. */
  263. /* punt for now */
  264. dprintf("iterate from %g to %g by %g, op = %c, fmt=%s\n",
  265. from, to, by, op, fmt ? fmt : "");
  266. switch (op) {
  267. case '+':
  268. case ' ':
  269. for (x = from; x <= to + (SLOP-1) * by; x += by)
  270. if (fmt)
  271. savetick(x, tostring(fmt));
  272. else
  273. dflt_tick(x);
  274. break;
  275. case '-':
  276. for (x = from; x >= to; x -= by)
  277. if (fmt)
  278. savetick(x, tostring(fmt));
  279. else
  280. dflt_tick(x);
  281. break;
  282. case '*':
  283. for (x = from; x <= SLOP * to; x *= by)
  284. if (fmt)
  285. savetick(x, tostring(fmt));
  286. else
  287. dflt_tick(x);
  288. break;
  289. case '/':
  290. for (x = from; x >= to; x /= by)
  291. if (fmt)
  292. savetick(x, tostring(fmt));
  293. else
  294. dflt_tick(x);
  295. break;
  296. }
  297. if (fmt)
  298. free(fmt);
  299. }
  300. void ticklist(Obj *p, int explicit) /* fire out the accumulated ticks */
  301. /* 1 => list, 0 => auto */
  302. {
  303. if (p == NULL)
  304. return;
  305. fprintf(tfd, "Ticks_%s:\n\tticklen = %g\n", p->name, ticklen);
  306. print_ticks(TICKS, explicit, p, "ticklen", "");
  307. }
  308. void print_ticks(int type, int explicit, Obj *p, char *lenstr, char *descstr)
  309. {
  310. int i, logflag, inside;
  311. char buf[100];
  312. double tv;
  313. for (i = 0; i < ntick; i++) /* any ticks given explicitly? */
  314. if (tickstr[i] != NULL)
  315. break;
  316. if (i >= ntick && type == TICKS) /* no, so use values */
  317. for (i = 0; i < ntick; i++) {
  318. if (tickval[i] >= 0.0)
  319. sprintf(buf, "%g", tickval[i]);
  320. else
  321. sprintf(buf, "\\-%g", -tickval[i]);
  322. tickstr[i] = tostring(buf);
  323. }
  324. else
  325. for (i = 0; i < ntick; i++) {
  326. if (tickstr[i] != NULL) {
  327. sprintf(buf, tickstr[i], tickval[i]);
  328. free(tickstr[i]);
  329. tickstr[i] = tostring(buf);
  330. }
  331. }
  332. logflag = sidelog(p->log, tside);
  333. for (i = 0; i < ntick; i++) {
  334. tv = tickval[i];
  335. halfrange(p, tside, tv);
  336. if (logflag) {
  337. if (tv <= 0.0)
  338. ERROR "can't take log of tick value %g", tv FATAL;
  339. logit(tv);
  340. }
  341. if (type == GRID)
  342. inside = LEFT|RIGHT|TOP|BOT;
  343. else if (explicit)
  344. inside = (tick_dir == IN) ? tside : 0;
  345. else
  346. inside = autodir;
  347. if (tside & BOT)
  348. maketick(type, p->name, BOT, inside, tv, tickstr[i], lenstr, descstr);
  349. if (tside & TOP)
  350. maketick(type, p->name, TOP, inside, tv, tickstr[i], lenstr, descstr);
  351. if (tside & LEFT)
  352. maketick(type, p->name, LEFT, inside, tv, tickstr[i], lenstr, descstr);
  353. if (tside & RIGHT)
  354. maketick(type, p->name, RIGHT, inside, tv, tickstr[i], lenstr, descstr);
  355. if (tickstr[i]) {
  356. free(tickstr[i]);
  357. tickstr[i] = NULL;
  358. }
  359. }
  360. ntick = 0;
  361. }
  362. void maketick(int type, char *name, int side, int inflag, double val,
  363. char *lab, char *lenstr, char *descstr)
  364. {
  365. char *sidestr, *td;
  366. fprintf(tfd, "\tline %s ", descstr);
  367. inflag &= side;
  368. switch (side) {
  369. case BOT:
  370. case 0:
  371. td = inflag ? "up" : "down";
  372. fprintf(tfd, "%s %s from (x_%s(%g),0)", td, lenstr, name, val);
  373. break;
  374. case TOP:
  375. td = inflag ? "down" : "up";
  376. fprintf(tfd, "%s %s from (x_%s(%g),frameht)", td, lenstr, name, val);
  377. break;
  378. case LEFT:
  379. td = inflag ? "right" : "left";
  380. fprintf(tfd, "%s %s from (0,y_%s(%g))", td, lenstr, name, val);
  381. break;
  382. case RIGHT:
  383. td = inflag ? "left" : "right";
  384. fprintf(tfd, "%s %s from (framewid,y_%s(%g))", td, lenstr, name, val);
  385. break;
  386. }
  387. fprintf(tfd, "\n");
  388. if (type == GRID && (side & goffside)) /* wanted no ticks on grid */
  389. return;
  390. sidestr = tick_dir == IN ? "start" : "end";
  391. if (lab != NULL) {
  392. /* BUG: should fix size of lab here */
  393. double wid = strlen(lab)/7.5 + (tick_dir == IN ? 0 : 0.1); /* estimate width at 15 chars/inch */
  394. switch (side) {
  395. case BOT: case 0:
  396. /* can drop "box invis" with new pic */
  397. fprintf(tfd, "\tbox invis \"%s\" ht .25 wid 0 with .n at last line.%s",
  398. lab, sidestr);
  399. break;
  400. case TOP:
  401. fprintf(tfd, "\tbox invis \"%s\" ht .2 wid 0 with .s at last line.%s",
  402. lab, sidestr);
  403. break;
  404. case LEFT:
  405. fprintf(tfd, "\t\"%s \" wid %.2f rjust at last line.%s",
  406. lab, wid, sidestr);
  407. break;
  408. case RIGHT:
  409. fprintf(tfd, "\t\" %s\" wid %.2f ljust at last line.%s",
  410. lab, wid, sidestr);
  411. break;
  412. }
  413. /* BUG: works only if "down x" comes before "at wherever" */
  414. lab_adjust();
  415. fprintf(tfd, "\n");
  416. }
  417. }
  418. Attr *grid_desc = 0;
  419. void griddesc(Attr *a)
  420. {
  421. grid_desc = a;
  422. }
  423. void gridlist(Obj *p)
  424. {
  425. char *framestr;
  426. if ((tside & (BOT|TOP)) || tside == 0)
  427. framestr = "frameht";
  428. else
  429. framestr = "framewid";
  430. fprintf(tfd, "Grid_%s:\n", p->name);
  431. tick_dir = IN;
  432. print_ticks(GRID, 0, p, framestr, desc_str(grid_desc));
  433. if (grid_desc) {
  434. freeattr(grid_desc);
  435. grid_desc = 0;
  436. }
  437. }
  438. char *desc_str(Attr *a) /* convert DOT to "dotted", etc. */
  439. {
  440. static char buf[50], *p;
  441. if (a == NULL)
  442. return p = "";
  443. switch (a->type) {
  444. case DOT: p = "dotted"; break;
  445. case DASH: p = "dashed"; break;
  446. case INVIS: p = "invis"; break;
  447. default: p = "";
  448. }
  449. if (a->fval != 0.0) {
  450. sprintf(buf, "%s %g", p, a->fval);
  451. return buf;
  452. } else
  453. return p;
  454. }
  455. sidelog(int logflag, int side) /* figure out whether to scale a side */
  456. {
  457. if ((logflag & XFLAG) && ((side & (BOT|TOP)) || side == 0))
  458. return 1;
  459. else if ((logflag & YFLAG) && (side & (LEFT|RIGHT)))
  460. return 1;
  461. else
  462. return 0;
  463. }