import { ConvexPolyhedron } from '../shapes/ConvexPolyhedron' import { Vec3 } from '../math/Vec3' import { Shape } from './Shape' /** * Cylinder class. * @example * const radiusTop = 0.5 * const radiusBottom = 0.5 * const height = 2 * const numSegments = 12 * const cylinderShape = new CANNON.Cylinder(radiusTop, radiusBottom, height, numSegments) * const cylinderBody = new CANNON.Body({ mass: 1, shape: cylinderShape }) * world.addBody(cylinderBody) */ export class Cylinder extends ConvexPolyhedron { /** The radius of the top of the Cylinder. */ radiusTop: number /** The radius of the bottom of the Cylinder. */ radiusBottom: number /** The height of the Cylinder. */ height: number /** The number of segments to build the cylinder out of. */ numSegments: number /** * @param radiusTop The radius of the top of the Cylinder. * @param radiusBottom The radius of the bottom of the Cylinder. * @param height The height of the Cylinder. * @param numSegments The number of segments to build the cylinder out of. */ constructor(radiusTop: number = 1, radiusBottom: number = 1, height: number = 1, numSegments: number = 8) { if (radiusTop < 0) { throw new Error('The cylinder radiusTop cannot be negative.') } if (radiusBottom < 0) { throw new Error('The cylinder radiusBottom cannot be negative.') } const N = numSegments const vertices = [] const axes = [] const faces = [] const bottomface = [] const topface = [] const cos = Math.cos const sin = Math.sin // First bottom point vertices.push(new Vec3(-radiusBottom * sin(0), -height * 0.5, radiusBottom * cos(0))) bottomface.push(0) // First top point vertices.push(new Vec3(-radiusTop * sin(0), height * 0.5, radiusTop * cos(0))) topface.push(1) for (let i = 0; i < N; i++) { const theta = ((2 * Math.PI) / N) * (i + 1) const thetaN = ((2 * Math.PI) / N) * (i + 0.5) if (i < N - 1) { // Bottom vertices.push(new Vec3(-radiusBottom * sin(theta), -height * 0.5, radiusBottom * cos(theta))) bottomface.push(2 * i + 2) // Top vertices.push(new Vec3(-radiusTop * sin(theta), height * 0.5, radiusTop * cos(theta))) topface.push(2 * i + 3) // Face faces.push([2 * i, 2 * i + 1, 2 * i + 3, 2 * i + 2]) } else { faces.push([2 * i, 2 * i + 1, 1, 0]) // Connect } // Axis: we can cut off half of them if we have even number of segments if (N % 2 === 1 || i < N / 2) { axes.push(new Vec3(-sin(thetaN), 0, cos(thetaN))) } } faces.push(bottomface) axes.push(new Vec3(0, 1, 0)) // Reorder top face const temp = [] for (let i = 0; i < topface.length; i++) { temp.push(topface[topface.length - i - 1]) } faces.push(temp) super({ vertices, faces, axes }) this.type = Shape.types.CYLINDER this.radiusTop = radiusTop this.radiusBottom = radiusBottom this.height = height this.numSegments = numSegments } }