Browse Source

Math: helpfully print the offending values in all assertions.

pull/286/merge
Vladimír Vondruš 8 years ago
parent
commit
0cf65741ae
  1. 2
      doc/changelog.dox
  2. 10
      src/Magnum/Math/Complex.h
  3. 4
      src/Magnum/Math/CubicHermite.h
  4. 2
      src/Magnum/Math/Distance.h
  5. 2
      src/Magnum/Math/DualComplex.h
  6. 10
      src/Magnum/Math/DualQuaternion.h
  7. 3
      src/Magnum/Math/Intersection.h
  8. 2
      src/Magnum/Math/Matrix.h
  9. 10
      src/Magnum/Math/Matrix3.h
  10. 12
      src/Magnum/Math/Matrix4.h
  11. 25
      src/Magnum/Math/Quaternion.h
  12. 61
      src/Magnum/Math/Test/ComplexTest.cpp
  13. 32
      src/Magnum/Math/Test/CubicHermiteTest.cpp
  14. 17
      src/Magnum/Math/Test/DistanceTest.cpp
  15. 32
      src/Magnum/Math/Test/DualComplexTest.cpp
  16. 68
      src/Magnum/Math/Test/DualQuaternionTest.cpp
  17. 14
      src/Magnum/Math/Test/IntersectionTest.cpp
  18. 54
      src/Magnum/Math/Test/Matrix3Test.cpp
  19. 73
      src/Magnum/Math/Test/Matrix4Test.cpp
  20. 20
      src/Magnum/Math/Test/MatrixTest.cpp
  21. 140
      src/Magnum/Math/Test/QuaternionTest.cpp
  22. 39
      src/Magnum/Math/Test/VectorTest.cpp
  23. 5
      src/Magnum/Math/Vector.h

2
doc/changelog.dox

@ -255,6 +255,8 @@ See also:
@subsubsection changelog-latest-changes-math Math library
- All assertions in the @ref Math namespace now helpfully print the offending
value that caused the assert for easier debugging
- @ref Math::Matrix3::rotation() const and @ref Math::Matrix4::rotation() const
now allow non-uniform scaling, but expect the roation/scaling part to be
orthogonal after normalization

10
src/Magnum/Math/Complex.h

@ -71,7 +71,7 @@ Expects that both complex numbers are normalized. @f[
*/
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", {});
"Math::angle(): complex numbers" << normalizedA << "and" << normalizedB << "are not normalized", {});
return Rad<T>(std::acos(dot(normalizedA, normalizedB)));
}
@ -115,7 +115,7 @@ template<class T> class Complex {
*/
static Complex<T> fromMatrix(const Matrix2x2<T>& matrix) {
CORRADE_ASSERT(matrix.isOrthogonal(),
"Math::Complex::fromMatrix(): the matrix is not orthogonal", {});
"Math::Complex::fromMatrix(): the matrix is not orthogonal:" << Corrade::Utility::Debug::newline << matrix, {});
return Implementation::complexFromMatrix(matrix);
}
@ -488,7 +488,7 @@ template<class T> class Complex {
*/
Complex<T> invertedNormalized() const {
CORRADE_ASSERT(isNormalized(),
"Math::Complex::invertedNormalized(): complex number must be normalized", {});
"Math::Complex::invertedNormalized():" << *this << "is not normalized", {});
return conjugated();
}
@ -574,7 +574,7 @@ Expects that both complex numbers are normalized. @f[
*/
template<class T> inline Complex<T> lerp(const Complex<T>& normalizedA, const Complex<T>& normalizedB, T t) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::lerp(): complex numbers must be normalized", {});
"Math::lerp(): complex numbers" << normalizedA << "and" << normalizedB << "are not normalized", {});
return ((T(1) - t)*normalizedA + t*normalizedB).normalized();
}
@ -596,7 +596,7 @@ the same, returns the first argument. @f[
*/
template<class T> inline Complex<T> slerp(const Complex<T>& normalizedA, const Complex<T>& normalizedB, T t) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::slerp(): complex numbers must be normalized", {});
"Math::slerp(): complex numbers" << normalizedA << "and" << normalizedB << "are not normalized", {});
const T cosAngle = dot(normalizedA, normalizedB);
/* Avoid division by zero */

4
src/Magnum/Math/CubicHermite.h

@ -493,7 +493,7 @@ Expects that @ref CubicHermite::point() is a normalized complex number in both
*/
template<class T> Complex<T> splerp(const CubicHermiteComplex<T>& a, const CubicHermiteComplex<T>& b, T t) {
CORRADE_ASSERT(a.point().isNormalized() && b.point().isNormalized(),
"Math::splerp(): complex spline points must be normalized", {});
"Math::splerp(): complex spline points" << a.point() << "and" << b.point() << "are not normalized", {});
return ((T(2)*t*t*t - T(3)*t*t + T(1))*a.point() +
(t*t*t - T(2)*t*t + t)*a.outTangent() +
(T(-2)*t*t*t + T(3)*t*t)*b.point() +
@ -523,7 +523,7 @@ and @p b.
*/
template<class T> Quaternion<T> splerp(const CubicHermiteQuaternion<T>& a, const CubicHermiteQuaternion<T>& b, T t) {
CORRADE_ASSERT(a.point().isNormalized() && b.point().isNormalized(),
"Math::splerp(): quaternion spline points must be normalized", {});
"Math::splerp(): quaternion spline points" << a.point() << "and" << b.point() << "are not normalized", {});
return ((T(2)*t*t*t - T(3)*t*t + T(1))*a.point() +
(t*t*t - T(2)*t*t + t)*a.outTangent() +
(T(-2)*t*t*t + T(3)*t*t)*b.point() +

2
src/Magnum/Math/Distance.h

@ -210,7 +210,7 @@ top.
*/
template<class T> inline T pointPlaneNormalized(const Vector3<T>& point, const Vector4<T>& plane) {
CORRADE_ASSERT(plane.xyz().isNormalized(),
"Math::Geometry::Distance::pointPlaneNormalized(): plane normal is not an unit vector", {});
"Math::Distance::pointPlaneNormalized(): plane normal" << plane.xyz() << "is not normalized", {});
return pointPlaneScaled<T>(point, plane);
}

2
src/Magnum/Math/DualComplex.h

@ -98,7 +98,7 @@ template<class T> class DualComplex: public Dual<Complex<T>> {
*/
static DualComplex<T> fromMatrix(const Matrix3<T>& matrix) {
CORRADE_ASSERT(matrix.isRigidTransformation(),
"Math::DualComplex::fromMatrix(): the matrix doesn't represent rigid transformation", {});
"Math::DualComplex::fromMatrix(): the matrix doesn't represent rigid transformation:" << Corrade::Utility::Debug::newline << matrix, {});
return {Implementation::complexFromMatrix(matrix.rotationScaling()), Complex<T>(matrix.translation())};
}

10
src/Magnum/Math/DualQuaternion.h

@ -79,7 +79,7 @@ Note that this function does not check for shortest path interpolation, see
*/
template<class T> inline DualQuaternion<T> sclerp(const DualQuaternion<T>& normalizedA, const DualQuaternion<T>& normalizedB, const T t) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::sclerp(): dual quaternions must be normalized", {});
"Math::sclerp(): dual quaternions" << normalizedA << "and" << normalizedB << "are not normalized", {});
const T cosHalfAngle = dot(normalizedA.real(), normalizedB.real());
/* Avoid division by zero: interpolate just the translation part */
@ -146,7 +146,7 @@ otherwise, the interpolation is performed as: @f[
*/
template<class T> inline DualQuaternion<T> sclerpShortestPath(const DualQuaternion<T>& normalizedA, const DualQuaternion<T>& normalizedB, const T t) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::sclerp(): dual quaternions must be normalized", {});
"Math::sclerp(): dual quaternions" << normalizedA << "and" << normalizedB << "are not normalized", {});
const T cosHalfAngle = dot(normalizedA.real(), normalizedB.real());
/* Avoid division by zero: interpolate just the translation part */
@ -238,7 +238,7 @@ template<class T> class DualQuaternion: public Dual<Quaternion<T>> {
*/
static DualQuaternion<T> fromMatrix(const Matrix4<T>& matrix) {
CORRADE_ASSERT(matrix.isRigidTransformation(),
"Math::DualQuaternion::fromMatrix(): the matrix doesn't represent rigid transformation", {});
"Math::DualQuaternion::fromMatrix(): the matrix doesn't represent a rigid transformation:" << Corrade::Utility::Debug::newline << matrix, {});
Quaternion<T> q = Implementation::quaternionFromMatrix(matrix.rotationScaling());
return {q, Quaternion<T>(matrix.translation()/2)*q};
@ -481,7 +481,7 @@ template<class T> class DualQuaternion: public Dual<Quaternion<T>> {
*/
DualQuaternion<T> invertedNormalized() const {
CORRADE_ASSERT(isNormalized(),
"Math::DualQuaternion::invertedNormalized(): dual quaternion must be normalized", {});
"Math::DualQuaternion::invertedNormalized():" << *this << "is not normalized", {});
return quaternionConjugated();
}
@ -515,7 +515,7 @@ template<class T> class DualQuaternion: public Dual<Quaternion<T>> {
*/
Vector3<T> transformPointNormalized(const Vector3<T>& vector) const {
CORRADE_ASSERT(isNormalized(),
"Math::DualQuaternion::transformPointNormalized(): dual quaternion must be normalized", {});
"Math::DualQuaternion::transformPointNormalized():" << *this << "is not normalized", {});
return ((*this)*DualQuaternion<T>(vector)*conjugated()).dual().vector();
}

3
src/Magnum/Math/Intersection.h

@ -517,7 +517,8 @@ template<class T> bool sphereConeView(const Vector3<T>& sphereCenter, const T sp
}
template<class T> bool sphereConeView(const Vector3<T>& sphereCenter, const T sphereRadius, const Matrix4<T>& coneView, const T sinAngle, const T tanAngle) {
CORRADE_ASSERT(coneView.isRigidTransformation(), "Math::Geometry::Intersection::sphereConeView(): coneView does not represent a rigid transformation", false);
CORRADE_ASSERT(coneView.isRigidTransformation(),
"Math::Intersection::sphereConeView(): coneView does not represent a rigid transformation:" << Corrade::Utility::Debug::newline << coneView, false);
/* Transform the sphere so that we can test against Z axis aligned origin
cone instead */

2
src/Magnum/Math/Matrix.h

@ -202,7 +202,7 @@ template<std::size_t size, class T> class Matrix: public RectangularMatrix<size,
*/
Matrix<size, T> invertedOrthogonal() const {
CORRADE_ASSERT(isOrthogonal(),
"Math::Matrix::invertedOrthogonal(): the matrix is not orthogonal", {});
"Math::Matrix::invertedOrthogonal(): the matrix is not orthogonal:" << Corrade::Utility::Debug::Debug::newline << *this, {});
return RectangularMatrix<size, size, T>::transposed();
}

10
src/Magnum/Math/Matrix3.h

@ -117,7 +117,7 @@ template<class T> class Matrix3: public Matrix3x3<T> {
*/
static Matrix3<T> reflection(const Vector2<T>& normal) {
CORRADE_ASSERT(normal.isNormalized(),
"Math::Matrix3::reflection(): normal must be normalized", {});
"Math::Matrix3::reflection(): normal" << normal << "is not normalized", {});
return from(Matrix2x2<T>() - T(2)*normal*RectangularMatrix<1, 2, T>(normal).transposed(), {});
}
@ -665,7 +665,7 @@ template<class T> Matrix2x2<T> Matrix3<T>::rotation() const {
Matrix2x2<T> rotation{(*this)[0].xy().normalized(),
(*this)[1].xy().normalized()};
CORRADE_ASSERT(rotation.isOrthogonal(),
"Math::Matrix3::rotation(): the normalized rotation part is not orthogonal", {});
"Math::Matrix3::rotation(): the normalized rotation part is not orthogonal:" << Corrade::Utility::Debug::newline << rotation, {});
return rotation;
}
@ -673,20 +673,20 @@ template<class T> Matrix2x2<T> Matrix3<T>::rotationNormalized() const {
Matrix2x2<T> rotation{(*this)[0].xy(),
(*this)[1].xy()};
CORRADE_ASSERT(rotation.isOrthogonal(),
"Math::Matrix3::rotationNormalized(): the rotation part is not orthogonal", {});
"Math::Matrix3::rotationNormalized(): the rotation part is not orthogonal:" << Corrade::Utility::Debug::newline << rotation, {});
return rotation;
}
template<class T> T Matrix3<T>::uniformScalingSquared() const {
const T scalingSquared = (*this)[0].xy().dot();
CORRADE_ASSERT(TypeTraits<T>::equals((*this)[1].xy().dot(), scalingSquared),
"Math::Matrix3::uniformScaling(): the matrix doesn't have uniform scaling", {});
"Math::Matrix3::uniformScaling(): the matrix doesn't have uniform scaling:" << Corrade::Utility::Debug::newline << rotationScaling(), {});
return scalingSquared;
}
template<class T> inline Matrix3<T> Matrix3<T>::invertedRigid() const {
CORRADE_ASSERT(isRigidTransformation(),
"Math::Matrix3::invertedRigid(): the matrix doesn't represent rigid transformation", {});
"Math::Matrix3::invertedRigid(): the matrix doesn't represent a rigid transformation:" << Corrade::Utility::Debug::newline << *this, {});
Matrix2x2<T> inverseRotation = rotationScaling().transposed();
return from(inverseRotation, inverseRotation*-translation());

