From cdcefa43e33f2ef1fbf2dd01bb31374f91fc0523 Mon Sep 17 00:00:00 2001 From: Iliya Katushenock Date: Thu, 6 Jun 2024 19:26:40 +0200 Subject: [PATCH] Fix #119703: Incorrect conversion of identity quaternion to axis angle Fix of conversion identity quaternion to axis angle. Basically, if the length of the imaginary part-vector is zero, it is incorrect to normalize it. Simple identity should be returned. Pull Request: https://projects.blender.org/blender/blender/pulls/119762 --- source/blender/blenlib/BLI_math_quaternion.hh | 7 +++++-- .../blenlib/tests/BLI_math_rotation_test.cc | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/source/blender/blenlib/BLI_math_quaternion.hh b/source/blender/blenlib/BLI_math_quaternion.hh index 4c0be97f225..9f18022bc9b 100644 --- a/source/blender/blenlib/BLI_math_quaternion.hh +++ b/source/blender/blenlib/BLI_math_quaternion.hh @@ -630,8 +630,11 @@ AxisAngleBase to_axis_angle(const QuaternionBase &quat) T sin_half_angle = math::length(axis); /* Prevent division by zero for axis conversion. */ if (sin_half_angle < T(0.0005)) { - sin_half_angle = T(1); - axis[1] = T(1); + using AngleAxisT = typename AxisAngleBase::vec3_type; + const AngleAxisT identity_axis = AxisAngleBase::identity().axis(); + BLI_assert(abs(cos_half_angle) > 0.0005); + AxisAngleBase identity(identity_axis * sign(cos_half_angle), AngleT(0)); + return identity; } /* Normalize the axis. */ axis /= sin_half_angle; diff --git a/source/blender/blenlib/tests/BLI_math_rotation_test.cc b/source/blender/blenlib/tests/BLI_math_rotation_test.cc index 8a04184532f..f5d0ed7cfb8 100644 --- a/source/blender/blenlib/tests/BLI_math_rotation_test.cc +++ b/source/blender/blenlib/tests/BLI_math_rotation_test.cc @@ -879,4 +879,22 @@ TEST(math_rotation, DualQuaternionTransform) } } +TEST(math_axis_angle, AxisAngleFromQuaternion) +{ + { + const math::AxisAngle axis_angle({0.0f, 1.0f, 0.0f}, math::AngleRadian(0)); + const math::Quaternion quaternion(1.0f, {0.0f, 0.0f, 0.0f}); + const math::AxisAngle from_quaternion = math::to_axis_angle(quaternion); + EXPECT_V3_NEAR(axis_angle.axis(), from_quaternion.axis(), 1e-6); + EXPECT_NEAR(axis_angle.angle().radian(), from_quaternion.angle().radian(), 1e-6); + } + { + const math::AxisAngle axis_angle({0.0f, -1.0f, 0.0f}, math::AngleRadian(0)); + const math::Quaternion quaternion(-1.0f, {0.0f, 0.0f, 0.0f}); + const math::AxisAngle from_quaternion = math::to_axis_angle(quaternion); + EXPECT_V3_NEAR(axis_angle.axis(), from_quaternion.axis(), 1e-6); + EXPECT_NEAR(axis_angle.angle().radian(), from_quaternion.angle().radian(), 1e-6); + } +} + } // namespace blender::math::tests