ploot-braille.c - 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
---
ploot-braille.c (4040B)
---
1 #include <assert.h>
2 #include <errno.h>
3 #include <stdint.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <time.h>
8 #include <math.h>
9 #include <unistd.h>
10 #include "drawille.h"
11 #include "util.h"
12 #include "tsv.h"
13
14 #ifndef __OpenBSD__
15 #define pledge(...) 0
16 #endif
17
18 /*
19 * Plot the body as an histogram interpolating the gaps and include
20 * a vertical and horizontal axis.
21 */
22 static int
23 braille_histogram(struct tsv *vl, struct drawille *drw,
24 time_t tmin, time_t tmax, double vmin, double vmax)
25 {
26 int x, xprev, y, yprev, zero;
27 double *v;
28 time_t *t;
29 size_t n;
30
31 #define SHIFT (4 / 2)
32 #define POSITION(val, min, max, sz) ((sz) * ((val) - (min)) / ((max) - (min)) + SHIFT)
33
34 zero = POSITION(0, vmin, vmax, drw->row*4);
35 v = vl->v;
36 t = vl->t;
37 n = vl->n;
38 for (; n > 0; n--, t++, v++) {
39 if (isnan(*v)) /* XXX: better handling? */
40 continue;
41 y = POSITION(*v, vmin, vmax, drw->row * 4);
42 x = POSITION(*t, tmin, tmax, drw->col * 2);
43 if (n < vl->n) /* only plot when xprev, yprev are set */
44 drawille_histogram_line(drw, xprev, yprev, x, y, zero);
45 xprev = x;
46 yprev = y;
47 }
48
49 #undef POSITION
50
51 return 0;
52 }
53
54 static int
55 braille_axis_x(FILE *fp, time_t tmin, time_t tmax, time_t tstep, int col)
56 {
57 int x, o, prec;
58 char tmp[sizeof("MM/DD HH:MM")], *fmt;
59 size_t n;
60 time_t t;
61
62 fmt =
63 (tstep < 3600 * 12) ? "^%H:%M:%S" :
64 (tstep < 3600 * 24) ? "^%m/%d %H:%M" :
65 "^%Y/%m/%d";
66 n = x = 0;
67
68 t = tmin + tstep - tmin % tstep;
69 for (; t < tmax; t += tstep) {
70 x = (t - tmin) * col / (tmax - tmin);
71 strftime(tmp, sizeof tmp, fmt, localtime(&t));
72 prec = x - n + strlen(tmp);
73 if ((o = fprintf(fp, "%*s", prec, tmp)) < 0)
74 return -1;
75 n += o;
76 }
77 fprintf(fp, "\n");
78 return 0;
79 }
80
81 /*
82 * Plot a single line out of the y axis, at row <r> out of <rows>.
83 */
84 static void
85 braille_axis_y(FILE *fp, double min, double max, int r, int rows)
86 {
87 char buf[10] = "";
88
89 humanize(buf, (rows - 1 - r) * (max - min) / rows);
90 fprintf(fp, "├%s ", buf);
91 }
92
93 static int
94 braille_render(struct drawille *drw, FILE *fp, double min, double max)
95 {
96 int row;
97
98 for (row = 0; row < drw->row; row++) {
99 drawille_put_row(fp, drw, row);
100 braille_axis_y(fp, min, max, row, drw->row);
101 fprintf(fp, "\n");
102 }
103 return 0;
104 }
105
106 static void
107 plot(struct tsv *vl, size_t ncol, int rows, int cols, FILE *fp)
108 {
109 double vmin, vmax, vstep;
110 time_t tmin, tmax, tstep;
111 struct drawille *drw;
112
113 rows = MAX(rows, 2); /* readable */
114
115 if (tsv_min_max(vl, ncol, &tmin, &tmax, &vmin, &vmax) < 0)
116 err(1, "invalid scale: tmin=%lld tmax=%lld vmin=%fd vmax=%fd",
117 (long long)tmin, (long long)tmax, vmin, vmax);
118
119 tstep = scale_time_t(tmin, tmax, cols);
120 vstep = scale_double(vmin, vmax, rows);
121 vmin = (int)(vmin / vstep) * vstep;
122 vmax = vmin + vstep * rows;
123
124 for (; ncol > 0; vl++, ncol--) {
125 if ((drw = drawille_new(rows, cols)) == NULL)
126 err(1, "drawille_new: %s", strerror(errno));
127 fprintf(fp, " %s\n", vl->label);
128 if (braille_histogram(vl, drw, tmin, tmax, vmin, vmax) == -1)
129 err(1, "allocating drawille canvas");
130 if (braille_render(drw, fp, vmin, vmax) == -1)
131 err(1, "rendering braille canvas");
132 free(drw);
133 }
134 if (braille_axis_x(fp, tmin, tmax, tstep * 10, cols) == -1)
135 err(1, "printing x axis");;
136 }
137
138 static void
139 usage(void)
140 {
141 fprintf(stderr, "usage: %s [-r rows] [-c cols]\n", arg0);
142 exit(1);
143 }
144
145 int
146 main(int argc, char **argv)
147 {
148 struct tsv *vl;
149 size_t ncol;
150 int c, rows, cols;
151
152 if (pledge("stdio", "") < 0)
153 err(1, "pledge: %s", strerror(errno));
154
155 rows = 4, cols = 60;
156 arg0 = *argv;
157 while ((c = getopt(argc, argv, "r:c:")) > -1) {
158 switch (c) {
159 case 'r':
160 rows = atoi(optarg);
161 if (rows < 1) {
162 warn("invalid number of rows");
163 usage();
164 }
165 break;
166 case 'c':
167 cols = atoi(optarg);
168 if (rows < 1) {
169 warn("invalid number of columns");
170 usage();
171 }
172 break;
173
174 default:
175 usage();
176 }
177 }
178 argc -= optind;
179 argv += optind;
180
181 if (argc > 0)
182 usage();
183
184 tsv_labels(stdin, &vl, &ncol);
185 tsv_values(stdin, vl, ncol);
186
187 plot(vl, ncol, rows, cols, stdout);
188
189 free(vl);
190 return 0;
191 }