Merge pull request #3746 from MomentumV/master

DayMoon 0.05 release
master
thyttan 2025-02-13 20:10:20 +01:00 committed by GitHub
commit d782914f88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 200 additions and 151 deletions

View File

@ -2,3 +2,4 @@
0.02: move moon down, rotate sunrise/sunset, shift Hours/minutes to corners 0.02: move moon down, rotate sunrise/sunset, shift Hours/minutes to corners
0.03: Change day and night to have different dots 0.03: Change day and night to have different dots
0.04: Update ChangeLog, coerce dark theme 0.04: Update ChangeLog, coerce dark theme
0.05: Add more screenshots, fix bug in dot colors

View File

@ -8,10 +8,12 @@ This uses the myLocation app to get your latitude and longitude for proper dayli
Feature roadmap: Feature roadmap:
- [x] 0.01 Fix blocking widgets - [x] 0.01 Fix blocking widgets
- [x] 0.03 Day and Night different color markers - [x] 0.03 Day and Night different color markers
- [ ] 0.05 add Day of week and month display - [x] 0.04 Add to App Loader
- [ ] 0.06 Seconds display - [x] 0.05 Add more screenshots with different moon phases
- [ ] 0.07 Color Themes (and settings/options) - [ ] 0.06 add Day of week and month display
- [ ] 0.08 Moon display angle represents how it looks in the sky - [ ] 0.07 Seconds display
- [ ] 0.08 Color Themes (and settings/options)
- [ ] 0.09 Moon display angle represents how it looks in the sky
- [ ] 0.10 custom/bigger/fitted time digits - [ ] 0.10 custom/bigger/fitted time digits
- [ ] 0.20 clockinfo support? - [ ] 0.20 clockinfo support?
- [ ] 0.30 Tap/swipe actions? - [ ] 0.30 Tap/swipe actions?

View File

