From 75ffd39fe0b3dd3c5e26b30e3e72a97781dea57f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 25 May 2019 00:11:43 +0200 Subject: [PATCH] Math: make it possible to use Deg/Rad in all functions. This should have been here ages ago, haha. --- doc/changelog.dox | 4 + doc/matrix-vector.dox | 8 +- doc/snippets/MagnumMath.cpp | 4 + src/Magnum/Math/Functions.h | 111 ++++++++++---------- src/Magnum/Math/FunctionsBatch.h | 6 +- src/Magnum/Math/Packing.h | 22 ++-- src/Magnum/Math/Test/FunctionsBatchTest.cpp | 11 ++ src/Magnum/Math/Test/FunctionsTest.cpp | 67 ++++++++++++ src/Magnum/Math/Test/PackingTest.cpp | 19 ++++ src/Magnum/Math/Vector.h | 4 +- 10 files changed, 183 insertions(+), 73 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 38c4ef30f..f7c5c4d42 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -211,6 +211,10 @@ See also: - Construction using @ref Math::ZeroInit and @ref Math::IdentityInit is now explicit, to be consistent with @ref Math::NoInit construction and avoid ambiguous cases +- All scalar/vector functions in the @ref Math namespace now work also with + special types such as @ref Deg or @ref Rad --- the only exception is + power functions such as @ref Math::sqrt() or @ref Math::log(), as the + resulting unit can't be represented. Those accept only unitless types. - Changed the way @ref Math::operator<<(Corrade::Utility::Debug&, const BoolVector&) works --- the output now has the same bit order as when constructing it using binary literals diff --git a/doc/matrix-vector.dox b/doc/matrix-vector.dox index 6c5ba44af..f96d46917 100644 --- a/doc/matrix-vector.dox +++ b/doc/matrix-vector.dox @@ -241,11 +241,15 @@ reinterpret the matrix as vector and do the operation on it (and vice versa): @snippet MagnumMath.cpp matrix-vector-operations-functions-componentwise -Note that all component-wise functions in @ref Math namespace work also for -scalars: +Note that all component-wise functions in the @ref Math namespace work also for +scalars --- and on the special @ref Deg / @ref Rad types too. @snippet MagnumMath.cpp matrix-vector-operations-functions-scalar +For types with units the only exception are power functions such as +@ref Math::pow() or @ref Math::log() --- the resulting unit of such an +operation can't be represented and thus those work only on unitless types. + @section matrix-vector-column-major Matrices are column-major and vectors are columns OpenGL matrices are column-major, thus it is reasonable to have matrices in diff --git a/doc/snippets/MagnumMath.cpp b/doc/snippets/MagnumMath.cpp index bef1f956a..41ccf9c5a 100644 --- a/doc/snippets/MagnumMath.cpp +++ b/doc/snippets/MagnumMath.cpp @@ -314,14 +314,18 @@ mat = Matrix3x2::fromVector(vec); } { +Deg value; /* [matrix-vector-operations-functions-scalar] */ std::pair minmax = Math::minmax(24, -5); // -5, 24 Int a = Math::lerp(0, 360, 0.75f); // 270 auto b = Math::pack(0.89f); // 226 + +Deg c = Math::clamp(value, 25.0_degf, 55.0_degf); /* [matrix-vector-operations-functions-scalar] */ static_cast(minmax); static_cast(a); static_cast(b); +static_cast(c); } { diff --git a/src/Magnum/Math/Functions.h b/src/Magnum/Math/Functions.h index 697b3e0cb..45731f25b 100644 --- a/src/Magnum/Math/Functions.h +++ b/src/Magnum/Math/Functions.h @@ -56,17 +56,6 @@ namespace Implementation { template struct IsBoolVectorOrScalar: std::false_type {}; template<> struct IsBoolVectorOrScalar: std::true_type {}; template struct IsBoolVectorOrScalar>: std::true_type {}; - - template struct IsVectorOrScalar: std::is_arithmetic::type {}; - template class Derived, class T> struct IsVectorOrScalar>: std::true_type {}; - template struct IsVectorOrScalar>: std::true_type {}; - template struct IsVectorOrScalar>: std::true_type {}; - template struct IsVectorOrScalar>: std::true_type {}; - template struct IsVectorOrScalar>: std::true_type {}; - template struct IsVectorOrScalar>: std::true_type {}; - template struct IsVectorOrScalar>: std::true_type {}; - template struct IsVectorOrScalar>: std::true_type {}; - template struct IsVectorOrScalar>: std::true_type {}; } /** @@ -113,7 +102,8 @@ Equivalent to the following, but possibly done in a single CPU instruction: @snippet MagnumMath.cpp div-equivalent */ template inline std::pair div(Integral x, Integral y) { - static_assert(std::is_integral{}, "Math::div(): not an integral type"); + static_assert(IsIntegral::value && IsScalar::value, + "scalar integral type expected"); const auto result = std::div(x, y); return {result.quot, result.rem}; } @@ -123,15 +113,15 @@ template inline std::pair div(Integral x, In @see @ref isNan(), @ref Constants::inf() */ -template inline typename std::enable_if::value, bool>::type isInf(T value) { - return std::isinf(value); +template inline typename std::enable_if::value, bool>::type isInf(T value) { + return std::isinf(UnderlyingTypeOf(value)); } /** @overload */ template inline BoolVector isInf(const Vector& value) { BoolVector out; for(std::size_t i = 0; i != size; ++i) - out.set(i, std::isinf(value[i])); + out.set(i, Math::isInf(value[i])); return out; } @@ -141,15 +131,15 @@ template inline BoolVector isInf(const Vector inline typename std::enable_if::value, bool>::type isNan(T value) { - return std::isnan(value); +template inline typename std::enable_if::value, bool>::type isNan(T value) { + return std::isnan(UnderlyingTypeOf(value)); } /** @overload */ template inline BoolVector isNan(const Vector& value) { BoolVector out; for(std::size_t i = 0; i != size; ++i) - out.set(i, std::isnan(value[i])); + out.set(i, Math::isNan(value[i])); return out; } @@ -217,27 +207,29 @@ template inline Rad atan(T value) { return Rad(std::atan(value)); /** @{ @name Scalar/vector functions -These functions are overloaded for both scalar and vector types. Scalar -versions function exactly as their possible STL equivalents, vector overloads -perform the operations component-wise. +These functions are overloaded for both scalar and vector types, including +@ref Deg and @ref Rad. Scalar versions function exactly as their possible STL +equivalents, vector overloads perform the operations component-wise. */ /** @brief Integral power -Returns integral power of base to the exponent. +Returns integral power of base to the exponent. Works only on types that +satisfy @ref IsUnitless. @see @ref pow(T, T) */ #ifdef DOXYGEN_GENERATING_OUTPUT template constexpr T pow(T base); #else -template constexpr typename std::enable_if::value, T>::type pow(T base) { +template constexpr typename std::enable_if::value, T>::type pow(T base) { + static_assert(IsUnitless::value, "expected an unitless type"); return Implementation::Pow::pow(base); } template inline Vector pow(const Vector& base) { Vector out{NoInit}; for(std::size_t i = 0; i != size; ++i) - out[i] = Implementation::Pow::pow(base[i]); + out[i] = Math::pow(base[i]); return out; } #endif @@ -245,19 +237,21 @@ template inline Vector /** @brief Power -Returns power of @p base to the @p exponent. +Returns power of @p base to the @p exponent. Works only on types that satisfy +@ref IsUnitless. @see @ref pow(T), @ref exp() */ #ifdef DOXYGEN_GENERATING_OUTPUT template T pow(T base, T exponent); #else -template inline typename std::enable_if::value, T>::type pow(T base, T exponent) { +template inline typename std::enable_if::value, T>::type pow(T base, T exponent) { + static_assert(IsUnitless::value, "expected an unitless type"); return std::pow(base, exponent); } template inline Vector pow(const Vector& base, T exponent) { Vector out{NoInit}; for(std::size_t i = 0; i != size; ++i) - out[i] = std::pow(base[i], exponent); + out[i] = Math::pow(base[i], exponent); return out; } #endif @@ -326,7 +320,7 @@ template inline Vector max(const Vector inline std::pair minmax(const T& a, const T& b); #else -template inline typename std::enable_if::value, std::pair>::type minmax(T a, T b) { +template inline typename std::enable_if::value, std::pair>::type minmax(T a, T b) { return a < b ? std::make_pair(a, b) : std::make_pair(b, a); } template inline std::pair, Vector> minmax(const Vector& a, const Vector& b) { @@ -352,13 +346,13 @@ set to @p max. Equivalent to: #ifdef DOXYGEN_GENERATING_OUTPUT template inline T clamp(const T& value, const T& min, const T& max); #else -template inline typename std::enable_if::value, T>::type clamp(T value, T min, T max) { +template inline typename std::enable_if::value, T>::type clamp(T value, T min, T max) { return Math::min(Math::max(value, min), max); } template inline Vector clamp(const Vector& value, const Vector& min, const Vector& max) { Vector out{NoInit}; for(std::size_t i = 0; i != size; ++i) - out[i] = clamp(value[i], min[i], max[i]); + out[i] = Math::clamp(value[i], min[i], max[i]); return out; } #endif @@ -367,7 +361,7 @@ template inline Vector clamp(const Vector inline Vector clamp(const Vector& value, T min, T max) { Vector out{NoInit}; for(std::size_t i = 0; i != size; ++i) - out[i] = clamp(value[i], min, max); + out[i] = Math::clamp(value[i], min, max); return out; } @@ -379,7 +373,7 @@ Returns `1` if @p x > 0, `0` if @p x = 0 and `-1` if @p x < 0. #ifdef DOXYGEN_GENERATING_OUTPUT template inline T sign(const T scalar); #else -template inline typename std::enable_if::value, T>::type sign(const T& scalar) { +template inline typename std::enable_if::value, T>::type sign(const T& scalar) { if(scalar > T(0)) return T(1); if(scalar < T(0)) return T(-1); return T(0); @@ -387,7 +381,7 @@ template inline typename std::enable_if::value, T template inline Vector sign(const Vector& a) { Vector out{NoInit}; for(std::size_t i = 0; i != size; ++i) - out[i] = sign(a[i]); + out[i] = Math::sign(a[i]); return out; } #endif @@ -396,13 +390,13 @@ template inline Vector sign(const Vector inline T abs(const T& a); #else -template inline typename std::enable_if::value, T>::type abs(T a) { - return std::abs(a); +template inline typename std::enable_if::value, T>::type abs(T a) { + return T(std::abs(UnderlyingTypeOf(a))); } template inline Vector abs(const Vector& a) { Vector out{NoInit}; for(std::size_t i = 0; i != size; ++i) - out[i] = std::abs(a[i]); + out[i] = Math::abs(a[i]); return out; } #endif @@ -411,13 +405,13 @@ template inline Vector abs(const Vector inline T floor(const T& a); #else -template inline typename std::enable_if::value, T>::type floor(T a) { - return std::floor(a); +template inline typename std::enable_if::value, T>::type floor(T a) { + return T(std::floor(UnderlyingTypeOf(a))); } template inline Vector floor(const Vector& a) { Vector out{NoInit}; for(std::size_t i = 0; i != size; ++i) - out[i] = std::floor(a[i]); + out[i] = Math::floor(a[i]); return out; } #endif @@ -426,13 +420,13 @@ template inline Vector floor(const Vector inline T round(const T& a); #else -template inline typename std::enable_if::value, T>::type round(T a) { - return std::round(a); +template inline typename std::enable_if::value, T>::type round(T a) { + return T(std::round(UnderlyingTypeOf(a))); } template inline Vector round(const Vector& a) { Vector out{NoInit}; for(std::size_t i = 0; i != size; ++i) - out[i] = std::round(a[i]); + out[i] = Math::round(a[i]); return out; } #endif @@ -441,13 +435,13 @@ template inline Vector round(const Vector inline T ceil(const T& a); #else -template inline typename std::enable_if::value, T>::type ceil(T a) { - return std::ceil(a); +template inline typename std::enable_if::value, T>::type ceil(T a) { + return T(std::ceil(UnderlyingTypeOf(a))); } template inline Vector ceil(const Vector& a) { Vector out{NoInit}; for(std::size_t i = 0; i != size; ++i) - out[i] = std::ceil(a[i]); + out[i] = Math::ceil(a[i]); return out; } #endif @@ -455,18 +449,20 @@ template inline Vector ceil(const Vector&) */ #ifdef DOXYGEN_GENERATING_OUTPUT template inline T sqrt(const T& a); #else -template inline typename std::enable_if::value, T>::type sqrt(T a) { - return T(std::sqrt(a)); +template inline typename std::enable_if::value, T>::type sqrt(T a) { + static_assert(IsUnitless::value, "expecting an unitless type"); + return std::sqrt(a); } template inline Vector sqrt(const Vector& a) { Vector out{NoInit}; for(std::size_t i = 0; i != size; ++i) - out[i] = T(std::sqrt(a[i])); + out[i] = Math::sqrt(a[i]); return out; } #endif @@ -474,17 +470,19 @@ template inline Vector sqrt(const Vector inline T sqrtInverted(const T& a); #else -template inline typename std::enable_if::value, T>::type sqrtInverted(T a) { +template inline typename std::enable_if::value, T>::type sqrtInverted(T a) { + static_assert(IsUnitless::value, "expecting an unitless type"); return T(1)/std::sqrt(a); } template inline Vector sqrtInverted(const Vector& a) { - return Vector(T(1))/sqrt(a); + return Vector(T(1))/Math::sqrt(a); } #endif @@ -509,7 +507,7 @@ See @ref select() for constant interpolation using the same API and */ template inline #ifndef DOXYGEN_GENERATING_OUTPUT - typename std::enable_if::value && !Implementation::IsBoolVectorOrScalar::value, T>::type + typename std::enable_if<(IsVector::value || IsScalar::value) && !Implementation::IsBoolVectorOrScalar::value, T>::type #else T #endif @@ -562,10 +560,10 @@ Returns interpolation phase *t*: @f[ #ifdef DOXYGEN_GENERATING_OUTPUT template inline T lerpInverted(const T& a, const T& b, const T& lerp); #else -template inline T lerpInverted(T a, T b, T lerp) { +template inline UnderlyingTypeOf::value, T>::type> lerpInverted(T a, T b, T lerp) { return (lerp - a)/(b - a); } -template inline Vector lerpInverted(const Vector& a, const Vector& b, const Vector& lerp) { +template inline Vector> lerpInverted(const Vector& a, const Vector& b, const Vector& lerp) { return (lerp - a)/(b - a); } #endif @@ -593,12 +591,14 @@ template constexpr T select(const T& a, const T& b, U t) { @brief Fused multiply-add Computes and returns @f$ ab + c @f$. On some architectures might be faster than -doing the computation manually. +doing the computation manually. Works only on types that satisfy +@ref IsUnitless. */ #ifdef DOXYGEN_GENERATING_OUTPUT template inline T fma(const T& a, const T& b, const T& c); #else -template inline typename std::enable_if::value, T>::type fma(T a, T b, T c) { +template inline typename std::enable_if::value, T>::type fma(T a, T b, T c) { + static_assert(IsUnitless::value, "expecting an unitless type"); /* On Emscripten it works with -O2 but not with -O1 (function not defined). I guess that's only because -O2 optimizes it out, so disabling it there. */ #ifndef CORRADE_TARGET_EMSCRIPTEN @@ -608,6 +608,7 @@ template inline typename std::enable_if::value, T #endif } template inline Vector fma(const Vector& a, const Vector& b, const Vector& c) { + static_assert(IsUnitless::value, "expecting an unitless type"); return a*b + c; } #endif diff --git a/src/Magnum/Math/FunctionsBatch.h b/src/Magnum/Math/FunctionsBatch.h index 5e9c4281f..18bda98ad 100644 --- a/src/Magnum/Math/FunctionsBatch.h +++ b/src/Magnum/Math/FunctionsBatch.h @@ -47,7 +47,7 @@ template inline T min(Corrade::Containers::ArrayView range) { T out(range[0]); for(std::size_t i = 1; i != range.size(); ++i) - out = min(out, range[i]); + out = Math::min(out, range[i]); return out; } @@ -71,7 +71,7 @@ template inline T max(Corrade::Containers::ArrayView range) { T out(range[0]); for(std::size_t i = 1; i != range.size(); ++i) - out = max(out, range[i]); + out = Math::max(out, range[i]); return out; } @@ -86,7 +86,7 @@ template inline T max(const T(&array)[size]) { } namespace Implementation { - template inline typename std::enable_if::value, void>::type minmax(T& min, T& max, T value) { + template inline typename std::enable_if::value, void>::type minmax(T& min, T& max, T value) { if(value < min) min = value; else if(value > max) diff --git a/src/Magnum/Math/Packing.h b/src/Magnum/Math/Packing.h index 5972847b8..c807fc760 100644 --- a/src/Magnum/Math/Packing.h +++ b/src/Magnum/Math/Packing.h @@ -73,20 +73,20 @@ representation to use. Example usage: */ template inline FloatingPoint unpack(const Integral& value); #else -template inline typename std::enable_if::value && std::is_unsigned::value, FloatingPoint>::type unpack(const Integral& value) { - static_assert(std::is_floating_point::value && std::is_integral::value, +template inline typename std::enable_if::value && std::is_unsigned::value, FloatingPoint>::type unpack(const Integral& value) { + static_assert(IsFloatingPoint::value && IsIntegral::value, "unpacking must be done from integral to floating-point type"); static_assert(bits <= sizeof(Integral)*8, "bit count larger than size of the integral type"); - return value/FloatingPoint(Implementation::bitMax()); + return FloatingPoint(value/UnderlyingTypeOf(Implementation::bitMax())); } -template inline typename std::enable_if::value && std::is_signed::value, FloatingPoint>::type unpack(const Integral& value) { - static_assert(std::is_floating_point::value && std::is_integral::value, +template inline typename std::enable_if::value && std::is_signed::value, FloatingPoint>::type unpack(const Integral& value) { + static_assert(IsFloatingPoint::value && IsIntegral::value, "unpacking must be done from integral to floating-point type"); static_assert(bits <= sizeof(Integral)*8, "bit count larger than size of the integral type"); /* According to https://www.opengl.org/registry/specs/EXT/texture_snorm.txt */ - return Math::max(value/FloatingPoint(Implementation::bitMax()), FloatingPoint(-1.0)); + return FloatingPoint(Math::max(value/UnderlyingTypeOf(Implementation::bitMax()), UnderlyingTypeOf(-1.0))); } template FloatingPoint unpack(const Vector& value) { static_assert(FloatingPoint::Size == size, @@ -102,7 +102,7 @@ template inline FloatingPoint unpack(const Integral& value); #else -template inline typename std::enable_if::value, FloatingPoint>::type unpack(const Integral& value) { +template inline typename std::enable_if::value, FloatingPoint>::type unpack(const Integral& value) { return unpack(value); } template inline FloatingPoint unpack(const Vector& value) { @@ -130,12 +130,12 @@ given *signed* integral type. #ifdef DOXYGEN_GENERATING_OUTPUT template inline Integral pack(const FloatingPoint& value); #else -template inline typename std::enable_if::value, Integral>::type pack(FloatingPoint value) { - static_assert(std::is_floating_point::value && std::is_integral::value, +template inline typename std::enable_if::value, Integral>::type pack(FloatingPoint value) { + static_assert(IsFloatingPoint::value && IsIntegral::value, "packing must be done from floating-point to integral type"); static_assert(bits <= sizeof(Integral)*8, "bit count larger than size of the integral type"); - return Integral(round(value*Implementation::bitMax())); + return Integral(round(UnderlyingTypeOf(value)*Implementation::bitMax())); } template Integral pack(const Vector& value) { static_assert(Integral::Size == size, @@ -158,7 +158,7 @@ representation to use. Example usage: #ifdef DOXYGEN_GENERATING_OUTPUT template inline Integral pack(FloatingPoint value); #else -template inline typename std::enable_if::value, Integral>::type pack(FloatingPoint value) { +template inline typename std::enable_if::value, Integral>::type pack(FloatingPoint value) { return pack(value); } template inline Integral pack(const Vector& value) { diff --git a/src/Magnum/Math/Test/FunctionsBatchTest.cpp b/src/Magnum/Math/Test/FunctionsBatchTest.cpp index 9b555fdab..944c19509 100644 --- a/src/Magnum/Math/Test/FunctionsBatchTest.cpp +++ b/src/Magnum/Math/Test/FunctionsBatchTest.cpp @@ -38,6 +38,8 @@ struct FunctionsBatchTest: Corrade::TestSuite::Tester { void minmaxList(); }; +using namespace Literals; + typedef Math::Vector2 Vector2; typedef Math::Vector3 Vector3i; @@ -57,6 +59,9 @@ void FunctionsBatchTest::minList() { const Int array[]{5, -2, 9}; CORRADE_COMPARE(Math::min(array), -2); + + /* Wrapped types */ + CORRADE_COMPARE(Math::min({5.0_degf, 2.0_degf, 9.0_degf}), 2.0_degf); } void FunctionsBatchTest::maxList() { @@ -69,6 +74,9 @@ void FunctionsBatchTest::maxList() { const Int array[]{5, -2, 9}; CORRADE_COMPARE(Math::max(array), 9); + + /* Wrapped types */ + CORRADE_COMPARE(Math::max({5.0_degf, 2.0_degf, 9.0_degf}), 9.0_degf); } void FunctionsBatchTest::minmaxList() { @@ -90,6 +98,9 @@ void FunctionsBatchTest::minmaxList() { const Float array[]{-1.0f, 2.0f, -3.0f}; CORRADE_COMPARE(Math::minmax(array), expected); + + /* Wrapped types */ + CORRADE_COMPARE(Math::minmax({1.0_radf, 2.0_radf, 3.0_radf}), std::make_pair(1.0_radf, 3.0_radf)); } }}}} diff --git a/src/Magnum/Math/Test/FunctionsTest.cpp b/src/Magnum/Math/Test/FunctionsTest.cpp index 193b262b6..f20d8ac8d 100644 --- a/src/Magnum/Math/Test/FunctionsTest.cpp +++ b/src/Magnum/Math/Test/FunctionsTest.cpp @@ -71,6 +71,8 @@ struct FunctionsTest: Corrade::TestSuite::Tester { void trigonometricWithBase(); }; +using namespace Literals; + typedef Math::Constants Constants; typedef Math::Deg Deg; typedef Math::Rad Rad; @@ -130,23 +132,33 @@ void FunctionsTest::powIntegral() { CORRADE_COMPARE(a, 125); CORRADE_COMPARE(Math::pow<2>(Vector3{2.0f, -3.0f, 1.5f}), (Vector3{4.0f, 9.0f, 2.25f})); + + /* Not testing wrapped types -- what unit should have degrees squared? */ } void FunctionsTest::pow() { CORRADE_COMPARE(Math::pow(2.0f, 0.5f), 1.414213562f); CORRADE_COMPARE(Math::pow(Vector3{2.0f, 9.0f, 25.0f}, 0.5f), (Vector3{1.414213562f, 3.0f, 5.0f})); + + /* Not testing wrapped types -- what unit should have degrees squared? */ } void FunctionsTest::min() { CORRADE_COMPARE(Math::min(5, 9), 5); CORRADE_COMPARE(Math::min(Vector3i(5, -3, 2), Vector3i(9, -5, 18)), Vector3i(5, -5, 2)); CORRADE_COMPARE(Math::min(Vector3i{5, -3, 2}, 1), (Vector3i{1, -3, 1})); + + /* Wrapped types */ + CORRADE_COMPARE(Math::min(5.0_degf, 9.0_degf), 5.0_degf); } void FunctionsTest::max() { CORRADE_COMPARE(Math::max(5, 9), 9); CORRADE_COMPARE(Math::max(Vector3i(5, -3, 2), Vector3i(9, -5, 18)), Vector3i(9, -3, 18)); CORRADE_COMPARE(Math::max(Vector3i{5, -3, 2}, 3), (Vector3i{5, 3, 3})); + + /* Wrapped types */ + CORRADE_COMPARE(Math::max(5.0_degf, 9.0_degf), 9.0_degf); } void FunctionsTest::minmax() { @@ -159,6 +171,9 @@ void FunctionsTest::minmax() { const std::pair expectedVector{{5.0f, -4.0f, 1.0f}, {7.0f, -3.0f, 1.0f}}; CORRADE_COMPARE_AS(Math::minmax(a, b), expectedVector, std::pair); CORRADE_COMPARE_AS(Math::minmax(b, a), expectedVector, std::pair); + + /* Wrapped types */ + CORRADE_COMPARE(Math::minmax(4.0_degf, 5.0_degf), std::make_pair(4.0_degf, 5.0_degf)); } void FunctionsTest::clamp() { @@ -173,6 +188,9 @@ void FunctionsTest::clamp() { Vector3(0.5f, 2.0f, 5.0f)); CORRADE_COMPARE(Math::clamp(Vector3(0.5f, -1.6f, 9.5f), -1.0f, 5.0f), Vector3(0.5f, -1.0f, 5.0f)); + + /* Wrapped types */ + CORRADE_COMPARE(Math::clamp(0.5_degf, 0.75_degf, 1.0_degf), 0.75_degf); } void FunctionsTest::nanPropagation() { @@ -194,17 +212,26 @@ void FunctionsTest::sign() { CORRADE_COMPARE(Math::sign(0.0f), 0.0f); CORRADE_COMPARE(Math::sign(-3.7), -1.0); CORRADE_COMPARE(Math::sign(Vector3i(0, -3, 2)), Vector3i(0, -1, 1)); + + /* Wrapped types */ + CORRADE_COMPARE(Math::sign(-3.7_degf), -1.0_degf); } void FunctionsTest::abs() { CORRADE_COMPARE(Math::abs(-5), 5); CORRADE_COMPARE(Math::abs(5), 5); CORRADE_COMPARE(Math::abs(Vector3i(5, -3, 2)), Vector3i(5, 3, 2)); + + /* Wrapped types */ + CORRADE_COMPARE(Math::abs(-5.0_degf), 5.0_degf); } void FunctionsTest::floor() { CORRADE_COMPARE(Math::floor(0.7f), 0.0f); CORRADE_COMPARE(Math::floor(Vector3(2.3f, 0.7f, 1.5f)), Vector3(2.0f, 0.0f, 1.0f)); + + /* Wrapped types */ + CORRADE_COMPARE(Math::floor(2.7_degf), 2.0_degf); } void FunctionsTest::round() { @@ -219,21 +246,31 @@ void FunctionsTest::round() { CORRADE_COMPARE(Math::round(1.3f), 1.0f); CORRADE_COMPARE(Math::round(1.5f), 2.0f); CORRADE_COMPARE(Math::round(2.0f), 2.0f); + + /* Wrapped types */ + CORRADE_COMPARE(Math::round(2.7_degf), 3.0_degf); } void FunctionsTest::ceil() { CORRADE_COMPARE(Math::ceil(2.3f), 3.0f); CORRADE_COMPARE(Math::ceil(Vector3(2.3f, 0.7f, 1.5f)), Vector3(3.0f, 1.0f, 2.0f)); + + /* Wrapped types */ + CORRADE_COMPARE(Math::ceil(2.7_degf), 3.0_degf); } void FunctionsTest::sqrt() { CORRADE_COMPARE(Math::sqrt(16), 4); CORRADE_COMPARE(Math::sqrt(Vector3i(256, 1, 0)), Vector3i(16, 1, 0)); + + /* Not testing wrapped types -- what unit should have degrees squared? */ } void FunctionsTest::sqrtInverted() { CORRADE_COMPARE(Math::sqrtInverted(16.0f), 0.25f); CORRADE_COMPARE(Math::sqrtInverted(Vector3(1.0f, 4.0f, 16.0f)), Vector3(1.0f, 0.5f, 0.25f)); + + /* Not testing wrapped types -- what unit should have degrees squared? */ } void FunctionsTest::lerp() { @@ -253,6 +290,9 @@ void FunctionsTest::lerp() { /* Vector as interpolation phase */ CORRADE_COMPARE(Math::lerp(a, b, Vector3(0.25f, 0.5f, 0.75f)), Vector3(0.0f, 0.0f, 9.0f)); + + /* Wrapped types */ + CORRADE_COMPARE(Math::lerp(2.0_degf, 5.0_degf, 0.5f), 3.5_degf); } void FunctionsTest::lerpBool() { @@ -263,6 +303,9 @@ void FunctionsTest::lerpBool() { /* Vector interpolation phase */ CORRADE_COMPARE(Math::lerp(Vector3i{1, 2, 3}, Vector3i{5, 6, 7}, BoolVector<3>(5)), (Vector3i{5, 2, 7})); CORRADE_COMPARE(Math::lerp(BoolVector<3>{false}, BoolVector<3>{true}, BoolVector<3>(5)), BoolVector<3>{5}); + + /* Wrapped types */ + CORRADE_COMPARE(Math::lerp(2.0_degf, 5.0_degf, true), 5.0_degf); } void FunctionsTest::lerpInverted() { @@ -273,6 +316,9 @@ void FunctionsTest::lerpInverted() { Vector3 a(-1.0f, 2.0f, 3.0f); Vector3 b(3.0f, -2.0f, 11.0f); CORRADE_COMPARE(Math::lerpInverted(a, b, Vector3(0.0f, 0.0f, 9.0f)), Vector3(0.25f, 0.5f, 0.75f)); + + /* Wrapped types */ + CORRADE_COMPARE(Math::lerpInverted(2.0_degf, 5.0_degf, 3.5_degf), 0.5f); } void FunctionsTest::select() { @@ -290,12 +336,18 @@ void FunctionsTest::select() { /* Vector as interpolation phase */ CORRADE_COMPARE(Math::select(a, b, Vector3(0.25f, 1.5f, 1.0f)), Vector3(-1.0f, -2.0f, 11.0f)); + + /* Wrapped types */ + CORRADE_COMPARE(Math::select(2.0_degf, 5.0_degf, 0.5_degf), 2.0_degf); } void FunctionsTest::selectBool() { CORRADE_COMPARE(Math::select(true, false, 0.5f), true); CORRADE_COMPARE(Math::select(Math::BoolVector<4>{0xa}, Math::BoolVector<4>{0x5}, 1.1f), Math::BoolVector<4>{0x5}); CORRADE_COMPARE(Math::select(Math::BoolVector<4>{0xa}, Math::BoolVector<4>{0x5}, Vector4{1.1f, -1.0f, 1.3f, 0.5f}), Math::BoolVector<4>{0xf}); + + /* Wrapped types */ + CORRADE_COMPARE(Math::select(true, false, 0.5_degf), true); } void FunctionsTest::fma() { @@ -304,6 +356,9 @@ void FunctionsTest::fma() { Vector3( 3.0f, 2.0f, -1.0f), Vector3(0.75f, 0.25f, 0.1f)), Vector3(6.75f, 3.25f, -0.4f)); + + /* Not testing wrapped types as the resulting unit is less clear -- convert + to an unitless type first */ } void FunctionsTest::logIntegral() { @@ -317,10 +372,14 @@ void FunctionsTest::log2() { void FunctionsTest::log() { CORRADE_COMPARE(Math::log(2.0f), 0.693147f); + + /* Not testing wrapped types -- what unit should have degrees squared? */ } void FunctionsTest::exp() { CORRADE_COMPARE(Math::exp(0.693147f), 2.0f); + + /* Not testing wrapped types -- what unit should have degrees squared? */ } void FunctionsTest::div() { @@ -334,6 +393,10 @@ void FunctionsTest::isInf() { CORRADE_VERIFY(Math::isInf(-Constants::inf())); CORRADE_VERIFY(!Math::isInf(Constants::nan())); CORRADE_VERIFY(!Math::isInf(5.3f)); + + /* Wrapped types */ + CORRADE_VERIFY(Math::isInf(-Rad(Constants::inf()))); + CORRADE_VERIFY(!Math::isInf(5.3_degf)); } void FunctionsTest::isInfVector() { @@ -346,6 +409,10 @@ void FunctionsTest::isNan() { CORRADE_VERIFY(!Math::isNan(-Constants::inf())); CORRADE_VERIFY(Math::isNan(Constants::nan())); CORRADE_VERIFY(!Math::isNan(5.3f)); + + /* Wrapped types */ + CORRADE_VERIFY(Math::isNan(-Rad(Constants::nan()))); + CORRADE_VERIFY(!Math::isNan(5.3_degf)); } void FunctionsTest::isNanfVector() { diff --git a/src/Magnum/Math/Test/PackingTest.cpp b/src/Magnum/Math/Test/PackingTest.cpp index e6a9a63ed..aeafb9f38 100644 --- a/src/Magnum/Math/Test/PackingTest.cpp +++ b/src/Magnum/Math/Test/PackingTest.cpp @@ -51,6 +51,9 @@ struct PackingTest: Corrade::TestSuite::Tester { because there's involved comparison and benchmarks to ground truth */ }; +using namespace Literals; + +typedef Math::Rad Rad; typedef Math::Vector3 Vector3; typedef Math::Vector3 Vector3ub; typedef Math::Vector3 Vector3b; @@ -116,6 +119,10 @@ void PackingTest::unpackUnsigned() { /* Vector overloads */ CORRADE_COMPARE(Math::unpack(Vector3ub(0, 127, 255)), Vector3(0.0f, 0.498039f, 1.0f)); CORRADE_COMPARE((Math::unpack(Vector3ub(0, 31, 63))), Vector3(0.0f, 0.492063f, 1.0f)); + + /* Wrapped types */ + CORRADE_COMPARE((Math::unpack(8191)), 0.124987_radf); + CORRADE_COMPARE((Math::unpack(8191u)), 0.499969_radf); } void PackingTest::unpackSigned() { @@ -149,6 +156,10 @@ void PackingTest::unpackSigned() { /* Vector overloads */ CORRADE_COMPARE(Math::unpack(Vector3b(0, -127, 64)), Vector3(0.0f, -1.0f, 0.503937f)); CORRADE_COMPARE((Math::unpack(Vector3b(0, -31, 16))), Vector3(0.0f, -1.0f, 0.516129f)); + + /* Wrapped types */ + CORRADE_COMPARE((Math::unpack(8191)), 0.249977_radf); + CORRADE_COMPARE((Math::unpack(8191)), 1.0_radf); } void PackingTest::packUnsigned() { @@ -190,6 +201,10 @@ void PackingTest::packUnsigned() { /* Vector overloads */ CORRADE_COMPARE(Math::pack(Vector3(0.0f, 0.5f, 1.0f)), Vector3ub(0, 128, 255)); CORRADE_COMPARE((Math::pack(Vector3(0.0f, 0.5f, 1.0f))), Vector3ub(0, 32, 63)); + + /* Wrapped types */ + CORRADE_COMPARE((Math::pack(0.5_degf)), 32768); + CORRADE_COMPARE((Math::pack(0.5_degf)), 8192); } void PackingTest::packSigned() { @@ -233,6 +248,10 @@ void PackingTest::packSigned() { /* Vector overloads */ CORRADE_COMPARE(Math::pack(Vector3(0.0f, -1.0f, 0.5f)), Vector3b(0, -127, 64)); CORRADE_COMPARE((Math::pack(Vector3(0.0f, -1.0f, 0.5f))), Vector3b(0, -31, 16)); + + /* Wrapped types */ + CORRADE_COMPARE((Math::pack(-0.5_degf)), -16384); + CORRADE_COMPARE((Math::pack(-0.5_degf)), -4096); } void PackingTest::reunpackUnsigned() { diff --git a/src/Magnum/Math/Vector.h b/src/Magnum/Math/Vector.h index 9909768e9..c882424f9 100644 --- a/src/Magnum/Math/Vector.h +++ b/src/Magnum/Math/Vector.h @@ -45,11 +45,11 @@ namespace Magnum { namespace Math { #ifndef DOXYGEN_GENERATING_OUTPUT /* Documented in Functions.h, defined here because Vector needs them */ -template constexpr typename std::enable_if::value, T>::type min(T a, T b) { +template constexpr typename std::enable_if::value, T>::type min(T a, T b) { return b < a ? b : a; } -template constexpr typename std::enable_if::value, T>::type max(T a, T b) { +template constexpr typename std::enable_if::value, T>::type max(T a, T b) { return a < b ? b : a; } #endif