prepare ffplot to read from stdin - 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 --- DIR commit 522bbe841ea86e232baf35ba638fe490cc763325 DIR parent df6b2deeefb42f4102a9bba819e7d06d15a3aebf HTML Author: Josuah Demangeon <mail@josuah.net> Date: Tue, 1 May 2018 20:00:10 +0200 prepare ffplot to read from stdin Diffstat: M Makefile | 4 ++-- M ffdraw.c | 19 ++++++++++++++++++- D ffdraw.h | 39 ------------------------------- M ffplot.c | 241 +++++++++++++++++++++++++++++++ M font.h | 3 ++- M main.c | 293 ++++--------------------------- A ploot.h | 44 +++++++++++++++++++++++++++++++ 7 files changed, 342 insertions(+), 301 deletions(-) --- DIR diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ CFLAGS = -Wall -Wextra -Werror -std=c89 -pedantic -D_POSIX_C_SOURCE=200809L LDFLAGS = -static -SRC = main.c ffdraw.c font_14x7.c +SRC = main.c ffplot.c ffdraw.c font_14x7.c OBJ = $(SRC:.c=.o) LIB = -lm @@ -19,4 +19,4 @@ clean:x x: -$(SRC): arg.h ffdraw.h font.h font_14x7.h +$(SRC): arg.h ploot.h font.h font_14x7.h DIR diff --git a/ffdraw.c b/ffdraw.c @@ -6,11 +6,14 @@ * - (0,1) is above it. +--x */ +#include <arpa/inet.h> + #include <stdint.h> #include <string.h> #include <stdlib.h> +#include <stdio.h> -#include "ffdraw.h" +#include "ploot.h" /* * Convert (x,y) coordinates to (row,col) for printing into the buffer. @@ -144,3 +147,17 @@ ffdraw_fill(Canvas *can, Color *col) can->x = x; can->y = y; } + +void +ffdraw_print(Canvas *can) +{ + uint32_t w, h; + + w = htonl(can->w); + h = htonl(can->h); + + fputs("farbfeld", stdout); + fwrite(&w, sizeof(w), 1, stdout); + fwrite(&h, sizeof(h), 1, stdout); + fwrite(can->b, can->w * can->h, sizeof(*can->b), stdout); +} DIR diff --git a/ffdraw.h b/ffdraw.h @@ -1,39 +0,0 @@ -#include <time.h> - -#define MIN(x, y) ((x) < (y) ? (x) : (y)) -#define MAX(x, y) ((x) > (y) ? (x) : (y)) - -typedef uint16_t Color[4]; - -typedef struct { - int w; /* width */ - int h; /* height */ - Color *b; /* buffer */ - int x; /* x offset */ - int y; /* x offset */ -} Canvas; - -typedef struct { - int w; /* width */ - int h; /* height */ - char *b[128]; /* buffer */ -} Font; - -typedef struct { - Color col; /* for drawing the curve and the legend */ -/* time_t *t; / * array of timestamps */ - double *v; /* array of values */ - int n; /* number of values */ - time_t step; - char *name; /* for the legend */ -} Vlist; - -/* ffdraw.c */ -void ffdraw_pixel (Canvas *, Color *, int, int); -void ffdraw_rectangle(Canvas *, Color *, int, int, int, int); -void ffdraw_line (Canvas *, Color *, int, int, int, int); -void ffdraw_char (Canvas *, Color *, char, Font *, int, int); -void ffdraw_str_left(Canvas *, Color *, char *, Font *, int, int); -void ffdraw_str_center(Canvas *, Color *, char *, Font *, int, int); -void ffdraw_str_right(Canvas *, Color *, char *, Font *, int, int); -void ffdraw_fill (Canvas *, Color *); DIR diff --git a/ffplot.c b/ffplot.c @@ -0,0 +1,241 @@ +/* + * Draw a plot + * + * Title (units) + * y ^ Legend + * label |- + - + - + - + - + * here |- + - + - + - + - + * +--+---+---+---+--> + * x label here + */ + +#include <math.h> +#include <string.h> +#include <stdio.h> +#include <time.h> + +#include "ploot.h" /* placed before for Font type declaration */ +#include "font_14x7.h" + +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +#define MARGIN 4 + +#define FONT_H 14 +#define FONT_W 7 + +#define TITLE_X (IMAGE_H - TITLE_H) +#define TITLE_Y (XLABEL_W) +#define TITLE_H (FONT_H * 2) +#define TITLE_W (PLOT_W) + +#define XLABEL_X (PLOT_X) +#define XLABEL_Y (0) +#define XLABEL_H (PLOT_H) +#define XLABEL_W (FONT_W * 9 + MARGIN) + +#define YLABEL_X (0) +#define YLABEL_Y (PLOT_Y) +#define YLABEL_H (FONT_H * 2) +#define YLABEL_W (PLOT_W) + +#define PLOT_X (YLABEL_H) +#define PLOT_Y (XLABEL_W) +#define PLOT_W 700 +#define PLOT_H 200 + +#define LEGEND_X (YLABEL_H) +#define LEGEND_Y (IMAGE_W - LEGEND_W) +#define LEGEND_W (FONT_W + 150 + FONT_W) +#define LEGEND_H (PLOT_H) + +#define IMAGE_H (TITLE_H + PLOT_H + YLABEL_H) +#define IMAGE_W (XLABEL_W + PLOT_W + LEGEND_W) + +Color buffer[IMAGE_W * IMAGE_H]; + +Color c_axis = { 0xffff, 0xffff, 0xffff, 0xfff }; +Font *font = &font_14x7; + +static int +t2y(time_t t, time_t tmin, time_t tmax) +{ + return (t - tmin) * PLOT_W / (tmax - tmin); +} + +static int +v2x(double v, double vmin, double vmax) +{ + return (v - vmin) * PLOT_H / (vmax - vmin); +} + +/* + * Set 'str' to a human-readable form of 'num' with always a width of 8 (+ 1 + * the '\0' terminator). Buffer overflow is ensured not to happen due to the + * max size of a double. Return the exponent. + */ +static int +humanize(char *str, double val) +{ + int exp, precision; + char label[] = { '\0', 'M', 'G', 'T', 'E' }; + + for (exp = 0; ABS(val) > 1000; exp++) + val /= 1000; + + precision = (ABS(val) < 10) ? 2 : (ABS(val) < 100) ? 1 : 0; + precision += (exp == 0); + + snprintf(str, 9, "%+.*f %c", precision, val, label[exp]); + str[8] = '\0'; + if (val >= 0) + str[0] = ' '; + + return exp * 3; +} + +static void +xaxis(Canvas *can, Color *label, Color *grid, + double vmin, double vmax, double vstep) +{ + double v; + int x; + char str[8 + 1]; + + for (v = vmax - fmod(vmax, vstep); v >= vmin; v -= vstep) { + x = v2x(v, vmin, vmax); + + ffdraw_line(can, grid, + x, XLABEL_W, + x, XLABEL_W + PLOT_W); + + humanize(str, v); + ffdraw_str_right(can, label, str, font, + x, XLABEL_W - MARGIN); + } +} + +static void +yaxis(Canvas *can, Color *label, Color *grid, + time_t tmin, time_t tmax, time_t tstep) +{ + time_t t; + int y; + char str[sizeof("YYYY/MM/DD")], *fmt; + + fmt = (tstep < 3600 * 24) ? " %H:%M:%S " : " %Y/%m/%d "; + + for (t = tmax - tmax % tstep; t >= tmin; t -= tstep) { + y = t2y(t, tmin, tmax); + + ffdraw_line(can, grid, + YLABEL_H, y, + YLABEL_H + PLOT_H, y); + + strftime(str, sizeof(str), fmt, localtime(&t)); + ffdraw_str_center(can, label, str, font, + YLABEL_H / 2, y); + } +} + +static void +title(Canvas *can, + Color *ct, char *title, + Color *cu, char *unit) +{ + ffdraw_str_left(can, ct, title, font, + TITLE_H / 2, 0); + ffdraw_str_right(can, cu, unit, font, + TITLE_H / 2, TITLE_W); +} + +static void +graph(Canvas *can, Vlist *v, + double vmin, double vmax, + time_t tmin, time_t tmax) +{ + time_t *tp; + double *vp; + int x, y, n, xlast, ylast, first; + + first = 1; + for (tp = v->t, vp = v->v, n = v->n; n-- > 0; n--, vp++, tp++) { + x = v2x(*vp, vmin, vmax); + y = t2y(*tp, tmin, tmax); + + if (!first) + ffdraw_line(can, &v->col, xlast, ylast, x, y); + + xlast = x; + ylast = y; + first = 0; + } +} + +static void +plot(Canvas *can, Vlist *v, int n, + double vmin, double vmax, + time_t tmin, time_t tmax) +{ + for (; n > 0; n--, v++) + graph(can, v, vmin, vmax, tmin, tmax); +} + +static void +legend(Canvas *can, Color *label_fg, Vlist *v, int n) +{ + int i, x, y; + + for (i = 0; i < n; i++, v++) { + x = LEGEND_H - n * (FONT_H + MARGIN) - FONT_H / 2; + + y = MARGIN + FONT_W; + ffdraw_str_left(can, &v->col, "\1", font, x, y); + + y += FONT_W * 2; + ffdraw_str_left(can, label_fg, v->name, font, x, y); + } +} + +void +ffdraw(char *name, char *units, Vlist *v, int n, + double vmin, double vmax, double vstep, + time_t tmin, time_t tmax, time_t tstep) +{ + Canvas can = { IMAGE_W, IMAGE_H, buffer, 0, 0 }; + Color plot_bg = { 0x2222, 0x2222, 0x2222, 0xffff }; + Color grid_bg = { 0x2929, 0x2929, 0x2929, 0xffff }; + Color grid_fg = { 0x3737, 0x3737, 0x3737, 0xffff }; + Color label_fg = { 0x8888, 0x8888, 0x8888, 0xffff }; + Color title_fg = { 0xdddd, 0xdddd, 0xdddd, 0xffff }; + + can.x = 0; + can.y = 0; + ffdraw_fill(&can, &plot_bg); + + can.x = PLOT_X; + can.y = PLOT_Y; + ffdraw_rectangle(&can, &grid_bg, 0, 0, PLOT_H, PLOT_W); + + can.x = YLABEL_X; + can.y = YLABEL_Y; + yaxis(&can, &label_fg, &grid_fg, tmin, tmax, tstep); + + can.x = XLABEL_X; + can.y = XLABEL_Y; + xaxis(&can, &label_fg, &grid_fg, vmin, vmax, vstep); + + can.x = TITLE_X; + can.y = TITLE_Y; + title(&can, &title_fg, name, &label_fg, units); + + can.x = PLOT_X; + can.y = PLOT_Y; + plot(&can, v, n, vmin, vmax, tmin, tmax); + + can.x = LEGEND_X; + can.y = LEGEND_Y; + legend(&can, &label_fg, v, n); + + ffdraw_print(&can); +} DIR diff --git a/font.h b/font.h @@ -1,6 +1,7 @@ #include <stddef.h> #include <stdint.h> -#include "ffdraw.h" + +#include "ploot.h" /* * Macros to make the fonts header file more readable. DIR diff --git a/main.c b/main.c @@ -1,289 +1,66 @@ -#include <arpa/inet.h> - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> #include <time.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> -#include <math.h> - -#include "ffdraw.h" -#include "font_14x7.h" +#include "arg.h" +#include "ploot.h" -#define ABS(x) ((x) < 0 ? -(x) : (x)) #define LEN(x) (sizeof(x) / sizeof(*x)) -/* - * Sizes and positions: - * - * Title on the first row legend - * y ^ here - * label |- + - + - + - + - with - * here |- + - + - + - + - the - * +--+---+---+---+--> full - * x label here height - */ - -#define MARGIN 4 - -#define FONT_H 14 -#define FONT_W 7 - -#define TITLE_X (IMAGE_H - TITLE_H) -#define TITLE_Y (XLABEL_W) -#define TITLE_H (FONT_H * 2) -#define TITLE_W (PLOT_W) - -#define XLABEL_X (PLOT_X) -#define XLABEL_Y (0) -#define XLABEL_H (PLOT_H) -#define XLABEL_W (FONT_W * 9 + MARGIN) +char *argv0; +char *tflag = ""; +char *uflag = ""; -#define YLABEL_X (0) -#define YLABEL_Y (PLOT_Y) -#define YLABEL_H (FONT_H * 2) -#define YLABEL_W (PLOT_W) - -#define PLOT_X (YLABEL_H) -#define PLOT_Y (XLABEL_W) -#define PLOT_W 700 -#define PLOT_H 200 - -#define LEGEND_X (YLABEL_H) -#define LEGEND_Y (IMAGE_W - LEGEND_W) -#define LEGEND_W (FONT_W + 150 + FONT_W) -#define LEGEND_H (PLOT_H) - -#define IMAGE_H (TITLE_H + PLOT_H + YLABEL_H) -#define IMAGE_W (XLABEL_W + PLOT_W + LEGEND_W) - -Color buffer[IMAGE_W * IMAGE_H]; - -Color c_axis = { 0xffff, 0xffff, 0xffff, 0xfff }; -Font *font = &font_14x7; - -int -ffplot_t2y(time_t t, time_t tmin, time_t tmax) +static void +usage(void) { - return (t - tmin) * PLOT_W / (tmax - tmin); -} - -int -ffplot_v2x(double v, double vmin, double vmax) -{ - return (v - vmin) * PLOT_H / (vmax - vmin); -} - -/* - * Set 'str' to a human-readable form of 'num' with always a width of 8 (+ 1 - * the '\0' terminator). Buffer overflow is ensured not to happen due to the - * max size of a double. Return the exponent. - */ -int -humanize(char *str, double val) -{ - int exp, precision; - char label[] = { '\0', 'M', 'G', 'T', 'E' }; - - for (exp = 0; ABS(val) > 1000; exp++) - val /= 1000; - - precision = (ABS(val) < 10) ? 2 : (ABS(val) < 100) ? 1 : 0; - precision += (exp == 0); - - snprintf(str, 9, "%+.*f %c", precision, val, label[exp]); - str[8] = '\0'; - if (val >= 0) - str[0] = ' '; - - return exp * 3; + fprintf(stderr, "usage: %s [-t title] [-u unit] label:color...\n", argv0); + exit(1); } void -ffplot_xaxis(Canvas *can, Color *label, Color *grid, - double vmin, double vmax, double vstep) +read_labels(Vlist *v, int n) { - double v; - int x; - char str[8 + 1]; - - for (v = vmax - fmod(vmax, vstep); v >= vmin; v -= vstep) { - x = ffplot_v2x(v, vmin, vmax); - - ffdraw_line(can, grid, - x, XLABEL_W, - x, XLABEL_W + PLOT_W); - - humanize(str, v); - ffdraw_str_right(can, label, str, font, - x, XLABEL_W - MARGIN); - } + (void)v; + (void)n; } void -ffplot_yaxis(Canvas *can, Color *label, Color *grid, - time_t tmin, time_t tmax, time_t tstep) +read_values(Vlist *v, int n) { - time_t t; - int y; - char str[sizeof("YYYY/MM/DD")], *fmt; - - fmt = (tstep < 3600 * 24) ? " %H:%M:%S " : " %Y/%m/%d "; - - for (t = tmax - tmax % tstep; t >= tmin; t -= tstep) { - y = ffplot_t2y(t, tmin, tmax); - - ffdraw_line(can, grid, - YLABEL_H, y, - YLABEL_H + PLOT_H, y); - - strftime(str, sizeof(str), fmt, localtime(&t)); - ffdraw_str_center(can, label, str, font, - YLABEL_H / 2, y); - } -} - -void -ffplot_title(Canvas *can, - Color *ct, char *title, - Color *cu, char *unit) -{ - ffdraw_str_left(can, ct, title, font, - TITLE_H / 2, 0); - ffdraw_str_right(can, cu, unit, font, - TITLE_H / 2, TITLE_W); -} - -void -ffplot_graph(Canvas *can, Vlist *v, - double vmin, double vmax, - time_t tmin, time_t tmax) -{ - time_t t; - double *vp; - int x, y, n, xlast, ylast, first; - - first = 1; - t = tmin; - for (vp = v->v, n = v->n; n-- > 0; n--, vp++) { - x = ffplot_v2x(*vp, vmin, vmax); - y = ffplot_t2y(t, tmin, tmax); - - if (!first) - ffdraw_line(can, &v->col, xlast, ylast, x, y); - - xlast = x; - ylast = y; - t += v->step; - first = 0; - } -} - -void -ffplot_plot(Canvas *can, Vlist **v, - double vmin, double vmax, - time_t tmin, time_t tmax) -{ - for (; *v != NULL; v++) - ffplot_graph(can, *v, vmin, vmax, tmin, tmax); -} - -void -ffplot_legend(Canvas *can, Color *label_fg, Vlist **v) -{ - int n, x, y; - - for (n = 0; *v != NULL; v++, n++) { - x = LEGEND_H - n * (FONT_H + MARGIN) - FONT_H / 2; - - y = MARGIN + FONT_W; - ffdraw_str_left(can, &(*v)->col, "\1", font, x, y); - - y += FONT_W * 2; - ffdraw_str_left(can, label_fg, (*v)->name, font, x, y); - } -} - -void -ffdraw(Canvas *can, char *title, char *units, Vlist **vlistv, - double vmin, double vmax, double vstep, - time_t tmin, time_t tmax, time_t tstep) -{ - Color plot_bg = { 0x2222, 0x2222, 0x2222, 0xffff }; - Color grid_bg = { 0x2929, 0x2929, 0x2929, 0xffff }; - Color grid_fg = { 0x3737, 0x3737, 0x3737, 0xffff }; - Color label_fg = { 0x8888, 0x8888, 0x8888, 0xffff }; - Color title_fg = { 0xdddd, 0xdddd, 0xdddd, 0xffff }; - - can->x = 0; - can->y = 0; - ffdraw_fill(can, &plot_bg); - - can->x = PLOT_X; - can->y = PLOT_Y; - ffdraw_rectangle(can, &grid_bg, 0, 0, PLOT_H, PLOT_W); - - can->x = YLABEL_X; - can->y = YLABEL_Y; - ffplot_yaxis(can, &label_fg, &grid_fg, tmin, tmax, tstep); - - can->x = XLABEL_X; - can->y = XLABEL_Y; - ffplot_xaxis(can, &label_fg, &grid_fg, vmin, vmax, vstep); - - can->x = TITLE_X; - can->y = TITLE_Y; - ffplot_title(can, &title_fg, title, &label_fg, units); - - can->x = PLOT_X; - can->y = PLOT_Y; - ffplot_plot(can, vlistv, vmin, vmax, tmin, tmax); - - can->x = LEGEND_X; - can->y = LEGEND_Y; - ffplot_legend(can, &label_fg, vlistv); + (void)v; + (void)n; } int -main(void) +main(int argc, char **argv) { - Canvas can = { IMAGE_W, IMAGE_H, buffer, 0, 0 }; - double v1[] = { 0.1, 30, -3, 42, 559, 343, 10, 345, 0 }; - double v2[] = { 30, -3, 42, 559, 343, 10, 345, 0, 0.3 }; - double v3[] = { 0, 0.3, 30, -3, 42, 5, 43, 345, 0, 10, - 0.3, 30, -3, 42, 59, 33, 35, 0, 40, 0.3, 30, - 0.3, 30, -3, 42, 55, 3, 5, 0, 100, 0.3, 30, - 95, 43, 45, 0, 40, 0.3, 30, 0.3, 30, -3, 42, - 0.3, 30, -3, 42, 5, 43, 3, 0, 100, 0.3, 30, - 0.3, 30, -3, 42, 59, 43, 45, 0, 4, 0.3, 30, - -3, 42, 559, 343, 45, 0, 10 }; - Vlist vl1 = { { 0x0000, 0xffff, 0xdddd, 0xffff }, NULL, LEN(v1), 500, "available ravens" }; - Vlist vl2 = { { 0xffff, 0x9999, 0x4444, 0xffff }, NULL, LEN(v2), 500, "used ravens" }; - Vlist vl3 = { { 0x1111, 0xffff, 0x5555, 0xffff }, NULL, LEN(v3), 57, "free ravens" }; - Vlist *vlistv[] = { NULL, NULL, NULL, NULL }; + Vlist *v; double vmin, vmax, vstep; time_t tmin, tmax, tstep; - uint32_t w, h; - vlistv[0] = &vl1; vl1.v = v1; - vlistv[1] = &vl2; vl2.v = v2; - vlistv[2] = &vl3; vl3.v = v3; - vlistv[3] = NULL; + ARGBEGIN { + case 't': + tflag = EARGF(usage()); + break; + case 'u': + uflag = EARGF(usage()); + break; + } ARGEND; + + if ((v = calloc(argc, sizeof(*v))) == NULL) + perror("calloc value list"), exit(1); vmin = -30; vmax = 700; vstep = 120; tmin = 0; tmax = 2000; tstep = 300; - ffdraw(&can, "Council of the ravens", "(feather per second)", vlistv, + read_labels(v, argc); + read_values(v, argc); + + ffdraw(tflag, uflag, v, argc, vmin, vmax, vstep, tmin, tmax, tstep); - w = htonl(IMAGE_W); - h = htonl(IMAGE_H); - - fputs("farbfeld", stdout); - fwrite(&w, sizeof(w), 1, stdout); - fwrite(&h, sizeof(h), 1, stdout); - fwrite(can.b, IMAGE_W * IMAGE_H, sizeof(*can.b), stdout); - return 0; } DIR diff --git a/ploot.h b/ploot.h @@ -0,0 +1,44 @@ +#include <time.h> +#include <stdint.h> + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +typedef uint16_t Color[4]; + +typedef struct { + int w; /* width */ + int h; /* height */ + Color *b; /* buffer */ + int x; /* x offset */ + int y; /* x offset */ +} Canvas; + +typedef struct { + int w; /* width */ + int h; /* height */ + char *b[128]; /* buffer */ +} Font; + +typedef struct { + Color col; /* for drawing the curve and the legend */ + time_t *t; /* array of timestamps */ + double *v; /* array of values */ + int n; /* number of values */ + char *name; /* for the legend */ +} Vlist; + +/* ffdraw.c */ +void ffdraw_pixel (Canvas *, Color *, int, int); +void ffdraw_rectangle(Canvas *, Color *, int, int, int, int); +void ffdraw_line (Canvas *, Color *, int, int, int, int); +void ffdraw_char (Canvas *, Color *, char, Font *, int, int); +void ffdraw_str_left(Canvas *, Color *, char *, Font *, int, int); +void ffdraw_str_center(Canvas *, Color *, char *, Font *, int, int); +void ffdraw_str_right(Canvas *, Color *, char *, Font *, int, int); +void ffdraw_fill (Canvas *, Color *); +void ffdraw_print (Canvas *); + +/* ffplot.c */ +void ffdraw (char *, char *, Vlist *, int, double, double, + double, time_t, time_t, time_t);