Browse Source

Math: make batch functions accept strided array views.

The obvious improvement.
pull/342/head
Vladimír Vondruš 7 years ago
parent
commit
bd4ed9ae0e
  1. 23
      doc/changelog.dox
  2. 13
      src/Magnum/Math/Functions.h
  3. 44
      src/Magnum/Math/FunctionsBatch.h

23
doc/changelog.dox

@ -216,12 +216,15 @@ See also:
special types such as @ref Deg or @ref Rad --- the only exception is 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 power functions such as @ref Math::sqrt() or @ref Math::log(), as the
resulting unit can't be represented. Those accept only unitless types. resulting unit can't be represented. Those accept only unitless types.
- Batch @ref Math::min(Containers::ArrayView<const T>), - All batch functions in @ref Magnum/Math/FunctionsBatch.h now accept
@ref Math::max(Containers::ArrayView<const T>) and @ref Corrade::Containers::StridedArrayView instead of a dense array view to
@ref Math::minmax(Containers::ArrayView<const T>) functions now ignore NaNs make them usable in more contexts
in the data, if possible. Use the batch - Batch @ref Math::min(Containers::StridedArrayView1D<const T>),
@ref Math::isNan(Containers::ArrayView<const T>) to detect presence of NaN @ref Math::max(Containers::StridedArrayView1D<const T>) and
values if needed. @ref Math::minmax(Containers::StridedArrayView1D<const T>) functions now
ignore NaNs in the data, if possible. Use the batch
@ref Math::isNan(Containers::StridedArrayView1D<const T>) to detect
presence of NaN values if needed.
- 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
@ -373,10 +376,10 @@ See also:
- @cpp Math::Frustum::planes() @ce are deprecated due to redundancy, use - @cpp Math::Frustum::planes() @ce are deprecated due to redundancy, use
either @ref Math::Frustum::operator[](), @ref Math::Frustum::data() or either @ref Math::Frustum::operator[](), @ref Math::Frustum::data() or
range access using @ref Math::Frustum::begin() / @ref Math::Frustum::end() range access using @ref Math::Frustum::begin() / @ref Math::Frustum::end()
- Batch @ref Math::min(Containers::ArrayView<const T>), - Batch @ref Math::min(Containers::StridedArrayView1D<const T>),
@ref Math::max(Containers::ArrayView<const T>) and @ref Math::max(Containers::StridedArrayView1D<const T>) and
@ref Math::minmax(Containers::ArrayView<const T>) are moved to a new @ref Math::minmax(Containers::StridedArrayView1D<const T>) are moved to a
@ref Magnum/Math/FunctionsBatch.h header in order to speed up compile new @ref Magnum/Math/FunctionsBatch.h header in order to speed up compile
times. This header is included from @ref Magnum/Math/FunctionsBatch.h when times. This header is included from @ref Magnum/Math/FunctionsBatch.h when
building with @ref MAGNUM_BUILD_DEPRECATED enabled, include it explicitly building with @ref MAGNUM_BUILD_DEPRECATED enabled, include it explicitly
to ensure forward compatibility to ensure forward compatibility

13
src/Magnum/Math/Functions.h

