SuperDex Physics C++ API
Loading...
Searching...
No Matches
matrix_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#include <mochi_core/utils/matrix_utils.h> // for intellisense
20
21#include <cmath>
22
23namespace superdex {
24
25/**************************************************************************************************
26 Frobenius Norm
27*/
28
29template <typename T, size_t N, size_t M>
31 T accum = T(0);
32 for (int i = 0; i < N; ++i) {
33 accum += Dot(a[i], a[i]);
34 }
35 return accum;
36}
37
38template <typename T, size_t N, size_t M>
40 return Sqrt(NormSqr(a));
41}
42
43template <typename T, size_t D0, int D1, MOCHI_CONCEPT_DEF(D0 >= 3 && D1 >= 3)>
45 return HSum<3>(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
46}
47
48template <typename T, size_t D0, int D1, MOCHI_CONCEPT_DEF(D0 >= 3 && D1 >= 3)>
52
53/**************************************************************************************************
54 Matrix-Vector Dot Product
55*/
56
57template <typename T, size_t N, size_t M>
59 NdArray<T, N, M> const& a,
60 NdArray<T, M> const& b) {
61 NdArray<T, N> result = {};
62 for (size_t i = 0; i < result.size(); ++i) {
63 result[i] = Dot(a[i], b);
64 }
65 return result;
66}
67
68template <typename T, size_t N, size_t M>
70 MOCHI_ASSERT_VERBOSE((M == b.size()) && (out.size() == N), "Size mismatch");
71 for (size_t i = 0; i < out.size(); ++i) {
72 out[i] = Dot(b, a[i]);
73 }
74}
75
76template <typename T, size_t N, size_t M>
78 NdArray<T, N> result = {};
79 DotMatVec(a, b, Span<T>(result));
80 return result;
81}
82
83template <typename T>
85 NdArray<T, 3, 3> const& a,
86 NdArray<T, 3> const& b) {
87 return NdArray<T, 3>{
88 a[0][0] * b[0] + a[0][1] * b[1] + a[0][2] * b[2],
89 a[1][0] * b[0] + a[1][1] * b[1] + a[1][2] * b[2],
90 a[2][0] * b[0] + a[2][1] * b[1] + a[2][2] * b[2]};
91}
92
93template <size_t N, typename T, size_t D0>
95 static_assert(D0 >= 3, "Requires a matrix with at least 3 rows");
96 auto const a = VDot<N>(m[0], v);
97 auto const b = VDot<N>(m[1], v);
98 auto const c = VDot<N>(m[2], v);
100}
101
102template <typename T, size_t D0>
106
107template <typename T>
109 auto a = VDot(m[0], v);
110 auto b = VDot(m[1], v);
111 auto c = VDot(m[2], v);
112 auto d = VDot(m[3], v);
113 auto ab = Shuffle<0, 0, 0, 0>(a, b); // ac = (a[0], a[0], b[0], b[0])
114 auto cd = Shuffle<0, 0, 0, 0>(c, d); // bd = (c[0], c[0], d[0], d[0])
115 return Shuffle<0, 2, 0, 2>(ab, cd); // return (a[0], b[0], c[0], d[0])
116}
117
118/**************************************************************************************************
119 Vector-Matrix Dot Product
120*/
121
122template <typename T, size_t N, size_t M>
124 NdArray<T, N> const& a,
125 NdArray<T, N, M> const& b) {
126 if constexpr (N == 3 && M == 3) {
127 return NdArray<T, 3>{
128 a[0] * b[0][0] + a[1] * b[1][0] + a[2] * b[2][0],
129 a[0] * b[0][1] + a[1] * b[1][1] + a[2] * b[2][1],
130 a[0] * b[0][2] + a[1] * b[1][2] + a[2] * b[2][2]};
131 } else {
132 return DotMatVec(Transpose(b), a);
133 }
134}
135
136template <typename T>
138 // |u v| | a b c | = | ua + vd |
139 // | d e f | | ub + ve |
140 // | uc + vf |
141 Simd<T, 4> temp0 = Broadcast<0>(v) * m[0]; // (ua, ub, uc, ?)
142 Simd<T, 4> temp1 = Broadcast<1>(v) * m[1]; // (vd, ve, vf, ?)
143 return temp0 + temp1; // (ua + vd, ub + ve, uc + vf, ?)
144}
145
146template <typename T, size_t D0>
148 static_assert(D0 == 3 || D0 == 4, "Requires a matrix with 3 or 4 rows");
149 return Broadcast<0>(a) * b[0] + Broadcast<1>(a) * b[1] + Broadcast<2>(a) * b[2];
150}
151
152template <typename T>
154 return Broadcast<0>(a) * b[0] + Broadcast<1>(a) * b[1] + Broadcast<2>(a) * b[2] +
155 Broadcast<3>(a) * b[3];
156}
157
158/**************************************************************************************************
159 Matrix-Matrix Dot Product
160*/
161
162template <typename T, size_t N, size_t M, size_t L>
164 NdArray<T, N, M> const& a,
165 NdArray<T, M, L> const& b) {
166 NdArray<T, N, L> result = {};
168 for (int n = 0; n < N; ++n) {
169 result[n] = DotMatVec(bT, a[n]);
170 }
171 return result;
172}
173
174template <typename T>
176 NdArray<Simd<T, 4>, 4> const& a,
177 NdArray<Simd<T, 4>, 4> const& b) {
178 return NdArray<Simd<T, 4>, 4>{
179 DotVecMat4x4(a[0], b), DotVecMat4x4(a[1], b), DotVecMat4x4(a[2], b), DotVecMat4x4(a[3], b)};
180}
181
182template <typename T>
184 NdArray<T, 3, 3> const& a,
185 NdArray<T, 3, 3> const& b) {
186 return {
188 a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0],
189 a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1],
190 a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2]},
192 a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0],
193 a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1],
194 a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2]},
196 a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0],
197 a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1],
198 a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2]}};
199}
200
201template <typename T, size_t D0A, size_t D0B>
203 NdArray<Simd<T, 4>, D0A> const& a,
204 NdArray<Simd<T, 4>, D0B> const& b) {
205 return {DotVecMat3x3(a[0], b), DotVecMat3x3(a[1], b), DotVecMat3x3(a[2], b)};
206}
207
208template <typename T>
210 Simd<T, 4> const a0 = Shuffle<0, 1, 3, 2>(a);
211 Simd<T, 4> const b0 = Shuffle<0, 3, 2, 1>(b);
212 Simd<T, 4> const a1 = Shuffle<1, 0, 2, 3>(a);
213 Simd<T, 4> const b1 = Shuffle<2, 1, 0, 3>(b);
214 return a0 * b0 + a1 * b1;
215}
216
217/**************************************************************************************************
218 Outer Product
219*/
220
221template <typename T>
223 NdArray<T, 2> const& a,
224 NdArray<T, 2> const& b) {
225 return {NdArray<T, 2>{a[0] * b[0], a[0] * b[1]}, NdArray<T, 2>{a[1] * b[0], a[1] * b[1]}};
226}
227
228template <typename T>
230 NdArray<T, 3> const& a,
231 NdArray<T, 2> const& b) {
232 return NdArray<T, 3, 2>{
233 NdArray<T, 2>{a[0] * b[0], a[0] * b[1]},
234 NdArray<T, 2>{a[1] * b[0], a[1] * b[1]},
235 NdArray<T, 2>{a[2] * b[0], a[2] * b[1]}};
236}
237
238template <typename T>
240 NdArray<T, 3> const& a,
241 NdArray<T, 3> const& b) {
242 return {
243 NdArray<T, 3>{a[0] * b[0], a[0] * b[1], a[0] * b[2]},
244 NdArray<T, 3>{a[1] * b[0], a[1] * b[1], a[1] * b[2]},
245 NdArray<T, 3>{a[2] * b[0], a[2] * b[1], a[2] * b[2]}};
246}
247
248// Outer product of 3 component vectors (assumed 4th component unused)
249template <typename T>
251 return NdArray<Simd<T, 4>, 3>{
252 Shuffle<0, 0, 0, 0>(a) * b, // {a[0] * b[0], a[0] * b[1], a[0] * b[2], a[0] * b[3] }
253 Shuffle<1, 1, 1, 1>(a) * b, // {a[1] * b[0], a[1] * b[1], a[1] * b[2], a[1] * b[3] }
254 Shuffle<2, 2, 2, 2>(a) * b, // {a[2] * b[0], a[2] * b[1], a[2] * b[2], a[2] * b[3] }
255 };
256}
257
258template <typename T>
260 Simd<T, 4> const& vec,
261 NdArray<Simd<T, 4>, 3> const& mat) {
262 NdArray<Simd<T, 4>, 3, 3> outProd;
263 outProd[0] = mat * Broadcast<0>(vec);
264 outProd[1] = mat * Broadcast<1>(vec);
265 outProd[2] = mat * Broadcast<2>(vec);
266 return outProd;
267}
268
269// Outer product of a 3x3 matrix and a 3x1 vector returning a 3x3x3 3rd-order tensor
270template <typename T>
272 NdArray<Simd<T, 4>, 3> const& mat,
273 Simd<T, 4> const& vec) {
274 NdArray<Simd<T, 4>, 3, 3> result;
275 for (int i = 0; i < 3; ++i) {
276 result[i] = Outer3(mat[i], vec);
277 }
278 return result;
279}
280
281template <typename T>
283 NdArray<Simd<T, 4>, 3, 3> const& ten,
284 Simd<T, 4> const& vec) {
285 NdArray<Simd<T, 4>, 3, 3, 3> outProd;
286 for (int i = 0; i < 3; ++i) {
287 outProd[i][0] = ten[i] * Broadcast<0>(vec);
288 outProd[i][1] = ten[i] * Broadcast<1>(vec);
289 outProd[i][2] = ten[i] * Broadcast<2>(vec);
290 }
291 return outProd;
292}
293
294template <typename T>
296 Simd<T, 4> const& vec,
297 NdArray<Simd<T, 4>, 3, 3> const& ten) {
298 NdArray<Simd<T, 4>, 3, 3, 3> outProd;
299 for (int i = 0; i < 3; ++i) {
300 outProd[0][i] = ten[i] * Broadcast<0>(vec);
301 outProd[1][i] = ten[i] * Broadcast<1>(vec);
302 outProd[2][i] = ten[i] * Broadcast<2>(vec);
303 }
304 return outProd;
305}
306
307template <typename T>
309 NdArray<Simd<T, 4>, 3> const& mat0,
310 NdArray<Simd<T, 4>, 3> const& mat1) {
311 NdArray<Simd<T, 4>, 3, 3, 3> outProd;
312 for (int i = 0; i < 3; ++i) {
313 outProd[i][0] = mat0 * Broadcast<0>(mat1[i]);
314 outProd[i][1] = mat0 * Broadcast<1>(mat1[i]);
315 outProd[i][2] = mat0 * Broadcast<2>(mat1[i]);
316 }
317 return outProd;
318}
319
320template <typename T>
322 // {Vec4r{a0b0, a1b1, a2b2, *}, Vec4r{a0b1, a0b2, a1b2, *}}
323 return {a * b, Shuffle<0, 0, 1, 3>(a) * Shuffle<1, 2, 2, 3>(b)};
324}
325
326template <typename T>
328 NdArray<Simd<T, 4>, 2, 2> result;
329 for (int i = 0; i < 2; i++) {
330 for (int j = 0; j < 2; j++) {
331 result[i][j] = a[2 * i + j] * b;
332 }
333 }
334 return result;
335}
336
337/**************************************************************************************************
338 Inner Product
339*/
340
341template <typename T>
343 T result = {};
344 for (int i = 0; i < 3; ++i) {
345 result += Dot<3>(A[i], B[i]);
346 }
347 return result;
348}
349
350template <typename T, size_t N, size_t M>
352 T result = T{};
353 for (size_t i = 0; i < N; ++i) {
354 result += Dot(A[i], B[i]);
355 }
356 return result;
357}
358
359/**************************************************************************************************
360 Invert
361*/
362
363template <typename T>
365 return Invert(A, Det(A));
366}
367
368template <typename T>
370 MOCHI_ASSERT_VERBOSE(!NearEqual(det, T(0), T(ScalarType<T>(1e-16))), "Non-invertible matrix.");
371 T const invDet = T(1) / det;
372 return {
373 NdArray<T, 2>{A[1][1] * invDet, -A[0][1] * invDet},
374 NdArray<T, 2>{-A[1][0] * invDet, A[0][0] * invDet}};
375}
376
377template <typename T>
379 return Invert(A, Det(A));
380}
381
382template <typename T>
384 MOCHI_ASSERT_VERBOSE(!NearEqual(det, T(0), T(ScalarType<T>(1e-16))), "Non-invertible matrix.");
385 NdArray<T, 3, 3> cofactorTranspose{
387 A[1][1] * A[2][2] - A[1][2] * A[2][1],
388 A[0][2] * A[2][1] - A[0][1] * A[2][2],
389 A[0][1] * A[1][2] - A[0][2] * A[1][1]},
391 A[1][2] * A[2][0] - A[1][0] * A[2][2],
392 A[0][0] * A[2][2] - A[0][2] * A[2][0],
393 A[0][2] * A[1][0] - A[0][0] * A[1][2]},
395 A[1][0] * A[2][1] - A[1][1] * A[2][0],
396 A[0][1] * A[2][0] - A[0][0] * A[2][1],
397 A[0][0] * A[1][1] - A[0][1] * A[1][0]}};
398 return cofactorTranspose / det;
399}
400
401template <typename T>
402inline void PseudoInvert(NdArray<T, 3, 2> const& a, NdArray<T, 2, 3>* outInv, T* outDet) {
403 NdArray<T, 2, 3> const at = Transpose(a);
404 NdArray<T, 3, 3> const coef_1 = Outer(at[0], at[1]) - Outer(at[1], at[0]);
405 T const coef_2 = Dot(DotVecMat(at[0], coef_1), at[1]);
406 (*outInv)[0] = DotMatVec(coef_1, at[1]) / coef_2;
407 (*outInv)[1] = -DotMatVec(coef_1, at[0]) / coef_2;
408 *outDet = Norm(Cross(at[0], at[1]));
409}
410
411template <typename T>
412MOCHI_FORCE_INLINE constexpr T
413Minor4x4(T const m[16], int r0, int r1, int r2, int c0, int c1, int c2) {
414 return m[4 * r0 + c0] * (m[4 * r1 + c1] * m[4 * r2 + c2] - m[4 * r2 + c1] * m[4 * r1 + c2]) -
415 m[4 * r0 + c1] * (m[4 * r1 + c0] * m[4 * r2 + c2] - m[4 * r2 + c0] * m[4 * r1 + c2]) +
416 m[4 * r0 + c2] * (m[4 * r1 + c0] * m[4 * r2 + c1] - m[4 * r2 + c0] * m[4 * r1 + c1]);
417}
418
419template <typename T>
420inline constexpr void Cofactor4x4(T const m[16], T adjOut[16]) {
421 adjOut[0] = Minor4x4(m, 1, 2, 3, 1, 2, 3);
422 adjOut[1] = -Minor4x4(m, 0, 2, 3, 1, 2, 3);
423 adjOut[2] = Minor4x4(m, 0, 1, 3, 1, 2, 3);
424 adjOut[3] = -Minor4x4(m, 0, 1, 2, 1, 2, 3);
425 adjOut[4] = -Minor4x4(m, 1, 2, 3, 0, 2, 3);
426 adjOut[5] = Minor4x4(m, 0, 2, 3, 0, 2, 3);
427 adjOut[6] = -Minor4x4(m, 0, 1, 3, 0, 2, 3);
428 adjOut[7] = Minor4x4(m, 0, 1, 2, 0, 2, 3);
429 adjOut[8] = Minor4x4(m, 1, 2, 3, 0, 1, 3);
430 adjOut[9] = -Minor4x4(m, 0, 2, 3, 0, 1, 3);
431 adjOut[10] = Minor4x4(m, 0, 1, 3, 0, 1, 3);
432 adjOut[11] = -Minor4x4(m, 0, 1, 2, 0, 1, 3);
433 adjOut[12] = -Minor4x4(m, 1, 2, 3, 0, 1, 2);
434 adjOut[13] = Minor4x4(m, 0, 2, 3, 0, 1, 2);
435 adjOut[14] = -Minor4x4(m, 0, 1, 3, 0, 1, 2);
436 adjOut[15] = Minor4x4(m, 0, 1, 2, 0, 1, 2);
437}
438
439template <typename T>
440inline constexpr T Det4x4(T const m[16]) {
441 return m[0] * Minor4x4(m, 1, 2, 3, 1, 2, 3) - m[1] * Minor4x4(m, 1, 2, 3, 0, 2, 3) +
442 m[2] * Minor4x4(m, 1, 2, 3, 0, 1, 3) - m[3] * Minor4x4(m, 1, 2, 3, 0, 1, 2);
443}
444
445template <typename T>
446inline constexpr void InvertRowMajor4x4(T const m[16], T invOut[16]) {
447 Cofactor4x4(m, invOut);
448
449 T det = Det4x4(m);
450
451 MOCHI_ASSERT_VERBOSE(!NearEqual(det, T(0), T(ScalarType<T>(1e-16))), "Non-invertible matrix.");
452
453 T inv_det = T(1) / det;
454 for (int i = 0; i < 16; ++i) {
455 invOut[i] = invOut[i] * inv_det;
456 }
457}
458
459template <typename T>
462 InvertRowMajor4x4(&A[0][0], &out[0][0]);
463 return out;
464}
465
466template <typename T>
468 // This calls the non-SIMD implementation above. Could probably be much faster with a proper SIMD
469 // implementation.
470 NdArray<Simd<T, 4>, 4> out;
471 static_assert(sizeof(NdArray<Simd<T, 4>, 4>) == sizeof(NdArray<T, 4, 4>));
472 InvertRowMajor4x4(reinterpret_cast<T const*>(&A[0]), reinterpret_cast<T*>(&out[0]));
473 return out;
474}
475
476template <typename T>
478 return Invert3x3(mat, VDet3x3(mat));
479}
480
481template <typename T>
483 NdArray<Simd<T, 4>, 3> const& mat,
484 Simd<T, 4> det) {
485 MOCHI_ASSERT_VERBOSE(!NearEqual(Get0(det), T(0), T(1.e-16)), "Non-invertible matrix");
486 return Transpose3x3(Cofactor3x3(mat)) / det;
487}
488
489template <typename T>
491 return Invert3x3(mat, Simd<T, 4>{det});
492}
493
494template <typename T>
498
499// Implementation inspired by:
500// https://lxjk.github.io/2017/09/03/Fast-4x4-Matrix-Inverse-with-SSE-SIMD-Explained.html
501//
502template <typename T>
504 NdArray<Simd<T, 4>, 4> const& mat) {
505#if MOCHI_ASSERT_VERBOSE_ENABLED
506 constexpr T tol = T{10} * kDefaultNearEqualEpsilon<T>;
507
509 (mat[0][3] == 0) && (mat[1][3] == 0) && (mat[2][3] == 0) && (mat[3][3] == 1),
510 "Expected a transformation matrix where the last column is {0, 0, 0, 1}");
511
512 auto n0 = Norm<3>(mat[0]);
513 auto n1 = Norm<3>(mat[1]);
514 auto n2 = Norm<3>(mat[2]);
516 NearZero(Dot<3>(mat[0], mat[1]), tol * n0 * n1) &&
517 NearZero(Dot<3>(mat[1], mat[2]), tol * n1 * n2) &&
518 NearZero(Dot<3>(mat[2], mat[0]), tol * n0 * n2),
519 "Expected a transformation matrix with orthogonal basis vectors");
520
521#endif // MOCHI_ASSERT_VERBOSE_ENABLED
522
523 NdArray<Simd<T, 4>, 4> r;
524
525 // Transpose 3x3. We know m03 = m13 = m23 = 0
526 auto t0 = Shuffle<0, 1, 0, 1>(mat[0], mat[1]); // 00, 01, 10, 11
527 auto t1 = Shuffle<2, 3, 2, 3>(mat[0], mat[1]); // 02, 03, 12, 13
528 r[0] = Shuffle<0, 2, 0, 3>(t0, mat[2]); // 00, 10, 20, 23(=0)
529 r[1] = Shuffle<1, 3, 1, 3>(t0, mat[2]); // 01, 11, 21, 23(=0)
530 r[2] = Shuffle<0, 2, 2, 3>(t1, mat[2]); // 02, 12, 22, 23(=0)
531
532 // Invert scale
533 auto sizeSqr = r[0] * r[0] + r[1] * r[1] + r[2] * r[2];
535 AllTrue<3>(sizeSqr > Sqr(tol)),
536 "Transformation matrix has degenerate scale on at least one axis");
537 auto rSizeSqr = T(1) / ToSimdPoint(sizeSqr);
538 r[0] *= rSizeSqr;
539 r[1] *= rSizeSqr;
540 r[2] *= rSizeSqr;
541
542 // Last row (translation)
543 r[3] = r[0] * Broadcast<0>(mat[3]) + r[1] * Broadcast<1>(mat[3]) + r[2] * Broadcast<2>(mat[3]);
544 r[3] = ToSimdPoint(-r[3]);
545
546 return r;
547}
548
549template <typename T>
551 // This performs 8 SIMD shuffles to transpose the input and another 8 to transpose the result
552 // (thus about 16 cycles slower than InvertTransformationTransposed). This could be improved if we
553 // think it is worth the effort.
555}
556
557/**************************************************************************************************
558 Matrix Builders
559*/
560
561template <size_t N, typename T>
563 NdArray<T, N, N> result = {};
564 for (size_t i = 0; i < result.size(); ++i) {
565 result[i][i] = valueOnDiagonal;
566 }
567 return result;
568}
569
570template <size_t N, typename T>
572 NdArray<T, N, N> result = {};
573 for (size_t i = 0; i < result.size(); ++i) {
574 result[i][i] = diagonalVector[i];
575 }
576 return result;
577}
578
579template <typename T>
580MOCHI_FORCE_INLINE constexpr NdArray<T, 2, 2> SymMatrix2x2(T a00, T a01, T a11) {
581 return {NdArray<T, 2>{a00, a01}, NdArray<T, 2>{a01, a11}};
582}
583
584template <size_t N, typename T>
586 return DiagonalMatrix<N, T>((T)1);
587}
588
589// Return an NxN array with a given array down the diagonal.
590template <size_t N, typename T>
592 static_assert(N >= 1 && N <= 4, "Requires 1-4 rows");
593 using V = Simd<T, 4>;
594 NdArray<V, N> result = {};
595 auto zeros = SimdZero<V>();
596 if constexpr (N >= 1) {
597 result[0] = Blend<0, 1, 1, 1>(diagonalVector, zeros);
598 }
599 if constexpr (N >= 2) {
600 result[1] = Blend<1, 0, 1, 1>(diagonalVector, zeros);
601 }
602 if constexpr (N >= 3) {
603 result[2] = Blend<1, 1, 0, 1>(diagonalVector, zeros);
604 }
605 if constexpr (N == 4) {
606 result[3] = Blend<1, 1, 1, 0>(diagonalVector, zeros);
607 }
608 return result;
609}
610
611template <size_t N, typename T>
613 return VDiagonalMatrix<N>(Simd<T, 4>{valueOnDiagonal});
614}
615
616template <size_t N, typename T>
620
621/**************************************************************************************************
622 Matrix Determinant
623*/
624
625template <typename T>
627 return A[0][0] * A[1][1] - A[0][1] * A[1][0];
628}
629
630template <typename T>
632 return //
633 A[0][0] * (A[1][1] * A[2][2] - A[1][2] * A[2][1]) -
634 A[0][1] * (A[1][0] * A[2][2] - A[1][2] * A[2][0]) +
635 A[0][2] * (A[1][0] * A[2][1] - A[1][1] * A[2][0]);
636}
637
638template <typename T>
640 return Det4x4(&A[0][0]);
641}
642
643template <typename T, size_t N>
645 static_assert(N == 3 || N == 4, "Expected a 3x3 or 4x4 Simd matrix");
646
647 // Equivalent to:
648 //
649 // return
650 // A[0][0] * (A[1][1] * A[2][2] - A[1][2] * A[2][1]) -
651 // A[0][1] * (A[1][0] * A[2][2] - A[1][2] * A[2][0]) +
652 // A[0][2] * (A[1][0] * A[2][1] - A[1][1] * A[2][0]);
653 //
654 auto const a = Shuffle<1, 0, 0, 0>(A[1]);
655 auto const b = Shuffle<2, 2, 1, 0>(A[2]);
656 auto const c = Shuffle<2, 2, 1, 0>(A[1]);
657 auto const d = Shuffle<1, 0, 0, 0>(A[2]);
658 auto const result = A[0] * (a * b - c * d);
659 return Broadcast<0>(result) - Broadcast<1>(result) + Broadcast<2>(result);
660}
661
662// Calculate the determinant of the 3x3 portion of a Vec4r[N]. Ignores the last column.
663template <typename T, size_t N>
665 return Get<0>(VDet3x3(A));
666}
667
668template <typename T>
670 return A[0] * A[3] - A[1] * A[2];
671}
672/**************************************************************************************************
673 Matrix Transpose
674*/
675
676template <typename T, size_t N, size_t M>
678 NdArray<T, M, N> result = {};
679 for (size_t i = 0; i < M; ++i) {
680 for (size_t j = 0; j < N; ++j) {
681 result[i][j] = mat[j][i];
682 }
683 }
684 return result;
685}
686
687template <typename T>
689 // Adapted from _MM_TRANSPOSE4_PS
690 auto const temp0 = Shuffle<0, 1, 0, 1>(m[0], m[1]);
691 auto const temp2 = Shuffle<2, 3, 2, 3>(m[0], m[1]);
692 auto const temp1 = Shuffle<0, 1, 0, 1>(m[2], m[3]);
693 auto const temp3 = Shuffle<2, 3, 2, 3>(m[2], m[3]);
694 return {
695 Shuffle<0, 2, 0, 2>(temp0, temp1),
696 Shuffle<1, 3, 1, 3>(temp0, temp1),
697 Shuffle<0, 2, 0, 2>(temp2, temp3),
698 Shuffle<1, 3, 1, 3>(temp2, temp3),
699 };
700}
701
702template <typename T>
704 // Input is row-major: [a00, a01, a10, a11]
705 // Output is row-major: [a00, a10, a01, a11]
706 return Shuffle<0, 2, 1, 3>(m);
707}
708
709template <typename T, size_t D0>
711 static_assert(D0 == 3 || D0 == 4, "Expected 3x3 or 4x3 matrix");
712 // Transpose the 3x3 portion. Fill the 4th column with m[2][3]
713 auto const temp0 = Shuffle<0, 1, 0, 1>(m[0], m[1]);
714 auto const temp1 = Shuffle<2, 3, 2, 3>(m[0], m[1]);
715 return {
716 Shuffle<0, 2, 0, 3>(temp0, m[2]),
717 Shuffle<1, 3, 1, 3>(temp0, m[2]),
718 Shuffle<0, 2, 2, 3>(temp1, m[2]),
719 };
720}
721
722/**************************************************************************************************
723 Matrix Cofactor
724*/
725
726template <typename T>
728 NdArray<T, 2, 2> cofactor{
729 NdArray<T, 2>{mat[1][1], -mat[1][0]}, NdArray<T, 2>{-mat[0][1], mat[0][0]}};
730 return cofactor;
731}
732
733template <typename T>
734inline constexpr NdArray<T, 3, 3> Cofactor(NdArray<T, 3, 3> const& mat) {
735 NdArray<T, 3, 3> cofactor{
737 mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1],
738 mat[1][2] * mat[2][0] - mat[1][0] * mat[2][2],
739 mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0]},
741 mat[0][2] * mat[2][1] - mat[0][1] * mat[2][2],
742 mat[0][0] * mat[2][2] - mat[0][2] * mat[2][0],
743 mat[0][1] * mat[2][0] - mat[0][0] * mat[2][1]},
745 mat[0][1] * mat[1][2] - mat[0][2] * mat[1][1],
746 mat[0][2] * mat[1][0] - mat[0][0] * mat[1][2],
747 mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]}};
748 return cofactor;
749}
750
751// Calculate the cofactor matrix from the 3x3 portion of a Vec4r[3].
752// The last column of the result should be ignored.
753template <typename T>
755 // Equivalent to Transpose(Invert(mat, 1_r)), which reduces to:
756 //
757 // return {
758 // NdArray<T, 3>{mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1],
759 // mat[1][2] * mat[2][0] - mat[1][0] * mat[2][2],
760 // mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0]},
761 // NdArray<T, 3>{mat[0][2] * mat[2][1] - mat[0][1] * mat[2][2],
762 // mat[0][0] * mat[2][2] - mat[0][2] * mat[2][0],
763 // mat[0][1] * mat[2][0] - mat[0][0] * mat[2][1]},
764 // NdArray<T, 3>{mat[0][1] * mat[1][2] - mat[0][2] * mat[1][1],
765 // mat[0][2] * mat[1][0] - mat[0][0] * mat[1][2],
766 // mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]},
767 // };
768
769 // Prepare multiples for row0 of the result
770 auto const a1 = Shuffle<1, 2, 2, 0>(mat[1], mat[1]);
771 auto const b1 = Shuffle<2, 1, 0, 2>(mat[2], mat[2]);
772 auto const a2 = mat[1]; // Shuffle<0, 1, 0, 0>(mat[1], mat[1]);
773 auto const b2 = Shuffle<1, 0, 0, 0>(mat[2], mat[2]);
774
775 // Prepare multiplies for row1 of the result
776 auto const c1 = Shuffle<2, 1, 0, 2>(mat[0], mat[0]);
777 auto const d1 = Shuffle<1, 2, 2, 0>(mat[2], mat[2]);
778 auto const c2 = Shuffle<1, 0, 0, 0>(mat[0], mat[0]);
779 auto const d2 = mat[2]; // Shuffle<0, 1, 0, 0>(mat[2], mat[2]);
780
781 // Prepare multiples for row2 of the result
782 auto const e1 = Shuffle<1, 2, 2, 0>(mat[0], mat[0]);
783 auto const f1 = Shuffle<2, 1, 0, 2>(mat[1], mat[1]);
784 auto const e2 = mat[0]; // Shuffle<0, 1, 0, 0>(mat[0], mat[0]);
785 auto const f2 = Shuffle<1, 0, 0, 0>(mat[1], mat[1]);
786
787 // Multiply cofactors
788 auto const a1b1 = a1 * b1;
789 auto const a2b2 = a2 * b2;
790 auto const c1d1 = c1 * d1;
791 auto const c2d2 = c2 * d2;
792 auto const e1f1 = e1 * f1;
793 auto const e2f2 = e2 * f2;
794
795 // Shuffle results of multiples so they can be subtracted and returned.
796 return {
797 Shuffle<0, 2, 0, 0>(a1b1, a2b2) - Shuffle<1, 3, 1, 0>(a1b1, a2b2),
798 Shuffle<0, 2, 0, 0>(c1d1, c2d2) - Shuffle<1, 3, 1, 0>(c1d1, c2d2),
799 Shuffle<0, 2, 0, 0>(e1f1, e2f2) - Shuffle<1, 3, 1, 0>(e1f1, e2f2),
800 };
801}
802
803// Calculate the cofactor matrix from the symmetric 2x2 SIMD matrix.
804template <typename T>
808
809// Calculate the cofactor matrix from the symmetric 3x3 SIMD matrix.
810template <typename T>
812 // Equivalent to Transpose(Invert(mat, 1_r)), which reduces to:
813 //
814 // |bc - ff, ef - dc, df - be|
815 // | · , ac - ee, de - af|
816 // | · , · , ab - dd|
817 //
818 // diag = (bc - ff, ac - ee, ab - dd)
819 // offd = (ef - dc, df - be, de - af)
820
821 auto diag = Shuffle<1, 0, 0, 3>(mat[0]) * Shuffle<2, 2, 1, 3>(mat[0]) -
822 Shuffle<2, 1, 0, 3>(mat[1] * mat[1]);
823
824 auto offd = Shuffle<1, 0, 0, 3>(mat[1]) * Shuffle<2, 2, 1, 3>(mat[1]) -
825 mat[1] * Shuffle<2, 1, 0, 3>(mat[0]);
826
827 return NdArray<Simd<T, 4>, 2>{diag, offd};
828}
829
830/**************************************************************************************************
831 Matrix Trace (sum of diagonal elements)
832*/
833template <typename T, size_t DimTotal, size_t DimTrace>
835 static_assert(DimTrace <= DimTotal, "Invalid dimensions");
836 static_assert(DimTotal >= 1, "Invalid dimensions");
837 static_assert(DimTrace >= 1, "Invalid dimensions");
838 T trace = T(0);
839 if constexpr (DimTrace >= 1) {
840 trace += mat[0][0];
841 }
842 if constexpr (DimTrace >= 2) {
843 trace += mat[1][1];
844 }
845 if constexpr (DimTrace >= 3) {
846 trace += mat[2][2];
847 }
848 if constexpr (DimTrace >= 4) {
849 trace += mat[3][3];
850 }
851 if constexpr (DimTrace >= 5) {
852 for (int i = 4; i < DimTrace; ++i) {
853 trace += mat[i][i];
854 }
855 }
856 return trace;
857}
858
859template <typename T>
860MOCHI_FORCE_INLINE constexpr T Trace2x2(Simd<T, 4> const& mat) {
861 return mat[0] + mat[3];
862}
863
864template <typename T>
865MOCHI_FORCE_INLINE constexpr T Trace3x3(NdArray<Simd<T, 4>, 3> const& mat) {
866 return Get<0>(mat[0]) + Get<1>(mat[1]) + Get<2>(mat[2]);
867}
868
869/**************************************************************************************************
870 Skew-symmetric matrix
871*/
872
873// Return the 3x3 skew-symmetric matrix [v] of a vector v, s.t. [v] * u = v x u, [v] = -[v]^T
874template <typename T>
876 T const zero{0};
877 return {
878 NdArray<T, 3>{zero, -v[2], v[1]},
879 NdArray<T, 3>{v[2], zero, -v[0]},
880 NdArray<T, 3>{-v[1], v[0], zero}};
881}
882
883// Return the 3x3 skew-symmetric matrix [v] of a vector v, s.t. [v] * u = v x u, [v] = -[v]^T
884template <typename T>
886 Simd<T, 4> v = Set<3>(vector, T(0));
887 return {
891 };
892}
893
894// Return the vector v s.t. skew(v) is the anti-symmetric part of the input matrix
895template <typename T>
897 return T(0.5) *
899 matrix[2][1] - matrix[1][2], matrix[0][2] - matrix[2][0], matrix[1][0] - matrix[0][1]};
900}
901
902// Computes the first derivative of Skew(v)
903template <typename T>
905 using V = Simd<T, 4>;
906 NdArray<V, 3, 3> dskew;
907 dskew[0] = Skew3(SimdBasisVector<0, V>());
908 dskew[1] = Skew3(SimdBasisVector<1, V>());
909 dskew[2] = Skew3(SimdBasisVector<2, V>());
910 return dskew;
911}
912
913/**************************************************************************************************
914 Vector Derivatives
915*/
916
917// Derivative of normalized 3-vector w.r.t. vector
918template <typename T>
920 T const vNorm = Norm<3>(v) + std::numeric_limits<T>::min();
921 Simd<T, 4> const vHat = v / vNorm;
922 return (VEye<3, T>() - Outer3(vHat, vHat)) / vNorm;
923}
924
925template <typename T, size_t N>
927 return DNormalize(v, NormSqr(v));
928}
929
930template <typename T, size_t N>
932 constexpr auto kMin = std::numeric_limits<ScalarType<T>>::min();
933 static_assert(kMin > 0); // Check numeric_limits has been correctly specialized.
934 T const invNorm = T(1) / (Sqrt(sqrNorm) + T(kMin));
935 NdArray<T, N> const n = v * invNorm;
936 NdArray<T, N, N> result{};
937 for (size_t i = 0; i < N; ++i) {
938 for (size_t j = 0; j < N; ++j) {
939 result[i][j] = -n[i] * n[j] * invNorm;
940 }
941 result[i][i] += invNorm;
942 }
943 return result;
944}
945
946/**************************************************************************************************
947 Matrix Row/Column Selection
948*/
949
950// Retrieves the largest row (in the L-2 sense) of the given matrix. Optionally outputs
951// its squared norm.
952template <typename T, size_t D0, size_t D1>
953inline constexpr auto LargestRow(NdArray<T, D0, D1> const& A, T* sqrNorm) {
954 // Compute per-row norms.
955 NdArray<T, D0> sqrNorms = {};
956 for (size_t i = 0; i < D0; ++i) {
957 sqrNorms[i] = NormSqr(A[i]);
958 }
959
960 // Identify row with largest squared L2 norm.
961 auto index = ArgMax(sqrNorms);
962
963 // Store its squared norm if requested.
964 if (sqrNorm != nullptr) {
965 *sqrNorm = sqrNorms[index];
966 }
967
968 // Return largest row.
969 return A[index];
970}
971
972inline auto LargestRowColSym2x2(VSymMatrix2x2r A, Vec4r& outNormSqr) {
973 // Vectors are:
974 // r₁ = (a, c)
975 // r₂ = (c, b)
976
977 // Evaluate squared norms.
978 Vec4r entriesSqr = A * A; // (a², c², b², ?)
979 Vec4r temp0 = Broadcast<1>(entriesSqr); // (c², c², ?, ?)
980 Vec4r normSqr = entriesSqr + temp0; // (|r₁|², ?, |r₂|², ?)
981 real r1NormSqr = Get<0>(normSqr);
982 real r2NormSqr = Get<2>(normSqr);
983
984 // Shuffle values according depending on which row was larger
985 if (r1NormSqr > r2NormSqr) {
986 outNormSqr = r1NormSqr;
987 return Shuffle<0, 1, 3, 3>(A);
988 } else {
989 outNormSqr = r2NormSqr;
990 return Shuffle<1, 2, 3, 3>(A);
991 }
992}
993
994inline auto LargestRowColSym3x3(VSymMatrix3x3r A, Vec4r& outNormSqr) {
995 // Vectors are:
996 // r₁ = (a, d, e)
997 // r₂ = (d, b, f)
998 // r₃ = (e, f, c)
999
1000 // Evaluate squared norms.
1001 Vec4r diagSqr = A[0] * A[0]; // (a², b², c², ?)
1002 Vec4r offdSqr = A[1] * A[1]; // (d², e², f², ?)
1003 Vec4r temp0 = Shuffle<0, 0, 2, 3>(offdSqr); // (d², d², f², ?)
1004 Vec4r temp1 = Shuffle<1, 2, 1, 3>(offdSqr); // (e², f², e², ?)
1005 Vec4r normSqr = diagSqr + temp0 + temp1; // (|r₁|², |r₂|², |r₃|², ?)
1006
1007 // Blend entries according to norms.
1008 real max = HMax<3>(normSqr);
1009 if (max == Get<0>(normSqr)) { // If r1 was the largest row
1010 outNormSqr = Broadcast<0>(normSqr);
1011 return Blend<0, 1, 1, 0>(A[0], Shuffle<0, 0, 1, 0>(A[1]));
1012 } else if (max == Get<1>(normSqr)) { // If r2 was the largest row
1013 outNormSqr = Broadcast<1>(normSqr);
1014 return Blend<1, 0, 1, 0>(A[0], Shuffle<0, 0, 2, 0>(A[1]));
1015 } else { // If r3 was the largest row
1016 outNormSqr = Broadcast<2>(normSqr);
1017 return Blend<1, 1, 0, 0>(A[0], Shuffle<1, 2, 0, 0>(A[1]));
1018 }
1019}
1020
1021} // namespace superdex
static constexpr int size()
Definition nd_array.h:78
constexpr SizeT size() const
Definition span.h:106
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
Definition debug.h:102
#define MOCHI_FORCE_INLINE
constexpr NdArray< T, M > DotVecMat(NdArray< T, N > const &a, NdArray< T, N, M > const &b)
T Dot(Simd< T, N > a, Simd< T, N > b)
Definition simd.h:666
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
Definition simd_inl.h:270
constexpr T Trace(NdArray< T, DimTotal, DimTotal > const &mat)
constexpr NdArray< T, N, N > DNormalize(NdArray< T, N > const &v)
constexpr NdArray< T, 2, 2 > SymMatrix2x2(T a00, T a01, T a11)
NdArray< Simd< T, 4 >, 3 > DNormalize3(Simd< T, 4 > const &v)
Simd< T, 4 > Transpose2x2(Simd< T, 4 > const &m)
T NormSqr(Simd< T, N > a)
Definition simd_inl.h:849
NdArray< Simd< T, 4 >, 3 > Cofactor3x3(NdArray< Simd< T, 4 >, 3 > const &mat)
Simd< T, 4 > DotVecMat4x4(Simd< T, 4 > a, NdArray< Simd< T, 4 >, 4 > const &b)
Simd< T, 4 > Invert2x2(Simd< T, 4 > const &mat, T det)
NdArray< Simd< T, 4 >, 4 > InvertTransformation(NdArray< Simd< T, 4 >, 4 > const &mat)
constexpr Simd< T, 4 > InvSkew3(NdArray< Simd< T, 4 >, 3 > const &matrix)
T HSum(Simd< T, N > a)
Definition simd_inl.h:379
constexpr T kDefaultNearEqualEpsilon
V VDot(V a, V b)
Definition simd_inl.h:826
T Norm(Simd< T, N > a)
Definition simd_inl.h:854
T Det2x2(Simd< T, 4 > const &A)
bool AllTrue(T const &a)
Definition basic_utils.h:60
V SimdBasisVector()
Definition simd_inl.h:151
Simd< T, 4 > DotMatVec3xN(NdArray< Simd< T, 4 >, D0 > const &m, Simd< T, 4 > v)
constexpr auto LargestRow(NdArray< T, D0, D1 > const &A, T *sqrNorm=nullptr)
NdArray< Simd< T, 4 >, 3, 3 > VDSkew3()
constexpr NdArray< T, N > DotMatVec(NdArray< T, N, M > const &a, NdArray< T, M > const &b)
Simd< T, 4 > DotMatVec4x4(NdArray< Simd< T, 4 >, 4 > const &m, Simd< T, 4 > v)
Simd< T, N > Set(Simd< T, N > a, T value)
Definition simd_inl.h:315
NdArray< Simd< T, 4 >, 3 > Transpose3x3(NdArray< Simd< T, 4 >, D0 > const &m)
Simd< real, 4 > Vec4r
Definition simd.h:206
NdArray< Simd< T, 4 >, 2 > CofactorSym3x3(NdArray< Simd< T, 4 >, 2 > const &mat)
V SimdZero()
Definition simd_inl.h:146
T HMax(Simd< T, N > a)
Definition simd_inl.h:397
NdArray< Simd< T, 4 >, 4 > InvertTransformationTransposed(NdArray< Simd< T, 4 >, 4 > const &mat)
Simd< real, 4 > VSymMatrix2x2r
Special case for the SIMD representation of a 2x2 symmetric matrix.
Definition vmatrix.h:84
V Broadcast(typename V::Scalar a)
Definition simd_inl.h:115
constexpr NdArray< T, M, N > Transpose(NdArray< T, N, M > const &mat)
auto LargestRowColSym2x2(VSymMatrix2x2r A, Vec4r &outNormSqr)
Simd< T, N > Blend(Simd< T, N > a, Simd< T, N > b)
Definition simd_inl.h:285
constexpr NdArray< T, N, N > Eye()
TransformRT Invert(TransformRT const &a)
Simd< T, 4 > DotVecMat3x3(Simd< T, 4 > v, NdArray< Simd< T, 4 >, D0 > const &m)
constexpr T Colon(NdArray< T, N, M > const &A, NdArray< T, N, M > const &B)
constexpr T Minor4x4(T const m[16], int r0, int r1, int r2, int c0, int c1, int c2)
Simd< T, 4 > DotVecMat2x3(Simd< T, 4 > v, NdArray< Simd< T, 4 >, 2 > const &m)
constexpr T Sqr(T a)
NdArray< Simd< T, 4 >, 4 > Transpose4x4(NdArray< Simd< T, 4 >, 4 > const &m)
NdArray< Simd< T, 4 >, 2, 2 > Outer2(Simd< T, 4 > const &a, Simd< T, 4 > const &b)
constexpr T Sqrt(T a)
void PseudoInvert(NdArray< T, 3, 2 > const &a, NdArray< T, 2, 3 > *outInv, T *outDet)
NdArray< Simd< T, 4 >, 4 > Dot4x4(NdArray< Simd< T, 4 >, 4 > const &a, NdArray< Simd< T, 4 >, 4 > const &b)
T Colon3x3(NdArray< Simd< T, 4 >, 3 > const &A, NdArray< Simd< T, 4 >, 3 > const &B)
constexpr T Det4x4(T const m[16])
NdArray< Simd< T, 4 >, 2 > OuterSym3(Simd< T, 4 > a, Simd< T, 4 > b)
auto LargestRowColSym3x3(VSymMatrix3x3r A, Vec4r &outNormSqr)
T Get(Simd< T, N > v)
Definition simd_inl.h:300
Simd< T, 4 > DotMatVec3x3(NdArray< Simd< T, 4 >, D0 > const &m, Simd< T, 4 > v)
Simd< T, 4 > Dot2x2(Simd< T, 4 > const &a, Simd< T, 4 > const &b)
constexpr NdArray< T, 3, 3 > Skew(NdArray< T, 3 > const &v)
constexpr NdArray< Simd< T, 4 >, 3 > Skew3(Simd< T, 4 > const &vector)
NdArray< Simd< T, 4 >, 3 > Invert3x3(NdArray< Simd< T, 4 >, 3 > const &mat)
constexpr size_t ArgMax(NdArray< T, N > const &a)
constexpr NdArray< T, 2, 2 > Cofactor(NdArray< T, 2, 2 > const &mat)
T Norm3x3(NdArray< Simd< T, D1 >, D0 > const &a)
T NormSqr3x3(NdArray< Simd< T, D1 >, D0 > const &a)
constexpr void Cofactor4x4(T const m[16], T adjOut[16])
T Get0(Simd< T, N > v)
Definition simd_inl.h:295
Simd< T, 4 > CofactorSym2x2(Simd< T, 4 > const &mat)
constexpr NdArray< T, 2, 2 > Outer(NdArray< T, 2 > const &a, NdArray< T, 2 > const &b)
constexpr T Trace3x3(NdArray< Simd< T, 4 >, 3 > const &mat)
NdArray< Simd< T, 4 >, 3 > Outer3(Simd< T, 4 > a, Simd< T, 4 > b)
constexpr void InvertRowMajor4x4(T const m[16], T invOut[16])
NdArray< Simd< real, 4 >, 2 > VSymMatrix3x3r
Special case for the SIMD representation of a 3x3 symmetric matrix.
Definition vmatrix.h:96
NdArray< Simd< T, 4 >, N > VEye()
constexpr T Det(NdArray< T, 2, 2 > const &A)
bool NearEqual(TransformRT const &a, TransformRT const &b, real epsilon=kDefaultNearEqualEpsilon< real >)
constexpr NdArray< T, N, N > DiagonalMatrix(T valueOnDiagonal)
NdArray< Simd< T, 4 >, N > VDiagonalMatrix(T valueOnDiagonal)
constexpr T Trace2x2(Simd< T, 4 > const &mat)
Simd< T, N > Neg(Simd< T, N > a)
Definition simd_inl.h:340
T Det3x3(NdArray< Simd< T, 4 >, N > const &A)
V ToSimdPoint(V a)
Definition simd_inl.h:174
constexpr auto NearZero(T const &a, T epsilon=kDefaultNearEqualEpsilon< T >)
constexpr NdArray< Simd< T, 4 >, 4 > Invert4x4(NdArray< Simd< T, 4 >, 4 > const &A)
constexpr NdArray< T, 3 > Cross(NdArray< T, 3 > const &a, NdArray< T, 3 > const &b)
Simd< T, 4 > VDet3x3(NdArray< Simd< T, 4 >, N > const &A)
NdArray< Simd< T, 4 >, 3 > Dot3x3(NdArray< Simd< T, 4 >, D0A > const &a, NdArray< Simd< T, 4 >, D0B > const &b)