12
src/Magnum/Math/Matrix4.h

@ -890,7 +890,7 @@ MAGNUM_MATRIXn_OPERATOR_IMPLEMENTATION(4, Matrix4)
template<class T> Matrix4<T> Matrix4<T>::rotation(const Rad<T> angle, const Vector3<T>& normalizedAxis) {
CORRADE_ASSERT(normalizedAxis.isNormalized(),
"Math::Matrix4::rotation(): axis must be normalized", {});
"Math::Matrix4::rotation(): axis" << normalizedAxis << "is not normalized", {});
const T sine = std::sin(T(angle));
const T cosine = std::cos(T(angle));
@ -952,7 +952,7 @@ template<class T> Matrix4<T> Matrix4<T>::rotationZ(const Rad<T> angle) {
template<class T> Matrix4<T> Matrix4<T>::reflection(const Vector3<T>& normal) {
CORRADE_ASSERT(normal.isNormalized(),
"Math::Matrix4::reflection(): normal must be normalized", {});
"Math::Matrix4::reflection(): normal" << normal << "is not normalized", {});
return from(Matrix3x3<T>() - T(2)*normal*RectangularMatrix<1, 3, T>(normal).transposed(), {});
}
@ -995,7 +995,7 @@ template<class T> Matrix3x3<T> Matrix4<T>::rotation() const {
(*this)[1].xyz().normalized(),
(*this)[2].xyz().normalized()};
CORRADE_ASSERT(rotation.isOrthogonal(),
"Math::Matrix4::rotation(): the normalized rotation part is not orthogonal", {});
"Math::Matrix4::rotation(): the normalized rotation part is not orthogonal:" << Corrade::Utility::Debug::newline << rotation, {});
return rotation;
}
@ -1004,7 +1004,7 @@ template<class T> Matrix3x3<T> Matrix4<T>::rotationNormalized() const {
(*this)[1].xyz(),
(*this)[2].xyz()};
CORRADE_ASSERT(rotation.isOrthogonal(),
"Math::Matrix4::rotationNormalized(): the rotation part is not orthogonal", {});
"Math::Matrix4::rotationNormalized(): the rotation part is not orthogonal:" << Corrade::Utility::Debug::newline << rotation, {});
return rotation;
}
@ -1012,13 +1012,13 @@ template<class T> T Matrix4<T>::uniformScalingSquared() const {
const T scalingSquared = (*this)[0].xyz().dot();
CORRADE_ASSERT(TypeTraits<T>::equals((*this)[1].xyz().dot(), scalingSquared) &&
TypeTraits<T>::equals((*this)[2].xyz().dot(), scalingSquared),
"Math::Matrix4::uniformScaling(): the matrix doesn't have uniform scaling", {});
"Math::Matrix4::uniformScaling(): the matrix doesn't have uniform scaling:" << Corrade::Utility::Debug::newline << rotationScaling(), {});
return scalingSquared;
}
template<class T> Matrix4<T> Matrix4<T>::invertedRigid() const {
CORRADE_ASSERT(isRigidTransformation(),
"Math::Matrix4::invertedRigid(): the matrix doesn't represent rigid transformation", {});
"Math::Matrix4::invertedRigid(): the matrix doesn't represent a rigid transformation:" << Corrade::Utility::Debug::newline << *this, {});
Matrix3x3<T> inverseRotation = rotationScaling().transposed();
return from(inverseRotation, inverseRotation*-translation());

25
src/Magnum/Math/Quaternion.h

@ -74,7 +74,7 @@ Expects that both quaternions are normalized. @f[
*/
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", {});
"Math::angle(): quaternions" << normalizedA << "and" << normalizedB << "are not normalized", {});
return Rad<T>{Implementation::angle(normalizedA, normalizedB)};
}
@ -101,7 +101,7 @@ alternative.
*/
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", {});
"Math::lerp(): quaternions" << normalizedA << "and" << normalizedB << "are not normalized", {});
return ((T(1) - t)*normalizedA + t*normalizedB).normalized();
}
@ -164,7 +164,7 @@ alternative.
*/
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", {});
"Math::slerp(): quaternions" << normalizedA << "and" << normalizedB << "are not normalized", {});
const T cosHalfAngle = dot(normalizedA, normalizedB);
/* Avoid division by zero */
@ -210,7 +210,7 @@ otherwise, the interpolation is performed as: @f[
*/
template<class T> inline Quaternion<T> slerpShortestPath(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB, T t) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::slerpShortestPath(): quaternions must be normalized", {});
"Math::slerpShortestPath(): quaternions" << normalizedA << "and" << normalizedB << "are not normalized", {});
const T cosHalfAngle = dot(normalizedA, normalizedB);
/* Avoid division by zero */
@ -685,22 +685,25 @@ template<class T> Quaternion<T> quaternionFromMatrix(const Matrix3x3<T>& m) {
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", {});
"Math::Quaternion::rotation(): axis" << normalizedAxis << "is not normalized", {});
return {normalizedAxis*std::sin(T(angle)/2), std::cos(T(angle)/2)};
}
template<class T> inline Quaternion<T> Quaternion<T>::fromMatrix(const Matrix3x3<T>& matrix) {
CORRADE_ASSERT(matrix.isOrthogonal(), "Math::Quaternion::fromMatrix(): the matrix is not orthogonal", {});
CORRADE_ASSERT(matrix.isOrthogonal(),
"Math::Quaternion::fromMatrix(): the matrix is not orthogonal:" << Corrade::Utility::Debug::newline << matrix, {});
return Implementation::quaternionFromMatrix(matrix);
}
template<class T> inline Rad<T> Quaternion<T>::angle() const {
CORRADE_ASSERT(isNormalized(), "Math::Quaternion::angle(): quaternion must be normalized", {});
CORRADE_ASSERT(isNormalized(),
"Math::Quaternion::angle():" << *this << "is not normalized", {});
return Rad<T>(T(2)*std::acos(_scalar));
}
template<class T> inline Vector3<T> Quaternion<T>::axis() const {
CORRADE_ASSERT(isNormalized(), "Math::Quaternion::axis(): quaternion must be normalized", {});
CORRADE_ASSERT(isNormalized(),
"Math::Quaternion::axis():" << *this << "is not normalized", {});
return _vector/std::sqrt(1-pow2(_scalar));
}
@ -724,12 +727,14 @@ template<class T> inline Quaternion<T> Quaternion<T>::operator*(const Quaternion
}
template<class T> inline Quaternion<T> Quaternion<T>::invertedNormalized() const {
CORRADE_ASSERT(isNormalized(), "Math::Quaternion::invertedNormalized(): quaternion must be normalized", {});
CORRADE_ASSERT(isNormalized(),
"Math::Quaternion::invertedNormalized():" << *this << "is not normalized", {});
return conjugated();
}
template<class T> inline Vector3<T> Quaternion<T>::transformVectorNormalized(const Vector3<T>& vector) const {
CORRADE_ASSERT(isNormalized(), "Math::Quaternion::transformVectorNormalized(): quaternion must be normalized", {});
CORRADE_ASSERT(isNormalized(),
"Math::Quaternion::transformVectorNormalized():" << *this << "is not normalized", {});
const Vector3<T> t = T(2)*Math::cross(_vector, vector);
return vector + _scalar*t + Math::cross(_vector, t);
}

