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 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 31 tickside(n) /* remember which side these ticks go on */ 32 int n; 33 { 34 tside |= n; 35 } 36 37 tickoff(side) /* remember explicit sides */ 38 int side; 39 { 40 toffside |= side; 41 } 42 43 setlist() /* remember that there was an explicit list */ 44 { 45 tlist = 1; 46 } 47 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 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 79 double modfloor(f, t) 80 double f, t; 81 { 82 t = fabs(t); 83 return floor(f/t) * t; 84 } 85 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 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 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 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 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 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 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 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 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 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 424 griddesc(a) 425 Attr *a; 426 { 427 grid_desc = a; 428 } 429 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 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 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