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