ploot-farbfeld.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-farbfeld.c (10488B) --- 1 #include <arpa/inet.h> 2 #include <ctype.h> 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <limits.h> 6 #include <math.h> 7 #include <stddef.h> 8 #include <stdint.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <time.h> 13 #include <unistd.h> 14 #include "tsv.h" 15 #include "font.h" 16 #include "util.h" 17 18 #ifndef __OpenBSD__ 19 #define pledge(...) 0 20 #endif 21 22 #define MARGIN 8 23 24 #define IMAGE_H (TITLE_H + PLOT_H + XLABEL_H) 25 #define IMAGE_W (MARGIN + YLABEL_W + PLOT_W + MARGIN) 26 27 #define TITLE_X (MARGIN) 28 #define TITLE_Y (IMAGE_H - TITLE_H / 2) 29 #define TITLE_H ((font)->height * 2) 30 #define TITLE_W (PLOT_W) 31 32 #define YLABEL_X (MARGIN) 33 #define YLABEL_Y (PLOT_Y) 34 #define YLABEL_H (PLOT_H) 35 #define YLABEL_W (40 + MARGIN) 36 37 #define XLABEL_X (PLOT_X) 38 #define XLABEL_Y (0) 39 #define XLABEL_H ((font)->height * 2) 40 #define XLABEL_W (PLOT_W) 41 42 #define PLOT_X (YLABEL_X + YLABEL_W) 43 #define PLOT_Y (XLABEL_H) 44 #define PLOT_W (700) 45 #define PLOT_H (160) 46 47 #define LEGEND_X (IMAGE_W / 2) 48 #define LEGEND_Y (TITLE_Y) 49 #define LEGEND_H (PLOT_H) 50 51 struct ffcolor { 52 uint16_t red; 53 uint16_t green; 54 uint16_t blue; 55 uint16_t alpha; 56 }; 57 58 struct ffplot { 59 int w, h, x, y; /* width, height and coordinamtes */ 60 struct ffcolor *buf; 61 }; 62 63 static struct colorname { 64 char *name; 65 struct ffcolor color; 66 } colorname[] = { 67 /* name red green blue alpha */ 68 { "red", { 0xffff, 0x4444, 0x4444, 0xffff } }, 69 { "orange", { 0xffff, 0x9999, 0x4444, 0xffff } }, 70 { "yellow", { 0xffff, 0xffff, 0x4444, 0xffff } }, 71 { "green", { 0x2222, 0xffff, 0x5555, 0xffff } }, 72 { "cyan", { 0x0000, 0xffff, 0xdddd, 0xffff } }, 73 { "blue", { 0x2222, 0x9999, 0xffff, 0xffff } }, 74 { NULL, { 0, 0, 0, 0 } } 75 }; 76 77 static char *flag_title = ""; 78 static struct font *font = &font13; 79 80 /* 81 * Convert (x,y) coordinates to (row,col) for printing into the buffer. 82 * The buffer only contain one number, so the coordinate is a single integer: 83 * width * y + y. 84 * The coordinates are shifted by offx and offy to permit relative coordinates. 85 * 86 * The convention used: y 87 * - (0,0) is at the lower left corner of the plotvas. | 88 * - (0,1) is above it. +--x 89 */ 90 static void 91 ffplot_pixel(struct ffplot *plot, struct ffcolor *color, 92 int x, int y) 93 { 94 x += plot->x; 95 y += plot->y; 96 if (x < 0 || x >= plot->w || y < 0 || y >= plot->h) 97 return; 98 memcpy(plot->buf + plot->w * (plot->h - 1 - y) + x, color, sizeof(*plot->buf)); 99 } 100 101 static void 102 ffplot_rectangle(struct ffplot *plot, struct ffcolor *color, 103 int y1, int x1, 104 int y2, int x2) 105 { 106 int x, y, ymin, xmin, ymax, xmax; 107 108 ymin = MIN(y1, y2); ymax = MAX(y1, y2); 109 xmin = MIN(x1, x2); xmax = MAX(x1, x2); 110 111 for (y = ymin; y <= ymax; y++) 112 for (x = xmin; x <= xmax; x++) 113 ffplot_pixel(plot, color, x, y); 114 } 115 116 /* 117 * From Bresenham's line algorithm and dcat's tplot. 118 */ 119 static void 120 ffplot_line(struct ffplot *plot, struct ffcolor *color, 121 int x0, int y0, 122 int x1, int y1) 123 { 124 int dy, dx, sy, sx, err, e; 125 126 sx = x0 < x1 ? 1 : -1; 127 sy = y0 < y1 ? 1 : -1; 128 dx = ABS(x1 - x0); 129 dy = ABS(y1 - y0); 130 err = (dy > dx ? dy : -dx) / 2; 131 132 for (;;) { 133 ffplot_pixel(plot, color, x0, y0); 134 135 if (y0 == y1 && x0 == x1) 136 break; 137 138 e = err; 139 if (e > -dy) { 140 y0 += sy; 141 err -= dx; 142 } 143 if (e < dx) { 144 x0 += sx; 145 err += dy; 146 } 147 } 148 } 149 150 /* 151 * Draw a coloured glyph from font f centered on y. 152 */ 153 static int 154 ffplot_char(struct ffplot *plot, struct ffcolor *color, struct font *ft, char c, 155 int x, int y) 156 { 157 int yf, xf, wf; 158 159 if (c & 0x80) 160 c = '\0'; 161 y -= ft->height / 2; 162 wf = font_width(ft, c); 163 for (xf = 0; xf < wf; xf++) 164 for (yf = 0; yf < ft->height; yf++) 165 if (ft->glyph[(int)c][wf * (ft->height - yf) + xf] == 3) 166 ffplot_pixel(plot, color, x + xf, y + yf); 167 return wf + 1; 168 } 169 170 /* 171 * Draw a left aligned string without wrapping it. 172 */ 173 static size_t 174 ffplot_text_left(struct ffplot *plot, struct ffcolor *color, struct font *ft, 175 char *s, int x, int y) 176 { 177 for (; *s != '\0'; s++) 178 x += ffplot_char(plot, color, ft, *s, x, y); 179 return x; 180 } 181 182 /* 183 * Draw a center aligned string without wrapping it. 184 */ 185 static size_t 186 ffplot_text_center(struct ffplot *plot, struct ffcolor *color, struct font *ft, 187 char *s, int x, int y) 188 { 189 x -= font_strlen(ft, s) / 2; 190 return ffplot_text_left(plot, color, ft, s, x, y); 191 } 192 193 /* 194 * Draw a right aligned string without wrapping it. 195 */ 196 static size_t 197 ffplot_text_right(struct ffplot *plot, struct ffcolor *color, struct font *ft, 198 char *s, int x, int y) 199 { 200 x -= font_strlen(ft, s); 201 return ffplot_text_left(plot, color, ft, s, x, y); 202 } 203 204 static void 205 ffplot_print(FILE *fp, struct ffplot *plot) 206 { 207 uint32_t w, h; 208 209 w = htonl(plot->w); 210 h = htonl(plot->h); 211 212 fprintf(stdout, "farbfeld"); 213 fwrite(&w, sizeof(w), 1, fp); 214 fwrite(&h, sizeof(h), 1, fp); 215 fwrite(plot->buf, plot->w * plot->h, sizeof(*plot->buf), fp); 216 } 217 218 static int 219 ffplot_t2x(time_t t, time_t tmin, time_t tmax) 220 { 221 if (tmin == tmax) 222 return PLOT_W; 223 return (t - tmin) * PLOT_W / (tmax - tmin); 224 } 225 226 static int 227 ffplot_v2y(double v, double vmin, double vmax) 228 { 229 if (vmin == vmax) 230 return PLOT_H; 231 return (v - vmin) * PLOT_H / (vmax - vmin); 232 } 233 234 static void 235 ffplot_xaxis(struct ffplot *plot, struct ffcolor *label, struct ffcolor *grid, 236 time_t tmin, time_t tmax, time_t tstep) 237 { 238 time_t t; 239 int x; 240 char str[sizeof("MM/DD HH/MM")], *fmt; 241 242 if (tstep < 3600 * 12) 243 fmt = "%H:%M:%S"; 244 else if (tstep < 3600 * 24) 245 fmt = "%m/%d %H:%M"; 246 else 247 fmt = "%X/%m/%d"; 248 249 for (t = tmax - tmax % tstep; t >= tmin; t -= tstep) { 250 x = ffplot_t2x(t, tmin, tmax); 251 252 ffplot_line(plot, grid, 253 x, XLABEL_H, 254 x, XLABEL_H + PLOT_H); 255 256 strftime(str, sizeof(str), fmt, localtime(&t)); 257 ffplot_text_center(plot, label, font, str, 258 x, XLABEL_H / 2); 259 } 260 } 261 262 static void 263 ffplot_yaxis(struct ffplot *plot, struct ffcolor *label, struct ffcolor *grid, 264 double vmin, double vmax, double vstep) 265 { 266 double v; 267 int y; 268 char str[8 + 1]; 269 270 for (v = vmax - fmod(vmax, vstep); v >= vmin; v -= vstep) { 271 y = ffplot_v2y(v, vmin, vmax); 272 273 ffplot_line(plot, grid, 274 YLABEL_W, y, 275 YLABEL_W + PLOT_W, y); 276 277 humanize(str, v); 278 ffplot_text_right(plot, label, font, str, 279 YLABEL_W - MARGIN, y); 280 } 281 } 282 283 static void 284 ffplot_title(struct ffplot *plot, struct ffcolor *ct, char *title) 285 { 286 ffplot_text_left(plot, ct, font, title, TITLE_H / 2, 0); 287 } 288 289 static void 290 ffplot_plot(struct ffplot *plot, struct tsv *vl, struct ffcolor *color, 291 double vmin, double vmax, 292 time_t tmin, time_t tmax) 293 { 294 time_t *tp; 295 double *vp; 296 int x, y, n, ylast, xlast, first; 297 298 first = 1; 299 for (tp = vl->t, vp = vl->v, n = vl->n; n > 0; n--, vp++, tp++) { 300 y = ffplot_v2y(*vp, vmin, vmax); 301 x = ffplot_t2x(*tp, tmin, tmax); 302 303 if (!first) 304 ffplot_line(plot, color, xlast, ylast, x, y); 305 306 ylast = y; 307 xlast = x; 308 first = 0; 309 } 310 } 311 312 static void 313 ffplot_values(struct ffplot *plot, struct tsv *vl, struct ffcolor **cl, size_t ncol, 314 time_t tmin, time_t tmax, 315 double vmin, double vmax) 316 { 317 for (; ncol > 0; ncol--, vl++, cl++) 318 ffplot_plot(plot, vl, *cl, vmin, vmax, tmin, tmax); 319 } 320 321 static void 322 ffplot_legend(struct ffplot *plot, struct ffcolor *fg, struct tsv *vl, struct ffcolor **cl, size_t ncol) 323 { 324 size_t x, y; 325 326 x = y = 0; 327 for (; ncol > 0; ncol--, vl++, cl++) { 328 x = ffplot_text_left(plot, *cl, font, "-", x, y) + MARGIN; 329 x = ffplot_text_left(plot, fg, font, vl->label, x, y); 330 x = ffplot_text_left(plot, fg, font, " ", x, y); 331 } 332 } 333 334 /* 335 * Plot the 'n' values list of the 'v' arrax with title 'name' label. 336 * 337 * Title Legend 338 * x ^ 339 * label | - + - + - + - + - 340 * here | - + - + - + - + - 341 * +---+---+---+---+--> 342 * x label here 343 */ 344 static void 345 plot(struct tsv *vl, struct ffcolor **cl, size_t ncol, char *name) 346 { 347 struct ffplot plot = { IMAGE_W, IMAGE_H, 0, 0, NULL }; 348 struct ffcolor plot_bg = { 0x2222, 0x2222, 0x2222, 0xffff }; 349 struct ffcolor grid_bg = { 0x2929, 0x2929, 0x2929, 0xffff }; 350 struct ffcolor grid_fg = { 0x3737, 0x3737, 0x3737, 0xffff }; 351 struct ffcolor label_fg = { 0x8888, 0x8888, 0x8888, 0xffff }; 352 struct ffcolor title_fg = { 0xdddd, 0xdddd, 0xdddd, 0xffff }; 353 double vmin, vmax, vstep; 354 time_t tmin, tmax, tstep; 355 356 tsv_min_max(vl, ncol, &tmin, &tmax, &vmin, &vmax); 357 tstep = scale_time_t(tmin, tmax, 7); 358 vstep = scale_double(vmin, vmax, 7); 359 360 if ((plot.buf = calloc(IMAGE_H * IMAGE_W, sizeof *plot.buf)) == NULL) 361 err(1, "calloc: %s", strerror(errno)); 362 363 plot.y = 0; 364 plot.x = 0; 365 ffplot_rectangle(&plot, &plot_bg, 0, 0, IMAGE_H - 1, IMAGE_W - 1); 366 367 plot.x = PLOT_X; 368 plot.y = PLOT_Y; 369 ffplot_rectangle(&plot, &grid_bg, 0, 0, PLOT_H, PLOT_W); 370 371 plot.x = XLABEL_X; 372 plot.y = XLABEL_Y; 373 ffplot_xaxis(&plot, &label_fg, &grid_fg, tmin, tmax, tstep); 374 375 plot.x = YLABEL_X; 376 plot.y = YLABEL_Y; 377 ffplot_yaxis(&plot, &label_fg, &grid_fg, vmin, vmax, vstep); 378 379 plot.x = TITLE_X; 380 plot.y = TITLE_Y; 381 ffplot_title(&plot, &title_fg, name); 382 383 plot.x = PLOT_X; 384 plot.y = PLOT_Y; 385 ffplot_values(&plot, vl, cl, ncol, tmin, tmax, vmin, vmax); 386 387 plot.x = LEGEND_X; 388 plot.y = LEGEND_Y; 389 ffplot_legend(&plot, &label_fg, vl, cl, ncol); 390 391 ffplot_print(stdout, &plot); 392 } 393 394 static struct ffcolor * 395 name_to_color(char *name) 396 { 397 struct colorname *cn; 398 399 for (cn = colorname; cn->name != NULL; cn++) 400 if (strcmp(name, cn->name) == 0) 401 return &cn->color; 402 return NULL; 403 } 404 405 static void 406 argv_to_color(struct ffcolor **cl, char **argv) 407 { 408 for (; *argv != NULL; cl++, argv++) 409 if ((*cl = name_to_color(*argv)) == NULL) 410 err(1, "unknown color name: %s", *argv); 411 } 412 413 static void 414 usage(void) 415 { 416 fprintf(stderr, "usage: %s [-t title] {", arg0); 417 fputs(colorname->name, stderr); 418 for (struct colorname *cn = colorname + 1; cn->name != NULL; cn++) 419 fprintf(stderr, ",%s", cn->name); 420 fputs("}...\n", stderr); 421 exit(1); 422 } 423 424 int 425 main(int argc, char **argv) 426 { 427 struct tsv *vl; 428 struct ffcolor **cl; 429 size_t ncol; 430 int c; 431 432 if (pledge("stdio", "") < 0) 433 err(1, "pledge: %s", strerror(errno)); 434 435 arg0 = *argv; 436 while ((c = getopt(argc, argv, "t:")) > -1) { 437 switch (c) { 438 case 't': 439 flag_title = optarg; 440 break; 441 default: 442 usage(); 443 } 444 } 445 argc -= optind; 446 argv += optind; 447 448 if (argc == 0) 449 usage(); 450 451 if ((cl = calloc(argc, sizeof *cl)) == NULL) 452 err(1, "calloc: %s", strerror(errno)); 453 454 tsv_labels(stdin, &vl, &ncol); 455 if (ncol > (size_t)argc) 456 err(1, "too many columns or not enough arguments"); 457 else if (ncol < (size_t)argc) 458 err(1, "too many arguments or not enough columns"); 459 tsv_values(stdin, vl, ncol); 460 argv_to_color(cl, argv); 461 462 plot(vl, cl, argc, flag_title); 463 464 free(vl); 465 free(cl); 466 return 0; 467 }