Three ring bugfixes and changes

master
David Volovskiy 2025-05-18 07:54:26 -04:00
parent 620ff4098f
commit d7f0113a86
2 changed files with 180 additions and 109 deletions

View File

@ -23,9 +23,9 @@ var pals = Array(3).fill().map(() => (
})); }));
let palbg; let palbg;
const infoLine = (3*h/4) - 6; const infoLineDefault = (3*h/4) - 6;
const infoWidth = 56; const infoWidthDefault = 56;
const infoHeight = 11; const infoHeightDefault = 11;
const ringEdge = 4; const ringEdge = 4;
const ringIterOffset = 10; const ringIterOffset = 10;
const ringThick = 6; const ringThick = 6;
@ -58,7 +58,8 @@ Graphics.prototype.setFontBloggerSansLight42 = function() {
atob("CQ0VFBQVFhUVFRUVCg=="), atob("CQ0VFBQVFhUVFRUVCg=="),
42|65536 42|65536
); );
} };
// Three rings // Three rings
Graphics.prototype.setFontBloggerSansLight38 = function() { Graphics.prototype.setFontBloggerSansLight38 = function() {
// Actual height 25 (28 - 4) // Actual height 25 (28 - 4)
@ -69,7 +70,7 @@ Graphics.prototype.setFontBloggerSansLight38 = function() {
atob("CAwTEhITFBMTExMTCQ=="), atob("CAwTEhITFBMTExMTCQ=="),
38|65536 38|65536
); );
} };
Graphics.prototype.setFontRoboto20 = function(scale) { Graphics.prototype.setFontRoboto20 = function(scale) {
// Actual height 21 (20 - 0) // Actual height 21 (20 - 0)
@ -138,13 +139,17 @@ function setLargeFont() {
function setSmallFont() { function setSmallFont() {
let size = 16; let size = 16;
if (infoMode == "ID_HRM" ) {
g.setFont('Vector', size);
return;
}
switch (innerMostRing) { switch (innerMostRing) {
case 3:
size = 9;
break;
case 2: case 2:
size = 13; size = 13;
break; break;
case 3:
size = 12;
break;
} }
g.setFont('Vector', size); g.setFont('Vector', size);
} }
@ -164,6 +169,7 @@ function getSteps() {
function loadSettings() { function loadSettings() {
settings = require("Storage").readJSON(SETTINGS_FILE,1)||{}; settings = require("Storage").readJSON(SETTINGS_FILE,1)||{};
settings.rings = settings.rings || [{}, {}, {}];
settings.rings[0].gy = settings.rings[0].gy||'#020'; settings.rings[0].gy = settings.rings[0].gy||'#020';
settings.rings[0].fg = settings.rings[0].fg||'#0f0'; settings.rings[0].fg = settings.rings[0].fg||'#0f0';
@ -173,16 +179,24 @@ function loadSettings() {
settings.rings[1].gy = settings.rings[1].gy||'#020'; settings.rings[1].gy = settings.rings[1].gy||'#020';
settings.rings[1].fg = settings.rings[1].fg||'#0f0'; settings.rings[1].fg = settings.rings[1].fg||'#0f0';
settings.rings[1].type = settings.rings[1].type||'Semi'; settings.rings[1].type = settings.rings[1].type||'None';
settings.rings[1].ring = settings.rings[1].ring||'Minutes'; settings.rings[1].ring = settings.rings[1].ring||'Minutes';
settings.rings[1].step_target = settings.rings[1].step_target||10000; settings.rings[1].step_target = settings.rings[1].step_target||10000;
settings.rings[2].gy = settings.rings[2].gy||'#020'; settings.rings[2].gy = settings.rings[2].gy||'#020';
settings.rings[2].fg = settings.rings[2].fg||'#0f0'; settings.rings[2].fg = settings.rings[2].fg||'#0f0';
settings.rings[2].type = 'None'; settings.rings[2].type = settings.rings[2].type||'None';
settings.rings[2].ring = settings.rings[2].ring||'Seconds'; settings.rings[2].ring = settings.rings[2].ring||'Hours';
settings.rings[2].step_target = settings.rings[2].step_target||10000; settings.rings[2].step_target = settings.rings[2].step_target||10000;
for (let i = 0; i < settings.rings.length; i++) {
if (settings.rings[i].color == 'Blk/Wht') {
settings.rings[i].gy = g.theme.fg;
settings.rings[i].fg = g.theme.fg;
}
}
settings.fg = settings.fg||'#0ff';
settings.idle_check = (settings.idle_check === undefined ? true : settings.idle_check); settings.idle_check = (settings.idle_check === undefined ? true : settings.idle_check);
settings.batt_hours = (settings.batt_hours === undefined ? false : settings.batt_hours); settings.batt_hours = (settings.batt_hours === undefined ? false : settings.batt_hours);
settings.hr_12 = (global_settings["12hour"] === undefined ? false : global_settings["12hour"]); settings.hr_12 = (global_settings["12hour"] === undefined ? false : global_settings["12hour"]);
@ -316,22 +330,36 @@ function prevInfo(idx) {
return idx; return idx;
} }
function clearInfo() { function getInfoDims() {
var line = infoLineDefault;
var width = infoWidth; var width = infoWidthDefault;
var height = infoHeight; var height = infoHeightDefault;
switch (innerMostRing) { switch (innerMostRing) {
case 3:
width -= 20;
height -= 3;
break;
case 2: case 2:
width -= 8; width -= 8;
height -= 2; height -= 2;
line -= 7;
break;
case 3:
width -= 10;
height -= 6;
line -= 10;
break; break;
} }
if (infoMode == "ID_HRM") {
width = 30;
height = infoHeightDefault;
}
return[line, width, height];
}
function clearInfo() {
var dims = getInfoDims();
var line = dims[0];
var width = dims[1];
var height = dims[2];
g.setColor(g.theme.bg); g.setColor(g.theme.bg);
g.fillRect((w/2) - width, infoLine - height, (w/2) + width, infoLine + height); g.fillRect((w/2) - width, line - height, (w/2) + width, line + height);
} }
function drawInfo() { function drawInfo() {
@ -339,18 +367,20 @@ function drawInfo() {
g.setColor(g.theme.fg); g.setColor(g.theme.fg);
setSmallFont(); setSmallFont();
g.setFontAlign(0,0); g.setFontAlign(0,0);
var dims = getInfoDims();
var line = dims[0];
var height = dims[2];
if (infoMode == "ID_HRM") { if (infoMode == "ID_HRM") {
clearInfo(); clearInfo();
g.setColor('#f00'); // red g.setColor('#f00'); // red
drawHeartIcon(); drawHeartIcon(line, height);
} else { } else {
g.drawString((infoData[infoMode].calc().toUpperCase()), w/2, infoLine); g.drawString((infoData[infoMode].calc().toUpperCase()), w/2, line);
} }
} }
function drawHeartIcon() { function drawHeartIcon(line, height) {
g.drawImage(hrmImg, (w/2) - infoHeight - 20, infoLine - infoHeight); g.drawImage(hrmImg, (w/2) - height - 20, line - height);
} }
function drawHrm() { function drawHrm() {
@ -358,11 +388,14 @@ function drawHrm() {
var d = new Date(); var d = new Date();
clearInfo(); clearInfo();
g.setColor(d.getSeconds()&1 ? '#f00' : g.theme.bg); g.setColor(d.getSeconds()&1 ? '#f00' : g.theme.bg);
drawHeartIcon(); var dims = getInfoDims();
var line = dims[0];
var height = dims[2];
drawHeartIcon(line, height);
setSmallFont(); setSmallFont();
g.setFontAlign(-1,0); // left g.setFontAlign(-1,0); // left
g.setColor(hrmConfidence >= 50 ? g.theme.fg : '#f00'); g.setColor(hrmConfidence >= 50 ? g.theme.fg : '#f00');
g.drawString(hrmCurrent, (w/2) + 10, infoLine); g.drawString(hrmCurrent, (w/2) + 10, line);
} }
function draw(updateSeconds) { function draw(updateSeconds) {
@ -372,7 +405,6 @@ function draw(updateSeconds) {
drawAllRings(date, updateSeconds); drawAllRings(date, updateSeconds);
} }
else { else {
g.clear();
drawClock(); drawClock();
} }
} }
@ -389,8 +421,8 @@ function getGaugeImage(date, ringType, step_target) {
var ring_max = 100; var ring_max = 100;
switch (ringType) { switch (ringType) {
case 'Hours': case 'Hours':
ring_fill = hh % 12; ring_fill = ((hh % 12) * 60) + mm;
ring_max = 12; ring_max = 12 * 60;
break; break;
case 'Minutes': case 'Minutes':
ring_fill = mm; ring_fill = mm;
@ -458,7 +490,7 @@ function drawAllRings(date, updateSeconds) {
let ring = settings.rings[i]; let ring = settings.rings[i];
if (ring.type == "None") continue; if (ring.type == "None") continue;
if (ring.ring != "Seconds" && updateSeconds) continue; if (ring.ring != "Seconds" && updateSeconds) continue;
if (ring.type == 'Full' && ring.color == 'Fore') ring.type = 'Semi'; if (ring.type == 'Full' && ring.color == 'Blk/Wht') ring.type = 'Semi';
result = getGaugeImage(date, ring.ring, ring.step_target); result = getGaugeImage(date, ring.ring, ring.step_target);
drawIfChanged(result[0], result[1], result[2], i, ring.type); drawIfChanged(result[0], result[1], result[2], i, ring.type);
} }
@ -478,10 +510,12 @@ function drawClock() {
g.reset(); g.reset();
g.setColor(g.theme.bg); g.setColor(g.theme.bg);
innerMostRing = getInnerMostRing(); innerMostRing = getInnerMostRing();
let edge = ringEdge + (innerMostRing * ringIterOffset);
g.fillEllipse(edge+ringThick,edge+ringThick,w-edge-ringThick,h-edge-ringThick); // Clears the text within the circle
drawAllRings(date, false); drawAllRings(date, false);
setLargeFont(); setLargeFont();
g.setColor(settings.rings[0].fg); g.setColor(settings.fg);
g.setFontAlign(1,0); // right aligned g.setFontAlign(1,0); // right aligned
g.drawString(hh, (w/2) - 1, h/2); g.drawString(hh, (w/2) - 1, h/2);
@ -521,7 +555,8 @@ function resetHrm() {
if (infoMode == "ID_HRM") { if (infoMode == "ID_HRM") {
clearInfo(); clearInfo();
g.setColor('#f00'); // red g.setColor('#f00'); // red
drawHeartIcon(); var dims = getInfoDims();
drawHeartIcon(dims[0], dims[2]);
} }
} }
@ -594,24 +629,29 @@ function drawRing(start, end, max, idx) {
function drawSemi(start, end, max, idx) { function drawSemi(start, end, max, idx) {
// Create persistent `buf` inside the function scope // Create persistent `buf` inside the function scope
var fullCircle = (end - start) >= max;
if (!drawSemi._buf) { if (!drawSemi._buf) {
drawSemi._buf = Graphics.createArrayBuffer(w, h, 2, { msb: true }); drawSemi._buf = Graphics.createArrayBuffer(w, h, 2, { msb: true });
} }
const buf = drawSemi._buf; const buf = drawSemi._buf;
let img = { width: w, height: h, transparent: 0, let img = { width: w, height: h, transparent: 0,
bpp: 2, palette: pals[idx].pal1, buffer: buf.buffer }; bpp: 2, palette: pals[idx].pal2, buffer: buf.buffer };
let edge = ringEdge + (idx * ringIterOffset); let edge = ringEdge + (idx * ringIterOffset);
buf.clear(); buf.clear();
buf.setColor(1).fillEllipse(edge,edge,w-edge,h-edge); 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).fillEllipse(edge+ringThick,edge+ringThick,w-edge-ringThick,h-edge-ringThick);
if (fullCircle)
img.palette = pals[idx].pal2;
else
img.palette = palbg; img.palette = palbg;
g.drawImage(img, 0, 0); // Draws a filled-in circle with the bg color, clearing it 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 if(end == start) return; //If the ring should be completely empty
if(fullCircle) return; // No need to add the unfilled circle
buf.clear(); buf.clear();
buf.setColor(1).fillEllipse(edge,edge,w-edge,h-edge); 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).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 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; img.palette = pals[idx].pal2;
g.drawImage(img, 0, 0); // Draws the unfilled-in segment g.drawImage(img, 0, 0); // Draws the unfilled-in segment
return; return;
} }
@ -623,7 +663,7 @@ function drawC(end, max, idx) {
} }
const buf = drawC._buf; const buf = drawC._buf;
let img = { width: w, height: h, transparent: 0, let img = { width: w, height: h, transparent: 0,
bpp: 2, palette: pals[idx].pal1, buffer: buf.buffer }; bpp: 2, palette: pals[idx].pal2, buffer: buf.buffer };
let edge = ringEdge + (idx * ringIterOffset); let edge = ringEdge + (idx * ringIterOffset);
buf.clear(); buf.clear();
buf.setColor(1).fillEllipse(edge,edge,w-edge,h-edge); buf.setColor(1).fillEllipse(edge,edge,w-edge,h-edge);
@ -633,10 +673,10 @@ function drawC(end, max, idx) {
buf.clear(); buf.clear();
buf.setColor(1).fillEllipse(edge,edge,w-edge,h-edge); 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).fillEllipse(edge+ringThick,edge+ringThick,w-edge-ringThick,h-edge-ringThick);
if (end > max) end = max;
var vertices = rotate_points(end, max); var vertices = rotate_points(end, max);
buf.setColor(0).fillPoly(vertices); buf.setColor(0).fillPoly(vertices);
img.palette = pals[idx].pal1; img.palette = pals[idx].pal2;
rotate = (2 * Math.PI) / (max / end);
g.drawImage(img, 0, 0); // Draws the unfilled-in segment g.drawImage(img, 0, 0); // Draws the unfilled-in segment
return; return;
} }
@ -846,6 +886,7 @@ Bangle.on('lcdPower',on=>{
}); });
Bangle.setUI("clockupdown", btn=> { Bangle.setUI("clockupdown", btn=> {
clearInfo(); // Used to clear infobox in case we're going to the HRM
if (btn<0) settings.idxInfo = prevInfo(settings.idxInfo); if (btn<0) settings.idxInfo = prevInfo(settings.idxInfo);
if (btn>0) settings.idxInfo = nextInfo(settings.idxInfo); if (btn>0) settings.idxInfo = nextInfo(settings.idxInfo);
// power HRM on/off accordingly // power HRM on/off accordingly

View File

@ -1,21 +1,41 @@
(function(back) { (function(back) {
const SETTINGS_FILE = "daisy.json"; const SETTINGS_FILE = "daisy.json";
// initialize with default settings... // default settings
let defaultRing = () => ({ let s = {
rings: [{}, {}, {}],
color: 'Green',
fg: '#0f0',
check_idle: true,
batt_hours: false,
idxInfo: 0,
};
s.rings[0] = {
color: 'Green', color: 'Green',
fg: '#0f0', fg: '#0f0',
gy: '#020', gy: '#020',
ring: 'Steps', ring: 'Steps',
type: 'Full', type: 'Full',
step_target: 10000, step_target: 10000,
}); };
let s = { s.rings[1] = {
rings: [defaultRing(), defaultRing(), defaultRing()], color: 'Blk/Wht',
check_idle: true, fg: g.theme.fg,
batt_hours: false, gy: g.theme.fg,
idxInfo: 0, ring: 'Minutes',
type: 'None',
step_target: 10000,
};
s.rings[2] = {
color: 'Green',
fg: '#0f0',
gy: '#020',
ring: 'Hours',
type: 'None',
step_target: 10000,
}; };
// ...and overwrite them with any saved values // ...and overwrite them with any saved values
@ -24,12 +44,7 @@ const storage = require('Storage');
let settings = storage.readJSON(SETTINGS_FILE, 1) || s; let settings = storage.readJSON(SETTINGS_FILE, 1) || s;
const saved = settings || {}; const saved = settings || {};
for (const key in saved) { for (const key in saved) {
s[key] = saved[key]; 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() { function save() {
@ -37,18 +52,34 @@ function save() {
storage.write(SETTINGS_FILE, settings); storage.write(SETTINGS_FILE, settings);
} }
var color_options = ['Green','Orange','Cyan','Purple','Red','Blue', 'Fore']; var color_options = ['Cyan','Green','Orange','Purple','Red','Blue', 'Blk/Wht'];
var fg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f', g.theme.fg]; var fg_code = ['#0ff','#0f0','#ff0','#f0f','#f00','#00f', g.theme.fg];
var gy_code = ['#020','#220','#022','#202','#200','#002', g.theme.fg]; var gy_code = ['#022','#020','#220','#202','#200','#002', g.theme.fg];
var ring_options = ['Hours', 'Minutes', 'Seconds', 'Day', 'Sun', 'Steps', 'Battery']; var ring_options = ['Hours', 'Minutes', 'Seconds', 'Day', 'Sun', 'Steps', 'Battery'];
var step_options = [100, 1000, 5000, 10000, 15000, 20000]; var ring_types = ['None', 'Full', 'Semi', 'C'];
var step_options = [100, 1000, 5000, 10000, 15000, 20000];
function showRingMenu(ringIndex) { function showRingMenu(ringIndex) {
const ring = s.rings[ringIndex]; const ring = s.rings[ringIndex];
let ringMenu = { let ringMenu = {
'': { title: `Ring ${ringIndex + 1}` }, '': { title: `Ring ${ringIndex + 1}` },
'< Back': showMainMenu, '< Back': showMainMenu,
'Color': { 'Type': {
value: ring_types.indexOf(ring.type),
min: 0, max: ring_types.length - 1,
format: v => ring_types[v],
onchange: v => {
let prev = ring.type;
ring.type = ring_types[v];
save();
if (prev != ring.type && (prev === 'None' || ring.type === 'None')) {
setTimeout(showRingMenu, 0, ringIndex);
}
}
},
};
if (ring.type != 'None') {
ringMenu['Color'] = {
value: 0 | color_options.indexOf(ring.color), value: 0 | color_options.indexOf(ring.color),
min: 0, max: color_options.length - 1, min: 0, max: color_options.length - 1,
format: v => color_options[v], format: v => color_options[v],
@ -58,17 +89,8 @@ var step_options = [100, 1000, 5000, 10000, 15000, 20000];
ring.gy = gy_code[v]; ring.gy = gy_code[v];
save(); save();
} }
}, };
'Type': { ringMenu['Display'] = {
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), value: 0 | ring_options.indexOf(ring.ring),
min: 0, max: ring_options.length - 1, min: 0, max: ring_options.length - 1,
format: v => ring_options[v], format: v => ring_options[v],
@ -77,13 +99,11 @@ var step_options = [100, 1000, 5000, 10000, 15000, 20000];
ring.ring = ring_options[v]; ring.ring = ring_options[v];
save(); save();
if (prev != ring.ring && (prev === 'Steps' || ring.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(showRingMenu, 0, ringIndex); setTimeout(showRingMenu, 0, ringIndex);
} }
}, },
}
}; };
}
if (ring.ring == 'Steps') { if (ring.ring == 'Steps') {
ringMenu[/*LANG*/"Step Target"] = { ringMenu[/*LANG*/"Step Target"] = {
value: 0 | step_options.indexOf(ring.step_target), value: 0 | step_options.indexOf(ring.step_target),
@ -105,10 +125,13 @@ var step_options = [100, 1000, 5000, 10000, 15000, 20000];
'Ring 1': () => showRingMenu(0), 'Ring 1': () => showRingMenu(0),
'Ring 2': () => showRingMenu(1), 'Ring 2': () => showRingMenu(1),
'Ring 3': () => showRingMenu(2), 'Ring 3': () => showRingMenu(2),
'Idle Warning' : { 'Hour Color': {
value: !!s.idle_check, value: 0 | color_options.indexOf(s.color),
min: 0, max: color_options.length - 1,
format: v => color_options[v],
onchange: v => { onchange: v => {
s.idle_check = v; s.color = color_options[v];
s.fg = fg_code[v];
save(); save();
}, },
}, },
@ -119,7 +142,14 @@ var step_options = [100, 1000, 5000, 10000, 15000, 20000];
s.batt_hours = v; s.batt_hours = v;
save(); save();
}, },
} },
'Idle Warning' : {
value: !!s.idle_check,
onchange: v => {
s.idle_check = v;
save();
},
},
}; };
E.showMenu(appMenu); E.showMenu(appMenu);
} }