#include <u.h>#include <libc.h>#include <draw.h>#include <bio.h>#include <thread.h>#include <mouse.h>#include <keyboard.h> enum { STACK = 8*1024, Dot = 2, /* height of dot */ Lx = 4, /* x offset */ Ly = 4, /* y offset */ Bw = 2, /* border width */}; Image *neutral;Image *light;Image *dark;Image *txtcolor; char *title = "histogram";Rectangle hrect;Point maxvloc;double *data;double vmax = 100, scale = 1.0;uint nval;int dontdie = 0, col = 1; int colors[][3] = { { 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF }, /* Peach */ { DPalebluegreen, DPalegreygreen, DPurpleblue }, /* Aqua */ { DPaleyellow, DDarkyellow, DYellowgreen }, /* Yellow */ { DPalegreen, DMedgreen, DDarkgreen }, /* Green */ { 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF }, /* Blue */ { 0xEEEEEEFF, 0xCCCCCCFF, 0x888888F }, /* Grey */}; voidinitcolor(int i){ neutral = allocimagemix(display, colors[i][0], DWhite); light = allocimage(display, Rect(0,0,1,1), CMAP8, 1, colors[i][1]); dark = allocimage(display, Rect(0,0,1,1), CMAP8, 1, colors[i][2]); txtcolor = display->black;} void*erealloc(void *v, ulong sz){ v = realloc(v, sz); if(v == nil){ sysfatal("realloc: %r"); threadexitsall("memory"); } return v;} Pointdatapoint(int x, double v){ Point p; double y; p.x = x; y = (v*scale) / vmax; p.y = hrect.max.y - Dy(hrect)*y - Dot; if(p.y < hrect.min.y) p.y = hrect.min.y; if(p.y > hrect.max.y - Dot) p.y = hrect.max.y - Dot; return p;} voiddrawdatum(int x, double prev, double v){ Point p, q; p = datapoint(x, v); q = datapoint(x, prev); if(p.y < q.y){ draw(screen, Rect(p.x, hrect.min.y, p.x+1, p.y), neutral, nil, ZP); draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), dark, nil, ZP); draw(screen, Rect(p.x, q.y+Dot, p.x+1, hrect.max.y), light, nil, ZP); }else{ draw(screen, Rect(p.x, hrect.min.y, p.x+1, q.y), neutral, nil, ZP); draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), dark, nil, ZP); draw(screen, Rect(p.x, p.y+Dot, p.x+1, hrect.max.y), light, nil, ZP); } } voidupdatehistogram(double v){ char buf[32]; draw(screen, hrect, screen, nil, Pt(hrect.min.x+1, hrect.min.y)); if(v * scale > vmax) v = vmax / scale; drawdatum(hrect.max.x-1, data[0], v); memmove(&data[1], &data[0], (nval-1) * sizeof data[0]); data[0] = v; snprint(buf, sizeof buf, "%0.9f", v); stringbg(screen, maxvloc, txtcolor, ZP, display->defaultfont, buf, neutral, ZP); flushimage(display, 1);} voidredrawhistogram(int new){ Point p, q; Rectangle r; uint onval = nval; int i; char buf[32]; if(new && getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); r = screen->r; draw(screen, r, neutral, nil, ZP); p = string(screen, addpt(r.min, Pt(Lx, Ly)), txtcolor, ZP, display->defaultfont, title); p.x = r.min.x + Lx; p.y += display->defaultfont->height + Ly; q = subpt(r.max, Pt(Lx, Ly)); hrect = Rpt(p, q); maxvloc = Pt(r.max.x - Lx - stringwidth(display->defaultfont, "999999999"), r.min.y + Ly); nval = abs(Dx(hrect)); if(nval != onval){ data = erealloc(data, nval * sizeof data[0]); if(nval > onval) memset(data+onval, 0, (nval - onval) * sizeof data[0]); } border(screen, hrect, -Bw, dark, ZP); snprint(buf, sizeof buf, "%0.9f", data[0]); stringbg(screen, maxvloc, txtcolor, ZP, display->defaultfont, buf, neutral, ZP); draw(screen, hrect, neutral, nil, ZP); for(i = 1; i < nval - 1; i++) drawdatum(hrect.max.x - i, data[i-1], data[i]); drawdatum(hrect.min.x, data[i], data[i]); flushimage(display, 1);} voidreader(void *arg){ int fd; double v; char *p, *f[2]; uchar buf[512]; Biobufhdr b; Channel *c = arg; threadsetname("reader"); fd = dup(0, -1); Binits(&b, fd, OREAD, buf, sizeof buf); while((p = Brdline(&b, '\n')) != nil) { p[Blinelen(&b) - 1] = '\0'; if(tokenize(p, f, 1) != 1) continue; v = strtod(f[0], 0); send(c, &v); } if(!dontdie) threadexitsall(nil);} voidhistogram(char *rect){ int rm; double dm; Channel *dc; Keyboardctl *kc; Mouse mm; Mousectl *mc; Rune km; Alt a[] = { /* c v op */ {nil, &dm, CHANRCV}, /* data from stdin */ {nil, &mm, CHANRCV}, /* mouse message */ {nil, &km, CHANRCV}, /* keyboard runes */ {nil, &rm, CHANRCV}, /* resize event */ {nil, nil, CHANEND}, }; static char *mitems[] = { "exit", nil }; static Menu menu = { mitems, nil, -1 }; memset(&mm, 0, sizeof mm); memset(&km, 0, sizeof km); dm = rm = 0; if(newwindow(rect) < 0) sysfatal("newwindow: %r"); if(initdraw(nil, nil, "histogram") < 0) sysfatal("initdraw: %r"); initcolor(col); mc = initmouse(nil, screen); if(!mc) sysfatal("initmouse: %r"); kc = initkeyboard(nil); if(!kc) sysfatal("initkeyboard: %r"); dc = chancreate(sizeof dm, 10); if(!dc) sysfatal("chancreate: %r"); a[0].c = dc; a[1].c = mc->c; a[2].c = kc->c; a[3].c = mc->resizec; proccreate(reader, a[0].c, STACK + sizeof(Biobuf)); redrawhistogram(0); for(;;) switch(alt(a)){ case 0: updatehistogram(dm); break; case 1: if(mm.buttons & 4 && menuhit(3, mc, &menu, nil) == 0) goto done; break; case 2: if(km == 0x7F) goto done; break; case 3: redrawhistogram(1); break; default: sysfatal("shouldn't happen"); }done: closekeyboard(kc); closemouse(mc); chanfree(a[0].c); threadexitsall(nil);} voidusage(void){ fprint(2, "usage: histogram [-h] [-c index] [-r minx,miny,maxx,maxy] " "[-s scale] [-t title] [-v maxv]\n"); exits("usage");} voidthreadmain(int argc, char **argv){ char *p, *q; p = "-r 0,0,400,150"; ARGBEGIN{ case 'v': vmax = strtod(EARGF(usage()), 0); break; case 'r': p = smprint("-r %s", EARGF(usage())); break; case 's': scale = strtod(EARGF(usage()), 0); if(scale <= 0) usage(); break; case 'h': dontdie = 1; break; case 't': title = EARGF(usage()); break; case 'c': col = atoi(EARGF(usage())) % nelem(colors); break; default: usage(); }ARGEND; while((q = strchr(p, ',')) != nil) *q = ' '; histogram(p);}
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <bio.h>
#include <thread.h>
#include <mouse.h>
#include <keyboard.h>
enum {
STACK = 8*1024,
Dot = 2, /* height of dot */
Lx = 4, /* x offset */
Ly = 4, /* y offset */
Bw = 2, /* border width */
};
Image *neutral;
Image *light;
Image *dark;
Image *txtcolor;
char *title = "histogram";
Rectangle hrect;
Point maxvloc;
double *data;
double vmax = 100, scale = 1.0;
uint nval;
int dontdie = 0, col = 1;
int colors[][3] = {
{ 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF }, /* Peach */
{ DPalebluegreen, DPalegreygreen, DPurpleblue }, /* Aqua */
{ DPaleyellow, DDarkyellow, DYellowgreen }, /* Yellow */
{ DPalegreen, DMedgreen, DDarkgreen }, /* Green */
{ 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF }, /* Blue */
{ 0xEEEEEEFF, 0xCCCCCCFF, 0x888888F }, /* Grey */
};
void
initcolor(int i)
{
neutral = allocimagemix(display, colors[i][0], DWhite);
light = allocimage(display, Rect(0,0,1,1), CMAP8, 1, colors[i][1]);
dark = allocimage(display, Rect(0,0,1,1), CMAP8, 1, colors[i][2]);
txtcolor = display->black;
}
void*
erealloc(void *v, ulong sz)
{
v = realloc(v, sz);
if(v == nil){
sysfatal("realloc: %r");
threadexitsall("memory");
}
return v;
}
Point
datapoint(int x, double v)
{
Point p;
double y;
p.x = x;
y = (v*scale) / vmax;
p.y = hrect.max.y - Dy(hrect)*y - Dot;
if(p.y < hrect.min.y)
p.y = hrect.min.y;
if(p.y > hrect.max.y - Dot)
p.y = hrect.max.y - Dot;
return p;
}
void
drawdatum(int x, double prev, double v)
{
Point p, q;
p = datapoint(x, v);
q = datapoint(x, prev);
if(p.y < q.y){
draw(screen, Rect(p.x, hrect.min.y, p.x+1, p.y), neutral,
nil, ZP);
draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), dark, nil, ZP);
draw(screen, Rect(p.x, q.y+Dot, p.x+1, hrect.max.y), light,
nil, ZP);
}else{
draw(screen, Rect(p.x, hrect.min.y, p.x+1, q.y), neutral,
nil, ZP);
draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), dark, nil, ZP);
draw(screen, Rect(p.x, p.y+Dot, p.x+1, hrect.max.y), light,
nil, ZP);
}
}
void
updatehistogram(double v)
{
char buf[32];
draw(screen, hrect, screen, nil, Pt(hrect.min.x+1, hrect.min.y));
if(v * scale > vmax)
v = vmax / scale;
drawdatum(hrect.max.x-1, data[0], v);
memmove(&data[1], &data[0], (nval-1) * sizeof data[0]);
data[0] = v;
snprint(buf, sizeof buf, "%0.9f", v);
stringbg(screen, maxvloc, txtcolor, ZP, display->defaultfont, buf,
neutral, ZP);
flushimage(display, 1);
}
void
redrawhistogram(int new)
{
Point p, q;
Rectangle r;
uint onval = nval;
int i;
char buf[32];
if(new && getwindow(display, Refnone) < 0)
sysfatal("getwindow: %r");
r = screen->r;
draw(screen, r, neutral, nil, ZP);
p = string(screen, addpt(r.min, Pt(Lx, Ly)), txtcolor, ZP,
display->defaultfont, title);
p.x = r.min.x + Lx;
p.y += display->defaultfont->height + Ly;
q = subpt(r.max, Pt(Lx, Ly));
hrect = Rpt(p, q);
maxvloc = Pt(r.max.x - Lx - stringwidth(display->defaultfont,
"999999999"), r.min.y + Ly);
nval = abs(Dx(hrect));
if(nval != onval){
data = erealloc(data, nval * sizeof data[0]);
if(nval > onval)
memset(data+onval, 0, (nval - onval) * sizeof data[0]);
}
border(screen, hrect, -Bw, dark, ZP);
snprint(buf, sizeof buf, "%0.9f", data[0]);
stringbg(screen, maxvloc, txtcolor, ZP, display->defaultfont, buf,
neutral, ZP);
draw(screen, hrect, neutral, nil, ZP);
for(i = 1; i < nval - 1; i++)
drawdatum(hrect.max.x - i, data[i-1], data[i]);
drawdatum(hrect.min.x, data[i], data[i]);
flushimage(display, 1);
}
void
reader(void *arg)
{
int fd;
double v;
char *p, *f[2];
uchar buf[512];
Biobufhdr b;
Channel *c = arg;
threadsetname("reader");
fd = dup(0, -1);
Binits(&b, fd, OREAD, buf, sizeof buf);
while((p = Brdline(&b, '\n')) != nil) {
p[Blinelen(&b) - 1] = '\0';
if(tokenize(p, f, 1) != 1)
continue;
v = strtod(f[0], 0);
send(c, &v);
}
if(!dontdie)
threadexitsall(nil);
}
void
histogram(char *rect)
{
int rm;
double dm;
Channel *dc;
Keyboardctl *kc;
Mouse mm;
Mousectl *mc;
Rune km;
Alt a[] = {
/* c v op */
{nil, &dm, CHANRCV}, /* data from stdin */
{nil, &mm, CHANRCV}, /* mouse message */
{nil, &km, CHANRCV}, /* keyboard runes */
{nil, &rm, CHANRCV}, /* resize event */
{nil, nil, CHANEND},
};
static char *mitems[] = {
"exit",
nil
};
static Menu menu = {
mitems,
nil,
-1
};
memset(&mm, 0, sizeof mm);
memset(&km, 0, sizeof km);
dm = rm = 0;
if(newwindow(rect) < 0)
sysfatal("newwindow: %r");
if(initdraw(nil, nil, "histogram") < 0)
sysfatal("initdraw: %r");
initcolor(col);
mc = initmouse(nil, screen);
if(!mc)
sysfatal("initmouse: %r");
kc = initkeyboard(nil);
if(!kc)
sysfatal("initkeyboard: %r");
dc = chancreate(sizeof dm, 10);
if(!dc)
sysfatal("chancreate: %r");
a[0].c = dc;
a[1].c = mc->c;
a[2].c = kc->c;
a[3].c = mc->resizec;
proccreate(reader, a[0].c, STACK + sizeof(Biobuf));
redrawhistogram(0);
for(;;)
switch(alt(a)){
case 0:
updatehistogram(dm);
break;
case 1:
if(mm.buttons & 4 && menuhit(3, mc, &menu, nil) == 0)
goto done;
break;
case 2:
if(km == 0x7F)
goto done;
break;
case 3:
redrawhistogram(1);
break;
default:
sysfatal("shouldn't happen");
}
done:
closekeyboard(kc);
closemouse(mc);
chanfree(a[0].c);
threadexitsall(nil);
}
void
usage(void)
{
fprint(2, "usage: histogram [-h] [-c index] [-r minx,miny,maxx,maxy] "
"[-s scale] [-t title] [-v maxv]\n");
exits("usage");
}
void
threadmain(int argc, char **argv)
{
char *p, *q;
p = "-r 0,0,400,150";
ARGBEGIN{
case 'v':
vmax = strtod(EARGF(usage()), 0);
break;
case 'r':
p = smprint("-r %s", EARGF(usage()));
break;
case 's':
scale = strtod(EARGF(usage()), 0);
if(scale <= 0)
usage();
break;
case 'h':
dontdie = 1;
break;
case 't':
title = EARGF(usage());
break;
case 'c':
col = atoi(EARGF(usage())) % nelem(colors);
break;
default:
usage();
}ARGEND;
while((q = strchr(p, ',')) != nil)
*q = ' ';
histogram(p);
}