464 lines
12 KiB
TypeScript
464 lines
12 KiB
TypeScript
import { Vector2 } from './Vector2';
|
|
import { Vector3 } from './Vector3';
|
|
import { Matrix4 } from './Matrix4';
|
|
import { Quaternion } from './Quaternion';
|
|
|
|
export class Matrix3
|
|
{
|
|
public static readonly identity: Matrix3 = new Matrix3().setIdentity();
|
|
|
|
private static readonly EPSILON: number = 1e-6;
|
|
|
|
private values: Float32Array = new Float32Array(9);
|
|
|
|
public constructor(values?: number[])
|
|
{
|
|
if (values)
|
|
{
|
|
this.init(values);
|
|
}
|
|
}
|
|
|
|
public at(index: number): number
|
|
{
|
|
return this.values[index];
|
|
}
|
|
|
|
public init(values: number[]): this
|
|
{
|
|
if (values.length !== 9)
|
|
{
|
|
throw new Error("Matrix3 requires exactly 9 values.");
|
|
}
|
|
|
|
let i = 0;
|
|
for (const value of values)
|
|
{
|
|
this.values[i] = value;
|
|
i++;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
public reset(): void
|
|
{
|
|
let i = 0;
|
|
for (const _ of this.values)
|
|
{
|
|
this.values[i] = 0;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
public copy(dest?: Matrix3): Matrix3
|
|
{
|
|
if (!dest)
|
|
{
|
|
dest = new Matrix3();
|
|
}
|
|
|
|
let i = 0;
|
|
for (const value of this.values)
|
|
{
|
|
dest.values[i] = value;
|
|
i++;
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
|
|
public all(): number[]
|
|
{
|
|
const data: number[] = [];
|
|
for (const value of this.values)
|
|
{
|
|
data.push(value);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
public row(index: number): number[]
|
|
{
|
|
return [
|
|
this.values[index * 3],
|
|
this.values[index * 3 + 1],
|
|
this.values[index * 3 + 2]
|
|
];
|
|
}
|
|
|
|
public col(index: number): number[]
|
|
{
|
|
return [
|
|
this.values[index],
|
|
this.values[index + 3],
|
|
this.values[index + 6]
|
|
];
|
|
}
|
|
|
|
public equals(matrix: Matrix3, threshold: number = Matrix3.EPSILON): boolean
|
|
{
|
|
let i = 0;
|
|
for (const value of this.values)
|
|
{
|
|
if (Math.abs(value - matrix.at(i)) > threshold)
|
|
{
|
|
return false;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public determinant(): number
|
|
{
|
|
const a00: number = this.values[0];
|
|
const a01: number = this.values[1];
|
|
const a02: number = this.values[2];
|
|
const a10: number = this.values[3];
|
|
const a11: number = this.values[4];
|
|
const a12: number = this.values[5];
|
|
const a20: number = this.values[6];
|
|
const a21: number = this.values[7];
|
|
const a22: number = this.values[8];
|
|
|
|
const det01: number = a22 * a11 - a12 * a21;
|
|
const det11: number = -a22 * a10 + a12 * a20;
|
|
const det21: number = a21 * a10 - a11 * a20;
|
|
|
|
return a00 * det01 + a01 * det11 + a02 * det21;
|
|
}
|
|
|
|
public setIdentity(): this
|
|
{
|
|
this.reset();
|
|
|
|
this.values[0] = 1;
|
|
this.values[4] = 1;
|
|
this.values[8] = 1;
|
|
|
|
return this;
|
|
}
|
|
|
|
public transpose(): Matrix3
|
|
{
|
|
const transposedValues: number[] = [
|
|
this.values[0],
|
|
this.values[3],
|
|
this.values[6],
|
|
|
|
this.values[1],
|
|
this.values[4],
|
|
this.values[7],
|
|
|
|
this.values[2],
|
|
this.values[5],
|
|
this.values[8]
|
|
];
|
|
|
|
return new Matrix3(transposedValues);
|
|
}
|
|
|
|
public inverse(): Matrix3 | null
|
|
{
|
|
const a00: number = this.values[0];
|
|
const a01: number = this.values[1];
|
|
const a02: number = this.values[2];
|
|
const a10: number = this.values[3];
|
|
const a11: number = this.values[4];
|
|
const a12: number = this.values[5];
|
|
const a20: number = this.values[6];
|
|
const a21: number = this.values[7];
|
|
const a22: number = this.values[8];
|
|
|
|
const det01: number = a22 * a11 - a12 * a21;
|
|
const det11: number = -a22 * a10 + a12 * a20;
|
|
const det21: number = a21 * a10 - a11 * a20;
|
|
|
|
let det: number = a00 * det01 + a01 * det11 + a02 * det21;
|
|
|
|
if (Math.abs(det) < Matrix3.EPSILON)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
det = 1.0 / det;
|
|
|
|
const invValues: number[] = [
|
|
det01 * det,
|
|
(-a22 * a01 + a02 * a21) * det,
|
|
(a12 * a01 - a02 * a11) * det,
|
|
|
|
det11 * det,
|
|
(a22 * a00 - a02 * a20) * det,
|
|
(-a12 * a00 + a02 * a10) * det,
|
|
|
|
det21 * det,
|
|
(-a21 * a00 + a01 * a20) * det,
|
|
(a11 * a00 - a01 * a10) * det
|
|
];
|
|
|
|
return new Matrix3(invValues);
|
|
}
|
|
|
|
public multiply(matrix: Matrix3): Matrix3
|
|
{
|
|
const a00: number = this.values[0];
|
|
const a01: number = this.values[1];
|
|
const a02: number = this.values[2];
|
|
const a10: number = this.values[3];
|
|
const a11: number = this.values[4];
|
|
const a12: number = this.values[5];
|
|
const a20: number = this.values[6];
|
|
const a21: number = this.values[7];
|
|
const a22: number = this.values[8];
|
|
|
|
const b00: number = matrix.at(0);
|
|
const b01: number = matrix.at(1);
|
|
const b02: number = matrix.at(2);
|
|
const b10: number = matrix.at(3);
|
|
const b11: number = matrix.at(4);
|
|
const b12: number = matrix.at(5);
|
|
const b20: number = matrix.at(6);
|
|
const b21: number = matrix.at(7);
|
|
const b22: number = matrix.at(8);
|
|
|
|
const resultValues: number[] = [
|
|
b00 * a00 + b01 * a10 + b02 * a20,
|
|
b00 * a01 + b01 * a11 + b02 * a21,
|
|
b00 * a02 + b01 * a12 + b02 * a22,
|
|
|
|
b10 * a00 + b11 * a10 + b12 * a20,
|
|
b10 * a01 + b11 * a11 + b12 * a21,
|
|
b10 * a02 + b11 * a12 + b12 * a22,
|
|
|
|
b20 * a00 + b21 * a10 + b22 * a20,
|
|
b20 * a01 + b21 * a11 + b22 * a21,
|
|
b20 * a02 + b21 * a12 + b22 * a22
|
|
];
|
|
|
|
return new Matrix3(resultValues);
|
|
}
|
|
|
|
public multiplyVector2(vector: Vector2, result?: Vector2): Vector2
|
|
{
|
|
const x: number = vector.x;
|
|
const y: number = vector.y;
|
|
|
|
if (result)
|
|
{
|
|
result.x = x * this.values[0] + y * this.values[3] + this.values[6];
|
|
result.y = x * this.values[1] + y * this.values[4] + this.values[7];
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return new Vector2([
|
|
x * this.values[0] + y * this.values[3] + this.values[6],
|
|
x * this.values[1] + y * this.values[4] + this.values[7]
|
|
]);
|
|
}
|
|
}
|
|
|
|
public multiplyVector3(vector: Vector3, result?: Vector3): Vector3
|
|
{
|
|
const x: number = vector.x;
|
|
const y: number = vector.y;
|
|
const z: number = vector.z;
|
|
|
|
if (result)
|
|
{
|
|
result.x = x * this.values[0] + y * this.values[3] + z * this.values[6];
|
|
result.y = x * this.values[1] + y * this.values[4] + z * this.values[7];
|
|
result.z = x * this.values[2] + y * this.values[5] + z * this.values[8];
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return new Vector3([
|
|
x * this.values[0] + y * this.values[3] + z * this.values[6],
|
|
x * this.values[1] + y * this.values[4] + z * this.values[7],
|
|
x * this.values[2] + y * this.values[5] + z * this.values[8]
|
|
]);
|
|
}
|
|
}
|
|
|
|
public toMatrix4(result?: Matrix4): Matrix4
|
|
{
|
|
const mat4Values: number[] = [
|
|
this.values[0],
|
|
this.values[1],
|
|
this.values[2],
|
|
0,
|
|
|
|
this.values[3],
|
|
this.values[4],
|
|
this.values[5],
|
|
0,
|
|
|
|
this.values[6],
|
|
this.values[7],
|
|
this.values[8],
|
|
0,
|
|
|
|
0,
|
|
0,
|
|
0,
|
|
1
|
|
];
|
|
|
|
if (result)
|
|
{
|
|
result.init(mat4Values);
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return new Matrix4(mat4Values);
|
|
}
|
|
}
|
|
|
|
public toQuaternion(): Quaternion
|
|
{
|
|
const m00: number = this.values[0];
|
|
const m01: number = this.values[1];
|
|
const m02: number = this.values[2];
|
|
const m10: number = this.values[3];
|
|
const m11: number = this.values[4];
|
|
const m12: number = this.values[5];
|
|
const m20: number = this.values[6];
|
|
const m21: number = this.values[7];
|
|
const m22: number = this.values[8];
|
|
|
|
const fourXSquaredMinus1: number = m00 - m11 - m22;
|
|
const fourYSquaredMinus1: number = m11 - m00 - m22;
|
|
const fourZSquaredMinus1: number = m22 - m00 - m11;
|
|
const fourWSquaredMinus1: number = m00 + m11 + m22;
|
|
|
|
let biggestIndex = 0;
|
|
let fourBiggestSquaredMinus1: number = fourWSquaredMinus1;
|
|
|
|
if (fourXSquaredMinus1 > fourBiggestSquaredMinus1)
|
|
{
|
|
fourBiggestSquaredMinus1 = fourXSquaredMinus1;
|
|
biggestIndex = 1;
|
|
}
|
|
|
|
if (fourYSquaredMinus1 > fourBiggestSquaredMinus1)
|
|
{
|
|
fourBiggestSquaredMinus1 = fourYSquaredMinus1;
|
|
biggestIndex = 2;
|
|
}
|
|
|
|
if (fourZSquaredMinus1 > fourBiggestSquaredMinus1)
|
|
{
|
|
fourBiggestSquaredMinus1 = fourZSquaredMinus1;
|
|
biggestIndex = 3;
|
|
}
|
|
|
|
const biggestVal: number = Math.sqrt(fourBiggestSquaredMinus1 + 1) * 0.5;
|
|
const mult: number = 0.25 / biggestVal;
|
|
|
|
const quat: Quaternion = new Quaternion();
|
|
|
|
switch (biggestIndex)
|
|
{
|
|
case 0:
|
|
quat.w = biggestVal;
|
|
quat.x = (m12 - m21) * mult;
|
|
quat.y = (m20 - m02) * mult;
|
|
quat.z = (m01 - m10) * mult;
|
|
break;
|
|
|
|
case 1:
|
|
quat.w = (m12 - m21) * mult;
|
|
quat.x = biggestVal;
|
|
quat.y = (m01 + m10) * mult;
|
|
quat.z = (m20 + m02) * mult;
|
|
break;
|
|
|
|
case 2:
|
|
quat.w = (m20 - m02) * mult;
|
|
quat.x = (m01 + m10) * mult;
|
|
quat.y = biggestVal;
|
|
quat.z = (m12 + m21) * mult;
|
|
break;
|
|
|
|
case 3:
|
|
quat.w = (m01 - m10) * mult;
|
|
quat.x = (m20 + m02) * mult;
|
|
quat.y = (m12 + m21) * mult;
|
|
quat.z = biggestVal;
|
|
break;
|
|
}
|
|
|
|
return quat;
|
|
}
|
|
|
|
public rotate(angle: number, axis: Vector3): Matrix3 | null
|
|
{
|
|
let x: number = axis.x;
|
|
let y: number = axis.y;
|
|
let z: number = axis.z;
|
|
|
|
let length: number = Math.sqrt(x * x + y * y + z * z);
|
|
|
|
if (length < Matrix3.EPSILON)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (length !== 1)
|
|
{
|
|
length = 1 / length;
|
|
x *= length;
|
|
y *= length;
|
|
z *= length;
|
|
}
|
|
|
|
const s: number = Math.sin(angle);
|
|
const c: number = Math.cos(angle);
|
|
const t: number = 1.0 - c;
|
|
|
|
const b00: number = x * x * t + c;
|
|
const b01: number = y * x * t + z * s;
|
|
const b02: number = z * x * t - y * s;
|
|
|
|
const b10: number = x * y * t - z * s;
|
|
const b11: number = y * y * t + c;
|
|
const b12: number = z * y * t + x * s;
|
|
|
|
const b20: number = x * z * t + y * s;
|
|
const b21: number = y * z * t - x * s;
|
|
const b22: number = z * z * t + c;
|
|
|
|
const a00: number = this.values[0];
|
|
const a01: number = this.values[1];
|
|
const a02: number = this.values[2];
|
|
const a10: number = this.values[3];
|
|
const a11: number = this.values[4];
|
|
const a12: number = this.values[5];
|
|
const a20: number = this.values[6];
|
|
const a21: number = this.values[7];
|
|
const a22: number = this.values[8];
|
|
|
|
const resultValues: number[] = [
|
|
a00 * b00 + a10 * b01 + a20 * b02,
|
|
a01 * b00 + a11 * b01 + a21 * b02,
|
|
a02 * b00 + a12 * b01 + a22 * b02,
|
|
|
|
a00 * b10 + a10 * b11 + a20 * b12,
|
|
a01 * b10 + a11 * b11 + a21 * b12,
|
|
a02 * b10 + a12 * b11 + a22 * b12,
|
|
|
|
a00 * b20 + a10 * b21 + a20 * b22,
|
|
a01 * b20 + a11 * b21 + a21 * b22,
|
|
a02 * b20 + a12 * b21 + a22 * b22
|
|
];
|
|
|
|
return new Matrix3(resultValues);
|
|
}
|
|
}
|