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 }