Browse Source

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<size, T>::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<T>::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.
pull/94/head
Vladimír Vondruš 11 years ago
parent
commit
64bc2393d1
  1. 6
      src/Magnum/DebugTools/Implementation/CapsuleRendererTransformation.h
  2. 6
      src/Magnum/DebugTools/Implementation/CylinderRendererTransformation.h
  3. 6
      src/Magnum/DebugTools/Implementation/ForceRendererTransformation.h
  4. 8
      src/Magnum/DebugTools/Test/CapsuleRendererTest.cpp
  5. 8
      src/Magnum/DebugTools/Test/CylinderRendererTest.cpp
  6. 8
      src/Magnum/DebugTools/Test/ForceRendererTest.cpp
  7. 12
      src/Magnum/Math/Algorithms/Test/GramSchmidtTest.cpp
  8. 63
      src/Magnum/Math/Complex.h
  9. 2
      src/Magnum/Math/DualQuaternion.h
  10. 4
      src/Magnum/Math/Functions.h
  11. 16
      src/Magnum/Math/Geometry/Distance.h
  12. 11
      src/Magnum/Math/Geometry/Intersection.h
  13. 2
      src/Magnum/Math/Matrix.h
  14. 4
      src/Magnum/Math/Matrix4.h
  15. 175
      src/Magnum/Math/Quaternion.h
  16. 18
      src/Magnum/Math/Test/ComplexTest.cpp
  17. 38
      src/Magnum/Math/Test/QuaternionTest.cpp
  18. 8
      src/Magnum/Math/Test/Vector2Test.cpp
  19. 2
      src/Magnum/Math/Test/Vector3Test.cpp
  20. 14
      src/Magnum/Math/Test/VectorTest.cpp
  21. 75
      src/Magnum/Math/Vector.h
  22. 39
      src/Magnum/Math/Vector2.h
  23. 45
      src/Magnum/Math/Vector3.h
  24. 4
      src/Magnum/MeshTools/GenerateFlatNormals.cpp

6
src/Magnum/DebugTools/Implementation/CapsuleRendererTransformation.h

@ -74,7 +74,7 @@ template<> std::array<Matrix4, 3> capsuleRendererTransformation<3>(const Vector3
Vector3 capDistance;
if(length >= Math::TypeTraits<Float>::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<Float>::epsilon()) {
@ -85,8 +85,8 @@ template<> std::array<Matrix4, 3> 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());
}

6
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<Float>::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<Float>::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());
}
}

6
src/Magnum/DebugTools/Implementation/ForceRendererTransformation.h

@ -47,17 +47,17 @@ template<> Matrix4 forceRendererTransformation<3>(const Vector3& forcePosition,
if(forceLength < Math::TypeTraits<Float>::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<Float>::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 */

8
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);

8
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));
}

8
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<Float>::epsilon());
CORRADE_VERIFY(Math::abs(Math::dot(m.up(), m.backward())) < Math::TypeTraits<Float>::epsilon());
}
}}}}

12
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),

