playcanvas
Version:
PlayCanvas WebGL game engine
702 lines (700 loc) • 23.4 kB
JavaScript
/**
* 3-dimensional vector.
*
* @category Math
*/ class Vec3 {
/**
* Creates a new Vec3 instance.
*
* @overload
* @param {number} [x] - The x value. Defaults to 0.
* @param {number} [y] - The y value. Defaults to 0.
* @param {number} [z] - The z value. Defaults to 0.
* @example
* const v1 = new pc.Vec3(); // defaults to 0, 0, 0
* const v2 = new pc.Vec3(1, 2, 3);
*/ /**
* Creates a new Vec3 instance.
*
* @overload
* @param {number[]} arr - The array to set the vector values from.
* @example
* const v = new pc.Vec3([1, 2, 3]);
*/ /**
* @param {number|number[]} [x] - The x value. Defaults to 0. If x is an array of length 3, the
* array will be used to populate all components.
* @param {number} [y] - The y value. Defaults to 0.
* @param {number} [z] - The z value. Defaults to 0.
*/ constructor(x = 0, y = 0, z = 0){
if (x.length === 3) {
this.x = x[0];
this.y = x[1];
this.z = x[2];
} else {
this.x = x;
this.y = y;
this.z = z;
}
}
/**
* Adds a 3-dimensional vector to another in place.
*
* @param {Vec3} rhs - The vector to add to the specified vector.
* @returns {Vec3} Self for chaining.
* @example
* const a = new pc.Vec3(10, 10, 10);
* const b = new pc.Vec3(20, 20, 20);
*
* a.add(b);
*
* // Outputs [30, 30, 30]
* console.log("The result of the addition is: " + a.toString());
*/ add(rhs) {
this.x += rhs.x;
this.y += rhs.y;
this.z += rhs.z;
return this;
}
/**
* Adds two 3-dimensional vectors together and returns the result.
*
* @param {Vec3} lhs - The first vector operand for the addition.
* @param {Vec3} rhs - The second vector operand for the addition.
* @returns {Vec3} Self for chaining.
* @example
* const a = new pc.Vec3(10, 10, 10);
* const b = new pc.Vec3(20, 20, 20);
* const r = new pc.Vec3();
*
* r.add2(a, b);
* // Outputs [30, 30, 30]
*
* console.log("The result of the addition is: " + r.toString());
*/ add2(lhs, rhs) {
this.x = lhs.x + rhs.x;
this.y = lhs.y + rhs.y;
this.z = lhs.z + rhs.z;
return this;
}
/**
* Adds a number to each element of a vector.
*
* @param {number} scalar - The number to add.
* @returns {Vec3} Self for chaining.
* @example
* const vec = new pc.Vec3(3, 4, 5);
*
* vec.addScalar(2);
*
* // Outputs [5, 6, 7]
* console.log("The result of the addition is: " + vec.toString());
*/ addScalar(scalar) {
this.x += scalar;
this.y += scalar;
this.z += scalar;
return this;
}
/**
* Adds a 3-dimensional vector scaled by scalar value. Does not modify the vector being added.
*
* @param {Vec3} rhs - The vector to add to the specified vector.
* @param {number} scalar - The number to multiply the added vector with.
* @returns {Vec3} Self for chaining.
* @example
* const vec = new pc.Vec3(1, 2, 3);
*
* vec.addScaled(pc.Vec3.UP, 2);
*
* // Outputs [1, 4, 3]
* console.log("The result of the addition is: " + vec.toString());
*/ addScaled(rhs, scalar) {
this.x += rhs.x * scalar;
this.y += rhs.y * scalar;
this.z += rhs.z * scalar;
return this;
}
/**
* Returns an identical copy of the specified 3-dimensional vector.
*
* @returns {this} A 3-dimensional vector containing the result of the cloning.
* @example
* const v = new pc.Vec3(10, 20, 30);
* const vclone = v.clone();
* console.log("The result of the cloning is: " + vclone.toString());
*/ clone() {
/** @type {this} */ const cstr = this.constructor;
return new cstr(this.x, this.y, this.z);
}
/**
* Copies the contents of a source 3-dimensional vector to a destination 3-dimensional vector.
*
* @param {Vec3} rhs - A vector to copy to the specified vector.
* @returns {Vec3} Self for chaining.
* @example
* const src = new pc.Vec3(10, 20, 30);
* const dst = new pc.Vec3();
*
* dst.copy(src);
*
* console.log("The two vectors are " + (dst.equals(src) ? "equal" : "different"));
*/ copy(rhs) {
this.x = rhs.x;
this.y = rhs.y;
this.z = rhs.z;
return this;
}
/**
* Returns the result of a cross product operation performed on the two specified 3-dimensional
* vectors.
*
* @param {Vec3} lhs - The first 3-dimensional vector operand of the cross product.
* @param {Vec3} rhs - The second 3-dimensional vector operand of the cross product.
* @returns {Vec3} Self for chaining.
* @example
* const back = new pc.Vec3().cross(pc.Vec3.RIGHT, pc.Vec3.UP);
*
* // Prints the Z axis (i.e. [0, 0, 1])
* console.log("The result of the cross product is: " + back.toString());
*/ cross(lhs, rhs) {
// Create temporary variables in case lhs or rhs are 'this'
const lx = lhs.x;
const ly = lhs.y;
const lz = lhs.z;
const rx = rhs.x;
const ry = rhs.y;
const rz = rhs.z;
this.x = ly * rz - ry * lz;
this.y = lz * rx - rz * lx;
this.z = lx * ry - rx * ly;
return this;
}
/**
* Returns the distance between the two specified 3-dimensional vectors.
*
* @param {Vec3} rhs - The second 3-dimensional vector to test.
* @returns {number} The distance between the two vectors.
* @example
* const v1 = new pc.Vec3(5, 10, 20);
* const v2 = new pc.Vec3(10, 20, 40);
* const d = v1.distance(v2);
* console.log("The distance between v1 and v2 is: " + d);
*/ distance(rhs) {
const x = this.x - rhs.x;
const y = this.y - rhs.y;
const z = this.z - rhs.z;
return Math.sqrt(x * x + y * y + z * z);
}
/**
* Divides a 3-dimensional vector by another in place.
*
* @param {Vec3} rhs - The vector to divide the specified vector by.
* @returns {Vec3} Self for chaining.
* @example
* const a = new pc.Vec3(4, 9, 16);
* const b = new pc.Vec3(2, 3, 4);
*
* a.div(b);
*
* // Outputs [2, 3, 4]
* console.log("The result of the division is: " + a.toString());
*/ div(rhs) {
this.x /= rhs.x;
this.y /= rhs.y;
this.z /= rhs.z;
return this;
}
/**
* Divides one 3-dimensional vector by another and writes the result to the specified vector.
*
* @param {Vec3} lhs - The dividend vector (the vector being divided).
* @param {Vec3} rhs - The divisor vector (the vector dividing the dividend).
* @returns {Vec3} Self for chaining.
* @example
* const a = new pc.Vec3(4, 9, 16);
* const b = new pc.Vec3(2, 3, 4);
* const r = new pc.Vec3();
*
* r.div2(a, b);
*
* // Outputs [2, 3, 4]
* console.log("The result of the division is: " + r.toString());
*/ div2(lhs, rhs) {
this.x = lhs.x / rhs.x;
this.y = lhs.y / rhs.y;
this.z = lhs.z / rhs.z;
return this;
}
/**
* Divides each element of a vector by a number.
*
* @param {number} scalar - The number to divide by.
* @returns {Vec3} Self for chaining.
* @example
* const vec = new pc.Vec3(3, 6, 9);
*
* vec.divScalar(3);
*
* // Outputs [1, 2, 3]
* console.log("The result of the division is: " + vec.toString());
*/ divScalar(scalar) {
this.x /= scalar;
this.y /= scalar;
this.z /= scalar;
return this;
}
/**
* Returns the result of a dot product operation performed on the two specified 3-dimensional
* vectors.
*
* @param {Vec3} rhs - The second 3-dimensional vector operand of the dot product.
* @returns {number} The result of the dot product operation.
* @example
* const v1 = new pc.Vec3(5, 10, 20);
* const v2 = new pc.Vec3(10, 20, 40);
* const v1dotv2 = v1.dot(v2);
* console.log("The result of the dot product is: " + v1dotv2);
*/ dot(rhs) {
return this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
}
/**
* Reports whether two vectors are equal.
*
* @param {Vec3} rhs - The vector to compare to the specified vector.
* @returns {boolean} True if the vectors are equal and false otherwise.
* @example
* const a = new pc.Vec3(1, 2, 3);
* const b = new pc.Vec3(4, 5, 6);
* console.log("The two vectors are " + (a.equals(b) ? "equal" : "different"));
*/ equals(rhs) {
return this.x === rhs.x && this.y === rhs.y && this.z === rhs.z;
}
/**
* Reports whether two vectors are equal using an absolute error tolerance.
*
* @param {Vec3} rhs - The vector to be compared against.
* @param {number} [epsilon] - The maximum difference between each component of the two
* vectors. Defaults to 1e-6.
* @returns {boolean} True if the vectors are equal and false otherwise.
* @example
* const a = new pc.Vec3();
* const b = new pc.Vec3();
* console.log("The two vectors are approximately " + (a.equalsApprox(b, 1e-9) ? "equal" : "different"));
*/ equalsApprox(rhs, epsilon = 1e-6) {
return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon;
}
/**
* Returns the magnitude of the specified 3-dimensional vector.
*
* @returns {number} The magnitude of the specified 3-dimensional vector.
* @example
* const vec = new pc.Vec3(3, 4, 0);
* const len = vec.length();
* // Outputs 5
* console.log("The length of the vector is: " + len);
*/ length() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
/**
* Returns the magnitude squared of the specified 3-dimensional vector.
*
* @returns {number} The magnitude of the specified 3-dimensional vector.
* @example
* const vec = new pc.Vec3(3, 4, 0);
* const len = vec.lengthSq();
* // Outputs 25
* console.log("The length squared of the vector is: " + len);
*/ lengthSq() {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
/**
* Returns the result of a linear interpolation between two specified 3-dimensional vectors.
*
* @param {Vec3} lhs - The 3-dimensional to interpolate from.
* @param {Vec3} rhs - The 3-dimensional to interpolate to.
* @param {number} alpha - The value controlling the point of interpolation. Between 0 and 1,
* the linear interpolant will occur on a straight line between lhs and rhs. Outside of this
* range, the linear interpolant will occur on a ray extrapolated from this line.
* @returns {Vec3} Self for chaining.
* @example
* const a = new pc.Vec3(0, 0, 0);
* const b = new pc.Vec3(10, 10, 10);
* const r = new pc.Vec3();
*
* r.lerp(a, b, 0); // r is equal to a
* r.lerp(a, b, 0.5); // r is 5, 5, 5
* r.lerp(a, b, 1); // r is equal to b
*/ lerp(lhs, rhs, alpha) {
this.x = lhs.x + alpha * (rhs.x - lhs.x);
this.y = lhs.y + alpha * (rhs.y - lhs.y);
this.z = lhs.z + alpha * (rhs.z - lhs.z);
return this;
}
/**
* Multiplies a 3-dimensional vector to another in place.
*
* @param {Vec3} rhs - The 3-dimensional vector used as the second multiplicand of the operation.
* @returns {Vec3} Self for chaining.
* @example
* const a = new pc.Vec3(2, 3, 4);
* const b = new pc.Vec3(4, 5, 6);
*
* a.mul(b);
*
* // Outputs [8, 15, 24]
* console.log("The result of the multiplication is: " + a.toString());
*/ mul(rhs) {
this.x *= rhs.x;
this.y *= rhs.y;
this.z *= rhs.z;
return this;
}
/**
* Returns the result of multiplying the specified 3-dimensional vectors together.
*
* @param {Vec3} lhs - The 3-dimensional vector used as the first multiplicand of the operation.
* @param {Vec3} rhs - The 3-dimensional vector used as the second multiplicand of the operation.
* @returns {Vec3} Self for chaining.
* @example
* const a = new pc.Vec3(2, 3, 4);
* const b = new pc.Vec3(4, 5, 6);
* const r = new pc.Vec3();
*
* r.mul2(a, b);
*
* // Outputs [8, 15, 24]
* console.log("The result of the multiplication is: " + r.toString());
*/ mul2(lhs, rhs) {
this.x = lhs.x * rhs.x;
this.y = lhs.y * rhs.y;
this.z = lhs.z * rhs.z;
return this;
}
/**
* Multiplies each element of a vector by a number.
*
* @param {number} scalar - The number to multiply by.
* @returns {Vec3} Self for chaining.
* @example
* const vec = new pc.Vec3(3, 6, 9);
*
* vec.mulScalar(3);
*
* // Outputs [9, 18, 27]
* console.log("The result of the multiplication is: " + vec.toString());
*/ mulScalar(scalar) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
return this;
}
/**
* Returns this 3-dimensional vector converted to a unit vector in place. If the vector has a
* length of zero, the vector's elements will be set to zero.
*
* @param {Vec3} [src] - The vector to normalize. If not set, the operation is done in place.
* @returns {Vec3} Self for chaining.
* @example
* const v = new pc.Vec3(25, 0, 0);
*
* v.normalize();
*
* // Outputs [1, 0, 0]
* console.log("The result of the vector normalization is: " + v.toString());
*/ normalize(src = this) {
const lengthSq = src.x * src.x + src.y * src.y + src.z * src.z;
if (lengthSq > 0) {
const invLength = 1 / Math.sqrt(lengthSq);
this.x = src.x * invLength;
this.y = src.y * invLength;
this.z = src.z * invLength;
}
return this;
}
/**
* Each element is set to the largest integer less than or equal to its value.
*
* @param {Vec3} [src] - The vector to floor. If not set, the operation is done in place.
* @returns {Vec3} Self for chaining.
*/ floor(src = this) {
this.x = Math.floor(src.x);
this.y = Math.floor(src.y);
this.z = Math.floor(src.z);
return this;
}
/**
* Each element is rounded up to the next largest integer.
*
* @param {Vec3} [src] - The vector to ceil. If not set, the operation is done in place.
* @returns {Vec3} Self for chaining.
*/ ceil(src = this) {
this.x = Math.ceil(src.x);
this.y = Math.ceil(src.y);
this.z = Math.ceil(src.z);
return this;
}
/**
* Each element is rounded up or down to the nearest integer.
*
* @param {Vec3} [src] - The vector to round. If not set, the operation is done in place.
* @returns {Vec3} Self for chaining.
*/ round(src = this) {
this.x = Math.round(src.x);
this.y = Math.round(src.y);
this.z = Math.round(src.z);
return this;
}
/**
* Each element is assigned a value from rhs parameter if it is smaller.
*
* @param {Vec3} rhs - The 3-dimensional vector used as the source of elements to compare to.
* @returns {Vec3} Self for chaining.
*/ min(rhs) {
if (rhs.x < this.x) this.x = rhs.x;
if (rhs.y < this.y) this.y = rhs.y;
if (rhs.z < this.z) this.z = rhs.z;
return this;
}
/**
* Each element is assigned a value from rhs parameter if it is larger.
*
* @param {Vec3} rhs - The 3-dimensional vector used as the source of elements to compare to.
* @returns {Vec3} Self for chaining.
*/ max(rhs) {
if (rhs.x > this.x) this.x = rhs.x;
if (rhs.y > this.y) this.y = rhs.y;
if (rhs.z > this.z) this.z = rhs.z;
return this;
}
/**
* Projects this 3-dimensional vector onto the specified vector.
*
* @param {Vec3} rhs - The vector onto which the original vector will be projected on.
* @returns {Vec3} Self for chaining.
* @example
* const v = new pc.Vec3(5, 5, 5);
* const normal = new pc.Vec3(1, 0, 0);
*
* v.project(normal);
*
* // Outputs [5, 0, 0]
* console.log("The result of the vector projection is: " + v.toString());
*/ project(rhs) {
const a_dot_b = this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
const b_dot_b = rhs.x * rhs.x + rhs.y * rhs.y + rhs.z * rhs.z;
const s = a_dot_b / b_dot_b;
this.x = rhs.x * s;
this.y = rhs.y * s;
this.z = rhs.z * s;
return this;
}
/**
* Sets the specified 3-dimensional vector to the supplied numerical values.
*
* @param {number} x - The value to set on the first component of the vector.
* @param {number} y - The value to set on the second component of the vector.
* @param {number} z - The value to set on the third component of the vector.
* @returns {Vec3} Self for chaining.
* @example
* const v = new pc.Vec3();
* v.set(5, 10, 20);
*
* // Outputs [5, 10, 20]
* console.log("The result of the vector set is: " + v.toString());
*/ set(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
return this;
}
/**
* Subtracts a 3-dimensional vector from another in place.
*
* @param {Vec3} rhs - The vector to subtract from the specified vector.
* @returns {Vec3} Self for chaining.
* @example
* const a = new pc.Vec3(10, 10, 10);
* const b = new pc.Vec3(20, 20, 20);
*
* a.sub(b);
*
* // Outputs [-10, -10, -10]
* console.log("The result of the subtraction is: " + a.toString());
*/ sub(rhs) {
this.x -= rhs.x;
this.y -= rhs.y;
this.z -= rhs.z;
return this;
}
/**
* Subtracts two 3-dimensional vectors from one another and returns the result.
*
* @param {Vec3} lhs - The first vector operand for the subtraction.
* @param {Vec3} rhs - The second vector operand for the subtraction.
* @returns {Vec3} Self for chaining.
* @example
* const a = new pc.Vec3(10, 10, 10);
* const b = new pc.Vec3(20, 20, 20);
* const r = new pc.Vec3();
*
* r.sub2(a, b);
*
* // Outputs [-10, -10, -10]
* console.log("The result of the subtraction is: " + r.toString());
*/ sub2(lhs, rhs) {
this.x = lhs.x - rhs.x;
this.y = lhs.y - rhs.y;
this.z = lhs.z - rhs.z;
return this;
}
/**
* Subtracts a number from each element of a vector.
*
* @param {number} scalar - The number to subtract.
* @returns {Vec3} Self for chaining.
* @example
* const vec = new pc.Vec3(3, 4, 5);
*
* vec.subScalar(2);
*
* // Outputs [1, 2, 3]
* console.log("The result of the subtraction is: " + vec.toString());
*/ subScalar(scalar) {
this.x -= scalar;
this.y -= scalar;
this.z -= scalar;
return this;
}
/**
* Set the values of the vector from an array.
*
* @param {number[]|ArrayBufferView} arr - The array to set the vector values from.
* @param {number} [offset] - The zero-based index at which to start copying elements from the
* array. Default is 0.
* @returns {Vec3} Self for chaining.
* @example
* const v = new pc.Vec3();
* v.fromArray([20, 10, 5]);
* // v is set to [20, 10, 5]
*/ fromArray(arr, offset = 0) {
this.x = arr[offset] ?? this.x;
this.y = arr[offset + 1] ?? this.y;
this.z = arr[offset + 2] ?? this.z;
return this;
}
/**
* Converts the vector to string form.
*
* @returns {string} The vector in string form.
* @example
* const v = new pc.Vec3(20, 10, 5);
* // Outputs [20, 10, 5]
* console.log(v.toString());
*/ toString() {
return `[${this.x}, ${this.y}, ${this.z}]`;
}
/**
* @overload
* @param {number[]} [arr] - The array to populate with the vector's number
* components. If not specified, a new array is created.
* @param {number} [offset] - The zero-based index at which to start copying elements to the
* array. Default is 0.
* @returns {number[]} The vector as an array.
*/ /**
* @overload
* @param {ArrayBufferView} arr - The array to populate with the vector's number
* components. If not specified, a new array is created.
* @param {number} [offset] - The zero-based index at which to start copying elements to the
* array. Default is 0.
* @returns {ArrayBufferView} The vector as an array.
*/ /**
* Converts the vector to an array.
*
* @param {number[]|ArrayBufferView} [arr] - The array to populate with the vector's number
* components. If not specified, a new array is created.
* @param {number} [offset] - The zero-based index at which to start copying elements to the
* array. Default is 0.
* @returns {number[]|ArrayBufferView} The vector as an array.
* @example
* const v = new pc.Vec3(20, 10, 5);
* // Outputs [20, 10, 5]
* console.log(v.toArray());
*/ toArray(arr = [], offset = 0) {
arr[offset] = this.x;
arr[offset + 1] = this.y;
arr[offset + 2] = this.z;
return arr;
}
static{
/**
* A constant vector set to [0, 0, 0].
*
* @type {Vec3}
* @readonly
*/ this.ZERO = Object.freeze(new Vec3(0, 0, 0));
}
static{
/**
* A constant vector set to [0.5, 0.5, 0.5].
*
* @type {Vec3}
* @readonly
*/ this.HALF = Object.freeze(new Vec3(0.5, 0.5, 0.5));
}
static{
/**
* A constant vector set to [1, 1, 1].
*
* @type {Vec3}
* @readonly
*/ this.ONE = Object.freeze(new Vec3(1, 1, 1));
}
static{
/**
* A constant vector set to [0, 1, 0].
*
* @type {Vec3}
* @readonly
*/ this.UP = Object.freeze(new Vec3(0, 1, 0));
}
static{
/**
* A constant vector set to [0, -1, 0].
*
* @type {Vec3}
* @readonly
*/ this.DOWN = Object.freeze(new Vec3(0, -1, 0));
}
static{
/**
* A constant vector set to [1, 0, 0].
*
* @type {Vec3}
* @readonly
*/ this.RIGHT = Object.freeze(new Vec3(1, 0, 0));
}
static{
/**
* A constant vector set to [-1, 0, 0].
*
* @type {Vec3}
* @readonly
*/ this.LEFT = Object.freeze(new Vec3(-1, 0, 0));
}
static{
/**
* A constant vector set to [0, 0, -1].
*
* @type {Vec3}
* @readonly
*/ this.FORWARD = Object.freeze(new Vec3(0, 0, -1));
}
static{
/**
* A constant vector set to [0, 0, 1].
*
* @type {Vec3}
* @readonly
*/ this.BACK = Object.freeze(new Vec3(0, 0, 1));
}
}
export { Vec3 };