Thingiloader = function(event) { // Code from https://developer.mozilla.org/En/Using_XMLHttpRequest#Receiving_binary_data this.load_binary_resource = function(url) { var req = new XMLHttpRequest(); req.open('GET', url, false); // The following line says we want to receive data as Binary and not as Unicode req.overrideMimeType('text/plain; charset=x-user-defined'); req.send(null); if (req.status != 200) return ''; return req.responseText; }; this.loadSTL = function(url) { var looksLikeBinary = function(reader) { // STL files don't specify a way to distinguish ASCII from binary. // The usual way is checking for "solid" at the start of the file -- // but Thingiverse has seen at least one binary STL file in the wild // that breaks this. // The approach here is different: binary STL files contain a triangle // count early in the file. If this correctly predicts the file's length, // it is most probably a binary STL file. reader.seek(80); // skip the header var count = reader.readUInt32(); var predictedSize = 80 /* header */ + 4 /* count */ + 50 * count; return reader.getSize() == predictedSize; }; workerFacadeMessage({'status':'message', 'content':'Downloading ' + url}); var file = this.load_binary_resource(url); var reader = new BinaryReader(file); if (looksLikeBinary(reader)) { this.loadSTLBinary(reader); } else { this.loadSTLString(file); } }; this.loadOBJ = function(url) { workerFacadeMessage({'status':'message', 'content':'Downloading ' + url}); var file = this.load_binary_resource(url); this.loadOBJString(file); }; this.loadJSON = function(url) { workerFacadeMessage({'status':'message', 'content':'Downloading ' + url}); var file = this.load_binary_resource(url); this.loadJSONString(file); }; this.loadPLY = function(url) { workerFacadeMessage({'status':'message', 'content':'Downloading ' + url}); var file = this.load_binary_resource(url); if (file.match(/format ascii/i)) { this.loadPLYString(file); } else { this.loadPLYBinary(file); } }; this.loadSTLString = function(STLString) { workerFacadeMessage({'status':'message', 'content':'Parsing STL String...'}); workerFacadeMessage({'status':'complete', 'content':this.ParseSTLString(STLString)}); }; this.loadSTLBinary = function(STLBinary) { workerFacadeMessage({'status':'message', 'content':'Parsing STL Binary...'}); workerFacadeMessage({'status':'complete', 'content':this.ParseSTLBinary(STLBinary)}); }; this.loadOBJString = function(OBJString) { workerFacadeMessage({'status':'message', 'content':'Parsing OBJ String...'}); workerFacadeMessage({'status':'complete', 'content':this.ParseOBJString(OBJString)}); }; this.loadJSONString = function(JSONString) { workerFacadeMessage({'status':'message', 'content':'Parsing JSON String...'}); workerFacadeMessage({'status':'complete', 'content':eval(JSONString)}); }; this.loadPLYString = function(PLYString) { workerFacadeMessage({'status':'message', 'content':'Parsing PLY String...'}); workerFacadeMessage({'status':'complete_points', 'content':this.ParsePLYString(PLYString)}); }; this.loadPLYBinary = function(PLYBinary) { workerFacadeMessage({'status':'message', 'content':'Parsing PLY Binary...'}); workerFacadeMessage({'status':'complete_points', 'content':this.ParsePLYBinary(PLYBinary)}); }; this.ParsePLYString = function(input) { var properties = []; var vertices = []; var colors = []; var vertex_count = 0; var header = /ply\n([\s\S]+)\nend_header/ig.exec(input)[1]; var data = /end_header\n([\s\S]+)$/ig.exec(input)[1]; // workerFacadeMessage({'status':'message', 'content':'header:\n' + header}); // workerFacadeMessage({'status':'message', 'content':'data:\n' + data}); header_parts = header.split("\n"); for (i in header_parts) { if (/element vertex/i.test(header_parts[i])) { vertex_count = /element vertex (\d+)/i.exec(header_parts[i])[1]; } else if (/property/i.test(header_parts[i])) { properties.push(/property (.*) (.*)/i.exec(header_parts[i])[2]); } } // workerFacadeMessage({'status':'message', 'content':'properties: ' + properties}); data_parts = data.split("\n"); for (i in data_parts) { data_line = data_parts[i]; data_line_parts = data_line.split(" "); vertices.push([ parseFloat(data_line_parts[properties.indexOf("x")]), parseFloat(data_line_parts[properties.indexOf("y")]), parseFloat(data_line_parts[properties.indexOf("z")]) ]); colors.push([ parseInt(data_line_parts[properties.indexOf("red")]), parseInt(data_line_parts[properties.indexOf("green")]), parseInt(data_line_parts[properties.indexOf("blue")]) ]); } // workerFacadeMessage({'status':'message', 'content':'vertices: ' + vertices}); return [vertices, colors]; }; this.ParsePLYBinary = function(input) { return false; }; this.ParseSTLBinary = function(input) { // Skip the header. input.seek(80); // Load the number of vertices. var count = input.readUInt32(); // During the parse loop we maintain the following data structures: var vertices = []; // Append-only list of all unique vertices. var vert_hash = {}; // Mapping from vertex to index in 'vertices', above. var faces = []; // List of triangle descriptions, each a three-element // list of indices in 'vertices', above. for (var i = 0; i < count; i++) { if (i % 100 == 0) { workerFacadeMessage({ 'status':'message', 'content':'Parsing ' + (i+1) + ' of ' + count + ' polygons...' }); workerFacadeMessage({ 'status':'progress', 'content':parseInt(i / count * 100) + '%' }); } // Skip the normal (3 single-precision floats) input.seek(input.getPosition() + 12); var face_indices = []; for (var x = 0; x < 3; x++) { var vertex = [input.readFloat(), input.readFloat(), input.readFloat()]; var vertexIndex = vert_hash[vertex]; if (vertexIndex == null) { vertexIndex = vertices.length; vertices.push(vertex); vert_hash[vertex] = vertexIndex; } face_indices.push(vertexIndex); } faces.push(face_indices); // Skip the "attribute" field (unused in common models) input.readUInt16(); } return [vertices, faces]; }; // build stl's vertex and face arrays this.ParseSTLString = function(STLString) { var vertexes = []; var faces = []; var face_vertexes = []; var vert_hash = {} // console.log(STLString); // strip out extraneous stuff STLString = STLString.replace(/\r/, "\n"); STLString = STLString.replace(/^solid[^\n]*/, ""); STLString = STLString.replace(/\n/g, " "); STLString = STLString.replace(/facet normal /g,""); STLString = STLString.replace(/outer loop/g,""); STLString = STLString.replace(/vertex /g,""); STLString = STLString.replace(/endloop/g,""); STLString = STLString.replace(/endfacet/g,""); STLString = STLString.replace(/endsolid[^\n]*/, ""); STLString = STLString.replace(/\s+/g, " "); STLString = STLString.replace(/^\s+/, ""); // console.log(STLString); var facet_count = 0; var block_start = 0; var points = STLString.split(" "); workerFacadeMessage({'status':'message', 'content':'Parsing vertices...'}); for (var i=0; i