Browse Source

Math: make it possible to use Deg/Rad in all functions.

This should have been here ages ago, haha.
pull/342/head
Vladimír Vondruš 7 years ago
parent
commit
75ffd39fe0
  1. 4
      doc/changelog.dox
  2. 8
      doc/matrix-vector.dox
  3. 4
      doc/snippets/MagnumMath.cpp
  4. 111
      src/Magnum/Math/Functions.h
  5. 6
      src/Magnum/Math/FunctionsBatch.h
  6. 22
      src/Magnum/Math/Packing.h
  7. 11
      src/Magnum/Math/Test/FunctionsBatchTest.cpp
  8. 67
      src/Magnum/Math/Test/FunctionsTest.cpp
  9. 19
      src/Magnum/Math/Test/PackingTest.cpp
  10. 4
      src/Magnum/Math/Vector.h

4
doc/changelog.dox

@ -211,6 +211,10 @@ See also:
- Construction using @ref Math::ZeroInit and @ref Math::IdentityInit is - Construction using @ref Math::ZeroInit and @ref Math::IdentityInit is
now explicit, to be consistent with @ref Math::NoInit construction and now explicit, to be consistent with @ref Math::NoInit construction and
avoid ambiguous cases 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<size>&) - Changed the way @ref Math::operator<<(Corrade::Utility::Debug&, const BoolVector<size>&)
works --- the output now has the same bit order as when constructing it works --- the output now has the same bit order as when constructing it
using binary literals using binary literals

8
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 @snippet MagnumMath.cpp matrix-vector-operations-functions-componentwise
Note that all component-wise functions in @ref Math namespace work also for Note that all component-wise functions in the @ref Math namespace work also for
scalars: scalars --- and on the special @ref Deg / @ref Rad types too.
@snippet MagnumMath.cpp matrix-vector-operations-functions-scalar @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 @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 OpenGL matrices are column-major, thus it is reasonable to have matrices in

4
doc/snippets/MagnumMath.cpp

