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');
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
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
);
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
);
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
) {
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(" ");
129 parseFloat(data_line_parts
[properties
.indexOf("x")]),
130 parseFloat(data_line_parts
[properties
.indexOf("y")]),
131 parseFloat(data_line_parts
[properties
.indexOf("z")])
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
) {
150 this.ParseSTLBinary = function(input
) {
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
++) {
165 workerFacadeMessage({
167 'content':'Parsing ' + (i
+1) + ' of ' + count
+ ' polygons...'
169 workerFacadeMessage({
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)
197 return [vertices
, faces
];
200 // build stl's vertex and face arrays
201 this.ParseSTLString = function(STLString
) {
205 var face_vertexes
= [];
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);
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
) {
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
) {
282 this.loadSTL(event
.data
.param
);
284 case "loadSTLString":
285 this.loadSTLString(event
.data
.param
);
287 case "loadSTLBinary":
288 this.loadSTLBinary(event
.data
.param
);
291 this.loadOBJ(event
.data
.param
);
293 case "loadOBJString":
294 this.loadOBJString(event
.data
.param
);
297 this.loadJSON(event
.data
.param
);
300 this.loadPLY(event
.data
.param
);
302 case "loadPLYString":
303 this.loadPLYString(event
.data
.param
);
305 case "loadPLYBinary":
306 this.loadPLYBinary(event
.data
.param
);
312 if (typeof(window
) === "undefined") {
313 onmessage
= Thingiloader
;
314 workerFacadeMessage
= postMessage
;
315 importScripts('binaryReader.js');
317 workerFacadeMessage
= WorkerFacade
.add(thingiurlbase
+ "/thingiloader.js", Thingiloader
);