61
src/Magnum/Math/Test/ComplexTest.cpp

@ -85,10 +85,13 @@ struct ComplexTest: Corrade::TestSuite::Tester {
void conjugated();
void inverted();
void invertedNormalized();
void invertedNormalizedNotNormalized();
void angle();
void angleNotNormalized();
void rotation();
void matrix();
void matrixNotOrthogonal();
void lerp();
void lerpNotNormalized();
void slerp();
@ -134,10 +137,13 @@ ComplexTest::ComplexTest() {
addTests({&ComplexTest::conjugated,
&ComplexTest::inverted,
&ComplexTest::invertedNormalized,
&ComplexTest::invertedNormalizedNotNormalized,
&ComplexTest::angle,
&ComplexTest::angleNotNormalized,
&ComplexTest::rotation,
&ComplexTest::matrix,
&ComplexTest::matrixNotOrthogonal,
&ComplexTest::lerp,
&ComplexTest::lerpNotNormalized,
&ComplexTest::slerp,
@ -392,31 +398,24 @@ void ComplexTest::inverted() {
}
void ComplexTest::invertedNormalized() {
std::ostringstream o;
Error redirectError{&o};
Complex a(-0.6f, 0.8f);
Complex b(-0.6f, -0.8f);
(a*2).invertedNormalized();
CORRADE_COMPARE(o.str(), "Math::Complex::invertedNormalized(): complex number must be normalized\n");
Complex inverted = a.invertedNormalized();
CORRADE_COMPARE(a*inverted, Complex());
CORRADE_COMPARE(inverted*a, Complex());
CORRADE_COMPARE(inverted, b);
}
void ComplexTest::angle() {
std::ostringstream o;
Error redirectError{&o};
Math::angle(Complex(1.5f, -2.0f).normalized(), {-4.0f, 3.5f});
CORRADE_COMPARE(o.str(), "Math::angle(): complex numbers must be normalized\n");
void ComplexTest::invertedNormalizedNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
o.str({});
Math::angle({1.5f, -2.0f}, Complex(-4.0f, 3.5f).normalized());
CORRADE_COMPARE(o.str(), "Math::angle(): complex numbers must be normalized\n");
(Complex(-0.6f, 0.8f)*2).invertedNormalized();
CORRADE_COMPARE(out.str(), "Math::Complex::invertedNormalized(): Complex(-1.2, 1.6) is not normalized\n");
}
void ComplexTest::angle() {
/* Verify also that the angle is the same as angle between 2D vectors */
Rad angle = Math::angle(Complex( 1.5f, -2.0f).normalized(),
Complex(-4.0f, 3.5f).normalized());
@ -425,6 +424,16 @@ void ComplexTest::angle() {
CORRADE_COMPARE(angle, Rad(2.933128f));
}
void ComplexTest::angleNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
Math::angle(Complex(1.5f, -2.0f).normalized(), {-4.0f, 3.5f});
Math::angle({1.5f, -2.0f}, Complex(-4.0f, 3.5f).normalized());
CORRADE_COMPARE(out.str(),
"Math::angle(): complex numbers Complex(0.6, -0.8) and Complex(-4, 3.5) are not normalized\n"
"Math::angle(): complex numbers Complex(1.5, -2) and Complex(-0.752577, 0.658505) are not normalized\n");
}
void ComplexTest::rotation() {
Complex a = Complex::rotation(Deg(120.0f));
CORRADE_COMPARE(a.length(), 1.0f);
@ -445,14 +454,18 @@ void ComplexTest::matrix() {
Matrix2x2 m = Matrix3::rotation(Deg(37.0f)).rotationScaling();
CORRADE_COMPARE(a.toMatrix(), m);
CORRADE_COMPARE(Complex::fromMatrix(m), a);
}
std::ostringstream o;
Error redirectError{&o};
Complex::fromMatrix(m*2);
CORRADE_COMPARE(o.str(), "Math::Complex::fromMatrix(): the matrix is not orthogonal\n");
void ComplexTest::matrixNotOrthogonal() {
std::ostringstream out;
Error redirectError{&out};
Complex b = Complex::fromMatrix(m);
CORRADE_COMPARE(b, a);
Complex::fromMatrix(Matrix3::rotation(Deg(37.0f)).rotationScaling()*2);
CORRADE_COMPARE(out.str(),
"Math::Complex::fromMatrix(): the matrix is not orthogonal:\n"
"Matrix(1.59727, -1.20363,\n"
" 1.20363, 1.59727)\n");
}
void ComplexTest::lerp() {
@ -476,8 +489,8 @@ void ComplexTest::lerpNotNormalized() {
Math::lerp(a*3.0f, a, 0.35f);
Math::lerp(a, a*-3.0f, 0.35f);
CORRADE_COMPARE(out.str(),
"Math::lerp(): complex numbers must be normalized\n"
"Math::lerp(): complex numbers must be normalized\n");
"Math::lerp(): complex numbers Complex(3, 0) and Complex(1, 0) are not normalized\n"
"Math::lerp(): complex numbers Complex(1, 0) and Complex(-3, -0) are not normalized\n");
}
void ComplexTest::slerp() {
@ -502,8 +515,8 @@ void ComplexTest::slerpNotNormalized() {
Math::slerp(a*3.0f, a, 0.35f);
Math::slerp(a, a*-3.0f, 0.35f);
CORRADE_COMPARE(out.str(),
"Math::slerp(): complex numbers must be normalized\n"
"Math::slerp(): complex numbers must be normalized\n");
"Math::slerp(): complex numbers Complex(3, 0) and Complex(1, 0) are not normalized\n"
"Math::slerp(): complex numbers Complex(1, 0) and Complex(-3, -0) are not normalized\n");
}
void ComplexTest::transformVector() {

32
src/Magnum/Math/Test/CubicHermiteTest.cpp

@ -817,8 +817,8 @@ void CubicHermiteTest::lerpComplexNotNormalized() {
Math::lerp({}, a, 0.3f);
Math::lerp(a, {}, 0.3f);
CORRADE_COMPARE(out.str(),
"Math::lerp(): complex numbers must be normalized\n"
"Math::lerp(): complex numbers must be normalized\n");
"Math::lerp(): complex numbers Complex(1, 0) and Complex(2, 0) are not normalized\n"
"Math::lerp(): complex numbers Complex(2, 0) and Complex(1, 0) are not normalized\n");
}
void CubicHermiteTest::lerpQuaternion() {
@ -858,8 +858,8 @@ void CubicHermiteTest::lerpQuaternionNotNormalized() {
Math::lerp({}, a, 0.3f);
Math::lerp(a, {}, 0.3f);
CORRADE_COMPARE(out.str(),
"Math::lerp(): quaternions must be normalized\n"
"Math::lerp(): quaternions must be normalized\n");
"Math::lerp(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({0, 0, 0}, 2) are not normalized\n"
"Math::lerp(): quaternions Quaternion({0, 0, 0}, 2) and Quaternion({0, 0, 0}, 1) are not normalized\n");
}
void CubicHermiteTest::lerpQuaternionShortestPath() {
@ -899,8 +899,8 @@ void CubicHermiteTest::lerpQuaternionShortestPathNotNormalized() {
Math::lerpShortestPath(a, {}, 0.3f);
/* lerpShortestPath() is calling lerp(), so the message is from there */
CORRADE_COMPARE(out.str(),
"Math::lerp(): quaternions must be normalized\n"
"Math::lerp(): quaternions must be normalized\n");
"Math::lerp(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({0, 0, 0}, 2) are not normalized\n"
"Math::lerp(): quaternions Quaternion({0, 0, 0}, 2) and Quaternion({0, 0, 0}, 1) are not normalized\n");
}
void CubicHermiteTest::slerpComplex() {
@ -934,8 +934,8 @@ void CubicHermiteTest::slerpComplexNotNormalized() {
Math::slerp({}, a, 0.3f);
Math::slerp(a, {}, 0.3f);
CORRADE_COMPARE(out.str(),
"Math::slerp(): complex numbers must be normalized\n"
"Math::slerp(): complex numbers must be normalized\n");
"Math::slerp(): complex numbers Complex(1, 0) and Complex(2, 0) are not normalized\n"
"Math::slerp(): complex numbers Complex(2, 0) and Complex(1, 0) are not normalized\n");
}
void CubicHermiteTest::slerpQuaternion() {
@ -975,8 +975,8 @@ void CubicHermiteTest::slerpQuaternionNotNormalized() {
Math::slerp({}, a, 0.3f);
Math::slerp(a, {}, 0.3f);
CORRADE_COMPARE(out.str(),
"Math::slerp(): quaternions must be normalized\n"
"Math::slerp(): quaternions must be normalized\n");
"Math::slerp(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({0, 0, 0}, 2) are not normalized\n"
"Math::slerp(): quaternions Quaternion({0, 0, 0}, 2) and Quaternion({0, 0, 0}, 1) are not normalized\n");
}
void CubicHermiteTest::slerpQuaternionShortestPath() {
@ -1016,8 +1016,8 @@ void CubicHermiteTest::slerpQuaternionShortestPathNotNormalized() {
Math::slerpShortestPath({}, a, 0.3f);
Math::slerpShortestPath(a, {}, 0.3f);
CORRADE_COMPARE(out.str(),
"Math::slerpShortestPath(): quaternions must be normalized\n"
"Math::slerpShortestPath(): quaternions must be normalized\n");
"Math::slerpShortestPath(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({0, 0, 0}, 2) are not normalized\n"
"Math::slerpShortestPath(): quaternions Quaternion({0, 0, 0}, 2) and Quaternion({0, 0, 0}, 1) are not normalized\n");
}
void CubicHermiteTest::splerpScalar() {
@ -1088,8 +1088,8 @@ void CubicHermiteTest::splerpComplexNotNormalized() {
Math::splerp({}, a, 0.3f);
Math::splerp(a, {}, 0.3f);
CORRADE_COMPARE(out.str(),
"Math::splerp(): complex spline points must be normalized\n"
"Math::splerp(): complex spline points must be normalized\n");
"Math::splerp(): complex spline points Complex(1, 0) and Complex(2, 0) are not normalized\n"
"Math::splerp(): complex spline points Complex(2, 0) and Complex(1, 0) are not normalized\n");
}
void CubicHermiteTest::splerpQuaternion() {
@ -1125,8 +1125,8 @@ void CubicHermiteTest::splerpQuaternionNotNormalized() {
Math::splerp({}, a, 0.3f);
Math::splerp(a, {}, 0.3f);
CORRADE_COMPARE(out.str(),
"Math::splerp(): quaternion spline points must be normalized\n"
"Math::splerp(): quaternion spline points must be normalized\n");
"Math::splerp(): quaternion spline points Quaternion({0, 0, 0}, 1) and Quaternion({0, 0, 0}, 2) are not normalized\n"
"Math::splerp(): quaternion spline points Quaternion({0, 0, 0}, 2) and Quaternion({0, 0, 0}, 1) are not normalized\n");
}
void CubicHermiteTest::debugScalar() {

17
src/Magnum/Math/Test/DistanceTest.cpp

@ -43,6 +43,7 @@ struct DistanceTest: Corrade::TestSuite::Tester {
void pointPlane();
void pointPlaneScaled();
void pointPlaneNormalized();
void pointPlaneNormalizedNotNormalized();
};
typedef Math::Vector2<Float> Vector2;
@ -58,7 +59,8 @@ DistanceTest::DistanceTest() {
&DistanceTest::pointPlane,
&DistanceTest::pointPlaneScaled,
&DistanceTest::pointPlaneNormalized});
&DistanceTest::pointPlaneNormalized,
&DistanceTest::pointPlaneNormalizedNotNormalized});
}
void DistanceTest::linePoint2D() {
@ -183,15 +185,18 @@ void DistanceTest::pointPlaneScaled() {
void DistanceTest::pointPlaneNormalized() {
Vector3 point{1.0f, 2.0f, 3.0f};
Vector4 invalidPlane{2.0f, 2.0f, 2.0f, 0.0f};
const Vector4 plane{0.0f, 1.0f, 0.0f, 1.0f};
CORRADE_COMPARE(Distance::pointPlaneNormalized(point, plane), 3.0f);
}
std::ostringstream o;
Error redirectError{&o};
Distance::pointPlaneNormalized(point, invalidPlane);
CORRADE_COMPARE(o.str(), "Math::Geometry::Distance::pointPlaneNormalized(): plane normal is not an unit vector\n");
void DistanceTest::pointPlaneNormalizedNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
Vector4 invalidPlane{2.0f, 2.0f, 2.0f, 0.0f};
Distance::pointPlaneNormalized({}, invalidPlane);
CORRADE_COMPARE(out.str(), "Math::Distance::pointPlaneNormalized(): plane normal Vector(2, 2, 2) is not normalized\n");
}
}}}

