diff options
Diffstat (limited to 'src/cmd/grap/ticks.c')
-rw-r--r-- | src/cmd/grap/ticks.c | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/src/cmd/grap/ticks.c b/src/cmd/grap/ticks.c new file mode 100644 index 00000000..72fc06f5 --- /dev/null +++ b/src/cmd/grap/ticks.c @@ -0,0 +1,492 @@ +#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; +} |