@turbox3d/math
Version:
Large-scale graphics application math library
494 lines (493 loc) • 15.3 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Quaternion = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _MathUtils = require("../MathUtils");
var Quaternion = exports.Quaternion = /*#__PURE__*/function () {
function Quaternion() {
var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var z = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
var w = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
(0, _classCallCheck2["default"])(this, Quaternion);
this._onChangeCallback = function () {
//
};
this.isQuaternion = true;
this._x = x;
this._y = y;
this._z = z;
this._w = w;
}
return (0, _createClass2["default"])(Quaternion, [{
key: "x",
get: function get() {
return this._x;
},
set: function set(value) {
this._x = value;
this._onChangeCallback();
}
}, {
key: "y",
get: function get() {
return this._y;
},
set: function set(value) {
this._y = value;
this._onChangeCallback();
}
}, {
key: "z",
get: function get() {
return this._z;
},
set: function set(value) {
this._z = value;
this._onChangeCallback();
}
}, {
key: "w",
get: function get() {
return this._w;
},
set: function set(value) {
this._w = value;
this._onChangeCallback();
}
}, {
key: "set",
value: function set(x, y, z, w) {
this._x = x;
this._y = y;
this._z = z;
this._w = w;
this._onChangeCallback();
return this;
}
}, {
key: "clone",
value: function clone() {
return new Quaternion(this._x, this._y, this._z, this._w);
}
}, {
key: "copy",
value: function copy(quaternion) {
this._x = quaternion.x;
this._y = quaternion.y;
this._z = quaternion.z;
this._w = quaternion.w;
this._onChangeCallback();
return this;
}
}, {
key: "setFromEuler",
value: function setFromEuler(euler) {
var x = euler._x;
var y = euler._y;
var z = euler._z;
var order = euler._order;
// http://www.mathworks.com/matlabcentral/fileexchange/
// 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
// content/SpinCalc.m
var cos = Math.cos;
var sin = Math.sin;
var c1 = cos(x / 2);
var c2 = cos(y / 2);
var c3 = cos(z / 2);
var s1 = sin(x / 2);
var s2 = sin(y / 2);
var s3 = sin(z / 2);
switch (order) {
case 'XYZ':
this._x = s1 * c2 * c3 + c1 * s2 * s3;
this._y = c1 * s2 * c3 - s1 * c2 * s3;
this._z = c1 * c2 * s3 + s1 * s2 * c3;
this._w = c1 * c2 * c3 - s1 * s2 * s3;
break;
case 'YXZ':
this._x = s1 * c2 * c3 + c1 * s2 * s3;
this._y = c1 * s2 * c3 - s1 * c2 * s3;
this._z = c1 * c2 * s3 - s1 * s2 * c3;
this._w = c1 * c2 * c3 + s1 * s2 * s3;
break;
case 'ZXY':
this._x = s1 * c2 * c3 - c1 * s2 * s3;
this._y = c1 * s2 * c3 + s1 * c2 * s3;
this._z = c1 * c2 * s3 + s1 * s2 * c3;
this._w = c1 * c2 * c3 - s1 * s2 * s3;
break;
case 'ZYX':
this._x = s1 * c2 * c3 - c1 * s2 * s3;
this._y = c1 * s2 * c3 + s1 * c2 * s3;
this._z = c1 * c2 * s3 - s1 * s2 * c3;
this._w = c1 * c2 * c3 + s1 * s2 * s3;
break;
case 'YZX':
this._x = s1 * c2 * c3 + c1 * s2 * s3;
this._y = c1 * s2 * c3 + s1 * c2 * s3;
this._z = c1 * c2 * s3 - s1 * s2 * c3;
this._w = c1 * c2 * c3 - s1 * s2 * s3;
break;
case 'XZY':
this._x = s1 * c2 * c3 - c1 * s2 * s3;
this._y = c1 * s2 * c3 - s1 * c2 * s3;
this._z = c1 * c2 * s3 + s1 * s2 * c3;
this._w = c1 * c2 * c3 + s1 * s2 * s3;
break;
default:
console.warn("THREE.Quaternion: .setFromEuler() encountered an unknown order: ".concat(order));
}
this._onChangeCallback();
return this;
}
}, {
key: "setFromAxisAngle",
value: function setFromAxisAngle(axis, angle) {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
// assumes axis is normalized
var halfAngle = angle / 2;
var s = Math.sin(halfAngle);
this._x = axis.x * s;
this._y = axis.y * s;
this._z = axis.z * s;
this._w = Math.cos(halfAngle);
this._onChangeCallback();
return this;
}
}, {
key: "setFromRotationMatrix",
value: function setFromRotationMatrix(m) {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
var te = m.elements;
var m11 = te[0];
var m12 = te[4];
var m13 = te[8];
var m21 = te[1];
var m22 = te[5];
var m23 = te[9];
var m31 = te[2];
var m32 = te[6];
var m33 = te[10];
var trace = m11 + m22 + m33;
if (trace > 0) {
var 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 && m11 > m33) {
var _s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33);
this._w = (m32 - m23) / _s;
this._x = 0.25 * _s;
this._y = (m12 + m21) / _s;
this._z = (m13 + m31) / _s;
} else if (m22 > m33) {
var _s2 = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33);
this._w = (m13 - m31) / _s2;
this._x = (m12 + m21) / _s2;
this._y = 0.25 * _s2;
this._z = (m23 + m32) / _s2;
} else {
var _s3 = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22);
this._w = (m21 - m12) / _s3;
this._x = (m13 + m31) / _s3;
this._y = (m23 + m32) / _s3;
this._z = 0.25 * _s3;
}
this._onChangeCallback();
return this;
}
}, {
key: "setFromUnitVectors",
value: function setFromUnitVectors(vFrom, vTo) {
// assumes direction vectors vFrom and vTo are normalized
var EPS = 0.000001;
var r = vFrom.dot(vTo) + 1;
if (r < EPS) {
r = 0;
if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) {
this._x = -vFrom.y;
this._y = vFrom.x;
this._z = 0;
this._w = r;
} else {
this._x = 0;
this._y = -vFrom.z;
this._z = vFrom.y;
this._w = r;
}
} else {
// crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3
this._x = vFrom.y * vTo.z - vFrom.z * vTo.y;
this._y = vFrom.z * vTo.x - vFrom.x * vTo.z;
this._z = vFrom.x * vTo.y - vFrom.y * vTo.x;
this._w = r;
}
return this.normalize();
}
}, {
key: "angleTo",
value: function angleTo(q) {
return 2 * Math.acos(Math.abs(_MathUtils.MathUtils.clamp(this.dot(q), -1, 1)));
}
}, {
key: "rotateTowards",
value: function rotateTowards(q, step) {
var angle = this.angleTo(q);
if (angle === 0) return this;
var t = Math.min(1, step / angle);
this.slerp(q, t);
return this;
}
}, {
key: "identity",
value: function identity() {
return this.set(0, 0, 0, 1);
}
}, {
key: "invert",
value: function invert() {
// quaternion is assumed to have unit length
return this.conjugate();
}
}, {
key: "conjugate",
value: function conjugate() {
this._x *= -1;
this._y *= -1;
this._z *= -1;
this._onChangeCallback();
return this;
}
}, {
key: "dot",
value: function dot(v) {
return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
}
}, {
key: "lengthSq",
value: function lengthSq() {
return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
}
}, {
key: "length",
value: function length() {
return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w);
}
}, {
key: "normalize",
value: function normalize() {
var l = this.length();
if (l === 0) {
this._x = 0;
this._y = 0;
this._z = 0;
this._w = 1;
} else {
l = 1 / l;
this._x *= l;
this._y *= l;
this._z *= l;
this._w *= l;
}
this._onChangeCallback();
return this;
}
}, {
key: "multiply",
value: function multiply(q) {
return this.multiplyQuaternions(this, q);
}
}, {
key: "premultiply",
value: function premultiply(q) {
return this.multiplyQuaternions(q, this);
}
}, {
key: "multiplyQuaternions",
value: function multiplyQuaternions(a, b) {
// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
var qax = a._x;
var qay = a._y;
var qaz = a._z;
var qaw = a._w;
var qbx = b._x;
var qby = b._y;
var qbz = b._z;
var 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;
this._onChangeCallback();
return this;
}
}, {
key: "slerp",
value: function slerp(qb, t) {
if (t === 0) return this;
if (t === 1) return this.copy(qb);
var x = this._x;
var y = this._y;
var z = this._z;
var w = this._w;
// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
if (cosHalfTheta < 0) {
this._w = -qb._w;
this._x = -qb._x;
this._y = -qb._y;
this._z = -qb._z;
cosHalfTheta = -cosHalfTheta;
} else {
this.copy(qb);
}
if (cosHalfTheta >= 1.0) {
this._w = w;
this._x = x;
this._y = y;
this._z = z;
return this;
}
var sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta;
if (sqrSinHalfTheta <= Number.EPSILON) {
var s = 1 - t;
this._w = s * w + t * this._w;
this._x = s * x + t * this._x;
this._y = s * y + t * this._y;
this._z = s * z + t * this._z;
this.normalize();
this._onChangeCallback();
return this;
}
var sinHalfTheta = Math.sqrt(sqrSinHalfTheta);
var halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta);
var ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta;
var 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;
this._onChangeCallback();
return this;
}
}, {
key: "equals",
value: function equals(quaternion) {
return quaternion._x === this._x && quaternion._y === this._y && quaternion._z === this._z && quaternion._w === this._w;
}
}, {
key: "fromArray",
value: function fromArray(array) {
var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
this._x = array[offset];
this._y = array[offset + 1];
this._z = array[offset + 2];
this._w = array[offset + 3];
this._onChangeCallback();
return this;
}
}, {
key: "toArray",
value: function toArray() {
var array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
array[offset] = this._x;
array[offset + 1] = this._y;
array[offset + 2] = this._z;
array[offset + 3] = this._w;
return array;
}
}, {
key: "_onChange",
value: function _onChange(callback) {
this._onChangeCallback = callback;
return this;
}
}], [{
key: "slerp",
value: function slerp(qa, qb, qm, t) {
return qm.copy(qa).slerp(qb, t);
}
}, {
key: "slerpFlat",
value: function slerpFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t) {
// fuzz-free, array-based Quaternion SLERP operation
var x0 = src0[srcOffset0 + 0];
var y0 = src0[srcOffset0 + 1];
var z0 = src0[srcOffset0 + 2];
var w0 = src0[srcOffset0 + 3];
var x1 = src1[srcOffset1 + 0];
var y1 = src1[srcOffset1 + 1];
var z1 = src1[srcOffset1 + 2];
var w1 = src1[srcOffset1 + 3];
if (t === 0) {
dst[dstOffset + 0] = x0;
dst[dstOffset + 1] = y0;
dst[dstOffset + 2] = z0;
dst[dstOffset + 3] = w0;
return;
}
if (t === 1) {
dst[dstOffset + 0] = x1;
dst[dstOffset + 1] = y1;
dst[dstOffset + 2] = z1;
dst[dstOffset + 3] = w1;
return;
}
if (w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1) {
var s = 1 - t;
var cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1;
var dir = cos >= 0 ? 1 : -1;
var sqrSin = 1 - cos * cos;
// Skip the Slerp for tiny steps to avoid numeric problems:
if (sqrSin > Number.EPSILON) {
var sin = Math.sqrt(sqrSin);
var len = Math.atan2(sin, cos * dir);
s = Math.sin(s * len) / sin;
t = Math.sin(t * len) / sin;
}
var tDir = t * dir;
x0 = x0 * s + x1 * tDir;
y0 = y0 * s + y1 * tDir;
z0 = z0 * s + z1 * tDir;
w0 = w0 * s + w1 * tDir;
// Normalize in case we just did a lerp:
if (s === 1 - t) {
var f = 1 / Math.sqrt(x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0);
x0 *= f;
y0 *= f;
z0 *= f;
w0 *= f;
}
}
dst[dstOffset] = x0;
dst[dstOffset + 1] = y0;
dst[dstOffset + 2] = z0;
dst[dstOffset + 3] = w0;
}
}, {
key: "multiplyQuaternionsFlat",
value: function multiplyQuaternionsFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1) {
var x0 = src0[srcOffset0];
var y0 = src0[srcOffset0 + 1];
var z0 = src0[srcOffset0 + 2];
var w0 = src0[srcOffset0 + 3];
var x1 = src1[srcOffset1];
var y1 = src1[srcOffset1 + 1];
var z1 = src1[srcOffset1 + 2];
var w1 = src1[srcOffset1 + 3];
dst[dstOffset] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1;
dst[dstOffset + 1] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1;
dst[dstOffset + 2] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1;
dst[dstOffset + 3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;
return dst;
}
}]);
}();