Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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;
}