@ -4,42 +4,46 @@ let location;
var Utils = require("graphics_utils"); var Utils = require("graphics_utils");
var SunCalc = require("suncalc"); var SunCalc = require("suncalc");
var RADII = { var RADII = {
moon: 40, moon: 40,
arcMin: 48, arcMin: 48,
arcMax: 63, arcMax: 63,
dots: 55, dots: 55,
needle: 54, needle: 54,
}; };
var COL = { var COL = {
moon: 65535, // moon: 65535, //
txture: 33792, // 0.5 ,0.5,0 txture: 33792, // 0.5 ,0.5,0
shadow: 8196, // .125,0, .125 shadow: 8196, // .125,0, .125
day: 40159, //0.6, 0.6,1 day: 40159, //0.6, 0.6,1
night: 6, // 0, 0, 0.2 night: 6, // 0, 0, 0.2
ndots: 2047, // 0, 1, 1 cyan ndots: 2047, // 0, 1, 1 cyan
ddots: 0, ddots: 0,
needle: 63488, // 1, 0, 0 red needle: 63488, // 1, 0, 0 red
stime: 2047 stime: 2047
}; };
const TAU = 2.0 * Math.PI; const TAU = 2.0 * Math.PI;
const MX = g.getWidth() / 2, const MX = g.getWidth() / 2,
MY = 24 + 3 + RADII.arcMax; MY = 24 + 3 + RADII.arcMax;
const DAY_MILLIS = 86400000; const DAY_MILLIS = 86400000;
const M_POS = { x: MX, y: MY, r: RADII.moon }; const M_POS = {
x: MX,
y: MY,
r: RADII.moon
};
// images // images
const moon_texture = { const moon_texture = {
width: 80, width: 80,
height: 80, height: 80,
bpp: 1, bpp: 1,
transparent: 0, transparent: 0,
buffer: require("heatshrink").decompress(atob("ABsRqAJHkEiBA0N0uq1AIEgNVqtRqoJEgUiAAQJEioTBAAIzEl2q12oxATECQdVioJD/eqne60UCHQoADoAJBgf+xWrFIOACYUFCYo8Cj/73f70er0ROHAANUBIM//3///q1WIFAV1qtXCggJB//7CYO6keikBOHKAUDCIInClSgCgonBu4TK1W73ShBMQxkCh5OC//uFIInBi91q5PFCYISC3er//iOwXVE41UCYf+9//9AnCJopVBqEv/+/3//E4P6kUgJw4nDKAP+14TB1Xoq4hBEwYFBqgnB3Wr3e737KB/QnIqp3B32OKAYTBE4Z4BAoYnBEoRSC0fyE5ITBJ4WuCYP4J4J3CeQQFClbvBJgOqn5kBnRPKTwJMB1B4B92qEgQACJ4JTBkYnBYwOilYsBO5NUhYmB9+qxGC9TxBEYTvFqki3Y8B1Uikei3+oionIgGrO4OqwGC9H/xATK1/7E4UAnU7kATIqEAl/uE4WA12u0ATJgSgB/+ikUgnW70EFCY9AgGDE4PowEAlWowEBCZJ4BneggUgkRSBCZEAgEKJoIEBgEIAYQOCKYcVBIMqJgIEBgQSCgAQBqiJDRQIOBEwYAEMgNRiITBqKKBCYJJBE4xQGMQIABlBPHHgInDHQQjEAQJTCHgbFEABg8EBg5SDCgxNDABI=")) buffer: require("heatshrink").decompress(atob("ABsRqAJHkEiBA0N0uq1AIEgNVqtRqoJEgUiAAQJEioTBAAIzEl2q12oxATECQdVioJD/eqne60UCHQoADoAJBgf+xWrFIOACYUFCYo8Cj/73f70er0ROHAANUBIM//3///q1WIFAV1qtXCggJB//7CYO6keikBOHKAUDCIInClSgCgonBu4TK1W73ShBMQxkCh5OC//uFIInBi91q5PFCYISC3er//iOwXVE41UCYf+9//9AnCJopVBqEv/+/3//E4P6kUgJw4nDKAP+14TB1Xoq4hBEwYFBqgnB3Wr3e737KB/QnIqp3B32OKAYTBE4Z4BAoYnBEoRSC0fyE5ITBJ4WuCYP4J4J3CeQQFClbvBJgOqn5kBnRPKTwJMB1B4B92qEgQACJ4JTBkYnBYwOilYsBO5NUhYmB9+qxGC9TxBEYTvFqki3Y8B1Uikei3+oionIgGrO4OqwGC9H/xATK1/7E4UAnU7kATIqEAl/uE4WA12u0ATJgSgB/+ikUgnW70EFCY9AgGDE4PowEAlWowEBCZJ4BneggUgkRSBCZEAgEKJoIEBgEIAYQOCKYcVBIMqJgIEBgQSCgAQBqiJDRQIOBEwYAEMgNRiITBqKKBCYJJBE4xQGMQIABlBPHHgInDHQQjEAQJTCHgbFEABg8EBg5SDCgxNDABI="))
}; };
const needle = { const needle = {
width: 23, width: 23,
height: 11, height: 10,
bpp: 1, bpp: 1,
transparent: 0, transparent: 0,
buffer: atob("///B///D///AAAPAAAHAAAHAAAcAADz///H//8P//wA=") buffer: atob("//+B///D///AAAHgAADgAAHAAA9///j//+H//gA=")
}; };
/* /*
@ -52,139 +56,173 @@ const needle = {
// requires the myLocation app // requires the myLocation app
function loadLocation() { function loadLocation() {
location = require("Storage").readJSON(LOCATION_FILE, 1) || { "lat": 45, "lon": -71.3, "location": "Nashua" }; //{"lat":51.5072,"lon":0.1276,"location":"London"}; location = require("Storage").readJSON(LOCATION_FILE, 1) || {
"lat": 45,
"lon": -71.3,
"location": "Nashua"
}; //{"lat":51.5072,"lon":0.1276,"location":"London"};
} }
function drawMoon(shadowShape) { function drawMoon(shadowShape) {
g.setColor(0, 0, 0).fillCircle(MX, MY, RADII.arcMax + 3); g.setColor(0, 0, 0).fillCircle(MX, MY, RADII.arcMax + 3);
g.setColor(COL.moon).fillCircle(MX, MY, RADII.moon); g.setColor(COL.moon).fillCircle(MX, MY, RADII.moon - 1);
g.setColor(COL.txture).drawImage(moon_texture, MX, MY, { rotate: 0 }); g.setColor(COL.txture).drawImage(moon_texture, MX, MY, {
// TODO: can set the rotation here to the parallacticAngle from getMoonPosition rotate: 0
g.setColor(COL.shadow).fillPoly(shadowShape); });
// TODO: set rotation of the fillPoly? parallactic-mp.angle I think. // TODO: can set the rotation here to the parallacticAngle from getMoonPosition
// Use g.transformVertices to do the rotation g.setColor(COL.shadow).fillPoly(shadowShape);
// TODO: set rotation of the fillPoly? parallactic-mp.angle I think.
// Use g.transformVertices to do the rotation
} }
function drawDayRing(times) { function drawDayRing(times) {
let r_ = RADII.arcMin; let r_ = RADII.arcMin;
let rm = RADII.arcMax; let rm = RADII.arcMax;
let rd = RADII.dots; let rd = RADII.dots;
let radT = [tToRad(times[0]), tToRad(times[1])]; let radT = [tToRad(times[0]), tToRad(times[1])];
let hhmm = [require("locale").time(times[0], 1), require("locale").time(times[1], 1)]; let hhmm = [require("locale").time(times[0], 1), require("locale").time(times[1], 1)];
g.setColor(COL.day); g.setColor(COL.day);
Utils.fillArc(g, MX, MY, r_, rm, radT[0], radT[1]); Utils.fillArc(g, MX, MY, r_, rm, radT[0], radT[1]);
g.setColor(COL.night); g.setColor(COL.night);
Utils.fillArc(g, MX, MY, r_, rm, radT[1] - TAU, radT[0]); Utils.fillArc(g, MX, MY, r_, rm, radT[1] - TAU, radT[0]);
// write sunrise/sunset times // write sunrise/sunset times
g.setFont('6x8').setColor(COL.stime); g.setFont('6x8').setColor(COL.stime);
g.setFontAlign(0, 1, 3).drawString(hhmm[0], MX - rm - 2, MY); g.setFontAlign(0, 1, 3).drawString(hhmm[0], MX - rm - 2, MY);
g.setFontAlign(0, 1, 1).drawString(hhmm[1], MX + rm + 2, MY); g.setFontAlign(0, 1, 1).drawString(hhmm[1], MX + rm + 2, MY);
// draw dots // draw dots
let edges = []; let edges = [];
let isDay = false; let isDay = false;
let flag = false; let flag = false;
if (radT[1] > TAU) { if (radT[1] > TAU) {
edges = [radT[1] - TAU, radT[0]]; edges = [radT[1] - TAU, radT[0]];
g.setColor(COL.ddots); g.setColor(COL.ddots);
isDay = true; isDay = true;
} else { } else {
edges = [radT[0], radT[1]]; edges = [radT[0], radT[1]];
g.setColor(COL.ndots);
isDay = false;
}
for (var i = 0; i < 24; i++) {
let a = i * TAU / 24;
if (!flag && a > edges[0] && a < edges[1]) {
//first cross
if (isDay) {
g.setColor(COL.ndots); g.setColor(COL.ndots);
isDay = false; } else {
} g.setColor(COL.ddots);
for (var i = 0; i < 24; i++) { }
let a = i * TAU / 24; flag = true;
if (!flag && a > edges[0]) { } else if (flag && a > edges[1]) {
//first cross //second cross
if (isDay) { g.setColor(COL.ndots); } else { g.setColor(COL.ddots); } if (isDay) {
flag = true; g.setColor(COL.ddots);
} else if (flag && a > edges[1]) { } else {
//second cross g.setColor(COL.ndots);
if (isDay) { g.setColor(COL.ddots); } else { g.setColor(COL.ndots); } }
flag = false; flag = false;
}
let dotSize = (i % 3 == 0) ? 2 : 1;
let pX = MX + Math.cos(a) * rd;
let pY = MY + Math.sin(a) * rd;
g.fillCircle(pX, pY, dotSize);
}
let labels = ['6P', '12A', '6A', '12P'];
let qX = [rd - 9, 2, 11 - rd, 2];
let qY = [1, rd - 10, 1, 12 - rd];
g.setFont('4x6').setFontAlign(0, 0, 0).setColor(COL.ndots);
for (var j = 0; j < 4; j++) {
g.drawString(labels[j], MX + qX[j], MY + qY[j]);
} }
let dotSize = (i % 3 == 0) ? 2 : 1;
let pX = MX + Math.cos(a) * rd;
let pY = MY + Math.sin(a) * rd;
g.fillCircle(pX, pY, dotSize);
}
let labels = ['6P', '12A', '6A', '12P'];
let qX = [rd - 9, 2, 11 - rd, 2];
let qY = [1, rd - 10, 1, 12 - rd];
g.setFont('4x6').setFontAlign(0, 0, 0).setColor(COL.ndots);
for (var j = 0; j < 4; j++) {
g.drawString(labels[j], MX + qX[j], MY + qY[j]);
}
} }
function drawHHMM(d) { function drawHHMM(d) {
var HM = require("locale").time(d, 1 /*omit seconds*/ ).split(":"); var HM = require("locale").time(d, 1 /*omit seconds*/ ).split(":");
// write digital time // write digital time
g.setBgColor(0, 0, 0).setColor(1, 1, 1).setFontVector(45); g.setBgColor(0, 0, 0).setColor(1, 1, 1).setFontVector(45);
g.setFontAlign(1, 1, 0).drawString(" " + HM[0], MX - 20, g.getHeight() + 3); g.setFontAlign(1, 1, 0).drawString(" " + HM[0], MX - 20, g.getHeight() + 3);
g.setFontAlign(-1, 1, 0).drawString(HM[1] + " ", MX + 30, g.getHeight() + 3); g.setFontAlign(-1, 1, 0).drawString(HM[1] + " ", MX + 30, g.getHeight() + 3);
// TODO: use the meridian text AM/PM or blank for 24 hr. // TODO: use the meridian text AM/PM or blank for 24 hr.
// var meridian = require("locale").meridian(d); // var meridian = require("locale").meridian(d);
} }
function moonShade(pos, mp) { function moonShade(pos, mp) {
pos = pos !== undefined ? pos : M_POS; pos = pos !== undefined ? pos : M_POS;
mp = mp !== undefined ? mp : SunCalc.getMoonIllumination(new Date()); mp = mp !== undefined ? mp : SunCalc.getMoonIllumination(new Date());
// pos has x,y, r for the drawing, mp is from SunCalc Moon Illumination // pos has x,y, r for the drawing, mp is from SunCalc Moon Illumination
let k = mp.fraction; let k = mp.fraction;
// k is the percent along the equator of the terminator // k is the percent along the equator of the terminator
const pts = Math.min(pos.r >> 1, 32); const pts = Math.min(pos.r >> 1, 32);
// this gives r/2 pts on the way down and up, capped at 64 total for polyfill // this gives r/2 pts on the way down and up, capped at 64 total for polyfill
let a = [], let a = [],
b = [], b = [],
s1 = 1, s1 = 1,
s2 = 0; s2 = 0;
// scale s1 is 1 or -1 for fixed edge of the shadow; defined via case switches below // scale s1 is 1 or -1 for fixed edge of the shadow; defined via case switches below
// scale s2 factor for the moving edge of the shadow // scale s2 factor for the moving edge of the shadow
// need to do some computation to simplify for new/full moon if k 'close enough' to 0 or 1/-1 // need to do some computation to simplify for new/full moon if k 'close enough' to 0 or 1/-1
// //
let isWaxing = (mp.phase < 0.5); let isWaxing = (mp.phase < 0.5);
s1 = isWaxing ? -1 : 1; s1 = isWaxing ? -1 : 1;
s2 = isWaxing ? 1 - 2 * k : 2 * k - 1; s2 = isWaxing ? 1 - 2 * k : 2 * k - 1;
let tr = (pos.r + 0.5); let tr = (pos.r + 1);
for (var i = 0; i < pts; i++) { for (var i = 0; i < pts; i++) {
// down stroke on the outer shadow // down stroke on the outer shadow
var t = i * Math.PI / (pts + 1); //pts+1 so we leave the last point for the starting of going up var t = i * Math.PI / (pts + 1); //pts+1 so we leave the last point for the starting of going up
let cirX = Math.sin(t) * tr; let cirX = Math.sin(t) * tr;
let cirY = Math.cos(t) * tr; let cirY = Math.cos(t) * tr;
a.push(pos.x + s1 * cirX); //x a.push(pos.x + s1 * cirX); //x
a.push(pos.y + cirY); //y a.push(pos.y + cirY); //y
b.push(pos.x + s2 * cirX); //x for shadow edge b.push(pos.x + s2 * cirX); //x for shadow edge
b.push(pos.y - cirY); //y going up for shadow edge b.push(pos.y - cirY); //y going up for shadow edge
} }
return a.concat(b); return a.concat(b);
} }
function tToRad(date) { function tToRad(date) {
date = (date !== undefined) ? new Date(date.getTime()) : new Date(); date = (date !== undefined) ? new Date(date.getTime()) : new Date();
let milli = date - new Date(date.setHours(0, 0, 0, 0)); let milli = date - new Date(date.setHours(0, 0, 0, 0));
return (milli / DAY_MILLIS + 0.25) * TAU; return (milli / DAY_MILLIS + 0.25) * TAU;
} }
function draw() { function draw(date) {
// work out how to display the current time var d = date !== undefined ? date : new Date();
var d = new Date(), var a = tToRad(d),
a = tToRad(d); shape = moonShade(M_POS, SunCalc.getMoonIllumination(d)),
var shape = moonShade(M_POS, SunCalc.getMoonIllumination(d)); sTimes = SunCalc.getTimes(d, location.lat, location.lon),
var sTimes = SunCalc.getTimes(d, location.lat, location.lon); daylight = [sTimes.sunrise, sTimes.sunset];
var daylight = [sTimes.sunrise, sTimes.sunset]; //clear time area
//clear time area g.clearRect(Bangle.appRect); //g.setColor(0).fillRect(0, 176 - 45, 176, 176);
g.setColor(0).fillRect(0, 176 - 45, 176, 176); drawMoon(shape);
drawMoon(shape); drawDayRing(daylight);
drawDayRing(daylight); drawHHMM(d);
drawHHMM(d); // draw pointer
// draw pointer // TODO: Maybe later make this an overlay that can be removed?? -avoid drawing so much every minute/second
// TODO: Maybe later make this an overlay that can be removed?? -avoid drawing so much every minute/second g.setColor(COL.needle).drawImage(needle, MX + RADII.needle * Math.cos(a), MY + RADII.needle * Math.sin(a), {
g.setColor(COL.needle).drawImage(needle, MX + RADII.needle * Math.cos(a), MY + RADII.needle * Math.sin(a), { rotate: a }); rotate: a
});
} }
/*
const shotTimes = [1720626960000, 1729184400000, 1738298880000, 1717575420000];
let desc =`first quarter -2 days moon at 10:20 in the summer
jun 10 2024 10:56
full moon at 12 noon near fall equinox
Sep 17 2024 12:00
new moon at 11pm in winter
dec 30 2024 23:48
3rd quarter moon at 03:17 am
May 5 2024 03:17`
function screenshots(times) {
let d = new Date();
for (let t of times) {
d.setTime(t);
draw(d);
g.dump();
}
}
*/
// Clear the screen once, at startup // Clear the screen once, at startup
g.reset(); g.reset();
// requires the myLocation app // requires the myLocation app
@ -197,12 +235,12 @@ draw();
var secondInterval = setInterval(draw, 10000); //was 1000 var secondInterval = setInterval(draw, 10000); //was 1000
// Stop updates when LCD is off, restart when on // Stop updates when LCD is off, restart when on
Bangle.on('lcdPower', on => { Bangle.on('lcdPower', on => {
if (secondInterval) clearInterval(secondInterval); if (secondInterval) clearInterval(secondInterval);
secondInterval = undefined; secondInterval = undefined;
if (on) { if (on) {
secondInterval = setInterval(draw, 10000); //was 1000 secondInterval = setInterval(draw, 10000); //was 1000
draw(); // draw immediately draw(); // draw immediately
} }
}); });
/* Show launcher when middle button pressed /* Show launcher when middle button pressed
This should be done *before* Bangle.loadWidgets so that This should be done *before* Bangle.loadWidgets so that
@ -210,5 +248,13 @@ widgets know if they're being loaded into a clock app or not */
Bangle.setUI("clock"); Bangle.setUI("clock");
// Load widgets // Load widgets
Bangle.loadWidgets(); Bangle.loadWidgets();
g.setTheme({fg:"#fff", bg:"#000", fg2:"#fff", bg2:"#004", fgH:"#fff", bgH:"#00f", dark:true }); g.setTheme({
fg: "#fff",
bg: "#000",
fg2: "#fff",
bg2: "#004",
fgH: "#fff",
bgH: "#00f",
dark: true
});
Bangle.drawWidgets(); Bangle.drawWidgets();

View File

@ -1,10 +1,10 @@
{ "id": "daymoon", { "id": "daymoon",
"name": "DayMoon Circadian Clock", "name": "DayMoon Circadian Clock",
"version": "0.04", "version": "0.05",
"dependencies": {"mylocation":"app"}, "dependencies": {"mylocation":"app"},
"description": "A 24 hour clockface showing the Moon Phase and portion of the day that the Sun is up inspired by Matthew Clark's *Fair Circadian* Pebble watchface", "description": "A 24 hour clockface showing the Moon Phase and portion of the day that the Sun is up inspired by Matthew Clark's *Fair Circadian* Pebble watchface",
"icon": "daymoon.png", "icon": "daymoon.png",
"screenshots": [{"url":"screenshot.png"}], "screenshots": [{"url":"s1.png"},{"url":"s2.png"},{"url":"s3.png"},{"url":"s4.png"}],
"type": "clock", "type": "clock",
"tags": "clock,moon,lunar", "tags": "clock,moon,lunar",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS2"],

BIN
apps/daymoon/s1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
apps/daymoon/s2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
apps/daymoon/s3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
apps/daymoon/s4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB