diff options
Diffstat (limited to 'extlib/thingiview.js/thingiloader.js')
-rw-r--r-- | extlib/thingiview.js/thingiloader.js | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/extlib/thingiview.js/thingiloader.js b/extlib/thingiview.js/thingiloader.js new file mode 100644 index 00000000..3791a49c --- /dev/null +++ b/extlib/thingiview.js/thingiloader.js @@ -0,0 +1,318 @@ +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<points.length/12-1; i++) { + if ((i % 100) == 0) { + workerFacadeMessage({'status':'progress', 'content':parseInt(i / (points.length/12-1) * 100) + '%'}); + } + + var face_indices = []; + for (var x=0; x<3; x++) { + var vertex = [parseFloat(points[block_start+x*3+3]), parseFloat(points[block_start+x*3+4]), parseFloat(points[block_start+x*3+5])]; + + var vertexIndex = vert_hash[vertex]; + if (vertexIndex == null) { + vertexIndex = vertexes.length; + vertexes.push(vertex); + vert_hash[vertex] = vertexIndex; + } + + face_indices.push(vertexIndex); + } + faces.push(face_indices); + + block_start = block_start + 12; + } + + return [vertexes, faces]; + }; + + this.ParseOBJString = function(OBJString) { + var vertexes = []; + var faces = []; + + var lines = OBJString.split("\n"); + + // var normal_position = 0; + + for (var i=0; i<lines.length; i++) { + workerFacadeMessage({'status':'progress', 'content':parseInt(i / lines.length * 100) + '%'}); + + line_parts = lines[i].replace(/\s+/g, " ").split(" "); + + if (line_parts[0] == "v") { + vertexes.push([parseFloat(line_parts[1]), parseFloat(line_parts[2]), parseFloat(line_parts[3])]); + } else if (line_parts[0] == "f") { + faces.push([parseFloat(line_parts[1].split("/")[0])-1, parseFloat(line_parts[2].split("/")[0])-1, parseFloat(line_parts[3].split("/")[0]-1), 0]) + } + } + + return [vertexes, faces]; + }; + + switch(event.data.cmd) { + case "loadSTL": + this.loadSTL(event.data.param); + break; + case "loadSTLString": + this.loadSTLString(event.data.param); + break; + case "loadSTLBinary": + this.loadSTLBinary(event.data.param); + break; + case "loadOBJ": + this.loadOBJ(event.data.param); + break; + case "loadOBJString": + this.loadOBJString(event.data.param); + break; + case "loadJSON": + this.loadJSON(event.data.param); + break; + case "loadPLY": + this.loadPLY(event.data.param); + break; + case "loadPLYString": + this.loadPLYString(event.data.param); + break; + case "loadPLYBinary": + this.loadPLYBinary(event.data.param); + break; + } + +}; + +if (typeof(window) === "undefined") { + onmessage = Thingiloader; + workerFacadeMessage = postMessage; + importScripts('binaryReader.js'); +} else { + workerFacadeMessage = WorkerFacade.add(thingiurlbase + "/thingiloader.js", Thingiloader); +} |