123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #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;
- }
|