jaulib v1.3.0
Jau Support Library (C++, Java, ..)
test_math_quaternion.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2024 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24#include <thread>
25#include <cassert>
26#include <cinttypes>
27#include <cstring>
28
30
31#include <jau/math/mat4f.hpp>
33
34using namespace jau;
35using namespace jau::math;
36
37static const float PI = (float)M_PI;
38static const float HALF_PI = (float)M_PI_2;
39static const float QUARTER_PI = (float)M_PI_4;
40static const float EPSILON = std::numeric_limits<float>::epsilon();
41
42static const Quat4f QUAT_IDENT = Quat4f(0, 0, 0, 1);
43
44static const Vec3f ZERO = Vec3f ( 0, 0, 0 );
45static const Vec3f ONE = Vec3f ( 1, 1, 1 );
46static const Vec3f NEG_ONE = Vec3f ( -1, -1, -1 );
47static const Vec3f UNIT_X = Vec3f ( 1, 0, 0 );
48static const Vec3f UNIT_Y = Vec3f ( 0, 1, 0 );
49static const Vec3f UNIT_Z = Vec3f ( 0, 0, 1 );
50static const Vec3f NEG_UNIT_X = Vec3f ( -1, 0, 0 );
51static const Vec3f NEG_UNIT_Y = Vec3f ( 0, -1, 0 );
52static const Vec3f NEG_UNIT_Z = Vec3f ( 0, 0, -1 );
53
54static const Vec4f NEG_ONE_v4 = Vec4f ( -1, -1, -1, 0 );
55static const Vec4f ONE_v4 = Vec4f ( 1, 1, 1, 0 );
56
57constexpr static const bool DEBUG_MODE = false;
58
59//
60// Basic
61//
62
63TEST_CASE( "Test 01 Normalize", "[quaternion][linear_algebra][math]" ) {
64 const Quat4f quat(0, 1, 2, 3);
65 const Quat4f quat2 = Quat4f(quat).normalize();
66 // Assert.assertTrue(Math.abs(1 - quat2.magnitude()) <= MACH_EPSILON);
67 REQUIRE( true == jau::equals( 0.0f, std::abs( 1 - quat2.magnitude() ) ) ) ;
68}
69
70TEST_CASE( "Test 02 Rotate Zero Vector", "[quaternion][linear_algebra][math]" ) {
71 Quat4f quat;
72 const Vec3f rotVec0 = quat.rotateVector(ZERO);
73 REQUIRE( ZERO == rotVec0 );
74}
75
76TEST_CASE( "Test 03 Invert and Conugate", "[quaternion][linear_algebra][math]" ) {
77 // inversion check
78 {
79 const Quat4f quat0(0, 1, 2, 3);
80 Quat4f quat0Inv = Quat4f(quat0).invert();
81 REQUIRE( quat0 == quat0Inv.invert() );
82 }
83 // conjugate check
84 {
85 const Quat4f quat0(-1, -2, -3, 4);
86 const Quat4f quat0Conj = Quat4f( 1, 2, 3, 4).conjugate();
87 REQUIRE( quat0 == quat0Conj );
88 }
89}
90
91TEST_CASE( "Test 04 Dot", "[quaternion][linear_algebra][math]" ) {
92 const Quat4f quat(7, 2, 5, -1);
93 REQUIRE( 35.0f == quat.dot(3, 1, 2, -2));
94 REQUIRE( -11.0f == quat.dot(Quat4f(-1, 1, -1, 1)));
95}
96
97//
98// Conversion
99//
100
101TEST_CASE( "Test 10 Angle Axis", "[quaternion][linear_algebra][math]" ) {
102 Quat4f quat1 = Quat4f().setFromAngleAxis(HALF_PI, Vec3f ( 2, 0, 0 ));
103 Quat4f quat2 = Quat4f().setFromAngleNormalAxis(HALF_PI, Vec3f ( 1, 0, 0 ) );
104
105 REQUIRE( quat2 == quat1 );
106 REQUIRE( true == jau::equals(0.0f, 1 - quat2.magnitude()));
107 REQUIRE( 1 - quat1.magnitude() <= std::numeric_limits<float>::epsilon() );
108
109 Vec3f vecOut1 = quat1.rotateVector(ONE);
110 Vec3f vecOut2 = quat2.rotateVector(ONE);
111 REQUIRE( vecOut1 == vecOut2 );
112 REQUIRE( true == jau::equals( 0.0f, std::abs( vecOut1.dist(vecOut2) ) ) );
113
114 vecOut1 = quat1.rotateVector(UNIT_Z);
115 REQUIRE( true == jau::equals( 0.0f, std::abs( NEG_UNIT_Y.dist(vecOut1) ) ) );
116
118 REQUIRE( QUAT_IDENT == quat2 );
119
120 float angle = quat1.toAngleAxis(vecOut1);
121 quat2.setFromAngleAxis(angle, vecOut1);
122 REQUIRE( quat1 == quat2 );
123
124 quat1.set(0, 0, 0, 0);
125 angle = quat1.toAngleAxis(vecOut1);
126 REQUIRE(0.0f == angle);
127 REQUIRE(UNIT_X == vecOut1);
128}
129
130TEST_CASE( "Test 11 From Vec to Vec", "[quaternion][linear_algebra][math]" ) {
131 Vec3f vecOut;
132 Quat4f quat;
134
135 Quat4f quat2;
137 REQUIRE( quat == quat2 );
138
140 REQUIRE( quat2 == quat );
141
143 vecOut = quat.rotateVector(UNIT_Z);
144 REQUIRE_THAT( std::abs( NEG_UNIT_Z.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
145
147 vecOut = quat.rotateVector(UNIT_X);
148 REQUIRE_THAT( std::abs( NEG_UNIT_X.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
149
151 vecOut = quat.rotateVector(UNIT_Y);
152 REQUIRE_THAT( std::abs( NEG_UNIT_Y.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
153
155 vecOut = quat.rotateVector(ONE);
156 REQUIRE_THAT( std::abs( NEG_ONE.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
157
158 quat.setFromVectors(ZERO, ZERO);
159 REQUIRE( QUAT_IDENT == quat );
160}
161
162
163TEST_CASE( "Test 12 From and to Euler Angles", "[quaternion][linear_algebra][math]" ) {
164 // Y.Z.X -> X.Y.Z
165 Quat4f quat;
166 Vec3f angles0Exp( 0, HALF_PI, 0 );
167 quat.setFromEuler(angles0Exp);
168 REQUIRE_THAT( quat.magnitude(), Catch::Matchers::WithinAbs(1.0f, EPSILON) );
169
170 Vec3f angles0Has = quat.toEuler();
171 REQUIRE( angles0Exp == angles0Has );
172
173 Quat4f quat2;
174 quat2.setFromEuler(angles0Has);
175 REQUIRE( quat == quat2 );
176
177 ///
178
179 Vec3f angles1Exp(0, 0, -HALF_PI);
180 quat.setFromEuler(angles1Exp);
181 REQUIRE_THAT( quat.magnitude(), Catch::Matchers::WithinAbs(1.0f, EPSILON) );
182
183 Vec3f angles1Has = quat.toEuler();
184 REQUIRE( angles1Exp == angles1Has );
185
186 quat2.setFromEuler(angles1Has);
187 REQUIRE( quat == quat2 );
188
189 ///
190
191 Vec3f angles2Exp(HALF_PI, 0, 0);
192 quat.setFromEuler(angles2Exp);
193 REQUIRE_THAT( quat.magnitude(), Catch::Matchers::WithinAbs(1.0f, EPSILON) );
194
195 Vec3f angles2Has = quat.toEuler();
196 REQUIRE( angles2Exp == angles2Has );
197
198 quat2.setFromEuler(angles2Has);
199 REQUIRE( quat == quat2 );
200}
201
202TEST_CASE( "Test 13 From Euler Angles and Rotate Vec", "[quaternion][linear_algebra][math]" ) {
203 Quat4f quat;
204 quat.setFromEuler(0, HALF_PI, 0); // 90 degrees y-axis
205 REQUIRE_THAT( quat.magnitude(), Catch::Matchers::WithinAbs(1.0f, EPSILON) );
206
207 Vec3f v2 = quat.rotateVector(UNIT_X);
208 REQUIRE_THAT( std::abs( NEG_UNIT_Z.dist(v2)), Catch::Matchers::WithinAbs(0.0f, EPSILON) );
209
210 quat.setFromEuler(0, 0, -HALF_PI);
211 REQUIRE_THAT( quat.magnitude(), Catch::Matchers::WithinAbs(1.0f, EPSILON) );
212 v2 = quat.rotateVector(UNIT_X);
213 REQUIRE_THAT( std::abs( NEG_UNIT_Y.dist(v2)), Catch::Matchers::WithinAbs(0.0f, EPSILON) );
214
215 quat.setFromEuler(HALF_PI, 0, 0);
216 REQUIRE_THAT( quat.magnitude(), Catch::Matchers::WithinAbs(1.0f, EPSILON) );
217 v2 = quat.rotateVector(UNIT_Y);
218 REQUIRE_THAT( std::abs( UNIT_Z.dist(v2)), Catch::Matchers::WithinAbs(0.0f, EPSILON) );
219}
220
221TEST_CASE( "Test 14 Matrix", "[matrix][quaternion][linear_algebra][math]" ) {
222 Vec3f vecHas;
223 Vec3f vecOut3;
224 // Vec4f vecOut4;
225 Mat4f mat1;
226 Mat4f mat2;
227 Quat4f quat1;
228 Quat4f quat2;
229
230 //
231 // IDENTITY CHECK
232 //
233 mat1.loadIdentity();
234 quat1.set(0, 0, 0, 0);
235 quat1.toMatrix(mat2);
236 // mat2 = quat.toMatrix();
237 REQUIRE( mat1 == mat2 );
238
239 //
240 // 90 degrees rotation on X
241 //
242
243 float a = HALF_PI;
244 float mat1_0[] = { // Column Order
245 1, 0, 0, 0, //
246 0, std::cos(a), std::sin(a), 0, //
247 0, -std::sin(a), std::cos(a), 0,
248 0, 0, 0, 1 };
249 mat1.load( mat1_0 );
250 {
251 // Matrix4f load() <-> toFloats()
252 float mat2_0[16];
253 mat1.get(mat2_0);
254 for(int i=0; i<16; ++i) { REQUIRE_THAT( mat2_0[i], Catch::Matchers::WithinAbs(mat1_0[i], EPSILON) ); }
255 }
256 {
257 // Validate Matrix via Euler rotation on Quat4f!
258 quat1.setFromEuler(a, 0, 0);
259 {
260 quat1.toMatrix(mat2);
261 // mat2 = quat.toMatrix();
262 REQUIRE( mat1 == mat2 );
263 quat2.setFromMat(mat1);
264 REQUIRE( quat1 == quat2 );
265
266 float mat2_0[16];
267 mat2.get(mat2_0);
268 Mat4f mat2c;
269 mat2c.load(mat2_0);
270 REQUIRE( mat2 == mat2c );
271 REQUIRE( mat1 == mat2c );
272 }
273 vecHas = quat1.rotateVector(UNIT_Y);
274 REQUIRE_THAT( std::abs( UNIT_Z.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
275 }
276 {
277 quat1.toMatrix(mat1);
278 quat2.setFromMat(mat1);
279 REQUIRE( quat1 == quat2 );
280 }
281 vecHas = quat1.rotateVector(UNIT_Y);
282 REQUIRE_THAT( std::abs( UNIT_Z.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
283
284 quat1.toMatrix(mat2);
285 REQUIRE( mat1 == mat2 );
286
287 vecHas = quat1.rotateVector(NEG_ONE);
288 {
289 // use Vec3f math
290 mat2.mulVec3(NEG_ONE, vecOut3);
291 REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
292 REQUIRE( vecHas == vecOut3);
293 }
294 {
295 // use Vec4f math
296 (mat2 * NEG_ONE_v4).getVec3(vecOut3);
297
298 REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
299 REQUIRE( vecHas == vecOut3);
300 }
301
302 //
303 // 180 degrees rotation on X
304 //
305 a = PI;
306 {
307 float fa[] = { // Column Order
308 1, 0, 0, 0, //
309 0, std::cos(a), std::sin(a), 0, //
310 0, -std::sin(a), std::cos(a), 0,
311 0, 0, 0, 1 };
312 mat1.load( fa );
313 }
314 {
315 // Validate Matrix via Euler rotation on Quat4f!
316 quat1.setFromEuler(a, 0, 0);
317 quat1.toMatrix(mat2);
318 REQUIRE(mat1 == mat2);
319 vecHas = quat1.rotateVector(UNIT_Y);
320 REQUIRE_THAT( std::abs( NEG_UNIT_Y.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
321 }
322 quat1.setFromMat(mat1);
323 vecHas = quat1.rotateVector(UNIT_Y);
324 REQUIRE_THAT( std::abs( NEG_UNIT_Y.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
325
326 quat1.toMatrix(mat2);
327 REQUIRE(mat1 == mat2);
328
329 vecHas = quat1.rotateVector(ONE);
330 (mat2 * ONE_v4).getVec3(vecOut3);
331 REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
332
333 //
334 // 180 degrees rotation on Y
335 //
336 a = PI;
337 {
338 float fa[] = { // Column Order
339 std::cos(a), 0, -std::sin(a), 0, //
340 0, 1, 0, 0, //
341 std::sin(a), 0, std::cos(a), 0,
342 0, 0, 0, 1 };
343 mat1.load( fa );
344 }
345 {
346 // Validate Matrix via Euler rotation on Quat4f!
347 quat1.setFromEuler(0, a, 0);
348 quat1.toMatrix(mat2);
349 REQUIRE(mat1 == mat2);
350
351 vecHas = quat1.rotateVector(UNIT_X);
352 REQUIRE_THAT( std::abs( NEG_UNIT_X.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
353 }
354 quat1.setFromMat(mat1);
355 vecHas = quat1.rotateVector(UNIT_X);
356 REQUIRE_THAT( std::abs( NEG_UNIT_X.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
357
358 quat1.toMatrix(mat2);
359 REQUIRE(mat1 == mat2);
360
361 vecHas = quat1.rotateVector(NEG_ONE);
362 (mat2 * NEG_ONE_v4).getVec3(vecOut3);
363 REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
364
365 //
366 // 180 degrees rotation on Z
367 //
368 a = PI;
369 {
370 float fa[] = { // Column Order
371 std::cos(a), std::sin(a), 0, 0, //
372 -std::sin(a), std::cos(a), 0, 0,
373 0, 0, 1, 0,
374 0, 0, 0, 1 };
375 mat1.load( fa );
376 }
377 {
378 // Validate Matrix via Euler rotation on Quat4f!
379 quat1.setFromEuler(0, 0, a);
380 quat1.toMatrix(mat2);
381 REQUIRE(mat1 == mat2);
382 vecHas = quat1.rotateVector(UNIT_X);
383 REQUIRE_THAT( std::abs( NEG_UNIT_X.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
384 }
385 quat1.setFromMat(mat1);
386 vecHas = quat1.rotateVector(UNIT_X);
387 REQUIRE_THAT( std::abs( NEG_UNIT_X.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
388
389 quat1.toMatrix(mat2);
390 REQUIRE(mat1 == mat2);
391
392 vecHas = quat1.rotateVector(ONE);
393 vecOut3 = to_vec3( mat2 * ONE_v4 );
394 // (mat2 * ONE_v4).getVec3(vecOut3);
395 REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
396
397 //
398 // Test Matrix-Columns
399 //
400
401 a = QUARTER_PI;
402 Vec3f vecExp0( std::cos(a), std::sin(a), 0);
403 Vec3f vecExp1(-std::sin(a), std::cos(a), 0);
404 Vec3f vecExp2( 0, 0, 1);
405 Vec3f vecCol;
406 {
407 float fa[] = { // Column Order
408 std::cos(a), std::sin(a), 0, 0, //
409 -std::sin(a), std::cos(a), 0, 0,
410 0, 0, 1, 0,
411 0, 0, 0, 1 };
412 mat1.load( fa );
413 }
414 mat1.getColumn(0, vecCol);
415 REQUIRE(vecExp0 == vecCol);
416 REQUIRE_THAT( std::abs( vecExp0.dist(vecCol) ), Catch::Matchers::WithinAbs(0.0f, EPSILON) );
417
418 mat1.getColumn(1, vecCol);
419 REQUIRE(vecExp1 == vecCol);
420 REQUIRE_THAT( std::abs( vecExp1.dist(vecCol) ), Catch::Matchers::WithinAbs(0.0f, EPSILON) );
421
422 mat1.getColumn(2, vecCol);
423 REQUIRE(vecExp2 == vecCol);
424 REQUIRE_THAT( std::abs( vecExp2.dist(vecCol) ), Catch::Matchers::WithinAbs(0.0f, EPSILON) );
425}
426
427TEST_CASE( "Test 15a Axes And Matrix", "[matrix][quaternion][linear_algebra][math]" ) {
428 Vec3f eulerExp( 0, HALF_PI, 0 );
429 Mat4f matExp1;
430 matExp1.setToRotationEuler(eulerExp);
431
432 Mat4f matHas;
433 Quat4f quat1;
434 quat1.setFromEuler(eulerExp);
435 quat1.toMatrix(matHas);
436 REQUIRE( matExp1 == matHas);
437
438 Quat4f quat2;
439 quat2.setFromMat(matExp1);
440 Vec3f eulerHas = quat2.toEuler();
441 std::cout << "exp-euler " << eulerExp << std::endl;
442 std::cout << "has-euler " << eulerHas << std::endl;
443 REQUIRE(eulerExp == eulerHas);
444
445 REQUIRE(quat2 == quat1);
446
447 Vec3f angles = quat2.toEuler();
448 quat1.setFromEuler(angles);
449 REQUIRE(quat2 == quat1);
450}
451
452TEST_CASE( "Test 15b Axes And Matrix", "[matrix][quaternion][linear_algebra][math]" ) {
453 Vec3f eulerExp(HALF_PI, 0, 0);
454 Mat4f matExp;
455 matExp.setToRotationEuler(eulerExp);
456
457 Mat4f matHas;
458 Quat4f quat1;
459 quat1.setFromEuler(eulerExp);
460 quat1.toMatrix(matHas);
461 REQUIRE( matExp == matHas);
462
463 Quat4f quat2;
464 quat2.setFromMat(matExp);
465 Vec3f eulerHas = quat2.toEuler();
466 std::cout << "exp-euler " << eulerExp << std::endl;
467 std::cout << "has-euler " << eulerHas << std::endl;
468 REQUIRE(eulerExp == eulerHas);
469
470 REQUIRE(quat2 == quat1);
471
472 Vec3f angles = quat2.toEuler();
473 quat1.setFromEuler(angles);
474 REQUIRE(quat2 == quat1);
475}
476
477TEST_CASE( "Test 15c Axes And Matrix", "[matrix][quaternion][linear_algebra][math]" ) {
478 Vec3f eulerExp1(QUARTER_PI, HALF_PI, 0); // 45 degr on X, 90 degr on Y
479 float eulerExp0[3];
480 eulerExp1.get(eulerExp0);
481
482 Mat4f matExp;
483 matExp.setToRotationEuler(eulerExp1);
484
485 Mat4f matHas;
486 Quat4f quat1;
487 quat1.setFromEuler(eulerExp1);
488 quat1.toMatrix(matHas);
489 printf("float epsilon %.20f\n", EPSILON);
490 std::cout << "matExp " << matExp << std::endl;
491 std::cout << "matHas " << matHas << std::endl;
492 // REQUIRE(matExp == matHas);
493 REQUIRE( matExp.equals(matHas, 2*EPSILON) ); // due to clang -O3 math optimizations, gcc -O3 OK
494 // eps 0.00000011920928955078
495 // exp -0.000000044
496 // has 0.000000000 (gcc -O3, ok)
497 // has 0.000000119 (clang -O3, err)
498
499 Quat4f quat2;
500 quat2.setFromMat(matExp);
501 Vec3f eulerHas1 = quat2.toEuler();
502 std::cout << "exp-euler " << eulerExp1 << std::endl;
503 std::cout << "has-euler " << eulerHas1 << std::endl;
504 std::cout << "diff-euler " << ( eulerExp1 - eulerHas1 ) << std::endl;
505 {
506 float eulerHas0[3];
507 eulerHas1.get(eulerHas0);
508 Vec3f eulerHas0v(eulerHas0), eulerExp0v(eulerExp0);
509 REQUIRE( eulerHas0v == eulerExp0v );
510 }
511 REQUIRE_THAT( std::abs( eulerExp1.dist(eulerHas1) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
512 REQUIRE( true == eulerExp1.equals(eulerHas1, Quat4f::allowed_deviation) );
513
514 REQUIRE(quat2 == quat1);
515
516 Vec3f angles = quat2.toEuler();
517 quat1.setFromEuler(angles);
518 REQUIRE(quat2 == quat1);
519}
520
521//
522// Functions
523//
524
525TEST_CASE( "Test 20 Add Subtract", "[quaternion][linear_algebra][math]" ) {
526 {
527 Quat4f quatExp(1, 2, 3, 4);
528 Quat4f quat1(0, 1, 2, 3);
529 Quat4f quat2(1, 1, 1, 1);
530
531 // +=
532 Quat4f quatHas = quat1;
533 quatHas += quat2;
534 REQUIRE(quatExp == quatHas);
535
536 // +
537 quatHas = quat1 + quat2;
538 REQUIRE(quatExp == quatHas);
539
540 }
541 {
542 Quat4f quatExp(-1, 0, 1, 2);
543 Quat4f quat1, quat2, quatHas;
544 quat1.set(0, 1, 2, 3);
545 quat2.set(1, 1, 1, 1);
546
547 // -=
548 quatHas = quat1;
549 quatHas -= quat2; // q3 = q1 - q2
550 REQUIRE(quatExp == quatHas);
551
552 // -
553 quatHas = quat1 - quat2;
554 REQUIRE(quatExp == quatHas);
555 }
556}
557
558TEST_CASE( "Test 21 Multiply", "[quaternion][linear_algebra][math]" ) {
559 // scalar
560 {
561 Quat4f quatExp(1, 2, 4, 6);
562 Quat4f quat1(0.5f, 1, 2, 3);
563 Quat4f quat2;
564
565 // *= scalar
566 quat2 = quat1;
567 quat2 *= 2; // q2 = q1 * 2
568 REQUIRE(quatExp == quat2);
569
570 // * scalar
571 quat2 = quat1 * 2.0f;
572 REQUIRE(quatExp == quat2);
573 // * scalar
574 quat2 = 2.0f * quat1;
575 REQUIRE(quatExp == quat2);
576 }
577
578 {
579 Quat4f quat1;
580 Quat4f quat2;
581
582 //
583 // mul and cmp rotated vector
584 //
585 {
586 // q *= q
587 quat1.setFromAngleNormalAxis(QUARTER_PI, UNIT_Y); // 45 degr on Y
588 quat2 = quat1;
589 quat2 *= quat1; // q2 = q1 * q1 -> 2 * 45 degr -> 90 degr on Y
590 Vec3f vecOut = quat2.rotateVector(UNIT_Z);
591 REQUIRE_THAT( std::abs( UNIT_X.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
592
593 // q * q
594 quat1.setFromAngleNormalAxis(QUARTER_PI, UNIT_Y); // 45 degr on Y
595 quat2 = quat1 * quat1; // q2 = q1 * q1 -> 2 * 45 degr -> 90 degr on Y
596 vecOut = quat2.rotateVector(UNIT_Z);
597 REQUIRE_THAT( std::abs( UNIT_X.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
598 }
599 {
600 quat1.setFromAngleNormalAxis(QUARTER_PI, UNIT_Y); // 45 degr on Y
601 quat2.setFromAngleNormalAxis(HALF_PI, UNIT_Y); // 90 degr on Y
602 quat1 *= quat1; // q1 = q1 * q1 -> 2 * 45 degr -> 90 degr on Y
603 quat1 *= quat2; // q1 = q1 * q2 -> 2 * 90 degr -> 180 degr on Y
604 Vec3f vecOut = quat1.rotateVector(UNIT_Z);
605 REQUIRE_THAT( std::abs( NEG_UNIT_Z.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
606
607 quat1.setFromAngleNormalAxis(QUARTER_PI, UNIT_Y); // 45 degr on Y
608 quat2.setFromAngleNormalAxis(HALF_PI, UNIT_Y); // 90 degr on Y
609 quat1 = quat1 * quat1 * quat2; // q1 = q1 * q1 * q2 -> 2 * 90 degr -> 180 degr on Y
610 quat1.rotateVector(UNIT_Z, vecOut);
611 REQUIRE_THAT( std::abs( NEG_UNIT_Z.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
612 }
613 {
614 quat2.setFromEuler(0, HALF_PI, 0);
615 quat1 *= quat2; // q1 = q1 * q2 = q1 * rotMat(0, 90degr, 0)
616 Vec3f vecOut = quat1.rotateVector(UNIT_Z);
617 REQUIRE_THAT( std::abs( NEG_UNIT_X.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
618 }
619 }
620}
621
622TEST_CASE( "Test 22 Invert-Mult-Normal-Conjugate", "[quaternion][linear_algebra][math]" ) {
623 Quat4f quat0(0, 1, 2, 3);
624 Quat4f quat1(quat0);
625 Quat4f quat2(quat0);
626 quat1.invert(); // q1 = invert(q0)
627 quat2 *= quat1; // q2 = q0 * q1 = q0 * invert(q0)
628 REQUIRE(QUAT_IDENT == quat2);
629 quat1.invert();
630 REQUIRE(quat0 == quat1);
631
632 // normalized version
634 quat1 = quat0;
635 quat1.invert(); // q1 = invert(q0)
636 quat2 = quat0 * quat1; // q2 = q0 * q1 = q0 * invert(q0)
637 REQUIRE(QUAT_IDENT == quat2);
638 quat1.invert();
639 REQUIRE(quat0 == quat1);
640
641 // conjugate check
642 quat0.set(-1.0f, -2.0f, -3.0f, 4.0f);
643 quat1.set( 1.0f, 2.0f, 3.0f, 4.0f);
644 quat2 = quat1;
645 quat2.conjugate();
646 REQUIRE(quat0 == quat2);
647}
648
649TEST_CASE( "Test 23 Rotation Order", "[quaternion][linear_algebra][math]" ) {
650 {
651 Quat4f quat1; quat1.setFromEuler( -2 * HALF_PI, 0, 0); // -180 degr X
652 Quat4f quat2; quat2.rotateByAngleX( -2 * HALF_PI); // angle: -180 degrees, axis X
653 REQUIRE(quat1 == quat2);
654 }
655 {
656 Quat4f quat1 = Quat4f().setFromEuler( HALF_PI, 0, 0); // 90 degr X
657 Quat4f quat2 = Quat4f().rotateByAngleX( HALF_PI); // angle: 90 degrees, axis X
658 REQUIRE(quat1 == quat2);
659 }
660 {
661 Quat4f quat1 = Quat4f().setFromEuler( HALF_PI, QUARTER_PI, 0);
662 Quat4f quat2 = Quat4f().rotateByAngleY(QUARTER_PI).rotateByAngleX(HALF_PI);
663 REQUIRE(quat1 == quat2);
664 }
665 {
666 Quat4f quat1 = Quat4f().setFromEuler( PI, QUARTER_PI, HALF_PI);
667 Quat4f quat2 = Quat4f().rotateByAngleY(QUARTER_PI).rotateByAngleZ(HALF_PI).rotateByAngleX(PI);
668 REQUIRE(quat1 == quat2);
669 }
670
671 Vec3f vecExp;
672 Vec3f vecRot;
673 Quat4f quat;
674
675 // Try a new way with new angles...
677 vecRot.set(1, 1, 1);
678 quat.rotateVector(vecRot, vecRot); // in-place
679
680 // expected
681 Quat4f worker;
682 // put together matrix, then apply to vector, so YZX
684 vecExp.set(1, 1, 1);
685 vecExp = quat.rotateVector(vecExp); // new vec3, assign back
686 REQUIRE_THAT( vecExp.dist(vecRot), Catch::Matchers::WithinAbs(0.0f, EPSILON) );
687 REQUIRE(vecExp == vecRot);
688
689 // test axis rotation methods against general purpose
690 // X AXIS
691 vecExp.set(1, 1, 1);
692 vecRot.set(1, 1, 1);
693 worker.setIdentity().rotateByAngleX(QUARTER_PI).rotateVector(vecExp, vecExp);
694 worker.setIdentity().rotateByAngleNormalAxis(QUARTER_PI, 1, 0, 0).rotateVector(vecRot, vecRot);
695 REQUIRE_THAT( vecExp.dist(vecRot), Catch::Matchers::WithinAbs(0.0f, EPSILON) );
696 REQUIRE(vecExp == vecRot);
697
698 // Y AXIS
699 vecExp.set(1, 1, 1);
700 vecRot.set(1, 1, 1);
701 worker.setIdentity().rotateByAngleY(QUARTER_PI).rotateVector(vecExp, vecExp);
702 worker.setIdentity().rotateByAngleNormalAxis(QUARTER_PI, 0, 1, 0).rotateVector(vecRot, vecRot);
703 REQUIRE_THAT( vecExp.dist(vecRot), Catch::Matchers::WithinAbs(0.0f, EPSILON) );
704 REQUIRE(vecExp == vecRot);
705
706 // Z AXIS
707 vecExp.set(1, 1, 1);
708 vecRot.set(1, 1, 1);
709 worker.setIdentity().rotateByAngleZ(QUARTER_PI).rotateVector(vecExp, vecExp);
710 worker.setIdentity().rotateByAngleNormalAxis(QUARTER_PI, 0, 0, 1).rotateVector(vecRot, vecRot);
711 REQUIRE_THAT( vecExp.dist(vecRot), Catch::Matchers::WithinAbs(0.0f, EPSILON) );
712 REQUIRE(vecExp == vecRot);
713
714 quat = worker;
715 worker.rotateByAngleNormalAxis(0, 0, 0, 0);
716 REQUIRE(quat == worker);
717}
718
719TEST_CASE( "Test 24 Axes", "[quaternion][linear_algebra][math]" ) {
720 Quat4f quat0 = Quat4f().rotateByAngleX(QUARTER_PI).rotateByAngleY(HALF_PI);
721 Mat4f rotMat;
722 quat0.toMatrix(rotMat);
723 Vec3f xAxis, yAxis, zAxis;
724 rotMat.getColumn(0, xAxis);
725 rotMat.getColumn(1, yAxis);
726 rotMat.getColumn(2, zAxis);
727
728 Quat4f quat1 = Quat4f().setFromAxes(xAxis, yAxis, zAxis);
729 REQUIRE(quat0 == quat1);
730 Quat4f quat2 = Quat4f().setFromMat(rotMat);
731 REQUIRE(quat2 == quat1);
732
733 quat1.toAxes(xAxis, yAxis, zAxis, rotMat);
734 quat2.setFromAxes(xAxis, yAxis, zAxis);
735 REQUIRE(quat0 == quat2);
736 REQUIRE(quat1 == quat2);
737}
738
739TEST_CASE( "Test 25 Slerp", "[quaternion][linear_algebra][math]" ) {
740 Quat4f quat1; // angle: 0 degrees
741 Quat4f quat2 = Quat4f().rotateByAngleY(HALF_PI); // angle: 90 degrees, axis Y
742
743 Vec3f vecExp( std::sin(QUARTER_PI), 0, std::sin(QUARTER_PI) );
744 Vec3f vecHas;
745 Quat4f quatS;
746 // System.err.println("Slerp #01: 1/2 * 90 degrees Y");
747 quatS.setSlerp(quat1, quat2, 0.5f);
748 quatS.rotateVector(UNIT_Z, vecHas);
749 // System.err.println("exp0 "+Arrays.toString(vecExp));
750 // System.err.println("has0 "+Arrays.toString(vecHas));
751 REQUIRE_THAT( std::abs( vecExp.dist(vecHas)), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
752
753 if( !vecExp.equals(vecHas) ) {
754 std::cout << "Deviation: " << vecExp << ", " << vecHas << ": " << ( vecExp - vecHas ) << ", dist " << vecExp.dist(vecHas) << std::endl;
755 }
756 // REQUIRE(vecExp == vecHas);
757
758 // delta == 100%
759 quat2.setIdentity().rotateByAngleZ(PI); // angle: 180 degrees, axis Z
760 // System.err.println("Slerp #02: 1 * 180 degrees Z");
761 quatS.setSlerp(quat1, quat2, 1.0f);
762 quatS.rotateVector(UNIT_X, vecHas);
763 // System.err.println("exp0 "+Arrays.toString(NEG_UNIT_X));
764 // System.err.println("has0 "+Arrays.toString(vecHas));
765 REQUIRE_THAT( std::abs( NEG_UNIT_X.dist(vecHas)), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
766 REQUIRE(NEG_UNIT_X == vecHas);
767
768 quat2.setIdentity().rotateByAngleZ(PI); // angle: 180 degrees, axis Z
769 // System.err.println("Slerp #03: 1/2 * 180 degrees Z");
770 quatS.setSlerp(quat1, quat2, 0.5f);
771 quatS.rotateVector(UNIT_X, vecHas);
772 // System.err.println("exp0 "+Arrays.toString(UNIT_Y));
773 // System.err.println("has0 "+Arrays.toString(vecHas));
774 REQUIRE_THAT( std::abs( UNIT_Y.dist(vecHas)), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
775 if( !UNIT_Y.equals(vecHas) ) {
776 std::cout << "Deviation: " << UNIT_Y << ", " << vecHas << ": " << ( UNIT_Y - vecHas ) << ", dist " << UNIT_Y.dist(vecHas) << std::endl;
777 }
778 // REQUIRE(UNIT_Y == vecHas);
779
780 // delta == 0%
781 quat2.setIdentity().rotateByAngleZ(PI); // angle: 180 degrees, axis Z
782 // System.err.println("Slerp #04: 0 * 180 degrees Z");
783 quatS.setSlerp(quat1, quat2, 0.0f);
784 quatS.rotateVector(UNIT_X, vecHas);
785 // System.err.println("exp0 "+Arrays.toString(UNIT_X));
786 // System.err.println("has0 "+Arrays.toString(vecHas));
787 REQUIRE_THAT( std::abs( UNIT_X.dist(vecHas)), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
788 REQUIRE(UNIT_X == vecHas);
789
790 // a==b
791 quat2.setIdentity();
792 // System.err.println("Slerp #05: 1/4 * 0 degrees");
793 quatS.setSlerp(quat1, quat2, 0.25f); // 1/4 of identity .. NOP
794 quatS.rotateVector(UNIT_X, vecHas);
795 // System.err.println("exp0 "+Arrays.toString(UNIT_X));
796 // System.err.println("has0 "+Arrays.toString(vecHas));
797 REQUIRE_THAT( std::abs( UNIT_X.dist(vecHas)), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
798 REQUIRE(UNIT_X == vecHas);
799
800 // negative dot product
801 vecExp.set(0, -std::sin(QUARTER_PI), std::sin(QUARTER_PI));
802 quat1.setIdentity().rotateByAngleX( -2 * HALF_PI); // angle: -180 degrees, axis X
803 quat2.setIdentity().rotateByAngleX( HALF_PI); // angle: 90 degrees, axis X
804 // System.err.println("Slerp #06: 1/2 * 270 degrees");
805 quatS.setSlerp(quat1, quat2, 0.5f);
806 quatS.rotateVector(UNIT_Y, vecHas);
807 // System.err.println("exp0 "+Arrays.toString(vecExp));
808 // System.err.println("has0 "+Arrays.toString(vecHas));
809 REQUIRE_THAT( std::abs( vecExp.dist(vecHas)), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
810 if( !vecExp.equals(vecHas) ) {
811 std::cout << "Deviation: " << vecExp << ", " << vecHas << ": " << ( vecExp - vecHas ) << ", dist " << vecExp.dist(vecHas) << std::endl;
812 }
813 // REQUIRE(vecExp == vecHas);
814}
815
816TEST_CASE( "Test 26 LookAt", "[quaternion][linear_algebra][math]" ) {
817 Vec3f xAxis, yAxis, zAxis, vecHas;
818
819 if( DEBUG_MODE ) std::cout << "LookAt #01" << std::endl;
820 Vec3f direction = NEG_UNIT_X;
821 Quat4f quat;
822 quat.setLookAt(direction, UNIT_Y, xAxis, yAxis, zAxis);
823 REQUIRE_THAT( direction.dist( quat.rotateVector(UNIT_Z, vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
824 REQUIRE(direction == vecHas);
825
826 if( DEBUG_MODE ) {
827 std::cout << "quat0 " << quat << std::endl;
828 std::cout << "exp0 " << direction << ", len " << direction.length() << std::endl;
829 std::cout << "has0 " << vecHas << ", len " << vecHas.length() << std::endl;
830
831 std::cout << std::endl << "LookAt #02" << std::endl;
832 }
833 (direction = ONE).normalize();
834 quat.setLookAt(direction, UNIT_Y, xAxis, yAxis, zAxis);
835 if( DEBUG_MODE ) {
836 std::cout << "direction " << direction << std::endl;
837 std::cout << "quat0.0 " << quat << std::endl;
838 }
839 quat.rotateVector(UNIT_Z, vecHas);
840 if( DEBUG_MODE ) {
841 std::cout << "quat0.1 " << quat << std::endl;
842 std::cout << "xAxis " << xAxis << ", len " << xAxis.length() << std::endl;
843 std::cout << "yAxis " << yAxis << ", len " << yAxis.length() << std::endl;
844 std::cout << "zAxis " << zAxis << ", len " << zAxis.length() << std::endl;
845 std::cout << "exp0 " << direction << ", len " << direction.length() << std::endl;
846 std::cout << "has0 " << vecHas << ", len " << vecHas.length() << std::endl;
847 }
848 // REQUIRE(0f, VectorUtil.distance(direction, quat.rotateVector(vecHas, 0, UNIT_Z, 0)), Quat4f.ALLOWED_DEVIANCE);
849 REQUIRE_THAT( direction.dist(vecHas), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
850 REQUIRE(direction == vecHas);
851
852 if( DEBUG_MODE ) std::cout << "LookAt #03" << std::endl;
853 direction.set(-1, 2, -1).normalize();
854 quat.setLookAt(direction, UNIT_Y, xAxis, yAxis, zAxis);
855 if( DEBUG_MODE ) std::cout << "quat0 " << quat << std::endl;
856 quat.rotateVector(UNIT_Z, vecHas);
857 if( DEBUG_MODE ) {
858 std::cout << "xAxis " << xAxis << ", len " << xAxis.length() << std::endl;
859 std::cout << "yAxis " << yAxis << ", len " << yAxis.length() << std::endl;
860 std::cout << "zAxis " << zAxis << ", len " << zAxis.length() << std::endl;
861 std::cout << "exp0 " << direction << ", len " << direction.length() << std::endl;
862 std::cout << "has0 " << vecHas << ", len " << vecHas.length() << std::endl;
863 }
864 // REQUIRE(0f, VectorUtil.distance(direction, quat.rotateVector(vecHas, 0, UNIT_Z, 0)), Quat4f.ALLOWED_DEVIANCE);
865 REQUIRE_THAT( direction.dist(vecHas), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
866 if( !direction.equals(vecHas) ) {
867 std::cout << "Deviation: " << direction << ", " << vecHas << ": " << ( direction - vecHas ) << ", dist " << direction.dist(vecHas) << std::endl;
868 }
869 // REQUIRE(direction == vecHas);
870}
871
Basic 4x4 value_type matrix implementation using fields for intensive use-cases (host operations).
Definition: mat4f.hpp:112
constexpr value_type get(const jau::nsize_t i) const noexcept
Returns the ith component of the given column-major order matrix, 0 <= i < 16, w/o boundary check.
Definition: mat4f.hpp:306
constexpr Vec3 & mulVec3(const Vec3 &v_in, Vec3 &v_out) const noexcept
Affine 3f-vector transformation by 4x4 matrix.
Definition: mat4f.hpp:854
constexpr bool equals(const Matrix4 &o, const value_type epsilon=std::numeric_limits< value_type >::epsilon()) const noexcept
Definition: mat4f.hpp:194
constexpr Vec4 & getColumn(const jau::nsize_t column, Vec4 &v_out) const noexcept
Get the named column of the given column-major matrix to v_out w/o boundary check.
Definition: mat4f.hpp:320
constexpr Matrix4 & loadIdentity() noexcept
Set this matrix to identity.
Definition: mat4f.hpp:250
constexpr_cxx26 Matrix4 & setToRotationEuler(const value_type bankX, const value_type headingY, const value_type attitudeZ) noexcept
Set this matrix to rotation from the given Euler rotation angles in radians.
Definition: mat4f.hpp:1080
constexpr Matrix4 & load(const_iterator src) noexcept
Load the values of the given matrix src to this matrix w/o boundary check.
Definition: mat4f.hpp:264
Quaternion implementation supporting Gimbal-Lock free rotations.
Definition: quaternion.hpp:62
constexpr_cxx26 Quaternion & rotateByAngleX(const value_type angle) noexcept
Rotate this quaternion around X axis with the given angle in radians.
Definition: quaternion.hpp:378
Vec3 rotateVector(const Vec3 &in) noexcept
Definition: quaternion.hpp:488
constexpr Quaternion & set(const value_type x, const value_type y, const value_type z, const value_type w) noexcept
Set all values of this quaternion using the given components.
Definition: quaternion.hpp:255
constexpr Quaternion & conjugate() noexcept
Conjugates this quaternion [-x, -y, -z, w].
Definition: quaternion.hpp:219
constexpr void toAxes(Vec3 &xAxis, Vec3 &yAxis, Vec3 &zAxis, Matrix4< value_type > &tmp) const noexcept
Extracts this quaternion's orthogonal rotation axes.
constexpr_cxx26 Quaternion & setFromEuler(const Vec3 &angradXYZ) noexcept
Initializes this quaternion from the given Euler rotation array angradXYZ in radians.
Definition: quaternion.hpp:853
constexpr_cxx26 Quaternion & setFromNormalVectors(const Vec3 &v1, const Vec3 &v2) noexcept
Initialize this quaternion from two normalized vectors.
Definition: quaternion.hpp:730
constexpr_cxx26 Quaternion & rotateByAngleNormalAxis(const value_type angle, const value_type axisX, const value_type axisY, const value_type axisZ) noexcept
Rotate this quaternion by the given angle and axis.
Definition: quaternion.hpp:338
constexpr value_type dot(value_type x, value_type y, value_type z, value_type w) const noexcept
Returns the dot product of this quaternion with the given x,y,z and m_w components.
Definition: quaternion.hpp:156
constexpr Quaternion & invert() noexcept
Invert the quaternion If rotational, will produce a the inverse rotation.
Definition: quaternion.hpp:237
constexpr_cxx26 Vec3 toEuler() noexcept
Transform this quaternion to Euler rotation angles in radians (pitchX, yawY and rollZ).
Definition: quaternion.hpp:927
constexpr_cxx26 value_type magnitude() const noexcept
Return the magnitude of this quaternion, i.e.
Definition: quaternion.hpp:126
constexpr Mat4f toMatrix() const noexcept
Transform this quaternion to a normalized 4x4 column matrix representing the rotation.
constexpr Quaternion & setIdentity() noexcept
Definition: quaternion.hpp:185
Quaternion & setLookAt(const Vec3 &directionIn, const Vec3 &upIn, Vec3 &xAxisOut, Vec3 &yAxisOut, Vec3 &zAxisOut) noexcept
Set this quaternion to equal the rotation required to point the z-axis at direction and the y-axis to...
Definition: quaternion.hpp:627
constexpr_cxx26 Quaternion & setFromAngleNormalAxis(const value_type angle, const Vec3 &vector) noexcept
Definition: quaternion.hpp:801
Quaternion & setFromAngleAxis(const value_type angle, const Vec3 &vector) noexcept
Definition: quaternion.hpp:782
constexpr Quaternion & setFromAxes(const Vec3 &xAxis, const Vec3 &yAxis, const Vec3 &zAxis) noexcept
Initializes this quaternion to represent a rotation formed by the given three orthogonal axes.
constexpr_cxx26 value_type toAngleAxis(Vec3 &axis) const noexcept
Transform the rotational quaternion to axis based rotation angles.
Definition: quaternion.hpp:822
constexpr_cxx26 Quaternion & setFromVectors(const Vec3 &v1, const Vec3 &v2) noexcept
Initialize this quaternion from two vectors.
Definition: quaternion.hpp:676
constexpr Quaternion & setFromMat(const value_type m00, const value_type m01, const value_type m02, const value_type m10, const value_type m11, const value_type m12, const value_type m20, const value_type m21, const value_type m22) noexcept
Compute the quaternion from a 3x3 column rotation matrix.
Definition: quaternion.hpp:963
constexpr_cxx26 Quaternion & setSlerp(const Quaternion &a, const Quaternion &b, const value_type changeAmnt) noexcept
Set this quaternion to a spherical linear interpolation between the given start and end quaternions b...
Definition: quaternion.hpp:548
constexpr_cxx26 Quaternion & rotateByAngleZ(value_type angle) noexcept
Rotate this quaternion around Z axis with the given angle in radians.
Definition: quaternion.hpp:414
constexpr_cxx26 Quaternion & rotateByAngleY(value_type angle) noexcept
Rotate this quaternion around Y axis with the given angle in radians.
Definition: quaternion.hpp:396
constexpr Vector3F & set(const Vec2f &o, const value_type z_) noexcept
TODO constexpr bool operator<=>(const vec3f_t& rhs ) const noexcept { return ... }.
Definition: vec3f.hpp:143
constexpr Vector3F & normalize() noexcept
Normalize this vector in place.
Definition: vec3f.hpp:237
constexpr iterator get(iterator xyz) const noexcept
xyz = this, returns xyz.
Definition: vec3f.hpp:118
constexpr value_type length() const noexcept
Return the length of a vector, a.k.a the norm or magnitude
Definition: vec3f.hpp:230
constexpr value_type dist(const Vector3F &o) const noexcept
Return the distance between this vector and the given one.
Definition: vec3f.hpp:269
constexpr bool equals(const Vector3F &o, const value_type epsilon=std::numeric_limits< value_type >::epsilon()) const noexcept
Definition: vec3f.hpp:125
4D vector using four value_type components.
Definition: vec4f.hpp:51
std::enable_if< std::is_floating_point_v< T >, bool >::type constexpr equals(const T &a, const T &b, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if both values are equal, i.e.
Definition: float_math.hpp:394
constexpr T abs(const T x) noexcept
Returns the absolute value of an arithmetic number (w/ branching) in O(1)
Definition: base_math.hpp:155
constexpr Vector3F< T > to_vec3(const Vector4F< T > &v) noexcept
out = { this.x, this.y, this.z } dropping w, returns out.
Definition: vec4f.hpp:313
Vector4F< float > Vec4f
Definition: vec4f.hpp:323
Quaternion< float > Quat4f
Vector3F< float > Vec3f
Definition: vec3f.hpp:371
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
static const float HALF_PI
static constexpr const bool DEBUG_MODE
static const Vec4f ONE_v4
static const Quat4f QUAT_IDENT
static const Vec3f UNIT_Y
static const Vec3f NEG_UNIT_X
static const Vec3f ONE
static const Vec3f NEG_UNIT_Y
static const float QUARTER_PI
static const Vec3f NEG_ONE
static const Vec3f NEG_UNIT_Z
static const Vec3f ZERO
static const float PI
static const Vec3f UNIT_X
static const float EPSILON
static const Vec4f NEG_ONE_v4
static const Vec3f UNIT_Z
TEST_CASE("Test 01 Normalize", "[quaternion][linear_algebra][math]")
int printf(const char *format,...)
Operating Systems predefined macros.