import { Vector } from "./vector.js";
import { PI } from "./common.js"
import { Box } from "./box.js";
import { Ray } from "./ray.js";
/**
* Matrix class for transforming paths and shapes.
*/
export class Matrix {
constructor(data = null) {
if (data) {
this.m = data;
} else {
this.m = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
];
}
this.x00 = this.m[0][0]; this.x01 = this.m[0][1]; this.x02 = this.m[0][2]; this.x03 = this.m[0][3];
this.x10 = this.m[1][0]; this.x11 = this.m[1][1]; this.x12 = this.m[1][2]; this.x13 = this.m[1][3];
this.x20 = this.m[2][0]; this.x21 = this.m[2][1]; this.x22 = this.m[2][2]; this.x23 = this.m[2][3];
this.x30 = this.m[3][0]; this.x31 = this.m[3][1]; this.x32 = this.m[3][2]; this.x33 = this.m[3][3];
}
static identity() {
return new Matrix();
}
/**
* returns new matrix translated
* @param {Vector} v
* @returns {Matrix} new matrix
*/
translate(v) {
v = Translate(v)
v = v.mult(this)
return v
}
/**
* returns new matrix scaled
* @param {Vector} v
* @returns {Matrix} new matrix
*/
scale(v) {
v = Scale(v)
v = v.mult(this)
return v
}
/**
* returns new matrix rotated
* @param {Vector} v
* @returns {Matrix} new matrix
*/
rotate(v, a) {
return Rotate(v, a).mult(this)
}
frustrum(l, r, b, t, n, f) {
let matrix = frustrum(l, r, b, t, n, f)
matrix = matrix.mult(this)
return matrix
}
orthographic(l, r, b, t, n, f) {
let m = orthographic(l, r, b, t, n, f)
m = m.mult(this)
return m
}
perspective(fovy, aspect, near, far) {
let m = perspective(fovy, aspect, near, far)
m = m.mult(this)
return m
}
/**
* returns new matrix multiplied by input matrix
* @param {Matrix}
* @returns {Matrix} new matrix
*/
mult(b) {
let m = new Matrix()
let a = this
m.x00 = a.x00 * b.x00 + a.x01 * b.x10 + a.x02 * b.x20 + a.x03 * b.x30
m.x10 = a.x10 * b.x00 + a.x11 * b.x10 + a.x12 * b.x20 + a.x13 * b.x30
m.x20 = a.x20 * b.x00 + a.x21 * b.x10 + a.x22 * b.x20 + a.x23 * b.x30
m.x30 = a.x30 * b.x00 + a.x31 * b.x10 + a.x32 * b.x20 + a.x33 * b.x30
m.x01 = a.x00 * b.x01 + a.x01 * b.x11 + a.x02 * b.x21 + a.x03 * b.x31
m.x11 = a.x10 * b.x01 + a.x11 * b.x11 + a.x12 * b.x21 + a.x13 * b.x31
m.x21 = a.x20 * b.x01 + a.x21 * b.x11 + a.x22 * b.x21 + a.x23 * b.x31
m.x31 = a.x30 * b.x01 + a.x31 * b.x11 + a.x32 * b.x21 + a.x33 * b.x31
m.x02 = a.x00 * b.x02 + a.x01 * b.x12 + a.x02 * b.x22 + a.x03 * b.x32
m.x12 = a.x10 * b.x02 + a.x11 * b.x12 + a.x12 * b.x22 + a.x13 * b.x32
m.x22 = a.x20 * b.x02 + a.x21 * b.x12 + a.x22 * b.x22 + a.x23 * b.x32
m.x32 = a.x30 * b.x02 + a.x31 * b.x12 + a.x32 * b.x22 + a.x33 * b.x32
m.x03 = a.x00 * b.x03 + a.x01 * b.x13 + a.x02 * b.x23 + a.x03 * b.x33
m.x13 = a.x10 * b.x03 + a.x11 * b.x13 + a.x12 * b.x23 + a.x13 * b.x33
m.x23 = a.x20 * b.x03 + a.x21 * b.x13 + a.x22 * b.x23 + a.x23 * b.x33
m.x33 = a.x30 * b.x03 + a.x31 * b.x13 + a.x32 * b.x23 + a.x33 * b.x33
return m
}
/**
* multiplies input vector b matrix.mulPosistion(b)
* @param {Vector} b
* @returns {Vector}
*/
mulPosition(b) {
let a = this
let x = a.x00*b.x + a.x01*b.y + a.x02*b.z + a.x03
let y = a.x10*b.x + a.x11*b.y + a.x12*b.z + a.x13
let z = a.x20*b.x + a.x21*b.y + a.x22*b.z + a.x23
return new Vector(x, y, z)
}
mulPositionW(b) {
let a = this
let x = a.x00 * b.x + a.x01 * b.y + a.x02 * b.z + a.x03
let y = a.x10 * b.x + a.x11 * b.y + a.x12 * b.z + a.x13
let z = a.x20 * b.x + a.x21 * b.y + a.x22 * b.z + a.x23
let w = a.x30 * b.x + a.x31 * b.y + a.x32 * b.z + a.x33
return new Vector(x / w, y / w, z / w)
}
mulDirection(b) {
let a = this
let x = a.x00 * b.x + a.x01 * b.y + a.x12 * b.z
let y = a.x10* b.x + a.x11 * b.y + a.x12 * b.z
let z = a.x20 * b.x + a.x21 * b.y + a.x22 * b.z
let out = new Vector(x, y, z)
out= out.normalize()
return out
}
mulRay(ray) {
let a = this
let b = ray
return new Ray(a.mulPosition(b.origin), a.mulDirection(b.direction))
}
mulBox(box) {
let a = this
let r = new Vector(a.x00, a.x10, a.x20)
let u = new Vector(a.x01, a.x11, a.x21)
let b = new Vector(a.x02, a.x12, a.x22)
let t = new Vector(a.x03, a.x13, a.x23)
let xa = r.mulScalar(box.min.x)
let xb = r.mulScalar(box.max.x)
let ya = u.mulScalar(box.min.y)
let yb = u.mulScalar(box.max.y)
let za = b.mulScalar(box.min.z)
let zb = b.mulScalar(box.max.z)
let temp = xa
xa = xa.min(xb); xb=temp.max(xb)
temp = ya
ya = ya.min(yb); yb= temp.max(yb)
temp = za
za = za.min(zb); zb = temp.max(zb)
let min = xa.add(ya).add(za).add(t)
let max = xb.add(yb).add(zb).add(t)
return new Box(min, max)
}
transpose() {
let a = this
return new Matrix * ([
[a.x00, a.x10, a.x20, a.x30],
[a.x01, a.x11, a.x21, a.x31],
[a.x02, a.x12, a.x22, a.x32],
[a.x03, a.x13, a.x23, a.x33]
])
}
determinant() {
/// returns float?
let a = this
return (a.x00 * a.x11 * a.x22 * a.x33 - a.x00 * a.x11 * a.x23 * a.x32 +
a.x00 * a.x12 * a.x23 * a.x31 - a.x00 * a.x12 * a.x21 * a.x33 +
a.x00 * a.x13 * a.x21 * a.x32 - a.x00 * a.x13 * a.x22 * a.x31 -
a.x01 * a.x12 * a.x23 * a.x30 + a.x01 * a.x12 * a.x20 * a.x33 -
a.x01 * a.x13 * a.x20 * a.x32 + a.x01 * a.x13 * a.x22 * a.x30 -
a.x01 * a.x10 * a.x22 * a.x33 + a.x01 * a.x10 * a.x23 * a.x32 +
a.x02 * a.x13 * a.x20 * a.x31 - a.x02 * a.x13 * a.x21 * a.x30 +
a.x02 * a.x10 * a.x21 * a.x33 - a.x02 * a.x10 * a.x23 * a.x31 +
a.x02 * a.x11 * a.x23 * a.x30 - a.x02 * a.x11 * a.x20 * a.x33 -
a.x03 * a.x10 * a.x21 * a.x32 + a.x03 * a.x10 * a.x22 * a.x31 -
a.x03 * a.x11 * a.x22 * a.x30 + a.x03 * a.x11 * a.x20 * a.x32 -
a.x03 * a.x12 * a.x20 * a.x31 + a.x03 * a.x12 * a.x21 * a.x30)
}
inverse() {
let a = this
let m = new Matrix()
let d = a.determinant()
m.x00 = (a.x12 * a.x23 * a.x31 - a.x13 * a.x22 * a.x31 + a.x13 * a.x21 * a.x32 - a.x11 * a.x23 * a.x32 - a.x12 * a.x21 * a.x33 + a.x11 * a.x22 * a.x33) / d
m.x01 = (a.x03 * a.x22 * a.x31 - a.x02 * a.x23 * a.x31 - a.x03 * a.x21 * a.x32 + a.x01 * a.x23 * a.x32 + a.x02 * a.x21 * a.x33 - a.x01 * a.x22 * a.x33) / d
m.x02 = (a.x02 * a.x13 * a.x31 - a.x03 * a.x12 * a.x31 + a.x03 * a.x11 * a.x32 - a.x01 * a.x13 * a.x32 - a.x02 * a.x11 * a.x33 + a.x01 * a.x12 * a.x33) / d
m.x03 = (a.x03 * a.x12 * a.x21 - a.x02 * a.x13 * a.x21 - a.x03 * a.x11 * a.x22 + a.x01 * a.x13 * a.x22 + a.x02 * a.x11 * a.x23 - a.x01 * a.x12 * a.x23) / d
m.x10 = (a.x13 * a.x22 * a.x30 - a.x12 * a.x23 * a.x30 - a.x13 * a.x20 * a.x32 + a.x10 * a.x23 * a.x32 + a.x12 * a.x20 * a.x33 - a.x10 * a.x22 * a.x33) / d
m.x11 = (a.x02 * a.x23 * a.x30 - a.x03 * a.x22 * a.x30 + a.x03 * a.x20 * a.x32 - a.x00 * a.x23 * a.x32 - a.x02 * a.x20 * a.x33 + a.x00 * a.x22 * a.x33) / d
m.x12 = (a.x03 * a.x12 * a.x30 - a.x02 * a.x13 * a.x30 - a.x03 * a.x10 * a.x32 + a.x00 * a.x13 * a.x32 + a.x02 * a.x10 * a.x33 - a.x00 * a.x12 * a.x33) / d
m.x13 = (a.x02 * a.x13 * a.x20 - a.x03 * a.x12 * a.x20 + a.x03 * a.x10 * a.x22 - a.x00 * a.x13 * a.x22 - a.x02 * a.x10 * a.x23 + a.x00 * a.x12 * a.x23) / d
m.x20 = (a.x11 * a.x23 * a.x30 - a.x13 * a.x21 * a.x30 + a.x13 * a.x20 * a.x31 - a.x10 * a.x23 * a.x31 - a.x11 * a.x20 * a.x33 + a.x10 * a.x21 * a.x33) / d
m.x21 = (a.x03 * a.x21 * a.x30 - a.x01 * a.x23 * a.x30 - a.x03 * a.x20 * a.x31 + a.x00 * a.x23 * a.x31 + a.x01 * a.x20 * a.x33 - a.x00 * a.x21 * a.x33) / d
m.x22 = (a.x01 * a.x13 * a.x30 - a.x03 * a.x11 * a.x30 + a.x03 * a.x10 * a.x31 - a.x00 * a.x13 * a.x31 - a.x01 * a.x10 * a.x33 + a.x00 * a.x11 * a.x33) / d
m.x23 = (a.x03 * a.x11 * a.x20 - a.x01 * a.x13 * a.x20 - a.x03 * a.x10 * a.x21 + a.x00 * a.x13 * a.x21 + a.x01 * a.x10 * a.x23 - a.x00 * a.x11 * a.x23) / d
m.x30 = (a.x12 * a.x21 * a.x30 - a.x11 * a.x22 * a.x30 - a.x12 * a.x20 * a.x31 + a.x10 * a.x22 * a.x31 + a.x11 * a.x20 * a.x32 - a.x10 * a.x21 * a.x32) / d
m.x31 = (a.x01 * a.x22 * a.x30 - a.x02 * a.x21 * a.x30 + a.x02 * a.x20 * a.x31 - a.x00 * a.x22 * a.x31 - a.x01 * a.x20 * a.x32 + a.x00 * a.x21 * a.x32) / d
m.x32 = (a.x02 * a.x11 * a.x30 - a.x01 * a.x12 * a.x30 - a.x02 * a.x10 * a.x31 + a.x00 * a.x12 * a.x31 + a.x01 * a.x10 * a.x32 - a.x00 * a.x11 * a.x32) / d
m.x33 = (a.x01 * a.x12 * a.x20 - a.x02 * a.x11 * a.x20 + a.x02 * a.x10 * a.x21 - a.x00 * a.x12 * a.x21 - a.x01 * a.x10 * a.x22 + a.x00 * a.x11 * a.x22) / d
return m
}
}
/**
* return translated matrix
* @param {Vector} v
* @returns {Matrix}
*/
export function Translate(v) {
return new Matrix(
[
[1, 0, 0, v.x],
[0, 1, 0, v.y],
[0, 0, 1, v.z],
[0, 0, 0, 1]
]
)
}
/**
* return scaled matrix
* @param {Vector} v
* @returns {Matrix}
*/
export function Scale(v) {
return new Matrix(
[
[v.x, 0, 0, 0],
[0, v.y, 0, 0],
[0, 0, v.z, 0],
[0, 0, 0, 1]
]
)
}
/**
*
* @param {Vector} v
* @param {Number} a Angle in radians or use radians(90) to translate degrees
* @returns {Matrix}
*/
export function Rotate(v, a) {
v = v.normalize()
let s = Math.sin(a)
let c = Math.cos(a)
let m = 1 - c
return new Matrix(
[
[m * v.x * v.x + c, m * v.x * v.y + v.z * s, m * v.z * v.z * v.x - v.y * s, 0],
[m * v.x * v.y - v.z * s, m * v.y * v.y + c, m * v.y * v.z + v.x * s, 0],
[m * v.z * v.x + v.y * s, m * v.y * v.z - v.x * s, m * v.z * v.z + c, 0],
[0, 0, 0, 1]
]
)
}
export function frustrum(l, r, b, t, n, f) {
// left , right , back , top , near , far
let t1 = 2 * n
let t2 = r - l
let t3 = t - b
let t4 = f - n
let m = new Matrix([
[t1 / t2, 0, (r + l) / t2, 0],
[0, t1 / t3, (t + b) / t3, 0],
[0, 0, (-f - n) / t4, (-t1 * f) / t4],
[0, 0, -1, 0]
])
// console.log(m)
return m
}
export function orthographic(l, r, b, t, n, f) {
return new Matrix([
[2 / (r - l), 0, 0, -(r + l) / (r - l)],
[0, 2 / (t - b), 0, -(t + b) / (t - b)],
[0, 0, -2 / (f - n), -(f + n) / (f - n)],
[0, 0, 0, 1]
])
}
export function perspective(fovy, aspect, near, far) {
let ymax = near * Math.tan(fovy * PI / 360)
let xmax = ymax * aspect
// console.log("at perspective", frustrum(-xmax, xmax, -ymax, ymax, near, far))
return frustrum(-xmax, xmax, -ymax, ymax, near, far)
}
export function lookAt(eye, center, up) {
up = up.normalize()
let f = center.sub(eye)
let s = f.cross(up)
let u = s.cross(f)
f = f.normalize(); s = s.normalize(); u = u.normalize()
let m = new Matrix([
[s.x, u.x, -f.x, eye.x],
[s.y, u.y, -f.y, eye.y],
[s.z, u.z, -f.z, eye.z],
[0, 0, 0, 1]
])
return m.inverse()
}