shithub: rcfitness

Download patch

ref: 60e255e0c98828271ff67001c749ad879dd1b859
parent: 2d161e298d7e0605fac08e574f2a03fbd49e58e0
author: rodri <[email protected]>
date: Thu Dec 19 11:43:43 EST 2024

chrono: implement a 7-segment display renderer.

--- a/chrono.c
+++ b/chrono.c
@@ -4,8 +4,10 @@
 #include <draw.h>
 #include <mouse.h>
 #include <keyboard.h>
+#include <geometry.h>
 
 #define HZ2MS(hz)	(1000/(hz))
+#define max(a, b)	((a) > (b)? (a): (b))
 
 enum {
 	Stop,
@@ -17,7 +19,7 @@
 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 @@
 	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 @@
 
 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<<i) == 0)
+			continue;
+
+		for(j = 0; j < segs[loc[i].segtype].npts-1; j++){
+			segpt[j] = addpt(segs[loc[i].segtype].pts[j], loc[i].p);
+			segpt[j] = addpt(segpt[j], bbox.min);
+		}
+		segpt[segs[loc[i].segtype].npts-1] = segpt[0];
+
+		fillpoly(dst, segpt, segs[loc[i].segtype].npts, 0, fg, ZP);
+	}
+
+	return Pt(bbox.max.x + 1, bbox.min.y);
+}
+
+static Point
+string7(Image *dst, Point dp, char *s, int scale, Image *fg, Image *bg)
+{
+	static struct {
+		char c;
+		uchar bits;
+	} dmap[] = {
+		'0', 0x3F,
+		'1', 0x06,
+		'2', 0x5B,
+		'3', 0x4F,
+		'4', 0x66,
+		'5', 0x6D,
+		'6', 0x7D,
+		'7', 0x07,
+		'8', 0x7F,
+		'9', 0x6F,
+	};
+	uchar bits;
+	int i;
+
+	if(s == nil)
+		return dp;
+
+	for(; *s != 0; s++)
+		for(i = 0; i < nelem(dmap); i++)
+			if(dmap[i].c == *s){
+				bits = dmap[i].bits;
+				if(s[1] == '.')
+					bits |= 0x80;
+				dp = d7(dst, dp, bits, scale, fg, bg);
+				break;
+			}
+	return dp;
+}
+
 static void
 stopwatch_start(Stopwatch *self)
 {
@@ -73,20 +237,20 @@
 	t *= 60;		HMS[2] = t;	t -= HMS[2];
 	t *= 1000;		HMS[3] = t;
 
-	for(i = 0; i < nelem(HMS); i++)
-		snprint(self->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 @@
 	s->update = stopwatch_update;
 	s->draw = stopwatch_draw;
 
+	s->update(s, 0);
 	proccreate(timer, s, mainstacksize);
 
 	return s;
@@ -148,9 +313,7 @@
 	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
@@ -157,7 +320,7 @@
 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 @@
 }
 
 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 @@
 {
 	Rune r;
 
+	GEOMfmtinstall();
 	ARGBEGIN{
 	default: usage();
 	}ARGEND;
@@ -221,6 +390,9 @@
 		sysfatal("initmouse: %r");
 	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);