past3d/pastebin/static/js/thingview.js/thingiloader.js

319 lines
10 KiB
JavaScript

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);
}