Browse Source

Math: added convenience {Quaternion,DualQuaternion}::isNormalized().

pull/278/head
Vladimír Vondruš 13 years ago
parent
commit
b00554fd7f
  1. 31
      src/Math/DualQuaternion.h
  2. 49
      src/Math/Quaternion.h
  3. 9
      src/Math/Test/DualQuaternionTest.cpp
  4. 9
      src/Math/Test/QuaternionTest.cpp

31
src/Math/DualQuaternion.h

@ -56,7 +56,7 @@ template<class T> class DualQuaternion: public Dual<Quaternion<T>> {
* @f] * @f]
* @see rotationAngle(), rotationAxis(), Quaternion::rotation(), * @see rotationAngle(), rotationAxis(), Quaternion::rotation(),
* Matrix4::rotation(), DualComplex::rotation(), Vector3::xAxis(), * Matrix4::rotation(), DualComplex::rotation(), Vector3::xAxis(),
* Vector3::yAxis(), Vector3::zAxis() * Vector3::yAxis(), Vector3::zAxis(), Vector::isNormalized()
*/ */
inline static DualQuaternion<T> rotation(Rad<T> angle, const Vector3<T>& normalizedAxis) { inline static DualQuaternion<T> rotation(Rad<T> angle, const Vector3<T>& normalizedAxis) {
return {Quaternion<T>::rotation(angle, normalizedAxis), {{}, T(0)}}; return {Quaternion<T>::rotation(angle, normalizedAxis), {{}, T(0)}};
@ -132,6 +132,18 @@ template<class T> class DualQuaternion: public Dual<Quaternion<T>> {
inline constexpr explicit DualQuaternion(const Vector3<T>& vector): Dual<Quaternion<T>>({}, {vector, T(0)}) {} inline constexpr explicit DualQuaternion(const Vector3<T>& vector): Dual<Quaternion<T>>({}, {vector, T(0)}) {}
#endif #endif
/**
* @brief Whether the dual quaternion is normalized
*
* Dual quaternion is normalized if it has unit length: @f[
* |\hat q|^2 = |\hat q| = 1 + \epsilon 0
* @f]
* @see lengthSquared(), normalized()
*/
inline bool isNormalized() const {
return lengthSquared() == Dual<T>(1);
}
/** /**
* @brief Rotation angle of unit dual quaternion * @brief Rotation angle of unit dual quaternion
* *
@ -240,7 +252,11 @@ template<class T> class DualQuaternion: public Dual<Quaternion<T>> {
return Math::sqrt(lengthSquared()); return Math::sqrt(lengthSquared());
} }
/** @brief Normalized dual quaternion (of unit length) */ /**
* @brief Normalized dual quaternion (of unit length)
*
* @see isNormalized()
*/
inline DualQuaternion<T> normalized() const { inline DualQuaternion<T> normalized() const {
return (*this)/length(); return (*this)/length();
} }
@ -264,10 +280,10 @@ template<class T> class DualQuaternion: public Dual<Quaternion<T>> {
* normalized. @f[ * normalized. @f[
* \hat q^{-1} = \frac{\hat q^*}{|\hat q|^2} = \hat q^* * \hat q^{-1} = \frac{\hat q^*}{|\hat q|^2} = \hat q^*
* @f] * @f]
* @see inverted() * @see isNormalized(), inverted()
*/ */
inline DualQuaternion<T> invertedNormalized() const { inline DualQuaternion<T> invertedNormalized() const {
CORRADE_ASSERT(lengthSquared() == Dual<T>(1), CORRADE_ASSERT(isNormalized(),
"Math::DualQuaternion::invertedNormalized(): dual quaternion must be normalized", {}); "Math::DualQuaternion::invertedNormalized(): dual quaternion must be normalized", {});
return quaternionConjugated(); return quaternionConjugated();
} }
@ -293,11 +309,12 @@ template<class T> class DualQuaternion: public Dual<Quaternion<T>> {
* quaternion is normalized. @f[ * quaternion is normalized. @f[
* v' = \hat q v \overline{\hat q^{-1}} = \hat q v \overline{\hat q^*} = \hat q ([\boldsymbol 0, 1] + \epsilon [\boldsymbol v, 0]) \overline{\hat q^*} * v' = \hat q v \overline{\hat q^{-1}} = \hat q v \overline{\hat q^*} = \hat q ([\boldsymbol 0, 1] + \epsilon [\boldsymbol v, 0]) \overline{\hat q^*}
* @f] * @f]
* @see DualQuaternion(const Vector3&), dual(), Matrix4::transformPoint(), * @see isNormalized(), DualQuaternion(const Vector3&), dual(),
* Quaternion::transformVectorNormalized(), DualComplex::transformPointNormalized() * Matrix4::transformPoint(), Quaternion::transformVectorNormalized(),
* DualComplex::transformPointNormalized()
*/ */
inline Vector3<T> transformPointNormalized(const Vector3<T>& vector) const { inline Vector3<T> transformPointNormalized(const Vector3<T>& vector) const {
CORRADE_ASSERT(lengthSquared() == Dual<T>(1), CORRADE_ASSERT(isNormalized(),
"Math::DualQuaternion::transformPointNormalized(): dual quaternion must be normalized", "Math::DualQuaternion::transformPointNormalized(): dual quaternion must be normalized",
Vector3<T>(std::numeric_limits<T>::quiet_NaN())); Vector3<T>(std::numeric_limits<T>::quiet_NaN()));
return ((*this)*DualQuaternion<T>(vector)*conjugated()).dual().vector(); return ((*this)*DualQuaternion<T>(vector)*conjugated()).dual().vector();

49
src/Math/Quaternion.h

@ -107,10 +107,10 @@ template<class T> class Quaternion {
* Expects that both quaternions are normalized. @f[ * Expects that both quaternions are normalized. @f[
* \theta = acos \left( \frac{p \cdot q}{|p| |q|} \right) = acos(p \cdot q) * \theta = acos \left( \frac{p \cdot q}{|p| |q|} \right) = acos(p \cdot q)
* @f] * @f]
* @see Complex::angle(), Vector::angle() * @see isNormalized(), Complex::angle(), Vector::angle()
*/ */
inline static Rad<T> angle(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB) { inline static Rad<T> angle(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB) {
CORRADE_ASSERT(TypeTraits<T>::equals(normalizedA.dot(), T(1)) && TypeTraits<T>::equals(normalizedB.dot(), T(1)), CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::Quaternion::angle(): quaternions must be normalized", Rad<T>(std::numeric_limits<T>::quiet_NaN())); "Math::Quaternion::angle(): quaternions must be normalized", Rad<T>(std::numeric_limits<T>::quiet_NaN()));
return Rad<T>(angleInternal(normalizedA, normalizedB)); return Rad<T>(angleInternal(normalizedA, normalizedB));
} }
@ -124,10 +124,10 @@ template<class T> class Quaternion {
* Expects that both quaternions are normalized. @f[ * Expects that both quaternions are normalized. @f[
* q_{LERP} = \frac{(1 - t) q_A + t q_B}{|(1 - t) q_A + t q_B|} * q_{LERP} = \frac{(1 - t) q_A + t q_B}{|(1 - t) q_A + t q_B|}
* @f] * @f]
* @see slerp(), Math::lerp() * @see isNormalized(), slerp(), Math::lerp()
*/ */
inline static Quaternion<T> lerp(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB, T t) { inline static Quaternion<T> lerp(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB, T t) {
CORRADE_ASSERT(TypeTraits<T>::equals(normalizedA.dot(), T(1)) && TypeTraits<T>::equals(normalizedB.dot(), T(1)), CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::Quaternion::lerp(): quaternions must be normalized", "Math::Quaternion::lerp(): quaternions must be normalized",
Quaternion<T>({}, std::numeric_limits<T>::quiet_NaN())); Quaternion<T>({}, std::numeric_limits<T>::quiet_NaN()));
return ((T(1) - t)*normalizedA + t*normalizedB).normalized(); return ((T(1) - t)*normalizedA + t*normalizedB).normalized();
@ -144,10 +144,10 @@ template<class T> class Quaternion {
* ~~~~~~~~~~ * ~~~~~~~~~~
* \theta = acos \left( \frac{q_A \cdot q_B}{|q_A| \cdot |q_B|} \right) = acos(q_A \cdot q_B) * \theta = acos \left( \frac{q_A \cdot q_B}{|q_A| \cdot |q_B|} \right) = acos(q_A \cdot q_B)
* @f] * @f]
* @see lerp() * @see isNormalized(), lerp()
*/ */
inline static Quaternion<T> slerp(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB, T t) { inline static Quaternion<T> slerp(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB, T t) {
CORRADE_ASSERT(TypeTraits<T>::equals(normalizedA.dot(), T(1)) && TypeTraits<T>::equals(normalizedB.dot(), T(1)), CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::Quaternion::slerp(): quaternions must be normalized", "Math::Quaternion::slerp(): quaternions must be normalized",
Quaternion<T>({}, std::numeric_limits<T>::quiet_NaN())); Quaternion<T>({}, std::numeric_limits<T>::quiet_NaN()));
T a = angleInternal(normalizedA, normalizedB); T a = angleInternal(normalizedA, normalizedB);
@ -164,10 +164,10 @@ template<class T> class Quaternion {
* @f] * @f]
* @see rotationAngle(), rotationAxis(), DualQuaternion::rotation(), * @see rotationAngle(), rotationAxis(), DualQuaternion::rotation(),
* Matrix4::rotation(), Complex::rotation(), Vector3::xAxis(), * Matrix4::rotation(), Complex::rotation(), Vector3::xAxis(),
* Vector3::yAxis(), Vector3::zAxis() * Vector3::yAxis(), Vector3::zAxis(), Vector::isNormalized()
*/ */
inline static Quaternion<T> rotation(Rad<T> angle, const Vector3<T>& normalizedAxis) { inline static Quaternion<T> rotation(Rad<T> angle, const Vector3<T>& normalizedAxis) {
CORRADE_ASSERT(TypeTraits<T>::equals(normalizedAxis.dot(), T(1)), CORRADE_ASSERT(normalizedAxis.isNormalized(),
"Math::Quaternion::rotation(): axis must be normalized", {}); "Math::Quaternion::rotation(): axis must be normalized", {});
return {normalizedAxis*std::sin(T(angle)/2), std::cos(T(angle)/2)}; return {normalizedAxis*std::sin(T(angle)/2), std::cos(T(angle)/2)};
@ -223,6 +223,18 @@ template<class T> class Quaternion {
return !operator==(other); return !operator==(other);
} }
/**
* @brief Whether the quaternion is normalized
*
* Quaternion is normalized if it has unit length: @f[
* |q|^2 = |q| = 1
* @f]
* @see dot(), normalized()
*/
inline bool isNormalized() const {
return TypeTraits<T>::equals(dot(), T(1));
}
/** @brief %Vector part */ /** @brief %Vector part */
inline constexpr Vector3<T> vector() const { return _vector; } inline constexpr Vector3<T> vector() const { return _vector; }
@ -238,7 +250,7 @@ template<class T> class Quaternion {
* @see rotationAxis(), rotation(), DualQuaternion::rotationAngle() * @see rotationAxis(), rotation(), DualQuaternion::rotationAngle()
*/ */
inline Rad<T> rotationAngle() const { inline Rad<T> rotationAngle() const {
CORRADE_ASSERT(TypeTraits<T>::equals(dot(), T(1)), CORRADE_ASSERT(isNormalized(),
"Math::Quaternion::rotationAngle(): quaternion must be normalized", "Math::Quaternion::rotationAngle(): quaternion must be normalized",
Rad<T>(std::numeric_limits<T>::quiet_NaN())); Rad<T>(std::numeric_limits<T>::quiet_NaN()));
return Rad<T>(T(2)*std::acos(_scalar)); return Rad<T>(T(2)*std::acos(_scalar));
@ -255,7 +267,7 @@ template<class T> class Quaternion {
* @see rotationAngle(), rotation() * @see rotationAngle(), rotation()
*/ */
inline Vector3<T> rotationAxis() const { inline Vector3<T> rotationAxis() const {
CORRADE_ASSERT(TypeTraits<T>::equals(dot(), T(1)), CORRADE_ASSERT(isNormalized(),
"Math::Quaternion::rotationAxis(): quaternion must be normalized", "Math::Quaternion::rotationAxis(): quaternion must be normalized",
{}); {});
return _vector/std::sqrt(1-pow2(_scalar)); return _vector/std::sqrt(1-pow2(_scalar));
@ -400,7 +412,7 @@ template<class T> class Quaternion {
* with other values, because it doesn't compute the square root. @f[ * with other values, because it doesn't compute the square root. @f[
* q \cdot q = \boldsymbol q_V \cdot \boldsymbol q_V + q_S^2 * q \cdot q = \boldsymbol q_V \cdot \boldsymbol q_V + q_S^2
* @f] * @f]
* @see dot(const Quaternion&, const Quaternion&) * @see isNormalized(), dot(const Quaternion&, const Quaternion&)
*/ */
inline T dot() const { inline T dot() const {
return dot(*this, *this); return dot(*this, *this);
@ -413,12 +425,17 @@ template<class T> class Quaternion {
* values. @f[ * values. @f[
* |q| = \sqrt{q \cdot q} * |q| = \sqrt{q \cdot q}
* @f] * @f]
* @see isNormalized()
*/ */
inline T length() const { inline T length() const {
return std::sqrt(dot()); return std::sqrt(dot());
} }
/** @brief Normalized quaternion (of unit length) */ /**
* @brief Normalized quaternion (of unit length)
*
* @see isNormalized()
*/
inline Quaternion<T> normalized() const { inline Quaternion<T> normalized() const {
return (*this)/length(); return (*this)/length();
} }
@ -453,10 +470,10 @@ template<class T> class Quaternion {
* normalized. @f[ * normalized. @f[
* q^{-1} = \frac{q^*}{|q|^2} = q^* * q^{-1} = \frac{q^*}{|q|^2} = q^*
* @f] * @f]
* @see inverted() * @see isNormalized(), inverted()
*/ */
inline Quaternion<T> invertedNormalized() const { inline Quaternion<T> invertedNormalized() const {
CORRADE_ASSERT(TypeTraits<T>::equals(dot(), T(1)), CORRADE_ASSERT(isNormalized(),
"Math::Quaternion::invertedNormalized(): quaternion must be normalized", "Math::Quaternion::invertedNormalized(): quaternion must be normalized",
Quaternion<T>({}, std::numeric_limits<T>::quiet_NaN())); Quaternion<T>({}, std::numeric_limits<T>::quiet_NaN()));
return conjugated(); return conjugated();
@ -483,11 +500,11 @@ template<class T> class Quaternion {
* is normalized. @f[ * is normalized. @f[
* v' = qvq^{-1} = qvq^* = q [\boldsymbol v, 0] q^* * v' = qvq^{-1} = qvq^* = q [\boldsymbol v, 0] q^*
* @f] * @f]
* @see Quaternion(const Vector3&), vector(), Matrix4::transformVector(), * @see isNormalized(), Quaternion(const Vector3&), vector(), Matrix4::transformVector(),
* DualQuaternion::transformPointNormalized(), Complex::transformVector() * DualQuaternion::transformPointNormalized(), Complex::transformVector()
*/ */
inline Vector3<T> transformVectorNormalized(const Vector3<T>& vector) const { inline Vector3<T> transformVectorNormalized(const Vector3<T>& vector) const {
CORRADE_ASSERT(TypeTraits<T>::equals(dot(), T(1)), CORRADE_ASSERT(isNormalized(),
"Math::Quaternion::transformVectorNormalized(): quaternion must be normalized", "Math::Quaternion::transformVectorNormalized(): quaternion must be normalized",
Vector3<T>(std::numeric_limits<T>::quiet_NaN())); Vector3<T>(std::numeric_limits<T>::quiet_NaN()));
return ((*this)*Quaternion<T>(vector)*conjugated()).vector(); return ((*this)*Quaternion<T>(vector)*conjugated()).vector();

9
src/Math/Test/DualQuaternionTest.cpp

@ -37,6 +37,8 @@ class DualQuaternionTest: public Corrade::TestSuite::Tester {
void constructDefault(); void constructDefault();
void constructFromVector(); void constructFromVector();
void isNormalized();
void constExpressions(); void constExpressions();
void lengthSquared(); void lengthSquared();
@ -72,6 +74,8 @@ DualQuaternionTest::DualQuaternionTest() {
&DualQuaternionTest::constructDefault, &DualQuaternionTest::constructDefault,
&DualQuaternionTest::constructFromVector, &DualQuaternionTest::constructFromVector,
&DualQuaternionTest::isNormalized,
&DualQuaternionTest::constExpressions, &DualQuaternionTest::constExpressions,
&DualQuaternionTest::lengthSquared, &DualQuaternionTest::lengthSquared,
@ -109,6 +113,11 @@ void DualQuaternionTest::constructFromVector() {
CORRADE_COMPARE(DualQuaternion(Vector3(1.0f, 2.0f, 3.0f)), DualQuaternion({{0.0f, 0.0f, 0.0f}, 1.0f}, {{1.0f, 2.0f, 3.0f}, 0.0f})); CORRADE_COMPARE(DualQuaternion(Vector3(1.0f, 2.0f, 3.0f)), DualQuaternion({{0.0f, 0.0f, 0.0f}, 1.0f}, {{1.0f, 2.0f, 3.0f}, 0.0f}));
} }
void DualQuaternionTest::isNormalized() {
CORRADE_VERIFY(!DualQuaternion({{1.0f, 2.0f, 3.0f}, 4.0f}, {}).isNormalized());
CORRADE_VERIFY((DualQuaternion::rotation(Deg(23.0f), Vector3::xAxis())*DualQuaternion::translation({3.0f, 1.0f, -0.5f})).isNormalized());
}
void DualQuaternionTest::constExpressions() { void DualQuaternionTest::constExpressions() {
/* Default constructor */ /* Default constructor */
constexpr DualQuaternion a; constexpr DualQuaternion a;

9
src/Math/Test/QuaternionTest.cpp

@ -37,7 +37,9 @@ class QuaternionTest: public Corrade::TestSuite::Tester {
void construct(); void construct();
void constructDefault(); void constructDefault();
void constructFromVector(); void constructFromVector();
void compare(); void compare();
void isNormalized();
void constExpressions(); void constExpressions();
@ -78,7 +80,9 @@ QuaternionTest::QuaternionTest() {
addTests({&QuaternionTest::construct, addTests({&QuaternionTest::construct,
&QuaternionTest::constructDefault, &QuaternionTest::constructDefault,
&QuaternionTest::constructFromVector, &QuaternionTest::constructFromVector,
&QuaternionTest::compare, &QuaternionTest::compare,
&QuaternionTest::isNormalized,
&QuaternionTest::constExpressions, &QuaternionTest::constExpressions,
@ -129,6 +133,11 @@ void QuaternionTest::compare() {
CORRADE_VERIFY(Quaternion({4.0f, 2.0f, 3.0f}, -1.0f+TypeTraits<Float>::epsilon()*2) != Quaternion({4.0f, 2.0f, 3.0f}, -1.0f)); CORRADE_VERIFY(Quaternion({4.0f, 2.0f, 3.0f}, -1.0f+TypeTraits<Float>::epsilon()*2) != Quaternion({4.0f, 2.0f, 3.0f}, -1.0f));
} }
void QuaternionTest::isNormalized() {
CORRADE_VERIFY(!Quaternion({1.0f, 2.0f, 3.0f}, 4.0f).isNormalized());
CORRADE_VERIFY(Quaternion::rotation(Deg(23.0f), Vector3::xAxis()).isNormalized());
}
void QuaternionTest::constExpressions() { void QuaternionTest::constExpressions() {
/* Default constructor */ /* Default constructor */
constexpr Quaternion a; constexpr Quaternion a;

Loading…
Cancel
Save