SuperDex Physics C++ API
Loading...
Searching...
No Matches
math_utils_inl.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19// Reverse include for intellisense
20#include "math_utils.h"
21
22#include <algorithm>
23#include <cmath>
24#include <limits>
25#include <type_traits>
26
27namespace superdex {
28
29/**************************************************************************************************
30 ArgMin, ArgMax
31*/
32
33// Reference implementation of std::min_element from:
34// https://en.cppreference.com/w/cpp/algorithm/min_element
35template <class ForwardIt>
36inline constexpr ForwardIt MinElement(ForwardIt first, ForwardIt last) {
37 if (first == last) {
38 return last;
39 }
40
41 ForwardIt smallest = first;
42 ++first;
43 for (; first != last; ++first) {
44 if (*first < *smallest) {
45 smallest = first;
46 }
47 }
48 return smallest;
49}
50
51// Reference implementation of std::max_element from:
52// https://en.cppreference.com/w/cpp/algorithm/max_element
53template <class ForwardIt>
54inline constexpr ForwardIt MaxElement(ForwardIt first, ForwardIt last) {
55 if (first == last) {
56 return last;
57 }
58
59 ForwardIt largest = first;
60 ++first;
61 for (; first != last; ++first) {
62 if (*largest < *first) {
63 largest = first;
64 }
65 }
66 return largest;
67}
68
69template <typename T, size_t N>
70MOCHI_FORCE_INLINE constexpr size_t ArgMin(NdArray<T, N> const& a) {
71 return (size_t)(MinElement(a.begin(), a.end()) - a.begin());
72}
73
74template <typename T, size_t N>
75MOCHI_FORCE_INLINE constexpr size_t ArgMax(NdArray<T, N> const& a) {
76 return (size_t)(MaxElement(a.begin(), a.end()) - a.begin());
77}
78
79/**************************************************************************************************
80 NearEqual: (abs(a-b) <= epsilon)
81*/
82
83template <typename T, typename Eps, size_t D0, size_t... DIMS>
84MOCHI_FORCE_INLINE constexpr bool
86 static_assert(D0 > 0, "NearEqual requires non-empty arrays");
87 bool isNear = NearEqual(a[0], b[0], epsilon);
88 for (size_t i = 1; i < D0; ++i) {
89 isNear = isNear && NearEqual(a[i], b[i], epsilon);
90 }
91 return isNear;
92}
93
94template <typename T, size_t D0, int D1>
96 NdArray<Simd<T, D1>, D0> const& a,
97 NdArray<Simd<T, D1>, D0> const& b,
98 Simd<T, D1> epsilon) {
99 static_assert(D0 > 0, "VNearEqual requires non-empty arrays");
100 auto isNear = VNearEqual(a[0], b[0], epsilon);
101 for (size_t i = 1; i < D0; ++i) {
102 isNear &= VNearEqual(a[i], b[i], epsilon);
103 }
104 return isNear;
105}
106
107template <typename T, size_t D0, int D1>
109NearEqual(NdArray<Simd<T, D1>, D0> const& a, NdArray<Simd<T, D1>, D0> const& b, T epsilon) {
110 return AllTrue(VNearEqual(a, b, Simd<T, D1>{epsilon}));
111}
112
113/**************************************************************************************************
114 Euclidean Norm
115*/
116
117template <typename T, size_t N>
119 return Dot(a, a);
120}
121
122template <typename T, size_t N>
124 return Sqrt(NormSqr<T, N>(a));
125}
126
127/**************************************************************************************************
128 Basis vector
129*/
130
131template <typename T, int N>
133 NdArray<T, N> v = {};
134
135 for (int i = 0; i < axis; ++i) {
136 v[i] = (T)0;
137 }
138
139 v[axis] = (T)1;
140
141 for (int i = axis + 1; i < N; ++i) {
142 v[i] = (T)0;
143 }
144
145 return v;
146}
147
148/**************************************************************************************************
149 Normalize (make unit length)
150*/
151
152template <typename T, size_t N>
154 // By adding the smallest possible scalar we prevent divide-by-zero and get a zero vector result
155 // There is no change in result for any vector longer than.... something very very very small.
156 constexpr auto kSmallestFloat = std::numeric_limits<ScalarType<T>>::min();
157 static_assert(kSmallestFloat > 0); // Check numeric_limits has been correctly specialized.
158 return a * (T(1) / (Norm(a) + T(kSmallestFloat)));
159}
160
161template <typename T, size_t N>
163 constexpr auto kSmallestFloat = std::numeric_limits<ScalarType<T>>::min();
164 static_assert(kSmallestFloat > 0); // Check numeric_limits has been correctly specialized.
165 return a * (T(1) / (Sqrt(sqrNorm) + T(kSmallestFloat)));
166}
167
168/**************************************************************************************************
169 Sum, Prod and Mean
170*/
171
172template <typename T, size_t N>
173MOCHI_FORCE_INLINE constexpr T Sum(NdArray<T, N> const& a) {
174 static_assert(std::is_arithmetic_v<T>, "Arithmetic type required");
175 static_assert(N > 0, "Sum requires a non-empty array");
176 T sum = a[0];
177 for (size_t i = 1; i < N; ++i) {
178 sum += a[i];
179 }
180 return sum;
181}
182
183template <typename T, size_t N>
184MOCHI_FORCE_INLINE constexpr T Prod(NdArray<T, N> const& a) {
185 static_assert(std::is_arithmetic_v<T>, "Arithmetic type required");
186 static_assert(N > 0, "Prod requires a non-empty array");
187 T prod = a[0];
188 for (size_t i = 1; i < N; ++i) {
189 prod *= a[i];
190 }
191 return prod;
192}
193
194template <typename T, size_t N>
195MOCHI_FORCE_INLINE constexpr T Mean(NdArray<T, N> const& a) {
196 static_assert(std::is_arithmetic_v<T>, "Arithmetic type required");
197 static_assert(N > 0, "Mean requires a non-empty array");
198 return Sum(a) / static_cast<T>(N);
199}
200
201template <typename T, size_t N>
202MOCHI_FORCE_INLINE constexpr T Max(NdArray<T, N> const& a) {
203 static_assert(N > 0, "Max requires a non-empty array");
204 static_assert(std::is_arithmetic_v<T>, "Arithmetic type required");
205 T max = a[0];
206 for (int i = 1; i < N; ++i) {
207 if (a[i] > max) {
208 max = a[i];
209 }
210 }
211 return max;
212}
213
214template <typename T, size_t N>
215MOCHI_FORCE_INLINE constexpr T Min(NdArray<T, N> const& a) {
216 static_assert(N > 0, "Min requires a non-empty array");
217 static_assert(std::is_arithmetic_v<T>, "Arithmetic type required");
218 T min = a[0];
219 for (int i = 1; i < N; ++i) {
220 if (a[i] < min) {
221 min = a[i];
222 }
223 }
224 return min;
225}
226
227/**************************************************************************************************
228 Floor, ceil, round
229*/
230
231template <typename T, size_t N>
233 NdArray<T, N> output = {};
234 for (int i = 0; i < N; ++i) {
235 output[i] = T(Floor(a[i]));
236 }
237 return output;
238}
239
240template <typename T, size_t N>
242 NdArray<T, N> output = {};
243 for (int i = 0; i < N; ++i) {
244 output[i] = T(Ceil(a[i]));
245 }
246 return output;
247}
248
249template <typename T, size_t N>
251 NdArray<T, N> output = {};
252 for (int i = 0; i < N; ++i) {
253 output[i] = T(Round(a[i]));
254 }
255 return output;
256}
257
258template <typename T, size_t N>
259MOCHI_FORCE_INLINE constexpr NdArray<T, N> Clamp(NdArray<T, N> const& a, T min, T max) {
260 NdArray<T, N> output = {};
261 for (int i = 0; i < N; ++i) {
262 output[i] = Clamp(a[i], min, max);
263 }
264 return output;
265}
266
267template <typename T, size_t N>
269 NdArray<T, N> output = {};
270 for (int i = 0; i < N; ++i) {
271 output[i] = Max(a[i], max);
272 }
273 return output;
274}
275
276template <typename T, size_t N>
278 NdArray<T, N> output = {};
279 for (int i = 0; i < N; ++i) {
280 output[i] = Min(a[i], min);
281 }
282 return output;
283}
284
285template <typename T, size_t N>
286MOCHI_FORCE_INLINE constexpr NdArray<T, N>
287Clamp(NdArray<T, N> const& a, NdArray<T, N> const& min, NdArray<T, N> const& max) {
288 NdArray<T, N> output = {};
289 for (int i = 0; i < N; ++i) {
290 output[i] = Clamp(a[i], min[i], max[i]);
291 }
292 return output;
293}
294
295template <typename T, size_t N, bool MinInclusiveT, bool MaxInclusiveT>
297 NdArray<T, N> const& a,
298 NdArray<T, N> const& min,
299 NdArray<T, N> const& max,
300 std::integral_constant<bool, MinInclusiveT> minInclusive,
301 std::integral_constant<bool, MaxInclusiveT> maxInclusive) {
302 NdArray<T, N> output = {};
303 for (int i = 0; i < N; ++i) {
304 output[i] = Rect(a[i], min[i], max[i], minInclusive, maxInclusive);
305 }
306 return output;
307}
308
309template <typename T, size_t N>
311 NdArray<T, N> output = {};
312 for (int i = 0; i < N; ++i) {
313 output[i] = Max(a[i], max[i]);
314 }
315 return output;
316}
317
318template <typename T, size_t N>
320 NdArray<T, N> output = {};
321 for (int i = 0; i < N; ++i) {
322 output[i] = Min(a[i], min[i]);
323 }
324 return output;
325}
326
327/**************************************************************************************************
328 Dot Product
329*/
330
331template <typename T, size_t N>
332MOCHI_FORCE_INLINE constexpr T Dot(NdArray<T, N> const& a, NdArray<T, N> const& b) {
333 static_assert(N > 0, "Dot requires non-empty arrays");
334 T result = a[0] * b[0];
335 for (size_t i = 1; i < N; ++i) {
336 result += a[i] * b[i];
337 }
338 return result;
339}
340
341template <typename T, size_t N>
343 static_assert(N > 0, "Dot requires non-empty arrays");
344 MOCHI_ASSERT_VERBOSE(a.size() == N, "Size mismatch");
345 T result = a[0] * b[0];
346 for (size_t i = 1; i < N; ++i) {
347 result += a[i] * b[i];
348 }
349 return result;
350}
351
352template <typename T>
354 MOCHI_ASSERT_VERBOSE(a.size() == b.size(), "Size mismatch");
355 T result = T{0};
356 for (size_t i = 0; i < a.size(); ++i) {
357 result += a[i] * b[i];
358 }
359 return result;
360}
361
362/**************************************************************************************************
363 Cross Product
364*/
365
366template <typename T>
368 return NdArray<T, 3>{
369 (a[1] * b[2] - a[2] * b[1]), (a[2] * b[0] - a[0] * b[2]), (a[0] * b[1] - a[1] * b[0])};
370}
371
372/**************************************************************************************************
373 Triple Product
374*/
375
376template <typename T>
377MOCHI_FORCE_INLINE constexpr T
379 static_assert(std::is_arithmetic_v<T>, "Arithmetic type required");
380 return Dot(a, Cross(b, c));
381}
382
383/**************************************************************************************************
384 Other Geometric Utilities
385*/
386
387template <typename T>
389 return NdArray<T, 2>{-vec[1], vec[0]};
390}
391
392template <typename T>
394 // Project out the coordinate of the minimum absolute value, then build the orthogonal vector in
395 // the remaining 2D subspace.
396 // NOTE: We use <= so that ties prefer the lower-index axis, matching the SIMD implementation
397 // OrthogonalVector3(Simd<T, 4>).
398 T const absX = Abs(vec[0]);
399 T const absY = Abs(vec[1]);
400 T const absZ = Abs(vec[2]);
401
402 auto const xSmallest = (absX <= absY) && (absX <= absZ);
403 auto const ySmallest = (absY <= absX) && (absY <= absZ);
404 T const zero{0};
405 return {
406 Select(xSmallest, zero, Select(ySmallest, -vec[2], vec[1])),
407 Select(xSmallest, vec[2], Select(ySmallest, zero, -vec[0])),
408 Select(xSmallest, -vec[1], Select(ySmallest, vec[0], zero))};
409}
410
411/**************************************************************************************************
412 IsFinite
413*/
414
415template <class T, int DN, size_t D0, size_t... DIMS>
417 static_assert(std::is_floating_point_v<T>, "VIsFinite only supports float or double");
418 static_assert(D0 > 0, "VIsFinite requires non-empty arrays");
419 auto isFinite = VIsFinite(a[0]);
420 for (size_t i = 1; i < D0; ++i) {
421 isFinite &= VIsFinite(a[i]);
422 }
423 return isFinite;
424}
425
426template <class T, int DN, size_t D0, size_t... DIMS>
427MOCHI_FORCE_INLINE bool IsFinite(NdArray<Simd<T, DN>, D0, DIMS...> const& a) {
428 if constexpr (std::is_integral_v<T>) {
429 return true;
430 } else {
431 return AllTrue(VIsFinite(a));
432 }
433}
434
435template <class T, size_t D0, size_t... DIMS>
437 static_assert(D0 > 0, "IsFinite requires non-empty arrays");
438 bool isFinite = IsFinite(a[0]);
439 for (size_t i = 1; i < D0; ++i) {
440 isFinite &= IsFinite(a[i]);
441 }
442 return isFinite;
443}
444
445} // namespace superdex
constexpr auto end()
Definition nd_array.h:83
constexpr auto begin()
Definition nd_array.h:81
constexpr SizeT size() const
Definition span.h:106
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
Definition debug.h:102
#define MOCHI_FORCE_INLINE
T Dot(Simd< T, N > a, Simd< T, N > b)
Definition simd.h:666
Simd< T, N > VIsFinite(Simd< T, N > a)
Definition simd_inl.h:755
constexpr T const & Min(T const &a, T const &b)
T NormSqr(Simd< T, N > a)
Definition simd_inl.h:849
constexpr T TripleProduct(NdArray< T, 3 > const &a, NdArray< T, 3 > const &b, NdArray< T, 3 > const &c)
constexpr ValT Rect(ValT value, MinT min, MaxT max, std::integral_constant< bool, kMinInclusive >=std::true_type{}, std::integral_constant< bool, kMaxInclusive >=std::false_type{})
T Norm(Simd< T, N > a)
Definition simd_inl.h:854
bool AllTrue(T const &a)
Definition basic_utils.h:60
V VNearEqual(V a, V b, V epsilon)
Definition simd_inl.h:733
constexpr NdArray< T, 2 > OrthogonalVector(NdArray< T, 2 > const &vec)
constexpr T Abs(T a)
Definition basic_utils.h:50
Simd< T, N > Normalize(Simd< T, N > a)
Definition simd_inl.h:859
constexpr T Select(bool condition, T a, T b)
constexpr size_t ArgMin(NdArray< T, N > const &a)
constexpr NdArray< T, N > BasisVector(int axis)
constexpr T Sqrt(T a)
constexpr T Mean(NdArray< T, N > const &a)
constexpr T Floor(T a)
constexpr T Ceil(T a)
constexpr T Sum(NdArray< T, N > const &a)
constexpr size_t ArgMax(NdArray< T, N > const &a)
constexpr ValT Clamp(ValT value, MinT min, MaxT max)
constexpr ForwardIt MinElement(ForwardIt first, ForwardIt last)
constexpr T const & Max(T const &a, T const &b)
constexpr T Prod(NdArray< T, N > const &a)
bool NearEqual(TransformRT const &a, TransformRT const &b, real epsilon=kDefaultNearEqualEpsilon< real >)
bool IsFinite(TransformRT const &a)
constexpr T Round(T a)
constexpr NdArray< T, 3 > Cross(NdArray< T, 3 > const &a, NdArray< T, 3 > const &b)
constexpr ForwardIt MaxElement(ForwardIt first, ForwardIt last)