import {Matrix4} from "three";
import {QuadRingElement} from "./QuadRingElement.js";
/**
* @class
*
* @classdesc
* 4x4 matrix over a quadratic field **with determinant 1** (o make inversion easier).
* @author Mostly borrowed from Three.js
*/
export class QuadRingMatrix4 {
constructor(ring) {
/**
* The underlying quadratic ring
* @type {QuadRing}
*/
this.ring = ring;
/**
* The elements of the matrix, in a column-major order
* @type {QuadRingElement[]}
*/
this.elements = [
this.ring.one.clone(), this.ring.zero.clone(), this.ring.zero.clone(), this.ring.zero.clone(),
this.ring.zero.clone(), this.ring.one.clone(), this.ring.zero.clone(), this.ring.zero.clone(),
this.ring.zero.clone(), this.ring.zero.clone(), this.ring.one.clone(), this.ring.zero.clone(),
this.ring.zero.clone(), this.ring.zero.clone(), this.ring.zero.clone(), this.ring.one.clone()
];
}
/**
* Flag to precise the type of the object
* @type {boolean}
*/
get isQuadRingMatrix4() {
return true;
}
/**
* The 4x4 matrix with all the a-parts.
* This data is need for the shader
* @type{Matrix4}
*/
get a() {
const entries = this.toArray().map(x => x.a);
return new Matrix4().fromArray(entries);
}
/**
* The 4x4 matrix with all the b-parts.
* This data is need for the shader
* @type{Matrix4}
*/
get b() {
const entries = this.toArray().map(x => x.b);
return new Matrix4().fromArray(entries);
}
/**
* Return the ij-entry
* @param {number} i - the row index
* @param {number} j - the column index
* @return {QuadRingElement}
*/
getEntry(i, j) {
return this.elements[4 * j + i];
}
/**
* Set the value of the ij-entry
* @param {number} i - the row index
* @param {number} j - the column index
* @param {QuadRingElement} value
* @return {QuadRingMatrix4}
*/
setEntry(i, j, value) {
this.elements[4 * j + i].copy(value);
return this;
}
/**
* Set the elements of this matrix to the supplied row-major values n11, n12, ... n44.
* @param {QuadRingElement} n11
* @param {QuadRingElement} n12
* @param {QuadRingElement} n13
* @param {QuadRingElement} n14
* @param {QuadRingElement} n21
* @param {QuadRingElement} n22
* @param {QuadRingElement} n23
* @param {QuadRingElement} n24
* @param {QuadRingElement} n31
* @param {QuadRingElement} n32
* @param {QuadRingElement} n33
* @param {QuadRingElement} n34
* @param {QuadRingElement} n41
* @param {QuadRingElement} n42
* @param {QuadRingElement} n43
* @param {QuadRingElement} n44
* @return {QuadRingMatrix4}
*/
set(n11, n12, n13, n14,
n21, n22, n23, n24,
n31, n32, n33, n34,
n41, n42, n43, n44) {
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
this.setEntry(i, j, arguments[4 * i + j]);
}
}
return this;
}
/**
* Set the current matrix to the identity
*/
identity() {
this.elements = [
this.ring.one.clone(), this.ring.zero.clone(), this.ring.zero.clone(), this.ring.zero.clone(),
this.ring.zero.clone(), this.ring.one.clone(), this.ring.zero.clone(), this.ring.zero.clone(),
this.ring.zero.clone(), this.ring.zero.clone(), this.ring.one.clone(), this.ring.zero.clone(),
this.ring.zero.clone(), this.ring.zero.clone(), this.ring.zero.clone(), this.ring.one.clone()
];
return this;
}
/**
* Set the matrix to the product m1 * m2
* @param {QuadRingMatrix4} m1
* @param {QuadRingMatrix4} m2
* @return {QuadRingMatrix4}
*/
multiplyMatrices(m1, m2) {
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
this.setEntry(i, j, this.ring.zero);
for (let k = 0; k < 4; k++) {
this.getEntry(i, j).addProduct(m1.getEntry(i, k), m2.getEntry(k, j));
}
}
}
return this;
}
/**
* Matrix multiplication
* @param {QuadRingMatrix4} m
* @return {QuadRingMatrix4}
*/
multiply(m) {
return this.multiplyMatrices(this.clone(), m);
}
/**
* Matrix pre-multiplication
* @param {QuadRingMatrix4} m
* @return {QuadRingMatrix4}
*/
premultiply(m) {
return this.multiplyMatrices(m, this.clone());
}
/**
* Multiply the matrix by a scalar
* @param {QuadRingElement} s
* @return {QuadRingMatrix4}
*/
multiplyScalar(s) {
for (let i = 0; i < 16; i++) {
this.elements[i].multiply(s);
}
return this;
}
/**
* Set the matrix to its transpose
* @return {QuadRingMatrix4}
*/
transpose() {
const te = this.elements;
let tmp = this.ring.element();
tmp.copy(te[1]);
te[1].copy(te[4]);
te[4].copy(tmp);
tmp.copy(te[2]);
te[2].copy(te[8]);
te[8].copy(tmp);
tmp.copy(te[6]);
te[6].copy(te[9]);
te[9].copy(tmp);
tmp.copy(te[3]);
te[3].copy(te[12]);
te[12].copy(tmp);
tmp.copy(te[7]);
te[7].copy(te[13]);
te[13].copy(tmp);
tmp.copy(te[11]);
te[11].copy(te[14]);
te[14].copy(tmp);
return this;
}
/**
* Set the matrix to its inverse.
* We recall that the determinant of the matrix is assumed to be one.
* @return {QuadRingMatrix4}
*/
invert() {
// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
const te = this.elements,
n11 = te[0], n21 = te[1], n31 = te[2], n41 = te[3],
n12 = te[4], n22 = te[5], n32 = te[6], n42 = te[7],
n13 = te[8], n23 = te[9], n33 = te[10], n43 = te[11],
n14 = te[12], n24 = te[13], n34 = te[14], n44 = te[15];
te[0] = this.ring.element()
.addProduct(n23, n34, n42)
.subProduct(n24, n33, n42)
.addProduct(n24, n32, n43)
.subProduct(n22, n34, n43)
.subProduct(n23, n32, n44)
.addProduct(n22, n33, n44);
te[1] = this.ring.element()
.addProduct(n24, n33, n41)
.subProduct(n23, n34, n41)
.subProduct(n24, n31, n43)
.addProduct(n21, n34, n43)
.addProduct(n23, n31, n44)
.subProduct(n21, n33, n44);
te[2] = this.ring.element()
.addProduct(n22, n34, n41)
.subProduct(n24, n32, n41)
.addProduct(n24, n31, n42)
.subProduct(n21, n34, n42)
.subProduct(n22, n31, n44)
.addProduct(n21, n32, n44);
te[3] = this.ring.element()
.addProduct(n23, n32, n41)
.subProduct(n22, n33, n41)
.subProduct(n23, n31, n42)
.addProduct(n21, n33, n42)
.addProduct(n22, n31, n43)
.subProduct(n21, n32, n43);
te[4] = this.ring.element()
.addProduct(n14, n33, n42)
.subProduct(n13, n34, n42)
.subProduct(n14, n32, n43)
.addProduct(n12, n34, n43)
.addProduct(n13, n32, n44)
.subProduct(n12, n33, n44);
te[5] = this.ring.element()
.addProduct(n13, n34, n41)
.subProduct(n14, n33, n41)
.addProduct(n14, n31, n43)
.subProduct(n11, n34, n43)
.subProduct(n13, n31, n44)
.addProduct(n11, n33, n44);
te[6] = this.ring.element()
.addProduct(n14, n32, n41)
.subProduct(n12, n34, n41)
.subProduct(n14, n31, n42)
.addProduct(n11, n34, n42)
.addProduct(n12, n31, n44)
.subProduct(n11, n32, n44);
te[7] = this.ring.element()
.addProduct(n12, n33, n41)
.subProduct(n13, n32, n41)
.addProduct(n13, n31, n42)
.subProduct(n11, n33, n42)
.subProduct(n12, n31, n43)
.addProduct(n11, n32, n43);
te[8] = this.ring.element()
.addProduct(n13, n24, n42)
.subProduct(n14, n23, n42)
.addProduct(n14, n22, n43)
.subProduct(n12, n24, n43)
.subProduct(n13, n22, n44)
.addProduct(n12, n23, n44);
te[9] = this.ring.element()
.addProduct(n14, n23, n41)
.subProduct(n13, n24, n41)
.subProduct(n14, n21, n43)
.addProduct(n11, n24, n43)
.addProduct(n13, n21, n44)
.subProduct(n11, n23, n44);
te[10] = this.ring.element()
.addProduct(n12, n24, n41)
.subProduct(n14, n22, n41)
.addProduct(n14, n21, n42)
.subProduct(n11, n24, n42)
.subProduct(n12, n21, n44)
.addProduct(n11, n22, n44);
te[11] = this.ring.element()
.addProduct(n13, n22, n41)
.subProduct(n12, n23, n41)
.subProduct(n13, n21, n42)
.addProduct(n11, n23, n42)
.addProduct(n12, n21, n43)
.subProduct(n11, n22, n43);
te[12] = this.ring.element()
.addProduct(n14, n23, n32)
.subProduct(n13, n24, n32)
.subProduct(n14, n22, n33)
.addProduct(n12, n24, n33)
.addProduct(n13, n22, n34)
.subProduct(n12, n23, n34);
te[13] = this.ring.element()
.addProduct(n13, n24, n31)
.subProduct(n14, n23, n31)
.addProduct(n14, n21, n33)
.subProduct(n11, n24, n33)
.subProduct(n13, n21, n34)
.addProduct(n11, n23, n34);
te[14] = this.ring.element()
.addProduct(n14, n22, n31)
.subProduct(n12, n24, n31)
.subProduct(n14, n21, n32)
.addProduct(n11, n24, n32)
.addProduct(n12, n21, n34)
.subProduct(n11, n22, n34);
te[15] = this.ring.element()
.addProduct(n12, n23, n31)
.subProduct(n13, n22, n31)
.addProduct(n13, n21, n32)
.subProduct(n11, n23, n32)
.subProduct(n12, n21, n33)
.addProduct(n11, n22, n33);
return this;
}
/**
* Check if the two matrices are equal
* @param {QuadRingMatrix4} matrix
* @return {boolean}
*/
equals(matrix) {
for (let i = 0; i < 16; i++) {
if (!this.elements[i].equals(matrix.elements[i])) return false;
}
return true;
}
/**
* Set the coefficient from an array
* @param {QuadRingElement[]} array
* @param {number} offset
* @return {QuadRingMatrix4}
*/
fromArray(array, offset = 0) {
for (let i = 0; i < 16; i++) {
this.elements[i].copy(array[i + offset]);
}
return this;
}
/**
* Return the elements of the matrix as an array
* @param {QuadRingElement[]} array
* @param {number} offset
* @return {QuadRingElement[]}
*/
toArray(array = [], offset = 0) {
const te = this.elements;
for (let i = 0; i < 16; i++) {
array[offset + i] = te[i].clone();
}
return array;
}
/**
* Convert the matrix to a Matrix4 (with number type entries)
* @return {Matrix4}
*/
toMatrix4() {
const entries = this.toArray().map(x => x.toNumber());
return new Matrix4().fromArray(entries);
}
/**
* Return a copy of the current matrix.
* @return {QuadRingMatrix4}
*/
clone() {
return new QuadRingMatrix4(this.ring).fromArray(this.elements);
}
/**
* Set the current matrix to m
* @param {QuadRingMatrix4} m
* @return {QuadRingMatrix4}
*/
copy(m) {
return this.fromArray(m.elements);
}
toLog() {
return this.toMatrix4().toLog();
}
}