Merge branch 'master' of github.com:espruino/BangleApps

master
Gordon Williams 2020-08-24 12:00:07 +01:00
commit badc82f4fc
10 changed files with 257 additions and 59 deletions

View File

@ -2160,7 +2160,7 @@
"name": "STL file viewer", "name": "STL file viewer",
"shortName":"ViewSTL", "shortName":"ViewSTL",
"icon": "icons8-octahedron-48.png", "icon": "icons8-octahedron-48.png",
"version":"0.01", "version":"0.02",
"description": "This app allows you to view STL 3D models on your watch", "description": "This app allows you to view STL 3D models on your watch",
"tags": "tool", "tags": "tool",
"readme": "README.md", "readme": "README.md",
@ -2171,5 +2171,21 @@
{"name":"cube.stl","url":"cube.stl"}, {"name":"cube.stl","url":"cube.stl"},
{"name":"icosa.stl","url":"icosa.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}
]
} }
] ]

2
apps/viewstl/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: New app!
0.02: Add accelerometer/compass viewing mode

View File

@ -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, only edges between non-coplanar facets visible
- wireframe, all facet (triangle) edges 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 - free rotation: button 1 zooms in, button 3 out
- Z-axis (vertical axis) rotation: buttons 1 and 3 tilt the Z-axis - 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. 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.

View File

