threejs/viewer: STLLoader.js

File STLLoader.js, 9.2 KB (added by Leon Kos, 11 years ago)

Nalagalnik STL datotek

Line 
1/**
2 * @author aleeper / http://adamleeper.com/
3 * @author mrdoob / http://mrdoob.com/
4 * @author gero3 / https://github.com/gero3
5 *
6 * Description: A THREE loader for STL ASCII files, as created by Solidworks and other CAD programs.
7 *
8 * Supports both binary and ASCII encoded files, with automatic detection of type.
9 *
10 * Limitations:
11 *      Binary decoding ignores header. There doesn't seem to be much of a use for it.
12 *      There is perhaps some question as to how valid it is to always assume little-endian-ness.
13 *      ASCII decoding assumes file is UTF-8. Seems to work for the examples...
14 *
15 * Usage:
16 *      var loader = new THREE.STLLoader();
17 *      loader.addEventListener( 'load', function ( event ) {
18 *
19 *              var geometry = event.content;
20 *              scene.add( new THREE.Mesh( geometry ) );
21 *
22 *      } );
23 *      loader.load( './models/stl/slotted_disk.stl' );
24 */
25
26
27THREE.STLLoader = function () {};
28
29THREE.STLLoader.prototype = {
30
31        constructor: THREE.STLLoader
32
33};
34
35THREE.STLLoader.prototype.load = function (url, callback) {
36
37        var scope = this;
38
39        var xhr = new XMLHttpRequest();
40
41        function onloaded( event ) {
42
43                if ( event.target.status === 200 || event.target.status === 0 ) {
44
45                                var geometry = scope.parse( event.target.response || event.target.responseText );
46
47                                scope.dispatchEvent( { type: 'load', content: geometry } );
48
49                                if ( callback ) callback( geometry );
50
51                } else {
52
53                        scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']',
54                                response: event.target.responseText } );
55
56                }
57
58        }
59
60        xhr.addEventListener( 'load', onloaded, false );
61
62        xhr.addEventListener( 'progress', function ( event ) {
63
64                scope.dispatchEvent( { type: 'progress', loaded: event.loaded, total: event.total } );
65
66        }, false );
67
68        xhr.addEventListener( 'error', function () {
69
70                scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } );
71
72        }, false );
73
74        xhr.overrideMimeType('text/plain; charset=x-user-defined');
75        xhr.open( 'GET', url, true );
76        xhr.responseType = "arraybuffer";
77        xhr.send( null );
78
79};
80
81THREE.STLLoader.prototype.parse = function (data) {
82
83
84        var isBinary = function () {
85
86                var expect, face_size, n_faces, reader;
87                reader = new DataView( binData );
88                face_size = (32 / 8 * 3) + ((32 / 8 * 3) * 3) + (16 / 8);
89                n_faces = reader.getUint32(80,true);
90                expect = 80 + (32 / 8) + (n_faces * face_size);
91                return expect === reader.byteLength;
92
93        };
94
95        var binData = this.ensureBinary( data );
96
97        return isBinary()
98                ? this.parseBinary( binData )
99                : this.parseASCII( this.ensureString( data ) );
100
101};
102
103THREE.STLLoader.prototype.parseBinary = function (data) {
104
105        var face, geometry, n_faces, reader, length, normal, i, dataOffset, faceLength, start, vertexstart;
106
107        reader = new DataView( data );
108        n_faces = reader.getUint32(80,true);
109        geometry = new THREE.Geometry();
110        dataOffset = 84;
111        faceLength = 12 * 4 + 2;
112
113        for (face = 0; face < n_faces; face++) {
114
115                start = dataOffset + face * faceLength;
116                normal = new THREE.Vector3(
117                        reader.getFloat32(start,true),
118                        reader.getFloat32(start + 4,true),
119                        reader.getFloat32(start + 8,true)
120                );
121
122                for (i = 1; i <= 3; i++) {
123
124                        vertexstart = start + i * 12;
125                        geometry.vertices.push(
126                                new THREE.Vector3(
127                                        reader.getFloat32(vertexstart,true),
128                                        reader.getFloat32(vertexstart +4,true),
129                                        reader.getFloat32(vertexstart + 8,true)
130                                )
131                        );
132
133                }
134
135                length = geometry.vertices.length;
136                geometry.faces.push(new THREE.Face3(length - 3, length - 2, length - 1, normal));
137
138        }
139
140        geometry.computeCentroids();
141        geometry.computeBoundingSphere();
142
143        return geometry;
144
145};
146
147THREE.STLLoader.prototype.parseASCII = function (data) {
148
149        var geometry, length, normal, patternFace, patternNormal, patternVertex, result, text;
150        geometry = new THREE.Geometry();
151        patternFace = /facet([\s\S]*?)endfacet/g;
152
153        while (((result = patternFace.exec(data)) != null)) {
154
155                text = result[0];
156                patternNormal = /normal[\s]+([\-+]?[0-9]+\.?[0-9]*([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+/g;
157
158                while (((result = patternNormal.exec(text)) != null)) {
159
160                        normal = new THREE.Vector3(parseFloat(result[1]), parseFloat(result[3]), parseFloat(result[5]));
161
162                }
163
164                patternVertex = /vertex[\s]+([\-+]?[0-9]+\.?[0-9]*([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+/g;
165
166                while (((result = patternVertex.exec(text)) != null)) {
167
168                        geometry.vertices.push(new THREE.Vector3(parseFloat(result[1]), parseFloat(result[3]), parseFloat(result[5])));
169
170                }
171
172                length = geometry.vertices.length;
173                geometry.faces.push(new THREE.Face3(length - 3, length - 2, length - 1, normal));
174
175        }
176
177        geometry.computeCentroids();
178        geometry.computeBoundingBox();
179        geometry.computeBoundingSphere();
180
181        return geometry;
182
183};
184
185THREE.STLLoader.prototype.ensureString = function (buf) {
186
187        if (typeof buf !== "string"){
188                var array_buffer = new Uint8Array(buf);
189                var str = '';
190                for(var i = 0; i < buf.byteLength; i++) {
191                        str += String.fromCharCode(array_buffer[i]); // implicitly assumes little-endian
192                }
193                return str;
194        } else {
195                return buf;
196        }
197
198};
199
200THREE.STLLoader.prototype.ensureBinary = function (buf) {
201
202        if (typeof buf === "string"){
203                var array_buffer = new Uint8Array(buf.length);
204                for(var i = 0; i < buf.length; i++) {
205                        array_buffer[i] = buf.charCodeAt(i) & 0xff; // implicitly assumes little-endian
206                }
207                return array_buffer.buffer || array_buffer;
208        } else {
209                return buf;
210        }
211
212};
213
214THREE.EventDispatcher.prototype.apply( THREE.STLLoader.prototype );
215
216if ( typeof DataView === 'undefined'){
217
218        DataView = function(buffer, byteOffset, byteLength){
219
220                this.buffer = buffer;
221                this.byteOffset = byteOffset || 0;
222                this.byteLength = byteLength || buffer.byteLength || buffer.length;
223                this._isString = typeof buffer === "string";
224
225        }
226
227        DataView.prototype = {
228
229                _getCharCodes:function(buffer,start,length){
230                        start = start || 0;
231                        length = length || buffer.length;
232                        var end = start + length;
233                        var codes = [];
234                        for (var i = start; i < end; i++) {
235                                codes.push(buffer.charCodeAt(i) & 0xff);
236                        }
237                        return codes;
238                },
239
240                _getBytes: function (length, byteOffset, littleEndian) {
241
242                        var result;
243
244                        // Handle the lack of endianness
245                        if (littleEndian === undefined) {
246
247                                littleEndian = this._littleEndian;
248
249                        }
250
251                        // Handle the lack of byteOffset
252                        if (byteOffset === undefined) {
253
254                                byteOffset = this.byteOffset;
255
256                        } else {
257
258                                byteOffset = this.byteOffset + byteOffset;
259
260                        }
261
262                        if (length === undefined) {
263
264                                length = this.byteLength - byteOffset;
265
266                        }
267
268                        // Error Checking
269                        if (typeof byteOffset !== 'number') {
270
271                                throw new TypeError('DataView byteOffset is not a number');
272
273                        }
274
275                        if (length < 0 || byteOffset + length > this.byteLength) {
276
277                                throw new Error('DataView length or (byteOffset+length) value is out of bounds');
278
279                        }
280
281                        if (this.isString){
282
283                                result = this._getCharCodes(this.buffer, byteOffset, byteOffset + length);
284
285                        } else {
286
287                                result = this.buffer.slice(byteOffset, byteOffset + length);
288
289                        }
290
291                        if (!littleEndian && length > 1) {
292
293                                if (!(result instanceof Array)) {
294
295                                        result = Array.prototype.slice.call(result);
296
297                                }
298
299                                result.reverse();
300                        }
301
302                        return result;
303
304                },
305
306                // Compatibility functions on a String Buffer
307
308                getFloat64: function (byteOffset, littleEndian) {
309
310                        var b = this._getBytes(8, byteOffset, littleEndian),
311
312                                sign = 1 - (2 * (b[7] >> 7)),
313                                exponent = ((((b[7] << 1) & 0xff) << 3) | (b[6] >> 4)) - ((1 << 10) - 1),
314
315                        // Binary operators such as | and << operate on 32 bit values, using + and Math.pow(2) instead
316                                mantissa = ((b[6] & 0x0f) * Math.pow(2, 48)) + (b[5] * Math.pow(2, 40)) + (b[4] * Math.pow(2, 32)) +
317                                                        (b[3] * Math.pow(2, 24)) + (b[2] * Math.pow(2, 16)) + (b[1] * Math.pow(2, 8)) + b[0];
318
319                        if (exponent === 1024) {
320                                if (mantissa !== 0) {
321                                        return NaN;
322                                } else {
323                                        return sign * Infinity;
324                                }
325                        }
326
327                        if (exponent === -1023) { // Denormalized
328                                return sign * mantissa * Math.pow(2, -1022 - 52);
329                        }
330
331                        return sign * (1 + mantissa * Math.pow(2, -52)) * Math.pow(2, exponent);
332
333                },
334
335                getFloat32: function (byteOffset, littleEndian) {
336
337                        var b = this._getBytes(4, byteOffset, littleEndian),
338
339                                sign = 1 - (2 * (b[3] >> 7)),
340                                exponent = (((b[3] << 1) & 0xff) | (b[2] >> 7)) - 127,
341                                mantissa = ((b[2] & 0x7f) << 16) | (b[1] << 8) | b[0];
342
343                        if (exponent === 128) {
344                                if (mantissa !== 0) {
345                                        return NaN;
346                                } else {
347                                        return sign * Infinity;
348                                }
349                        }
350
351                        if (exponent === -127) { // Denormalized
352                                return sign * mantissa * Math.pow(2, -126 - 23);
353                        }
354
355                        return sign * (1 + mantissa * Math.pow(2, -23)) * Math.pow(2, exponent);
356                },
357
358                getInt32: function (byteOffset, littleEndian) {
359                        var b = this._getBytes(4, byteOffset, littleEndian);
360                        return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
361                },
362
363                getUint32: function (byteOffset, littleEndian) {
364                        return this.getInt32(byteOffset, littleEndian) >>> 0;
365                },
366
367                getInt16: function (byteOffset, littleEndian) {
368                        return (this.getUint16(byteOffset, littleEndian) << 16) >> 16;
369                },
370
371                getUint16: function (byteOffset, littleEndian) {
372                        var b = this._getBytes(2, byteOffset, littleEndian);
373                        return (b[1] << 8) | b[0];
374                },
375
376                getInt8: function (byteOffset) {
377                        return (this.getUint8(byteOffset) << 24) >> 24;
378                },
379
380                getUint8: function (byteOffset) {
381                        return this._getBytes(1, byteOffset)[0];
382                }
383
384         };
385
386}