PageSourceSearch

https://solusi3d.co.id/wp-content/plugins/3dprint-lite/includes/ext/threejs/js/loaders/STLLoader.js?ver=2.1.4

js solusi3d.co.id collected 2026-10-08 02:37:36 UTC 9,519 bytes, 373 lines download raw bytes

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 THREEP3DL.STLLoader();
20 *  loader.load( './models/stl/slotted_disk.stl', function ( geometry ) {
21 *    scene.add( new THREEP3DL.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 THREEP3DL.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: THREEP3DL.VertexColors });
28 *  } else { .... }
29 *  var mesh = new THREEP3DL.Mesh( geometry, material );
30 */
31
32
33THREEP3DL.STLLoader = function ( manager ) {
34
35	this.manager = ( manager !== undefined ) ? manager : THREEP3DL.DefaultLoadingManager;
36
37};
38
39THREEP3DL.STLLoader.prototype = {
40
41	constructor: THREEP3DL.STLLoader,
42
43	load: function ( url, onLoad, onProgress, onError ) {
44
45		var scope = this;
46
47		var loader = new THREEP3DL.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			p3dlite.model_total_volume=0;
142			p3dlite.model_surface_area=0;
143
144
145			// process STL header
146			// check for default color in header ("COLOR=rgba" sequence).
147
148			for ( var index = 0; index < 80 - 10; index ++ ) {
149
150				if ( ( reader.getUint32( index, false ) == 0x434F4C4F /*COLO*/ ) &&
151					( reader.getUint8( index + 4 ) == 0x52 /*'R'*/ ) &&
152					( reader.getUint8( index + 5 ) == 0x3D /*'='*/ ) ) {
153
154					hasColors = true;
155					colors = [];
156
157					defaultR = reader.getUint8( index + 6 ) / 255;
158					defaultG = reader.getUint8( index + 7 ) / 255;
159					defaultB = reader.getUint8( index + 8 ) / 255;
160					alpha = reader.getUint8( index + 9 ) / 255;
161
162				}
163
164			}
165
166			var dataOffset = 84;
167			var faceLength = 12 * 4 + 2;
168
169			var geometry = new THREEP3DL.BufferGeometry();
170
171			var vertices = [];
172			var normals = [];
173
174			for ( var face = 0; face < faces; face ++ ) {
175
176				var start = dataOffset + face * faceLength;
177				var normalX = reader.getFloat32( start, true );
178				var normalY = reader.getFloat32( start + 4, true );
179				var normalZ = reader.getFloat32( start + 8, true );
180
181				if ( hasColors ) {
182
183					var packedColor = reader.getUint16( start + 48, true );
184
185					if ( ( packedColor & 0x8000 ) === 0 ) {
186
187						// facet has its own unique color
188
189						r = ( packedColor & 0x1F ) / 31;
190						g = ( ( packedColor >> 5 ) & 0x1F ) / 31;
191						b = ( ( packedColor >> 10 ) & 0x1F ) / 31;
192
193					} else {
194
195						r = defaultR;
196						g = defaultG;
197						b = defaultB;
198
199					}
200
201				}
202				var tetrahedron = new Array();
203				for ( var i = 1; i <= 3; i ++ ) {
204
205					var vertexstart = start + i * 12;
206
207					vertices.push( reader.getFloat32( vertexstart, true ) );
208					vertices.push( reader.getFloat32( vertexstart + 4, true ) );
209					vertices.push( reader.getFloat32( vertexstart + 8, true ) );
210
211					tetrahedron[i] = new Array();
212					tetrahedron[i].push(reader.getFloat32( vertexstart, true ));
213					tetrahedron[i].push(reader.getFloat32( vertexstart + 4, true ));
214					tetrahedron[i].push(reader.getFloat32( vertexstart + 8, true ));
215
216
217					normals.push( normalX, normalY, normalZ );
218
219					if ( hasColors ) {
220
221						colors.push( r, g, b );
222
223					}
224
225				}
226				p3dlite.model_total_volume+=p3dliteSignedVolume(tetrahedron[1], tetrahedron[2], tetrahedron[3]);
227				p3dlite.model_surface_area+=p3dliteSurfaceArea(tetrahedron[1], tetrahedron[2], tetrahedron[3]);
228
229
230			}
231
232			geometry.addAttribute( 'position', new THREEP3DL.BufferAttribute( new Float32Array( vertices ), 3 ) );
233			geometry.addAttribute( 'normal', new THREEP3DL.BufferAttribute( new Float32Array( normals ), 3 ) );
234
235			if ( hasColors ) {
236
237				geometry.addAttribute( 'color', new THREEP3DL.BufferAttribute( new Float32Array( colors ), 3 ) );
238				geometry.hasColors = true;
239				geometry.alpha = alpha;
240
241			}
242
243			return geometry;
244
245		}
246
247		function parseASCII( data ) {
248
249			var geometry = new THREEP3DL.BufferGeometry();
250			var patternFace = /facet([\s\S]*?)endfacet/g;
251			var faceCounter = 0;
252
253			var patternFloat = /[\s]+([+-]?(?:\d*)(?:\.\d*)?(?:[eE][+-]?\d+)?)/.source;
254			var patternVertex = new RegExp( 'vertex' + patternFloat + patternFloat + patternFloat, 'g' );
255			var patternNormal = new RegExp( 'normal' + patternFloat + patternFloat + patternFloat, 'g' );
256
257			var vertices = [];
258			var normals = [];
259
260			var normal = new THREEP3DL.Vector3();
261
262			var result;
263
264			p3dlite.model_total_volume=0;
265			p3dlite.model_surface_area=0;
266
267
268			while ( ( result = patternFace.exec( data ) ) !== null ) {
269
270				var vertexCountPerFace = 0;
271				var normalCountPerFace = 0;
272
273				var text = result[ 0 ];
274
275				while ( ( result = patternNormal.exec( text ) ) !== null ) {
276
277					normal.x = parseFloat( result[ 1 ] );
278					normal.y = parseFloat( result[ 2 ] );
279					normal.z = parseFloat( result[ 3 ] );
280					normalCountPerFace ++;
281
282				}
283
284				tetrahedron = new Array();
285				var i = 1;
286
287
288				while ( ( result = patternVertex.exec( text ) ) !== null ) {
289					tetrahedron[i] = new Array();
290					tetrahedron[i].push(parseFloat( result[ 1 ] ));
291					tetrahedron[i].push(parseFloat( result[ 2 ] ));
292					tetrahedron[i].push(parseFloat( result[ 3 ] ));
293
294
295					vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) );
296					normals.push( normal.x, normal.y, normal.z );
297					vertexCountPerFace ++;
298
299					i++;
300
301				}
302				p3dlite.model_total_volume+=p3dliteSignedVolume(tetrahedron[1], tetrahedron[2], tetrahedron[3]);
303				p3dlite.model_surface_area+=p3dliteSurfaceArea(tetrahedron[1], tetrahedron[2], tetrahedron[3]);
304
305
306				// every face have to own ONE valid normal
307
308				if ( normalCountPerFace !== 1 ) {
309
310					console.error( 'THREEP3DL.STLLoader: Something isn\'t right with the normal of face number ' + faceCounter );
311
312				}
313
314				// each face have to own THREE valid vertices
315
316				if ( vertexCountPerFace !== 3 ) {
317
318					console.error( 'THREEP3DL.STLLoader: Something isn\'t right with the vertices of face number ' + faceCounter );
319
320				}
321
322				faceCounter ++;
323
324			}
325
326			geometry.addAttribute( 'position', new THREEP3DL.Float32BufferAttribute( vertices, 3 ) );
327			geometry.addAttribute( 'normal', new THREEP3DL.Float32BufferAttribute( normals, 3 ) );
328
329			return geometry;
330
331		}
332
333		function ensureString( buffer ) {
334
335			if ( typeof buffer !== 'string' ) {
336
337				return THREEP3DL.LoaderUtils.decodeText( new Uint8Array( buffer ) );
338
339			}
340
341			return buffer;
342
343		}
344
345		function ensureBinary( buffer ) {
346
347			if ( typeof buffer === 'string' ) {
348
349				var array_buffer = new Uint8Array( buffer.length );
350				for ( var i = 0; i < buffer.length; i ++ ) {
351
352					array_buffer[ i ] = buffer.charCodeAt( i ) & 0xff; // implicitly assumes little-endian
353
354				}
355				return array_buffer.buffer || array_buffer;
356
357			} else {
358
359				return buffer;
360
361			}
362
363		}
364
365		// start
366
367		var binData = ensureBinary( data );
368
369		return isBinary( binData ) ? parseBinary( binData ) : parseASCII( ensureString( data ) );
370
371	}
372
373};

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.