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
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<size>&)
works --- the output now has the same bit order as when constructing it
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
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

4
doc/snippets/MagnumMath.cpp

@ -314,14 +314,18 @@ mat = Matrix3x2::fromVector(vec);
}
{
Deg value;
/* [matrix-vector-operations-functions-scalar] */
std::pair<Int, Int> minmax = Math::minmax(24, -5); // -5, 24
Int a = Math::lerp(0, 360, 0.75f); // 270
auto b = Math::pack<UnsignedByte>(0.89f); // 226
Deg c = Math::clamp(value, 25.0_degf, 55.0_degf);
/* [matrix-vector-operations-functions-scalar] */
static_cast<void>(minmax);
static_cast<void>(a);
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<> struct IsBoolVectorOrScalar<bool>: 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
*/
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);
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()
*/
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, bool>::type isInf(T value) {
return std::isinf(value);
template<class T> inline typename std::enable_if<IsScalar<T>::value, bool>::type isInf(T value) {
return std::isinf(UnderlyingTypeOf<T>(value));
}
/** @overload */
template<std::size_t size, class T> inline BoolVector<size> isInf(const Vector<size, T>& value) {
BoolVector<size> 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<std::size_t size, class T> inline BoolVector<size> isInf(const Vector<s
Equivalent to @cpp value != value @ce.
@see @ref isInf(), @ref Constants::nan()
*/
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, bool>::type isNan(T value) {
return std::isnan(value);
template<class T> inline typename std::enable_if<IsScalar<T>::value, bool>::type isNan(T value) {
return std::isnan(UnderlyingTypeOf<T>(value));
}
/** @overload */
template<std::size_t size, class T> inline BoolVector<size> isNan(const Vector<size, T>& value) {
BoolVector<size> 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<class T> inline Rad<T> atan(T value) { return Rad<T>(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<UnsignedInt exponent, class T> constexpr T pow(T base);
#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);
}
template<UnsignedInt exponent, std::size_t size, class T> inline Vector<size, T> pow(const Vector<size, T>& base) {
Vector<size, T> out{NoInit};
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;
}
#endif
@ -245,19 +237,21 @@ template<UnsignedInt exponent, std::size_t size, class T> inline Vector<size, T>
/**
@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<class T> T pow(T base, T exponent);
#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);
}
template<std::size_t size, class T> inline Vector<size, T> pow(const Vector<size, T>& base, T exponent) {
Vector<size, T> 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<std::size_t size, class T> inline Vector<size, T> max(const Vector<size
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline std::pair<T, T> minmax(const T& a, const T& b);
#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);
}
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
template<class T, class U> inline T clamp(const T& value, const T& min, const T& max);
#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);
}
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};
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<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) {
Vector<size, T> 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<class T> inline T sign(const T scalar);
#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);
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) {
Vector<size, T> 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<std::size_t size, class T> inline Vector<size, T> sign(const Vector<siz
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T abs(const T& a);
#else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type abs(T a) {
return std::abs(a);
template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type abs(T 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) {
Vector<size, T> 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<std::size_t size, class T> inline Vector<size, T> abs(const Vector<size
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T floor(const T& a);
#else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type floor(T a) {
return std::floor(a);
template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type floor(T 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) {
Vector<size, T> 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<std::size_t size, class T> inline Vector<size, T> floor(const Vector<si
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T round(const T& a);
#else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type round(T a) {
return std::round(a);
template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type round(T 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) {
Vector<size, T> 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<std::size_t size, class T> inline Vector<size, T> round(const Vector<si
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T ceil(const T& a);
#else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type ceil(T a) {
return std::ceil(a);
template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type ceil(T 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) {
Vector<size, T> 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<std::size_t size, class T> inline Vector<size, T> ceil(const Vector<siz
/**
@brief Square root
Works only on types that satisfy @ref IsUnitless.
@see @ref sqrtInverted(), @ref Vector::length(), @ref sqrt(const Dual<T>&)
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T sqrt(const T& a);
#else
template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T>::type sqrt(T a) {
return T(std::sqrt(a));
template<class T> inline typename std::enable_if<IsScalar<T>::value, T>::type sqrt(T 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) {
Vector<size, T> 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<std::size_t size, class T> inline Vector<size, T> sqrt(const Vector<siz
/**
@brief Inverse square root
Works only on types that satisfy @ref IsUnitless.
@see @ref sqrt(), @ref Vector::lengthInverted()
@m_keyword{inversesqrt(),GLSL inversesqrt(),}
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T sqrtInverted(const T& a);
#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);
}
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
@ -509,7 +507,7 @@ See @ref select() for constant interpolation using the same API and
*/
template<class T, class U> inline
#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
T
#endif
@ -562,10 +560,10 @@ Returns interpolation phase *t*: @f[
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> inline T lerpInverted(const T& a, const T& b, const T& lerp);
#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);
}
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);
}
#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
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<class T> inline T fma(const T& a, const T& b, const T& c);
#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).
I guess that's only because -O2 optimizes it out, so disabling it there. */
#ifndef CORRADE_TARGET_EMSCRIPTEN
@ -608,6 +608,7 @@ template<class T> inline typename std::enable_if<std::is_arithmetic<T>::value, T
#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) {
static_assert(IsUnitless<T>::value, "expecting an unitless type");
return a*b + c;
}
#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]);
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<class T> inline T max(Corrade::Containers::ArrayView<const T> 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<class T, std::size_t size> inline T max(const T(&array)[size]) {
}
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)
min = value;
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);
#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) {
static_assert(std::is_floating_point<FloatingPoint>::value && std::is_integral<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(IsFloatingPoint<FloatingPoint>::value && IsIntegral<Integral>::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<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) {
static_assert(std::is_floating_point<FloatingPoint>::value && std::is_integral<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(IsFloatingPoint<FloatingPoint>::value && IsIntegral<Integral>::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<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) {
static_assert(FloatingPoint::Size == size,
@ -102,7 +102,7 @@ template<class FloatingPoint, std::size_t size, class Integral, UnsignedInt bits
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class FloatingPoint, UnsignedInt bits, class Integral> inline FloatingPoint unpack(const Integral& value);
#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);
}
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
template<class Integral, class FloatingPoint> inline Integral pack(const FloatingPoint& value);
#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) {
static_assert(std::is_floating_point<FloatingPoint>::value && std::is_integral<Integral>::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(IsFloatingPoint<FloatingPoint>::value && IsIntegral<Integral>::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<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) {
static_assert(Integral::Size == size,
@ -158,7 +158,7 @@ representation to use. Example usage:
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class Integral, UnsignedInt bits, class FloatingPoint> inline Integral pack(FloatingPoint value);
#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);
}
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();
};
using namespace Literals;
typedef Math::Vector2<Float> Vector2;
typedef Math::Vector3<Int> 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));
}
}}}}

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

