URI: 
       ploot-farbfeld.c - ploot - simple plotting tools
  HTML git clone git://bitreich.org/ploot git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/ploot
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR README
   DIR LICENSE
       ---
       ploot-farbfeld.c (10488B)
       ---
            1 #include <arpa/inet.h>
            2 #include <ctype.h>
            3 #include <errno.h>
            4 #include <fcntl.h>
            5 #include <limits.h>
            6 #include <math.h>
            7 #include <stddef.h>
            8 #include <stdint.h>
            9 #include <stdio.h>
           10 #include <stdlib.h>
           11 #include <string.h>
           12 #include <time.h>
           13 #include <unistd.h>
           14 #include "tsv.h"
           15 #include "font.h"
           16 #include "util.h"
           17 
           18 #ifndef __OpenBSD__
           19 #define pledge(...) 0
           20 #endif
           21 
           22 #define MARGIN                8
           23 
           24 #define IMAGE_H                (TITLE_H + PLOT_H + XLABEL_H)
           25 #define IMAGE_W                (MARGIN + YLABEL_W + PLOT_W + MARGIN)
           26 
           27 #define TITLE_X                (MARGIN)
           28 #define TITLE_Y                (IMAGE_H - TITLE_H / 2)
           29 #define TITLE_H                ((font)->height * 2)
           30 #define TITLE_W                (PLOT_W)
           31 
           32 #define YLABEL_X        (MARGIN)
           33 #define YLABEL_Y        (PLOT_Y)
           34 #define YLABEL_H        (PLOT_H)
           35 #define YLABEL_W        (40 + MARGIN)
           36 
           37 #define XLABEL_X        (PLOT_X)
           38 #define XLABEL_Y        (0)
           39 #define XLABEL_H        ((font)->height * 2)
           40 #define XLABEL_W        (PLOT_W)
           41 
           42 #define PLOT_X                (YLABEL_X + YLABEL_W)
           43 #define PLOT_Y                (XLABEL_H)
           44 #define PLOT_W                (700)
           45 #define PLOT_H                (160)
           46 
           47 #define LEGEND_X        (IMAGE_W / 2)
           48 #define LEGEND_Y        (TITLE_Y)
           49 #define LEGEND_H        (PLOT_H)
           50 
           51 struct ffcolor {
           52         uint16_t red;
           53         uint16_t green;
           54         uint16_t blue;
           55         uint16_t alpha;
           56 };
           57 
           58 struct ffplot {
           59         int w, h, x, y; /* width, height and coordinamtes */
           60         struct ffcolor *buf;
           61 };
           62 
           63 static struct colorname {
           64         char *name;
           65         struct ffcolor color;
           66 } colorname[] = {
           67         /* name       red     green   blue    alpha */
           68         { "red",    { 0xffff, 0x4444, 0x4444, 0xffff } },
           69         { "orange", { 0xffff, 0x9999, 0x4444, 0xffff } },
           70         { "yellow", { 0xffff, 0xffff, 0x4444, 0xffff } },
           71         { "green",  { 0x2222, 0xffff, 0x5555, 0xffff } },
           72         { "cyan",   { 0x0000, 0xffff, 0xdddd, 0xffff } },
           73         { "blue",   { 0x2222, 0x9999, 0xffff, 0xffff } },
           74         { NULL, { 0, 0, 0, 0 } }
           75 };
           76 
           77 static char *flag_title = "";
           78 static struct font *font = &font13;
           79 
           80 /*
           81  * Convert (x,y) coordinates to (row,col) for printing into the buffer.
           82  * The buffer only contain one number, so the coordinate is a single integer:
           83  *        width * y + y.
           84  * The coordinates are shifted by offx and offy to permit relative coordinates.
           85  *
           86  * The convention used:                                      y
           87  * - (0,0) is at the lower left corner of the plotvas.        |
           88  * - (0,1) is above it.                                      +--x
           89  */
           90 static void
           91 ffplot_pixel(struct ffplot *plot, struct ffcolor *color,
           92         int x, int y)
           93 {
           94         x += plot->x;
           95         y += plot->y;
           96         if (x < 0 || x >= plot->w || y < 0 || y >= plot->h)
           97                 return;
           98         memcpy(plot->buf + plot->w * (plot->h - 1 - y) + x, color, sizeof(*plot->buf));
           99 }
          100 
          101 static void
          102 ffplot_rectangle(struct ffplot *plot, struct ffcolor *color,
          103         int y1, int x1,
          104         int y2, int x2)
          105 {
          106         int x, y, ymin, xmin, ymax, xmax;
          107 
          108         ymin = MIN(y1, y2); ymax = MAX(y1, y2);
          109         xmin = MIN(x1, x2); xmax = MAX(x1, x2);
          110 
          111         for (y = ymin; y <= ymax; y++)
          112                 for (x = xmin; x <= xmax; x++)
          113                         ffplot_pixel(plot, color, x, y);
          114 }
          115 
          116 /*
          117  * From Bresenham's line algorithm and dcat's tplot.
          118  */
          119 static void
          120 ffplot_line(struct ffplot *plot, struct ffcolor *color,
          121         int x0, int y0,
          122         int x1, int y1)
          123 {
          124         int dy, dx, sy, sx, err, e;
          125 
          126         sx = x0 < x1 ? 1 : -1;
          127         sy = y0 < y1 ? 1 : -1;
          128         dx = ABS(x1 - x0);
          129         dy = ABS(y1 - y0);
          130         err = (dy > dx ? dy : -dx) / 2;
          131 
          132         for (;;) {
          133                 ffplot_pixel(plot, color, x0, y0);
          134 
          135                 if (y0 == y1 && x0 == x1)
          136                         break;
          137 
          138                 e = err;
          139                 if (e > -dy) {
          140                         y0 += sy;
          141                         err -= dx;
          142                 }
          143                 if (e < dx) {
          144                         x0 += sx;
          145                         err += dy;
          146                 }
          147         }
          148 }
          149 
          150 /*
          151  * Draw a coloured glyph from font f centered on y.
          152  */
          153 static int
          154 ffplot_char(struct ffplot *plot, struct ffcolor *color, struct font *ft, char c,
          155         int x, int y)
          156 {
          157         int yf, xf, wf;
          158 
          159         if (c & 0x80)
          160                 c = '\0';
          161         y -= ft->height / 2;
          162         wf = font_width(ft, c);
          163         for (xf = 0; xf < wf; xf++)
          164                 for (yf = 0; yf < ft->height; yf++)
          165                         if (ft->glyph[(int)c][wf * (ft->height - yf) + xf] == 3)
          166                                 ffplot_pixel(plot, color, x + xf, y + yf);
          167         return wf + 1;
          168 }
          169 
          170 /*
          171  * Draw a left aligned string without wrapping it.
          172  */
          173 static size_t
          174 ffplot_text_left(struct ffplot *plot, struct ffcolor *color, struct font *ft,
          175         char *s, int x, int y)
          176 {
          177         for (; *s != '\0'; s++)
          178                 x += ffplot_char(plot, color, ft, *s, x, y);
          179         return x;
          180 }
          181 
          182 /*
          183  * Draw a center aligned string without wrapping it.
          184  */
          185 static size_t
          186 ffplot_text_center(struct ffplot *plot, struct ffcolor *color, struct font *ft,
          187         char *s, int x, int y)
          188 {
          189         x -= font_strlen(ft, s) / 2;
          190         return ffplot_text_left(plot, color, ft, s, x, y);
          191 }
          192 
          193 /*
          194  * Draw a right aligned string without wrapping it.
          195  */
          196 static size_t
          197 ffplot_text_right(struct ffplot *plot, struct ffcolor *color, struct font *ft,
          198         char *s, int x, int y)
          199 {
          200         x -= font_strlen(ft, s);
          201         return ffplot_text_left(plot, color, ft, s, x, y);
          202 }
          203 
          204 static void
          205 ffplot_print(FILE *fp, struct ffplot *plot)
          206 {
          207         uint32_t w, h;
          208 
          209         w = htonl(plot->w);
          210         h = htonl(plot->h);
          211 
          212         fprintf(stdout, "farbfeld");
          213         fwrite(&w, sizeof(w), 1, fp);
          214         fwrite(&h, sizeof(h), 1, fp);
          215         fwrite(plot->buf, plot->w * plot->h, sizeof(*plot->buf), fp);
          216 }
          217 
          218 static int
          219 ffplot_t2x(time_t t, time_t tmin, time_t tmax)
          220 {
          221         if (tmin == tmax)
          222                 return PLOT_W;
          223         return (t - tmin) * PLOT_W / (tmax - tmin);
          224 }
          225 
          226 static int
          227 ffplot_v2y(double v, double vmin, double vmax)
          228 {
          229         if (vmin == vmax)
          230                 return PLOT_H;
          231         return (v - vmin) * PLOT_H / (vmax - vmin);
          232 }
          233 
          234 static void
          235 ffplot_xaxis(struct ffplot *plot, struct ffcolor *label, struct ffcolor *grid,
          236         time_t tmin, time_t tmax, time_t tstep)
          237 {
          238         time_t t;
          239         int x;
          240         char str[sizeof("MM/DD HH/MM")], *fmt;
          241 
          242         if (tstep < 3600 * 12)
          243                 fmt = "%H:%M:%S";
          244         else if (tstep < 3600 * 24)
          245                 fmt = "%m/%d %H:%M";
          246         else
          247                 fmt = "%X/%m/%d";
          248 
          249         for (t = tmax - tmax % tstep; t >= tmin; t -= tstep) {
          250                 x = ffplot_t2x(t, tmin, tmax);
          251 
          252                 ffplot_line(plot, grid,
          253                         x, XLABEL_H,
          254                         x, XLABEL_H + PLOT_H);
          255 
          256                 strftime(str, sizeof(str), fmt, localtime(&t));
          257                 ffplot_text_center(plot, label, font, str,
          258                         x, XLABEL_H / 2);
          259         }
          260 }
          261 
          262 static void
          263 ffplot_yaxis(struct ffplot *plot, struct ffcolor *label, struct ffcolor *grid,
          264         double vmin, double vmax, double vstep)
          265 {
          266         double v;
          267         int y;
          268         char str[8 + 1];
          269 
          270         for (v = vmax - fmod(vmax, vstep); v >= vmin; v -= vstep) {
          271                 y = ffplot_v2y(v, vmin, vmax);
          272 
          273                 ffplot_line(plot, grid,
          274                         YLABEL_W, y,
          275                         YLABEL_W + PLOT_W, y);
          276 
          277                 humanize(str, v);
          278                 ffplot_text_right(plot, label, font, str,
          279                         YLABEL_W - MARGIN, y);
          280         }
          281 }
          282 
          283 static void
          284 ffplot_title(struct ffplot *plot, struct ffcolor *ct, char *title)
          285 {
          286         ffplot_text_left(plot, ct, font, title, TITLE_H / 2, 0);
          287 }
          288 
          289 static void
          290 ffplot_plot(struct ffplot *plot, struct tsv *vl, struct ffcolor *color,
          291         double vmin, double vmax,
          292         time_t tmin, time_t tmax)
          293 {
          294         time_t *tp;
          295         double *vp;
          296         int x, y, n, ylast, xlast, first;
          297 
          298         first = 1;
          299         for (tp = vl->t, vp = vl->v, n = vl->n; n > 0; n--, vp++, tp++) {
          300                 y = ffplot_v2y(*vp, vmin, vmax);
          301                 x = ffplot_t2x(*tp, tmin, tmax);
          302 
          303                 if (!first)
          304                         ffplot_line(plot, color, xlast, ylast, x, y);
          305 
          306                 ylast = y;
          307                 xlast = x;
          308                 first = 0;
          309         }
          310 }
          311 
          312 static void
          313 ffplot_values(struct ffplot *plot, struct tsv *vl, struct ffcolor **cl, size_t ncol,
          314         time_t tmin, time_t tmax,
          315         double vmin, double vmax)
          316 {
          317         for (; ncol > 0; ncol--, vl++, cl++)
          318                 ffplot_plot(plot, vl, *cl, vmin, vmax, tmin, tmax);
          319 }
          320 
          321 static void
          322 ffplot_legend(struct ffplot *plot, struct ffcolor *fg, struct tsv *vl, struct ffcolor **cl, size_t ncol)
          323 {
          324         size_t x, y;
          325 
          326         x = y = 0;
          327         for (; ncol > 0; ncol--, vl++, cl++) {
          328                 x = ffplot_text_left(plot, *cl, font, "-", x, y) + MARGIN;
          329                 x = ffplot_text_left(plot, fg, font, vl->label, x, y);
          330                 x = ffplot_text_left(plot, fg, font, "   ", x, y);
          331         }
          332 }
          333 
          334 /*
          335  * Plot the 'n' values list of the 'v' arrax with title 'name' label.
          336  *
          337  *               Title      Legend
          338  *             x ^                   
          339  *         label | - + - + - + - + -
          340  *          here | - + - + - + - + -
          341  *               +---+---+---+---+-->
          342  *                x label here        
          343  */
          344 static void
          345 plot(struct tsv *vl, struct ffcolor **cl, size_t ncol, char *name)
          346 {
          347         struct ffplot plot = { IMAGE_W, IMAGE_H, 0, 0, NULL };
          348         struct ffcolor plot_bg = { 0x2222, 0x2222, 0x2222, 0xffff };
          349         struct ffcolor grid_bg = { 0x2929, 0x2929, 0x2929, 0xffff };
          350         struct ffcolor grid_fg = { 0x3737, 0x3737, 0x3737, 0xffff };
          351         struct ffcolor label_fg = { 0x8888, 0x8888, 0x8888, 0xffff };
          352         struct ffcolor title_fg = { 0xdddd, 0xdddd, 0xdddd, 0xffff };
          353         double vmin, vmax, vstep;
          354         time_t tmin, tmax, tstep;
          355 
          356         tsv_min_max(vl, ncol, &tmin, &tmax, &vmin, &vmax);
          357         tstep = scale_time_t(tmin, tmax, 7);
          358         vstep = scale_double(vmin, vmax, 7);
          359 
          360         if ((plot.buf = calloc(IMAGE_H * IMAGE_W, sizeof *plot.buf)) == NULL)
          361                 err(1, "calloc: %s", strerror(errno));
          362 
          363         plot.y = 0;
          364         plot.x = 0;
          365         ffplot_rectangle(&plot, &plot_bg, 0, 0, IMAGE_H - 1, IMAGE_W - 1);
          366 
          367         plot.x = PLOT_X;
          368         plot.y = PLOT_Y;
          369         ffplot_rectangle(&plot, &grid_bg, 0, 0, PLOT_H, PLOT_W);
          370 
          371         plot.x = XLABEL_X;
          372         plot.y = XLABEL_Y;
          373         ffplot_xaxis(&plot, &label_fg, &grid_fg, tmin, tmax, tstep);
          374 
          375         plot.x = YLABEL_X;
          376         plot.y = YLABEL_Y;
          377         ffplot_yaxis(&plot, &label_fg, &grid_fg, vmin, vmax, vstep);
          378 
          379         plot.x = TITLE_X;
          380         plot.y = TITLE_Y;
          381         ffplot_title(&plot, &title_fg, name);
          382 
          383         plot.x = PLOT_X;
          384         plot.y = PLOT_Y;
          385         ffplot_values(&plot, vl, cl, ncol, tmin, tmax, vmin, vmax);
          386 
          387         plot.x = LEGEND_X;
          388         plot.y = LEGEND_Y;
          389         ffplot_legend(&plot, &label_fg, vl, cl, ncol);
          390 
          391         ffplot_print(stdout, &plot);
          392 }
          393 
          394 static struct ffcolor *
          395 name_to_color(char *name)
          396 {
          397         struct colorname *cn;
          398 
          399         for (cn = colorname; cn->name != NULL; cn++)
          400                 if (strcmp(name, cn->name) == 0)
          401                         return &cn->color;
          402         return NULL;
          403 }
          404 
          405 static void
          406 argv_to_color(struct ffcolor **cl, char **argv)
          407 {
          408         for (; *argv != NULL; cl++, argv++)
          409                 if ((*cl = name_to_color(*argv)) == NULL)
          410                         err(1, "unknown color name: %s", *argv);
          411 }
          412 
          413 static void
          414 usage(void)
          415 {
          416         fprintf(stderr, "usage: %s [-t title] {", arg0);
          417         fputs(colorname->name, stderr);
          418         for (struct colorname *cn = colorname + 1; cn->name != NULL; cn++)
          419                 fprintf(stderr, ",%s", cn->name);
          420         fputs("}...\n", stderr);
          421         exit(1);
          422 }
          423 
          424 int
          425 main(int argc, char **argv)
          426 {
          427         struct tsv *vl;
          428         struct ffcolor **cl;
          429         size_t ncol;
          430         int c;
          431 
          432         if (pledge("stdio", "") < 0)
          433                 err(1, "pledge: %s", strerror(errno));
          434 
          435         arg0 = *argv;
          436         while ((c = getopt(argc, argv, "t:")) > -1) {
          437                 switch (c) {
          438                 case 't':
          439                         flag_title = optarg;
          440                         break;
          441                 default:
          442                         usage();
          443                 }
          444         }
          445         argc -= optind;
          446         argv += optind;
          447 
          448         if (argc == 0)
          449                 usage();
          450 
          451         if ((cl = calloc(argc, sizeof *cl)) == NULL)
          452                 err(1, "calloc: %s", strerror(errno));
          453 
          454         tsv_labels(stdin, &vl, &ncol);
          455         if (ncol > (size_t)argc)
          456                 err(1, "too many columns or not enough arguments");
          457         else if (ncol < (size_t)argc)
          458                 err(1, "too many arguments or not enough columns");
          459         tsv_values(stdin, vl, ncol);
          460         argv_to_color(cl, argv);
          461 
          462         plot(vl, cl, argc, flag_title);
          463 
          464         free(vl);
          465         free(cl);
          466         return 0;
          467 }