function parseAsciiSTL(stl,fn) { var src = "// OpenJSCAD.org: stl importer (ascii) '"+fn+"'\n\n"; var n = 0; var converted = 0; var o; src += "function main() { return union(\n"; // -- Find all models var objects = stl.split('endsolid'); src += "// objects: "+(objects.length-1)+"\n"; for (o = 1; o < objects.length; o++) { // -- Translation: a non-greedy regex for facet {...} endloop pattern var patt = /\bfacet[\s\S]*?endloop/mgi; var vertices = []; var triangles = []; var normals = []; var vertexIndex = 0; var err = 0; match = stl.match(patt); if (match == null) continue; for (var i = 0; i < match.length; i++) { //if(converted%100==0) status('stl to jscad: converted '+converted+' out of '+match.length+ ' facets'); // -- 1 normal with 3 numbers, 3 different vertex objects each with 3 numbers: //var vpatt = /\bfacet\s+normal\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s*outer\s+loop\s+vertex\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s*vertex\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s*vertex\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)/mgi; // (-?\d+\.?\d*) -1.21223 // (-?\d+\.?\d*[Ee]?[-+]?\d*) var vpatt = /\bfacet\s+normal\s+(\S+)\s+(\S+)\s+(\S+)\s+outer\s+loop\s+vertex\s+(\S+)\s+(\S+)\s+(\S+)\s+vertex\s+(\S+)\s+(\S+)\s+(\S+)\s+vertex\s+(\S+)\s+(\S+)\s+(\S+)\s*/mgi; var v = vpatt.exec(match[i]); if (v == null) continue; if (v.length != 13) { echo("Failed to parse " + match[i]); break; } var skip = 0; for(var k=0; k<v.length; k++) { if(v[k]=='NaN') { echo("bad normal or triangle vertice #"+converted+" "+k+": '"+v[k]+"', skipped"); skip++; } } err += skip; if(skip) { continue; } if(0&&skip) { var j = 1+3; var v1 = []; v1.push(parseFloat(v[j++])); v1.push(parseFloat(v[j++])); v1.push(parseFloat(v[j++])); var v2 = []; v2.push(parseFloat(v[j++])); v2.push(parseFloat(v[j++])); v2.push(parseFloat(v[j++])); var v3 = []; v3.push(parseFloat(v[j++])); v3.push(parseFloat(v[j++])); v3.push(parseFloat(v[j++])); echo("recalculate norm",v1,v2,v3); var w1 = new CSG.Vector3D(v1); var w2 = new CSG.Vector3D(v2); var w3 = new CSG.Vector3D(v3); var _u = w1.minus(w3); var _v = w1.minus(w2); var norm = _u.cross(_v).unit(); j = 1; v[j++] = norm._x; v[j++] = norm._y; v[j++] = norm._z; skip = false; } var j = 1; var no = []; no.push(parseFloat(v[j++])); no.push(parseFloat(v[j++])); no.push(parseFloat(v[j++])); var v1 = []; v1.push(parseFloat(v[j++])); v1.push(parseFloat(v[j++])); v1.push(parseFloat(v[j++])); var v2 = []; v2.push(parseFloat(v[j++])); v2.push(parseFloat(v[j++])); v2.push(parseFloat(v[j++])); var v3 = []; v3.push(parseFloat(v[j++])); v3.push(parseFloat(v[j++])); v3.push(parseFloat(v[j++])); var triangle = []; triangle.push(vertexIndex++); triangle.push(vertexIndex++); triangle.push(vertexIndex++); // -- Add 3 vertices for every triangle // TODO: OPTIMIZE: Check if the vertex is already in the array, if it is just reuse the index if(skip==0) { // checking cw vs ccw // E1 = B - A // E2 = C - A // test = dot( Normal, cross( E1, E2 ) ) // test > 0: cw, test < 0: ccw var w1 = new CSG.Vector3D(v1); var w2 = new CSG.Vector3D(v2); var w3 = new CSG.Vector3D(v3); var e1 = w2.minus(w1); var e2 = w3.minus(w1); var t = new CSG.Vector3D(no).dot(e1.cross(e2)); if(t>0) { // 1,2,3 -> 3,2,1 var tmp = v3; v3 = v1; v1 = tmp; } } vertices.push(v1); vertices.push(v2); vertices.push(v3); normals.push(no); triangles.push(triangle); converted++; } if(n++) src += ","; if(err) src += "// WARNING: import errors: "+err+" (some triangles might be misaligned or missing)\n"; src += "// object #"+(o)+": triangles: "+match.length+"\n"; src += vt2jscad(vertices,triangles,normals); } src += "); }\n"; return src; }
function parseBinarySTL(stl,fn) { // -- This makes more sense if you read http://en.wikipedia.org/wiki/STL_(file_format)#Binary_STL var vertices = []; var triangles = []; var normals = []; var vertexIndex = 0; var converted = 0; var err = 0; var br = new BinaryReader(stl); br.seek(80); //Skip header //for(var i=0; i<80; i++) // br.readInt8(); var totalTriangles = br.readUInt32(); //Read # triangles for (var tr = 0; tr < totalTriangles; tr++) { //if(tr%100==0) status('stl importer: converted '+converted+' out of '+totalTriangles+' triangles'); /* REAL32[3] . Normal vector REAL32[3] . Vertex 1 REAL32[3] . Vertex 2 REAL32[3] . Vertex 3 UINT16 . Attribute byte count */ // -- Parse normal var no = []; no.push(br.readFloat()); no.push(br.readFloat()); no.push(br.readFloat()); // -- Parse every 3 subsequent floats as a vertex var v1 = []; v1.push(br.readFloat()); v1.push(br.readFloat()); v1.push(br.readFloat()); var v2 = []; v2.push(br.readFloat()); v2.push(br.readFloat()); v2.push(br.readFloat()); var v3 = []; v3.push(br.readFloat()); v3.push(br.readFloat()); v3.push(br.readFloat()); var skip = 0; if(1) { for(var i=0; i<3; i++) { if(isNaN(v1[i])) skip++; if(isNaN(v2[i])) skip++; if(isNaN(v3[i])) skip++; if(isNaN(no[i])) skip++; } if(skip>0) { echo("bad triangle vertice coords/normal: ",skip); } } err += skip; // -- every 3 vertices create a triangle. var triangle = []; triangle.push(vertexIndex++); triangle.push(vertexIndex++); triangle.push(vertexIndex++); br.readUInt16(); // -- Add 3 vertices for every triangle // -- TODO: OPTIMIZE: Check if the vertex is already in the array, if it is just reuse the index if(skip==0) { // checking cw vs ccw, given all normal/vertice are valid // E1 = B - A // E2 = C - A // test = dot( Normal, cross( E1, E2 ) ) // test > 0: cw, test < 0 : ccw var w1 = new CSG.Vector3D(v1); var w2 = new CSG.Vector3D(v2); var w3 = new CSG.Vector3D(v3); var e1 = w2.minus(w1); var e2 = w3.minus(w1); var t = new CSG.Vector3D(no).dot(e1.cross(e2)); if(t>0) { // 1,2,3 -> 3,2,1 var tmp = v3; v3 = v1; v1 = tmp; } } vertices.push(v1); vertices.push(v2); vertices.push(v3); triangles.push(triangle); normals.push(no); converted++; } var src = "// OpenJSCAD.org: stl importer (binary) '"+fn+"'\n\n"; if(err) src += "// WARNING: import errors: "+err+" (some triangles might be misaligned or missing)\n"; src += "// objects: 1\n// object #1: triangles: "+totalTriangles+"\n"; src += "function main() { return "; src += vt2jscad(vertices,triangles,normals); src += "; }"; return src; }