28#include <jau/test/catch2_ext.hpp>
36static const float PI = std::numbers::pi_v<float>;
37static const float HALF_PI = (float)M_PI_2;
39static const float EPSILON = std::numeric_limits<float>::epsilon();
62TEST_CASE(
"Test 01 Normalize",
"[quaternion][linear_algebra][math]" ) {
63 const Quat4f quat(0, 1, 2, 3);
69TEST_CASE(
"Test 02 Rotate Zero Vector",
"[quaternion][linear_algebra][math]" ) {
72 REQUIRE(
ZERO == rotVec0 );
75TEST_CASE(
"Test 03 Invert and Conugate",
"[quaternion][linear_algebra][math]" ) {
78 const Quat4f quat0(0, 1, 2, 3);
80 REQUIRE( quat0 == quat0Inv.
invert() );
84 const Quat4f quat0(-1, -2, -3, 4);
86 REQUIRE( quat0 == quat0Conj );
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)));
100TEST_CASE(
"Test 10 Angle Axis",
"[quaternion][linear_algebra][math]" ) {
104 REQUIRE( quat2 == quat1 );
106 REQUIRE( 1 - quat1.
magnitude() <= std::numeric_limits<float>::epsilon() );
110 REQUIRE( vecOut1 == vecOut2 );
111 REQUIRE(
true ==
jau::equals( 0.0f, std::abs( vecOut1.
dist(vecOut2) ) ) );
121 REQUIRE( quat1 == quat2 );
123 quat1.
set(0, 0, 0, 0);
125 REQUIRE(0.0f == angle);
126 REQUIRE(
UNIT_X == vecOut1);
129TEST_CASE(
"Test 11 From Vec to Vec",
"[quaternion][linear_algebra][math]" ) {
136 REQUIRE( quat == quat2 );
139 REQUIRE( quat2 == quat );
162TEST_CASE(
"Test 12 From and to Euler Angles",
"[quaternion][linear_algebra][math]" ) {
167 REQUIRE_THAT( quat.
magnitude(), Catch::Matchers::WithinAbs(1.0f,
EPSILON) );
170 REQUIRE( angles0Exp == angles0Has );
174 REQUIRE( quat == quat2 );
180 REQUIRE_THAT( quat.
magnitude(), Catch::Matchers::WithinAbs(1.0f,
EPSILON) );
183 REQUIRE( angles1Exp == angles1Has );
186 REQUIRE( quat == quat2 );
192 REQUIRE_THAT( quat.
magnitude(), Catch::Matchers::WithinAbs(1.0f,
EPSILON) );
195 REQUIRE( angles2Exp == angles2Has );
198 REQUIRE( quat == quat2 );
201TEST_CASE(
"Test 13 From Euler Angles and Rotate Vec",
"[quaternion][linear_algebra][math]" ) {
204 REQUIRE_THAT( quat.
magnitude(), Catch::Matchers::WithinAbs(1.0f,
EPSILON) );
207 REQUIRE_THAT( std::abs(
NEG_UNIT_Z.dist(v2)), Catch::Matchers::WithinAbs(0.0f,
EPSILON) );
210 REQUIRE_THAT( quat.
magnitude(), Catch::Matchers::WithinAbs(1.0f,
EPSILON) );
212 REQUIRE_THAT( std::abs(
NEG_UNIT_Y.dist(v2)), Catch::Matchers::WithinAbs(0.0f,
EPSILON) );
215 REQUIRE_THAT( quat.
magnitude(), Catch::Matchers::WithinAbs(1.0f,
EPSILON) );
217 REQUIRE_THAT( std::abs(
UNIT_Z.dist(v2)), Catch::Matchers::WithinAbs(0.0f,
EPSILON) );
220TEST_CASE(
"Test 14 Matrix",
"[matrix][quaternion][linear_algebra][math]" ) {
233 quat1.
set(0, 0, 0, 0);
236 REQUIRE( mat1 == mat2 );
245 0, std::cos(a), std::sin(a), 0,
246 0, -std::sin(a), std::cos(a), 0,
253 for(
int i=0; i<16; ++i) { REQUIRE_THAT( mat2_0[i], Catch::Matchers::WithinAbs(mat1_0[i],
EPSILON) ); }
261 REQUIRE( mat1 == mat2 );
263 REQUIRE( quat1 == quat2 );
269 REQUIRE( mat2 == mat2c );
270 REQUIRE( mat1 == mat2c );
278 REQUIRE( quat1 == quat2 );
284 REQUIRE( mat1 == mat2 );
291 REQUIRE( vecHas == vecOut3);
298 REQUIRE( vecHas == vecOut3);
308 0, std::cos(a), std::sin(a), 0,
309 0, -std::sin(a), std::cos(a), 0,
317 REQUIRE(mat1 == mat2);
326 REQUIRE(mat1 == mat2);
329 (mat2 *
ONE_v4).getVec3(vecOut3);
338 std::cos(a), 0, -std::sin(a), 0,
340 std::sin(a), 0, std::cos(a), 0,
348 REQUIRE(mat1 == mat2);
358 REQUIRE(mat1 == mat2);
370 std::cos(a), std::sin(a), 0, 0,
371 -std::sin(a), std::cos(a), 0, 0,
380 REQUIRE(mat1 == mat2);
389 REQUIRE(mat1 == mat2);
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);
407 std::cos(a), std::sin(a), 0, 0,
408 -std::sin(a), std::cos(a), 0, 0,
414 REQUIRE(vecExp0 == vecCol);
415 REQUIRE_THAT( std::abs( vecExp0.
dist(vecCol) ), Catch::Matchers::WithinAbs(0.0f,
EPSILON) );
418 REQUIRE(vecExp1 == vecCol);
419 REQUIRE_THAT( std::abs( vecExp1.
dist(vecCol) ), Catch::Matchers::WithinAbs(0.0f,
EPSILON) );
422 REQUIRE(vecExp2 == vecCol);
423 REQUIRE_THAT( std::abs( vecExp2.
dist(vecCol) ), Catch::Matchers::WithinAbs(0.0f,
EPSILON) );
426TEST_CASE(
"Test 15a Axes And Matrix",
"[matrix][quaternion][linear_algebra][math]" ) {
435 REQUIRE( matExp1 == matHas);
440 std::cout <<
"exp-euler " << eulerExp << std::endl;
441 std::cout <<
"has-euler " << eulerHas << std::endl;
442 REQUIRE(eulerExp == eulerHas);
444 REQUIRE(quat2 == quat1);
448 REQUIRE(quat2 == quat1);
451TEST_CASE(
"Test 15b Axes And Matrix",
"[matrix][quaternion][linear_algebra][math]" ) {
460 REQUIRE( matExp == matHas);
465 std::cout <<
"exp-euler " << eulerExp << std::endl;
466 std::cout <<
"has-euler " << eulerHas << std::endl;
467 REQUIRE(eulerExp == eulerHas);
469 REQUIRE(quat2 == quat1);
473 REQUIRE(quat2 == quat1);
476TEST_CASE(
"Test 15c Axes And Matrix",
"[matrix][quaternion][linear_algebra][math]" ) {
479 eulerExp1.
get(eulerExp0);
489 std::cout <<
"matExp " << matExp << std::endl;
490 std::cout <<
"matHas " << matHas << std::endl;
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;
506 eulerHas1.
get(eulerHas0);
507 Vec3f eulerHas0v(eulerHas0), eulerExp0v(eulerExp0);
508 REQUIRE( eulerHas0v == eulerExp0v );
513 REQUIRE(quat2 == quat1);
517 REQUIRE(quat2 == quat1);
524TEST_CASE(
"Test 20 Add Subtract",
"[quaternion][linear_algebra][math]" ) {
526 Quat4f quatExp(1, 2, 3, 4);
533 REQUIRE(quatExp == quatHas);
536 quatHas = quat1 + quat2;
537 REQUIRE(quatExp == quatHas);
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);
549 REQUIRE(quatExp == quatHas);
552 quatHas = quat1 - quat2;
553 REQUIRE(quatExp == quatHas);
557TEST_CASE(
"Test 21 Multiply",
"[quaternion][linear_algebra][math]" ) {
560 Quat4f quatExp(1, 2, 4, 6);
561 Quat4f quat1(0.5f, 1, 2, 3);
567 REQUIRE(quatExp == quat2);
570 quat2 = quat1 * 2.0f;
571 REQUIRE(quatExp == quat2);
573 quat2 = 2.0f * quat1;
574 REQUIRE(quatExp == quat2);
594 quat2 = quat1 * quat1;
608 quat1 = quat1 * quat1 * quat2;
621TEST_CASE(
"Test 22 Invert-Mult-Normal-Conjugate",
"[quaternion][linear_algebra][math]" ) {
629 REQUIRE(quat0 == quat1);
635 quat2 = quat0 * quat1;
638 REQUIRE(quat0 == quat1);
641 quat0.
set(-1.0f, -2.0f, -3.0f, 4.0f);
642 quat1.
set( 1.0f, 2.0f, 3.0f, 4.0f);
645 REQUIRE(quat0 == quat2);
648TEST_CASE(
"Test 23 Rotation Order",
"[quaternion][linear_algebra][math]" ) {
652 REQUIRE(quat1 == quat2);
657 REQUIRE(quat1 == quat2);
662 REQUIRE(quat1 == quat2);
667 REQUIRE(quat1 == quat2);
685 REQUIRE_THAT( vecExp.
dist(vecRot), Catch::Matchers::WithinAbs(0.0f,
EPSILON) );
686 REQUIRE(vecExp == vecRot);
694 REQUIRE_THAT( vecExp.
dist(vecRot), Catch::Matchers::WithinAbs(0.0f,
EPSILON) );
695 REQUIRE(vecExp == vecRot);
702 REQUIRE_THAT( vecExp.
dist(vecRot), Catch::Matchers::WithinAbs(0.0f,
EPSILON) );
703 REQUIRE(vecExp == vecRot);
710 REQUIRE_THAT( vecExp.
dist(vecRot), Catch::Matchers::WithinAbs(0.0f,
EPSILON) );
711 REQUIRE(vecExp == vecRot);
715 REQUIRE(quat == worker);
718TEST_CASE(
"Test 24 Axes",
"[quaternion][linear_algebra][math]" ) {
722 Vec3f xAxis, yAxis, zAxis;
728 REQUIRE(quat0 == quat1);
730 REQUIRE(quat2 == quat1);
732 quat1.
toAxes(xAxis, yAxis, zAxis, rotMat);
734 REQUIRE(quat0 == quat2);
735 REQUIRE(quat1 == quat2);
738TEST_CASE(
"Test 25 Slerp",
"[quaternion][linear_algebra][math]" ) {
752 if( !vecExp.
equals(vecHas) ) {
753 std::cout <<
"Deviation: " << vecExp <<
", " << vecHas <<
": " << ( vecExp - vecHas ) <<
", dist " << vecExp.
dist(vecHas) << std::endl;
774 if( !
UNIT_Y.equals(vecHas) ) {
775 std::cout <<
"Deviation: " <<
UNIT_Y <<
", " << vecHas <<
": " << (
UNIT_Y - vecHas ) <<
", dist " <<
UNIT_Y.dist(vecHas) << std::endl;
787 REQUIRE(
UNIT_X == vecHas);
792 quatS.
setSlerp(quat1, quat2, 0.25f);
797 REQUIRE(
UNIT_X == vecHas);
809 if( !vecExp.
equals(vecHas) ) {
810 std::cout <<
"Deviation: " << vecExp <<
", " << vecHas <<
": " << ( vecExp - vecHas ) <<
", dist " << vecExp.
dist(vecHas) << std::endl;
815TEST_CASE(
"Test 26 LookAt",
"[quaternion][linear_algebra][math]" ) {
816 Vec3f xAxis, yAxis, zAxis, vecHas;
818 if(
DEBUG_MODE ) std::cout <<
"LookAt #01" << std::endl;
823 REQUIRE(direction == vecHas);
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;
830 std::cout << std::endl <<
"LookAt #02" << std::endl;
832 (direction =
ONE).normalize();
835 std::cout <<
"direction " << direction << std::endl;
836 std::cout <<
"quat0.0 " << quat << std::endl;
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;
849 REQUIRE(direction == vecHas);
851 if(
DEBUG_MODE ) std::cout <<
"LookAt #03" << std::endl;
854 if(
DEBUG_MODE ) std::cout <<
"quat0 " << quat << std::endl;
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;
865 if( !direction.
equals(vecHas) ) {
866 std::cout <<
"Deviation: " << direction <<
", " << vecHas <<
": " << ( direction - vecHas ) <<
", dist " << direction.
dist(vecHas) << std::endl;
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.
constexpr Vec3 & mulVec3(const Vec3 &v_in, Vec3 &v_out) const noexcept
Affine 3f-vector transformation by 4x4 matrix.
constexpr bool equals(const Matrix4 &o, const value_type epsilon=std::numeric_limits< value_type >::epsilon()) const noexcept
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.
constexpr Matrix4 & loadIdentity() noexcept
Set this matrix to identity.
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.
constexpr Matrix4 & load(const_iterator src) noexcept
Load the values of the given matrix src to this matrix w/o boundary check.
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 ... }...
constexpr Vector3F & normalize() noexcept
Normalize this vector in place.
constexpr iterator get(iterator xyz) const noexcept
xyz = this, returns xyz.
constexpr value_type length() const noexcept
Return the length of a vector, a.k.a the norm or magnitude
constexpr value_type dist(const Vector3F &o) const noexcept
Return the distance between this vector and the given one.
constexpr bool equals(const Vector3F &o, const value_type epsilon=std::numeric_limits< value_type >::epsilon()) const noexcept
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.
Quaternion< float > Quat4f
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
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 NEG_UNIT_Y
static const float QUARTER_PI
static const Vec3f NEG_ONE
static const Vec3f NEG_UNIT_Z
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.