mirror of
https://github.com/luanti-org/luanti.git
synced 2025-08-11 17:51:04 +00:00
Fix set/getRotationRadians unit test
Gimbal lock is a situation where the pitch (the middle angle) of the Tait-Bryan angles (usually called Euler angles incorrectly) is 90 degrees. If the angles specify a rotation close to gimbal lock, the precision requirements increase significantly, beyond what a single-precision float can provide, and at exactly gimbal lock, there's a loss of information. The test didn't take this into account. Fix this by decreasing the expected precision when close to gimbal lock. The increased error rate on ARM Macs is probably caused by lesser precision in trigonometric functions. IEC-559 does not specify any semantics for those, and while Intel typically has a precision < 1 ulp for trigonometric functions with angles < 2*pi, it's likely that ARM's precision is a bit worse.
This commit is contained in:
parent
ecc876045f
commit
ae97435d80
1 changed files with 20 additions and 7 deletions
|
@ -113,7 +113,9 @@ SECTION("getScale") {
|
|||
}
|
||||
|
||||
SECTION("getRotationRadians") {
|
||||
auto test_rotation_degrees = [](v3f rad, v3f scale) {
|
||||
// Test that we can correctly extract a previously set rotation,
|
||||
// even after applying a scale, with reasonable precision.
|
||||
auto test_rotation_radians = [](v3f rad, v3f scale) {
|
||||
matrix4 S;
|
||||
S.setScale(scale);
|
||||
matrix4 R;
|
||||
|
@ -121,12 +123,23 @@ SECTION("getRotationRadians") {
|
|||
v3f rot = (R * S).getRotationRadians();
|
||||
matrix4 B;
|
||||
B.setRotationRadians(rot);
|
||||
// TODO we would like to use a lower tolerance here, but that makes the test likely to fail.
|
||||
// Decrease the precision when the angles are close to gimbal lock, as
|
||||
// that breaks the expectations of precision with Tait-Bryan angles.
|
||||
// Gimbal lock happens when pitch (the angle applied second) is 90 deg
|
||||
if (std::abs(std::abs(rot.Y) - QUARTER_TURN) < 0.01f) {
|
||||
f32 tol = 0.001f;
|
||||
CHECK(matrix_equals(R, B, tol));
|
||||
} else {
|
||||
CHECK(matrix_equals(R, B));
|
||||
}
|
||||
};
|
||||
SECTION("returns a rotation equivalent to the original rotation") {
|
||||
test_rotation_degrees({1.0f, 2.0f, 3.0f}, v3f(1));
|
||||
test_rotation_radians({1.0f, 2.0f, 3.0f}, v3f(1));
|
||||
// Test cases at or near gimbal lock. These cases fail when using a
|
||||
// smaller tolerance.
|
||||
test_rotation_radians({1.0f, QUARTER_TURN, 3.0f}, v3f(10.f));
|
||||
test_rotation_radians({1.0f, QUARTER_TURN + 0.01001f, 3.0f}, v3f(10.f));
|
||||
test_rotation_radians({1.0f, QUARTER_TURN + 0.00999f, 3.0f}, v3f(10.f));
|
||||
Catch::Generators::RandomFloatingGenerator<f32> gen_angle(0.0f, 2 * core::PI, Catch::getSeed());
|
||||
Catch::Generators::RandomFloatingGenerator<f32> gen_scale(0.1f, 10, Catch::getSeed());
|
||||
auto draw = [](auto &gen) {
|
||||
|
@ -138,13 +151,13 @@ SECTION("getRotationRadians") {
|
|||
return v3f{draw(gen), draw(gen), draw(gen)};
|
||||
};
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
test_rotation_degrees(draw_v3f(gen_angle), draw_v3f(gen_scale));
|
||||
test_rotation_radians(draw_v3f(gen_angle), draw_v3f(gen_scale));
|
||||
for (f32 i = 0; i < 4; ++i)
|
||||
for (f32 j = 0; j < 4; ++j)
|
||||
for (f32 k = 0; k < 4; ++k) {
|
||||
v3f rad = core::PI / 4.0f * v3f(i, j, k);
|
||||
for (int l = 0; l < 100; ++l) {
|
||||
test_rotation_degrees(rad, draw_v3f(gen_scale));
|
||||
test_rotation_radians(rad, draw_v3f(gen_scale));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue