ticks.c 11 KB

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