From 64bc2393d1c00e9b207f47f78b2a6d4e0cd6321d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 25 Mar 2015 22:54:49 +0100 Subject: [PATCH] Math: made dot(), angle(), *lerp() and cross() free functions. It is often annoying to write e.g. this, especially in generic code: T dot = Math::Vector::dot(a, b); When this is more than enough and the compiler can infer the rest from the context: T dot = Math::dot(a, b); There are more downsides and confusing cases (you can call Math::Vector<3, T>::dot(), Math::Vector3::dot() and Color3::dot() and it is still the same function), so I made these as free functions in Math namespace. You can now also abuse ADL for the calls, but I would advise against that for better readability: T d = dot(a, b); // dot?! what on earth is dot? and what is a? The only downside found when porting is that you need to specify the type somehow when having both parameters as initializer lists: T d = dot({2.0f, -1.5f}, {1.0f, 2.5f}); // error T d = dot(Complex{2.0f, -1.5f}, {1.0f, 2.5f}); // okay But that's probably reasonable (and it's also highly corner case, the functions were used this way only in tests). The original static member functions are of course still present, but marked as deprecated and will be removed at some point in future. --- .../CapsuleRendererTransformation.h | 6 +- .../CylinderRendererTransformation.h | 6 +- .../ForceRendererTransformation.h | 6 +- .../DebugTools/Test/CapsuleRendererTest.cpp | 8 +- .../DebugTools/Test/CylinderRendererTest.cpp | 8 +- .../DebugTools/Test/ForceRendererTest.cpp | 8 +- .../Math/Algorithms/Test/GramSchmidtTest.cpp | 12 +- src/Magnum/Math/Complex.h | 63 ++++--- src/Magnum/Math/DualQuaternion.h | 2 +- src/Magnum/Math/Functions.h | 4 +- src/Magnum/Math/Geometry/Distance.h | 16 +- src/Magnum/Math/Geometry/Intersection.h | 11 +- src/Magnum/Math/Matrix.h | 2 +- src/Magnum/Math/Matrix4.h | 4 +- src/Magnum/Math/Quaternion.h | 175 +++++++++++------- src/Magnum/Math/Test/ComplexTest.cpp | 18 +- src/Magnum/Math/Test/QuaternionTest.cpp | 38 ++-- src/Magnum/Math/Test/Vector2Test.cpp | 8 +- src/Magnum/Math/Test/Vector3Test.cpp | 2 +- src/Magnum/Math/Test/VectorTest.cpp | 14 +- src/Magnum/Math/Vector.h | 75 +++++--- src/Magnum/Math/Vector2.h | 39 ++-- src/Magnum/Math/Vector3.h | 45 +++-- src/Magnum/MeshTools/GenerateFlatNormals.cpp | 4 +- 24 files changed, 331 insertions(+), 243 deletions(-) diff --git a/src/Magnum/DebugTools/Implementation/CapsuleRendererTransformation.h b/src/Magnum/DebugTools/Implementation/CapsuleRendererTransformation.h index 09230632c..7064c4069 100644 --- a/src/Magnum/DebugTools/Implementation/CapsuleRendererTransformation.h +++ b/src/Magnum/DebugTools/Implementation/CapsuleRendererTransformation.h @@ -74,7 +74,7 @@ template<> std::array capsuleRendererTransformation<3>(const Vector3 Vector3 capDistance; if(length >= Math::TypeTraits::epsilon()) { const Vector3 directionNormalized = direction/length; - const Float dot = Vector3::dot(directionNormalized, Vector3::zAxis()); + const Float dot = Math::dot(directionNormalized, Vector3::zAxis()); /* Direction is parallel to Z axis, special rotation case */ if(Math::abs(dot) > 1.0f - Math::TypeTraits::epsilon()) { @@ -85,8 +85,8 @@ template<> std::array capsuleRendererTransformation<3>(const Vector3 /* Common case */ } else { rotation.up() = directionNormalized; - rotation.right() = Vector3::cross(rotation.up(), Vector3::zAxis()).normalized(); - rotation.backward() = Vector3::cross(rotation.right(), rotation.up()); + rotation.right() = Math::cross(rotation.up(), Vector3::zAxis()).normalized(); + rotation.backward() = Math::cross(rotation.right(), rotation.up()); CORRADE_INTERNAL_ASSERT(rotation.up().isNormalized() && rotation.backward().isNormalized()); } diff --git a/src/Magnum/DebugTools/Implementation/CylinderRendererTransformation.h b/src/Magnum/DebugTools/Implementation/CylinderRendererTransformation.h index 479cba460..aec563175 100644 --- a/src/Magnum/DebugTools/Implementation/CylinderRendererTransformation.h +++ b/src/Magnum/DebugTools/Implementation/CylinderRendererTransformation.h @@ -63,7 +63,7 @@ template<> Matrix4 cylinderRendererTransformation<3>(const Vector3& a, const Vec Matrix4 rotation; if(length >= Math::TypeTraits::epsilon()) { const Vector3 directionNormalized = direction/length; - const Float dot = Vector3::dot(directionNormalized, Vector3::zAxis()); + const Float dot = Math::dot(directionNormalized, Vector3::zAxis()); /* Direction is parallel to Z axis, special rotation case */ if(Math::abs(dot) > 1.0f - Math::TypeTraits::epsilon()) { @@ -74,8 +74,8 @@ template<> Matrix4 cylinderRendererTransformation<3>(const Vector3& a, const Vec /* Common case */ } else { rotation.up() = directionNormalized; - rotation.right() = Vector3::cross(rotation.up(), Vector3::zAxis()).normalized(); - rotation.backward() = Vector3::cross(rotation.right(), rotation.up()); + rotation.right() = Math::cross(rotation.up(), Vector3::zAxis()).normalized(); + rotation.backward() = Math::cross(rotation.right(), rotation.up()); CORRADE_INTERNAL_ASSERT(rotation.up().isNormalized() && rotation.backward().isNormalized()); } } diff --git a/src/Magnum/DebugTools/Implementation/ForceRendererTransformation.h b/src/Magnum/DebugTools/Implementation/ForceRendererTransformation.h index a8d0070da..a446e7f76 100644 --- a/src/Magnum/DebugTools/Implementation/ForceRendererTransformation.h +++ b/src/Magnum/DebugTools/Implementation/ForceRendererTransformation.h @@ -47,17 +47,17 @@ template<> Matrix4 forceRendererTransformation<3>(const Vector3& forcePosition, if(forceLength < Math::TypeTraits::epsilon()) return translation*Matrix4::scaling(Vector3(0.0f)); - const Float dot = Vector3::dot(force/forceLength, Vector3::xAxis()); + const Float dot = Math::dot(force/forceLength, Vector3::xAxis()); /* Force is parallel to X axis, just scaling */ if(Math::abs(dot) > 1.0f - Math::TypeTraits::epsilon()) return translation*Matrix4::scaling({Math::sign(dot)*forceLength, forceLength, forceLength}); /* Normal of plane going through force vector and X axis vector */ - const Vector3 normal = Vector3::cross(Vector3::xAxis(), force).normalized(); + const Vector3 normal = Math::cross(Vector3::xAxis(), force).normalized(); /* Third base vector, orthogonal to force and normal */ - const Vector3 binormal = Vector3::cross(normal, force/forceLength); + const Vector3 binormal = Math::cross(normal, force/forceLength); CORRADE_INTERNAL_ASSERT(binormal.isNormalized()); /* Transformation matrix from scaled base vectors and translation vector */ diff --git a/src/Magnum/DebugTools/Test/CapsuleRendererTest.cpp b/src/Magnum/DebugTools/Test/CapsuleRendererTest.cpp index 23bd3a001..94c4537fe 100644 --- a/src/Magnum/DebugTools/Test/CapsuleRendererTest.cpp +++ b/src/Magnum/DebugTools/Test/CapsuleRendererTest.cpp @@ -82,7 +82,7 @@ void CapsuleRendererTest::common2D() { CORRADE_COMPARE(transformation[2].right(), right); /* Orthogonality */ - CORRADE_COMPARE(Vector2::dot(transformation[0].up(), transformation[0].right()), 0.0f); + CORRADE_COMPARE(Math::dot(transformation[0].up(), transformation[0].right()), 0.0f); const Vector2 capDistance = up.resized(3.5f); CORRADE_COMPARE(transformation[0].translation(), a+capDistance); @@ -162,9 +162,9 @@ void CapsuleRendererTest::common3D() { CORRADE_COMPARE(transformation[2].backward(), backward); /* Orthogonality */ - CORRADE_COMPARE(Vector3::dot(transformation[0].up(), transformation[0].right()), 0.0f); - CORRADE_COMPARE(Vector3::dot(transformation[0].up(), transformation[0].backward()), 0.0f); - CORRADE_COMPARE(Vector3::dot(transformation[0].right(), transformation[0].backward()), 0.0f); + CORRADE_COMPARE(Math::dot(transformation[0].up(), transformation[0].right()), 0.0f); + CORRADE_COMPARE(Math::dot(transformation[0].up(), transformation[0].backward()), 0.0f); + CORRADE_COMPARE(Math::dot(transformation[0].right(), transformation[0].backward()), 0.0f); const Vector3 capDistance = up.resized(3.5f); CORRADE_COMPARE(transformation[0].translation(), a+capDistance); diff --git a/src/Magnum/DebugTools/Test/CylinderRendererTest.cpp b/src/Magnum/DebugTools/Test/CylinderRendererTest.cpp index 807329bf5..eebf18980 100644 --- a/src/Magnum/DebugTools/Test/CylinderRendererTest.cpp +++ b/src/Magnum/DebugTools/Test/CylinderRendererTest.cpp @@ -67,7 +67,7 @@ void CylinderRendererTest::common2D() { /* Rotation + scaling, test orthogonality */ CORRADE_COMPARE(transformation.up(), Vector2(3.5f, -2.0f)); CORRADE_COMPARE(transformation.right(), Vector2(4.0f, 7.0f).resized(3.5f)); - CORRADE_COMPARE(Vector2::dot(transformation.up(), transformation.right()), 0.0f); + CORRADE_COMPARE(Math::dot(transformation.up(), transformation.right()), 0.0f); CORRADE_COMPARE(transformation.translation(), 0.5f*(a + b)); } @@ -113,9 +113,9 @@ void CylinderRendererTest::common3D() { CORRADE_COMPARE(transformation.backward(), Vector3(9.625f, -5.5f, 16.25f).resized(3.5f)); /* Orthogonality */ - CORRADE_COMPARE(Vector3::dot(transformation.up(), transformation.right()), 0.0f); - CORRADE_COMPARE(Vector3::dot(transformation.up(), transformation.backward()), 0.0f); - CORRADE_COMPARE(Vector3::dot(transformation.right(), transformation.backward()), 0.0f); + CORRADE_COMPARE(Math::dot(transformation.up(), transformation.right()), 0.0f); + CORRADE_COMPARE(Math::dot(transformation.up(), transformation.backward()), 0.0f); + CORRADE_COMPARE(Math::dot(transformation.right(), transformation.backward()), 0.0f); CORRADE_COMPARE(transformation.translation(), 0.5f*(a + b)); } diff --git a/src/Magnum/DebugTools/Test/ForceRendererTest.cpp b/src/Magnum/DebugTools/Test/ForceRendererTest.cpp index ef3f50c6f..c07c3a7df 100644 --- a/src/Magnum/DebugTools/Test/ForceRendererTest.cpp +++ b/src/Magnum/DebugTools/Test/ForceRendererTest.cpp @@ -68,7 +68,7 @@ void ForceRendererTest::common2D() { CORRADE_COMPARE(m.up().length(), force.length()); /* All vectors are orthogonal */ - CORRADE_COMPARE(Vector2::dot(m.right(), m.up()), 0.0f); + CORRADE_COMPARE(Math::dot(m.right(), m.up()), 0.0f); } void ForceRendererTest::zero3D() { @@ -99,10 +99,10 @@ void ForceRendererTest::arbitrary3D() { CORRADE_COMPARE(m.backward().length(), force.length()); /* All vectors are orthogonal */ - CORRADE_COMPARE(Vector3::dot(m.right(), m.up()), 0.0f); - CORRADE_COMPARE(Vector3::dot(m.right(), m.backward()), 0.0f); + CORRADE_COMPARE(Math::dot(m.right(), m.up()), 0.0f); + CORRADE_COMPARE(Math::dot(m.right(), m.backward()), 0.0f); /** @todo This shouldn't be too different */ - CORRADE_VERIFY(Math::abs(Vector3::dot(m.up(), m.backward())) < Math::TypeTraits::epsilon()); + CORRADE_VERIFY(Math::abs(Math::dot(m.up(), m.backward())) < Math::TypeTraits::epsilon()); } }}}} diff --git a/src/Magnum/Math/Algorithms/Test/GramSchmidtTest.cpp b/src/Magnum/Math/Algorithms/Test/GramSchmidtTest.cpp index 386cddc6f..92c35a476 100644 --- a/src/Magnum/Math/Algorithms/Test/GramSchmidtTest.cpp +++ b/src/Magnum/Math/Algorithms/Test/GramSchmidtTest.cpp @@ -57,9 +57,9 @@ void GramSchmidtTest::orthogonalize() { /* (The vectors don't need to unit length) */ /* Verify the vectors are orthogonal */ - CORRADE_COMPARE(Vector3::dot(orthogonalized[0], orthogonalized[1]), 0.0f); - CORRADE_COMPARE(Vector3::dot(orthogonalized[0], orthogonalized[2]), 0.0f); - CORRADE_COMPARE(Vector3::dot(orthogonalized[1], orthogonalized[2]), 0.0f); + CORRADE_COMPARE(dot(orthogonalized[0], orthogonalized[1]), 0.0f); + CORRADE_COMPARE(dot(orthogonalized[0], orthogonalized[2]), 0.0f); + CORRADE_COMPARE(dot(orthogonalized[1], orthogonalized[2]), 0.0f); /* Just to be sure */ Matrix3x3 expected(Vector3( 3.0f, 5.0f, 1.0f), @@ -84,9 +84,9 @@ void GramSchmidtTest::orthonormalize() { CORRADE_COMPARE(orthonormalized[2].length(), 1.0f); /* Verify the vectors are orthogonal */ - CORRADE_COMPARE(Vector3::dot(orthonormalized[0], orthonormalized[1]), 0.0f); - CORRADE_COMPARE(Vector3::dot(orthonormalized[0], orthonormalized[2]), 0.0f); - CORRADE_COMPARE(Vector3::dot(orthonormalized[1], orthonormalized[2]), 0.0f); + CORRADE_COMPARE(dot(orthonormalized[0], orthonormalized[1]), 0.0f); + CORRADE_COMPARE(dot(orthonormalized[0], orthonormalized[2]), 0.0f); + CORRADE_COMPARE(dot(orthonormalized[1], orthonormalized[2]), 0.0f); /* Just to be sure */ Matrix3x3 expected(Vector3( 0.3030458f, 0.5050763f, 0.8081220f), diff --git a/src/Magnum/Math/Complex.h b/src/Magnum/Math/Complex.h index efd8a8c43..5372469f0 100644 --- a/src/Magnum/Math/Complex.h +++ b/src/Magnum/Math/Complex.h @@ -45,6 +45,34 @@ namespace Implementation { } } +/** @relatesalso Complex +@brief Dot product of two complex numbers + +@f[ + c_0 \cdot c_1 = a_0 a_1 + b_0 b_1 +@f] +@see @ref Complex::dot() const +*/ +template inline T dot(const Complex& a, const Complex& b) { + return a.real()*b.real() + a.imaginary()*b.imaginary(); +} + +/** @relatesalso Complex +@brief Angle between normalized complex numbers + +Expects that both complex numbers are normalized. @f[ + \theta = acos \left( \frac{Re(c_0 \cdot c_1))}{|c_0| |c_1|} \right) = acos (a_0 a_1 + b_0 b_1) +@f] +@see @ref Complex::isNormalized(), + @ref angle(const Quaternion&, const Quaternion&), + @ref angle(const Vector&, const Vector&) +*/ +template inline Rad angle(const Complex& normalizedA, const Complex& normalizedB) { + CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), + "Math::angle(): complex numbers must be normalized", {}); + return Rad(std::acos(normalizedA.real()*normalizedB.real() + normalizedA.imaginary()*normalizedB.imaginary())); +} + /** @brief Complex number @tparam T Data type @@ -56,32 +84,25 @@ template class Complex { public: typedef T Type; /**< @brief Underlying data type */ + #ifdef MAGNUM_BUILD_DEPRECATED /** - * @brief Dot product - * - * @f[ - * c_0 \cdot c_1 = a_0 a_1 + b_0 b_1 - * @f] - * @see @ref Complex::dot() const + * @copybrief Math::dot(const Complex&, const Complex&) + * @deprecated Use @ref Math::dot(const Complex&, const Complex&) + * instead. */ - static T dot(const Complex& a, const Complex& b) { - return a._real*b._real + a._imaginary*b._imaginary; + CORRADE_DEPRECATED("use Math::dot() instead") static T dot(const Complex& a, const Complex& b) { + return Math::dot(a, b); } /** - * @brief Angle between normalized complex numbers - * - * Expects that both complex numbers are normalized. @f[ - * \theta = acos \left( \frac{Re(c_0 \cdot c_1))}{|c_0| |c_1|} \right) = acos (a_0 a_1 + b_0 b_1) - * @f] - * @see @ref isNormalized(), @ref Quaternion::angle(), - * @ref Vector::angle() + * @copybrief Math::angle(const Complex&, const Complex&) + * @deprecated Use @ref Math::angle(const Complex&, const Complex&) + * instead. */ - static Rad angle(const Complex& normalizedA, const Complex& normalizedB) { - CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::Complex::angle(): complex numbers must be normalized", {}); - return Rad(std::acos(normalizedA._real*normalizedB._real + normalizedA._imaginary*normalizedB._imaginary)); + CORRADE_DEPRECATED("use Math::angle() instead") static Rad angle(const Complex& normalizedA, const Complex& normalizedB) { + return Math::angle(normalizedA, normalizedB); } + #endif /** * @brief Rotation complex number @@ -328,9 +349,7 @@ template class Complex { * @f] * @see @ref dot(const Complex&, const Complex&), @ref isNormalized() */ - T dot() const { - return dot(*this, *this); - } + T dot() const { return Math::dot(*this, *this); } /** * @brief Complex number length diff --git a/src/Magnum/Math/DualQuaternion.h b/src/Magnum/Math/DualQuaternion.h index 3f2b34a28..694e080e1 100644 --- a/src/Magnum/Math/DualQuaternion.h +++ b/src/Magnum/Math/DualQuaternion.h @@ -233,7 +233,7 @@ template class DualQuaternion: public Dual> { * @f] */ Dual lengthSquared() const { - return {Dual>::real().dot(), T(2)*Quaternion::dot(Dual>::real(), Dual>::dual())}; + return {Dual>::real().dot(), T(2)*dot(Dual>::real(), Dual>::dual())}; } /** diff --git a/src/Magnum/Math/Functions.h b/src/Magnum/Math/Functions.h index 73054d02d..c6e7ae14e 100644 --- a/src/Magnum/Math/Functions.h +++ b/src/Magnum/Math/Functions.h @@ -358,9 +358,7 @@ template Vector sqrtInverted(const Vector&, const Quaternion&, T) */ #ifdef DOXYGEN_GENERATING_OUTPUT template inline T lerp(const T& a, const T& b, U t); diff --git a/src/Magnum/Math/Geometry/Distance.h b/src/Magnum/Math/Geometry/Distance.h index 5d3c31a05..7c514990b 100644 --- a/src/Magnum/Math/Geometry/Distance.h +++ b/src/Magnum/Math/Geometry/Distance.h @@ -46,7 +46,7 @@ class Distance { * @param point Point * * The distance *d* is computed from point **p** and line defined by **a** - * and **b** using @ref Vector2::cross() "perp-dot product": @f[ + * and **b** using @ref cross(const Vector2&, const Vector2&) "perp-dot product": @f[ * d = \frac{|(\boldsymbol b - \boldsymbol a)_\bot \cdot (\boldsymbol a - \boldsymbol p)|} {|\boldsymbol b - \boldsymbol a|} * @f] * Source: http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html @@ -54,7 +54,7 @@ class Distance { */ template static T linePoint(const Vector2& a, const Vector2& b, const Vector2& point) { const Vector2 bMinusA = b - a; - return std::abs(Vector2::cross(bMinusA, a - point))/bMinusA.length(); + return std::abs(cross(bMinusA, a - point))/bMinusA.length(); } /** @@ -70,7 +70,7 @@ class Distance { */ template static T linePointSquared(const Vector2& a, const Vector2& b, const Vector2& point) { const Vector2 bMinusA = b - a; - return Math::pow<2>(Vector2::cross(bMinusA, a - point))/bMinusA.dot(); + return Math::pow<2>(cross(bMinusA, a - point))/bMinusA.dot(); } /** @@ -80,7 +80,7 @@ class Distance { * @param point Point * * The distance *d* is computed from point **p** and line defined by **a** - * and **b** using @ref Vector3::cross() "cross product": @f[ + * and **b** using @ref cross(const Vector3&, const Vector3&) "cross product": @f[ * d = \frac{|(\boldsymbol p - \boldsymbol a) \times (\boldsymbol p - \boldsymbol b)|} * {|\boldsymbol b - \boldsymbol a|} * @f] @@ -99,7 +99,7 @@ class Distance { * compute the square root. */ template static T linePointSquared(const Vector3& a, const Vector3& b, const Vector3& point) { - return Vector3::cross(point - a, point - b).dot()/(b - a).dot(); + return cross(point - a, point - b).dot()/(b - a).dot(); } /** @@ -181,7 +181,7 @@ template T Distance::lineSegmentPoint(const Vector2& a, const Vector return std::sqrt(pointDistanceB); /* Between A and B */ - return std::abs(Vector2::cross(bMinusA, -pointMinusA))/std::sqrt(bDistanceA); + return std::abs(cross(bMinusA, -pointMinusA))/std::sqrt(bDistanceA); } template T Distance::lineSegmentPointSquared(const Vector2& a, const Vector2& b, const Vector2& point) { @@ -201,7 +201,7 @@ template T Distance::lineSegmentPointSquared(const Vector2& a, const return pointDistanceB; /* Between A and B */ - return Math::pow<2>(Vector2::cross(bMinusA, -pointMinusA))/bDistanceA; + return Math::pow<2>(cross(bMinusA, -pointMinusA))/bDistanceA; } template T Distance::lineSegmentPointSquared(const Vector3& a, const Vector3& b, const Vector3& point) { @@ -220,7 +220,7 @@ template T Distance::lineSegmentPointSquared(const Vector3& a, const return pointDistanceB; /* Between A and B */ - return Vector3::cross(pointMinusA, pointMinusB).dot()/bDistanceA; + return cross(pointMinusA, pointMinusB).dot()/bDistanceA; } }}} diff --git a/src/Magnum/Math/Geometry/Intersection.h b/src/Magnum/Math/Geometry/Intersection.h index 87a188321..78ba97b9c 100644 --- a/src/Magnum/Math/Geometry/Intersection.h +++ b/src/Magnum/Math/Geometry/Intersection.h @@ -72,9 +72,8 @@ class Intersection { */ template static std::pair lineSegmentLineSegment(const Vector2& p, const Vector2& r, const Vector2& q, const Vector2& s) { const Vector2 qp = q - p; - const T rs = Vector2::cross(r, s); - return {Vector2::cross(qp, s)/rs, - Vector2::cross(qp, r)/rs}; + const T rs = cross(r, s); + return {cross(qp, s)/rs, cross(qp, r)/rs}; } /** @@ -92,7 +91,7 @@ class Intersection { * Unlike @ref lineSegmentLineSegment() computes only **t**. */ template static T lineSegmentLine(const Vector2& p, const Vector2& r, const Vector2& q, const Vector2& s) { - return Vector2::cross(q - p, s)/Vector2::cross(r, s); + return cross(q - p, s)/cross(r, s); } /** @@ -121,8 +120,8 @@ class Intersection { * @f] */ template static T planeLine(const Vector3& planePosition, const Vector3& planeNormal, const Vector3& p, const Vector3& r) { - const T f = Vector3::dot(planePosition, planeNormal); - return (f-Vector3::dot(planeNormal, p))/Vector3::dot(planeNormal, r); + const T f = dot(planePosition, planeNormal); + return (f-dot(planeNormal, p))/dot(planeNormal, r); } }; diff --git a/src/Magnum/Math/Matrix.h b/src/Magnum/Math/Matrix.h index a124925c2..380df95e4 100644 --- a/src/Magnum/Math/Matrix.h +++ b/src/Magnum/Math/Matrix.h @@ -284,7 +284,7 @@ template bool Matrix::isOrthogonal() const { /* Orthogonality */ for(std::size_t i = 0; i != size-1; ++i) for(std::size_t j = i+1; j != size; ++j) - if(Vector::dot((*this)[i], (*this)[j]) > TypeTraits::epsilon()) + if(dot((*this)[i], (*this)[j]) > TypeTraits::epsilon()) return false; return true; diff --git a/src/Magnum/Math/Matrix4.h b/src/Magnum/Math/Matrix4.h index 2b2395af4..d38d34eea 100644 --- a/src/Magnum/Math/Matrix4.h +++ b/src/Magnum/Math/Matrix4.h @@ -539,8 +539,8 @@ template Matrix4 Matrix4::perspectiveProjection(const Vector2& template Matrix4 Matrix4::lookAt(const Vector3& eye, const Vector3& target, const Vector3& up) { const Vector3 backward = (eye - target).normalized(); - const Vector3 right = Vector3::cross(up, backward).normalized(); - const Vector3 realUp = Vector3::cross(backward, right); + const Vector3 right = cross(up, backward).normalized(); + const Vector3 realUp = cross(backward, right); return {{ right, T(0)}, { realUp, T(0)}, diff --git a/src/Magnum/Math/Quaternion.h b/src/Magnum/Math/Quaternion.h index 0c309400f..d13e15c44 100644 --- a/src/Magnum/Math/Quaternion.h +++ b/src/Magnum/Math/Quaternion.h @@ -39,6 +39,79 @@ namespace Magnum { namespace Math { +/** @relatesalso Quaternion +@brief Dot product between two quaternions + +@f[ + p \cdot q = \boldsymbol p_V \cdot \boldsymbol q_V + p_S q_S +@f] +@see @ref Quaternion::dot() const +*/ +template inline T dot(const Quaternion& a, const Quaternion& b) { + return dot(a.vector(), b.vector()) + a.scalar()*b.scalar(); +} + +namespace Implementation { + /* Used in angle() and slerp() (no assertions) */ + template inline Rad angle(const Quaternion& normalizedA, const Quaternion& normalizedB) { + return Rad{std::acos(dot(normalizedA, normalizedB))}; + } +} + +/** @relatesalso Quaternion +@brief Angle between normalized quaternions + +Expects that both quaternions are normalized. @f[ + \theta = acos \left( \frac{p \cdot q}{|p| |q|} \right) = acos(p \cdot q) +@f] +@see @ref Quaternion::isNormalized(), + @ref angle(const Complex&, const Complex&), + @ref angle(const Vector&, const Vector&) + */ +template inline Rad angle(const Quaternion& normalizedA, const Quaternion& normalizedB) { + CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), + "Math::angle(): quaternions must be normalized", {}); + return Implementation::angle(normalizedA, normalizedB); +} + +/** @relatesalso Quaternion +@brief Linear interpolation of two quaternions +@param normalizedA First quaternion +@param normalizedB Second quaternion +@param t Interpolation phase (from range @f$ [0; 1] @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|} +@f] +@see @ref Quaternion::isNormalized(), @ref slerp(const Quaternion&, const Quaternion&, T), + @ref lerp(const T&, const T&, U) + */ +template inline Quaternion lerp(const Quaternion& normalizedA, const Quaternion& normalizedB, T t) { + CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), + "Math::lerp(): quaternions must be normalized", {}); + return ((T(1) - t)*normalizedA + t*normalizedB).normalized(); +} + +/** @relatesalso Quaternion +@brief Spherical linear interpolation of two quaternions +@param normalizedA First quaternion +@param normalizedB Second quaternion +@param t Interpolation phase (from range @f$ [0; 1] @f$) + +Expects that both quaternions are normalized. @f[ + q_{SLERP} = \frac{sin((1 - t) \theta) q_A + sin(t \theta) q_B}{sin \theta} + ~ ~ ~ ~ ~ ~ ~ + \theta = acos \left( \frac{q_A \cdot q_B}{|q_A| \cdot |q_B|} \right) = acos(q_A \cdot q_B) +@f] +@see @ref Quaternion::isNormalized(), @ref lerp(const Quaternion&, const Quaternion&, T) + */ +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); + return (std::sin((T(1) - t)*a)*normalizedA + std::sin(t*a)*normalizedB)/std::sin(a); +} + /** @brief Quaternion @tparam T Underlying data type @@ -51,57 +124,43 @@ template class Quaternion { public: typedef T Type; /**< @brief Underlying data type */ + #ifdef MAGNUM_BUILD_DEPRECATED /** - * @brief Dot product - * - * @f[ - * p \cdot q = \boldsymbol p_V \cdot \boldsymbol q_V + p_S q_S - * @f] - * @see @ref dot() const + * @copybrief Math::dot(const Quaternion&, const Quaternion&) + * @deprecated Use @ref Math::dot(const Quaternion&, const Quaternion&) + * instead. */ - static T dot(const Quaternion& a, const Quaternion& b) { - /** @todo Use four-component SIMD implementation when available */ - return Vector3::dot(a.vector(), b.vector()) + a.scalar()*b.scalar(); + CORRADE_DEPRECATED("use Math::dot() instead") static T dot(const Quaternion& a, const Quaternion& b) { + return Math::dot(a, b); } /** - * @brief Angle between normalized quaternions - * - * Expects that both quaternions are normalized. @f[ - * \theta = acos \left( \frac{p \cdot q}{|p| |q|} \right) = acos(p \cdot q) - * @f] - * @see @ref isNormalized(), @ref Complex::angle(), - * @ref Vector::angle() + * @copybrief Math::angle(const Quaternion&, const Quaternion&) + * @deprecated Use @ref Math::angle(const Quaternion&, const Quaternion&) + * instead. */ - static Rad angle(const Quaternion& normalizedA, const Quaternion& normalizedB); + CORRADE_DEPRECATED("use Math::angle() instead") static Rad angle(const Quaternion& normalizedA, const Quaternion& normalizedB) { + return Math::angle(normalizedA, normalizedB); + } /** - * @brief Linear interpolation of two quaternions - * @param normalizedA First quaternion - * @param normalizedB Second quaternion - * @param t Interpolation phase (from range @f$ [0; 1] @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|} - * @f] - * @see @ref isNormalized(), @ref slerp(), @ref Math::lerp() + * @copybrief Math::lerp(const Quaternion&, const Quaternion&, T) + * @deprecated Use @ref Math::lerp(const Quaternion&, const Quaternion&, T) + * instead. */ - static Quaternion lerp(const Quaternion& normalizedA, const Quaternion& normalizedB, T t); + CORRADE_DEPRECATED("use Math::lerp() instead") static Quaternion lerp(const Quaternion& normalizedA, const Quaternion& normalizedB, T t) { + return Math::lerp(normalizedA, normalizedB, t); + } /** - * @brief Spherical linear interpolation of two quaternions - * @param normalizedA First quaternion - * @param normalizedB Second quaternion - * @param t Interpolation phase (from range @f$ [0; 1] @f$) - * - * Expects that both quaternions are normalized. @f[ - * q_{SLERP} = \frac{sin((1 - t) \theta) q_A + sin(t \theta) q_B}{sin \theta} - * ~~~~~~~~~~ - * \theta = acos \left( \frac{q_A \cdot q_B}{|q_A| \cdot |q_B|} \right) = acos(q_A \cdot q_B) - * @f] - * @see @ref isNormalized(), @ref lerp() + * @copybrief Math::slerp(const Quaternion&, const Quaternion&, T) + * @deprecated Use @ref Math::slerp(const Quaternion&, const Quaternion&, T) + * instead. */ - static Quaternion slerp(const Quaternion& normalizedA, const Quaternion& normalizedB, T t); + CORRADE_DEPRECATED("use Math::slerp() instead") static Quaternion slerp(const Quaternion& normalizedA, const Quaternion& normalizedB, T t) { + return Math::slerp(normalizedA, normalizedB, t); + } + #endif /** * @brief Rotation quaternion @@ -331,7 +390,7 @@ template class Quaternion { * @see @ref isNormalized(), * @ref dot(const Quaternion&, const Quaternion&) */ - T dot() const { return dot(*this, *this); } + T dot() const { return Math::dot(*this, *this); } /** * @brief Quaternion length @@ -424,11 +483,6 @@ template class Quaternion { return value*value; } - /* Used in angle() and slerp() (no assertions) */ - static T angleInternal(const Quaternion& normalizedA, const Quaternion& normalizedB) { - return std::acos(dot(normalizedA, normalizedB)); - } - Vector3 _vector; T _scalar; }; @@ -463,16 +517,12 @@ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug deb return debug; } -/** @todoc Remove the workaround when Doxygen is really able to preprocessor */ - /* Explicit instantiation for commonly used types */ #ifndef DOXYGEN_GENERATING_OUTPUT -/** @privatesection */ extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Quaternion&); #ifndef MAGNUM_TARGET_GLES extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Quaternion&); #endif -/** @endprivatesection */ #endif namespace Implementation { @@ -510,25 +560,6 @@ template Quaternion quaternionFromMatrix(const Matrix3x3& m) { } -template inline Rad Quaternion::angle(const Quaternion& normalizedA, const Quaternion& normalizedB) { - CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::Quaternion::angle(): quaternions must be normalized", {}); - return Rad(angleInternal(normalizedA, normalizedB)); -} - -template inline Quaternion Quaternion::lerp(const Quaternion& normalizedA, const Quaternion& normalizedB, const T t) { - CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::Quaternion::lerp(): quaternions must be normalized", {}); - return ((T(1) - t)*normalizedA + t*normalizedB).normalized(); -} - -template inline Quaternion Quaternion::slerp(const Quaternion& normalizedA, const Quaternion& normalizedB, const T t) { - CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::Quaternion::slerp(): quaternions must be normalized", {}); - const T a = angleInternal(normalizedA, normalizedB); - return (std::sin((T(1) - t)*a)*normalizedA + std::sin(t*a)*normalizedB)/std::sin(a); -} - template inline Quaternion Quaternion::rotation(const Rad angle, const Vector3& normalizedAxis) { CORRADE_ASSERT(normalizedAxis.isNormalized(), "Math::Quaternion::rotation(): axis must be normalized", {}); @@ -565,8 +596,8 @@ template Matrix3x3 Quaternion::toMatrix() const { } template inline Quaternion Quaternion::operator*(const Quaternion& other) const { - return {_scalar*other._vector + other._scalar*_vector + Vector3::cross(_vector, other._vector), - _scalar*other._scalar - Vector3::dot(_vector, other._vector)}; + return {_scalar*other._vector + other._scalar*_vector + Math::cross(_vector, other._vector), + _scalar*other._scalar - Math::dot(_vector, other._vector)}; } template inline Quaternion Quaternion::invertedNormalized() const { @@ -576,8 +607,8 @@ template inline Quaternion Quaternion::invertedNormalized() const template inline Vector3 Quaternion::transformVectorNormalized(const Vector3& vector) const { CORRADE_ASSERT(isNormalized(), "Math::Quaternion::transformVectorNormalized(): quaternion must be normalized", {}); - const Vector3 t = T(2)*Vector3::cross(_vector, vector); - return vector + _scalar*t + Vector3::cross(_vector, t); + const Vector3 t = T(2)*Math::cross(_vector, vector); + return vector + _scalar*t + Math::cross(_vector, t); } }} diff --git a/src/Magnum/Math/Test/ComplexTest.cpp b/src/Magnum/Math/Test/ComplexTest.cpp index cd654a1cb..43ba7ce18 100644 --- a/src/Magnum/Math/Test/ComplexTest.cpp +++ b/src/Magnum/Math/Test/ComplexTest.cpp @@ -188,7 +188,7 @@ void ComplexTest::dot() { Complex a(5.0f, 3.0f); Complex b(6.0f, -7.0f); - CORRADE_COMPARE(Complex::dot(a, b), 9.0f); + CORRADE_COMPARE(Math::dot(a, b), 9.0f); } void ComplexTest::dotSelf() { @@ -240,18 +240,18 @@ void ComplexTest::invertedNormalized() { void ComplexTest::angle() { std::ostringstream o; Error::setOutput(&o); - Complex::angle(Complex(1.5f, -2.0f).normalized(), {-4.0f, 3.5f}); - CORRADE_COMPARE(o.str(), "Math::Complex::angle(): complex numbers must be normalized\n"); + Math::angle(Complex(1.5f, -2.0f).normalized(), {-4.0f, 3.5f}); + CORRADE_COMPARE(o.str(), "Math::angle(): complex numbers must be normalized\n"); o.str({}); - Complex::angle({1.5f, -2.0f}, Complex(-4.0f, 3.5f).normalized()); - CORRADE_COMPARE(o.str(), "Math::Complex::angle(): complex numbers must be normalized\n"); + Math::angle({1.5f, -2.0f}, Complex(-4.0f, 3.5f).normalized()); + CORRADE_COMPARE(o.str(), "Math::angle(): complex numbers must be normalized\n"); /* Verify also that the angle is the same as angle between 2D vectors */ - Rad angle = Complex::angle(Complex( 1.5f, -2.0f).normalized(), - Complex(-4.0f, 3.5f).normalized()); - CORRADE_COMPARE(angle, Vector2::angle(Vector2( 1.5f, -2.0f).normalized(), - Vector2(-4.0f, 3.5f).normalized())); + Rad angle = Math::angle(Complex( 1.5f, -2.0f).normalized(), + Complex(-4.0f, 3.5f).normalized()); + CORRADE_COMPARE(angle, Math::angle(Vector2( 1.5f, -2.0f).normalized(), + Vector2(-4.0f, 3.5f).normalized())); CORRADE_COMPARE(angle, Rad(2.933128f)); } diff --git a/src/Magnum/Math/Test/QuaternionTest.cpp b/src/Magnum/Math/Test/QuaternionTest.cpp index 479bb8342..3da43b1c7 100644 --- a/src/Magnum/Math/Test/QuaternionTest.cpp +++ b/src/Magnum/Math/Test/QuaternionTest.cpp @@ -184,7 +184,7 @@ void QuaternionTest::dot() { Quaternion a({ 1.0f, 3.0f, -2.0f}, -4.0f); Quaternion b({-0.5f, 1.5f, 3.0f}, 12.0f); - CORRADE_COMPARE(Quaternion::dot(a, b), -50.0f); + CORRADE_COMPARE(Math::dot(a, b), -50.0f); } void QuaternionTest::dotSelf() { @@ -260,18 +260,18 @@ void QuaternionTest::rotation() { void QuaternionTest::angle() { std::ostringstream o; Error::setOutput(&o); - Quaternion::angle(Quaternion({1.0f, 2.0f, -3.0f}, -4.0f).normalized(), {{4.0f, -3.0f, 2.0f}, -1.0f}); - CORRADE_COMPARE(o.str(), "Math::Quaternion::angle(): quaternions must be normalized\n"); + Math::angle(Quaternion({1.0f, 2.0f, -3.0f}, -4.0f).normalized(), {{4.0f, -3.0f, 2.0f}, -1.0f}); + CORRADE_COMPARE(o.str(), "Math::angle(): quaternions must be normalized\n"); o.str({}); - Quaternion::angle({{1.0f, 2.0f, -3.0f}, -4.0f}, Quaternion({4.0f, -3.0f, 2.0f}, -1.0f).normalized()); - CORRADE_COMPARE(o.str(), "Math::Quaternion::angle(): quaternions must be normalized\n"); + Math::angle({{1.0f, 2.0f, -3.0f}, -4.0f}, Quaternion({4.0f, -3.0f, 2.0f}, -1.0f).normalized()); + CORRADE_COMPARE(o.str(), "Math::angle(): quaternions must be normalized\n"); /* Verify also that the angle is the same as angle between 4D vectors */ - Rad angle = Quaternion::angle(Quaternion({1.0f, 2.0f, -3.0f}, -4.0f).normalized(), - Quaternion({4.0f, -3.0f, 2.0f}, -1.0f).normalized()); - CORRADE_COMPARE(angle, Vector4::angle(Vector4(1.0f, 2.0f, -3.0f, -4.0f).normalized(), - Vector4(4.0f, -3.0f, 2.0f, -1.0f).normalized())); + Rad angle = Math::angle(Quaternion({1.0f, 2.0f, -3.0f}, -4.0f).normalized(), + Quaternion({4.0f, -3.0f, 2.0f}, -1.0f).normalized()); + CORRADE_COMPARE(angle, Math::angle(Vector4(1.0f, 2.0f, -3.0f, -4.0f).normalized(), + Vector4(4.0f, -3.0f, 2.0f, -1.0f).normalized())); CORRADE_COMPARE(angle, Rad(1.704528f)); } @@ -308,14 +308,14 @@ void QuaternionTest::lerp() { std::ostringstream o; Corrade::Utility::Error::setOutput(&o); - Quaternion::lerp(a*3.0f, b, 0.35f); - CORRADE_COMPARE(o.str(), "Math::Quaternion::lerp(): quaternions must be normalized\n"); + Math::lerp(a*3.0f, b, 0.35f); + CORRADE_COMPARE(o.str(), "Math::lerp(): quaternions must be normalized\n"); o.str({}); - Quaternion::lerp(a, b*-3.0f, 0.35f); - CORRADE_COMPARE(o.str(), "Math::Quaternion::lerp(): quaternions must be normalized\n"); + Math::lerp(a, b*-3.0f, 0.35f); + CORRADE_COMPARE(o.str(), "Math::lerp(): quaternions must be normalized\n"); - Quaternion lerp = Quaternion::lerp(a, b, 0.35f); + Quaternion lerp = Math::lerp(a, b, 0.35f); CORRADE_COMPARE(lerp, Quaternion({0.119127f, 0.049134f, 0.049134f}, 0.990445f)); } @@ -326,14 +326,14 @@ void QuaternionTest::slerp() { std::ostringstream o; Corrade::Utility::Error::setOutput(&o); - Quaternion::slerp(a*3.0f, b, 0.35f); - CORRADE_COMPARE(o.str(), "Math::Quaternion::slerp(): quaternions must be normalized\n"); + Math::slerp(a*3.0f, b, 0.35f); + CORRADE_COMPARE(o.str(), "Math::slerp(): quaternions must be normalized\n"); o.str({}); - Quaternion::slerp(a, b*-3.0f, 0.35f); - CORRADE_COMPARE(o.str(), "Math::Quaternion::slerp(): quaternions must be normalized\n"); + Math::slerp(a, b*-3.0f, 0.35f); + CORRADE_COMPARE(o.str(), "Math::slerp(): quaternions must be normalized\n"); - Quaternion slerp = Quaternion::slerp(a, b, 0.35f); + Quaternion slerp = Math::slerp(a, b, 0.35f); CORRADE_COMPARE(slerp, Quaternion({0.1191653f, 0.0491109f, 0.0491109f}, 0.9904423f)); } diff --git a/src/Magnum/Math/Test/Vector2Test.cpp b/src/Magnum/Math/Test/Vector2Test.cpp index 61664c404..a2d62a9ed 100644 --- a/src/Magnum/Math/Test/Vector2Test.cpp +++ b/src/Magnum/Math/Test/Vector2Test.cpp @@ -27,7 +27,7 @@ #include #include -#include "Magnum/Math/Vector3.h" /* Vector3 used in Vector2::cross() */ +#include "Magnum/Math/Vector3.h" /* Vector3 used in Vector2Test::cross() */ struct Vec2 { float x, y; @@ -166,8 +166,8 @@ void Vector2Test::cross() { Vector2i a(1, -1); Vector2i b(4, 3); - CORRADE_COMPARE(Vector2i::cross(a, b), 7); - CORRADE_COMPARE(Vector3i::cross({a, 0}, {b, 0}), Vector3i(0, 0, Vector2i::cross(a, b))); + CORRADE_COMPARE(Math::cross(a, b), 7); + CORRADE_COMPARE(Math::cross({a, 0}, {b, 0}), Vector3i(0, 0, Math::cross(a, b))); } void Vector2Test::axes() { @@ -187,7 +187,7 @@ void Vector2Test::scales() { void Vector2Test::perpendicular() { const Vector2 a(0.5f, -15.0f); CORRADE_COMPARE(a.perpendicular(), Vector2(15.0f, 0.5f)); - CORRADE_COMPARE(Vector2::dot(a.perpendicular(), a), 0.0f); + CORRADE_COMPARE(dot(a.perpendicular(), a), 0.0f); CORRADE_COMPARE(Vector2::xAxis().perpendicular(), Vector2::yAxis()); } diff --git a/src/Magnum/Math/Test/Vector3Test.cpp b/src/Magnum/Math/Test/Vector3Test.cpp index 9c32093d3..b7859cf00 100644 --- a/src/Magnum/Math/Test/Vector3Test.cpp +++ b/src/Magnum/Math/Test/Vector3Test.cpp @@ -183,7 +183,7 @@ void Vector3Test::cross() { Vector3i a(1, -1, 1); Vector3i b(4, 3, 7); - CORRADE_COMPARE(Vector3i::cross(a, b), Vector3i(-10, -3, 7)); + CORRADE_COMPARE(Math::cross(a, b), Vector3i(-10, -3, 7)); } void Vector3Test::axes() { diff --git a/src/Magnum/Math/Test/VectorTest.cpp b/src/Magnum/Math/Test/VectorTest.cpp index 5980b2c44..7d8f90206 100644 --- a/src/Magnum/Math/Test/VectorTest.cpp +++ b/src/Magnum/Math/Test/VectorTest.cpp @@ -364,7 +364,7 @@ void VectorTest::bitwise() { } void VectorTest::dot() { - CORRADE_COMPARE(Vector4::dot({1.0f, 0.5f, 0.75f, 1.5f}, {2.0f, 4.0f, 1.0f, 7.0f}), 15.25f); + CORRADE_COMPARE(Math::dot(Vector4{1.0f, 0.5f, 0.75f, 1.5f}, {2.0f, 4.0f, 1.0f, 7.0f}), 15.25f); } void VectorTest::dotSelf() { @@ -435,15 +435,15 @@ void VectorTest::projectedOntoNormalized() { void VectorTest::angle() { std::ostringstream o; Error::setOutput(&o); - Vector3::angle(Vector3(2.0f, 3.0f, 4.0f).normalized(), {1.0f, -2.0f, 3.0f}); - CORRADE_COMPARE(o.str(), "Math::Vector::angle(): vectors must be normalized\n"); + Math::angle(Vector3(2.0f, 3.0f, 4.0f).normalized(), {1.0f, -2.0f, 3.0f}); + CORRADE_COMPARE(o.str(), "Math::angle(): vectors must be normalized\n"); o.str({}); - Vector3::angle({2.0f, 3.0f, 4.0f}, Vector3(1.0f, -2.0f, 3.0f).normalized()); - CORRADE_COMPARE(o.str(), "Math::Vector::angle(): vectors must be normalized\n"); + Math::angle({2.0f, 3.0f, 4.0f}, Vector3(1.0f, -2.0f, 3.0f).normalized()); + CORRADE_COMPARE(o.str(), "Math::angle(): vectors must be normalized\n"); - CORRADE_COMPARE(Vector3::angle(Vector3(2.0f, 3.0f, 4.0f).normalized(), - Vector3(1.0f, -2.0f, 3.0f).normalized()), + CORRADE_COMPARE(Math::angle(Vector3(2.0f, 3.0f, 4.0f).normalized(), + Vector3(1.0f, -2.0f, 3.0f).normalized()), Rad(1.162514f)); } diff --git a/src/Magnum/Math/Vector.h b/src/Magnum/Math/Vector.h index cf5a5f59d..2a4c9f695 100644 --- a/src/Magnum/Math/Vector.h +++ b/src/Magnum/Math/Vector.h @@ -31,8 +31,9 @@ #include #include -#include #include +#include +#include #include "Magnum/visibility.h" #include "Magnum/Math/Angle.h" @@ -45,6 +46,35 @@ namespace Implementation { template struct VectorConverter; } +/** @relatesalso Vector +@brief Dot product of two vectors + +Returns `0` when two vectors are perpendicular, `1` when two *normalized* +vectors are parallel and `-1` when two *normalized* vectors are antiparallel. @f[ + \boldsymbol a \cdot \boldsymbol b = \sum_{i=0}^{n-1} \boldsymbol a_i \boldsymbol b_i +@f] +@see @ref Vector::dot() const, @ref Vector::operator-(), @ref Vector2::perpendicular() +*/ +template inline T dot(const Vector& a, const Vector& b) { + return (a*b).sum(); +} + +/** @relatesalso Vector +@brief Angle between normalized vectors + +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 @ref Vector::isNormalized(), + @ref angle(const Complex&, const Complex&), + @ref angle(const Quaternion&, const Quaternion&) +*/ +template inline Rad angle(const Vector& normalizedA, const Vector& normalizedB) { + CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), + "Math::angle(): vectors must be normalized", {}); + return Rad(std::acos(dot(normalizedA, normalizedB))); +} + /** @brief Vector @tparam size Vector size @@ -89,30 +119,25 @@ template class Vector { return padInternal(typename Implementation::GenerateSequence::Type(), a, value); } + #ifdef MAGNUM_BUILD_DEPRECATED /** - * @brief Dot product - * - * Returns `0` when two vectors are perpendicular, `1` when two - * *normalized* vectors are parallel and `-1` when two *normalized* - * vectors are antiparallel. @f[ - * \boldsymbol a \cdot \boldsymbol b = \sum_{i=0}^{n-1} \boldsymbol a_i \boldsymbol b_i - * @f] - * @see @ref dot() const, @ref operator-(), @ref Vector2::perpendicular() + * @copybrief Math::dot(const Vector&, const Vector&) + * @deprecated Use @ref Math::dot(const Vector&, const Vector&) + * instead. */ - static T dot(const Vector& a, const Vector& b) { - return (a*b).sum(); + CORRADE_DEPRECATED("use Math::dot() instead") static T dot(const Vector& a, const Vector& b) { + return Math::dot(a, b); } /** - * @brief Angle between normalized vectors - * - * 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 @ref isNormalized(), @ref Quaternion::angle(), - * @ref Complex::angle() + * @copybrief Math::angle(const Vector&, const Vector&) + * @deprecated Use @ref Math::angle(const Vector&, const Vector&) + * instead. */ - static Rad angle(const Vector& normalizedA, const Vector& normalizedB); + CORRADE_DEPRECATED("use Math::angle() instead") static Rad angle(const Vector& normalizedA, const Vector& normalizedB) { + return Math::angle(normalizedA, normalizedB); + } + #endif /** * @brief Default constructor @@ -409,7 +434,7 @@ template class Vector { * @see @ref dot(const Vector&, const Vector&), * @ref isNormalized() */ - T dot() const { return dot(*this, *this); } + T dot() const { return Math::dot(*this, *this); } /** * @brief Vector length @@ -466,7 +491,7 @@ template class Vector { * @see @ref dot(), @ref projectedOntoNormalized() */ Vector projected(const Vector& line) const { - return line*dot(*this, line)/line.dot(); + return line*Math::dot(*this, line)/line.dot(); } /** @@ -1214,12 +1239,6 @@ extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utilit } #endif -template inline Rad Vector::angle(const Vector& normalizedA, const Vector& normalizedB) { - CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), - "Math::Vector::angle(): vectors must be normalized", {}); - return Rad(std::acos(dot(normalizedA, normalizedB))); -} - template inline BoolVector Vector::operator<(const Vector& other) const { BoolVector out; @@ -1267,7 +1286,7 @@ template inline Vector Vector::oper template inline Vector Vector::projectedOntoNormalized(const Vector& line) const { CORRADE_ASSERT(line.isNormalized(), "Math::Vector::projectedOntoNormalized(): line must be normalized", {}); - return line*dot(*this, line); + return line*Math::dot(*this, line); } template inline T Vector::sum() const { diff --git a/src/Magnum/Math/Vector2.h b/src/Magnum/Math/Vector2.h index 2885966eb..d73ccf29e 100644 --- a/src/Magnum/Math/Vector2.h +++ b/src/Magnum/Math/Vector2.h @@ -33,6 +33,24 @@ namespace Magnum { namespace Math { +/** +@brief 2D cross product + +2D version of cross product, also called perp-dot product, equivalent to +calling @ref cross(const Vector3&, const Vector3&) with Z coordinate set +to `0` and extracting only Z coordinate from the result (X and Y coordinates +are always zero). Returns `0` either when one of the vectors is zero or they +are parallel or antiparallel and `1` when two *normalized* vectors are +perpendicular. @f[ + \boldsymbol a \times \boldsymbol b = \boldsymbol a_\bot \cdot \boldsymbol b = a_xb_y - a_yb_x +@f] +@see @ref Vector2::perpendicular(), + @ref dot(const Vector&, const Vector&) + */ +template inline T cross(const Vector2& a, const Vector2& b) { + return dot(a.perpendicular(), b); +} + /** @brief Two-component vector @tparam T Data type @@ -82,23 +100,16 @@ template class Vector2: public Vector<2, T> { */ constexpr static Vector2 yScale(T scale) { return {T(1), scale}; } + #ifdef MAGNUM_BUILD_DEPRECATED /** - * @brief 2D cross product - * - * 2D version of cross product, also called perp-dot product, - * equivalent to calling @ref Vector3::cross() with Z coordinate set to - * `0` and extracting only Z coordinate from the result (X and Y - * coordinates are always zero). Returns `0` either when one of the - * vectors is zero or they are parallel or antiparallel and `1` when - * two *normalized* vectors are perpendicular, @f[ - * \boldsymbol a \times \boldsymbol b = \boldsymbol a_\bot \cdot \boldsymbol b = a_xb_y - a_yb_x - * @f] - * @see @ref perpendicular(), - * @ref dot(const Vector&, const Vector&) + * @copybrief Math::cross(const Vector2&, const Vector2&) + * @deprecated Use @ref Math::cross(const Vector2&, const Vector2&) + * instead. */ - static T cross(const Vector2& a, const Vector2& b) { - return Vector<2, T>::dot(a.perpendicular(), b); + CORRADE_DEPRECATED("use Math::cross() instead") static T cross(const Vector2& a, const Vector2& b) { + return Math::cross(a, b); } + #endif /** @copydoc Vector::Vector() */ constexpr /*implicit*/ Vector2() {} diff --git a/src/Magnum/Math/Vector3.h b/src/Magnum/Math/Vector3.h index 8273c3c36..93277ac0d 100644 --- a/src/Magnum/Math/Vector3.h +++ b/src/Magnum/Math/Vector3.h @@ -34,6 +34,27 @@ namespace Magnum { namespace Math { +/** +@brief Cross product + +Result has length of `0` either when one of them is zero or they are parallel +or antiparallel and length of `1` when two *normalized* vectors are +perpendicular. Done using the following equation: @f[ + \boldsymbol a \times \boldsymbol b = \begin{pmatrix} c_y \\ c_z \\ c_x \end{pmatrix} ~~~~~ + \boldsymbol c = \boldsymbol a \begin{pmatrix} b_y \\ b_z \\ b_x \end{pmatrix} - + \boldsymbol b \begin{pmatrix} a_y \\ a_z \\ a_x \end{pmatrix} +@f] +Which is equivalent to the common one (source: +https://twitter.com/sjb3d/status/563640846671953920): @f[ + \boldsymbol a \times \boldsymbol b = \begin{pmatrix}a_yb_z - a_zb_y \\ a_zb_x - a_xb_z \\ a_xb_y - a_yb_x \end{pmatrix} +@f] +@see @ref cross(const Vector2&, const Vector2&) +*/ +template inline Vector3 cross(const Vector3& a, const Vector3& b) { + return swizzle<'y', 'z', 'x'>(a*swizzle<'y', 'z', 'x'>(b) - + b*swizzle<'y', 'z', 'x'>(a)); +} + /** @brief Three-component vector @tparam T Data type @@ -101,26 +122,16 @@ template class Vector3: public Vector<3, T> { */ constexpr static Vector3 zScale(T scale) { return {T(1), T(1), scale}; } + #ifdef MAGNUM_BUILD_DEPRECATED /** - * @brief Cross product - * - * Result has length of `0` either when one of them is zero or they are - * parallel or antiparallel and length of `1` when two *normalized* - * vectors are perpendicular. Done using the following equation: @f[ - * \boldsymbol a \times \boldsymbol b = \begin{pmatrix} c_y \\ c_z \\ c_x \end{pmatrix} ~~~~~ - * \boldsymbol c = \boldsymbol a \begin{pmatrix} b_y \\ b_z \\ b_x \end{pmatrix} - - * \boldsymbol b \begin{pmatrix} a_y \\ a_z \\ a_x \end{pmatrix} - * @f] - * Which is equivalent to the common one (source: - * https://twitter.com/sjb3d/status/563640846671953920): @f[ - * \boldsymbol a \times \boldsymbol b = \begin{pmatrix}a_yb_z - a_zb_y \\ a_zb_x - a_xb_z \\ a_xb_y - a_yb_x \end{pmatrix} - * @f] - * @see @ref Vector2::cross() + * @copybrief Math::cross(const Vector3&, const Vector3&) + * @deprecated Use @ref Math::cross(const Vector3&, const Vector3&) + * instead. */ - static Vector3 cross(const Vector3& a, const Vector3& b) { - return swizzle<'y', 'z', 'x'>(a*swizzle<'y', 'z', 'x'>(b) - - b*swizzle<'y', 'z', 'x'>(a)); + CORRADE_DEPRECATED("use Math::cross() instead") static Vector3 cross(const Vector3& a, const Vector3& b) { + return Math::cross(a, b); } + #endif /** @copydoc Vector::Vector() */ constexpr /*implicit*/ Vector3() {} diff --git a/src/Magnum/MeshTools/GenerateFlatNormals.cpp b/src/Magnum/MeshTools/GenerateFlatNormals.cpp index 2e7ab6ac7..242ff0c1c 100644 --- a/src/Magnum/MeshTools/GenerateFlatNormals.cpp +++ b/src/Magnum/MeshTools/GenerateFlatNormals.cpp @@ -40,8 +40,8 @@ std::tuple, std::vector> generateFlatNormals(c std::vector normals; normals.reserve(indices.size()/3); for(std::size_t i = 0; i != indices.size(); i += 3) { - Vector3 normal = Vector3::cross(positions[indices[i+2]]-positions[indices[i+1]], - positions[indices[i]]-positions[indices[i+1]]).normalized(); + const Vector3 normal = Math::cross(positions[indices[i+2]]-positions[indices[i+1]], + positions[indices[i]]-positions[indices[i+1]]).normalized(); /* Use the same normal for all three vertices of the face */ normalIndices.push_back(normals.size());