ploot-braille.c - ploot - simple plotting tools HTML 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 }