added a flag for color scheme options. Reference: /n/sources/patch/applied/histogram Date: Tue Mar 16 21:13:51 CET 2010 Signed-off-by: 9nut@9netics.com --- /sys/src/cmd/histogram.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/histogram.c Thu Jul 15 00:56:32 2010 @@ -0,0 +1,314 @@ +#include +#include +#include +#include +#include +#include +#include + +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); +} --- /sys/man/8/histogram Thu Jan 1 00:00:00 1970 +++ /sys/man/8/histogram Thu Jul 15 01:00:26 2010 @@ -0,0 +1,90 @@ +.TH HISTOGRAM 8 +.SH NAME +histogram \- draw a histogram +.SH SYNOPSIS +.B histogram +[ +.B -h +] +[ +.B -c +.I index +] +[ +.B -r +.I minx,miny,maxx,maxy +] +[ +.B -s +.I scale +] +[ +.B -t +.I title +] +[ +.B -v +.I maxv +] +.SH DESCRIPTION +.I Histogram +reads numbers, one per line, from its standard input +and draws them as bars in a histogram. +.PP +Use +.B -c +to set the color +.I index +for the graph. +A modulus operation on the value keeps the color index within the available range. +.PP +Unless +.B -h +.RI ( hold ) +is given, +.I histogram +will exit when it reaches the end-of-file. +It will exit immediately if it is interrupted +or if the +.I exit +menu option is chosen. +.PP +.B -r +sets the initial window +.I rectangle +coordinates. +.PP +.B -s +sets the +.I scaling +factor. +.PP +.B -t +sets the +.I title +displayed on a line above the histogram. +The last value read is displayed to the right of the title. +.PP +.B -v +sets the maximum +.I value +that can be expected. +.SH EXAMPLE +Plot a sine wave: +.IP +.EX +hoc -e 'for(i=0.0;i<20*PI;i=i+0.1) print (10+10*sin(i)), "\\n"'| + histogram -t 'sin(t), 0 ≤ t ≤ 20π' -v 20 -h +.EE +.PP +Show the Dow Jones adjusted daily closing price back to January 1, 2000: +.IP +.EX +site=http://ichart.finance.yahoo.com +hget $site'/table.csv?s=^DJI&a=00&b=1&c=2000' | + awk -F, '{print $NF}' | histogram -t DJI -v 15000 -h +.EE +.SH SOURCE +.B /sys/src/cmd/histogram.c +.SH SEE ALSO +.IR statusbar (8)