@ -314,14 +314,18 @@ mat = Matrix3x2::fromVector(vec);
} }
{ {
Deg value;
/* [matrix-vector-operations-functions-scalar] */ /* [matrix-vector-operations-functions-scalar] */
std::pair<Int, Int> minmax = Math::minmax(24, -5); // -5, 24 std::pair<Int, Int> minmax = Math::minmax(24, -5); // -5, 24
Int a = Math::lerp(0, 360, 0.75f); // 270 Int a = Math::lerp(0, 360, 0.75f); // 270
auto b = Math::pack<UnsignedByte>(0.89f); // 226 auto b = Math::pack<UnsignedByte>(0.89f); // 226
Deg c = Math::clamp(value, 25.0_degf, 55.0_degf);
/* [matrix-vector-operations-functions-scalar] */ /* [matrix-vector-operations-functions-scalar] */
static_cast<void>(minmax); static_cast<void>(minmax);
static_cast<void>(a); static_cast<void>(a);
static_cast<void>(b); static_cast<void>(b);
static_cast<void>(c);
} }
{ {

111
src/Magnum/Math/Functions.h

@ -56,17 +56,6 @@ namespace Implementation {
template<class> struct IsBoolVectorOrScalar: std::false_type {}; template<class> struct IsBoolVectorOrScalar: std::false_type {};
template<> struct IsBoolVectorOrScalar<bool>: std::true_type {}; template<> struct IsBoolVectorOrScalar<bool>: std::true_type {};
template<std::size_t size> struct IsBoolVectorOrScalar<BoolVector<size>>: std::true_type {}; template<std::size_t size> struct IsBoolVectorOrScalar<BoolVector<size>>: std::true_type {};
template<class T> struct IsVectorOrScalar: std::is_arithmetic<T>::type {};
template<template<class> class Derived, class T> struct IsVectorOrScalar<Unit<Derived, T>>: std::true_type {};
template<class T> struct IsVectorOrScalar<Deg<T>>: std::true_type {};
template<class T> struct IsVectorOrScalar<Rad<T>>: std::true_type {};
template<std::size_t size, class T> struct IsVectorOrScalar<Vector<size, T>>: std::true_type {};
template<class T> struct IsVectorOrScalar<Vector2<T>>: std::true_type {};
template<class T> struct IsVectorOrScalar<Vector3<T>>: std::true_type {};
template<class T> struct IsVectorOrScalar<Vector4<T>>: std::true_type {};
template<class T> struct IsVectorOrScalar<Color3<T>>: std::true_type {};
template<class T> struct IsVectorOrScalar<Color4<T>>: std::true_type {};
} }
/** /**
@ -113,7 +102,8 @@ Equivalent to the following, but possibly done in a single CPU instruction:
@snippet MagnumMath.cpp div-equivalent @snippet MagnumMath.cpp div-equivalent
*/ */
template<class Integral> inline std::pair<Integral, Integral> div(Integral x, Integral y) { template<class Integral> inline std::pair<Integral, Integral> div(Integral x, Integral y) {
static_assert(std::is_integral<Integral>{}, "Math::div(): not an integral type"); static_assert(IsIntegral<Integral>::value && IsScalar<Integral>::value,
"scalar integral type expected");
const auto result = std::div(x, y); const auto result = std::div(x, y);
return {result.quot, result.rem}; return {result.quot, result.rem};
} }
@ -123,15 +113,15 @@ template<class Integral> inline std::pair<Integral, Integral> div(Integral x, In
@see @ref isNan(), @ref Constants::inf() @see @ref isNan(), @ref Constants::inf()
*/ */
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, bool>::type isInf(T value) { template<class T> inline typename std::enable_if<IsScalar<T>::value, bool>::type isInf(T value) {
return std::isinf(value); return std::isinf(UnderlyingTypeOf<T>(value));
} }
/** @overload */ /** @overload */
template<std::size_t size, class T> inline BoolVector<size> isInf(const Vector<size, T>& value) { template<std::size_t size, class T> inline BoolVector<size> isInf(const Vector<size, T>& value) {
BoolVector<size> out; BoolVector<size> out;
for(std::size_t i = 0; i != size; ++i) 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; return out;
} }
@ -141,15 +131,15 @@ template<std::size_t size, class T> inline BoolVector<size> isInf(const Vector<s
Equivalent to @cpp value != value @ce. Equivalent to @cpp value != value @ce.
@see @ref isInf(), @ref Constants::nan() @see @ref isInf(), @ref Constants::nan()
*/ */
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, bool>::type isNan(T value) { template<class T> inline typename std::enable_if<IsScalar<T>::value, bool>::type isNan(T value) {
return std::isnan(value); return std::isnan(UnderlyingTypeOf<T>(value));
} }
/** @overload */ /** @overload */
template<std::size_t size, class T> inline BoolVector<size> isNan(const Vector<size, T>& value) { template<std::size_t size, class T> inline BoolVector<size> isNan(const Vector<size, T>& value) {
BoolVector<size> out; BoolVector<size> out;
for(std::size_t i = 0; i != size; ++i) 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; return out;
} }
@ -217,27 +207,29 @@ template<class T> inline Rad<T> atan(T value) { return Rad<T>(std::atan(value));
/** /**
@{ @name Scalar/vector functions @{ @name Scalar/vector functions
These functions are overloaded for both scalar and vector types. Scalar These functions are overloaded for both scalar and vector types, including
versions function exactly as their possible STL equivalents, vector overloads @ref Deg and @ref Rad. Scalar versions function exactly as their possible STL
perform the operations component-wise. equivalents, vector overloads perform the operations component-wise.
*/ */
/** /**
@brief Integral power @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) @see @ref pow(T, T)
*/ */
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<UnsignedInt exponent, class T> constexpr T pow(T base); template<UnsignedInt exponent, class T> constexpr T pow(T base);
#else #else
template<UnsignedInt exponent, class T> constexpr typename std::enable_if<std::is_arithmetic<T>::value, T>::type pow(T base) { template<UnsignedInt exponent, class T> constexpr typename std::enable_if<IsScalar<T>::value, T>::type pow(T base) {
static_assert(IsUnitless<T>::value, "expected an unitless type");
return Implementation::Pow<exponent>::pow(base); return Implementation::Pow<exponent>::pow(base);
} }
template<UnsignedInt exponent, std::size_t size, class T> inline Vector<size, T> pow(const Vector<size, T>& base) { template<UnsignedInt exponent, std::size_t size, class T> inline Vector<size, T> pow(const Vector<size, T>& base) {
Vector<size, T> out{NoInit}; Vector<size, T> out{NoInit};
for(std::size_t i = 0; i != size; ++i) for(std::size_t i = 0; i != size; ++i)
out[i] = Implementation::Pow<exponent>::pow(base[i]); out[i] = Math::pow<exponent>(base[i]);
return out; return out;
} }
#endif #endif
@ -245,19 +237,21 @@ template<UnsignedInt exponent, std::size_t size, class T> inline Vector<size, T>
/** /**
@brief Power @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() @see @ref pow(T), @ref exp()
*/ */
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> T pow(T base, T exponent); template<class T> T pow(T base, T exponent);
#else #else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type pow(T base, T exponent) { template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type pow(T base, T exponent) {
static_assert(IsUnitless<T>::value, "expected an unitless type");
return std::pow(base, exponent); return std::pow(base, exponent);
} }
template<std::size_t size, class T> inline Vector<size, T> pow(const Vector<size, T>& base, T exponent) { template<std::size_t size, class T> inline Vector<size, T> pow(const Vector<size, T>& base, T exponent) {
Vector<size, T> out{NoInit}; Vector<size, T> out{NoInit};
for(std::size_t i = 0; i != size; ++i) 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; return out;
} }
#endif #endif
@ -326,7 +320,7 @@ template<std::size_t size, class T> inline Vector<size, T> max(const Vector<size
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline std::pair<T, T> minmax(const T& a, const T& b); template<class T> inline std::pair<T, T> minmax(const T& a, const T& b);
#else #else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, std::pair<T, T>>::type minmax(T a, T b) { template<class T> inline typename std::enable_if<IsScalar<T>::value, std::pair<T, T>>::type minmax(T a, T b) {
return a < b ? std::make_pair(a, b) : std::make_pair(b, a); return a < b ? std::make_pair(a, b) : std::make_pair(b, a);
} }
template<std::size_t size, class T> inline std::pair<Vector<size, T>, Vector<size, T>> minmax(const Vector<size, T>& a, const Vector<size, T>& b) { template<std::size_t size, class T> inline std::pair<Vector<size, T>, Vector<size, T>> minmax(const Vector<size, T>& a, const Vector<size, T>& b) {
@ -352,13 +346,13 @@ set to @p max. Equivalent to:
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class T, class U> inline T clamp(const T& value, const T& min, const T& max); template<class T, class U> inline T clamp(const T& value, const T& min, const T& max);
#else #else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type clamp(T value, T min, T max) { template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type clamp(T value, T min, T max) {
return Math::min(Math::max(value, min), max); return Math::min(Math::max(value, min), max);
} }
template<std::size_t size, class T> inline Vector<size, T> clamp(const Vector<size, T>& value, const Vector<size, T>& min, const Vector<size, T>& max) { template<std::size_t size, class T> inline Vector<size, T> clamp(const Vector<size, T>& value, const Vector<size, T>& min, const Vector<size, T>& max) {
Vector<size, T> out{NoInit}; Vector<size, T> out{NoInit};
for(std::size_t i = 0; i != size; ++i) 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; return out;
} }
#endif #endif
@ -367,7 +361,7 @@ template<std::size_t size, class T> inline Vector<size, T> clamp(const Vector<si
template<std::size_t size, class T> inline Vector<size, T> clamp(const Vector<size, T>& value, T min, T max) { template<std::size_t size, class T> inline Vector<size, T> clamp(const Vector<size, T>& value, T min, T max) {
Vector<size, T> out{NoInit}; Vector<size, T> out{NoInit};
for(std::size_t i = 0; i != size; ++i) 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; 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 #ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T sign(const T scalar); template<class T> inline T sign(const T scalar);
#else #else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type sign(const T& scalar) { template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type sign(const T& scalar) {
if(scalar > T(0)) return T(1); if(scalar > T(0)) return T(1);
if(scalar < T(0)) return T(-1); if(scalar < T(0)) return T(-1);
return T(0); return T(0);
@ -387,7 +381,7 @@ template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T
template<std::size_t size, class T> inline Vector<size, T> sign(const Vector<size, T>& a) { template<std::size_t size, class T> inline Vector<size, T> sign(const Vector<size, T>& a) {
Vector<size, T> out{NoInit}; Vector<size, T> out{NoInit};
for(std::size_t i = 0; i != size; ++i) for(std::size_t i = 0; i != size; ++i)
out[i] = sign(a[i]); out[i] = Math::sign(a[i]);
return out; return out;
} }
#endif #endif
@ -396,13 +390,13 @@ template<std::size_t size, class T> inline Vector<size, T> sign(const Vector<siz
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T abs(const T& a); template<class T> inline T abs(const T& a);
#else #else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type abs(T a) { template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type abs(T a) {
return std::abs(a); return T(std::abs(UnderlyingTypeOf<T>(a)));
} }
template<std::size_t size, class T> inline Vector<size, T> abs(const Vector<size, T>& a) { template<std::size_t size, class T> inline Vector<size, T> abs(const Vector<size, T>& a) {
Vector<size, T> out{NoInit}; Vector<size, T> out{NoInit};
for(std::size_t i = 0; i != size; ++i) for(std::size_t i = 0; i != size; ++i)
out[i] = std::abs(a[i]); out[i] = Math::abs(a[i]);
return out; return out;
} }
#endif #endif
@ -411,13 +405,13 @@ template<std::size_t size, class T> inline Vector<size, T> abs(const Vector<size
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T floor(const T& a); template<class T> inline T floor(const T& a);
#else #else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type floor(T a) { template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type floor(T a) {
return std::floor(a); return T(std::floor(UnderlyingTypeOf<T>(a)));
} }
template<std::size_t size, class T> inline Vector<size, T> floor(const Vector<size, T>& a) { template<std::size_t size, class T> inline Vector<size, T> floor(const Vector<size, T>& a) {
Vector<size, T> out{NoInit}; Vector<size, T> out{NoInit};
for(std::size_t i = 0; i != size; ++i) for(std::size_t i = 0; i != size; ++i)
out[i] = std::floor(a[i]); out[i] = Math::floor(a[i]);
return out; return out;
} }
#endif #endif
@ -426,13 +420,13 @@ template<std::size_t size, class T> inline Vector<size, T> floor(const Vector<si
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T round(const T& a); template<class T> inline T round(const T& a);
#else #else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type round(T a) { template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type round(T a) {
return std::round(a); return T(std::round(UnderlyingTypeOf<T>(a)));
} }
template<std::size_t size, class T> inline Vector<size, T> round(const Vector<size, T>& a) { template<std::size_t size, class T> inline Vector<size, T> round(const Vector<size, T>& a) {
Vector<size, T> out{NoInit}; Vector<size, T> out{NoInit};
for(std::size_t i = 0; i != size; ++i) for(std::size_t i = 0; i != size; ++i)
out[i] = std::round(a[i]); out[i] = Math::round(a[i]);
return out; return out;
} }
#endif #endif
@ -441,13 +435,13 @@ template<std::size_t size, class T> inline Vector<size, T> round(const Vector<si
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T ceil(const T& a); template<class T> inline T ceil(const T& a);
#else #else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type ceil(T a) { template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type ceil(T a) {
return std::ceil(a); return T(std::ceil(UnderlyingTypeOf<T>(a)));
} }
template<std::size_t size, class T> inline Vector<size, T> ceil(const Vector<size, T>& a) { template<std::size_t size, class T> inline Vector<size, T> ceil(const Vector<size, T>& a) {
Vector<size, T> out{NoInit}; Vector<size, T> out{NoInit};
for(std::size_t i = 0; i != size; ++i) for(std::size_t i = 0; i != size; ++i)
out[i] = std::ceil(a[i]); out[i] = Math::ceil(a[i]);
return out; return out;
} }
#endif #endif
@ -455,18 +449,20 @@ template<std::size_t size, class T> inline Vector<size, T> ceil(const Vector<siz
/** /**
@brief Square root @brief Square root
Works only on types that satisfy @ref IsUnitless.
@see @ref sqrtInverted(), @ref Vector::length(), @ref sqrt(const Dual<T>&) @see @ref sqrtInverted(), @ref Vector::length(), @ref sqrt(const Dual<T>&)
*/ */
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T sqrt(const T& a); template<class T> inline T sqrt(const T& a);
#else #else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type sqrt(T a) { template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type sqrt(T a) {
return T(std::sqrt(a)); static_assert(IsUnitless<T>::value, "expecting an unitless type");
return std::sqrt(a);
} }
template<std::size_t size, class T> inline Vector<size, T> sqrt(const Vector<size, T>& a) { template<std::size_t size, class T> inline Vector<size, T> sqrt(const Vector<size, T>& a) {
Vector<size, T> out{NoInit}; Vector<size, T> out{NoInit};
for(std::size_t i = 0; i != size; ++i) for(std::size_t i = 0; i != size; ++i)
out[i] = T(std::sqrt(a[i])); out[i] = Math::sqrt(a[i]);
return out; return out;
} }
#endif #endif
@ -474,17 +470,19 @@ template<std::size_t size, class T> inline Vector<size, T> sqrt(const Vector<siz
/** /**
@brief Inverse square root @brief Inverse square root
Works only on types that satisfy @ref IsUnitless.
@see @ref sqrt(), @ref Vector::lengthInverted() @see @ref sqrt(), @ref Vector::lengthInverted()
@m_keyword{inversesqrt(),GLSL inversesqrt(),} @m_keyword{inversesqrt(),GLSL inversesqrt(),}
*/ */
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T sqrtInverted(const T& a); template<class T> inline T sqrtInverted(const T& a);
#else #else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type sqrtInverted(T a) { template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type sqrtInverted(T a) {
static_assert(IsUnitless<T>::value, "expecting an unitless type");
return T(1)/std::sqrt(a); return T(1)/std::sqrt(a);
} }
template<std::size_t size, class T> inline Vector<size, T> sqrtInverted(const Vector<size, T>& a) { template<std::size_t size, class T> inline Vector<size, T> sqrtInverted(const Vector<size, T>& a) {
return Vector<size, T>(T(1))/sqrt(a); return Vector<size, T>(T(1))/Math::sqrt(a);
} }
#endif #endif
@ -509,7 +507,7 @@ See @ref select() for constant interpolation using the same API and
*/ */
template<class T, class U> inline template<class T, class U> inline
#ifndef DOXYGEN_GENERATING_OUTPUT #ifndef DOXYGEN_GENERATING_OUTPUT
typename std::enable_if<Implementation::IsVectorOrScalar<T>::value && !Implementation::IsBoolVectorOrScalar<U>::value, T>::type typename std::enable_if<(IsVector<T>::value || IsScalar<T>::value) && !Implementation::IsBoolVectorOrScalar<U>::value, T>::type
#else #else
T T
#endif #endif
@ -562,10 +560,10 @@ Returns interpolation phase *t*: @f[
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T lerpInverted(const T& a, const T& b, const T& lerp); template<class T> inline T lerpInverted(const T& a, const T& b, const T& lerp);
#else #else
template<class T> inline T lerpInverted(T a, T b, T lerp) { template<class T> inline UnderlyingTypeOf<typename std::enable_if<IsScalar<T>::value, T>::type> lerpInverted(T a, T b, T lerp) {
return (lerp - a)/(b - a); return (lerp - a)/(b - a);
} }
template<std::size_t size, class T, class U> inline Vector<size, T> lerpInverted(const Vector<size, T>& a, const Vector<size, T>& b, const Vector<size, T>& lerp) { template<std::size_t size, class T> inline Vector<size, UnderlyingTypeOf<T>> lerpInverted(const Vector<size, T>& a, const Vector<size, T>& b, const Vector<size, T>& lerp) {
return (lerp - a)/(b - a); return (lerp - a)/(b - a);
} }
#endif #endif
@ -593,12 +591,14 @@ template<class T, class U> constexpr T select(const T& a, const T& b, U t) {
@brief Fused multiply-add @brief Fused multiply-add
Computes and returns @f$ ab + c @f$. On some architectures might be faster than 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 #ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T fma(const T& a, const T& b, const T& c); template<class T> inline T fma(const T& a, const T& b, const T& c);
#else #else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type fma(T a, T b, T c) { template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type fma(T a, T b, T c) {
static_assert(IsUnitless<T>::value, "expecting an unitless type");
/* On Emscripten it works with -O2 but not with -O1 (function not defined). /* 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. */ I guess that's only because -O2 optimizes it out, so disabling it there. */
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
@ -608,6 +608,7 @@ template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T
#endif #endif
} }
template<std::size_t size, class T> inline Vector<size, T> fma(const Vector<size, T>& a, const Vector<size, T>& b, const Vector<size, T>& c) { template<std::size_t size, class T> inline Vector<size, T> fma(const Vector<size, T>& a, const Vector<size, T>& b, const Vector<size, T>& c) {
static_assert(IsUnitless<T>::value, "expecting an unitless type");
return a*b + c; return a*b + c;
} }
#endif #endif

