import { MathUtils } from './MathUtils.js';
/**
* Class representing a 3D vector.
*
* @author {@link https://github.com/Mugen87|Mugen87}
*/
class Vector3 {
/**
* Constructs a new 3D vector with the given values.
*
* @param {Number} x - The x component.
* @param {Number} y - The y component.
* @param {Number} z - The z component.
*/
constructor( x = 0, y = 0, z = 0 ) {
/**
* The x component.
* @type {Number}
*/
this.x = x;
/**
* The y component.
* @type {Number}
*/
this.y = y;
/**
* The z component.
* @type {Number}
*/
this.z = z;
}
/**
* Sets the given values to this 3D vector.
*
* @param {Number} x - The x component.
* @param {Number} y - The y component.
* @param {Number} z - The z component.
* @return {Vector3} A reference to this vector.
*/
set( x, y, z ) {
this.x = x;
this.y = y;
this.z = z;
return this;
}
/**
* Copies all values from the given 3D vector to this 3D vector.
*
* @param {Vector3} v - The vector to copy.
* @return {Vector3} A reference to this vector.
*/
copy( v ) {
this.x = v.x;
this.y = v.y;
this.z = v.z;
return this;
}
/**
* Creates a new 3D vector and copies all values from this 3D vector.
*
* @return {Vector3} A new 3D vector.
*/
clone() {
return new this.constructor().copy( this );
}
/**
* Adds the given 3D vector to this 3D vector.
*
* @param {Vector3} v - The vector to add.
* @return {Vector3} A reference to this vector.
*/
add( v ) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
}
/**
* Adds the given scalar to this 3D vector.
*
* @param {Number} s - The scalar to add.
* @return {Vector3} A reference to this vector.
*/
addScalar( s ) {
this.x += s;
this.y += s;
this.z += s;
return this;
}
/**
* Adds two given 3D vectors and stores the result in this 3D vector.
*
* @param {Vector3} a - The first vector of the operation.
* @param {Vector3} b - The second vector of the operation.
* @return {Vector3} A reference to this vector.
*/
addVectors( a, b ) {
this.x = a.x + b.x;
this.y = a.y + b.y;
this.z = a.z + b.z;
return this;
}
/**
* Subtracts the given 3D vector from this 3D vector.
*
* @param {Vector3} v - The vector to substract.
* @return {Vector3} A reference to this vector.
*/
sub( v ) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
return this;
}
/**
* Subtracts the given scalar from this 3D vector.
*
* @param {Number} s - The scalar to substract.
* @return {Vector3} A reference to this vector.
*/
subScalar( s ) {
this.x -= s;
this.y -= s;
this.z -= s;
return this;
}
/**
* Subtracts two given 3D vectors and stores the result in this 3D vector.
*
* @param {Vector3} a - The first vector of the operation.
* @param {Vector3} b - The second vector of the operation.
* @return {Vector3} A reference to this vector.
*/
subVectors( a, b ) {
this.x = a.x - b.x;
this.y = a.y - b.y;
this.z = a.z - b.z;
return this;
}
/**
* Multiplies the given 3D vector with this 3D vector.
*
* @param {Vector3} v - The vector to multiply.
* @return {Vector3} A reference to this vector.
*/
multiply( v ) {
this.x *= v.x;
this.y *= v.y;
this.z *= v.z;
return this;
}
/**
* Multiplies the given scalar with this 3D vector.
*
* @param {Number} s - The scalar to multiply.
* @return {Vector3} A reference to this vector.
*/
multiplyScalar( s ) {
this.x *= s;
this.y *= s;
this.z *= s;
return this;
}
/**
* Multiplies two given 3D vectors and stores the result in this 3D vector.
*
* @param {Vector3} a - The first vector of the operation.
* @param {Vector3} b - The second vector of the operation.
* @return {Vector3} A reference to this vector.
*/
multiplyVectors( a, b ) {
this.x = a.x * b.x;
this.y = a.y * b.y;
this.z = a.z * b.z;
return this;
}
/**
* Divides the given 3D vector through this 3D vector.
*
* @param {Vector3} v - The vector to divide.
* @return {Vector3} A reference to this vector.
*/
divide( v ) {
this.x /= v.x;
this.y /= v.y;
this.z /= v.z;
return this;
}
/**
* Divides the given scalar through this 3D vector.
*
* @param {Number} s - The scalar to multiply.
* @return {Vector3} A reference to this vector.
*/
divideScalar( s ) {
this.x /= s;
this.y /= s;
this.z /= s;
return this;
}
/**
* Divides two given 3D vectors and stores the result in this 3D vector.
*
* @param {Vector3} a - The first vector of the operation.
* @param {Vector3} b - The second vector of the operation.
* @return {Vector3} A reference to this vector.
*/
divideVectors( a, b ) {
this.x = a.x / b.x;
this.y = a.y / b.y;
this.z = a.z / b.z;
return this;
}
/**
* Reflects this vector along the given normal.
*
* @param {Vector3} normal - The normal vector.
* @return {Vector3} A reference to this vector.
*/
reflect( normal ) {
// solve r = v - 2( v * n ) * n
return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
}
/**
* Ensures this 3D vector lies in the given min/max range.
*
* @param {Vector3} min - The min range.
* @param {Vector3} max - The max range.
* @return {Vector3} A reference to this vector.
*/
clamp( min, max ) {
this.x = Math.max( min.x, Math.min( max.x, this.x ) );
this.y = Math.max( min.y, Math.min( max.y, this.y ) );
this.z = Math.max( min.z, Math.min( max.z, this.z ) );
return this;
}
/**
* Compares each vector component of this 3D vector and the
* given one and stores the minimum value in this instance.
*
* @param {Vector3} v - The 3D vector to check.
* @return {Vector3} A reference to this vector.
*/
min( v ) {
this.x = Math.min( this.x, v.x );
this.y = Math.min( this.y, v.y );
this.z = Math.min( this.z, v.z );
return this;
}
/**
* Compares each vector component of this 3D vector and the
* given one and stores the maximum value in this instance.
*
* @param {Vector3} v - The 3D vector to check.
* @return {Vector3} A reference to this vector.
*/
max( v ) {
this.x = Math.max( this.x, v.x );
this.y = Math.max( this.y, v.y );
this.z = Math.max( this.z, v.z );
return this;
}
/**
* Computes the dot product of this and the given 3D vector.
*
* @param {Vector3} v - The given 3D vector.
* @return {Number} The results of the dor product.
*/
dot( v ) {
return ( this.x * v.x ) + ( this.y * v.y ) + ( this.z * v.z );
}
/**
* Computes the cross product of this and the given 3D vector and
* stores the result in this 3D vector.
*
* @param {Vector3} v - A 3D vector.
* @return {Vector3} A reference to this vector.
*/
cross( v ) {
const x = this.x, y = this.y, z = this.z;
this.x = ( y * v.z ) - ( z * v.y );
this.y = ( z * v.x ) - ( x * v.z );
this.z = ( x * v.y ) - ( y * v.x );
return this;
}
/**
* Computes the cross product of the two given 3D vectors and
* stores the result in this 3D vector.
*
* @param {Vector3} a - The first 3D vector.
* @param {Vector3} b - The second 3D vector.
* @return {Vector3} A reference to this vector.
*/
crossVectors( a, b ) {
const ax = a.x, ay = a.y, az = a.z;
const bx = b.x, by = b.y, bz = b.z;
this.x = ( ay * bz ) - ( az * by );
this.y = ( az * bx ) - ( ax * bz );
this.z = ( ax * by ) - ( ay * bx );
return this;
}
/**
* Computes the angle between this and the given vector.
*
* @param {Vector3} v - A 3D vector.
* @return {Number} The angle in radians.
*/
angleTo( v ) {
const denominator = Math.sqrt( this.squaredLength() * v.squaredLength() );
if ( denominator === 0 ) return 0;
const theta = this.dot( v ) / denominator;
// clamp, to handle numerical problems
return Math.acos( MathUtils.clamp( theta, - 1, 1 ) );
}
/**
* Computes the length of this 3D vector.
*
* @return {Number} The length of this 3D vector.
*/
length() {
return Math.sqrt( this.squaredLength() );
}
/**
* Computes the squared length of this 3D vector.
* Calling this method is faster than calling {@link Vector3#length},
* since it avoids computing a square root.
*
* @return {Number} The squared length of this 3D vector.
*/
squaredLength() {
return this.dot( this );
}
/**
* Computes the manhattan length of this 3D vector.
*
* @return {Number} The manhattan length of this 3D vector.
*/
manhattanLength() {
return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
}
/**
* Computes the euclidean distance between this 3D vector and the given one.
*
* @param {Vector3} v - A 3D vector.
* @return {Number} The euclidean distance between two 3D vectors.
*/
distanceTo( v ) {
return Math.sqrt( this.squaredDistanceTo( v ) );
}
/**
* Computes the squared euclidean distance between this 3D vector and the given one.
* Calling this method is faster than calling {@link Vector3#distanceTo},
* since it avoids computing a square root.
*
* @param {Vector3} v - A 3D vector.
* @return {Number} The squared euclidean distance between two 3D vectors.
*/
squaredDistanceTo( v ) {
const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
return ( dx * dx ) + ( dy * dy ) + ( dz * dz );
}
/**
* Computes the manhattan distance between this 3D vector and the given one.
*
* @param {Vector3} v - A 3D vector.
* @return {Number} The manhattan distance between two 3D vectors.
*/
manhattanDistanceTo( v ) {
const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
return Math.abs( dx ) + Math.abs( dy ) + Math.abs( dz );
}
/**
* Normalizes this 3D vector.
*
* @return {Vector3} A reference to this vector.
*/
normalize() {
return this.divideScalar( this.length() || 1 );
}
/**
* Multiplies the given 4x4 matrix with this 3D vector
*
* @param {Matrix4} m - A 4x4 matrix.
* @return {Vector3} A reference to this vector.
*/
applyMatrix4( m ) {
const x = this.x, y = this.y, z = this.z;
const e = m.elements;
const w = 1 / ( ( e[ 3 ] * x ) + ( e[ 7 ] * y ) + ( e[ 11 ] * z ) + e[ 15 ] );
this.x = ( ( e[ 0 ] * x ) + ( e[ 4 ] * y ) + ( e[ 8 ] * z ) + e[ 12 ] ) * w;
this.y = ( ( e[ 1 ] * x ) + ( e[ 5 ] * y ) + ( e[ 9 ] * z ) + e[ 13 ] ) * w;
this.z = ( ( e[ 2 ] * x ) + ( e[ 6 ] * y ) + ( e[ 10 ] * z ) + e[ 14 ] ) * w;
return this;
}
/**
* Multiplies the given quaternion with this 3D vector.
*
* @param {Quaternion} q - A quaternion.
* @return {Vector3} A reference to this vector.
*/
applyRotation( q ) {
const x = this.x, y = this.y, z = this.z;
const qx = q.x, qy = q.y, qz = q.z, qw = q.w;
// calculate quat * vector
const ix = qw * x + qy * z - qz * y;
const iy = qw * y + qz * x - qx * z;
const iz = qw * z + qx * y - qy * x;
const iw = - qx * x - qy * y - qz * z;
// calculate result * inverse quat
this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
return this;
}
/**
* Extracts the position portion of the given 4x4 matrix and stores it in this 3D vector.
*
* @param {Matrix4} m - A 4x4 matrix.
* @return {Vector3} A reference to this vector.
*/
extractPositionFromMatrix( m ) {
const e = m.elements;
this.x = e[ 12 ];
this.y = e[ 13 ];
this.z = e[ 14 ];
return this;
}
/**
* Transform this direction vector by the given 4x4 matrix.
*
* @param {Matrix4} m - A 4x4 matrix.
* @return {Vector3} A reference to this vector.
*/
transformDirection( m ) {
const x = this.x, y = this.y, z = this.z;
const e = m.elements;
this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
return this.normalize();
}
/**
* Sets the components of this 3D vector from a column of a 3x3 matrix.
*
* @param {Matrix3} m - A 3x3 matrix.
* @param {Number} i - The index of the column.
* @return {Vector3} A reference to this vector.
*/
fromMatrix3Column( m, i ) {
return this.fromArray( m.elements, i * 3 );
}
/**
* Sets the components of this 3D vector from a column of a 4x4 matrix.
*
* @param {Matrix3} m - A 4x4 matrix.
* @param {Number} i - The index of the column.
* @return {Vector3} A reference to this vector.
*/
fromMatrix4Column( m, i ) {
return this.fromArray( m.elements, i * 4 );
}
/**
* Sets the components of this 3D vector from a spherical coordinate.
*
* @param {Number} radius - The radius.
* @param {Number} phi - The polar or inclination angle in radians. Should be in the range of (−π/2, +π/2].
* @param {Number} theta - The azimuthal angle in radians. Should be in the range of (−π, +π].
* @return {Vector3} A reference to this vector.
*/
fromSpherical( radius, phi, theta ) {
const sinPhiRadius = Math.sin( phi ) * radius;
this.x = sinPhiRadius * Math.sin( theta );
this.y = Math.cos( phi ) * radius;
this.z = sinPhiRadius * Math.cos( theta );
return this;
}
/**
* Sets the components of this 3D vector from an array.
*
* @param {Array<Number>} array - An array.
* @param {Number} offset - An optional offset.
* @return {Vector3} A reference to this vector.
*/
fromArray( array, offset = 0 ) {
this.x = array[ offset + 0 ];
this.y = array[ offset + 1 ];
this.z = array[ offset + 2 ];
return this;
}
/**
* Copies all values of this 3D vector to the given array.
*
* @param {Array<Number>} array - An array.
* @param {Number} offset - An optional offset.
* @return {Array<Number>} The array with the 3D vector components.
*/
toArray( array, offset = 0 ) {
array[ offset + 0 ] = this.x;
array[ offset + 1 ] = this.y;
array[ offset + 2 ] = this.z;
return array;
}
/**
* Returns true if the given 3D vector is deep equal with this 3D vector.
*
* @param {Vector3} v - The 3D vector to test.
* @return {Boolean} The result of the equality test.
*/
equals( v ) {
return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
}
}
const v1 = new Vector3();
export { Vector3 };