cannon-es-control
Version:
A lightweight 3D physics engine written in JavaScript with control system tools
99 lines (86 loc) • 3.2 kB
text/typescript
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
}
}