blob: 72c81b21fe4a6990c0e792afc4170b561207b5be [file] [log] [blame]
#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;
}
int
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;
}