2015-04-02 14:57:30 +11:00
|
|
|
#include "vector.hpp"
|
|
|
|
|
|
|
|
#include "maths.hpp"
|
2015-04-13 16:45:56 +10:00
|
|
|
#include "tap.hpp"
|
2015-04-02 14:57:30 +11:00
|
|
|
|
2018-01-17 17:45:21 +11:00
|
|
|
#include "float.hpp"
|
|
|
|
#include "coord/iostream.hpp"
|
|
|
|
|
2018-08-05 14:42:02 +10:00
|
|
|
using cruft::vector;
|
|
|
|
using cruft::vector2f;
|
2015-04-02 14:57:30 +11:00
|
|
|
|
|
|
|
|
2016-09-21 17:13:25 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2015-04-02 14:57:30 +11:00
|
|
|
void
|
2018-08-05 14:42:02 +10:00
|
|
|
test_polar (cruft::TAP::logger &tap)
|
2015-04-02 14:57:30 +11:00
|
|
|
{
|
|
|
|
static const struct {
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::vector2f polar;
|
|
|
|
cruft::vector2f cartesian;
|
2015-04-02 14:57:30 +11:00
|
|
|
const char *desc;
|
|
|
|
} TESTS[] {
|
|
|
|
{
|
|
|
|
{ 0.f, 0.f },
|
|
|
|
{ 0.f, 0.f },
|
|
|
|
"all zeroes"
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
{ 1.f, 0.f },
|
|
|
|
{ 1.f, 0.f },
|
|
|
|
"unit length, unrotated"
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
2018-08-05 14:42:02 +10:00
|
|
|
{ 1.f, cruft::pi<float> / 2.f },
|
2015-04-02 14:57:30 +11:00
|
|
|
{ 0.f, 1.f },
|
|
|
|
"unit length, rotated"
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
2018-08-05 14:42:02 +10:00
|
|
|
{ 1.f, 2 * cruft::pi<float> },
|
2015-04-02 14:57:30 +11:00
|
|
|
{ 1.f, 0.f },
|
|
|
|
"full rotation, unit length"
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const auto &t: TESTS) {
|
|
|
|
// Compare the difference of cartesian representations. Don't use
|
|
|
|
// direct equality comparisons here as the numeric stability can be
|
|
|
|
// poor and we have nice whole numbers to start with.
|
|
|
|
auto in_cart = t.cartesian;
|
2018-08-05 14:42:02 +10:00
|
|
|
auto to_cart = cruft::polar_to_cartesian (t.polar);
|
2015-04-02 14:57:30 +11:00
|
|
|
|
2016-08-11 14:58:46 +10:00
|
|
|
tap.expect_lt (norm (in_cart - to_cart), 0.00001f, "%s", t.desc);
|
2015-04-02 14:57:30 +11:00
|
|
|
|
|
|
|
// Compare polar representations. Make sure to normalise them first.
|
|
|
|
auto in_polar = t.polar;
|
2018-08-05 14:42:02 +10:00
|
|
|
auto to_polar = cruft::cartesian_to_polar (t.cartesian);
|
2015-04-02 14:57:30 +11:00
|
|
|
|
2018-08-05 14:42:02 +10:00
|
|
|
in_polar[1] = std::fmod (in_polar[1], 2 * cruft::pi<float>);
|
|
|
|
to_polar[1] = std::fmod (to_polar[1], 2 * cruft::pi<float>);
|
2015-04-02 14:57:30 +11:00
|
|
|
|
2016-07-28 13:36:23 +10:00
|
|
|
tap.expect_eq (in_polar, to_polar, "%s", t.desc);
|
2015-04-02 14:57:30 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-21 17:13:25 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2015-07-21 02:56:37 +10:00
|
|
|
void
|
2018-08-05 14:42:02 +10:00
|
|
|
test_euler (cruft::TAP::logger &tap)
|
2015-07-21 02:56:37 +10:00
|
|
|
{
|
|
|
|
static const struct {
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::vector3f dir;
|
|
|
|
cruft::vector2f euler;
|
2015-07-21 02:56:37 +10:00
|
|
|
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) {
|
2018-08-05 14:42:02 +10:00
|
|
|
tap.expect_eq (cruft::to_euler (i.dir),
|
|
|
|
i.euler * cruft::pi<float>,
|
2015-07-21 02:56:37 +10:00
|
|
|
"to euler, %s", i.name);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check error in round trip through euler angles
|
|
|
|
for (auto i: TESTS) {
|
2018-08-05 14:42:02 +10:00
|
|
|
auto trip = cruft::from_euler (cruft::to_euler (i.dir));
|
2015-07-21 02:56:37 +10:00
|
|
|
auto diff = i.dir - trip;
|
2016-08-11 14:58:46 +10:00
|
|
|
auto mag = norm (diff);
|
2015-07-21 02:56:37 +10:00
|
|
|
|
|
|
|
// trig functions reduce precision above almost_equal levels, so we
|
|
|
|
// hard code a fairly low bound here instead.
|
2016-08-11 14:58:46 +10:00
|
|
|
tap.expect_lt (mag, 1e-7, "euler round-trip error, %s", i.name);
|
2015-07-21 02:56:37 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-02 18:11:16 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void
|
2018-08-05 14:42:02 +10:00
|
|
|
test_spherical (cruft::TAP::logger &tap)
|
2017-11-02 18:11:16 +11:00
|
|
|
{
|
2018-08-05 14:42:02 +10:00
|
|
|
constexpr auto q = cruft::pi<float> / 2.f;
|
2018-01-17 17:45:21 +11:00
|
|
|
|
2017-11-02 18:11:16 +11:00
|
|
|
static constexpr struct {
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::vector3f spherical;
|
|
|
|
cruft::vector3f cartesian;
|
2017-11-02 18:11:16 +11:00
|
|
|
const char *message;
|
2018-01-17 17:45:21 +11:00
|
|
|
} S2C [] = {
|
|
|
|
{ { 1, 0 * q, 0 * q }, { 0, 0, 1 }, "+zero", },
|
|
|
|
{ { 1, 2 * q, 0 * q }, { 0, 0, -1 }, "-zero", },
|
2017-11-02 18:11:16 +11:00
|
|
|
|
2018-01-17 17:45:21 +11:00
|
|
|
{ { 1, 1 * q, 0 * q }, { 1, 0, 0 }, "90-theta", },
|
|
|
|
{ { 1, 2 * q, 0 * q }, { 0, 0, -1 }, "180-theta", },
|
|
|
|
{ { 1, 3 * q, 0 * q }, { -1, 0, 0 }, "270-theta", },
|
2017-11-02 18:11:16 +11:00
|
|
|
|
2018-01-17 17:45:21 +11:00
|
|
|
{ { 1, 0 * q, 1 * q }, { 0, 0, 1 }, "90-phi", },
|
|
|
|
{ { 1, 0 * q, 2 * q }, { 0, 0, 1 }, "180-phi", },
|
|
|
|
{ { 1, 0 * q, 3 * q }, { 0, 0, 1 }, "270-phi", },
|
2017-11-02 18:11:16 +11:00
|
|
|
|
2018-01-17 17:45:21 +11:00
|
|
|
{ { 1, 1 * q, 1 * q }, { 0, 1, 0 }, "90-theta, 90-phi" },
|
|
|
|
{ { 1, 1 * q, 2 * q }, { -1, 0, 0 }, "90-theta, 180-phi" },
|
|
|
|
{ { 1, 1 * q, 3 * q }, { 0, -1, 0 }, "90-theta, 270-phi" },
|
2017-11-02 18:11:16 +11:00
|
|
|
};
|
|
|
|
|
2018-01-17 17:45:21 +11:00
|
|
|
for (const auto t: S2C) {
|
|
|
|
tap.expect (
|
2018-08-05 14:42:02 +10:00
|
|
|
all (abs (cruft::spherical_to_cartesian (t.spherical) - t.cartesian) < 1e-7f),
|
2017-11-02 18:11:16 +11:00
|
|
|
"%s, spherical-cartesian",
|
|
|
|
t.message
|
|
|
|
);
|
2018-01-17 17:45:21 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// double check origin points are transformed correctly. the zeros and
|
|
|
|
// multiple representations need to be accounted for.
|
|
|
|
tap.expect_eq (
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::cartesian_to_spherical (cruft::vector3f{0}).x,
|
2018-01-17 17:45:21 +11:00
|
|
|
0,
|
|
|
|
"origin, cartesian-spherical"
|
|
|
|
);
|
|
|
|
|
|
|
|
tap.expect_eq (
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::spherical_to_cartesian (cruft::vector3f{0,1,-1}),
|
|
|
|
cruft::vector3f{0},
|
2018-01-17 17:45:21 +11:00
|
|
|
"origin, cartesian-spherical"
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
// dedicated cartesian-spherical test cases. it's hard to use the
|
|
|
|
// spherical-cartesian cases from above because. some of the tests don't
|
|
|
|
// have unique representations in spherical space,
|
|
|
|
// eg, {1,0,z} is always {0,0,1})
|
|
|
|
// so the reverse transform isn't usable as a test.
|
|
|
|
static constexpr struct {
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::vector3f cartesian;
|
|
|
|
cruft::vector3f spherical;
|
2018-01-17 17:45:21 +11:00
|
|
|
const char *message;
|
|
|
|
} C2S[] = {
|
|
|
|
{ { 0, 0, 1 }, { 1, 0, 0 }, "+z" },
|
|
|
|
{ { 0, 0, -1 }, { 1, q*2, 0 }, "-z" },
|
|
|
|
|
|
|
|
{ { 0, 1, 0 }, { 1, q, q }, "+y" },
|
|
|
|
{ { 0, -1, 0 }, { 1, q, -q }, "-y" },
|
|
|
|
|
|
|
|
{ { 1, 0, 0 }, { 1, q, 0 }, "+x" },
|
|
|
|
{ { -1, 0, 0 }, { 1, q, q*2 }, "-x" },
|
|
|
|
|
|
|
|
{ { 1, 1, 1 }, { 1.732f, 0.95539f, q/2 }, "+ve" },
|
|
|
|
{ { -1, -1, -1 }, { 1.732f, 2.18619942f, -2.35619f}, "-ve" },
|
|
|
|
|
|
|
|
{ { 9, 9, 9 }, { 15.5885f, 0.955317f, q/2 }, "+9ve" },
|
|
|
|
};
|
|
|
|
|
2017-11-02 18:11:16 +11:00
|
|
|
|
2018-01-17 17:45:21 +11:00
|
|
|
for (const auto &t: C2S) {
|
2018-08-05 14:42:02 +10:00
|
|
|
const auto s0 = cruft::canonical_spherical (cruft::cartesian_to_spherical (t.cartesian));
|
|
|
|
const auto s1 = cruft::canonical_spherical (t.spherical);
|
2018-01-17 17:45:21 +11:00
|
|
|
tap.expect (
|
|
|
|
all (abs (s0 - s1) < 1e-4f),
|
2017-11-02 18:11:16 +11:00
|
|
|
"%s, cartesian-spherical",
|
|
|
|
t.message
|
|
|
|
);
|
|
|
|
}
|
2018-08-05 14:42:02 +10:00
|
|
|
//auto s = cruft::cartesian_to_spherical (t.cartesian);
|
2018-01-17 17:45:21 +11:00
|
|
|
//std::clog << s << " == " << t.spherical << '\n';
|
|
|
|
//tap.expect_eq (
|
2018-08-05 14:42:02 +10:00
|
|
|
// cruft::cartesian_to_spherical (t.cartesian),
|
2018-01-17 17:45:21 +11:00
|
|
|
// t.spherical,
|
|
|
|
// "%s, cartesian-spherical",
|
|
|
|
// t.message
|
|
|
|
//);
|
2017-11-02 18:11:16 +11:00
|
|
|
|
|
|
|
|
|
|
|
{
|
2018-08-05 14:42:02 +10:00
|
|
|
//cruft::vector3f s { 1, .5f, 2/3.f };
|
|
|
|
//cruft::vector3f c { 0.35f, 0.61f, 0.71f };
|
2017-11-02 18:11:16 +11:00
|
|
|
//tap.expect_eq
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-09-21 17:13:25 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2015-04-02 14:57:30 +11:00
|
|
|
int
|
|
|
|
main ()
|
|
|
|
{
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::TAP::logger tap;
|
2015-04-15 13:48:51 +10:00
|
|
|
|
2015-07-21 02:55:11 +10:00
|
|
|
test_polar (tap);
|
2015-07-21 02:56:37 +10:00
|
|
|
test_euler (tap);
|
2017-11-02 18:11:16 +11:00
|
|
|
test_spherical (tap);
|
2015-04-15 13:48:51 +10:00
|
|
|
|
2018-08-05 14:42:02 +10:00
|
|
|
tap.expect (!is_normalised (cruft::vector3f::zeros ()), "zeros isn't normalised");
|
|
|
|
tap.expect (!is_normalised (cruft::vector3f::ones ()), "ones isn't normalised");
|
2015-04-15 13:48:51 +10:00
|
|
|
|
2016-09-21 17:13:25 +10:00
|
|
|
tap.expect_eq (
|
2018-08-05 14:42:02 +10:00
|
|
|
cruft::hypot (cruft::vector3f{0,1,2} - cruft::vector3f{3,2,4}),
|
2016-09-21 17:13:25 +10:00
|
|
|
std::sqrt (14.f),
|
|
|
|
"vector3f hypot"
|
|
|
|
);
|
|
|
|
|
2015-07-21 02:55:11 +10:00
|
|
|
return tap.status ();
|
2015-04-02 14:57:30 +11:00
|
|
|
}
|