From 60e255e0c98828271ff67001c749ad879dd1b859 Mon Sep 17 00:00:00 2001 From: rodri Date: Thu, 19 Dec 2024 16:43:43 +0000 Subject: chrono: implement a 7-segment display renderer. --- chrono.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 185 insertions(+), 13 deletions(-) diff --git a/chrono.c b/chrono.c index 26750b4..1a3de69 100644 --- a/chrono.c +++ b/chrono.c @@ -4,8 +4,10 @@ #include #include #include +#include #define HZ2MS(hz) (1000/(hz)) +#define max(a, b) ((a) > (b)? (a): (b)) enum { Stop, @@ -17,7 +19,7 @@ typedef struct Stopwatch Stopwatch; struct Stopwatch { uvlong elapsed; /* in ms */ - char hms[4][4]; + char hms[3][6+1]; /* HH MM SS.ss */ int state; void (*start)(Stopwatch*); @@ -27,7 +29,10 @@ struct Stopwatch void (*draw)(Stopwatch*, Image*, Point, double); }; +Rectangle UR = {0,0,1,1}; char deffont[] = "/lib/font/bit/lucida/unicode.32.font"; +Image *d7bg, *d7fg; +int d7scale = 60; Image *screenb; Keyboardctl *kc; @@ -37,6 +42,165 @@ Stopwatch *chrono; uvlong nanosec(void); +static Image * +eallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong col) +{ + Image *i; + + i = allocimage(d, r, chan, repl, col); + if(i == nil) + sysfatal("allocimage: %r"); + return i; +} + +/* + * 7-segment display interface + * + * A + * ---- + * | | B + * F | G | + * ---- + * | | C + * E | | + * ---- [] H + * D + * + * bits: 7 6 5 4 3 2 1 0 + * func: H G F E D C B A + */ +static Point +d7(Image *dst, Point dp, uchar bits, int scale, Image *fg, Image *bg) +{ + enum { TV, TH, TD, NSEGS }; + struct { + Point2 poly[6]; + Point pts[6+1]; + int npts; + } segs[NSEGS] = { /* segment parameters */ + [TV] { .poly = { + { 1, 0, 1 }, + { 1.5, 1, 1 }, + { 1.5, 5, 1 }, + { 1, 6, 1 }, + { 0.5, 5, 1 }, + { 0.5, 1, 1 }, + }, .npts = 6+1 }, + [TH] { .npts = 6+1 }, + [TD] { .poly = { + { 0, 0, 1 }, + { 1, 0, 1 }, + { 1, 1, 1 }, + { 0, 1, 1 }, + }, .npts = 4+1 }, + }; + struct { + Point p; + int segtype; + } loc[8] = { /* segment locations (layout) */ + { 1, 2, TH }, /* A */ + { 6, 1, TV }, /* B */ + { 6, 7, TV }, /* C */ + { 1, 14, TH }, /* D */ + { 0, 7, TV }, /* E */ + { 0, 1, TV }, /* F */ + { 1, 8, TH }, /* G */ + { 8, 13, TD }, /* H (dot) */ + }; + Rectangle bbox = { + { 0, 0 }, + { 9, 14 }, + }; + Point segpt[7]; + double maxlen; + int i, j; + + maxlen = 0; + for(i = 0; i < segs[TV].npts-1; i++) + maxlen = max(maxlen, vec2len(segs[TV].poly[i])); + + bbox.max.x = (double)bbox.max.x/maxlen * scale; + bbox.max.y = (double)bbox.max.y/maxlen * scale; + for(i = 0; i < nelem(loc); i++){ + loc[i].p.x = (double)loc[i].p.x/maxlen * scale; + loc[i].p.y = (double)loc[i].p.y/maxlen * scale; + } + + /* normalize TV and build TH out of it */ + for(i = 0; i < segs[TV].npts-1; i++){ + segs[TV].poly[i] = divpt2(segs[TV].poly[i], maxlen); + segs[TH].poly[i] = Vec2(segs[TV].poly[i].y, -segs[TV].poly[i].x); + + segs[TV].pts[i] = Pt(segs[TV].poly[i].x*scale, segs[TV].poly[i].y*scale); + segs[TH].pts[i] = Pt(segs[TH].poly[i].x*scale, segs[TH].poly[i].y*scale); + } + segs[TH].pts[segs[TH].npts-1] = segs[TH].pts[0]; + segs[TV].pts[segs[TV].npts-1] = segs[TV].pts[0]; + + /* normalize TD */ + for(i = 0; i < segs[TD].npts-1; i++){ + segs[TD].poly[i] = divpt2(segs[TD].poly[i], maxlen); + segs[TD].pts[i] = Pt(segs[TD].poly[i].x*scale, segs[TD].poly[i].y*scale); + } + segs[TD].pts[segs[TD].npts-1] = segs[TD].pts[0]; + + /* paint case */ + bbox = rectaddpt(bbox, addpt(dst->r.min, dp)); + draw(dst, bbox, bg, nil, ZP); + + /* paint segments */ + for(i = 0; i < nelem(loc); i++){ + if((bits & 1<hms[i], sizeof self->hms[i], i < 3? "%02d": "%03d", HMS[i]); + for(i = 0; i < nelem(HMS)-2; i++) + snprint(self->hms[i], sizeof self->hms[i], "%02d", HMS[i]); + snprint(self->hms[i], sizeof self->hms[i], "%02d.%03d", HMS[i], HMS[i+1]); } static void stopwatch_draw(Stopwatch *self, Image *dst, Point dp, double scale) { - USED(scale); int i; for(i = 0; i < nelem(self->hms); i++){ - if(i > 0) - dp = string(dst, dp, display->white, ZP, font, i < 3? ":": "."); - dp = string(dst, dp, display->white, ZP, font, self->hms[i]); + if(i > 0 && i < 3) + dp.x += scale/3; + dp = string7(dst, dp, self->hms[i], scale, d7fg, d7bg); } } @@ -131,6 +295,7 @@ mkstopwatch(void) s->update = stopwatch_update; s->draw = stopwatch_draw; + s->update(s, 0); proccreate(timer, s, mainstacksize); return s; @@ -148,16 +313,14 @@ initscreenb(void) if(screenb != nil) freeimage(screenb); - screenb = allocimage(display, rectsubpt(screen->r, screen->r.min), screen->chan, 0, DNofill); - if(screenb == nil) - sysfatal("allocimage: %r"); + screenb = eallocimage(display, rectsubpt(screen->r, screen->r.min), screen->chan, 0, DNofill); } void redraw(void) { draw(screenb, screenb->r, display->black, nil, ZP); - chrono->draw(chrono, screenb, Pt(10, 10), 1); + chrono->draw(chrono, screenb, Pt(10, 10), d7scale); draw(screen, screen->r, screenb, nil, ZP); flushimage(display, 1); } @@ -173,9 +336,14 @@ resize(void) } void -mouse(Mousectl *) +mouse(Mousectl *mc) { - + if(mc->buttons & 8) + d7scale += 2; + if(mc->buttons & 16) + d7scale -= 2; + if(mc->buttons != 0) + nbsend(drawc, nil); } void @@ -211,6 +379,7 @@ threadmain(int argc, char *argv[]) { Rune r; + GEOMfmtinstall(); ARGBEGIN{ default: usage(); }ARGEND; @@ -222,6 +391,9 @@ threadmain(int argc, char *argv[]) if((kc = initkeyboard(nil)) == nil) sysfatal("initkeyboard: %r"); + d7bg = eallocimage(display, UR, XRGB32, 1, 0x333333FF); + d7fg = eallocimage(display, UR, XRGB32, 1, DRed); + initscreenb(); drawc = chancreate(sizeof(void*), 1); nbsend(drawc, nil); -- cgit v1.2.3