32
src/Magnum/Math/Test/DualComplexTest.cpp

@ -82,17 +82,21 @@ struct DualComplexTest: Corrade::TestSuite::Tester {
void conjugated();
void inverted();
void invertedNormalized();
void invertedNormalizedNotNormalized();
void rotation();
void translation();
void combinedTransformParts();
void matrix();
void matrixNotOrthogonal();
void transformPoint();
void debug();
void configuration();
};
using namespace Math::Literals;
typedef Math::Deg<Float> Deg;
typedef Math::Rad<Float> Rad;
typedef Math::Complex<Float> Complex;
@ -134,11 +138,13 @@ DualComplexTest::DualComplexTest() {
&DualComplexTest::conjugated,
&DualComplexTest::inverted,
&DualComplexTest::invertedNormalized,
&DualComplexTest::invertedNormalizedNotNormalized,
&DualComplexTest::rotation,
&DualComplexTest::translation,
&DualComplexTest::combinedTransformParts,
&DualComplexTest::matrix,
&DualComplexTest::matrixNotOrthogonal,
&DualComplexTest::transformPoint,
&DualComplexTest::debug,
@ -358,17 +364,20 @@ void DualComplexTest::invertedNormalized() {
DualComplex a({-0.316228f, 0.9486831f}, { 3.0f, -2.5f});
DualComplex b({-0.316228f, -0.9486831f}, {3.320391f, 2.05548f});
std::ostringstream o;
Error redirectError{&o};
DualComplex({-1.0f, -2.5f}, {}).invertedNormalized();
CORRADE_COMPARE(o.str(), "Math::Complex::invertedNormalized(): complex number must be normalized\n");
DualComplex inverted = a.invertedNormalized();
CORRADE_COMPARE(a*inverted, DualComplex());
CORRADE_COMPARE(inverted*a, DualComplex());
CORRADE_COMPARE(inverted, b);
}
void DualComplexTest::invertedNormalizedNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
DualComplex({-1.0f, -2.5f}, {}).invertedNormalized();
CORRADE_COMPARE(out.str(), "Math::Complex::invertedNormalized(): Complex(-1, -2.5) is not normalized\n");
}
void DualComplexTest::rotation() {
DualComplex a = DualComplex::rotation(Deg(120.0f));
CORRADE_COMPARE(a.length(), 1.0f);
@ -405,14 +414,19 @@ void DualComplexTest::matrix() {
Matrix3 m = Matrix3::rotation(Deg(23.0f))*Matrix3::translation({2.0f, 3.0f});
CORRADE_COMPARE(a.toMatrix(), m);
CORRADE_COMPARE(DualComplex::fromMatrix(m), a);
}
void DualComplexTest::matrixNotOrthogonal() {
std::ostringstream o;
Error redirectError{&o};
DualComplex::fromMatrix(m*2);
CORRADE_COMPARE(o.str(), "Math::DualComplex::fromMatrix(): the matrix doesn't represent rigid transformation\n");
DualComplex b = DualComplex::fromMatrix(m);
CORRADE_COMPARE(b, a);
DualComplex::fromMatrix(Matrix3::rotation(23.0_degf)*Matrix3::translation({2.0f, 3.0f})*2);
CORRADE_COMPARE(o.str(),
"Math::DualComplex::fromMatrix(): the matrix doesn't represent rigid transformation:\n"
"Matrix(1.84101, -0.781462, 1.33763,\n"
" 0.781462, 1.84101, 7.08595,\n"
" 0, 0, 2)\n");
}
void DualComplexTest::transformPoint() {

68
src/Magnum/Math/Test/DualQuaternionTest.cpp

@ -83,13 +83,17 @@ struct DualQuaternionTest: Corrade::TestSuite::Tester {
void conjugated();
void inverted();
void invertedNormalized();
void invertedNormalizedNotNormalized();
void rotation();
void rotationNotNormalized();
void translation();
void combinedTransformParts();
void matrix();
void matrixNotOrthogonal();
void transformPoint();
void transformPointNormalized();
void transformPointNormalizedNotNormalized();
void sclerp();
void sclerpShortestPath();
@ -140,13 +144,17 @@ DualQuaternionTest::DualQuaternionTest() {
&DualQuaternionTest::conjugated,
&DualQuaternionTest::inverted,
&DualQuaternionTest::invertedNormalized,
&DualQuaternionTest::invertedNormalizedNotNormalized,
&DualQuaternionTest::rotation,
&DualQuaternionTest::rotationNotNormalized,
&DualQuaternionTest::translation,
&DualQuaternionTest::combinedTransformParts,
&DualQuaternionTest::matrix,
&DualQuaternionTest::matrixNotOrthogonal,
&DualQuaternionTest::transformPoint,
&DualQuaternionTest::transformPointNormalized,
&DualQuaternionTest::transformPointNormalizedNotNormalized,
&DualQuaternionTest::sclerp,
&DualQuaternionTest::sclerpShortestPath,
@ -391,11 +399,6 @@ void DualQuaternionTest::invertedNormalized() {
DualQuaternion a({{ 1.0f, 2.0f, 3.0f}, -4.0f}, {{ 2.5f, -3.1f, 3.3f}, 2.0f});
DualQuaternion b({{-1.0f, -2.0f, -3.0f}, -4.0f}, {{-2.5f, 3.1f, -3.3f}, 2.0f});
std::ostringstream o;
Error redirectError{&o};
CORRADE_COMPARE(a.invertedNormalized(), DualQuaternion());
CORRADE_COMPARE(o.str(), "Math::DualQuaternion::invertedNormalized(): dual quaternion must be normalized\n");
DualQuaternion normalized = a.normalized();
DualQuaternion inverted = normalized.invertedNormalized();
CORRADE_COMPARE(normalized*inverted, DualQuaternion());
@ -403,14 +406,16 @@ void DualQuaternionTest::invertedNormalized() {
CORRADE_COMPARE(inverted, b/Math::sqrt(Dual(30.0f, -3.6f)));
}
void DualQuaternionTest::rotation() {
std::ostringstream o;
Error redirectError{&o};
void DualQuaternionTest::invertedNormalizedNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
Vector3 axis(1.0f/Constants<Float>::sqrt3());
DualQuaternion({{ 1.0f, 2.0f, 3.0f}, -4.0f}, {{ 2.5f, -3.1f, 3.3f}, 2.0f}).invertedNormalized();
CORRADE_COMPARE(out.str(), "Math::DualQuaternion::invertedNormalized(): DualQuaternion({{1, 2, 3}, -4}, {{2.5, -3.1, 3.3}, 2}) is not normalized\n");
}
CORRADE_COMPARE(DualQuaternion::rotation(Deg(120.0f), axis*2.0f), DualQuaternion());
CORRADE_COMPARE(o.str(), "Math::Quaternion::rotation(): axis must be normalized\n");
void DualQuaternionTest::rotation() {
Vector3 axis(1.0f/Constants<Float>::sqrt3());
DualQuaternion q = DualQuaternion::rotation(Deg(120.0f), axis);
CORRADE_COMPARE(q.length(), 1.0f);
@ -424,6 +429,14 @@ void DualQuaternionTest::rotation() {
CORRADE_COMPARE(c, Quaternion({-1.0f, 2.0f, 3.0f}, 4.0f));
}
void DualQuaternionTest::rotationNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
DualQuaternion::rotation(120.0_degf, Vector3(2.0f));
CORRADE_COMPARE(out.str(), "Math::Quaternion::rotation(): axis Vector(2, 2, 2) is not normalized\n");
}
void DualQuaternionTest::translation() {
Vector3 vec(1.0f, -3.5f, 0.5f);
DualQuaternion q = DualQuaternion::translation(vec);
@ -454,13 +467,20 @@ void DualQuaternionTest::matrix() {
CORRADE_COMPARE(q.toMatrix(), m);
CORRADE_COMPARE((-q).toMatrix(), m);
std::ostringstream o;
Error redirectError{&o};
DualQuaternion::fromMatrix(m*2);
CORRADE_COMPARE(o.str(), "Math::DualQuaternion::fromMatrix(): the matrix doesn't represent rigid transformation\n");
CORRADE_COMPARE(DualQuaternion::fromMatrix(m), q);
}
DualQuaternion p = DualQuaternion::fromMatrix(m);
CORRADE_COMPARE(p, q);
void DualQuaternionTest::matrixNotOrthogonal() {
std::ostringstream out;
Error redirectError{&out};
DualQuaternion::fromMatrix(Matrix4::rotationX(23.0_degf)*Matrix4::translation({-1.0f, 2.0f, 3.0f})*2);
CORRADE_COMPARE(out.str(),
"Math::DualQuaternion::fromMatrix(): the matrix doesn't represent a rigid transformation:\n"
"Matrix(2, 0, 0, -2,\n"
" 0, 1.84101, -0.781462, 1.33763,\n"
" 0, 0.781462, 1.84101, 7.08595,\n"
" 0, 0, 0, 2)\n");
}
void DualQuaternionTest::transformPoint() {
@ -486,11 +506,6 @@ void DualQuaternionTest::transformPointNormalized() {
Matrix4 n = Matrix4::rotationX(Deg(23.0f))*Matrix4::translation({-1.0f, 2.0f, 3.0f});
Vector3 v(0.0f, -3.6f, 0.7f);
std::ostringstream o;
Error redirectError{&o};
(a*Dual(2)).transformPointNormalized(v);
CORRADE_COMPARE(o.str(), "Math::DualQuaternion::transformPointNormalized(): dual quaternion must be normalized\n");
Vector3 transformedA = a.transformPointNormalized(v);
CORRADE_COMPARE(transformedA, m.transformPoint(v));
CORRADE_COMPARE(transformedA, Vector3(-1.0f, -1.58733f, 2.237721f));
@ -500,6 +515,15 @@ void DualQuaternionTest::transformPointNormalized() {
CORRADE_COMPARE(transformedB, Vector3(-1.0f, -2.918512f, 2.780698f));
}
void DualQuaternionTest::transformPointNormalizedNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
DualQuaternion a = DualQuaternion::translation({-1.0f, 2.0f, 3.0f})*DualQuaternion::rotation(Deg(23.0f), Vector3::xAxis());
(a*Dual(2)).transformPointNormalized({});
CORRADE_COMPARE(out.str(), "Math::DualQuaternion::transformPointNormalized(): DualQuaternion({{0.398736, 0, 0}, 1.95985}, {{-0.979925, 2.55795, 2.54104}, 0.199368}) is not normalized\n");
}
void DualQuaternionTest::sclerp() {
auto from = DualQuaternion::translation({20.0f, 0.0f, 0.0f})*
DualQuaternion::rotation(65.0_degf, Vector3::yAxis());

14
src/Magnum/Math/Test/IntersectionTest.cpp

@ -48,6 +48,7 @@ struct IntersectionTest: Corrade::TestSuite::Tester {
void pointDoubleCone();
void sphereCone();
void sphereConeView();
void sphereConeViewNotRigid();
void rangeCone();
void aabbCone();
};
@ -77,6 +78,7 @@ IntersectionTest::IntersectionTest() {
&IntersectionTest::pointDoubleCone,
&IntersectionTest::sphereCone,
&IntersectionTest::sphereConeView,
&IntersectionTest::sphereConeViewNotRigid,
&IntersectionTest::rangeCone,
&IntersectionTest::aabbCone});
}
@ -377,11 +379,19 @@ void IntersectionTest::sphereConeView() {
CORRADE_VERIFY(!Intersection::sphereConeView(center - surface + sNormal*0.25f, 0.5f, coneView, angle));
CORRADE_VERIFY(!Intersection::sphereConeView(center - 4.0f*surface, 0.5f, coneView, angle));
}
void IntersectionTest::sphereConeViewNotRigid() {
std::ostringstream out;
Error redirectError{&out};
CORRADE_VERIFY(!Intersection::sphereConeView(center, 1.0f, Matrix4{ZeroInit}, angle));
CORRADE_COMPARE(out.str(), "Math::Geometry::Intersection::sphereConeView(): coneView does not represent a rigid transformation\n");
CORRADE_VERIFY(!Intersection::sphereConeView({}, 1.0f, Matrix4{ZeroInit}, {}));
CORRADE_COMPARE(out.str(),
"Math::Intersection::sphereConeView(): coneView does not represent a rigid transformation:\n"
"Matrix(0, 0, 0, 0,\n"
" 0, 0, 0, 0,\n"
" 0, 0, 0, 0,\n"
" 0, 0, 0, 0)\n");
}
void IntersectionTest::rangeCone() {

54
src/Magnum/Math/Test/Matrix3Test.cpp

@ -75,6 +75,7 @@ struct Matrix3Test: Corrade::TestSuite::Tester {
void scaling();
void rotation();
void reflection();
void reflectionNotNormalized();
void reflectionIsScaling();
void shearingX();
void shearingY();
@ -92,6 +93,7 @@ struct Matrix3Test: Corrade::TestSuite::Tester {
void uniformScalingPartNotUniform();
void vectorParts();
void invertedRigid();
void invertedRigidNotRigid();
void transform();
void debug();
@ -122,6 +124,7 @@ Matrix3Test::Matrix3Test() {
&Matrix3Test::scaling,
&Matrix3Test::rotation,
&Matrix3Test::reflection,
&Matrix3Test::reflectionNotNormalized,
&Matrix3Test::reflectionIsScaling,
&Matrix3Test::shearingX,
&Matrix3Test::shearingY,
@ -139,6 +142,7 @@ Matrix3Test::Matrix3Test() {
&Matrix3Test::uniformScalingPartNotUniform,
&Matrix3Test::vectorParts,
&Matrix3Test::invertedRigid,
&Matrix3Test::invertedRigidNotRigid,
&Matrix3Test::transform,
&Matrix3Test::debug,
@ -328,14 +332,7 @@ void Matrix3Test::rotation() {
}
void Matrix3Test::reflection() {
std::ostringstream o;
Error redirectError{&o};
Vector2 normal(-1.0f, 2.0f);
CORRADE_COMPARE(Matrix3::reflection(normal), Matrix3());
CORRADE_COMPARE(o.str(), "Math::Matrix3::reflection(): normal must be normalized\n");
Matrix3 actual = Matrix3::reflection(normal.normalized());
Matrix3 expected({0.6f, 0.8f, 0.0f},
{0.8f, -0.6f, 0.0f},
@ -346,6 +343,14 @@ void Matrix3Test::reflection() {
CORRADE_COMPARE(actual, expected);
}
void Matrix3Test::reflectionNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
Matrix3::reflection({-1.0f, 2.0f});
CORRADE_COMPARE(out.str(), "Math::Matrix3::reflection(): normal Vector(-1, 2) is not normalized\n");
}
void Matrix3Test::reflectionIsScaling() {
CORRADE_COMPARE(Matrix3::reflection(Vector2::yAxis()), Matrix3::scaling(Vector2::yScale(-1.0f)));
}
@ -458,8 +463,12 @@ void Matrix3Test::rotationPartNotOrthogonal() {
Matrix3::scaling(Vector2::yScale(0.0f)).rotation();
CORRADE_COMPARE(out.str(),
"Math::Matrix3::rotation(): the normalized rotation part is not orthogonal\n"
"Math::Matrix3::rotation(): the normalized rotation part is not orthogonal\n");
"Math::Matrix3::rotation(): the normalized rotation part is not orthogonal:\n"
"Matrix(1, 0.83205,\n"
" 0, 0.5547)\n"
"Math::Matrix3::rotation(): the normalized rotation part is not orthogonal:\n"
"Matrix(1, -nan,\n"
" 0, -nan)\n");
}
void Matrix3Test::rotationNormalizedPart() {
@ -480,7 +489,10 @@ void Matrix3Test::rotationNormalizedPartNotOrthogonal() {
{7.0f, -1.0f, 8.0f});
a.rotationNormalized();
CORRADE_COMPARE(out.str(), "Math::Matrix3::rotationNormalized(): the rotation part is not orthogonal\n");
CORRADE_COMPARE(out.str(),
"Math::Matrix3::rotationNormalized(): the rotation part is not orthogonal:\n"
"Matrix(1, 1,\n"
" 0, 0.1)\n");
}
void Matrix3Test::scalingPart() {
@ -502,7 +514,10 @@ void Matrix3Test::uniformScalingPartNotUniform() {
std::ostringstream out;
Error redirectError{&out};
Matrix3::scaling(Vector2::yScale(3.0f)).uniformScaling();
CORRADE_COMPARE(out.str(), "Math::Matrix3::uniformScaling(): the matrix doesn't have uniform scaling\n");
CORRADE_COMPARE(out.str(),
"Math::Matrix3::uniformScaling(): the matrix doesn't have uniform scaling:\n"
"Matrix(1, 0,\n"
" 0, 3)\n");
}
void Matrix3Test::vectorParts() {
@ -535,15 +550,22 @@ void Matrix3Test::invertedRigid() {
Matrix3::reflection(Vector2(0.5f, -2.0f).normalized())*
Matrix3::rotation(Deg(74.0f));
std::ostringstream o;
Error redirectError{&o};
(2*actual).invertedRigid();
CORRADE_COMPARE(o.str(), "Math::Matrix3::invertedRigid(): the matrix doesn't represent rigid transformation\n");
CORRADE_COMPARE(actual.invertedRigid(), expected);
CORRADE_COMPARE(actual.invertedRigid(), actual.inverted());
}
void Matrix3Test::invertedRigidNotRigid() {
std::ostringstream out;
Error redirectError{&out};
(Matrix3::rotation(60.0_degf)*2.0f).invertedRigid();
CORRADE_COMPARE(out.str(),
"Math::Matrix3::invertedRigid(): the matrix doesn't represent a rigid transformation:\n"
"Matrix(1, -1.73205, 0,\n"
" 1.73205, 1, 0,\n"
" 0, 0, 2)\n");
}
void Matrix3Test::transform() {
Matrix3 a = Matrix3::translation({1.0f, -5.0f})*Matrix3::rotation(Deg(90.0f));
Vector2 v(1.0f, -2.0f);

73
src/Magnum/Math/Test/Matrix4Test.cpp

@ -77,10 +77,12 @@ struct Matrix4Test: Corrade::TestSuite::Tester {
void translation();
void scaling();
void rotation();
void rotationNotNormalized();
void rotationX();
void rotationY();
void rotationZ();
void reflection();
void reflectionNotNormalized();
void reflectionIsScaling();
void shearingXY();
void shearingXZ();
@ -104,6 +106,7 @@ struct Matrix4Test: Corrade::TestSuite::Tester {
void uniformScalingPartNotUniform();
void vectorParts();
void invertedRigid();
void invertedRigidNotRigid();
void transform();
void transformProjection();
@ -138,10 +141,12 @@ Matrix4Test::Matrix4Test() {
&Matrix4Test::translation,
&Matrix4Test::scaling,
&Matrix4Test::rotation,
&Matrix4Test::rotationNotNormalized,
&Matrix4Test::rotationX,
&Matrix4Test::rotationY,
&Matrix4Test::rotationZ,
&Matrix4Test::reflection,
&Matrix4Test::reflectionNotNormalized,
&Matrix4Test::reflectionIsScaling,
&Matrix4Test::shearingXY,
&Matrix4Test::shearingXZ,
@ -165,6 +170,7 @@ Matrix4Test::Matrix4Test() {
&Matrix4Test::uniformScalingPartNotUniform,
&Matrix4Test::vectorParts,
&Matrix4Test::invertedRigid,
&Matrix4Test::invertedRigidNotRigid,
&Matrix4Test::transform,
&Matrix4Test::transformProjection,
@ -368,12 +374,6 @@ void Matrix4Test::scaling() {
}
void Matrix4Test::rotation() {
std::ostringstream o;
Error redirectError{&o};
CORRADE_COMPARE(Matrix4::rotation(Deg(-74.0f), {-1.0f, 2.0f, 2.0f}), Matrix4());
CORRADE_COMPARE(o.str(), "Math::Matrix4::rotation(): axis must be normalized\n");
Matrix4 matrix({ 0.35612202f, -0.80181062f, 0.47987163f, 0.0f},
{ 0.47987163f, 0.59757626f, 0.6423596f, 0.0f},
{-0.80181062f, 0.00151846f, 0.59757626f, 0.0f},
@ -381,6 +381,14 @@ void Matrix4Test::rotation() {
CORRADE_COMPARE(Matrix4::rotation(Deg(-74.0f), Vector3(-1.0f, 2.0f, 2.0f).normalized()), matrix);
}
void Matrix4Test::rotationNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
Matrix4::rotation(Deg(-74.0f), {-1.0f, 2.0f, 2.0f});
CORRADE_COMPARE(out.str(), "Math::Matrix4::rotation(): axis Vector(-1, 2, 2) is not normalized\n");
}
void Matrix4Test::rotationX() {
Matrix4 matrix({1.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 0.90096887f, 0.43388374f, 0.0f},
@ -409,14 +417,7 @@ void Matrix4Test::rotationZ() {
}
void Matrix4Test::reflection() {
std::ostringstream o;
Error redirectError{&o};
Vector3 normal(-1.0f, 2.0f, 2.0f);
CORRADE_COMPARE(Matrix4::reflection(normal), Matrix4());
CORRADE_COMPARE(o.str(), "Math::Matrix4::reflection(): normal must be normalized\n");
Matrix4 actual = Matrix4::reflection(normal.normalized());
Matrix4 expected({0.777778f, 0.444444f, 0.444444f, 0.0f},
{0.444444f, 0.111111f, -0.888889f, 0.0f},
@ -428,6 +429,14 @@ void Matrix4Test::reflection() {
CORRADE_COMPARE(actual, expected);
}
void Matrix4Test::reflectionNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
Matrix4::reflection({-1.0f, 2.0f, 2.0f});
CORRADE_COMPARE(out.str(), "Math::Matrix4::reflection(): normal Vector(-1, 2, 2) is not normalized\n");
}
void Matrix4Test::reflectionIsScaling() {
CORRADE_COMPARE(Matrix4::reflection(Vector3::yAxis()), Matrix4::scaling(Vector3::yScale(-1.0f)));
}
@ -609,8 +618,14 @@ void Matrix4Test::rotationPartNotOrthogonal() {
Matrix4::scaling(Vector3::yScale(0.0f)).rotation();
CORRADE_COMPARE(out.str(),
"Math::Matrix4::rotation(): the normalized rotation part is not orthogonal\n"
"Math::Matrix4::rotation(): the normalized rotation part is not orthogonal\n");
"Math::Matrix4::rotation(): the normalized rotation part is not orthogonal:\n"
"Matrix(1, 0, 0.83205,\n"
" 0, 1, 0,\n"
" 0, 0, 0.5547)\n"
"Math::Matrix4::rotation(): the normalized rotation part is not orthogonal:\n"
"Matrix(1, -nan, 0,\n"
" 0, -nan, 0,\n"
" 0, -nan, 1)\n");
}
void Matrix4Test::rotationNormalizedPart() {
@ -633,7 +648,11 @@ void Matrix4Test::rotationNormalizedPartNotOrthogonal() {
{0.0f, -1.0f, 0.1f, 0.0f},
{9.0f, 4.0f, 5.0f, 9.0f});
a.rotationNormalized();
CORRADE_COMPARE(out.str(), "Math::Matrix4::rotationNormalized(): the rotation part is not orthogonal\n");
CORRADE_COMPARE(out.str(),
"Math::Matrix4::rotationNormalized(): the rotation part is not orthogonal:\n"
"Matrix(0, 1, 0,\n"
" 0, 0, -1,\n"
" 1, 0, 0.1)\n");
}
void Matrix4Test::scalingPart() {
@ -654,7 +673,10 @@ void Matrix4Test::uniformScalingPart() {
void Matrix4Test::uniformScalingPartNotUniform() {
std::ostringstream out;
Error redirectError{&out}; Matrix4::scaling(Vector3::yScale(3.0f)).uniformScaling();
CORRADE_COMPARE(out.str(), "Math::Matrix4::uniformScaling(): the matrix doesn't have uniform scaling\n");
CORRADE_COMPARE(out.str(), "Math::Matrix4::uniformScaling(): the matrix doesn't have uniform scaling:\n"
"Matrix(1, 0, 0,\n"
" 0, 3, 0,\n"
" 0, 0, 1)\n");
}
void Matrix4Test::vectorParts() {
@ -693,15 +715,24 @@ void Matrix4Test::invertedRigid() {
Matrix4::reflection(Vector3(0.5f, -2.0f, 2.0f).normalized())*
Matrix4::rotation(Deg(74.0f), Vector3(-1.0f, 0.5f, 2.0f).normalized());
std::ostringstream o;
Error redirectError{&o};
(2*actual).invertedRigid();
CORRADE_COMPARE(o.str(), "Math::Matrix4::invertedRigid(): the matrix doesn't represent rigid transformation\n");
CORRADE_COMPARE(actual.invertedRigid(), expected);
CORRADE_COMPARE(actual.invertedRigid(), actual.inverted());
}
void Matrix4Test::invertedRigidNotRigid() {
std::ostringstream out;
Error redirectError{&out};
(Matrix4::rotationX(-60.0_degf)*2.0f).invertedRigid();
CORRADE_COMPARE(out.str(),
"Math::Matrix4::invertedRigid(): the matrix doesn't represent a rigid transformation:\n"
"Matrix(2, 0, 0, 0,\n"
" 0, 1, 1.73205, 0,\n"
" 0, -1.73205, 1, 0,\n"
" 0, 0, 0, 2)\n");
}
void Matrix4Test::transform() {
Matrix4 a = Matrix4::translation({1.0f, -5.0f, 3.5f})*Matrix4::rotation(Deg(90.0f), Vector3::zAxis());
Vector3 v(1.0f, -2.0f, 5.5f);

20
src/Magnum/Math/Test/MatrixTest.cpp

@ -77,6 +77,7 @@ struct MatrixTest: Corrade::TestSuite::Tester {
void determinant();
void inverted();
void invertedOrthogonal();
void invertedOrthogonalNotOrthogonal();
void subclassTypes();
void subclass();
@ -114,6 +115,7 @@ MatrixTest::MatrixTest() {
&MatrixTest::determinant,
&MatrixTest::inverted,
&MatrixTest::invertedOrthogonal,
&MatrixTest::invertedOrthogonalNotOrthogonal,
&MatrixTest::subclassTypes,
&MatrixTest::subclass,
@ -360,6 +362,15 @@ void MatrixTest::inverted() {
}
void MatrixTest::invertedOrthogonal() {
Matrix3x3 a(Vector3(Constants::sqrt3()/2.0f, 0.5f, 0.0f),
Vector3(-0.5f, Constants::sqrt3()/2.0f, 0.0f),
Vector3(0.0f, 0.0f, 1.0f));
CORRADE_COMPARE(a.invertedOrthogonal()*a, Matrix3x3());
CORRADE_COMPARE(a.invertedOrthogonal(), a.inverted());
}
void MatrixTest::invertedOrthogonalNotOrthogonal() {
std::ostringstream o;
Error redirectError{&o};
@ -367,10 +378,11 @@ void MatrixTest::invertedOrthogonal() {
Vector3(-0.5f, Constants::sqrt3()/2.0f, 0.0f),
Vector3(0.0f, 0.0f, 1.0f));
(a*2).invertedOrthogonal();
CORRADE_COMPARE(o.str(), "Math::Matrix::invertedOrthogonal(): the matrix is not orthogonal\n");
CORRADE_COMPARE(a.invertedOrthogonal()*a, Matrix3x3());
CORRADE_COMPARE(a.invertedOrthogonal(), a.inverted());
CORRADE_COMPARE(o.str(),
"Math::Matrix::invertedOrthogonal(): the matrix is not orthogonal:\n"
"Matrix(1.73205, -1, 0,\n"
" 1, 1.73205, 0,\n"
" 0, 0, 2)\n");
}
template<class T> class BasicVec2: public Math::Vector<2, T> {

140
src/Magnum/Math/Test/QuaternionTest.cpp

@ -70,6 +70,8 @@ struct QuaternionTest: Corrade::TestSuite::Tester {
void compare();
void isNormalized();
template<class T> void isNormalizedEpsilon();
void axisAngle();
void axisAngleNotNormalized();
void addSubtract();
void negated();
@ -85,10 +87,14 @@ struct QuaternionTest: Corrade::TestSuite::Tester {
void conjugated();
void inverted();
void invertedNormalized();
void invertedNormalizedNotNormalized();
void rotation();
void rotationNotNormalized();
void angle();
void angleNotNormalized();
void matrix();
void matrixNotOrthogonal();
void lerp();
void lerp2D();
@ -103,6 +109,7 @@ struct QuaternionTest: Corrade::TestSuite::Tester {
void transformVector();
void transformVectorNormalized();
void transformVectorNormalizedNotNormalized();
void debug();
void configuration();
@ -134,6 +141,8 @@ QuaternionTest::QuaternionTest() {
&QuaternionTest::isNormalized,
&QuaternionTest::isNormalizedEpsilon<Float>,
&QuaternionTest::isNormalizedEpsilon<Double>,
&QuaternionTest::axisAngle,
&QuaternionTest::axisAngleNotNormalized,
&QuaternionTest::addSubtract,
&QuaternionTest::negated,
@ -152,10 +161,14 @@ QuaternionTest::QuaternionTest() {
addTests({&QuaternionTest::conjugated,
&QuaternionTest::inverted,
&QuaternionTest::invertedNormalized,
&QuaternionTest::invertedNormalizedNotNormalized,
&QuaternionTest::rotation,
&QuaternionTest::rotationNotNormalized,
&QuaternionTest::angle,
&QuaternionTest::angleNotNormalized,
&QuaternionTest::matrix,
&QuaternionTest::matrixNotOrthogonal,
&QuaternionTest::lerp,
&QuaternionTest::lerp2D,
@ -170,6 +183,7 @@ QuaternionTest::QuaternionTest() {
&QuaternionTest::transformVector,
&QuaternionTest::transformVectorNormalized,
&QuaternionTest::transformVectorNormalizedNotNormalized,
&QuaternionTest::debug,
&QuaternionTest::configuration});
@ -312,6 +326,24 @@ template<class T> void QuaternionTest::isNormalizedEpsilon() {
CORRADE_VERIFY(!(Math::Quaternion<T>{{T(0.0106550719778129), T(0.311128101752138), T(-0.0468823167023769)}, T(0.949151106053128) + TypeTraits<T>::epsilon()*T(2.0)}.isNormalized()));
}
void QuaternionTest::axisAngle() {
Quaternion a = Quaternion::rotation(23.0_degf, {0.6f, -0.8f, 0.0f});
CORRADE_COMPARE(a.angle(), 23.0_degf);
CORRADE_COMPARE(a.axis(), (Vector3{0.6f, -0.8f, 0.0f}));
}
void QuaternionTest::axisAngleNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
Quaternion a = Quaternion::rotation(23.0_degf, {0.6f, -0.8f, 0.0f})*2;
a.angle();
a.axis();
CORRADE_COMPARE(out.str(),
"Math::Quaternion::angle(): Quaternion({0.239242, -0.318989, 0}, 1.95985) is not normalized\n"
"Math::Quaternion::axis(): Quaternion({0.239242, -0.318989, 0}, 1.95985) is not normalized\n");
}
void QuaternionTest::addSubtract() {
Quaternion a({ 1.0f, 3.0f, -2.0f}, -4.0f);
Quaternion b({-0.5f, 1.4f, 3.0f}, 12.0f);
@ -390,29 +422,24 @@ void QuaternionTest::inverted() {
}
void QuaternionTest::invertedNormalized() {
Quaternion a = Quaternion({1.0f, 3.0f, -2.0f}, -4.0f);
Quaternion a = Quaternion{{1.0f, 3.0f, -2.0f}, -4.0f}.normalized();
std::ostringstream o;
Error redirectError{&o};
a.invertedNormalized();
CORRADE_COMPARE(o.str(), "Math::Quaternion::invertedNormalized(): quaternion must be normalized\n");
Quaternion aNormalized = a.normalized();
Quaternion inverted = aNormalized.invertedNormalized();
CORRADE_COMPARE(aNormalized*inverted, Quaternion());
CORRADE_COMPARE(inverted*aNormalized, Quaternion());
Quaternion inverted = a.invertedNormalized();
CORRADE_COMPARE(a*inverted, Quaternion());
CORRADE_COMPARE(inverted*a, Quaternion());
CORRADE_COMPARE(inverted, Quaternion({-1.0f, -3.0f, 2.0f}, -4.0f)/std::sqrt(30.0f));
}
void QuaternionTest::rotation() {
std::ostringstream o;
Error redirectError{&o};
Vector3 axis(1.0f/Constants<Float>::sqrt3());
void QuaternionTest::invertedNormalizedNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
CORRADE_COMPARE(Quaternion::rotation(Deg(-74.0f), {-1.0f, 2.0f, 2.0f}), Quaternion());
CORRADE_COMPARE(o.str(), "Math::Quaternion::rotation(): axis must be normalized\n");
Quaternion{{1.0f, 3.0f, -2.0f}, -4.0f}.invertedNormalized();
CORRADE_COMPARE(out.str(), "Math::Quaternion::invertedNormalized(): Quaternion({1, 3, -2}, -4) is not normalized\n");
}
void QuaternionTest::rotation() {
Vector3 axis(1.0f/Constants<Float>::sqrt3());
Quaternion q = Quaternion::rotation(Deg(120.0f), axis);
CORRADE_COMPARE(q.length(), 1.0f);
CORRADE_COMPARE(q, Quaternion(Vector3(0.5f, 0.5f, 0.5f), 0.5f));
@ -431,24 +458,35 @@ void QuaternionTest::rotation() {
CORRADE_VERIFY(Quaternion().axis() != Quaternion().axis());
}
void QuaternionTest::angle() {
std::ostringstream o;
Error redirectError{&o};
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");
void QuaternionTest::rotationNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
o.str({});
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");
Quaternion::rotation(-74.0_degf, {-1.0f, 2.0f, 2.0f});
CORRADE_COMPARE(out.str(), "Math::Quaternion::rotation(): axis Vector(-1, 2, 2) is not normalized\n");
}
void QuaternionTest::angle() {
/* Verify also that the angle is the same as angle between 4D vectors */
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()));
Vector4(4.0f, -3.0f, 2.0f, -1.0f).normalized()));
CORRADE_COMPARE(angle, Rad(1.704528f));
}
void QuaternionTest::angleNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
Math::angle(Quaternion{{1.0f, 2.0f, -3.0f}, -4.0f}.normalized(), {{4.0f, -3.0f, 2.0f}, -1.0f});
Math::angle({{1.0f, 2.0f, -3.0f}, -4.0f}, Quaternion{{4.0f, -3.0f, 2.0f}, -1.0f}.normalized());
CORRADE_COMPARE(out.str(),
"Math::angle(): quaternions Quaternion({0.182574, 0.365148, -0.547723}, -0.730297) and Quaternion({4, -3, 2}, -1) are not normalized\n"
"Math::angle(): quaternions Quaternion({1, 2, -3}, -4) and Quaternion({0.730297, -0.547723, 0.365148}, -0.182574) are not normalized\n");
}
void QuaternionTest::matrix() {
Vector3 axis = Vector3(-3.0f, 1.0f, 5.0f).normalized();
@ -459,11 +497,6 @@ void QuaternionTest::matrix() {
CORRADE_COMPARE(q.toMatrix(), m);
CORRADE_COMPARE((-q).toMatrix(), m);
std::ostringstream o;
Error redirectError{&o};
Quaternion::fromMatrix(m*2);
CORRADE_COMPARE(o.str(), "Math::Quaternion::fromMatrix(): the matrix is not orthogonal\n");
/* Trace > 0 */
CORRADE_COMPARE_AS(m.trace(), 0.0f, Corrade::TestSuite::Compare::Greater);
CORRADE_COMPARE(Quaternion::fromMatrix(m), q);
@ -498,6 +531,20 @@ void QuaternionTest::matrix() {
CORRADE_COMPARE(Quaternion::fromMatrix(m4), q4);
}
void QuaternionTest::matrixNotOrthogonal() {
std::ostringstream out;
Error redirectError{&out};
Vector3 axis = Vector3(-3.0f, 1.0f, 5.0f).normalized();
Matrix3x3 m = Matrix4::rotation(37.0_degf, axis).rotationScaling();
Quaternion::fromMatrix(m*2);
CORRADE_COMPARE(out.str(),
"Math::Quaternion::fromMatrix(): the matrix is not orthogonal:\n"
"Matrix(1.70083, -1.05177, 0.0308525,\n"
" 0.982733, 1.60878, 0.667885,\n"
" -0.376049, -0.552819, 1.88493)\n");
}
void QuaternionTest::lerp() {
Quaternion a = Quaternion::rotation(15.0_degf, Vector3(1.0f/Constants<Float>::sqrt3()));
Quaternion b = Quaternion::rotation(23.0_degf, Vector3::xAxis());
@ -532,8 +579,8 @@ void QuaternionTest::lerpNotNormalized() {
Math::lerp(a*3.0f, a, 0.35f);
Math::lerp(a, a*-3.0f, 0.35f);
CORRADE_COMPARE(out.str(),
"Math::lerp(): quaternions must be normalized\n"
"Math::lerp(): quaternions must be normalized\n");
"Math::lerp(): quaternions Quaternion({0, 0, 0}, 3) and Quaternion({0, 0, 0}, 1) are not normalized\n"
"Math::lerp(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({-0, -0, -0}, -3) are not normalized\n");
}
void QuaternionTest::lerpShortestPath() {
@ -563,8 +610,8 @@ void QuaternionTest::lerpShortestPathNotNormalized() {
Math::lerpShortestPath(a, a*-3.0f, 0.35f);
/* lerpShortestPath() is calling lerp(), so the message is from there */
CORRADE_COMPARE(out.str(),
"Math::lerp(): quaternions must be normalized\n"
"Math::lerp(): quaternions must be normalized\n");
"Math::lerp(): quaternions Quaternion({0, 0, 0}, 3) and Quaternion({0, 0, 0}, 1) are not normalized\n"
"Math::lerp(): quaternions Quaternion({-0, -0, -0}, -1) and Quaternion({-0, -0, -0}, -3) are not normalized\n");
}
void QuaternionTest::slerp() {
@ -607,8 +654,8 @@ void QuaternionTest::slerpNotNormalized() {
Math::slerp(a*3.0f, a, 0.35f);
Math::slerp(a, a*-3.0f, 0.35f);
CORRADE_COMPARE(out.str(),
"Math::slerp(): quaternions must be normalized\n"
"Math::slerp(): quaternions must be normalized\n");
"Math::slerp(): quaternions Quaternion({0, 0, 0}, 3) and Quaternion({0, 0, 0}, 1) are not normalized\n"
"Math::slerp(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({-0, -0, -0}, -3) are not normalized\n");
}
void QuaternionTest::slerpShortestPath() {
@ -637,8 +684,8 @@ void QuaternionTest::slerpShortestPathNotNormalized() {
Math::slerpShortestPath(a*3.0f, a, 0.35f);
Math::slerpShortestPath(a, a*-3.0f, 0.35f);
CORRADE_COMPARE(out.str(),
"Math::slerpShortestPath(): quaternions must be normalized\n"
"Math::slerpShortestPath(): quaternions must be normalized\n");
"Math::slerpShortestPath(): quaternions Quaternion({0, 0, 0}, 3) and Quaternion({0, 0, 0}, 1) are not normalized\n"
"Math::slerpShortestPath(): quaternions Quaternion({0, 0, 0}, 1) and Quaternion({-0, -0, -0}, -3) are not normalized\n");
}
void QuaternionTest::transformVector() {
@ -655,17 +702,20 @@ void QuaternionTest::transformVectorNormalized() {
Quaternion a = Quaternion::rotation(Deg(23.0f), Vector3::xAxis());
Matrix4 m = Matrix4::rotationX(Deg(23.0f));
Vector3 v(5.0f, -3.6f, 0.7f);
std::ostringstream o;
Error redirectError{&o};
(a*2).transformVectorNormalized(v);
CORRADE_COMPARE(o.str(), "Math::Quaternion::transformVectorNormalized(): quaternion must be normalized\n");
Vector3 rotated = a.transformVectorNormalized(v);
CORRADE_COMPARE(rotated, m.transformVector(v));
CORRADE_COMPARE(rotated, a.transformVector(v));
}
void QuaternionTest::transformVectorNormalizedNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
Quaternion a = Quaternion::rotation(23.0_degf, Vector3::xAxis());
(a*2).transformVectorNormalized({});
CORRADE_COMPARE(out.str(), "Math::Quaternion::transformVectorNormalized(): Quaternion({0.398736, 0, 0}, 1.95985) is not normalized\n");
}
void QuaternionTest::debug() {
std::ostringstream o;

39
src/Magnum/Math/Test/VectorTest.cpp

@ -98,9 +98,11 @@ struct VectorTest: Corrade::TestSuite::Tester {
void projected();
void projectedOntoNormalized();
void projectedOntoNormalizedNotNormalized();
void flipped();
void angle();
void angleNotNormalized();
void subclassTypes();
void subclass();
@ -159,9 +161,11 @@ VectorTest::VectorTest() {
&VectorTest::projected,
&VectorTest::projectedOntoNormalized,
&VectorTest::projectedOntoNormalizedNotNormalized,
&VectorTest::flipped,
&VectorTest::angle,
&VectorTest::angleNotNormalized,
&VectorTest::subclassTypes,
&VectorTest::subclass,
@ -481,13 +485,8 @@ void VectorTest::projected() {
}
void VectorTest::projectedOntoNormalized() {
std::ostringstream o;
Error redirectError{&o};
Vector3 vector(1.0f, 2.0f, 3.0f);
Vector3 line(1.0f, -1.0f, 0.5f);
vector.projectedOntoNormalized(line);
CORRADE_COMPARE(o.str(), "Math::Vector::projectedOntoNormalized(): line must be normalized\n");
Vector3 projected = vector.projectedOntoNormalized(line.normalized());
CORRADE_COMPARE(projected, Vector3(0.222222f, -0.222222f, 0.111111f));
@ -495,6 +494,16 @@ void VectorTest::projectedOntoNormalized() {
CORRADE_COMPARE(projected, vector.projected(line));
}
void VectorTest::projectedOntoNormalizedNotNormalized() {
Vector3 vector(1.0f, 2.0f, 3.0f);
Vector3 line(1.0f, -1.0f, 0.5f);
std::ostringstream out;
Error redirectError{&out};
vector.projectedOntoNormalized(line);
CORRADE_COMPARE(out.str(), "Math::Vector::projectedOntoNormalized(): line Vector(1, -1, 0.5) is not normalized\n");
}
void VectorTest::flipped() {
constexpr Vector4 vector{1.0f, -3.5f, 2.1f, 0.5f};
constexpr Vector4 flipped = vector.flipped();
@ -502,20 +511,22 @@ void VectorTest::flipped() {
}
void VectorTest::angle() {
std::ostringstream o;
Error redirectError{&o};
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({});
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(Math::angle(Vector3(2.0f, 3.0f, 4.0f).normalized(),
Vector3(1.0f, -2.0f, 3.0f).normalized()),
Rad(1.162514f));
}
void VectorTest::angleNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
Math::angle(Vector3(2.0f, 3.0f, 4.0f).normalized(), {1.0f, -2.0f, 3.0f});
Math::angle({2.0f, 3.0f, 4.0f}, Vector3(1.0f, -2.0f, 3.0f).normalized());
CORRADE_COMPARE(out.str(),
"Math::angle(): vectors Vector(0.371391, 0.557086, 0.742781) and Vector(1, -2, 3) are not normalized\n"
"Math::angle(): vectors Vector(2, 3, 4) and Vector(0.267261, -0.534522, 0.801784) are not normalized\n");
}
template<class T> class BasicVec2: public Math::Vector<2, T> {
public:
template<class ...U> constexpr BasicVec2(U&&... args): Math::Vector<2, T>{args...} {}

5
src/Magnum/Math/Vector.h

@ -100,7 +100,7 @@ typename std::enable_if<std::is_floating_point<FloatingPoint>::value, Rad<Floati
#endif
angle(const Vector<size, FloatingPoint>& normalizedA, const Vector<size, FloatingPoint>& normalizedB) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),
"Math::angle(): vectors must be normalized", {});
"Math::angle(): vectors" << normalizedA << "and" << normalizedB << "are not normalized", {});
return Rad<FloatingPoint>(std::acos(dot(normalizedA, normalizedB)));
}
@ -1359,7 +1359,8 @@ inline Vector<size, T>
template<class U> inline typename std::enable_if<std::is_floating_point<U>::value, Vector<size, T>>::type
#endif
Vector<size, T>::projectedOntoNormalized(const Vector<size, T>& line) const {
CORRADE_ASSERT(line.isNormalized(), "Math::Vector::projectedOntoNormalized(): line must be normalized", {});
CORRADE_ASSERT(line.isNormalized(),
"Math::Vector::projectedOntoNormalized(): line" << line << "is not normalized", {});
return line*Math::dot(*this, line);
}

Loading…
Cancel
Save