63
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<class T> inline T dot(const Complex<T>& a, const Complex<T>& 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<T>&, const Quaternion<T>&),
@ref angle(const Vector<size, T>&, const Vector<size, T>&)
*/
template<class T> inline Rad<T> angle(const Complex<T>& normalizedA, const Complex<T>& normalizedB) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::angle(): complex numbers must be normalized", {});
return Rad<T>(std::acos(normalizedA.real()*normalizedB.real() + normalizedA.imaginary()*normalizedB.imaginary()));
}
/**
@brief Complex number
@tparam T Data type
@ -56,32 +84,25 @@ template<class T> 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<T>&, const Complex<T>&)
* @deprecated Use @ref Math::dot(const Complex<T>&, const Complex<T>&)
* instead.
*/
static T dot(const Complex<T>& a, const Complex<T>& b) {
return a._real*b._real + a._imaginary*b._imaginary;
CORRADE_DEPRECATED("use Math::dot() instead") static T dot(const Complex<T>& a, const Complex<T>& 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<T>&, const Complex<T>&)
* @deprecated Use @ref Math::angle(const Complex<T>&, const Complex<T>&)
* instead.
*/
static Rad<T> angle(const Complex<T>& normalizedA, const Complex<T>& normalizedB) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::Complex::angle(): complex numbers must be normalized", {});
return Rad<T>(std::acos(normalizedA._real*normalizedB._real + normalizedA._imaginary*normalizedB._imaginary));
CORRADE_DEPRECATED("use Math::angle() instead") static Rad<T> angle(const Complex<T>& normalizedA, const Complex<T>& normalizedB) {
return Math::angle(normalizedA, normalizedB);
}
#endif
/**
* @brief Rotation complex number
@ -328,9 +349,7 @@ template<class T> 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

2
src/Magnum/Math/DualQuaternion.h

@ -233,7 +233,7 @@ template<class T> class DualQuaternion: public Dual<Quaternion<T>> {
* @f]
*/
Dual<T> lengthSquared() const {
return {Dual<Quaternion<T>>::real().dot(), T(2)*Quaternion<T>::dot(Dual<Quaternion<T>>::real(), Dual<Quaternion<T>>::dual())};
return {Dual<Quaternion<T>>::real().dot(), T(2)*dot(Dual<Quaternion<T>>::real(), Dual<Quaternion<T>>::dual())};
}
/**

4
src/Magnum/Math/Functions.h

@ -358,9 +358,7 @@ template<std::size_t size, class T> Vector<size, T> sqrtInverted(const Vector<si
The interpolation for vectors is done as in following, similarly for scalars: @f[
\boldsymbol v_{LERP} = (1 - t) \boldsymbol v_A + t \boldsymbol v_B
@f]
@see @ref lerpInverted(), @ref Quaternion::lerp()
@todo http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
(when SIMD is in place)
@see @ref lerpInverted(), @ref lerp(const Quaternion<T>&, const Quaternion<T>&, T)
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class T, class U> inline T lerp(const T& a, const T& b, U t);

16
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<T>&, const Vector2<T>&) "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<class T> static T linePoint(const Vector2<T>& a, const Vector2<T>& b, const Vector2<T>& point) {
const Vector2<T> bMinusA = b - a;
return std::abs(Vector2<T>::cross(bMinusA, a - point))/bMinusA.length();
return std::abs(cross(bMinusA, a - point))/bMinusA.length();
}
/**
@ -70,7 +70,7 @@ class Distance {
*/
template<class T> static T linePointSquared(const Vector2<T>& a, const Vector2<T>& b, const Vector2<T>& point) {
const Vector2<T> bMinusA = b - a;
return Math::pow<2>(Vector2<T>::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<T>&, const Vector3<T>&) "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<class T> static T linePointSquared(const Vector3<T>& a, const Vector3<T>& b, const Vector3<T>& point) {
return Vector3<T>::cross(point - a, point - b).dot()/(b - a).dot();
return cross(point - a, point - b).dot()/(b - a).dot();
}
/**
@ -181,7 +181,7 @@ template<class T> T Distance::lineSegmentPoint(const Vector2<T>& a, const Vector
return std::sqrt(pointDistanceB);
/* Between A and B */
return std::abs(Vector2<T>::cross(bMinusA, -pointMinusA))/std::sqrt(bDistanceA);
return std::abs(cross(bMinusA, -pointMinusA))/std::sqrt(bDistanceA);
}
template<class T> T Distance::lineSegmentPointSquared(const Vector2<T>& a, const Vector2<T>& b, const Vector2<T>& point) {
@ -201,7 +201,7 @@ template<class T> T Distance::lineSegmentPointSquared(const Vector2<T>& a, const
return pointDistanceB;
/* Between A and B */
return Math::pow<2>(Vector2<T>::cross(bMinusA, -pointMinusA))/bDistanceA;
return Math::pow<2>(cross(bMinusA, -pointMinusA))/bDistanceA;
}
template<class T> T Distance::lineSegmentPointSquared(const Vector3<T>& a, const Vector3<T>& b, const Vector3<T>& point) {
@ -220,7 +220,7 @@ template<class T> T Distance::lineSegmentPointSquared(const Vector3<T>& a, const
return pointDistanceB;
/* Between A and B */
return Vector3<T>::cross(pointMinusA, pointMinusB).dot()/bDistanceA;
return cross(pointMinusA, pointMinusB).dot()/bDistanceA;
}
}}}

11
src/Magnum/Math/Geometry/Intersection.h

