@openhps/core
Version:
Open Hybrid Positioning System - Core component
335 lines (319 loc) • 9.51 kB
JavaScript
import { Quaternion } from './Quaternion.js';
import { Matrix4 } from './Matrix4.js';
import { clamp } from './MathUtils.js';
const _matrix = /*@__PURE__*/new Matrix4();
const _quaternion = /*@__PURE__*/new Quaternion();
/**
* A class representing Euler angles.
*
* Euler angles describe a rotational transformation by rotating an object on
* its various axes in specified amounts per axis, and a specified axis
* order.
*
* Iterating through an instance will yield its components (x, y, z,
* order) in the corresponding order.
*
* ```js
* const a = new THREE.Euler( 0, 1, 1.57, 'XYZ' );
* const b = new THREE.Vector3( 1, 0, 1 );
* b.applyEuler(a);
* ```
*/
class Euler {
/**
* Constructs a new euler instance.
*
* @param {number} [x=0] - The angle of the x axis in radians.
* @param {number} [y=0] - The angle of the y axis in radians.
* @param {number} [z=0] - The angle of the z axis in radians.
* @param {string} [order=Euler.DEFAULT_ORDER] - A string representing the order that the rotations are applied.
*/
constructor(x = 0, y = 0, z = 0, order = Euler.DEFAULT_ORDER) {
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isEuler = true;
this._x = x;
this._y = y;
this._z = z;
this._order = order;
}
/**
* The angle of the x axis in radians.
*
* @type {number}
* @default 0
*/
get x() {
return this._x;
}
set x(value) {
this._x = value;
this._onChangeCallback();
}
/**
* The angle of the y axis in radians.
*
* @type {number}
* @default 0
*/
get y() {
return this._y;
}
set y(value) {
this._y = value;
this._onChangeCallback();
}
/**
* The angle of the z axis in radians.
*
* @type {number}
* @default 0
*/
get z() {
return this._z;
}
set z(value) {
this._z = value;
this._onChangeCallback();
}
/**
* A string representing the order that the rotations are applied.
*
* @type {string}
* @default 'XYZ'
*/
get order() {
return this._order;
}
set order(value) {
this._order = value;
this._onChangeCallback();
}
/**
* Sets the Euler components.
*
* @param {number} x - The angle of the x axis in radians.
* @param {number} y - The angle of the y axis in radians.
* @param {number} z - The angle of the z axis in radians.
* @param {string} [order] - A string representing the order that the rotations are applied.
* @return {Euler} A reference to this Euler instance.
*/
set(x, y, z, order = this._order) {
this._x = x;
this._y = y;
this._z = z;
this._order = order;
this._onChangeCallback();
return this;
}
/**
* Returns a new Euler instance with copied values from this instance.
*
* @return {Euler} A clone of this instance.
*/
clone() {
return new this.constructor(this._x, this._y, this._z, this._order);
}
/**
* Copies the values of the given Euler instance to this instance.
*
* @param {Euler} euler - The Euler instance to copy.
* @return {Euler} A reference to this Euler instance.
*/
copy(euler) {
this._x = euler._x;
this._y = euler._y;
this._z = euler._z;
this._order = euler._order;
this._onChangeCallback();
return this;
}
/**
* Sets the angles of this Euler instance from a pure rotation matrix.
*
* @param {Matrix4} m - A 4x4 matrix of which the upper 3x3 of matrix is a pure rotation matrix (i.e. unscaled).
* @param {string} [order] - A string representing the order that the rotations are applied.
* @param {boolean} [update=true] - Whether the internal `onChange` callback should be executed or not.
* @return {Euler} A reference to this Euler instance.
*/
setFromRotationMatrix(m, order = this._order, update = true) {
const te = m.elements;
const m11 = te[0],
m12 = te[4],
m13 = te[8];
const m21 = te[1],
m22 = te[5],
m23 = te[9];
const m31 = te[2],
m32 = te[6],
m33 = te[10];
switch (order) {
case 'XYZ':
this._y = Math.asin(clamp(m13, -1, 1));
if (Math.abs(m13) < 0.9999999) {
this._x = Math.atan2(-m23, m33);
this._z = Math.atan2(-m12, m11);
} else {
this._x = Math.atan2(m32, m22);
this._z = 0;
}
break;
case 'YXZ':
this._x = Math.asin(-clamp(m23, -1, 1));
if (Math.abs(m23) < 0.9999999) {
this._y = Math.atan2(m13, m33);
this._z = Math.atan2(m21, m22);
} else {
this._y = Math.atan2(-m31, m11);
this._z = 0;
}
break;
case 'ZXY':
this._x = Math.asin(clamp(m32, -1, 1));
if (Math.abs(m32) < 0.9999999) {
this._y = Math.atan2(-m31, m33);
this._z = Math.atan2(-m12, m22);
} else {
this._y = 0;
this._z = Math.atan2(m21, m11);
}
break;
case 'ZYX':
this._y = Math.asin(-clamp(m31, -1, 1));
if (Math.abs(m31) < 0.9999999) {
this._x = Math.atan2(m32, m33);
this._z = Math.atan2(m21, m11);
} else {
this._x = 0;
this._z = Math.atan2(-m12, m22);
}
break;
case 'YZX':
this._z = Math.asin(clamp(m21, -1, 1));
if (Math.abs(m21) < 0.9999999) {
this._x = Math.atan2(-m23, m22);
this._y = Math.atan2(-m31, m11);
} else {
this._x = 0;
this._y = Math.atan2(m13, m33);
}
break;
case 'XZY':
this._z = Math.asin(-clamp(m12, -1, 1));
if (Math.abs(m12) < 0.9999999) {
this._x = Math.atan2(m32, m22);
this._y = Math.atan2(m13, m11);
} else {
this._x = Math.atan2(-m23, m33);
this._y = 0;
}
break;
default:
console.warn('THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order);
}
this._order = order;
if (update === true) this._onChangeCallback();
return this;
}
/**
* Sets the angles of this Euler instance from a normalized quaternion.
*
* @param {Quaternion} q - A normalized Quaternion.
* @param {string} [order] - A string representing the order that the rotations are applied.
* @param {boolean} [update=true] - Whether the internal `onChange` callback should be executed or not.
* @return {Euler} A reference to this Euler instance.
*/
setFromQuaternion(q, order, update) {
_matrix.makeRotationFromQuaternion(q);
return this.setFromRotationMatrix(_matrix, order, update);
}
/**
* Sets the angles of this Euler instance from the given vector.
*
* @param {Vector3} v - The vector.
* @param {string} [order] - A string representing the order that the rotations are applied.
* @return {Euler} A reference to this Euler instance.
*/
setFromVector3(v, order = this._order) {
return this.set(v.x, v.y, v.z, order);
}
/**
* Resets the euler angle with a new order by creating a quaternion from this
* euler angle and then setting this euler angle with the quaternion and the
* new order.
*
* Warning: This discards revolution information.
*
* @param {string} [newOrder] - A string representing the new order that the rotations are applied.
* @return {Euler} A reference to this Euler instance.
*/
reorder(newOrder) {
_quaternion.setFromEuler(this);
return this.setFromQuaternion(_quaternion, newOrder);
}
/**
* Returns `true` if this Euler instance is equal with the given one.
*
* @param {Euler} euler - The Euler instance to test for equality.
* @return {boolean} Whether this Euler instance is equal with the given one.
*/
equals(euler) {
return euler._x === this._x && euler._y === this._y && euler._z === this._z && euler._order === this._order;
}
/**
* Sets this Euler instance's components to values from the given array. The first three
* entries of the array are assign to the x,y and z components. An optional fourth entry
* defines the Euler order.
*
* @param {Array<number,number,number,?string>} array - An array holding the Euler component values.
* @return {Euler} A reference to this Euler instance.
*/
fromArray(array) {
this._x = array[0];
this._y = array[1];
this._z = array[2];
if (array[3] !== undefined) this._order = array[3];
this._onChangeCallback();
return this;
}
/**
* Writes the components of this Euler instance to the given array. If no array is provided,
* the method returns a new instance.
*
* @param {Array<number,number,number,string>} [array=[]] - The target array holding the Euler components.
* @param {number} [offset=0] - Index of the first element in the array.
* @return {Array<number,number,number,string>} The Euler components.
*/
toArray(array = [], offset = 0) {
array[offset] = this._x;
array[offset + 1] = this._y;
array[offset + 2] = this._z;
array[offset + 3] = this._order;
return array;
}
_onChange(callback) {
this._onChangeCallback = callback;
return this;
}
_onChangeCallback() {}
*[Symbol.iterator]() {
yield this._x;
yield this._y;
yield this._z;
yield this._order;
}
}
/**
* The default Euler angle order.
*
* @static
* @type {string}
* @default 'XYZ'
*/
Euler.DEFAULT_ORDER = 'XYZ';
export { Euler };