1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <bio.h>
5 #include <thread.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8
9 enum {
10 STACK = 8*1024,
11
12 Dot = 2, /* height of dot */
13 Lx = 4, /* x offset */
14 Ly = 4, /* y offset */
15 Bw = 2, /* border width */
16 };
17
18 Image *neutral;
19 Image *light;
20 Image *dark;
21 Image *txtcolor;
22
23 char *title = "histogram";
24 Rectangle hrect;
25 Point maxvloc;
26 double *data;
27 double vmax = 100, scale = 1.0;
28 uint nval;
29 int dontdie = 0, col = 1;
30
31 int colors[][3] = {
32 { 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF }, /* Peach */
33 { DPalebluegreen, DPalegreygreen, DPurpleblue }, /* Aqua */
34 { DPaleyellow, DDarkyellow, DYellowgreen }, /* Yellow */
35 { DPalegreen, DMedgreen, DDarkgreen }, /* Green */
36 { 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF }, /* Blue */
37 { 0xEEEEEEFF, 0xCCCCCCFF, 0x888888F }, /* Grey */
38 };
39
40 void
initcolor(int i)41 initcolor(int i)
42 {
43 neutral = allocimagemix(display, colors[i][0], DWhite);
44 light = allocimage(display, Rect(0,0,1,1), CMAP8, 1, colors[i][1]);
45 dark = allocimage(display, Rect(0,0,1,1), CMAP8, 1, colors[i][2]);
46 txtcolor = display->black;
47 }
48
49 void*
erealloc(void * v,ulong sz)50 erealloc(void *v, ulong sz)
51 {
52 v = realloc(v, sz);
53 if(v == nil){
54 sysfatal("realloc: %r");
55 threadexitsall("memory");
56 }
57 return v;
58 }
59
60 Point
datapoint(int x,double v)61 datapoint(int x, double v)
62 {
63 Point p;
64 double y;
65
66 p.x = x;
67 y = (v*scale) / vmax;
68 p.y = hrect.max.y - Dy(hrect)*y - Dot;
69 if(p.y < hrect.min.y)
70 p.y = hrect.min.y;
71 if(p.y > hrect.max.y - Dot)
72 p.y = hrect.max.y - Dot;
73 return p;
74 }
75
76 void
drawdatum(int x,double prev,double v)77 drawdatum(int x, double prev, double v)
78 {
79 Point p, q;
80
81 p = datapoint(x, v);
82 q = datapoint(x, prev);
83 if(p.y < q.y){
84 draw(screen, Rect(p.x, hrect.min.y, p.x+1, p.y), neutral,
85 nil, ZP);
86 draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), dark, nil, ZP);
87 draw(screen, Rect(p.x, q.y+Dot, p.x+1, hrect.max.y), light,
88 nil, ZP);
89 }else{
90 draw(screen, Rect(p.x, hrect.min.y, p.x+1, q.y), neutral,
91 nil, ZP);
92 draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), dark, nil, ZP);
93 draw(screen, Rect(p.x, p.y+Dot, p.x+1, hrect.max.y), light,
94 nil, ZP);
95 }
96
97 }
98
99 void
updatehistogram(double v)100 updatehistogram(double v)
101 {
102 char buf[32];
103
104 draw(screen, hrect, screen, nil, Pt(hrect.min.x+1, hrect.min.y));
105 if(v * scale > vmax)
106 v = vmax / scale;
107 drawdatum(hrect.max.x-1, data[0], v);
108 memmove(&data[1], &data[0], (nval-1) * sizeof data[0]);
109 data[0] = v;
110 snprint(buf, sizeof buf, "%0.9f", v);
111 stringbg(screen, maxvloc, txtcolor, ZP, display->defaultfont, buf,
112 neutral, ZP);
113 flushimage(display, 1);
114 }
115
116 void
redrawhistogram(int new)117 redrawhistogram(int new)
118 {
119 Point p, q;
120 Rectangle r;
121 uint onval = nval;
122 int i;
123 char buf[32];
124
125 if(new && getwindow(display, Refnone) < 0)
126 sysfatal("getwindow: %r");
127
128 r = screen->r;
129 draw(screen, r, neutral, nil, ZP);
130 p = string(screen, addpt(r.min, Pt(Lx, Ly)), txtcolor, ZP,
131 display->defaultfont, title);
132
133 p.x = r.min.x + Lx;
134 p.y += display->defaultfont->height + Ly;
135
136 q = subpt(r.max, Pt(Lx, Ly));
137 hrect = Rpt(p, q);
138
139 maxvloc = Pt(r.max.x - Lx - stringwidth(display->defaultfont,
140 "999999999"), r.min.y + Ly);
141
142 nval = abs(Dx(hrect));
143 if(nval != onval){
144 data = erealloc(data, nval * sizeof data[0]);
145 if(nval > onval)
146 memset(data+onval, 0, (nval - onval) * sizeof data[0]);
147 }
148
149 border(screen, hrect, -Bw, dark, ZP);
150 snprint(buf, sizeof buf, "%0.9f", data[0]);
151 stringbg(screen, maxvloc, txtcolor, ZP, display->defaultfont, buf,
152 neutral, ZP);
153 draw(screen, hrect, neutral, nil, ZP);
154 for(i = 1; i < nval - 1; i++)
155 drawdatum(hrect.max.x - i, data[i-1], data[i]);
156 drawdatum(hrect.min.x, data[i], data[i]);
157 flushimage(display, 1);
158 }
159
160 void
reader(void * arg)161 reader(void *arg)
162 {
163 int fd;
164 double v;
165 char *p, *f[2];
166 uchar buf[512];
167 Biobufhdr b;
168 Channel *c = arg;
169
170 threadsetname("reader");
171 fd = dup(0, -1);
172 Binits(&b, fd, OREAD, buf, sizeof buf);
173
174 while((p = Brdline(&b, '\n')) != nil) {
175 p[Blinelen(&b) - 1] = '\0';
176 if(tokenize(p, f, 1) != 1)
177 continue;
178 v = strtod(f[0], 0);
179 send(c, &v);
180 }
181 if(!dontdie)
182 threadexitsall(nil);
183 }
184
185
186 void
histogram(char * rect)187 histogram(char *rect)
188 {
189 int rm;
190 double dm;
191 Channel *dc;
192 Keyboardctl *kc;
193 Mouse mm;
194 Mousectl *mc;
195 Rune km;
196 Alt a[] = {
197 /* c v op */
198 {nil, &dm, CHANRCV}, /* data from stdin */
199 {nil, &mm, CHANRCV}, /* mouse message */
200 {nil, &km, CHANRCV}, /* keyboard runes */
201 {nil, &rm, CHANRCV}, /* resize event */
202 {nil, nil, CHANEND},
203 };
204 static char *mitems[] = {
205 "exit",
206 nil
207 };
208 static Menu menu = {
209 mitems,
210 nil,
211 -1
212 };
213
214 memset(&mm, 0, sizeof mm);
215 memset(&km, 0, sizeof km);
216 dm = rm = 0;
217
218 if(newwindow(rect) < 0)
219 sysfatal("newwindow: %r");
220 if(initdraw(nil, nil, "histogram") < 0)
221 sysfatal("initdraw: %r");
222
223 initcolor(col);
224
225 mc = initmouse(nil, screen);
226 if(!mc)
227 sysfatal("initmouse: %r");
228 kc = initkeyboard(nil);
229 if(!kc)
230 sysfatal("initkeyboard: %r");
231
232 dc = chancreate(sizeof dm, 10);
233 if(!dc)
234 sysfatal("chancreate: %r");
235
236 a[0].c = dc;
237 a[1].c = mc->c;
238 a[2].c = kc->c;
239 a[3].c = mc->resizec;
240
241 proccreate(reader, a[0].c, STACK + sizeof(Biobuf));
242
243 redrawhistogram(0);
244 for(;;)
245 switch(alt(a)){
246 case 0:
247 updatehistogram(dm);
248 break;
249 case 1:
250 if(mm.buttons & 4 && menuhit(3, mc, &menu, nil) == 0)
251 goto done;
252 break;
253 case 2:
254 if(km == 0x7F)
255 goto done;
256 break;
257 case 3:
258 redrawhistogram(1);
259 break;
260 default:
261 sysfatal("shouldn't happen");
262 }
263 done:
264 closekeyboard(kc);
265 closemouse(mc);
266 chanfree(a[0].c);
267 threadexitsall(nil);
268 }
269
270 void
usage(void)271 usage(void)
272 {
273 fprint(2, "usage: histogram [-h] [-c index] [-r minx,miny,maxx,maxy] "
274 "[-s scale] [-t title] [-v maxv]\n");
275 exits("usage");
276 }
277
278 void
threadmain(int argc,char ** argv)279 threadmain(int argc, char **argv)
280 {
281 char *p, *q;
282
283 p = "-r 0,0,400,150";
284
285 ARGBEGIN{
286 case 'v':
287 vmax = strtod(EARGF(usage()), 0);
288 break;
289 case 'r':
290 p = smprint("-r %s", EARGF(usage()));
291 break;
292 case 's':
293 scale = strtod(EARGF(usage()), 0);
294 if(scale <= 0)
295 usage();
296 break;
297 case 'h':
298 dontdie = 1;
299 break;
300 case 't':
301 title = EARGF(usage());
302 break;
303 case 'c':
304 col = atoi(EARGF(usage())) % nelem(colors);
305 break;
306 default:
307 usage();
308 }ARGEND;
309
310 while((q = strchr(p, ',')) != nil)
311 *q = ' ';
312
313 histogram(p);
314 }
315