6
src/Magnum/Math/FunctionsBatch.h

@ -47,7 +47,7 @@ template<class T> inline T min(Corrade::Containers::ArrayView<const T> range) {
T out(range[0]); T out(range[0]);
for(std::size_t i = 1; i != range.size(); ++i) for(std::size_t i = 1; i != range.size(); ++i)
out = min(out, range[i]); out = Math::min(out, range[i]);
return out; return out;
} }
@ -71,7 +71,7 @@ template<class T> inline T max(Corrade::Containers::ArrayView<const T> range) {
T out(range[0]); T out(range[0]);
for(std::size_t i = 1; i != range.size(); ++i) for(std::size_t i = 1; i != range.size(); ++i)
out = max(out, range[i]); out = Math::max(out, range[i]);
return out; return out;
} }
@ -86,7 +86,7 @@ template<class T, std::size_t size> inline T max(const T(&array)[size]) {
} }
namespace Implementation { namespace Implementation {
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, void>::type minmax(T& min, T& max, T value) { template<class T> inline typename std::enable_if<IsScalar<T>::value, void>::type minmax(T& min, T& max, T value) {
if(value < min) if(value < min)
min = value; min = value;
else if(value > max) else if(value > max)

22
src/Magnum/Math/Packing.h

@ -73,20 +73,20 @@ representation to use. Example usage:
*/ */
template<class FloatingPoint, class Integral, UnsignedInt bits> inline FloatingPoint unpack(const Integral& value); template<class FloatingPoint, class Integral, UnsignedInt bits> inline FloatingPoint unpack(const Integral& value);
#else #else
template<class FloatingPoint, class Integral, UnsignedInt bits = sizeof(Integral)*8> inline typename std::enable_if<std::is_arithmetic<Integral>::value && std::is_unsigned<Integral>::value, FloatingPoint>::type unpack(const Integral& value) { template<class FloatingPoint, class Integral, UnsignedInt bits = sizeof(Integral)*8> inline typename std::enable_if<IsScalar<Integral>::value && std::is_unsigned<Integral>::value, FloatingPoint>::type unpack(const Integral& value) {
static_assert(std::is_floating_point<FloatingPoint>::value && std::is_integral<Integral>::value, static_assert(IsFloatingPoint<FloatingPoint>::value && IsIntegral<Integral>::value,
"unpacking must be done from integral to floating-point type"); "unpacking must be done from integral to floating-point type");
static_assert(bits <= sizeof(Integral)*8, static_assert(bits <= sizeof(Integral)*8,
"bit count larger than size of the integral type"); "bit count larger than size of the integral type");
return value/FloatingPoint(Implementation::bitMax<Integral, bits>()); return FloatingPoint(value/UnderlyingTypeOf<FloatingPoint>(Implementation::bitMax<Integral, bits>()));
} }
template<class FloatingPoint, class Integral, UnsignedInt bits = sizeof(Integral)*8> inline typename std::enable_if<std::is_arithmetic<Integral>::value && std::is_signed<Integral>::value, FloatingPoint>::type unpack(const Integral& value) { template<class FloatingPoint, class Integral, UnsignedInt bits = sizeof(Integral)*8> inline typename std::enable_if<IsScalar<Integral>::value && std::is_signed<Integral>::value, FloatingPoint>::type unpack(const Integral& value) {
static_assert(std::is_floating_point<FloatingPoint>::value && std::is_integral<Integral>::value, static_assert(IsFloatingPoint<FloatingPoint>::value && IsIntegral<Integral>::value,
"unpacking must be done from integral to floating-point type"); "unpacking must be done from integral to floating-point type");
static_assert(bits <= sizeof(Integral)*8, static_assert(bits <= sizeof(Integral)*8,
"bit count larger than size of the integral type"); "bit count larger than size of the integral type");
/* According to https://www.opengl.org/registry/specs/EXT/texture_snorm.txt */ /* According to https://www.opengl.org/registry/specs/EXT/texture_snorm.txt */
return Math::max(value/FloatingPoint(Implementation::bitMax<Integral, bits>()), FloatingPoint(-1.0)); return FloatingPoint(Math::max(value/UnderlyingTypeOf<FloatingPoint>(Implementation::bitMax<Integral, bits>()), UnderlyingTypeOf<FloatingPoint>(-1.0)));
} }
template<class FloatingPoint, std::size_t size, class Integral, UnsignedInt bits = sizeof(Integral)*8> FloatingPoint unpack(const Vector<size, Integral>& value) { template<class FloatingPoint, std::size_t size, class Integral, UnsignedInt bits = sizeof(Integral)*8> FloatingPoint unpack(const Vector<size, Integral>& value) {
static_assert(FloatingPoint::Size == size, static_assert(FloatingPoint::Size == size,
@ -102,7 +102,7 @@ template<class FloatingPoint, std::size_t size, class Integral, UnsignedInt bits
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class FloatingPoint, UnsignedInt bits, class Integral> inline FloatingPoint unpack(const Integral& value); template<class FloatingPoint, UnsignedInt bits, class Integral> inline FloatingPoint unpack(const Integral& value);
#else #else
template<class FloatingPoint, UnsignedInt bits, class Integral> inline typename std::enable_if<std::is_arithmetic<Integral>::value, FloatingPoint>::type unpack(const Integral& value) { template<class FloatingPoint, UnsignedInt bits, class Integral> inline typename std::enable_if<IsScalar<Integral>::value, FloatingPoint>::type unpack(const Integral& value) {
return unpack<FloatingPoint, Integral, bits>(value); return unpack<FloatingPoint, Integral, bits>(value);
} }
template<class FloatingPoint, UnsignedInt bits, std::size_t size, class Integral> inline FloatingPoint unpack(const Vector<size, Integral>& value) { template<class FloatingPoint, UnsignedInt bits, std::size_t size, class Integral> inline FloatingPoint unpack(const Vector<size, Integral>& value) {
@ -130,12 +130,12 @@ given *signed* integral type.
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class Integral, class FloatingPoint> inline Integral pack(const FloatingPoint& value); template<class Integral, class FloatingPoint> inline Integral pack(const FloatingPoint& value);
#else #else
template<class Integral, class FloatingPoint, UnsignedInt bits = sizeof(Integral)*8> inline typename std::enable_if<std::is_arithmetic<FloatingPoint>::value, Integral>::type pack(FloatingPoint value) { template<class Integral, class FloatingPoint, UnsignedInt bits = sizeof(Integral)*8> inline typename std::enable_if<IsScalar<FloatingPoint>::value, Integral>::type pack(FloatingPoint value) {
static_assert(std::is_floating_point<FloatingPoint>::value && std::is_integral<Integral>::value, static_assert(IsFloatingPoint<FloatingPoint>::value && IsIntegral<Integral>::value,
"packing must be done from floating-point to integral type"); "packing must be done from floating-point to integral type");
static_assert(bits <= sizeof(Integral)*8, static_assert(bits <= sizeof(Integral)*8,
"bit count larger than size of the integral type"); "bit count larger than size of the integral type");
return Integral(round(value*Implementation::bitMax<Integral, bits>())); return Integral(round(UnderlyingTypeOf<FloatingPoint>(value)*Implementation::bitMax<Integral, bits>()));
} }
template<class Integral, std::size_t size, class FloatingPoint, UnsignedInt bits = sizeof(typename Integral::Type)*8> Integral pack(const Vector<size, FloatingPoint>& value) { template<class Integral, std::size_t size, class FloatingPoint, UnsignedInt bits = sizeof(typename Integral::Type)*8> Integral pack(const Vector<size, FloatingPoint>& value) {
static_assert(Integral::Size == size, static_assert(Integral::Size == size,
@ -158,7 +158,7 @@ representation to use. Example usage:
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class Integral, UnsignedInt bits, class FloatingPoint> inline Integral pack(FloatingPoint value); template<class Integral, UnsignedInt bits, class FloatingPoint> inline Integral pack(FloatingPoint value);
#else #else
template<class Integral, UnsignedInt bits, class FloatingPoint> inline typename std::enable_if<std::is_arithmetic<FloatingPoint>::value, Integral>::type pack(FloatingPoint value) { template<class Integral, UnsignedInt bits, class FloatingPoint> inline typename std::enable_if<IsScalar<FloatingPoint>::value, Integral>::type pack(FloatingPoint value) {
return pack<Integral, FloatingPoint, bits>(value); return pack<Integral, FloatingPoint, bits>(value);
} }
template<class Integral, UnsignedInt bits, std::size_t size, class FloatingPoint> inline Integral pack(const Vector<size, FloatingPoint>& value) { template<class Integral, UnsignedInt bits, std::size_t size, class FloatingPoint> inline Integral pack(const Vector<size, FloatingPoint>& value) {

11
src/Magnum/Math/Test/FunctionsBatchTest.cpp

@ -38,6 +38,8 @@ struct FunctionsBatchTest: Corrade::TestSuite::Tester {
void minmaxList(); void minmaxList();
}; };
using namespace Literals;
typedef Math::Vector2<Float> Vector2; typedef Math::Vector2<Float> Vector2;
typedef Math::Vector3<Int> Vector3i; typedef Math::Vector3<Int> Vector3i;
@ -57,6 +59,9 @@ void FunctionsBatchTest::minList() {
const Int array[]{5, -2, 9}; const Int array[]{5, -2, 9};
CORRADE_COMPARE(Math::min(array), -2); 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() { void FunctionsBatchTest::maxList() {
@ -69,6 +74,9 @@ void FunctionsBatchTest::maxList() {
const Int array[]{5, -2, 9}; const Int array[]{5, -2, 9};
CORRADE_COMPARE(Math::max(array), 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() { void FunctionsBatchTest::minmaxList() {
@ -90,6 +98,9 @@ void FunctionsBatchTest::minmaxList() {
const Float array[]{-1.0f, 2.0f, -3.0f}; const Float array[]{-1.0f, 2.0f, -3.0f};
CORRADE_COMPARE(Math::minmax(array), expected); 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));
} }
}}}} }}}}

67
src/Magnum/Math/Test/FunctionsTest.cpp

@ -71,6 +71,8 @@ struct FunctionsTest: Corrade::TestSuite::Tester {
void trigonometricWithBase(); void trigonometricWithBase();
}; };
using namespace Literals;
typedef Math::Constants<Float> Constants; typedef Math::Constants<Float> Constants;
typedef Math::Deg<Float> Deg; typedef Math::Deg<Float> Deg;
typedef Math::Rad<Float> Rad; typedef Math::Rad<Float> Rad;
@ -130,23 +132,33 @@ void FunctionsTest::powIntegral() {
CORRADE_COMPARE(a, 125); CORRADE_COMPARE(a, 125);
CORRADE_COMPARE(Math::pow<2>(Vector3{2.0f, -3.0f, 1.5f}), (Vector3{4.0f, 9.0f, 2.25f})); 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() { void FunctionsTest::pow() {
CORRADE_COMPARE(Math::pow(2.0f, 0.5f), 1.414213562f); 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})); 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() { void FunctionsTest::min() {
CORRADE_COMPARE(Math::min(5, 9), 5); 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), Vector3i(9, -5, 18)), Vector3i(5, -5, 2));
CORRADE_COMPARE(Math::min(Vector3i{5, -3, 2}, 1), (Vector3i{1, -3, 1})); 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() { void FunctionsTest::max() {
CORRADE_COMPARE(Math::max(5, 9), 9); 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), Vector3i(9, -5, 18)), Vector3i(9, -3, 18));
CORRADE_COMPARE(Math::max(Vector3i{5, -3, 2}, 3), (Vector3i{5, 3, 3})); 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() { void FunctionsTest::minmax() {
@ -159,6 +171,9 @@ void FunctionsTest::minmax() {
const std::pair<Vector3, Vector3> expectedVector{{5.0f, -4.0f, 1.0f}, {7.0f, -3.0f, 1.0f}}; const std::pair<Vector3, Vector3> expectedVector{{5.0f, -4.0f, 1.0f}, {7.0f, -3.0f, 1.0f}};
CORRADE_COMPARE_AS(Math::minmax(a, b), expectedVector, std::pair<Vector3, Vector3>); CORRADE_COMPARE_AS(Math::minmax(a, b), expectedVector, std::pair<Vector3, Vector3>);
CORRADE_COMPARE_AS(Math::minmax(b, a), expectedVector, std::pair<Vector3, Vector3>); CORRADE_COMPARE_AS(Math::minmax(b, a), expectedVector, std::pair<Vector3, Vector3>);
/* Wrapped types */
CORRADE_COMPARE(Math::minmax(4.0_degf, 5.0_degf), std::make_pair(4.0_degf, 5.0_degf));
} }
void FunctionsTest::clamp() { void FunctionsTest::clamp() {
@ -173,6 +188,9 @@ void FunctionsTest::clamp() {
Vector3(0.5f, 2.0f, 5.0f)); 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)); 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() { void FunctionsTest::nanPropagation() {
@ -194,17 +212,26 @@ void FunctionsTest::sign() {
CORRADE_COMPARE(Math::sign(0.0f), 0.0f); CORRADE_COMPARE(Math::sign(0.0f), 0.0f);
CORRADE_COMPARE(Math::sign(-3.7), -1.0); CORRADE_COMPARE(Math::sign(-3.7), -1.0);
CORRADE_COMPARE(Math::sign(Vector3i(0, -3, 2)), Vector3i(0, -1, 1)); 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() { void FunctionsTest::abs() {
CORRADE_COMPARE(Math::abs(-5), 5); CORRADE_COMPARE(Math::abs(-5), 5);
CORRADE_COMPARE(Math::abs(5), 5); CORRADE_COMPARE(Math::abs(5), 5);
CORRADE_COMPARE(Math::abs(Vector3i(5, -3, 2)), Vector3i(5, 3, 2)); 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() { void FunctionsTest::floor() {
CORRADE_COMPARE(Math::floor(0.7f), 0.0f); 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)); 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() { void FunctionsTest::round() {
@ -219,21 +246,31 @@ void FunctionsTest::round() {
CORRADE_COMPARE(Math::round(1.3f), 1.0f); CORRADE_COMPARE(Math::round(1.3f), 1.0f);
CORRADE_COMPARE(Math::round(1.5f), 2.0f); CORRADE_COMPARE(Math::round(1.5f), 2.0f);
CORRADE_COMPARE(Math::round(2.0f), 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() { void FunctionsTest::ceil() {
CORRADE_COMPARE(Math::ceil(2.3f), 3.0f); 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)); 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() { void FunctionsTest::sqrt() {
CORRADE_COMPARE(Math::sqrt(16), 4); CORRADE_COMPARE(Math::sqrt(16), 4);
CORRADE_COMPARE(Math::sqrt(Vector3i(256, 1, 0)), Vector3i(16, 1, 0)); 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() { void FunctionsTest::sqrtInverted() {
CORRADE_COMPARE(Math::sqrtInverted(16.0f), 0.25f); 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)); 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() { void FunctionsTest::lerp() {
@ -253,6 +290,9 @@ void FunctionsTest::lerp() {
/* Vector as interpolation phase */ /* Vector as interpolation phase */
CORRADE_COMPARE(Math::lerp(a, b, Vector3(0.25f, 0.5f, 0.75f)), Vector3(0.0f, 0.0f, 9.0f)); 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() { void FunctionsTest::lerpBool() {
@ -263,6 +303,9 @@ void FunctionsTest::lerpBool() {
/* Vector interpolation phase */ /* 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(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}); 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() { void FunctionsTest::lerpInverted() {
@ -273,6 +316,9 @@ void FunctionsTest::lerpInverted() {
Vector3 a(-1.0f, 2.0f, 3.0f); Vector3 a(-1.0f, 2.0f, 3.0f);
Vector3 b(3.0f, -2.0f, 11.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)); 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() { void FunctionsTest::select() {
@ -290,12 +336,18 @@ void FunctionsTest::select() {
/* Vector as interpolation phase */ /* Vector as interpolation phase */
CORRADE_COMPARE(Math::select(a, b, Vector3(0.25f, 1.5f, 1.0f)), Vector3(-1.0f, -2.0f, 11.0f)); 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() { void FunctionsTest::selectBool() {
CORRADE_COMPARE(Math::select(true, false, 0.5f), true); 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}, 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}); 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() { void FunctionsTest::fma() {
@ -304,6 +356,9 @@ void FunctionsTest::fma() {
Vector3( 3.0f, 2.0f, -1.0f), Vector3( 3.0f, 2.0f, -1.0f),
Vector3(0.75f, 0.25f, 0.1f)), Vector3(0.75f, 0.25f, 0.1f)),
Vector3(6.75f, 3.25f, -0.4f)); 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() { void FunctionsTest::logIntegral() {
@ -317,10 +372,14 @@ void FunctionsTest::log2() {
void FunctionsTest::log() { void FunctionsTest::log() {
CORRADE_COMPARE(Math::log(2.0f), 0.693147f); CORRADE_COMPARE(Math::log(2.0f), 0.693147f);
/* Not testing wrapped types -- what unit should have degrees squared? */
} }
void FunctionsTest::exp() { void FunctionsTest::exp() {
CORRADE_COMPARE(Math::exp(0.693147f), 2.0f); CORRADE_COMPARE(Math::exp(0.693147f), 2.0f);
/* Not testing wrapped types -- what unit should have degrees squared? */
} }
void FunctionsTest::div() { void FunctionsTest::div() {
@ -334,6 +393,10 @@ void FunctionsTest::isInf() {
CORRADE_VERIFY(Math::isInf(-Constants::inf())); CORRADE_VERIFY(Math::isInf(-Constants::inf()));
CORRADE_VERIFY(!Math::isInf(Constants::nan())); CORRADE_VERIFY(!Math::isInf(Constants::nan()));
CORRADE_VERIFY(!Math::isInf(5.3f)); 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() { void FunctionsTest::isInfVector() {
@ -346,6 +409,10 @@ void FunctionsTest::isNan() {
CORRADE_VERIFY(!Math::isNan(-Constants::inf())); CORRADE_VERIFY(!Math::isNan(-Constants::inf()));
CORRADE_VERIFY(Math::isNan(Constants::nan())); CORRADE_VERIFY(Math::isNan(Constants::nan()));
CORRADE_VERIFY(!Math::isNan(5.3f)); 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() { void FunctionsTest::isNanfVector() {

19
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 */ because there's involved comparison and benchmarks to ground truth */
}; };
using namespace Literals;
typedef Math::Rad<Float> Rad;
typedef Math::Vector3<Float> Vector3; typedef Math::Vector3<Float> Vector3;
typedef Math::Vector3<UnsignedByte> Vector3ub; typedef Math::Vector3<UnsignedByte> Vector3ub;
typedef Math::Vector3<Byte> Vector3b; typedef Math::Vector3<Byte> Vector3b;
@ -116,6 +119,10 @@ void PackingTest::unpackUnsigned() {
/* Vector overloads */ /* Vector overloads */
CORRADE_COMPARE(Math::unpack<Vector3>(Vector3ub(0, 127, 255)), Vector3(0.0f, 0.498039f, 1.0f)); CORRADE_COMPARE(Math::unpack<Vector3>(Vector3ub(0, 127, 255)), Vector3(0.0f, 0.498039f, 1.0f));
CORRADE_COMPARE((Math::unpack<Vector3, 6>(Vector3ub(0, 31, 63))), Vector3(0.0f, 0.492063f, 1.0f)); CORRADE_COMPARE((Math::unpack<Vector3, 6>(Vector3ub(0, 31, 63))), Vector3(0.0f, 0.492063f, 1.0f));
/* Wrapped types */
CORRADE_COMPARE((Math::unpack<Rad, UnsignedShort>(8191)), 0.124987_radf);
CORRADE_COMPARE((Math::unpack<Rad, 14>(8191u)), 0.499969_radf);
} }
void PackingTest::unpackSigned() { void PackingTest::unpackSigned() {
@ -149,6 +156,10 @@ void PackingTest::unpackSigned() {
/* Vector overloads */ /* Vector overloads */
CORRADE_COMPARE(Math::unpack<Vector3>(Vector3b(0, -127, 64)), Vector3(0.0f, -1.0f, 0.503937f)); CORRADE_COMPARE(Math::unpack<Vector3>(Vector3b(0, -127, 64)), Vector3(0.0f, -1.0f, 0.503937f));
CORRADE_COMPARE((Math::unpack<Vector3, 6>(Vector3b(0, -31, 16))), Vector3(0.0f, -1.0f, 0.516129f)); CORRADE_COMPARE((Math::unpack<Vector3, 6>(Vector3b(0, -31, 16))), Vector3(0.0f, -1.0f, 0.516129f));
/* Wrapped types */
CORRADE_COMPARE((Math::unpack<Rad, Short>(8191)), 0.249977_radf);
CORRADE_COMPARE((Math::unpack<Rad, 14>(8191)), 1.0_radf);
} }
void PackingTest::packUnsigned() { void PackingTest::packUnsigned() {
@ -190,6 +201,10 @@ void PackingTest::packUnsigned() {
/* Vector overloads */ /* Vector overloads */
CORRADE_COMPARE(Math::pack<Vector3ub>(Vector3(0.0f, 0.5f, 1.0f)), Vector3ub(0, 128, 255)); CORRADE_COMPARE(Math::pack<Vector3ub>(Vector3(0.0f, 0.5f, 1.0f)), Vector3ub(0, 128, 255));
CORRADE_COMPARE((Math::pack<Vector3ub, 6>(Vector3(0.0f, 0.5f, 1.0f))), Vector3ub(0, 32, 63)); CORRADE_COMPARE((Math::pack<Vector3ub, 6>(Vector3(0.0f, 0.5f, 1.0f))), Vector3ub(0, 32, 63));
/* Wrapped types */
CORRADE_COMPARE((Math::pack<UnsignedShort>(0.5_degf)), 32768);
CORRADE_COMPARE((Math::pack<UnsignedShort, 14>(0.5_degf)), 8192);
} }
void PackingTest::packSigned() { void PackingTest::packSigned() {
@ -233,6 +248,10 @@ void PackingTest::packSigned() {
/* Vector overloads */ /* Vector overloads */
CORRADE_COMPARE(Math::pack<Vector3b>(Vector3(0.0f, -1.0f, 0.5f)), Vector3b(0, -127, 64)); CORRADE_COMPARE(Math::pack<Vector3b>(Vector3(0.0f, -1.0f, 0.5f)), Vector3b(0, -127, 64));
CORRADE_COMPARE((Math::pack<Vector3b, 6>(Vector3(0.0f, -1.0f, 0.5f))), Vector3b(0, -31, 16)); CORRADE_COMPARE((Math::pack<Vector3b, 6>(Vector3(0.0f, -1.0f, 0.5f))), Vector3b(0, -31, 16));
/* Wrapped types */
CORRADE_COMPARE((Math::pack<Short>(-0.5_degf)), -16384);
CORRADE_COMPARE((Math::pack<Short, 14>(-0.5_degf)), -4096);
} }
void PackingTest::reunpackUnsigned() { void PackingTest::reunpackUnsigned() {

4
src/Magnum/Math/Vector.h

@ -45,11 +45,11 @@ namespace Magnum { namespace Math {
#ifndef DOXYGEN_GENERATING_OUTPUT #ifndef DOXYGEN_GENERATING_OUTPUT
/* Documented in Functions.h, defined here because Vector needs them */ /* Documented in Functions.h, defined here because Vector needs them */
template<class T> constexpr typename std::enable_if<std::is_arithmetic<T>::value, T>::type min(T a, T b) { template<class T> constexpr typename std::enable_if<IsScalar<T>::value, T>::type min(T a, T b) {
return b < a ? b : a; return b < a ? b : a;
} }
template<class T> constexpr typename std::enable_if<std::is_arithmetic<T>::value, T>::type max(T a, T b) { template<class T> constexpr typename std::enable_if<IsScalar<T>::value, T>::type max(T a, T b) {
return a < b ? b : a; return a < b ? b : a;
} }
#endif #endif

Loading…
Cancel
Save