Add new Line Clock application
This commit introduces the Line Clock application. The Line Clock is a readable analog clock that is customizable via the theme configuration. It includes a JavaScript logic file, an app icon, a metadata JSON file, a README, and a ChangeLog file. This also includes the MIT license file.master
parent
68e5eff15d
commit
7ab3fe2f37
|
|
@ -0,0 +1 @@
|
||||||
|
0.1 init app
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Paul Spenke
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Line Clock
|
||||||
|
|
||||||
|
This app displays a simple, different looking, analog clock. It considers the
|
||||||
|
currently configured "theme" (and may therefore look different than shown in
|
||||||
|
the screenshot on your watch depending on which theme you prefer).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT License](LICENSE)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwgYMJh/4AgUD+AeKgIRDj/+n41O/4RQABcfIJYAEKZgAkL4U/8ARNBwIRP/+AGx6YBPSH/4ASPh/A/hfDAAZAHg/8gP/LguSoARHEwIRFiVJkDCFjgRHgEJkg4CcwQjIAAMEHAUDCoIRB46kIHAkH//xLIw4I8eAnCNKHAYAO/xxEABg4ByASPHAkBKAbUE/5xGhP//wRFv4RDOIYIB//ACQr1FHAIRJAA0TCAP/ZwIALgYRJVowRCj/4BIkBLIgABgRHC/KqFaI4RC5MkJBlPR4UECJizJJwoAKCKImVQAwAJv0HL5S6CbwIjLCKMAn4RDh0/LMKMhWaYAKA="))
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
|
|
@ -0,0 +1,273 @@
|
||||||
|
const handWidth = 6;
|
||||||
|
const hourRadius = 4;
|
||||||
|
const hourWidth = 8;
|
||||||
|
const hourLength = 40;
|
||||||
|
const hourSLength = 20;
|
||||||
|
const radius = 220;
|
||||||
|
const lineOffset = 115;
|
||||||
|
const hourOffset = 32;
|
||||||
|
const numberOffset = 85;
|
||||||
|
const numberSize = 22;
|
||||||
|
|
||||||
|
let settings = {
|
||||||
|
showLock: true
|
||||||
|
};
|
||||||
|
|
||||||
|
let gWidth = g.getWidth(), gCenterX = gWidth/2;
|
||||||
|
let gHeight = g.getHeight(), gCenterY = gHeight/2;
|
||||||
|
|
||||||
|
let currentTime = new Date();
|
||||||
|
let currentHour = currentTime.getHours();
|
||||||
|
let currentMinute = currentTime.getMinutes();
|
||||||
|
|
||||||
|
let drawTimeout;
|
||||||
|
|
||||||
|
function imgLock() {
|
||||||
|
return {
|
||||||
|
width : 16, height : 16, bpp : 1,
|
||||||
|
transparent : 0,
|
||||||
|
buffer : E.toArrayBuffer(atob("A8AH4A5wDDAYGBgYP/w//D/8Pnw+fD58Pnw//D/8P/w="))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the angle of the hour hand for the current time.
|
||||||
|
*
|
||||||
|
* @returns {number} The angle of the hour hand in degrees.
|
||||||
|
*/
|
||||||
|
function getHourHandAngle() {
|
||||||
|
let hourHandAngle = 30 * currentHour;
|
||||||
|
hourHandAngle += 0.5 * currentMinute;
|
||||||
|
return hourHandAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hourAngle = getHourHandAngle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts degrees to radians.
|
||||||
|
*
|
||||||
|
* @param {number} degrees - The degrees to be converted to radians.
|
||||||
|
* @return {number} - The equivalent value in radians.
|
||||||
|
*/
|
||||||
|
function degreesToRadians(degrees) {
|
||||||
|
return degrees * (Math.PI / 180);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotates an array of points around a given angle and radius.
|
||||||
|
*
|
||||||
|
* @param {Array} points - The array of points to be rotated.
|
||||||
|
* @param {number} angle - The angle in degrees to rotate the points.
|
||||||
|
* @param {number} rad - The radius to offset the rotation.
|
||||||
|
* @returns {Array} - The array of rotated points.
|
||||||
|
*/
|
||||||
|
function rotatePoints(points, angle, rad) {
|
||||||
|
const ang = degreesToRadians(angle);
|
||||||
|
const hAng = degreesToRadians(hourAngle);
|
||||||
|
const rotatedPoints = [];
|
||||||
|
points.map(function(point) {
|
||||||
|
return {
|
||||||
|
x: point.x * Math.cos(ang) - point.y * Math.sin(ang),
|
||||||
|
y: point.x * Math.sin(ang) + point.y * Math.cos(ang)
|
||||||
|
};
|
||||||
|
}).forEach(function(point) {
|
||||||
|
rotatedPoints.push(point.x + gCenterX - (rad * Math.sin(hAng)));
|
||||||
|
rotatedPoints.push(point.y + gCenterY + (rad * Math.cos(hAng)));
|
||||||
|
});
|
||||||
|
return rotatedPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a hand on the canvas.
|
||||||
|
*
|
||||||
|
* @function drawHand
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function drawHand() {
|
||||||
|
g.setColor(0xF800);
|
||||||
|
const halfWidth = handWidth / 2;
|
||||||
|
|
||||||
|
const points = [{
|
||||||
|
x: -halfWidth,
|
||||||
|
y: -gHeight
|
||||||
|
}, {
|
||||||
|
x: halfWidth,
|
||||||
|
y: -gHeight
|
||||||
|
}, {
|
||||||
|
x: halfWidth,
|
||||||
|
y: gHeight
|
||||||
|
}, {
|
||||||
|
x: -halfWidth,
|
||||||
|
y: gHeight
|
||||||
|
}];
|
||||||
|
|
||||||
|
g.fillPolyAA(rotatePoints(points, hourAngle, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the hour coordinates for a given small flag.
|
||||||
|
* @param {boolean} small - Determines if the flag is small.
|
||||||
|
* @returns {Array} - An array of hour coordinates.
|
||||||
|
*/
|
||||||
|
function getHourCoordinates(small) {
|
||||||
|
const dist = small ? (hourSLength - hourLength) : 0;
|
||||||
|
const halfWidth = hourWidth / 2;
|
||||||
|
const gh = gHeight + lineOffset;
|
||||||
|
return [{
|
||||||
|
x: -halfWidth,
|
||||||
|
y: -gh - dist
|
||||||
|
}, {
|
||||||
|
x: halfWidth,
|
||||||
|
y: -gh - dist
|
||||||
|
}, {
|
||||||
|
x: halfWidth,
|
||||||
|
y: -gh + hourLength
|
||||||
|
}, {
|
||||||
|
x: -halfWidth,
|
||||||
|
y: -gh + hourLength
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign the given time to the hour dot on the clock face.
|
||||||
|
*
|
||||||
|
* @param {number} a - The time value to assign to the hour dot.
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
function hourDot(a) {
|
||||||
|
const h = gHeight + lineOffset;
|
||||||
|
const rotatedPoints = rotatePoints(
|
||||||
|
[{
|
||||||
|
x: 0,
|
||||||
|
y: -h + hourLength - (hourRadius / 2)
|
||||||
|
}], a, radius
|
||||||
|
);
|
||||||
|
g.fillCircle(rotatedPoints[0], rotatedPoints[1], hourRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an hour into a number and display it on the clock face.
|
||||||
|
*
|
||||||
|
* @param {number} a - The hour to be converted (between 0 and 360 degrees).
|
||||||
|
*/
|
||||||
|
function hourNumber(a) {
|
||||||
|
const h = gHeight + lineOffset;
|
||||||
|
const rotatedPoints = rotatePoints(
|
||||||
|
[{
|
||||||
|
x: 0,
|
||||||
|
y: -h + hourLength + hourOffset
|
||||||
|
}], a, radius
|
||||||
|
);
|
||||||
|
g.drawString(String(a / 30), rotatedPoints[0], rotatedPoints[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a number on the display.
|
||||||
|
*
|
||||||
|
* @param {number} n - The number to be drawn.
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
function drawNumber(n) {
|
||||||
|
const h = gHeight + lineOffset;
|
||||||
|
const halfWidth = handWidth / 2;
|
||||||
|
const rotatedPoints = rotatePoints(
|
||||||
|
[{
|
||||||
|
x: 0,
|
||||||
|
y: -h + hourLength + numberOffset
|
||||||
|
}], hourAngle, radius
|
||||||
|
);
|
||||||
|
g.setColor(0xF800);
|
||||||
|
g.fillCircle(rotatedPoints[0], rotatedPoints[1], numberSize+ halfWidth);
|
||||||
|
g.setColor(g.theme.bg);
|
||||||
|
g.fillCircle(rotatedPoints[0], rotatedPoints[1], numberSize - halfWidth);
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
g.setFont("Vector:"+numberSize);
|
||||||
|
g.drawString(String(n), rotatedPoints[0], rotatedPoints[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hourPoints = getHourCoordinates(false);
|
||||||
|
const hourSPoints = getHourCoordinates(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an hour on a clock face.
|
||||||
|
*
|
||||||
|
* @param {number} h - The hour to be drawn on the clock face.
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
function drawHour(h) {
|
||||||
|
if (h === 0) { h= 12; }
|
||||||
|
if (h === 13) { h= 1; }
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
g.setFont("Vector:32");
|
||||||
|
const a = h * 30;
|
||||||
|
g.fillPolyAA(rotatePoints(hourPoints, a, radius));
|
||||||
|
g.fillPolyAA(rotatePoints(hourSPoints, a + 15, radius));
|
||||||
|
hourNumber(a);
|
||||||
|
hourDot(a + 5);
|
||||||
|
hourDot(a + 10);
|
||||||
|
hourDot(a + 20);
|
||||||
|
hourDot(a + 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueDraw() {
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = setTimeout(function() {
|
||||||
|
drawTimeout = undefined;
|
||||||
|
draw();
|
||||||
|
}, 60000 - (Date.now() % 60000));
|
||||||
|
}
|
||||||
|
|
||||||
|
function lockListenerBw(isLocked) {
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = undefined;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
Bangle.on('lock', lockListenerBw);
|
||||||
|
|
||||||
|
Bangle.setUI({
|
||||||
|
mode : "clock",
|
||||||
|
remove : function() {
|
||||||
|
Bangle.removeListener('lock', lockListenerBw);
|
||||||
|
if (drawTimeout) clearTimeout(drawTimeout);
|
||||||
|
drawTimeout = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a clock on the canvas using the current time.
|
||||||
|
*
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
function draw() {
|
||||||
|
queueDraw();
|
||||||
|
currentTime = new Date();
|
||||||
|
currentHour = currentTime.getHours();
|
||||||
|
if (currentHour > 12) {
|
||||||
|
currentHour -= 12;
|
||||||
|
}
|
||||||
|
currentMinute = currentTime.getMinutes();
|
||||||
|
|
||||||
|
hourAngle = getHourHandAngle();
|
||||||
|
|
||||||
|
g.clear();
|
||||||
|
g.setFontAlign(0, 0);
|
||||||
|
|
||||||
|
g.setColor(g.theme.bg);
|
||||||
|
g.fillRect(0, 0, gWidth, gHeight);
|
||||||
|
|
||||||
|
if(settings.showLock && Bangle.isLocked()){
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
g.drawImage(imgLock(), gWidth-16, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawHour(currentHour);
|
||||||
|
drawHour(currentHour-1);
|
||||||
|
drawHour(currentHour+1);
|
||||||
|
|
||||||
|
|
||||||
|
drawHand();
|
||||||
|
drawNumber(currentMinute);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw();
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
{ "id": "line_clock",
|
||||||
|
"name": "Line Clock",
|
||||||
|
"shortName":"Line Clock",
|
||||||
|
"version":"0.1",
|
||||||
|
"description": "a readable analog clock",
|
||||||
|
"icon": "app-icon.png",
|
||||||
|
"type": "clock",
|
||||||
|
"tags": "clock",
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"allow_emulator": true,
|
||||||
|
"screenshots": [{"url":"app-screenshot.png"}],
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"line_clock.app.js","url":"app.js"},
|
||||||
|
{"name":"line_clock.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue