ploot-farbfeld: comeback - ploot - simple plotting tools
(HTM) 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
(HTM) 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