315 lines
5.9 KiB
C
315 lines
5.9 KiB
C
|
#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);
|
||
|
}
|