@awayjs/core
Version:
AwayJS core classes
355 lines (354 loc) • 13.1 kB
JavaScript
import { Orientation3D } from './Orientation3D';
import { Matrix3D } from './Matrix3D';
import { Vector3D } from './Vector3D';
/**
* A Quaternion object which can be used to represent rotations.
*/
var Quaternion = /** @class */ (function () {
/**
* Creates a new Quaternion object.
* @param x The x value of the quaternion.
* @param y The y value of the quaternion.
* @param z The z value of the quaternion.
* @param w The w value of the quaternion.
*/
function Quaternion(x, y, z, w) {
if (x === void 0) { x = 0; }
if (y === void 0) { y = 0; }
if (z === void 0) { z = 0; }
if (w === void 0) { w = 1; }
/**
* The x value of the quaternion.
*/
this.x = 0;
/**
* The y value of the quaternion.
*/
this.y = 0;
/**
* The z value of the quaternion.
*/
this.z = 0;
/**
* The w value of the quaternion.
*/
this.w = 1;
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
Object.defineProperty(Quaternion.prototype, "magnitude", {
/**
* Returns the magnitude of the quaternion object.
*/
get: function () {
return Math.sqrt(this.w * this.w + this.x * this.x + this.y * this.y + this.z * this.z);
},
enumerable: false,
configurable: true
});
/**
* Fills the quaternion object with the result from a multiplication of two
* quaternion objects.
*
* @param qa The first quaternion in the multiplication.
* @param qb The second quaternion in the multiplication.
*/
Quaternion.prototype.multiply = function (qa, qb) {
var w1 = qa.w, x1 = qa.x, y1 = qa.y, z1 = qa.z;
var w2 = qb.w, x2 = qb.x, y2 = qb.y, z2 = qb.z;
this.w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2;
this.x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2;
this.y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2;
this.z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2;
};
Quaternion.prototype.multiplyVector = function (vector, target) {
if (target === void 0) { target = null; }
//target ||= new Quaternion();
if (target === null) {
target = new Quaternion();
}
var x2 = vector.x;
var y2 = vector.y;
var z2 = vector.z;
target.w = -this.x * x2 - this.y * y2 - this.z * z2;
target.x = this.w * x2 + this.y * z2 - this.z * y2;
target.y = this.w * y2 - this.x * z2 + this.z * x2;
target.z = this.w * z2 + this.x * y2 - this.y * x2;
return target;
};
/**
* Fills the quaternion object with values representing the given rotation
* around a vector.
*
* @param axis The axis around which to rotate
* @param angle The angle in radians of the rotation.
*/
Quaternion.prototype.fromAxisAngle = function (axis, angle) {
var sin_a = Math.sin(angle / 2);
var cos_a = Math.cos(angle / 2);
this.x = axis.x * sin_a;
this.y = axis.y * sin_a;
this.z = axis.z * sin_a;
this.w = cos_a;
this.normalize();
};
/**
* Spherically interpolates between two quaternions, providing an
* interpolation between rotations with constant angle change rate.
*
* @param qa The first quaternion to interpolate.
* @param qb The second quaternion to interpolate.
* @param t The interpolation weight, a value between 0 and 1.
*/
Quaternion.prototype.slerp = function (qa, qb, t) {
var w1 = qa.w, x1 = qa.x, y1 = qa.y, z1 = qa.z;
var w2 = qb.w, x2 = qb.x, y2 = qb.y, z2 = qb.z;
var dot = w1 * w2 + x1 * x2 + y1 * y2 + z1 * z2;
// shortest direction
if (dot < 0) {
dot = -dot;
w2 = -w2;
x2 = -x2;
y2 = -y2;
z2 = -z2;
}
if (dot < 0.95) {
// interpolate angle linearly
var angle = Math.acos(dot);
var s = 1 / Math.sin(angle);
var s1 = Math.sin(angle * (1 - t)) * s;
var s2 = Math.sin(angle * t) * s;
this.w = w1 * s1 + w2 * s2;
this.x = x1 * s1 + x2 * s2;
this.y = y1 * s1 + y2 * s2;
this.z = z1 * s1 + z2 * s2;
}
else {
// nearly identical angle, interpolate linearly
this.w = w1 + t * (w2 - w1);
this.x = x1 + t * (x2 - x1);
this.y = y1 + t * (y2 - y1);
this.z = z1 + t * (z2 - z1);
var len = 1.0 / Math.sqrt(this.w * this.w + this.x * this.x + this.y * this.y + this.z * this.z);
this.w *= len;
this.x *= len;
this.y *= len;
this.z *= len;
}
};
/**
* Linearly interpolates between two quaternions.
* @param qa The first quaternion to interpolate.
* @param qb The second quaternion to interpolate.
* @param t The interpolation weight, a value between 0 and 1.
*/
Quaternion.prototype.lerp = function (qa, qb, t) {
var w1 = qa.w, x1 = qa.x, y1 = qa.y, z1 = qa.z;
var w2 = qb.w, x2 = qb.x, y2 = qb.y, z2 = qb.z;
// shortest direction
if (w1 * w2 + x1 * x2 + y1 * y2 + z1 * z2 < 0) {
w2 = -w2;
x2 = -x2;
y2 = -y2;
z2 = -z2;
}
this.w = w1 + t * (w2 - w1);
this.x = x1 + t * (x2 - x1);
this.y = y1 + t * (y2 - y1);
this.z = z1 + t * (z2 - z1);
var len = 1.0 / Math.sqrt(this.w * this.w + this.x * this.x + this.y * this.y + this.z * this.z);
this.w *= len;
this.x *= len;
this.y *= len;
this.z *= len;
};
/**
* Fills the quaternion object with values representing the given euler
* rotation.
*
* @param ax The angle in radians of the rotation around the ax axis.
* @param ay The angle in radians of the rotation around the ay axis.
* @param az The angle in radians of the rotation around the az axis.
*/
Quaternion.prototype.fromEulerAngles = function (ax, ay, az) {
var halfX = ax * .5, halfY = ay * .5, halfZ = az * .5;
var cosX = Math.cos(halfX), sinX = Math.sin(halfX);
var cosY = Math.cos(halfY), sinY = Math.sin(halfY);
var cosZ = Math.cos(halfZ), sinZ = Math.sin(halfZ);
this.w = cosX * cosY * cosZ + sinX * sinY * sinZ;
this.x = sinX * cosY * cosZ - cosX * sinY * sinZ;
this.y = cosX * sinY * cosZ + sinX * cosY * sinZ;
this.z = cosX * cosY * sinZ - sinX * sinY * cosZ;
};
/**
* Fills a target Vector3D object with the Euler angles that form the
* rotation represented by this quaternion.
*
* @param target An optional Vector3D object to contain the Euler angles. If
* not provided, a new object is created.
* @return The Vector3D containing the Euler angles.
*/
Quaternion.prototype.toEulerAngles = function (target) {
if (target === void 0) { target = null; }
//target ||= new Vector3D();
if (target === null) {
target = new Vector3D();
}
target.x = Math.atan2(2 * (this.w * this.x + this.y * this.z), 1 - 2 * (this.x * this.x + this.y * this.y));
target.y = Math.asin(2 * (this.w * this.y - this.z * this.x));
target.z = Math.atan2(2 * (this.w * this.z + this.x * this.y), 1 - 2 * (this.y * this.y + this.z * this.z));
return target;
};
/**
* Normalises the quaternion object.
*/
Quaternion.prototype.normalize = function (val) {
if (val === void 0) { val = 1; }
var mag = val / Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
this.x *= mag;
this.y *= mag;
this.z *= mag;
this.w *= mag;
};
/**
* Used to trace the values of a quaternion.
*
* @return A string representation of the quaternion object.
*/
Quaternion.prototype.toString = function () {
return '{x:' + this.x + ' y:' + this.y + ' z:' + this.z + ' w:' + this.w + '}';
};
/**
* Converts the quaternion to a Matrix3D object representing an equivalent
* rotation.
*
* @param target An optional Matrix3D container to store the transformation
* in. If not provided, a new object is created.
* @return A Matrix3D object representing an equivalent rotation.
*/
Quaternion.prototype.toMatrix3D = function (target) {
if (target === void 0) { target = null; }
var xy2 = 2.0 * this.x * this.y;
var xz2 = 2.0 * this.x * this.z;
var xw2 = 2.0 * this.x * this.w;
var yz2 = 2.0 * this.y * this.z;
var yw2 = 2.0 * this.y * this.w;
var zw2 = 2.0 * this.z * this.w;
var xx = this.x * this.x;
var yy = this.y * this.y;
var zz = this.z * this.z;
var ww = this.w * this.w;
if (!target)
target = new Matrix3D();
var rawData = target._rawData;
rawData[0] = xx - yy - zz + ww;
rawData[4] = xy2 - zw2;
rawData[8] = xz2 + yw2;
rawData[12] = 0;
rawData[1] = xy2 + zw2;
rawData[5] = -xx + yy - zz + ww;
rawData[9] = yz2 - xw2;
rawData[13] = 0;
rawData[2] = xz2 - yw2;
rawData[6] = yz2 + xw2;
rawData[10] = -xx - yy + zz + ww;
rawData[14] = 0;
rawData[3] = 0.0;
rawData[7] = 0.0;
rawData[11] = 0;
rawData[15] = 1;
return target;
};
/**
* Extracts a quaternion rotation matrix out of a given Matrix3D object.
* @param matrix The Matrix3D out of which the rotation will be extracted.
*/
Quaternion.prototype.fromMatrix = function (matrix) {
var v = matrix.decompose(Orientation3D.QUATERNION)[1];
this.x = v.x;
this.y = v.y;
this.z = v.z;
this.w = v.w;
};
/**
* Converts the quaternion to a Vector.<Number> matrix representation
* of a rotation equivalent to this quaternion.
*
* @param target The Vector.<Number> to contain the raw matrix data.
* @param exclude4thRow If true, the last row will be omitted, and a 4x3
* matrix will be generated instead of a 4x4.
*/
Quaternion.prototype.toRawData = function (target, exclude4thRow) {
if (exclude4thRow === void 0) { exclude4thRow = false; }
var xy2 = 2.0 * this.x * this.y;
var xz2 = 2.0 * this.x * this.z;
var xw2 = 2.0 * this.x * this.w;
var yz2 = 2.0 * this.y * this.z;
var yw2 = 2.0 * this.y * this.w;
var zw2 = 2.0 * this.z * this.w;
var xx = this.x * this.x;
var yy = this.y * this.y;
var zz = this.z * this.z;
var ww = this.w * this.w;
target[0] = xx - yy - zz + ww;
target[1] = xy2 - zw2;
target[2] = xz2 + yw2;
target[4] = xy2 + zw2;
target[5] = -xx + yy - zz + ww;
target[6] = yz2 - xw2;
target[8] = xz2 - yw2;
target[9] = yz2 + xw2;
target[10] = -xx - yy + zz + ww;
target[3] = target[7] = target[11] = 0;
if (!exclude4thRow) {
target[12] = target[13] = target[14] = 0;
target[15] = 1;
}
};
/**
* Clones the quaternion.
* @return An exact duplicate of the current Quaternion.
*/
Quaternion.prototype.clone = function () {
return new Quaternion(this.x, this.y, this.z, this.w);
};
/**
* Rotates a point.
*
* @param vector The Vector3D object to be rotated.
* @param target An optional Vector3D object that will contain the rotated
* coordinates. If not provided, a new object will be created.
* @return A Vector3D object containing the rotated point.
*/
Quaternion.prototype.rotatePoint = function (vector, target) {
if (target === void 0) { target = null; }
var x2 = vector.x, y2 = vector.y, z2 = vector.z;
//target ||= new Vector3D();
if (target === null) {
target = new Vector3D();
}
// p*q'
var w1 = -this.x * x2 - this.y * y2 - this.z * z2;
var x1 = this.w * x2 + this.y * z2 - this.z * y2;
var y1 = this.w * y2 - this.x * z2 + this.z * x2;
var z1 = this.w * z2 + this.x * y2 - this.y * x2;
target.x = -w1 * this.x + x1 * this.w - y1 * this.z + z1 * this.y;
target.y = -w1 * this.y + x1 * this.z + y1 * this.w - z1 * this.x;
target.z = -w1 * this.z - x1 * this.y + y1 * this.x + z1 * this.w;
return target;
};
/**
* Copies the data from a quaternion into this instance.
* @param q The quaternion to copy from.
*/
Quaternion.prototype.copyFrom = function (q) {
this.x = q.x;
this.y = q.y;
this.z = q.z;
this.w = q.w;
};
return Quaternion;
}());
export { Quaternion };