put code in commont between ploot-ff.c and ploot-braille.c to separate files - … | |
git clone git://bitreich.org/ploot git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65… | |
Log | |
Files | |
Refs | |
Tags | |
README | |
LICENSE | |
--- | |
commit 1c1a69494de95f4f5a3a439a16fac98026e2aa09 | |
parent 62211b846caa7b980b6a43dea1fdf0a0e2f6de34 | |
Author: Josuah Demangeon <[email protected]> | |
Date: Sat, 8 Feb 2020 18:38:14 +0100 | |
put code in commont between ploot-ff.c and ploot-braille.c to separate files | |
Diffstat: | |
M Makefile | 2 +- | |
M arg.h | 6 +++--- | |
M def.h | 27 +++++++++++++++++++++------ | |
A ploot-braille.c | 200 +++++++++++++++++++++++++++++… | |
M ploot-ff.c | 295 +++++++----------------------… | |
D ploot-plot.c | 201 ------------------------------ | |
6 files changed, 292 insertions(+), 439 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
@@ -5,7 +5,7 @@ BIN = ploot-ff ploot-feed | |
LIB = -lm | |
MANDIR = $(PREFIX)/share/man | |
-SRC = util.c drawille.c font.c font7.c font8.c font13.c | |
+SRC = csv.c drawille.c font.c font7.c font8.c font13.c util.c scale.c | |
all: $(BIN) | |
diff --git a/arg.h b/arg.h | |
@@ -5,11 +5,11 @@ extern char const *arg0; | |
#define ARG_SWITCH(argc, argv) \ | |
arg0 = *argv; \ | |
- while (++argv && --argc && **argv == '-' && (*argv)[1]) \ | |
+ while (++argv && --argc && **argv == '-' && (*argv)[1]) … | |
if ((*argv)[1] == '-' && (*argv)[2] == '\0') { \ | |
++argv; break; \ | |
- } else for (int stop = 0; !stop && *++*argv != '\0' ;) \ | |
- switch (**argv) | |
+ } else for (int stop = 0; !stop && *++*argv != '\0' ;) \ | |
+ switch (**argv) | |
#define ARG ((*++*argv != '\0' || *++argv != NULL) \ | |
? ((stop = 1), argc--, *argv) \ | |
diff --git a/def.h b/def.h | |
@@ -23,6 +23,23 @@ struct font { | |
char *glyph[128]; /* 0: end, 1: off, 2: on. */ | |
}; | |
+/* | |
+ * List of values and timestamps. Both have their dedicated buffer | |
+ * so that the timestamp buffer can be shared across vlist objects. | |
+ */ | |
+struct vlist { | |
+ time_t *t; /* array of timestamps */ | |
+ double *v; /* array of values */ | |
+ size_t n; /* number of values */ | |
+ char *label; /* for the legend */ | |
+}; | |
+ | |
+/* csv.c */ | |
+ | |
+void csv_addrow (struct vlist *, size_t, char *); | |
+void csv_values (struct vlist *, size_t); | |
+void csv_labels (struct vlist *, char **, char *… | |
+ | |
/* drawille.c */ | |
size_t drawille_fmt_row (struct drawille *, char *, size… | |
@@ -38,17 +55,15 @@ char * drawille_text (struct … | |
size_t font_width (struct font *, int); | |
size_t font_strlen (struct font *, char *); | |
-/* font13.c */ | |
+/* font*.c */ | |
struct font font13; | |
- | |
-/* font7.c */ | |
- | |
+struct font font7; | |
struct font font8; | |
-/* font8.c */ | |
+/* scale.c */ | |
-struct font font8; | |
+void scale (struct vlist *, int, time_t … | |
/* util.c */ | |
diff --git a/ploot-braille.c b/ploot-braille.c | |
@@ -0,0 +1,200 @@ | |
+#include <assert.h> | |
+#include <errno.h> | |
+#include <stdint.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <time.h> | |
+#include <math.h> | |
+ | |
+#include "def.h" | |
+ | |
+/* | |
+ * Adjust the vertical scale so that it gets possible to | |
+ */ | |
+static void | |
+plot_scale(double *min, double *max, int row) | |
+{ | |
+ double unit, range, mi; | |
+ | |
+ range = *max - *min; | |
+ unit = 1; | |
+ | |
+ /* Zoom until it fills the canvas. */ | |
+ for (; (row - 1) * unit > range; unit /= 10) | |
+ continue; | |
+ | |
+ /* Dezoom until it fits the canvas. */ | |
+ for (; (row - 1) * unit < range; unit *= 10) | |
+ continue; | |
+ | |
+ /* Fine tune. */ | |
+ if ((row - 1) * unit / 5 > range) | |
+ unit /= 5; | |
+ if ((row - 1) * unit / 4 > range) | |
+ unit /= 4; | |
+ if ((row - 1) * unit / 2 > range) | |
+ unit /= 2; | |
+ | |
+ /* Align the minimum (and the zero). */ | |
+ for (mi = 0; mi > *min - unit; mi -= unit) | |
+ continue; | |
+ | |
+ /* Update the displayed minimal and maximal. */ | |
+ *min = mi; | |
+ *max = mi + unit * row; | |
+} | |
+ | |
+/* | |
+ * Return the step between two values. | |
+ */ | |
+static int | |
+plot_time_interval(time_t step) | |
+{ | |
+ time_t scale[] = { | |
+ 1, 5, 2, 10, 20, 30, 60, 60*2, 60*5, 60*10, 60*20, 60*30, | |
+ 3600, 3600*2, 3600*5, 3600*10, 3600*18, 3600*24, 3600*24*2, | |
+ 3600*24*5, 3600*24*10, 3600*24*20, 3600*24*30, 3600*24*50, | |
+ 3600*24*100, 3600*24*365, 0 | |
+ }; | |
+ | |
+ for (time_t *s = scale; *s != 0; s++) | |
+ if (*s >= 20 * step) | |
+ return *s; | |
+ return 1; | |
+} | |
+ | |
+static size_t | |
+plot_axis_x(char *buf, size_t sz, time_t step, time_t t2, int col) | |
+{ | |
+ int x, prec; | |
+ char tmp[sizeof("MM/DD HH:MM")], *fmt; | |
+ size_t n; | |
+ time_t t, interval; | |
+ | |
+ interval = plot_time_interval(step); | |
+ fmt = (step < 3600 * 12) ? "^%H:%M:%S" : | |
+ (step < 3600 * 24) ? "^%m/%d %H:%M" : | |
+ "^%Y/%m/%d"; | |
+ n = x = 0; | |
+ | |
+ t = t2 - col * 2 * step; | |
+ t += interval - t % interval; | |
+ for (; t < t2; t += interval) { | |
+ strftime(tmp, sizeof tmp, fmt, localtime(&t)); | |
+ x = ((t - t2) / 2 + col * step) / step; | |
+ prec = x - n + strlen(tmp); | |
+ assert((n += snprintf(buf+n, sz-n, "%*s", prec, tmp)) <= sz); | |
+ } | |
+ assert((n += strlcpy(buf+n, "\n", sz-n)) < sz); | |
+ return n; | |
+} | |
+ | |
+/* | |
+ * Plot a single line out of the y axis, at row <r> out of <rows>. | |
+ */ | |
+static size_t | |
+plot_axis_y(char *buf, size_t sz, double min, double max, int r, int rows) | |
+{ | |
+ size_t i; | |
+ char tmp[10] = "", *s; | |
+ double val; | |
+ | |
+ val = (max - min) * (rows - r) / rows + min; | |
+ humanize(tmp, sizeof tmp, val); | |
+ s = (r == 0) ? "┌" : | |
+ (r == rows - 1) ? "└" : | |
+ "├"; | |
+ i = snprintf(buf, sz, "%s%-6s ", s, tmp); | |
+ return (i > sz) ? (sz) : (i); | |
+} | |
+ | |
+static char * | |
+plot_render(struct drawille *drw, double min, double max, time_t step, time_t … | |
+{ | |
+ char *buf; | |
+ size_t sz; | |
+ size_t n; | |
+ | |
+ /* Render the plot line by line. */ | |
+ sz = drw->row * (20 + drw->col * 3 + 1) + 1; | |
+ sz += drw->col + 1 + 100000; | |
+ if ((buf = calloc(sz, 1)) == NULL) | |
+ goto err; | |
+ n = 0; | |
+ for (int row = 0; row < drw->row; row++) { | |
+ n += drawille_fmt_row(drw, buf+n, sz-n, row); | |
+ n += plot_axis_y(buf+n, sz-n, min, max, row, drw->row); | |
+ n += strlcpy(buf+n, "\n", sz-n); | |
+ } | |
+ plot_axis_x(buf+n, sz-n, step, t2, drw->col); | |
+ return buf; | |
+err: | |
+ errno = ENOBUFS; | |
+ free(buf); | |
+ return NULL; | |
+} | |
+ | |
+/* | |
+ * Plot the body as an histogram interpolating the gaps and include | |
+ * a vertical and horizontal axis. | |
+ */ | |
+static char * | |
+plot_hist(struct vlist *vl, time_t t2, struct drawille *drw) | |
+{ | |
+ int x, y, zero, shift; | |
+ double min, max, val; | |
+ time_t t1, t; | |
+ | |
+ /* Adjust the y scale. */ | |
+ shift = min = max = 0; | |
+ timeserie_stats(vl, &min, &max); | |
+ if (drw->row > 1) { | |
+ shift = 2; /* Align values to the middle of the scale: |- */ | |
+ plot_scale(&min, &max, drw->row); | |
+ } | |
+ zero = timeserie_ypos(0, min, max, drw->row*4) - shift; | |
+ | |
+ /* Adjust the x scale. */ | |
+ t2 = t2 + vl->step - t2 % vl->step; | |
+ t1 = t2 - vl->step * vl->len; | |
+ | |
+ /* Plot the data in memory in <drw> starting from the end (t2). */ | |
+ t = t2; | |
+ for (x = drw->col * 2; x > 0; x--) { | |
+ val = timeserie_get(vl, t); | |
+ if (!isnan(val)) { | |
+ y = timeserie_ypos(val, min, max, drw->row*4) - shift; | |
+ drawille_dot_hist(drw, x, y, zero); | |
+ } | |
+ t -= vl->step; | |
+ } | |
+ | |
+ return plot_render(drw, min, max, vl->step, t2); | |
+} | |
+ | |
+static char * | |
+plot(struct vlist *vl, time_t t2, int row, int col) | |
+{ | |
+ struct drawille *drw; | |
+ size_t len; | |
+ char *buf; | |
+ | |
+ len = 500; | |
+ buf = NULL; | |
+ drw = NULL; | |
+ col -= 8; | |
+ | |
+ if (timeserie_read(vl) == -1) | |
+ goto err; | |
+ | |
+ if ((drw = drawille_new(row, col)) == NULL) | |
+ goto err; | |
+ | |
+ buf = plot_hist(vl, t2, drw); | |
+err: | |
+ if (buf == NULL) | |
+ timedb_close(&vl->db); | |
+ free(drw); | |
+ return buf; | |
+} | |
diff --git a/ploot-ff.c b/ploot-ff.c | |
@@ -18,9 +18,6 @@ | |
#define MARGIN 4 | |
-#define XDENSITX 7 /* nb of values on x axis */ | |
-#define YDENSITX 7 /* nb of values on y axis */ | |
- | |
#define IMAGE_H (TITLE_H + PLOT_H + XLABEL_H) | |
#define IMAGE_W (YLABEL_W + PLOT_W + LEGEND_W) | |
@@ -45,8 +42,8 @@ | |
#define PLOT_H (160) | |
#define LEGEND_X (IMAGE_W - LEGEND_W) | |
-#define LEGEND_Y (XLABEL_H) | |
-#define LEGEND_W (150) | |
+#define LEGEND_Y (TITLE_H + PLOT_H - (font)->height) | |
+#define LEGEND_W (100) | |
#define LEGEND_H (PLOT_H) | |
struct color { | |
@@ -56,12 +53,9 @@ struct color { | |
uint16_t alpha; | |
}; | |
-struct vlist { | |
- struct color color; /* color to use to draw the … | |
- time_t *t; /* array of timestamps */ | |
- double *v; /* array of values */ | |
- int n; /* number of values */ | |
- char *label; /* for the legend */ | |
+struct cname { | |
+ char *name; | |
+ struct color color; | |
}; | |
struct canvas { | |
@@ -72,17 +66,12 @@ struct canvas { | |
struct color *buf; | |
}; | |
-struct clist { | |
- char *name; | |
- struct color color; | |
-}; | |
- | |
char const *arg0; | |
static char *tflag = ""; | |
static char *uflag = ""; | |
static struct font *font = &font13; | |
-struct clist clist[] = { | |
+static struct cname cname[] = { | |
/* name red green blue alpha */ | |
{ "red", { 0xffff, 0x4444, 0x4444, 0xffff } }, | |
{ "orange", { 0xffff, 0x9999, 0x4444, 0xffff } }, | |
@@ -93,98 +82,6 @@ struct clist clist[] = { | |
{ NULL, { 0, 0, 0, 0 } } | |
}; | |
-static struct color * | |
-name_to_color(char *name) | |
-{ | |
- for (struct clist *c = clist; c->name != NULL; c++) | |
- if (strcmp(name, c->name) == 0) | |
- return &c->color; | |
- return NULL; | |
-} | |
- | |
-static void | |
-scale_minmax(struct vlist *v, int n, | |
- time_t *tmin, time_t *tmax, | |
- double *vmin, double *vmax) | |
-{ | |
- int i; | |
- | |
- *vmin = *vmax = 0; | |
- *tmin = *tmax = *v->t; | |
- | |
- for (; n-- > 0; v++) { | |
- for (i = 0; i < v->n; i++) { | |
- if (v->v[i] < *vmin) | |
- *vmin = v->v[i]; | |
- if (v->v[i] > *vmax) | |
- *vmax = v->v[i]; | |
- if (v->t[i] < *tmin) | |
- *tmin = v->t[i]; | |
- if (v->t[i] > *tmax) | |
- *tmax = v->t[i]; | |
- } | |
- } | |
-} | |
- | |
-static void | |
-scale_tstep(time_t *step, int density, time_t min, time_t max) | |
-{ | |
- time_t dt, *s, scale[] = { | |
- 1, 5, 2, 10, 20, 30, 60, 60*2, 60*5, 60*10, 60*20, 60*30, 3600… | |
- 3600*2, 3600*5, 3600*10, 3600*18, 3600*24, 3600*24*2, | |
- 3600*24*5, 3600*24*10, 3600*24*20, 3600*24*30, 3600*24*50, | |
- 3600*24*100, 3600*24*365, 0 | |
- }; | |
- | |
- dt = max - min; | |
- | |
- for (s = scale; s < scale + LEN(scale); s++) { | |
- if (dt < *s * density) { | |
- *step = *s; | |
- break; | |
- } | |
- } | |
-} | |
- | |
-static void | |
-scale_vstep(double *step, int density, double min, double max) | |
-{ | |
- double dv, *s, scale[] = { 1, 2, 3, 5 }; | |
- int i; | |
- | |
- dv = max - min; | |
- | |
- if (dv > 1) { | |
- for (i = 1; i != 0; i *= 10) { | |
- for (s = scale; s < scale + LEN(scale); s++) { | |
- if (dv < *s * i * density) { | |
- *step = *s * i; | |
- return; | |
- } | |
- } | |
- } | |
- } else { | |
- for (i = 1; i != 0; i *= 10) { | |
- for (s = scale + LEN(scale) - 1; s >= scale; s--) { | |
- if (dv > *s / i * density / 2) { | |
- *step = *s / i; | |
- return; | |
- } | |
- } | |
- } | |
- } | |
-} | |
- | |
-static void | |
-scale(struct vlist *v, int n, | |
- time_t *tmin, time_t *tmax, time_t *tstep, | |
- double *vmin, double *vmax, double *vstep) | |
-{ | |
- scale_minmax(v, n, tmin, tmax, vmin, vmax); | |
- scale_tstep(tstep, XDENSITX, *tmin, *tmax); | |
- scale_vstep(vstep, YDENSITX, *vmin, *vmax); | |
-} | |
- | |
/* | |
* Convert (x,y) coordinates to (row,col) for printing into the buffer. | |
* The buffer only contain one number, so the coordinate is a single integer: | |
@@ -196,18 +93,18 @@ scale(struct vlist *v, int n, | |
* - (0,1) is above it. +--x | |
*/ | |
static void | |
-ff_pixel(struct canvas *can, struct color *col, | |
+ff_pixel(struct canvas *can, struct color *color, | |
int x, int y) | |
{ | |
x += can->x; | |
y += can->y; | |
if (x < 0 || x >= can->w || y < 0 || y >= can->h) | |
return; | |
- memcpy(can->buf + can->w * (can->h - 1 - y) + x, col, sizeof(*can->buf… | |
+ memcpy(can->buf + can->w * (can->h - 1 - y) + x, color, sizeof(*can->b… | |
} | |
static void | |
-ff_rectangle(struct canvas *can, struct color *col, | |
+ff_rectangle(struct canvas *can, struct color *color, | |
int y1, int x1, | |
int y2, int x2) | |
{ | |
@@ -218,14 +115,14 @@ ff_rectangle(struct canvas *can, struct color *col, | |
for (y = ymin; y <= ymax; y++) | |
for (x = xmin; x <= xmax; x++) | |
- ff_pixel(can, col, x, y); | |
+ ff_pixel(can, color, x, y); | |
} | |
/* | |
* From Bresenham's line algorithm and dcat's tplot. | |
*/ | |
static void | |
-ff_line(struct canvas *can, struct color *col, | |
+ff_line(struct canvas *can, struct color *color, | |
int x0, int y0, | |
int x1, int y1) | |
{ | |
@@ -238,7 +135,7 @@ ff_line(struct canvas *can, struct color *col, | |
err = (dy > dx ? dy : -dx) / 2; | |
for (;;) { | |
- ff_pixel(can, col, x0, y0); | |
+ ff_pixel(can, color, x0, y0); | |
if (y0 == y1 && x0 == x1) | |
break; | |
@@ -259,7 +156,7 @@ ff_line(struct canvas *can, struct color *col, | |
* Draw a coloured glyph from font f centered on y. | |
*/ | |
static int | |
-ff_char(struct canvas *can, struct color *col, char c, | |
+ff_char(struct canvas *can, struct color *color, char c, | |
int x, int y) | |
{ | |
int yf, xf, wf; | |
@@ -271,7 +168,7 @@ ff_char(struct canvas *can, struct color *col, char c, | |
for (xf = 0; xf < wf; xf++) | |
for (yf = 0; yf < font->height; yf++) | |
if (font->glyph[(int)c][wf * (font->height - yf) + xf]… | |
- ff_pixel(can, col, x + xf, y + yf); | |
+ ff_pixel(can, color, x + xf, y + yf); | |
return wf + 1; | |
} | |
@@ -279,11 +176,11 @@ ff_char(struct canvas *can, struct color *col, char c, | |
* Draw a left aligned string without wrapping it. | |
*/ | |
static size_t | |
-ff_text_left(struct canvas *can, struct color *col, char *s, | |
+ff_text_left(struct canvas *can, struct color *color, char *s, | |
int x, int y) | |
{ | |
for (; *s != '\0'; s++) | |
- x += ff_char(can, col, *s, x, y); | |
+ x += ff_char(can, color, *s, x, y); | |
return x; | |
} | |
@@ -291,22 +188,22 @@ ff_text_left(struct canvas *can, struct color *col, char … | |
* Draw a center aligned string without wrapping it. | |
*/ | |
static size_t | |
-ff_text_center(struct canvas *can, struct color *col, char *s, | |
+ff_text_center(struct canvas *can, struct color *color, char *s, | |
int x, int y) | |
{ | |
x -= font_strlen(font, s) / 2; | |
- return ff_text_left(can, col, s, x, y); | |
+ return ff_text_left(can, color, s, x, y); | |
} | |
/* | |
* Draw a right aligned string without wrapping it. | |
*/ | |
static size_t | |
-ff_text_right(struct canvas *can, struct color *col, char *s, | |
+ff_text_right(struct canvas *can, struct color *color, char *s, | |
int x, int y) | |
{ | |
x -= font_strlen(font, s); | |
- return ff_text_left(can, col, s, x, y); | |
+ return ff_text_left(can, color, s, x, y); | |
} | |
static void | |
@@ -326,12 +223,16 @@ ff_print(struct canvas *can) | |
static int | |
ff_t2x(time_t t, time_t tmin, time_t tmax) | |
{ | |
+ if (tmin == tmax) | |
+ return PLOT_W; | |
return (t - tmin) * PLOT_W / (tmax - tmin); | |
} | |
static int | |
ff_v2y(double v, double vmin, double vmax) | |
{ | |
+ if (vmin == vmax) | |
+ return PLOT_H; | |
return (v - vmin) * PLOT_H / (vmax - vmin); | |
} | |
@@ -394,7 +295,7 @@ ff_title(struct canvas *can, | |
} | |
static void | |
-ff_plot(struct canvas *can, struct vlist *v, | |
+ff_plot(struct canvas *can, struct vlist *vl, struct color *color, | |
double vmin, double vmax, | |
time_t tmin, time_t tmax) | |
{ | |
@@ -403,12 +304,12 @@ ff_plot(struct canvas *can, struct vlist *v, | |
int x, y, n, ylast, xlast, first; | |
first = 1; | |
- for (tp = v->t, vp = v->v, n = v->n; n > 0; n--, vp++, tp++) { | |
+ for (tp = vl->t, vp = vl->v, n = vl->n; n > 0; n--, vp++, tp++) { | |
y = ff_v2y(*vp, vmin, vmax); | |
x = ff_t2x(*tp, tmin, tmax); | |
if (!first) | |
- ff_line(can, &v->color, xlast, ylast, x, y); | |
+ ff_line(can, color, xlast, ylast, x, y); | |
ylast = y; | |
xlast = x; | |
@@ -417,24 +318,24 @@ ff_plot(struct canvas *can, struct vlist *v, | |
} | |
static void | |
-ff_values(struct canvas *can, struct vlist *v, int n, | |
+ff_values(struct canvas *can, struct vlist *vl, struct color **cl, size_t ncol, | |
time_t tmin, time_t tmax, | |
double vmin, double vmax) | |
{ | |
- for (; n > 0; n--, v++) | |
- ff_plot(can, v, vmin, vmax, tmin, tmax); | |
+ for (; ncol > 0; ncol--, vl++, cl++) | |
+ ff_plot(can, vl, *cl, vmin, vmax, tmin, tmax); | |
} | |
static void | |
-ff_legend(struct canvas *can, struct color *label_fg, struct vlist *v, int n) | |
+ff_legend(struct canvas *can, struct color *fg, struct vlist *vl, struct color… | |
{ | |
- int i, x, y; | |
+ size_t i, x, y; | |
- for (i = 0; i < n; i++, v++) { | |
- x = MARGIN; | |
- x = ff_text_left(can, &v->color, "\1", x, y); | |
- x = ff_text_left(can, label_fg, v->label, x, y); | |
- y = LEGEND_H - i * (font->height + MARGIN) - font->height / 2; | |
+ for (i = 0; i < ncol; i++, vl++, cl++) { | |
+ x = MARGIN * 2; | |
+ x = ff_text_left(can, *cl, "\1", x, y) + MARGIN; | |
+ x = ff_text_left(can, fg, vl->label, x, y); | |
+ y = LEGEND_H - i * (font->height + MARGIN); | |
} | |
} | |
@@ -450,7 +351,7 @@ ff_legend(struct canvas *can, struct color *label_fg, struc… | |
* x label here | |
*/ | |
static void | |
-ff(struct vlist *v, int n, char *name, char *units) | |
+ff(struct vlist *vl, struct color **cl, size_t ncol, char *name, char *units) | |
{ | |
struct canvas can = { IMAGE_W, IMAGE_H, 0, 0, NULL }; | |
struct color plot_bg = { 0x2222, 0x2222, 0x2222, 0xffff }; | |
@@ -461,7 +362,7 @@ ff(struct vlist *v, int n, char *name, char *units) | |
double vmin, vmax, vstep; | |
time_t tmin, tmax, tstep; | |
- scale(v, n, &tmin, &tmax, &tstep, &vmin, &vmax, &vstep); | |
+ scale(vl, ncol, &tmin, &tmax, &tstep, &vmin, &vmax, &vstep); | |
assert(can.buf = calloc(IMAGE_H * IMAGE_W, sizeof *can.buf)); | |
@@ -487,108 +388,41 @@ ff(struct vlist *v, int n, char *name, char *units) | |
can.x = PLOT_X; | |
can.y = PLOT_Y; | |
- ff_values(&can, v, n, tmin, tmax, vmin, vmax); | |
+ ff_values(&can, vl, cl, ncol, tmin, tmax, vmin, vmax); | |
can.x = LEGEND_X; | |
can.y = LEGEND_Y; | |
- ff_legend(&can, &label_fg, v, n); | |
+ ff_legend(&can, &label_fg, vl, cl, ncol); | |
ff_print(&can); | |
} | |
- | |
-static void | |
-csv_labels(struct vlist *v, char **argv, char *buf) | |
-{ | |
- struct color *color; | |
- | |
- if (esfgets(buf, LINE_MAX, stdin) == NULL) | |
- err(1, "missing label line"); | |
- | |
- if (strcmp(strsep(&buf, ","), "epoch") != 0) | |
- err(1, "first label must be \"epoch\""); | |
- | |
- for (; *argv != NULL; v++, argv++) { | |
- if ((v->label = strsep(&buf, ",")) == NULL) | |
- err(1, "more arguments than columns"); | |
- else if ((color = name_to_color(*argv)) == NULL) | |
- err(1, "unknown color: %s", *argv); | |
- v->color = *color; | |
- } | |
- | |
- if (strsep(&buf, ",") != NULL) | |
- err(1, "more columns than arguments"); | |
-} | |
- | |
-static int | |
-csv_addval(struct vlist *v, size_t sz, size_t nval, double field, time_t epoch) | |
-{ | |
- if (nval >= sz) { | |
- sz = sz * 2 + 1; | |
- if ((v->v = realloc(v->v, sz * sizeof(*v->v))) == NULL) | |
- err(1, "reallocating values buffer"); | |
- if ((v->t = realloc(v->t, sz * sizeof(*v->t))) == NULL) | |
- err(1, "reallocating values buffer"); | |
- } | |
- v->v[nval] = field; | |
- v->t[nval] = epoch; | |
- v->n = nval + 1; | |
- | |
- return sz; | |
-} | |
-/* | |
- * Add to each column the value on the current row. | |
- */ | |
-static int | |
-csv_addrow(struct vlist *v, size_t sz, size_t ncol, size_t nval, char *line) | |
+static struct color * | |
+name_to_color(char *name) | |
{ | |
- time_t epoch; | |
- int bs; | |
- char *field, *dot; | |
- | |
- if ((field = strsep(&line, ",")) == NULL) | |
- err(1, "%d: missing epoch", nval); | |
- | |
- if ((dot = strchr(field, '.')) != NULL) | |
- *dot = '\0'; | |
- epoch = eatol(field); | |
- for (; (field = strsep(&line, ",")) != NULL; ncol--, v++) { | |
- if (ncol <= 0) | |
- err(1, "%d: too many fields", nval); | |
- bs = csv_addval(v, sz, nval, eatof(field), epoch); | |
- } | |
- if (ncol > 0) | |
- err(1, "%d: too few fields", ncol); | |
+ struct cname *cn; | |
- return bs; | |
+ for (cn = cname; cn->name != NULL; cn++) | |
+ if (strcmp(name, cn->name) == 0) | |
+ return &cn->color; | |
+ return NULL; | |
} | |
-/* | |
- * < ncol > | |
- * epoch,a1,b1,c1 ^ | |
- * epoch,a2,b2,c2 nval | |
- * epoch,a3,b3,c3 v | |
- */ | |
static void | |
-csv_values(struct vlist *v, size_t ncol) | |
+argv_to_color(struct color **cl, char **argv) | |
{ | |
- int nval, sz; | |
- char line[LINE_MAX]; | |
- | |
- sz = 0; | |
- for (nval = 0; esfgets(line, sizeof(line), stdin) != NULL; nval++) | |
- sz = csv_addrow(v, sz, ncol, nval, line); | |
- if (nval == 0) | |
- err(1, "no value could be read\n"); | |
+ for (; *argv != NULL; cl++, argv++) | |
+ if ((*cl = name_to_color(*argv)) == NULL) | |
+ err(1, "unknown color name: %s", *argv); | |
} | |
static void | |
usage(void) | |
{ | |
fprintf(stderr, "usage: %s [-t title] [-u unit] {", arg0); | |
- fputs(clist->name, stderr); | |
- for (struct clist *c = clist + 1; c->name != NULL; c++) | |
- fprintf(stderr, ",%s", c->name); | |
+ fputs(cname->name, stderr); | |
+ for (struct cname *cn = cname + 1; cn->name != NULL; cn++) | |
+ fprintf(stderr, ",%s", cn->name); | |
fputs("}...\n", stderr); | |
exit(1); | |
} | |
@@ -596,7 +430,8 @@ usage(void) | |
int | |
main(int argc, char **argv) | |
{ | |
- struct vlist *v; | |
+ struct vlist *vl; | |
+ struct color **cl; | |
char labels[LINE_MAX]; | |
ARG_SWITCH(argc, argv) { | |
@@ -610,15 +445,19 @@ main(int argc, char **argv) | |
usage(); | |
} | |
- fflush(stdout); | |
+ if (argc == 0) | |
+ usage(); | |
- if ((v = calloc(argc, sizeof(*v))) == NULL) | |
- err(1, "calloc value list"); | |
+ assert(vl = calloc(argc, sizeof(*vl))); | |
+ assert(cl = calloc(argc, sizeof(*cl))); | |
- csv_labels(v, argv, labels); | |
- csv_values(v, argc); | |
+ csv_labels(vl, argv, labels); | |
+ csv_values(vl, argc); | |
+ argv_to_color(cl, argv); | |
- ff(v, argc, tflag, uflag); | |
+ ff(vl, cl, argc, tflag, uflag); | |
+ free(vl); | |
+ free(cl); | |
return 0; | |
} | |
diff --git a/ploot-plot.c b/ploot-plot.c | |
@@ -1,201 +0,0 @@ | |
-#include <assert.h> | |
-#include <errno.h> | |
-#include <stdint.h> | |
-#include <stdio.h> | |
-#include <stdlib.h> | |
-#include <string.h> | |
-#include <time.h> | |
-#include <math.h> | |
- | |
-#include "def.h" | |
- | |
-/* | |
- * Adjust the vertical scale so that it gets possible to | |
- */ | |
-static void | |
-plot_scale(double *min, double *max, int row) | |
-{ | |
- double unit, range, mi; | |
- | |
- range = *max - *min; | |
- unit = 1; | |
- | |
- /* Zoom until it fills the canvas. */ | |
- for (; (row - 1) * unit > range; unit /= 10) | |
- continue; | |
- | |
- /* Dezoom until it fits the canvas. */ | |
- for (; (row - 1) * unit < range; unit *= 10) | |
- continue; | |
- | |
- /* Fine tune. */ | |
- if ((row - 1) * unit / 5 > range) | |
- unit /= 5; | |
- if ((row - 1) * unit / 4 > range) | |
- unit /= 4; | |
- if ((row - 1) * unit / 2 > range) | |
- unit /= 2; | |
- | |
- /* Align the minimum (and the zero). */ | |
- for (mi = 0; mi > *min - unit; mi -= unit) | |
- continue; | |
- | |
- /* Update the displayed minimal and maximal. */ | |
- *min = mi; | |
- *max = mi + unit * row; | |
-} | |
- | |
-/* | |
- * Return the step between two values. | |
- */ | |
-static int | |
-plot_time_interval(time_t step) | |
-{ | |
- time_t scale[] = { | |
- 1, 5, 2, 10, 20, 30, 60, 60*2, 60*5, 60*10, 60*20, 60*30, | |
- 3600, 3600*2, 3600*5, 3600*10, 3600*18, 3600*24, 3600*24*2, | |
- 3600*24*5, 3600*24*10, 3600*24*20, 3600*24*30, 3600*24*50, | |
- 3600*24*100, 3600*24*365, 0 | |
- }; | |
- | |
- for (time_t *s = scale; *s != 0; s++) | |
- if (*s >= 20 * step) | |
- return *s; | |
- return 1; | |
-} | |
- | |
-static size_t | |
-plot_axis_x(char *buf, size_t sz, time_t step, time_t t2, int col) | |
-{ | |
- int x, prec; | |
- char tmp[sizeof("MM/DD HH:MM")], *fmt; | |
- size_t n; | |
- time_t t, interval; | |
- | |
- interval = plot_time_interval(step); | |
- fmt = (step < 3600 * 12) ? "^%H:%M:%S" : | |
- (step < 3600 * 24) ? "^%m/%d %H:%M" : | |
- "^%Y/%m/%d"; | |
- n = x = 0; | |
- | |
- t = t2 - col * 2 * step; | |
- t += interval - t % interval; | |
- for (; t < t2; t += interval) { | |
- strftime(tmp, sizeof tmp, fmt, localtime(&t)); | |
- x = ((t - t2) / 2 + col * step) / step; | |
- prec = x - n + strlen(tmp); | |
- assert((n += snprintf(buf+n, sz-n, "%*s", prec, tmp)) <= sz); | |
- } | |
- assert((n += strlcpy(buf+n, "\n", sz-n)) < sz); | |
- return n; | |
-} | |
- | |
-/* | |
- * Plot a single line out of the y axis, at row <r> out of <rows>. | |
- */ | |
-static size_t | |
-plot_axis_y(char *buf, size_t sz, double min, double max, int r, int rows) | |
-{ | |
- size_t i; | |
- char tmp[10] = "", *s; | |
- double val; | |
- | |
- val = (max - min) * (rows - r) / rows + min; | |
- humanize(tmp, sizeof tmp, val); | |
- s = (r == 0) ? "┌" : | |
- (r == rows - 1) ? "└" : | |
- "├"; | |
- i = snprintf(buf, sz, "%s%-6s ", s, tmp); | |
- return (i > sz) ? (sz) : (i); | |
-} | |
- | |
-static char * | |
-plot_render(struct drawille *drw, double min, double max, time_t step, time_t … | |
-{ | |
- char *buf; | |
- size_t sz; | |
- size_t n; | |
- | |
- /* Render the plot line by line. */ | |
- sz = drw->row * (20 + drw->col * 3 + 1) + 1; | |
- sz += drw->col + 1 + 100000; | |
- if ((buf = calloc(sz, 1)) == NULL) | |
- goto err; | |
- n = 0; | |
- for (int row = 0; row < drw->row; row++) { | |
- n += drawille_fmt_row(drw, buf+n, sz-n, row); | |
- n += plot_axis_y(buf+n, sz-n, min, max, row, drw->row); | |
- n += strlcpy(buf+n, "\n", sz-n); | |
- } | |
- plot_axis_x(buf+n, sz-n, step, t2, drw->col); | |
- | |
- return buf; | |
-err: | |
- errno = ENOBUFS; | |
- free(buf); | |
- return NULL; | |
-} | |
- | |
-/* | |
- * Plot the body as an histogram interpolating the gaps and include | |
- * a vertical and horizontal axis. | |
- */ | |
-static char * | |
-plot_hist(struct timeserie *ts, time_t t2, struct drawille *drw) | |
-{ | |
- int x, y, zero, shift; | |
- double min, max, val; | |
- time_t t1, t; | |
- | |
- /* Adjust the y scale. */ | |
- shift = min = max = 0; | |
- timeserie_stats(ts, &min, &max); | |
- if (drw->row > 1) { | |
- shift = 2; /* Align values to the middle of the scale: |- */ | |
- plot_scale(&min, &max, drw->row); | |
- } | |
- zero = timeserie_ypos(0, min, max, drw->row*4) - shift; | |
- | |
- /* Adjust the x scale. */ | |
- t2 = t2 + ts->step - t2 % ts->step; | |
- t1 = t2 - ts->step * ts->len; | |
- | |
- /* Plot the data in memory in <drw> starting from the end (t2). */ | |
- t = t2; | |
- for (x = drw->col * 2; x > 0; x--) { | |
- val = timeserie_get(ts, t); | |
- if (!isnan(val)) { | |
- y = timeserie_ypos(val, min, max, drw->row*4) - shift; | |
- drawille_dot_hist(drw, x, y, zero); | |
- } | |
- t -= ts->step; | |
- } | |
- | |
- return plot_render(drw, min, max, ts->step, t2); | |
-} | |
- | |
-static char * | |
-plot(struct timeserie *ts, time_t t2, int row, int col) | |
-{ | |
- struct drawille *drw; | |
- size_t len; | |
- char *buf; | |
- | |
- len = 500; | |
- buf = NULL; | |
- drw = NULL; | |
- col -= 8; | |
- | |
- if (timeserie_read(ts) == -1) | |
- goto err; | |
- | |
- if ((drw = drawille_new(row, col)) == NULL) | |
- goto err; | |
- | |
- buf = plot_hist(ts, t2, drw); | |
-err: | |
- if (buf == NULL) | |
- timedb_close(&ts->db); | |
- free(drw); | |
- return buf; | |
-} |