BangleApps/apps/viewstl/viewstl.min.js

280 lines
10 KiB
JavaScript
Raw Normal View History

2020-08-13 17:54:33 +00:00
// 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;
2020-08-13 17:54:33 +00:00
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) nverts++;
2020-08-13 17:54:33 +00:00
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 (p<fb.length) {
var line = '';
while (fb[p]!="\n") line += fb[p++];
p++;
if (line.toLowerCase().includes("vertex")) {
var v = line.trim().split(/\s+/);
filex[0] = v[1];
filex[1] = v[3];
filex[2] = v[2];
var slot = 0|c.findSlot(p_points, p_filex, npoints);
if (slot==npoints) npoints++;
faces[fp++] = slot;
if (fp%3 == 0) g.fillRect(21, 141, 21+198*fp/nverts, 159);
}
}
vpoints = new Uint16Array(Math.max(12,2*npoints));
p_vpoints = E.getAddressOf(vpoints, true);
normals = new Float32Array(faces.length);
p_normals = E.getAddressOf(normals, true);
initNormals();
rnormals = new Float32Array(faces.length);
p_rnormals = E.getAddressOf(rnormals, true);
rpoints = new Float32Array(points.length);
p_rpoints = E.getAddressOf(rpoints, true);
zbuf = new Int32Array(Math.max(12,faces.length/3));
p_zbuf = E.getAddressOf(zbuf, true);
addr[0] = p_faces;
addr[1] = p_rnormals;
addr[2] = p_vpoints;
addr[3] = p_polyp;
addr[4] = p_light;
addr[5] = p_edges;
addr[6] = p_polyedge;
addr[7] = p_normals;
c.initEdges(p_addr, faces.length/3);
}
function rotV(v, u, c, s) {
"ram"
return [v[0]*(c+u[0]*u[0]*(1-c)) + v[1]*(u[0]*u[1]*(1-c)-u[2]*s) + v[2]*(u[0]*u[2]*(1-c)+u[1]*s),
v[0]*(u[1]*u[0]*(1-c)+u[2]*s) + v[1]*(c+u[1]*u[1]*(1-c)) + v[2]*(u[1]*u[2]*(1-c)-u[0]*s),
v[0]*(u[2]*u[0]*(1-c)-u[1]*s) + v[1]*(u[2]*u[1]*(1-c)+u[0]*s) + v[2]*(c+u[2]*u[2]*(1-c))];
}
function largestExtent(pts) {
var x = 0;
var i = pts.length/3;
while (i--) {
var e = pts[3*i]*pts[3*i]+pts[3*i+1]*pts[3*i+1]+pts[3*i+2]*pts[3*i+2];
if (e>x) 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);
2020-08-13 17:54:33 +00:00
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{
2020-08-13 17:54:33 +00:00
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);
2020-08-13 17:54:33 +00:00
else {
g.setColor(0).fillPoly(polyp).setColor(shade);
2020-08-13 17:54:33 +00:00
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);
2020-08-13 17:54:33 +00:00
}
}
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();
2020-08-13 17:54:33 +00:00
}
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;
2020-08-13 17:54:33 +00:00
}, BTN1, {repeat:true});
setWatch(function() {
if (qZrot==1) {
if (zBeta>-2*Math.PI/2-0.08) zBeta -= 0.08;
} else zDist /= 0.9;
2020-08-13 17:54:33 +00:00
}, BTN3, {repeat:true});
Bangle.on('swipe', function(direction){
switch(direction){
case 1:
qZrot = (qZrot+1)%3;
2020-08-13 17:54:33 +00:00
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<files.length; ++i) {
menu[files[i]] = loadFile.bind(null, files[i]);
}
menu['Exit'] = function() { load(); };
2020-08-13 17:54:33 +00:00
E.showMenu(menu);
}
Bangle.on('kill',()=>{Bangle.setCompassPower(0);});
Bangle.setCompassPower(1);
2020-08-13 17:54:33 +00:00
drawMenu();