|
|
ploot-farbfeld.c - ploot - simple plotting tools |
|
|
 |
git clone git://bitreich.org/ploot git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/ploot (git://bitreich.org) |
|
|
 |
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 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 } |
|