ploot-farbfeld.c - ploot - simple plotting tools | |
git clone git://bitreich.org/ploot git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65… | |
Log | |
Files | |
Refs | |
Tags | |
README | |
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 int… | |
83 * width * y + y. | |
84 * The coordinates are shifted by offx and offy to permit relative coord… | |
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, sizeo… | |
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,… | |
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) + x… | |
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… | |
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 fo… | |
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 fon… | |
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 … | |
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 … | |
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, … | |
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, s… | |
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) + MARGI… | |
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)) == … | |
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 } |