cannon-es-control
Version:
A lightweight 3D physics engine written in JavaScript with control system tools
363 lines (327 loc) • 8.82 kB
text/typescript
import { Mat3 } from '../math/Mat3'
/**
* 3-dimensional vector
* @example
* const v = new Vec3(1, 2, 3)
* console.log('x=' + v.x) // x=1
*/
export class Vec3 {
x: number
y: number
z: number
static ZERO: Vec3
static UNIT_X: Vec3
static UNIT_Y: Vec3
static UNIT_Z: Vec3
constructor(x = 0.0, y = 0.0, z = 0.0) {
this.x = x
this.y = y
this.z = z
}
/**
* Vector cross product
* @param target Optional target to save in.
*/
cross(vector: Vec3, target = new Vec3()): Vec3 {
const vx = vector.x
const vy = vector.y
const vz = vector.z
const x = this.x
const y = this.y
const z = this.z
target.x = y * vz - z * vy
target.y = z * vx - x * vz
target.z = x * vy - y * vx
return target
}
/**
* Set the vectors' 3 elements
*/
set(x: number, y: number, z: number): Vec3 {
this.x = x
this.y = y
this.z = z
return this
}
/**
* Set all components of the vector to zero.
*/
setZero(): void {
this.x = this.y = this.z = 0
}
/**
* Vector addition
*/
vadd(vector: Vec3): Vec3
vadd(vector: Vec3, target: Vec3): void
vadd(vector: Vec3, target?: Vec3): Vec3 | void {
if (target) {
target.x = vector.x + this.x
target.y = vector.y + this.y
target.z = vector.z + this.z
} else {
return new Vec3(this.x + vector.x, this.y + vector.y, this.z + vector.z)
}
}
/**
* Vector subtraction
* @param target Optional target to save in.
*/
vsub(vector: Vec3): Vec3
vsub(vector: Vec3, target: Vec3): void
vsub(vector: Vec3, target?: Vec3): Vec3 | void {
if (target) {
target.x = this.x - vector.x
target.y = this.y - vector.y
target.z = this.z - vector.z
} else {
return new Vec3(this.x - vector.x, this.y - vector.y, this.z - vector.z)
}
}
/**
* Get the cross product matrix a_cross from a vector, such that a x b = a_cross * b = c
*
* See {@link https://www8.cs.umu.se/kurser/TDBD24/VT06/lectures/Lecture6.pdf Umeå University Lecture}
*/
crossmat(): Mat3 {
return new Mat3([0, -this.z, this.y, this.z, 0, -this.x, -this.y, this.x, 0])
}
/**
* Normalize the vector. Note that this changes the values in the vector.
* @return Returns the norm of the vector
*/
normalize(): number {
const x = this.x
const y = this.y
const z = this.z
const n = Math.sqrt(x * x + y * y + z * z)
if (n > 0.0) {
const invN = 1 / n
this.x *= invN
this.y *= invN
this.z *= invN
} else {
// Make something up
this.x = 0
this.y = 0
this.z = 0
}
return n
}
/**
* Get the version of this vector that is of length 1.
* @param target Optional target to save in
* @return Returns the unit vector
*/
unit(target = new Vec3()): Vec3 {
const x = this.x
const y = this.y
const z = this.z
let ninv = Math.sqrt(x * x + y * y + z * z)
if (ninv > 0.0) {
ninv = 1.0 / ninv
target.x = x * ninv
target.y = y * ninv
target.z = z * ninv
} else {
target.x = 1
target.y = 0
target.z = 0
}
return target
}
/**
* Get the length of the vector
*/
length(): number {
const x = this.x
const y = this.y
const z = this.z
return Math.sqrt(x * x + y * y + z * z)
}
/**
* Get the squared length of the vector.
*/
lengthSquared(): number {
return this.dot(this)
}
/**
* Get distance from this point to another point
*/
distanceTo(p: Vec3): number {
const x = this.x
const y = this.y
const z = this.z
const px = p.x
const py = p.y
const pz = p.z
return Math.sqrt((px - x) * (px - x) + (py - y) * (py - y) + (pz - z) * (pz - z))
}
/**
* Get squared distance from this point to another point
*/
distanceSquared(p: Vec3): number {
const x = this.x
const y = this.y
const z = this.z
const px = p.x
const py = p.y
const pz = p.z
return (px - x) * (px - x) + (py - y) * (py - y) + (pz - z) * (pz - z)
}
/**
* Multiply all the components of the vector with a scalar.
* @param target The vector to save the result in.
*/
scale(scalar: number, target = new Vec3()): Vec3 {
const x = this.x
const y = this.y
const z = this.z
target.x = scalar * x
target.y = scalar * y
target.z = scalar * z
return target
}
/**
* Multiply the vector with an other vector, component-wise.
* @param target The vector to save the result in.
*/
vmul(vector: Vec3, target = new Vec3()): Vec3 {
target.x = vector.x * this.x
target.y = vector.y * this.y
target.z = vector.z * this.z
return target
}
/**
* Scale a vector and add it to this vector. Save the result in "target". (target = this + vector * scalar)
* @param target The vector to save the result in.
*/
addScaledVector(scalar: number, vector: Vec3, target = new Vec3()): Vec3 {
target.x = this.x + scalar * vector.x
target.y = this.y + scalar * vector.y
target.z = this.z + scalar * vector.z
return target
}
/**
* Calculate dot product
* @param vector
*/
dot(vector: Vec3): number {
return this.x * vector.x + this.y * vector.y + this.z * vector.z
}
isZero(): boolean {
return this.x === 0 && this.y === 0 && this.z === 0
}
/**
* Make the vector point in the opposite direction.
* @param target Optional target to save in
*/
negate(target = new Vec3()): Vec3 {
target.x = -this.x
target.y = -this.y
target.z = -this.z
return target
}
/**
* Compute two artificial tangents to the vector
* @param t1 Vector object to save the first tangent in
* @param t2 Vector object to save the second tangent in
*/
tangents(t1: Vec3, t2: Vec3): void {
const norm = this.length()
if (norm > 0.0) {
const n = Vec3_tangents_n
const inorm = 1 / norm
n.set(this.x * inorm, this.y * inorm, this.z * inorm)
const randVec = Vec3_tangents_randVec
if (Math.abs(n.x) < 0.9) {
randVec.set(1, 0, 0)
n.cross(randVec, t1)
} else {
randVec.set(0, 1, 0)
n.cross(randVec, t1)
}
n.cross(t1, t2)
} else {
// The normal length is zero, make something up
t1.set(1, 0, 0)
t2.set(0, 1, 0)
}
}
/**
* Converts to a more readable format
*/
toString(): string {
return `${this.x},${this.y},${this.z}`
}
/**
* Converts to an array
*/
toArray(): [number, number, number] {
return [this.x, this.y, this.z]
}
/**
* Copies value of source to this vector.
*/
copy(vector: Vec3): Vec3 {
this.x = vector.x
this.y = vector.y
this.z = vector.z
return this
}
/**
* Do a linear interpolation between two vectors
* @param t A number between 0 and 1. 0 will make this function return u, and 1 will make it return v. Numbers in between will generate a vector in between them.
*/
lerp(vector: Vec3, t: number, target: Vec3): void {
const x = this.x
const y = this.y
const z = this.z
target.x = x + (vector.x - x) * t
target.y = y + (vector.y - y) * t
target.z = z + (vector.z - z) * t
}
/**
* Check if a vector equals is almost equal to another one.
*/
almostEquals(vector: Vec3, precision = 1e-6): boolean {
if (
Math.abs(this.x - vector.x) > precision ||
Math.abs(this.y - vector.y) > precision ||
Math.abs(this.z - vector.z) > precision
) {
return false
}
return true
}
/**
* Check if a vector is almost zero
*/
almostZero(precision = 1e-6): boolean {
if (Math.abs(this.x) > precision || Math.abs(this.y) > precision || Math.abs(this.z) > precision) {
return false
}
return true
}
/**
* Check if the vector is anti-parallel to another vector.
* @param precision Set to zero for exact comparisons
*/
isAntiparallelTo(vector: Vec3, precision?: number): boolean {
this.negate(antip_neg)
return antip_neg.almostEquals(vector, precision)
}
/**
* Clone the vector
*/
clone(): Vec3 {
return new Vec3(this.x, this.y, this.z)
}
}
Vec3.ZERO = new Vec3(0, 0, 0)
Vec3.UNIT_X = new Vec3(1, 0, 0)
Vec3.UNIT_Y = new Vec3(0, 1, 0)
Vec3.UNIT_Z = new Vec3(0, 0, 1)
const Vec3_tangents_n = new Vec3()
const Vec3_tangents_randVec = new Vec3()
const antip_neg = new Vec3()