@ -37,7 +37,7 @@ var p_rnormals;
var lastTime; var lastTime;
var nFrames = 0; var nFrames = 0;
var interv; var interv;
var qZrot = false; var qZrot = 0;
var qWireframe = 0; var qWireframe = 0;
var zBeta = 0; var zBeta = 0;
@ -195,28 +195,6 @@ int findSlot(float *p, float *x, int len) {
p[3*len+2] = x[2]; p[3*len+2] = x[2];
return len; 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() { function initNormals() {
@ -283,7 +261,6 @@ function readSTL(fn) {
addr[6] = p_polyedge; addr[6] = p_polyedge;
addr[7] = p_normals; addr[7] = p_normals;
c.initEdges(p_addr, faces.length/3); c.initEdges(p_addr, faces.length/3);
console.log(edges);
} }
function rotV(v, u, c, s) { function rotV(v, u, c, s) {
@ -306,8 +283,22 @@ function largestExtent(pts) {
function draw() { function draw() {
"ram" "ram"
const n = [1, 0, 0]; const n = [1, 0, 0];
if (qZrot) { if (qZrot>0) {
var ca=Math.cos(a), sa=Math.sin(a), cb=Math.cos(zBeta), sb=Math.sin(zBeta); 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); 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]; u = [(sb+ca*sb)/ul, (-sa-sa*cb)/ul, (-sa*sb)/ul];
var ra = Math.acos((ca+cb+ca*cb-1)/2); var ra = Math.acos((ca+cb+ca*cb-1)/2);
@ -389,17 +380,19 @@ function loadFile(fn) {
interv = setInterval(function() { draw();}, 30); interv = setInterval(function() { draw();}, 30);
} }, BTN2, {repeat:true, debounce:50}); } }, BTN2, {repeat:true, debounce:50});
setWatch(function() { setWatch(function() {
if (qZrot && zBeta<2*Math.PI/2-0.08) zBeta += 0.08; if (qZrot==1) {
else zDist *= 0.9; if (zBeta<2*Math.PI/2-0.08) zBeta += 0.08;
} else zDist *= 0.9;
}, BTN1, {repeat:true}); }, BTN1, {repeat:true});
setWatch(function() { setWatch(function() {
if (qZrot && zBeta>-2*Math.PI/2-0.08) zBeta -= 0.08; if (qZrot==1) {
else zDist /= 0.9; if (zBeta>-2*Math.PI/2-0.08) zBeta -= 0.08;
} else zDist /= 0.9;
}, BTN3, {repeat:true}); }, BTN3, {repeat:true});
Bangle.on('swipe', function(direction){ Bangle.on('swipe', function(direction){
switch(direction){ switch(direction){
case 1: case 1:
qZrot = !qZrot; qZrot = (qZrot+1)%3;
break; break;
case -1: case -1:
qWireframe = (qWireframe+1)%4; qWireframe = (qWireframe+1)%4;
@ -419,4 +412,6 @@ function drawMenu() {
E.showMenu(menu); E.showMenu(menu);
} }
Bangle.on('kill',()=>{Bangle.setCompassPower(0);});
Bangle.setCompassPower(1);
drawMenu(); drawMenu();

View File

@ -37,7 +37,7 @@ var p_rnormals;
var lastTime; var lastTime;
var nFrames = 0; var nFrames = 0;
var interv; var interv;
var qZrot = false; var qZrot = 0;
var qWireframe = 0; var qWireframe = 0;
var zBeta = 0; var zBeta = 0;
@ -145,8 +145,22 @@ function largestExtent(pts) {
function draw() { function draw() {
"ram" "ram"
const n = [1, 0, 0]; const n = [1, 0, 0];
if (qZrot) { if (qZrot>0) {
var ca=Math.cos(a), sa=Math.sin(a), cb=Math.cos(zBeta), sb=Math.sin(zBeta); 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); 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]; u = [(sb+ca*sb)/ul, (-sa-sa*cb)/ul, (-sa*sb)/ul];
var ra = Math.acos((ca+cb+ca*cb-1)/2); var ra = Math.acos((ca+cb+ca*cb-1)/2);
@ -173,16 +187,9 @@ function draw() {
z = zbuf[i]; z = zbuf[i];
shade = 0|c.processFace(p_addr, z, 1); shade = 0|c.processFace(p_addr, z, 1);
if (shade > 0) { if (shade > 0) {
if (qWireframe==1) { if (qWireframe==1) g.setColor(shade).fillPoly(polyp).setColor(0).drawPoly(polyedge);
g.setColor(shade);
g.fillPoly(polyp);
g.setColor(0);
g.drawPoly(polyedge);
}
else { else {
g.setColor(0); g.setColor(0).fillPoly(polyp).setColor(shade);
g.fillPoly(polyp);
g.setColor(shade);
if (qWireframe==2) g.drawPoly(polyedge); if (qWireframe==2) g.drawPoly(polyedge);
else g.drawPoly(polyp, true); else g.drawPoly(polyp, true);
} }
@ -194,10 +201,7 @@ function draw() {
while (i--) { while (i--) {
z = zbuf[i]; z = zbuf[i];
shade = 0|c.processFace(p_addr, z, 0); shade = 0|c.processFace(p_addr, z, 0);
if (shade > 0) { if (shade > 0) g.setColor(shade).fillPoly(polyp);
g.setColor(shade);
g.fillPoly(polyp);
}
} }
} }
nFrames++; nFrames++;
@ -229,7 +233,7 @@ function loadFile(fn) {
setWatch(function() { setWatch(function() {
if (interv) { if (interv) {
interv = clearInterval(interv); interv = clearInterval(interv);
c.clearFPU(); load(); load();
} }
else { else {
Bangle.setLCDMode("doublebuffered"); Bangle.setLCDMode("doublebuffered");
@ -238,17 +242,19 @@ function loadFile(fn) {
interv = setInterval(function() { draw();}, 30); interv = setInterval(function() { draw();}, 30);
} }, BTN2, {repeat:true, debounce:50}); } }, BTN2, {repeat:true, debounce:50});
setWatch(function() { setWatch(function() {
if (qZrot && zBeta<2*Math.PI/2-0.08) zBeta += 0.08; if (qZrot==1) {
else zDist *= 0.9; if (zBeta<2*Math.PI/2-0.08) zBeta += 0.08;
} else zDist *= 0.9;
}, BTN1, {repeat:true}); }, BTN1, {repeat:true});
setWatch(function() { setWatch(function() {
if (qZrot && zBeta>-2*Math.PI/2-0.08) zBeta -= 0.08; if (qZrot==1) {
else zDist /= 0.9; if (zBeta>-2*Math.PI/2-0.08) zBeta -= 0.08;
} else zDist /= 0.9;
}, BTN3, {repeat:true}); }, BTN3, {repeat:true});
Bangle.on('swipe', function(direction){ Bangle.on('swipe', function(direction){
switch(direction){ switch(direction){
case 1: case 1:
qZrot = !qZrot; qZrot = (qZrot+1)%3;
break; break;
case -1: case -1:
qWireframe = (qWireframe+1)%4; qWireframe = (qWireframe+1)%4;
@ -264,8 +270,10 @@ function drawMenu() {
for (var i=0; i<files.length; ++i) { for (var i=0; i<files.length; ++i) {
menu[files[i]] = loadFile.bind(null, files[i]); menu[files[i]] = loadFile.bind(null, files[i]);
} }
menu['Exit'] = function() { c.clearFPU(); load(); }; menu['Exit'] = function() { load(); };
E.showMenu(menu); E.showMenu(menu);
} }
Bangle.on('kill',()=>{Bangle.setCompassPower(0);});
Bangle.setCompassPower(1);
drawMenu(); drawMenu();

View File

@ -0,0 +1 @@
0.01: First try

98
apps/worldclock/app.js Normal file
View File

@ -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"});

BIN
apps/worldclock/app.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -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>

View File

@ -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"))