1/** 2 * @author aleeper / http://adamleeper.com/ 3 * @author mrdoob / http://mrdoob.com/ 4 * @author gero3 / https://github.com/gero3 5 * @author Mugen87 / https://github.com/Mugen87 6 * 7 * Description: A THREE loader for STL ASCII files, as created by Solidworks and other CAD programs. 8 * 9 * Supports both binary and ASCII encoded files, with automatic detection of type. 10 * 11 * The loader returns a non-indexed buffer geometry. 12 * 13 * Limitations: 14 * Binary decoding supports "Magics" color format (http://en.wikipedia.org/wiki/STL_(file_format)#Color_in_binary_STL). 15 * There is perhaps some question as to how valid it is to always assume little-endian-ness. 16 * ASCII decoding assumes file is UTF-8. 17 * 18 * Usage: 19 * var loader = new THREEW.STLLoader(); 20 * loader.load( './models/stl/slotted_disk.stl', function ( geometry ) { 21 * scene.add( new THREEW.Mesh( geometry ) ); 22 * }); 23 * 24 * For binary STLs geometry might contain colors for vertices. To use it: 25 * // use the same code to load STL as above 26 * if (geometry.hasColors) { 27 * material = new THREEW.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: THREEW.VertexColors }); 28 * } else { .... } 29 * var mesh = new THREEW.Mesh( geometry, material ); 30 */ 31 32 33THREEW.STLLoader = function ( manager ) { 34 35 this.manager = ( manager !== undefined ) ? manager : THREEW.DefaultLoadingManager; 36 37}; 38 39THREEW.STLLoader.prototype = { 40 41 constructor: THREEW.STLLoader, 42 43 load: function ( url, onLoad, onProgress, onError ) { 44 45 var scope = this; 46 47 var loader = new THREEW.FileLoader( scope.manager ); 48 loader.setPath( scope.path ); 49 loader.setResponseType( 'arraybuffer' ); 50 loader.load( url, function ( text ) { 51 52 try { 53 54 onLoad( scope.parse( text ) ); 55 56 } catch ( exception ) { 57 58 if ( onError ) { 59 60 onError( exception ); 61 62 } 63 64 } 65 66 }, onProgress, onError ); 67 68 }, 69 70 setPath: function ( value ) { 71 72 this.path = value; 73 return this; 74 75 }, 76 77 parse: function ( data ) { 78 79 function isBinary( data ) { 80 81 var expect, face_size, n_faces, reader; 82 reader = new DataView( data ); 83 face_size = ( 32 / 8 * 3 ) + ( ( 32 / 8 * 3 ) * 3 ) + ( 16 / 8 ); 84 n_faces = reader.getUint32( 80, true ); 85 expect = 80 + ( 32 / 8 ) + ( n_faces * face_size ); 86 87 if ( expect === reader.byteLength ) { 88 89 return true; 90 91 } 92 93 // An ASCII STL data must begin with 'solid ' as the first six bytes. 94 // However, ASCII STLs lacking the SPACE after the 'd' are known to be 95 // plentiful. So, check the first 5 bytes for 'solid'. 96 97 // Several encodings, such as UTF-8, precede the text with up to 5 bytes: 98 // https://en.wikipedia.org/wiki/Byte_order_mark#Byte_order_marks_by_encoding 99 // Search for "solid" to start anywhere after those prefixes. 100 101 // US-ASCII ordinal values for 's', 'o', 'l', 'i', 'd' 102 103 var solid = [ 115, 111, 108, 105, 100 ]; 104 105 for ( var off = 0; off < 5; off ++ ) { 106 107 // If "solid" text is matched to the current offset, declare it to be an ASCII STL. 108 109 if ( matchDataViewAt ( solid, reader, off ) ) return false; 110 111 } 112 113 // Couldn't find "solid" text at the beginning; it is binary STL. 114 115 return true; 116 117 } 118 119 function matchDataViewAt( query, reader, offset ) { 120 121 // Check if each byte in query matches the corresponding byte from the current offset 122 123 for ( var i = 0, il = query.length; i < il; i ++ ) { 124 125 if ( query[ i ] !== reader.getUint8( offset + i, false ) ) return false; 126 127 } 128 129 return true; 130 131 } 132 133 function parseBinary( data ) { 134 135 var reader = new DataView( data ); 136 var faces = reader.getUint32( 80, true ); 137 138 var r, g, b, hasColors = false, colors; 139 var defaultR, defaultG, defaultB, alpha; 140 141 // process STL header 142 // check for default color in header ("COLOR=rgba" sequence). 143 144 for ( var index = 0; index < 80 - 10; index ++ ) { 145 146 if ( ( reader.getUint32( index, false ) == 0x434F4C4F /*COLO*/ ) && 147 ( reader.getUint8( index + 4 ) == 0x52 /*'R'*/ ) && 148 ( reader.getUint8( index + 5 ) == 0x3D /*'='*/ ) ) { 149 150 hasColors = true; 151 colors = []; 152 153 defaultR = reader.getUint8( index + 6 ) / 255; 154 defaultG = reader.getUint8( index + 7 ) / 255; 155 defaultB = reader.getUint8( index + 8 ) / 255;
156 alpha = reader.getUint8( index + 9 ) / 255; 157 158 } 159 160 } 161 162 var dataOffset = 84; 163 var faceLength = 12 * 4 + 2; 164 165 var geometry = new THREEW.BufferGeometry(); 166 167 var vertices = []; 168 var normals = []; 169 170 for ( var face = 0; face < faces; face ++ ) { 171 172 var start = dataOffset + face * faceLength; 173 var normalX = reader.getFloat32( start, true ); 174 var normalY = reader.getFloat32( start + 4, true ); 175 var normalZ = reader.getFloat32( start + 8, true ); 176 177 if ( hasColors ) { 178 179 var packedColor = reader.getUint16( start + 48, true ); 180 181 if ( ( packedColor & 0x8000 ) === 0 ) { 182 183 // facet has its own unique color 184 185 r = ( packedColor & 0x1F ) / 31; 186 g = ( ( packedColor >> 5 ) & 0x1F ) / 31; 187 b = ( ( packedColor >> 10 ) & 0x1F ) / 31; 188 189 } else { 190 191 r = defaultR; 192 g = defaultG; 193 b = defaultB; 194 195 } 196 197 } 198 199 for ( var i = 1; i <= 3; i ++ ) { 200 201 var vertexstart = start + i * 12; 202 203 vertices.push( reader.getFloat32( vertexstart, true ) ); 204 vertices.push( reader.getFloat32( vertexstart + 4, true ) ); 205 vertices.push( reader.getFloat32( vertexstart + 8, true ) ); 206 207 normals.push( normalX, normalY, normalZ ); 208 209 if ( hasColors ) { 210 211 colors.push( r, g, b ); 212 213 } 214 215 } 216 217 } 218 219 geometry.addAttribute( 'position', new THREEW.BufferAttribute( new Float32Array( vertices ), 3 ) ); 220 geometry.addAttribute( 'normal', new THREEW.BufferAttribute( new Float32Array( normals ), 3 ) ); 221 222 if ( hasColors ) { 223 224 geometry.addAttribute( 'color', new THREEW.BufferAttribute( new Float32Array( colors ), 3 ) ); 225 geometry.hasColors = true; 226 geometry.alpha = alpha; 227 228 } 229 230 return geometry; 231 232 } 233 234 function parseASCII( data ) { 235 236 var geometry = new THREEW.BufferGeometry(); 237 var patternFace = /facet([\s\S]*?)endfacet/g; 238 var faceCounter = 0; 239 240 var patternFloat = /[\s]+([+-]?(?:\d*)(?:\.\d*)?(?:[eE][+-]?\d+)?)/.source; 241 var patternVertex = new RegExp( 'vertex' + patternFloat + patternFloat + patternFloat, 'g' ); 242 var patternNormal = new RegExp( 'normal' + patternFloat + patternFloat + patternFloat, 'g' ); 243 244 var vertices = []; 245 var normals = []; 246 247 var normal = new THREEW.Vector3(); 248 249 var result; 250 251 while ( ( result = patternFace.exec( data ) ) !== null ) { 252 253 var vertexCountPerFace = 0; 254 var normalCountPerFace = 0; 255 256 var text = result[ 0 ]; 257 258 while ( ( result = patternNormal.exec( text ) ) !== null ) { 259 260 normal.x = parseFloat( result[ 1 ] ); 261 normal.y = parseFloat( result[ 2 ] ); 262 normal.z = parseFloat( result[ 3 ] ); 263 normalCountPerFace ++; 264 265 } 266 267 while ( ( result = patternVertex.exec( text ) ) !== null ) { 268 269 vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) ); 270 normals.push( normal.x, normal.y, normal.z ); 271 vertexCountPerFace ++; 272 273 } 274 275 // every face have to own ONE valid normal 276 277 if ( normalCountPerFace !== 1 ) { 278 279 console.error( 'THREEW.STLLoader: Something isn\'t right with the normal of face number ' + faceCounter ); 280 281 } 282 283 // each face have to own THREE valid vertices 284 285 if ( vertexCountPerFace !== 3 ) { 286 287 console.error( 'THREEW.STLLoader: Something isn\'t right with the vertices of face number ' + faceCounter ); 288 289 } 290 291 faceCounter ++; 292 293 } 294 295 geometry.addAttribute( 'position', new THREEW.Float32BufferAttribute( vertices, 3 ) ); 296 geometry.addAttribute( 'normal', new THREEW.Float32BufferAttribute( normals, 3 ) ); 297 298 return geometry; 299 300 } 301 302 function ensureString( buffer ) { 303 304 if ( typeof buffer !== 'string' ) { 305 306 return THREEW.LoaderUtils.decodeText( new Uint8Array( buffer ) ); 307 308 } 309 310 return buffer; 311 312 } 313 314 function ensureBinary( buffer ) { 315 316 if ( typeof buffer === 'string' ) { 317 318 var array_buffer = new Uint8Array( buffer.length ); 319 for ( var i = 0; i < buffer.length; i ++ ) { 320 321 array_buffer[ i ] = buffer.charCodeAt( i ) & 0xff; // implicitly assumes little-endian 322 323 } 324 return array_buffer.buffer || array_buffer; 325 326 } else { 327 328 return buffer; 329 330 } 331 332 } 333 334 // start 335 336 var binData = ensureBinary( data ); 337 338 339 //fuzzoid mod 340 var myreader = new DataView( data ); 341 this.byteLength = myreader.byteLength; 342 343 return isBinary( binData ) ? parseBinary( binData ) : parseASCII( ensureString( data ) ); 344 345 } 346 347};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.