forked from FOSS/BangleApps
Merge branch 'master' of github.com:espruino/BangleApps
commit
badc82f4fc
18
apps.json
18
apps.json
|
@ -2160,7 +2160,7 @@
|
|||
"name": "STL file viewer",
|
||||
"shortName":"ViewSTL",
|
||||
"icon": "icons8-octahedron-48.png",
|
||||
"version":"0.01",
|
||||
"version":"0.02",
|
||||
"description": "This app allows you to view STL 3D models on your watch",
|
||||
"tags": "tool",
|
||||
"readme": "README.md",
|
||||
|
@ -2171,5 +2171,21 @@
|
|||
{"name":"cube.stl","url":"cube.stl"},
|
||||
{"name":"icosa.stl","url":"icosa.stl"}
|
||||
]
|
||||
},
|
||||
{ "id": "worldclock",
|
||||
"name": "World Clock - 4 time zones",
|
||||
"shortName":"World Clock",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"description": "Current time zone plus up to four others",
|
||||
"tags": "clock",
|
||||
"type" : "clock",
|
||||
"custom": "custom.html",
|
||||
"storage": [
|
||||
{"name":"worldclock.app.js","url":"app.js"},
|
||||
{"name":"worldclock.settings.json"},
|
||||
{"name":"worldclock.img","url":"worldclock-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New app!
|
||||
0.02: Add accelerometer/compass viewing mode
|
|
@ -14,10 +14,11 @@ The app supports 4 different rendering modes, swiping right-to-left on the touch
|
|||
- wireframe, only edges between non-coplanar facets visible
|
||||
- wireframe, all facet (triangle) edges visible
|
||||
|
||||
There are two different rotation modes that slightly alter the function of buttons 1 and 3, swiping left-to-right toggles between the two modes:
|
||||
There are three different rotation modes that slightly alter the function of buttons 1 and 3, swiping left-to-right cycles through the modes:
|
||||
- free rotation: button 1 zooms in, button 3 out
|
||||
- Z-axis (vertical axis) rotation: buttons 1 and 3 tilt the Z-axis
|
||||
- align rotation with compass and accelerometer readings: button 1 zooms in, button 3 out
|
||||
|
||||
There is currently no interface to upload STL files to the watch, the web IDE storage icon can be used instead.
|
||||
A future version might contain rotation based on accelerometer/magnetometer readings.
|
||||
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ var p_rnormals;
|
|||
var lastTime;
|
||||
var nFrames = 0;
|
||||
var interv;
|
||||
var qZrot = false;
|
||||
var qZrot = 0;
|
||||
var qWireframe = 0;
|
||||
var zBeta = 0;
|
||||
|
||||
|
@ -195,28 +195,6 @@ int findSlot(float *p, float *x, int len) {
|
|||
p[3*len+2] = x[2];
|
||||
return len;
|
||||
}
|
||||
typedef unsigned int uint32_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
/*
|
||||
https://github.com/espruino/Espruino/blob/master/targetlibs/nrf5x_12/components/toolchain/cmsis/include/cmsis_gcc.h
|
||||
*/
|
||||
__attribute__( ( always_inline ) ) static inline uint32_t __get_FPSCR(void)
|
||||
{
|
||||
uint32_t result;
|
||||
/* Empty asm statement works as a scheduling barrier */
|
||||
__asm volatile ("");
|
||||
__asm volatile ("VMRS %0, fpscr" : "=r" (result) );
|
||||
__asm volatile ("");
|
||||
return(result);
|
||||
}
|
||||
__attribute__( ( always_inline ) ) static inline void __set_FPSCR(uint32_t fpscr)
|
||||
{
|
||||
/* Empty asm statement works as a scheduling barrier */
|
||||
__asm volatile ("");
|
||||
__asm volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc");
|
||||
__asm volatile ("");
|
||||
}
|
||||
`);
|
||||
|
||||
function initNormals() {
|
||||
|
@ -283,7 +261,6 @@ function readSTL(fn) {
|
|||
addr[6] = p_polyedge;
|
||||
addr[7] = p_normals;
|
||||
c.initEdges(p_addr, faces.length/3);
|
||||
console.log(edges);
|
||||
}
|
||||
|
||||
function rotV(v, u, c, s) {
|
||||
|
@ -306,8 +283,22 @@ function largestExtent(pts) {
|
|||
function draw() {
|
||||
"ram"
|
||||
const n = [1, 0, 0];
|
||||
if (qZrot) {
|
||||
var ca=Math.cos(a), sa=Math.sin(a), cb=Math.cos(zBeta), sb=Math.sin(zBeta);
|
||||
if (qZrot>0) {
|
||||
var ca, sa, cb, sb;
|
||||
if (qZrot==2) {
|
||||
var acc = Bangle.getAccel();
|
||||
zBeta = -Math.atan2(acc.z, -acc.y);
|
||||
var comp = Bangle.getCompass();
|
||||
if (!isNaN(comp.heading)) {
|
||||
var m = [comp.dx, comp.dy, comp.dz];
|
||||
//console.log(m);
|
||||
var rm = rotV(m, [1, 0, 0], Math.cos(zBeta), Math.sin(zBeta));
|
||||
a = -Math.atan2(rm[0], rm[2]);
|
||||
//console.log("heading="+a*180/Math.PI, "zBeta="+zBeta);
|
||||
}
|
||||
else a = 0;
|
||||
}
|
||||
ca=Math.cos(a); sa=Math.sin(a); cb=Math.cos(zBeta); sb=Math.sin(zBeta);
|
||||
var ul = Math.sqrt(sb*sb+ca*ca*sb*sb+2*sa*sa*cb+2*ca*sb*sb+2*sa*sa);
|
||||
u = [(sb+ca*sb)/ul, (-sa-sa*cb)/ul, (-sa*sb)/ul];
|
||||
var ra = Math.acos((ca+cb+ca*cb-1)/2);
|
||||
|
@ -315,7 +306,7 @@ function draw() {
|
|||
aux[3] = Math.cos(ra);
|
||||
aux[4] = Math.sin(ra);
|
||||
}
|
||||
else {
|
||||
else{
|
||||
u = rotV(u, n, c05, s05);
|
||||
aux[3] = Math.cos(a);
|
||||
aux[4] = Math.sin(a);
|
||||
|
@ -389,17 +380,19 @@ function loadFile(fn) {
|
|||
interv = setInterval(function() { draw();}, 30);
|
||||
} }, BTN2, {repeat:true, debounce:50});
|
||||
setWatch(function() {
|
||||
if (qZrot && zBeta<2*Math.PI/2-0.08) zBeta += 0.08;
|
||||
else zDist *= 0.9;
|
||||
if (qZrot==1) {
|
||||
if (zBeta<2*Math.PI/2-0.08) zBeta += 0.08;
|
||||
} else zDist *= 0.9;
|
||||
}, BTN1, {repeat:true});
|
||||
setWatch(function() {
|
||||
if (qZrot && zBeta>-2*Math.PI/2-0.08) zBeta -= 0.08;
|
||||
else zDist /= 0.9;
|
||||
if (qZrot==1) {
|
||||
if (zBeta>-2*Math.PI/2-0.08) zBeta -= 0.08;
|
||||
} else zDist /= 0.9;
|
||||
}, BTN3, {repeat:true});
|
||||
Bangle.on('swipe', function(direction){
|
||||
switch(direction){
|
||||
case 1:
|
||||
qZrot = !qZrot;
|
||||
qZrot = (qZrot+1)%3;
|
||||
break;
|
||||
case -1:
|
||||
qWireframe = (qWireframe+1)%4;
|
||||
|
@ -419,4 +412,6 @@ function drawMenu() {
|
|||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
Bangle.on('kill',()=>{Bangle.setCompassPower(0);});
|
||||
Bangle.setCompassPower(1);
|
||||
drawMenu();
|
||||
|
|
|
@ -37,7 +37,7 @@ var p_rnormals;
|
|||
var lastTime;
|
||||
var nFrames = 0;
|
||||
var interv;
|
||||
var qZrot = false;
|
||||
var qZrot = 0;
|
||||
var qWireframe = 0;
|
||||
var zBeta = 0;
|
||||
|
||||
|
@ -145,8 +145,22 @@ function largestExtent(pts) {
|
|||
function draw() {
|
||||
"ram"
|
||||
const n = [1, 0, 0];
|
||||
if (qZrot) {
|
||||
var ca=Math.cos(a), sa=Math.sin(a), cb=Math.cos(zBeta), sb=Math.sin(zBeta);
|
||||
if (qZrot>0) {
|
||||
var ca, sa, cb, sb;
|
||||
if (qZrot==2) {
|
||||
var acc = Bangle.getAccel();
|
||||
zBeta = -Math.atan2(acc.z, -acc.y);
|
||||
var comp = Bangle.getCompass();
|
||||
if (!isNaN(comp.heading)) {
|
||||
var m = [comp.dx, comp.dy, comp.dz];
|
||||
//console.log(m);
|
||||
var rm = rotV(m, [1, 0, 0], Math.cos(zBeta), Math.sin(zBeta));
|
||||
a = -Math.atan2(rm[0], rm[2]);
|
||||
//console.log("heading="+a*180/Math.PI, "zBeta="+zBeta);
|
||||
}
|
||||
else a = 0;
|
||||
}
|
||||
ca=Math.cos(a); sa=Math.sin(a); cb=Math.cos(zBeta); sb=Math.sin(zBeta);
|
||||
var ul = Math.sqrt(sb*sb+ca*ca*sb*sb+2*sa*sa*cb+2*ca*sb*sb+2*sa*sa);
|
||||
u = [(sb+ca*sb)/ul, (-sa-sa*cb)/ul, (-sa*sb)/ul];
|
||||
var ra = Math.acos((ca+cb+ca*cb-1)/2);
|
||||
|
@ -154,7 +168,7 @@ function draw() {
|
|||
aux[3] = Math.cos(ra);
|
||||
aux[4] = Math.sin(ra);
|
||||
}
|
||||
else {
|
||||
else{
|
||||
u = rotV(u, n, c05, s05);
|
||||
aux[3] = Math.cos(a);
|
||||
aux[4] = Math.sin(a);
|
||||
|
@ -173,16 +187,9 @@ function draw() {
|
|||
z = zbuf[i];
|
||||
shade = 0|c.processFace(p_addr, z, 1);
|
||||
if (shade > 0) {
|
||||
if (qWireframe==1) {
|
||||
g.setColor(shade);
|
||||
g.fillPoly(polyp);
|
||||
g.setColor(0);
|
||||
g.drawPoly(polyedge);
|
||||
}
|
||||
if (qWireframe==1) g.setColor(shade).fillPoly(polyp).setColor(0).drawPoly(polyedge);
|
||||
else {
|
||||
g.setColor(0);
|
||||
g.fillPoly(polyp);
|
||||
g.setColor(shade);
|
||||
g.setColor(0).fillPoly(polyp).setColor(shade);
|
||||
if (qWireframe==2) g.drawPoly(polyedge);
|
||||
else g.drawPoly(polyp, true);
|
||||
}
|
||||
|
@ -194,10 +201,7 @@ function draw() {
|
|||
while (i--) {
|
||||
z = zbuf[i];
|
||||
shade = 0|c.processFace(p_addr, z, 0);
|
||||
if (shade > 0) {
|
||||
g.setColor(shade);
|
||||
g.fillPoly(polyp);
|
||||
}
|
||||
if (shade > 0) g.setColor(shade).fillPoly(polyp);
|
||||
}
|
||||
}
|
||||
nFrames++;
|
||||
|
@ -229,7 +233,7 @@ function loadFile(fn) {
|
|||
setWatch(function() {
|
||||
if (interv) {
|
||||
interv = clearInterval(interv);
|
||||
c.clearFPU(); load();
|
||||
load();
|
||||
}
|
||||
else {
|
||||
Bangle.setLCDMode("doublebuffered");
|
||||
|
@ -238,17 +242,19 @@ function loadFile(fn) {
|
|||
interv = setInterval(function() { draw();}, 30);
|
||||
} }, BTN2, {repeat:true, debounce:50});
|
||||
setWatch(function() {
|
||||
if (qZrot && zBeta<2*Math.PI/2-0.08) zBeta += 0.08;
|
||||
else zDist *= 0.9;
|
||||
if (qZrot==1) {
|
||||
if (zBeta<2*Math.PI/2-0.08) zBeta += 0.08;
|
||||
} else zDist *= 0.9;
|
||||
}, BTN1, {repeat:true});
|
||||
setWatch(function() {
|
||||
if (qZrot && zBeta>-2*Math.PI/2-0.08) zBeta -= 0.08;
|
||||
else zDist /= 0.9;
|
||||
if (qZrot==1) {
|
||||
if (zBeta>-2*Math.PI/2-0.08) zBeta -= 0.08;
|
||||
} else zDist /= 0.9;
|
||||
}, BTN3, {repeat:true});
|
||||
Bangle.on('swipe', function(direction){
|
||||
switch(direction){
|
||||
case 1:
|
||||
qZrot = !qZrot;
|
||||
qZrot = (qZrot+1)%3;
|
||||
break;
|
||||
case -1:
|
||||
qWireframe = (qWireframe+1)%4;
|
||||
|
@ -264,8 +270,10 @@ function drawMenu() {
|
|||
for (var i=0; i<files.length; ++i) {
|
||||
menu[files[i]] = loadFile.bind(null, files[i]);
|
||||
}
|
||||
menu['Exit'] = function() { c.clearFPU(); load(); };
|
||||
menu['Exit'] = function() { load(); };
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
Bangle.on('kill',()=>{Bangle.setCompassPower(0);});
|
||||
Bangle.setCompassPower(1);
|
||||
drawMenu();
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: First try
|
|
@ -0,0 +1,98 @@
|
|||
/* jshint esversion: 6 */
|
||||
const timeFontSize = 6;
|
||||
const dateFontSize = 3;
|
||||
const gmtFontSize = 2;
|
||||
const font = "6x8";
|
||||
|
||||
const xyCenter = g.getWidth() / 2;
|
||||
const xcol1=10;
|
||||
const xcol2=g.getWidth()-xcol1;
|
||||
const yposTime = 75;
|
||||
const yposDate = 130;
|
||||
//const yposYear = 175;
|
||||
//const yposGMT = 220;
|
||||
const yposWorld=170;
|
||||
|
||||
|
||||
var offsets = require("Storage").readJSON("worldclock.settings.json");
|
||||
|
||||
// Check settings for what type our clock should be
|
||||
//var is12Hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"];
|
||||
var secondInterval = undefined;
|
||||
|
||||
function doublenum(x) {
|
||||
return x<10? "0"+x : ""+x;
|
||||
}
|
||||
|
||||
function offset(dt,offset) {
|
||||
return new Date(dt.getTime() + (offset*60*60*1000));
|
||||
}
|
||||
|
||||
function drawSimpleClock() {
|
||||
// get date
|
||||
var d = new Date();
|
||||
var da = d.toString().split(" ");
|
||||
|
||||
g.reset(); // default draw styles
|
||||
// drawSting centered
|
||||
g.setFontAlign(0, 0);
|
||||
|
||||
// draw time
|
||||
var time = da[4].substr(0, 5).split(":");
|
||||
var hours = time[0], minutes = time[1];
|
||||
|
||||
g.setFont(font, timeFontSize);
|
||||
g.drawString(`${hours}:${minutes}`, xyCenter, yposTime, true);
|
||||
|
||||
// draw Day, name of month, Date
|
||||
var date = [da[0], da[1], da[2]].join(" ");
|
||||
g.setFont(font, dateFontSize);
|
||||
|
||||
g.drawString(date, xyCenter, yposDate, true);
|
||||
|
||||
// draw year
|
||||
//g.setFont(font, dateFontSize);
|
||||
//g.drawString(d.getFullYear(), xyCenter, yposYear, true);
|
||||
|
||||
// draw gmt
|
||||
//console.log(d.getTimezoneOffset());//offset to GMT in minutes
|
||||
var gmt = new Date(d.getTime()+(d.getTimezoneOffset()*60*1000));
|
||||
//gmt is now UTC+0
|
||||
|
||||
for (var i=0; i<offsets.length; i++) {
|
||||
g.setFont(font, gmtFontSize);
|
||||
dx=offset(gmt,offsets[i][1]);
|
||||
hours=doublenum(dx.getHours());
|
||||
minutes=doublenum(dx.getMinutes());
|
||||
g.setFontAlign(-1, 0);
|
||||
g.drawString(offsets[i][0],xcol1,yposWorld+i*15, true);
|
||||
g.setFontAlign(1, 0);
|
||||
g.drawString(`${hours}:${minutes}`, xcol2, yposWorld+i*15, true);
|
||||
//g.drawString(gmt, xyCenter, yposWorld, true);
|
||||
}
|
||||
}
|
||||
|
||||
// clean app screen
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
// refesh every 15 sec when screen is on
|
||||
Bangle.on('lcdPower',on=>{
|
||||
if (secondInterval) clearInterval(secondInterval);
|
||||
secondInterval = undefined;
|
||||
if (on) {
|
||||
secondInterval = setInterval(drawSimpleClock, 15E3);
|
||||
drawSimpleClock(); // draw immediately
|
||||
}
|
||||
});
|
||||
|
||||
// draw now and every 15 sec until display goes off
|
||||
drawSimpleClock();
|
||||
if (Bangle.isLCDOn()) {
|
||||
secondInterval = setInterval(drawSimpleClock, 15E3);
|
||||
}
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
|
@ -0,0 +1,76 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>You can add up to 4 timezones. Please give a name and UTC offset in hours.
|
||||
If you want less than 4, clear the checkbox to the left.</p>
|
||||
|
||||
<table id="worldclock-offsets">
|
||||
<tr>
|
||||
<th>Enabled?</th>
|
||||
<th>Name</th>
|
||||
<th>UTC Offset</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||
|
||||
<script src="../../lib/customize.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
var offsets=[];
|
||||
try{
|
||||
var stored = localStorage.getItem('worldclock-offset-list')
|
||||
if(stored) offsets = JSON.parse(stored);
|
||||
if (!offsets || offsets.length!=4) {
|
||||
throw "Offsets invalid";
|
||||
}
|
||||
} catch(e){
|
||||
offsets=[
|
||||
[true,"Tokyo",9],
|
||||
[true,"Delhi",5.5],
|
||||
[true, "London",0],
|
||||
[true, "São Paulo",-3],
|
||||
];
|
||||
}
|
||||
console.log(offsets);
|
||||
var tbl=document.getElementById("worldclock-offsets");
|
||||
for (var i=0; i<4; i++) {
|
||||
var $offset = document.createElement('tr')
|
||||
$offset.innerHTML = `
|
||||
<td><input type="checkbox" id="enabled_${i}" ${offsets[i][0]? "checked" : ""}></td>
|
||||
<td><input type="text" id="name_${i}" value="${offsets[i][1]}"></td>
|
||||
<td><input type="number" id="offset_${i}" value="${offsets[i][2]}"></td>`
|
||||
tbl.append($offset);
|
||||
}
|
||||
|
||||
// When the 'upload' button is clicked...
|
||||
document.getElementById("upload").addEventListener("click", function() {
|
||||
var storage_offsets=[];
|
||||
var app_offsets=[];
|
||||
for (var i=0; i<4; i++) {
|
||||
var checked=document.getElementById("enabled_"+i).checked;
|
||||
var name=document.getElementById("name_"+i).value;
|
||||
var offset=document.getElementById("offset_"+i).value;
|
||||
if (checked) {
|
||||
app_offsets.push([name,offset]);
|
||||
}
|
||||
storage_offsets.push([checked,name,offset]);
|
||||
}
|
||||
console.log(storage_offsets);
|
||||
console.log(app_offsets);
|
||||
localStorage.setItem('worldclock-offset-list',JSON.stringify(storage_offsets));
|
||||
// send finished app (in addition to contents of app.json)
|
||||
sendCustomizedApp({
|
||||
storage:[
|
||||
{name:"worldclock.settings.json", content:JSON.stringify(app_offsets)},
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgJC/ABEE+EA4EAj9E8HF//gn/gwP///wt/MgF//8gh/8gYLBwEP+EHAofghgFD4EOj//gEPA4ILBGgIxB/wFBgwFB/lsgCKBj/4oxHBvAFBJoV8gP4TQX+gJUBAAN/Aok+AoVgAoXogAfBjkA8AfBAoXAAoUYY4cAiCDEAooA/ABg"))
|
Loading…
Reference in New Issue