API Docs for:
Show:

File: workspace\templates\jsstl.js

// Code copied from Github repo of tonylukasavage (and somewhat modified)
// License of this code, as stated on the repo is:
//
//    Do whatever you want with this code. I offer it without expectation or warranty. 
//    No need to credit me in your project or source code. A digital high five would be
//    nice, but is not required.
//


// Notes:
// - STL file format: http://en.wikipedia.org/wiki/STL_(file_format)
// - 80 byte unused header
// - All binary STLs are assumed to be little endian, as per wiki doc
/**
*
* Converts a given block of binary stl data (as an arraybuffer) to a threeJS representation
* of the geometry. Function courtesy of 'tonylukasavage' from Github, who released this to
* the public domain. 
*
* @method parseStlBinary
* @for renderGlobal
* @param {Arraybuffer} stl The binary stl data
* @return {Object} threeJS geometry object
* 
*/
var parseStlBinary = function(stl) {
	var geo = new THREE.Geometry();
	var dv = new DataView(stl, 80); // 80 == unused header
	var isLittleEndian = true;
	var triangles = dv.getUint32(0, isLittleEndian); 
	// console.log('arraybuffer length:  ' + stl.byteLength);
	// console.log('number of triangles: ' + triangles);
	var offset = 4;
	for (var i = 0; i < triangles; i++) {
		// Get the normal for this triangle
		var normal = new THREE.Vector3(
			dv.getFloat32(offset, isLittleEndian),
			dv.getFloat32(offset+4, isLittleEndian),
			dv.getFloat32(offset+8, isLittleEndian)
		);
		offset += 12;
		// Get all 3 vertices for this triangle
		for (var j = 0; j < 3; j++) {
			geo.vertices.push(
				new THREE.Vector3(
					dv.getFloat32(offset, isLittleEndian),
					dv.getFloat32(offset+4, isLittleEndian),
					dv.getFloat32(offset+8, isLittleEndian)
				)
			);
			offset += 12
		}
		// there's also a Uint16 "attribute byte count" that we
		// don't need, it should always be zero.
		offset += 2;   
		// Create a new face for from the vertices and the normal             
		geo.faces.push(new THREE.Face3(i*3, i*3+1, i*3+2, normal));
	}
	// The binary STL I'm testing with seems to have all
	// zeroes for the normals, unlike its ASCII counterpart.
	// We can use three.js to compute the normals for us, though,
	// once we've assembled our geometry. This is a relatively 
	// expensive operation, but only needs to be done once.
	geo.computeFaceNormals();
	
	return geo;
};  



/**
*
* Processes a given string to make it parsible for parseStl and returns
* the results
*
* @method trim
* @for renderGlobal
* @param {String} str ASCII STL data
* @return {String} processed string
* 
*/
function trim (str) {
	str = str.replace(/^\s+/, '');
	for (var i = str.length - 1; i >= 0; i--) {
		if (/\S/.test(str.charAt(i))) {
			str = str.substring(0, i + 1);
			break;
		}
	}
	return str;
}
			

			
// Added this in to turn the input buffer into an actual string
/**
*
* Converts an arraybuffer into a string of equivalent binary content
* @method arrayToString
* @for renderGlobal
* @param {Arraybuffer} buf The arraybuffer
* @return {String} 
* 
*/
function arrayToString(buf) {
	var pos=0;
	var arr=new Uint8Array(buf);
	var lim=arr.length;
	var result="";
	while(pos<lim){
		result=result+String.fromCharCode(arr[pos]);
		pos++;
	}
	return result;
}



/**
*
* Converts a given block of ASCII stl data (as an arraybuffer) to a threeJS representation
* of the geometry. Function courtesy of 'tonylukasavage' from Github, who released this to
* the public domain. 
*
* @method parseStl
* @for renderGlobal
* @param {Arraybuffer} stl The ASCII stl data
* @return {Object} threeJS geometry object
* 
*/
var parseStl = function(stl) {
	
	var state = '';
	
	stl=arrayToString(stl);
	
	
	var lines = stl.split('\n');
	var geo = new THREE.Geometry();
	var name, parts, line, normal, done, vertices = [];
	var vCount = 0;
	stl = null;
	for (var len = lines.length, i = 0; i < len; i++) {
		if (done) {
			break;
		}
		line = trim(lines[i]);
		parts = line.split(' ');
		switch (state) {
			case '':
				if (parts[0] !== 'solid') {
					console.error(line);
					console.error('Invalid state "' + parts[0] + '", should be "solid"');
					return null;
				} else {
					name = parts[1];
					state = 'solid';
				}
				break;
			case 'solid':
				if (parts[0] !== 'facet' || parts[1] !== 'normal') {
					console.error(line);
					console.error('Invalid state "' + parts[0] + '", should be "facet normal"');
					return null;
				} else {
					normal = [
						parseFloat(parts[2]), 
						parseFloat(parts[3]), 
						parseFloat(parts[4])
					];
					state = 'facet normal';
				}
				break;
			case 'facet normal':
				if (parts[0] !== 'outer' || parts[1] !== 'loop') {
					console.error(line);
					console.error('Invalid state "' + parts[0] + '", should be "outer loop"');
					return null;
				} else {
					state = 'vertex';
				}
				break;
			case 'vertex': 
				if (parts[0] === 'vertex') {
					geo.vertices.push(new THREE.Vector3(
						parseFloat(parts[1]),
						parseFloat(parts[2]),
						parseFloat(parts[3])
					));
				} else if (parts[0] === 'endloop') {
					geo.faces.push( new THREE.Face3( vCount*3, vCount*3+1, vCount*3+2, new THREE.Vector3(normal[0], normal[1], normal[2]) ) );
					vCount++;
					state = 'endloop';
				} else {
					console.error(line);
					console.error('Invalid state "' + parts[0] + '", should be "vertex" or "endloop"');
					return null;
				}
				break;
			case 'endloop':
				if (parts[0] !== 'endfacet') {
					console.error(line);
					console.error('Invalid state "' + parts[0] + '", should be "endfacet"');
					return null;
				} else {
					state = 'endfacet';
				}
				break;
			case 'endfacet':
				if (parts[0] === 'endsolid') {
					return geo;
					done = true;
				} else if (parts[0] === 'facet' && parts[1] === 'normal') {
					normal = [
						parseFloat(parts[2]), 
						parseFloat(parts[3]), 
						parseFloat(parts[4])
					];
					if (vCount % 1000 === 0) {
						//console.log(normal);
					}
					state = 'facet normal';
				} else {
					console.error(line);
					console.error('Invalid state "' + parts[0] + '", should be "endsolid" or "facet normal"');
					return null;
				}
				break;
			default:
				console.error('Invalid state "' + state + '"');
				break;
		}
	}
};