plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

ticks.c (10891B)


      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 
      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	goffside = 0;		/* no ticks on grid on these sides */
     17 int	tick_dir = OUT;
     18 double	ticklen	= TICKLEN;	/* default tick length */
     19 int	autoticks = LEFT|BOT;
     20 int	autodir = 0;		/* set LEFT, etc. if automatic ticks go in */
     21 
     22 void 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 
     31 void dflt_tick(double f)
     32 {
     33 	if (f >= 0.0)
     34 		savetick(f, tostring("%g"));
     35 	else
     36 		savetick(f, tostring("\\%g"));
     37 }
     38 
     39 void tickside(int n)	/* remember which side these ticks/gridlines go on */
     40 {
     41 	tside |= n;
     42 }
     43 
     44 void tickoff(int side)	/* remember explicit sides */
     45 {
     46 	toffside |= side;
     47 }
     48 
     49 void gridtickoff(void)	/* turn grid ticks off on the side previously specified (ugh) */
     50 {
     51 	goffside = tside;
     52 }
     53 
     54 void setlist(void)	/* remember that there was an explicit list */
     55 {
     56 	tlist = 1;
     57 }
     58 
     59 void tickdir(int dir, double val, int explicit)	/* remember in/out [expr] */
     60 {
     61 	tick_dir = dir;
     62 	if (explicit)
     63 		ticklen = val;
     64 }
     65 
     66 void 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 
     91 double modfloor(double f, double t)
     92 {
     93 	t = fabs(t);
     94 	return floor(f/t) * t;
     95 }
     96 
     97 double modceil(double f, double t)
     98 {
     99 	t = fabs(t);
    100 	return ceil(f/t) * t;
    101 }
    102 
    103 double	xtmin, xtmax;	/* range of ticks */
    104 double	ytmin, ytmax;
    105 double	xquant, xmult;	/* quantization & scale for auto x ticks */
    106 double	yquant, ymult;
    107 double	lograt = 5;
    108 
    109 void 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 
    167 void 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 
    175 Obj *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 
    197 void 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 
    228 void 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 
    275 void 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 
    319 void 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 
    328 void 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 
    384 void 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 
    441 Attr	*grid_desc	= 0;
    442 
    443 void griddesc(Attr *a)
    444 {
    445 	grid_desc = a;
    446 }
    447 
    448 void 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 
    465 char *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 
    484 int
    485 sidelog(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 }