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