ref: 2657773699da675d436705002230f2d25142f570
dir: /sys/src/cmd/grap/ticks.c/
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include "grap.h" #include "y.tab.h" #define MAXTICK 200 int ntick = 0; double tickval[MAXTICK]; /* tick values (one axis at a time */ char *tickstr[MAXTICK]; /* and labels */ int tside = 0; int tlist = 0; /* 1 => explicit values given */ int toffside = 0; /* no ticks on these sides */ int goffside = 0; /* no ticks on grid on these sides */ int tick_dir = OUT; double ticklen = TICKLEN; /* default tick length */ int autoticks = LEFT|BOT; int autodir = 0; /* set LEFT, etc. if automatic ticks go in */ void savetick(double f, char *s) /* remember tick location and label */ { if (ntick >= MAXTICK) ERROR "too many ticks (%d)", MAXTICK FATAL; tickval[ntick] = f; tickstr[ntick] = s; ntick++; } void dflt_tick(double f) { if (f >= 0.0) savetick(f, tostring("%g")); else savetick(f, tostring("\\%g")); } void tickside(int n) /* remember which side these ticks/gridlines go on */ { tside |= n; } void tickoff(int side) /* remember explicit sides */ { toffside |= side; } void gridtickoff(void) /* turn grid ticks off on the side previously specified (ugh) */ { goffside = tside; } void setlist(void) /* remember that there was an explicit list */ { tlist = 1; } void tickdir(int dir, double val, int explicit) /* remember in/out [expr] */ { tick_dir = dir; if (explicit) ticklen = val; } void ticks(void) /* set autoticks after ticks statement */ { /* was there an explicit "ticks [side] off"? */ if (toffside) autoticks &= ~toffside; /* was there an explicit list? (eg "ticks at ..." or "ticks from ...") */ if (tlist) { if (tside & (BOT|TOP)) autoticks &= ~(BOT|TOP); if (tside & (LEFT|RIGHT)) autoticks &= ~(LEFT|RIGHT); } /* was there a side without a list? (eg "ticks left in") */ if (tside && !tlist) { if (tick_dir == IN) autodir |= tside; if (tside & (BOT|TOP)) autoticks = (autoticks & ~(BOT|TOP)) | (tside & (BOT|TOP)); if (tside & (LEFT|RIGHT)) autoticks = (autoticks & ~(LEFT|RIGHT)) | (tside & (LEFT|RIGHT)); } tlist = tside = toffside = goffside = 0; tick_dir = OUT; } double modfloor(double f, double t) { t = fabs(t); return floor(f/t) * t; } double modceil(double f, double t) { t = fabs(t); return ceil(f/t) * t; } double xtmin, xtmax; /* range of ticks */ double ytmin, ytmax; double xquant, xmult; /* quantization & scale for auto x ticks */ double yquant, ymult; double lograt = 5; void do_autoticks(Obj *p) /* make set of ticks for default coord only */ { double x, xl, xu, q; if (p == NULL) return; fprintf(tfd, "Autoticks:\t# x %g..%g, y %g..%g", p->pt.x, p->pt1.x, p->pt.y, p->pt1.y); fprintf(tfd, "; xt %g,%g, yt %g,%g, xq,xm = %g,%g, yq,ym = %g,%g\n", xtmin, xtmax, ytmin, ytmax, xquant, xmult, yquant, ymult); if ((autoticks & (BOT|TOP)) && p->pt1.x >= p->pt.x) { /* make x ticks */ q = xquant; xl = p->pt.x; xu = p->pt1.x; if (xl >= xu) dflt_tick(xl); else if ((p->log & XFLAG) && xu/xl >= lograt) { for (x = q; x < xu; x *= 10) { logtick(x, xl, xu); if (xu/xl <= 100) { logtick(2*x, xl, xu); logtick(5*x, xl, xu); } } } else { xl = modceil(xtmin - q/100, q); xu = modfloor(xtmax + q/100, q) + q/2; for (x = xl; x <= xu; x += q) dflt_tick(x); } tside = autoticks & (BOT|TOP); ticklist(p, 0); } if ((autoticks & (LEFT|RIGHT)) && p->pt1.y >= p->pt.y) { /* make y ticks */ q = yquant; xl = p->pt.y; xu = p->pt1.y; if (xl >= xu) dflt_tick(xl); else if ((p->log & YFLAG) && xu/xl >= lograt) { for (x = q; x < xu; x *= 10) { logtick(x, xl, xu); if (xu/xl <= 100) { logtick(2*x, xl, xu); logtick(5*x, xl, xu); } } } else { xl = modceil(ytmin - q/100, q); xu = modfloor(ytmax + q/100, q) + q/2; for (x = xl; x <= xu; x += q) dflt_tick(x); } tside = autoticks & (LEFT|RIGHT); ticklist(p, 0); } } void logtick(double v, double lb, double ub) { float slop = 1.0; /* was 1.001 */ if (slop * lb <= v && ub >= slop * v) dflt_tick(v); } Obj *setauto(void) /* compute new min,max, and quant & mult */ { Obj *p, *q; if ((q = lookup("lograt",0)) != NULL) lograt = q->fval; for (p = objlist; p; p = p->next) if (p->type == NAME && strcmp(p->name,dflt_coord) == 0) break; if (p) { if ((p->log & XFLAG) && p->pt1.x/p->pt.x >= lograt) autolog(p, 'x'); else autoside(p, 'x'); if ((p->log & YFLAG) && p->pt1.y/p->pt.y >= lograt) autolog(p, 'y'); else autoside(p, 'y'); } return p; } void autoside(Obj *p, int side) { double r, s, d, ub, lb; if (side == 'x') { xtmin = lb = p->pt.x; xtmax = ub = p->pt1.x; } else { ytmin = lb = p->pt.y; ytmax = ub = p->pt1.y; } if (ub <= lb) return; /* cop out on little ranges */ d = ub - lb; r = s = 1; while (d * s < 10) s *= 10; d *= s; while (10 * r < d) r *= 10; if (r > d/3) r /= 2; else if (r <= d/6) r *= 2; if (side == 'x') { xquant = r / s; } else { yquant = r / s; } } void autolog(Obj *p, int side) { double r, s, t, ub, lb; int flg; if (side == 'x') { xtmin = lb = p->pt.x; xtmax = ub = p->pt1.x; flg = p->coord & XFLAG; } else { ytmin = lb = p->pt.y; ytmax = ub = p->pt1.y; flg = p->coord & YFLAG; } for (s = 1; lb * s < 1; s *= 10) ; lb *= s; ub *= s; for (r = 1; 10 * r < lb; r *= 10) ; for (t = 1; t < ub; t *= 10) ; if (side == 'x') xquant = r / s; else yquant = r / s; if (flg) return; if (ub / lb < 100) { if (lb >= 5 * r) r *= 5; else if (lb >= 2 * r) r *= 2; if (ub * 5 <= t) t /= 5; else if (ub * 2 <= t) t /= 2; if (side == 'x') { xtmin = r / s; xtmax = t / s; } else { ytmin = r / s; ytmax = t / s; } } } void iterator(double from, double to, int op, double by, char *fmt) /* create an iterator */ { double x; /* should validate limits, etc. */ /* punt for now */ dprintf("iterate from %g to %g by %g, op = %c, fmt=%s\n", from, to, by, op, fmt ? fmt : ""); switch (op) { case '+': case ' ': for (x = from; x <= to + (SLOP-1) * by; x += by) if (fmt) savetick(x, tostring(fmt)); else dflt_tick(x); break; case '-': for (x = from; x >= to; x -= by) if (fmt) savetick(x, tostring(fmt)); else dflt_tick(x); break; case '*': for (x = from; x <= SLOP * to; x *= by) if (fmt) savetick(x, tostring(fmt)); else dflt_tick(x); break; case '/': for (x = from; x >= to; x /= by) if (fmt) savetick(x, tostring(fmt)); else dflt_tick(x); break; } if (fmt) free(fmt); } void ticklist(Obj *p, int explicit) /* fire out the accumulated ticks */ /* 1 => list, 0 => auto */ { if (p == NULL) return; fprintf(tfd, "Ticks_%s:\n\tticklen = %g\n", p->name, ticklen); print_ticks(TICKS, explicit, p, "ticklen", ""); } void print_ticks(int type, int explicit, Obj *p, char *lenstr, char *descstr) { int i, logflag, inside; char buf[100]; double tv; for (i = 0; i < ntick; i++) /* any ticks given explicitly? */ if (tickstr[i] != NULL) break; if (i >= ntick && type == TICKS) /* no, so use values */ for (i = 0; i < ntick; i++) { if (tickval[i] >= 0.0) sprintf(buf, "%g", tickval[i]); else sprintf(buf, "\\-%g", -tickval[i]); tickstr[i] = tostring(buf); } else for (i = 0; i < ntick; i++) { if (tickstr[i] != NULL) { sprintf(buf, tickstr[i], tickval[i]); free(tickstr[i]); tickstr[i] = tostring(buf); } } logflag = sidelog(p->log, tside); for (i = 0; i < ntick; i++) { tv = tickval[i]; halfrange(p, tside, tv); if (logflag) { if (tv <= 0.0) ERROR "can't take log of tick value %g", tv FATAL; logit(tv); } if (type == GRID) inside = LEFT|RIGHT|TOP|BOT; else if (explicit) inside = (tick_dir == IN) ? tside : 0; else inside = autodir; if (tside & BOT) maketick(type, p->name, BOT, inside, tv, tickstr[i], lenstr, descstr); if (tside & TOP) maketick(type, p->name, TOP, inside, tv, tickstr[i], lenstr, descstr); if (tside & LEFT) maketick(type, p->name, LEFT, inside, tv, tickstr[i], lenstr, descstr); if (tside & RIGHT) maketick(type, p->name, RIGHT, inside, tv, tickstr[i], lenstr, descstr); if (tickstr[i]) { free(tickstr[i]); tickstr[i] = NULL; } } ntick = 0; } void maketick(int type, char *name, int side, int inflag, double val, char *lab, char *lenstr, char *descstr) { char *sidestr, *td; fprintf(tfd, "\tline %s ", descstr); inflag &= side; switch (side) { case BOT: case 0: td = inflag ? "up" : "down"; fprintf(tfd, "%s %s from (x_%s(%g),0)", td, lenstr, name, val); break; case TOP: td = inflag ? "down" : "up"; fprintf(tfd, "%s %s from (x_%s(%g),frameht)", td, lenstr, name, val); break; case LEFT: td = inflag ? "right" : "left"; fprintf(tfd, "%s %s from (0,y_%s(%g))", td, lenstr, name, val); break; case RIGHT: td = inflag ? "left" : "right"; fprintf(tfd, "%s %s from (framewid,y_%s(%g))", td, lenstr, name, val); break; } fprintf(tfd, "\n"); if (type == GRID && (side & goffside)) /* wanted no ticks on grid */ return; sidestr = tick_dir == IN ? "start" : "end"; if (lab != NULL) { /* BUG: should fix size of lab here */ double wid = strlen(lab)/7.5 + (tick_dir == IN ? 0 : 0.1); /* estimate width at 15 chars/inch */ switch (side) { case BOT: case 0: /* can drop "box invis" with new pic */ fprintf(tfd, "\tbox invis \"%s\" ht .25 wid 0 with .n at last line.%s", lab, sidestr); break; case TOP: fprintf(tfd, "\tbox invis \"%s\" ht .2 wid 0 with .s at last line.%s", lab, sidestr); break; case LEFT: fprintf(tfd, "\t\"%s \" wid %.2f rjust at last line.%s", lab, wid, sidestr); break; case RIGHT: fprintf(tfd, "\t\" %s\" wid %.2f ljust at last line.%s", lab, wid, sidestr); break; } /* BUG: works only if "down x" comes before "at wherever" */ lab_adjust(); fprintf(tfd, "\n"); } } Attr *grid_desc = 0; void griddesc(Attr *a) { grid_desc = a; } void gridlist(Obj *p) { char *framestr; if ((tside & (BOT|TOP)) || tside == 0) framestr = "frameht"; else framestr = "framewid"; fprintf(tfd, "Grid_%s:\n", p->name); tick_dir = IN; print_ticks(GRID, 0, p, framestr, desc_str(grid_desc)); if (grid_desc) { freeattr(grid_desc); grid_desc = 0; } } char *desc_str(Attr *a) /* convert DOT to "dotted", etc. */ { static char buf[50], *p; if (a == NULL) return p = ""; switch (a->type) { case DOT: p = "dotted"; break; case DASH: p = "dashed"; break; case INVIS: p = "invis"; break; default: p = ""; } if (a->fval != 0.0) { sprintf(buf, "%s %g", p, a->fval); return buf; } else return p; } sidelog(int logflag, int side) /* figure out whether to scale a side */ { if ((logflag & XFLAG) && ((side & (BOT|TOP)) || side == 0)) return 1; else if ((logflag & YFLAG) && (side & (LEFT|RIGHT))) return 1; else return 0; }