opynsim
Unofficial C++ Documentation
Loading...
Searching...
No Matches
matrix_functions.h
Go to the documentation of this file.
1#pragma once
2
10
11#include <concepts>
12#include <cstddef>
13#include <functional>
14
15namespace osc
16{
17 template<std::floating_point T>
19 const Vector<T, 3>& eye,
20 const Vector<T, 3>& center,
21 const Vector<T, 3>& up)
22 {
24 const Vector<T, 3> s(normalize(cross(f, up)));
25 const Vector<T, 3> u(cross(s, f));
26
28 rv[0][0] = s.x();
29 rv[1][0] = s.y();
30 rv[2][0] = s.z();
31 rv[0][1] = u.x();
32 rv[1][1] = u.y();
33 rv[2][1] = u.z();
34 rv[0][2] = -f.x();
35 rv[1][2] = -f.y();
36 rv[2][2] = -f.z();
37 rv[3][0] = -dot(s, eye);
38 rv[3][1] = -dot(u, eye);
39 rv[3][2] = dot(f, eye);
40 return rv;
41 }
42
43 template<std::floating_point T, AngularUnitTraits Units>
45 {
46 if (fabs(aspect - epsilon_v<T>) <= T{}) {
47 // edge-case: some UIs ask for a perspective matrix on first frame before
48 // aspect ratio is known or the aspect ratio is NaN because of a division
49 // by zero
50 return Matrix<T, 4, 4>{T(1)};
51 }
52
53 const T tan_half_fovy = tan(vertical_field_of_view / static_cast<T>(2));
54
55 Matrix<T, 4, 4> rv(static_cast<T>(0));
56 rv[0][0] = static_cast<T>(1) / (aspect * tan_half_fovy);
57 rv[1][1] = static_cast<T>(1) / (tan_half_fovy);
58 rv[2][2] = - (z_far + z_near) / (z_far - z_near);
59 rv[2][3] = - static_cast<T>(1);
60 rv[3][2] = - (static_cast<T>(2) * z_far * z_near) / (z_far - z_near);
61 return rv;
62 }
63
64 template<std::floating_point T>
66 {
68 rv[0][0] = static_cast<T>(2) / (right - left);
69 rv[1][1] = static_cast<T>(2) / (top - bottom);
70 rv[2][2] = - static_cast<T>(2) / (z_far - z_near);
71 rv[3][0] = - (right + left) / (right - left);
72 rv[3][1] = - (top + bottom) / (top - bottom);
73 rv[3][2] = - (z_far + z_near) / (z_far - z_near);
74 return rv;
75 }
76
77 template<typename T>
79 {
81 rv[0] = m[0] * v[0];
82 rv[1] = m[1] * v[1];
83 rv[2] = m[2] * v[2];
84 rv[3] = m[3];
85 return rv;
86 }
87
88 template<std::floating_point T, AngularUnitTraits Units>
90 {
91 const T c = cos(angle);
92 const T s = sin(angle);
93
94 const Vector<T, 3> temp((T(1) - c) * axis);
95
97 rotate[0][0] = c + temp[0] * axis[0];
98 rotate[0][1] = temp[0] * axis[1] + s * axis[2];
99 rotate[0][2] = temp[0] * axis[2] - s * axis[1];
100
101 rotate[1][0] = temp[1] * axis[0] - s * axis[2];
102 rotate[1][1] = c + temp[1] * axis[1];
103 rotate[1][2] = temp[1] * axis[2] + s * axis[0];
104
105 rotate[2][0] = temp[2] * axis[0] + s * axis[1];
106 rotate[2][1] = temp[2] * axis[1] - s * axis[0];
107 rotate[2][2] = c + temp[2] * axis[2];
108
110 rv[0] = m[0] * rotate[0][0] + m[1] * rotate[0][1] + m[2] * rotate[0][2];
111 rv[1] = m[0] * rotate[1][0] + m[1] * rotate[1][1] + m[2] * rotate[1][2];
112 rv[2] = m[0] * rotate[2][0] + m[1] * rotate[2][1] + m[2] * rotate[2][2];
113 rv[3] = m[3];
114 return rv;
115 }
116
117 template<typename T>
119 {
121 rv[3] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3];
122 return rv;
123 }
124
125 template<std::floating_point T>
127 {
128 return
129 + m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
130 - m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])
131 + m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]
132 );
133 }
134
135 template<std::floating_point T>
137 {
138 const T subfactor_00 = m[2][2] * m[3][3] - m[3][2] * m[2][3];
139 const T subfactor_01 = m[2][1] * m[3][3] - m[3][1] * m[2][3];
140 const T subfactor_02 = m[2][1] * m[3][2] - m[3][1] * m[2][2];
141 const T subfactor_03 = m[2][0] * m[3][3] - m[3][0] * m[2][3];
142 const T subfactor_04 = m[2][0] * m[3][2] - m[3][0] * m[2][2];
143 const T subfactor_05 = m[2][0] * m[3][1] - m[3][0] * m[2][1];
144
146 + (m[1][1] * subfactor_00 - m[1][2] * subfactor_01 + m[1][3] * subfactor_02),
147 - (m[1][0] * subfactor_00 - m[1][2] * subfactor_03 + m[1][3] * subfactor_04),
148 + (m[1][0] * subfactor_01 - m[1][1] * subfactor_03 + m[1][3] * subfactor_05),
149 - (m[1][0] * subfactor_02 - m[1][1] * subfactor_04 + m[1][2] * subfactor_05)
150 );
151
152 return
153 m[0][0] * determinant_coef[0] + m[0][1] * determinant_coef[1] +
154 m[0][2] * determinant_coef[2] + m[0][3] * determinant_coef[3];
155 }
156
157 template<std::floating_point T>
159 {
160 const T one_over_determinant = static_cast<T>(1) / determinant_of(m);
161
163 rv[0][0] = + (m[1][1] * m[2][2] - m[2][1] * m[1][2]) * one_over_determinant;
164 rv[1][0] = - (m[1][0] * m[2][2] - m[2][0] * m[1][2]) * one_over_determinant;
165 rv[2][0] = + (m[1][0] * m[2][1] - m[2][0] * m[1][1]) * one_over_determinant;
166 rv[0][1] = - (m[0][1] * m[2][2] - m[2][1] * m[0][2]) * one_over_determinant;
167 rv[1][1] = + (m[0][0] * m[2][2] - m[2][0] * m[0][2]) * one_over_determinant;
168 rv[2][1] = - (m[0][0] * m[2][1] - m[2][0] * m[0][1]) * one_over_determinant;
169 rv[0][2] = + (m[0][1] * m[1][2] - m[1][1] * m[0][2]) * one_over_determinant;
170 rv[1][2] = - (m[0][0] * m[1][2] - m[1][0] * m[0][2]) * one_over_determinant;
171 rv[2][2] = + (m[0][0] * m[1][1] - m[1][0] * m[0][1]) * one_over_determinant;
172 return rv;
173 }
174
175 template<std::floating_point T>
177 {
178 const T coef_00 = m[2][2] * m[3][3] - m[3][2] * m[2][3];
179 const T coef_02 = m[1][2] * m[3][3] - m[3][2] * m[1][3];
180 const T coef_03 = m[1][2] * m[2][3] - m[2][2] * m[1][3];
181
182 const T coef_04 = m[2][1] * m[3][3] - m[3][1] * m[2][3];
183 const T coef_06 = m[1][1] * m[3][3] - m[3][1] * m[1][3];
184 const T coef_07 = m[1][1] * m[2][3] - m[2][1] * m[1][3];
185
186 const T coef_08 = m[2][1] * m[3][2] - m[3][1] * m[2][2];
187 const T coef_10 = m[1][1] * m[3][2] - m[3][1] * m[1][2];
188 const T coef_11 = m[1][1] * m[2][2] - m[2][1] * m[1][2];
189
190 const T coef_12 = m[2][0] * m[3][3] - m[3][0] * m[2][3];
191 const T coef_14 = m[1][0] * m[3][3] - m[3][0] * m[1][3];
192 const T coef_15 = m[1][0] * m[2][3] - m[2][0] * m[1][3];
193
194 const T coef_16 = m[2][0] * m[3][2] - m[3][0] * m[2][2];
195 const T coef_18 = m[1][0] * m[3][2] - m[3][0] * m[1][2];
196 const T coef_19 = m[1][0] * m[2][2] - m[2][0] * m[1][2];
197
198 const T coef_20 = m[2][0] * m[3][1] - m[3][0] * m[2][1];
199 const T coef_22 = m[1][0] * m[3][1] - m[3][0] * m[1][1];
200 const T coef_23 = m[1][0] * m[2][1] - m[2][0] * m[1][1];
201
208
209 const Vector<T, 4> vec_0(m[1][0], m[0][0], m[0][0], m[0][0]);
210 const Vector<T, 4> vec_1(m[1][1], m[0][1], m[0][1], m[0][1]);
211 const Vector<T, 4> vec_2(m[1][2], m[0][2], m[0][2], m[0][2]);
212 const Vector<T, 4> vec_3(m[1][3], m[0][3], m[0][3], m[0][3]);
213
218
219 const Vector<T, 4> sign_a(+1, -1, +1, -1);
220 const Vector<T, 4> sign_b(-1, +1, -1, +1);
222
223 const Vector<T, 4> row_0(inverted[0][0], inverted[1][0], inverted[2][0], inverted[3][0]);
224
225 const Vector<T, 4> dot_0(m[0] * row_0);
226 const T dot_1 = (dot_0.x() + dot_0.y()) + (dot_0.z() + dot_0.w());
227
228 const T one_over_determinant = static_cast<T>(1) / dot_1;
229
231 }
232
233 template<typename T>
235 {
237 rv[0][0] = m[0][0];
238 rv[0][1] = m[1][0];
239 rv[0][2] = m[2][0];
240
241 rv[1][0] = m[0][1];
242 rv[1][1] = m[1][1];
243 rv[1][2] = m[2][1];
244
245 rv[2][0] = m[0][2];
246 rv[2][1] = m[1][2];
247 rv[2][2] = m[2][2];
248 return rv;
249 }
250
251 template<typename T>
253 {
255 rv[0][0] = m[0][0];
256 rv[0][1] = m[1][0];
257 rv[0][2] = m[2][0];
258 rv[0][3] = m[3][0];
259
260 rv[1][0] = m[0][1];
261 rv[1][1] = m[1][1];
262 rv[1][2] = m[2][1];
263 rv[1][3] = m[3][1];
264
265 rv[2][0] = m[0][2];
266 rv[2][1] = m[1][2];
267 rv[2][2] = m[2][2];
268 rv[2][3] = m[3][2];
269
270 rv[3][0] = m[0][3];
271 rv[3][1] = m[1][3];
272 rv[3][2] = m[2][3];
273 rv[3][3] = m[3][3];
274 return rv;
275 }
276
277 // returns euler angles for performing an intrinsic, step-by-step, rotation about X, Y, and then Z
278 template<std::floating_point T>
280 {
281 const RadiansT<T> t1 = atan2(m[2][1], m[2][2]);
282 const T c2 = sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0]);
283 const RadiansT<T> t2 = atan2(-m[2][0], c2);
284 const T s1 = sin(t1);
285 const T c1 = cos(t1);
286 const RadiansT<T> t3 = atan2(s1*m[0][2] - c1*m[0][1], c1*m[1][1] - s1*m[1][2 ]);
287
288 return Vector<RadiansT<T>, 3>{-t1, -t2, -t3};
289 }
290
291 namespace detail
292 {
294 template<typename T>
296 {
297 return (a * ascl) + (b * bscl);
298 }
299
300 template<typename T>
302 {
303 return v * desiredLength / length(v);
304 }
305 }
306
307 template<typename T>
315 {
316 // Matrix decompose
317 // http://www.opensource.apple.com/source/WebCore/WebCore-514/platform/graphics/transforms/TransformationMatrix.cpp
318 // Decomposes the mode matrix to translations,rotation scale components
319
321
322 // normalize the matrix
323 if (equal_within_epsilon(local_matrix[3][3], static_cast<T>(0))) {
324 return false;
325 }
326
327 for (size_t i = 0; i < 4; ++i) {
328 for (size_t j = 0; j < 4; ++j) {
329 local_matrix[i][j] /= local_matrix[3][3];
330 }
331 }
332
333 // `perspective_matrix` is used to solve for perspective, but it also provides
334 // an easy way to test for singularity of the upper 3x3 component
336 for (size_t i = 0; i < 3; i++) {
337 perspective_matrix[i][3] = static_cast<T>(0);
338 }
339 perspective_matrix[3][3] = static_cast<T>(1);
340
342 return false;
343 }
344
345 // first, isolate perspective, which is the messiest
346 if (
347 not equal_within_epsilon(local_matrix[0][3], static_cast<T>(0)) or
348 not equal_within_epsilon(local_matrix[1][3], static_cast<T>(0)) or
349 not equal_within_epsilon(local_matrix[2][3], static_cast<T>(0)))
350 {
351 // `right_hand_side` is the right hand side of the equation
353 local_matrix[0][3],
354 local_matrix[1][3],
355 local_matrix[2][3],
356 local_matrix[3][3],
357 };
358
359 // solve the equation by inverting `perspective_matrix` and multiplying
360 // `right_hand_side` by the inverse. This is the easiest way, not
361 // necessarily the best.
363 const Matrix<T, 4, 4> transposed_inverse_perspective_matrix = transpose(inverse_perspective_matrix);// transposeMatrix4(inversePerspectiveMatrix, transposedInversePerspectiveMatrix);
364
366
367 // clear the perspective partition
368 local_matrix[0][3] = local_matrix[1][3] = local_matrix[2][3] = static_cast<T>(0);
369 local_matrix[3][3] = static_cast<T>(1);
370 }
371 else {
372 // no perspective
373 r_perspective = Vector<T, 4>(0, 0, 0, 1);
374 }
375
376 // second, take care of translation (easy).
378 local_matrix[3] = Vector<T, 4>(0, 0, 0, local_matrix[3].w());
379
380 // third/fourth, calculate the scale and shear
381 Vector<T, 3> Row[3];
383 for (size_t i = 0; i < 3; ++i) {
384 for(size_t j = 0; j < 3; ++j) {
385 Row[i][j] = local_matrix[i][j];
386 }
387 }
388
389 // compute X scale factor and normalize first row
390 r_scale.x() = length(Row[0]);
391
392 Row[0] = detail::scale(Row[0], static_cast<T>(1));
393
394 // compute XY shear factor and make 2nd row orthogonal to 1st
395 r_skew.z() = dot(Row[0], Row[1]);
396 Row[1] = detail::combine(Row[1], Row[0], static_cast<T>(1), -r_skew.z());
397
398 // compute Y scale and normalize 2nd row
399 r_scale.y() = length(Row[1]);
400 Row[1] = detail::scale(Row[1], static_cast<T>(1));
401 r_skew.z() /= r_scale.y();
402
403 // compute XZ and YZ shears, orthogonalize 3rd row
404 r_skew.y() = dot(Row[0], Row[2]);
405 Row[2] = detail::combine(Row[2], Row[0], static_cast<T>(1), -r_skew.y());
406 r_skew.x() = dot(Row[1], Row[2]);
407 Row[2] = detail::combine(Row[2], Row[1], static_cast<T>(1), -r_skew.x());
408
409 // get Z scale and normalize 3rd row
410 r_scale.z() = length(Row[2]);
411 Row[2] = detail::scale(Row[2], static_cast<T>(1));
412 r_skew.y() /= r_scale.z();
413 r_skew.x() /= r_scale.z();
414
415 // at this point, the matrix (in rows[]) is orthonormal
416 //
417 // check for a coordinate system flip. If the determinant
418 // is -1, then negate the matrix and the scaling factors.
419 Pdum3 = cross(Row[1], Row[2]);
420 if (dot(Row[0], Pdum3) < 0) {
421 for (size_t i = 0; i < 3; i++) {
422 r_scale[i] *= static_cast<T>(-1);
423 Row[i] *= static_cast<T>(-1);
424 }
425 }
426
427 // fifth (finally), get the rotations out, as described in the gem
428
429 // FIXME - Add the ability to return either quaternions (which are
430 // easier to recompose with) or Euler angles (rx, ry, rz), which
431 // are easier for authors to deal with. The latter will only be useful
432 // when we fix https://bugs.webkit.org/show_bug.cgi?id=23799, so I
433 // will leave the Euler angle code here for now.
434
435 // ret.rotateY = asin(-Row[0][2]);
436 // if (cos(ret.rotateY) != 0) {
437 // ret.rotateX = atan2(Row[1][2], Row[2][2]);
438 // ret.rotateZ = atan2(Row[0][1], Row[0][0]);
439 // }
440 // else {
441 // ret.rotateX = atan2(-Row[2][0], Row[1][1]);
442 // ret.rotateZ = 0;
443 // }
444
445 int i = 0;
446 int j = 0;
447 int k = 0;
448 T root{};
449 T trace = Row[0].x() + Row[1].y() + Row[2].z();
450 if (trace > static_cast<T>(0)) {
451 root = sqrt(trace + static_cast<T>(1.0));
452 r_orientation.w = static_cast<T>(0.5) * root;
453 root = static_cast<T>(0.5) / root;
454 r_orientation.x = root * (Row[1].z() - Row[2].y());
455 r_orientation.y = root * (Row[2].x() - Row[0].z());
456 r_orientation.z = root * (Row[0].y() - Row[1].x());
457 } // end_panel if > 0
458 else {
459 static int next[3] = {1, 2, 0};
460 i = 0;
461 if(Row[1].y() > Row[0].x()) i = 1;
462 if(Row[2].z() > Row[i][i]) i = 2;
463 j = next[i];
464 k = next[j];
465
466 const int off = 1;
467
468 root = sqrt(Row[i][i] - Row[j][j] - Row[k][k] + static_cast<T>(1.0));
469
470 r_orientation[i + off] = static_cast<T>(0.5) * root;
471 root = static_cast<T>(0.5) / root;
472 r_orientation[j + off] = root * (Row[i][j] + Row[j][i]);
473 r_orientation[k + off] = root * (Row[i][k] + Row[k][i]);
474 r_orientation.w = root * (Row[j][k] - Row[k][j]);
475 } // end_panel if <= 0
476
477 return true;
478 }
479
480 template<std::floating_point T>
482 {
483 // google: "Adjugate Matrix": it's related to the cofactor matrix and is
484 // related to the inverse of a matrix through:
485 //
486 // inverse(M) = Adjugate(M) / determinant_of(M);
487
489 rv[0][0] = + (m[1][1] * m[2][2] - m[2][1] * m[1][2]);
490 rv[1][0] = - (m[1][0] * m[2][2] - m[2][0] * m[1][2]);
491 rv[2][0] = + (m[1][0] * m[2][1] - m[2][0] * m[1][1]);
492 rv[0][1] = - (m[0][1] * m[2][2] - m[2][1] * m[0][2]);
493 rv[1][1] = + (m[0][0] * m[2][2] - m[2][0] * m[0][2]);
494 rv[2][1] = - (m[0][0] * m[2][1] - m[2][0] * m[0][1]);
495 rv[0][2] = + (m[0][1] * m[1][2] - m[1][1] * m[0][2]);
496 rv[1][2] = - (m[0][0] * m[1][2] - m[1][0] * m[0][2]);
497 rv[2][2] = + (m[0][0] * m[1][1] - m[1][0] * m[0][1]);
498 return rv;
499 }
500
501 template<std::floating_point T>
503 {
504 // "On the Transformation of Surface Normals" by Andrew Glassner (1987)
505 //
506 // "One option is to replace the inverse with the adjoint of M. The
507 // adjoint is attractive because it always exists, even when M is
508 // singular. The inverse and the adjoint are related by:
509 //
510 // inverse(M) = adjoint(M) / determinant_of(M);
511 //
512 // so, when the inverse exists, they only differ by a constant factor.
513 // Therefore, using adjoint(M) instead of inverse(M) only affects the
514 // magnitude of the resulting normal vector. Normal vectors have to
515 // be normalized after mutiplication with a normal matrix anyway, so
516 // nothing is lost"
517
519 return adjugate(transpose(top_left));
520 }
521
522 template<std::floating_point T>
527
528 template<typename T, size_t C, size_t R>
529 constexpr const T* value_ptr(const Matrix<T, C, R>& m)
530 {
531 return m.data()->data();
532 }
533
534 template<typename T, size_t C, size_t R>
536 {
537 return m.data()->data();
538 }
539}
540
541template<typename T, size_t C, size_t R>
542struct std::hash<osc::Matrix<T, C, R>> final {
543 size_t operator()(const osc::Matrix<T, C, R>& m) const
544 {
545 return osc::hash_range(m);
546 }
547};
Definition angle.h:25
Definition vector.h:63
constexpr reference x()
Returns (*this)[0]
Definition vector.h:157
constexpr reference z()
Returns (*this)[2]
Definition vector.h:167
constexpr reference y()
Returns (*this)[1]
Definition vector.h:162
constexpr pointer data()
Definition vector.h:141
Vector< T, 3 > scale(const Vector< T, 3 > &v, T desiredLength)
Definition matrix_functions.h:301
Vector< T, 3 > combine(const Vector< T, 3 > &a, const Vector< T, 3 > &b, T ascl, T bscl)
Make a linear combination of two vectors and return the result.
Definition matrix_functions.h:295
Definition custom_decoration_generator.h:5
EulerAngles extract_eulers_xyz(const Quaternion &)
bool equal_within_epsilon(T x, T y)
Definition common_functions.h:265
GenType cos(GenType v)
Definition trigonometric_functions.h:34
constexpr Matrix< T, 3, 3 > adjugate(const Matrix< T, 3, 3 > &m)
Definition matrix_functions.h:481
T length(const Vector< T, N > &v)
Definition geometric_functions.h:60
Matrix< T, 4, 4 > translate(const Matrix< T, 4, 4 > &m, const Vector< T, 3 > &v)
Definition matrix_functions.h:118
Matrix< T, 4, 4 > look_at(const Vector< T, 3 > &eye, const Vector< T, 3 > &center, const Vector< T, 3 > &up)
Definition matrix_functions.h:18
constexpr T dot(T x, T y)
Definition geometric_functions.h:29
Matrix< T, 4, 4 > perspective(Angle< T, Units > vertical_field_of_view, T aspect, T z_near, T z_far)
Definition matrix_functions.h:44
Matrix< T, 4, 4 > ortho(T left, T right, T bottom, T top, T z_near, T z_far)
Definition matrix_functions.h:65
constexpr U to(T &&value)
Definition conversion.h:56
GenType sin(GenType v)
Definition trigonometric_functions.h:13
Matrix< T, 3, 3 > normal_matrix(const Matrix< T, 4, 4 > &m)
Definition matrix_functions.h:502
Matrix< T, 4, 4 > rotate(const Matrix< T, 4, 4 > &m, Angle< T, Units > angle, Vector< T, 3 > axis)
Definition matrix_functions.h:89
T inverse(const Matrix< T, 3, 3 > &m)
Definition matrix_functions.h:158
Matrix< T, 4, 4 > normal_matrix4x4(const Matrix< T, 4, 4 > &m)
Definition matrix_functions.h:523
Vector< T, N > normalize(const Vector< T, N > &v)
Definition geometric_functions.h:74
RadiansT< Rep > atan2(Rep x, Rep y)
Definition trigonometric_functions.h:100
T determinant_of(const Matrix< T, 3, 3 > &m)
Definition matrix_functions.h:126
Matrix< T, 4, 4 > scale(const Matrix< T, 4, 4 > &m, const Vector< T, 3 > &v)
Definition matrix_functions.h:78
constexpr Matrix< T, 3, 3 > transpose(const Matrix< T, 3, 3 > &m)
Definition matrix_functions.h:234
GenType tan(GenType v)
Definition trigonometric_functions.h:58
T sqrt(T num)
Definition geometric_functions.h:14
bool decompose(const Matrix< T, 4, 4 > &model_matrix, Vector< T, 3 > &r_scale, Qua< T > &r_orientation, Vector< T, 3 > &r_translation, Vector< T, 3 > &r_skew, Vector< T, 4 > &r_perspective)
Definition matrix_functions.h:308
constexpr const T * value_ptr(const Rgba< T > &rgba)
Definition rgba.h:180
size_t hash_range(const Range &range) noexcept(noexcept(hash_combine(0, *std::ranges::begin(range))))
Definition hash_helpers.h:36
constexpr CoordinateDirection cross(CoordinateDirection x, CoordinateDirection y)
Definition coordinate_direction.h:117
constexpr pointer data()
Definition matrix3x3.h:113
Definition matrix.h:15
Definition qua.h:22
size_t operator()(const osc::Matrix< T, C, R > &m) const
Definition matrix_functions.h:543