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