Introduction
Introduction Statistics Contact Development Disclaimer Help
tploot-ff.c - ploot - simple plotting tools
git clone git://bitreich.org/ploot git://hg6vgqziawt5s4dj.onion/ploot
Log
Files
Refs
Tags
README
---
tploot-ff.c (13227B)
---
1 #include <arpa/inet.h>
2
3 #include <math.h>
4 #include <stdint.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <time.h>
9 #include <time.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include <limits.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <time.h>
17 #include <stdint.h>
18
19 #include "arg.h"
20 #include "util.h"
21 #include "font.h"
22
23 #define MARGIN 4
24
25 #define XDENSITY 7 /* nb of values on x axis */
26 #define YDENSITY 7 /* nb of values on y axis */
27
28 #define TITLE_X (IMAGE_H - TITLE_H)
29 #define TITLE_Y (XLABEL_W)
30 #define TITLE_H (FONT_H * 2)
31 #define TITLE_W (PLOT_W)
32
33 #define XLABEL_X (PLOT_X)
34 #define XLABEL_Y (0)
35 #define XLABEL_H (PLOT_H)
36 #define XLABEL_W (FONT_W * 9 + MARGIN)
37
38 #define YLABEL_X (0)
39 #define YLABEL_Y (PLOT_Y)
40 #define YLABEL_H (FONT_H * 2)
41 #define YLABEL_W (PLOT_W)
42
43 #define PLOT_X (YLABEL_H)
44 #define PLOT_Y (XLABEL_W)
45 #define PLOT_W 700
46 #define PLOT_H 160
47
48 #define LEGEND_X (YLABEL_H)
49 #define LEGEND_Y (IMAGE_W - LEGEND_W)
50 #define LEGEND_W (FONT_W + 150 + FONT_W)
51 #define LEGEND_H (PLOT_H)
52
53 #define IMAGE_H (TITLE_H + PLOT_H + YLABEL_H)
54 #define IMAGE_W (XLABEL_W + PLOT_W + LEGEND_W)
55
56 typedef uint16_t Color[4];
57 typedef struct clist Clist;
58 typedef struct vlist Vlist;
59 typedef struct canvas Canvas;
60 typedef struct font Font;
61
62 struct vlist {
63 Color col; /* color to use to draw the line */
64 time_t *t; /* array of timestamps */
65 double *v; /* array of values */
66 int n; /* number of values */
67 char *label; /* for the legend */
68 };
69
70 struct canvas {
71 int w; /* width */
72 int h; /* height */
73 int x; /* x offset */
74 int y; /* x offset */
75 Color b[IMAGE_W * IMAGE_H];
76 };
77
78 struct font {
79 int w; /* width */
80 int h; /* height */
81 char **b; /* buffer */
82 };
83
84 struct clist {
85 char *name;
86 Color col;
87 };
88
89 char *argv0;
90 char *tflag = "";
91 char *uflag = "";
92
93 Clist clist[] = {
94 /* name red green blue alpha */
95 { "red", { 0xffff, 0x4444, 0x4444, 0xffff } },
96 { "orange", { 0xffff, 0x9999, 0x4444, 0xffff } },
97 { "yellow", { 0xffff, 0xffff, 0x4444, 0xffff } },
98 { "green", { 0x2222, 0xffff, 0x5555, 0xffff } },
99 { "cyan", { 0x0000, 0xffff, 0xdddd, 0xffff } },
100 { "blue", { 0x2222, 0x9999, 0xffff, 0xffff } },
101 { NULL, { 0, 0, 0, 0 } }
102 };
103
104 Font font = { FONT_W, FONT_H, glyph };
105
106 static int
107 color(Color *col, char *name)
108 {
109 Clist *c;
110
111 for (c = clist; c->name != NULL; c++) {
112 if (strcmp(name, c->name) == 0) {
113 memcpy(col, c->col, sizeof(*col));
114 return 0;
115 }
116 }
117
118 return -1;
119 }
120
121 static void
122 scale_minmax(Vlist *v, int n,
123 double *vmin, double *vmax,
124 time_t *tmin, time_t *tmax)
125 {
126 int i;
127
128 *vmin = *vmax = 0;
129 *tmin = *tmax = *v->t;
130
131 for (; n-- > 0; v++) {
132 for (i = 0; i < v->n; i++) {
133 if (v->v[i] < *vmin)
134 *vmin = v->v[i];
135 if (v->v[i] > *vmax)
136 *vmax = v->v[i];
137 if (v->t[i] < *tmin)
138 *tmin = v->t[i];
139 if (v->t[i] > *tmax)
140 *tmax = v->t[i];
141 }
142 }
143 }
144
145 static void
146 scale_tstep(time_t *step, int density, time_t min, time_t max)
147 {
148 time_t dt, *s, scale[] = {
149 1, 5, 2, 10, 20, 30, 60, 60*2, 60*5, 60*10, 60*20, 60*30…
150 3600*2, 3600*5, 3600*10, 3600*18, 3600*24, 3600*24*2,
151 3600*24*5, 3600*24*10, 3600*24*20, 3600*24*30, 3600*24*5…
152 3600*24*100, 3600*24*365
153 };
154
155 dt = max - min;
156
157 for (s = scale; s < scale + LEN(scale); s++) {
158 if (dt < *s * density) {
159 *step = *s;
160 break;
161 }
162 }
163 }
164
165 static void
166 scale_vstep(double *step, int density, double min, double max)
167 {
168 double dv, *s, scale[] = { 1, 2, 3, 5 };
169 int i;
170
171 dv = max - min;
172
173 if (dv > 1) {
174 for (i = 1; i != 0; i *= 10) {
175 for (s = scale; s < scale + LEN(scale); s++) {
176 if (dv < *s * i * density) {
177 *step = *s * i;
178 return;
179 }
180 }
181 }
182 } else {
183 for (i = 1; i != 0; i *= 10) {
184 for (s = scale + LEN(scale) - 1; s >= scale; s--…
185 if (dv > *s / i * density / 2) {
186 *step = *s / i;
187 return;
188 }
189 }
190 }
191 }
192 }
193
194 static void
195 scale(Vlist *v, int n,
196 double *vmin, double *vmax, double *vstep,
197 time_t *tmin, time_t *tmax, time_t *tstep)
198 {
199 scale_minmax(v, n, vmin, vmax, tmin, tmax);
200 scale_tstep(tstep, YDENSITY, *tmin, *tmax);
201 scale_vstep(vstep, XDENSITY, *vmin, *vmax);
202 }
203
204 /*
205 * Convert (x,y) coordinates to (row,col) for printing into the buffer.
206 * The buffer only contain one number, so the coordinate is a single int…
207 * width * x + y.
208 * The coordinates are shifted by offx and offy to permit relative coord…
209 *
210 * The convention used: y
211 * - (0,0) is at the lower left corner of the canvas. |
212 * - (0,1) is above it. +--x
213 */
214 static void
215 ff_pixel(Canvas *can, Color *col,
216 int x, int y)
217 {
218 x += can->x;
219 y += can->y;
220 if (x < 0 || x >= can->h || y < 0 || y >= can->w)
221 return;
222 memcpy(can->b + can->w * (can->h - 1 - x) + y, col, sizeof(*can-…
223 }
224
225 static void
226 ff_rectangle(Canvas *can, Color *col,
227 int x1, int y1,
228 int x2, int y2)
229 {
230 int x, y, xmin, ymin, xmax, ymax;
231
232 xmin = MIN(x1, x2); xmax = MAX(x1, x2);
233 ymin = MIN(y1, y2); ymax = MAX(y1, y2);
234
235 for (x = xmin; x <= xmax; x++)
236 for (y = ymin; y <= ymax; y++)
237 ff_pixel(can, col, x, y);
238 }
239
240 /*
241 * From Bresenham's line algorithm and dcat's tplot.
242 */
243 static void
244 ff_line(Canvas *can, Color *col,
245 int x0, int y0,
246 int x1, int y1)
247 {
248 int dx, dy, sx, sy, err, e;
249
250 sx = x0 < x1 ? 1 : -1;
251 sy = y0 < y1 ? 1 : -1;
252 dx = abs(x1 - x0);
253 dy = abs(y1 - y0);
254 err = (dx > dy ? dx : -dy) / 2;
255
256 for (;;) {
257 ff_pixel(can, col, x0, y0);
258
259 if (x0 == x1 && y0 == y1)
260 break;
261
262 e = err;
263 if (e > -dx) {
264 x0 += sx;
265 err -= dy;
266 }
267 if (e < dy) {
268 y0 += sy;
269 err += dx;
270 }
271 }
272 }
273
274 /*
275 * Draw a coloured glyph from font f centered on x.
276 */
277 static void
278 ff_char(Canvas *can, Color *col, char c, Font *f,
279 int x, int y)
280 {
281 int xf, yf;
282
283 if (c & 0x80)
284 c = '\0';
285
286
287 x -= f->h / 2;
288
289 for (xf = 0; xf < f->h; xf++)
290 for (yf = 0; yf < f->w; yf++)
291 if (f->b[(int)c][f->w * (f->h - xf) + yf] == 1)
292 ff_pixel(can, col, x + xf, y + yf);
293 }
294
295 /*
296 * Draw a left aligned string without wrapping it.
297 */
298 static void
299 ff_str_left(Canvas *can, Color *col, char *s, Font *f,
300 int x, int y)
301 {
302 for (; *s != '\0'; y += f->w, s++)
303 ff_char(can, col, *s, f, x, y);
304 }
305
306 /*
307 * Draw a center aligned string without wrapping it.
308 */
309 static void
310 ff_str_center(Canvas *can, Color *col, char *s, Font *f,
311 int x, int y)
312 {
313 y -= f->w * strlen(s) / 2;
314 ff_str_left(can, col, s, f, x, y);
315 }
316
317 /*
318 * Draw a right aligned string without wrapping it.
319 */
320 static void
321 ff_str_right(Canvas *can, Color *col, char *s, Font *f,
322 int x, int y)
323 {
324 y -= f->w * strlen(s);
325 ff_str_left(can, col, s, f, x, y);
326 }
327
328 static void
329 ff_print(Canvas *can)
330 {
331 uint32_t w, h;
332
333 w = htonl(can->w);
334 h = htonl(can->h);
335
336 fputs("farbfeld", stdout);
337 fwrite(&w, sizeof(w), 1, stdout);
338 fwrite(&h, sizeof(h), 1, stdout);
339 fwrite(can->b, can->w * can->h, sizeof(*can->b), stdout);
340 }
341
342 static int
343 ff_t2y(time_t t, time_t tmin, time_t tmax)
344 {
345 return (t - tmin) * PLOT_W / (tmax - tmin);
346 }
347
348 static int
349 ff_v2x(double v, double vmin, double vmax)
350 {
351 return (v - vmin) * PLOT_H / (vmax - vmin);
352 }
353
354 static void
355 ff_xaxis(Canvas *can, Color *label, Color *grid,
356 double vmin, double vmax, double vstep)
357 {
358 double v;
359 int x;
360 char str[8 + 1];
361
362 for (v = vmax - fmod(vmax, vstep); v >= vmin; v -= vstep) {
363 x = ff_v2x(v, vmin, vmax);
364
365 ff_line(can, grid,
366 x, XLABEL_W,
367 x, XLABEL_W + PLOT_W);
368
369 humanize(str, v);
370 ff_str_right(can, label, str, &font,
371 x, XLABEL_W - MARGIN);
372 }
373 }
374
375 static void
376 ff_yaxis(Canvas *can, Color *label, Color *grid,
377 time_t tmin, time_t tmax, time_t tstep)
378 {
379 time_t t;
380 int y;
381 char str[sizeof("MM/DD HH/MM")], *fmt;
382
383 if (tstep < 3600 * 12)
384 fmt = "%H:%M:%S";
385 else if (tstep < 3600 * 24)
386 fmt = "%m/%d %H:%M";
387 else
388 fmt = "%Y/%m/%d";
389
390 for (t = tmax - tmax % tstep; t >= tmin; t -= tstep) {
391 y = ff_t2y(t, tmin, tmax);
392
393 ff_line(can, grid,
394 YLABEL_H, y,
395 YLABEL_H + PLOT_H, y);
396
397 strftime(str, sizeof(str), fmt, localtime(&t));
398 ff_str_center(can, label, str, &font,
399 YLABEL_H / 2, y);
400 }
401 }
402
403 static void
404 ff_title(Canvas *can,
405 Color *ct, char *title,
406 Color *cu, char *unit)
407 {
408 ff_str_left(can, ct, title, &font,
409 TITLE_H / 2, 0);
410 ff_str_right(can, cu, unit, &font,
411 TITLE_H / 2, TITLE_W);
412 }
413
414 static void
415 ff_plot(Canvas *can, Vlist *v,
416 double vmin, double vmax,
417 time_t tmin, time_t tmax)
418 {
419 time_t *tp;
420 double *vp;
421 int x, y, n, xlast, ylast, first;
422
423 first = 1;
424 for (tp = v->t, vp = v->v, n = v->n; n > 0; n--, vp++, tp++) {
425 x = ff_v2x(*vp, vmin, vmax);
426 y = ff_t2y(*tp, tmin, tmax);
427
428 if (!first)
429 ff_line(can, &v->col, xlast, ylast, x, y);
430
431 xlast = x;
432 ylast = y;
433 first = 0;
434 }
435 }
436
437 static void
438 ff_values(Canvas *can, Vlist *v, int n,
439 double vmin, double vmax,
440 time_t tmin, time_t tmax)
441 {
442 for (; n > 0; n--, v++)
443 ff_plot(can, v, vmin, vmax, tmin, tmax);
444 }
445
446 static void
447 ff_legend(Canvas *can, Color *label_fg, Vlist *v, int n)
448 {
449 int i, x, y;
450
451 for (i = 0; i < n; i++, v++) {
452 x = LEGEND_H - i * (FONT_H + MARGIN) - FONT_H / 2;
453
454 y = MARGIN + FONT_W;
455 ff_str_left(can, &v->col, "\1", &font, x, y);
456
457 y += FONT_W * 2;
458 ff_str_left(can, label_fg, v->label, &font, x, y);
459 }
460 }
461
462 /*
463 * Plot the 'n' values list of the 'v' array with title 'name' and
464 * 'units' label.
465 *
466 * Title (units)
467 * y ^ Legend
468 * label |- + - + - + - + - ....
469 * here |- + - + - + - + - ....
470 * +--+---+---+---+-->
471 * x label here
472 */
473 static void
474 ff(Vlist *v, int n, char *name, char *units)
475 {
476 Canvas can = { IMAGE_W, IMAGE_H, 0, 0, { { 0 }, { 0 } } };
477 Color plot_bg = { 0x2222, 0x2222, 0x2222, 0xffff };
478 Color grid_bg = { 0x2929, 0x2929, 0x2929, 0xffff };
479 Color grid_fg = { 0x3737, 0x3737, 0x3737, 0xffff };
480 Color label_fg = { 0x8888, 0x8888, 0x8888, 0xffff };
481 Color title_fg = { 0xdddd, 0xdddd, 0xdddd, 0xffff };
482 double vmin, vmax, vstep;
483 time_t tmin, tmax, tstep;
484
485 scale(v, n, &vmin, &vmax, &vstep, &tmin, &tmax, &tstep);
486
487 can.x = 0;
488 can.y = 0;
489 ff_rectangle(&can, &plot_bg, 0, 0, IMAGE_H - 1, IMAGE_W - 1);
490
491 can.x = PLOT_X;
492 can.y = PLOT_Y;
493 ff_rectangle(&can, &grid_bg, 0, 0, PLOT_H, PLOT_W);
494
495 can.x = YLABEL_X;
496 can.y = YLABEL_Y;
497 ff_yaxis(&can, &label_fg, &grid_fg, tmin, tmax, tstep);
498
499 can.x = XLABEL_X;
500 can.y = XLABEL_Y;
501 ff_xaxis(&can, &label_fg, &grid_fg, vmin, vmax, vstep);
502
503 can.x = TITLE_X;
504 can.y = TITLE_Y;
505 ff_title(&can, &title_fg, name, &label_fg, units);
506
507 can.x = PLOT_X;
508 can.y = PLOT_Y;
509 ff_values(&can, v, n, vmin, vmax, tmin, tmax);
510
511 can.x = LEGEND_X;
512 can.y = LEGEND_Y;
513 ff_legend(&can, &label_fg, v, n);
514
515 ff_print(&can);
516 }
517
518 static void
519 csv_labels(Vlist *v, char **argv, char *buf)
520 {
521 if (esfgets(buf, LINE_MAX, stdin) == NULL)
522 fputs("missing label line\n", stderr), exit(1);
523
524 if (strcmp(strsep(&buf, ","), "epoch") != 0)
525 fputs("first label must be \"epoch\"\n", stderr), exit(1…
526
527 for (; *argv != NULL; v++, argv++) {
528 if ((v->label = strsep(&buf, ",")) == NULL)
529 fputs("more arguments than columns\n", stderr), …
530 else if (color(&v->col, *argv) == -1)
531 fprintf(stderr, "unknown color: %s\n", *argv), e…
532 }
533
534 if (strsep(&buf, ",") != NULL)
535 fputs("more columns than arguments\n", stderr), exit(1);
536 }
537
538 static int
539 csv_addval(Vlist *v, int bufsize, int nval, double field, time_t epoch)
540 {
541 if (nval >= bufsize) {
542 bufsize = bufsize * 2 + 1;
543 if ((v->v = realloc(v->v, bufsize * sizeof(*v->v))) == N…
544 perror("reallocating values buffer"), exit(1);
545 if ((v->t = realloc(v->t, bufsize * sizeof(*v->t))) == N…
546 perror("reallocating values buffer"), exit(1);
547 }
548 v->v[nval] = field;
549 v->t[nval] = epoch;
550 v->n = nval + 1;
551
552 return bufsize;
553 }
554
555 /*
556 * Add to each column the value on the current row.
557 */
558 static int
559 csv_addrow(Vlist *v, int bufsize, int ncol, int nval, char *line)
560 {
561 time_t epoch;
562 int bs;
563 char *field, *dot;
564
565 if ((field = strsep(&line, ",")) == NULL)
566 fprintf(stderr, "%d: missing epoch\n", nval), exit(1);
567
568 if ((dot = strchr(field, '.')) != NULL)
569 *dot = '\0';
570 epoch = eatol(field);
571 for (; (field = strsep(&line, ",")) != NULL; ncol--, v++) {
572 if (ncol <= 0)
573 fprintf(stderr, "%d: too many fields\n", nval), …
574 bs = csv_addval(v, bufsize, nval, eatof(field), epoch);
575 }
576 if (ncol > 0)
577 fprintf(stderr, "%d: too few fields\n", nval), exit(1);
578
579 return bs;
580 }
581
582 /*
583 * < ncol >
584 * epoch,a1,b1,c1 ^
585 * epoch,a2,b2,c2 nval
586 * epoch,a3,b3,c3 v
587 */
588 static void
589 csv_values(Vlist *v, int ncol)
590 {
591 int nval, bufsize;
592 char line[LINE_MAX];
593
594 bufsize = 0;
595 for (nval = 0; esfgets(line, sizeof(line), stdin) != NULL; nval+…
596 bufsize = csv_addrow(v, bufsize, ncol, nval, line);
597 if (nval == 0)
598 fputs("no value could be read\n", stderr), exit(1);
599 }
600
601 static void
602 usage(void)
603 {
604 Clist *c;
605
606 fprintf(stderr, "usage: %s [-t title] [-u unit] {", argv0);
607 fputs(clist->name, stderr);
608 for (c = clist + 1; c->name != NULL; c++)
609 fprintf(stderr, ",%s", c->name);
610 fputs("}...\n", stderr);
611 exit(1);
612 }
613
614 int
615 main(int argc, char **argv)
616 {
617 Vlist *v;
618 char labels[LINE_MAX];
619
620 ARGBEGIN {
621 case 't':
622 tflag = EARGF(usage());
623 break;
624 case 'u':
625 uflag = EARGF(usage());
626 break;
627 default:
628 usage();
629 } ARGEND;
630
631 if ((v = calloc(argc, sizeof(*v))) == NULL)
632 perror("calloc value list"), exit(1);
633
634 csv_labels(v, argv, labels);
635 csv_values(v, argc);
636
637 ff(v, argc, tflag, uflag);
638
639 return 0;
640 }
You are viewing proxied material from bitreich.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.