matrix: add analytical 4x4 inverse

This commit is contained in:
Danny Robson 2014-08-19 20:45:28 +10:00
parent c086e2c9d7
commit 3ab2e8ed57
4 changed files with 265 additions and 17 deletions

View File

@ -52,7 +52,127 @@ matrix<T>::translate (T x, T y, T z) {
template <typename T> template <typename T>
matrix<T> matrix<T>
matrix<T>::inverse (void) const { matrix<T>::inverse (void) const {
return matrix<T>(*this).invert (); matrix<T> m;
T d = det ();
if (almost_zero (d))
throw std::runtime_error ("non-singular matrix");
auto v = values;
m.values[0][0] = v[1][2] * v[2][3] * v[3][1] -
v[1][3] * v[2][2] * v[3][1] +
v[1][3] * v[2][1] * v[3][2] -
v[1][1] * v[2][3] * v[3][2] -
v[1][2] * v[2][1] * v[3][3] +
v[1][1] * v[2][2] * v[3][3];
m.values[0][1] = v[0][3] * v[2][2] * v[3][1] -
v[0][2] * v[2][3] * v[3][1] -
v[0][3] * v[2][1] * v[3][2] +
v[0][1] * v[2][3] * v[3][2] +
v[0][2] * v[2][1] * v[3][3] -
v[0][1] * v[2][2] * v[3][3];
m.values[0][2] = v[0][2] * v[1][3] * v[3][1] -
v[0][3] * v[1][2] * v[3][1] +
v[0][3] * v[1][1] * v[3][2] -
v[0][1] * v[1][3] * v[3][2] -
v[0][2] * v[1][1] * v[3][3] +
v[0][1] * v[1][2] * v[3][3];
m.values[0][3] = v[0][3] * v[1][2] * v[2][1] -
v[0][2] * v[1][3] * v[2][1] -
v[0][3] * v[1][1] * v[2][2] +
v[0][1] * v[1][3] * v[2][2] +
v[0][2] * v[1][1] * v[2][3] -
v[0][1] * v[1][2] * v[2][3];
m.values[1][0] = v[1][3] * v[2][2] * v[3][0] -
v[1][2] * v[2][3] * v[3][0] -
v[1][3] * v[2][0] * v[3][2] +
v[1][0] * v[2][3] * v[3][2] +
v[1][2] * v[2][0] * v[3][3] -
v[1][0] * v[2][2] * v[3][3];
m.values[1][1] = v[0][2] * v[2][3] * v[3][0] -
v[0][3] * v[2][2] * v[3][0] +
v[0][3] * v[2][0] * v[3][2] -
v[0][0] * v[2][3] * v[3][2] -
v[0][2] * v[2][0] * v[3][3] +
v[0][0] * v[2][2] * v[3][3];
m.values[1][2] = v[0][3] * v[1][2] * v[3][0] -
v[0][2] * v[1][3] * v[3][0] -
v[0][3] * v[1][0] * v[3][2] +
v[0][0] * v[1][3] * v[3][2] +
v[0][2] * v[1][0] * v[3][3] -
v[0][0] * v[1][2] * v[3][3];
m.values[1][3] = v[0][2] * v[1][3] * v[2][0] -
v[0][3] * v[1][2] * v[2][0] +
v[0][3] * v[1][0] * v[2][2] -
v[0][0] * v[1][3] * v[2][2] -
v[0][2] * v[1][0] * v[2][3] +
v[0][0] * v[1][2] * v[2][3];
m.values[2][0] = v[1][1] * v[2][3] * v[3][0] -
v[1][3] * v[2][1] * v[3][0] +
v[1][3] * v[2][0] * v[3][1] -
v[1][0] * v[2][3] * v[3][1] -
v[1][1] * v[2][0] * v[3][3] +
v[1][0] * v[2][1] * v[3][3];
m.values[2][1] = v[0][3] * v[2][1] * v[3][0] -
v[0][1] * v[2][3] * v[3][0] -
v[0][3] * v[2][0] * v[3][1] +
v[0][0] * v[2][3] * v[3][1] +
v[0][1] * v[2][0] * v[3][3] -
v[0][0] * v[2][1] * v[3][3];
m.values[2][2] = v[0][1] * v[1][3] * v[3][0] -
v[0][3] * v[1][1] * v[3][0] +
v[0][3] * v[1][0] * v[3][1] -
v[0][0] * v[1][3] * v[3][1] -
v[0][1] * v[1][0] * v[3][3] +
v[0][0] * v[1][1] * v[3][3];
m.values[2][3] = v[0][3] * v[1][1] * v[2][0] -
v[0][1] * v[1][3] * v[2][0] -
v[0][3] * v[1][0] * v[2][1] +
v[0][0] * v[1][3] * v[2][1] +
v[0][1] * v[1][0] * v[2][3] -
v[0][0] * v[1][1] * v[2][3];
m.values[3][0] = v[1][2] * v[2][1] * v[3][0] -
v[1][1] * v[2][2] * v[3][0] -
v[1][2] * v[2][0] * v[3][1] +
v[1][0] * v[2][2] * v[3][1] +
v[1][1] * v[2][0] * v[3][2] -
v[1][0] * v[2][1] * v[3][2];
m.values[3][1] = v[0][1] * v[2][2] * v[3][0] -
v[0][2] * v[2][1] * v[3][0] +
v[0][2] * v[2][0] * v[3][1] -
v[0][0] * v[2][2] * v[3][1] -
v[0][1] * v[2][0] * v[3][2] +
v[0][0] * v[2][1] * v[3][2];
m.values[3][2] = v[0][2] * v[1][1] * v[3][0] -
v[0][1] * v[1][2] * v[3][0] -
v[0][2] * v[1][0] * v[3][1] +
v[0][0] * v[1][2] * v[3][1] +
v[0][1] * v[1][0] * v[3][2] -
v[0][0] * v[1][1] * v[3][2];
m.values[3][3] = v[0][1] * v[1][2] * v[2][0] -
v[0][2] * v[1][1] * v[2][0] +
v[0][2] * v[1][0] * v[2][1] -
v[0][0] * v[1][2] * v[2][1] -
v[0][1] * v[1][0] * v[2][2] +
v[0][0] * v[1][1] * v[2][2];
m /= d;
return m;
} }
@ -60,10 +180,30 @@ matrix<T>::inverse (void) const {
template <typename T> template <typename T>
matrix<T>& matrix<T>&
matrix<T>::invert (void) { matrix<T>::invert (void) {
auto m = *this;
m.invert ();
*this = m;
return *this;
}
//-----------------------------------------------------------------------------
template <typename T>
matrix<T>
matrix<T>::inverse_affine (void) const {
return matrix<T>(*this).invert_affine ();
}
//-----------------------------------------------------------------------------
template <typename T>
matrix<T>&
matrix<T>::invert_affine (void) {
CHECK_HARD (is_affine ()); CHECK_HARD (is_affine ());
// inv ([ M b ] == [ inv(M) -inv(M).b ] // inv ([ M b ] == [ inv(M) -inv(M).b ]
// [ 0 1 ]) [ 0 1 // [ 0 1 ]) [ 0 1 ]
// Invert the 3x3 M // Invert the 3x3 M
T A = (values[1][1] * values[2][2] - values[1][2] * values[2][1]); T A = (values[1][1] * values[2][2] - values[1][2] * values[2][1]);
@ -78,18 +218,18 @@ matrix<T>::invert (void) {
T H = (values[0][2] * values[1][0] - values[0][0] * values[1][2]); T H = (values[0][2] * values[1][0] - values[0][0] * values[1][2]);
T K = (values[0][0] * values[1][1] - values[0][1] * values[1][0]); T K = (values[0][0] * values[1][1] - values[0][1] * values[1][0]);
T det = values[0][0] * A + values[0][1] * B + values[0][2] * C; T d = values[0][0] * A + values[0][1] * B + values[0][2] * C;
CHECK_NEQ (det, 0.0); CHECK_NEQ (d, 0.0);
values[0][0] = A / det; values[0][0] = A / d;
values[0][1] = D / det; values[0][1] = D / d;
values[0][2] = G / det; values[0][2] = G / d;
values[1][0] = B / det; values[1][0] = B / d;
values[1][1] = E / det; values[1][1] = E / d;
values[1][2] = H / det; values[1][2] = H / d;
values[2][0] = C / det; values[2][0] = C / d;
values[2][1] = F / det; values[2][1] = F / d;
values[2][2] = K / det; values[2][2] = K / d;
// Multiply the b // Multiply the b
T b0 = - values[0][0] * values[0][3] - values[0][1] * values[1][3] - values[0][2] * values[2][3]; T b0 = - values[0][0] * values[0][3] - values[0][1] * values[1][3] - values[0][2] * values[2][3];
@ -104,6 +244,40 @@ matrix<T>::invert (void) {
} }
//-----------------------------------------------------------------------------
template <typename T>
T
matrix<T>::det (void) const {
return values[0][3] * values[1][2] * values[2][1] * values[3][0] -
values[0][2] * values[1][3] * values[2][1] * values[3][0] -
values[0][3] * values[1][1] * values[2][2] * values[3][0] +
values[0][1] * values[1][3] * values[2][2] * values[3][0] +
values[0][2] * values[1][1] * values[2][3] * values[3][0] -
values[0][1] * values[1][2] * values[2][3] * values[3][0] -
values[0][3] * values[1][2] * values[2][0] * values[3][1] +
values[0][2] * values[1][3] * values[2][0] * values[3][1] +
values[0][3] * values[1][0] * values[2][2] * values[3][1] -
values[0][0] * values[1][3] * values[2][2] * values[3][1] -
values[0][2] * values[1][0] * values[2][3] * values[3][1] +
values[0][0] * values[1][2] * values[2][3] * values[3][1] +
values[0][3] * values[1][1] * values[2][0] * values[3][2] -
values[0][1] * values[1][3] * values[2][0] * values[3][2] -
values[0][3] * values[1][0] * values[2][1] * values[3][2] +
values[0][0] * values[1][3] * values[2][1] * values[3][2] +
values[0][1] * values[1][0] * values[2][3] * values[3][2] -
values[0][0] * values[1][1] * values[2][3] * values[3][2] -
values[0][2] * values[1][1] * values[2][0] * values[3][3] +
values[0][1] * values[1][2] * values[2][0] * values[3][3] +
values[0][2] * values[1][0] * values[2][1] * values[3][3] -
values[0][0] * values[1][2] * values[2][1] * values[3][3] -
values[0][1] * values[1][0] * values[2][2] * values[3][3] +
values[0][0] * values[1][1] * values[2][2] * values[3][3];
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <typename T> template <typename T>
matrix<T> matrix<T>
@ -150,10 +324,10 @@ matrix<T>::to_global (const util::point<3> &p) const {
template <typename T> template <typename T>
bool bool
matrix<T>::is_affine (void) const { matrix<T>::is_affine (void) const {
return exactly_equal (values[3][0], static_cast<T> (0)) && return exactly_equal (values[3][0], T {0}) &&
exactly_equal (values[3][1], static_cast<T> (0)) && exactly_equal (values[3][1], T {0}) &&
exactly_equal (values[3][2], static_cast<T> (0)) && exactly_equal (values[3][2], T {0}) &&
exactly_equal (values[3][3], static_cast<T> (1)); exactly_equal (values[3][3], T {1});
} }

View File

@ -29,11 +29,18 @@ namespace util {
struct matrix { struct matrix {
T values[4][4]; T values[4][4];
static const size_t rows = 4;
static const size_t cols = 4;
void scale (T x, T y, T z); void scale (T x, T y, T z);
void translate (T x, T y, T z); void translate (T x, T y, T z);
matrix<T> inverse (void) const; matrix<T> inverse (void) const;
matrix<T>& invert (void); matrix<T>& invert (void);
matrix<T> inverse_affine (void) const;
matrix<T>& invert_affine (void);
T det (void) const;
matrix<T> operator* (const matrix<T>&) const; matrix<T> operator* (const matrix<T>&) const;

View File

@ -11,6 +11,7 @@ TEST_BIN = \
json/types \ json/types \
maths/maths \ maths/maths \
maths/matrix \ maths/matrix \
matrix \
md2 \ md2 \
md4 \ md4 \
md5 \ md5 \

66
test/matrix.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "matrix.hpp"
#include "vector.hpp"
#include "debug.hpp"
#include <cstdlib>
int
main (int, char **) {
{
// Identity matrix-vector multiplication
auto v = util::vector<4> { 1, 2, 3, 4 };
auto r = util::matrix<float>::IDENTITY * v;
CHECK_EQ (r, v);
}
{
// Simple matrix-vector multiplication
util::matrix<float> m { {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 },
{ 13, 14, 15, 16 }
} };
util::vector<4> v { 1, 2, 3, 4 };
auto r = m * v;
CHECK_EQ (r.x, 30);
CHECK_EQ (r.y, 70);
CHECK_EQ (r.z, 110);
CHECK_EQ (r.w, 150);
}
{
// Ensure identity inverts to identity
auto m = util::matrix<float>::IDENTITY.inverse ();
for (size_t r = 0; r < m.rows; ++r)
for (size_t c = 0; c < m.cols; ++c)
if (r == c)
CHECK_EQ (m.values[r][c], 1);
else
CHECK_EQ (m.values[r][c], 0);
}
{
// Simple inversion test
util::matrix<float> m { {
{ 4, 1, 2, 3 },
{ 2, 3, 4, 1 },
{ 3, 4, 1, 2 },
{ 1, 2, 3, 4 }
} };
util::matrix<float> r { {
{ 11, 1, 1, -9 },
{ -9, 1, 11, 1 },
{ 1, 11, -9, 1 },
{ 1, -9, 1, 11 }
} };
CHECK_EQ (m.inverse (), r / 40.f);
}
return EXIT_SUCCESS;
}