diff options
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenlib/BLI_math_base.hh | 121 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_math_vec_types.hh | 23 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_math_vector.hh | 40 | ||||
-rw-r--r-- | source/blender/blenlib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/blenlib/tests/BLI_math_base_test.cc | 23 |
5 files changed, 193 insertions, 15 deletions
diff --git a/source/blender/blenlib/BLI_math_base.hh b/source/blender/blenlib/BLI_math_base.hh new file mode 100644 index 00000000000..89a3fd3b826 --- /dev/null +++ b/source/blender/blenlib/BLI_math_base.hh @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#include <algorithm> +#include <cmath> +#include <type_traits> + +#include "BLI_math_base_safe.h" +#include "BLI_math_vec_types.hh" +#include "BLI_utildefines.h" + +#ifdef WITH_GMP +# include "BLI_math_mpq.hh" +#endif + +namespace blender::math { + +/* To avoid being overly specific about what a "basic" type is, for now simply allow anything that + * isn't a `vec_base` type. In the future, if another implementation of these functions is needed, + * this would have to become more specific. */ +#define BLI_ENABLE_IF_BASE(T) BLI_ENABLE_IF((!is_math_vec_type<T>)) + +#ifdef WITH_GMP +# define BLI_ENABLE_IF_FLT(T) \ + BLI_ENABLE_IF_BASE(T), \ + BLI_ENABLE_IF((std::is_floating_point_v<T> || std::is_same_v<T, mpq_class>)) +#else +# define BLI_ENABLE_IF_FLT(T) BLI_ENABLE_IF_BASE(T), BLI_ENABLE_IF((std::is_floating_point_v<T>)) +#endif + +#define BLI_ENABLE_IF_INT(T) BLI_ENABLE_IF_BASE(T), BLI_ENABLE_IF((std::is_integral_v<T>)) + +template<typename T, BLI_ENABLE_IF_BASE(T)> inline bool is_zero(const T &a) +{ + return a == T(0); +} + +template<typename T, BLI_ENABLE_IF_BASE(T)> inline bool is_any_zero(const T &a) +{ + return is_zero(a); +} + +template<typename T, BLI_ENABLE_IF_BASE(T)> inline T abs(const T &a) +{ + return std::abs(a); +} + +template<typename T, BLI_ENABLE_IF_BASE(T)> inline T min(const T &a, const T &b) +{ + return std::min(a, b); +} + +template<typename T, BLI_ENABLE_IF_BASE(T)> inline T max(const T &a, const T &b) +{ + return std::max(a, b); +} + +template<typename T, BLI_ENABLE_IF_BASE(T)> inline T clamp(const T &a, const T &min, const T &max) +{ + return std::clamp(a, min, max); +} + +template<typename T, BLI_ENABLE_IF_FLT(T)> inline T mod(const T &a, const T &b) +{ + return std::fmod(a, b); +} + +template<typename T, BLI_ENABLE_IF_FLT(T)> inline T safe_mod(const T &a, const T &b) +{ + return (b != 0) ? std::fmod(a, b) : 0; +} + +template<typename T, BLI_ENABLE_IF_BASE(T)> +inline void min_max(const T &vector, T &min_vec, T &max_vec) +{ + min_vec = min(vector, min_vec); + max_vec = max(vector, max_vec); +} + +template<typename T, BLI_ENABLE_IF_FLT(T)> inline T safe_divide(const T &a, const T &b) +{ + return (b != 0) ? a / b : T(0.0f); +} + +template<typename T, BLI_ENABLE_IF_FLT(T)> inline T floor(const T &a) +{ + return std::floor(a); +} + +template<typename T, BLI_ENABLE_IF_FLT(T)> inline T ceil(const T &a) +{ + return std::ceil(a); +} + +template<typename T, BLI_ENABLE_IF_FLT(T)> inline T fract(const T &a) +{ + return a - std::floor(a); +} + +template<typename T, typename FactorT, BLI_ENABLE_IF_FLT(T), BLI_ENABLE_IF_FLT(FactorT)> +inline T interpolate(const T &a, const T &b, const FactorT &t) +{ + return a * (1 - t) + b * t; +} + +template<typename T, BLI_ENABLE_IF_FLT(T)> inline T midpoint(const T &a, const T &b) +{ + return (a + b) * 0.5; +} + +#undef BLI_ENABLE_IF_BASE +#undef BLI_ENABLE_IF_FLT +#undef BLI_ENABLE_IF_INT + +} // namespace blender::math diff --git a/source/blender/blenlib/BLI_math_vec_types.hh b/source/blender/blenlib/BLI_math_vec_types.hh index ad885bde27d..b8a208f0a0d 100644 --- a/source/blender/blenlib/BLI_math_vec_types.hh +++ b/source/blender/blenlib/BLI_math_vec_types.hh @@ -265,6 +265,16 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> } \ return *this; + bool is_any_zero() const + { + for (int i = 0; i < Size; i++) { + if ((*this)[i] == T(0)) { + return true; + } + } + return false; + } + /** Arithmetic operators. */ friend vec_base operator+(const vec_base &a, const vec_base &b) @@ -367,7 +377,7 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> vec_base &operator/=(T b) { - BLI_assert(b != T(0)); + BLI_assert(!b.is_any_zero()); BLI_VEC_OP_IMPL_SELF(i, (*this)[i] /= b); } @@ -579,4 +589,15 @@ using double2 = vec_base<double, 2>; using double3 = vec_base<double, 3>; using double4 = vec_base<double, 4>; +template<typename T> constexpr bool is_math_vec_type = false; +template<typename BaseType, int Size> +constexpr bool is_math_vec_type<vec_base<BaseType, Size>> = true; + +static_assert(is_math_vec_type<int2>); +static_assert(is_math_vec_type<uint4>); +static_assert(is_math_vec_type<float2>); +static_assert(is_math_vec_type<vec_base<float, 20>>); +static_assert(!is_math_vec_type<int>); +static_assert(!is_math_vec_type<float>); + } // namespace blender diff --git a/source/blender/blenlib/BLI_math_vector.hh b/source/blender/blenlib/BLI_math_vector.hh index d2ef2a1c5c8..32cc15e44a4 100644 --- a/source/blender/blenlib/BLI_math_vector.hh +++ b/source/blender/blenlib/BLI_math_vector.hh @@ -35,17 +35,25 @@ namespace blender::math { #define bT typename T::base_type +template<typename T> +inline constexpr bool is_float_vector = (std::is_floating_point_v<typename T::base_type> #ifdef WITH_GMP -# define BLI_ENABLE_IF_FLT_VEC(T) \ - BLI_ENABLE_IF((std::is_floating_point_v<typename T::base_type> || \ - std::is_same_v<typename T::base_type, mpq_class>)) -#else -# define BLI_ENABLE_IF_FLT_VEC(T) BLI_ENABLE_IF((std::is_floating_point_v<typename T::base_type>)) + || std::is_same_v<typename T::base_type, mpq_class> #endif +); + +template<typename T> +inline constexpr bool is_integral_vector = std::is_integral_v<typename T::base_type>; + +/* In this file, only implement math functions for the vector types. This allows other + * files to use overloading to implement the same functions with different types. */ +#define BLI_ENABLE_IF_VEC(T) BLI_ENABLE_IF((is_math_vec_type<T>)) + +#define BLI_ENABLE_IF_FLT_VEC(T) BLI_ENABLE_IF_VEC(T), BLI_ENABLE_IF((is_float_vector<T>)) -#define BLI_ENABLE_IF_INT_VEC(T) BLI_ENABLE_IF((std::is_integral_v<typename T::base_type>)) +#define BLI_ENABLE_IF_INT_VEC(T) BLI_ENABLE_IF_VEC(T), BLI_ENABLE_IF((is_integral_vector<T>)) -template<typename T> inline bool is_zero(const T &a) +template<typename T, BLI_ENABLE_IF_VEC(T)> inline bool is_zero(const T &a) { for (int i = 0; i < T::type_length; i++) { if (a[i] != bT(0)) { @@ -55,7 +63,7 @@ template<typename T> inline bool is_zero(const T &a) return true; } -template<typename T> inline T abs(const T &a) +template<typename T, BLI_ENABLE_IF_VEC(T)> inline T abs(const T &a) { T result; for (int i = 0; i < T::type_length; i++) { @@ -64,7 +72,7 @@ template<typename T> inline T abs(const T &a) return result; } -template<typename T> inline T min(const T &a, const T &b) +template<typename T, BLI_ENABLE_IF_VEC(T)> inline T min(const T &a, const T &b) { T result; for (int i = 0; i < T::type_length; i++) { @@ -73,7 +81,7 @@ template<typename T> inline T min(const T &a, const T &b) return result; } -template<typename T> inline T max(const T &a, const T &b) +template<typename T, BLI_ENABLE_IF_VEC(T)> inline T max(const T &a, const T &b) { T result; for (int i = 0; i < T::type_length; i++) { @@ -82,7 +90,8 @@ template<typename T> inline T max(const T &a, const T &b) return result; } -template<typename T> inline T clamp(const T &a, const T &min_v, const T &max_v) +template<typename T, BLI_ENABLE_IF_VEC(T)> +inline T clamp(const T &a, const T &min_v, const T &max_v) { T result = a; for (int i = 0; i < T::type_length; i++) { @@ -91,7 +100,8 @@ template<typename T> inline T clamp(const T &a, const T &min_v, const T &max_v) return result; } -template<typename T> inline T clamp(const T &a, const bT &min_v, const bT &max_v) +template<typename T, BLI_ENABLE_IF_VEC(T)> +inline T clamp(const T &a, const bT &min_v, const bT &max_v) { T result = a; for (int i = 0; i < T::type_length; i++) { @@ -141,7 +151,8 @@ template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_mod(const T &a, bT return result; } -template<typename T> inline void min_max(const T &vector, T &min_vec, T &max_vec) +template<typename T, BLI_ENABLE_IF_VEC(T)> +inline void min_max(const T &vector, T &min_vec, T &max_vec) { min_vec = min(vector, min_vec); max_vec = max(vector, max_vec); @@ -197,7 +208,7 @@ template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT dot(const T &a, const T return result; } -template<typename T> inline bT length_manhattan(const T &a) +template<typename T, BLI_ENABLE_IF_VEC(T)> inline bT length_manhattan(const T &a) { bT result = std::abs(a[0]); for (int i = 1; i < T::type_length; i++) { @@ -354,6 +365,7 @@ template<typename T> struct isect_result { template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> isect_result<T> isect_seg_seg(const T &v1, const T &v2, const T &v3, const T &v4); +#undef BLI_ENABLE_IF_VEC #undef BLI_ENABLE_IF_FLT_VEC #undef BLI_ENABLE_IF_INT_VEC #undef bT diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 29015084679..e67d673eb73 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -220,6 +220,7 @@ set(SRC BLI_map.hh BLI_map_slots.hh BLI_math.h + BLI_math_base.hh BLI_math_base.h BLI_math_base_safe.h BLI_math_bits.h diff --git a/source/blender/blenlib/tests/BLI_math_base_test.cc b/source/blender/blenlib/tests/BLI_math_base_test.cc index 33acefeeac2..34da00cb4bf 100644 --- a/source/blender/blenlib/tests/BLI_math_base_test.cc +++ b/source/blender/blenlib/tests/BLI_math_base_test.cc @@ -4,6 +4,12 @@ #include "BLI_math.h" +#include "BLI_math_vector.hh" + +#include "BLI_math_base.hh" + +namespace blender::tests { + /* In tests below, when we are using -1.0f as max_diff value, we actually turn the function into a * pure-ULP one. */ @@ -131,3 +137,20 @@ TEST(math_base, FloorPowerOf10) EXPECT_NEAR(floor_power_of_10(100.1f), 100.0f, 1e-4f); EXPECT_NEAR(floor_power_of_10(99.9f), 10.0f, 1e-4f); } + +TEST(math_base, MinVectorAndFloat) +{ + EXPECT_EQ(math::min(1.0f, 2.0f), 1.0f); +} + +TEST(math_base, ClampInt) +{ + EXPECT_EQ(math::clamp(111, -50, 101), 101); +} + +TEST(math_base, Midpoint) +{ + EXPECT_NEAR(math::midpoint(100.0f, 200.0f), 150.0f, 1e-4f); +} + +} // namespace blender::tests |