Please Help! Lathe Geometry + Export object to 3D printing STL file

Hi,

I can’t calculate the Lather Geometry appear the way I wanted. It always too wide at the top . How can I calculate it and make it appear randomly around the subtracted object? Can we add the thickness to the Lathe? Also, I want to export the object to 3D printing STL file. The code that I have it doesn’t work.
I would appreciate it if you could help me to solve my problems. I spent hours for this but they didn’t work for me.

Here is the Lathe Geometry that I have and also, it doesn’t show the whole geometry , the back doesn’t show

Here is the Lathe Geometry that I wanted to have.
8c5d81e394d84af886c93d39cb7f5a31

Here is the code

import * as THREE from 'three';
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
import { MeshSurfaceSampler } from 'three/examples/jsm/math/MeshSurfaceSampler.js';
import { STLExporter } from 'three-addons';

import { 
	exportBinary,
	Brush,
	Evaluator,
	ADDITION,
	SUBTRACTION,
	INTERSECTION,
	DIFFERENCE,
} from '..';

const params = {
	exportBinary: exportBinary,
	operation: SUBTRACTION,
	wireframe: false,
	displayBrushes: false,
	shadows: true,
	useGroups: true,

	randomize: () => {

		randomizeBrushes();
		updateCSG();

	}
};


let renderer, camera, scene, controls, gui, outputContainer;
let cut, brushes;
let material, surfaceSampler;
let resultObject, wireframeResult, light;
let csgEvaluator = new Evaluator();
csgEvaluator.attributes = [ 'position', 'normal' ];
csgEvaluator.useGroups = false;

const materialMap = new Map();

init();

async function init() {

	const bgColor = 0x111111;

	outputContainer = document.getElementById( 'output' );

	// renderer setup
	renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.setClearColor( bgColor, 1 );
	renderer.shadowMap.enabled = true;
	renderer.shadowMap.type = THREE.PCFSoftShadowMap;
	renderer.outputEncoding = THREE.sRGBEncoding;
	document.body.appendChild( renderer.domElement );

	// scene setup
	scene = new THREE.Scene();

	// lights
	light = new THREE.DirectionalLight( 0xffffff, 1 );
	light.position.set( 1, 2, 1 );
	scene.add( light, new THREE.AmbientLight( 0xb0bec5, 0.1 ) );

	// shadows
	const shadowCam = light.shadow.camera;
	light.castShadow = true;
	light.shadow.mapSize.setScalar( 4096 );
	light.shadow.bias = 1e-5;
	light.shadow.normalBias = 1e-2;

	shadowCam.left = shadowCam.bottom = - 2.5;
	shadowCam.right = shadowCam.top = 2.5;
	shadowCam.updateProjectionMatrix();

	// camera setup
	camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 50 );
	camera.position.set( 0, 0.65, 2.5 );
	camera.far = 100;
	camera.updateProjectionMatrix();

	// controls
	controls = new OrbitControls( camera, renderer.domElement );

	const axesHelper = new THREE.AxesHelper( 5 );
	scene.add( axesHelper );

	//main object
	const geometry = new THREE.BoxGeometry( 10, 0.2, 10 );

	const pi = Math.PI

	// initialize brushes
	cut = new Brush( geometry, new THREE.MeshStandardMaterial() );
	cut.position.y = - 0.5;
	cut.updateMatrixWorld();
	cut.receiveShadow = true;
	scene.add( cut );

	material = new THREE.MeshStandardMaterial();
	brushes = [];

	surfaceSampler = new MeshSurfaceSampler( cut );
	surfaceSampler.build();

	for ( let i = 0; i < 70; i ++ ) {

		const b = new Brush( new THREE.BoxBufferGeometry( 1, 1, 20 ), material );
		b.receiveShadow = true;
		scene.add( b );
		brushes.push( b );

		const r = new Brush( new THREE.BoxBufferGeometry( 0.5, 0.5, 20 ), material );
		r.receiveShadow = true;
		r.rotateY( Math.PI );
		scene.add( r );
		brushes.push( r);

	}

	for (let i = 0; i < 70; i ++) {
		const c = new Brush (new THREE.TorusBufferGeometry(5, 0.5, 8, 50), material );
		c.reveiveShadow = true;
		c.rotation.set( Math.PI / 2, 0, 0 );
		c.position.y = Math.random() * 2 - 1;
		scene.add (c);
		brushes.push (c);
	}

	//LATHE Geometry
	const points = [];	
	for ( let i = 0; i < 5; i ++ ) {
		points.push( new THREE.Vector2( Math.sin( i * 0.02 ) * 1 + 0.5, ( i - 0.5 ) * 0.2 ) );
	}
	const e = new Brush (new THREE.LatheGeometry( points));
	e.receiveShadow = true;
	e.position.x= Math.random()
	e.position.z= Math.random()
	e.rotation.set( Math.PI, 0, 0 );
	scene.add(e)

	// initialize materials
	cut.material.opacity = 0.15;
	cut.material.transparent = true;
	cut.material.depthWrite = false;
	cut.material.polygonOffset = true;
	cut.material.polygonOffsetFactor = 0.1;
	cut.material.polygonOffsetUnits = 0.1;
	cut.material.side = THREE.DoubleSide;
	cut.material.premultipliedAlpha = true;
	cut.material.color.set( 0xE0F7FA ).convertSRGBToLinear();

	material.opacity = 0.15;
	material.transparent = true;
	material.depthWrite = false;
	material.polygonOffset = true;
	material.polygonOffsetFactor = 0.1;
	material.polygonOffsetUnits = 0.1;
	material.side = THREE.DoubleSide;
	material.premultipliedAlpha = true;
	material.roughness = 0.25;
	material.color.set( 0x4DD0E1 ).convertSRGBToLinear();

	// create solid material equivalents
	let mat;
	mat = cut.material.clone();
	mat.opacity = 1;
	mat.transparent = false;
	mat.depthWrite = true;
	materialMap.set( cut.material, mat );

	mat = material.clone();
	mat.opacity = 1;
	mat.transparent = false;
	mat.depthWrite = true;
	materialMap.set( material, mat );

	// add object displaying the result
	resultObject = new THREE.Mesh( new THREE.BufferGeometry(), new THREE.MeshStandardMaterial( {
		roughness: 0.1,
		flatShading: false,
		polygonOffset: true,
		polygonOffsetUnits: 1,
		polygonOffsetFactor: 1,
	} ) );
	resultObject.castShadow = true;
	resultObject.receiveShadow = true;
	scene.add( resultObject );


	// gui
	gui = new GUI();
	gui.add( params, 'operation', { ADDITION, SUBTRACTION, INTERSECTION, DIFFERENCE } ).onChange( () => {

		updateCSG();

	} );
	gui.add( params, 'displayBrushes' );
	gui.add( params, 'shadows' );
	gui.add( params, 'useGroups' ).onChange( updateCSG );
	gui.add( params, 'randomize' );
	gui.add (params, 'exportBinary');
	gui.open()
	
	

	window.addEventListener( 'resize', function () {

		camera.aspect = window.innerWidth / window.innerHeight;
		camera.updateProjectionMatrix();

		renderer.setSize( window.innerWidth, window.innerHeight );

	}, false );

	randomizeBrushes();
	updateCSG();
	render();


