Introduction
Introduction Statistics Contact Development Disclaimer Help
auto-scale - ploot - simple plotting tools
git clone git://bitreich.org/ploot git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65…
Log
Files
Refs
Tags
README
LICENSE
---
commit c3911021718dc5e5b2dd6469495e7f3bc4befdd2
parent d97badd46c19a6903a589c41b8b87d044f48f8dc
Author: Josuah Demangeon <[email protected]>
Date: Wed, 2 May 2018 06:37:05 +0200
auto-scale
Diffstat:
M Makefile | 2 +-
M ffplot.c | 66 +++++++++++++++++++++++++++++…
D main.c | 191 -----------------------------…
M ploot.c | 364 +++++++++++------------------…
M ploot.h | 3 +--
5 files changed, 186 insertions(+), 440 deletions(-)
---
diff --git a/Makefile b/Makefile
@@ -1,7 +1,7 @@
CFLAGS = -Wall -Wextra -Werror -std=c89 -pedantic -D_POSIX_C_SOURCE=200…
LDFLAGS = -static
-SRC = main.c ffplot.c ffdraw.c font_14x7.c
+SRC = ploot.c ffplot.c ffdraw.c font_14x7.c
OBJ = $(SRC:.c=.o)
LIB = -lm
diff --git a/ffplot.c b/ffplot.c
@@ -18,6 +18,7 @@
#include "font_14x7.h"
#define ABS(x) ((x) < 0 ? -(x) : (x))
+#define LEN(x) (sizeof(x) / sizeof(*x))
#define MARGIN 4
@@ -42,7 +43,7 @@
#define PLOT_X (YLABEL_H)
#define PLOT_Y (XLABEL_W)
#define PLOT_W 700
-#define PLOT_H 200
+#define PLOT_H 160
#define LEGEND_X (YLABEL_H)
#define LEGEND_Y (IMAGE_W - LEGEND_W)
@@ -198,9 +199,60 @@ legend(Canvas *can, Color *label_fg, Vlist *v, int n)
}
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)
+find_scales(Vlist *v, int n,
+ double *vmin, double *vmax, double *vstep,
+ time_t *tmin, time_t *tmax, time_t *tstep)
+{
+ double dv, *vs, vscale[] = { 5, 2, 1 };
+ time_t dt, *ts, tscale[] = {
+ 3600*24*30, 3600*24*5, 3600*24*2, 3600*24, 3600*18, 3600*10,
+ 3600*5, 3600*2, 3600, 60*30, 60*20, 60*10, 60*5, 60*2, 60, 30,
+ 20, 10, 5, 2, 1
+ };
+ int i;
+
+ *vmin = *vmax = *tmin = *tmax = 0;
+
+ 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];
+ }
+ }
+
+ dv = *vmax - *vmin;
+ dt = *tmax - *tmin;
+
+ for (ts = tscale; ts < tscale + LEN(tscale); ts++) {
+ if (dt > *ts * 5) {
+ *tstep = *ts;
+ break;
+ }
+ }
+
+ for (i = 1; i != 0; i *= 10) {
+ for (vs = vscale; vs < vscale + LEN(vscale); vs++) {
+ if (dv > *vs * i * 1) {
+ *vstep = *vs * i * 10;
+ i = 0;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Plot the 'n' values list of the 'v' array with title 'name' and
+ * 'units' label.
+ */
+void
+ffplot(Vlist *v, int n, char *name, char *units)
{
Canvas can = { IMAGE_W, IMAGE_H, buffer, 0, 0 };
Color plot_bg = { 0x2222, 0x2222, 0x2222, 0xffff };
@@ -208,6 +260,12 @@ ffdraw(char *name, char *units, Vlist *v, int n,
Color grid_fg = { 0x3737, 0x3737, 0x3737, 0xffff };
Color label_fg = { 0x8888, 0x8888, 0x8888, 0xffff };
Color title_fg = { 0xdddd, 0xdddd, 0xdddd, 0xffff };
+ double vmin, vmax, vstep = 30;
+ time_t tmin, tmax, tstep = 30;
+
+ find_scales(v, n, &vmin, &vmax, &vstep, &tmin, &tmax, &tstep);
+
+ fprintf(stderr, "%f %f %lld %lld\n", vmin, vmax, tmin, tmax);
can.x = 0;
can.y = 0;
diff --git a/main.c b/main.c
@@ -1,191 +0,0 @@
-#include <time.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <string.h>
-#include <ctype.h>
-
-#include "arg.h"
-#include "ploot.h"
-#include "config.h" /* after ploot.h for type definitions */
-
-#define LEN(x) (sizeof(x) / sizeof(*x))
-
-char *argv0;
-char *tflag = "";
-char *uflag = "";
-
-static int
-color(Color *col, char *name)
-{
- ColorList *c;
-
- for (c = colorlist; c->name != NULL; c++) {
- if (strcmp(name, c->name) == 0) {
- memcpy(col, &c->col, sizeof(*col));
- return 0;
- }
- }
-
- return -1;
-}
-
-void
-estriplf(char *line)
-{
- char *lf;
-
- if ((lf = strchr(line, '\n')) == NULL || lf[1] != '\0')
- fputs("invalid input\n", stderr), exit(1);
- *lf = '\0';
-}
-
-static void
-read_labels(Vlist *v, char **argv, char *buf)
-{
- if (fgets(buf, LINE_MAX, stdin) == NULL) {
- if (ferror(stdin))
- perror("fread from stdin");
- else
- fputs("missing label line\n", stderr);
- exit(1);
- }
- estriplf(buf);
-
- if (strcmp(strsep(&buf, ","), "epoch") != 0)
- fputs("first label must be \"epoch\"\n", stderr), exit(1);
-
- for (; *argv != NULL; v++, argv++)
- if ((v->label = strsep(&buf, ",")) == NULL)
- fputs("more arguments than columns\n", stderr), exit(1…
- else if (color(&v->col, *argv) == -1)
- fprintf(stderr, "unknown color: %s\n", *argv), exit(1);
-
- if (strsep(&buf, ",") != NULL)
- fputs("more columns than arguments\n", stderr), exit(1);
-}
-
-double
-eatof(char *str)
-{
- char *s;
-
- for (s = str; *s != '\0'; s++)
- if (!isdigit(*s) && *s != '.')
- fputs("invalid floatrformat", stderr), exit(0);
- return atof(str);
-}
-
-long
-eatol(char *str)
-{
- char *s;
-
- for (s = str; *s != '\0'; s++)
- if (!isdigit(*s))
- fputs("invalid number format", stderr), exit(0);
- return atol(str);
-}
-
-void
-add_val(Vlist *v, int *bufsiz, int nval, double field, time_t epoch)
-{
- if (nval >= *bufsiz) {
- *bufsiz = *bufsiz * 2 + 1;
- if ((v->v = realloc(v->v, *bufsiz * sizeof(*v->v))) == NULL)
- perror("reallocating values buffer"), exit(1);
- if ((v->t = realloc(v->t, *bufsiz * sizeof(*v->t))) == NULL)
- perror("reallocating values buffer"), exit(1);
- }
- v->v[nval] = field;
- v->t[nval] = epoch;
- v->n = nval + 1;
-}
-
-/*
- * Add to each column the value on the current row.
- */
-void
-add_row(Vlist *v, int *bufsiz, int ncol, int nval, char *line)
-{
- time_t epoch;
- int n;
- char *field;
-
- if ((field = strsep(&line, ",")) == NULL)
- fprintf(stderr, "%d: missing epoch\n", nval), exit(0);
-
- epoch = eatol(field);
- for (n = 0; (field = strsep(&line, ",")) != NULL; n++, v++) {
- if (n > ncol)
- fprintf(stderr, "%d: too many fields\n", nval), exit(0…
- add_val(v, bufsiz, nval, eatof(field), epoch);
- }
- if (n < ncol)
- fprintf(stderr, "%d: too few fields\n", nval), exit(0);
-}
-
-/*
- * < ncol >
- * epoch,a1,b1,c1 ^
- * epoch,a2,b2,c2 nval
- * epoch,a3,b3,c3 v
- */
-void
-read_values(Vlist *v, int ncol)
-{
- int nval, bufsiz;
- char line[LINE_MAX];
-
- bufsiz = 0;
- for (nval = 0; fgets(line, sizeof(line), stdin); nval++) {
- estriplf(line);
- add_row(v, &bufsiz, ncol, nval, line);
- }
-}
-
-static void
-usage(void)
-{
- ColorList *c;
-
- fprintf(stderr, "usage: %s [-t title] [-u unit] color...\n"
- "available colors as defined by \"config.h\":\n", argv0);
- for (c = colorlist; c->name != NULL; c++)
- fprintf(stderr, "- %s\n", c->name);
- exit(1);
-}
-
-int
-main(int argc, char **argv)
-{
- Vlist *v;
- double vmin, vmax, vstep;
- time_t tmin, tmax, tstep;
- char labels[LINE_MAX];
-
- 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;
-
- read_labels(v, argv, labels);
- read_values(v, argc);
-
- ffdraw(tflag, uflag, v, argc,
- vmin, vmax, vstep,
- tmin, tmax, tstep);
-
- return 0;
-}
diff --git a/ploot.c b/ploot.c
@@ -1,304 +1,184 @@
-#include <sys/time.h>
-
-#include <stdio.h>
+#include <time.h>
#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <limits.h>
#include <string.h>
-#include <unistd.h>
-#include <time.h>
+#include <ctype.h>
#include "arg.h"
-#include "config.h"
+#include "ploot.h"
+#include "config.h" /* after ploot.h for type definitions */
-#define ABS(x) ((x) < 0 ? -(x) : (x))
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
-#define MAX(x, y) ((x) > (y) ? (x) : (y))
-#define LEN(buf) (sizeof(buf) / sizeof(*(buf)))
+#define LEN(x) (sizeof(x) / sizeof(*x))
char *argv0;
+char *tflag = "";
+char *uflag = "";
-/*
-** Add 'val' at the current position 'pos' of the 'ring' buffer and set pos to
-** the next postion.
-*/
-#define RING_ADD(rbuf, len, pos, val) \
-do { \
- rbuf[pos] = val; \
- pos = (pos + 1 < len) ? (pos + 1) : (0); \
-} while (0)
-
-/*
-** Copy the ring buffer 'rbuf' content with current position 'pos' into the
-** buffer 'buf'. Both buffer of length 'len'.
-*/
-#define RING_COPY(buf, rbuf, len, pos) \
-do { \
- memcpy(buf, rbuf + pos, (len - pos) * sizeof(*rbuf)); \
- memcpy(buf + (len - pos), rbuf, pos * sizeof(*rbuf)); \
-} while (0)
-
-#define MAX_VAL 80
-#define MARGIN 7
-
-int hflag = 20;
-char *tflag = NULL;
-time_t oflag = 0;
-
-/*
-** Set 'str' to a human-readable form of 'num' with always a width of 7 (+ 1
-** the '\0' terminator). Buffer overflow is ensured not to happen due to the
-** max size of a double.
-*/
-void
-humanize(char *str, double val)
+static int
+color(Color *col, char *name)
{
- int exp, precision;
- char *label = "\0kMGTE";
+ ColorList *c;
- for (exp = 0; ABS(val) > 1000; exp++)
- val /= 1000;
+ for (c = colorlist; c->name != NULL; c++) {
+ if (strcmp(name, c->name) == 0) {
+ memcpy(col, &c->col, sizeof(*col));
+ return 0;
+ }
+ }
- precision = (ABS(val) < 10) ? (3) : (ABS(val) < 100) ? (2) : (1);
- if (exp == 0)
- precision++;
- snprintf(str, 8, "%+.*f%c", precision, val, label[exp]);
- str[7] = '\0';
- if (val >= 0)
- str[0] = ' ';
+ return -1;
}
-/*
-** Returns the maximal double of values between 'beg' and 'end'.
-*/
-double
-maxdv(double *beg, double *end)
+static void
+estriplf(char *line)
{
- double *val, max;
+ char *lf;
- max = *beg;
- for (val = beg; val < end; val++) {
- if (*val > max)
- max = *val;
- }
- return max;
+ if ((lf = strchr(line, '\n')) == NULL || lf[1] != '\0')
+ fputs("invalid input\n", stderr), exit(1);
+ *lf = '\0';
}
-/*
-** If not null, print the title 'str' centered on width.
-*/
-void
-title(char *str, int width)
+static void
+read_labels(Vlist *v, char **argv, char *buf)
{
- if (str == NULL)
- return;
- printf("%*s\n", (int)(width + strlen(str)) / 2 + MARGIN + 3, str);
-}
-
-/*
-** Print vertical axis with humanized number from time to time, with occurences
-** determined after the position on the vertical axis from the bottom 'pos'.
-*/
-void
-vaxis(double val, int pos)
-{
- char label[10];
-
- if (pos % 4 == 0) {
- humanize(label, val);
- printf("%*s -", MARGIN, label);
- } else {
- printf("%*c ", MARGIN, ' ');
+ if (fgets(buf, LINE_MAX, stdin) == NULL) {
+ if (ferror(stdin))
+ perror("fread from stdin");
+ else
+ fputs("missing label line\n", stderr);
+ exit(1);
}
-}
+ estriplf(buf);
-/*
-** Print horizontal axis for up to 'col' values along with dates if reading ti…
-** series.
-*/
-void
-haxis(double *beg, double *end, time_t time)
-{
- double *tp;
- char buf[9], dbeg[11], dend[11];
+ if (strcmp(strsep(&buf, ","), "epoch") != 0)
+ fputs("first label must be \"epoch\"\n", stderr), exit(1);
- printf("%*d -+", MARGIN, 0);
- for (tp = beg; tp < end; tp++)
- putchar((*tp < 0) ? ('x') : ('-'));
- putchar('\n');
- if (oflag > 0) {
- printf("%*c", MARGIN - 1, ' ');
- strftime(dbeg, sizeof(dbeg), "%Y/%m/%d", localtime(&time));
- for (tp = beg; tp < end; tp += 7) {
- strftime(buf, sizeof(buf), " %H:%M", localtime(&time)…
- fputs(buf, stdout);
- time += oflag * 7;
- }
- strftime(dend, sizeof(dend), "%Y/%m/%d", localtime(&time));
- printf("\n %-*s %s\n", (int)(beg - end) + 4, dbeg, dend);
- }
+ for (; *argv != NULL; v++, argv++)
+ if ((v->label = strsep(&buf, ",")) == NULL)
+ fputs("more arguments than columns\n", stderr), exit(1…
+ else if (color(&v->col, *argv) == -1)
+ fprintf(stderr, "unknown color: %s\n", *argv), exit(1);
+
+ if (strsep(&buf, ",") != NULL)
+ fputs("more columns than arguments\n", stderr), exit(1);
}
-/*
-** Print two rows of a plot into a single line using ' ', '.' and ':'.
-*/
-void
-line(double *beg, double *end, double top, double bot)
+static double
+eatof(char *str)
{
- double *val;
+ char *s;
- putchar('|');
- for (val = beg; val != end; val++)
- putchar((*val < bot) ? ' ' : (*val < top) ? '.' : ':');
- putchar('\n');
+ for (s = str; *s != '\0'; s++)
+ if (!isdigit(*s) && *s != '-' && *s != '.')
+ fputs("invalid floatrformat", stderr), exit(0);
+ return atof(str);
}
-/*
-** Plot values between 'beg' and 'end' in a plot of height 'height'.
-** If 'str' is not NULL, it is set as a title above the graph.
-*/
-void
-plot(double *beg, double *end, int height, char *str, time_t start)
+static long
+eatol(char *str)
{
- double top, bot, max;
- int h;
+ char *s;
- putchar('\n');
-
- max = maxdv(beg, end);
- for (h = height + height % 2; h > 0; h -= 2) {
- top = h * max / height;
- bot = (h - 1) * max / height;
-
- vaxis(top, h);
- line(beg, end, top, bot);
- }
-
- haxis(beg, end, start);
-
- if (str != NULL)
- title(str, end - beg);
-
- putchar('\n');
+ for (s = str; *s != '\0'; s++)
+ if (!isdigit(*s) && *s != '-')
+ fputs("invalid number format", stderr), exit(0);
+ return atol(str);
}
-/*
-** Read a simple format with one double per line and save the last 'MAX_WIDTH'
-** values into 'buf' which must be at least MAX_VAL wide and return a pointer
-** to the last element or NULL if the input contains error.
-*/
-double *
-read_simple(double buf[MAX_VAL])
+static void
+add_val(Vlist *v, int *bufsiz, int nval, double field, time_t epoch)
{
- double rbuf[MAX_VAL], val;
- size_t p, pos, len;
-
- len = LEN(rbuf);
- for (p = pos = 0; scanf("%lf\n", &val) > 0; p++)
- RING_ADD(rbuf, len, pos, val);
- len = MIN(len, p);
-
- RING_COPY(buf, rbuf, len, pos);
-
- return buf + len;
+ if (nval >= *bufsiz) {
+ *bufsiz = *bufsiz * 2 + 1;
+ if ((v->v = realloc(v->v, *bufsiz * sizeof(*v->v))) == NULL)
+ perror("reallocating values buffer"), exit(1);
+ if ((v->t = realloc(v->t, *bufsiz * sizeof(*v->t))) == NULL)
+ perror("reallocating values buffer"), exit(1);
+ }
+ v->v[nval] = field;
+ v->t[nval] = epoch;
+ v->n = nval + 1;
}
/*
-** Read a format with blank separated time_t-double pairs, one per line and sa…
-** the last 'MAX_WIDTH' values into 'tbuf' and 'vbuf' which must both be at
-** least MAX_VAL wide and return a pointer to the last element of 'vbuf' or
-** NULL if the input contains error.
-*/
-time_t *
-read_time_series(double *vbuf, time_t *tbuf)
+ * Add to each column the value on the current row.
+ */
+static void
+add_row(Vlist *v, int *bufsiz, int ncol, int nval, char *line)
{
- size_t p, pos, nul, len;
- double vrbuf[MAX_VAL], vval, dval;
- time_t trbuf[MAX_VAL], tval;
-
- len = LEN(vrbuf);
- for (p = pos = 0; scanf("%lf %lf\n", &dval, &vval) > 0; p++) {
- tval = (time_t)dval;
- RING_ADD(trbuf, len, pos, tval);
- RING_ADD(vrbuf, len, nul, vval);
+ time_t epoch;
+ int n;
+ char *field;
+
+ if ((field = strsep(&line, ",")) == NULL)
+ fprintf(stderr, "%d: missing epoch\n", nval), exit(0);
+
+ epoch = eatol(field);
+ for (n = 0; (field = strsep(&line, ",")) != NULL; n++, v++) {
+ if (n > ncol)
+ fprintf(stderr, "%d: too many fields\n", nval), exit(0…
+ add_val(v, bufsiz, nval, eatof(field), epoch);
}
- len = MIN(len, p);
-
- RING_COPY(tbuf, trbuf, len, pos);
- RING_COPY(vbuf, vrbuf, len, pos);
-
- return tbuf + len;
+ if (n < ncol)
+ fprintf(stderr, "%d: too few fields\n", nval), exit(0);
}
/*
-** Walk from 'tbeg' and 'tend' and add offset in 'tbuf' every time there is no
-** value in 'step' amount of time, by setting a value to -1.
-*/
-double *
-skip_gaps(time_t *tbeg, time_t *tend, double *vbuf, time_t step)
+ * < ncol >
+ * epoch,a1,b1,c1 ^
+ * epoch,a2,b2,c2 nval
+ * epoch,a3,b3,c3 v
+ */
+static void
+read_values(Vlist *v, int ncol)
{
- size_t p, pos, len;
- time_t *tp, toff;
- double *vp, vrbuf[MAX_VAL];
-
- /* Compute the average alignment of the timestamps values according to
- ** the step size. */
- toff = 0;
- for (tp = tbeg; tp < tend; tp++)
- toff += *tp % step;
- toff = *tbeg + toff / (tend - tbeg) + step / 2;
+ int nval, bufsiz;
+ char line[LINE_MAX];
- /* Fill 'vbuf' with gap added at each time gap using vrbuf as
- ** intermediate ring buffer. */
- len = LEN(vrbuf);
- for (p = pos = 0, tp = tbeg, vp = vbuf; tp < tend; p++, vp++, tp++) {
- for (; toff < *tp; toff += step)
- RING_ADD(vrbuf, len, pos, -1);
- RING_ADD(vrbuf, len, pos, *vp);
- toff += step;
+ bufsiz = 0;
+ for (nval = 0; fgets(line, sizeof(line), stdin); nval++) {
+ estriplf(line);
+ add_row(v, &bufsiz, ncol, nval, line);
}
- len = MAX(MIN(p, len), pos);
-
- RING_COPY(vbuf, vrbuf, len, pos);
-
- return vbuf + len;
}
-void
+static void
usage(void)
{
- printf("usage: ploot [-h <height>] [-o <offset>] [-t <title>]\n");
+ ColorList *c;
+
+ fprintf(stderr, "usage: %s [-t title] [-u unit] color...\n"
+ "available colors as defined by \"config.h\":\n", argv0);
+ for (c = colorlist; c->name != NULL; c++)
+ fprintf(stderr, "- %s\n", c->name);
exit(1);
}
int
main(int argc, char **argv)
{
- time_t tbuf[MAX_VAL], *tend, start;
- double vbuf[MAX_VAL], *vend;
+ Vlist *v;
+ char labels[LINE_MAX];
- ARGBEGIN(argc, argv) {
- case 'h':
- if ((hflag = atoi(EARGF(usage()))) <= 0)
- usage();
- break;
+ ARGBEGIN {
case 't':
tflag = EARGF(usage());
break;
- case 'o':
- oflag = atol(EARGF(usage()));
+ case 'u':
+ uflag = EARGF(usage());
break;
- default:
- usage();
- } ARGEND
+ } ARGEND;
- if (oflag == 0) {
- vend = read_simple(vbuf);
- start = 0;
- } else {
- tend = read_time_series(vbuf, tbuf);
- vend = skip_gaps(tbuf, tend, vbuf, oflag);
- start = *tbuf;
- }
+ if ((v = calloc(argc, sizeof(*v))) == NULL)
+ perror("calloc value list"), exit(1);
+
+ read_labels(v, argv, labels);
+ read_values(v, argc);
+
+ ffplot(v, argc, tflag, uflag);
- plot(vbuf, vend, hflag, tflag, start);
return 0;
}
diff --git a/ploot.h b/ploot.h
@@ -45,8 +45,7 @@ void ffdraw_fill (Canvas *, Color *);
void ffdraw_print (Canvas *);
/* ffplot.c */
-void ffdraw (char *, char *, Vlist *, int, doub…
- double, time_t, time_t, time_t);
+void ffplot (Vlist *, int, char *, char *);
/* util.c */
char *strsep (char **, const char *);
You are viewing proxied material from bitreich.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.