@ -71,6 +71,8 @@ struct FunctionsTest: Corrade::TestSuite::Tester {
void trigonometricWithBase();
};
using namespace Literals;
typedef Math::Constants<Float> Constants;
typedef Math::Deg<Float> Deg;
typedef Math::Rad<Float> 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<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(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() {
@ -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() {

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 */
};
using namespace Literals;
typedef Math::Rad<Float> Rad;
typedef Math::Vector3<Float> Vector3;
typedef Math::Vector3<UnsignedByte> Vector3ub;
typedef Math::Vector3<Byte> Vector3b;
@ -116,6 +119,10 @@ void PackingTest::unpackUnsigned() {
/* Vector overloads */
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));
/* 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() {
@ -149,6 +156,10 @@ void PackingTest::unpackSigned() {
/* Vector overloads */
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));
/* 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() {
@ -190,6 +201,10 @@ void PackingTest::packUnsigned() {
/* Vector overloads */
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));
/* Wrapped types */
CORRADE_COMPARE((Math::pack<UnsignedShort>(0.5_degf)), 32768);
CORRADE_COMPARE((Math::pack<UnsignedShort, 14>(0.5_degf)), 8192);
}
void PackingTest::packSigned() {
@ -233,6 +248,10 @@ void PackingTest::packSigned() {
/* Vector overloads */
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));
/* Wrapped types */
CORRADE_COMPARE((Math::pack<Short>(-0.5_degf)), -16384);
CORRADE_COMPARE((Math::pack<Short, 14>(-0.5_degf)), -4096);
}
void PackingTest::reunpackUnsigned() {

4
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<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;
}
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;
}
#endif

Loading…
Cancel
Save