diff --git a/apps.json b/apps.json index 57f2d1e1e..3c9124446 100644 --- a/apps.json +++ b/apps.json @@ -2147,7 +2147,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", diff --git a/apps/viewstl/ChangeLog b/apps/viewstl/ChangeLog new file mode 100644 index 000000000..fa2dcb2e1 --- /dev/null +++ b/apps/viewstl/ChangeLog @@ -0,0 +1,2 @@ +0.01: New app! +0.02: Add accelerometer/compass viewing mode diff --git a/apps/viewstl/README.md b/apps/viewstl/README.md index bd54f15bb..2676f3df7 100644 --- a/apps/viewstl/README.md +++ b/apps/viewstl/README.md @@ -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. + diff --git a/apps/viewstl/viewstl.app.js b/apps/viewstl/viewstl.app.js index c732ef298..0d2b6471c 100644 --- a/apps/viewstl/viewstl.app.js +++ b/apps/viewstl/viewstl.app.js @@ -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() { @@ -236,7 +214,7 @@ function initNormals() { function readSTL(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); p_points = E.getAddressOf(points, true); faces = new Uint16Array(nverts); @@ -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(); diff --git a/apps/viewstl/viewstl.min.js b/apps/viewstl/viewstl.min.js index 227143f6f..f2e7d0685 100644 --- a/apps/viewstl/viewstl.min.js +++ b/apps/viewstl/viewstl.min.js @@ -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; @@ -76,7 +76,7 @@ function initNormals() { function readSTL(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); p_points = E.getAddressOf(points, true); faces = new Uint16Array(nverts); @@ -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{Bangle.setCompassPower(0);}); +Bangle.setCompassPower(1); drawMenu();