function updateCSG() {

	const startTime = window.performance.now();
	let finalBrush = brushes[ 0 ];
	csgEvaluator.useGroups = false;
	for ( let i = 1, l = brushes.length; i < l; i ++ ) {

		const b = brushes[ i ];
		finalBrush = csgEvaluator.evaluate( finalBrush, b, ADDITION );
		finalBrush.material = material;

	}

	csgEvaluator.useGroups = params.useGroups;
	csgEvaluator.evaluate( cut, finalBrush, params.operation, resultObject );
	if ( params.useGroups ) {

		resultObject.material = resultObject.material.map( m => materialMap.get( m ) );

	} else {

		resultObject.material = materialMap.get( cut.material );

	}

	const deltaTime = window.performance.now() - startTime;
	outputContainer.innerText = `${ deltaTime.toFixed( 3 ) }ms`;

}

function randomizeBrushes() {

	for ( let i = 0; i < brushes.length; i ++ ) {

		const b = brushes[ i ];
		surfaceSampler.sample( b.position );
		b.position.applyMatrix4( cut.matrixWorld );
		b.scale.setScalar( THREE.MathUtils.lerp( 0.07, 0.27, Math.random() ) );
		b.updateMatrixWorld();

	}

}

function render() {

	requestAnimationFrame( render );

	//wireframeResult.visible = params.wireframe;
	cut.visible = params.displayBrushes;
	brushes.forEach( b => b.visible = params.displayBrushes );

	light.castShadow = params.shadows;

	renderer.render( scene, camera );

}

function exportBinary() {

	const result = exporter.parse( SUBTRACTION, { binary: true } );
	saveArrayBuffer( result, 'landscape.stl' );

}

const link = document.createElement( 'a' );
link.style.display = 'none';
document.body.appendChild( link );

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 );

}
  1. Nope, that is not possible with the built-in geometries. You’d need to write your custom geometry based on the code from the built-in one.

  2. For what you describe, aren’t you looking for CylinderGeometry rather than Lathe?

  3. If not - to make Lathe geometry narrower / wider on the top (or any other part of it) - just modify the points lathe is created from (in docs slide down “phiLength” to 0, to see how points define the curve of the lathe geometry.)

Thank you for your reply.

How can I write custom geometry based on the code from the built-in one?

I can make the CylinderGeometry wide at the bottom and narrow at the top but can I make a hole in the middle?