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;
|
2020-08-22 02:43:55 +00:00
|
|
|
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);
|
2020-08-22 02:52:54 +00:00
|
|
|
var nverts=0,i=0; while((i=fb.indexOf("vertex",i)+1)!=0) 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;
|
|
|
|
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];
|
2020-08-22 02:43:55 +00:00
|
|
|
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);
|
|
|
|
}
|
2020-08-22 02:43:55 +00:00
|
|
|
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) {
|
2020-08-22 02:43:55 +00:00
|
|
|
if (qWireframe==1) g.setColor(shade).fillPoly(polyp).setColor(0).drawPoly(polyedge);
|
2020-08-13 17:54:33 +00:00
|
|
|
else {
|
2020-08-22 02:43:55 +00:00
|
|
|
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);
|
2020-08-22 02:43:55 +00:00
|
|
|
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();
|
2022-04-10 19:35:46 +00:00
|
|
|
E.showMessage(/*LANG*/"Loading...", fn);
|
2020-08-13 17:54:33 +00:00
|
|
|
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);
|
2020-08-22 02:43:55 +00:00
|
|
|
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() {
|
2020-08-22 02:43:55 +00:00
|
|
|
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() {
|
2020-08-22 02:43:55 +00:00
|
|
|
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:
|
2020-08-22 02:43:55 +00:00
|
|
|
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]);
|
|
|
|
}
|
2020-08-22 02:43:55 +00:00
|
|
|
menu['Exit'] = function() { load(); };
|
2020-08-13 17:54:33 +00:00
|
|
|
E.showMenu(menu);
|
|
|
|
}
|
|
|
|
|
2020-08-22 02:43:55 +00:00
|
|
|
Bangle.on('kill',()=>{Bangle.setCompassPower(0);});
|
|
|
|
Bangle.setCompassPower(1);
|
2020-08-13 17:54:33 +00:00
|
|
|
drawMenu();
|