@ -72,9 +72,8 @@ class Intersection {
*/
template<class T> static std::pair<T, T> lineSegmentLineSegment(const Vector2<T>& p, const Vector2<T>& r, const Vector2<T>& q, const Vector2<T>& s) {
const Vector2<T> qp = q - p;
const T rs = Vector2<T>::cross(r, s);
return {Vector2<T>::cross(qp, s)/rs,
Vector2<T>::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<class T> static T lineSegmentLine(const Vector2<T>& p, const Vector2<T>& r, const Vector2<T>& q, const Vector2<T>& s) {
return Vector2<T>::cross(q - p, s)/Vector2<T>::cross(r, s);
return cross(q - p, s)/cross(r, s);
}
/**
@ -121,8 +120,8 @@ class Intersection {
* @f]
*/
template<class T> static T planeLine(const Vector3<T>& planePosition, const Vector3<T>& planeNormal, const Vector3<T>& p, const Vector3<T>& r) {
const T f = Vector3<T>::dot(planePosition, planeNormal);
return (f-Vector3<T>::dot(planeNormal, p))/Vector3<T>::dot(planeNormal, r);
const T f = dot(planePosition, planeNormal);
return (f-dot(planeNormal, p))/dot(planeNormal, r);
}
};

2
src/Magnum/Math/Matrix.h

@ -284,7 +284,7 @@ template<std::size_t size, class T> bool Matrix<size, T>::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<size, T>::dot((*this)[i], (*this)[j]) > TypeTraits<T>::epsilon())
if(dot((*this)[i], (*this)[j]) > TypeTraits<T>::epsilon())
return false;
return true;

4
src/Magnum/Math/Matrix4.h

@ -539,8 +539,8 @@ template<class T> Matrix4<T> Matrix4<T>::perspectiveProjection(const Vector2<T>&
template<class T> Matrix4<T> Matrix4<T>::lookAt(const Vector3<T>& eye, const Vector3<T>& target, const Vector3<T>& up) {
const Vector3<T> backward = (eye - target).normalized();
const Vector3<T> right = Vector3<T>::cross(up, backward).normalized();
const Vector3<T> realUp = Vector3<T>::cross(backward, right);
const Vector3<T> right = cross(up, backward).normalized();
const Vector3<T> realUp = cross(backward, right);
return {{ right, T(0)},
{ realUp, T(0)},

175
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<class T> inline T dot(const Quaternion<T>& a, const Quaternion<T>& b) {
return dot(a.vector(), b.vector()) + a.scalar()*b.scalar();
}
namespace Implementation {
/* Used in angle() and slerp() (no assertions) */
template<class T> inline Rad<T> angle(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB) {
return Rad<T>{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<T>&, const Complex<T>&),
@ref angle(const Vector<size, T>&, const Vector<size, T>&)
*/
template<class T> inline Rad<T> angle(const Quaternion<T>& normalizedA, const Quaternion<T>& 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<T>&, const Quaternion<T>&, T),
@ref lerp(const T&, const T&, U)
*/
template<class T> inline Quaternion<T> lerp(const Quaternion<T>& normalizedA, const Quaternion<T>& 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<T>&, const Quaternion<T>&, T)
*/
template<class T> inline Quaternion<T> slerp(const Quaternion<T>& normalizedA, const Quaternion<T>& 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 T> 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<T>&, const Quaternion<T>&)
* @deprecated Use @ref Math::dot(const Quaternion<T>&, const Quaternion<T>&)
* instead.
*/
static T dot(const Quaternion<T>& a, const Quaternion<T>& b) {
/** @todo Use four-component SIMD implementation when available */
return Vector3<T>::dot(a.vector(), b.vector()) + a.scalar()*b.scalar();
CORRADE_DEPRECATED("use Math::dot() instead") static T dot(const Quaternion<T>& a, const Quaternion<T>& 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<T>&, const Quaternion<T>&)
* @deprecated Use @ref Math::angle(const Quaternion<T>&, const Quaternion<T>&)
* instead.
*/
static Rad<T> angle(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB);
CORRADE_DEPRECATED("use Math::angle() instead") static Rad<T> angle(const Quaternion<T>& normalizedA, const Quaternion<T>& 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<T>&, const Quaternion<T>&, T)
* @deprecated Use @ref Math::lerp(const Quaternion<T>&, const Quaternion<T>&, T)
* instead.
*/
static Quaternion<T> lerp(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB, T t);
CORRADE_DEPRECATED("use Math::lerp() instead") static Quaternion<T> lerp(const Quaternion<T>& normalizedA, const Quaternion<T>& 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<T>&, const Quaternion<T>&, T)
* @deprecated Use @ref Math::slerp(const Quaternion<T>&, const Quaternion<T>&, T)
* instead.
*/
static Quaternion<T> slerp(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB, T t);
CORRADE_DEPRECATED("use Math::slerp() instead") static Quaternion<T> slerp(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB, T t) {
return Math::slerp(normalizedA, normalizedB, t);
}
#endif
/**
* @brief Rotation quaternion
@ -331,7 +390,7 @@ template<class T> class Quaternion {
* @see @ref isNormalized(),
* @ref dot(const Quaternion<T>&, const Quaternion<T>&)
*/
T dot() const { return dot(*this, *this); }
T dot() const { return Math::dot(*this, *this); }
/**
* @brief Quaternion length
@ -424,11 +483,6 @@ template<class T> class Quaternion {
return value*value;
}
/* Used in angle() and slerp() (no assertions) */
static T angleInternal(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB) {
return std::acos(dot(normalizedA, normalizedB));
}
Vector3<T> _vector;
T _scalar;
};
@ -463,16 +517,12 @@ template<class T> 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<Float>&);
#ifndef MAGNUM_TARGET_GLES
extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Quaternion<Double>&);
#endif
/** @endprivatesection */
#endif
namespace Implementation {
@ -510,25 +560,6 @@ template<class T> Quaternion<T> quaternionFromMatrix(const Matrix3x3<T>& m) {
}
template<class T> inline Rad<T> Quaternion<T>::angle(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::Quaternion::angle(): quaternions must be normalized", {});
return Rad<T>(angleInternal(normalizedA, normalizedB));
}
template<class T> inline Quaternion<T> Quaternion<T>::lerp(const Quaternion<T>& normalizedA, const Quaternion<T>& 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<class T> inline Quaternion<T> Quaternion<T>::slerp(const Quaternion<T>& normalizedA, const Quaternion<T>& 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<class T> inline Quaternion<T> Quaternion<T>::rotation(const Rad<T> angle, const Vector3<T>& normalizedAxis) {
CORRADE_ASSERT(normalizedAxis.isNormalized(),
"Math::Quaternion::rotation(): axis must be normalized", {});
@ -565,8 +596,8 @@ template<class T> Matrix3x3<T> Quaternion<T>::toMatrix() const {
}
template<class T> inline Quaternion<T> Quaternion<T>::operator*(const Quaternion<T>& other) const {
return {_scalar*other._vector + other._scalar*_vector + Vector3<T>::cross(_vector, other._vector),
_scalar*other._scalar - Vector3<T>::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<class T> inline Quaternion<T> Quaternion<T>::invertedNormalized() const {
@ -576,8 +607,8 @@ template<class T> inline Quaternion<T> Quaternion<T>::invertedNormalized() const
template<class T> inline Vector3<T> Quaternion<T>::transformVectorNormalized(const Vector3<T>& vector) const {
CORRADE_ASSERT(isNormalized(), "Math::Quaternion::transformVectorNormalized(): quaternion must be normalized", {});
const Vector3<T> t = T(2)*Vector3<T>::cross(_vector, vector);
return vector + _scalar*t + Vector3<T>::cross(_vector, t);
const Vector3<T> t = T(2)*Math::cross(_vector, vector);
return vector + _scalar*t + Math::cross(_vector, t);
}
}}

18
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));
}

38
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));
}

8
src/Magnum/Math/Test/Vector2Test.cpp

@ -27,7 +27,7 @@
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/Utility/Configuration.h>
#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<Int>({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());
}

2
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() {

14
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));
}

