URI: 
       tploot-ff.c - ploot - simple plotting tools
  HTML git clone git://bitreich.org/ploot git://hg6vgqziawt5s4dj.onion/ploot
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR README
       ---
       tploot-ff.c (13227B)
       ---
            1 #include <arpa/inet.h>
            2 
            3 #include <math.h>
            4 #include <stdint.h>
            5 #include <stdio.h>
            6 #include <stdlib.h>
            7 #include <string.h>
            8 #include <time.h>
            9 #include <time.h>
           10 #include <stdlib.h>
           11 #include <stdio.h>
           12 #include <fcntl.h>
           13 #include <limits.h>
           14 #include <string.h>
           15 #include <ctype.h>
           16 #include <time.h>
           17 #include <stdint.h>
           18 
           19 #include "arg.h"
           20 #include "util.h"
           21 #include "font.h"
           22 
           23 #define MARGIN 4
           24 
           25 #define XDENSITY        7        /* nb of values on x axis */
           26 #define YDENSITY        7        /* nb of values on y axis */
           27 
           28 #define TITLE_X                (IMAGE_H - TITLE_H)
           29 #define TITLE_Y                (XLABEL_W)
           30 #define TITLE_H                (FONT_H * 2)
           31 #define TITLE_W                (PLOT_W)
           32 
           33 #define XLABEL_X        (PLOT_X)
           34 #define XLABEL_Y        (0)
           35 #define XLABEL_H        (PLOT_H)
           36 #define XLABEL_W        (FONT_W * 9 + MARGIN)
           37 
           38 #define YLABEL_X        (0)
           39 #define YLABEL_Y        (PLOT_Y)
           40 #define YLABEL_H        (FONT_H * 2)
           41 #define YLABEL_W        (PLOT_W)
           42 
           43 #define PLOT_X                (YLABEL_H)
           44 #define PLOT_Y                (XLABEL_W)
           45 #define PLOT_W                700
           46 #define PLOT_H                160
           47 
           48 #define LEGEND_X        (YLABEL_H)
           49 #define LEGEND_Y        (IMAGE_W - LEGEND_W)
           50 #define LEGEND_W        (FONT_W + 150 + FONT_W)
           51 #define LEGEND_H        (PLOT_H)
           52 
           53 #define IMAGE_H                (TITLE_H + PLOT_H + YLABEL_H)
           54 #define IMAGE_W                (XLABEL_W + PLOT_W + LEGEND_W)
           55 
           56 typedef uint16_t        Color[4];
           57 typedef struct clist        Clist;
           58 typedef struct vlist        Vlist;
           59 typedef struct canvas        Canvas;
           60 typedef struct font        Font;
           61 
           62 struct vlist {
           63         Color col;        /* color to use to draw the line */
           64         time_t *t;        /* array of timestamps */
           65         double *v;        /* array of values */
           66         int n;                /* number of values */
           67         char *label;        /* for the legend */
           68 };
           69 
           70 struct canvas {
           71         int w;                /* width */
           72         int h;                /* height */
           73         int x;                /* x offset */
           74         int y;                /* x offset */
           75         Color b[IMAGE_W * IMAGE_H];
           76 };
           77 
           78 struct font {
           79         int w;                /* width */
           80         int h;                /* height */
           81         char **b;        /* buffer */
           82 };
           83 
           84 struct clist {
           85         char *name;
           86         Color col;
           87 };
           88 
           89 char *argv0;
           90 char *tflag        = "";
           91 char *uflag        = "";
           92 
           93 Clist clist[] = {
           94         /* name       red     green   blue    alpha */
           95         { "red",    { 0xffff, 0x4444, 0x4444, 0xffff } },
           96         { "orange", { 0xffff, 0x9999, 0x4444, 0xffff } },
           97         { "yellow", { 0xffff, 0xffff, 0x4444, 0xffff } },
           98         { "green",  { 0x2222, 0xffff, 0x5555, 0xffff } },
           99         { "cyan",   { 0x0000, 0xffff, 0xdddd, 0xffff } },
          100         { "blue",   { 0x2222, 0x9999, 0xffff, 0xffff } },
          101         { NULL, { 0, 0, 0, 0 } }
          102 };
          103 
          104 Font font = { FONT_W, FONT_H, glyph };
          105 
          106 static int
          107 color(Color *col, char *name)
          108 {
          109         Clist *c;
          110  
          111         for (c = clist; c->name != NULL; c++) {
          112                 if (strcmp(name, c->name) == 0) {
          113                         memcpy(col, c->col, sizeof(*col));
          114                         return 0;
          115                 }
          116         }
          117  
          118         return -1;
          119 }
          120 
          121 static void
          122 scale_minmax(Vlist *v, int n,
          123         double *vmin, double *vmax,
          124         time_t *tmin, time_t *tmax)
          125 {
          126         int i;
          127 
          128         *vmin = *vmax = 0;
          129         *tmin = *tmax = *v->t;
          130 
          131         for (; n-- > 0; v++) {
          132                 for (i = 0; i < v->n; i++) {
          133                         if (v->v[i] < *vmin)
          134                                 *vmin = v->v[i];
          135                         if (v->v[i] > *vmax)
          136                                 *vmax = v->v[i];
          137                         if (v->t[i] < *tmin)
          138                                 *tmin = v->t[i];
          139                         if (v->t[i] > *tmax)
          140                                 *tmax = v->t[i];
          141                 }
          142         }
          143 }
          144 
          145 static void
          146 scale_tstep(time_t *step, int density, time_t min, time_t max)
          147 {
          148         time_t dt, *s, scale[] = {
          149                 1, 5, 2, 10, 20, 30, 60, 60*2, 60*5, 60*10, 60*20, 60*30, 3600, 
          150                 3600*2, 3600*5, 3600*10, 3600*18, 3600*24, 3600*24*2, 
          151                 3600*24*5, 3600*24*10, 3600*24*20, 3600*24*30, 3600*24*50,
          152                 3600*24*100, 3600*24*365
          153         };
          154 
          155         dt = max - min;
          156 
          157         for (s = scale; s < scale + LEN(scale); s++) {
          158                 if (dt < *s * density) {
          159                         *step = *s;
          160                         break;
          161                 }
          162         }
          163 }
          164 
          165 static void
          166 scale_vstep(double *step, int density, double min, double max)
          167 {
          168         double dv, *s, scale[] = { 1, 2, 3, 5 };
          169         int i;
          170 
          171         dv = max - min;
          172 
          173         if (dv > 1) {
          174                 for (i = 1; i != 0; i *= 10) {
          175                         for (s = scale; s < scale + LEN(scale); s++) {
          176                                 if (dv < *s * i * density) {
          177                                         *step = *s * i;
          178                                         return;
          179                                 }
          180                         }
          181                 }
          182         } else {
          183                 for (i = 1; i != 0; i *= 10) {
          184                         for (s = scale + LEN(scale) - 1; s >= scale; s--) {
          185                                 if (dv > *s / i * density / 2) {
          186                                         *step = *s / i;
          187                                         return;
          188                                 }
          189                         }
          190                 }
          191         }
          192 }
          193 
          194 static void
          195 scale(Vlist *v, int n,
          196         double *vmin, double *vmax, double *vstep,
          197         time_t *tmin, time_t *tmax, time_t *tstep)
          198 {
          199         scale_minmax(v, n, vmin, vmax, tmin, tmax);
          200         scale_tstep(tstep, YDENSITY, *tmin, *tmax);
          201         scale_vstep(vstep, XDENSITY, *vmin, *vmax);
          202 }
          203 
          204 /*
          205  * Convert (x,y) coordinates to (row,col) for printing into the buffer.
          206  * The buffer only contain one number, so the coordinate is a single integer:
          207  *        width * x + y.
          208  * The coordinates are shifted by offx and offy to permit relative coordinates.
          209  *
          210  * The convention used:                                      y
          211  * - (0,0) is at the lower left corner of the canvas.        |
          212  * - (0,1) is above it.                                      +--x
          213  */
          214 static void
          215 ff_pixel(Canvas *can, Color *col,
          216         int x, int y)
          217 {
          218         x += can->x;
          219         y += can->y;
          220         if (x < 0 || x >= can->h || y < 0 || y >= can->w)
          221                 return;
          222         memcpy(can->b + can->w * (can->h - 1 - x) + y, col, sizeof(*can->b));
          223 }
          224 
          225 static void
          226 ff_rectangle(Canvas *can, Color *col,
          227         int x1, int y1,
          228         int x2, int y2)
          229 {
          230         int x, y, xmin, ymin, xmax, ymax;
          231 
          232         xmin = MIN(x1, x2); xmax = MAX(x1, x2);
          233         ymin = MIN(y1, y2); ymax = MAX(y1, y2);
          234 
          235         for (x = xmin; x <= xmax; x++)
          236                 for (y = ymin; y <= ymax; y++)
          237                         ff_pixel(can, col, x, y);
          238 }
          239 
          240 /*
          241  * From Bresenham's line algorithm and dcat's tplot.
          242  */
          243 static void
          244 ff_line(Canvas *can, Color *col,
          245         int x0, int y0,
          246         int x1, int y1)
          247 {
          248         int dx, dy, sx, sy, err, e;
          249 
          250         sx = x0 < x1 ? 1 : -1;
          251         sy = y0 < y1 ? 1 : -1;
          252         dx = abs(x1 - x0);
          253         dy = abs(y1 - y0);
          254         err = (dx > dy ? dx : -dy) / 2;
          255 
          256         for (;;) {
          257                 ff_pixel(can, col, x0, y0);
          258 
          259                 if (x0 == x1 && y0 == y1)
          260                         break;
          261 
          262                 e = err;
          263                 if (e > -dx) {
          264                         x0 += sx;
          265                         err -= dy;
          266                 }
          267                 if (e < dy) {
          268                         y0 += sy;
          269                         err += dx;
          270                 }
          271         }
          272 }
          273 
          274 /*
          275  * Draw a coloured glyph from font f centered on x.
          276  */
          277 static void
          278 ff_char(Canvas *can, Color *col, char c, Font *f,
          279         int x, int y)
          280 {
          281         int xf, yf;
          282 
          283         if (c & 0x80)
          284                 c = '\0';
          285 
          286 
          287         x -= f->h / 2;
          288 
          289         for (xf = 0; xf < f->h; xf++)
          290                 for (yf = 0; yf < f->w; yf++)
          291                         if (f->b[(int)c][f->w * (f->h - xf) + yf] == 1)
          292                                 ff_pixel(can, col, x + xf, y + yf);
          293 }
          294 
          295 /*
          296  * Draw a left aligned string without wrapping it.
          297  */
          298 static void
          299 ff_str_left(Canvas *can, Color *col, char *s, Font *f,
          300         int x, int y)
          301 {
          302         for (; *s != '\0'; y += f->w, s++)
          303                 ff_char(can, col, *s, f, x, y);
          304 }
          305 
          306 /*
          307  * Draw a center aligned string without wrapping it.
          308  */
          309 static void
          310 ff_str_center(Canvas *can, Color *col, char *s, Font *f,
          311         int x, int y)
          312 {
          313         y -= f->w * strlen(s) / 2;
          314         ff_str_left(can, col, s, f, x, y);
          315 }
          316 
          317 /*
          318  * Draw a right aligned string without wrapping it.
          319  */
          320 static void
          321 ff_str_right(Canvas *can, Color *col, char *s, Font *f,
          322         int x, int y)
          323 {
          324         y -= f->w * strlen(s);
          325         ff_str_left(can, col, s, f, x, y);
          326 }
          327 
          328 static void
          329 ff_print(Canvas *can)
          330 {
          331         uint32_t w, h;
          332 
          333         w = htonl(can->w);
          334         h = htonl(can->h);
          335 
          336         fputs("farbfeld", stdout);
          337         fwrite(&w, sizeof(w), 1, stdout);
          338         fwrite(&h, sizeof(h), 1, stdout);
          339         fwrite(can->b, can->w * can->h, sizeof(*can->b), stdout);
          340 }
          341 
          342 static int
          343 ff_t2y(time_t t, time_t tmin, time_t tmax)
          344 {
          345         return (t - tmin) * PLOT_W / (tmax - tmin);
          346 }
          347 
          348 static int
          349 ff_v2x(double v, double vmin, double vmax)
          350 {
          351         return (v - vmin) * PLOT_H / (vmax - vmin);
          352 }
          353 
          354 static void
          355 ff_xaxis(Canvas *can, Color *label, Color *grid,
          356         double vmin, double vmax, double vstep)
          357 {
          358         double v;
          359         int x;
          360         char str[8 + 1];
          361 
          362         for (v = vmax - fmod(vmax, vstep); v >= vmin; v -= vstep) {
          363                 x = ff_v2x(v, vmin, vmax);
          364 
          365                 ff_line(can, grid,
          366                         x, XLABEL_W,
          367                         x, XLABEL_W + PLOT_W);
          368 
          369                 humanize(str, v);
          370                 ff_str_right(can, label, str, &font,
          371                         x, XLABEL_W - MARGIN);
          372         }
          373 }
          374 
          375 static void
          376 ff_yaxis(Canvas *can, Color *label, Color *grid,
          377         time_t tmin, time_t tmax, time_t tstep)
          378 {
          379         time_t t;
          380         int y;
          381         char str[sizeof("MM/DD HH/MM")], *fmt;
          382 
          383         if (tstep < 3600 * 12)
          384                 fmt = "%H:%M:%S";
          385         else if (tstep < 3600 * 24)
          386                 fmt = "%m/%d %H:%M";
          387         else
          388                 fmt = "%Y/%m/%d";
          389 
          390         for (t = tmax - tmax % tstep; t >= tmin; t -= tstep) {
          391                 y = ff_t2y(t, tmin, tmax);
          392 
          393                 ff_line(can, grid,
          394                         YLABEL_H, y,
          395                         YLABEL_H + PLOT_H, y);
          396 
          397                 strftime(str, sizeof(str), fmt, localtime(&t));
          398                 ff_str_center(can, label, str, &font,
          399                         YLABEL_H / 2, y);
          400         }
          401 }
          402 
          403 static void
          404 ff_title(Canvas *can,
          405         Color *ct, char *title,
          406         Color *cu, char *unit)
          407 {
          408         ff_str_left(can, ct, title, &font,
          409                 TITLE_H / 2, 0);
          410         ff_str_right(can, cu, unit, &font,
          411                 TITLE_H / 2, TITLE_W);
          412 }
          413 
          414 static void
          415 ff_plot(Canvas *can, Vlist *v,
          416         double vmin, double vmax,
          417         time_t tmin, time_t tmax)
          418 {
          419         time_t *tp;
          420         double *vp;
          421         int x, y, n, xlast, ylast, first;
          422 
          423         first = 1;
          424         for (tp = v->t, vp = v->v, n = v->n; n > 0; n--, vp++, tp++) {
          425                 x = ff_v2x(*vp, vmin, vmax);
          426                 y = ff_t2y(*tp, tmin, tmax);
          427 
          428                 if (!first)
          429                         ff_line(can, &v->col, xlast, ylast, x, y);
          430 
          431                 xlast = x;
          432                 ylast = y;
          433                 first = 0;
          434         }
          435 }
          436 
          437 static void
          438 ff_values(Canvas *can, Vlist *v, int n,
          439         double vmin, double vmax,
          440         time_t tmin, time_t tmax)
          441 {
          442         for (; n > 0; n--, v++)
          443                 ff_plot(can, v, vmin, vmax, tmin, tmax);
          444 }
          445 
          446 static void
          447 ff_legend(Canvas *can, Color *label_fg, Vlist *v, int n)
          448 {
          449         int i, x, y;
          450 
          451         for (i = 0; i < n; i++, v++) {
          452                 x = LEGEND_H - i * (FONT_H + MARGIN) - FONT_H / 2;
          453 
          454                 y = MARGIN + FONT_W;
          455                 ff_str_left(can, &v->col, "\1", &font, x, y);
          456 
          457                 y += FONT_W * 2;
          458                 ff_str_left(can, label_fg, v->label, &font, x, y);
          459         }
          460 }
          461 
          462 /*
          463  * Plot the 'n' values list of the 'v' array with title 'name' and
          464  * 'units' label.
          465  *
          466  *               Title       (units)
          467  *             y ^                    Legend
          468  *         label |- + - + - + - + -    ....
          469  *          here |- + - + - + - + -    ....
          470  *               +--+---+---+---+-->
          471  *                x label here        
          472  */
          473 static void
          474 ff(Vlist *v, int n, char *name, char *units)
          475 {
          476         Canvas can        = { IMAGE_W, IMAGE_H, 0, 0, { { 0 }, { 0 } } };
          477         Color plot_bg        = { 0x2222, 0x2222, 0x2222, 0xffff };
          478         Color grid_bg        = { 0x2929, 0x2929, 0x2929, 0xffff };
          479         Color grid_fg        = { 0x3737, 0x3737, 0x3737, 0xffff };
          480         Color label_fg        = { 0x8888, 0x8888, 0x8888, 0xffff };
          481         Color title_fg        = { 0xdddd, 0xdddd, 0xdddd, 0xffff };
          482         double vmin, vmax, vstep;
          483         time_t tmin, tmax, tstep;
          484 
          485         scale(v, n, &vmin, &vmax, &vstep, &tmin, &tmax, &tstep);
          486 
          487         can.x = 0;
          488         can.y = 0;
          489         ff_rectangle(&can, &plot_bg, 0, 0, IMAGE_H - 1, IMAGE_W - 1);
          490 
          491         can.x = PLOT_X;
          492         can.y = PLOT_Y;
          493         ff_rectangle(&can, &grid_bg, 0, 0, PLOT_H, PLOT_W);
          494 
          495         can.x = YLABEL_X;
          496         can.y = YLABEL_Y;
          497         ff_yaxis(&can, &label_fg, &grid_fg, tmin, tmax, tstep);
          498 
          499         can.x = XLABEL_X;
          500         can.y = XLABEL_Y;
          501         ff_xaxis(&can, &label_fg, &grid_fg, vmin, vmax, vstep);
          502 
          503         can.x = TITLE_X;
          504         can.y = TITLE_Y;
          505         ff_title(&can, &title_fg, name, &label_fg, units);
          506 
          507         can.x = PLOT_X;
          508         can.y = PLOT_Y;
          509         ff_values(&can, v, n, vmin, vmax, tmin, tmax);
          510 
          511         can.x = LEGEND_X;
          512         can.y = LEGEND_Y;
          513         ff_legend(&can, &label_fg, v, n);
          514 
          515         ff_print(&can);
          516 }
          517  
          518 static void
          519 csv_labels(Vlist *v, char **argv, char *buf)
          520 {
          521         if (esfgets(buf, LINE_MAX, stdin) == NULL)
          522                 fputs("missing label line\n", stderr), exit(1);
          523  
          524         if (strcmp(strsep(&buf, ","), "epoch") != 0)
          525                 fputs("first label must be \"epoch\"\n", stderr), exit(1);
          526  
          527         for (; *argv != NULL; v++, argv++) {
          528                 if ((v->label = strsep(&buf, ",")) == NULL)
          529                         fputs("more arguments than columns\n", stderr), exit(1);
          530                 else if (color(&v->col, *argv) == -1)
          531                         fprintf(stderr, "unknown color: %s\n", *argv), exit(1);
          532         }
          533  
          534         if (strsep(&buf, ",") != NULL)
          535                 fputs("more columns than arguments\n", stderr), exit(1);
          536 }
          537 
          538 static int
          539 csv_addval(Vlist *v, int bufsize, int nval, double field, time_t epoch)
          540 {
          541         if (nval >= bufsize) {
          542                 bufsize = bufsize * 2 + 1;
          543                 if ((v->v = realloc(v->v, bufsize * sizeof(*v->v))) == NULL)
          544                         perror("reallocating values buffer"), exit(1);
          545                 if ((v->t = realloc(v->t, bufsize * sizeof(*v->t))) == NULL)
          546                         perror("reallocating values buffer"), exit(1);
          547         }
          548         v->v[nval] = field;
          549         v->t[nval] = epoch;
          550         v->n = nval + 1;
          551 
          552         return bufsize;
          553 }
          554 
          555 /*
          556  * Add to each column the value on the current row.
          557  */
          558 static int
          559 csv_addrow(Vlist *v, int bufsize, int ncol, int nval, char *line)
          560 {
          561         time_t epoch;
          562         int bs;
          563         char *field, *dot;
          564 
          565         if ((field = strsep(&line, ",")) == NULL)
          566                 fprintf(stderr, "%d: missing epoch\n", nval), exit(1);
          567 
          568         if ((dot = strchr(field, '.')) != NULL)
          569                 *dot = '\0';
          570         epoch = eatol(field);
          571         for (; (field = strsep(&line, ",")) != NULL; ncol--, v++) {
          572                 if (ncol <= 0)
          573                         fprintf(stderr, "%d: too many fields\n", nval), exit(1);
          574                 bs = csv_addval(v, bufsize, nval, eatof(field), epoch);
          575         }
          576         if (ncol > 0)
          577                 fprintf(stderr, "%d: too few fields\n", nval), exit(1);
          578 
          579         return bs;
          580 }
          581 
          582 /*
          583  *       < ncol >
          584  * epoch,a1,b1,c1  ^
          585  * epoch,a2,b2,c2 nval
          586  * epoch,a3,b3,c3  v
          587  */
          588 static void
          589 csv_values(Vlist *v, int ncol)
          590 {
          591         int nval, bufsize;
          592         char line[LINE_MAX];
          593 
          594         bufsize = 0;
          595         for (nval = 0; esfgets(line, sizeof(line), stdin) != NULL; nval++)
          596                 bufsize = csv_addrow(v, bufsize, ncol, nval, line);
          597         if (nval == 0)
          598                 fputs("no value could be read\n", stderr), exit(1);
          599 }
          600 
          601 static void
          602 usage(void)
          603 {
          604         Clist *c;
          605 
          606         fprintf(stderr, "usage: %s [-t title] [-u unit] {", argv0);
          607         fputs(clist->name, stderr);
          608         for (c = clist + 1; c->name != NULL; c++)
          609                 fprintf(stderr, ",%s", c->name);
          610         fputs("}...\n", stderr);
          611         exit(1);
          612 }
          613 
          614 int
          615 main(int argc, char **argv)
          616 {
          617         Vlist *v;
          618         char labels[LINE_MAX];
          619 
          620         ARGBEGIN {
          621         case 't':
          622                 tflag = EARGF(usage());
          623                 break;
          624         case 'u':
          625                 uflag = EARGF(usage());
          626                 break;
          627         default:
          628                 usage();
          629         } ARGEND;
          630 
          631         if ((v = calloc(argc, sizeof(*v))) == NULL)
          632                 perror("calloc value list"), exit(1);
          633 
          634         csv_labels(v, argv, labels);
          635         csv_values(v, argc);
          636 
          637         ff(v, argc, tflag, uflag);
          638 
          639         return 0;
          640 }