mirror of https://github.com/espruino/BangleApps
Add magnetometer/accelerometer rotation mode
parent
1cdf5d5ba8
commit
9850892c7c
|
@ -2147,7 +2147,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",
|
||||||
|
|
|
@ -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, 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.
|
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
@ -236,7 +214,7 @@ function initNormals() {
|
||||||
|
|
||||||
function readSTL(fn) {
|
function readSTL(fn) {
|
||||||
var fb = require("Storage").read(fn);
|
var fb = require("Storage").read(fn);
|
||||||
var nverts=0,i=0; while((i=fb.indexOf("vertex",i)+1)!=0) nverts++;
|
var nverts=0,i=0; while(i=fb.indexOf("vertex",i)+1) nverts++;
|
||||||
points = new Float32Array(nverts);
|
points = new Float32Array(nverts);
|
||||||
p_points = E.getAddressOf(points, true);
|
p_points = E.getAddressOf(points, true);
|
||||||
faces = new Uint16Array(nverts);
|
faces = new Uint16Array(nverts);
|
||||||
|
@ -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);
|
||||||
|
@ -315,7 +306,7 @@ function draw() {
|
||||||
aux[3] = Math.cos(ra);
|
aux[3] = Math.cos(ra);
|
||||||
aux[4] = Math.sin(ra);
|
aux[4] = Math.sin(ra);
|
||||||
}
|
}
|
||||||
else {
|
else{
|
||||||
u = rotV(u, n, c05, s05);
|
u = rotV(u, n, c05, s05);
|
||||||
aux[3] = Math.cos(a);
|
aux[3] = Math.cos(a);
|
||||||
aux[4] = Math.sin(a);
|
aux[4] = Math.sin(a);
|
||||||
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ function initNormals() {
|
||||||
|
|
||||||
function readSTL(fn) {
|
function readSTL(fn) {
|
||||||
var fb = require("Storage").read(fn);
|
var fb = require("Storage").read(fn);
|
||||||
var nverts=0,i=0; while((i=fb.indexOf("vertex",i)+1)!=0) nverts++;
|
var nverts=0,i=0; while(i=fb.indexOf("vertex",i)+1) nverts++;
|
||||||
points = new Float32Array(nverts);
|
points = new Float32Array(nverts);
|
||||||
p_points = E.getAddressOf(points, true);
|
p_points = E.getAddressOf(points, true);
|
||||||
faces = new Uint16Array(nverts);
|
faces = new Uint16Array(nverts);
|
||||||
|
@ -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);
|
||||||
|
@ -154,7 +168,7 @@ function draw() {
|
||||||
aux[3] = Math.cos(ra);
|
aux[3] = Math.cos(ra);
|
||||||
aux[4] = Math.sin(ra);
|
aux[4] = Math.sin(ra);
|
||||||
}
|
}
|
||||||
else {
|
else{
|
||||||
u = rotV(u, n, c05, s05);
|
u = rotV(u, n, c05, s05);
|
||||||
aux[3] = Math.cos(a);
|
aux[3] = Math.cos(a);
|
||||||
aux[4] = Math.sin(a);
|
aux[4] = Math.sin(a);
|
||||||
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue