forked from icaven/glm
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Open
Description
glm::determinant(glm::mat) is very useful...
However, it is a product of the cofactor matrix, which computes sub-dimensional determinants, eventually outputting a single scalar. (There are physical and photonic methods that require tensor intermediates).
Striving for modularity:
The cofactor matrix should be the basis of computing determinants, so that developers don't have to roll their own and the machinery used to compute cofactors can be optimized and reused for determinants.
Below is an unrolled implementation of 2d, 3d, 4d cofactors:
glm::dmat2 cofactor(const glm::dmat2& A)
{
return {A[1][1], -A[0][1], -A[1][0], A[0][0]};
}
glm::dmat3 cofactor(const glm::dmat3& A)
{
glm::dmat3 B;
B[0][0] = A[1][1]*A[2][2] - A[1][2]*A[2][1]; // +M00
B[0][1] = -(A[1][0]*A[2][2] - A[1][2]*A[2][0]); // -M01
B[0][2] = A[1][0]*A[2][1] - A[1][1]*A[2][0]; // +M02
B[1][0] = -(A[0][1]*A[2][2] - A[0][2]*A[2][1]); // -M10
B[1][1] = A[0][0]*A[2][2] - A[0][2]*A[2][0]; // +M11
B[1][2] = -(A[0][0]*A[2][1] - A[0][1]*A[2][0]); // -M12
B[2][0] = A[0][1]*A[1][2] - A[0][2]*A[1][1]; // +M20
B[2][1] = -(A[0][0]*A[1][2] - A[0][2]*A[1][0]); // -M21
B[2][2] = A[0][0]*A[1][1] - A[0][1]*A[1][0]; // +M22
return B;
}
glm::dmat4 cofactor(const glm::dmat4& A)
{
glm::dmat4 C;
// Cofactor matrix C[i][j] = (-1)^(i+j) * det(minor(i, j))
// glm is column-major, so C[col][row] = C[j][i]
// === Row 0 ===
C[0][0] = glm::determinant(glm::mat3(
A[1][1], A[2][1], A[3][1],
A[1][2], A[2][2], A[3][2],
A[1][3], A[2][3], A[3][3]));
C[1][0] = -glm::determinant(glm::mat3(
A[0][1], A[2][1], A[3][1],
A[0][2], A[2][2], A[3][2],
A[0][3], A[2][3], A[3][3]));
C[2][0] = glm::determinant(glm::mat3(
A[0][1], A[1][1], A[3][1],
A[0][2], A[1][2], A[3][2],
A[0][3], A[1][3], A[3][3]));
C[3][0] = -glm::determinant(glm::mat3(
A[0][1], A[1][1], A[2][1],
A[0][2], A[1][2], A[2][2],
A[0][3], A[1][3], A[2][3]));
// === Row 1 ===
C[0][1] = -glm::determinant(glm::mat3(
A[1][0], A[2][0], A[3][0],
A[1][2], A[2][2], A[3][2],
A[1][3], A[2][3], A[3][3]));
C[1][1] = glm::determinant(glm::mat3(
A[0][0], A[2][0], A[3][0],
A[0][2], A[2][2], A[3][2],
A[0][3], A[2][3], A[3][3]));
C[2][1] = -glm::determinant(glm::mat3(
A[0][0], A[1][0], A[3][0],
A[0][2], A[1][2], A[3][2],
A[0][3], A[1][3], A[3][3]));
C[3][1] = glm::determinant(glm::mat3(
A[0][0], A[1][0], A[2][0],
A[0][2], A[1][2], A[2][2],
A[0][3], A[1][3], A[2][3]));
// === Row 2 ===
C[0][2] = glm::determinant(glm::mat3(
A[1][0], A[2][0], A[3][0],
A[1][1], A[2][1], A[3][1],
A[1][3], A[2][3], A[3][3]));
C[1][2] = -glm::determinant(glm::mat3(
A[0][0], A[2][0], A[3][0],
A[0][1], A[2][1], A[3][1],
A[0][3], A[2][3], A[3][3]));
C[2][2] = glm::determinant(glm::mat3(
A[0][0], A[1][0], A[3][0],
A[0][1], A[1][1], A[3][1],
A[0][3], A[1][3], A[3][3]));
C[3][2] = -glm::determinant(glm::mat3(
A[0][0], A[1][0], A[2][0],
A[0][1], A[1][1], A[2][1],
A[0][3], A[1][3], A[2][3]));
// === Row 3 ===
C[0][3] = -glm::determinant(glm::mat3(
A[1][0], A[2][0], A[3][0],
A[1][1], A[2][1], A[3][1],
A[1][2], A[2][2], A[3][2]));
C[1][3] = glm::determinant(glm::mat3(
A[0][0], A[2][0], A[3][0],
A[0][1], A[2][1], A[3][1],
A[0][2], A[2][2], A[3][2]));
C[2][3] = -glm::determinant(glm::mat3(
A[0][0], A[1][0], A[3][0],
A[0][1], A[1][1], A[3][1],
A[0][2], A[1][2], A[3][2]));
C[3][3] = glm::determinant(glm::mat3(
A[0][0], A[1][0], A[2][0],
A[0][1], A[1][1], A[2][1],
A[0][2], A[1][2], A[2][2]));
return C;
}
On a separate note, for convenience the following adjoint functions could be added:
glm::dmat2 adjoint(const glm::dmat2& m) {return glm::transpose(cofactor(m));}
glm::dmat3 adjoint(const glm::dmat3& m) {return glm::transpose(cofactor(m));}
glm::dmat4 adjoint(const glm::dmat4& m) {return glm::transpose(cofactor(m));}
//or perhaps more generally:
template<typename Matrix>
Matrix adjoint(const Matrix& m){return glm::transpose(cofactor(m));};
Metadata
Metadata
Assignees
Labels
No labels