libcruft-util/test/matrix.cpp

226 lines
5.9 KiB
C++
Raw Normal View History

2014-08-19 20:45:28 +10:00
#include "matrix.hpp"
2015-04-13 16:45:56 +10:00
2014-08-19 20:45:28 +10:00
#include "debug.hpp"
2015-04-13 16:45:56 +10:00
#include "tap.hpp"
#include "vector.hpp"
2016-09-14 17:45:33 +10:00
#include "coord/iostream.hpp"
#include "quaternion.hpp"
2014-08-19 20:45:28 +10:00
#include <cstdlib>
2016-09-14 17:45:33 +10:00
///////////////////////////////////////////////////////////////////////////////
2014-08-19 20:45:28 +10:00
int
2015-07-13 16:29:01 +10:00
main (void)
{
util::TAP::logger tap;
// a quick check to make sure this function is actually provided
tap.expect_eq (sum (util::matrix4f::zeroes ()), 0.f, "zero matrix sums to zero");
// trivial check for matrix summation. useful to sanity test some
// alignment constraints if run under a tool like memorysanitizer or
// valgrind.
2016-08-15 20:32:24 +10:00
static constexpr util::matrix4f SEQ { {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 },
{ 13, 14, 15, 16 }
} };
2016-08-15 20:32:24 +10:00
tap.expect_eq (sum (SEQ), 136.f, "element summation");
2016-10-11 20:57:03 +11:00
// tranposition
{
static constexpr util::matrix4f QES {{
{ 1, 5, 9, 13 },
{ 2, 6, 10, 14 },
{ 3, 7, 11, 15 },
{ 4, 8, 12, 16 }
}};
tap.expect_eq (transposed (SEQ), QES, "transposition");
tap.expect_eq (transposed (transposed (SEQ)), SEQ, "double tranposition is identity");
}
2016-08-15 20:32:24 +10:00
// matrix-scalar operations
{
tap.expect_eq (sum (SEQ + 1.f), 152.f, "matrix-scalar addition");
tap.expect_eq (sum (SEQ - 1.f), 120.f, "matrix-scalar subtraction");
tap.expect_eq (sum (SEQ * 2.f), 272.f, "matrix-scalar multiplication");
tap.expect_eq (sum (SEQ / 2.f), 68.f, "matrix-scalar division");
}
// Simple matrix-vector multiplication
2014-08-19 20:45:28 +10:00
{
// Identity matrix-vector multiplication
2015-10-30 23:40:13 +11:00
auto v = util::vector4f { 1.f, 2.f, 3.f, 4.f };
auto r = util::matrix4f::identity () * v;
2015-07-13 16:29:01 +10:00
tap.expect_eq (r, v, "identity matrix-vector multiplication");
2014-08-19 20:45:28 +10:00
}
{
util::vector<4,float> v { 1.f, 2.f, 3.f, 4.f };
2014-08-19 20:45:28 +10:00
2016-08-15 20:32:24 +10:00
auto r = SEQ * v;
2014-08-19 20:45:28 +10:00
2015-07-13 16:29:01 +10:00
tap.expect (
util::almost_equal (r.x, 30.f) &&
util::almost_equal (r.y, 70.f) &&
util::almost_equal (r.z, 110.f) &&
util::almost_equal (r.w, 150.f),
2015-07-13 16:29:01 +10:00
"simple matrix-vector multiplication"
);
2014-08-19 20:45:28 +10:00
}
2014-12-15 13:30:37 +11:00
{
// Simple matrix-matrix multiplication
2015-10-30 23:40:13 +11:00
util::matrix4f a { {
2014-12-15 13:30:37 +11:00
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 },
{ 13, 14, 15, 16 },
} };
2015-10-30 23:40:13 +11:00
util::matrix4f b { {
2014-12-15 13:30:37 +11:00
{ 17, 18, 19, 20 },
{ 21, 22, 23, 24 },
{ -1, -2, -3, -4 },
{ -5, -6, -7, -8 }
} };
2015-10-30 23:40:13 +11:00
util::matrix4f ab { {
2014-12-15 13:30:37 +11:00
{ 9, 8, 7, 6 },
{ 41, 40, 39, 38 },
{ 73, 72, 71, 70 },
{ 105, 104, 103, 102 },
} };
2015-10-30 23:40:13 +11:00
ab *= 4.f;
2014-12-15 13:30:37 +11:00
auto res = a * b;
2015-07-13 16:29:01 +10:00
tap.expect_eq (ab, res, "simple matrix-matrix multiplication");
2014-12-15 13:30:37 +11:00
}
2014-08-19 20:45:28 +10:00
{
2015-07-13 16:29:01 +10:00
bool success = true;
2014-08-19 20:45:28 +10:00
// Ensure identity inverts to identity
auto m = util::matrix4f::identity ().inverse ();
2014-08-19 20:45:28 +10:00
for (size_t r = 0; r < m.rows; ++r)
for (size_t c = 0; c < m.cols; ++c)
if (r == c)
2017-11-24 13:08:56 +11:00
success = success && util::almost_equal (m[r][c], 1.f);
2014-08-19 20:45:28 +10:00
else
2017-11-24 13:08:56 +11:00
success = success && util::almost_equal (m[r][c], 0.f);
2015-07-13 16:29:01 +10:00
tap.expect (success, "identity inversion");
2014-08-19 20:45:28 +10:00
}
// Simple 2x2 inversion test
{
util::matrix2f m { {
{ 1, 2 },
{ 3, 4 }
} };
tap.expect_eq (-2.f, m.determinant (), "2x2 determinant");
util::matrix2f r { {
{ -4, 2 },
{ 3, -1 }
} };
tap.expect_eq (r / 2.f, m.inverse (), "2x2 inversion");
}
// Simple 3x3 inversion test
{
util::matrix3f m { {
{ 3, 1, 2 },
{ 2, 3, 1 },
{ 4, 0, 2 }
} };
tap.expect_eq (-6.f, m.determinant (), "3x3 determinant");
util::matrix3f r { {
{ -6, 2, 5 },
{ 0, 2, -1 },
{ 12, -4, -7 }
} };
tap.expect_eq (m.inverse (), r / 6.f, "3x3 inversion");
}
// Simple 4x4 inversion test
2014-08-19 20:45:28 +10:00
{
2015-10-30 23:40:13 +11:00
util::matrix4f m { {
2014-08-19 20:45:28 +10:00
{ 4, 1, 2, 3 },
{ 2, 3, 4, 1 },
{ 3, 4, 1, 2 },
{ 1, 2, 3, 4 }
} };
tap.expect_eq (-160.f, m.determinant (), "4x4 determinant");
2015-10-30 23:40:13 +11:00
util::matrix4f r { {
2014-08-19 20:45:28 +10:00
{ 11, 1, 1, -9 },
{ -9, 1, 11, 1 },
{ 1, 11, -9, 1 },
{ 1, -9, 1, 11 }
} };
tap.expect_eq (m.inverse (), r / 40.f, "4x4 inversion");
2014-08-19 20:45:28 +10:00
}
2016-09-14 17:45:33 +10:00
// sanity check euler rotations
{
static const struct {
util::vector3f euler;
const char *msg;
} TESTS[] = {
{ util::vector3f { 0 }, "zeroes" },
{ { 1, 0, 0 }, "x-axis" },
{ { 0, 1, 0 }, "y-axis" },
{ { 0, 0, 1 }, "z-axis" },
{ util::vector3f { 1 }, "ones" },
{ { 3, 5, 7 }, "positive primes" },
{ { -3, -5, -7 }, "negative primes" },
{ { 3, -5, 7 }, "mixed primes" },
};
for (auto t: TESTS) {
constexpr auto PI2 = 2 * util::PI<float>;
auto matrix = (
util::quaternionf::angle_axis (t.euler[2], { 0, 0, 1 }) *
util::quaternionf::angle_axis (t.euler[1], { 0, 1, 0 }) *
util::quaternionf::angle_axis (t.euler[0], { 1, 0, 0 })
2016-09-14 17:45:33 +10:00
).as_matrix ();
auto euler = to_euler (matrix);
auto truth = t.euler;
euler = mod (euler + 4 * PI2, PI2);
truth = mod (truth + 4 * PI2, PI2);
tap.expect (
all (compare (
truth, euler,
[] (auto a, auto b) { return util::almost_equal (a, b); }
)),
"matrix-to-euler, %s",
t.msg
);
2016-09-14 17:45:33 +10:00
}
}
2015-07-13 16:29:01 +10:00
return tap.status ();
2014-08-19 20:45:28 +10:00
}