Fix #5398 and #5395 - Fix tests failing due to problem creating connection for alembic
[larjonas-mediagoblin.git] / extlib / thingiview.js / thingiloader.js
blob3791a49cd88c5f45430442d926882aa04993b6ac
1 Thingiloader = function(event) {
2 // Code from https://developer.mozilla.org/En/Using_XMLHttpRequest#Receiving_binary_data
3 this.load_binary_resource = function(url) {
4 var req = new XMLHttpRequest();
5 req.open('GET', url, false);
6 // The following line says we want to receive data as Binary and not as Unicode
7 req.overrideMimeType('text/plain; charset=x-user-defined');
8 req.send(null);
9 if (req.status != 200) return '';
11 return req.responseText;
14 this.loadSTL = function(url) {
15 var looksLikeBinary = function(reader) {
16 // STL files don't specify a way to distinguish ASCII from binary.
17 // The usual way is checking for "solid" at the start of the file --
18 // but Thingiverse has seen at least one binary STL file in the wild
19 // that breaks this.
21 // The approach here is different: binary STL files contain a triangle
22 // count early in the file. If this correctly predicts the file's length,
23 // it is most probably a binary STL file.
25 reader.seek(80); // skip the header
26 var count = reader.readUInt32();
28 var predictedSize = 80 /* header */ + 4 /* count */ + 50 * count;
29 return reader.getSize() == predictedSize;
32 workerFacadeMessage({'status':'message', 'content':'Downloading ' + url});
33 var file = this.load_binary_resource(url);
34 var reader = new BinaryReader(file);
36 if (looksLikeBinary(reader)) {
37 this.loadSTLBinary(reader);
38 } else {
39 this.loadSTLString(file);
43 this.loadOBJ = function(url) {
44 workerFacadeMessage({'status':'message', 'content':'Downloading ' + url});
45 var file = this.load_binary_resource(url);
46 this.loadOBJString(file);
49 this.loadJSON = function(url) {
50 workerFacadeMessage({'status':'message', 'content':'Downloading ' + url});
51 var file = this.load_binary_resource(url);
52 this.loadJSONString(file);
55 this.loadPLY = function(url) {
56 workerFacadeMessage({'status':'message', 'content':'Downloading ' + url});
58 var file = this.load_binary_resource(url);
60 if (file.match(/format ascii/i)) {
61 this.loadPLYString(file);
62 } else {
63 this.loadPLYBinary(file);
67 this.loadSTLString = function(STLString) {
68 workerFacadeMessage({'status':'message', 'content':'Parsing STL String...'});
69 workerFacadeMessage({'status':'complete', 'content':this.ParseSTLString(STLString)});
72 this.loadSTLBinary = function(STLBinary) {
73 workerFacadeMessage({'status':'message', 'content':'Parsing STL Binary...'});
74 workerFacadeMessage({'status':'complete', 'content':this.ParseSTLBinary(STLBinary)});
77 this.loadOBJString = function(OBJString) {
78 workerFacadeMessage({'status':'message', 'content':'Parsing OBJ String...'});
79 workerFacadeMessage({'status':'complete', 'content':this.ParseOBJString(OBJString)});
82 this.loadJSONString = function(JSONString) {
83 workerFacadeMessage({'status':'message', 'content':'Parsing JSON String...'});
84 workerFacadeMessage({'status':'complete', 'content':eval(JSONString)});
87 this.loadPLYString = function(PLYString) {
88 workerFacadeMessage({'status':'message', 'content':'Parsing PLY String...'});
89 workerFacadeMessage({'status':'complete_points', 'content':this.ParsePLYString(PLYString)});
92 this.loadPLYBinary = function(PLYBinary) {
93 workerFacadeMessage({'status':'message', 'content':'Parsing PLY Binary...'});
94 workerFacadeMessage({'status':'complete_points', 'content':this.ParsePLYBinary(PLYBinary)});
97 this.ParsePLYString = function(input) {
98 var properties = [];
99 var vertices = [];
100 var colors = [];
102 var vertex_count = 0;
104 var header = /ply\n([\s\S]+)\nend_header/ig.exec(input)[1];
105 var data = /end_header\n([\s\S]+)$/ig.exec(input)[1];
107 // workerFacadeMessage({'status':'message', 'content':'header:\n' + header});
108 // workerFacadeMessage({'status':'message', 'content':'data:\n' + data});
110 header_parts = header.split("\n");
112 for (i in header_parts) {
113 if (/element vertex/i.test(header_parts[i])) {
114 vertex_count = /element vertex (\d+)/i.exec(header_parts[i])[1];
115 } else if (/property/i.test(header_parts[i])) {
116 properties.push(/property (.*) (.*)/i.exec(header_parts[i])[2]);
120 // workerFacadeMessage({'status':'message', 'content':'properties: ' + properties});
122 data_parts = data.split("\n");
124 for (i in data_parts) {
125 data_line = data_parts[i];
126 data_line_parts = data_line.split(" ");
128 vertices.push([
129 parseFloat(data_line_parts[properties.indexOf("x")]),
130 parseFloat(data_line_parts[properties.indexOf("y")]),
131 parseFloat(data_line_parts[properties.indexOf("z")])
134 colors.push([
135 parseInt(data_line_parts[properties.indexOf("red")]),
136 parseInt(data_line_parts[properties.indexOf("green")]),
137 parseInt(data_line_parts[properties.indexOf("blue")])
141 // workerFacadeMessage({'status':'message', 'content':'vertices: ' + vertices});
143 return [vertices, colors];
146 this.ParsePLYBinary = function(input) {
147 return false;
150 this.ParseSTLBinary = function(input) {
151 // Skip the header.
152 input.seek(80);
154 // Load the number of vertices.
155 var count = input.readUInt32();
157 // During the parse loop we maintain the following data structures:
158 var vertices = []; // Append-only list of all unique vertices.
159 var vert_hash = {}; // Mapping from vertex to index in 'vertices', above.
160 var faces = []; // List of triangle descriptions, each a three-element
161 // list of indices in 'vertices', above.
163 for (var i = 0; i < count; i++) {
164 if (i % 100 == 0) {
165 workerFacadeMessage({
166 'status':'message',
167 'content':'Parsing ' + (i+1) + ' of ' + count + ' polygons...'
169 workerFacadeMessage({
170 'status':'progress',
171 'content':parseInt(i / count * 100) + '%'
175 // Skip the normal (3 single-precision floats)
176 input.seek(input.getPosition() + 12);
178 var face_indices = [];
179 for (var x = 0; x < 3; x++) {
180 var vertex = [input.readFloat(), input.readFloat(), input.readFloat()];
182 var vertexIndex = vert_hash[vertex];
183 if (vertexIndex == null) {
184 vertexIndex = vertices.length;
185 vertices.push(vertex);
186 vert_hash[vertex] = vertexIndex;
189 face_indices.push(vertexIndex);
191 faces.push(face_indices);
193 // Skip the "attribute" field (unused in common models)
194 input.readUInt16();
197 return [vertices, faces];
200 // build stl's vertex and face arrays
201 this.ParseSTLString = function(STLString) {
202 var vertexes = [];
203 var faces = [];
205 var face_vertexes = [];
206 var vert_hash = {}
208 // console.log(STLString);
210 // strip out extraneous stuff
211 STLString = STLString.replace(/\r/, "\n");
212 STLString = STLString.replace(/^solid[^\n]*/, "");
213 STLString = STLString.replace(/\n/g, " ");
214 STLString = STLString.replace(/facet normal /g,"");
215 STLString = STLString.replace(/outer loop/g,"");
216 STLString = STLString.replace(/vertex /g,"");
217 STLString = STLString.replace(/endloop/g,"");
218 STLString = STLString.replace(/endfacet/g,"");
219 STLString = STLString.replace(/endsolid[^\n]*/, "");
220 STLString = STLString.replace(/\s+/g, " ");
221 STLString = STLString.replace(/^\s+/, "");
223 // console.log(STLString);
225 var facet_count = 0;
226 var block_start = 0;
228 var points = STLString.split(" ");
230 workerFacadeMessage({'status':'message', 'content':'Parsing vertices...'});
231 for (var i=0; i<points.length/12-1; i++) {
232 if ((i % 100) == 0) {
233 workerFacadeMessage({'status':'progress', 'content':parseInt(i / (points.length/12-1) * 100) + '%'});
236 var face_indices = [];
237 for (var x=0; x<3; x++) {
238 var vertex = [parseFloat(points[block_start+x*3+3]), parseFloat(points[block_start+x*3+4]), parseFloat(points[block_start+x*3+5])];
240 var vertexIndex = vert_hash[vertex];
241 if (vertexIndex == null) {
242 vertexIndex = vertexes.length;
243 vertexes.push(vertex);
244 vert_hash[vertex] = vertexIndex;
247 face_indices.push(vertexIndex);
249 faces.push(face_indices);
251 block_start = block_start + 12;
254 return [vertexes, faces];
257 this.ParseOBJString = function(OBJString) {
258 var vertexes = [];
259 var faces = [];
261 var lines = OBJString.split("\n");
263 // var normal_position = 0;
265 for (var i=0; i<lines.length; i++) {
266 workerFacadeMessage({'status':'progress', 'content':parseInt(i / lines.length * 100) + '%'});
268 line_parts = lines[i].replace(/\s+/g, " ").split(" ");
270 if (line_parts[0] == "v") {
271 vertexes.push([parseFloat(line_parts[1]), parseFloat(line_parts[2]), parseFloat(line_parts[3])]);
272 } else if (line_parts[0] == "f") {
273 faces.push([parseFloat(line_parts[1].split("/")[0])-1, parseFloat(line_parts[2].split("/")[0])-1, parseFloat(line_parts[3].split("/")[0]-1), 0])
277 return [vertexes, faces];
280 switch(event.data.cmd) {
281 case "loadSTL":
282 this.loadSTL(event.data.param);
283 break;
284 case "loadSTLString":
285 this.loadSTLString(event.data.param);
286 break;
287 case "loadSTLBinary":
288 this.loadSTLBinary(event.data.param);
289 break;
290 case "loadOBJ":
291 this.loadOBJ(event.data.param);
292 break;
293 case "loadOBJString":
294 this.loadOBJString(event.data.param);
295 break;
296 case "loadJSON":
297 this.loadJSON(event.data.param);
298 break;
299 case "loadPLY":
300 this.loadPLY(event.data.param);
301 break;
302 case "loadPLYString":
303 this.loadPLYString(event.data.param);
304 break;
305 case "loadPLYBinary":
306 this.loadPLYBinary(event.data.param);
307 break;
312 if (typeof(window) === "undefined") {
313 onmessage = Thingiloader;
314 workerFacadeMessage = postMessage;
315 importScripts('binaryReader.js');
316 } else {
317 workerFacadeMessage = WorkerFacade.add(thingiurlbase + "/thingiloader.js", Thingiloader);