@ -161,7 +161,7 @@ the operations component-wise.
@brief If given number is a positive or negative infinity @brief If given number is a positive or negative infinity
@see @ref isNan(), @ref Constants::inf(), @see @ref isNan(), @ref Constants::inf(),
@ref isInf(Corrade::Containers::ArrayView<const T>) @ref isInf(Corrade::Containers::StridedArrayView1D<const T>)
*/ */
template<class T> inline typename std::enable_if<IsScalar<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(UnderlyingTypeOf<T>(value)); return std::isinf(UnderlyingTypeOf<T>(value));
@ -180,7 +180,7 @@ 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(),
@ref isNan(Corrade::Containers::ArrayView<const T>) @ref isNan(Corrade::Containers::StridedArrayView1D<const T>)
*/ */
template<class T> inline typename std::enable_if<IsScalar<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(UnderlyingTypeOf<T>(value)); return std::isnan(UnderlyingTypeOf<T>(value));
@ -199,7 +199,8 @@ template<std::size_t size, class T> inline BoolVector<size> isNan(const Vector<s
<em>NaN</em>s passed in the @p value parameter are propagated. <em>NaN</em>s passed in the @p value parameter are propagated.
@see @ref max(), @ref minmax(), @ref clamp(), @see @ref max(), @ref minmax(), @ref clamp(),
@ref min(Corrade::Containers::ArrayView<const T>), @ref Vector::min() @ref min(Corrade::Containers::StridedArrayView1D<const T>),
@ref Vector::min()
*/ */
/* defined in Vector.h */ /* defined in Vector.h */
template<class T> constexpr typename std::enable_if<IsScalar<T>::value, T>::type min(T value, T min); template<class T> constexpr typename std::enable_if<IsScalar<T>::value, T>::type min(T value, T min);
@ -225,7 +226,8 @@ template<std::size_t size, class T> inline Vector<size, T> min(const Vector<size
<em>NaN</em>s passed in the @p value parameter are propagated. <em>NaN</em>s passed in the @p value parameter are propagated.
@see @ref min(), @ref minmax(), @ref clamp(), @see @ref min(), @ref minmax(), @ref clamp(),
@ref max(Corrade::Containers::ArrayView<const T>), @ref Vector::max() @ref max(Corrade::Containers::StridedArrayView1D<const T>),
@ref Vector::max()
*/ */
/* defined in Vector.h */ /* defined in Vector.h */
template<class T> constexpr typename std::enable_if<IsScalar<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);
@ -250,7 +252,8 @@ template<std::size_t size, class T> inline Vector<size, T> max(const Vector<size
@brief Minimum and maximum of two values @brief Minimum and maximum of two values
@see @ref min(), @ref max(), @ref clamp(), @see @ref min(), @ref max(), @ref clamp(),
@ref minmax(Corrade::Containers::ArrayView<const T>), @ref Vector::minmax(), @ref minmax(Corrade::Containers::StridedArrayView1D<const T>),
@ref Vector::minmax(),
@ref Range::Range(const std::pair<VectorType, VectorType>&) @ref Range::Range(const std::pair<VectorType, VectorType>&)
*/ */
template<class T> inline typename std::enable_if<IsScalar<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) {

44
src/Magnum/Math/FunctionsBatch.h

@ -30,7 +30,7 @@
*/ */
#include <initializer_list> #include <initializer_list>
#include <Corrade/Containers/ArrayView.h> #include <Corrade/Containers/StridedArrayView.h>
#include "Magnum/Math/Functions.h" #include "Magnum/Math/Functions.h"
@ -52,7 +52,7 @@ set to @cpp 1 @ce if any value has that component infinite. If the range is
empty, returns @cpp false @ce or a @ref BoolVector with no bits set. empty, returns @cpp false @ce or a @ref BoolVector with no bits set.
@see @ref isInf(T), @ref Constants::inf() @see @ref isInf(T), @ref Constants::inf()
*/ */
template<class T> auto isInf(Corrade::Containers::ArrayView<const T> range) -> decltype(isInf(std::declval<T>())) { template<class T> auto isInf(Corrade::Containers::StridedArrayView1D<const T> range) -> decltype(isInf(std::declval<T>())) {
if(range.empty()) return {}; if(range.empty()) return {};
/* For scalars, this loop exits once any value is infinity. For vectors /* For scalars, this loop exits once any value is infinity. For vectors
@ -69,12 +69,12 @@ template<class T> auto isInf(Corrade::Containers::ArrayView<const T> range) -> d
/** @overload */ /** @overload */
template<class T> inline auto isInf(std::initializer_list<T> list) -> decltype(isInf(std::declval<T>())) { template<class T> inline auto isInf(std::initializer_list<T> list) -> decltype(isInf(std::declval<T>())) {
return isInf(Corrade::Containers::arrayView(list.begin(), list.size())); return isInf<T>(Corrade::Containers::arrayView(list.begin(), list.size()));
} }
/** @overload */ /** @overload */
template<class T, std::size_t size> inline auto isInf(const T(&array)[size]) -> decltype(isInf(std::declval<T>())) { template<class T, std::size_t size> inline auto isInf(const T(&array)[size]) -> decltype(isInf(std::declval<T>())) {
return isInf(Corrade::Containers::arrayView(array)); return isInf<T>(Corrade::Containers::arrayView(array));
} }
/** /**
@ -86,7 +86,7 @@ set to @cpp 1 @ce if any value has that component NaN. If the range is empty,
returns @cpp false @ce or a @ref BoolVector with no bits set. returns @cpp false @ce or a @ref BoolVector with no bits set.
@see @ref isNan(T), @ref Constants::nan() @see @ref isNan(T), @ref Constants::nan()
*/ */
template<class T> inline auto isNan(Corrade::Containers::ArrayView<const T> range) -> decltype(isNan(std::declval<T>())) { template<class T> inline auto isNan(Corrade::Containers::StridedArrayView1D<const T> range) -> decltype(isNan(std::declval<T>())) {
if(range.empty()) return {}; if(range.empty()) return {};
/* For scalars, this loop exits once any value is infinity. For vectors /* For scalars, this loop exits once any value is infinity. For vectors
@ -103,21 +103,21 @@ template<class T> inline auto isNan(Corrade::Containers::ArrayView<const T> rang
/** @overload */ /** @overload */
template<class T> inline auto isNan(std::initializer_list<T> list) -> decltype(isInf(std::declval<T>())) { template<class T> inline auto isNan(std::initializer_list<T> list) -> decltype(isInf(std::declval<T>())) {
return isNan(Corrade::Containers::arrayView(list.begin(), list.size())); return isNan<T>(Corrade::Containers::arrayView(list.begin(), list.size()));
} }
/** @overload */ /** @overload */
template<class T, std::size_t size> inline bool isNan(const T(&array)[size]) { template<class T, std::size_t size> inline bool isNan(const T(&array)[size]) {
return isNan(Corrade::Containers::arrayView(array)); return isNan<T>(Corrade::Containers::arrayView(array));
} }
namespace Implementation { namespace Implementation {
/* Non-floating-point types, the first is a non-NaN for sure */ /* Non-floating-point types, the first is a non-NaN for sure */
template<class T, bool any> constexpr std::pair<std::size_t, T> firstNonNan(Corrade::Containers::ArrayView<const T> range, std::false_type, std::integral_constant<bool, any>) { template<class T, bool any> constexpr std::pair<std::size_t, T> firstNonNan(Corrade::Containers::StridedArrayView1D<const T> range, std::false_type, std::integral_constant<bool, any>) {
return {0, range.front()}; return {0, range.front()};
} }
/* Floating-point scalars, return the first that's not NaN */ /* Floating-point scalars, return the first that's not NaN */
template<class T> inline std::pair<std::size_t, T> firstNonNan(Corrade::Containers::ArrayView<const T> range, std::true_type, std::false_type) { template<class T> inline std::pair<std::size_t, T> firstNonNan(Corrade::Containers::StridedArrayView1D<const T> range, std::true_type, std::false_type) {
/* Find the first non-NaN value to compare against. If all are NaN, /* Find the first non-NaN value to compare against. If all are NaN,
return the last value so the following loop in min/max/minmax() return the last value so the following loop in min/max/minmax()
doesn't even execute. */ doesn't even execute. */
@ -132,7 +132,7 @@ namespace Implementation {
apply the min/max/minmax operation. I expect the cases of heavily apply the min/max/minmax operation. I expect the cases of heavily
NaN-filled vectors (and thus the need to loop twice through most of the NaN-filled vectors (and thus the need to loop twice through most of the
range) to be very rare, so this shouldn't be a problem. */ range) to be very rare, so this shouldn't be a problem. */
template<class T> inline std::pair<std::size_t, T> firstNonNan(Corrade::Containers::ArrayView<const T> range, std::true_type, std::true_type) { template<class T> inline std::pair<std::size_t, T> firstNonNan(Corrade::Containers::StridedArrayView1D<const T> range, std::true_type, std::true_type) {
T out = range[0]; T out = range[0];
std::size_t firstValid = 0; std::size_t firstValid = 0;
for(std::size_t i = 1; i != range.size(); ++i) { for(std::size_t i = 1; i != range.size(); ++i) {
@ -150,9 +150,9 @@ namespace Implementation {
If the range is empty, returns default-constructed value. <em>NaN</em>s are If the range is empty, returns default-constructed value. <em>NaN</em>s are
ignored, unless the range is all <em>NaN</em>s. ignored, unless the range is all <em>NaN</em>s.
@see @ref min(T, T), @ref isNan(Corrade::Containers::ArrayView<const T>) @see @ref min(T, T), @ref isNan(Corrade::Containers::StridedArrayView1D<const T>)
*/ */
template<class T> inline T min(Corrade::Containers::ArrayView<const T> range) { template<class T> inline T min(Corrade::Containers::StridedArrayView1D<const T> range) {
if(range.empty()) return {}; if(range.empty()) return {};
std::pair<std::size_t, T> iOut = Implementation::firstNonNan(range, IsFloatingPoint<T>{}, IsVector<T>{}); std::pair<std::size_t, T> iOut = Implementation::firstNonNan(range, IsFloatingPoint<T>{}, IsVector<T>{});
@ -164,12 +164,12 @@ template<class T> inline T min(Corrade::Containers::ArrayView<const T> range) {
/** @overload */ /** @overload */
template<class T> inline T min(std::initializer_list<T> list) { template<class T> inline T min(std::initializer_list<T> list) {
return min(Corrade::Containers::arrayView(list.begin(), list.size())); return min<T>(Corrade::Containers::arrayView(list.begin(), list.size()));
} }
/** @overload */ /** @overload */
template<class T, std::size_t size> inline T min(const T(&array)[size]) { template<class T, std::size_t size> inline T min(const T(&array)[size]) {
return min(Corrade::Containers::arrayView(array)); return min<T>(Corrade::Containers::arrayView(array));
} }
/** /**
@ -177,9 +177,9 @@ template<class T, std::size_t size> inline T min(const T(&array)[size]) {
If the range is empty, returns default-constructed value. <em>NaN</em>s are If the range is empty, returns default-constructed value. <em>NaN</em>s are
ignored, unless the range is all <em>NaN</em>s. ignored, unless the range is all <em>NaN</em>s.
@see @ref max(T, T), @ref isNan(Corrade::Containers::ArrayView<const T>) @see @ref max(T, T), @ref isNan(Corrade::Containers::StridedArrayView1D<const T>)
*/ */
template<class T> inline T max(Corrade::Containers::ArrayView<const T> range) { template<class T> inline T max(Corrade::Containers::StridedArrayView1D<const T> range) {
if(range.empty()) return {}; if(range.empty()) return {};
std::pair<std::size_t, T> iOut = Implementation::firstNonNan(range, IsFloatingPoint<T>{}, IsVector<T>{}); std::pair<std::size_t, T> iOut = Implementation::firstNonNan(range, IsFloatingPoint<T>{}, IsVector<T>{});
@ -191,12 +191,12 @@ template<class T> inline T max(Corrade::Containers::ArrayView<const T> range) {
/** @overload */ /** @overload */
template<class T> inline T max(std::initializer_list<T> list) { template<class T> inline T max(std::initializer_list<T> list) {
return max(Corrade::Containers::arrayView(list.begin(), list.size())); return max<T>(Corrade::Containers::arrayView(list.begin(), list.size()));
} }
/** @overload */ /** @overload */
template<class T, std::size_t size> inline T max(const T(&array)[size]) { template<class T, std::size_t size> inline T max(const T(&array)[size]) {
return max(Corrade::Containers::arrayView(array)); return max<T>(Corrade::Containers::arrayView(array));
} }
namespace Implementation { namespace Implementation {
@ -219,9 +219,9 @@ If the range is empty, returns default-constructed values. <em>NaN</em>s are
ignored, unless the range is all <em>NaN</em>s. ignored, unless the range is all <em>NaN</em>s.
@see @ref minmax(T, T), @see @ref minmax(T, T),
@ref Range::Range(const std::pair<VectorType, VectorType>&), @ref Range::Range(const std::pair<VectorType, VectorType>&),
@ref isNan(Corrade::Containers::ArrayView<const T>) @ref isNan(Corrade::Containers::StridedArrayView1D<const T>)
*/ */
template<class T> inline std::pair<T, T> minmax(Corrade::Containers::ArrayView<const T> range) { template<class T> inline std::pair<T, T> minmax(Corrade::Containers::StridedArrayView1D<const T> range) {
if(range.empty()) return {}; if(range.empty()) return {};
std::pair<std::size_t, T> iOut = Implementation::firstNonNan(range, IsFloatingPoint<T>{}, IsVector<T>{}); std::pair<std::size_t, T> iOut = Implementation::firstNonNan(range, IsFloatingPoint<T>{}, IsVector<T>{});
@ -234,12 +234,12 @@ template<class T> inline std::pair<T, T> minmax(Corrade::Containers::ArrayView<c
/** @overload */ /** @overload */
template<class T> inline std::pair<T, T> minmax(std::initializer_list<T> list) { template<class T> inline std::pair<T, T> minmax(std::initializer_list<T> list) {
return minmax(Corrade::Containers::arrayView(list.begin(), list.size())); return minmax<T>(Corrade::Containers::arrayView(list.begin(), list.size()));
} }
/** @overload */ /** @overload */
template<class T, std::size_t size> inline std::pair<T, T> minmax(const T(&array)[size]) { template<class T, std::size_t size> inline std::pair<T, T> minmax(const T(&array)[size]) {
return minmax(Corrade::Containers::arrayView(array)); return minmax<T>(Corrade::Containers::arrayView(array));
} }
/*@}*/ /*@}*/

Loading…
Cancel
Save