URI:
       tticks.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tticks.c (10891B)
       ---
            1 #include <stdio.h>
            2 #include <stdlib.h>
            3 #include <string.h>
            4 #include <math.h>
            5 #include "grap.h"
            6 #include "y.tab.h"
            7 
            8 #define        MAXTICK        200
            9 int        ntick        = 0;
           10 double        tickval[MAXTICK];        /* tick values (one axis at a time */
           11 char        *tickstr[MAXTICK];        /* and labels */
           12 
           13 int        tside        = 0;
           14 int        tlist        = 0;                /* 1 => explicit values given */
           15 int        toffside = 0;                /* no ticks on these sides */
           16 int        goffside = 0;                /* no ticks on grid on these sides */
           17 int        tick_dir = OUT;
           18 double        ticklen        = TICKLEN;        /* default tick length */
           19 int        autoticks = LEFT|BOT;
           20 int        autodir = 0;                /* set LEFT, etc. if automatic ticks go in */
           21 
           22 void savetick(double f, char *s)        /* remember tick location and label */
           23 {
           24         if (ntick >= MAXTICK)
           25                 ERROR "too many ticks (%d)", MAXTICK FATAL;
           26         tickval[ntick] = f;
           27         tickstr[ntick] = s;
           28         ntick++;
           29 }
           30 
           31 void dflt_tick(double f)
           32 {
           33         if (f >= 0.0)
           34                 savetick(f, tostring("%g"));
           35         else
           36                 savetick(f, tostring("\\%g"));
           37 }
           38 
           39 void tickside(int n)        /* remember which side these ticks/gridlines go on */
           40 {
           41         tside |= n;
           42 }
           43 
           44 void tickoff(int side)        /* remember explicit sides */
           45 {
           46         toffside |= side;
           47 }
           48 
           49 void gridtickoff(void)        /* turn grid ticks off on the side previously specified (ugh) */
           50 {
           51         goffside = tside;
           52 }
           53 
           54 void setlist(void)        /* remember that there was an explicit list */
           55 {
           56         tlist = 1;
           57 }
           58 
           59 void tickdir(int dir, double val, int explicit)        /* remember in/out [expr] */
           60 {
           61         tick_dir = dir;
           62         if (explicit)
           63                 ticklen = val;
           64 }
           65 
           66 void ticks(void)                /* set autoticks after ticks statement */
           67 {
           68         /* was there an explicit "ticks [side] off"? */
           69         if (toffside)
           70                 autoticks &= ~toffside;
           71         /* was there an explicit list? (eg "ticks at ..." or "ticks from ...") */
           72         if (tlist) {
           73                 if (tside & (BOT|TOP))
           74                         autoticks &= ~(BOT|TOP);
           75                 if (tside & (LEFT|RIGHT))
           76                         autoticks &= ~(LEFT|RIGHT);
           77         }
           78         /* was there a side without a list? (eg "ticks left in") */
           79         if (tside && !tlist) {
           80                 if (tick_dir == IN)
           81                         autodir |= tside;
           82                 if (tside & (BOT|TOP))
           83                         autoticks = (autoticks & ~(BOT|TOP)) | (tside & (BOT|TOP));
           84                 if (tside & (LEFT|RIGHT))
           85                         autoticks = (autoticks & ~(LEFT|RIGHT)) | (tside & (LEFT|RIGHT));
           86         }
           87         tlist = tside = toffside = goffside = 0;
           88         tick_dir = OUT;
           89 }
           90 
           91 double modfloor(double f, double t)
           92 {
           93         t = fabs(t);
           94         return floor(f/t) * t;
           95 }
           96 
           97 double modceil(double f, double t)
           98 {
           99         t = fabs(t);
          100         return ceil(f/t) * t;
          101 }
          102 
          103 double        xtmin, xtmax;        /* range of ticks */
          104 double        ytmin, ytmax;
          105 double        xquant, xmult;        /* quantization & scale for auto x ticks */
          106 double        yquant, ymult;
          107 double        lograt = 5;
          108 
          109 void do_autoticks(Obj *p)        /* make set of ticks for default coord only */
          110 {
          111         double x, xl, xu, q;
          112 
          113         if (p == NULL)
          114                 return;
          115         fprintf(tfd, "Autoticks:\t# x %g..%g, y %g..%g",
          116                 p->pt.x, p->pt1.x, p->pt.y, p->pt1.y);
          117         fprintf(tfd, ";   xt %g,%g, yt %g,%g, xq,xm = %g,%g, yq,ym = %g,%g\n",
          118                 xtmin, xtmax, ytmin, ytmax, xquant, xmult, yquant, ymult);
          119         if ((autoticks & (BOT|TOP)) && p->pt1.x >= p->pt.x) {        /* make x ticks */
          120                 q = xquant;
          121                 xl = p->pt.x;
          122                 xu = p->pt1.x;
          123                 if (xl >= xu)
          124                         dflt_tick(xl);
          125                 else if ((p->log & XFLAG) && xu/xl >= lograt) {
          126                         for (x = q; x < xu; x *= 10) {
          127                                 logtick(x, xl, xu);
          128                                 if (xu/xl <= 100) {
          129                                         logtick(2*x, xl, xu);
          130                                         logtick(5*x, xl, xu);
          131                                 }
          132                         }
          133                 } else {
          134                         xl = modceil(xtmin - q/100, q);
          135                         xu = modfloor(xtmax + q/100, q) + q/2;
          136                         for (x = xl; x <= xu; x += q)
          137                                 dflt_tick(x);
          138                 }
          139                 tside = autoticks & (BOT|TOP);
          140                 ticklist(p, 0);
          141         }
          142         if ((autoticks & (LEFT|RIGHT)) && p->pt1.y >= p->pt.y) {        /* make y ticks */
          143                 q = yquant;
          144                 xl = p->pt.y;
          145                 xu = p->pt1.y;
          146                 if (xl >= xu)
          147                         dflt_tick(xl);
          148                 else if ((p->log & YFLAG) && xu/xl >= lograt) {
          149                         for (x = q; x < xu; x *= 10) {
          150                                 logtick(x, xl, xu);
          151                                 if (xu/xl <= 100) {
          152                                         logtick(2*x, xl, xu);
          153                                         logtick(5*x, xl, xu);
          154                                 }
          155                         }
          156                 } else {
          157                         xl = modceil(ytmin - q/100, q);
          158                         xu = modfloor(ytmax + q/100, q) + q/2;
          159                         for (x = xl; x <= xu; x += q)
          160                                 dflt_tick(x);
          161                 }
          162                 tside = autoticks & (LEFT|RIGHT);
          163                 ticklist(p, 0);
          164         }
          165 }
          166 
          167 void logtick(double v, double lb, double ub)
          168 {
          169         float slop = 1.0;        /* was 1.001 */
          170 
          171         if (slop * lb <= v && ub >= slop * v)
          172                 dflt_tick(v);
          173 }
          174 
          175 Obj *setauto(void)        /* compute new min,max, and quant & mult */
          176 {
          177         Obj *p, *q;
          178 
          179         if ((q = lookup("lograt",0)) != NULL)
          180                 lograt = q->fval;
          181         for (p = objlist; p; p = p->next)
          182                 if (p->type == NAME && strcmp(p->name,dflt_coord) == 0)
          183                         break;
          184         if (p) {
          185                 if ((p->log & XFLAG) && p->pt1.x/p->pt.x >= lograt)
          186                         autolog(p, 'x');
          187                 else
          188                         autoside(p, 'x');
          189                 if ((p->log & YFLAG) && p->pt1.y/p->pt.y >= lograt)
          190                         autolog(p, 'y');
          191                 else
          192                         autoside(p, 'y');
          193         }
          194         return p;
          195 }
          196 
          197 void autoside(Obj *p, int side)
          198 {
          199         double r, s, d, ub, lb;
          200 
          201         if (side == 'x') {
          202                 xtmin = lb = p->pt.x;
          203                 xtmax = ub = p->pt1.x;
          204         } else {
          205                 ytmin = lb = p->pt.y;
          206                 ytmax = ub = p->pt1.y;
          207         }
          208         if (ub <= lb)
          209                 return;        /* cop out on little ranges */
          210         d = ub - lb;
          211         r = s = 1;
          212         while (d * s < 10)
          213                 s *= 10;
          214         d *= s;
          215         while (10 * r < d)
          216                 r *= 10;
          217         if (r > d/3)
          218                 r /= 2;
          219         else if (r <= d/6)
          220                 r *= 2;
          221         if (side == 'x') {
          222                 xquant = r / s;
          223         } else {
          224                 yquant = r / s;
          225         }
          226 }
          227 
          228 void autolog(Obj *p, int side)
          229 {
          230         double r, s, t, ub, lb;
          231         int flg;
          232 
          233         if (side == 'x') {
          234                 xtmin = lb = p->pt.x;
          235                 xtmax = ub = p->pt1.x;
          236                 flg = p->coord & XFLAG;
          237         } else {
          238                 ytmin = lb = p->pt.y;
          239                 ytmax = ub = p->pt1.y;
          240                 flg = p->coord & YFLAG;
          241         }
          242         for (s = 1; lb * s < 1; s *= 10)
          243                 ;
          244         lb *= s;
          245         ub *= s;
          246         for (r = 1; 10 * r < lb; r *= 10)
          247                 ;
          248         for (t = 1; t < ub; t *= 10)
          249                 ;
          250         if (side == 'x')
          251                 xquant = r / s;
          252         else
          253                 yquant = r / s;
          254         if (flg)
          255                 return;
          256         if (ub / lb < 100) {
          257                 if (lb >= 5 * r)
          258                         r *= 5;
          259                 else if (lb >= 2 * r)
          260                         r *= 2;
          261                 if (ub * 5 <= t)
          262                         t /= 5;
          263                 else if (ub * 2 <= t)
          264                         t /= 2;
          265                 if (side == 'x') {
          266                         xtmin = r / s;
          267                         xtmax = t / s;
          268                 } else {
          269                         ytmin = r / s;
          270                         ytmax = t / s;
          271                 }
          272         }
          273 }
          274 
          275 void iterator(double from, double to, int op, double by, char *fmt)        /* create an iterator */
          276 {
          277         double x;
          278 
          279         /* should validate limits, etc. */
          280         /* punt for now */
          281 
          282         dprintf("iterate from %g to %g by %g, op = %c, fmt=%s\n",
          283                 from, to, by, op, fmt ? fmt : "");
          284         switch (op) {
          285         case '+':
          286         case ' ':
          287                 for (x = from; x <= to + (SLOP-1) * by; x += by)
          288                         if (fmt)
          289                                 savetick(x, tostring(fmt));
          290                         else
          291                                 dflt_tick(x);
          292                 break;
          293         case '-':
          294                 for (x = from; x >= to; x -= by)
          295                         if (fmt)
          296                                 savetick(x, tostring(fmt));
          297                         else
          298                                 dflt_tick(x);
          299                 break;
          300         case '*':
          301                 for (x = from; x <= SLOP * to; x *= by)
          302                         if (fmt)
          303                                 savetick(x, tostring(fmt));
          304                         else
          305                                 dflt_tick(x);
          306                 break;
          307         case '/':
          308                 for (x = from; x >= to; x /= by)
          309                         if (fmt)
          310                                 savetick(x, tostring(fmt));
          311                         else
          312                                 dflt_tick(x);
          313                 break;
          314         }
          315         if (fmt)
          316                 free(fmt);
          317 }
          318 
          319 void ticklist(Obj *p, int explicit)        /* fire out the accumulated ticks */
          320                                         /* 1 => list, 0 => auto */
          321 {
          322         if (p == NULL)
          323                 return;
          324         fprintf(tfd, "Ticks_%s:\n\tticklen = %g\n", p->name, ticklen);
          325         print_ticks(TICKS, explicit, p, "ticklen", "");
          326 }
          327 
          328 void print_ticks(int type, int explicit, Obj *p, char *lenstr, char *descstr)
          329 {
          330         int i, logflag, inside;
          331         char buf[100];
          332         double tv;
          333 
          334         for (i = 0; i < ntick; i++)        /* any ticks given explicitly? */
          335                 if (tickstr[i] != NULL)
          336                         break;
          337         if (i >= ntick && type == TICKS)        /* no, so use values */
          338                 for (i = 0; i < ntick; i++) {
          339                         if (tickval[i] >= 0.0)
          340                                 sprintf(buf, "%g", tickval[i]);
          341                         else
          342                                 sprintf(buf, "\\-%g", -tickval[i]);
          343                         tickstr[i] = tostring(buf);
          344                 }
          345         else
          346                 for (i = 0; i < ntick; i++) {
          347                         if (tickstr[i] != NULL) {
          348                                 sprintf(buf, tickstr[i], tickval[i]);
          349                                 free(tickstr[i]);
          350                                 tickstr[i] = tostring(buf);
          351                         }
          352                 }
          353         logflag = sidelog(p->log, tside);
          354         for (i = 0; i < ntick; i++) {
          355                 tv = tickval[i];
          356                 halfrange(p, tside, tv);
          357                 if (logflag) {
          358                         if (tv <= 0.0)
          359                                 ERROR "can't take log of tick value %g", tv FATAL;
          360                         logit(tv);
          361                 }
          362                 if (type == GRID)
          363                         inside = LEFT|RIGHT|TOP|BOT;
          364                 else if (explicit)
          365                         inside = (tick_dir == IN) ? tside : 0;
          366                 else
          367                         inside = autodir;
          368                 if (tside & BOT)
          369                         maketick(type, p->name, BOT, inside, tv, tickstr[i], lenstr, descstr);
          370                 if (tside & TOP)
          371                         maketick(type, p->name, TOP, inside, tv, tickstr[i], lenstr, descstr);
          372                 if (tside & LEFT)
          373                         maketick(type, p->name, LEFT, inside, tv, tickstr[i], lenstr, descstr);
          374                 if (tside & RIGHT)
          375                         maketick(type, p->name, RIGHT, inside, tv, tickstr[i], lenstr, descstr);
          376                 if (tickstr[i]) {
          377                         free(tickstr[i]);
          378                         tickstr[i] = NULL;
          379                 }
          380         }
          381         ntick = 0;
          382 }
          383 
          384 void maketick(int type, char *name, int side, int inflag, double val, char *lab, char *lenstr, char *descstr)
          385 {
          386         char *sidestr, *td;
          387 
          388         fprintf(tfd, "\tline %s ", descstr);
          389         inflag &= side;
          390         switch (side) {
          391         case BOT:
          392         case 0:
          393                 td = inflag ? "up" : "down";
          394                 fprintf(tfd, "%s %s from (x_%s(%g),0)", td, lenstr, name, val);
          395                 break;
          396         case TOP:
          397                 td = inflag ? "down" : "up";
          398                 fprintf(tfd, "%s %s from (x_%s(%g),frameht)", td, lenstr, name, val);
          399                 break;
          400         case LEFT:
          401                 td = inflag ? "right" : "left";
          402                 fprintf(tfd, "%s %s from (0,y_%s(%g))", td, lenstr, name, val);
          403                 break;
          404         case RIGHT:
          405                 td = inflag ? "left" : "right";
          406                 fprintf(tfd, "%s %s from (framewid,y_%s(%g))", td, lenstr, name, val);
          407                 break;
          408         }
          409         fprintf(tfd, "\n");
          410         if (type == GRID && (side & goffside))        /* wanted no ticks on grid */
          411                 return;
          412         sidestr = tick_dir == IN ? "start" : "end";
          413         if (lab != NULL) {
          414                 /* BUG: should fix size of lab here */
          415                 double wid = strlen(lab)/7.5 + (tick_dir == IN ? 0 : 0.1);        /* estimate width at 15 chars/inch */
          416                 switch (side) {
          417                 case BOT: case 0:
          418                         /* can drop "box invis" with new pic */
          419                         fprintf(tfd, "\tbox invis \"%s\" ht .25 wid 0 with .n at last line.%s",
          420                                 lab, sidestr);
          421                         break;
          422                 case TOP:
          423                         fprintf(tfd, "\tbox invis \"%s\" ht .2 wid 0 with .s at last line.%s",
          424                                 lab, sidestr);
          425                         break;
          426                 case LEFT:
          427                         fprintf(tfd, "\t\"%s \" wid %.2f rjust at last line.%s",
          428                                 lab, wid, sidestr);
          429                         break;
          430                 case RIGHT:
          431                         fprintf(tfd, "\t\" %s\" wid %.2f ljust at last line.%s",
          432                                 lab, wid, sidestr);
          433                         break;
          434                 }
          435                 /* BUG: works only if "down x" comes before "at wherever" */
          436                 lab_adjust();
          437                 fprintf(tfd, "\n");
          438         }
          439 }
          440 
          441 Attr        *grid_desc        = 0;
          442 
          443 void griddesc(Attr *a)
          444 {
          445         grid_desc = a;
          446 }
          447 
          448 void gridlist(Obj *p)
          449 {
          450         char *framestr;
          451 
          452         if ((tside & (BOT|TOP)) || tside == 0)
          453                 framestr = "frameht";
          454         else
          455                 framestr = "framewid";
          456         fprintf(tfd, "Grid_%s:\n", p->name);
          457         tick_dir = IN;
          458         print_ticks(GRID, 0, p, framestr, desc_str(grid_desc));
          459         if (grid_desc) {
          460                 freeattr(grid_desc);
          461                 grid_desc = 0;
          462         }
          463 }
          464 
          465 char *desc_str(Attr *a)        /* convert DOT to "dotted", etc. */
          466 {
          467         static char buf[50], *p;
          468 
          469         if (a == NULL)
          470                 return p = "";
          471         switch (a->type) {
          472         case DOT:        p = "dotted"; break;
          473         case DASH:        p = "dashed"; break;
          474         case INVIS:        p = "invis"; break;
          475         default:        p = "";
          476         }
          477         if (a->fval != 0.0) {
          478                 sprintf(buf, "%s %g", p, a->fval);
          479                 return buf;
          480         } else
          481                 return p;
          482 }
          483 
          484 int
          485 sidelog(int logflag, int side)        /* figure out whether to scale a side */
          486 {
          487         if ((logflag & XFLAG) && ((side & (BOT|TOP)) || side == 0))
          488                 return 1;
          489         else if ((logflag & YFLAG) && (side & (LEFT|RIGHT)))
          490                 return 1;
          491         else
          492                 return 0;
          493 }