forked from FOSS/BangleApps
commit
30e982f06a
22
apps.json
22
apps.json
|
@ -1690,5 +1690,25 @@
|
|||
{"name":"getup.settings.js","url":"settings.js"},
|
||||
{"name":"getup.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "gallifr",
|
||||
"name": "Time Traveller's Chronometer",
|
||||
"shortName": "Time Travel Clock",
|
||||
"icon": "gallifr.png",
|
||||
"version": "0.01",
|
||||
"description": "A clock for time travellers. The light pie segment shows the minutes, the black circle, the hour. The dial itself reads 'time' just in case you forget.",
|
||||
"tags": "clock",
|
||||
"readme": "README.md",
|
||||
"type": "clock",
|
||||
"allow_emulator":true,
|
||||
"storage": [
|
||||
{ "name": "gallifr.app.js", "url": "app.js" },
|
||||
{ "name": "gallifr.img", "url": "app-icon.js", "evaluate": true },
|
||||
{ "name": "gallifr.settings.js", "url": "settings.js" }
|
||||
],
|
||||
"data": [
|
||||
{"name":"app.json"}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
|
@ -0,0 +1 @@
|
|||
0.01: First released version
|
|
@ -0,0 +1,20 @@
|
|||
# Time Traveller's Clock
|
||||
|
||||
The time travelling wristwatch is for those who are so attuned to the ebb and flow of time that they no longer require antiquated numbers to read the time.
|
||||
|
||||
For those that need some tuition in the ways of the time traveller, the light coloured segment of the pie chart provides a traditional readout of minute. The black sphere that revolves around the edge of the display provides an indication of the hour.
|
||||
|
||||
## Features
|
||||
|
||||
The following aspects are customisable using the App Loader Menu system:
|
||||
|
||||
1. Colour; the dial has four colour schemes:
|
||||
- shades of green
|
||||
- shades of red
|
||||
- shades of blue
|
||||
- a 1980's scheme
|
||||
2. Widgets; these can be turned on or off - when turned off, the dial uses the whole screen and is slightly larger
|
||||
3. Decoration; for those attuned to the time streams, the dial itself reads 'time'. For those who don't need to be reminded what the dial is for, this can be optionally turned off.
|
||||
|
||||
## Code description
|
||||
The code includes some functions that others may find useful in creating their own applications. These are explained in my robot-building blog [here](https://k9-build.blogspot.com/).
|
|
@ -0,0 +1 @@
|
|||
E.toArrayBuffer(atob("MDAIAAAAAAAAAAAAAAAABgYAAAAAAAYGDAwSGBgYGBISBgAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAwMEhISGBgYHh4eHh4eHhgSDAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABhgYEhIYGBgYEhIYHh4eHh4eHh4eHhgMAAAGAAAAAAAAAAAAAAAAAAAAAAAGAAASHh4eGBISEhISEhIYHh4eHh4eHh4eHh4eGAYABgAAAAAAAAAAAAAAAAAAAAYADBgeHh4eGBIYEhISEhIYHh4eHh4eHh4eHh4eHh4SAAYAAAAAAAAAAAAAAAAABgAMHh4eHh4eHhgSEhIYGBgYHh4eHh4eHh4eHh4eHh4eEgAGAAAAAAAAAAAAAAAGABIeHh4eHh4eHhgSGBgSEhIMEhgYHh4eHh4eHh4eHh4eHhIABgAAAAAAAAAAAAYAEh4eHh4eHh4eHh4SEgwAAAAAAAAAABIYHh4eHh4eHh4eHh4SAAYAAAAAAAAABgAMHh4eHh4eHh4eHhIGAAAMEhISGB4YEgAABhIeHh4eHh4eHh4eEgAGAAAAAAAAAAweHh4eHh4eHh4YDAAABhIYGBgYHh4eHhgGAAAMGB4eHh4eHh4eHhIABgAAAAAAABgeHh4eHh4eHhISGAAMGBgSEhIYHh4eHh4eDAASEgweHh4eHh4eHh4MAAAAAAYAEh4eHh4eHh4eBhIeDAAeHhISGBgYHh4eHh4eGAAGHhIAHh4eHh4eHh4YAAAAAAAGHh4eHh4eHh4GEh4YABgeHhgSEhIMEhgeHh4eHhgAEh4SBh4eHh4eHh4eEgAGBgAYHh4eHh4eHhISHh4MDB4eHhgSDAAAAAAMHh4eHh4AAB4eEgweHh4eHh4eHgAAAAYeHh4eHh4eGBIeHh4AEh4eHh4MABISGBgADB4eHh4SABgeHgwSHh4eHh4eHhIAABIeHh4eHh4eBh4eHhgAGB4eHhgADBgYHh4SABgeHh4YABgeHhgAHh4eHh4eHhgAABgeHh4eHh4SEh4eHhgAGB4eHhgAEhgYHh4YABgeHh4YABIeHh4MEh4eHh4eHh4MBh4eHh4eHh4MHh4eHhgAGB4eHhgAEhgYHh4YABgeHh4YABgeHh4YAB4eHh4eHh4SDB4eHh4eHhgSHh4eHhgAGB4eHh4ADBIYHh4MAB4eHh4YABgeHh4eDBIeHh4eHh4YEh4eHh4eHhIYHh4eHhgADB4eHh4YAAYSEgwAGB4eHh4SAAweHh4eEgYYHh4eHh4YGB4eHh4eHgwYHh4eHh4MAB4eHh4eGAYAAAYYHh4eHh4AEh4YGBgSAAAAABgeHh4eGB4eHh4eHgweHh4eHh4YABIeHh4eHh4YGB4eHh4eHhIAGB4eGAAAEgYSEgAYHh4eGB4eHh4eGBIeHh4eHh4eDAAYHh4eHh4eHh4eHh4eGAASHh4eGAAYHgwYHgYAHh4eGB4eHh4eGAweHh4eHh4MAAYAGB4eHh4eHh4eHh4YAAweHh4eEgAeHgwYHhIAGB4eHh4eHh4eGAweHh4eGAYMGB4MABIeHh4eHh4eHhIADB4eHh4eEgAeHgwYHhIAGB4eHh4eHh4eGAweHhgSEhgeHh4YAAAADBIYGBIMAAASHh4eHh4eGAAYHgweHgwGHh4eHh4eHh4eGAYeGBgeHh4eHh4MABgSDAAAAAAMEhgeHh4eHh4eHgwAEgwYEgAYHh4eGB4eHh4eGAAYHh4eHh4eHhgAGB4eHh4YGB4eHh4eHh4eHh4eHh4MAAAAABgeHh4YGB4eHh4YGAYSHh4eHh4eHgASHh4eHh4eHh4eHh4eHh4eHh4eHh4eEgwYHh4eHh4YEh4YEgwYHhIMHh4eHh4eDBIeHh4eHh4eHh4eHh4eHh4eHh4eHh4eEhgeHh4eHh4SDBIAEh4eHh4AGB4eHh4YEh4eHh4eHh4YGB4eHh4eHh4eHh4eHh4eDB4eHh4eHh4MAAwYHh4eHh4SDB4eHhgGEhgYHh4YEhgSEhgYGB4eHh4eHh4eHh4SEh4eHh4eHh4GABgeHh4eHh4eABgeDAAAAAAABgwYHh4eHh4eGBIYHh4eHh4eHh4GHh4eHh4eHhgAABIeHh4eHh4eGAwGAAYAAAYGAAASHh4eHh4eHh4SEh4eHh4eHhIYHh4eHh4eHhIAAAAYHh4eHh4eGAAABgAAAAAAAAYAEh4eHh4eHh4eGBIeHh4eEhIeHh4eHh4eGAAABgASHh4eHh4eDAAGAAAAAAAAAAAGABgeHh4eHh4eHhIYHh4SBh4eHh4eHh4eEgAGAAYAGB4eHh4YAAAAAAAAAAAAAAAGABIeHh4eHh4eHh4SHhIGHh4eHh4eHh4YAAAAAAAABh4eHh4YAAYAAAAAAAAAAAAGAAweHh4eHh4eHh4YDBIeHh4eHh4eHh4GAAAAAAAGABIeHh4MAAAAAAAAAAAAAAAGAAYeHh4eHh4eHh4SGB4eHh4eHh4eHhIABgAAAAAABgASHhIABgAAAAAAAAAAAAAGAAweHh4eHh4eHh4eHh4eHh4eHh4eEgAGAAAAAAAAAAAADAYSAAAAAAAAAAAAAAAGABIeHh4eHh4eHh4eHh4eHh4eHh4YAAAAAAAAAAAAAAAAABIeBgAGAAAAAAAAAAAGABgeHh4eHh4eHh4eHh4eHh4eHhgAAAAAAAAAAAAAAAAAAAAYGAAAAAAAAAAAAAYADB4eHh4eHh4eHh4eHh4eHh4eEgAAAAAAAAAAAAAAAAAAAAAAEgwAAAAABgYGBgAAGB4eHh4eHh4eHh4eHh4eHh4SAAAAAAAAAAAAAAAAAAAAAAAAAAYGAAAAAAAAAAweHh4eHh4eHh4eHh4eHh4eGAYABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAYMDAwSGB4eHh4eHh4eHh4eHh4eHhgSAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYMEhgeHh4eHh4eHh4eHh4eHh4YEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYMEhgYHh4eHh4eGBgSDAYAAAAGAAAAAAAAAAAAAAAA"))
|
|
@ -0,0 +1,247 @@
|
|||
//
|
||||
// Time Travellers Watch
|
||||
// Written May 2020 by Richard Hopkins
|
||||
// based on a skeleton app by Gordon Williams
|
||||
//
|
||||
const locale = require('locale');
|
||||
let timer = null;
|
||||
let currentDate = new Date();
|
||||
const cirRad = 2*Math.PI;
|
||||
const proportion = 0.3; // relative size of hour hand
|
||||
const thickness = 4; // thickness of decorative lines
|
||||
// retrieve settings from menu
|
||||
let settings = require('Storage').readJSON('app.json',1)||{};
|
||||
const decoration = !settings.decoration;
|
||||
const widgets = !settings.widgets;
|
||||
if (widgets) {
|
||||
widgetHeight = 24;}
|
||||
else {
|
||||
widgetHeight = 0;}
|
||||
const colours = ["green","red","blue","80s"];
|
||||
const colour = colours[settings.colour];
|
||||
const centerX = Math.round(g.getWidth() / 2);
|
||||
const centerY = widgetHeight + Math.round((g.getHeight()-widgetHeight) / 2);
|
||||
const radius = Math.round(Math.min(g.getWidth()/2,(g.getHeight()-widgetHeight) / 2));
|
||||
|
||||
const drawSegment = (params) => {
|
||||
angle1 = params.start/360*cirRad;
|
||||
angle2 = (params.start + params.arc)/360*cirRad;
|
||||
segRadius = Math.round(params.radius*radius);
|
||||
x = centerX + (params.x * radius);
|
||||
y = centerY - (params.y *radius);
|
||||
g.setColor(0,0,0);
|
||||
incr = cirRad/15;
|
||||
for (i = angle1; i < angle2; i=i+incr) {
|
||||
brush = thickness * (angle2-angle1) /angle2;
|
||||
points = [
|
||||
x + Math.sin(i) * (segRadius+brush),
|
||||
y - Math.cos(i) * (segRadius+brush),
|
||||
x + Math.sin(i+incr) * (segRadius+brush),
|
||||
y - Math.cos(i+incr) * (segRadius+brush),
|
||||
x + Math.sin(i+incr) * (segRadius-brush),
|
||||
y - Math.cos(i+incr) * (segRadius-brush),
|
||||
x + Math.sin(i) * (segRadius-brush),
|
||||
y - Math.cos(i) * (segRadius-brush)
|
||||
];
|
||||
g.fillPoly(points);
|
||||
}
|
||||
};
|
||||
|
||||
const drawThickLine = (params) => {
|
||||
g.setColor(0,0,0);
|
||||
from = {
|
||||
x: centerX + (params.fromX * radius),
|
||||
y: centerY - (params.fromY * radius)
|
||||
};
|
||||
to = {
|
||||
x: centerX + (params.toX * radius),
|
||||
y: centerY - (params.toY * radius)
|
||||
};
|
||||
vec = {};
|
||||
vec.x = to.x - from.x;
|
||||
vec.y = to.y - from.y;
|
||||
pVec = {};
|
||||
pVec.x = vec.y;
|
||||
pVec.y = -vec.x;
|
||||
length = Math.sqrt(pVec.x * pVec.x + pVec.y * pVec.y);
|
||||
nVec = {};
|
||||
nVec.x = pVec.x / length;
|
||||
nVec.y = pVec.y / length;
|
||||
array = [
|
||||
from.x + nVec.x * thickness,
|
||||
from.y + nVec.y * thickness,
|
||||
from.x - nVec.x * thickness,
|
||||
from.y - nVec.y * thickness,
|
||||
to.x + nVec.x * thickness,
|
||||
to.y + nVec.y * thickness,
|
||||
to.x - nVec.x * thickness,
|
||||
to.y - nVec.y * thickness
|
||||
];
|
||||
g.fillPoly(array);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const drawHands = () => {
|
||||
drawMinuteHand();
|
||||
drawHourHand();
|
||||
if (decoration) {
|
||||
drawDecoration();
|
||||
}
|
||||
};
|
||||
|
||||
const drawDecoration = () => {
|
||||
params = {
|
||||
start: 210,
|
||||
arc: 295,
|
||||
radius: 0.7,
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
drawSegment(params);
|
||||
params = {
|
||||
start: 290,
|
||||
arc: 135,
|
||||
radius: 0.4,
|
||||
x: 0,
|
||||
y: -0.7
|
||||
};
|
||||
drawSegment(params);
|
||||
params = {
|
||||
start: 0,
|
||||
arc: 360,
|
||||
radius: 0.4,
|
||||
x: 0,
|
||||
y: 0.3
|
||||
};
|
||||
drawSegment(params);
|
||||
params = {
|
||||
start: 0,
|
||||
arc: 360,
|
||||
radius: 0.15,
|
||||
x: 0,
|
||||
y: 0.3
|
||||
};
|
||||
drawSegment(params);
|
||||
params = {
|
||||
start: 0,
|
||||
arc: 360,
|
||||
radius: 0.15,
|
||||
x: 0.7,
|
||||
y: 0
|
||||
};
|
||||
drawSegment(params);
|
||||
params = {
|
||||
fromX: 0.4,
|
||||
fromY: 0.2,
|
||||
toX: 0.6,
|
||||
toY: 0.1
|
||||
};
|
||||
drawThickLine(params);
|
||||
params = {
|
||||
fromX: -0.2,
|
||||
fromY: -0.05,
|
||||
toX: -0.7,
|
||||
toY: -0.7
|
||||
};
|
||||
drawThickLine(params);
|
||||
params = {
|
||||
fromX: -0.3,
|
||||
fromY: 0.05,
|
||||
toX: -0.95,
|
||||
toY: -0.3
|
||||
};
|
||||
drawThickLine(params);
|
||||
};
|
||||
|
||||
const drawMinuteHand = () => {
|
||||
angle = currentDate.getMinutes()/60 * cirRad;
|
||||
//angle = currentDate.getSeconds()/60 * cirRad;
|
||||
switch(colour) {
|
||||
case "red":
|
||||
g.setColor(1,0,0);
|
||||
break;
|
||||
case "green":
|
||||
g.setColor(0,1,0);
|
||||
break;
|
||||
case "blue":
|
||||
g.setColor(0,0,1);
|
||||
break;
|
||||
case "80s":
|
||||
g.setColor(1,0,0);
|
||||
break;
|
||||
default:
|
||||
g.setColor(0,1,0);
|
||||
}
|
||||
|
||||
var points = [centerX,centerY];
|
||||
for (i = 0; i < angle; i=i+cirRad/60) {
|
||||
points.push(Math.round(centerX + Math.sin(i) * radius),
|
||||
Math.round(centerY - Math.cos(i) * radius));
|
||||
}
|
||||
g.fillPoly(points);
|
||||
};
|
||||
|
||||
const drawHourHand = () => {
|
||||
g.setColor(0,0,0);
|
||||
//angle = currentDate.getMinutes()/60 * cirRad;
|
||||
angle = currentDate.getHours()/12 * cirRad;
|
||||
g.fillCircle(
|
||||
Math.round(centerX + Math.sin(angle) * radius * (1-proportion)),
|
||||
Math.round(centerY - Math.cos(angle) * radius * (1-proportion)),
|
||||
radius * proportion
|
||||
);
|
||||
};
|
||||
|
||||
const drawClockFace = () => {
|
||||
switch(colour) {
|
||||
case "red":
|
||||
g.setColor(0.8,0.3,0);
|
||||
break;
|
||||
case "green":
|
||||
g.setColor(0.1,0.7,0);
|
||||
break;
|
||||
case "blue":
|
||||
g.setColor(0,0.3,0.8);
|
||||
break;
|
||||
case "80s":
|
||||
g.setColor(1,1,1);
|
||||
break;
|
||||
default:
|
||||
g.setColor(0.1,0.7,0);
|
||||
}
|
||||
g.fillCircle(centerX,centerY,radius*0.98);
|
||||
};
|
||||
|
||||
const drawAll = () => {
|
||||
currentDate = new Date();
|
||||
g.clear();
|
||||
if (widgets) {Bangle.drawWidgets();}
|
||||
drawClockFace();
|
||||
drawHands();
|
||||
};
|
||||
|
||||
|
||||
const startTimers = () => {
|
||||
//timer = setInterval(drawAll, 1000);
|
||||
timer = setInterval(drawAll, 1000*20);
|
||||
};
|
||||
|
||||
Bangle.on('lcdPower', (on) => {
|
||||
if (on) {
|
||||
startTimers();
|
||||
drawAll();
|
||||
} else {
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
g.clear();
|
||||
startTimers();
|
||||
Bangle.loadWidgets();
|
||||
drawAll();
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });
|
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
|
@ -0,0 +1,33 @@
|
|||
// make sure to enclose the function in parentheses
|
||||
(function (back) {
|
||||
let settings = require('Storage').readJSON('app.json',1)||{};
|
||||
let colours = ["green","red","blue","80s"];
|
||||
let onoff = ["on","off"];
|
||||
function save(key, value) {
|
||||
settings[key] = value;
|
||||
require('Storage').write('app.json',settings);
|
||||
}
|
||||
const appMenu = {
|
||||
'': {'title': 'Clock Settings'},
|
||||
'< Back': back,
|
||||
'Colour': {
|
||||
value: 0|settings['colour'],
|
||||
min:0,max:3,
|
||||
format: m => colours[m],
|
||||
onchange: m => {save('colour', m)}
|
||||
},
|
||||
'Widgets': {
|
||||
value: 0|settings['widgets'],
|
||||
min:0,max:1,
|
||||
format: m => onoff[m],
|
||||
onchange: m => {save('widgets', m)}
|
||||
},
|
||||
'Decoration': {
|
||||
value: 0|settings['decoration'],
|
||||
min:0,max:1,
|
||||
format: m => onoff[m],
|
||||
onchange: m => {save('decoration', m)}
|
||||
}
|
||||
};
|
||||
E.showMenu(appMenu)
|
||||
})
|
|
@ -11,6 +11,6 @@
|
|||
"start": "npx http-server"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": "^7.1.1"
|
||||
"acorn": "^7.2.0"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue