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