diff options
author | Alexander Gavrilov <angavrilov@gmail.com> | 2020-12-13 21:41:08 +0300 |
---|---|---|
committer | Alexander Gavrilov <angavrilov@gmail.com> | 2020-12-13 22:06:01 +0300 |
commit | 9c0df8e27533657f49919e45d46c85c847048d8f (patch) | |
tree | 8437042e03cc707ad6b9f0af1ef9498a5c4a9cd8 /source/blender | |
parent | c6075118d5d35657f9adcd6867b9655962e7386c (diff) |
Fix weird Swing+Twist decomposition with noncanonical quaternions.
It turns out that after the fix to T83196 (rB814b2787cadd) the matrix
to quaternion conversion can produce noncanonical results in large
areas of the rotation space, when previously this was limited to
way smaller areas. This in turn causes Swing+Twist math to produce
angles beyond 180 degrees, e.g. outputting a -120..240 range.
This fixes both issues, ensuring that conversion outputs a canonical
result, and decomposition canonifies its input.
This was reported in chat by @jpbouza.
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenlib/intern/math_rotation.c | 17 | ||||
-rw-r--r-- | source/blender/blenlib/tests/BLI_math_rotation_test.cc | 20 |
2 files changed, 36 insertions, 1 deletions
diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index a0ee16bee76..19828e69638 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -372,6 +372,11 @@ void mat3_normalized_to_quat(float q[4], const float mat[3][3]) q[1] = (mat[2][0] + mat[0][2]) * s; q[2] = (mat[2][1] + mat[1][2]) * s; } + + /* Make sure w is nonnegative for a canonical result. */ + if (q[0] < 0) { + negate_v4(q); + } } normalize_qt(q); @@ -556,10 +561,20 @@ void rotation_between_quats_to_quat(float q[4], const float q1[4], const float q * \param r_twist: if not NULL, receives the twist quaternion. * \returns twist angle. */ -float quat_split_swing_and_twist(const float q[4], int axis, float r_swing[4], float r_twist[4]) +float quat_split_swing_and_twist(const float q_in[4], int axis, float r_swing[4], float r_twist[4]) { BLI_assert(axis >= 0 && axis <= 2); + /* The calculation requires a canonical quaternion. */ + float q[4]; + + if (q_in[0] < 0) { + negate_v4_v4(q, q_in); + } + else { + copy_v4_v4(q, q_in); + } + /* Half-twist angle can be computed directly. */ float t = atan2f(q[axis + 1], q[0]); diff --git a/source/blender/blenlib/tests/BLI_math_rotation_test.cc b/source/blender/blenlib/tests/BLI_math_rotation_test.cc index 02257ba83dd..90fd4f8c2e6 100644 --- a/source/blender/blenlib/tests/BLI_math_rotation_test.cc +++ b/source/blender/blenlib/tests/BLI_math_rotation_test.cc @@ -71,6 +71,12 @@ TEST(math_rotation, quat_to_mat_to_quat_bad_T83196) test_quat_to_mat_to_quat(0.0149f, 0.9996f, -0.0212f, -0.0107f); } +TEST(math_rotation, quat_to_mat_to_quat_bad_negative) +{ + /* This shouldn't produce a negative q[0]. */ + test_quat_to_mat_to_quat(0.5f - 1e-6f, 0, -sqrtf(3) / 2 - 1e-6f, 0); +} + TEST(math_rotation, quat_to_mat_to_quat_near_1000) { test_quat_to_mat_to_quat(0.9999f, 0.01f, -0.001f, -0.01f); @@ -126,3 +132,17 @@ TEST(math_rotation, quat_to_mat_to_quat_near_0001) test_quat_to_mat_to_quat(0.25f, -0.025f, -0.25f, 0.97f); test_quat_to_mat_to_quat(0.30f, -0.030f, -0.30f, 0.95f); } + +TEST(math_rotation, quat_split_swing_and_twist_negative) +{ + const float input[4] = {-0.5f, 0, sqrtf(3) / 2, 0}; + const float expected_swing[4] = {1.0f, 0, 0, 0}; + const float expected_twist[4] = {0.5f, 0, -sqrtf(3) / 2, 0}; + float swing[4], twist[4]; + + float twist_angle = quat_split_swing_and_twist(input, 1, swing, twist); + + EXPECT_NEAR(twist_angle, -M_PI * 2 / 3, FLT_EPSILON); + EXPECT_V4_NEAR(swing, expected_swing, FLT_EPSILON); + EXPECT_V4_NEAR(twist, expected_twist, FLT_EPSILON); +} |