[ReactNative] decompose transform matrix
This commit is contained in:
parent
6a699302c8
commit
89e26e92b6
|
@ -12,6 +12,7 @@
|
|||
'use strict';
|
||||
|
||||
var MatrixMath = require('MatrixMath');
|
||||
var Platform = require('Platform');
|
||||
var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
|
||||
var invariant = require('invariant');
|
||||
|
||||
|
@ -36,8 +37,9 @@ function precomputeStyle(style: ?Object): ?Object {
|
|||
* Generate a transform matrix based on the provided transforms, and use that
|
||||
* within the style object instead.
|
||||
*
|
||||
* This allows us to provide an API that is similar to CSS and to have a
|
||||
* universal, singular interface to native code.
|
||||
* This allows us to provide an API that is similar to CSS, where transforms may
|
||||
* be applied in an arbitrary order, and yet have a universal, singular
|
||||
* interface to native code.
|
||||
*/
|
||||
function _precomputeTransforms(style: Object): Object {
|
||||
var {transform} = style;
|
||||
|
@ -80,6 +82,17 @@ function _precomputeTransforms(style: Object): Object {
|
|||
}
|
||||
});
|
||||
|
||||
// Android does not support the direct application of a transform matrix to
|
||||
// a view, so we need to decompose the result matrix into transforms that can
|
||||
// get applied in the specific order of (1) translate (2) scale (3) rotate.
|
||||
// Once we can directly apply a matrix, we can remove this decomposition.
|
||||
if (Platform.OS === 'android') {
|
||||
return {
|
||||
...style,
|
||||
transformMatrix: result,
|
||||
decomposedMatrix: MatrixMath.decomposeMatrix(result),
|
||||
};
|
||||
}
|
||||
return {
|
||||
...style,
|
||||
transformMatrix: result,
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
/**
|
||||
* Memory conservative (mutative) matrix math utilities. Uses "command"
|
||||
* matrices, which are reusable.
|
||||
|
@ -74,17 +76,18 @@ var MatrixMath = {
|
|||
matrixCommand[10] = factor;
|
||||
},
|
||||
|
||||
reuseRotateYCommand: function(matrixCommand, amount) {
|
||||
matrixCommand[0] = Math.cos(amount);
|
||||
matrixCommand[2] = Math.sin(amount);
|
||||
matrixCommand[8] = Math.sin(-amount);
|
||||
matrixCommand[10] = Math.cos(amount);
|
||||
reuseRotateXCommand: function(matrixCommand, radians) {
|
||||
matrixCommand[5] = Math.cos(radians);
|
||||
matrixCommand[6] = Math.sin(radians);
|
||||
matrixCommand[9] = -Math.sin(radians);
|
||||
matrixCommand[10] = Math.cos(radians);
|
||||
},
|
||||
|
||||
createRotateZ: function(radians) {
|
||||
var mat = MatrixMath.createIdentityMatrix();
|
||||
MatrixMath.reuseRotateZCommand(mat, radians);
|
||||
return mat;
|
||||
reuseRotateYCommand: function(matrixCommand, amount) {
|
||||
matrixCommand[0] = Math.cos(amount);
|
||||
matrixCommand[2] = -Math.sin(amount);
|
||||
matrixCommand[8] = Math.sin(amount);
|
||||
matrixCommand[10] = Math.cos(amount);
|
||||
},
|
||||
|
||||
// http://www.w3.org/TR/css3-transforms/#recomposing-to-a-2d-matrix
|
||||
|
@ -95,6 +98,12 @@ var MatrixMath = {
|
|||
matrixCommand[5] = Math.cos(radians);
|
||||
},
|
||||
|
||||
createRotateZ: function(radians) {
|
||||
var mat = MatrixMath.createIdentityMatrix();
|
||||
MatrixMath.reuseRotateZCommand(mat, radians);
|
||||
return mat;
|
||||
},
|
||||
|
||||
multiplyInto: function(out, a, b) {
|
||||
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
|
||||
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
|
||||
|
@ -124,7 +133,398 @@ var MatrixMath = {
|
|||
out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
|
||||
out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
|
||||
out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
|
||||
}
|
||||
},
|
||||
|
||||
determinant(matrix: Array<number>): number {
|
||||
var [
|
||||
m00, m01, m02, m03,
|
||||
m10, m11, m12, m13,
|
||||
m20, m21, m22, m23,
|
||||
m30, m31, m32, m33
|
||||
] = matrix;
|
||||
return (
|
||||
m03 * m12 * m21 * m30 - m02 * m13 * m21 * m30 -
|
||||
m03 * m11 * m22 * m30 + m01 * m13 * m22 * m30 +
|
||||
m02 * m11 * m23 * m30 - m01 * m12 * m23 * m30 -
|
||||
m03 * m12 * m20 * m31 + m02 * m13 * m20 * m31 +
|
||||
m03 * m10 * m22 * m31 - m00 * m13 * m22 * m31 -
|
||||
m02 * m10 * m23 * m31 + m00 * m12 * m23 * m31 +
|
||||
m03 * m11 * m20 * m32 - m01 * m13 * m20 * m32 -
|
||||
m03 * m10 * m21 * m32 + m00 * m13 * m21 * m32 +
|
||||
m01 * m10 * m23 * m32 - m00 * m11 * m23 * m32 -
|
||||
m02 * m11 * m20 * m33 + m01 * m12 * m20 * m33 +
|
||||
m02 * m10 * m21 * m33 - m00 * m12 * m21 * m33 -
|
||||
m01 * m10 * m22 * m33 + m00 * m11 * m22 * m33
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Inverse of a matrix. Multiplying by the inverse is used in matrix math
|
||||
* instead of division.
|
||||
*
|
||||
* Formula from:
|
||||
* http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
|
||||
*/
|
||||
inverse(matrix: Array<number>): Array<number> {
|
||||
var det = MatrixMath.determinant(matrix);
|
||||
if (!det) {
|
||||
return matrix;
|
||||
}
|
||||
var [
|
||||
m00, m01, m02, m03,
|
||||
m10, m11, m12, m13,
|
||||
m20, m21, m22, m23,
|
||||
m30, m31, m32, m33
|
||||
] = matrix;
|
||||
return [
|
||||
(m12*m23*m31 - m13*m22*m31 + m13*m21*m32 - m11*m23*m32 - m12*m21*m33 + m11*m22*m33) / det,
|
||||
(m03*m22*m31 - m02*m23*m31 - m03*m21*m32 + m01*m23*m32 + m02*m21*m33 - m01*m22*m33) / det,
|
||||
(m02*m13*m31 - m03*m12*m31 + m03*m11*m32 - m01*m13*m32 - m02*m11*m33 + m01*m12*m33) / det,
|
||||
(m03*m12*m21 - m02*m13*m21 - m03*m11*m22 + m01*m13*m22 + m02*m11*m23 - m01*m12*m23) / det,
|
||||
(m13*m22*m30 - m12*m23*m30 - m13*m20*m32 + m10*m23*m32 + m12*m20*m33 - m10*m22*m33) / det,
|
||||
(m02*m23*m30 - m03*m22*m30 + m03*m20*m32 - m00*m23*m32 - m02*m20*m33 + m00*m22*m33) / det,
|
||||
(m03*m12*m30 - m02*m13*m30 - m03*m10*m32 + m00*m13*m32 + m02*m10*m33 - m00*m12*m33) / det,
|
||||
(m02*m13*m20 - m03*m12*m20 + m03*m10*m22 - m00*m13*m22 - m02*m10*m23 + m00*m12*m23) / det,
|
||||
(m11*m23*m30 - m13*m21*m30 + m13*m20*m31 - m10*m23*m31 - m11*m20*m33 + m10*m21*m33) / det,
|
||||
(m03*m21*m30 - m01*m23*m30 - m03*m20*m31 + m00*m23*m31 + m01*m20*m33 - m00*m21*m33) / det,
|
||||
(m01*m13*m30 - m03*m11*m30 + m03*m10*m31 - m00*m13*m31 - m01*m10*m33 + m00*m11*m33) / det,
|
||||
(m03*m11*m20 - m01*m13*m20 - m03*m10*m21 + m00*m13*m21 + m01*m10*m23 - m00*m11*m23) / det,
|
||||
(m12*m21*m30 - m11*m22*m30 - m12*m20*m31 + m10*m22*m31 + m11*m20*m32 - m10*m21*m32) / det,
|
||||
(m01*m22*m30 - m02*m21*m30 + m02*m20*m31 - m00*m22*m31 - m01*m20*m32 + m00*m21*m32) / det,
|
||||
(m02*m11*m30 - m01*m12*m30 - m02*m10*m31 + m00*m12*m31 + m01*m10*m32 - m00*m11*m32) / det,
|
||||
(m01*m12*m20 - m02*m11*m20 + m02*m10*m21 - m00*m12*m21 - m01*m10*m22 + m00*m11*m22) / det
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* Turns columns into rows and rows into columns.
|
||||
*/
|
||||
transpose(m: Array<number>): Array<number> {
|
||||
return [
|
||||
m[0], m[4], m[8], m[12],
|
||||
m[1], m[5], m[9], m[13],
|
||||
m[2], m[6], m[10], m[14],
|
||||
m[3], m[7], m[11], m[15]
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* Based on: http://tog.acm.org/resources/GraphicsGems/gemsii/unmatrix.c
|
||||
*/
|
||||
multiplyVectorByMatrix(
|
||||
v: Array<number>,
|
||||
m: Array<number>
|
||||
): Array<number> {
|
||||
var [vx, vy, vz, vw] = v;
|
||||
return [
|
||||
vx * m[0] + vy * m[4] + vz * m[8] + vw * m[12],
|
||||
vx * m[1] + vy * m[5] + vz * m[9] + vw * m[13],
|
||||
vx * m[2] + vy * m[6] + vz * m[10] + vw * m[14],
|
||||
vx * m[3] + vy * m[7] + vz * m[11] + vw * m[15]
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* From: https://code.google.com/p/webgl-mjs/source/browse/mjs.js
|
||||
*/
|
||||
v3Length(a: Array<number>): number {
|
||||
return Math.sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Based on: https://code.google.com/p/webgl-mjs/source/browse/mjs.js
|
||||
*/
|
||||
v3Normalize(
|
||||
vector: Array<number>,
|
||||
v3Length: number
|
||||
): Array<number> {
|
||||
var im = 1 / (v3Length || MatrixMath.v3Length(vector));
|
||||
return [
|
||||
vector[0] * im,
|
||||
vector[1] * im,
|
||||
vector[2] * im
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* The dot product of a and b, two 3-element vectors.
|
||||
* From: https://code.google.com/p/webgl-mjs/source/browse/mjs.js
|
||||
*/
|
||||
v3Dot(a, b) {
|
||||
return a[0] * b[0] +
|
||||
a[1] * b[1] +
|
||||
a[2] * b[2];
|
||||
},
|
||||
|
||||
/**
|
||||
* From:
|
||||
* http://www.opensource.apple.com/source/WebCore/WebCore-514/platform/graphics/transforms/TransformationMatrix.cpp
|
||||
*/
|
||||
v3Combine(
|
||||
a: Array<number>,
|
||||
b: Array<number>,
|
||||
aScale: number,
|
||||
bScale: number
|
||||
): Array<number> {
|
||||
return [
|
||||
aScale * a[0] + bScale * b[0],
|
||||
aScale * a[1] + bScale * b[1],
|
||||
aScale * a[2] + bScale * b[2]
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* From:
|
||||
* http://www.opensource.apple.com/source/WebCore/WebCore-514/platform/graphics/transforms/TransformationMatrix.cpp
|
||||
*/
|
||||
v3Cross(a: Array<number>, b: Array<number>): Array<number> {
|
||||
return [
|
||||
a[1] * b[2] - a[2] * b[1],
|
||||
a[2] * b[0] - a[0] * b[2],
|
||||
a[0] * b[1] - a[1] * b[0]
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* Based on:
|
||||
* http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
|
||||
* and:
|
||||
* http://quat.zachbennett.com/
|
||||
*
|
||||
* Note that this rounds degrees to the thousandth of a degree, due to
|
||||
* floating point errors in the creation of the quaternion.
|
||||
*
|
||||
* Also note that this expects the qw value to be last, not first.
|
||||
*
|
||||
* Also, when researching this, remember that:
|
||||
* yaw === heading === z-axis
|
||||
* pitch === elevation/attitude === y-axis
|
||||
* roll === bank === x-axis
|
||||
*/
|
||||
quaternionToDegreesXYZ(q: Array<number>, matrix, row): Array<number> {
|
||||
var [qx, qy, qz, qw] = q;
|
||||
var qw2 = qw * qw;
|
||||
var qx2 = qx * qx;
|
||||
var qy2 = qy * qy;
|
||||
var qz2 = qz * qz;
|
||||
var test = qx * qy + qz * qw;
|
||||
var unit = qw2 + qx2 + qy2 + qz2;
|
||||
var conv = 180 / Math.PI;
|
||||
|
||||
if (test > 0.49999 * unit) {
|
||||
return [0, 2 * Math.atan2(qx, qw) * conv, 90];
|
||||
}
|
||||
if (test < -0.49999 * unit) {
|
||||
return [0, -2 * Math.atan2(qx, qw) * conv, -90];
|
||||
}
|
||||
|
||||
return [
|
||||
MatrixMath.roundTo3Places(
|
||||
Math.atan2(2*qx*qw-2*qy*qz,1-2*qx2-2*qz2) * conv
|
||||
),
|
||||
MatrixMath.roundTo3Places(
|
||||
Math.atan2(2*qy*qw-2*qx*qz,1-2*qy2-2*qz2) * conv
|
||||
),
|
||||
MatrixMath.roundTo3Places(
|
||||
Math.asin(2*qx*qy+2*qz*qw) * conv
|
||||
)
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* Based on:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
|
||||
*/
|
||||
roundTo3Places(n: number): number {
|
||||
var arr = n.toString().split('e');
|
||||
return Math.round(arr[0] + 'e' + (arr[1] ? (+arr[1] - 3) : 3)) * 0.001;
|
||||
},
|
||||
|
||||
/**
|
||||
* Decompose a matrix into separate transform values, for use on platforms
|
||||
* where applying a precomposed matrix is not possible, and transforms are
|
||||
* applied in an inflexible ordering (e.g. Android).
|
||||
*
|
||||
* Implementation based on
|
||||
* http://www.w3.org/TR/css3-transforms/#decomposing-a-2d-matrix
|
||||
* http://www.w3.org/TR/css3-transforms/#decomposing-a-3d-matrix
|
||||
* which was based on
|
||||
* http://tog.acm.org/resources/GraphicsGems/gemsii/unmatrix.c
|
||||
*/
|
||||
decomposeMatrix(transformMatrix: Array<number>): ?Object {
|
||||
|
||||
invariant(
|
||||
transformMatrix.length === 16,
|
||||
'Matrix decomposition needs a list of 3d matrix values, received %s',
|
||||
transformMatrix
|
||||
);
|
||||
|
||||
// output values
|
||||
var perspective = [];
|
||||
var quaternion = [];
|
||||
var scale = [];
|
||||
var skew = [];
|
||||
var translation = [];
|
||||
|
||||
// create normalized, 2d array matrix
|
||||
// and normalized 1d array perspectiveMatrix with redefined 4th column
|
||||
if (!transformMatrix[15]) {
|
||||
return;
|
||||
}
|
||||
var matrix = [];
|
||||
var perspectiveMatrix = [];
|
||||
for (var i = 0; i < 4; i++) {
|
||||
matrix.push([]);
|
||||
for (var j = 0; j < 4; j++) {
|
||||
var value = transformMatrix[(i * 4) + j] / transformMatrix[15];
|
||||
matrix[i].push(value);
|
||||
perspectiveMatrix.push(j === 3 ? 0 : value);
|
||||
}
|
||||
}
|
||||
perspectiveMatrix[15] = 1;
|
||||
|
||||
// test for singularity of upper 3x3 part of the perspective matrix
|
||||
if (!MatrixMath.determinant(perspectiveMatrix)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// isolate perspective
|
||||
if (matrix[0][3] !== 0 || matrix[1][3] !== 0 || matrix[2][3] !== 0) {
|
||||
// rightHandSide is the right hand side of the equation.
|
||||
// rightHandSide is a vector, or point in 3d space relative to the origin.
|
||||
var rightHandSide = [
|
||||
matrix[0][3],
|
||||
matrix[1][3],
|
||||
matrix[2][3],
|
||||
matrix[3][3]
|
||||
];
|
||||
|
||||
// Solve the equation by inverting perspectiveMatrix and multiplying
|
||||
// rightHandSide by the inverse.
|
||||
var inversePerspectiveMatrix = MatrixMath.inverse3x3(
|
||||
perspectiveMatrix
|
||||
);
|
||||
var transposedInversePerspectiveMatrix = MatrixMath.transpose4x4(
|
||||
inversePerspectiveMatrix
|
||||
);
|
||||
var perspective = MatrixMath.multiplyVectorByMatrix(
|
||||
rightHandSide,
|
||||
transposedInversePerspectiveMatrix
|
||||
);
|
||||
} else {
|
||||
// no perspective
|
||||
perspective[0] = perspective[1] = perspective[2] = 0;
|
||||
perspective[3] = 1;
|
||||
}
|
||||
|
||||
// translation is simple
|
||||
for (var i = 0; i < 3; i++) {
|
||||
translation[i] = matrix[3][i];
|
||||
}
|
||||
|
||||
// Now get scale and shear.
|
||||
// 'row' is a 3 element array of 3 component vectors
|
||||
var row = [];
|
||||
for (i = 0; i < 3; i++) {
|
||||
row[i] = [
|
||||
matrix[i][0],
|
||||
matrix[i][1],
|
||||
matrix[i][2]
|
||||
];
|
||||
}
|
||||
|
||||
// Compute X scale factor and normalize first row.
|
||||
scale[0] = MatrixMath.v3Length(row[0]);
|
||||
row[0] = MatrixMath.v3Normalize(row[0], scale[0]);
|
||||
|
||||
// Compute XY shear factor and make 2nd row orthogonal to 1st.
|
||||
skew[0] = MatrixMath.v3Dot(row[0], row[1]);
|
||||
row[1] = MatrixMath.v3Combine(row[1], row[0], 1.0, -skew[0]);
|
||||
|
||||
// Compute XY shear factor and make 2nd row orthogonal to 1st.
|
||||
skew[0] = MatrixMath.v3Dot(row[0], row[1]);
|
||||
row[1] = MatrixMath.v3Combine(row[1], row[0], 1.0, -skew[0]);
|
||||
|
||||
// Now, compute Y scale and normalize 2nd row.
|
||||
scale[1] = MatrixMath.v3Length(row[1]);
|
||||
row[1] = MatrixMath.v3Normalize(row[1], scale[1]);
|
||||
skew[0] /= scale[1];
|
||||
|
||||
// Compute XZ and YZ shears, orthogonalize 3rd row
|
||||
skew[1] = MatrixMath.v3Dot(row[0], row[2]);
|
||||
row[2] = MatrixMath.v3Combine(row[2], row[0], 1.0, -skew[1]);
|
||||
skew[2] = MatrixMath.v3Dot(row[1], row[2]);
|
||||
row[2] = MatrixMath.v3Combine(row[2], row[1], 1.0, -skew[2]);
|
||||
|
||||
// Next, get Z scale and normalize 3rd row.
|
||||
scale[2] = MatrixMath.v3Length(row[2]);
|
||||
row[2] = MatrixMath.v3Normalize(row[2], scale[2]);
|
||||
skew[1] /= scale[2];
|
||||
skew[2] /= scale[2];
|
||||
|
||||
// At this point, the matrix (in rows) is orthonormal.
|
||||
// Check for a coordinate system flip. If the determinant
|
||||
// is -1, then negate the matrix and the scaling factors.
|
||||
var pdum3 = MatrixMath.v3Cross(row[1], row[2]);
|
||||
if (MatrixMath.v3Dot(row[0], pdum3) < 0) {
|
||||
for (i = 0; i < 3; i++) {
|
||||
scale[i] *= -1;
|
||||
row[i][0] *= -1;
|
||||
row[i][1] *= -1;
|
||||
row[i][2] *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Now, get the rotations out
|
||||
quaternion[0] =
|
||||
0.5 * Math.sqrt(Math.max(1 + row[0][0] - row[1][1] - row[2][2], 0));
|
||||
quaternion[1] =
|
||||
0.5 * Math.sqrt(Math.max(1 - row[0][0] + row[1][1] - row[2][2], 0));
|
||||
quaternion[2] =
|
||||
0.5 * Math.sqrt(Math.max(1 - row[0][0] - row[1][1] + row[2][2], 0));
|
||||
quaternion[3] =
|
||||
0.5 * Math.sqrt(Math.max(1 + row[0][0] + row[1][1] + row[2][2], 0));
|
||||
|
||||
if (row[2][1] > row[1][2]) {
|
||||
quaternion[0] = -quaternion[0];
|
||||
}
|
||||
if (row[0][2] > row[2][0]) {
|
||||
quaternion[1] = -quaternion[1];
|
||||
}
|
||||
if (row[1][0] > row[0][1]) {
|
||||
quaternion[2] = -quaternion[2];
|
||||
}
|
||||
|
||||
// correct for occasional, weird Euler synonyms for 2d rotation
|
||||
var rotationDegrees;
|
||||
if (
|
||||
quaternion[0] < 0.001 && quaternion[0] >= 0 &&
|
||||
quaternion[1] < 0.001 && quaternion[1] >= 0
|
||||
) {
|
||||
// this is a 2d rotation on the z-axis
|
||||
rotationDegrees = [0, 0, MatrixMath.roundTo3Places(
|
||||
Math.atan2(row[0][1], row[0][0]) * 180 / Math.PI
|
||||
)];
|
||||
} else {
|
||||
rotationDegrees = MatrixMath.quaternionToDegreesXYZ(quaternion, matrix, row);
|
||||
}
|
||||
|
||||
// expose both base data and convenience names
|
||||
return {
|
||||
rotationDegrees,
|
||||
perspective,
|
||||
quaternion,
|
||||
scale,
|
||||
skew,
|
||||
translation,
|
||||
|
||||
rotate: rotationDegrees[2],
|
||||
scaleX: scale[0],
|
||||
scaleY: scale[1],
|
||||
translateX: translation[0],
|
||||
translateY: translation[1],
|
||||
};
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
jest.dontMock('MatrixMath');
|
||||
jest.dontMock('invariant');
|
||||
|
||||
var MatrixMath = require('MatrixMath');
|
||||
|
||||
function degreesToRadians(degrees) {
|
||||
return degrees * Math.PI / 180;
|
||||
}
|
||||
|
||||
describe('MatrixMath', () => {
|
||||
|
||||
it('decomposes a 4x4 matrix to produce accurate Z-axis angles', () => {
|
||||
expect(MatrixMath.decomposeMatrix([
|
||||
1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1
|
||||
]).rotationDegrees).toEqual([0, 0, 0]);
|
||||
|
||||
[30, 45, 60, 75, 90, 100, 115, 120, 133, 167].forEach(angle => {
|
||||
var mat = MatrixMath.createRotateZ(degreesToRadians(angle));
|
||||
expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
|
||||
.toEqual([0, 0, angle]);
|
||||
|
||||
mat = MatrixMath.createRotateZ(degreesToRadians(-angle));
|
||||
expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
|
||||
.toEqual([0, 0, -angle]);
|
||||
});
|
||||
|
||||
expect(MatrixMath.decomposeMatrix(
|
||||
MatrixMath.createRotateZ(degreesToRadians(180))
|
||||
).rotationDegrees).toEqual([0, 0, 180]);
|
||||
|
||||
// all values are between 0 and 180;
|
||||
// change of sign and direction in the third and fourth quadrant
|
||||
expect(MatrixMath.decomposeMatrix(
|
||||
MatrixMath.createRotateZ(degreesToRadians(222))
|
||||
).rotationDegrees).toEqual([0, 0, -138]);
|
||||
|
||||
expect(MatrixMath.decomposeMatrix(
|
||||
MatrixMath.createRotateZ(degreesToRadians(270))
|
||||
).rotationDegrees).toEqual([0, 0, -90]);
|
||||
|
||||
// 360 is expressed as 0
|
||||
expect(MatrixMath.decomposeMatrix(
|
||||
MatrixMath.createRotateZ(degreesToRadians(360))
|
||||
).rotationDegrees).toEqual([0, 0, 0]);
|
||||
|
||||
expect(MatrixMath.decomposeMatrix(
|
||||
MatrixMath.createRotateZ(degreesToRadians(33.33333333))
|
||||
).rotationDegrees).toEqual([0, 0, 33.333]);
|
||||
|
||||
expect(MatrixMath.decomposeMatrix(
|
||||
MatrixMath.createRotateZ(degreesToRadians(86.75309))
|
||||
).rotationDegrees).toEqual([0, 0, 86.753]);
|
||||
|
||||
expect(MatrixMath.decomposeMatrix(
|
||||
MatrixMath.createRotateZ(degreesToRadians(42.00000000001))
|
||||
).rotationDegrees).toEqual([0, 0, 42]);
|
||||
|
||||
expect(MatrixMath.decomposeMatrix(
|
||||
MatrixMath.createRotateZ(degreesToRadians(42.99999999999))
|
||||
).rotationDegrees).toEqual([0, 0, 43]);
|
||||
|
||||
expect(MatrixMath.decomposeMatrix(
|
||||
MatrixMath.createRotateZ(degreesToRadians(42.49999999999))
|
||||
).rotationDegrees).toEqual([0, 0, 42.5]);
|
||||
|
||||
expect(MatrixMath.decomposeMatrix(
|
||||
MatrixMath.createRotateZ(degreesToRadians(42.55555555555))
|
||||
).rotationDegrees).toEqual([0, 0, 42.556]);
|
||||
});
|
||||
|
||||
it('decomposes a 4x4 matrix to produce accurate Y-axis angles', () => {
|
||||
var mat;
|
||||
[30, 45, 60, 75, 90, 100, 110, 120, 133, 167].forEach(angle => {
|
||||
mat = MatrixMath.createIdentityMatrix();
|
||||
MatrixMath.reuseRotateYCommand(mat, degreesToRadians(angle));
|
||||
expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
|
||||
.toEqual([0, angle, 0]);
|
||||
|
||||
mat = MatrixMath.createIdentityMatrix();
|
||||
MatrixMath.reuseRotateYCommand(mat, degreesToRadians(-angle));
|
||||
expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
|
||||
.toEqual([0, -angle, 0]);
|
||||
});
|
||||
|
||||
// all values are between 0 and 180;
|
||||
// change of sign and direction in the third and fourth quadrant
|
||||
mat = MatrixMath.createIdentityMatrix();
|
||||
MatrixMath.reuseRotateYCommand(mat, degreesToRadians(222));
|
||||
expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
|
||||
.toEqual([0, -138, 0]);
|
||||
|
||||
mat = MatrixMath.createIdentityMatrix();
|
||||
MatrixMath.reuseRotateYCommand(mat, degreesToRadians(270));
|
||||
expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
|
||||
.toEqual([0, -90, 0]);
|
||||
|
||||
mat = MatrixMath.createIdentityMatrix();
|
||||
MatrixMath.reuseRotateYCommand(mat, degreesToRadians(360));
|
||||
expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
|
||||
.toEqual([0, 0, 0]);
|
||||
});
|
||||
|
||||
it('decomposes a 4x4 matrix to produce accurate X-axis angles', () => {
|
||||
var mat;
|
||||
[30, 45, 60, 75, 90, 100, 110, 120, 133, 167].forEach(angle => {
|
||||
mat = MatrixMath.createIdentityMatrix();
|
||||
MatrixMath.reuseRotateXCommand(mat, degreesToRadians(angle));
|
||||
expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
|
||||
.toEqual([angle, 0, 0]);
|
||||
});
|
||||
|
||||
// all values are between 0 and 180;
|
||||
// change of sign and direction in the third and fourth quadrant
|
||||
mat = MatrixMath.createIdentityMatrix();
|
||||
MatrixMath.reuseRotateXCommand(mat, degreesToRadians(222));
|
||||
expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
|
||||
.toEqual([-138, 0, 0]);
|
||||
|
||||
mat = MatrixMath.createIdentityMatrix();
|
||||
MatrixMath.reuseRotateXCommand(mat, degreesToRadians(270));
|
||||
expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
|
||||
.toEqual([-90, 0, 0]);
|
||||
|
||||
mat = MatrixMath.createIdentityMatrix();
|
||||
MatrixMath.reuseRotateXCommand(mat, degreesToRadians(360));
|
||||
expect(MatrixMath.decomposeMatrix(mat).rotationDegrees)
|
||||
.toEqual([0, 0, 0]);
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue