|
7 | 7 |
|
8 | 8 | #include <cstdlib> |
9 | 9 | #include <cmath> |
| 10 | +#include <algorithm> |
10 | 11 |
|
11 | 12 | /// \file |
12 | 13 | /// ofMath provides a collection of mathematical utilities and functions. |
@@ -158,6 +159,104 @@ float ofMap(float value, float inputMin, float inputMax, float outputMin, float |
158 | 159 | /// \returns a floating point number in the range [min, max]. |
159 | 160 | float ofClamp(float value, float min, float max); |
160 | 161 |
|
| 162 | +/// \brief return the smallest of 2 values of same type |
| 163 | +/// |
| 164 | +/// \param a first value to compare |
| 165 | +/// \param b second value to compare |
| 166 | + |
| 167 | +template<typename T> |
| 168 | +T ofMin(const T & t1, const T & t2) { return std::min(t1, t2); } |
| 169 | + |
| 170 | +/// \brief return the smallest of 2 values of different types |
| 171 | +/// |
| 172 | +/// Leverages 'std::common_type' to efficiently cast the values in compatible types |
| 173 | +/// special case for comparisons when signed and unsigned are mixed |
| 174 | +/// special case for comparisons when size_t / uint64_t are involved |
| 175 | +/// |
| 176 | +/// \param a first value to compare |
| 177 | +/// \param b second value to compare |
| 178 | + |
| 179 | +template<typename T, typename Q> |
| 180 | +auto ofMin(const T & t, const Q & q) { |
| 181 | + using CommonType = typename std::common_type<T, Q>::type; |
| 182 | + if constexpr ((std::is_same_v<T, uint64_t> or std::is_same_v<T, size_t>) && std::is_signed_v<Q>) { |
| 183 | + if (q < 0) { |
| 184 | + return q; |
| 185 | + } else { |
| 186 | + return std::min<Q>(static_cast<Q>(t), q); |
| 187 | + } |
| 188 | + } else if constexpr ((std::is_same_v<Q, uint64_t> or std::is_same_v<Q, size_t>) && std::is_signed_v<T>) { |
| 189 | + if (t < 0) { |
| 190 | + return t; |
| 191 | + } else { |
| 192 | + return std::min<T>(t, static_cast<T>(q)); |
| 193 | + } |
| 194 | + } else if constexpr (std::is_signed_v<T> && std::is_unsigned_v<Q>) { |
| 195 | + if (t < 0 || q > static_cast<Q>(std::numeric_limits<T>::max())) { |
| 196 | + return static_cast<CommonType>(t); |
| 197 | + } else { |
| 198 | + return std::min(static_cast<CommonType>(t), static_cast<CommonType>(q)); |
| 199 | + } |
| 200 | + } else if constexpr (std::is_signed_v<Q> && std::is_unsigned_v<T>){ |
| 201 | + if (q < 0 || t > static_cast<T>(std::numeric_limits<Q>::max())) { |
| 202 | + return static_cast<CommonType>(q); |
| 203 | + } else { |
| 204 | + return std::min(static_cast<CommonType>(t), static_cast<CommonType>(q)); |
| 205 | + } |
| 206 | + } else { |
| 207 | + return std::min(static_cast<CommonType>(t), static_cast<CommonType>(q)); |
| 208 | + } |
| 209 | +} |
| 210 | + |
| 211 | +/// \brief return the largest of 2 values of same type |
| 212 | +/// |
| 213 | +/// \param a first value to compare |
| 214 | +/// \param b second value to compare |
| 215 | + |
| 216 | +template<typename T> |
| 217 | +T ofMax(const T & t1, const T & t2) { return std::max(t1, t2); } |
| 218 | + |
| 219 | +/// \brief return the largest of 2 values of different types |
| 220 | +/// |
| 221 | +/// Leverages 'std::common_type' to efficiently cast the values in compatible types |
| 222 | +/// special case for comparisons when signed and unsigned are mixed |
| 223 | +/// special case for comparisons when size_t / uint64_t are involved |
| 224 | +/// |
| 225 | +/// \param a first value to compare |
| 226 | +/// \param b second value to compare |
| 227 | + |
| 228 | +template<typename T, typename Q> |
| 229 | +auto ofMax(const T & t, const Q & q) { |
| 230 | + using CommonType = typename std::common_type<T, Q>::type; |
| 231 | + if constexpr ((std::is_same_v<T, uint64_t> or std::is_same_v<T, size_t>) && std::is_signed_v<Q>) { |
| 232 | + if (q < 0) { |
| 233 | + return t; |
| 234 | + } else { |
| 235 | + return std::max<T>(t, static_cast<T>(q)); |
| 236 | + } |
| 237 | + } else if constexpr ((std::is_same_v<Q, uint64_t> or std::is_same_v<Q, size_t>) && std::is_signed_v<T>) { |
| 238 | + if (t < 0) { |
| 239 | + return q; |
| 240 | + } else { |
| 241 | + return std::max<Q>(static_cast<Q>(t), q); |
| 242 | + } |
| 243 | + } else if constexpr (std::is_signed_v<T> && std::is_unsigned_v<Q>) { |
| 244 | + if (t < 0) { |
| 245 | + return static_cast<CommonType>(q); |
| 246 | + } else { |
| 247 | + return std::max(static_cast<CommonType>(t), static_cast<CommonType>(q)); |
| 248 | + } |
| 249 | + } else if constexpr (std::is_signed_v<Q> && std::is_unsigned_v<T>){ |
| 250 | + if (q < 0) { |
| 251 | + return static_cast<CommonType>(t); |
| 252 | + } else { |
| 253 | + return std::max(static_cast<CommonType>(t), static_cast<CommonType>(q)); |
| 254 | + } |
| 255 | + } else { |
| 256 | + return std::max(static_cast<CommonType>(t), static_cast<CommonType>(q)); |
| 257 | + } |
| 258 | +} |
| 259 | + |
161 | 260 | /// \brief Determine if a number is inside of a giv(float)(en range. |
162 | 261 | /// \param t The value to test. |
163 | 262 | /// \param min The lower bound of the range. |
|
0 commit comments