diff --git a/test/vector.cpp b/test/vector.cpp index 1931c2fb..66984cad 100644 --- a/test/vector.cpp +++ b/test/vector.cpp @@ -61,12 +61,52 @@ test_polar (util::TAP::logger &tap) } +void +test_euler (util::TAP::logger &tap) +{ + static const struct { + util::vector3f dir; + util::vector2f euler; + const char *name; + } TESTS[] = { + // y-axis + { { 0, 0, -1 }, { 0.5f, 0.5f }, "forward" }, + { { -1, 0, 0 }, { 0.5f, -1.0f }, "left" }, + { { 0, 0, 1 }, { 0.5f, -0.5f }, "back" }, + { { 1, 0, 0 }, { 0.5f, 0.0f }, "right" }, + + // x-axis + { { 0, 1, 0 }, { 0, 0 }, "up" }, + { { 0, -1, 0 }, { 1, 0 }, "down" }, + }; + + // check that simple axis rotations look correct + for (auto i: TESTS) { + tap.expect_eq (util::to_euler (i.dir), + i.euler * PI, + "to euler, %s", i.name); + } + + // check error in round trip through euler angles + for (auto i: TESTS) { + auto trip = util::from_euler (util::to_euler (i.dir)); + auto diff = i.dir - trip; + auto norm = diff.magnitude (); + + // trig functions reduce precision above almost_equal levels, so we + // hard code a fairly low bound here instead. + tap.expect_lt (norm, 1e-7, "euler round-trip error, %s", i.name); + } +} + + int main () { util::TAP::logger tap; test_polar (tap); + test_euler (tap); tap.expect (!util::vector3f::ZERO.is_normalised (), "zero isn't normalised"); tap.expect (!util::vector3f::UNIT.is_normalised (), "unit is normalised"); diff --git a/vector.cpp b/vector.cpp index 2efe07fc..a9d004ba 100644 --- a/vector.cpp +++ b/vector.cpp @@ -136,6 +136,39 @@ util::cartesian_to_polar (vector<2,T> v) } +/////////////////////////////////////////////////////////////////////////////// +template +vector<3,T> +util::from_euler (vector<2,T> euler) +{ + return { + std::sin (euler.x) * std::cos (euler.y), + std::cos (euler.x), + -std::sin (euler.x) * std::sin (euler.y), + }; +} + +template util::vector3f util::from_euler (util::vector2f); +template util::vector3d util::from_euler (util::vector2d); + + +//----------------------------------------------------------------------------- +template +vector<2,T> +util::to_euler (vector<3,T> vec) +{ + vec.normalise (); + + return { + std::acos (vec.y / vec.magnitude ()), + -std::atan2 (vec.z, vec.x), + }; +} + +template util::vector2f util::to_euler (util::vector3f); +template util::vector2d util::to_euler (util::vector3d); + + /////////////////////////////////////////////////////////////////////////////// template vector<3,T> diff --git a/vector.hpp b/vector.hpp index ce194f14..266905aa 100644 --- a/vector.hpp +++ b/vector.hpp @@ -63,6 +63,9 @@ namespace util { template vector<3,T> spherical_to_cartesian (vector<3,T>); template vector<3,T> cartesian_to_spherical (vector<3,T>); + template vector<2,T> to_euler (vector<3,T>); + template vector<3,T> from_euler (vector<2,T>); + // output and serialisation operators template std::ostream& operator<< (std::ostream&, vector);