diff --git a/src/Math/Matrix3.h b/src/Math/Matrix3.h index a60e0133e..344f55d86 100644 --- a/src/Math/Matrix3.h +++ b/src/Math/Matrix3.h @@ -93,10 +93,10 @@ template class Matrix3: public Matrix<3, T> { * @param normal Normal of the line through which to reflect * * Expects that the normal is normalized. - * @see Matrix4::reflection() + * @see Matrix4::reflection(), Vector::isNormalized() */ static Matrix3 reflection(const Vector2& normal) { - CORRADE_ASSERT(TypeTraits::equals(normal.dot(), T(1)), + CORRADE_ASSERT(normal.isNormalized(), "Math::Matrix3::reflection(): normal must be normalized", {}); return from(Matrix<2, T>() - T(2)*normal*RectangularMatrix<1, 2, T>(normal).transposed(), {}); } diff --git a/src/Math/Matrix4.h b/src/Math/Matrix4.h index 9837bdae7..9202d3437 100644 --- a/src/Math/Matrix4.h +++ b/src/Math/Matrix4.h @@ -88,10 +88,10 @@ template class Matrix4: public Matrix<4, T> { * faster alternatives like rotationX(), rotationY() and rotationZ(). * @see rotation() const, Quaternion::rotation(), DualQuaternion::rotation(), * Matrix3::rotation(Rad), Vector3::xAxis(), Vector3::yAxis(), - * Vector3::zAxis() + * Vector3::zAxis(), Vector::isNormalized() */ static Matrix4 rotation(Rad angle, const Vector3& normalizedAxis) { - CORRADE_ASSERT(TypeTraits::equals(normalizedAxis.dot(), T(1)), + CORRADE_ASSERT(normalizedAxis.isNormalized(), "Math::Matrix4::rotation(): axis must be normalized", {}); T sine = std::sin(T(angle)); @@ -181,10 +181,10 @@ template class Matrix4: public Matrix<4, T> { * @param normal Normal of the plane through which to reflect * * Expects that the normal is normalized. - * @see Matrix3::reflection() + * @see Matrix3::reflection(), Vector::isNormalized() */ static Matrix4 reflection(const Vector3& normal) { - CORRADE_ASSERT(TypeTraits::equals(normal.dot(), T(1)), + CORRADE_ASSERT(normal.isNormalized(), "Math::Matrix4::reflection(): normal must be normalized", {}); return from(Matrix<3, T>() - T(2)*normal*RectangularMatrix<1, 3, T>(normal).transposed(), {}); } diff --git a/src/Math/Test/VectorTest.cpp b/src/Math/Test/VectorTest.cpp index 7c301cd7e..9b492efcd 100644 --- a/src/Math/Test/VectorTest.cpp +++ b/src/Math/Test/VectorTest.cpp @@ -62,6 +62,8 @@ class VectorTest: public Corrade::TestSuite::Tester { void constructConversion(); void constructCopy(); + void isNormalized(); + void convert(); void data(); @@ -107,6 +109,8 @@ VectorTest::VectorTest() { &VectorTest::constructConversion, &VectorTest::constructCopy, + &VectorTest::isNormalized, + &VectorTest::convert, &VectorTest::data, @@ -188,6 +192,11 @@ void VectorTest::constructCopy() { CORRADE_COMPARE(b, Vector4(1.0f, 3.5f, 4.0f, -2.7f)); } +void VectorTest::isNormalized() { + CORRADE_VERIFY(!Vector3(1.0f, 2.0f, -1.0f).isNormalized()); + CORRADE_VERIFY(Vector3(0.0f, 1.0f, 0.0f).isNormalized()); +} + void VectorTest::convert() { Vec3 a{1.5f, 2.0f, -3.5f}; Vector3 b(1.5f, 2.0f, -3.5f); diff --git a/src/Math/Vector.h b/src/Math/Vector.h index 767c79ce0..1d4c197a9 100644 --- a/src/Math/Vector.h +++ b/src/Math/Vector.h @@ -99,10 +99,10 @@ template class Vector { * Expects that both vectors are normalized. @f[ * \theta = acos \left( \frac{\boldsymbol a \cdot \boldsymbol b}{|\boldsymbol a| |\boldsymbol b|} \right) = acos (\boldsymbol a \cdot \boldsymbol b) * @f] - * @see Quaternion::angle(), Complex::angle() + * @see isNormalized(), Quaternion::angle(), Complex::angle() */ inline static Rad angle(const Vector& normalizedA, const Vector& normalizedB) { - CORRADE_ASSERT(TypeTraits::equals(normalizedA.dot(), T(1)) && TypeTraits::equals(normalizedB.dot(), T(1)), + CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), "Math::Vector::angle(): vectors must be normalized", Rad(std::numeric_limits::quiet_NaN())); return Rad(std::acos(dot(normalizedA, normalizedB))); } @@ -245,6 +245,18 @@ template class Vector { return out; } + /** + * @brief Whether the vector is normalized + * + * The vector is normalized if it has unit length: @f[ + * |\boldsymbol a|^2 = |\boldsymbol a| = 1 + * @f] + * @see dot(), normalized() + */ + inline bool isNormalized() const { + return TypeTraits::equals(dot(), T(1)); + } + /** * @brief Negated vector * @@ -422,7 +434,7 @@ template class Vector { * other values, because it doesn't compute the square root. @f[ * \boldsymbol a \cdot \boldsymbol a = \sum_{i=0}^{n-1} \boldsymbol a_i^2 * @f] - * @see dot(const Vector&, const Vector&) + * @see dot(const Vector&, const Vector&), isNormalized() */ inline T dot() const { return dot(*this, *this); @@ -435,13 +447,18 @@ template class Vector { * values. @f[ * |\boldsymbol a| = \sqrt{\boldsymbol a \cdot \boldsymbol a} * @f] + * @see isNormalized() * @todo something like std::hypot() for possibly better precision? */ inline T length() const { return std::sqrt(dot()); } - /** @brief Normalized vector (of unit length) */ + /** + * @brief Normalized vector (of unit length) + * + * @see isNormalized() + */ inline Vector normalized() const { return *this/length(); } @@ -468,7 +485,7 @@ template class Vector { * @f] */ inline Vector projectedOntoNormalized(const Vector& line) const { - CORRADE_ASSERT(TypeTraits::equals(line.dot(), T(1)), "Math::Vector::projectedOntoNormalized(): line must be normalized", (Vector(std::numeric_limits::quiet_NaN()))); + CORRADE_ASSERT(line.isNormalized(), "Math::Vector::projectedOntoNormalized(): line must be normalized", (Vector(std::numeric_limits::quiet_NaN()))); return line*dot(*this, line); }