src/math/S3Matrix.js
import S3Math from "./S3Math.js";
import S3Vector from "./S3Vector.js";
/**
* 3DCG用の4x4行列クラス
* 主に変換行列や射影行列などに使用されます。
*
* @class
* @module S3
*/
export default class S3Matrix {
/**
* 3DCG用 の4x4行列 (immutable)
* 行列を作成します。MATLABと同様に行ごとに指定します。
* 9引数で3x3行列、16引数で4x4行列、引数なしで0埋め行列
* @param {Number} [m00]
* @param {Number} [m01]
* @param {Number} [m02]
* @param {Number} [m03]
* @param {Number} [m10]
* @param {Number} [m11]
* @param {Number} [m12]
* @param {Number} [m13]
* @param {Number} [m20]
* @param {Number} [m21]
* @param {Number} [m22]
* @param {Number} [m23]
* @param {Number} [m30]
* @param {Number} [m31]
* @param {Number} [m32]
* @param {Number} [m33]
*/
constructor(
m00,
m01,
m02,
m03, // row 1
m10,
m11,
m12,
m13, // row 2
m20,
m21,
m22,
m23, // row 3
m30,
m31,
m32,
m33
) {
// row 4
if (arguments.length === 0) {
/** @type {number} */
this.m00 = 0.0;
/** @type {number} */
this.m01 = 0.0;
/** @type {number} */
this.m02 = 0.0;
/** @type {number} */
this.m03 = 0.0;
/** @type {number} */
this.m10 = 0.0;
/** @type {number} */
this.m11 = 0.0;
/** @type {number} */
this.m12 = 0.0;
/** @type {number} */
this.m13 = 0.0;
/** @type {number} */
this.m20 = 0.0;
/** @type {number} */
this.m21 = 0.0;
/** @type {number} */
this.m22 = 0.0;
/** @type {number} */
this.m23 = 0.0;
/** @type {number} */
this.m30 = 0.0;
/** @type {number} */
this.m31 = 0.0;
/** @type {number} */
this.m32 = 0.0;
/** @type {number} */
this.m33 = 0.0;
} else if (arguments.length === 9) {
// 3x3行列
this.m00 = m00;
this.m01 = m01;
this.m02 = m02;
this.m03 = 0.0;
this.m10 = m03;
this.m11 = m10;
this.m12 = m11;
this.m13 = 0.0;
this.m20 = m12;
this.m21 = m13;
this.m22 = m20;
this.m23 = 0.0;
this.m30 = 0.0;
this.m31 = 0.0;
this.m32 = 0.0;
this.m33 = 1.0;
} else if (arguments.length === 16) {
// 4x4行列
this.m00 = m00;
this.m01 = m01;
this.m02 = m02;
this.m03 = m03;
this.m10 = m10;
this.m11 = m11;
this.m12 = m12;
this.m13 = m13;
this.m20 = m20;
this.m21 = m21;
this.m22 = m22;
this.m23 = m23;
this.m30 = m30;
this.m31 = m31;
this.m32 = m32;
this.m33 = m33;
} else {
throw "IllegalArgumentException";
}
}
/**
* 2つの行列が等しいか判定します。
* @param {S3Matrix} tgt
* @returns {boolean}
*/
equals(tgt) {
return (
S3Math.equals(this.m00, tgt.m00) &&
S3Math.equals(this.m01, tgt.m01) &&
S3Math.equals(this.m02, tgt.m02) &&
S3Math.equals(this.m03, tgt.m03) &&
S3Math.equals(this.m10, tgt.m10) &&
S3Math.equals(this.m11, tgt.m11) &&
S3Math.equals(this.m12, tgt.m12) &&
S3Math.equals(this.m13, tgt.m13) &&
S3Math.equals(this.m20, tgt.m20) &&
S3Math.equals(this.m21, tgt.m21) &&
S3Math.equals(this.m22, tgt.m22) &&
S3Math.equals(this.m23, tgt.m23) &&
S3Math.equals(this.m30, tgt.m30) &&
S3Math.equals(this.m31, tgt.m31) &&
S3Math.equals(this.m32, tgt.m32) &&
S3Math.equals(this.m33, tgt.m33)
);
}
/**
* 自身のクローンを作成します。
* @returns {S3Matrix}
*/
clone() {
return new S3Matrix(
this.m00,
this.m01,
this.m02,
this.m03,
this.m10,
this.m11,
this.m12,
this.m13,
this.m20,
this.m21,
this.m22,
this.m23,
this.m30,
this.m31,
this.m32,
this.m33
);
}
/**
* 転置行列を返します。
* @returns {S3Matrix}
*/
transposed() {
return new S3Matrix(
this.m00,
this.m10,
this.m20,
this.m30,
this.m01,
this.m11,
this.m21,
this.m31,
this.m02,
this.m12,
this.m22,
this.m32,
this.m03,
this.m13,
this.m23,
this.m33
);
}
/**
* 非数成分を含むか判定します。
* @returns {boolean}
*/
isNaN() {
return (
isNaN(this.m00) ||
isNaN(this.m01) ||
isNaN(this.m02) ||
isNaN(this.m03) ||
isNaN(this.m10) ||
isNaN(this.m11) ||
isNaN(this.m12) ||
isNaN(this.m13) ||
isNaN(this.m20) ||
isNaN(this.m21) ||
isNaN(this.m22) ||
isNaN(this.m23) ||
isNaN(this.m30) ||
isNaN(this.m31) ||
isNaN(this.m32) ||
isNaN(this.m33)
);
}
/**
* 有限の成分のみか判定します。
* @returns {boolean}
*/
isFinite() {
return (
(isFinite(this.m00) && isFinite(this.m01) && isFinite(this.m02) && isFinite(this.m03)) ||
(isFinite(this.m10) && isFinite(this.m11) && isFinite(this.m12) && isFinite(this.m13)) ||
(isFinite(this.m20) && isFinite(this.m21) && isFinite(this.m22) && isFinite(this.m23)) ||
(isFinite(this.m30) && isFinite(this.m31) && isFinite(this.m32) && isFinite(this.m33))
);
}
/**
* 実数値成分のみか判定します。
* @returns {boolean}
*/
isRealNumber() {
return !this.isNaN() && this.isFinite();
}
/**
* 行列またはベクトルとの掛け算を行います。
* @param {S3Matrix} tgt 行列
* @returns {S3Matrix}
*/
mulMatrix(tgt) {
const A = this;
const B = tgt;
const C = new S3Matrix();
// 行列クラスのコンストラクタを変更しても問題がないように
// 後で代入を行っております。
C.m00 = A.m00 * B.m00 + A.m01 * B.m10 + A.m02 * B.m20 + A.m03 * B.m30;
C.m01 = A.m00 * B.m01 + A.m01 * B.m11 + A.m02 * B.m21 + A.m03 * B.m31;
C.m02 = A.m00 * B.m02 + A.m01 * B.m12 + A.m02 * B.m22 + A.m03 * B.m32;
C.m03 = A.m00 * B.m03 + A.m01 * B.m13 + A.m02 * B.m23 + A.m03 * B.m33;
C.m10 = A.m10 * B.m00 + A.m11 * B.m10 + A.m12 * B.m20 + A.m13 * B.m30;
C.m11 = A.m10 * B.m01 + A.m11 * B.m11 + A.m12 * B.m21 + A.m13 * B.m31;
C.m12 = A.m10 * B.m02 + A.m11 * B.m12 + A.m12 * B.m22 + A.m13 * B.m32;
C.m13 = A.m10 * B.m03 + A.m11 * B.m13 + A.m12 * B.m23 + A.m13 * B.m33;
C.m20 = A.m20 * B.m00 + A.m21 * B.m10 + A.m22 * B.m20 + A.m23 * B.m30;
C.m21 = A.m20 * B.m01 + A.m21 * B.m11 + A.m22 * B.m21 + A.m23 * B.m31;
C.m22 = A.m20 * B.m02 + A.m21 * B.m12 + A.m22 * B.m22 + A.m23 * B.m32;
C.m23 = A.m20 * B.m03 + A.m21 * B.m13 + A.m22 * B.m23 + A.m23 * B.m33;
C.m30 = A.m30 * B.m00 + A.m31 * B.m10 + A.m32 * B.m20 + A.m33 * B.m30;
C.m31 = A.m30 * B.m01 + A.m31 * B.m11 + A.m32 * B.m21 + A.m33 * B.m31;
C.m32 = A.m30 * B.m02 + A.m31 * B.m12 + A.m32 * B.m22 + A.m33 * B.m32;
C.m33 = A.m30 * B.m03 + A.m31 * B.m13 + A.m32 * B.m23 + A.m33 * B.m33;
return C;
}
/**
* 縦ベクトルとの掛け算を行います。
* @param {S3Vector} tgt 縦ベクトル
* @returns {S3Vector}
*/
mulVector(tgt) {
const A = this;
const v = tgt;
// 行列×縦ベクトル=縦ベクトル
// Av = u なので、各項を行列の行ごとで掛け算する
return new S3Vector(
A.m00 * v.x + A.m01 * v.y + A.m02 * v.z + A.m03 * v.w,
A.m10 * v.x + A.m11 * v.y + A.m12 * v.z + A.m13 * v.w,
A.m20 * v.x + A.m21 * v.y + A.m22 * v.z + A.m23 * v.w,
A.m30 * v.x + A.m31 * v.y + A.m32 * v.z + A.m33 * v.w
);
}
/**
* 3x3部分行列の行列式を計算します。
* @returns {number}
*/
det3() {
const A = this;
let out;
out = A.m00 * A.m11 * A.m22;
out += A.m10 * A.m21 * A.m02;
out += A.m20 * A.m01 * A.m12;
out -= A.m00 * A.m21 * A.m12;
out -= A.m20 * A.m11 * A.m02;
out -= A.m10 * A.m01 * A.m22;
return out;
}
/**
* 3x3部分行列の逆行列を返します。
* @returns {S3Matrix|null}
*/
inverse3() {
const A = this;
const det = A.det3();
if (det === 0.0) {
return null;
}
const id = 1.0 / det;
const B = A.clone();
B.m00 = (A.m11 * A.m22 - A.m12 * A.m21) * id;
B.m01 = (A.m02 * A.m21 - A.m01 * A.m22) * id;
B.m02 = (A.m01 * A.m12 - A.m02 * A.m11) * id;
B.m10 = (A.m12 * A.m20 - A.m10 * A.m22) * id;
B.m11 = (A.m00 * A.m22 - A.m02 * A.m20) * id;
B.m12 = (A.m02 * A.m10 - A.m00 * A.m12) * id;
B.m20 = (A.m10 * A.m21 - A.m11 * A.m20) * id;
B.m21 = (A.m01 * A.m20 - A.m00 * A.m21) * id;
B.m22 = (A.m00 * A.m11 - A.m01 * A.m10) * id;
return B;
}
/**
* 4x4行列の行列式を計算します。
* @returns {number}
*/
det4() {
const A = this;
let out;
out = A.m00 * A.m11 * A.m22 * A.m33;
out += A.m00 * A.m12 * A.m23 * A.m31;
out += A.m00 * A.m13 * A.m21 * A.m32;
out += A.m01 * A.m10 * A.m23 * A.m32;
out += A.m01 * A.m12 * A.m20 * A.m33;
out += A.m01 * A.m13 * A.m22 * A.m30;
out += A.m02 * A.m10 * A.m21 * A.m33;
out += A.m02 * A.m11 * A.m23 * A.m30;
out += A.m02 * A.m13 * A.m20 * A.m31;
out += A.m03 * A.m10 * A.m22 * A.m31;
out += A.m03 * A.m11 * A.m20 * A.m32;
out += A.m03 * A.m12 * A.m21 * A.m30;
out -= A.m00 * A.m11 * A.m23 * A.m32;
out -= A.m00 * A.m12 * A.m21 * A.m33;
out -= A.m00 * A.m13 * A.m22 * A.m31;
out -= A.m01 * A.m10 * A.m22 * A.m33;
out -= A.m01 * A.m12 * A.m23 * A.m30;
out -= A.m01 * A.m13 * A.m20 * A.m32;
out -= A.m02 * A.m10 * A.m23 * A.m31;
out -= A.m02 * A.m11 * A.m20 * A.m33;
out -= A.m02 * A.m13 * A.m21 * A.m30;
out -= A.m03 * A.m10 * A.m21 * A.m32;
out -= A.m03 * A.m11 * A.m22 * A.m30;
out -= A.m03 * A.m12 * A.m20 * A.m31;
return out;
}
/**
* 4x4行列の逆行列を返します。
* @returns {S3Matrix|null}
*/
inverse4() {
const A = this;
const det = A.det4();
if (det === 0.0) {
return null;
}
const id = 1.0 / det;
const B = new S3Matrix();
B.m00 =
(A.m11 * A.m22 * A.m33 +
A.m12 * A.m23 * A.m31 +
A.m13 * A.m21 * A.m32 -
A.m11 * A.m23 * A.m32 -
A.m12 * A.m21 * A.m33 -
A.m13 * A.m22 * A.m31) *
id;
B.m01 =
(A.m01 * A.m23 * A.m32 +
A.m02 * A.m21 * A.m33 +
A.m03 * A.m22 * A.m31 -
A.m01 * A.m22 * A.m33 -
A.m02 * A.m23 * A.m31 -
A.m03 * A.m21 * A.m32) *
id;
B.m02 =
(A.m01 * A.m12 * A.m33 +
A.m02 * A.m13 * A.m31 +
A.m03 * A.m11 * A.m32 -
A.m01 * A.m13 * A.m32 -
A.m02 * A.m11 * A.m33 -
A.m03 * A.m12 * A.m31) *
id;
B.m03 =
(A.m01 * A.m13 * A.m22 +
A.m02 * A.m11 * A.m23 +
A.m03 * A.m12 * A.m21 -
A.m01 * A.m12 * A.m23 -
A.m02 * A.m13 * A.m21 -
A.m03 * A.m11 * A.m22) *
id;
B.m10 =
(A.m10 * A.m23 * A.m32 +
A.m12 * A.m20 * A.m33 +
A.m13 * A.m22 * A.m30 -
A.m10 * A.m22 * A.m33 -
A.m12 * A.m23 * A.m30 -
A.m13 * A.m20 * A.m32) *
id;
B.m11 =
(A.m00 * A.m22 * A.m33 +
A.m02 * A.m23 * A.m30 +
A.m03 * A.m20 * A.m32 -
A.m00 * A.m23 * A.m32 -
A.m02 * A.m20 * A.m33 -
A.m03 * A.m22 * A.m30) *
id;
B.m12 =
(A.m00 * A.m13 * A.m32 +
A.m02 * A.m10 * A.m33 +
A.m03 * A.m12 * A.m30 -
A.m00 * A.m12 * A.m33 -
A.m02 * A.m13 * A.m30 -
A.m03 * A.m10 * A.m32) *
id;
B.m13 =
(A.m00 * A.m12 * A.m23 +
A.m02 * A.m13 * A.m20 +
A.m03 * A.m10 * A.m22 -
A.m00 * A.m13 * A.m22 -
A.m02 * A.m10 * A.m23 -
A.m03 * A.m12 * A.m20) *
id;
B.m20 =
(A.m10 * A.m21 * A.m33 +
A.m11 * A.m23 * A.m30 +
A.m13 * A.m20 * A.m31 -
A.m10 * A.m23 * A.m31 -
A.m11 * A.m20 * A.m33 -
A.m13 * A.m21 * A.m30) *
id;
B.m21 =
(A.m00 * A.m23 * A.m31 +
A.m01 * A.m20 * A.m33 +
A.m03 * A.m21 * A.m30 -
A.m00 * A.m21 * A.m33 -
A.m01 * A.m23 * A.m30 -
A.m03 * A.m20 * A.m31) *
id;
B.m22 =
(A.m00 * A.m11 * A.m33 +
A.m01 * A.m13 * A.m30 +
A.m03 * A.m10 * A.m31 -
A.m00 * A.m13 * A.m31 -
A.m01 * A.m10 * A.m33 -
A.m03 * A.m11 * A.m30) *
id;
B.m23 =
(A.m00 * A.m13 * A.m21 +
A.m01 * A.m10 * A.m23 +
A.m03 * A.m11 * A.m20 -
A.m00 * A.m11 * A.m23 -
A.m01 * A.m13 * A.m20 -
A.m03 * A.m10 * A.m21) *
id;
B.m30 =
(A.m10 * A.m22 * A.m31 +
A.m11 * A.m20 * A.m32 +
A.m12 * A.m21 * A.m30 -
A.m10 * A.m21 * A.m32 -
A.m11 * A.m22 * A.m30 -
A.m12 * A.m20 * A.m31) *
id;
B.m31 =
(A.m00 * A.m21 * A.m32 +
A.m01 * A.m22 * A.m30 +
A.m02 * A.m20 * A.m31 -
A.m00 * A.m22 * A.m31 -
A.m01 * A.m20 * A.m32 -
A.m02 * A.m21 * A.m30) *
id;
B.m32 =
(A.m00 * A.m12 * A.m31 +
A.m01 * A.m10 * A.m32 +
A.m02 * A.m11 * A.m30 -
A.m00 * A.m11 * A.m32 -
A.m01 * A.m12 * A.m30 -
A.m02 * A.m10 * A.m31) *
id;
B.m33 =
(A.m00 * A.m11 * A.m22 +
A.m01 * A.m12 * A.m20 +
A.m02 * A.m10 * A.m21 -
A.m00 * A.m12 * A.m21 -
A.m01 * A.m10 * A.m22 -
A.m02 * A.m11 * A.m20) *
id;
return B;
}
/**
* 行列を文字列に変換します。
* @returns {string}
*/
toString() {
return (
"[" +
"[" +
this.m00 +
" " +
this.m01 +
" " +
this.m02 +
" " +
this.m03 +
"]\n" +
" [" +
this.m10 +
" " +
this.m11 +
" " +
this.m12 +
" " +
this.m13 +
"]\n" +
" [" +
this.m20 +
" " +
this.m21 +
" " +
this.m22 +
" " +
this.m23 +
"]\n" +
" [" +
this.m30 +
" " +
this.m31 +
" " +
this.m32 +
" " +
this.m33 +
"]]"
);
}
/**
* 他の型のインスタンスに変換します(配列化)。
* @param {{new(array: number[]): any}} Instance 配列型のコンストラクタ
* @param {number} dimension 配列長
* @returns {*} 変換結果
*/
toInstanceArray(Instance, dimension) {
if (dimension === 1) {
return new Instance([this.m00]);
} else if (dimension === 4) {
return new Instance([this.m00, this.m10, this.m01, this.m11]);
} else if (dimension === 9) {
return new Instance([
this.m00,
this.m10,
this.m20,
this.m01,
this.m11,
this.m21,
this.m02,
this.m12,
this.m22
]);
} else {
return new Instance([
this.m00,
this.m10,
this.m20,
this.m30,
this.m01,
this.m11,
this.m21,
this.m31,
this.m02,
this.m12,
this.m22,
this.m32,
this.m03,
this.m13,
this.m23,
this.m33
]);
}
}
}