Skip to content

⬅️ Back to Table of Contents

📄 CylinderGeometry.js

📊 Analysis Summary

Metric Count
🔧 Functions 4
🧱 Classes 1
📦 Imports 4
📊 Variables & Constants 33

📚 Table of Contents

🛠️ File Location:

📂 src/geometries/CylinderGeometry.js

📦 Imports

Name Source
BufferGeometry ../core/BufferGeometry.js
Float32BufferAttribute ../core/BufferAttribute.js
Vector3 ../math/Vector3.js
Vector2 ../math/Vector2.js

Variables & Constants

Name Type Kind Value Exported
scope this let/var this
indices any[] let/var []
vertices any[] let/var []
normals any[] let/var []
uvs any[] let/var []
index number let/var 0
indexArray any[] let/var []
halfHeight number let/var height / 2
groupStart number let/var 0
normal Vector3 let/var new Vector3()
vertex Vector3 let/var new Vector3()
groupCount number let/var 0
slope number let/var ( radiusBottom - radiusTop ) / height
indexRow any[] let/var []
v number let/var y / heightSegments
radius number let/var v * ( radiusBottom - radiusTop ) + radiusTop
u number let/var x / radialSegments
theta number let/var u * thetaLength + thetaStart
a any let/var indexArray[ y ][ x ]
b any let/var indexArray[ y + 1 ][ x ]
c any let/var indexArray[ y + 1 ][ x + 1 ]
d any let/var indexArray[ y ][ x + 1 ]
centerIndexStart number let/var index
uv Vector2 let/var new Vector2()
vertex Vector3 let/var new Vector3()
groupCount number let/var 0
radius number let/var ( top === true ) ? radiusTop : radiusBottom
sign 1 \| -1 let/var ( top === true ) ? 1 : - 1
centerIndexEnd number let/var index
u number let/var x / radialSegments
theta number let/var u * thetaLength + thetaStart
c number let/var centerIndexStart + x
i number let/var centerIndexEnd + x

Functions

CylinderGeometry.copy(source: any): this

Parameters:

  • source any

Returns: this

Calls:

  • super.copy
  • Object.assign
Code
copy( source ) {

        super.copy( source );

        this.parameters = Object.assign( {}, source.parameters );

        return this;

    }

CylinderGeometry.fromJSON(data: any): CylinderGeometry

JSDoc:

/**
     * 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 {CylinderGeometry} A new instance.
     */

Parameters:

  • data any

Returns: CylinderGeometry

Code
static fromJSON( data ) {

        return new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength );

    }

generateTorso(): void

Returns: void

Calls:

  • Math.sin
  • Math.cos
  • vertices.push
  • normal.set( sinTheta, slope, cosTheta ).normalize
  • normals.push
  • uvs.push
  • indexRow.push
  • indexArray.push
  • indices.push
  • scope.addGroup

Internal Comments:

// this will be used to calculate the normal (x2)
// generate vertices, normals and uvs
// calculate the radius of the current row (x2)
// vertex (x4)
// normal (x6)
// uv (x4)
// save index of vertex in respective row (x4)
// now save vertices of the row in our index array (x4)
// generate indices
// we use the index array to access the correct indices (x2)
// faces
// add a group to the geometry. this will ensure multi material support (x4)
// calculate new start value for groups (x3)

Code
function generateTorso() {

            const normal = new Vector3();
            const vertex = new Vector3();

            let groupCount = 0;

            // this will be used to calculate the normal
            const slope = ( radiusBottom - radiusTop ) / height;

            // generate vertices, normals and uvs

            for ( let y = 0; y <= heightSegments; y ++ ) {

                const indexRow = [];

                const v = y / heightSegments;

                // calculate the radius of the current row

                const radius = v * ( radiusBottom - radiusTop ) + radiusTop;

                for ( let x = 0; x <= radialSegments; x ++ ) {

                    const u = x / radialSegments;

                    const theta = u * thetaLength + thetaStart;

                    const sinTheta = Math.sin( theta );
                    const cosTheta = Math.cos( theta );

                    // vertex

                    vertex.x = radius * sinTheta;
                    vertex.y = - v * height + halfHeight;
                    vertex.z = radius * cosTheta;
                    vertices.push( vertex.x, vertex.y, vertex.z );

                    // normal

                    normal.set( sinTheta, slope, cosTheta ).normalize();
                    normals.push( normal.x, normal.y, normal.z );

                    // uv

                    uvs.push( u, 1 - v );

                    // save index of vertex in respective row

                    indexRow.push( index ++ );

                }

                // now save vertices of the row in our index array

                indexArray.push( indexRow );

            }

            // generate indices

            for ( let x = 0; x < radialSegments; x ++ ) {

                for ( let y = 0; y < heightSegments; y ++ ) {

                    // we use the index array to access the correct indices

                    const a = indexArray[ y ][ x ];
                    const b = indexArray[ y + 1 ][ x ];
                    const c = indexArray[ y + 1 ][ x + 1 ];
                    const d = indexArray[ y ][ x + 1 ];

                    // faces

                    if ( radiusTop > 0 || y !== 0 ) {

                        indices.push( a, b, d );
                        groupCount += 3;

                    }

                    if ( radiusBottom > 0 || y !== heightSegments - 1 ) {

                        indices.push( b, c, d );
                        groupCount += 3;

                    }

                }

            }

            // add a group to the geometry. this will ensure multi material support

            scope.addGroup( groupStart, groupCount, 0 );

            // calculate new start value for groups

            groupStart += groupCount;

        }

generateCap(top: any): void

Parameters:

  • top any

Returns: void

Calls:

  • vertices.push
  • normals.push
  • uvs.push
  • Math.cos
  • Math.sin
  • indices.push
  • scope.addGroup

Internal Comments:

// save the index of the first center vertex (x2)
// first we generate the center vertex data of the cap.
// because the geometry needs one set of uvs per face,
// we must generate a center vertex per face/segment
// vertex (x8)
// normal (x8)
// uv (x8)
// increase index (x6)
// save the index of the last center vertex (x2)
// now we generate the surrounding vertices, normals and uvs
// generate indices
// face top (x4)
// face bottom (x4)
// add a group to the geometry. this will ensure multi material support (x4)
// calculate new start value for groups (x3)

Code
function generateCap( top ) {

            // save the index of the first center vertex
            const centerIndexStart = index;

            const uv = new Vector2();
            const vertex = new Vector3();

            let groupCount = 0;

            const radius = ( top === true ) ? radiusTop : radiusBottom;
            const sign = ( top === true ) ? 1 : - 1;

            // first we generate the center vertex data of the cap.
            // because the geometry needs one set of uvs per face,
            // we must generate a center vertex per face/segment

            for ( let x = 1; x <= radialSegments; x ++ ) {

                // vertex

                vertices.push( 0, halfHeight * sign, 0 );

                // normal

                normals.push( 0, sign, 0 );

                // uv

                uvs.push( 0.5, 0.5 );

                // increase index

                index ++;

            }

            // save the index of the last center vertex
            const centerIndexEnd = index;

            // now we generate the surrounding vertices, normals and uvs

            for ( let x = 0; x <= radialSegments; x ++ ) {

                const u = x / radialSegments;
                const theta = u * thetaLength + thetaStart;

                const cosTheta = Math.cos( theta );
                const sinTheta = Math.sin( theta );

                // vertex

                vertex.x = radius * sinTheta;
                vertex.y = halfHeight * sign;
                vertex.z = radius * cosTheta;
                vertices.push( vertex.x, vertex.y, vertex.z );

                // normal

                normals.push( 0, sign, 0 );

                // uv

                uv.x = ( cosTheta * 0.5 ) + 0.5;
                uv.y = ( sinTheta * 0.5 * sign ) + 0.5;
                uvs.push( uv.x, uv.y );

                // increase index

                index ++;

            }

            // generate indices

            for ( let x = 0; x < radialSegments; x ++ ) {

                const c = centerIndexStart + x;
                const i = centerIndexEnd + x;

                if ( top === true ) {

                    // face top

                    indices.push( i, i + 1, c );

                } else {

                    // face bottom

                    indices.push( i + 1, i, c );

                }

                groupCount += 3;

            }

            // add a group to the geometry. this will ensure multi material support

            scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 );

            // calculate new start value for groups

            groupStart += groupCount;

        }

Classes

CylinderGeometry

Class Code
class CylinderGeometry extends BufferGeometry {

    /**
     * Constructs a new cylinder geometry.
     *
     * @param {number} [radiusTop=1] - Radius of the cylinder at the top.
     * @param {number} [radiusBottom=1] - Radius of the cylinder at the bottom.
     * @param {number} [height=1] - Height of the cylinder.
     * @param {number} [radialSegments=32] - Number of segmented faces around the circumference of the cylinder.
     * @param {number} [heightSegments=1] - Number of rows of faces along the height of the cylinder.
     * @param {boolean} [openEnded=false] - Whether the base of the cylinder is open or capped.
     * @param {number} [thetaStart=0] - Start angle for first segment, in radians.
     * @param {number} [thetaLength=Math.PI*2] - The central angle, often called theta, of the circular sector, in radians.
     * The default value results in a complete cylinder.
     */
    constructor( radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) {

        super();

        this.type = 'CylinderGeometry';

        /**
         * 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 = {
            radiusTop: radiusTop,
            radiusBottom: radiusBottom,
            height: height,
            radialSegments: radialSegments,
            heightSegments: heightSegments,
            openEnded: openEnded,
            thetaStart: thetaStart,
            thetaLength: thetaLength
        };

        const scope = this;

        radialSegments = Math.floor( radialSegments );
        heightSegments = Math.floor( heightSegments );

        // buffers

        const indices = [];
        const vertices = [];
        const normals = [];
        const uvs = [];

        // helper variables

        let index = 0;
        const indexArray = [];
        const halfHeight = height / 2;
        let groupStart = 0;

        // generate geometry

        generateTorso();

        if ( openEnded === false ) {

            if ( radiusTop > 0 ) generateCap( true );
            if ( radiusBottom > 0 ) generateCap( false );

        }

        // build geometry

        this.setIndex( indices );
        this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
        this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
        this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );

        function generateTorso() {

            const normal = new Vector3();
            const vertex = new Vector3();

            let groupCount = 0;

            // this will be used to calculate the normal
            const slope = ( radiusBottom - radiusTop ) / height;

            // generate vertices, normals and uvs

            for ( let y = 0; y <= heightSegments; y ++ ) {

                const indexRow = [];

                const v = y / heightSegments;

                // calculate the radius of the current row

                const radius = v * ( radiusBottom - radiusTop ) + radiusTop;

                for ( let x = 0; x <= radialSegments; x ++ ) {

                    const u = x / radialSegments;

                    const theta = u * thetaLength + thetaStart;

                    const sinTheta = Math.sin( theta );
                    const cosTheta = Math.cos( theta );

                    // vertex

                    vertex.x = radius * sinTheta;
                    vertex.y = - v * height + halfHeight;
                    vertex.z = radius * cosTheta;
                    vertices.push( vertex.x, vertex.y, vertex.z );

                    // normal

                    normal.set( sinTheta, slope, cosTheta ).normalize();
                    normals.push( normal.x, normal.y, normal.z );

                    // uv

                    uvs.push( u, 1 - v );

                    // save index of vertex in respective row

                    indexRow.push( index ++ );

                }

                // now save vertices of the row in our index array

                indexArray.push( indexRow );

            }

            // generate indices

            for ( let x = 0; x < radialSegments; x ++ ) {

                for ( let y = 0; y < heightSegments; y ++ ) {

                    // we use the index array to access the correct indices

                    const a = indexArray[ y ][ x ];
                    const b = indexArray[ y + 1 ][ x ];
                    const c = indexArray[ y + 1 ][ x + 1 ];
                    const d = indexArray[ y ][ x + 1 ];

                    // faces

                    if ( radiusTop > 0 || y !== 0 ) {

                        indices.push( a, b, d );
                        groupCount += 3;

                    }

                    if ( radiusBottom > 0 || y !== heightSegments - 1 ) {

                        indices.push( b, c, d );
                        groupCount += 3;

                    }

                }

            }

            // add a group to the geometry. this will ensure multi material support

            scope.addGroup( groupStart, groupCount, 0 );

            // calculate new start value for groups

            groupStart += groupCount;

        }

        function generateCap( top ) {

            // save the index of the first center vertex
            const centerIndexStart = index;

            const uv = new Vector2();
            const vertex = new Vector3();

            let groupCount = 0;

            const radius = ( top === true ) ? radiusTop : radiusBottom;
            const sign = ( top === true ) ? 1 : - 1;

            // first we generate the center vertex data of the cap.
            // because the geometry needs one set of uvs per face,
            // we must generate a center vertex per face/segment

            for ( let x = 1; x <= radialSegments; x ++ ) {

                // vertex

                vertices.push( 0, halfHeight * sign, 0 );

                // normal

                normals.push( 0, sign, 0 );

                // uv

                uvs.push( 0.5, 0.5 );

                // increase index

                index ++;

            }

            // save the index of the last center vertex
            const centerIndexEnd = index;

            // now we generate the surrounding vertices, normals and uvs

            for ( let x = 0; x <= radialSegments; x ++ ) {

                const u = x / radialSegments;
                const theta = u * thetaLength + thetaStart;

                const cosTheta = Math.cos( theta );
                const sinTheta = Math.sin( theta );

                // vertex

                vertex.x = radius * sinTheta;
                vertex.y = halfHeight * sign;
                vertex.z = radius * cosTheta;
                vertices.push( vertex.x, vertex.y, vertex.z );

                // normal

                normals.push( 0, sign, 0 );

                // uv

                uv.x = ( cosTheta * 0.5 ) + 0.5;
                uv.y = ( sinTheta * 0.5 * sign ) + 0.5;
                uvs.push( uv.x, uv.y );

                // increase index

                index ++;

            }

            // generate indices

            for ( let x = 0; x < radialSegments; x ++ ) {

                const c = centerIndexStart + x;
                const i = centerIndexEnd + x;

                if ( top === true ) {

                    // face top

                    indices.push( i, i + 1, c );

                } else {

                    // face bottom

                    indices.push( i + 1, i, c );

                }

                groupCount += 3;

            }

            // add a group to the geometry. this will ensure multi material support

            scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 );

            // calculate new start value for groups

            groupStart += groupCount;

        }

    }

    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 {CylinderGeometry} A new instance.
     */
    static fromJSON( data ) {

        return new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength );

    }

}

Methods

copy(source: any): this
Code
copy( source ) {

        super.copy( source );

        this.parameters = Object.assign( {}, source.parameters );

        return this;

    }
fromJSON(data: any): CylinderGeometry
Code
static fromJSON( data ) {

        return new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength );

    }