Skip to content

Instantly share code, notes, and snippets.

@jarek-foksa
Created June 18, 2016 10:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jarek-foksa/e379ee0414cf9fb03f38d4bed9140cae to your computer and use it in GitHub Desktop.
Save jarek-foksa/e379ee0414cf9fb03f38d4bed9140cae to your computer and use it in GitHub Desktop.
// @copyright
// © 2005, 2006, 2013 Apple Inc. All rights reserved.
// © 2009 Torch Mobile, Inc.
// © 2016 Jarosław Foksa
// @src
// https://drafts.csswg.org/css-transforms-1/#recomposing-to-a-3d-matrix
// http://trac.webkit.org/browser/trunk/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp
let {abs, sqrt, asin, sin, cos, tan, atan2} = Math;
const SMALL_NUMBER = 1.e-8;
export default class DecomposedTransform {
constructor(rotation = "eulerAngles") {
this._translateX = 0;
this._translateY = 0;
this._translateZ = 0;
this._scaleX = 1;
this._scaleY = 1;
this._scaleZ = 1;
this._perspectiveX = 0;
this._perspectiveY = 0;
this._perspectiveZ = 0;
this._perspectiveW = 1;
this._skewXY = 0;
this._skewXZ = 0;
this._skewYZ = 0;
this._rotation = rotation;
if (rotation === "eulerAngles") {
this._rotateX = 0;
this._rotateY = 0;
this._rotateZ = 0;
}
else if (rotation === "quaternions" ) {
this._quaternionX = 0;
this._quaternionY = 0;
this._quaternionZ = 0;
this._quaternionW = 0;
}
}
static fromSVGMatrix(svgMatrix, rotation = "eulerAngles") {
let transform = new DecomposedTransform(rotation);
let matrix = [
[svgMatrix.a, svgMatrix.b, 0, 0],
[svgMatrix.c, svgMatrix.d, 0, 0],
[ 0, 0, 1, 0],
[svgMatrix.e, svgMatrix.f, 0, 1]
];
// Normalize the matrix.
if (matrix[3][3] == 0) {
return false;
}
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
matrix[i][j] /= matrix[3][3];
}
}
// perspectiveMatrix is used to solve for perspective, but it also provides an easy way to test for singularity
// of the upper 3x3 component.
let perspectiveMatrix = [];
for (let i = 0; i < 4; i++) {
perspectiveMatrix[i] = [];
for (let j = 0; j < 4; j++) {
perspectiveMatrix[i][j] = matrix[i][j];
}
}
for (let i = 0; i < 3; i++) {
perspectiveMatrix[i][3] = 0;
}
perspectiveMatrix[3][3] = 1;
if (determinant4x4(perspectiveMatrix) == 0) {
return false;
}
// First, isolate perspective. This is the messiest.
if (matrix[0][3] != 0 || matrix[1][3] != 0 || matrix[2][3] != 0) {
// rightHandSide is the right hand side of the equation.
let rightHandSide = [0, 0, 0, 0];
rightHandSide[0] = matrix[0][3];
rightHandSide[1] = matrix[1][3];
rightHandSide[2] = matrix[2][3];
rightHandSide[3] = matrix[3][3];
// Solve the equation by inverting perspectiveMatrix and multiplying rightHandSide by the inverse. (This is
// the easiest way, not necessarily the best.)
let inversePerspectiveMatrix = inverse(perspectiveMatrix);
let transposedInversePerspectiveMatrix = transposeMatrix4(inversePerspectiveMatrix);
let perspectivePoint = v4MulPointByMatrix(rightHandSide, transposedInversePerspectiveMatrix);
transform.perspectiveX = perspectivePoint[0];
transform.perspectiveY = perspectivePoint[1];
transform.perspectiveZ = perspectivePoint[2];
transform.perspectiveW = perspectivePoint[3];
// Clear the perspective partition
matrix[0][3] = matrix[1][3] = matrix[2][3] = 0;
matrix[3][3] = 1;
}
else {
// No perspective.
transform.perspectiveX = transform.perspectiveY = transform.perspectiveZ = 0;
transform.perspectiveW = 1;
}
// Next take care of translation (easy).
transform.translateX = matrix[3][0];
matrix[3][0] = 0;
transform.translateY = matrix[3][1];
matrix[3][1] = 0;
transform.translateZ = matrix[3][2];
matrix[3][2] = 0;
// Vector4 type and functions need to be added to the common set.
let row = [[], [], []];
let pdum3 = [0, 0, 0];
// Now get scale and shear.
for (let i = 0; i < 3; i++) {
row[i][0] = matrix[i][0];
row[i][1] = matrix[i][1];
row[i][2] = matrix[i][2];
}
// Compute X scale factor and normalize first row.
transform.scaleX = v3Length(row[0]);
v3Scale(row[0], 1.0);
// Compute XY shear factor and make 2nd row orthogonal to 1st.
transform.skewXY = v3Dot(row[0], row[1]);
v3Combine(row[1], row[0], row[1], 1.0, -transform.skewXY);
// Now, compute Y scale and normalize 2nd row.
transform.scaleY = v3Length(row[1]);
v3Scale(row[1], 1.0);
transform.skewXY /= transform.scaleY;
// Compute XZ and YZ shears, orthogonalize 3rd row.
transform.skewXZ = v3Dot(row[0], row[2]);
v3Combine(row[2], row[0], row[2], 1.0, -transform.skewXZ);
transform.skewYZ = v3Dot(row[1], row[2]);
v3Combine(row[2], row[1], row[2], 1.0, -transform.skewYZ);
// Next, get Z scale and normalize 3rd row.
transform.scaleZ = v3Length(row[2]);
v3Scale(row[2], 1.0);
transform.skewXZ /= transform.scaleZ;
transform.skewYZ /= transform.scaleZ;
// 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.
v3Cross(row[1], row[2], pdum3);
if (v3Dot(row[0], pdum3) < 0) {
transform.scaleX *= -1;
transform.scaleY *= -1;
transform.scaleZ *= -1;
for (let i = 0; i < 3; i++) {
row[i][0] *= -1;
row[i][1] *= -1;
row[i][2] *= -1;
}
}
// Rotation
if (rotation === "eulerAngles") {
transform.rotateY = asin(-row[0][2]);
if (cos(transform.rotateY) !== 0) {
transform.rotateX = atan2(row[1][2], row[2][2]);
transform.rotateZ = atan2(row[0][1], row[0][0]);
}
else {
transform.rotateX = atan2(-row[2][0], row[1][1]);
transform.rotateZ = 0;
}
}
else if (rotation === "quaternions") {
let s, t, x, y, z, w;
t = row[0][0] + row[1][1] + row[2][2] + 1.0;
if (t > 1e-4) {
s = 0.5 / sqrt(t);
w = 0.25 / s;
x = (row[2][1] - row[1][2]) * s;
y = (row[0][2] - row[2][0]) * s;
z = (row[1][0] - row[0][1]) * s;
}
else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) {
s = sqrt(1.0 + row[0][0] - row[1][1] - row[2][2]) * 2.0; // S = 4 * qx.
x = 0.25 * s;
y = (row[0][1] + row[1][0]) / s;
z = (row[0][2] + row[2][0]) / s;
w = (row[2][1] - row[1][2]) / s;
}
else if (row[1][1] > row[2][2]) {
s = sqrt(1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0; // S = 4 * qy.
x = (row[0][1] + row[1][0]) / s;
y = 0.25 * s;
z = (row[1][2] + row[2][1]) / s;
w = (row[0][2] - row[2][0]) / s;
}
else {
s = sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0; // S = 4 * qz.
x = (row[0][2] + row[2][0]) / s;
y = (row[1][2] + row[2][1]) / s;
z = 0.25 * s;
w = (row[1][0] - row[0][1]) / s;
}
transform.quaternionX = x;
transform.quaternionY = y;
transform.quaternionZ = z;
transform.quaternionW = w;
}
return transform;
}
// @info
// Recompose the decomposed tranasform back into a transformation matrix.
// @type
// () => SVGMatrix
toSVGMatrix() {
let matrix = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
];
// Perspective
matrix[0][3] = this.perspectiveX;
matrix[1][3] = this.perspectiveY;
matrix[2][3] = this.perspectiveZ;
matrix[3][3] = this.perspectiveW;
// Translate
translate3d(matrix, this.translateX, this.translateY, this.translateZ);
// Rotate
if (this.rotation === "eulerAngles") {
let a = cos(this.rotateX);
let b = sin(this.rotateX);
let c = cos(this.rotateY);
let d = sin(this.rotateY);
let e = cos(this.rotateZ);
let f = sin(this.rotateZ);
let ce = c * e;
let cf = c * f;
let de = d * e;
let df = d * f;
let rotationMatrix = [
[ce - df * b, cf + de * b, -a * d, 0],
[ -a * f, a * e, b, 0],
[de + cf * b, df - ce * b, a * c, 0],
[ 0, 0, 0, 1]
];
matrix = multiply(matrix, rotationMatrix);
}
else if (this.rotation === "quaternions") {
let xx = this.quaternionX * this.quaternionX;
let xy = this.quaternionX * this.quaternionY;
let xz = this.quaternionX * this.quaternionZ;
let xw = this.quaternionX * this.quaternionW;
let yy = this.quaternionY * this.quaternionY;
let yz = this.quaternionY * this.quaternionZ;
let yw = this.quaternionY * this.quaternionW;
let zz = this.quaternionZ * this.quaternionZ;
let zw = this.quaternionZ * this.quaternionW;
let rotationMatrix = [
[1 - 2 * (yy + zz), 2 * (xy - zw), 2 * (xz + yw), 0],
[ 2 * (xy + zw), 1 - 2 * (xx + zz), 2 * (yz - xw), 0],
[ 2 * (xz - yw), 2 * (yz + xw), 1 - 2 * (xx + yy), 0],
[ 0, 0, 0, 1]
];
matrix = multiply(matrix, rotationMatrix);
}
// Skew
if (this.skewYZ) {
let skewMatrix = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
];
skewMatrix[2][1] = this.skewYZ;
matrix = multiply(matrix, skewMatrix);
}
if (this.skewXZ) {
let skewMatrix = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
];
skewMatrix[2][1] = 0
skewMatrix[2][0] = this.skewXZ;
matrix = multiply(matrix, skewMatrix);
}
if (this.skewXY) {
let skewMatrix = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
];
skewMatrix[2][0] = 0
skewMatrix[1][0] = this.skewXY;
matrix = multiply(matrix, skewMatrix);
}
// Scale
scale3d(matrix, this.scaleX, this.scaleY, this.scaleZ);
// ...
return new SVGMatrix([matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1], matrix[3][0], matrix[3][1]]);
}
clone() {
let transform = new DecomposedTransform(this._rotation);
transform.translateX = this._translateX;
transform.translateY = this._translateY;
transform.translateZ = this._translateZ;
transform.scaleX = this._scaleX;
transform.scaleY = this._scaleY;
transform.scaleZ = this._scaleZ;
transform.perspectiveX = this._perspectiveX;
transform.perspectiveY = this._perspectiveY;
transform.perspectiveZ = this._perspectiveZ;
transform.perspectiveW = this._perspectiveW;
transform.skewXY = this._skewXY;
transform.skewXZ = this._skewXZ;
transform.skewYZ = this._skewYZ;
if (this._rotation === "eulerAngles") {
transform.rotateX = this._rotateX;
transform.rotateY = this._rotateY;
transform.rotateZ = this._rotateZ;
}
else if (this._rotation === "quaternions" ) {
transform.quaternionX = this._quaternionX;
transform.quaternionY = this._quaternionY;
transform.quaternionZ = this._quaternionZ;
transform.quaternionW = this._quaternionW;
}
return transform;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// @info
// Translation along X axis.
// @type
// number
get translateX() {
return this._translateX;
}
set translateX(translateX) {
this._translateX = translateX;
}
// @info
// Translation along Y axis.
// @type
// number
get translateY() {
return this._translateY;
}
set translateY(translateY) {
this._translateY = translateY;
}
// @info
// Translation along Z axis.
// @type
// number
get translateZ() {
return this._translateZ;
}
set translateZ(translateZ) {
this._translateZ = translateZ;
}
// @info
// Scale along X axis
// @type
// number
get scaleX() {
return this._scaleX;
}
set scaleX(scaleX) {
this._scaleX = scaleX;
}
// @info
// Scale along Y axis
// @type
// number
get scaleY() {
return this._scaleY;
}
set scaleY(scaleY) {
this._scaleY = scaleY;
}
// @info
// Scale along Z axis
// @type
// number
get scaleZ() {
return this._scaleZ;
}
set scaleZ(scaleZ) {
this._scaleZ = scaleZ;
}
// @info
// Perspective, X component.
// @type
// number
get perspectiveX() {
return this._perspectiveX;
}
set perspectiveX(perspectiveX) {
this._perspectiveX = perspectiveX;
}
// @info
// Perspective, Y component.
// @type
// number
get perspectiveY() {
return this._perspectiveY;
}
set perspectiveY(perspectiveY) {
this._perspectiveY = perspectiveY;
}
// @info
// Perspective, Z component.
// @type
// number
get perspectiveZ() {
return this._perspectiveZ;
}
set perspectiveZ(perspectiveZ) {
this._perspectiveZ = perspectiveZ;
}
// @info
// Perspective, W component.
// @type
// number
get perspectiveW() {
return this._perspectiveW;
}
set perspectiveW(perspectiveW) {
this._perspectiveW = perspectiveW;
}
// @info
// Skew the XY plane
// @type
// number
get skewXY() {
return this._skewXY;
}
set skewXY(skewXY) {
this._skewXY = skewXY;
}
// @info
// Skew the XZ plane
// @type
// number
get skewXZ() {
return this._skewXZ;
}
set skewXZ(skewXZ) {
this._skewXZ = skewXZ;
}
// @info
// Skew the YZ plane
// @type
// number
get skewYZ() {
return this._skewYZ;
}
set skewYZ(skewYZ) {
this._skewYZ = skewYZ;
}
// @info
// Rotation representation. Either Euler angles which are easier to work with in 2D space or quaternions which
// are more flexible in 3D space (no gimbal locks).
// @type
// "eulerAngles" || "quaternions"
get rotation() {
return this._rotation;
}
set rotation(rotation) {
throw new Error(`"rotation" property is read-only.`);
}
// @info
// Rotation along the X axis, in Euler angles.
// @type
// number?
get rotateX() {
if (this._rotation === "eulerAngles") {
return this._rotateX;
}
else {
throw new Error(`Can't get "rotateX" propery - decomposed transform does not use Euler angles.`);
}
}
set rotateX(rotateX) {
if (this._rotation === "eulerAngles") {
this._rotateX = rotateX;
}
else {
throw new Error(`Can't set "rotateX" propery - decomposed transform does not use Euler angles.`);
}
}
// @info
// Rotation along the Y axis, in Euler angles.
// @type
// number?
get rotateY() {
if (this._rotation === "eulerAngles") {
return this._rotateY;
}
else {
throw new Error(`Can't get "rotateY" propery - decomposed transform does not use Euler angles.`);
}
}
set rotateY(rotateY) {
if (this._rotation === "eulerAngles") {
this._rotateY = rotateY;
}
else {
throw new Error(`Can't set "rotateY" propery - decomposed transform does not use Euler angles.`);
}
}
// @info
// Rotation along the Z axis, in Euler angles.
// @type
// number?
get rotateZ() {
if (this._rotation === "eulerAngles") {
return this._rotateZ;
}
else {
throw new Error(`Can't get "rotateZ" propery - decomposed transform does not use Euler angles.`);
}
}
set rotateZ(rotateZ) {
if (this._rotation === "eulerAngles") {
this._rotateZ = rotateZ;
}
else {
throw new Error(`Can't set "rotateZ" propery - decomposed transform does not use Euler angles.`);
}
}
// @info
// Quaternion, X component.
// @type
// number?
get quaternionX() {
if (this._rotation === "quaternions") {
return this._quaternionX;
}
else {
throw new Error(`Can't get "quaternionX" propery - decomposed transform does not use quaternions.`);
}
}
set quaternionX(quaternionX) {
if (this._rotation === "quaternions") {
this._quaternionX = quaternionX;
}
else {
throw new Error(`Can't set "quaternionX" propery - decomposed transform does not use quaternions.`);
}
}
// @info
// Quaternion, Y component.
// @type
// number?
get quaternionY() {
if (this._rotation === "quaternions") {
return this._quaternionY;
}
else {
throw new Error(`Can't get "quaternionY" propery - decomposed transform does not use quaternions.`);
}
}
set quaternionY(quaternionY) {
if (this._rotation === "quaternions") {
this._quaternionY = quaternionY;
}
else {
throw new Error(`Can't set "quaternionY" propery - decomposed transform does not use quaternions.`);
}
}
// @info
// Quaternion, Z component.
// @type
// number?
get quaternionZ() {
if (this._rotation === "quaternions") {
return this._quaternionZ;
}
else {
throw new Error(`Can't get "quaternionZ" propery - decomposed transform does not use quaternions.`);
}
}
set quaternionZ(quaternionZ) {
if (this._rotation === "quaternions") {
this._quaternionX = quaternionZ;
}
else {
throw new Error(`Can't set "quaternionZ" propery - decomposed transform does not use quaternions.`);
}
}
// @info
// Quaternion, W component.
// @type
// number?
get quaternionW() {
if (this._rotation === "quaternions") {
return this._quaternionW;
}
else {
throw new Error(`Can't get "quaternionW" propery - decomposed transform does not use quaternions.`);
}
}
set quaternionW(quaternionW) {
if (this._rotation === "quaternions") {
this._quaternionW = quaternionW;
}
else {
throw new Error(`Can't set "quaternionW" propery - decomposed transform does not use quaternions.`);
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let determinant2x2 = (a, b, c, d) => {
return a * d - b * c;
};
let determinant3x3 = (a1, a2, a3, b1, b2, b3, c1, c2, c3) => {
return a1 * determinant2x2(b2, b3, c2, c3)
- b1 * determinant2x2(a2, a3, c2, c3)
+ c1 * determinant2x2(a2, a3, b2, b3);
};
let determinant4x4 = (m) => {
let a1 = m[0][0];
let b1 = m[0][1];
let c1 = m[0][2];
let d1 = m[0][3];
let a2 = m[1][0];
let b2 = m[1][1];
let c2 = m[1][2];
let d2 = m[1][3];
let a3 = m[2][0];
let b3 = m[2][1];
let c3 = m[2][2];
let d3 = m[2][3];
let a4 = m[3][0];
let b4 = m[3][1];
let c4 = m[3][2];
let d4 = m[3][3];
return a1 * determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4)
- b1 * determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4)
+ c1 * determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4)
- d1 * determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
};
let adjoint = (matrix) => {
let result = [[], [], [], []];
// Assign to individual variable names to aid selecting correct values
let a1 = matrix[0][0];
let b1 = matrix[0][1];
let c1 = matrix[0][2];
let d1 = matrix[0][3];
let a2 = matrix[1][0];
let b2 = matrix[1][1];
let c2 = matrix[1][2];
let d2 = matrix[1][3];
let a3 = matrix[2][0];
let b3 = matrix[2][1];
let c3 = matrix[2][2];
let d3 = matrix[2][3];
let a4 = matrix[3][0];
let b4 = matrix[3][1];
let c4 = matrix[3][2];
let d4 = matrix[3][3];
// Row column labeling reversed since we transpose rows & columns
result[0][0] = determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4);
result[1][0] = - determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4);
result[2][0] = determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4);
result[3][0] = - determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
result[0][1] = - determinant3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4);
result[1][1] = determinant3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4);
result[2][1] = - determinant3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4);
result[3][1] = determinant3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4);
result[0][2] = determinant3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4);
result[1][2] = - determinant3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4);
result[2][2] = determinant3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4);
result[3][2] = - determinant3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4);
result[0][3] = - determinant3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3);
result[1][3] = determinant3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3);
result[2][3] = - determinant3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3);
result[3][3] = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3);
return;
};
let inverse = (matrix) => {
// Calculate the adjoint matrix
let result = adjoint(matrix);
// Calculate the 4x4 determinant. If the determinant is zero, then the inverse matrix is not unique.
let det = determinant4x4(matrix);
if (abs(det) < SMALL_NUMBER) {
return false;
}
// Scale the adjoint matrix to get the inverse
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
result[i][j] = result[i][j] / det;
}
}
return result;
};
let transposeMatrix4 = (a) => {
let b = [[], [], [], []];
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
b[i][j] = a[j][i];
}
}
return b;
};
let v4MulPointByMatrix = (p, m) => {
let result = [];
result[0] = (p[0] * m[0][0]) + (p[1] * m[1][0]) +
(p[2] * m[2][0]) + (p[3] * m[3][0]);
result[1] = (p[0] * m[0][1]) + (p[1] * m[1][1]) +
(p[2] * m[2][1]) + (p[3] * m[3][1]);
result[2] = (p[0] * m[0][2]) + (p[1] * m[1][2]) +
(p[2] * m[2][2]) + (p[3] * m[3][2]);
result[3] = (p[0] * m[0][3]) + (p[1] * m[1][3]) +
(p[2] * m[2][3]) + (p[3] * m[3][3]);
return result;
};
let v3Length = (a) => {
return sqrt((a[0] * a[0]) + (a[1] * a[1]) + (a[2] * a[2]));
}
let v3Scale = (v, desiredLength) => {
let len = v3Length(v);
if (len != 0) {
let l = desiredLength / len;
v[0] *= l;
v[1] *= l;
v[2] *= l;
}
};
let v3Dot = (a, b) => {
return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]);
};
// Make a linear combination of two vectors and return the result.
// result = (a * ascl) + (b * bscl)
let v3Combine = (a, b, result, ascl, bscl) => {
result[0] = (ascl * a[0]) + (bscl * b[0]);
result[1] = (ascl * a[1]) + (bscl * b[1]);
result[2] = (ascl * a[2]) + (bscl * b[2]);
};
// Return the cross product result = a cross b */
let v3Cross = (a, b, result) => {
result[0] = (a[1] * b[2]) - (a[2] * b[1]);
result[1] = (a[2] * b[0]) - (a[0] * b[2]);
result[2] = (a[0] * b[1]) - (a[1] * b[0]);
};
let translate3d = (m_matrix, tx, ty, tz) => {
m_matrix[3][0] += tx * m_matrix[0][0] + ty * m_matrix[1][0] + tz * m_matrix[2][0];
m_matrix[3][1] += tx * m_matrix[0][1] + ty * m_matrix[1][1] + tz * m_matrix[2][1];
m_matrix[3][2] += tx * m_matrix[0][2] + ty * m_matrix[1][2] + tz * m_matrix[2][2];
m_matrix[3][3] += tx * m_matrix[0][3] + ty * m_matrix[1][3] + tz * m_matrix[2][3];
};
let multiply = (mat1, mat2) => {
let result = [[], [], [], []];
result[0][0] = (mat2[0][0] * mat1[0][0] + mat2[0][1] * mat1[1][0]
+ mat2[0][2] * mat1[2][0] + mat2[0][3] * mat1[3][0]);
result[0][1] = (mat2[0][0] * mat1[0][1] + mat2[0][1] * mat1[1][1]
+ mat2[0][2] * mat1[2][1] + mat2[0][3] * mat1[3][1]);
result[0][2] = (mat2[0][0] * mat1[0][2] + mat2[0][1] * mat1[1][2]
+ mat2[0][2] * mat1[2][2] + mat2[0][3] * mat1[3][2]);
result[0][3] = (mat2[0][0] * mat1[0][3] + mat2[0][1] * mat1[1][3]
+ mat2[0][2] * mat1[2][3] + mat2[0][3] * mat1[3][3]);
result[1][0] = (mat2[1][0] * mat1[0][0] + mat2[1][1] * mat1[1][0]
+ mat2[1][2] * mat1[2][0] + mat2[1][3] * mat1[3][0]);
result[1][1] = (mat2[1][0] * mat1[0][1] + mat2[1][1] * mat1[1][1]
+ mat2[1][2] * mat1[2][1] + mat2[1][3] * mat1[3][1]);
result[1][2] = (mat2[1][0] * mat1[0][2] + mat2[1][1] * mat1[1][2]
+ mat2[1][2] * mat1[2][2] + mat2[1][3] * mat1[3][2]);
result[1][3] = (mat2[1][0] * mat1[0][3] + mat2[1][1] * mat1[1][3]
+ mat2[1][2] * mat1[2][3] + mat2[1][3] * mat1[3][3]);
result[2][0] = (mat2[2][0] * mat1[0][0] + mat2[2][1] * mat1[1][0]
+ mat2[2][2] * mat1[2][0] + mat2[2][3] * mat1[3][0]);
result[2][1] = (mat2[2][0] * mat1[0][1] + mat2[2][1] * mat1[1][1]
+ mat2[2][2] * mat1[2][1] + mat2[2][3] * mat1[3][1]);
result[2][2] = (mat2[2][0] * mat1[0][2] + mat2[2][1] * mat1[1][2]
+ mat2[2][2] * mat1[2][2] + mat2[2][3] * mat1[3][2]);
result[2][3] = (mat2[2][0] * mat1[0][3] + mat2[2][1] * mat1[1][3]
+ mat2[2][2] * mat1[2][3] + mat2[2][3] * mat1[3][3]);
result[3][0] = (mat2[3][0] * mat1[0][0] + mat2[3][1] * mat1[1][0]
+ mat2[3][2] * mat1[2][0] + mat2[3][3] * mat1[3][0]);
result[3][1] = (mat2[3][0] * mat1[0][1] + mat2[3][1] * mat1[1][1]
+ mat2[3][2] * mat1[2][1] + mat2[3][3] * mat1[3][1]);
result[3][2] = (mat2[3][0] * mat1[0][2] + mat2[3][1] * mat1[1][2]
+ mat2[3][2] * mat1[2][2] + mat2[3][3] * mat1[3][2]);
result[3][3] = (mat2[3][0] * mat1[0][3] + mat2[3][1] * mat1[1][3]
+ mat2[3][2] * mat1[2][3] + mat2[3][3] * mat1[3][3]);
return result;
};
let scaleNonUniform = (m_matrix, sx, sy) => {
m_matrix[0][0] *= sx;
m_matrix[0][1] *= sx;
m_matrix[0][2] *= sx;
m_matrix[0][3] *= sx;
m_matrix[1][0] *= sy;
m_matrix[1][1] *= sy;
m_matrix[1][2] *= sy;
m_matrix[1][3] *= sy;
};
let scale3d = (m_matrix, sx, sy, sz) => {
scaleNonUniform(m_matrix, sx, sy);
m_matrix[2][0] *= sz;
m_matrix[2][1] *= sz;
m_matrix[2][2] *= sz;
m_matrix[2][3] *= sz;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment