mirror of
https://github.com/belsabbagh/dotfiles.git
synced 2026-04-11 17:47:09 +00:00
199 lines
5.9 KiB
JavaScript
199 lines
5.9 KiB
JavaScript
.pragma library
|
|
|
|
.import "../geometry/offset.js" as Offset
|
|
|
|
class Matrix {
|
|
constructor(values = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) {
|
|
this.values = values;
|
|
}
|
|
|
|
get(row, column) {
|
|
return this.values[(row * 4) + column];
|
|
}
|
|
|
|
set(row, column, v) {
|
|
this.values[(row * 4) + column] = v;
|
|
}
|
|
|
|
/** Does the 3D transform on [point] and returns the `x` and `y` values in an [Offset]. */
|
|
map(point) {
|
|
if (this.values.length < 16) return point;
|
|
|
|
const v00 = this.get(0, 0);
|
|
const v01 = this.get(0, 1);
|
|
const v03 = this.get(0, 3);
|
|
const v10 = this.get(1, 0);
|
|
const v11 = this.get(1, 1);
|
|
const v13 = this.get(1, 3);
|
|
const v30 = this.get(3, 0);
|
|
const v31 = this.get(3, 1);
|
|
const v33 = this.get(3, 3);
|
|
|
|
const x = point.x;
|
|
const y = point.y;
|
|
const z = v03 * x + v13 * y + v33;
|
|
const inverseZ = 1 / z;
|
|
const pZ = isFinite(inverseZ) ? inverseZ : 0;
|
|
|
|
return new Offset.Offset(pZ * (v00 * x + v10 * y + v30), pZ * (v01 * x + v11 * y + v31));
|
|
}
|
|
|
|
/** Multiply this matrix by [m] and assign the result to this matrix. */
|
|
timesAssign(m) {
|
|
const v = this.values;
|
|
if (v.length < 16) return;
|
|
if (m.values.length < 16) return;
|
|
|
|
const v00 = this.dot(0, m, 0);
|
|
const v01 = this.dot(0, m, 1);
|
|
const v02 = this.dot(0, m, 2);
|
|
const v03 = this.dot(0, m, 3);
|
|
const v10 = this.dot(1, m, 0);
|
|
const v11 = this.dot(1, m, 1);
|
|
const v12 = this.dot(1, m, 2);
|
|
const v13 = this.dot(1, m, 3);
|
|
const v20 = this.dot(2, m, 0);
|
|
const v21 = this.dot(2, m, 1);
|
|
const v22 = this.dot(2, m, 2);
|
|
const v23 = this.dot(2, m, 3);
|
|
const v30 = this.dot(3, m, 0);
|
|
const v31 = this.dot(3, m, 1);
|
|
const v32 = this.dot(3, m, 2);
|
|
const v33 = this.dot(3, m, 3);
|
|
|
|
v[0] = v00;
|
|
v[1] = v01;
|
|
v[2] = v02;
|
|
v[3] = v03;
|
|
v[4] = v10;
|
|
v[5] = v11;
|
|
v[6] = v12;
|
|
v[7] = v13;
|
|
v[8] = v20;
|
|
v[9] = v21;
|
|
v[10] = v22;
|
|
v[11] = v23;
|
|
v[12] = v30;
|
|
v[13] = v31;
|
|
v[14] = v32;
|
|
v[15] = v33;
|
|
}
|
|
|
|
dot(row, m, column) {
|
|
return this.get(row, 0) * m.get(0, column) +
|
|
this.get(row, 1) * m.get(1, column) +
|
|
this.get(row, 2) * m.get(2, column) +
|
|
this.get(row, 3) * m.get(3, column);
|
|
}
|
|
|
|
/** Resets the `this` to the identity matrix. */
|
|
reset() {
|
|
const v = this.values;
|
|
if (v.length < 16) return;
|
|
v[0] = 1;
|
|
v[1] = 0;
|
|
v[2] = 0;
|
|
v[3] = 0;
|
|
v[4] = 0;
|
|
v[5] = 1;
|
|
v[6] = 0;
|
|
v[7] = 0;
|
|
v[8] = 0;
|
|
v[9] = 0;
|
|
v[10] = 1;
|
|
v[11] = 0;
|
|
v[12] = 0;
|
|
v[13] = 0;
|
|
v[14] = 0;
|
|
v[15] = 1;
|
|
}
|
|
|
|
/** Applies a [degrees] rotation around Z to `this`. */
|
|
rotateZ(degrees) {
|
|
if (this.values.length < 16) return;
|
|
|
|
const r = degrees * (Math.PI / 180.0);
|
|
const s = Math.sin(r);
|
|
const c = Math.cos(r);
|
|
|
|
const a00 = this.get(0, 0);
|
|
const a10 = this.get(1, 0);
|
|
const v00 = c * a00 + s * a10;
|
|
const v10 = -s * a00 + c * a10;
|
|
|
|
const a01 = this.get(0, 1);
|
|
const a11 = this.get(1, 1);
|
|
const v01 = c * a01 + s * a11;
|
|
const v11 = -s * a01 + c * a11;
|
|
|
|
const a02 = this.get(0, 2);
|
|
const a12 = this.get(1, 2);
|
|
const v02 = c * a02 + s * a12;
|
|
const v12 = -s * a02 + c * a12;
|
|
|
|
const a03 = this.get(0, 3);
|
|
const a13 = this.get(1, 3);
|
|
const v03 = c * a03 + s * a13;
|
|
const v13 = -s * a03 + c * a13;
|
|
|
|
this.set(0, 0, v00);
|
|
this.set(0, 1, v01);
|
|
this.set(0, 2, v02);
|
|
this.set(0, 3, v03);
|
|
this.set(1, 0, v10);
|
|
this.set(1, 1, v11);
|
|
this.set(1, 2, v12);
|
|
this.set(1, 3, v13);
|
|
}
|
|
|
|
/** Scale this matrix by [x], [y], [z] */
|
|
scale(x = 1, y = 1, z = 1) {
|
|
if (this.values.length < 16) return;
|
|
this.set(0, 0, this.get(0, 0) * x);
|
|
this.set(0, 1, this.get(0, 1) * x);
|
|
this.set(0, 2, this.get(0, 2) * x);
|
|
this.set(0, 3, this.get(0, 3) * x);
|
|
this.set(1, 0, this.get(1, 0) * y);
|
|
this.set(1, 1, this.get(1, 1) * y);
|
|
this.set(1, 2, this.get(1, 2) * y);
|
|
this.set(1, 3, this.get(1, 3) * y);
|
|
this.set(2, 0, this.get(2, 0) * z);
|
|
this.set(2, 1, this.get(2, 1) * z);
|
|
this.set(2, 2, this.get(2, 2) * z);
|
|
this.set(2, 3, this.get(2, 3) * z);
|
|
}
|
|
|
|
/** Translate this matrix by [x], [y], [z] */
|
|
translate(x = 0, y = 0, z = 0) {
|
|
if (this.values.length < 16) return;
|
|
const t1 = this.get(0, 0) * x + this.get(1, 0) * y + this.get(2, 0) * z + this.get(3, 0);
|
|
const t2 = this.get(0, 1) * x + this.get(1, 1) * y + this.get(2, 1) * z + this.get(3, 1);
|
|
const t3 = this.get(0, 2) * x + this.get(1, 2) * y + this.get(2, 2) * z + this.get(3, 2);
|
|
const t4 = this.get(0, 3) * x + this.get(1, 3) * y + this.get(2, 3) * z + this.get(3, 3);
|
|
this.set(3, 0, t1);
|
|
this.set(3, 1, t2);
|
|
this.set(3, 2, t3);
|
|
this.set(3, 3, t4);
|
|
}
|
|
|
|
toString() {
|
|
return `${this.get(0, 0)} ${this.get(0, 1)} ${this.get(0, 2)} ${this.get(0, 3)}\n` +
|
|
`${this.get(1, 0)} ${this.get(1, 1)} ${this.get(1, 2)} ${this.get(1, 3)}\n` +
|
|
`${this.get(2, 0)} ${this.get(2, 1)} ${this.get(2, 2)} ${this.get(2, 3)}\n` +
|
|
`${this.get(3, 0)} ${this.get(3, 1)} ${this.get(3, 2)} ${this.get(3, 3)}`;
|
|
}
|
|
}
|
|
|
|
// Companion object constants
|
|
Matrix.ScaleX = 0;
|
|
Matrix.SkewY = 1;
|
|
Matrix.Perspective0 = 3;
|
|
Matrix.SkewX = 4;
|
|
Matrix.ScaleY = 5;
|
|
Matrix.Perspective1 = 7;
|
|
Matrix.ScaleZ = 10;
|
|
Matrix.TranslateX = 12;
|
|
Matrix.TranslateY = 13;
|
|
Matrix.TranslateZ = 14;
|
|
Matrix.Perspective2 = 15;
|