ploot-farbfeld: comeback - 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 8836c19534760f2ce037c39bde9dc5591011ed07 DIR parent eb816ab512727f55665f05809c78563ff93a94cc HTML Author: Josuah Demangeon <me@josuah.net> Date: Sun, 27 Jun 2021 04:51:38 +0200 ploot-farbfeld: comeback Diffstat: M Makefile | 2 +- M csv.c | 27 ++++++++++++++++++++++++++- M csv.h | 1 + A example.png | 0 M ploot-braille.c | 90 +++---------------------------- M ploot-farbfeld.1 | 4 ---- M ploot-farbfeld.c | 64 +++++++++++++------------------ M util.c | 53 ++++++++++++++++++++++++++++++ M util.h | 3 +++ 9 files changed, 118 insertions(+), 126 deletions(-) --- DIR diff --git a/Makefile b/Makefile @@ -9,7 +9,7 @@ MANOREFIX = $(PREFIX)/share/man SRC = csv.c drawille.c font.c font13.c font8.c util.c INC = csv.h drawille.h font.h util.h -BIN = ploot-feed ploot-braille ploot-text # ploot-farbfeld +BIN = ploot-feed ploot-braille ploot-text ploot-farbfeld OBJ = ${SRC:.c=.o} all: ${BIN} DIR diff --git a/csv.c b/csv.c @@ -9,9 +9,34 @@ #include "util.h" /* - * Read CSV data onto a set of (struct csv). + * Read CSV data onto a set of (struct csv) and some utilities to work on these data. */ +int +csv_min_max(struct csv *vl, int ncol, + time_t *tmin, time_t *tmax, + double *vmin, double *vmax) +{ + double *v; + time_t *t; + size_t n; + + *vmin = *vmax = 0; /* always show 0 on the scale */ + *tmin = *tmax = *vl->t; + + for (; ncol > 0; ncol--, vl++) { + for (t = vl->t, v = vl->v, n = vl->n; n > 0; t++, v++, n--) { + if (*v < *vmin) *vmin = *v; + if (*v > *vmax) *vmax = *v; + if (*t < *tmin) *tmin = *t; + if (*t > *tmax) *tmax = *t; + } + } + if (*tmin == *tmax) + return -1; + return 0; +} + static void csv_add_time(struct csv *vl, time_t epoch) { DIR diff --git a/csv.h b/csv.h @@ -17,5 +17,6 @@ struct csv { void csv_labels(FILE *, struct csv **, size_t *); void csv_values(FILE *, struct csv *, size_t); +int csv_min_max(struct csv *, int, time_t *, time_t *, double *, double *); #endif DIR diff --git a/example.png b/example.png Binary files differ. DIR diff --git a/ploot-braille.c b/ploot-braille.c @@ -15,85 +15,6 @@ #define pledge(...) 0 #endif -static int -get_min_max(struct csv *vl, int ncol, - time_t *tmin, time_t *tmax, - double *vmin, double *vmax) -{ - double *v; - time_t *t; - size_t n; - - *vmin = *vmax = 0; /* always show 0 on the scale */ - *tmin = *tmax = *vl->t; - - for (; ncol > 0; ncol--, vl++) { - for (t = vl->t, v = vl->v, n = vl->n; n > 0; t++, v++, n--) { - if (*v < *vmin) *vmin = *v; - if (*v > *vmax) *vmax = *v; - if (*t < *tmin) *tmin = *t; - if (*t > *tmax) *tmax = *t; - } - } - if (*tmin == *tmax) - return -1; - return 0; -} - -static time_t -time_mark_step(time_t min, time_t max, int dots) -{ - time_t dt, scale[] = { - 1, 5, 2, 10, 20, 30, 60, 60*2, 60*5, 60*10, 60*20, 60*30, 3600, - 3600*2, 3600*6, 3600*12, 3600*24, 3600*24*2, - 3600*24*7, 3600*24*14, 3600*24*20, 3600*24*21, 3600*24*28, 3600*24*50, - 3600*24*100, 3600*24*365, 0 - }; - - dt = max - min; - for (time_t *sc = scale; *sc > 0; sc++) - if (dt < *sc * dots) - return *sc; - return dt / dots; -} - -/* - * Make the value scale aligned with round values by changing the - * minimal and maximal values. - */ -static void -adjust_scale(double *min, double *max, int rows) -{ - double dv, step, scale[] = { 1, 2, 2.5, 5, }; - - dv = *max - *min; - - step = 1; - if (dv > 1) { - for (double mant = 1;; mant *= 10) { - double *sc = scale; - for (; sc < scale + LEN(scale); sc++) { - step = mant * *sc; - if (dv < rows * step) - goto end; - } - } - } else { - for (double mant = 1;; mant /= 10) { - double *sc = scale + LEN(scale) - 1; - for (; sc >= scale; sc--) { - double tmp = mant * *sc; - if (dv > rows * tmp) - goto end; - step = tmp; - } - } - } -end: - *min = (int)(*min / step) * step; - *max = *min + step * rows; -} - /* * Plot the body as an histogram interpolating the gaps and include * a vertical and horizontal axis. @@ -185,17 +106,20 @@ braille_render(struct drawille *drw, FILE *fp, double min, double max) static void plot(struct csv *vl, size_t ncol, int rows, int cols, FILE *fp) { - double vmin, vmax; + double vmin, vmax, vstep; time_t tmin, tmax, tstep; struct drawille *drw; rows = MAX(rows, 2); /* readable */ - if (get_min_max(vl, ncol, &tmin, &tmax, &vmin, &vmax) < 0) + if (csv_min_max(vl, ncol, &tmin, &tmax, &vmin, &vmax) < 0) err(1, "invalid scale: tmin=%lld tmax=%lld vmin=%fd vmax=%fd", (long long)tmin, (long long)tmax, vmin, vmax); - adjust_scale(&vmin, &vmax, rows); - tstep = time_mark_step(tmin, tmax, cols); + + tstep = scale_time_t(tmin, tmax, cols); + vstep = scale_double(vmin, vmax, rows); + vmin = (int)(vmin / vstep) * vstep; + vmax = vmin + vstep * rows; for (; ncol > 0; vl++, ncol--) { if ((drw = drawille_new(rows, cols)) == NULL) DIR diff --git a/ploot-farbfeld.1 b/ploot-farbfeld.1 @@ -13,7 +13,6 @@ . .Nm ploot-ffplot .Op Fl t Ar title -.Op Fl u Ar unit .Ar colors... . . @@ -28,9 +27,6 @@ utility plots an image in the ffplot format out of csv values coming from stdin. .It Fl t Set the title of the plot printed at the top left corner. . -.It Fl u -Set the unit description printed at the top right corner. -. .It Ar colors List of argument that specify the color for each column. If the input csv have 5 columns in addition of the timestamp, there must DIR diff --git a/ploot-farbfeld.c b/ploot-farbfeld.c @@ -13,24 +13,23 @@ #include <unistd.h> #include "csv.h" #include "font.h" -#include "scale.h" #include "util.h" #ifndef __OpenBSD__ #define pledge(...) 0 #endif -#define MARGIN 4 +#define MARGIN 8 #define IMAGE_H (TITLE_H + PLOT_H + XLABEL_H) -#define IMAGE_W (YLABEL_W + PLOT_W + LEGEND_W) +#define IMAGE_W (MARGIN + YLABEL_W + PLOT_W + MARGIN) -#define TITLE_X (YLABEL_W) -#define TITLE_Y (IMAGE_H - TITLE_H) +#define TITLE_X (MARGIN) +#define TITLE_Y (IMAGE_H - TITLE_H / 2) #define TITLE_H ((font)->height * 2) #define TITLE_W (PLOT_W) -#define YLABEL_X (0) +#define YLABEL_X (MARGIN) #define YLABEL_Y (PLOT_Y) #define YLABEL_H (PLOT_H) #define YLABEL_W (40 + MARGIN) @@ -40,14 +39,13 @@ #define XLABEL_H ((font)->height * 2) #define XLABEL_W (PLOT_W) -#define PLOT_X (YLABEL_W) +#define PLOT_X (YLABEL_X + YLABEL_W) #define PLOT_Y (XLABEL_H) #define PLOT_W (700) #define PLOT_H (160) -#define LEGEND_X (IMAGE_W - LEGEND_W) -#define LEGEND_Y (TITLE_H + PLOT_H - (font)->height) -#define LEGEND_W (100) +#define LEGEND_X (IMAGE_W / 2) +#define LEGEND_Y (TITLE_Y) #define LEGEND_H (PLOT_H) struct ffcolor { @@ -76,8 +74,7 @@ static struct colorname { { NULL, { 0, 0, 0, 0 } } }; -static char *tflag = ""; -static char *uflag = ""; +static char *flag_title = ""; static struct font *font = &font13; /* @@ -212,7 +209,7 @@ ffplot_print(FILE *fp, struct ffplot *plot) w = htonl(plot->w); h = htonl(plot->h); - fprintf(stdout, "ffplot"); + fprintf(stdout, "farbfeld"); fwrite(&w, sizeof(w), 1, fp); fwrite(&h, sizeof(h), 1, fp); fwrite(plot->buf, plot->w * plot->h, sizeof(*plot->buf), fp); @@ -284,12 +281,9 @@ ffplot_yaxis(struct ffplot *plot, struct ffcolor *label, struct ffcolor *grid, } static void -ffplot_title(struct ffplot *plot, - struct ffcolor *ct, char *title, - struct ffcolor *cu, char *unit) +ffplot_title(struct ffplot *plot, struct ffcolor *ct, char *title) { ffplot_text_left(plot, ct, font, title, TITLE_H / 2, 0); - ffplot_text_right(plot, cu, font, unit, TITLE_H / 2, TITLE_W); } static void @@ -329,27 +323,26 @@ ffplot_legend(struct ffplot *plot, struct ffcolor *fg, struct csv *vl, struct ff { size_t x, y; + x = y = 0; for (; ncol > 0; ncol--, vl++, cl++) { - y = -(ncol - 1) * (font->height + MARGIN); - x = MARGIN * 2; x = ffplot_text_left(plot, *cl, font, "-", x, y) + MARGIN; x = ffplot_text_left(plot, fg, font, vl->label, x, y); + x = ffplot_text_left(plot, fg, font, " ", x, y); } } /* - * Plot the 'n' values list of the 'v' arrax with title 'name' and - * 'units' label. + * Plot the 'n' values list of the 'v' arrax with title 'name' label. * - * Title (units) - * x ^ Legend - * label | - + - + - + - + - .... - * here | - + - + - + - + - .... + * Title Legend + * x ^ + * label | - + - + - + - + - + * here | - + - + - + - + - * +---+---+---+---+--> * x label here */ static void -plot(struct csv *vl, struct ffcolor **cl, size_t ncol, char *name, char *units) +plot(struct csv *vl, struct ffcolor **cl, size_t ncol, char *name) { struct ffplot plot = { IMAGE_W, IMAGE_H, 0, 0, NULL }; struct ffcolor plot_bg = { 0x2222, 0x2222, 0x2222, 0xffff }; @@ -360,9 +353,9 @@ plot(struct csv *vl, struct ffcolor **cl, size_t ncol, char *name, char *units) double vmin, vmax, vstep; time_t tmin, tmax, tstep; - scale_minmax(vl, ncol, &tmin, &tmax, &vmin, &vmax); - tstep = scale_tstep(tmin, tmax, 7); - vstep = scale_vstep(vmin, vmax, 7); + csv_min_max(vl, ncol, &tmin, &tmax, &vmin, &vmax); + tstep = scale_time_t(tmin, tmax, 7); + vstep = scale_double(vmin, vmax, 7); if ((plot.buf = calloc(IMAGE_H * IMAGE_W, sizeof *plot.buf)) == NULL) err(1, "calloc: %s", strerror(errno)); @@ -385,7 +378,7 @@ plot(struct csv *vl, struct ffcolor **cl, size_t ncol, char *name, char *units) plot.x = TITLE_X; plot.y = TITLE_Y; - ffplot_title(&plot, &title_fg, name, &label_fg, units); + ffplot_title(&plot, &title_fg, name); plot.x = PLOT_X; plot.y = PLOT_Y; @@ -420,7 +413,7 @@ argv_to_color(struct ffcolor **cl, char **argv) static void usage(void) { - fprintf(stderr, "usage: %s [-t title] [-u unit] {", arg0); + fprintf(stderr, "usage: %s [-t title] {", arg0); fputs(colorname->name, stderr); for (struct colorname *cn = colorname + 1; cn->name != NULL; cn++) fprintf(stderr, ",%s", cn->name); @@ -440,13 +433,10 @@ main(int argc, char **argv) err(1, "pledge: %s", strerror(errno)); arg0 = *argv; - while ((c = getopt(argc, argv, "t:u:")) > -1) { + while ((c = getopt(argc, argv, "t:")) > -1) { switch (c) { case 't': - tflag = optarg; - break; - case 'u': - uflag = optarg; + flag_title = optarg; break; default: usage(); @@ -469,7 +459,7 @@ main(int argc, char **argv) csv_values(stdin, vl, ncol); argv_to_color(cl, argv); - plot(vl, cl, argc, tflag, uflag); + plot(vl, cl, argc, flag_title); free(vl); free(cl); DIR diff --git a/util.c b/util.c @@ -1,4 +1,5 @@ #include "util.h" +#include <assert.h> #include <ctype.h> #include <errno.h> #include <limits.h> @@ -122,3 +123,55 @@ humanize(char *str, double val) return exp * 3; } + +time_t +scale_time_t(time_t min, time_t max, int dots) +{ + time_t dt, scale[] = { + 1, 5, 2, 10, 20, 30, 60, 60*2, 60*5, 60*10, 60*20, 60*30, 3600, + 3600*2, 3600*6, 3600*12, 3600*24, 3600*24*2, + 3600*24*7, 3600*24*14, 3600*24*20, 3600*24*21, 3600*24*28, 3600*24*50, + 3600*24*100, 3600*24*365, 0 + }; + + dt = max - min; + for (time_t *sc = scale; *sc > 0; sc++) + if (dt < *sc * dots) + return *sc; + return dt / dots; +} + +/* + * Make the value scale aligned with round values by changing the + * minimal and maximal values. + */ +double +scale_double(double min, double max, int rows) +{ + double dv, step, scale[] = { 1, 2, 2.5, 5, }; + + dv = max - min; + step = 1; + if (dv > 1) { + for (double mant = 1;; mant *= 10) { + double *sc = scale; + for (; sc < scale + LEN(scale); sc++) { + step = mant * *sc; + if (dv < rows * step) + return step; + } + } + } else { + for (double mant = 1;; mant /= 10) { + double *sc = scale + LEN(scale) - 1; + for (; sc >= scale; sc--) { + double tmp = mant * *sc; + if (dv > rows * tmp) + return step; + step = tmp; + } + } + } + assert(!"not reached"); + return 0; +} DIR diff --git a/util.h b/util.h @@ -2,6 +2,7 @@ #define TOOL_H #include <stddef.h> +#include <time.h> #define LEN(x) (sizeof(x) / sizeof(*x)) #define MAX(x, y) ((x) > (y) ? (x) : (y)) @@ -18,5 +19,7 @@ void put3utf(long); char *strsep(char **, const char *); void strchomp(char *); int humanize(char *, double); +time_t scale_time_t(time_t, time_t, int); +double scale_double(double, double, int); #endif