From 998f63bcb21d3eff81caff0057e4d7024c9d49f5 Mon Sep 17 00:00:00 2001 From: Squareys Date: Wed, 4 Nov 2015 19:58:47 +0100 Subject: [PATCH] Math: Fix slerp interpolation of equal quaternions and add test. If and only if the quaternions to interpolate are equal, the angle between them is 0.0 and we therefore cannot safely divide by the sin of that angle. Credits to @wivlaro for finding this one. Fixes #117. Signed-off-by: Squareys --- src/Magnum/Math/Quaternion.h | 8 +++++++- src/Magnum/Math/Test/QuaternionTest.cpp | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Math/Quaternion.h b/src/Magnum/Math/Quaternion.h index 119b55877..cb0232d05 100644 --- a/src/Magnum/Math/Quaternion.h +++ b/src/Magnum/Math/Quaternion.h @@ -112,7 +112,13 @@ Expects that both quaternions are normalized. @f[ template inline Quaternion slerp(const Quaternion& normalizedA, const Quaternion& normalizedB, T t) { CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), "Math::slerp(): quaternions must be normalized", {}); - const T a = Implementation::angle(normalizedA, normalizedB); + const T cosHalfAngle = dot(normalizedA, normalizedB); + if(std::abs(cosHalfAngle) >= T(1)) { + /* The angle `a` between the quaternions `A` and `B` is 0 and we cannot + divide by sin(a). This is the case for `A == B` or `A == -B`. */ + return Quaternion{normalizedA}; + } + const T a = std::acos(cosHalfAngle); return (std::sin((T(1) - t)*a)*normalizedA + std::sin(t*a)*normalizedB)/std::sin(a); } diff --git a/src/Magnum/Math/Test/QuaternionTest.cpp b/src/Magnum/Math/Test/QuaternionTest.cpp index c7aabe50d..33cf20b0b 100644 --- a/src/Magnum/Math/Test/QuaternionTest.cpp +++ b/src/Magnum/Math/Test/QuaternionTest.cpp @@ -438,6 +438,9 @@ void QuaternionTest::slerp() { Quaternion slerp = Math::slerp(a, b, 0.35f); CORRADE_COMPARE(slerp, Quaternion({0.1191653f, 0.0491109f, 0.0491109f}, 0.9904423f)); + + CORRADE_COMPARE(Math::slerp(Quaternion(), Quaternion(), 0.25f), Quaternion()); + CORRADE_COMPARE(Math::slerp(Quaternion(), -Quaternion(), 0.42f), Quaternion()); } void QuaternionTest::transformVector() {