UNPKG

three

Version:

JavaScript 3D library

230 lines (155 loc) 5.34 kB
import { Float32BufferAttribute } from '../core/BufferAttribute.js'; import { BufferGeometry } from '../core/BufferGeometry.js'; import { Vector3 } from '../math/Vector3.js'; import { Vector2 } from '../math/Vector2.js'; import { clamp } from '../math/MathUtils.js'; /** * Creates meshes with axial symmetry like vases. The lathe rotates around the Y axis. * * ```js * const points = []; * for ( let i = 0; i < 10; i ++ ) { * points.push( new THREE.Vector2( Math.sin( i * 0.2 ) * 10 + 5, ( i - 5 ) * 2 ) ); * } * const geometry = new THREE.LatheGeometry( points ); * const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); * const lathe = new THREE.Mesh( geometry, material ); * scene.add( lathe ); * ``` * * @augments BufferGeometry */ class LatheGeometry extends BufferGeometry { /** * Constructs a new lathe geometry. * * @param {Array<Vector2|Vector3>} [points] - An array of points in 2D space. The x-coordinate of each point * must be greater than zero. * @param {number} [segments=12] - The number of circumference segments to generate. * @param {number} [phiStart=0] - The starting angle in radians. * @param {number} [phiLength=Math.PI*2] - The radian (0 to 2PI) range of the lathed section 2PI is a * closed lathe, less than 2PI is a portion. */ constructor( points = [ new Vector2( 0, - 0.5 ), new Vector2( 0.5, 0 ), new Vector2( 0, 0.5 ) ], segments = 12, phiStart = 0, phiLength = Math.PI * 2 ) { super(); this.type = 'LatheGeometry'; /** * Holds the constructor parameters that have been * used to generate the geometry. Any modification * after instantiation does not change the geometry. * * @type {Object} */ this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; segments = Math.floor( segments ); // clamp phiLength so it's in range of [ 0, 2PI ] phiLength = clamp( phiLength, 0, Math.PI * 2 ); // buffers const indices = []; const vertices = []; const uvs = []; const initNormals = []; const normals = []; // helper variables const inverseSegments = 1.0 / segments; const vertex = new Vector3(); const uv = new Vector2(); const normal = new Vector3(); const curNormal = new Vector3(); const prevNormal = new Vector3(); let dx = 0; let dy = 0; // pre-compute normals for initial "meridian" for ( let j = 0; j <= ( points.length - 1 ); j ++ ) { switch ( j ) { case 0: // special handling for 1st vertex on path dx = points[ j + 1 ].x - points[ j ].x; dy = points[ j + 1 ].y - points[ j ].y; normal.x = dy * 1.0; normal.y = - dx; normal.z = dy * 0.0; prevNormal.copy( normal ); normal.normalize(); initNormals.push( normal.x, normal.y, normal.z ); break; case ( points.length - 1 ): // special handling for last Vertex on path initNormals.push( prevNormal.x, prevNormal.y, prevNormal.z ); break; default: // default handling for all vertices in between dx = points[ j + 1 ].x - points[ j ].x; dy = points[ j + 1 ].y - points[ j ].y; normal.x = dy * 1.0; normal.y = - dx; normal.z = dy * 0.0; curNormal.copy( normal ); normal.x += prevNormal.x; normal.y += prevNormal.y; normal.z += prevNormal.z; normal.normalize(); initNormals.push( normal.x, normal.y, normal.z ); prevNormal.copy( curNormal ); } } // generate vertices, uvs and normals for ( let i = 0; i <= segments; i ++ ) { const phi = phiStart + i * inverseSegments * phiLength; const sin = Math.sin( phi ); const cos = Math.cos( phi ); for ( let j = 0; j <= ( points.length - 1 ); j ++ ) { // vertex vertex.x = points[ j ].x * sin; vertex.y = points[ j ].y; vertex.z = points[ j ].x * cos; vertices.push( vertex.x, vertex.y, vertex.z ); // uv uv.x = i / segments; uv.y = j / ( points.length - 1 ); uvs.push( uv.x, uv.y ); // normal const x = initNormals[ 3 * j + 0 ] * sin; const y = initNormals[ 3 * j + 1 ]; const z = initNormals[ 3 * j + 0 ] * cos; normals.push( x, y, z ); } } // indices for ( let i = 0; i < segments; i ++ ) { for ( let j = 0; j < ( points.length - 1 ); j ++ ) { const base = j + i * points.length; const a = base; const b = base + points.length; const c = base + points.length + 1; const d = base + 1; // faces indices.push( a, b, d ); indices.push( c, d, b ); } } // build geometry this.setIndex( indices ); this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); } copy( source ) { super.copy( source ); this.parameters = Object.assign( {}, source.parameters ); return this; } /** * Factory method for creating an instance of this class from the given * JSON object. * * @param {Object} data - A JSON object representing the serialized geometry. * @return {LatheGeometry} A new instance. */ static fromJSON( data ) { return new LatheGeometry( data.points, data.segments, data.phiStart, data.phiLength ); } } export { LatheGeometry };