Add magnetometer/accelerometer rotation mode

pull/550/head
Marko.Kl.Berkenbusch@gmail.com 2020-08-21 22:43:55 -04:00
parent 1cdf5d5ba8
commit 9850892c7c
5 changed files with 67 additions and 61 deletions

View File

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

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() {
@ -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();

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;
@ -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();