yuka
Version:
JavaScript Game AI library
2,286 lines (1,671 loc) • 443 kB
JavaScript
/**
* The MIT License
*
* Copyright © 2022 Yuka authors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Class for representing a telegram, an envelope which contains a message
* and certain metadata like sender and receiver. Part of the messaging system
* for game entities.
*
* @author {@link https://github.com/Mugen87|Mugen87}
*/
class Telegram {
/**
* Constructs a new telegram object.
*
* @param {GameEntity} sender - The sender.
* @param {GameEntity} receiver - The receiver.
* @param {String} message - The actual message.
* @param {Number} delay - A time value in millisecond used to delay the message dispatching.
* @param {Object} data - An object for custom data.
*/
constructor( sender, receiver, message, delay, data ) {
/**
* The sender.
* @type {GameEntity}
*/
this.sender = sender;
/**
* The receiver.
* @type {GameEntity}
*/
this.receiver = receiver;
/**
* The actual message.
* @type {String}
*/
this.message = message;
/**
* A time value in millisecond used to delay the message dispatching.
* @type {Number}
*/
this.delay = delay;
/**
* An object for custom data.
* @type {Object}
*/
this.data = data;
}
/**
* Transforms this instance into a JSON object.
*
* @return {Object} The JSON object.
*/
toJSON() {
return {
type: this.constructor.name,
sender: this.sender.uuid,
receiver: this.receiver.uuid,
message: this.message,
delay: this.delay,
data: this.data
};
}
/**
* Restores this instance from the given JSON object.
*
* @param {Object} json - The JSON object.
* @return {Telegram} A reference to this telegram.
*/
fromJSON( json ) {
this.sender = json.sender;
this.receiver = json.receiver;
this.message = json.message;
this.delay = json.delay;
this.data = json.data;
return this;
}
/**
* Restores UUIDs with references to GameEntity objects.
*
* @param {Map<String,GameEntity>} entities - Maps game entities to UUIDs.
* @return {Telegram} A reference to this telegram.
*/
resolveReferences( entities ) {
this.sender = entities.get( this.sender );
this.receiver = entities.get( this.receiver );
return this;
}
}
/* istanbul ignore next */
/**
* Class with a logger interface. Messages are only logged to console if
* their log level is smaller or equal than the current log level.
*
* @author {@link https://github.com/Mugen87|Mugen87}
*/
class Logger {
/**
* Sets the log level for the logger. Allow values are: *LOG*,
* *WARN*, *ERROR*, *SILENT*. The default level is *WARN*. The constants
* are accessible over the *Logger.LEVEL* namespace.
*
* @param {Number} level - The log level.
*/
static setLevel( level ) {
currentLevel = level;
}
/**
* Logs a message with the level *LOG*.
*
* @param {...Any} args - The arguments to log.
*/
static log( ...args ) {
if ( currentLevel <= Logger.LEVEL.LOG ) console.log( ...args );
}
/**
* Logs a message with the level *WARN*.
*
* @param {...Any} args - The arguments to log.
*/
static warn( ...args ) {
if ( currentLevel <= Logger.LEVEL.WARN ) console.warn( ...args );
}
/**
* Logs a message with the level *ERROR*.
*
* @param {...Any} args - The arguments to log.
*/
static error( ...args ) {
if ( currentLevel <= Logger.LEVEL.ERROR ) console.error( ...args );
}
}
Logger.LEVEL = Object.freeze( {
LOG: 0,
WARN: 1,
ERROR: 2,
SILENT: 3
} );
let currentLevel = Logger.LEVEL.WARN;
/**
* This class is the core of the messaging system for game entities and used by the
* {@link EntityManager}. The implementation can directly dispatch messages or use a
* delayed delivery for deferred communication. This can be useful if a game entity
* wants to inform itself about a particular event in the future.
*
* @author {@link https://github.com/Mugen87|Mugen87}
*/
class MessageDispatcher {
/**
* Constructs a new message dispatcher.
*/
constructor() {
/**
* A list of delayed telegrams.
* @type {Array<Telegram>}
* @readonly
*/
this.delayedTelegrams = new Array();
}
/**
* Delivers the message to the receiver.
*
* @param {Telegram} telegram - The telegram to deliver.
* @return {MessageDispatcher} A reference to this message dispatcher.
*/
deliver( telegram ) {
const receiver = telegram.receiver;
if ( receiver.handleMessage( telegram ) === false ) {
Logger.warn( 'YUKA.MessageDispatcher: Message not handled by receiver: %o', receiver );
}
return this;
}
/**
* Receives the raw telegram data and decides how to dispatch the telegram (with or without delay).
*
* @param {GameEntity} sender - The sender.
* @param {GameEntity} receiver - The receiver.
* @param {String} message - The actual message.
* @param {Number} delay - A time value in millisecond used to delay the message dispatching.
* @param {Object} data - An object for custom data.
* @return {MessageDispatcher} A reference to this message dispatcher.
*/
dispatch( sender, receiver, message, delay, data ) {
const telegram = new Telegram( sender, receiver, message, delay, data );
if ( delay <= 0 ) {
this.deliver( telegram );
} else {
this.delayedTelegrams.push( telegram );
}
return this;
}
/**
* Used to process delayed messages.
*
* @param {Number} delta - The time delta.
* @return {MessageDispatcher} A reference to this message dispatcher.
*/
dispatchDelayedMessages( delta ) {
let i = this.delayedTelegrams.length;
while ( i -- ) {
const telegram = this.delayedTelegrams[ i ];
telegram.delay -= delta;
if ( telegram.delay <= 0 ) {
this.deliver( telegram );
this.delayedTelegrams.pop();
}
}
return this;
}
/**
* Clears the internal state of this message dispatcher.
*
* @return {MessageDispatcher} A reference to this message dispatcher.
*/
clear() {
this.delayedTelegrams.length = 0;
return this;
}
/**
* Transforms this instance into a JSON object.
*
* @return {Object} The JSON object.
*/
toJSON() {
const data = {
type: this.constructor.name,
delayedTelegrams: new Array()
};
// delayed telegrams
for ( let i = 0, l = this.delayedTelegrams.length; i < l; i ++ ) {
const delayedTelegram = this.delayedTelegrams[ i ];
data.delayedTelegrams.push( delayedTelegram.toJSON() );
}
return data;
}
/**
* Restores this instance from the given JSON object.
*
* @param {Object} json - The JSON object.
* @return {MessageDispatcher} A reference to this message dispatcher.
*/
fromJSON( json ) {
this.clear();
const telegramsJSON = json.delayedTelegrams;
for ( let i = 0, l = telegramsJSON.length; i < l; i ++ ) {
const telegramJSON = telegramsJSON[ i ];
const telegram = new Telegram().fromJSON( telegramJSON );
this.delayedTelegrams.push( telegram );
}
return this;
}
/**
* Restores UUIDs with references to GameEntity objects.
*
* @param {Map<String,GameEntity>} entities - Maps game entities to UUIDs.
* @return {MessageDispatcher} A reference to this message dispatcher.
*/
resolveReferences( entities ) {
const delayedTelegrams = this.delayedTelegrams;
for ( let i = 0, l = delayedTelegrams.length; i < l; i ++ ) {
const delayedTelegram = delayedTelegrams[ i ];
delayedTelegram.resolveReferences( entities );
}
return this;
}
}
const lut = new Array();
for ( let i = 0; i < 256; i ++ ) {
lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 );
}
/**
* Class with various math helpers.
*
* @author {@link https://github.com/Mugen87|Mugen87}
*/
class MathUtils {
/**
* Computes the signed area of a rectangle defined by three points.
* This method can also be used to calculate the area of a triangle.
*
* @param {Vector3} a - The first point in 3D space.
* @param {Vector3} b - The second point in 3D space.
* @param {Vector3} c - The third point in 3D space.
* @return {Number} The signed area.
*/
static area( a, b, c ) {
return ( ( c.x - a.x ) * ( b.z - a.z ) ) - ( ( b.x - a.x ) * ( c.z - a.z ) );
}
/**
* Returns the indices of the maximum values of the given array.
*
* @param {Array<Number>} array - The input array.
* @return {Array<Number>} Array of indices into the array.
*/
static argmax( array ) {
const max = Math.max( ...array );
const indices = [];
for ( let i = 0, l = array.length; i < l; i ++ ) {
if ( array[ i ] === max ) indices.push( i );
}
return indices;
}
/**
* Returns a random sample from a given array.
*
* @param {Array<Any>} array - The array that is used to generate the random sample.
* @param {Array<Number>} probabilities - The probabilities associated with each entry. If not given, the sample assumes a uniform distribution over all entries.
* @return {Any} The random sample value.
*/
static choice( array, probabilities = null ) {
const random = Math.random();
if ( probabilities === null ) {
return array[ Math.floor( Math.random() * array.length ) ];
} else {
let probability = 0;
const index = array.map( ( value, index ) => {
probability += probabilities[ index ];
return probability;
} ).findIndex( ( probability ) => probability >= random );
return array[ index ];
}
}
/**
* Ensures the given scalar value is within a given min/max range.
*
* @param {Number} value - The value to clamp.
* @param {Number} min - The min value.
* @param {Number} max - The max value.
* @return {Number} The clamped value.
*/
static clamp( value, min, max ) {
return Math.max( min, Math.min( max, value ) );
}
/**
* Computes a RFC4122 Version 4 complied Universally Unique Identifier (UUID).
*
* @return {String} The UUID.
*/
static generateUUID() {
// https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript/21963136#21963136
const d0 = Math.random() * 0xffffffff | 0;
const d1 = Math.random() * 0xffffffff | 0;
const d2 = Math.random() * 0xffffffff | 0;
const d3 = Math.random() * 0xffffffff | 0;
const uuid = lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + '-' +
lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + '-' + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + '-' +
lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + '-' + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] +
lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ];
return uuid.toUpperCase();
}
/**
* Computes a random float value within a given min/max range.
*
* @param {Number} min - The min value.
* @param {Number} max - The max value.
* @return {Number} The random float value.
*/
static randFloat( min, max ) {
return min + Math.random() * ( max - min );
}
/**
* Computes a random integer value within a given min/max range.
*
* @param {Number} min - The min value.
* @param {Number} max - The max value.
* @return {Number} The random integer value.
*/
static randInt( min, max ) {
return min + Math.floor( Math.random() * ( max - min + 1 ) );
}
}
/**
* 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$4.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$4 = new Vector3();
const WorldUp = new Vector3( 0, 1, 0 );
const localRight = new Vector3();
const worldRight = new Vector3();
const perpWorldUp = new Vector3();
const temp = new Vector3();
const colVal = [ 2, 2, 1 ];
const rowVal = [ 1, 0, 0 ];
/**
* Class representing a 3x3 matrix. The elements of the matrix
* are stored in column-major order.
*
* @author {@link https://github.com/Mugen87|Mugen87}
*/
class Matrix3 {
/**
* Constructs a new 3x3 identity matrix.
*/
constructor() {
/**
* The elements of the matrix in column-major order.
* @type {Array<Number>}
*/
this.elements = [
1, 0, 0,
0, 1, 0,
0, 0, 1
];
}
/**
* Sets the given values to this matrix. The arguments are in row-major order.
*
* @param {Number} n11 - An element of the matrix.
* @param {Number} n12 - An element of the matrix.
* @param {Number} n13 - An element of the matrix.
* @param {Number} n21 - An element of the matrix.
* @param {Number} n22 - An element of the matrix.
* @param {Number} n23 - An element of the matrix.
* @param {Number} n31 - An element of the matrix.
* @param {Number} n32 - An element of the matrix.
* @param {Number} n33 - An element of the matrix.
* @return {Matrix3} A reference to this matrix.
*/
set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
const e = this.elements;
e[ 0 ] = n11; e[ 3 ] = n12; e[ 6 ] = n13;
e[ 1 ] = n21; e[ 4 ] = n22; e[ 7 ] = n23;
e[ 2 ] = n31; e[ 5 ] = n32; e[ 8 ] = n33;
return this;
}
/**
* Copies all values from the given matrix to this matrix.
*
* @param {Matrix3} m - The matrix to copy.
* @return {Matrix3} A reference to this matrix.
*/
copy( m ) {
const e = this.elements;
const me = m.elements;
e[ 0 ] = me[ 0 ]; e[ 1 ] = me[ 1 ]; e[ 2 ] = me[ 2 ];
e[ 3 ] = me[ 3 ]; e[ 4 ] = me[ 4 ]; e[ 5 ] = me[ 5 ];
e[ 6 ] = me[ 6 ]; e[ 7 ] = me[ 7 ]; e[ 8 ] = me[ 8 ];
return this;
}
/**
* Creates a new matrix and copies all values from this matrix.
*
* @return {Matrix3} A new matrix.
*/
clone() {
return new this.constructor().copy( this );
}
/**
* Transforms this matrix to an identity matrix.
*
* @return {Matrix3} A reference to this matrix.
*/
identity() {
this.set(
1, 0, 0,
0, 1, 0,
0, 0, 1
);
return this;
}
/**
* Multiplies this matrix with the given matrix.
*
* @param {Matrix3} m - The matrix to multiply.
* @return {Matrix3} A reference to this matrix.
*/
multiply( m ) {
return this.multiplyMatrices( this, m );
}
/**
* Multiplies this matrix with the given matrix.
* So the order of the multiplication is switched compared to {@link Matrix3#multiply}.
*
* @param {Matrix3} m - The matrix to multiply.
* @return {Matrix3} A reference to this matrix.
*/
premultiply( m ) {
return this.multiplyMatrices( m, this );
}
/**
* Multiplies two given matrices and stores the result in this matrix.
*
* @param {Matrix3} a - The first matrix of the operation.
* @param {Matrix3} b - The second matrix of the operation.
* @return {Matrix3} A reference to this matrix.
*/
multiplyMatrices( a, b ) {
const ae = a.elements;
const be = b.elements;
const e = this.elements;
const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];
const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];
const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];
const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];
const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];
const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];
e[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;
e[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;
e[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;
e[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;
e[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;
e[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;
e[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;
e[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;
e[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;
return this;
}
/**
* Multiplies the given scalar with this matrix.
*
* @param {Number} s - The scalar to multiply.
* @return {Matrix3} A reference to this matrix.
*/
multiplyScalar( s ) {
const e = this.elements;
e[ 0 ] *= s; e[ 3 ] *= s; e[ 6 ] *= s;
e[ 1 ] *= s; e[ 4 ] *= s; e[ 7 ] *= s;
e[ 2 ] *= s; e[ 5 ] *= s; e[ 8 ] *= s;
return this;
}
/**
* Extracts the basis vectors and stores them to the given vectors.
*
* @param {Vector3} xAxis - The first result vector for the x-axis.
* @param {Vector3} yAxis - The second result vector for the y-axis.
* @param {Vector3} zAxis - The third result vector for the z-axis.
* @return {Matrix3} A reference to this matrix.
*/
extractBasis( xAxis, yAxis, zAxis ) {
xAxis.fromMatrix3Column( this, 0 );
yAxis.fromMatrix3Column( this, 1 );
zAxis.fromMatrix3Column( this, 2 );
return this;
}
/**
* Makes a basis from the given vectors.
*
* @param {Vector3} xAxis - The first basis vector for the x-axis.
* @param {Vector3} yAxis - The second basis vector for the y-axis.
* @param {Vector3} zAxis - The third basis vector for the z-axis.
* @return {Matrix3} A reference to this matrix.
*/
makeBasis( xAxis, yAxis, zAxis ) {
this.set(
xAxis.x, yAxis.x, zAxis.x,
xAxis.y, yAxis.y, zAxis.y,
xAxis.z, yAxis.z, zAxis.z
);
return this;
}
/**
* Creates a rotation matrix that orients an object to face towards a specified target direction.
*
* @param {Vector3} localForward - Specifies the forward direction in the local space of the object.
* @param {Vector3} targetDirection - Specifies the desired world space direction the object should look at.
* @param {Vector3} localUp - Specifies the up direction in the local space of the object.
* @return {Matrix3} A reference to this matrix.
*/
lookAt( localForward, targetDirection, localUp ) {
localRight.crossVectors( localUp, localForward ).normalize();
// orthonormal linear basis A { localRight, localUp, localForward } for the object local space
worldRight.crossVectors( WorldUp, targetDirection ).normalize();
if ( worldRight.squaredLength() === 0 ) {
// handle case when it's not possible to build a basis from targetDirection and worldUp
// slightly shift targetDirection in order to avoid collinearity
temp.copy( targetDirection ).addScalar( Number.EPSILON );
worldRight.crossVectors( WorldUp, temp ).normalize();
}
perpWorldUp.crossVectors( targetDirection, worldRight ).normalize();
// orthonormal linear basis B { worldRight, perpWorldUp, targetDirection } for the desired target orientation
m1.makeBasis( worldRight, perpWorldUp, targetDirection );
m2.makeBasis( localRight, localUp, localForward );
// construct a matrix that maps basis A to B
this.multiplyMatrices( m1, m2.transpose() );
return this;
}
/**
* Transposes this matrix.
*
* @return {Matrix3} A reference to this matrix.
*/
transpose() {
const e = this.elements;
let t;
t = e[ 1 ]; e[ 1 ] = e[ 3 ]; e[ 3 ] = t;
t = e[ 2 ]; e[ 2 ] = e[ 6 ]; e[ 6 ] = t;
t = e[ 5 ]; e[ 5 ] = e[ 7 ]; e[ 7 ] = t;
return this;
}
/**
* Computes the element index according to the given column and row.
*
* @param {Number} column - Index of the column.
* @param {Number} row - Index of the row.
* @return {Number} The index of the element at the provided row and column.
*/
getElementIndex( column, row ) {
return column * 3 + row;
}
/**
* Computes the frobenius norm. It's the squareroot of the sum of all
* squared matrix elements.
*
* @return {Number} The frobenius norm.
*/
frobeniusNorm() {
const e = this.elements;
let norm = 0;
for ( let i = 0; i < 9; i ++ ) {
norm += e[ i ] * e[ i ];
}
return Math.sqrt( norm );
}
/**
* Computes the "off-diagonal" frobenius norm. Assumes the matrix is symmetric.
*
* @return {Number} The "off-diagonal" frobenius norm.
*/
offDiagonalFrobeniusNorm() {
const e = this.elements;
let norm = 0;
for ( let i = 0; i < 3; i ++ ) {
const t = e[ this.getElementIndex( colVal[ i ], rowVal[ i ] ) ];
norm += 2 * t * t; // multiply the result by two since the matrix is symetric
}
return Math.sqrt( norm );
}
/**
* Computes the eigenvectors and eigenvalues.
*
* Reference: https://github.com/AnalyticalGraphicsInc/cesium/blob/411a1afbd36b72df64d7362de6aa934730447234/Source/Core/Matrix3.js#L1141 (Apache License 2.0)
*
* The values along the diagonal of the diagonal matrix are the eigenvalues.
* The columns of the unitary matrix are the corresponding eigenvectors.
*
* @param {Object} result - An object with unitary and diagonal properties which are matrices onto which to store the result.
* @return {Object} An object with unitary and diagonal properties which are matrices onto which to store the result.
*/
eigenDecomposition( result ) {
let count = 0;
let sweep = 0;
const maxSweeps = 10;
result.unitary.identity();
result.diagonal.copy( this );
const unitaryMatrix = result.unitary;
const diagonalMatrix = result.diagonal;
const epsilon = Number.EPSILON * diagonalMatrix.frobeniusNorm();
while ( sweep < maxSweeps && diagonalMatrix.offDiagonalFrobeniusNorm() > epsilon ) {
diagonalMatrix.shurDecomposition( m1 );
m2.copy( m1 ).transpose();
diagonalMatrix.multiply( m1 );
diagonalMatrix.premultiply( m2 );
unitaryMatrix.multiply( m1 );
if ( ++ count > 2 ) {
sweep ++;
count = 0;
}
}
return result;
}
/**
* Finds the largest off-diagonal term and then creates a matrix
* which can be used to help reduce it.
*
* @param {Matrix3} result - The result matrix.
* @return {Matrix3} The result matrix.
*/
shurDecomposition( result ) {
let maxDiagonal = 0;
let rotAxis = 1;
// find pivot (rotAxis) based on largest off-diagonal term
const e = this.elements;
for ( let i = 0; i < 3; i ++ ) {
const t = Math.abs( e[ this.getElementIndex( colVal[ i ], rowVal[ i ] ) ] );
if ( t > maxDiagonal ) {
maxDiagonal = t;
rotAxis = i;
}
}
let c = 1;
let s = 0;
const p = rowVal[ rotAxis ];
const q = colVal[ rotAxis ];
if ( Math.abs( e[ this.getElementIndex( q, p ) ] ) > Number.EPSILON ) {
const qq = e[ this.getElementIndex( q, q ) ];
const pp = e[ this.getElementIndex( p, p ) ];
const qp = e[ this.getElementIndex( q, p ) ];
const tau = ( qq - pp ) / 2 / qp;
let t;
if ( tau < 0 ) {
t = - 1 / ( - tau + Math.sqrt( 1 + tau * tau ) );
} else {
t = 1 / ( tau + Math.sqrt( 1.0 + tau * tau ) );
}
c = 1.0 / Math.sqrt( 1.0 + t * t );
s = t * c;
}
result.identity();
result.elements[ this.getElementIndex( p, p ) ] = c;
result.elements[ this.getElementIndex( q, q ) ] = c;
result.elements[ this.getElementIndex( q, p ) ] = s;
result.elements[ this.getElementIndex( p, q ) ] = - s;
return result;
}
/**
* Creates a rotation matrix from the given quaternion.
*
* @param {Quaternion} q - A quaternion representing a rotation.
* @return {Matrix3} A reference to this matrix.
*/
fromQuaternion( q ) {
const e = this.elements;
const x = q.x, y = q.y, z = q.z, w = q.w;
const x2 = x + x, y2 = y + y, z2 = z + z;
const xx = x * x2, xy = x * y2, xz = x * z2;
const yy = y * y2, yz = y * z2, zz = z * z2;
const wx = w * x2, wy = w * y2, wz = w * z2;
e[ 0 ] = 1 - ( yy + zz );
e[ 3 ] = xy - wz;
e[ 6 ] = xz + wy;
e[ 1 ] = xy + wz;
e[ 4 ] = 1 - ( xx + zz );
e[ 7 ] = yz - wx;
e[ 2 ] = xz - wy;
e[ 5 ] = yz + wx;
e[ 8 ] = 1 - ( xx + yy );
return this;
}
/**
* Sets the elements of this matrix by extracting the upper-left 3x3 portion
* from a 4x4 matrix.
*
* @param {Matrix4} m - A 4x4 matrix.
* @return {Matrix3} A reference to this matrix.
*/
fromMatrix4( m ) {
const e = this.elements;
const me = m.elements;
e[ 0 ] = me[ 0 ]; e[ 1 ] = me[ 1 ]; e[ 2 ] = me[ 2 ];
e[ 3 ] = me[ 4 ]; e[ 4 ] = me[ 5 ]; e[ 5 ] = me[ 6 ];
e[ 6 ] = me[ 8 ]; e[ 7 ] = me[ 9 ]; e[ 8 ] = me[ 10 ];
return this;
}
/**
* Sets the elements of this matrix from an array.
*
* @param {Array<Number>} array - An array.
* @param {Number} offset - An optional offset.
* @return {Matrix3} A reference to this matrix.
*/
fromArray( array, offset = 0 ) {
const e = this.elements;
for ( let i = 0; i < 9; i ++ ) {
e[ i ] = array[ i + offset ];
}
return this;
}
/**
* Copies all elements of this matrix to the given array.
*
* @param {Array<Number>} array - An array.
* @param {Number} offset - An optional offset.
* @return {Array<Number>} The array with the elements of the matrix.
*/
toArray( array, offset = 0 ) {
const e = this.elements;
array[ offset + 0 ] = e[ 0 ];
array[ offset + 1 ] = e[ 1 ];
array[ offset + 2 ] = e[ 2 ];
array[ offset + 3 ] = e[ 3 ];
array[ offset + 4 ] = e[ 4 ];
array[ offset + 5 ] = e[ 5 ];
array[ offset + 6 ] = e[ 6 ];
array[ offset + 7 ] = e[ 7 ];
array[ offset + 8 ] = e[ 8 ];
return array;
}
/**
* Returns true if the given matrix is deep equal with this matrix.
*
* @param {Matrix3} m - The matrix to test.
* @return {Boolean} The result of the equality test.
*/
equals( m ) {
const e = this.elements;
const me = m.elements;
for ( let i = 0; i < 9; i ++ ) {
if ( e[ i ] !== me[ i ] ) return false;
}
return true;
}
}
const m1 = new Matrix3();
const m2 = new Matrix3();
const matrix$1 = new Matrix3();
const vector$1 = new Vector3();
/**
* Class representing a quaternion.
*
* @author {@link https://github.com/Mugen87|Mugen87}
*/
class Quaternion {
/**
* Constructs a new quaternion with the given values.
*
* @param {Number} x - The x component.
* @param {Number} y - The y component.
* @param {Number} z - The z component.
* @param {Number} w - The w component.
*/
constructor( x = 0, y = 0, z = 0, w = 1 ) {
/**
* The x component.
* @type {Number}
*/
this.x = x;
/**
* The y component.
* @type {Number}
*/
this.y = y;
/**
* The z component.
* @type {Number}
*/
this.z = z;
/**
* The w component.
* @type {Number}
*/
this.w = w;
}
/**
* Sets the given values to this quaternion.
*
* @param {Number} x - The x component.
* @param {Number} y - The y component.
* @param {Number} z - The z component.
* @param {Number} w - The w component.
* @return {Quaternion} A reference to this quaternion.
*/
set( x, y, z, w ) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
return this;
}
/**
* Copies all values from the given quaternion to this quaternion.
*
* @param {Quaternion} q - The quaternion to copy.
* @return {Quaternion} A reference to this quaternion.
*/
copy( q ) {
this.x = q.x;
this.y = q.y;
this.z = q.z;
this.w = q.w;
return this;
}
/**
* Creates a new quaternion and copies all values from this quaternion.
*
* @return {Quaternion} A new quaternion.
*/
clone() {
return new this.constructor().copy( this );
}
/**
* Computes the inverse of this quaternion.
*
* @return {Quaternion} A reference to this quaternion.
*/
inverse() {
return this.conjugate().normalize();
}
/**
* Computes the conjugate of this quaternion.
*
* @return {Quaternion} A reference to this quaternion.
*/
conjugate() {
this.x *= - 1;
this.y *= - 1;
this.z *= - 1;
return this;
}
/**
* Computes the dot product of this and the given quaternion.
*
* @param {Quaternion} q - The given quaternion.
* @return {Quaternion} A reference to this quaternion.
*/
dot( q ) {
return ( this.x * q.x ) + ( this.y * q.y ) + ( this.z * q.z ) + ( this.w * q.w );
}
/**
* Computes the length of this quaternion.
*
* @return {Number} The length of this quaternion.
*/
length() {
return Math.sqrt( this.squaredLength() );
}
/**
* Computes the squared length of this quaternion.
*
* @return {Number} The squared length of this quaternion.
*/
squaredLength() {
return this.dot( this );
}
/**
* Normalizes this quaternion.
*
* @return {Quaternion} A reference to this quaternion.
*/
normalize() {
let l = this.length();
if ( l === 0 ) {
this.x = 0;
this.y = 0;
this.z = 0;
this.w = 1;
} else {
l = 1 / l;
this.x = this.x * l;
this.y = this.y * l;
this.z = this.z * l;
this.w = this.w * l;
}
return this;
}
/**
* Multiplies this quaternion with the given quaternion.
*
* @param {Quaternion} q - The quaternion to multiply.
* @return {Quaternion} A reference to this quaternion.
*/
multiply( q ) {
return this.multiplyQuaternions( this, q );
}
/**
* Multiplies the given quaternion with this quaternion.
* So the order of the multiplication is switched compared to {@link Quaternion#multiply}.
*
* @param {Quaternion} q - The quaternion to multiply.
* @return {Quaternion} A reference to this quaternion.
*/
premultiply( q ) {
return this.multiplyQuaternions( q, this );
}
/**
* Multiplies two given quaternions and stores the result in this quaternion.
*
* @param {Quaternion} a - The first quaternion of the operation.
* @param {Quaternion} b - The second quaternion of the operation.
* @return {Quaternion} A reference to this quaternion.
*/
multiplyQuaternions( a, b ) {
const qax = a.x, qay = a.y, qaz = a.z, qaw = a.w;
const qbx = b.x, qby = b.y, qbz = b.z, qbw = b.w;
this.x = ( qax * qbw ) + ( qaw * qbx ) + ( qay * qbz ) - ( qaz * qby );
this.y = ( qay * qbw ) + ( qaw * qby ) + ( qaz * qbx ) - ( qax * qbz );
this.z = ( qaz * qbw ) + ( qaw * qbz ) + ( qax * qby ) - ( qay * qbx );
this.w = ( qaw * qbw ) - ( qax * qbx ) - ( qay * qby ) - ( qaz * qbz );
return this;
}
/**
* Computes the shortest angle between two rotation defined by this quaternion and the given one.
*
* @param {Quaternion} q - The given quaternion.
* @return {Number} The angle in radians.
*/
angleTo( q ) {
return 2 * Math.acos( Math.abs( MathUtils.clamp( this.dot( q ), - 1, 1 ) ) );
}
/**
* Transforms this rotation defined by this quaternion towards the target rotation
* defined by the given quaternion by the given angular step. The rotation will not overshoot.
*
* @param {Quaternion} q - The target rotation.
* @param {Number} step - The maximum step in radians.
* @param {Number} tolerance - A tolerance value in radians to tweak the result
* when both rotations are considered to be equal.
* @return {Boolean} Whether the given quaternion already represents the target rotation.
*/
rotateTo( q, step, tolerance = 0.0001 ) {
const angle = this.angleTo( q );
if ( angle < tolerance ) return true;
const t = Math.min( 1, step / angle );
this.slerp( q, t );
return false;
}
/**
* Creates a quaternion that orients an object to face towards a specified target direction.
*
* @param {Vector3} localForward - Specifies the forward direction in the local space of the object.
* @param {Vector3} targetDirection - Specifies the desired world space direction the object should look at.
* @param {Vector3} localUp - Specifies the up direction in the local space of the object.
* @return {Quaternion} A reference to this quaternion.
*/
lookAt( localForward, targetDirection, localUp ) {
matrix$1.lookAt( localForward, targetDirection, localUp );
this.fromMatrix3( matrix$1 );
}
/**
* Spherically interpolates between this quaternion and the given quaternion by t.
* The parameter t is clamped to the range [0, 1].
*
* @param {Quaternion} q - The target rotation.
* @param {Number} t - The interpolation parameter.
* @return {Quaternion} A reference to this quaternion.
*/
slerp( q, t ) {
if ( t === 0 ) return this;
if ( t === 1 ) return this.copy( q );
const x = this.x, y = this.y, z = this.z, w = this.w;
let cosHalfTheta = w * q.w + x * q.x + y * q.y + z * q.z;
if ( cosHalfTheta < 0 ) {
this.w = - q.w;
this.x = - q.x;
this.y = - q.y;
this.z = - q.z;
cosHalfTheta = - cosHalfTheta;
} else {
this.copy( q );
}
if ( cosHalfTheta >= 1.0 ) {
this.w = w;
this.x = x;
this.y = y;
this.z = z;
return this;
}
const sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
if ( Math.abs( sinHalfTheta ) < 0.001 ) {
this.w = 0.5 * ( w + this.w );
this.x = 0.5 * ( x + this.x );
this.y = 0.5 * ( y + this.y );
this.z = 0.5 * ( z + this.z );
return this;
}
const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta;
const ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
this.w = ( w * ratioA ) + ( this.w * ratioB );
this.x = ( x * ratioA ) + ( this.x * ratioB );
this.y = ( y * ratioA ) + ( this.y * ratioB );
this.z = ( z * ratioA ) + ( this.z * ratioB );
return this;
}
/**
* Extracts the rotation of the given 4x4 matrix and stores it in this quaternion.
*
* @param {Matrix4} m - A 4x4 matrix.
* @return {Quaternion} A reference to this quaternion.
*/
extractRotationFromMatrix( m ) {
const e = matrix$1.elements;
const me = m.elements;
// remove scaling from the 3x3 portion
const sx = 1 / vector$1.fromMatrix4Column( m, 0 ).length();
const sy = 1 / vector$1.fromMatrix4Column( m, 1 ).length();
const sz = 1 / vector$1.fromMatrix4Column( m, 2 ).length();
e[ 0 ] = me[ 0 ] * sx;
e[ 1 ] = me[ 1 ] * sx;
e[ 2 ] = me[ 2 ] * sx;
e[ 3 ] = me[ 4 ] * sy;
e[ 4 ] = me[ 5 ] * sy;
e[ 5 ] = me[ 6 ] * sy;
e[ 6 ] = me[ 8 ] * sz;
e[ 7 ] = me[ 9 ] * sz;
e[ 8 ] = me[ 10 ] * sz;
this.fromMatrix3( matrix$1 );
return this;
}
/**
* Sets the components of this quaternion from the given euler angle (YXZ order).
*
* @param {Number} x - Rotation around x axis in radians.
* @param {Number} y - Rotation around y axis in radians.
* @param {Number} z - Rotation around z axis in radians.
* @return {Quaternion} A reference to this quaternion.
*/
fromEuler( x, y, z ) {
// from 3D Math Primer for Graphics and Game Development
// 8.7.5 Converting Euler Angles to a Quaternion
// assuming YXZ (head/pitch/bank or yaw/pitch/roll) order
const c1 = Math.cos( y / 2 );
const c2 = Math.cos( x / 2 );
const c3 = Math.cos( z / 2 );
const s1 = Math.sin( y / 2 );
const s2 = Math.sin( x / 2 );
const s3 = Math.sin( z / 2 );
this.w = c1 * c2 * c3 + s1 * s2 * s3;
this.x = c1 * s2 * c3 + s1 * c2 * s3;
this.y = s1 * c2 * c3 - c1 * s2 * s3;
this.z = c1 * c2 * s3 - s1 * s2 * c3;
return this;
}
/**
* Returns an euler angel (YXZ order) representation of this quaternion.
*
* @param {Object} euler - The resulting euler angles.
* @return {Object} The resulting euler angles.
*/
toEuler( euler ) {
// from 3D Math Primer for Graphics and Game Development
// 8.7.6 Converting a Quaternion to Euler Angles
// extract pitch
const sp = - 2 * ( this.y * this.z - this.x * this.w );
// check for gimbal lock
if ( Math.abs( sp ) > 0.9999 ) {
// looking straight up or down
euler.x = Math.PI * 0.5 * sp;
euler.y = Math.atan2( this.x * this.z + this.w * this.y, 0.5 - this.x * this.x - this.y * this.y );
euler.z = 0;
} else { //todo test
euler.x = Math.asin( sp );
euler.y = Math.atan2( this.x * this.z + this.w * this.y, 0.5 - this.x * this.x - this.y * this.y );
euler.z = Math.atan2( this.x * this.y + this.w * this.z, 0.5 - this.x * this.x - this.z * this.z );
}
return euler;
}
/**
* Sets the components of this quaternion from the given 3x3 rotation matrix.
*
* @param {Matrix3} m - The rotation matrix.
* @return {Quaternion} A reference to this quaternion.
*/
fromMatrix3( m ) {
const e = m.elements;
const m11 = e[ 0 ], m12 = e[ 3 ], m13 = e[ 6 ];
const m21 = e[ 1 ], m22 = e[ 4 ], m23 = e[ 7 ];
const m31 = e[ 2 ], m32 = e[ 5 ], m33 = e[ 8 ];
const trace = m11 + m22 + m33;
if ( trace > 0 ) {
let s = 0.5 / Math.sqrt( trace + 1.0 );
this.w = 0.25 / s;
this.x = ( m32 - m23 ) * s;
this.y = ( m13 - m31 ) * s;
this.z = ( m21 - m12 ) * s;
} else if ( ( m11 > m22 ) &&