mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-27 11:52:03 +00:00
493 lines
11 KiB
C
493 lines
11 KiB
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;
|
|
}
|
|
|
|
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;
|
|
}
|