75
src/Magnum/Math/Vector.h

@ -31,8 +31,9 @@
#include <cmath>
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Debug.h>
#include <Corrade/Utility/ConfigurationValue.h>
#include <Corrade/Utility/Debug.h>
#include <Corrade/Utility/Macros.h>
#include "Magnum/visibility.h"
#include "Magnum/Math/Angle.h"
@ -45,6 +46,35 @@ namespace Implementation {
template<std::size_t, class, class> 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<std::size_t size, class T> inline T dot(const Vector<size, T>& a, const Vector<size, T>& 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<T>&, const Complex<T>&),
@ref angle(const Quaternion<T>&, const Quaternion<T>&)
*/
template<std::size_t size, class T> inline Rad<T> angle(const Vector<size, T>& normalizedA, const Vector<size, T>& normalizedB) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::angle(): vectors must be normalized", {});
return Rad<T>(std::acos(dot(normalizedA, normalizedB)));
}
/**
@brief Vector
@tparam size Vector size
@ -89,30 +119,25 @@ template<std::size_t size, class T> class Vector {
return padInternal<otherSize>(typename Implementation::GenerateSequence<size>::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<size, T>&, const Vector<size, T>&)
* @deprecated Use @ref Math::dot(const Vector<size, T>&, const Vector<size, T>&)
* instead.
*/
static T dot(const Vector<size, T>& a, const Vector<size, T>& b) {
return (a*b).sum();
CORRADE_DEPRECATED("use Math::dot() instead") static T dot(const Vector<size, T>& a, const Vector<size, T>& 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<size, T>&, const Vector<size, T>&)
* @deprecated Use @ref Math::angle(const Vector<size, T>&, const Vector<size, T>&)
* instead.
*/
static Rad<T> angle(const Vector<size, T>& normalizedA, const Vector<size, T>& normalizedB);
CORRADE_DEPRECATED("use Math::angle() instead") static Rad<T> angle(const Vector<size, T>& normalizedA, const Vector<size, T>& normalizedB) {
return Math::angle(normalizedA, normalizedB);
}
#endif
/**
* @brief Default constructor
@ -409,7 +434,7 @@ template<std::size_t size, class T> class Vector {
* @see @ref dot(const Vector<size, T>&, const Vector<size, T>&),
* @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<std::size_t size, class T> class Vector {
* @see @ref dot(), @ref projectedOntoNormalized()
*/
Vector<size, T> projected(const Vector<size, T>& 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<std::size_t size, class T> inline Rad<T> Vector<size, T>::angle(const Vector<size, T>& normalizedA, const Vector<size, T>& normalizedB) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::Vector::angle(): vectors must be normalized", {});
return Rad<T>(std::acos(dot(normalizedA, normalizedB)));
}
template<std::size_t size, class T> inline BoolVector<size> Vector<size, T>::operator<(const Vector<size, T>& other) const {
BoolVector<size> out;
@ -1267,7 +1286,7 @@ template<std::size_t size, class T> inline Vector<size, T> Vector<size, T>::oper
template<std::size_t size, class T> inline Vector<size, T> Vector<size, T>::projectedOntoNormalized(const Vector<size, T>& 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<std::size_t size, class T> inline T Vector<size, T>::sum() const {

39
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<T>&, const Vector3<T>&) 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<size, T>&, const Vector<size, T>&)
*/
template<class T> inline T cross(const Vector2<T>& a, const Vector2<T>& b) {
return dot(a.perpendicular(), b);
}
/**
@brief Two-component vector
@tparam T Data type
@ -82,23 +100,16 @@ template<class T> class Vector2: public Vector<2, T> {
*/
constexpr static Vector2<T> 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<size, T>&, const Vector<size, T>&)
* @copybrief Math::cross(const Vector2<T>&, const Vector2<T>&)
* @deprecated Use @ref Math::cross(const Vector2<T>&, const Vector2<T>&)
* instead.
*/
static T cross(const Vector2<T>& a, const Vector2<T>& b) {
return Vector<2, T>::dot(a.perpendicular(), b);
CORRADE_DEPRECATED("use Math::cross() instead") static T cross(const Vector2<T>& a, const Vector2<T>& b) {
return Math::cross(a, b);
}
#endif
/** @copydoc Vector::Vector() */
constexpr /*implicit*/ Vector2() {}

45
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<T>&, const Vector2<T>&)
*/
template<class T> inline Vector3<T> cross(const Vector3<T>& a, const Vector3<T>& 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 T> class Vector3: public Vector<3, T> {
*/
constexpr static Vector3<T> 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<T>&, const Vector3<T>&)
* @deprecated Use @ref Math::cross(const Vector3<T>&, const Vector3<T>&)
* instead.
*/
static Vector3<T> cross(const Vector3<T>& a, const Vector3<T>& 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<T> cross(const Vector3<T>& a, const Vector3<T>& b) {
return Math::cross(a, b);
}
#endif
/** @copydoc Vector::Vector() */
constexpr /*implicit*/ Vector3() {}

4
src/Magnum/MeshTools/GenerateFlatNormals.cpp

@ -40,8 +40,8 @@ std::tuple<std::vector<UnsignedInt>, std::vector<Vector3>> generateFlatNormals(c
std::vector<Vector3> 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());

Loading…
Cancel
Save