xref: /plan9/sys/src/cmd/histogram.c (revision 43751f27fa93b002d5e921851faf5da5cbdcb417)
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