Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STL export #250

Draft
wants to merge 6 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ <h1 class="text-center m-3 mb-5">GCode Preview
</div>
</div>
<div>
<button class="bulma-button bulma-is-light bulma-is-small bulma-is-outlined" @click="exportScene">Export STL</button>
<footer class="is-size-7 p-0 has-text-grey-light">


Expand Down
199 changes: 199 additions & 0 deletions demo/js/STLExporter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import { Vector3 } from 'three';

/**
* Usage:
* const exporter = new STLExporter();
*
* // second argument is a list of options
* const data = exporter.parse( mesh, { binary: true } );
*
*/

class STLExporter {

parse( scene, options = {} ) {

options = Object.assign( {
binary: false
}, options );

const binary = options.binary;

//

const objects = [];
let triangles = 0;

scene.traverse( function ( object ) {

if ( object.isMesh ) {

const geometry = object.geometry;

const index = geometry.index;
const positionAttribute = geometry.getAttribute( 'position' );

triangles += ( index !== null ) ? ( index.count / 3 ) : ( positionAttribute.count / 3 );

objects.push( {
object3d: object,
geometry: geometry
} );

}

} );

let output;
let offset = 80; // skip header

if ( binary === true ) {

const bufferLength = triangles * 2 + triangles * 3 * 4 * 4 + 80 + 4;
const arrayBuffer = new ArrayBuffer( bufferLength );
output = new DataView( arrayBuffer );
output.setUint32( offset, triangles, true ); offset += 4;

} else {

output = '';
output += 'solid exported\n';

}

const vA = new Vector3();
const vB = new Vector3();
const vC = new Vector3();
const cb = new Vector3();
const ab = new Vector3();
const normal = new Vector3();

for ( let i = 0, il = objects.length; i < il; i ++ ) {

const object = objects[ i ].object3d;
const geometry = objects[ i ].geometry;

const index = geometry.index;
const positionAttribute = geometry.getAttribute( 'position' );

if ( index !== null ) {

// indexed geometry

for ( let j = 0; j < index.count; j += 3 ) {

const a = index.getX( j + 0 );
const b = index.getX( j + 1 );
const c = index.getX( j + 2 );

writeFace( a, b, c, positionAttribute, object );

}

} else {

// non-indexed geometry

for ( let j = 0; j < positionAttribute.count; j += 3 ) {

const a = j + 0;
const b = j + 1;
const c = j + 2;

writeFace( a, b, c, positionAttribute, object );

}

}

}

if ( binary === false ) {

output += 'endsolid exported\n';

}

return output;

function writeFace( a, b, c, positionAttribute, object ) {

vA.fromBufferAttribute( positionAttribute, a );
vB.fromBufferAttribute( positionAttribute, b );
vC.fromBufferAttribute( positionAttribute, c );

if ( object.isSkinnedMesh === true ) {

object.applyBoneTransform( a, vA );
object.applyBoneTransform( b, vB );
object.applyBoneTransform( c, vC );

}

vA.applyMatrix4( object.matrixWorld );
vB.applyMatrix4( object.matrixWorld );
vC.applyMatrix4( object.matrixWorld );

writeNormal( vA, vB, vC );

writeVertex( vA );
writeVertex( vB );
writeVertex( vC );

if ( binary === true ) {

output.setUint16( offset, 0, true ); offset += 2;

} else {

output += '\t\tendloop\n';
output += '\tendfacet\n';

}

}

function writeNormal( vA, vB, vC ) {

cb.subVectors( vC, vB );
ab.subVectors( vA, vB );
cb.cross( ab ).normalize();

normal.copy( cb ).normalize();

if ( binary === true ) {

output.setFloat32( offset, normal.x, true ); offset += 4;
output.setFloat32( offset, normal.y, true ); offset += 4;
output.setFloat32( offset, normal.z, true ); offset += 4;

} else {

output += '\tfacet normal ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
output += '\t\touter loop\n';

}

}

function writeVertex( vertex ) {

if ( binary === true ) {

output.setFloat32( offset, vertex.x, true ); offset += 4;
output.setFloat32( offset, vertex.y, true ); offset += 4;
output.setFloat32( offset, vertex.z, true ); offset += 4;

} else {

output += '\t\t\tvertex ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';

}

}

}

}

export { STLExporter };
9 changes: 8 additions & 1 deletion demo/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { presets } from './presets.js';
import * as GCodePreview from 'gcode-preview';
import { defaultSettings } from './default-settings.js';
import { debounce, humanFileSize, readFile } from './utils.js';
import { exportSTL } from './export-stl.js';

const defaultPreset = 'arcs';
const preferDarkMode = window.matchMedia('(prefers-color-scheme: dark)');
Expand Down Expand Up @@ -238,6 +239,11 @@ export const app = (window.app = createApp({
});
});

function exportScene() {
console.log(preview.scene);
exportSTL(preview.scene , 'preview.stl', true);
}

return {
presets,
activeTab,
Expand All @@ -260,7 +266,8 @@ export const app = (window.app = createApp({
loadGCodeFromServer,
startLoadingProgressive,
loadDroppedFile,
selectPreset
selectPreset,
exportScene
};
}
}).mount('#app'));
43 changes: 43 additions & 0 deletions demo/js/export-stl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { STLExporter } from './STLExporter.js';

let link;

export function setup() {
link = document.createElement( 'a' );
link.style.display = 'none';
document.body.appendChild( link );
}

export function exportSTL(mesh, file, binary) {
console.log('exporting...');

// Instantiate an exporter
const exporter = new STLExporter();

// Configure export options
const options = { binary: !!binary }

// Parse the input and generate the STL encoded output
const result = exporter.parse(mesh, options );

if (options.binary) {
saveArrayBuffer(result, file )
}
else {
saveString(result, file)
}
}

function save( blob, filename ) {
link.href = URL.createObjectURL( blob );
link.download = filename;
link.click();
}

function saveString( text, filename ) {
save( new Blob( [ text ], { type: 'text/plain' } ), filename );
}

function saveArrayBuffer( buffer, filename ) {
save( new Blob( [ buffer ], { type: 'application/octet-stream' } ), filename );
}
Loading