// globals var u = [0, 1, 0]; // axis of rotation var aux = new Float32Array(12); var p_aux = E.getAddressOf(aux, true); var a = 0.5; // angle of rotation var light = new Float32Array(12); var p_light = E.getAddressOf(light, true); light[0] = 1/Math.sqrt(2); light[1] = 1/Math.sqrt(2); light[2] = 0; var filex = new Float32Array(6); var p_filex = E.getAddressOf(filex, true); var addr = new Int32Array(12); var p_addr = E.getAddressOf(addr, true); var polyp = new Int32Array(6); var p_polyp = E.getAddressOf(polyp, true); var polyedge = new Int32Array(8); var p_polyedge = E.getAddressOf(polyedge, true); var edges; var p_edges; var zbuf; var p_zbuf; var zDist; var points; var p_points; var rpoints; var p_rpoints; var npoints = 0; var faces; var p_faces; var vpoints; var p_vpoints; var normals; var p_normals; var rnormals; var p_rnormals; var lastTime; var nFrames = 0; var interv; var qZrot = 0; var qWireframe = 0; var zBeta = 0; const c05 = Math.cos(0.05); const s05 = Math.sin(0.05); var c = (function(){ var bin=atob("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"); return { rotatePoints:E.nativeCall(1253, "void(int, int, int, int)", bin), projectPoints:E.nativeCall(1121, "void(int, int, int, int)", bin), popZBuf:E.nativeCall(1021, "void(int, int, int, int)", bin), processFace:E.nativeCall(461, "int(int, int, int)", bin), initEdges:E.nativeCall(209, "void(int, int)", bin), findSlot:E.nativeCall(57, "int(int, int, int)", bin), clearFPU:E.nativeCall(25, "void()", bin), getFPSCR:E.nativeCall(1, "int()", bin), getFPUPendingIRQ:E.nativeCall(9, "int()", bin), }; })(); function initNormals() { var i = faces.length/3; while (i--) { normals[i*3+0] = (points[faces[3*i+1]*3+1]-points[faces[3*i+0]*3+1])*(points[faces[3*i+2]*3+2]-points[faces[3*i+0]*3+2]) - (points[faces[3*i+1]*3+2]-points[faces[3*i+0]*3+2])*(points[faces[3*i+2]*3+1]-points[faces[3*i+0]*3+1]); normals[i*3+1] = (points[faces[3*i+1]*3+2]-points[faces[3*i+0]*3+2])*(points[faces[3*i+2]*3+0]-points[faces[3*i+0]*3+0]) - (points[faces[3*i+1]*3+0]-points[faces[3*i+0]*3+0])*(points[faces[3*i+2]*3+2]-points[faces[3*i+0]*3+2]); normals[i*3+2] = (points[faces[3*i+1]*3+0]-points[faces[3*i+0]*3+0])*(points[faces[3*i+2]*3+1]-points[faces[3*i+0]*3+1]) - (points[faces[3*i+1]*3+1]-points[faces[3*i+0]*3+1])*(points[faces[3*i+2]*3+0]-points[faces[3*i+0]*3+0]); var n = Math.sqrt(normals[i*3]*normals[i*3]+normals[i*3+1]*normals[i*3+1]+normals[i*3+2]*normals[i*3+2]); if (n>0) { normals[i*3] /= -n; normals[i*3+1] /= -n; normals[i*3+2] /= -n; } } } function readSTL(fn) { var fb = require("Storage").read(fn); var nverts=0,i=0; while((i=fb.indexOf("vertex",i)+1)!=0) nverts++; points = new Float32Array(nverts); p_points = E.getAddressOf(points, true); faces = new Uint16Array(nverts); p_faces = E.getAddressOf(faces, true); edges = new Uint8Array(Math.max(faces.length/3,24)) p_edges = E.getAddressOf(edges, true); var fp=0, p=0; var nf = 0; g.setColor(0.9, 0.9, 0.9); g.drawRect(20, 140, 220, 160); g.setColor(0.6, 0.6, 0.9); while (px) x = e; } return Math.sqrt(x); } function draw() { "ram" const n = [1, 0, 0]; 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); if (ra<0) ra += Math.PI; aux[3] = Math.cos(ra); aux[4] = Math.sin(ra); } else{ u = rotV(u, n, c05, s05); aux[3] = Math.cos(a); aux[4] = Math.sin(a); } a += 0.08; aux[0] = u[0]; aux[1]=u[1]; aux[2]=u[2]; c.rotatePoints(p_points, p_rpoints, npoints, p_aux); c.rotatePoints(p_normals, p_rnormals, faces.length/3, p_aux); c.projectPoints(p_rpoints, p_vpoints, npoints, 0|zDist*100); c.popZBuf(p_rpoints, p_faces, p_zbuf, faces.length/3); g.clear(); var z, shade; if (qWireframe>0) { var i = faces.length/3; while (i--) { z = zbuf[i]; shade = 0|c.processFace(p_addr, z, 1); if (shade > 0) { if (qWireframe==1) g.setColor(shade).fillPoly(polyp).setColor(0).drawPoly(polyedge); else { g.setColor(0).fillPoly(polyp).setColor(shade); if (qWireframe==2) g.drawPoly(polyedge); else g.drawPoly(polyp, true); } } } } else { var i = faces.length/3; while (i--) { z = zbuf[i]; shade = 0|c.processFace(p_addr, z, 0); if (shade > 0) g.setColor(shade).fillPoly(polyp); } } nFrames++; var fps = Math.round(nFrames*100000/(Date.now()-lastTime))/100; g.setColor(0.7, 0.7, 0.7); g.setFont("6x8", 1); g.drawString("fps:"+fps.toString(), 20, 0); g.flip(); } function loadFile(fn) { Bangle.setLCDMode("direct"); g.clear(); E.showMenu(); E.showMessage("Loading...", fn); readSTL(fn); zDist = 5*largestExtent(points); g.clear(); g.setColor(1, 1, 1); g.setFont("6x8", 2); g.setFontAlign(-1, -1); g.setColor(1, 0.5, 0.5); g.drawString("Model info",15, 40); g.setColor(1, 1, 1); g.drawString("# faces: "+faces.length/3, 15, 80); g.drawString("# vertices: "+npoints, 15, 110); g.drawString("max extent: "+Math.round(100*(zDist/5))/100, 15, 140); g.flip(); setWatch(function() { if (interv) { interv = clearInterval(interv); load(); } else { Bangle.setLCDMode("doublebuffered"); lastTime = Date.now(); nFrames = 0; interv = setInterval(function() { draw();}, 30); } }, BTN2, {repeat:true, debounce:50}); setWatch(function() { 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==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+1)%3; break; case -1: qWireframe = (qWireframe+1)%4; break; }}); } function drawMenu() { const menu = { '': { 'title': 'STL files' } }; var files = require("Storage").list(".stl"); for (var i=0; i{Bangle.setCompassPower(0);}); Bangle.setCompassPower(1); drawMenu();