ploot-farbfeld: comeback - ploot - simple plotting tools | |
git clone git://bitreich.org/ploot git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65… | |
Log | |
Files | |
Refs | |
Tags | |
README | |
LICENSE | |
--- | |
commit 8836c19534760f2ce037c39bde9dc5591011ed07 | |
parent eb816ab512727f55665f05809c78563ff93a94cc | |
Author: Josuah Demangeon <[email protected]> | |
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(-) | |
--- | |
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} | |
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 thes… | |
*/ | |
+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) | |
{ | |
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 | |
diff --git a/example.png b/example.png | |
Binary files differ. | |
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, 360… | |
- 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… | |
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) | |
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… | |
.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 | |
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, s… | |
} | |
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, st… | |
{ | |
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… | |
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… | |
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); | |
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, 360… | |
+ 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; | |
+} | |
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 |