diff --git a/apps/daisy/app.js b/apps/daisy/app.js index c6101bac9..ae8d76c76 100644 --- a/apps/daisy/app.js +++ b/apps/daisy/app.js @@ -17,16 +17,22 @@ let warned = 0; let idle = false; let IDLE_MINUTES = 26; -let pal1; // palette for 0-49% -let pal2; // palette for 50-100% +var pals = Array(3).fill().map(() => ( + { pal1: null, // palette for 0-49% + pal2: null // palette for 50-100% + })); + +let palbg; const infoLine = (3*h/4) - 6; const infoWidth = 56; const infoHeight = 11; const ringEdge = 4; +const ringIterOffset = 10; const ringThick = 6; let nextUpdateMs; var drawingSteps = false; -var prevRing = {start: null, end: null, max: null}; +var innerMostRing = 0; +var prevRing = Array(3).fill().map(() => ({ start: null, end: null, max: null })); function log_debug(o) { //print(o); @@ -35,12 +41,36 @@ function log_debug(o) { var hrmImg = require("heatshrink").decompress(atob("i0WgIKHgPh8Ef5/g///44CBz///1///5A4PnBQk///wA4PBA4MDA4MH/+Ah/8gEP4EAjw0GA")); // https://www.1001fonts.com/rounded-fonts.html?page=3 +//one ring Graphics.prototype.setFontBloggerSansLight46 = function(scale) { // Actual height 46 (45 - 0) this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAAAAA/AAAAAAAAPwAAAAAAAD4AAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAH/gAAAAAAP/wAAAAAAf/gAAAAAAf/AAAAAAA//AAAAAAB/+AAAAAAD/8AAAAAAH/4AAAAAAH/wAAAAAAP/gAAAAAAf/gAAAAAA//AAAAAAB/+AAAAAAA/8AAAAAAAP4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///8AAAAP////4AAAP/////AAAH/////4AAD+AAAB/AAA8AAAAHwAAeAAAAA+AAHgAAAAHgADwAAAAB4AA8AAAAAPAAPAAAAADwADwAAAAA8AA8AAAAAPAAPAAAAADwAB4AAAAB4AAeAAAAAeAAHwAAAAPgAA/AAAAPwAAH/////4AAA/////8AAAH////+AAAAf///+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAPAAAAAAAAHwAAAAAAAB4AAAAAAAA+AAAAAAAAfAAAAAAAAHgAAAAAAAD4AAAAAAAB8AAAAAAAAeAAAAAAAAPgAAAAAAADwAAAAAAAB//////4AAf//////AAH//////gAA//////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAD4AAHAAAAD+AAD4AAAB/gAA8AAAB/4AAfAAAA/+AAHgAAAf3gAB4AAAPx4AA8AAAH4eAAPAAAD4HgADwAAB8B4AA8AAA+AeAAPAAAfAHgADwAAPgB4AA8AAHwAeAAHgAD4AHgAB4AD8AB4AAfAB+AAeAAD8B/AAHgAAf//gAB4AAH//wAAeAAAf/wAAHgAAB/wAAA4AAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AADgAAAAPAAB4AAAADwAAeAAAAA+AAHgAAAAHgAB4ABgAB4AAeAA8AAeAAHgA/AADwAB4AfwAA8AAeAP8AAPAAHgH/AADwAB4H7wAA8AAeD48AAPAAHh8PAAHgAB5+BwAB4AAe/AeAA+AAH/AHwAfAAB/gA/AfgAAfwAH//wAAHwAA//4AAA4AAH/8AAAAAAAf4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAD+AAAAAAAD/gAAAAAAH/4AAAAAAH/+AAAAAAP/ngAAAAAP/h4AAAAAf/AeAAAAAf/AHgAAAA/+AB4AAAA/+AAeAAAB/8AAHgAAA/8AAB4AAAP4AAAeAAAB4AAAHgAAAAAAAB4AAAAAAAAeAAAAAAP///4AAAAH////AAAAA////gAAAAP///4AAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAD4AA8AAD///gAPAAB///4AD4AAf//+AAeAAH+APAAHgAB4AHgAA4AAeAB4AAOAAHgAcAADwAB4AHAAA8AAeADwAAPAAHgAcAADwAB4AHAAA8AAeAB4AAeAAHgAeAAHgAB4AHwAD4AAeAA+AB8AAHgAP4B+AAB4AB///gAAOAAP//gAABAAA//wAAAAAAD/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/gAAAAAB///4AAAAD////wAAAD////+AAAB/////4AAA/gPgB/AAAfgDwAHwAAPgA8AA+AADwAeAAHgAB4AHgAB4AAeAB4AAfAAHgAeAADwABwAHgAA8AAcAB4AAPAAHAAeAAHwAB4AHgAB4AAeAB8AAeAAHgAPAAPgAB4AD8APwAAOAAfwP4AADgAD//8AAAAAAf/+AAAAAAB/+AAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAB4AAAAA4AAeAAAAB/AAHgAAAB/wAB4AAAB/4AAeAAAD/4AAHgAAD/wAAB4AAH/wAAAeAAH/gAAAHgAP/gAAAB4AP/AAAAAeAf/AAAAAHgf+AAAAAB4/+AAAAAAe/8AAAAAAH/8AAAAAAB/4AAAAAAAf4AAAAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/gAAAA/AB/+AAAA/8B//wAAA//gf/+AAAf/8PgPgAAH4fngB8AAD4B/wAPgAA8AP8AB4AAeAB+AAeAAHgAfgADwAB4ADwAA8AAcAA8AAPAAHAAPAADwAB4ADwAA8AAeAB+AAPAAHgAfgAHgAB8AP8AB4AAPgH/AA+AAD8H54AfAAAf/8fgPwAAD/+D//4AAAf/Af/8AAAB/AD/+AAAAAAAP+AAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAAAAAAAf/wAAAAAAf/+AAAAAAP//4AAwAAH//+AAeAAD+APwAHgAA+AA+AB4AAfAAHgAOAAHgAB4ADwAB4AAPAA8AAeAADwAPAAHgAA8ADwAB4AAPAA8AAeAADwAPAAHgAA8AHgAB8AAeAB4AAPgAHgA+AAD8ADwA/AAAfwA8A/gAAD/wef/wAAAf////4AAAB////4AAAAH///wAAAAAD/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AB4AAAAAfgA/AAAAAH4APwAAAAB+AD4AAAAAPAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("DRAcHBwcHBwcHBwcDQ=="), 56+(scale<<8)+(1<<16)); return this; }; +//Two Rings +Graphics.prototype.setFontBloggerSansLight42 = function() { + // Actual height 28 (31 - 4) + // 1 BPP + return this.setFontCustom( + atob('AAAAAAAAAAAAAAAAAAAAAAAAAHwAAAAAHwAAAAAHwAAAAAHwAAAAADgAAAAAAAAAAAAAwAAAAAHwAAAAA/wAAAAH+AAAAA/wAAAAH+AAAAA/wAAAAH+AAAAA/wAAAAD+AAAAADwAAAAACAAAAAAAAAAAAAAAAAAAAAAP8AAAAD//wAAAP//8AAA////AAB////gAB4AAPgADwAADwADgAAAwADAAAAwADAAAAwADAAAAwADgAAAwADwAADwAB8AAPgAB////gAA////AAAP//8AAAD//wAAAAP4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAMAAAQAAYAAAwAAYAAAwAA4AAAwAAwAAAwAB////wAD////wAD////wAD////wAAAAAAwAAAAAAwAAAAAAwAAAAAAwAAAAAAQAAAAAAQAAAAAAAAAAAAAAAAAAAAAAB8AADwAB+AAHwAD8AAPwADgAAfwADAAA7wADAABzwADAABzwADAAHjwADAAPDwADAAeDwADgA8DwAD4H4DwAB//wDwAB//gDwAA/+ADwAAP8ADwAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAfgAB8AAfwAD8AABwADgAABwADAAAAwADADAAwADADAAwADADAAwADADAAwADAHAAwADgHgBwADwfgBwAB//4DgAB/8//gAA/4//AAAfwf+AAAAAP8AAAAABgAAAAAAAAAAAAAAAAAAADwAAAAAPwAAAAAfwAAAAA9wAAAADxwAAAAHhwAAAAeBwAAAA8BwAAADwBwAAAHgBwAAAfABwAAA8ABwAAB////wAD////wAD////wAD////wAAAABwAAAAABwAAAAABwAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAD//AfgAD//AHwAD//ABwADwHAAwADwHAAwADwGAAwADwGAAwADwGAAwADwHAAwADwHABwADwHABwADwDgDgADwD//gADwB//AADgB/+AAAAAf8AAAAADAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAH//8AAAP//+AAAf///AAA/DAfgAB4DAHwABwHABwADgGAAwADgGAAwADAGAAwADAHAAwADAHAAwADAHgBwADgH8fgAD4D//gAD8D//AAAAA/+AAAAAf4AAAAAAAAAAAAAAAAAAAAAAAD8AAAAAD+AAAAAD8AAAAADwAAAwADwAADwADwAAPwADwAA/gADwAD8AADwAPwAADwA/AAADwD8AAADwPwAAADw/AAAADz4AAAAD/gAAAAD+AAAAAD4AAAAADgAAAAAAAAAAAAAAAAAAAAAADgAAAHgP8AAAf4f/AAA/8//gAB////gAD8/wDwADgHgBwADAHAAwADADAAwADADAAwADADAAwADAHAAwADgHgBwAB8/wDwAB////gAA/8//gAAf4f/AAAHgP8AAAAADgAAAAAAAAAAAAAAAAAD8AAAAAP/AHgAA//gPwAB//wDwAB/f4AwADwB4AwADgA4AwADAAYAwADAAYAwADAAYAwADAAYBwADgA4DwADwAwHgAB+Aw/gAB////AAA///+AAAf//4AAAD//gAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMADgAAAeAHwAAA/AHwAAA/AHwAAAeAHwAAAAABAAAAAAAAAA'), + 46, + atob("CQ0VFBQVFhUVFRUVCg=="), + 42|65536 + ); +} +// Three rings +Graphics.prototype.setFontBloggerSansLight38 = function() { + // Actual height 25 (28 - 4) + // 1 BPP + return this.setFontCustom( + atob('AAAAAAAAAAAAAAAAAwAAAAAeAAAAAPgAAAAD4AAAAAcAAAAAAAAAAAAYAAAAA+AAAAB/AAAAD+AAAAH8AAAAP4AAAAP4AAAAfwAAAA/gAAAAPAAAAACAAAAAAAAAAAAAAAAAAAA/4AAAB//wAAB///AAA///8AAfgA/AAPAAB4ADAAAGAAwAABgAMAAAYADAAAGAA4AABgAPAAB4AB+AD8AAP//+AAB///AAAH//gAAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAACAAGAAAgABgAAYAAwAAGAAcAABgAH///4AD///+AA////gAP///4AAAAAGAAAAABgAAAAAYAAAAACAAAAAAgAAAAAAAAAAAAAAAAADgAHwAB4AD8AA+AA4AAfgAMAAG4ADAADOAAwABjgAMAA44ADAAcOAAwAeDgAOAPA4AB8/gOAAf/wDgAD/4A4AAf4AOAAB4ADgAAAAAAAAAAAAAAAAAAAAHwAH4AD8AA+AA8AABgAMAAAYADAGAGAAwBgBgAMAYAYADAGAGAAwDgBgAOA4AYAD5/AOAAf+8PAAH/n/wAA/x/4AABgP8AAAAA8AAAAAAAAAAABgAAAAA8AAAAA/AAAAAdwAAAAecAAAAPHAAAAHBwAAAHgcAAADgHAAADwBwAAB4AcAAA8AHAAA////gAP///4AD///+AAAABwAAAAAcAAAAAHAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAHwAD/8B+AA//ADgAOAwAYADgMAGAA4DABgAOAwAYADgMAGAA4DABgAOA4A4ADgOAOAA4B4PAAOAf/wADgD/4AAAAf8AAAAB4AAAAAAAAAAAAAAAAA+AAAAD//AAAD//8AAB///gAA/2f8AAfBgHAAHAwA4ADgMAGAAwDABgAMAwAYADAMAGAAwDgDgAMA8B4ADwP/8AA+B//AAHgP/AAAAA/AAAAAAAAAAAAAAAAAAAAAA/AAAAAPwAAAADwAACAA4AADgAOAAD4ADgAD8AA4AD8AAOAD8AADgD8AAA4D8AAAOD8AAADj4AAAA74AAAAP4AAAAD4AAAAA4AAAAAAAAAAAAAAAAAAAAHwAAB8H/AAA/z/4AAf+//AAH/+B4ADgeAOAAwDgBgAMAwAYADAMAGAAwDABgAMA4AYADgeAOAAf/4PgAH/v/wAA/z/8AAHwP8AAAAB8AAAAAAAAAAAAAAAAfgAAAAf+A+AAP/wPgAH/8AYADwHgGAA4A4BgAMAOAYADABgGAAwAYBgAMAGA4ADgDgeAA8AwPAAH+N/wAA///4AAH//4AAAf/8AAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgBwAAB8A+AAAfAPgAAHgB4AAAwAMAAAAAAAAA=='), + 46, + atob("CAwTEhITFBMTExMTCQ=="), + 38|65536 + ); +} + Graphics.prototype.setFontRoboto20 = function(scale) { // Actual height 21 (20 - 0) this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAH/zA/+YAAAAAAAHwAAwAAHwAA+AAAAAAAAAAAQACDAAYbADP4B/8A/zAGYZADH4A/+A/7AHYYADCAAAAAAAQAeHgH4eBzgwMMHnhw88GGBw4wHj+AcPgAAAAAAAAAAB4AA/gAGMAAwhwGMcAfuABzgABzgAc+AOMYBhBAAMYAB/AAHwAAAAAHwD5+A/8YGPDAw8YGPzA/HYD4fAADwAB/AAOYAABAAAAHwAA4AAAAAAAAAAH/gD//B8A+cAA7AADAAAAAAAYAAbwAHHgHwf/4A/8AAAAEAABiAAGwAA8AA/AAH+AAGwAByAAEAAAAAAAMAABgAAMAABgAH/wA/+AAMAABgAAMAABgAAAAAAAIAAfAADwAAAABgAAMAABgAAMAABgAAAAAAAAAAAAADAAAYAAAAAAAAADgAB8AB+AA+AA+AA/AAHAAAgAAAAAAB8AB/8Af/wHAHAwAYGADAwAYHAHAf/wB/8AAAAAAAAAAABgAAcAADAAAYAAH//A//4AAAAAAAAAAAAAAAAAAAAABwDAeA4HAPAwHYGBzAwcYHHDAfwYB8DAAAYAAAAAAABgOAcBwHADAwwYGGDAwwYHPHAf/wB58AAAAAAAAADAAB4AAfAAPYAHjAB4YA8DAH//A//4AAYAADAAAAAAAAAEMA/xwH+HAxgYGMDAxgYGODAw/4GD+AAHAAAAAAAAAf8AP/wD2HA5wYGMDAxgYGOHAA/wAD8AAAAAAAAAAAGAAAwAAGADAwB4GB+Aw+AGfAA/gAHwAAwAAAAAAADAB5+Af/wHPDAwwYGGDAwwYHPHAfvwB58AAAAAAAAAAAB+AAf4AHDjAwMYGBjAwM4HDOAf/gB/4AAAAAAAAAAAAYDADAYAAAAAAAAAAYDAfAYHwAAAABAAAcAADgAA+AAGwAB3AAMYABjgAYMAAAAAAAAAAAAAAABmAAMwABmAAMwABmAAMwABmAAMwAAiAAAAAAAAAYMADjgAMYAB3AAGwAA2AADgAAcAABAAAAAAAAAMAADgAA4AAGBzAweYGHAA/wAD8AAEAAAAwAB/4A/PwOAGDgAYYPxmH/Mw4ZmMDMxgZmM+Mx/5mHDAYAIDgDAPBwAf8AAMAAAAAAAYAAfAAPwAP4AH+AH4wA8GAH4wAP2AAPwAAfwAAfAAAYAAAAAAAAAAA//4H//AwwYGGDAwwYGGDAwwYH/HAf/wB58AAAAADAAH/AD/+AcBwHADAwAYGADAwAYGADA4A4DweAODgAAAAAAAAAAAAAAH//A//4GADAwAYGADAwAYGADAYAwD4+AP/gAfwAAAAAAAAAAAH//A//4GDDAwYYGDDAwYYGDDAwYYGCDAgAYAAAAAAAH//A//4GDAAwYAGDAAwYAGDAAwYAGAAAAAAAAAAH/AD/8AcBwHAHAwAYGADAwYYGDDA4YYDz/AOfwAAAAAAAAAAA//4H//A//4ADAAAYAADAAAYAADAAAYAADAA//4H//AAAAAAAAAAAAAAA//4H//AAAAAAAAABAAAeAAB4AADAAAYAADAAAYAAHA//wH/8AAAAAAAAAAAAAAA//4H//AAcAAPAAD4AA/wAOPADg8A4B4GAHAgAYAAAAAAAH//A//4AADAAAYAADAAAYAADAAAYAADAAAAAAAA//4H//A+AAB+AAD8AAD8AAH4AAPAAH4AH4AD8AD8AA+AAH//A//4AAAAAAAH//A//4H//AeAAB8AADwAAPgAAeAAA8AADwH//A//4AAAAAAAAAAAH/AB/8AeDwHAHAwAYGADAwAYGADA4A4DweAP/gA/4AAAAAAAAAAAH//A//4GBgAwMAGBgAwMAGBgAwcAH/AAfwAA8AAAAAA/4AP/gDgOA4A4GADAwAYGADAwAYHAHgeD+B/8wD+GAAAAAAAAAAA//4H//AwYAGDAAwYAGDgAweAHH8Afz4B8HAAAIAAYAPDwD8OA5w4GGDAwwYGHDAwYYHDnAePwBw8AAAAGAAAwAAGAAAwAAGAAA//4H//AwAAGAAAwAAGAAAwAAAAAAAAAH/4A//wAAPAAAYAADAAAYAADAAAYAAPA//wH/8AAAAAAAAgAAHAAA/AAB/AAD+AAD+AAD4AAfAAfwAfwAfwAH4AA4AAEAAA+AAH/AAH/gAD/AAD4AD+AH+AH8AA+AAH+AAD+AAD/AAD4AH/AP/AH+AA8AAAAAAAAAGADA4A4HweAPPgA/wAB8AAfwAPvgDweA8B4GADAAAIGAAA4AAHwAAPgAAfAAA/4AH/AD4AB8AA+AAHgAAwAAAAAAAAAGADAwB4GAfAwPYGDzAx4YGeDA/AYHwDA4AYGADAAAAAAAA///3//+wAA2AAGAAAGAAA+AAD8AAD8AAD4AAH4AAHgAAMAAAAwAA2AAG///3//+AAAAAAAAAAAOAAHwAD4AA8AAD8AADwAAGAAAAAAABgAAMAABgAAMAABgAAMAABgAAMAABgAAAEAAAwAADAAAIAAAAAAAAAAEeABn4Ad3ADMYAZjADMYAZmAB/4AP/AAAAAAAA//4H//ABgwAYDADAYAYDADg4AP+AA/gABwAAAAAAAAA/gAP+ADg4AYDADAYAYDADAYAOOABxwAAAAAEAAH8AB/wAcHADAYAYDADAYAcDA//4H//AAAAAAAAAAAAH8AB/wAdnADMYAZjADMYAZjAB84AHmAAMAAMAABgAB//gf/8HMAAxgAGIAAAAAAH8IB/zAcHMDAZgYDMDAZgcHcD//Af/wAAAAAAAAAAH//A//4AMAADAAAYAADAAAcAAD/4AP/AAAAAAAAAAAGf/Az/4AAAAAAAAAAMz//mf/4AAAAAAAAAAH//A//4ABwAAeAAH4ABzwAcPACAYAABAAAAAAAA//4H//AAAAAAAAAAAAf/AD/4AMAADAAAYAADAAAcAAD/4AP/ABgAAYAADAAAYAADgAAP/AA/4AAAAAAAAf/AD/4AMAADAAAYAADAAAcAAD/4AP/AAAAAAAAAAAAH8AB/wAcHADAYAYDADAYAYDADx4AP+AA/gAAAAAAAAf/8D//gYDADAYAYDADAYAcHAB/wAH8AAEAAAAAAEAAH8AB/wAcHADAYAYDADAYAYDAD//gf/8AAAAAAAAAAAf/AD/4AcAADAAAYAACAAAAEAB5wAfnADMYAZjADGYAYzADn4AOeAAAAAAAADAAAYAAf/wD//ADAYAYDAAAAAAAAD/gAf/AAA4AADAAAYAADAAAwAf/AD/4AAAAAAAAYAAD4AAP4AAP4AAPAAH4AH4AD8AAcAAAAAAQAADwAAf4AAf4AAPAAP4AP4ADwAAfgAA/gAA/AAD4AH+AD+AAeAAAAAAAAACAYAcHADzwAH8AAfAAH8ADx4AcHACAIAcAMD4BgP4MAP/AAPwAP4AP4AD4AAcAAAAAAAAADAYAYHADD4AY7ADOYAfjADwYAcDADAYAAAAADAAA4AH//B/v8cABzAACAAAH//w//+AAAAAAACAACcAAx/n+H//AA4AAHAAAAAAAAAAAAAOAADgAAYAADAAAcAABgAAGAAAwAAGAADwAAcAAAAA"), 32, atob("BQUHDQwPDQQHBwkMBAYGCQwMDAwMDAwMDAwFBAsMCwoTDg0ODgwMDg8GDA0LEg8ODQ4NDA0ODRMNDQ0GCQYJCQYLDAsMCwcMDAUFCwUSDAwMDAcLBwwKEAoKCgcFBw4A"), 21+(scale<<8)+(1<<16)); @@ -48,29 +78,75 @@ Graphics.prototype.setFontRoboto20 = function(scale) { }; function assignPalettes() { - if (g.theme.dark) { - // palette for 0-49% - pal1 = new Uint16Array([g.theme.bg, g.toColor(settings.gy), g.toColor(settings.fg), g.toColor("#00f")]); - // palette for 50-100% - pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.toColor(settings.gy), g.toColor("#00f")]); - } else { - // palette for 0-49% - pal1 = new Uint16Array([g.theme.bg, g.theme.fg, g.toColor(settings.fg), g.toColor("#00f")]); - // palette for 50-100% - pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.theme.fg, g.toColor("#00f")]); + palbg = new Uint16Array([g.toColor(g.theme.bg)]); + for (let i = 0; i < settings.rings.length; i++) { + let ring = settings.rings[i]; + if (g.theme.dark) { + // palette for 0-49% + pals[i].pal1 = new Uint16Array([g.theme.bg, g.toColor(ring.gy), g.toColor(ring.fg), g.toColor("#00f")]); + // palette for 50-100% + pals[i].pal2 = new Uint16Array([g.theme.bg, g.toColor(ring.fg), g.toColor(ring.gy), g.toColor("#00f")]); + } else { + // palette for 0-49% + pals[i].pal1 = new Uint16Array([g.theme.bg, g.theme.fg, g.toColor(ring.fg), g.toColor("#00f")]); + // palette for 50-100% + pals[i].pal2 = new Uint16Array([g.theme.bg, g.toColor(ring.fg), g.theme.fg, g.toColor("#00f")]); + if (ring.type !== 'Full') pals[i].pal1 = pals[i].pal2; // In light mode, we only want the full circle's filled portion to be black + } } } +function rotate_points(end, max) { + const midH = h/2; + const midW = w/2; + const off = 5; + const points = [midW-off,0, midW+off,0, midW+off,midH, midW-off,midH]; + var rotate = (2 * Math.PI) / (max / end); + var rotated_arr = []; + for (let i = 0; i < points.length; i += 2) { + let x = points[i]; + let y = points[i + 1]; + x -= midW; + y -= midH; + let x_new = x * Math.cos(rotate) - y * Math.sin(rotate); + let y_new = x * Math.sin(rotate) + y * Math.cos(rotate); + x = x_new + midW; + y = y_new + midH; + rotated_arr.push(x); + rotated_arr.push(y); + } + return rotated_arr; +} + function setSmallFont20() { g.setFontRoboto20(); } function setLargeFont() { - g.setFontBloggerSansLight46(1); -} + switch (innerMostRing) { + case 3: + g.setFontBloggerSansLight38(); + break; + case 2: + g.setFontBloggerSansLight42(); + break; + default: + g.setFontBloggerSansLight46(1); + break; + } + } function setSmallFont() { - g.setFont('Vector', 16); + let size = 16; + switch (innerMostRing) { + case 3: + size = 9; + break; + case 2: + size = 13; + break; + } + g.setFont('Vector', size); } function getSteps() { @@ -88,14 +164,29 @@ function getSteps() { function loadSettings() { settings = require("Storage").readJSON(SETTINGS_FILE,1)||{}; - settings.gy = settings.gy||'#020'; - settings.fg = settings.fg||'#0f0'; + + settings.rings[0].gy = settings.rings[0].gy||'#020'; + settings.rings[0].fg = settings.rings[0].fg||'#0f0'; + settings.rings[0].type = settings.rings[0].type||'Full'; + settings.rings[0].ring = settings.rings[0].ring||'Steps'; + settings.rings[0].step_target = settings.rings[0].step_target||10000; + + settings.rings[1].gy = settings.rings[1].gy||'#020'; + settings.rings[1].fg = settings.rings[1].fg||'#0f0'; + settings.rings[1].type = settings.rings[1].type||'Semi'; + settings.rings[1].ring = settings.rings[1].ring||'Minutes'; + settings.rings[1].step_target = settings.rings[1].step_target||10000; + + settings.rings[2].gy = settings.rings[2].gy||'#020'; + settings.rings[2].fg = settings.rings[2].fg||'#0f0'; + settings.rings[2].type = 'None'; + settings.rings[2].ring = settings.rings[2].ring||'Seconds'; + settings.rings[2].step_target = settings.rings[2].step_target||10000; + settings.idle_check = (settings.idle_check === undefined ? true : settings.idle_check); settings.batt_hours = (settings.batt_hours === undefined ? false : settings.batt_hours); settings.hr_12 = (global_settings["12hour"] === undefined ? false : global_settings["12hour"]); - settings.ring = settings.ring||'Steps'; settings.idxInfo = settings.idxInfo||0; - settings.step_target = settings.step_target||10000; assignPalettes(); } @@ -108,12 +199,12 @@ function loadLocation() { } function extractTime(d){ - var h = d.getHours(), m = d.getMinutes(); + var hh = d.getHours(), mm = d.getMinutes(); if (settings.hr_12) { - h = h % 12; - if (h == 0) h = 12; + hh = hh % 12; + if (hh == 0) hh = 12; } - return(("0"+h).substr(-2) + ":" + ("0"+m).substr(-2)); + return(("0"+hh).substr(-2) + ":" + ("0"+mm).substr(-2)); } var sunRise = "00:00"; @@ -226,9 +317,21 @@ function prevInfo(idx) { } function clearInfo() { + + var width = infoWidth; + var height = infoHeight; + switch (innerMostRing) { + case 3: + width -= 20; + height -= 3; + break; + case 2: + width -= 8; + height -= 2; + break; + } g.setColor(g.theme.bg); - //g.setColor(g.theme.fg); - g.fillRect((w/2) - infoWidth, infoLine - infoHeight, (w/2) + infoWidth, infoLine + infoHeight); + g.fillRect((w/2) - width, infoLine - height, (w/2) + width, infoLine + height); } function drawInfo() { @@ -262,12 +365,14 @@ function drawHrm() { g.drawString(hrmCurrent, (w/2) + 10, infoLine); } -function draw(drawRingOnly) { +function draw(updateSeconds) { if (!idle) { - if (drawRingOnly) { - drawGaugeImage(new Date()); + if (updateSeconds) { + let date = new Date(); + drawAllRings(date, updateSeconds); } else { + g.clear(); drawClock(); } } @@ -276,16 +381,16 @@ function draw(drawRingOnly) { queueDraw(); } -function drawGaugeImage(date) { +function getGaugeImage(date, ringType, step_target) { var hh = date.getHours(); var mm = date.getMinutes(); var ring_fill; var invertRing = false; var ring_max = 100; - switch (settings.ring) { + switch (ringType) { case 'Hours': - ring_fill = ((hh % 12) * 60) + mm; - ring_max = 720; + ring_fill = hh % 12; + ring_max = 12; break; case 'Minutes': ring_fill = mm; @@ -301,7 +406,7 @@ function drawGaugeImage(date) { break; case 'Steps': ring_fill = getSteps(); - ring_max = settings.step_target; + ring_max = step_target; break; case 'Battery': ring_fill = E.getBattery(); @@ -325,14 +430,38 @@ function drawGaugeImage(date) { start = ring_max - end; end = ring_max; } - if (end !== prevRing.end || start !== prevRing.start || ring_max !== prevRing.max) { - drawRing(start, end, ring_max); - prevRing.start = start; - prevRing.end = end; - prevRing.max = ring_max; - log_debug("Redrew ring at " + hh + ":" + mm); - } log_debug("Start: "+ start + " end: " +end); + return [start, end, ring_max]; +} + +function drawIfChanged(start, end, ring_max, idx, type) { + if (end === prevRing[idx].end && start === prevRing[idx].start && ring_max === prevRing[idx].max) return; + switch (type) { + case 'Full': + drawRing(start, end, ring_max, idx); + break; + case 'Semi': + drawSemi(start, end, ring_max, idx); + break; + case 'C': + drawC(end, ring_max, idx); + break; + } + prevRing[idx].start = start; + prevRing[idx].end = end; + prevRing[idx].max = ring_max; + log_debug("Redrew ring #" + idx); +} + +function drawAllRings(date, updateSeconds) { + for (let i = 0; i < settings.rings.length; i++) { + let ring = settings.rings[i]; + if (ring.type == "None") continue; + if (ring.ring != "Seconds" && updateSeconds) continue; + if (ring.type == 'Full' && ring.color == 'Fore') ring.type = 'Semi'; + result = getGaugeImage(date, ring.ring, ring.step_target); + drawIfChanged(result[0], result[1], result[2], i, ring.type); + } } function drawClock() { @@ -348,11 +477,11 @@ function drawClock() { g.reset(); g.setColor(g.theme.bg); - g.fillEllipse(ringEdge+ringThick,ringEdge+ringThick,w-ringEdge-ringThick,h-ringEdge-ringThick); // Clears the text within the circle - drawGaugeImage(date); + innerMostRing = getInnerMostRing(); + drawAllRings(date, false); setLargeFont(); - g.setColor(settings.fg); + g.setColor(settings.rings[0].fg); g.setFontAlign(1,0); // right aligned g.drawString(hh, (w/2) - 1, h/2); @@ -439,29 +568,79 @@ function polyArray(start, end, max) { return array; } -function drawRing(start, end, max) { +function drawRing(start, end, max, idx) { // Create persistent `buf` inside the function scope if (!drawRing._buf) { drawRing._buf = Graphics.createArrayBuffer(w, h, 2, { msb: true }); } const buf = drawRing._buf; let img = { width: w, height: h, transparent: 0, - bpp: 2, palette: pal1, buffer: buf.buffer }; + bpp: 2, palette: pals[idx].pal1, buffer: buf.buffer }; + let edge = ringEdge + (idx * ringIterOffset); buf.clear(); - buf.setColor(1).fillEllipse(ringEdge,ringEdge,w-ringEdge,h-ringEdge); - buf.setColor(0).fillEllipse(ringEdge+ringThick,ringEdge+ringThick,w-ringEdge-ringThick,h-ringEdge-ringThick); - img.palette = pal2; + buf.setColor(1).fillEllipse(edge,edge,w-edge,h-edge); + buf.setColor(0).fillEllipse(edge+ringThick,edge+ringThick,w-edge-ringThick,h-edge-ringThick); + img.palette = pals[idx].pal2; g.drawImage(img, 0, 0); // Draws a filled-in circle if((end - start) >= max) return; // No need to add the unfilled circle buf.clear(); - buf.setColor(1).fillEllipse(ringEdge,ringEdge,w-ringEdge,h-ringEdge); - buf.setColor(0).fillEllipse(ringEdge+ringThick,ringEdge+ringThick,w-ringEdge-ringThick,h-ringEdge-ringThick); + buf.setColor(1).fillEllipse(edge,edge,w-edge,h-edge); + buf.setColor(0).fillEllipse(edge+ringThick,edge+ringThick,w-edge-ringThick,h-edge-ringThick); buf.setColor(0).fillPoly(polyArray(start, end, max)); // Masks the filled-in part of the segment over the unfilled part - img.palette = pal1; + img.palette = pals[idx].pal1; g.drawImage(img, 0, 0); // Draws the unfilled-in segment return; } +function drawSemi(start, end, max, idx) { + // Create persistent `buf` inside the function scope + if (!drawSemi._buf) { + drawSemi._buf = Graphics.createArrayBuffer(w, h, 2, { msb: true }); + } + const buf = drawSemi._buf; + let img = { width: w, height: h, transparent: 0, + bpp: 2, palette: pals[idx].pal1, buffer: buf.buffer }; + let edge = ringEdge + (idx * ringIterOffset); + buf.clear(); + buf.setColor(1).fillEllipse(edge,edge,w-edge,h-edge); + buf.setColor(0).fillEllipse(edge+ringThick,edge+ringThick,w-edge-ringThick,h-edge-ringThick); + img.palette = palbg; + g.drawImage(img, 0, 0); // Draws a filled-in circle with the bg color, clearing it + if((end - start) >= max) return; // No need to add the unfilled circle + buf.clear(); + buf.setColor(1).fillEllipse(edge,edge,w-edge,h-edge); + buf.setColor(0).fillEllipse(edge+ringThick,edge+ringThick,w-edge-ringThick,h-edge-ringThick); + buf.setColor(0).fillPoly(polyArray(end, start, max)); // Masks the filled-in part of the segment over the unfilled part + img.palette = pals[idx].pal1; + g.drawImage(img, 0, 0); // Draws the unfilled-in segment + return; +} + +function drawC(end, max, idx) { + // Create persistent `buf` inside the function scope + if (!drawC._buf) { + drawC._buf = Graphics.createArrayBuffer(w, h, 2, { msb: true }); + } + const buf = drawC._buf; + let img = { width: w, height: h, transparent: 0, + bpp: 2, palette: pals[idx].pal1, buffer: buf.buffer }; + let edge = ringEdge + (idx * ringIterOffset); + buf.clear(); + buf.setColor(1).fillEllipse(edge,edge,w-edge,h-edge); + buf.setColor(0).fillEllipse(edge+ringThick,edge+ringThick,w-edge-ringThick,h-edge-ringThick); + img.palette = palbg; + g.drawImage(img, 0, 0); // Draws a filled-in circle with the bg color, clearing it + buf.clear(); + buf.setColor(1).fillEllipse(edge,edge,w-edge,h-edge); + buf.setColor(0).fillEllipse(edge+ringThick,edge+ringThick,w-edge-ringThick,h-edge-ringThick); + var vertices = rotate_points(end, max); + buf.setColor(0).fillPoly(vertices); + img.palette = pals[idx].pal1; + rotate = (2 * Math.PI) / (max / end); + g.drawImage(img, 0, 0); // Draws the unfilled-in segment + return; +} + ///////////////// IDLE TIMER ///////////////////////////////////// @@ -603,7 +782,7 @@ function getDelayMs(prevDelayMs, ring_setting, now) { const sec_batt = [20, 50]; const sec_delay = [10000, 2000, 1000]; const deadband = 5; - if (ring_setting == 'Seconds') { + if (ring_setting.some(ring => ring.ring === 'Seconds')) { const nearNextMinute = (now % 60000) >= (60000 - prevDelayMs); if (nearNextMinute) { let batt = E.getBattery(); @@ -634,7 +813,7 @@ var drawTimeout; // schedule a draw for the next minute or every sec_update ms function queueDraw() { let now = Date.now(); - var nextUpdateRet = getDelayMs(nextUpdateMs, settings.ring, now); + var nextUpdateRet = getDelayMs(nextUpdateMs, settings.rings, now); nextUpdateMs = nextUpdateRet[0]; let delay = nextUpdateMs - (now % nextUpdateMs); if (drawTimeout) clearTimeout(drawTimeout); @@ -645,6 +824,17 @@ function queueDraw() { }, delay); } +function getInnerMostRing() { + var innerMost = 0; + for (let i = settings.rings.length - 1; i >= 0; i--) { + if (settings.rings[i].type !== "None") { + innerMost = i+1; + break; + } + } + return innerMost; +} + // Stop updates when LCD is off, restart when on Bangle.on('lcdPower',on=>{ if (on) { @@ -669,9 +859,10 @@ Bangle.setUI("clockupdown", btn=> { loadSettings(); loadLocation(); +innerMostRing = getInnerMostRing(); var infoMode = infoList[settings.idxInfo]; updateSunRiseSunSet(new Date(), location.lat, location.lon, true); -nextUpdateMs = getDelayMs(1000, settings.ring, Date.now())[0]; +nextUpdateMs = getDelayMs(1000, settings.rings, Date.now())[0]; g.clear(); Bangle.loadWidgets(); diff --git a/apps/daisy/settings.js b/apps/daisy/settings.js index 741d4f3ea..139c24da7 100644 --- a/apps/daisy/settings.js +++ b/apps/daisy/settings.js @@ -2,14 +2,21 @@ const SETTINGS_FILE = "daisy.json"; // initialize with default settings... - let s = {'gy' : '#020', - 'fg' : '#0f0', - 'color': 'Green', - 'check_idle' : true, - 'batt_hours' : false, - 'ring' : 'Steps', - 'idxInfo' : 0, - 'step_target' : 10000}; + let defaultRing = () => ({ + color: 'Green', + fg: '#0f0', + gy: '#020', + ring: 'Steps', + type: 'Full', + step_target: 10000, + }); + + let s = { + rings: [defaultRing(), defaultRing(), defaultRing()], + check_idle: true, + batt_hours: false, + idxInfo: 0, + }; // ...and overwrite them with any saved values // This way saved values are preserved if a new version adds more settings @@ -20,73 +27,99 @@ for (const key in saved) { s[key] = saved[key]; } +// Fill in any missing ring defaults +for (let i = 0; i < 3; i++) { + s.rings[i] = Object.assign(defaultRing(), s.rings[i] || {}); +} + function save() { settings = s; storage.write(SETTINGS_FILE, settings); } -var color_options = ['Green','Orange','Cyan','Purple','Red','Blue']; -var fg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f']; -var gy_code = ['#020','#220','#022','#202','#200','#002']; +var color_options = ['Green','Orange','Cyan','Purple','Red','Blue', 'Fore']; +var fg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f', g.theme.fg]; +var gy_code = ['#020','#220','#022','#202','#200','#002', g.theme.fg]; var ring_options = ['Hours', 'Minutes', 'Seconds', 'Day', 'Sun', 'Steps', 'Battery']; var step_options = [100, 1000, 5000, 10000, 15000, 20000]; - function showMainMenu() { - let appMenu = { - '': { 'title': 'Daisy Clock' }, - '< Back': back, + function showRingMenu(ringIndex) { + const ring = s.rings[ringIndex]; + let ringMenu = { + '': { title: `Ring ${ringIndex + 1}` }, + '< Back': showMainMenu, 'Color': { - value: 0 | color_options.indexOf(s.color), + value: 0 | color_options.indexOf(ring.color), min: 0, max: color_options.length - 1, format: v => color_options[v], onchange: v => { - s.color = color_options[v]; - s.fg = fg_code[v]; - s.gy = gy_code[v]; + ring.color = color_options[v]; + ring.fg = fg_code[v]; + ring.gy = gy_code[v]; save(); } }, - 'Ring Display': { - value: 0 | ring_options.indexOf(s.ring), + 'Type': { + value: ring_types.indexOf(ring.type), + min: 0, max: ring_types.length - 1, + format: v => ring_types[v], + onchange: v => { + ring.type = ring_types[v]; + save(); + } + }, + 'Display': { + value: 0 | ring_options.indexOf(ring.ring), min: 0, max: ring_options.length - 1, format: v => ring_options[v], onchange: v => { - let prev = s.ring; - s.ring = ring_options[v]; + let prev = ring.ring; + ring.ring = ring_options[v]; save(); - if (prev != s.ring && (prev === 'Steps' || s.ring === 'Steps')) { + if (prev != ring.ring && (prev === 'Steps' || ring.ring === 'Steps')) { // redisplay the menu with/without ring setting // Reference https://github.com/orgs/espruino/discussions/7697 - setTimeout(showMainMenu, 0); + setTimeout(showRingMenu, 0, ringIndex); } }, } }; - if (s.ring == 'Steps') { - appMenu[/*LANG*/"Step Target"] = { - value: 0 | step_options.indexOf(s.step_target), + if (ring.ring == 'Steps') { + ringMenu[/*LANG*/"Step Target"] = { + value: 0 | step_options.indexOf(ring.step_target), min: 0, max: step_options.length - 1, format: v => step_options[v], onchange: v => { - s.step_target = step_options[v]; + ring.step_target = step_options[v]; save(); }, }; - } - appMenu['Idle Warning'] = { - value: !!s.idle_check, - onchange: v => { - s.idle_check = v; - save(); - }, - }; - appMenu['Battery Life Format'] = { - value: !!s.batt_hours, - format: value => value?"Days":"%", - onchange: v => { - s.batt_hours = v; - save(); + } + E.showMenu(ringMenu); + } + + function showMainMenu() { + let appMenu = { + '': { title: 'Daisy Clock' }, + '< Back': back, + 'Ring 1': () => showRingMenu(0), + 'Ring 2': () => showRingMenu(1), + 'Ring 3': () => showRingMenu(2), + 'Idle Warning' : { + value: !!s.idle_check, + onchange: v => { + s.idle_check = v; + save(); + }, }, + 'Battery Life Format' : { + value: !!s.batt_hours, + format: value => value?"Days":"%", + onchange: v => { + s.batt_hours = v; + save(); + }, + } }; E.showMenu(appMenu); }