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