import { PointToPointConstraint } from '../constraints/PointToPointConstraint' import { RotationalEquation } from '../equations/RotationalEquation' import { RotationalMotorEquation } from '../equations/RotationalMotorEquation' import { Vec3 } from '../math/Vec3' import type { Body } from '../objects/Body' export type HingeConstraintOptions = ConstructorParameters[2] /** * Hinge constraint. Think of it as a door hinge. It tries to keep the door in the correct place and with the correct orientation. */ export class HingeConstraint extends PointToPointConstraint { /** * Rotation axis, defined locally in bodyA. */ axisA: Vec3 /** * Rotation axis, defined locally in bodyB. */ axisB: Vec3 rotationalEquation1: RotationalEquation rotationalEquation2: RotationalEquation motorEquation: RotationalMotorEquation constructor( bodyA: Body, bodyB: Body, options: { /** * A point defined locally in bodyA. This defines the offset of axisA. */ pivotA?: Vec3 /** * A point defined locally in bodyB. This defines the offset of axisB. */ pivotB?: Vec3 /** * An axis that bodyA can rotate around, defined locally in bodyA. */ axisA?: Vec3 /** * An axis that bodyB can rotate around, defined locally in bodyB. */ axisB?: Vec3 /** * Wheter to collide the connected bodies or not. * @default false */ collideConnected?: boolean /** * The maximum force that should be applied to constrain the bodies. * @default 1e6 */ maxForce?: number } = {} ) { const maxForce = typeof options.maxForce !== 'undefined' ? options.maxForce : 1e6 const pivotA = options.pivotA ? options.pivotA.clone() : new Vec3() const pivotB = options.pivotB ? options.pivotB.clone() : new Vec3() super(bodyA, pivotA, bodyB, pivotB, maxForce) const axisA = (this.axisA = options.axisA ? options.axisA.clone() : new Vec3(1, 0, 0)) axisA.normalize() const axisB = (this.axisB = options.axisB ? options.axisB.clone() : new Vec3(1, 0, 0)) axisB.normalize() this.collideConnected = !!options.collideConnected const rotational1 = (this.rotationalEquation1 = new RotationalEquation(bodyA, bodyB, options)) const rotational2 = (this.rotationalEquation2 = new RotationalEquation(bodyA, bodyB, options)) const motor = (this.motorEquation = new RotationalMotorEquation(bodyA, bodyB, maxForce)) motor.enabled = false // Not enabled by default // Equations to be fed to the solver this.equations.push(rotational1, rotational2, motor) } /** * enableMotor */ enableMotor(): void { this.motorEquation.enabled = true } /** * disableMotor */ disableMotor(): void { this.motorEquation.enabled = false } /** * setMotorSpeed */ setMotorSpeed(speed: number): void { this.motorEquation.targetVelocity = speed } /** * setMotorMaxForce */ setMotorMaxForce(maxForce: number): void { this.motorEquation.maxForce = maxForce this.motorEquation.minForce = -maxForce } /** * update */ update(): void { const bodyA = this.bodyA const bodyB = this.bodyB const motor = this.motorEquation const r1 = this.rotationalEquation1 const r2 = this.rotationalEquation2 const worldAxisA = HingeConstraint_update_tmpVec1 const worldAxisB = HingeConstraint_update_tmpVec2 const axisA = this.axisA const axisB = this.axisB super.update() // Get world axes bodyA.quaternion.vmult(axisA, worldAxisA) bodyB.quaternion.vmult(axisB, worldAxisB) worldAxisA.tangents(r1.axisA, r2.axisA) r1.axisB.copy(worldAxisB) r2.axisB.copy(worldAxisB) if (this.motorEquation.enabled) { bodyA.quaternion.vmult(this.axisA, motor.axisA) bodyB.quaternion.vmult(this.axisB, motor.axisB) } } } const HingeConstraint_update_tmpVec1 = new Vec3() const HingeConstraint_update_tmpVec2 = new Vec3()