aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Göthel <[email protected]>2024-04-15 20:14:42 +0200
committerSven Göthel <[email protected]>2024-04-15 20:14:42 +0200
commit49319ae9d330931e4f3d8344d393b9d6cd4c819e (patch)
tree228b13c37f49c2078e472c67b557e2aaa971bd49
parent72a62edb65ace20d86dd4c773a77d1f612fbf844 (diff)
math: Use templates, fix alignment and 'iterator' (pointer) r/w access; enforce 'constexpr'; mat4f dropped Stack for constexpr; Dropped math.cpp having clear dependencies
Dropped math.cpp having clear dependencies Adding ctor w/ initializer_list Enforcing 'constexpr' where possible also enforces strict aliasing etc, hence resulting in a more tight definition. Template types for floating-point or integral types are used, added the generic inner class typedef variations used in methods for clarity and proper meta-programming. alignas() on base type enforces utilization with arrays. operator[] uses address of first element w/ strict alignment and proper type (for constexpr).
-rw-r--r--JaulibSetup.cmake37
-rw-r--r--include/jau/math/aabbox3f.hpp44
-rw-r--r--include/jau/math/geom/plane/affine_transform.hpp44
-rw-r--r--include/jau/math/mat4f.hpp1119
-rw-r--r--include/jau/math/quaternion.hpp508
-rw-r--r--include/jau/math/recti.hpp87
-rw-r--r--include/jau/math/vec2f.hpp220
-rw-r--r--include/jau/math/vec2i.hpp175
-rw-r--r--include/jau/math/vec3f.hpp230
-rw-r--r--include/jau/math/vec4f.hpp198
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/math.cpp181
-rw-r--r--test/test_math_quaternion.cpp156
-rw-r--r--test/test_math_vec.cpp59
14 files changed, 1630 insertions, 1429 deletions
diff --git a/JaulibSetup.cmake b/JaulibSetup.cmake
index 2848594..14c8ef2 100644
--- a/JaulibSetup.cmake
+++ b/JaulibSetup.cmake
@@ -80,6 +80,8 @@ message(STATUS "${PROJECT_NAME} USE_STRIP = ${USE_STRIP} (pre-set)")
if(DEBUG)
if(CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -ggdb -DDEBUG -fno-omit-frame-pointer ${GCC_FLAGS_STACK} -no-pie")
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -no-pie")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -DDEBUG")
endif(CMAKE_COMPILER_IS_GNUCC)
@@ -96,33 +98,26 @@ if(DEBUG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_FLAGS_SANITIZE_THREAD}")
endif(CMAKE_COMPILER_IS_GNUCC)
endif(INSTRUMENTATION)
-elseif(GPROF)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -g -ggdb -pg")
-elseif(PERF_ANALYSIS)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -g -ggdb")
else()
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
- find_program(STRIP strip)
- if (STRIP STREQUAL "STRIP-NOTFOUND")
- set(USE_STRIP OFF)
- message(STATUS "${PROJECT_NAME} USE_STRIP:=false, strip not found")
- elseif(NOT DEFINED USE_STRIP)
- set(USE_STRIP ON)
- message(STATUS "${PROJECT_NAME} USE_STRIP:=true, !DEBUG and not set")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DNDEBUG")
+ if(GPROF)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -ggdb -pg")
+ elseif(PERF_ANALYSIS)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -ggdb")
+ else()
+ find_program(STRIP strip)
+ if (STRIP STREQUAL "STRIP-NOTFOUND")
+ set(USE_STRIP OFF)
+ message(STATUS "${PROJECT_NAME} USE_STRIP:=false, strip not found")
+ elseif(NOT DEFINED USE_STRIP)
+ set(USE_STRIP ON)
+ message(STATUS "${PROJECT_NAME} USE_STRIP:=true, !DEBUG and not set")
+ endif()
endif()
endif(DEBUG)
message(STATUS "${PROJECT_NAME} USE_STRIP = ${USE_STRIP} (final)")
-if(DEBUG)
- if(CMAKE_COMPILER_IS_GNUCC)
- set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -no-pie")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie")
- endif(CMAKE_COMPILER_IS_GNUCC)
-else()
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG")
-endif(DEBUG)
-
if(DONT_USE_RTTI)
message(STATUS "${PROJECT_NAME} RTTI disabled")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
diff --git a/include/jau/math/aabbox3f.hpp b/include/jau/math/aabbox3f.hpp
index aa0e1db..bf0ee90 100644
--- a/include/jau/math/aabbox3f.hpp
+++ b/include/jau/math/aabbox3f.hpp
@@ -24,12 +24,9 @@
#ifndef JAU_AABBOX3F_HPP_
#define JAU_AABBOX3F_HPP_
+#include <jau/functional.hpp>
#include <jau/math/vec3f.hpp>
-namespace jau::math::geom::plane {
- class AffineTransform; // forward
-}
-
namespace jau::math {
/** \addtogroup Math
@@ -260,13 +257,48 @@ namespace jau::math {
}
/**
+ * General purpose Vec3f transform function
+ */
+ typedef jau::function<jau::math::Vec3f(const jau::math::Vec3f&)> transform_vec3f_func;
+
+ /**
* Resize the aabbox3f to encapsulate another AABox, which will be <i>transformed</i> on the fly first.
* @param newBox aabbox3f to be encapsulated in
- * @param t the {@link AffineTransform} applied on <i>newBox</i> on the fly
+ * @param transform the transform function, applied on <i>newBox</i> on the fly
* @param tmpV3 temporary storage
* @return this aabbox3f for chaining
*/
- AABBox3f& resize(const AABBox3f& newBox, const geom::plane::AffineTransform& t, Vec3f& tmpV3) noexcept;
+ AABBox3f& resize(const AABBox3f& newBox, transform_vec3f_func& transform) noexcept {
+ /** test low */
+ {
+ const jau::math::Vec3f newBL = transform(newBox.low());
+ if (newBL.x < m_lo.x) {
+ m_lo.x = newBL.x;
+ }
+ if (newBL.y < m_lo.y) {
+ m_lo.y = newBL.y;
+ }
+ if (newBL.z < m_lo.z) {
+ m_lo.z = newBL.z;
+ }
+ }
+
+ /** test high */
+ {
+ const jau::math::Vec3f newTR = transform(newBox.high());
+ if (newTR.x > m_hi.x) {
+ m_hi.x = newTR.x;
+ }
+ if (newTR.y > m_hi.y) {
+ m_hi.y = newTR.y;
+ }
+ if (newTR.z > m_hi.z) {
+ m_hi.z = newTR.z;
+ }
+ }
+ computeCenter();
+ return *this;
+ }
/**
* Resize the aabbox3f to encapsulate the passed
diff --git a/include/jau/math/geom/plane/affine_transform.hpp b/include/jau/math/geom/plane/affine_transform.hpp
index 87fa485..60be2a0 100644
--- a/include/jau/math/geom/plane/affine_transform.hpp
+++ b/include/jau/math/geom/plane/affine_transform.hpp
@@ -507,6 +507,17 @@ namespace jau::math::geom::plane {
/**
* @param src source of transformation
+ * @return resulting Vec2f
+ */
+ Vec2f transform(const Vec2f& src) const noexcept {
+ const float x = src.x;
+ const float y = src.y;
+ return Vec2f( x * m00 + y * m01 + m02,
+ x * m10 + y * m11 + m12 );
+ }
+
+ /**
+ * @param src source of transformation
* @param dst destination of transformation, maybe be equal to <code>src</code>
* @return dst for chaining
*/
@@ -519,6 +530,39 @@ namespace jau::math::geom::plane {
return dst;
}
+ private:
+ Vec3f transformVec3f(const Vec3f& src) const noexcept {
+ const float x = src.x;
+ const float y = src.y;
+ return Vec3f( x * m00 + y * m01 + m02,
+ x * m10 + y * m11 + m12,
+ src.z
+ ); // just copy z
+ }
+
+ public:
+ /**
+ * @param src source of transformation
+ * @param dst destination of transformation, maybe be equal to <code>src</code>
+ * @return resulting Vec3f
+ */
+ Vec3f transform(const Vec3f& src) const noexcept {
+ return transformVec3f(src);
+ }
+
+ /**
+ * Resize the aabbox3f to encapsulate another AABox, which will be <i>transformed</i> on the fly first.
+ * @param newBox aabbox3f to be encapsulated in
+ * @param t the {@link AffineTransform} applied on <i>newBox</i> on the fly
+ * @param tmpV3 temporary storage
+ * @return this aabbox3f for chaining
+ */
+ AABBox3f& resizeBox(AABBox3f& box, const AABBox3f& newBox) noexcept {
+ AABBox3f::transform_vec3f_func transform(this, &AffineTransform::transformVec3f);
+ return box.resize(newBox, transform);
+
+ }
+
/**
*
* @param src
diff --git a/include/jau/math/mat4f.hpp b/include/jau/math/mat4f.hpp
index e80e8d3..f3c51f1 100644
--- a/include/jau/math/mat4f.hpp
+++ b/include/jau/math/mat4f.hpp
@@ -27,9 +27,11 @@
#include <cmath>
#include <cstdarg>
#include <cstdint>
+#include <cassert>
#include <limits>
#include <string>
#include <vector>
+#include <initializer_list>
#include <iostream>
#include <jau/float_math.hpp>
@@ -50,10 +52,12 @@ namespace jau::math {
* @{
*/
+ template<typename Value_type,
+ std::enable_if_t<std::is_floating_point_v<Value_type>, bool>>
class Quaternion; // forward
/**
- * Basic 4x4 float matrix implementation using fields for intensive use-cases (host operations).
+ * Basic 4x4 value_type matrix implementation using fields for intensive use-cases (host operations).
* <p>
* Implementation covers {@link FloatUtil} matrix functionality, exposed in an object oriented manner.
* </p>
@@ -102,111 +106,86 @@ namespace jau::math {
* </ul>
* </p>
*/
-class Mat4f {
- private:
- float m00, m10, m20, m30;
- float m01, m11, m21, m31;
- float m02, m12, m22, m32;
- float m03, m13, m23, m33;
-
- friend geom::Frustum;
- friend Quaternion;
-
- class Stack {
- private:
- int growSize;
- std::vector<float> buffer;
-
- public:
- /**
- * @param initialSize initial size
- * @param growSize grow size if {@link #position()} is reached, maybe <code>0</code>
- */
- Stack(int initialSize, int growSize_)
- : growSize(growSize_), buffer(initialSize) {}
-
- size_t growIfNecessary(int length) {
- const size_t p = buffer.size();
- const size_t nsz = buffer.size() + length;
- if( nsz > buffer.capacity() ) {
- buffer.reserve(buffer.size() + std::max(length, growSize));
- }
- buffer.resize(nsz);
- return p;
- }
-
- Mat4f& push(Mat4f& src) {
- size_t p = growIfNecessary(16);
- src.get(buffer, p);
- return src;
- }
-
- Mat4f& pop(Mat4f& dest) {
- size_t sz = buffer.size();
- if( sz < 16 ) {
- throw jau::IndexOutOfBoundsException(0, sz, E_FILE_LINE);
- }
- size_t p = sz - 16;
- dest.load(buffer, p);
- buffer.resize(p);
- return dest;
- }
- };
-
- Stack stack; // start w/ zero size, growSize is half GL-min size (32)
+
+template<typename Value_type,
+ std::enable_if_t<std::is_floating_point_v<Value_type>, bool> = true>
+class alignas(Value_type) Matrix4 {
+ public:
+ typedef Value_type value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef value_type* iterator;
+ typedef const value_type* const_iterator;
+
+ typedef Vector4F<value_type, std::is_floating_point_v<Value_type>> Vec4;
+
+ constexpr static const value_type zero = value_type(0);
+ constexpr static const value_type one = value_type(1);
+ constexpr static const value_type two = value_type(2);
+ constexpr static const value_type half = one/two;
+
+ private:
+ // RC
+ value_type m00, m10, m20, m30; // column 0
+ value_type m01, m11, m21, m31; // column 1
+ value_type m02, m12, m22, m32; // column 2
+ value_type m03, m13, m23, m33; // column 3
+
+ friend geom::Frustum;
+ friend Quaternion<value_type, std::is_floating_point_v<Value_type>>;
public:
/**
* Creates a new identity matrix.
*/
- Mat4f() noexcept
- : m00(1.0f), m10(0.0f), m20(0.0f), m30(0.0f),
- m01(0.0f), m11(1.0f), m21(0.0f), m31(0.0f),
- m02(0.0f), m12(0.0f), m22(1.0f), m32(0.0f),
- m03(0.0f), m13(0.0f), m23(0.0f), m33(1.0f),
- stack(0, 16*16)
+ constexpr Matrix4() noexcept
+ : m00(one), m10(zero), m20(zero), m30(zero),
+ m01(zero), m11(one), m21(zero), m31(zero),
+ m02(zero), m12(zero), m22(one), m32(zero),
+ m03(zero), m13(zero), m23(zero), m33(one)
{ }
/**
- * Creates a new matrix based on given float[4*4] column major order.
+ * Creates a new matrix based on given value_type[4*4] column major order.
* @param m 4x4 matrix in column-major order
*/
- Mat4f(const float m[]) noexcept
- : stack(0, 16*16)
- {
- load(m);
- }
+ constexpr Matrix4(const_iterator m) noexcept
+ : m00(m[0+0*4]), m10(m[1+0*4]), m20(m[2+0*4]), m30(m[3+0*4]), // column 0
+ m01(m[0+1*4]), m11(m[1+1*4]), m21(m[2+1*4]), m31(m[3+1*4]), // column 1
+ m02(m[0+2*4]), m12(m[1+2*4]), m22(m[2+2*4]), m32(m[3+2*4]), // column 2
+ m03(m[0+3*4]), m13(m[1+3*4]), m23(m[2+3*4]), m33(m[3+3*4]) // column 3
+ {}
/**
- * Creates a new matrix based on given std::vector 4x4 column major order.
- * @param m 4x4 matrix std::vector in column-major order starting at {@code src_off}
- * @param m_off offset for matrix {@code src}
+ * Creates a new matrix based on given value_type initializer list in column major order.
+ * @param m source initializer list value_type data to be copied into this new instance, implied size must be >= 16
*/
- Mat4f(const std::vector<float>& m, const size_t m_off) noexcept
- : stack(0, 16*16)
+ constexpr Matrix4(std::initializer_list<value_type> m) noexcept
+ : m00(m.begin()[0+0*4]), m10(m.begin()[1+0*4]), m20(m.begin()[2+0*4]), m30(m.begin()[3+0*4]), // column 0
+ m01(m.begin()[0+1*4]), m11(m.begin()[1+1*4]), m21(m.begin()[2+1*4]), m31(m.begin()[3+1*4]), // column 1
+ m02(m.begin()[0+2*4]), m12(m.begin()[1+2*4]), m22(m.begin()[2+2*4]), m32(m.begin()[3+2*4]), // column 2
+ m03(m.begin()[0+3*4]), m13(m.begin()[1+3*4]), m23(m.begin()[2+3*4]), m33(m.begin()[3+3*4]) // column 3
{
- load(m, m_off);
+ assert(m.size() >= 16 );
}
/**
* Creates a new matrix copying the values of the given {@code src} matrix.
- *
- * The stack is not copied.
*/
- Mat4f(const Mat4f& o) noexcept
+ constexpr Matrix4(const Matrix4& o) noexcept
: m00(o.m00), m10(o.m10), m20(o.m20), m30(o.m30),
m01(o.m01), m11(o.m11), m21(o.m21), m31(o.m31),
m02(o.m02), m12(o.m12), m22(o.m22), m32(o.m32),
- m03(o.m03), m13(o.m13), m23(o.m23), m33(o.m33),
- stack(0, 16*16) { }
+ m03(o.m03), m13(o.m13), m23(o.m23), m33(o.m33)
+ { }
/**
* Copy assignment using the the values of the given {@code src} matrix.
- *
- * The stack is not copied.
*/
- Mat4f& operator=(const Mat4f& o) noexcept {
+ constexpr Matrix4& operator=(const Matrix4& o) noexcept {
m00 = o.m00; m10 = o.m10; m20 = o.m20; m30 = o.m30;
m01 = o.m01; m11 = o.m11; m21 = o.m21; m31 = o.m31;
m02 = o.m02; m12 = o.m12; m22 = o.m22; m32 = o.m32;
@@ -214,7 +193,7 @@ class Mat4f {
return *this;
}
- constexpr bool equals(const Mat4f& o, const float epsilon=std::numeric_limits<float>::epsilon()) const noexcept {
+ constexpr bool equals(const Matrix4& o, const value_type epsilon=std::numeric_limits<value_type>::epsilon()) const noexcept {
if( this == &o ) {
return true;
} else {
@@ -236,7 +215,7 @@ class Mat4f {
jau::equals(m33, o.m33, epsilon);
}
}
- constexpr bool operator==(const Mat4f& rhs) const noexcept {
+ constexpr bool operator==(const Matrix4& rhs) const noexcept {
return equals(rhs);
}
@@ -245,38 +224,22 @@ class Mat4f {
//
/**
- * Returns writable reference to the {@code i}th component of the given column-major order matrix, 0 <= i < 16 w/o boundary check
+ * Returns writable reference to the {@code i}th component of this column-major order matrix, 0 <= i < 16 w/o boundary check
*/
- float& operator[](size_t i) noexcept {
- return reinterpret_cast<float*>(this)[i];
+ constexpr reference operator[](size_t i) noexcept {
+ assert( i < 16 );
+ return (&m00)[i];
}
- /** Sets the {@code i}th component with float {@code v} 0 <= i < 16 w/ boundary check*/
- void set(const jau::nsize_t i, const float v) {
- switch (i) {
- case 0+4*0: m00 = v; break;
- case 1+4*0: m10 = v; break;
- case 2+4*0: m20 = v; break;
- case 3+4*0: m30 = v; break;
-
- case 0+4*1: m01 = v; break;
- case 1+4*1: m11 = v; break;
- case 2+4*1: m21 = v; break;
- case 3+4*1: m31 = v; break;
-
- case 0+4*2: m02 = v; break;
- case 1+4*2: m12 = v; break;
- case 2+4*2: m22 = v; break;
- case 3+4*2: m32 = v; break;
-
- case 0+4*3: m03 = v; break;
- case 1+4*3: m13 = v; break;
- case 2+4*3: m23 = v; break;
- case 3+4*3: m33 = v; break;
- default: throw jau::IndexOutOfBoundsException(i, 16, E_FILE_LINE);
- }
+ /** Sets the {@code i}th component of this column-major order matrix with value_type {@code v}, 0 <= i < 16 w/o boundary check*/
+ constexpr void set(const jau::nsize_t i, const value_type v) noexcept {
+ assert( i < 16 );
+ (&m00)[i] = v;
}
+ explicit operator pointer() noexcept { return &m00; }
+ constexpr iterator begin() noexcept { return &m00; }
+
/**
* Set this matrix to identity.
* <pre>
@@ -288,12 +251,12 @@ class Mat4f {
* </pre>
* @return this matrix for chaining
*/
- Mat4f& loadIdentity() noexcept {
- m00 = m11 = m22 = m33 = 1.0f;
+ constexpr Matrix4& loadIdentity() noexcept {
+ m00 = m11 = m22 = m33 = one;
m01 = m02 = m03 =
m10 = m12 = m13 =
m20 = m21 = m23 =
- m30 = m31 = m32 = 0.0f;
+ m30 = m31 = m32 = zero;
return *this;
}
@@ -302,7 +265,7 @@ class Mat4f {
* @param src the source values
* @return this matrix for chaining
*/
- Mat4f& load(const Mat4f& src) noexcept {
+ constexpr Matrix4& load(const Matrix4& src) noexcept {
m00 = src.m00; m10 = src.m10; m20 = src.m20; m30 = src.m30;
m01 = src.m01; m11 = src.m11; m21 = src.m21; m31 = src.m31;
m02 = src.m02; m12 = src.m12; m22 = src.m22; m32 = src.m32;
@@ -312,10 +275,11 @@ class Mat4f {
/**
* Load the values of the given matrix {@code src} to this matrix w/o boundary check.
- * @param src 4x4 matrix float[16] in column-major order
+ * @param src 4x4 matrix value_type[16] in column-major order
* @return this matrix for chaining
*/
- Mat4f& load(const float src[]) noexcept {
+ constexpr Matrix4& load(const_iterator src) noexcept {
+ // RC
m00 = src[0+0*4]; // column 0
m10 = src[1+0*4];
m20 = src[2+0*4];
@@ -335,35 +299,6 @@ class Mat4f {
return *this;
}
- /**
- * Load the values of the given matrix {@code src} to this matrix w/ boundary check.
- * @param src 4x4 matrix std::vector in column-major order starting at {@code src_off}
- * @param src_off offset for matrix {@code src}
- * @return this matrix for chaining
- */
- Mat4f& load(const std::vector<float>& src, const size_t src_off) {
- if( src.size() < src_off+16 || src_off > std::numeric_limits<size_t>::max() - 15) {
- throw jau::IndexOutOfBoundsException(src_off, 16, E_FILE_LINE);
- }
- m00 = src[src_off+0+0*4];
- m10 = src[src_off+1+0*4];
- m20 = src[src_off+2+0*4];
- m30 = src[src_off+3+0*4];
- m01 = src[src_off+0+1*4];
- m11 = src[src_off+1+1*4];
- m21 = src[src_off+2+1*4];
- m31 = src[src_off+3+1*4];
- m02 = src[src_off+0+2*4];
- m12 = src[src_off+1+2*4];
- m22 = src[src_off+2+2*4];
- m32 = src[src_off+3+2*4];
- m03 = src[src_off+0+3*4];
- m13 = src[src_off+1+3*4];
- m23 = src[src_off+2+3*4];
- m33 = src[src_off+3+3*4];
- return *this;
- }
-
//
// Read out Matrix via get(..)
//
@@ -371,82 +306,57 @@ class Mat4f {
/**
* Returns read-only {@code i}th component of the given column-major order matrix, 0 <= i < 16 w/o boundary check
*/
- float operator[](size_t i) const noexcept {
- return reinterpret_cast<const float*>(this)[i];
+ constexpr value_type operator[](size_t i) const noexcept {
+ assert( i < 16 );
+ return (&m00)[i];
}
- /** Returns the {@code i}th component of the given column-major order matrix, 0 <= i < 16, w/ boundary check */
- float get(const jau::nsize_t i) const {
- switch (i) {
- case 0+4*0: return m00;
- case 1+4*0: return m10;
- case 2+4*0: return m20;
- case 3+4*0: return m30;
-
- case 0+4*1: return m01;
- case 1+4*1: return m11;
- case 2+4*1: return m21;
- case 3+4*1: return m31;
-
- case 0+4*2: return m02;
- case 1+4*2: return m12;
- case 2+4*2: return m22;
- case 3+4*2: return m32;
-
- case 0+4*3: return m03;
- case 1+4*3: return m13;
- case 2+4*3: return m23;
- case 3+4*3: return m33;
-
- default: throw jau::IndexOutOfBoundsException(i, 16, E_FILE_LINE);
- }
+ /** Returns the {@code i}th component of the given column-major order matrix, 0 <= i < 16, w/o boundary check */
+ constexpr value_type get(const jau::nsize_t i) const noexcept {
+ assert( i < 16 );
+ return (&m00)[i];
}
+ explicit operator const_pointer() const noexcept { return &m00; }
+ constexpr const_iterator cbegin() const noexcept { return &m00; }
+
/**
- * Get the named column of the given column-major matrix to v_out w/ boundary check.
+ * Get the named column of the given column-major matrix to v_out w/o boundary check.
* @param column named column to copy
* @param v_out the column-vector storage
* @return given result vector <i>v_out</i> for chaining
*/
- Vec4f& getColumn(const jau::nsize_t column, Vec4f& v_out) const {
- if( column > 3 ) {
- throw jau::IndexOutOfBoundsException(3+column*4, 16, E_FILE_LINE);
- }
- v_out.set( get(0+column*4),
- get(1+column*4),
- get(2+column*4),
- get(3+column*4) );
- return v_out;
+ constexpr Vec4& getColumn(const jau::nsize_t column, Vec4& v_out) const noexcept {
+ assert( column < 4 );
+ return v_out.set( get(0+column*4),
+ get(1+column*4),
+ get(2+column*4),
+ get(3+column*4) );
}
+
/**
- * Get the named column of the given column-major matrix to v_out w/ boundary check.
+ * Get the named column of the given column-major matrix to v_out w/o boundary check.
* @param column named column to copy
* @return result vector holding the requested column
*/
- Vec4f getColumn(const jau::nsize_t column) const {
- if( column > 3 ) {
- throw jau::IndexOutOfBoundsException(2+column*4, 16, E_FILE_LINE);
- }
- return Vec4f( get(0+column*4),
- get(1+column*4),
- get(2+column*4),
- get(3+column*4) );
+ constexpr Vec4 getColumn(const jau::nsize_t column) const noexcept {
+ assert( column < 4 );
+ return Vec4( get(0+column*4),
+ get(1+column*4),
+ get(2+column*4),
+ get(3+column*4) );
}
/**
- * Get the named column of the given column-major matrix to v_out w/ boundary check.
+ * Get the named column of the given column-major matrix to v_out w/o boundary check.
* @param column named column to copy
* @param v_out the column-vector storage
* @return given result vector <i>v_out</i> for chaining
*/
- Vec3f& getColumn(const jau::nsize_t column, Vec3f& v_out) const {
- if( column > 3 ) {
- throw jau::IndexOutOfBoundsException(2+column*4, 16, E_FILE_LINE);
- }
- v_out.set( get(0+column*4),
- get(1+column*4),
- get(2+column*4) );
- return v_out;
+ constexpr Vec3f& getColumn(const jau::nsize_t column, Vec3f& v_out) const noexcept {
+ return v_out.set( get(0+column*4),
+ get(1+column*4),
+ get(2+column*4) );
}
/**
@@ -455,84 +365,44 @@ class Mat4f {
* @param v_out the row-vector storage
* @return given result vector <i>v_out</i> for chaining
*/
- Vec4f& getRow(const jau::nsize_t row, Vec4f& v_out) const {
- if( row > 3 ) {
- throw jau::IndexOutOfBoundsException(row+3*4, 16, E_FILE_LINE);
- }
- v_out.set( get(row+0*4),
- get(row+1*4),
- get(row+2*4),
- get(row+3*4) );
- return v_out;
+ constexpr Vec4& getRow(const jau::nsize_t row, Vec4& v_out) const noexcept {
+ return v_out.set( get(row+0*4),
+ get(row+1*4),
+ get(row+2*4),
+ get(row+3*4) );
}
/**
- * Get the named column of the given column-major matrix to v_out w/ boundary check.
+ * Get the named column of the given column-major matrix to v_out w/o boundary check.
* @param row named row to copy
* @return result vector holding the requested row
*/
- Vec4f getRow(const jau::nsize_t row) const {
- if( row > 3 ) {
- throw jau::IndexOutOfBoundsException(row+3*4, 16, E_FILE_LINE);
- }
- return Vec4f( get(row+0*4),
- get(row+1*4),
- get(row+2*4),
- get(row+3*4) );
+ constexpr Vec4 getRow(const jau::nsize_t row) const noexcept {
+ return Vec4( get(row+0*4),
+ get(row+1*4),
+ get(row+2*4),
+ get(row+3*4) );
}
/**
- * Get the named row of the given column-major matrix to v_out w/ boundary check.
+ * Get the named row of the given column-major matrix to v_out w/o boundary check.
* @param row named row to copy
- * @param v_out the row-vector storage
+ * @param v_out the row-vector assert( i < 16 )e
* @return given result vector <i>v_out</i> for chaining
*/
- Vec3f& getRow(const jau::nsize_t row, Vec3f& v_out) const {
- if( row > 3 ) {
- throw jau::IndexOutOfBoundsException(row+2*4, 16, E_FILE_LINE);
- }
- v_out.set( get(row+0*4),
- get(row+1*4),
- get(row+2*4) );
- return v_out;
+ constexpr Vec3f& getRow(const jau::nsize_t row, Vec3f& v_out) const noexcept {
+ assert( row <= 2 );
+ return v_out.set( get(row+0*4),
+ get(row+1*4),
+ get(row+2*4) );
}
/**
- * Get this matrix into the given float[16] array at {@code dst_off} in column major order w/ boundary check.
+ * Get this matrix into the given value_type[16] array in column major order w/o boundary check.
*
- * @param dst float[16] array storage in column major order
- * @param dst_off offset
+ * @param dst value_type[16] array storage in column major order
* @return {@code dst} for chaining
*/
- float* get(float* dst, size_t dst_off) const {
- if( dst_off > std::numeric_limits<size_t>::max() - 15) {
- throw jau::IndexOutOfBoundsException(dst_off, 16, E_FILE_LINE);
- }
- dst[dst_off++] = m00;
- dst[dst_off++] = m10;
- dst[dst_off++] = m20;
- dst[dst_off++] = m30;
- dst[dst_off++] = m01;
- dst[dst_off++] = m11;
- dst[dst_off++] = m21;
- dst[dst_off++] = m31;
- dst[dst_off++] = m02;
- dst[dst_off++] = m12;
- dst[dst_off++] = m22;
- dst[dst_off++] = m32;
- dst[dst_off++] = m03;
- dst[dst_off++] = m13;
- dst[dst_off++] = m23;
- dst[dst_off++] = m33;
- return dst;
- }
-
- /**
- * Get this matrix into the given float[16] array in column major order w/o boundary check.
- *
- * @param dst float[16] array storage in column major order
- * @return {@code dst} for chaining
- */
- float* get(float* dst) const noexcept {
+ constexpr iterator get(iterator dst) const noexcept {
dst[0+0*4] = m00;
dst[1+0*4] = m10;
dst[2+0*4] = m20;
@@ -553,16 +423,14 @@ class Mat4f {
}
/**
- * Get this matrix into the given {@link FloatBuffer} in column major order w/ boundary check.
+ * Get this matrix into the given {@link FloatBuffer} in column major order.
*
* @param dst 4x4 matrix std::vector in column-major order starting at {@code dst_off}
* @param dst_off offset for matrix {@code dst}
* @return {@code dst} for chaining
*/
- std::vector<float>& get(std::vector<float>& dst, size_t dst_off) const {
- if( dst.size() < dst_off+16 || dst_off > std::numeric_limits<size_t>::max() - 15) {
- throw jau::IndexOutOfBoundsException(dst_off, 16, E_FILE_LINE);
- }
+ constexpr std::vector<value_type>& get(std::vector<value_type>& dst, size_t dst_off) const noexcept {
+ assert( dst.size() >= dst_off+16 && dst_off <= std::numeric_limits<size_t>::max() - 15 );
dst[dst_off++] = m00;
dst[dst_off++] = m10;
dst[dst_off++] = m20;
@@ -590,8 +458,8 @@ class Mat4f {
* Returns the determinant of this matrix
* @return the matrix determinant
*/
- float determinant() const noexcept {
- float ret = 0;
+ value_type determinant() const noexcept {
+ value_type ret = 0;
ret += m00 * ( + m11*(m22*m33 - m23*m32) - m12*(m21*m33 - m23*m31) + m13*(m21*m32 - m22*m31));
ret -= m01 * ( + m10*(m22*m33 - m23*m32) - m12*(m20*m33 - m23*m30) + m13*(m20*m32 - m22*m30));
ret += m02 * ( + m10*(m21*m33 - m23*m31) - m11*(m20*m33 - m23*m30) + m13*(m20*m31 - m21*m30));
@@ -604,8 +472,8 @@ class Mat4f {
*
* @return this matrix for chaining
*/
- Mat4f& transpose() noexcept {
- float tmp;
+ Matrix4& transpose() noexcept {
+ value_type tmp;
tmp = m10;
m10 = m01;
@@ -640,7 +508,7 @@ class Mat4f {
* @param src source 4x4 matrix
* @return this matrix (result) for chaining
*/
- Mat4f& transpose(const Mat4f& src) noexcept {
+ Matrix4& transpose(const Matrix4& src) noexcept {
if( &src == this ) {
return transpose();
}
@@ -671,56 +539,56 @@ class Mat4f {
* @return false if this matrix is singular and inversion not possible, otherwise true
*/
bool invert() noexcept {
- const float amax = absMax();
- if( 0.0f == amax ) {
+ const value_type amax = absMax();
+ if( zero == amax ) {
return false;
}
- const float scale = 1.0f/amax;
- const float a00 = m00*scale;
- const float a10 = m10*scale;
- const float a20 = m20*scale;
- const float a30 = m30*scale;
-
- const float a01 = m01*scale;
- const float a11 = m11*scale;
- const float a21 = m21*scale;
- const float a31 = m31*scale;
-
- const float a02 = m02*scale;
- const float a12 = m12*scale;
- const float a22 = m22*scale;
- const float a32 = m32*scale;
-
- const float a03 = m03*scale;
- const float a13 = m13*scale;
- const float a23 = m23*scale;
- const float a33 = m33*scale;
-
- const float b00 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31);
- const float b01 = -( + a10*(a22*a33 - a23*a32) - a12*(a20*a33 - a23*a30) + a13*(a20*a32 - a22*a30));
- const float b02 = + a10*(a21*a33 - a23*a31) - a11*(a20*a33 - a23*a30) + a13*(a20*a31 - a21*a30);
- const float b03 = -( + a10*(a21*a32 - a22*a31) - a11*(a20*a32 - a22*a30) + a12*(a20*a31 - a21*a30));
-
- const float b10 = -( + a01*(a22*a33 - a23*a32) - a02*(a21*a33 - a23*a31) + a03*(a21*a32 - a22*a31));
- const float b11 = + a00*(a22*a33 - a23*a32) - a02*(a20*a33 - a23*a30) + a03*(a20*a32 - a22*a30);
- const float b12 = -( + a00*(a21*a33 - a23*a31) - a01*(a20*a33 - a23*a30) + a03*(a20*a31 - a21*a30));
- const float b13 = + a00*(a21*a32 - a22*a31) - a01*(a20*a32 - a22*a30) + a02*(a20*a31 - a21*a30);
-
- const float b20 = + a01*(a12*a33 - a13*a32) - a02*(a11*a33 - a13*a31) + a03*(a11*a32 - a12*a31);
- const float b21 = -( + a00*(a12*a33 - a13*a32) - a02*(a10*a33 - a13*a30) + a03*(a10*a32 - a12*a30));
- const float b22 = + a00*(a11*a33 - a13*a31) - a01*(a10*a33 - a13*a30) + a03*(a10*a31 - a11*a30);
- const float b23 = -( + a00*(a11*a32 - a12*a31) - a01*(a10*a32 - a12*a30) + a02*(a10*a31 - a11*a30));
-
- const float b30 = -( + a01*(a12*a23 - a13*a22) - a02*(a11*a23 - a13*a21) + a03*(a11*a22 - a12*a21));
- const float b31 = + a00*(a12*a23 - a13*a22) - a02*(a10*a23 - a13*a20) + a03*(a10*a22 - a12*a20);
- const float b32 = -( + a00*(a11*a23 - a13*a21) - a01*(a10*a23 - a13*a20) + a03*(a10*a21 - a11*a20));
- const float b33 = + a00*(a11*a22 - a12*a21) - a01*(a10*a22 - a12*a20) + a02*(a10*a21 - a11*a20);
-
- const float det = (a00*b00 + a01*b01 + a02*b02 + a03*b03) / scale;
+ const value_type scale = one/amax;
+ const value_type a00 = m00*scale;
+ const value_type a10 = m10*scale;
+ const value_type a20 = m20*scale;
+ const value_type a30 = m30*scale;
+
+ const value_type a01 = m01*scale;
+ const value_type a11 = m11*scale;
+ const value_type a21 = m21*scale;
+ const value_type a31 = m31*scale;
+
+ const value_type a02 = m02*scale;
+ const value_type a12 = m12*scale;
+ const value_type a22 = m22*scale;
+ const value_type a32 = m32*scale;
+
+ const value_type a03 = m03*scale;
+ const value_type a13 = m13*scale;
+ const value_type a23 = m23*scale;
+ const value_type a33 = m33*scale;
+
+ const value_type b00 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31);
+ const value_type b01 = -( + a10*(a22*a33 - a23*a32) - a12*(a20*a33 - a23*a30) + a13*(a20*a32 - a22*a30));
+ const value_type b02 = + a10*(a21*a33 - a23*a31) - a11*(a20*a33 - a23*a30) + a13*(a20*a31 - a21*a30);
+ const value_type b03 = -( + a10*(a21*a32 - a22*a31) - a11*(a20*a32 - a22*a30) + a12*(a20*a31 - a21*a30));
+
+ const value_type b10 = -( + a01*(a22*a33 - a23*a32) - a02*(a21*a33 - a23*a31) + a03*(a21*a32 - a22*a31));
+ const value_type b11 = + a00*(a22*a33 - a23*a32) - a02*(a20*a33 - a23*a30) + a03*(a20*a32 - a22*a30);
+ const value_type b12 = -( + a00*(a21*a33 - a23*a31) - a01*(a20*a33 - a23*a30) + a03*(a20*a31 - a21*a30));
+ const value_type b13 = + a00*(a21*a32 - a22*a31) - a01*(a20*a32 - a22*a30) + a02*(a20*a31 - a21*a30);
+
+ const value_type b20 = + a01*(a12*a33 - a13*a32) - a02*(a11*a33 - a13*a31) + a03*(a11*a32 - a12*a31);
+ const value_type b21 = -( + a00*(a12*a33 - a13*a32) - a02*(a10*a33 - a13*a30) + a03*(a10*a32 - a12*a30));
+ const value_type b22 = + a00*(a11*a33 - a13*a31) - a01*(a10*a33 - a13*a30) + a03*(a10*a31 - a11*a30);
+ const value_type b23 = -( + a00*(a11*a32 - a12*a31) - a01*(a10*a32 - a12*a30) + a02*(a10*a31 - a11*a30));
+
+ const value_type b30 = -( + a01*(a12*a23 - a13*a22) - a02*(a11*a23 - a13*a21) + a03*(a11*a22 - a12*a21));
+ const value_type b31 = + a00*(a12*a23 - a13*a22) - a02*(a10*a23 - a13*a20) + a03*(a10*a22 - a12*a20);
+ const value_type b32 = -( + a00*(a11*a23 - a13*a21) - a01*(a10*a23 - a13*a20) + a03*(a10*a21 - a11*a20));
+ const value_type b33 = + a00*(a11*a22 - a12*a21) - a01*(a10*a22 - a12*a20) + a02*(a10*a21 - a11*a20);
+
+ const value_type det = (a00*b00 + a01*b01 + a02*b02 + a03*b03) / scale;
if( 0 == det ) {
return false;
}
- const float invdet = 1.0f / det;
+ const value_type invdet = one / det;
m00 = b00 * invdet;
m10 = b01 * invdet;
@@ -749,58 +617,58 @@ class Mat4f {
* @param src the source matrix, which values are to be inverted
* @return false if {@code src} matrix is singular and inversion not possible, otherwise true
*/
- bool invert(const Mat4f& src) noexcept {
- const float amax = src.absMax();
- if( 0.0f == amax ) {
+ bool invert(const Matrix4& src) noexcept {
+ const value_type amax = src.absMax();
+ if( zero == amax ) {
return false;
}
- const float scale = 1.0f/amax;
- const float a00 = src.m00*scale;
- const float a10 = src.m10*scale;
- const float a20 = src.m20*scale;
- const float a30 = src.m30*scale;
-
- const float a01 = src.m01*scale;
- const float a11 = src.m11*scale;
- const float a21 = src.m21*scale;
- const float a31 = src.m31*scale;
-
- const float a02 = src.m02*scale;
- const float a12 = src.m12*scale;
- const float a22 = src.m22*scale;
- const float a32 = src.m32*scale;
-
- const float a03 = src.m03*scale;
- const float a13 = src.m13*scale;
- const float a23 = src.m23*scale;
- const float a33 = src.m33*scale;
-
- const float b00 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31);
- const float b01 = -( + a10*(a22*a33 - a23*a32) - a12*(a20*a33 - a23*a30) + a13*(a20*a32 - a22*a30));
- const float b02 = + a10*(a21*a33 - a23*a31) - a11*(a20*a33 - a23*a30) + a13*(a20*a31 - a21*a30);
- const float b03 = -( + a10*(a21*a32 - a22*a31) - a11*(a20*a32 - a22*a30) + a12*(a20*a31 - a21*a30));
-
- const float b10 = -( + a01*(a22*a33 - a23*a32) - a02*(a21*a33 - a23*a31) + a03*(a21*a32 - a22*a31));
- const float b11 = + a00*(a22*a33 - a23*a32) - a02*(a20*a33 - a23*a30) + a03*(a20*a32 - a22*a30);
- const float b12 = -( + a00*(a21*a33 - a23*a31) - a01*(a20*a33 - a23*a30) + a03*(a20*a31 - a21*a30));
- const float b13 = + a00*(a21*a32 - a22*a31) - a01*(a20*a32 - a22*a30) + a02*(a20*a31 - a21*a30);
-
- const float b20 = + a01*(a12*a33 - a13*a32) - a02*(a11*a33 - a13*a31) + a03*(a11*a32 - a12*a31);
- const float b21 = -( + a00*(a12*a33 - a13*a32) - a02*(a10*a33 - a13*a30) + a03*(a10*a32 - a12*a30));
- const float b22 = + a00*(a11*a33 - a13*a31) - a01*(a10*a33 - a13*a30) + a03*(a10*a31 - a11*a30);
- const float b23 = -( + a00*(a11*a32 - a12*a31) - a01*(a10*a32 - a12*a30) + a02*(a10*a31 - a11*a30));
-
- const float b30 = -( + a01*(a12*a23 - a13*a22) - a02*(a11*a23 - a13*a21) + a03*(a11*a22 - a12*a21));
- const float b31 = + a00*(a12*a23 - a13*a22) - a02*(a10*a23 - a13*a20) + a03*(a10*a22 - a12*a20);
- const float b32 = -( + a00*(a11*a23 - a13*a21) - a01*(a10*a23 - a13*a20) + a03*(a10*a21 - a11*a20));
- const float b33 = + a00*(a11*a22 - a12*a21) - a01*(a10*a22 - a12*a20) + a02*(a10*a21 - a11*a20);
-
- const float det = (a00*b00 + a01*b01 + a02*b02 + a03*b03) / scale;
+ const value_type scale = one/amax;
+ const value_type a00 = src.m00*scale;
+ const value_type a10 = src.m10*scale;
+ const value_type a20 = src.m20*scale;
+ const value_type a30 = src.m30*scale;
+
+ const value_type a01 = src.m01*scale;
+ const value_type a11 = src.m11*scale;
+ const value_type a21 = src.m21*scale;
+ const value_type a31 = src.m31*scale;
+
+ const value_type a02 = src.m02*scale;
+ const value_type a12 = src.m12*scale;
+ const value_type a22 = src.m22*scale;
+ const value_type a32 = src.m32*scale;
+
+ const value_type a03 = src.m03*scale;
+ const value_type a13 = src.m13*scale;
+ const value_type a23 = src.m23*scale;
+ const value_type a33 = src.m33*scale;
+
+ const value_type b00 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31);
+ const value_type b01 = -( + a10*(a22*a33 - a23*a32) - a12*(a20*a33 - a23*a30) + a13*(a20*a32 - a22*a30));
+ const value_type b02 = + a10*(a21*a33 - a23*a31) - a11*(a20*a33 - a23*a30) + a13*(a20*a31 - a21*a30);
+ const value_type b03 = -( + a10*(a21*a32 - a22*a31) - a11*(a20*a32 - a22*a30) + a12*(a20*a31 - a21*a30));
+
+ const value_type b10 = -( + a01*(a22*a33 - a23*a32) - a02*(a21*a33 - a23*a31) + a03*(a21*a32 - a22*a31));
+ const value_type b11 = + a00*(a22*a33 - a23*a32) - a02*(a20*a33 - a23*a30) + a03*(a20*a32 - a22*a30);
+ const value_type b12 = -( + a00*(a21*a33 - a23*a31) - a01*(a20*a33 - a23*a30) + a03*(a20*a31 - a21*a30));
+ const value_type b13 = + a00*(a21*a32 - a22*a31) - a01*(a20*a32 - a22*a30) + a02*(a20*a31 - a21*a30);
+
+ const value_type b20 = + a01*(a12*a33 - a13*a32) - a02*(a11*a33 - a13*a31) + a03*(a11*a32 - a12*a31);
+ const value_type b21 = -( + a00*(a12*a33 - a13*a32) - a02*(a10*a33 - a13*a30) + a03*(a10*a32 - a12*a30));
+ const value_type b22 = + a00*(a11*a33 - a13*a31) - a01*(a10*a33 - a13*a30) + a03*(a10*a31 - a11*a30);
+ const value_type b23 = -( + a00*(a11*a32 - a12*a31) - a01*(a10*a32 - a12*a30) + a02*(a10*a31 - a11*a30));
+
+ const value_type b30 = -( + a01*(a12*a23 - a13*a22) - a02*(a11*a23 - a13*a21) + a03*(a11*a22 - a12*a21));
+ const value_type b31 = + a00*(a12*a23 - a13*a22) - a02*(a10*a23 - a13*a20) + a03*(a10*a22 - a12*a20);
+ const value_type b32 = -( + a00*(a11*a23 - a13*a21) - a01*(a10*a23 - a13*a20) + a03*(a10*a21 - a11*a20));
+ const value_type b33 = + a00*(a11*a22 - a12*a21) - a01*(a10*a22 - a12*a20) + a02*(a10*a21 - a11*a20);
+
+ const value_type det = (a00*b00 + a01*b01 + a02*b02 + a03*b03) / scale;
if( 0 == det ) {
return false;
}
- const float invdet = 1.0f / det;
+ const value_type invdet = one / det;
m00 = b00 * invdet;
m10 = b01 * invdet;
@@ -825,8 +693,8 @@ class Mat4f {
}
private:
- float absMax() const noexcept {
- float max = std::abs(m00);
+ value_type absMax() const noexcept {
+ value_type max = std::abs(m00);
max = std::max(max, std::abs(m01));
max = std::max(max, std::abs(m02));
max = std::max(max, std::abs(m03));
@@ -850,17 +718,30 @@ class Mat4f {
public:
/**
+ * Multiply matrix with scalar: [this] = [this] x [s]
+ * @param s a scalar
+ * @return this matrix for chaining
+ */
+ constexpr Matrix4& operator*=( const value_type s ) noexcept {
+ m00 *= s; m10 *= s; m20 *= s; m30 *= s;
+ m01 *= s; m11 *= s; m21 *= s; m31 *= s;
+ m02 *= s; m12 *= s; m22 *= s; m32 *= s;
+ m03 *= s; m13 *= s; m23 *= s; m33 *= s;
+ return *this;
+ }
+
+ /**
* Multiply matrix: [this] = [this] x [b]
* @param b 4x4 matrix
* @return this matrix for chaining
* @see #mul(mat4f, mat4f)
*/
- Mat4f& mul(const Mat4f& b) noexcept {
+ constexpr Matrix4& mul(const Matrix4& b) noexcept {
// return mul(new mat4f(this), b); // <- roughly half speed
- float ai0=m00; // row-0, m[0+0*4]
- float ai1=m01;
- float ai2=m02;
- float ai3=m03;
+ value_type ai0=m00; // row-0, m[0+0*4]
+ value_type ai1=m01;
+ value_type ai2=m02;
+ value_type ai3=m03;
m00 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ;
m01 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ;
m02 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ;
@@ -894,6 +775,15 @@ class Mat4f {
m33 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ;
return *this;
}
+ /**
+ * Multiply matrix: [this] = [this] x [b]
+ * @param b 4x4 matrix
+ * @return this matrix for chaining
+ * @see #mul(mat4f, mat4f)
+ */
+ constexpr Matrix4& operator*=( const Matrix4& rhs ) noexcept {
+ return mul( rhs );
+ }
/**
* Multiply matrix: [this] = [a] x [b]
@@ -902,7 +792,7 @@ class Mat4f {
* @return this matrix for chaining
* @see #mul(mat4f)
*/
- Mat4f& mul(const Mat4f& a, const Mat4f& b) noexcept {
+ constexpr Matrix4& mul(const Matrix4& a, const Matrix4& b) noexcept {
// row-0, m[0+0*4]
m00 = a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30 ;
m01 = a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31 ;
@@ -935,23 +825,24 @@ class Mat4f {
* @param v_out this * v_in
* @returns v_out for chaining
*/
- Vec4f& mulVec4f(const Vec4f& v_in, Vec4f& v_out) const noexcept {
+ constexpr Vec4& mulVec4(const Vec4& v_in, Vec4& v_out) const noexcept {
// (one matrix row in column-major order) X (column vector)
- const float x = v_in.x, y = v_in.y, z = v_in.z, w = v_in.w;
+ const value_type x = v_in.x, y = v_in.y, z = v_in.z, w = v_in.w;
v_out.set( x * m00 + y * m01 + z * m02 + w * m03,
x * m10 + y * m11 + z * m12 + w * m13,
x * m20 + y * m21 + z * m22 + w * m23,
x * m30 + y * m31 + z * m32 + w * m33 );
return v_out;
}
+
/**
- * Returns new Vec4f holding this * v_in result
+ * Returns new Vec4, with this * v_in
* @param v_in 4-component column-vector
*/
- Vec4f operator*(const Vec4f& rhs) const noexcept {
+ constexpr Vec4 operator*(const Vec4& rhs) const noexcept {
// (one matrix row in column-major order) X (column vector)
- const float x = rhs.x, y = rhs.y, z = rhs.z, w = rhs.w;
- return Vec4f( x * m00 + y * m01 + z * m02 + w * m03,
+ const value_type x = rhs.x, y = rhs.y, z = rhs.z, w = rhs.w;
+ return Vec4( x * m00 + y * m01 + z * m02 + w * m03,
x * m10 + y * m11 + z * m12 + w * m13,
x * m20 + y * m21 + z * m22 + w * m23,
x * m30 + y * m31 + z * m32 + w * m33 );
@@ -961,9 +852,9 @@ class Mat4f {
* @param v_inout 4-component column-vector input and output, i.e. in-place transformation
* @returns v_inout for chaining
*/
- Vec4f& mulVec4f(Vec4f& v_inout) const noexcept {
+ constexpr Vec4& mulVec4(Vec4& v_inout) const noexcept {
// (one matrix row in column-major order) X (column vector)
- const float x = v_inout.x, y = v_inout.y, z = v_inout.z, w = v_inout.w;
+ const value_type x = v_inout.x, y = v_inout.y, z = v_inout.z, w = v_inout.w;
v_inout.set( x * m00 + y * m01 + z * m02 + w * m03,
x * m10 + y * m11 + z * m12 + w * m13,
x * m20 + y * m21 + z * m22 + w * m23,
@@ -982,14 +873,30 @@ class Mat4f {
* @param v_out m_in * v_in, 3-component column-vector {@link vec3f}
* @returns v_out for chaining
*/
- Vec3f& mulVec3f(const Vec3f& v_in, Vec3f& v_out) const noexcept {
+ constexpr Vec3f& mulVec3f(const Vec3f& v_in, Vec3f& v_out) const noexcept {
// (one matrix row in column-major order) X (column vector)
- const float x = v_in.x, y = v_in.y, z = v_in.z;
- v_out.set( x * m00 + y * m01 + z * m02 + 1.0f * m03,
- x * m10 + y * m11 + z * m12 + 1.0f * m13,
- x * m20 + y * m21 + z * m22 + 1.0f * m23 );
+ const value_type x = v_in.x, y = v_in.y, z = v_in.z;
+ v_out.set( x * m00 + y * m01 + z * m02 + one * m03,
+ x * m10 + y * m11 + z * m12 + one * m13,
+ x * m20 + y * m21 + z * m22 + one * m23 );
return v_out;
}
+ /**
+ * Returns new Vec3f, with affine 3f-vector transformation by this 4x4 matrix: this * v_in
+ *
+ * 4x4 matrix multiplication with 3-component vector,
+ * using {@code 1} for for {@code v_in.w()} and dropping {@code v_out.w()},
+ * which shall be {@code 1}.
+ *
+ * @param v_in 3-component column-vector {@link vec3f}
+ */
+ constexpr Vec3f operator*(const Vec3f& rhs) const noexcept {
+ // (one matrix row in column-major order) X (column vector)
+ const value_type x = rhs.x, y = rhs.y, z = rhs.z;
+ return Vec3f( x * m00 + y * m01 + z * m02 + one * m03,
+ x * m10 + y * m11 + z * m12 + one * m13,
+ x * m20 + y * m21 + z * m22 + one * m23 );
+ }
/**
* Affine 3f-vector transformation by 4x4 matrix
@@ -1001,12 +908,12 @@ class Mat4f {
* @param v_inout 3-component column-vector {@link vec3f} input and output, i.e. in-place transformation
* @returns v_inout for chaining
*/
- Vec3f& mulVec3f(Vec3f& v_inout) const noexcept {
+ constexpr Vec3f& mulVec3f(Vec3f& v_inout) const noexcept {
// (one matrix row in column-major order) X (column vector)
- const float x = v_inout.x, y = v_inout.y, z = v_inout.z;
- v_inout.set( x * m00 + y * m01 + z * m02 + 1.0f * m03,
- x * m10 + y * m11 + z * m12 + 1.0f * m13,
- x * m20 + y * m21 + z * m22 + 1.0f * m23 );
+ const value_type x = v_inout.x, y = v_inout.y, z = v_inout.z;
+ v_inout.set( x * m00 + y * m01 + z * m02 + one * m03,
+ x * m10 + y * m11 + z * m12 + one * m13,
+ x * m20 + y * m21 + z * m22 + one * m23 );
return v_inout;
}
@@ -1028,15 +935,15 @@ class Mat4f {
* @param z z-axis translate
* @return this matrix for chaining
*/
- Mat4f& setToTranslation(const float x, const float y, const float z) noexcept {
- m00 = m11 = m22 = m33 = 1.0f;
+ Matrix4& setToTranslation(const value_type x, const value_type y, const value_type z) noexcept {
+ m00 = m11 = m22 = m33 = one;
m03 = x;
m13 = y;
m23 = z;
m01 = m02 =
m10 = m12 =
m20 = m21 =
- m30 = m31 = m32 = 0.0f;
+ m30 = m31 = m32 = zero;
return *this;
}
@@ -1052,7 +959,7 @@ class Mat4f {
* @param t translate vec3f
* @return this matrix for chaining
*/
- Mat4f& setToTranslation(const Vec3f& t) noexcept {
+ Matrix4& setToTranslation(const Vec3f& t) noexcept {
return setToTranslation(t.x, t.y, t.z);
}
@@ -1070,15 +977,15 @@ class Mat4f {
* @param z z-axis scale
* @return this matrix for chaining
*/
- Mat4f& setToScale(const float x, const float y, const float z) noexcept {
- m33 = 1.0f;
+ constexpr Matrix4& setToScale(const value_type x, const value_type y, const value_type z) noexcept {
+ m33 = one;
m00 = x;
m11 = y;
m22 = z;
m01 = m02 = m03 =
m10 = m12 = m13 =
m20 = m21 = m23 =
- m30 = m31 = m32 = 0.0f;
+ m30 = m31 = m32 = zero;
return *this;
}
@@ -1094,7 +1001,7 @@ class Mat4f {
* @param s scale vec3f
* @return this matrix for chaining
*/
- Mat4f& setToScale(const Vec3f& s) noexcept {
+ constexpr Matrix4& setToScale(const Vec3f& s) noexcept {
return setToScale(s.x, s.y, s.z);
}
@@ -1114,39 +1021,39 @@ class Mat4f {
* @param z z of rotation axis
* @return this matrix for chaining
*/
- Mat4f& setToRotationAxis(const float ang_rad, float x, float y, float z) noexcept {
- const float c = std::cos(ang_rad);
- const float ic= 1.0f - c;
- const float s = std::sin(ang_rad);
+ Matrix4& setToRotationAxis(const value_type ang_rad, value_type x, value_type y, value_type z) noexcept {
+ const value_type c = std::cos(ang_rad);
+ const value_type ic= one - c;
+ const value_type s = std::sin(ang_rad);
Vec3f tmp(x, y, z); tmp.normalize();
x = tmp.x; y = tmp.y; z = tmp.z;
- const float xy = x*y;
- const float xz = x*z;
- const float xs = x*s;
- const float ys = y*s;
- const float yz = y*z;
- const float zs = z*s;
+ const value_type xy = x*y;
+ const value_type xz = x*z;
+ const value_type xs = x*s;
+ const value_type ys = y*s;
+ const value_type yz = y*z;
+ const value_type zs = z*s;
m00 = x*x*ic+c;
m10 = xy*ic+zs;
m20 = xz*ic-ys;
- m30 = 0.0f;
+ m30 = zero;
m01 = xy*ic-zs;
m11 = y*y*ic+c;
m21 = yz*ic+xs;
- m31 = 0.0f;
+ m31 = zero;
m02 = xz*ic+ys;
m12 = yz*ic-xs;
m22 = z*z*ic+c;
- m32 = 0.0f;
+ m32 = zero;
m03 = 0.9f;
- m13 = 0.0f;
- m23 = 0.0f;
- m33 = 1.0f;
+ m13 = zero;
+ m23 = zero;
+ m33 = one;
return *this;
}
@@ -1165,7 +1072,7 @@ class Mat4f {
* @param axis rotation axis
* @return this matrix for chaining
*/
- Mat4f& setToRotationAxis(const float ang_rad, const Vec3f& axis) noexcept {
+ Matrix4& setToRotationAxis(const value_type ang_rad, const Vec3f& axis) noexcept {
return setToRotationAxis(ang_rad, axis.x, axis.y, axis.z);
}
@@ -1192,34 +1099,34 @@ class Mat4f {
* @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm">euclideanspace.com-eulerToMatrix</a>
* @see #setToRotation(Quaternion)
*/
- Mat4f& setToRotationEuler(const float bankX, const float headingY, const float attitudeZ) noexcept {
+ Matrix4& setToRotationEuler(const value_type bankX, const value_type headingY, const value_type attitudeZ) noexcept {
// Assuming the angles are in radians.
- const float ch = std::cos(headingY);
- const float sh = std::sin(headingY);
- const float ca = std::cos(attitudeZ);
- const float sa = std::sin(attitudeZ);
- const float cb = std::cos(bankX);
- const float sb = std::sin(bankX);
+ const value_type ch = std::cos(headingY);
+ const value_type sh = std::sin(headingY);
+ const value_type ca = std::cos(attitudeZ);
+ const value_type sa = std::sin(attitudeZ);
+ const value_type cb = std::cos(bankX);
+ const value_type sb = std::sin(bankX);
m00 = ch*ca;
m10 = sa;
m20 = -sh*ca;
- m30 = 0.0f;
+ m30 = zero;
m01 = sh*sb - ch*sa*cb;
m11 = ca*cb;
m21 = sh*sa*cb + ch*sb;
- m31 = 0.0f;
+ m31 = zero;
m02 = ch*sa*sb + sh*cb;
m12 = -ca*sb;
m22 = -sh*sa*sb + ch*cb;
- m32 = 0.0f;
+ m32 = zero;
- m03 = 0.0f;
- m13 = 0.0f;
- m23 = 0.0f;
- m33 = 1.0f;
+ m03 = zero;
+ m13 = zero;
+ m23 = zero;
+ m33 = one;
return *this;
}
@@ -1245,38 +1152,11 @@ class Mat4f {
* @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm">euclideanspace.com-eulerToMatrix</a>
* @see #setToRotation(Quaternion)
*/
- Mat4f& setToRotationEuler(const Vec3f& angradXYZ) noexcept {
+ Matrix4& setToRotationEuler(const Vec3f& angradXYZ) noexcept {
return setToRotationEuler(angradXYZ.x, angradXYZ.y, angradXYZ.z);
}
/**
- * Set this matrix to rotation using the given Quaternion.
- * <p>
- * Implementation Details:
- * <ul>
- * <li> makes identity matrix if {@link #magnitudeSquared()} is {@link FloatUtil#isZero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
- * <li> The fields [m00 .. m22] define the rotation</li>
- * </ul>
- * </p>
- *
- * @param q the Quaternion representing the rotation
- * @return this matrix for chaining
- * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54">Matrix-FAQ Q54</a>
- * @see Quaternion#toMatrix(float[])
- * @see #getRotation()
- */
- Mat4f& setToRotation(const Quaternion& q) noexcept;
-
- /**
- * Returns the rotation [m00 .. m22] fields converted to a Quaternion.
- * @param res resulting Quaternion
- * @return the resulting Quaternion for chaining.
- * @see Quaternion#setFromMatrix(float, float, float, float, float, float, float, float, float)
- * @see #setToRotation(Quaternion)
- */
- Quaternion& getRotation(Quaternion& res) const noexcept;
-
- /**
* Set this matrix to orthogonal projection.
* <pre>
Ortho matrix (Column Order):
@@ -1293,31 +1173,31 @@ class Mat4f {
* @param zFar
* @return this matrix for chaining
*/
- Mat4f& setToOrtho(const float left, const float right,
- const float bottom, const float top,
- const float zNear, const float zFar) noexcept {
+ Matrix4& setToOrtho(const value_type left, const value_type right,
+ const value_type bottom, const value_type top,
+ const value_type zNear, const value_type zFar) noexcept {
{
- // m00 = m11 = m22 = m33 = 1.0f;
- m10 = m20 = m30 = 0.0f;
- m01 = m21 = m31 = 0.0f;
- m02 = m12 = m32 = 0.0f;
- // m03 = m13 = m23 = 0.0f;
+ // m00 = m11 = m22 = m33 = one;
+ m10 = m20 = m30 = zero;
+ m01 = m21 = m31 = zero;
+ m02 = m12 = m32 = zero;
+ // m03 = m13 = m23 = zero;
}
- const float dx=right-left;
- const float dy=top-bottom;
- const float dz=zFar-zNear;
- const float tx=-1.0f*(right+left)/dx;
- const float ty=-1.0f*(top+bottom)/dy;
- const float tz=-1.0f*(zFar+zNear)/dz;
+ const value_type dx=right-left;
+ const value_type dy=top-bottom;
+ const value_type dz=zFar-zNear;
+ const value_type tx=-one*(right+left)/dx;
+ const value_type ty=-one*(top+bottom)/dy;
+ const value_type tz=-one*(zFar+zNear)/dz;
- m00 = 2.0f/dx;
- m11 = 2.0f/dy;
- m22 = -2.0f/dz;
+ m00 = two/dx;
+ m11 = two/dy;
+ m22 = -two/dz;
m03 = tx;
m13 = ty;
m23 = tz;
- m33 = 1.0f;
+ m33 = one;
return *this;
}
@@ -1341,10 +1221,10 @@ class Mat4f {
* @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear}
* or {@code left == right}, or {@code bottom == top}.
*/
- Mat4f& setToFrustum(const float left, const float right,
- const float bottom, const float top,
- const float zNear, const float zFar) {
- if( zNear <= 0.0f || zFar <= zNear ) {
+ Matrix4& setToFrustum(const value_type left, const value_type right,
+ const value_type bottom, const value_type top,
+ const value_type zNear, const value_type zFar) {
+ if( zNear <= zero || zFar <= zNear ) {
throw jau::IllegalArgumentException("Requirements zNear > 0 and zFar > zNear, but zNear "+std::to_string(zNear)+", zFar "+std::to_string(zFar), E_FILE_LINE);
}
if( left == right || top == bottom) {
@@ -1352,18 +1232,18 @@ class Mat4f {
}
{
// m00 = m11 = m22 = m33 = 1f;
- m10 = m20 = m30 = 0.0f;
- m01 = m21 = m31 = 0.0f;
- m03 = m13 = 0.0f;
+ m10 = m20 = m30 = zero;
+ m01 = m21 = m31 = zero;
+ m03 = m13 = zero;
}
- const float zNear2 = 2.0f*zNear;
- const float dx=right-left;
- const float dy=top-bottom;
- const float dz=zFar-zNear;
- const float A=(right+left)/dx;
- const float B=(top+bottom)/dy;
- const float C=-1.0f*(zFar+zNear)/dz;
- const float D=-2.0f*(zFar*zNear)/dz;
+ const value_type zNear2 = two*zNear;
+ const value_type dx=right-left;
+ const value_type dy=top-bottom;
+ const value_type dz=zFar-zNear;
+ const value_type A=(right+left)/dx;
+ const value_type B=(top+bottom)/dy;
+ const value_type C=-one*(zFar+zNear)/dz;
+ const value_type D=-two*(zFar*zNear)/dz;
m00 = zNear2/dx;
m11 = zNear2/dy;
@@ -1371,16 +1251,16 @@ class Mat4f {
m02 = A;
m12 = B;
m22 = C;
- m32 = -1.0f;
+ m32 = -one;
m23 = D;
- m33 = 0.0f;
+ m33 = zero;
return *this;
}
/**
- * Set this matrix to perspective {@link #setToFrustum(float, float, float, float, float, float) frustum} projection.
+ * Set this matrix to perspective {@link #setToFrustum(value_type, value_type, value_type, value_type, value_type, value_type) frustum} projection.
*
* @param fovy_rad angle in radians
* @param aspect aspect ratio width / height
@@ -1388,50 +1268,37 @@ class Mat4f {
* @param zFar
* @return this matrix for chaining
* @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear}
- * @see #setToFrustum(float, float, float, float, float, float)
+ * @see #setToFrustum(value_type, value_type, value_type, value_type, value_type, value_type)
*/
- Mat4f& setToPerspective(const float fovy_rad, const float aspect, const float zNear, const float zFar) {
- const float top = std::tan(fovy_rad/2.0f) * zNear; // use tangent of half-fov !
- const float bottom = -1.0f * top; // -1f * fovhvTan.top * zNear
- const float left = aspect * bottom; // aspect * -1f * fovhvTan.top * zNear
- const float right = aspect * top; // aspect * fovhvTan.top * zNear
+ Matrix4& setToPerspective(const value_type fovy_rad, const value_type aspect, const value_type zNear, const value_type zFar) {
+ const value_type top = std::tan(fovy_rad/two) * zNear; // use tangent of half-fov !
+ const value_type bottom = -one * top; // -1f * fovhvTan.top * zNear
+ const value_type left = aspect * bottom; // aspect * -1f * fovhvTan.top * zNear
+ const value_type right = aspect * top; // aspect * fovhvTan.top * zNear
return setToFrustum(left, right, bottom, top, zNear, zFar);
}
/**
- * Set this matrix to perspective {@link #setToFrustum(float, float, float, float, float, float) frustum} projection.
+ * Set this matrix to perspective {@link #setToFrustum(value_type, value_type, value_type, value_type, value_type, value_type) frustum} projection.
*
* @param fovhv {@link FovHVHalves} field of view in both directions, may not be centered, either in radians or tangent
* @param zNear
* @param zFar
* @return this matrix for chaining
* @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear}
- * @see #setToFrustum(float, float, float, float, float, float)
+ * @see #setToFrustum(value_type, value_type, value_type, value_type, value_type, value_type)
* @see Frustum#updateByFovDesc(mat4f, com.jogamp.math.geom.Frustum.FovDesc)
*/
- Mat4f& setToPerspective(const FovHVHalves& fovhv, const float zNear, const float zFar) {
+ Matrix4& setToPerspective(const FovHVHalves& fovhv, const value_type zNear, const value_type zFar) {
const FovHVHalves fovhvTan = fovhv.toTangents(); // use tangent of half-fov !
- const float top = fovhvTan.top * zNear;
- const float bottom = -1.0f * fovhvTan.bottom * zNear;
- const float left = -1.0f * fovhvTan.left * zNear;
- const float right = fovhvTan.right * zNear;
+ const value_type top = fovhvTan.top * zNear;
+ const value_type bottom = -one * fovhvTan.bottom * zNear;
+ const value_type left = -one * fovhvTan.left * zNear;
+ const value_type right = fovhvTan.right * zNear;
return setToFrustum(left, right, bottom, top, zNear, zFar);
}
/**
- * Calculate the frustum planes in world coordinates
- * using this column major order matrix, usually a projection (P) or premultiplied P*MV matrix.
- *
- * Frustum plane's normals will point to the inside of the viewing frustum,
- * as required by the {@link Frustum} class.
- *
- * May use geom::Frustum::setFromMat4() directly.
- *
- * @see geom::Frustum::setFromMat4()
- */
- geom::Frustum& getFrustum(geom::Frustum& frustum) noexcept;
-
- /**
* Set this matrix to the <i>look-at</i> matrix based on given parameters.
* <p>
* Consist out of two matrix multiplications:
@@ -1451,7 +1318,7 @@ class Mat4f {
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
- Mat4f& setToLookAt(const Vec3f& eye, const Vec3f& center, const Vec3f& up, Mat4f& tmp) noexcept {
+ Matrix4& setToLookAt(const Vec3f& eye, const Vec3f& center, const Vec3f& up, Matrix4& tmp) noexcept {
// normalized forward!
const Vec3f fwd = ( center - eye ).normalize();
@@ -1503,8 +1370,8 @@ class Mat4f {
* </p>
* <p>
* To effectively use the generated pick matrix for picking,
- * call {@link #setToPick(float, float, float, float, Recti, mat4f) setToPick(..)}
- * and multiply a {@link #setToPerspective(float, float, float, float) custom perspective matrix}
+ * call {@link #setToPick(value_type, value_type, value_type, value_type, Recti, mat4f) setToPick(..)}
+ * and multiply a {@link #setToPerspective(value_type, value_type, value_type, value_type) custom perspective matrix}
* by this pick matrix. Then you may load the result onto the perspective matrix stack.
* </p>
* @param x the center x-component of a picking region in window coordinates
@@ -1515,16 +1382,16 @@ class Mat4f {
* @param mat4Tmp temp storage
* @return true if successful or false if either delta value is <= zero.
*/
- bool setToPick(const float x, const float y, const float deltaX, const float deltaY,
- const Recti& viewport, Mat4f& mat4Tmp) noexcept {
+ bool setToPick(const value_type x, const value_type y, const value_type deltaX, const value_type deltaY,
+ const Recti& viewport, Matrix4& mat4Tmp) noexcept {
if (deltaX <= 0 || deltaY <= 0) {
return false;
}
/* Translate and scale the picked region to the entire window */
- setToTranslation( ( viewport.width() - 2.0f * ( x - viewport.x() ) ) / deltaX,
- ( viewport.height() - 2.0f * ( y - viewport.y() ) ) / deltaY,
+ setToTranslation( ( viewport.width() - two * ( x - viewport.x() ) ) / deltaX,
+ ( viewport.height() - two * ( y - viewport.y() ) ) / deltaY,
0);
- mat4Tmp.setToScale( viewport.width() / deltaX, viewport.height() / deltaY, 1.0f );
+ mat4Tmp.setToScale( viewport.width() / deltaX, viewport.height() / deltaY, one );
mul(mat4Tmp);
return true;
}
@@ -1534,7 +1401,7 @@ class Mat4f {
//
/**
- * Rotate this matrix about give axis and angle in radians, i.e. multiply by {@link #setToRotationAxis(float, float, float, float) axis-rotation matrix}.
+ * Rotate this matrix about give axis and angle in radians, i.e. multiply by {@link #setToRotationAxis(value_type, value_type, value_type, value_type) axis-rotation matrix}.
* @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q38">Matrix-FAQ Q38</a>
* @param angrad angle in radians
* @param x x of rotation axis
@@ -1543,40 +1410,31 @@ class Mat4f {
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
- Mat4f& rotate(const float ang_rad, const float x, const float y, const float z, Mat4f& tmp) noexcept {
+ Matrix4& rotate(const value_type ang_rad, const value_type x, const value_type y, const value_type z, Matrix4& tmp) noexcept {
return mul( tmp.setToRotationAxis(ang_rad, x, y, z) );
}
/**
- * Rotate this matrix about give axis and angle in radians, i.e. multiply by {@link #setToRotationAxis(float, vec3f) axis-rotation matrix}.
+ * Rotate this matrix about give axis and angle in radians, i.e. multiply by {@link #setToRotationAxis(value_type, vec3f) axis-rotation matrix}.
* @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q38">Matrix-FAQ Q38</a>
* @param angrad angle in radians
* @param axis rotation axis
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
- Mat4f& rotate(const float ang_rad, const Vec3f& axis, Mat4f& tmp) noexcept {
+ Matrix4& rotate(const value_type ang_rad, const Vec3f& axis, Matrix4& tmp) noexcept {
return mul( tmp.setToRotationAxis(ang_rad, axis) );
}
/**
- * Rotate this matrix with the given {@link Quaternion}, i.e. multiply by {@link #setToRotation(Quaternion) Quaternion's rotation matrix}.
- * @param tmp temporary mat4f used for multiplication
- * @return this matrix for chaining
- */
- Mat4f& rotate(const Quaternion& quat, Mat4f& tmp) noexcept {
- return mul( tmp.setToRotation(quat) );
- }
-
- /**
- * Translate this matrix, i.e. multiply by {@link #setToTranslation(float, float, float) translation matrix}.
+ * Translate this matrix, i.e. multiply by {@link #setToTranslation(value_type, value_type, value_type) translation matrix}.
* @param x x translation
* @param y y translation
* @param z z translation
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
- Mat4f& translate(const float x, const float y, const float z, Mat4f& tmp) noexcept {
+ Matrix4& translate(const value_type x, const value_type y, const value_type z, Matrix4& tmp) noexcept {
return mul( tmp.setToTranslation(x, y, z) );
}
@@ -1587,29 +1445,29 @@ class Mat4f {
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
- public final Mat4f translate(final Vec3f t, final Mat4f tmp) {
+ public final Matrix4 translate(final Vec3f t, final Matrix4 tmp) {
return mul( tmp.setToTranslation(t) );
}
/**
- * Scale this matrix, i.e. multiply by {@link #setToScale(float, float, float) scale matrix}.
+ * Scale this matrix, i.e. multiply by {@link #setToScale(value_type, value_type, value_type) scale matrix}.
* @param x x scale
* @param y y scale
* @param z z scale
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
- public final Mat4f scale(const float x, const float y, const float z, final Mat4f tmp) {
+ public final Matrix4 scale(const value_type x, const value_type y, const value_type z, final Matrix4 tmp) {
return mul( tmp.setToScale(x, y, z) );
}
/**
- * Scale this matrix, i.e. multiply by {@link #setToScale(float, float, float) scale matrix}.
+ * Scale this matrix, i.e. multiply by {@link #setToScale(value_type, value_type, value_type) scale matrix}.
* @param s scale for x-, y- and z-axis
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
- public final Mat4f scale(const float s, final Mat4f tmp) {
+ public final Matrix4 scale(const value_type s, final Matrix4 tmp) {
return mul( tmp.setToScale(s, s, s) );
}
@@ -1638,7 +1496,7 @@ class Mat4f {
//
/**
- * Equals check using a given {@link FloatUtil#EPSILON} value and {@link FloatUtil#isEqual(float, float, float)}.
+ * Equals check using a given {@link FloatUtil#EPSILON} value and {@link FloatUtil#isEqual(value_type, value_type, value_type)}.
* <p>
* Implementation considers following corner cases:
* <ul>
@@ -1650,7 +1508,7 @@ class Mat4f {
* @param epsilon consider using {@link FloatUtil#EPSILON}
* @return true if all components differ less than {@code epsilon}, otherwise false.
*/
- public boolean isEqual(final Mat4f o, const float epsilon) {
+ public boolean isEqual(final Matrix4 o, const value_type epsilon) {
if( this == o ) {
return true;
} else {
@@ -1674,7 +1532,7 @@ class Mat4f {
}
/**
- * Equals check using {@link FloatUtil#EPSILON} value and {@link FloatUtil#isEqual(float, float, float)}.
+ * Equals check using {@link FloatUtil#EPSILON} value and {@link FloatUtil#isEqual(value_type, value_type, value_type)}.
* <p>
* Implementation considers following corner cases:
* <ul>
@@ -1685,14 +1543,14 @@ class Mat4f {
* @param o comparison value
* @return true if all components differ less than {@link FloatUtil#EPSILON}, otherwise false.
*/
- public boolean isEqual(final Mat4f o) {
+ public boolean isEqual(final Matrix4 o) {
return isEqual(o, FloatUtil.EPSILON);
}
@Override
public boolean equals(final Object o) {
- if( o instanceof Mat4f ) {
- return isEqual((Mat4f)o, FloatUtil.EPSILON);
+ if( o instanceof Matrix4 ) {
+ return isEqual((Matrix4)o, FloatUtil.EPSILON);
} else {
return false;
}
@@ -1715,26 +1573,26 @@ class Mat4f {
* @param winPos 3 component window coordinate, the result
* @return true if successful, otherwise false (z is 1)
*/
- public static boolean mapObjToWin(final Vec3f obj, final Mat4f mMv, final Mat4f mP,
+ public static boolean mapObjToWin(final Vec3f obj, final Matrix4 mMv, final Matrix4 mP,
final Recti viewport, final Vec3f winPos)
{
- final Vec4f vec4Tmp1 = new Vec4f(obj, 1f);
+ final Vec4 vec4Tmp1 = new Vec4(obj, 1f);
// vec4Tmp2 = Mv * o
// rawWinPos = P * vec4Tmp2
// rawWinPos = P * ( Mv * o )
// rawWinPos = P * Mv * o
- final Vec4f vec4Tmp2 = mMv.mulVec4f(vec4Tmp1, new Vec4f());
- final Vec4f rawWinPos = mP.mulVec4f(vec4Tmp2, vec4Tmp1);
+ final Vec4 vec4Tmp2 = mMv.mulVec4(vec4Tmp1, new Vec4());
+ final Vec4 rawWinPos = mP.mulVec4(vec4Tmp2, vec4Tmp1);
- if (rawWinPos.w() == 0.0f) {
+ if (rawWinPos.w() == zero) {
return false;
}
- const float s = ( 1.0f / rawWinPos.w() ) * 0.5f;
+ const value_type s = ( one / rawWinPos.w() ) * half;
// Map x, y and z to range 0-1 (w is ignored)
- rawWinPos.scale(s).add(0.5f, 0.5f, 0.5f, 0f);
+ rawWinPos.scale(s).add(half, half, half, 0f);
// Map x,y to viewport
winPos.set( rawWinPos.x() * viewport.width() + viewport.x(),
@@ -1756,22 +1614,22 @@ class Mat4f {
* @param winPos 3 component window coordinate, the result
* @return true if successful, otherwise false (z is 1)
*/
- public static boolean mapObjToWin(final Vec3f obj, final Mat4f mPMv,
+ public static boolean mapObjToWin(final Vec3f obj, final Matrix4 mPMv,
final Recti viewport, final Vec3f winPos)
{
- final Vec4f vec4Tmp2 = new Vec4f(obj, 1f);
+ final Vec4 vec4Tmp2 = new Vec4(obj, 1f);
// rawWinPos = P * Mv * o
- final Vec4f rawWinPos = mPMv.mulVec4f(vec4Tmp2, new Vec4f());
+ final Vec4 rawWinPos = mPMv.mulVec4(vec4Tmp2, new Vec4());
- if (rawWinPos.w() == 0.0f) {
+ if (rawWinPos.w() == zero) {
return false;
}
- const float s = ( 1.0f / rawWinPos.w() ) * 0.5f;
+ const value_type s = ( one / rawWinPos.w() ) * half;
// Map x, y and z to range 0-1 (w is ignored)
- rawWinPos.scale(s).add(0.5f, 0.5f, 0.5f, 0f);
+ rawWinPos.scale(s).add(half, half, half, 0f);
// Map x,y to viewport
winPos.set( rawWinPos.x() * viewport.width() + viewport.x(),
@@ -1797,19 +1655,19 @@ class Mat4f {
* @param mat4Tmp 16 component matrix for temp storage
* @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z)
*/
- public static boolean mapWinToObj(const float winx, const float winy, const float winz,
- final Mat4f mMv, final Mat4f mP,
+ public static boolean mapWinToObj(const value_type winx, const value_type winy, const value_type winz,
+ final Matrix4 mMv, final Matrix4 mP,
final Recti viewport,
final Vec3f objPos,
- final Mat4f mat4Tmp)
+ final Matrix4 mat4Tmp)
{
// invPMv = Inv(P x Mv)
- final Mat4f invPMv = mat4Tmp.mul(mP, mMv);
+ final Matrix4 invPMv = mat4Tmp.mul(mP, mMv);
if( !invPMv.invert() ) {
return false;
}
- final Vec4f winPos = new Vec4f(winx, winy, winz, 1f);
+ final Vec4 winPos = new Vec4(winx, winy, winz, 1f);
// Map x and y from window coordinates
winPos.add(-viewport.x(), -viewport.y(), 0f, 0f).mul(1f/viewport.width(), 1f/viewport.height(), 1f, 1f);
@@ -1818,9 +1676,9 @@ class Mat4f {
winPos.mul(2f, 2f, 2f, 1f).add(-1f, -1f, -1f, 0f);
// rawObjPos = Inv(P x Mv) * winPos
- final Vec4f rawObjPos = invPMv.mulVec4f(winPos, new Vec4f());
+ final Vec4 rawObjPos = invPMv.mulVec4(winPos, new Vec4());
- if ( rawObjPos.w() == 0.0f ) {
+ if ( rawObjPos.w() == zero ) {
return false;
}
objPos.set( rawObjPos.scale( 1f / rawObjPos.w() ) );
@@ -1842,15 +1700,15 @@ class Mat4f {
* @param objPos 3 component object coordinate, the result
* @return true if successful, otherwise false (null invert matrix, or becomes infinity due to zero z)
*/
- public static boolean mapWinToObj(const float winx, const float winy, const float winz,
- final Mat4f invPMv,
+ public static boolean mapWinToObj(const value_type winx, const value_type winy, const value_type winz,
+ final Matrix4 invPMv,
final Recti viewport,
final Vec3f objPos)
{
if( null == invPMv ) {
return false;
}
- final Vec4f winPos = new Vec4f(winx, winy, winz, 1f);
+ final Vec4 winPos = new Vec4(winx, winy, winz, 1f);
// Map x and y from window coordinates
winPos.add(-viewport.x(), -viewport.y(), 0f, 0f).mul(1f/viewport.width(), 1f/viewport.height(), 1f, 1f);
@@ -1859,9 +1717,9 @@ class Mat4f {
winPos.mul(2f, 2f, 2f, 1f).add(-1f, -1f, -1f, 0f);
// rawObjPos = Inv(P x Mv) * winPos
- final Vec4f rawObjPos = invPMv.mulVec4f(winPos, new Vec4f());
+ final Vec4 rawObjPos = invPMv.mulVec4(winPos, new Vec4());
- if ( rawObjPos.w() == 0.0f ) {
+ if ( rawObjPos.w() == zero ) {
return false;
}
objPos.set( rawObjPos.scale( 1f / rawObjPos.w() ) );
@@ -1885,15 +1743,15 @@ class Mat4f {
* @param objPos1 3 component object coordinate, the result
* @return true if successful, otherwise false (null invert matrix, or becomes infinity due to zero z)
*/
- public static boolean mapWinToObj(const float winx, const float winy, const float winz1, const float winz2,
- final Mat4f invPMv,
+ public static boolean mapWinToObj(const value_type winx, const value_type winy, const value_type winz1, const value_type winz2,
+ final Matrix4 invPMv,
final Recti viewport,
final Vec3f objPos1, final Vec3f objPos2)
{
if( null == invPMv ) {
return false;
}
- final Vec4f winPos = new Vec4f(winx, winy, winz1, 1f);
+ final Vec4 winPos = new Vec4(winx, winy, winz1, 1f);
// Map x and y from window coordinates
winPos.add(-viewport.x(), -viewport.y(), 0f, 0f).mul(1f/viewport.width(), 1f/viewport.height(), 1f, 1f);
@@ -1902,9 +1760,9 @@ class Mat4f {
winPos.mul(2f, 2f, 2f, 1f).add(-1f, -1f, -1f, 0f);
// rawObjPos = Inv(P x Mv) * winPos1
- final Vec4f rawObjPos = invPMv.mulVec4f(winPos, new Vec4f());
+ final Vec4 rawObjPos = invPMv.mulVec4(winPos, new Vec4());
- if ( rawObjPos.w() == 0.0f ) {
+ if ( rawObjPos.w() == zero ) {
return false;
}
objPos1.set( rawObjPos.scale( 1f / rawObjPos.w() ) );
@@ -1916,9 +1774,9 @@ class Mat4f {
winPos.setZ( winz2 * 2f - 1f );
// rawObjPos = Inv(P x Mv) * winPos2
- invPMv.mulVec4f(winPos, rawObjPos);
+ invPMv.mulVec4(winPos, rawObjPos);
- if ( rawObjPos.w() == 0.0f ) {
+ if ( rawObjPos.w() == zero ) {
return false;
}
objPos2.set( rawObjPos.scale( 1f / rawObjPos.w() ) );
@@ -1945,20 +1803,20 @@ class Mat4f {
* @param mat4Tmp 16 component matrix for temp storage
* @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z)
*/
- public static boolean mapWinToObj4(const float winx, const float winy, const float winz, const float clipw,
- final Mat4f mMv, final Mat4f mP,
+ public static boolean mapWinToObj4(const value_type winx, const value_type winy, const value_type winz, const value_type clipw,
+ final Matrix4 mMv, final Matrix4 mP,
final Recti viewport,
- const float near, const float far,
- final Vec4f objPos,
- final Mat4f mat4Tmp)
+ const value_type near, const value_type far,
+ final Vec4 objPos,
+ final Matrix4 mat4Tmp)
{
// invPMv = Inv(P x Mv)
- final Mat4f invPMv = mat4Tmp.mul(mP, mMv);
+ final Matrix4 invPMv = mat4Tmp.mul(mP, mMv);
if( !invPMv.invert() ) {
return false;
}
- final Vec4f winPos = new Vec4f(winx, winy, winz, clipw);
+ final Vec4 winPos = new Vec4(winx, winy, winz, clipw);
// Map x and y from window coordinates
winPos.add(-viewport.x(), -viewport.y(), -near, 0f).mul(1f/viewport.width(), 1f/viewport.height(), 1f/(far-near), 1f);
@@ -1967,9 +1825,9 @@ class Mat4f {
winPos.mul(2f, 2f, 2f, 1f).add(-1f, -1f, -1f, 0f);
// objPos = Inv(P x Mv) * winPos
- invPMv.mulVec4f(winPos, objPos);
+ invPMv.mulVec4(winPos, objPos);
- if ( objPos.w() == 0.0f ) {
+ if ( objPos.w() == zero ) {
return false;
}
return true;
@@ -1992,16 +1850,16 @@ class Mat4f {
* @param obj_pos 4 component object coordinate, the result
* @return true if successful, otherwise false (null invert matrix, or becomes infinity due to zero z)
*/
- public static boolean mapWinToObj4(const float winx, const float winy, const float winz, const float clipw,
- final Mat4f invPMv,
+ public static boolean mapWinToObj4(const value_type winx, const value_type winy, const value_type winz, const value_type clipw,
+ final Matrix4 invPMv,
final Recti viewport,
- const float near, const float far,
- final Vec4f objPos)
+ const value_type near, const value_type far,
+ final Vec4 objPos)
{
if( null == invPMv ) {
return false;
}
- final Vec4f winPos = new Vec4f(winx, winy, winz, clipw);
+ final Vec4 winPos = new Vec4(winx, winy, winz, clipw);
// Map x and y from window coordinates
winPos.add(-viewport.x(), -viewport.y(), -near, 0f).mul(1f/viewport.width(), 1f/viewport.height(), 1f/(far-near), 1f);
@@ -2010,9 +1868,9 @@ class Mat4f {
winPos.mul(2f, 2f, 2f, 1f).add(-1f, -1f, -1f, 0f);
// objPos = Inv(P x Mv) * winPos
- invPMv.mulVec4f(winPos, objPos);
+ invPMv.mulVec4(winPos, objPos);
- if ( objPos.w() == 0.0f ) {
+ if ( objPos.w() == zero ) {
return false;
}
return true;
@@ -2021,13 +1879,13 @@ class Mat4f {
/**
* Map two window coordinates w/ shared X/Y and distinctive Z
* to a {@link Ray}. The resulting {@link Ray} maybe used for <i>picking</i>
- * using a {@link AABBox#getRayIntersection(vec3f, Ray, float, boolean)}.
+ * using a {@link AABBox#getRayIntersection(vec3f, Ray, value_type, boolean)}.
* <p>
* Notes for picking <i>winz0</i> and <i>winz1</i>:
* <ul>
- * <li>see {@link FloatUtil#getZBufferEpsilon(int, float, float)}</li>
- * <li>see {@link FloatUtil#getZBufferValue(int, float, float, float)}</li>
- * <li>see {@link FloatUtil#getOrthoWinZ(float, float, float)}</li>
+ * <li>see {@link FloatUtil#getZBufferEpsilon(int, value_type, value_type)}</li>
+ * <li>see {@link FloatUtil#getZBufferValue(int, value_type, value_type, value_type)}</li>
+ * <li>see {@link FloatUtil#getOrthoWinZ(value_type, value_type, value_type)}</li>
* </ul>
* </p>
* @param winx
@@ -2042,13 +1900,13 @@ class Mat4f {
* @param mat4Tmp2 16 component matrix for temp storage
* @return true if successful, otherwise false (failed to invert matrix, or becomes z is infinity)
*/
- public static boolean mapWinToRay(const float winx, const float winy, const float winz0, const float winz1,
- final Mat4f mMv, final Mat4f mP,
+ public static boolean mapWinToRay(const value_type winx, const value_type winy, const value_type winz0, const value_type winz1,
+ final Matrix4 mMv, final Matrix4 mP,
final Recti viewport,
final Ray ray,
- final Mat4f mat4Tmp1, final Mat4f mat4Tmp2) {
+ final Matrix4 mat4Tmp1, final Matrix4 mat4Tmp2) {
// invPMv = Inv(P x Mv)
- final Mat4f invPMv = mat4Tmp1.mul(mP, mMv);
+ final Matrix4 invPMv = mat4Tmp1.mul(mP, mMv);
if( !invPMv.invert() ) {
return false;
}
@@ -2064,13 +1922,13 @@ class Mat4f {
/**
* Map two window coordinates w/ shared X/Y and distinctive Z
* to a {@link Ray}. The resulting {@link Ray} maybe used for <i>picking</i>
- * using a {@link AABBox#getRayIntersection(vec3f, Ray, float, boolean)}.
+ * using a {@link AABBox#getRayIntersection(vec3f, Ray, value_type, boolean)}.
* <p>
* Notes for picking <i>winz0</i> and <i>winz1</i>:
* <ul>
- * <li>see {@link FloatUtil#getZBufferEpsilon(int, float, float)}</li>
- * <li>see {@link FloatUtil#getZBufferValue(int, float, float, float)}</li>
- * <li>see {@link FloatUtil#getOrthoWinZ(float, float, float)}</li>
+ * <li>see {@link FloatUtil#getZBufferEpsilon(int, value_type, value_type)}</li>
+ * <li>see {@link FloatUtil#getZBufferValue(int, value_type, value_type, value_type)}</li>
+ * <li>see {@link FloatUtil#getOrthoWinZ(value_type, value_type, value_type)}</li>
* </ul>
* </p>
* @param winx
@@ -2082,8 +1940,8 @@ class Mat4f {
* @param ray storage for the resulting {@link Ray}
* @return true if successful, otherwise false (null invert matrix, or becomes z is infinity)
*/
- public static boolean mapWinToRay(const float winx, const float winy, const float winz0, const float winz1,
- final Mat4f invPMv,
+ public static boolean mapWinToRay(const value_type winx, const value_type winy, const value_type winz0, const value_type winz1,
+ final Matrix4 invPMv,
final Recti viewport,
final Ray ray) {
if( mapWinToObj(winx, winy, winz0, winz1, invPMv, viewport, ray.orig, ray.dir) ) {
@@ -2101,11 +1959,11 @@ class Mat4f {
/**
* @param sb optional passed StringBuilder instance to be used
* @param rowPrefix optional prefix for each row
- * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter}
+ * @param f the format string of one value_typeing point, i.e. "%10.5f", see {@link java.util.Formatter}
* @return matrix string representation
*/
public StringBuilder toString(final StringBuilder sb, final String rowPrefix, final String f) {
- const float[] tmp = new float[16];
+ const value_type[] tmp = new value_type[16];
this.get(tmp);
return FloatUtil.matrixToString(sb, rowPrefix, f,tmp, 0, 4, 4, false /* rowMajorOrder */);
}
@@ -2119,18 +1977,47 @@ class Mat4f {
/**
* Returns a formatted string representation of this matrix
* @param rowPrefix prefix for each row
- * @param f format string for each float element, e.g. "%10.5f"
+ * @param f format string for each value_type element, e.g. "%10.5f"
* @return matrix
*/
- std::string toString(const std::string& rowPrefix, const std::string& f) const noexcept;
+ std::string toString(const std::string& rowPrefix, const std::string& f) const noexcept {
+ std::string sb;
+ value_type tmp[16];
+ get(tmp);
+ return jau::mat_to_string(sb, rowPrefix, f, tmp, 4, 4, false /* rowMajorOrder */); // creates a copy-out!
+ }
std::string toString() const noexcept { return toString("", "%10.5f"); }
};
-std::ostream& operator<<(std::ostream& out, const Mat4f& v) noexcept {
+template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+constexpr Matrix4<T> operator*(const Matrix4<T>& lhs, const Matrix4<T>& rhs ) noexcept {
+ Matrix4<T> r(lhs); r *= rhs; return r;
+}
+
+template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+constexpr Matrix4<T> operator*(const Matrix4<T>& lhs, const T s ) noexcept {
+ Matrix4<T> r(lhs); r *= s; return r;
+}
+
+template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+constexpr Matrix4<T> operator*(const T s, const Matrix4<T>& rhs) noexcept {
+ Matrix4<T> r(rhs); r *= s; return r;
+}
+
+template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+std::ostream& operator<<(std::ostream& out, const Matrix4<T>& v) noexcept {
return out << v.toString();
}
+typedef Matrix4<float> Mat4f;
+
+static_assert(alignof(float) == alignof(Mat4f));
+
/**@}*/
} // namespace jau::math
diff --git a/include/jau/math/quaternion.hpp b/include/jau/math/quaternion.hpp
index 6429ac5..a423c88 100644
--- a/include/jau/math/quaternion.hpp
+++ b/include/jau/math/quaternion.hpp
@@ -57,11 +57,24 @@ namespace jau::math {
* See <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/index.htm">euclideanspace.com-Quaternion</a>
* </p>
*/
-class Quaternion {
- private:
- float m_x, m_y, m_z, m_w;
-
+template<typename Value_type,
+ std::enable_if_t<std::is_floating_point_v<Value_type>, bool> = true>
+class alignas(Value_type) Quaternion {
public:
+ typedef Value_type value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef value_type* iterator;
+ typedef const value_type* const_iterator;
+
+ typedef Vector3F<value_type, std::is_floating_point_v<Value_type>> Vec3;
+
+ constexpr static const value_type zero = value_type(0);
+ constexpr static const value_type one = value_type(1);
+ constexpr static const value_type two = value_type(2);
+ constexpr static const value_type half = one/two;
/**
* Quaternion Epsilon, used with equals method to determine if two Quaternions are close enough to be considered equal.
@@ -69,12 +82,17 @@ class Quaternion {
* Using {@value}, which is ~10 times {@link FloatUtil#EPSILON}.
* </p>
*/
- constexpr static const float ALLOWED_DEVIANCE = 1.0E-6f; // FloatUtil.EPSILON == 1.1920929E-7f; double ALLOWED_DEVIANCE: 1.0E-8f
+ constexpr static const value_type allowed_deviation = 1.0E-6f; // FIXME: float EPSILON == 1.1920929E-7f; double ALLOWED_DEVIANCE: 1.0E-8f
+
+ private:
+ value_type m_x, m_y, m_z, m_w;
+
+ public:
constexpr Quaternion() noexcept
: m_x(0), m_y(0), m_z(0), m_w(1) {}
- constexpr Quaternion(const float x, const float y, const float z, const float w) noexcept
+ constexpr Quaternion(const value_type x, const value_type y, const value_type z, const value_type w) noexcept
: m_x(x), m_y(y), m_z(z), m_w(w) {}
constexpr Quaternion(const Quaternion& o) noexcept = default;
@@ -87,7 +105,7 @@ class Quaternion {
* which is not applied here.
* @return the squared magnitude of this quaternion.
*/
- float magnitudeSquared() const noexcept {
+ constexpr value_type magnitudeSquared() const noexcept {
return m_w*m_w + m_x*m_x + m_y*m_y + m_z*m_z;
}
@@ -100,13 +118,13 @@ class Quaternion {
* <p>
* Implementation Details:
* <ul>
- * <li> returns 0f if {@link #magnitudeSquared()} is {@link jau::is_zero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
- * <li> returns 1f if {@link #magnitudeSquared()} is {@link FloatUtil#isEqual(float, float, float) equals 1f} using {@link FloatUtil#EPSILON epsilon}</li>
+ * <li> returns 0f if {@link #magnitudeSquared()} is {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
+ * <li> returns 1f if {@link #magnitudeSquared()} is {@link FloatUtil#isEqual(value_type, value_type, value_type) equals 1f} using {@link FloatUtil#EPSILON epsilon}</li>
* </ul>
* </p>
*/
- float magnitude() const noexcept {
- const float magnitudeSQ = magnitudeSquared();
+ constexpr_cxx26 value_type magnitude() const noexcept {
+ const value_type magnitudeSQ = magnitudeSquared();
if ( jau::is_zero(magnitudeSQ) ) {
return 0.0f;
}
@@ -116,33 +134,33 @@ class Quaternion {
return std::sqrt(magnitudeSQ);
}
- float w() const noexcept { return m_w; }
+ constexpr value_type w() const noexcept { return m_w; }
- void set_w(float w) noexcept { m_w = w; }
+ constexpr void set_w(value_type w) noexcept { m_w = w; }
- float x() const noexcept { return m_x; }
+ constexpr value_type x() const noexcept { return m_x; }
- void set_x(float x) noexcept { m_x = x; }
+ constexpr void set_x(value_type x) noexcept { m_x = x; }
- float y() const noexcept { return m_y; }
+ constexpr value_type y() const noexcept { return m_y; }
- void set_y(float y) noexcept { m_y = y; }
+ constexpr void set_y(value_type y) noexcept { m_y = y; }
- float z() const noexcept { return m_z; }
+ constexpr value_type z() const noexcept { return m_z; }
- void set_z(float z) noexcept { m_z = z; }
+ constexpr void set_z(value_type z) noexcept { m_z = z; }
/**
* Returns the dot product of this quaternion with the given x,y,z and m_w components.
*/
- float dot(float x, float y, float z, float w) const noexcept {
+ constexpr value_type dot(value_type x, value_type y, value_type z, value_type w) const noexcept {
return m_x * x + m_y * y + m_z * z + m_w * w;
}
/**
* Returns the dot product of this quaternion with the given quaternion
*/
- float dot(const Quaternion& quat) const noexcept {
+ constexpr value_type dot(const Quaternion& quat) const noexcept {
return dot(quat.x(), quat.y(), quat.z(), quat.w());
}
@@ -155,7 +173,7 @@ class Quaternion {
* {@link jau::is_zero3f() against zero}.
* </p>
*/
- bool is_identity() const noexcept {
+ constexpr bool is_identity() const noexcept {
return jau::equals(1.0f, m_w) && jau::is_zero3f(m_x, m_y, m_z);
// return m_w == 1f && m_x == 0f && m_y == 0f && m_z == 0f;
}
@@ -164,7 +182,7 @@ class Quaternion {
* Set this quaternion to identity (x=0,y=0,z=0,w=1)
* @return this quaternion for chaining.
*/
- Quaternion& set_identity() noexcept {
+ constexpr Quaternion& set_identity() noexcept {
m_x = m_y = m_z = 0.0f; m_w = 1.0f;
return *this;
}
@@ -179,12 +197,12 @@ class Quaternion {
* </p>
* @return this quaternion for chaining.
*/
- Quaternion& normalize() noexcept {
- const float norm = magnitude();
+ constexpr Quaternion& normalize() noexcept {
+ const value_type norm = magnitude();
if ( jau::is_zero(norm) ) {
set_identity();
} else {
- const float invNorm = 1.0f/norm;
+ const value_type invNorm = 1.0f/norm;
m_w *= invNorm;
m_x *= invNorm;
m_y *= invNorm;
@@ -198,7 +216,7 @@ class Quaternion {
* @return this quaternion for chaining.
* @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q49">Matrix-FAQ Q49</a>
*/
- Quaternion& conjugate() noexcept {
+ constexpr Quaternion& conjugate() noexcept {
m_x = -m_x;
m_y = -m_y;
m_z = -m_z;
@@ -210,18 +228,18 @@ class Quaternion {
* <p>
* Implementation Details:
* <ul>
- * <li> {@link #conjugate() conjugates} if {@link #magnitudeSquared()} is is {@link FloatUtil#isEqual(float, float, float) equals 1f} using {@link FloatUtil#EPSILON epsilon}</li>
+ * <li> {@link #conjugate() conjugates} if {@link #magnitudeSquared()} is is {@link FloatUtil#isEqual(value_type, value_type, value_type) equals 1f} using {@link FloatUtil#EPSILON epsilon}</li>
* </ul>
* </p>
* @return this quaternion for chaining.
* @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q50">Matrix-FAQ Q50</a>
*/
- Quaternion& invert() noexcept {
- const float magnitudeSQ = magnitudeSquared();
+ constexpr Quaternion& invert() noexcept {
+ const value_type magnitudeSQ = magnitudeSquared();
if ( jau::equals(1.0f, magnitudeSQ) ) {
conjugate();
} else {
- const float invmsq = 1.0f/magnitudeSQ;
+ const value_type invmsq = 1.0f/magnitudeSQ;
m_w *= invmsq;
m_x = -m_x * invmsq;
m_y = -m_y * invmsq;
@@ -234,7 +252,7 @@ class Quaternion {
* Set all values of this quaternion using the given components.
* @return this quaternion for chaining.
*/
- constexpr Quaternion& set(const float x, const float y, const float z, const float w) noexcept {
+ constexpr Quaternion& set(const value_type x, const value_type y, const value_type z, const value_type w) noexcept {
m_x = x;
m_y = y;
m_z = z;
@@ -290,11 +308,11 @@ class Quaternion {
/**
* Scale this quaternion by a scalar: this = this * rhs, returns this
*
- * @param n a float constant
+ * @param n a value_type constant
* @return this quaternion for chaining.
* @see <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm#scale">euclideanspace.com-QuaternionScale</a>
*/
- constexpr Quaternion& operator*=(const float rhs) noexcept {
+ constexpr Quaternion& operator*=(const value_type rhs) noexcept {
m_x *= rhs;
m_y *= rhs;
m_z *= rhs;
@@ -317,17 +335,17 @@ class Quaternion {
* @param axisZ m_z-coord of rotation axis
* @return this quaternion for chaining.
*/
- Quaternion& rotateByAngleNormalAxis(float angle, float axisX, float axisY, float axisZ) noexcept {
+ constexpr_cxx26 Quaternion& rotateByAngleNormalAxis(const value_type angle, const value_type axisX, const value_type axisY, const value_type axisZ) noexcept {
if( jau::is_zero3f(axisX, axisY, axisZ) ) {
// no change
return *this;
}
- const float halfAngle = 0.5f * angle;
- const float sin = std::sin(halfAngle);
- const float qw = std::cos(halfAngle);
- const float qx = sin * axisX;
- const float qy = sin * axisY;
- const float qz = sin * axisZ;
+ const value_type halfAngle = 0.5f * angle;
+ const value_type sin = std::sin(halfAngle);
+ const value_type qw = std::cos(halfAngle);
+ const value_type qx = sin * axisX;
+ const value_type qy = sin * axisY;
+ const value_type qz = sin * axisZ;
return set( m_x * qw + m_y * qz - m_z * qy + m_w * qx,
-m_x * qz + m_y * qw + m_z * qx + m_w * qy,
m_x * qy - m_y * qx + m_z * qw + m_w * qz,
@@ -344,10 +362,10 @@ class Quaternion {
* </p>
*
* @param angle in radians
- * @param axis Vec3f coord of rotation axis
+ * @param axis Vec3 coord of rotation axis
* @return this quaternion for chaining.
*/
- Quaternion& rotateByAngleNormalAxis(float angle, const Vec3f& axis) noexcept {
+ constexpr_cxx26 Quaternion& rotateByAngleNormalAxis(const value_type angle, const Vec3& axis) noexcept {
return rotateByAngleNormalAxis(angle, axis.x, axis.y, axis.z);
}
@@ -357,10 +375,12 @@ class Quaternion {
* @param angle in radians
* @return this quaternion for chaining.
*/
- Quaternion& rotateByAngleX(float angle) noexcept {
- const float halfAngle = 0.5f * angle;
- const float sin = std::sin(halfAngle);
- const float cos = std::cos(halfAngle);
+ constexpr_cxx26 Quaternion& rotateByAngleX(const value_type angle) noexcept {
+ const value_type halfAngle = 0.5f * angle;
+ return rotateByAngleX(std::sin(halfAngle), std::cos(halfAngle));
+ }
+ /** Rotate this quaternion around X axis with the given angle's sin + cos values */
+ constexpr Quaternion& rotateByAngleX(const value_type sin, const value_type cos) noexcept {
return set( m_x * cos + m_w * sin,
m_y * cos + m_z * sin,
-m_y * sin + m_z * cos,
@@ -373,10 +393,12 @@ class Quaternion {
* @param angle in radians
* @return this quaternion for chaining.
*/
- Quaternion& rotateByAngleY(float angle) noexcept {
- const float halfAngle = 0.5f * angle;
- const float sin = std::sin(halfAngle);
- const float cos = std::cos(halfAngle);
+ constexpr_cxx26 Quaternion& rotateByAngleY(value_type angle) noexcept {
+ const value_type halfAngle = 0.5f * angle;
+ return rotateByAngleY(std::sin(halfAngle), std::cos(halfAngle));
+ }
+ /** Rotate this quaternion around Y axis with the given angle's sin + cos values */
+ constexpr Quaternion& rotateByAngleY(const value_type sin, const value_type cos) noexcept {
return set( m_x * cos - m_z * sin,
m_y * cos + m_w * sin,
m_x * sin + m_z * cos,
@@ -389,10 +411,12 @@ class Quaternion {
* @param angle in radians
* @return this quaternion for chaining.
*/
- Quaternion& rotateByAngleZ(float angle) noexcept {
- const float halfAngle = 0.5f * angle;
- const float sin = std::sin(halfAngle);
- const float cos = std::cos(halfAngle);
+ constexpr_cxx26 Quaternion& rotateByAngleZ(value_type angle) noexcept {
+ const value_type halfAngle = 0.5f * angle;
+ return rotateByAngleZ(std::sin(halfAngle), std::cos(halfAngle));
+ }
+ /** Rotate this quaternion around Y axis with the given angle's sin + cos values */
+ constexpr Quaternion& rotateByAngleZ(const value_type sin, const value_type cos) noexcept {
return set( m_x * cos + m_y * sin,
-m_x * sin + m_y * cos,
m_z * cos + m_w * sin,
@@ -409,12 +433,12 @@ class Quaternion {
* <li>z - attitude</li>
* </ul>
* </p>
- * For details see {@link #rotateByEuler(float, float, float)}.
+ * For details see {@link #rotateByEuler(value_type, value_type, value_type)}.
* @param angradXYZ euler angle array in radians
* @return this quaternion for chaining.
- * @see #rotateByEuler(float, float, float)
+ * @see #rotateByEuler(value_type, value_type, value_type)
*/
- Quaternion& rotateByEuler(const Vec3f& angradXYZ) noexcept {
+ Quaternion& rotateByEuler(const Vec3& angradXYZ) noexcept {
return rotateByEuler(angradXYZ.x, angradXYZ.y, angradXYZ.z);
}
@@ -423,15 +447,15 @@ class Quaternion {
* <p>
* The rotations are applied in the given order and using chained rotation per axis:
* <ul>
- * <li>y - heading - {@link #rotateByAngleY(float)}</li>
- * <li>z - attitude - {@link #rotateByAngleZ(float)}</li>
- * <li>x - bank - {@link #rotateByAngleX(float)}</li>
+ * <li>y - heading - {@link #rotateByAngleY(value_type)}</li>
+ * <li>z - attitude - {@link #rotateByAngleZ(value_type)}</li>
+ * <li>x - bank - {@link #rotateByAngleX(value_type)}</li>
* </ul>
* </p>
* <p>
* Implementation Details:
* <ul>
- * <li> NOP if all angles are {@link jau::is_zero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
+ * <li> NOP if all angles are {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
* <li> result is {@link #normalize()}ed</li>
* </ul>
* </p>
@@ -439,12 +463,12 @@ class Quaternion {
* @param headingY the Euler yaw angle in radians. (rotation about the Y axis)
* @param attitudeZ the Euler roll angle in radians. (rotation about the Z axis)
* @return this quaternion for chaining.
- * @see #rotateByAngleY(float)
- * @see #rotateByAngleZ(float)
- * @see #rotateByAngleX(float)
- * @see #setFromEuler(float, float, float)
+ * @see #rotateByAngleY(value_type)
+ * @see #rotateByAngleZ(value_type)
+ * @see #rotateByAngleX(value_type)
+ * @see #setFromEuler(value_type, value_type, value_type)
*/
- Quaternion& rotateByEuler(float bankX, float headingY, float attitudeZ) noexcept {
+ constexpr_cxx26 Quaternion& rotateByEuler(const value_type bankX, const value_type headingY, const value_type attitudeZ) noexcept {
if ( jau::is_zero3f(bankX, headingY, attitudeZ) ) {
return *this;
} else {
@@ -461,18 +485,18 @@ class Quaternion {
* @return the given out store for chaining
* @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q63">Matrix-FAQ Q63</a>
*/
- Vec3f rotateVector(const Vec3f& in) noexcept {
- Vec3f out;
+ Vec3 rotateVector(const Vec3& in) noexcept {
+ Vec3 out;
if( in.is_zero() ) {
out.set(0, 0, 0);
} else {
- const float vecX = in.x;
- const float vecY = in.y;
- const float vecZ = in.z;
- const float x_x = m_x*m_x;
- const float y_y = m_y*m_y;
- const float z_z = m_z*m_z;
- const float w_w = m_w*m_w;
+ const value_type vecX = in.x;
+ const value_type vecY = in.y;
+ const value_type vecZ = in.z;
+ const value_type x_x = m_x*m_x;
+ const value_type y_y = m_y*m_y;
+ const value_type z_z = m_z*m_z;
+ const value_type w_w = m_w*m_w;
out.x = w_w * vecX
+ x_x * vecX
@@ -505,26 +529,26 @@ class Quaternion {
*
* @param a start quaternion
* @param b end quaternion
- * @param changeAmnt float between 0 and 1 representing interpolation.
+ * @param changeAmnt value_type between 0 and 1 representing interpolation.
* @return this quaternion for chaining.
* @see <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/">euclideanspace.com-QuaternionSlerp</a>
*/
- Quaternion& set_slerp(const Quaternion& a, const Quaternion& b, const float changeAmnt) noexcept {
+ constexpr_cxx26 Quaternion& set_slerp(const Quaternion& a, const Quaternion& b, const value_type changeAmnt) noexcept {
// std::cerr << "Slerp.0: A " << a << ", B " << b << ", t " << changeAmnt << std::endl;
if (changeAmnt == 0.0f) {
*this = a;
} else if (changeAmnt == 1.0f) {
*this = b;
} else {
- float bx = b.m_x;
- float by = b.m_y;
- float bz = b.m_z;
- float bw = b.m_w;
+ value_type bx = b.m_x;
+ value_type by = b.m_y;
+ value_type bz = b.m_z;
+ value_type bw = b.m_w;
// Calculate angle between them (quat dot product)
- float cosHalfTheta = a.m_x * bx + a.m_y * by + a.m_z * bz + a.m_w * bw;
+ value_type cosHalfTheta = a.m_x * bx + a.m_y * by + a.m_z * bz + a.m_w * bw;
- float scale0, scale1;
+ value_type scale0, scale1;
if( cosHalfTheta >= 0.95f ) {
// quaternions are close, just use linear interpolation
@@ -536,7 +560,7 @@ class Quaternion {
scale0 = 0.5f;
scale1 = 0.5f;
} else {
- if( cosHalfTheta <= -std::numeric_limits<float>::epsilon() ) { // FIXME: .. or shall we use the upper bound 'cosHalfTheta < EPSILON' ?
+ if( cosHalfTheta <= -std::numeric_limits<value_type>::epsilon() ) { // FIXME: .. or shall we use the upper bound 'cosHalfTheta < EPSILON' ?
// Negate the second quaternion and the result of the dot product (Inversion)
bx *= -1.0f;
by *= -1.0f;
@@ -544,8 +568,8 @@ class Quaternion {
bw *= -1.0f;
cosHalfTheta *= -1.0f;
}
- const float halfTheta = std::acos(cosHalfTheta);
- const float sinHalfTheta = std::sqrt(1.0f - cosHalfTheta*cosHalfTheta);
+ const value_type halfTheta = std::acos(cosHalfTheta);
+ const value_type sinHalfTheta = std::sqrt(1.0f - cosHalfTheta*cosHalfTheta);
// if theta = 180 degrees then result is not fully defined
// we could rotate around any axis normal to qa or qb
if ( std::abs(sinHalfTheta) < 0.001f ){ // fabs is floating point absolute
@@ -588,8 +612,8 @@ class Quaternion {
* @return this quaternion for chaining.
* @see <a href="http://www.euclideanspace.com/maths/algebra/vectors/lookat/index.htm">euclideanspace.com-LookUp</a>
*/
- Quaternion& setLookAt(const Vec3f& directionIn, const Vec3f& upIn,
- Vec3f& xAxisOut, Vec3f& yAxisOut, Vec3f& zAxisOut) noexcept {
+ Quaternion& setLookAt(const Vec3& directionIn, const Vec3& upIn,
+ Vec3& xAxisOut, Vec3& yAxisOut, Vec3& zAxisOut) noexcept {
// Z = norm(dir)
(zAxisOut = directionIn).normalize();
@@ -603,15 +627,15 @@ class Quaternion {
yAxisOut.cross(zAxisOut, xAxisOut).normalize();
/**
- const float m00 = xAxisOut[0];
- const float m01 = yAxisOut[0];
- const float m02 = zAxisOut[0];
- const float m10 = xAxisOut[1];
- const float m11 = yAxisOut[1];
- const float m12 = zAxisOut[1];
- const float m20 = xAxisOut[2];
- const float m21 = yAxisOut[2];
- const float m22 = zAxisOut[2];
+ const value_type m00 = xAxisOut[0];
+ const value_type m01 = yAxisOut[0];
+ const value_type m02 = zAxisOut[0];
+ const value_type m10 = xAxisOut[1];
+ const value_type m11 = yAxisOut[1];
+ const value_type m12 = zAxisOut[1];
+ const value_type m20 = xAxisOut[2];
+ const value_type m21 = yAxisOut[2];
+ const value_type m22 = zAxisOut[2];
*/
return setFromAxes(xAxisOut, yAxisOut, zAxisOut).normalize();
}
@@ -630,22 +654,22 @@ class Quaternion {
* <p>
* Implementation Details:
* <ul>
- * <li> set_identity() if square vector-length is jau::is_zero2f(float, float) using epsilon</li>
+ * <li> set_identity() if square vector-length is jau::is_zero2f(value_type, value_type) using epsilon</li>
* </ul>
* </p>
* @param v1 not normalized
* @param v2 not normalized
* @return this quaternion for chaining.
*/
- Quaternion& setFromVectors(const Vec3f& v1, const Vec3f& v2) noexcept {
- const float factor = v1.length() * v2.length();
+ constexpr_cxx26 Quaternion& setFromVectors(const Vec3& v1, const Vec3& v2) noexcept {
+ const value_type factor = v1.length() * v2.length();
if ( jau::is_zero(factor) ) {
return set_identity();
} else {
- const float dot = v1.dot(v2) / factor; // normalize
- const float theta = std::acos(std::max(-1.0f, std::min(dot, 1.0f))); // clipping [-1..1]
+ const value_type dot = v1.dot(v2) / factor; // normalize
+ const value_type theta = std::acos(std::max(-1.0f, std::min(dot, 1.0f))); // clipping [-1..1]
- Vec3f tmpPivotVec = v1.cross(v2);
+ Vec3 tmpPivotVec = v1.cross(v2);
if ( dot < 0.0f && jau::is_zero( tmpPivotVec.length() ) ) {
// Vectors parallel and opposite direction, therefore a rotation of 180 degrees about any vector
@@ -684,22 +708,22 @@ class Quaternion {
* <p>
* Implementation Details:
* <ul>
- * <li> {@link #setIdentity()} if square vector-length is {@link jau::is_zero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
+ * <li> {@link #setIdentity()} if square vector-length is {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
* </ul>
* </p>
* @param v1 normalized
* @param v2 normalized
* @return this quaternion for chaining.
*/
- Quaternion& setFromNormalVectors(const Vec3f& v1, const Vec3f& v2) noexcept {
- const float factor = v1.length() * v2.length();
+ constexpr_cxx26 Quaternion& setFromNormalVectors(const Vec3& v1, const Vec3& v2) noexcept {
+ const value_type factor = v1.length() * v2.length();
if ( jau::is_zero(factor) ) {
return set_identity();
} else {
- const float dot = v1.dot(v2) / factor; // normalize
- const float theta = std::acos(std::max(-1.0f, std::min(dot, 1.0f))); // clipping [-1..1]
+ const value_type dot = v1.dot(v2) / factor; // normalize
+ const value_type theta = std::acos(std::max(-1.0f, std::min(dot, 1.0f))); // clipping [-1..1]
- Vec3f tmpPivotVec = v1.cross(v2);
+ Vec3 tmpPivotVec = v1.cross(v2);
if ( dot < 0.0f && jau::is_zero( tmpPivotVec.length() ) ) {
// Vectors parallel and opposite direction, therefore a rotation of 180 degrees about any vector
@@ -733,7 +757,7 @@ class Quaternion {
* <p>
* Implementation Details:
* <ul>
- * <li> {@link #setIdentity()} if axis is {@link jau::is_zero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
+ * <li> {@link #setIdentity()} if axis is {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
* </ul>
* </p>
* @param angle rotation angle (rads)
@@ -741,10 +765,10 @@ class Quaternion {
* @return this quaternion for chaining.
*
* @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56">Matrix-FAQ Q56</a>
- * @see #toAngleAxis(Vec3f)
+ * @see #toAngleAxis(Vec3)
*/
- Quaternion& setFromAngleAxis(const float angle, const Vec3f& vector) noexcept {
- return setFromAngleNormalAxis(angle, Vec3f(vector).normalize());
+ Quaternion& setFromAngleAxis(const value_type angle, const Vec3& vector) noexcept {
+ return setFromAngleNormalAxis(angle, Vec3(vector).normalize());
}
/***
@@ -752,7 +776,7 @@ class Quaternion {
* <p>
* Implementation Details:
* <ul>
- * <li> {@link #setIdentity()} if axis is {@link jau::is_zero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
+ * <li> {@link #setIdentity()} if axis is {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
* </ul>
* </p>
* @param angle rotation angle (rads)
@@ -760,14 +784,14 @@ class Quaternion {
* @return this quaternion for chaining.
*
* @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56">Matrix-FAQ Q56</a>
- * @see #toAngleAxis(Vec3f)
+ * @see #toAngleAxis(Vec3)
*/
- Quaternion& setFromAngleNormalAxis(const float angle, const Vec3f& vector) noexcept {
+ constexpr_cxx26 Quaternion& setFromAngleNormalAxis(const value_type angle, const Vec3& vector) noexcept {
if( vector.is_zero() ) {
set_identity();
} else {
- const float halfangle = angle * 0.5f;
- const float sin = std::sin(halfangle);
+ const value_type halfangle = angle * 0.5f;
+ const value_type sin = std::sin(halfangle);
m_x = vector.x * sin;
m_y = vector.y * sin;
m_z = vector.z * sin;
@@ -781,17 +805,17 @@ class Quaternion {
*
* @param axis storage for computed axis
* @return the rotation angle in radians
- * @see #setFromAngleAxis(float, Vec3f, Vec3f)
+ * @see #setFromAngleAxis(value_type, Vec3, Vec3)
*/
- float toAngleAxis(Vec3f& axis) const noexcept {
- const float sqrLength = m_x*m_x + m_y*m_y + m_z*m_z;
- float angle;
+ constexpr_cxx26 value_type toAngleAxis(Vec3& axis) const noexcept {
+ const value_type sqrLength = m_x*m_x + m_y*m_y + m_z*m_z;
+ value_type angle;
if ( jau::is_zero(sqrLength) ) { // length is ~0
angle = 0.0f;
axis.set( 1.0f, 0.0f, 0.0f );
} else {
angle = std::acos(m_w) * 2.0f;
- const float invLength = 1.0f / std::sqrt(sqrLength);
+ const value_type invLength = 1.0f / std::sqrt(sqrLength);
axis.set( m_x * invLength,
m_y * invLength,
m_z * invLength );
@@ -809,12 +833,12 @@ class Quaternion {
* <li>z - attitude</li>
* </ul>
* </p>
- * For details see {@link #setFromEuler(float, float, float)}.
+ * For details see {@link #setFromEuler(value_type, value_type, value_type)}.
* @param angradXYZ euler angle vector in radians holding x-bank, m_y-heading and m_z-attitude
* @return this quaternion for chaining.
- * @see #setFromEuler(float, float, float)
+ * @see #setFromEuler(value_type, value_type, value_type)
*/
- Quaternion& setFromEuler(const Vec3f& angradXYZ) noexcept {
+ constexpr_cxx26 Quaternion& setFromEuler(const Vec3& angradXYZ) noexcept {
return setFromEuler(angradXYZ.x, angradXYZ.y, angradXYZ.z);
}
@@ -831,7 +855,7 @@ class Quaternion {
* <p>
* Implementation Details:
* <ul>
- * <li> {@link #setIdentity()} if all angles are {@link jau::is_zero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
+ * <li> {@link #setIdentity()} if all angles are {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
* <li> result is {@link #normalize()}ed</li>
* </ul>
* </p>
@@ -843,27 +867,27 @@ class Quaternion {
* @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60">Matrix-FAQ Q60</a>
* @see <a href="http://vered.rose.utoronto.ca/people/david_dir/GEMS/GEMS.html">Gems</a>
* @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm">euclideanspace.com-eulerToQuaternion</a>
- * @see #toEuler(Vec3f)
+ * @see #toEuler(Vec3)
*/
- Quaternion& setFromEuler(const float bankX, const float headingY, const float attitudeZ) noexcept {
+ constexpr_cxx26 Quaternion& setFromEuler(const value_type bankX, const value_type headingY, const value_type attitudeZ) noexcept {
if ( jau::is_zero3f(bankX, headingY, attitudeZ) ) {
return set_identity();
} else {
- float angle = headingY * 0.5f;
- const float sinHeadingY = std::sin(angle);
- const float cosHeadingY = std::cos(angle);
+ value_type angle = headingY * 0.5f;
+ const value_type sinHeadingY = std::sin(angle);
+ const value_type cosHeadingY = std::cos(angle);
angle = attitudeZ * 0.5f;
- const float sinAttitudeZ = std::sin(angle);
- const float cosAttitudeZ = std::cos(angle);
+ const value_type sinAttitudeZ = std::sin(angle);
+ const value_type cosAttitudeZ = std::cos(angle);
angle = bankX * 0.5f;
- const float sinBankX = std::sin(angle);
- const float cosBankX = std::cos(angle);
+ const value_type sinBankX = std::sin(angle);
+ const value_type cosBankX = std::cos(angle);
// variables used to reduce multiplication calls.
- const float cosHeadingXcosAttitude = cosHeadingY * cosAttitudeZ;
- const float sinHeadingXsinAttitude = sinHeadingY * sinAttitudeZ;
- const float cosHeadingXsinAttitude = cosHeadingY * sinAttitudeZ;
- const float sinHeadingXcosAttitude = sinHeadingY * cosAttitudeZ;
+ const value_type cosHeadingXcosAttitude = cosHeadingY * cosAttitudeZ;
+ const value_type sinHeadingXsinAttitude = sinHeadingY * sinAttitudeZ;
+ const value_type cosHeadingXsinAttitude = cosHeadingY * sinAttitudeZ;
+ const value_type sinHeadingXcosAttitude = sinHeadingY * cosAttitudeZ;
m_w = cosHeadingXcosAttitude * cosBankX - sinHeadingXsinAttitude * sinBankX;
m_x = cosHeadingXcosAttitude * sinBankX + sinHeadingXsinAttitude * cosBankX;
@@ -884,28 +908,28 @@ class Quaternion {
* </ul>
* </p>
*
- * @return new Vec3f euler angle result vector filled with x-bank, y-heading and m_z-attitude
+ * @return new Vec3 euler angle result vector filled with x-bank, y-heading and m_z-attitude
* @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm">euclideanspace.com-quaternionToEuler</a>
- * @see #setFromEuler(float, float, float)
+ * @see #setFromEuler(value_type, value_type, value_type)
*/
- Vec3f toEuler() noexcept {
- const float sqw = m_w*m_w;
- const float sqx = m_x*m_x;
- const float sqy = m_y*m_y;
- const float sqz = m_z*m_z;
- const float unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise, is correction factor
- const float test = m_x*m_y + m_z*m_w;
+ constexpr_cxx26 Vec3 toEuler() noexcept {
+ const value_type sqw = m_w*m_w;
+ const value_type sqx = m_x*m_x;
+ const value_type sqy = m_y*m_y;
+ const value_type sqz = m_z*m_z;
+ const value_type unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise, is correction factor
+ const value_type test = m_x*m_y + m_z*m_w;
if (test > 0.499f * unit) { // singularity at north pole
- return Vec3f( 0.0f, // m_x-bank
+ return Vec3( 0.0f, // m_x-bank
2.0f * std::atan2(m_x, m_w), // y-heading
M_PI_2 ); // z-attitude
} else if (test < -0.499f * unit) { // singularity at south pole
- return Vec3f( 0.0f, // m_x-bank
+ return Vec3( 0.0f, // m_x-bank
-2.0 * std::atan2(m_x, m_w), // m_y-heading
-M_PI_2 ); // m_z-attitude
} else {
- return Vec3f( std::atan2(2.0f * m_x * m_w - 2.0f * m_y * m_z, -sqx + sqy - sqz + sqw), // m_x-bank
+ return Vec3( std::atan2(2.0f * m_x * m_w - 2.0f * m_y * m_z, -sqx + sqy - sqz + sqw), // m_x-bank
std::atan2(2.0f * m_y * m_w - 2.0f * m_x * m_z, sqx - sqy - sqz + sqw), // m_y-heading
std::asin( 2.0f * test / unit) ); // z-attitude
}
@@ -924,35 +948,35 @@ class Quaternion {
* @return this quaternion for chaining.
* @see #setFromMatrix(Matrix4f)
*/
- Quaternion& setFromMat3(const float m00, const float m01, const float m02,
- const float m10, const float m11, const float m12,
- const float m20, const float m21, const float m22) noexcept {
+ 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 {
// Note: Other implementations uses 'T' w/o '+1f' and compares 'T >= 0' while adding missing 1f in sqrt expr.
// However .. this causes setLookAt(..) to fail and actually violates the 'trace definition'.
// The trace T is the sum of the diagonal elements; see
// http://mathworld.wolfram.com/MatrixTrace.html
- const float T = m00 + m11 + m22 + 1.0f;
+ const value_type T = m00 + m11 + m22 + 1.0f;
if ( T > 0.0f ) {
- const float S = 0.5f / std::sqrt(T); // S = 1 / ( 2 t )
+ const value_type S = 0.5f / std::sqrt(T); // S = 1 / ( 2 t )
m_w = 0.25f / S; // m_w = 1 / ( 4 S ) = t / 2
m_x = ( m21 - m12 ) * S;
m_y = ( m02 - m20 ) * S;
m_z = ( m10 - m01 ) * S;
} else if ( m00 > m11 && m00 > m22) {
- const float S = 0.5f / std::sqrt(1.0f + m00 - m11 - m22); // S=4*qx
+ const value_type S = 0.5f / std::sqrt(1.0f + m00 - m11 - m22); // S=4*qx
m_w = ( m21 - m12 ) * S;
m_x = 0.25f / S;
m_y = ( m10 + m01 ) * S;
m_z = ( m02 + m20 ) * S;
} else if ( m11 > m22 ) {
- const float S = 0.5f / std::sqrt(1.0f + m11 - m00 - m22); // S=4*qy
+ const value_type S = 0.5f / std::sqrt(1.0f + m11 - m00 - m22); // S=4*qy
m_w = ( m02 - m20 ) * S;
m_x = ( m20 + m01 ) * S;
m_y = 0.25f / S;
m_z = ( m21 + m12 ) * S;
} else {
- const float S = 0.5f / std::sqrt(1.0f + m22 - m00 - m11); // S=4*qz
+ const value_type S = 0.5f / std::sqrt(1.0f + m22 - m00 - m11); // S=4*qz
m_w = ( m10 - m01 ) * S;
m_x = ( m02 + m20 ) * S;
m_y = ( m21 + m12 ) * S;
@@ -973,10 +997,27 @@ class Quaternion {
*
* @return this quaternion for chaining.
* @see Matrix4f#getRotation(Quaternion)
- * @see #setFromMatrix(float, float, float, float, float, float, float, float, float)
+ * @see #setFromMatrix(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type)
*/
- Quaternion& setFromMat3(const Mat4f& m) noexcept {
- return setFromMat3(m.m00, m.m01, m.m02, m.m10, m.m11, m.m12, m.m20, m.m21, m.m22);
+ constexpr Quaternion& setFromMat(const Mat4f& m) noexcept {
+ return setFromMat(m.m00, m.m01, m.m02, m.m10, m.m11, m.m12, m.m20, m.m21, m.m22);
+ }
+
+ /**
+ * Initializes this quaternion to represent a rotation formed by the given three <i>orthogonal</i> axes.
+ * <p>
+ * No validation whether the axes are <i>orthogonal</i> is performed.
+ * </p>
+ *
+ * @param xAxis vector representing the <i>orthogonal</i> x-axis of the coordinate system.
+ * @param yAxis vector representing the <i>orthogonal</i> y-axis of the coordinate system.
+ * @param zAxis vector representing the <i>orthogonal</i> m_z-axis of the coordinate system.
+ * @return this quaternion for chaining.
+ */
+ constexpr Quaternion& setFromAxes(const Vec3& xAxis, const Vec3& yAxis, const Vec3& zAxis) noexcept {
+ return setFromMat(xAxis.x, yAxis.x, zAxis.x,
+ xAxis.y, yAxis.y, zAxis.y,
+ xAxis.z, yAxis.z, zAxis.z);
}
/**
@@ -984,20 +1025,19 @@ class Quaternion {
* <p>
* Implementation Details:
* <ul>
- * <li> makes identity matrix if {@link #magnitudeSquared()} is {@link jau::is_zero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
+ * <li> makes identity matrix if {@link #magnitudeSquared()} is {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
* </ul>
* </p>
*
* @return resulting normalized column matrix 4x4
* @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54">Matrix-FAQ Q54</a>
- * @see #setFromMatrix(float, float, float, float, float, float, float, float, float)
+ * @see #setFromMatrix(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type)
* @see Matrix4f#setToRotation(Quaternion)
*/
Mat4f toMatrix() const noexcept {
- Mat4f m;
- m.setToRotation(*this);
- return m;
+ Mat4f m; toMatrix(m); return m;
}
+
/**
* Transform this quaternion to a normalized 4x4 column matrix representing the rotation,
* see toMatrix().
@@ -1005,25 +1045,72 @@ class Quaternion {
* @param out store for the resulting normalized column matrix 4x4
* @return the given matrix store
*/
- Mat4f& toMatrix(Mat4f& out) const noexcept {
- return out.setToRotation(*this);
+ Mat4f& toMatrix(Mat4f& m) const noexcept {
+ // pre-multiply scaled-reciprocal-magnitude to reduce multiplications
+ const value_type norm = magnitudeSquared();
+ if ( jau::is_zero(norm) ) {
+ // identity matrix -> srecip = 0f
+ m.loadIdentity();
+ return m;
+ }
+ value_type srecip;
+ if ( jau::equals(1.0f, norm) ) {
+ srecip = 2.0f;
+ } else {
+ srecip = 2.0f / norm;
+ }
+ const value_type x = m_x;
+ const value_type y = m_y;
+ const value_type z = m_z;
+ const value_type w = m_w;
+
+ const value_type xs = srecip * x;
+ const value_type ys = srecip * y;
+ const value_type zs = srecip * z;
+
+ const value_type xx = x * xs;
+ const value_type xy = x * ys;
+ const value_type xz = x * zs;
+ const value_type xw = xs * w;
+ const value_type yy = y * ys;
+ const value_type yz = y * zs;
+ const value_type yw = ys * w;
+ const value_type zz = z * zs;
+ const value_type zw = zs * w;
+
+ m.m00 = 1.0f - ( yy + zz );
+ m.m01 = ( xy - zw );
+ m.m02 = ( xz + yw );
+ m.m03 = 0.0f;
+
+ m.m10 = ( xy + zw );
+ m.m11 = 1.0f - ( xx + zz );
+ m.m12 = ( yz - xw );
+ m.m13 = 0.0f;
+
+ m.m20 = ( xz - yw );
+ m.m21 = ( yz + xw );
+ m.m22 = 1.0f - ( xx + yy );
+ m.m23 = 0.0f;
+
+ m.m30 = m.m31 = m.m32 = 0.0f;
+ m.m33 = 1.0f;
+ return m;
}
/**
- * Initializes this quaternion to represent a rotation formed by the given three <i>orthogonal</i> axes.
- * <p>
- * No validation whether the axes are <i>orthogonal</i> is performed.
- * </p>
+ * Extracts this quaternion's <i>orthogonal</i> rotation axes.
*
* @param xAxis vector representing the <i>orthogonal</i> x-axis of the coordinate system.
* @param yAxis vector representing the <i>orthogonal</i> y-axis of the coordinate system.
* @param zAxis vector representing the <i>orthogonal</i> m_z-axis of the coordinate system.
- * @return this quaternion for chaining.
+ * @param tmp temporary Matrix4 used for toMatrix()
*/
- Quaternion& setFromAxes(const Vec3f& xAxis, const Vec3f& yAxis, const Vec3f& zAxis) noexcept {
- return setFromMat3(xAxis.x, yAxis.x, zAxis.x,
- xAxis.y, yAxis.y, zAxis.y,
- xAxis.z, yAxis.z, zAxis.z);
+ void toAxes(Vec3& xAxis, Vec3& yAxis, Vec3& zAxis, Matrix4<value_type>& tmp) const noexcept {
+ toMatrix(tmp);
+ tmp.getColumn(2, zAxis);
+ tmp.getColumn(1, yAxis);
+ tmp.getColumn(0, xAxis);
}
/**
@@ -1033,12 +1120,9 @@ class Quaternion {
* @param yAxis vector representing the <i>orthogonal</i> y-axis of the coordinate system.
* @param zAxis vector representing the <i>orthogonal</i> m_z-axis of the coordinate system.
*/
- void toAxes(Vec3f& xAxis, Vec3f& yAxis, Vec3f& zAxis) const noexcept {
- Mat4f tmpMat4;
- tmpMat4.setToRotation(*this);
- tmpMat4.getColumn(2, zAxis);
- tmpMat4.getColumn(1, yAxis);
- tmpMat4.getColumn(0, xAxis);
+ void toAxes(Vec3& xAxis, Vec3& yAxis, Vec3& zAxis) const noexcept {
+ Matrix4<value_type> tmp;
+ toAxes(xAxis, yAxis, zAxis, tmp);
}
//
@@ -1049,14 +1133,14 @@ class Quaternion {
* @param o the object to compare for equality
* @return true if this quaternion and the provided quaternion have roughly the same x, m_y, m_z and m_w values.
*/
- constexpr bool equals(const Quaternion& o) const noexcept {
+ constexpr bool operator==(const Quaternion& o) const noexcept {
if (this == &o) {
return true;
}
- return std::abs(m_x - o.m_x) <= ALLOWED_DEVIANCE &&
- std::abs(m_y - o.m_y) <= ALLOWED_DEVIANCE &&
- std::abs(m_z - o.m_z) <= ALLOWED_DEVIANCE &&
- std::abs(m_w - o.m_w) <= ALLOWED_DEVIANCE;
+ return std::abs(m_x - o.m_x) <= allowed_deviation &&
+ std::abs(m_y - o.m_y) <= allowed_deviation &&
+ std::abs(m_z - o.m_z) <= allowed_deviation &&
+ std::abs(m_w - o.m_w) <= allowed_deviation;
}
std::string toString() const noexcept {
@@ -1064,35 +1148,45 @@ class Quaternion {
}
};
-constexpr bool operator==(const Quaternion& lhs, const Quaternion& rhs ) noexcept {
- return lhs.equals(rhs);
-}
-
-constexpr Quaternion operator+(const Quaternion& lhs, const Quaternion& rhs ) noexcept {
- return Quaternion(lhs) += rhs;
+template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+constexpr Quaternion<T> operator+(const Quaternion<T>& lhs, const Quaternion<T>& rhs ) noexcept {
+ Quaternion<T> r(lhs); r += rhs; return r;
}
-constexpr Quaternion operator-(const Quaternion& lhs, const Quaternion& rhs ) noexcept {
- return Quaternion(lhs) -= rhs;
+template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+constexpr Quaternion<T> operator-(const Quaternion<T>& lhs, const Quaternion<T>& rhs ) noexcept {
+ Quaternion<T> r(lhs); r -= rhs; return r;
}
-constexpr Quaternion operator*(const Quaternion& lhs, const Quaternion& rhs ) noexcept {
- return Quaternion(lhs) *= rhs;
+template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+constexpr Quaternion<T> operator*(const Quaternion<T>& lhs, const Quaternion<T>& rhs ) noexcept {
+ Quaternion<T> r(lhs); r *= rhs; return r;
}
-constexpr Quaternion operator*(const Quaternion& lhs, const float s ) noexcept {
- return Quaternion(lhs) *= s;
+template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+constexpr Quaternion<T> operator*(const Quaternion<T>& lhs, const T s ) noexcept {
+ Quaternion<T> r(lhs); r *= s; return r;
}
-constexpr Quaternion operator*(const float s, const Quaternion& rhs) noexcept {
- return Quaternion(rhs) *= s;
+template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+constexpr Quaternion<T> operator*(const T s, const Quaternion<T>& rhs) noexcept {
+ Quaternion<T> r(rhs); r *= s; return r;
}
-
-std::ostream& operator<<(std::ostream& out, const Quaternion& v) noexcept {
+template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+std::ostream& operator<<(std::ostream& out, const Quaternion<T>& v) noexcept {
return out << v.toString();
}
+typedef Quaternion<float> Quat4f;
+static_assert(alignof(float) == alignof(Quat4f));
+
/**@}*/
} // namespace jau::math
diff --git a/include/jau/math/recti.hpp b/include/jau/math/recti.hpp
index 43a9aba..846be22 100644
--- a/include/jau/math/recti.hpp
+++ b/include/jau/math/recti.hpp
@@ -38,31 +38,42 @@ namespace jau::math {
/**
* Rectangle with x, y, width and height integer components.
*/
- class Recti {
+ template<typename Value_type,
+ std::enable_if_t<std::is_integral_v<Value_type>, bool> = true>
+ class RectI {
+ public:
+ typedef Value_type value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef value_type* iterator;
+ typedef const value_type* const_iterator;
+
private:
- int m_x;
- int m_y;
- int m_width;
- int m_height;
+ value_type m_x;
+ value_type m_y;
+ value_type m_width;
+ value_type m_height;
public:
- constexpr Recti() noexcept
+ constexpr RectI() noexcept
: m_x(0), m_y(0), m_width(0), m_height(0) {}
- constexpr Recti(const int xywh[/*4*/]) noexcept{
+ constexpr RectI(const value_type xywh[/*4*/]) noexcept{
set(xywh);
}
- constexpr Recti(const int x, const int y, const int width, const int height) noexcept {
+ constexpr RectI(const value_type x, const value_type y, const value_type width, const value_type height) noexcept {
set(x, y, width, height);
}
- constexpr Recti(const Recti& o) noexcept = default;
- constexpr Recti(Recti&& o) noexcept = default;
- constexpr Recti& operator=(const Recti&) noexcept = default;
- constexpr Recti& operator=(Recti&&) noexcept = default;
+ constexpr RectI(const RectI& o) noexcept = default;
+ constexpr RectI(RectI&& o) noexcept = default;
+ constexpr RectI& operator=(const RectI&) noexcept = default;
+ constexpr RectI& operator=(RectI&&) noexcept = default;
- constexpr bool operator==(const Recti& rhs ) const noexcept {
+ constexpr bool operator==(const RectI& rhs ) const noexcept {
if( this == &rhs ) {
return true;
}
@@ -76,41 +87,26 @@ namespace jau::math {
} */
/** this = { x, y, width, height }, returns this. */
- constexpr Recti& set(const int x, const int y, const int width, const int height) noexcept {
- m_x = x;
- m_y = y;
- m_width = width;
- m_height= height;
- return *this;
- }
+ constexpr RectI& set(const value_type x, const value_type y, const value_type width, const value_type height) noexcept
+ { m_x = x; m_y = y; m_width = width; m_height= height; return *this; }
/** this = xywh, returns this. */
- constexpr Recti& set(const int xywh[/*4*/]) noexcept {
- m_x = xywh[0];
- m_y = xywh[1];
- m_width = xywh[2];
- m_height= xywh[3];
- return *this;
- }
+ constexpr RectI& set(const_iterator xywh) noexcept
+ { m_x = xywh[0]; m_y = xywh[1]; m_width = xywh[2]; m_height= xywh[3]; return *this; }
/** xywh = this, returns xywh. */
- int* get(int xywh[/*4*/]) const noexcept {
- xywh[0] = m_x;
- xywh[1] = m_y;
- xywh[2] = m_width;
- xywh[3] = m_height;
- return xywh;
- }
+ iterator get(iterator xywh) const noexcept
+ { xywh[0] = m_x; xywh[1] = m_y; xywh[2] = m_width; xywh[3] = m_height; return xywh; }
- int x() const noexcept { return m_x; }
- int y() const noexcept { return m_y; }
- int width() const noexcept { return m_width; }
- int height() const noexcept { return m_height; }
+ value_type x() const noexcept { return m_x; }
+ value_type y() const noexcept { return m_y; }
+ value_type width() const noexcept { return m_width; }
+ value_type height() const noexcept { return m_height; }
- void setX(const int x) noexcept { m_x = x; }
- void setY(const int y) noexcept { m_y = y; }
- void setWidth(const int width) noexcept { m_width = width; }
- void setHeight(const int height) noexcept { m_height = height; }
+ void setX(const value_type x) noexcept { m_x = x; }
+ void setY(const value_type y) noexcept { m_y = y; }
+ void setWidth(const value_type width) noexcept { m_width = width; }
+ void setHeight(const value_type height) noexcept { m_height = height; }
/** Return true if area is zero. */
bool is_zero() const noexcept {
@@ -121,10 +117,15 @@ namespace jau::math {
{ return std::to_string(m_x)+"/"+std::to_string(m_y)+" "+std::to_string(m_width)+"x"+std::to_string(m_height); }
};
- std::ostream& operator<<(std::ostream& out, const Recti& v) noexcept {
+ template<typename T,
+ std::enable_if_t<std::numeric_limits<T>::is_integer, bool> = true>
+ std::ostream& operator<<(std::ostream& out, const RectI<T>& v) noexcept {
return out << v.toString();
}
+ typedef RectI<int> Recti;
+ static_assert(alignof(int) == alignof(Recti));
+
/**@}*/
} // namespace jau::math
diff --git a/include/jau/math/vec2f.hpp b/include/jau/math/vec2f.hpp
index 20943bd..e4e954a 100644
--- a/include/jau/math/vec2f.hpp
+++ b/include/jau/math/vec2f.hpp
@@ -27,6 +27,7 @@
#include <cmath>
#include <cstdarg>
#include <cstdint>
+#include <cassert>
#include <limits>
#include <string>
#include <iostream>
@@ -41,46 +42,64 @@ namespace jau::math {
*/
/**
- * 2D vector using two float components.
+ * 2D vector using two value_type components.
*/
- class Vec2f {
- public:
- float x;
- float y;
-
- static constexpr Vec2f from_length_angle(const float magnitude, const float radians) noexcept {
- return Vec2f(magnitude * std::cos(radians), magnitude * std::sin(radians));
+ template<typename Value_type,
+ std::enable_if_t<std::is_floating_point_v<Value_type>, bool> = true>
+ class alignas(Value_type) Vector2F {
+ public:
+ typedef Value_type value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef value_type* iterator;
+ typedef const value_type* const_iterator;
+
+ constexpr static const value_type zero = value_type(0);
+ constexpr static const value_type one = value_type(1);
+
+ value_type x;
+ value_type y;
+
+ static constexpr_cxx26 Vector2F from_length_angle(const value_type magnitude, const value_type radians) noexcept {
+ return Vector2F(magnitude * std::cos(radians), magnitude * std::sin(radians));
}
- constexpr Vec2f() noexcept
- : x(0), y(0) {}
+ constexpr Vector2F() noexcept
+ : x(zero), y(zero) {}
- constexpr Vec2f(const float x_, const float y_) noexcept
+ constexpr Vector2F(const value_type v) noexcept
+ : x(v), y(v) {}
+
+ constexpr Vector2F(const value_type x_, const value_type y_) noexcept
: x(x_), y(y_) {}
- constexpr Vec2f(const Vec2f& o) noexcept = default;
- constexpr Vec2f(Vec2f&& o) noexcept = default;
- constexpr Vec2f& operator=(const Vec2f&) noexcept = default;
- constexpr Vec2f& operator=(Vec2f&&) noexcept = default;
+ constexpr Vector2F(const Vector2F& o) noexcept = default;
+ constexpr Vector2F(Vector2F&& o) noexcept = default;
+ constexpr Vector2F& operator=(const Vector2F&) noexcept = default;
+ constexpr Vector2F& operator=(Vector2F&&) noexcept = default;
- /** Returns read-only component w/o boundary check */
- float operator[](size_t i) const noexcept {
- return reinterpret_cast<const float*>(this)[i];
+ /** Returns read-only component */
+ constexpr value_type operator[](size_t i) const noexcept {
+ assert(i < 2);
+ return (&x)[i];
}
- /** Returns writeable reference to component w/o boundary check */
- float& operator[](size_t i) noexcept {
- return reinterpret_cast<float*>(this)[i];
+ /** Returns writeable reference to component */
+ constexpr reference operator[](size_t i) noexcept {
+ assert(i < 2);
+ return (&x)[i];
}
/** xy = this, returns xy. */
- float* get(float xy[/*2*/]) const noexcept {
+ constexpr iterator get(iterator xy) const noexcept {
xy[0] = x;
xy[1] = y;
return xy;
}
- constexpr bool operator==(const Vec2f& rhs ) const noexcept {
+ constexpr bool operator==(const Vector2F& rhs ) const noexcept {
if( this == &rhs ) {
return true;
}
@@ -91,18 +110,22 @@ namespace jau::math {
return ...
} */
- constexpr Vec2f& set(const float vx, const float vy) noexcept
+ constexpr Vector2F& set(const value_type vx, const value_type vy) noexcept
{ x=vx; y=vy; return *this; }
- constexpr Vec2f& add(const float dx, const float dy) noexcept
+ /** this = xy, returns this. */
+ constexpr Vector2F& set(const_iterator xy) noexcept
+ { x=xy[0]; y=xy[1]; return *this; }
+
+ constexpr Vector2F& add(const value_type dx, const value_type dy) noexcept
{ x+=dx; y+=dy; return *this; }
- constexpr Vec2f& operator+=(const Vec2f& rhs ) noexcept {
+ constexpr Vector2F& operator+=(const Vector2F& rhs ) noexcept {
x+=rhs.x; y+=rhs.y;
return *this;
}
- constexpr Vec2f& operator-=(const Vec2f& rhs ) noexcept {
+ constexpr Vector2F& operator-=(const Vector2F& rhs ) noexcept {
x-=rhs.x; y-=rhs.y;
return *this;
}
@@ -112,7 +135,7 @@ namespace jau::math {
* @param s scale factor
* @return this instance
*/
- constexpr Vec2f& operator*=(const float s ) noexcept {
+ constexpr Vector2F& operator*=(const value_type s ) noexcept {
x*=s; y*=s;
return *this;
}
@@ -122,33 +145,33 @@ namespace jau::math {
* @param s scale factor
* @return this instance
*/
- constexpr Vec2f& operator/=(const float s ) noexcept {
+ constexpr Vector2F& operator/=(const value_type s ) noexcept {
x/=s; y/=s;
return *this;
}
/** Rotates this vector in place, returns *this */
- Vec2f& rotate(const float radians, const Vec2f& ctr) noexcept {
+ constexpr_cxx26 Vector2F& rotate(const value_type radians, const Vector2F& ctr) noexcept {
return rotate(std::sin(radians), std::cos(radians), ctr);
}
/** Rotates this vector in place, returns *this */
- constexpr Vec2f& rotate(const float sin, const float cos, const Vec2f& ctr) noexcept {
- const float x0 = x - ctr.x;
- const float y0 = y - ctr.y;
+ constexpr Vector2F& rotate(const value_type sin, const value_type cos, const Vector2F& ctr) noexcept {
+ const value_type x0 = x - ctr.x;
+ const value_type y0 = y - ctr.y;
x = x0 * cos - y0 * sin + ctr.x;
y = x0 * sin + y0 * cos + ctr.y;
return *this;
}
/** Rotates this vector in place, returns *this */
- Vec2f& rotate(const float radians) noexcept {
+ constexpr_cxx26 Vector2F& rotate(const value_type radians) noexcept {
return rotate(std::sin(radians), std::cos(radians));
}
/** Rotates this vector in place, returns *this */
- constexpr Vec2f& rotate(const float sin, const float cos) noexcept {
- const float x0 = x;
+ constexpr Vector2F& rotate(const value_type sin, const value_type cos) noexcept {
+ const value_type x0 = x;
x = x0 * cos - y * sin;
y = x0 * sin + y * cos;
return *this;
@@ -163,33 +186,25 @@ namespace jau::math {
/**
* Return the squared length of this vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i>
*/
- constexpr float length_sq() const noexcept {
+ constexpr value_type length_sq() const noexcept {
return x*x + y*y;
}
/**
* Return the length of this vector, a.k.a the <i>norm</i> or <i>magnitude</i>
*/
- constexpr float length() const noexcept {
+ constexpr value_type length() const noexcept {
return std::sqrt(length_sq());
}
- /**
- * Return the direction angle of this vector in radians
- */
- float angle() const noexcept {
- // Utilize atan2 taking y=sin(a) and x=cos(a), resulting in proper direction angle for all quadrants.
- return std::atan2( y, x );
- }
-
/** Normalize this vector in place, returns *this */
- constexpr Vec2f& normalize() noexcept {
- const float lengthSq = length_sq();
+ constexpr Vector2F& normalize() noexcept {
+ const value_type lengthSq = length_sq();
if ( jau::is_zero( lengthSq ) ) {
- x = 0.0f;
- y = 0.0f;
+ x = zero;
+ y = zero;
} else {
- const float invSqr = 1.0f / std::sqrt(lengthSq);
+ const value_type invSqr = one / std::sqrt(lengthSq);
x *= invSqr;
y *= invSqr;
}
@@ -197,30 +212,38 @@ namespace jau::math {
}
/**
+ * Return the direction angle of this vector in radians
+ */
+ constexpr_cxx26 value_type angle() const noexcept {
+ // Utilize atan2 taking y=sin(a) and x=cos(a), resulting in proper direction angle for all quadrants.
+ return std::atan2( y, x );
+ }
+
+ /**
* Return the squared distance between this vector and the given one.
* <p>
* When comparing the relative distance between two points it is usually sufficient to compare the squared
* distances, thus avoiding an expensive square root operation.
* </p>
*/
- constexpr float dist_sq(const Vec2f& o) const noexcept {
- const float dx = x - o.x;
- const float dy = y - o.y;
+ constexpr value_type dist_sq(const Vector2F& o) const noexcept {
+ const value_type dx = x - o.x;
+ const value_type dy = y - o.y;
return dx*dx + dy*dy;
}
/**
* Return the distance between this vector and the given one.
*/
- constexpr float dist(const Vec2f& o) const noexcept {
+ constexpr value_type dist(const Vector2F& o) const noexcept {
return std::sqrt(dist_sq(o));
}
/**
* Return the dot product of this vector and the given one
- * @return the dot product as float
+ * @return the dot product as value_type
*/
- constexpr float dot(const Vec2f& o) const noexcept {
+ constexpr value_type dot(const Vector2F& o) const noexcept {
return x*o.x + y*o.y;
}
@@ -231,65 +254,95 @@ namespace jau::math {
*
* @return the resulting scalar
*/
- constexpr float cross(const Vec2f& o) const noexcept {
+ constexpr value_type cross(const Vector2F& o) const noexcept {
return x * o.y - y * o.x;
}
/**
* Return the cosines of the angle between two vectors
*/
- constexpr float cos_angle(const Vec2f& o) const noexcept {
+ constexpr value_type cos_angle(const Vector2F& o) const noexcept {
return dot(o) / ( length() * o.length() ) ;
}
/**
* Return the angle between two vectors in radians
*/
- float angle(const Vec2f& o) const noexcept {
+ constexpr_cxx26 value_type angle(const Vector2F& o) const noexcept {
return std::acos( cos_angle(o) );
}
/**
* Return the counter-clock-wise (CCW) normal of this vector, i.e. perp(endicular) vector
*/
- Vec2f normal_ccw() const noexcept {
- return Vec2f(-y, x);
+ constexpr Vector2F normal_ccw() const noexcept {
+ return Vector2F(-y, x);
}
- bool intersects(const Vec2f& o) const noexcept {
- const float eps = std::numeric_limits<float>::epsilon();
+ constexpr_cxx23 bool intersects(const Vector2F& o) const noexcept {
+ const value_type eps = std::numeric_limits<value_type>::epsilon();
if( std::abs(x-o.x) >= eps || std::abs(y-o.y) >= eps ) {
return false;
}
return true;
}
};
- typedef Vec2f Point2f;
- constexpr Vec2f operator+(const Vec2f& lhs, const Vec2f& rhs ) noexcept {
- return Vec2f(lhs) += rhs;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector2F<T> operator+(const Vector2F<T>& lhs, const Vector2F<T>& rhs ) noexcept {
+ // Returning a Vector2F<T> object from the returned reference of operator+=()
+ // may hinder copy-elision or "named return value optimization" (NRVO).
+ // return Vector2F<T>(lhs) += rhs;
+
+ // Returning named object allows copy-elision (NRVO),
+ // only one object is created 'on target'.
+ Vector2F<T> r(lhs); r += rhs; return r;
}
- constexpr Vec2f operator-(const Vec2f& lhs, const Vec2f& rhs ) noexcept {
- return Vec2f(lhs) -= rhs;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector2F<T> operator-(const Vector2F<T>& lhs, const Vector2F<T>& rhs ) noexcept {
+ Vector2F<T> r(lhs); r -= rhs; return r;
}
- constexpr Vec2f operator*(const Vec2f& lhs, const float s ) noexcept {
- return Vec2f(lhs) *= s;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector2F<T> operator*(const Vector2F<T>& lhs, const T s ) noexcept {
+ Vector2F<T> r(lhs); r *= s; return r;
}
- constexpr Vec2f operator*(const float s, const Vec2f& rhs) noexcept {
- return Vec2f(rhs) *= s;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector2F<T> operator*(const T s, const Vector2F<T>& rhs) noexcept {
+ Vector2F<T> r(rhs); r *= s; return r;
}
- constexpr Vec2f operator/(const Vec2f& lhs, const float s ) noexcept {
- return Vec2f(lhs) /= s;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector2F<T> operator/(const Vector2F<T>& lhs, const T s ) noexcept {
+ Vector2F<T> r(lhs); r /= s; return r;
}
- std::ostream& operator<<(std::ostream& out, const Vec2f& v) noexcept {
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ std::ostream& operator<<(std::ostream& out, const Vector2F<T>& v) noexcept {
return out << v.toString();
}
+ typedef Vector2F<float> Vec2f;
+ static_assert(alignof(float) == alignof(Vec2f));
+
+ /**
+ * Point2F alias of Vector2F
+ */
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ using Point2F = Vector2F<T>;
+
+ typedef Point2F<float> Point2f;
+ static_assert(alignof(float) == alignof(Point2f));
+
/**
* Simple compound denoting a ray.
* <p>
@@ -301,21 +354,28 @@ namespace jau::math {
* </pre>
* </p>
*/
- class Ray2f {
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ class alignas(T) Ray2F {
public:
/** Origin of Ray. */
- Point2f orig;
+ Point2F<T> orig;
/** Normalized direction vector of ray. */
- Vec2f dir;
+ Vector2F<T> dir;
std::string toString() const noexcept { return "Ray[orig "+orig.toString()+", dir "+dir.toString() +"]"; }
};
- std::ostream& operator<<(std::ostream& out, const Ray2f& v) noexcept {
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ std::ostream& operator<<(std::ostream& out, const Ray2F<T>& v) noexcept {
return out << v.toString();
}
+ typedef Ray2F<float> Ray2f;
+ static_assert(alignof(float) == alignof(Ray2f));
+
/**@}*/
} // namespace jau::math
diff --git a/include/jau/math/vec2i.hpp b/include/jau/math/vec2i.hpp
index 8b6934c..40f3ecd 100644
--- a/include/jau/math/vec2i.hpp
+++ b/include/jau/math/vec2i.hpp
@@ -27,6 +27,7 @@
#include <cmath>
#include <cstdarg>
#include <cstdint>
+#include <cassert>
#include <limits>
#include <string>
#include <iostream>
@@ -43,32 +44,59 @@ namespace jau::math {
/**
* 2D vector using two integer components.
*/
- struct Vec2i {
- int x;
- int y;
+ template<typename Value_type,
+ std::enable_if_t<std::is_integral_v<Value_type>, bool> = true>
+ struct alignas(Value_type) Vector2I {
+ typedef Value_type value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef value_type* iterator;
+ typedef const value_type* const_iterator;
- constexpr Vec2i() noexcept
- : x(0), y(0) {}
+ typedef typename jau::float_bytes<sizeof(value_type)>::type float_type;
- constexpr Vec2i(const int x_, const int y_) noexcept
+ constexpr static const value_type zero = value_type(0);
+ constexpr static const value_type one = value_type(1);
+
+ value_type x;
+ value_type y;
+
+ constexpr Vector2I() noexcept
+ : x(zero), y(zero) {}
+
+ constexpr Vector2I(const value_type v) noexcept
+ : x(v), y(v) {}
+
+ constexpr Vector2I(const value_type x_, const value_type y_) noexcept
: x(x_), y(y_) {}
- constexpr Vec2i(const Vec2i& o) noexcept = default;
- constexpr Vec2i(Vec2i&& o) noexcept = default;
- constexpr Vec2i& operator=(const Vec2i&) noexcept = default;
- constexpr Vec2i& operator=(Vec2i&&) noexcept = default;
+ constexpr Vector2I(const Vector2I& o) noexcept = default;
+ constexpr Vector2I(Vector2I&& o) noexcept = default;
+ constexpr Vector2I& operator=(const Vector2I&) noexcept = default;
+ constexpr Vector2I& operator=(Vector2I&&) noexcept = default;
- /** Returns read-only component w/o boundary check */
- int operator[](size_t i) const noexcept {
- return reinterpret_cast<const int*>(this)[i];
+ /** Returns read-only component */
+ constexpr value_type operator[](size_t i) const noexcept {
+ assert(i < 2);
+ return (&x)[i];
}
- /** Returns writeable reference to component w/o boundary check */
- int& operator[](size_t i) noexcept {
- return reinterpret_cast<int*>(this)[i];
+ /** Returns writeable reference to component */
+ constexpr reference operator[](size_t i) noexcept {
+ assert(i < 2);
+ return (&x)[i];
}
- constexpr bool operator==(const Vec2i& rhs ) const noexcept {
+ /** xy = this, returns xy. */
+ constexpr iterator get(iterator xy) const noexcept {
+ xy[0] = x;
+ xy[1] = y;
+ return xy;
+ }
+
+ constexpr bool operator==(const Vector2I& rhs ) const noexcept {
if( this == &rhs ) {
return true;
}
@@ -79,18 +107,23 @@ namespace jau::math {
return ...
} */
- constexpr Vec2i& set(const int vx, const int vy) noexcept
+ constexpr Vector2I& set(const value_type vx, const value_type vy) noexcept
{ x=vx; y=vy; return *this; }
- constexpr Vec2i& add(const int dx, const int dy) noexcept
+ /** this = xy, returns this. */
+ constexpr Vector2I& set(const_iterator xy) noexcept
+ { x=xy[0]; y=xy[1]; return *this; }
+
+
+ constexpr Vector2I& add(const value_type dx, const value_type dy) noexcept
{ x+=dx; y+=dy; return *this; }
- constexpr Vec2i& operator+=(const Vec2i& rhs ) noexcept {
+ constexpr Vector2I& operator+=(const Vector2I& rhs ) noexcept {
x+=rhs.x; y+=rhs.y;
return *this;
}
- constexpr Vec2i& operator-=(const Vec2i& rhs ) noexcept {
+ constexpr Vector2I& operator-=(const Vector2I& rhs ) noexcept {
x-=rhs.x; y-=rhs.y;
return *this;
}
@@ -100,7 +133,7 @@ namespace jau::math {
* @param s scale factor
* @return this instance
*/
- constexpr Vec2i& operator*=(const int s ) noexcept {
+ constexpr Vector2I& operator*=(const value_type s ) noexcept {
x*=s; y*=s;
return *this;
}
@@ -110,56 +143,110 @@ namespace jau::math {
* @param s scale factor
* @return this instance
*/
- constexpr Vec2i& operator/=(const int s ) noexcept {
+ constexpr Vector2I& operator/=(const value_type s ) noexcept {
x/=s; y/=s;
return *this;
}
- void rotate(const float radians, const Vec2i& ctr) {
- const float cos = std::cos(radians);
- const float sin = std::sin(radians);
+ constexpr_cxx26 void rotate(const float_type radians, const Vector2I& ctr) {
+ const float_type cos = std::cos(radians);
+ const float_type sin = std::sin(radians);
rotate(sin, cos, ctr);
}
- void rotate(const float sin, const float cos, const Vec2i& ctr) {
- const float x0 = (float)(x - ctr.x);
- const float y0 = (float)(y - ctr.y);
- const int tmp = jau::round_to_int( x0 * cos - y0 * sin ) + ctr.x;
- y = jau::round_to_int( x0 * sin + y0 * cos ) + ctr.y;
+ void rotate(const float_type sin, const float_type cos, const Vector2I& ctr) {
+ const float_type x0 = static_cast<float_type>(x - ctr.x);
+ const float_type y0 = static_cast<float_type>(y - ctr.y);
+ const value_type tmp = jau::round_to_int<float_type>( x0 * cos - y0 * sin ) + ctr.x;
+ y = jau::round_to_int<float_type>( x0 * sin + y0 * cos ) + ctr.y;
x = tmp;
}
std::string toString() const noexcept { return std::to_string(x)+"/"+std::to_string(y); }
- bool intersects(const Vec2i& o) {
+ constexpr bool is_zero() const noexcept {
+ return jau::is_zero(x) && jau::is_zero(y);
+ }
+
+ /**
+ * Return the squared length of this vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i>
+ */
+ constexpr value_type length_sq() const noexcept {
+ return x*x + y*y;
+ }
+
+ /**
+ * Return the length of this vector, a.k.a the <i>norm</i> or <i>magnitude</i>
+ */
+ constexpr value_type length() const noexcept {
+ // return jau::round_to_int<float_type>( std::sqrt<float_type>( length_sq() ) );
+ return jau::round_to_int<float_type>( std::sqrt( static_cast<float_type> ( length_sq() )) );
+ }
+
+ /** Normalize this vector in place, returns *this */
+ constexpr Vector2I& normalize() noexcept {
+ const value_type lengthSq = length_sq();
+ if ( jau::is_zero( lengthSq ) ) {
+ x = zero;
+ y = zero;
+ } else {
+ const float_type invSqr = one / static_cast<float_type>( std::sqrt(lengthSq) );
+ x = jau::round_to_int<float_type>( x * invSqr );
+ y = jau::round_to_int<float_type>( x * invSqr );
+ }
+ return *this;
+ }
+
+
+ bool intersects(const Vector2I& o) {
return x == o.x && y == o.y;
}
};
- typedef Vec2i Point2i;
- constexpr Vec2i operator+(const Vec2i& lhs, const Vec2i& rhs ) noexcept {
- return Vec2i(lhs) += rhs;
+ template<typename T,
+ std::enable_if_t<std::numeric_limits<T>::is_integer, bool> = true>
+ constexpr Vector2I<T> operator+(const Vector2I<T>& lhs, const Vector2I<T>& rhs ) noexcept {
+ // Returning a Vector2I<T> object from the returned reference of operator+=()
+ // may hinder copy-elision or "named return value optimization" (NRVO).
+ // return Vector2I<T>(lhs) += rhs;
+
+ // Returning named object allows copy-elision (NRVO),
+ // only one object is created 'on target'.
+ Vector2I<T> r(lhs); r += rhs; return r;
}
- constexpr Vec2i operator-(const Vec2i& lhs, const Vec2i& rhs ) noexcept {
- return Vec2i(lhs) -= rhs;
+ template<typename T,
+ std::enable_if_t<std::numeric_limits<T>::is_integer, bool> = true>
+ constexpr Vector2I<T> operator-(const Vector2I<T>& lhs, const Vector2I<T>& rhs ) noexcept {
+ Vector2I<T> r(lhs); r -= rhs; return r;
}
- constexpr Vec2i operator*(const Vec2i& lhs, const float s ) noexcept {
- return Vec2i(lhs) *= s;
+ template<typename T,
+ std::enable_if_t<std::numeric_limits<T>::is_integer, bool> = true>
+ constexpr Vector2I<T> operator*(const Vector2I<T>& lhs, const float s ) noexcept {
+ Vector2I<T> r(lhs); r *= s; return r;
}
- constexpr Vec2i operator*(const float s, const Vec2i& rhs) noexcept {
- return Vec2i(rhs) *= s;
+ template<typename T,
+ std::enable_if_t<std::numeric_limits<T>::is_integer, bool> = true>
+ constexpr Vector2I<T> operator*(const float s, const Vector2I<T>& rhs) noexcept {
+ Vector2I<T> r(rhs); r *= s; return r;
}
- constexpr Vec2i operator/(const Vec2i& lhs, const float s ) noexcept {
- return Vec2i(lhs) /= s;
+ template<typename T,
+ std::enable_if_t<std::numeric_limits<T>::is_integer, bool> = true>
+ constexpr Vector2I<T> operator/(const Vector2I<T>& lhs, const float s ) noexcept {
+ Vector2I<T> r(lhs); r /= s; return r;
}
- std::ostream& operator<<(std::ostream& out, const Vec2i& v) noexcept {
+ template<typename T,
+ std::enable_if_t<std::numeric_limits<T>::is_integer, bool> = true>
+ std::ostream& operator<<(std::ostream& out, const Vector2I<T>& v) noexcept {
return out << v.toString();
}
+ typedef Vector2I<int> Vec2i;
+ static_assert(alignof(int) == alignof(Vec2i));
+
/**@}*/
} // namespace jau::math
diff --git a/include/jau/math/vec3f.hpp b/include/jau/math/vec3f.hpp
index e94ad40..74a2034 100644
--- a/include/jau/math/vec3f.hpp
+++ b/include/jau/math/vec3f.hpp
@@ -27,8 +27,10 @@
#include <cmath>
#include <cstdarg>
#include <cstdint>
+#include <cassert>
#include <limits>
#include <string>
+#include <initializer_list>
#include <iostream>
#include <jau/float_math.hpp>
@@ -41,47 +43,69 @@ namespace jau::math {
* @{
*/
- class Vec4f; // forward
-
/**
- * 3D vector using three float components.
+ * 3D vector using three value_type components.
*/
- struct Vec3f {
+ template<typename Value_type,
+ std::enable_if_t<std::is_floating_point_v<Value_type>, bool> = true>
+ class alignas(Value_type) Vector3F {
public:
- float x;
- float y;
- float z;
+ typedef Value_type value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef value_type* iterator;
+ typedef const value_type* const_iterator;
+
+ constexpr static const value_type zero = value_type(0);
+ constexpr static const value_type one = value_type(1);
+
+ value_type x;
+ value_type y;
+ value_type z;
- constexpr Vec3f() noexcept
- : x(0), y(0), z(0) {}
+ constexpr Vector3F() noexcept
+ : x(zero), y(zero), z(zero) {}
- constexpr Vec3f(const float x_, const float y_, const float z_) noexcept
+ constexpr Vector3F(const value_type v) noexcept
+ : x(v), y(v), z(v) {}
+
+ constexpr Vector3F(const value_type x_, const value_type y_, const value_type z_) noexcept
: x(x_), y(y_), z(z_) {}
- constexpr Vec3f(const Vec3f& o) noexcept = default;
- constexpr Vec3f(Vec3f&& o) noexcept = default;
- constexpr Vec3f& operator=(const Vec3f&) noexcept = default;
- constexpr Vec3f& operator=(Vec3f&&) noexcept = default;
+ constexpr Vector3F(const_iterator v) noexcept
+ : x(v[0]), y(v[1]), z(v[2]) {}
+
+ constexpr Vector3F(std::initializer_list<value_type> v) noexcept
+ : x(v[0]), y(v[1]), z(v[2]) {}
- /** Returns read-only component w/o boundary check */
- float operator[](size_t i) const noexcept {
- return reinterpret_cast<const float*>(this)[i];
+ constexpr Vector3F(const Vector3F& o) noexcept = default;
+ constexpr Vector3F(Vector3F&& o) noexcept = default;
+ constexpr Vector3F& operator=(const Vector3F&) noexcept = default;
+ constexpr Vector3F& operator=(Vector3F&&) noexcept = default;
+
+ /** Returns read-only component */
+ constexpr value_type operator[](size_t i) const noexcept {
+ assert(i < 3);
+ return (&x)[i];
}
- /** Returns writeable reference to component w/o boundary check */
- float& operator[](size_t i) noexcept {
- return reinterpret_cast<float*>(this)[i];
+ /** Returns writeable reference to component */
+ constexpr reference operator[](size_t i) noexcept {
+ assert(i < 3);
+ return (&x)[i];
}
/** xyz = this, returns xyz. */
- float* get(float xyz[/*3*/]) const noexcept {
+ constexpr iterator get(iterator xyz) const noexcept {
xyz[0] = x;
xyz[1] = y;
xyz[2] = z;
return xyz;
}
- constexpr bool equals(const Vec3f& o, const float epsilon=std::numeric_limits<float>::epsilon()) const noexcept {
+ constexpr bool equals(const Vector3F& o, const value_type epsilon=std::numeric_limits<value_type>::epsilon()) const noexcept {
if( this == &o ) {
return true;
} else {
@@ -90,7 +114,7 @@ namespace jau::math {
jau::equals(z, o.z, epsilon);
}
}
- constexpr bool operator==(const Vec3f& rhs) const noexcept {
+ constexpr bool operator==(const Vector3F& rhs) const noexcept {
return equals(rhs);
}
/** TODO
@@ -99,27 +123,28 @@ namespace jau::math {
} */
/** this = { o, z }, returns this. */
- constexpr Vec3f& set(const Vec2f& o, const float z_) noexcept {
+ constexpr Vector3F& set(const Vec2f& o, const value_type z_) noexcept {
x = o.x;
y = o.y;
z = z_;
return *this;
}
- /** this = o while dropping w, returns this. */
- Vec3f& set(const Vec4f& o) noexcept;
-
- constexpr Vec3f& set(const float vx, const float vy, const float vz) noexcept
+ constexpr Vector3F& set(const value_type vx, const value_type vy, const value_type vz) noexcept
{ x=vx; y=vy; z=vz; return *this; }
- constexpr void add(const float dx, const float dy, const float dz) noexcept { x+=dx; y+=dy; z+=dz; }
+ /** this = xyz, returns this. */
+ constexpr Vector3F& set(const_iterator xyz) noexcept
+ { x=xyz[0]; y=xyz[1]; z=xyz[2]; return *this; }
- constexpr Vec3f& operator+=(const Vec3f& rhs ) noexcept {
+ constexpr void add(const value_type dx, const value_type dy, const value_type dz) noexcept { x+=dx; y+=dy; z+=dz; }
+
+ constexpr Vector3F& operator+=(const Vector3F& rhs ) noexcept {
x+=rhs.x; y+=rhs.y; z+=rhs.z;
return *this;
}
- constexpr Vec3f& operator-=(const Vec3f& rhs ) noexcept {
+ constexpr Vector3F& operator-=(const Vector3F& rhs ) noexcept {
x-=rhs.x; y-=rhs.y; z-=rhs.z;
return *this;
}
@@ -129,7 +154,7 @@ namespace jau::math {
* @param s scale factor
* @return this instance
*/
- constexpr Vec3f& operator*=(const float s ) noexcept {
+ constexpr Vector3F& operator*=(const value_type s ) noexcept {
x*=s; y*=s; z*=s;
return *this;
}
@@ -139,28 +164,22 @@ namespace jau::math {
* @param s scale factor
* @return this instance
*/
- constexpr Vec3f& operator/=(const float s ) noexcept {
+ constexpr Vector3F& operator/=(const value_type s ) noexcept {
x/=s; y/=s; z/=s;
return *this;
}
- void rotate(const float radians, const Vec3f& ctr) noexcept {
- // FIXME z, consider using a matrix or quaternions
- const float cos = std::cos(radians);
- const float sin = std::sin(radians);
- const float x0 = x - ctr.x;
- const float y0 = y - ctr.y;
- const float tmp = x0 * cos - y0 * sin + ctr.x;
- y = x0 * sin + y0 * cos + ctr.y;
- x = tmp;
+ /** Rotates this vector around the Z-axis in place, returns *this */
+ constexpr_cxx26 Vector3F& rotateZ(const value_type radians) noexcept {
+ return rotateZ(std::sin(radians), std::cos(radians));
}
- constexpr void rotate(const float sin, const float cos, const Vec3f& ctr) noexcept {
- // FIXME z, consider using a matrix or quaternions
- const float x0 = x - ctr.x;
- const float y0 = y - ctr.y;
- const float tmp = x0 * cos - y0 * sin + ctr.x;
- y = x0 * sin + y0 * cos + ctr.y;
- x = tmp;
+
+ /** Rotates this vector in place, returns *this */
+ constexpr Vector3F& rotateZ(const value_type sin, const value_type cos) noexcept {
+ const value_type x0 = x;
+ x = x0 * cos - y * sin;
+ y = x0 * sin + y * cos;
+ return *this;
}
std::string toString() const noexcept { return std::to_string(x)+"/"+std::to_string(y)+"/"+std::to_string(z); }
@@ -172,28 +191,28 @@ namespace jau::math {
/**
* Return the squared length of a vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i>
*/
- constexpr float length_sq() const noexcept {
+ constexpr value_type length_sq() const noexcept {
return x*x + y*y + z*z;
}
/**
* Return the length of a vector, a.k.a the <i>norm</i> or <i>magnitude</i>
*/
- constexpr float length() const noexcept {
+ constexpr value_type length() const noexcept {
return std::sqrt(length_sq());
}
/**
* Normalize this vector in place
*/
- constexpr Vec3f& normalize() noexcept {
- const float lengthSq = length_sq();
+ constexpr Vector3F& normalize() noexcept {
+ const value_type lengthSq = length_sq();
if ( jau::is_zero( lengthSq ) ) {
- x = 0.0f;
- y = 0.0f;
- z = 0.0f;
+ x = zero;
+ y = zero;
+ z = zero;
} else {
- const float invSqr = 1.0f / std::sqrt(lengthSq);
+ const value_type invSqr = one / std::sqrt(lengthSq);
x *= invSqr;
y *= invSqr;
z *= invSqr;
@@ -208,25 +227,25 @@ namespace jau::math {
* distances, thus avoiding an expensive square root operation.
* </p>
*/
- constexpr float dist_sq(const Vec3f& o) const noexcept {
- const float dx = x - o.x;
- const float dy = y - o.y;
- const float dz = z - o.z;
+ constexpr value_type dist_sq(const Vector3F& o) const noexcept {
+ const value_type dx = x - o.x;
+ const value_type dy = y - o.y;
+ const value_type dz = z - o.z;
return dx*dx + dy*dy + dz*dz;
}
/**
* Return the distance between this vector and the given one.
*/
- constexpr float dist(const Vec3f& o) const noexcept {
+ constexpr value_type dist(const Vector3F& o) const noexcept {
return std::sqrt(dist_sq(o));
}
/**
* Return the dot product of this vector and the given one
- * @return the dot product as float
+ * @return the dot product as value_type
*/
- constexpr float dot(const Vec3f& o) const noexcept {
+ constexpr value_type dot(const Vector3F& o) const noexcept {
return x*o.x + y*o.y + z*o.z;
}
@@ -234,8 +253,8 @@ namespace jau::math {
* cross product this x b
* @return new resulting vector
*/
- constexpr Vec3f cross(const Vec3f& b) const noexcept {
- return Vec3f( y * b.z - z * b.y,
+ constexpr Vector3F cross(const Vector3F& b) const noexcept {
+ return Vector3F( y * b.z - z * b.y,
z * b.x - x * b.z,
x * b.y - y * b.y);
}
@@ -244,7 +263,7 @@ namespace jau::math {
* cross product this = a x b, with a, b different from this
* @return this for chaining
*/
- constexpr Vec3f& cross(const Vec3f& a, const Vec3f& b) noexcept {
+ constexpr Vector3F& cross(const Vector3F& a, const Vector3F& b) noexcept {
x = a.y * b.z - a.z * b.y;
y = a.z * b.x - a.x * b.z;
z = a.x * b.y - a.y * b.y;
@@ -256,7 +275,7 @@ namespace jau::math {
* @param vec1 vector 1
* @param vec2 vector 2
*/
- constexpr float cos_angle(const Vec3f& o) const noexcept {
+ constexpr value_type cos_angle(const Vector3F& o) const noexcept {
return dot(o) / ( length() * o.length() ) ;
}
@@ -265,44 +284,74 @@ namespace jau::math {
* @param vec1 vector 1
* @param vec2 vector 2
*/
- float angle(const Vec3f& o) const noexcept {
+ constexpr_cxx26 value_type angle(const Vector3F& o) const noexcept {
return std::acos( cos_angle(o) );
}
- bool intersects(const Vec3f& o) const noexcept {
- const float eps = std::numeric_limits<float>::epsilon();
+ constexpr_cxx23 bool intersects(const Vector3F& o) const noexcept {
+ const value_type eps = std::numeric_limits<value_type>::epsilon();
if( std::abs(x-o.x) >= eps || std::abs(y-o.y) >= eps || std::abs(z-o.z) >= eps ) {
return false;
}
return true;
}
};
- typedef Vec3f Point3f;
- constexpr Vec3f operator+(const Vec3f& lhs, const Vec3f& rhs ) noexcept {
- return Vec3f(lhs) += rhs;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector3F<T> operator+(const Vector3F<T>& lhs, const Vector3F<T>& rhs ) noexcept {
+ // Returning a Vector3 object from the returned reference of operator+=()
+ // may hinder copy-elision or "named return value optimization" (NRVO).
+ // return Vector3<T>(lhs) += rhs;
+
+ // Returning named object allows copy-elision (NRVO),
+ // only one object is created 'on target'.
+ Vector3F<T> r(lhs); r += rhs; return r;
}
- constexpr Vec3f operator-(const Vec3f& lhs, const Vec3f& rhs ) noexcept {
- return Vec3f(lhs) -= rhs;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector3F<T> operator-(const Vector3F<T>& lhs, const Vector3F<T>& rhs ) noexcept {
+ Vector3F<T> r(lhs); r -= rhs; return r;
}
- constexpr Vec3f operator*(const Vec3f& lhs, const float s ) noexcept {
- return Vec3f(lhs) *= s;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector3F<T> operator*(const Vector3F<T>& lhs, const T s ) noexcept {
+ Vector3F<T> r(lhs); r *= s; return r;
}
- constexpr Vec3f operator*(const float s, const Vec3f& rhs) noexcept {
- return Vec3f(rhs) *= s;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector3F<T> operator*(const T s, const Vector3F<T>& rhs) noexcept {
+ Vector3F<T> r(rhs); r *= s; return r;
}
- constexpr Vec3f operator/(const Vec3f& lhs, const float s ) noexcept {
- return Vec3f(lhs) /= s;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector3F<T> operator/(const Vector3F<T>& lhs, const T s ) noexcept {
+ Vector3F<T> r(lhs); r /= s; return r;
}
- std::ostream& operator<<(std::ostream& out, const Vec3f& v) noexcept {
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ std::ostream& operator<<(std::ostream& out, const Vector3F<T>& v) noexcept {
return out << v.toString();
}
+ typedef Vector3F<float> Vec3f;
+ static_assert(alignof(float) == alignof(Vec3f));
+
+ /**
+ * Point3F alias of Vector3F
+ */
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ using Point3F = Vector3F<T>;
+
+ typedef Point3F<float> Point3f;
+ static_assert(alignof(float) == alignof(Point3f));
+
/**
* Simple compound denoting a ray.
* <p>
@@ -314,20 +363,27 @@ namespace jau::math {
* </pre>
* </p>
*/
- struct Ray3f {
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ struct alignas(T) Ray3F {
/** Origin of Ray. */
- Point3f orig;
+ Point3F<T> orig;
/** Normalized direction vector of ray. */
- Vec3f dir;
+ Vector3F<T> dir;
std::string toString() const noexcept { return "Ray[orig "+orig.toString()+", dir "+dir.toString() +"]"; }
};
- std::ostream& operator<<(std::ostream& out, const Ray3f& v) noexcept {
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ std::ostream& operator<<(std::ostream& out, const Ray3F<T>& v) noexcept {
return out << v.toString();
}
+ typedef Ray3F<float> Ray3f;
+ static_assert(alignof(float) == alignof(Ray3f));
+
/**@}*/
} // namespace jau::math
diff --git a/include/jau/math/vec4f.hpp b/include/jau/math/vec4f.hpp
index ba23056..dc936b3 100644
--- a/include/jau/math/vec4f.hpp
+++ b/include/jau/math/vec4f.hpp
@@ -27,8 +27,10 @@
#include <cmath>
#include <cstdarg>
#include <cstdint>
+#include <cassert>
#include <limits>
#include <string>
+#include <initializer_list>
#include <iostream>
#include <jau/float_math.hpp>
@@ -42,38 +44,64 @@ namespace jau::math {
*/
/**
- * 4D vector using four float components.
+ * 4D vector using four value_type components.
*/
- class Vec4f {
+ template<typename Value_type,
+ std::enable_if_t<std::is_floating_point_v<Value_type>, bool> = true>
+ class alignas(Value_type) Vector4F {
public:
- float x;
- float y;
- float z;
- float w;
+ typedef Value_type value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef value_type* iterator;
+ typedef const value_type* const_iterator;
- constexpr Vec4f() noexcept
- : x(0), y(0), z(0), w(0) {}
+ typedef Vector3F<value_type, std::is_floating_point_v<Value_type>> Vec3;
- constexpr Vec4f(const float x_, const float y_, const float z_, const float w_) noexcept
+ constexpr static const value_type zero = value_type(0);
+ constexpr static const value_type one = value_type(1);
+
+ value_type x;
+ value_type y;
+ value_type z;
+ value_type w;
+
+ constexpr Vector4F() noexcept
+ : x(zero), y(zero), z(zero), w(zero) {}
+
+ constexpr Vector4F(const value_type v) noexcept
+ : x(v), y(v), z(v), w(v) {}
+
+ constexpr Vector4F(const value_type x_, const value_type y_, const value_type z_, const value_type w_) noexcept
: x(x_), y(y_), z(z_), w(w_) {}
- constexpr Vec4f(const Vec4f& o) noexcept = default;
- constexpr Vec4f(Vec4f&& o) noexcept = default;
- constexpr Vec4f& operator=(const Vec4f&) noexcept = default;
- constexpr Vec4f& operator=(Vec4f&&) noexcept = default;
+ constexpr Vector4F(const_iterator v) noexcept
+ : x(v[0]), y(v[1]), z(v[2]), w(v[3]) {}
+
+ constexpr Vector4F(std::initializer_list<value_type> v) noexcept
+ : x(v[0]), y(v[1]), z(v[2]), w(v[3]) {}
- /** Returns read-only component w/o boundary check */
- float operator[](size_t i) const noexcept {
- return reinterpret_cast<const float*>(this)[i];
+ constexpr Vector4F(const Vector4F& o) noexcept = default;
+ constexpr Vector4F(Vector4F&& o) noexcept = default;
+ constexpr Vector4F& operator=(const Vector4F&) noexcept = default;
+ constexpr Vector4F& operator=(Vector4F&&) noexcept = default;
+
+ /** Returns read-only component */
+ constexpr value_type operator[](size_t i) const noexcept {
+ assert(i < 4);
+ return (&x)[i];
}
- /** Returns writeable reference to component w/o boundary check */
- float& operator[](size_t i) noexcept {
- return reinterpret_cast<float*>(this)[i];
+ /** Returns writeable reference to component */
+ constexpr reference operator[](size_t i) noexcept {
+ assert(i < 4);
+ return (&x)[i];
}
/** xyzw = this, returns xyzw. */
- float* get(float xyzw[/*4*/]) const noexcept {
+ constexpr iterator get(iterator xyzw) const noexcept {
xyzw[0] = x;
xyzw[1] = y;
xyzw[2] = z;
@@ -81,7 +109,15 @@ namespace jau::math {
return xyzw;
}
- constexpr bool operator==(const Vec4f& rhs ) const noexcept {
+ /** out = { this.x, this.y, this.z } dropping w, returns out. */
+ constexpr Vec3& getVec3(Vec3& out) const noexcept {
+ out.x = x;
+ out.y = y;
+ out.z = z;
+ return out;
+ }
+
+ constexpr bool operator==(const Vector4F& rhs ) const noexcept {
if( this == &rhs ) {
return true;
}
@@ -94,27 +130,26 @@ namespace jau::math {
} */
/** this = { o, w }, returns this. */
- constexpr Vec4f& set(const Vec3f& o, const float w_) noexcept {
- x = o.x;
- y = o.y;
- z = o.z;
- w = w_;
- return *this;
- }
+ constexpr Vector4F& set(const Vec3f& o, const value_type w_) noexcept
+ { x = o.x; y = o.y; z = o.z; w = w_; return *this; }
- constexpr Vec4f& set(const float vx, const float vy, const float vz, const float vw) noexcept
+ constexpr Vector4F& set(const value_type vx, const value_type vy, const value_type vz, const value_type vw) noexcept
{ x=vx; y=vy; z=vz; w=vw; return *this; }
- constexpr void add(const float dx, const float dy, const float dz, const float dw) noexcept {
+ /** this = xyzw, returns this. */
+ constexpr Vector4F& set(const_iterator xyzw) noexcept
+ { x=xyzw[0]; y=xyzw[1]; z=xyzw[2]; z=xyzw[3]; return *this; }
+
+ constexpr void add(const value_type dx, const value_type dy, const value_type dz, const value_type dw) noexcept {
x+=dx; y+=dy; z+=dz; w+=dw;
}
- constexpr Vec4f& operator+=(const Vec4f& rhs ) noexcept {
+ constexpr Vector4F& operator+=(const Vector4F& rhs ) noexcept {
x+=rhs.x; y+=rhs.y; z+=rhs.z; w+=rhs.w;
return *this;
}
- constexpr Vec4f& operator-=(const Vec4f& rhs ) noexcept {
+ constexpr Vector4F& operator-=(const Vector4F& rhs ) noexcept {
x-=rhs.x; y-=rhs.y; z-=rhs.z; w-=rhs.w;
return *this;
}
@@ -124,7 +159,7 @@ namespace jau::math {
* @param s scale factor
* @return this instance
*/
- constexpr Vec4f& operator*=(const float s ) noexcept {
+ constexpr Vector4F& operator*=(const value_type s ) noexcept {
x*=s; y*=s; z*=s; w*=s;
return *this;
}
@@ -134,7 +169,7 @@ namespace jau::math {
* @param s scale factor
* @return this instance
*/
- constexpr Vec4f& operator/=(const float s ) noexcept {
+ constexpr Vector4F& operator/=(const value_type s ) noexcept {
x/=s; y/=s; z/=s; w/=s;
return *this;
}
@@ -148,29 +183,29 @@ namespace jau::math {
/**
* Return the squared length of a vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i>
*/
- constexpr float length_sq() const noexcept {
+ constexpr value_type length_sq() const noexcept {
return x*x + y*y + z*z + w*w;
}
/**
* Return the length of a vector, a.k.a the <i>norm</i> or <i>magnitude</i>
*/
- constexpr float length() const noexcept {
+ constexpr value_type length() const noexcept {
return std::sqrt(length_sq());
}
/**
* Normalize this vector in place
*/
- constexpr Vec4f& normalize() noexcept {
- const float lengthSq = length_sq();
+ constexpr Vector4F& normalize() noexcept {
+ const value_type lengthSq = length_sq();
if ( jau::is_zero( lengthSq ) ) {
- x = 0.0f;
- y = 0.0f;
- z = 0.0f;
- w = 0.0f;
+ x = zero;
+ y = zero;
+ z = zero;
+ w = zero;
} else {
- const float invSqr = 1.0f / std::sqrt(lengthSq);
+ const value_type invSqr = one / std::sqrt(lengthSq);
x *= invSqr;
y *= invSqr;
z *= invSqr;
@@ -186,23 +221,23 @@ namespace jau::math {
* distances, thus avoiding an expensive square root operation.
* </p>
*/
- constexpr float dist_sq(const Vec4f& o) const noexcept {
- const float dx = x - o.x;
- const float dy = y - o.y;
- const float dz = z - o.z;
- const float dw = w - o.w;
+ constexpr value_type dist_sq(const Vector4F& o) const noexcept {
+ const value_type dx = x - o.x;
+ const value_type dy = y - o.y;
+ const value_type dz = z - o.z;
+ const value_type dw = w - o.w;
return dx*dx + dy*dy + dz*dz + dw*dw;
}
/**
* Return the distance between this vector and the given one.
*/
- constexpr float dist(const Vec4f& o) const noexcept {
+ constexpr value_type dist(const Vector4F& o) const noexcept {
return std::sqrt(dist_sq(o));
}
- bool intersects(const Vec4f& o) const noexcept {
- const float eps = std::numeric_limits<float>::epsilon();
+ constexpr_cxx23 bool intersects(const Vector4F& o) const noexcept {
+ const value_type eps = std::numeric_limits<value_type>::epsilon();
if( std::abs(x-o.x) >= eps || std::abs(y-o.y) >= eps ||
std::abs(z-o.z) >= eps || std::abs(w-o.w) >= eps ) {
return false;
@@ -210,32 +245,69 @@ namespace jau::math {
return true;
}
};
- typedef Vec4f Point4f;
- constexpr Vec4f operator+(const Vec4f& lhs, const Vec4f& rhs ) noexcept {
- return Vec4f(lhs) += rhs;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector4F<T> operator+(const Vector4F<T>& lhs, const Vector4F<T>& rhs ) noexcept {
+ // Returning a Vector4 object from the returned reference of operator+=()
+ // may hinder copy-elision or "named return value optimization" (NRVO).
+ // return Vector4<T>(lhs) += rhs;
+
+ // Returning named object allows copy-elision (NRVO),
+ // only one object is created 'on target'.
+ Vector4F<T> r(lhs); r += rhs; return r;
}
- constexpr Vec4f operator-(const Vec4f& lhs, const Vec4f& rhs ) noexcept {
- return Vec4f(lhs) -= rhs;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector4F<T> operator-(const Vector4F<T>& lhs, const Vector4F<T>& rhs ) noexcept {
+ Vector4F<T> r(lhs); r -= rhs; return r;
}
- constexpr Vec4f operator*(const Vec4f& lhs, const float s ) noexcept {
- return Vec4f(lhs) *= s;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector4F<T> operator*(const Vector4F<T>& lhs, const T s ) noexcept {
+ Vector4F<T> r(lhs); r *= s; return r;
}
- constexpr Vec4f operator*(const float s, const Vec4f& rhs) noexcept {
- return Vec4f(rhs) *= s;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector4F<T> operator*(const T s, const Vector4F<T>& rhs) noexcept {
+ Vector4F<T> r(rhs); r *= s; return r;
}
- constexpr Vec4f operator/(const Vec4f& lhs, const float s ) noexcept {
- return Vec4f(lhs) /= s;
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector4F<T> operator/(const Vector4F<T>& lhs, const T s ) noexcept {
+ Vector4F<T> r(lhs); r /= s; return r;
}
- std::ostream& operator<<(std::ostream& out, const Vec4f& v) noexcept {
+ /** out = { this.x, this.y, this.z } dropping w, returns out. */
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ constexpr Vector3F<T> to_vec3(const Vector4F<T>& v) noexcept {
+ Vector3F<T> r; v.getVec3(r); return r;
+ }
+
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ std::ostream& operator<<(std::ostream& out, const Vector4F<T>& v) noexcept {
return out << v.toString();
}
+ typedef Vector4F<float> Vec4f;
+ static_assert(alignof(float) == alignof(Vec4f));
+
+ /**
+ * Point4F alias of Vector4F
+ */
+ template<typename T,
+ std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
+ using Point4F = Vector4F<T>;
+
+ typedef Point4F<float> Point4f;
+ static_assert(alignof(float) == alignof(Point4f));
+
/**@}*/
} // namespace jau::math
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 29f20a1..6eb11f3 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -14,7 +14,6 @@ set (jaulib_LIB_SRCS
cpuid.cpp
debug.cpp
basic_types.cpp
- math.cpp
base_codec.cpp
byte_stream.cpp
eui48.cpp
diff --git a/src/math.cpp b/src/math.cpp
deleted file mode 100644
index 8f26d10..0000000
--- a/src/math.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Author: Sven Gothel <[email protected]>
- * Copyright (c) 2014-2024 Gothel Software e.K.
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <cstdint>
-#include <cinttypes>
-#include <cstring>
-
-#include <ctime>
-
-#include <algorithm>
-
-#include <jau/debug.hpp>
-#include <jau/string_util.hpp>
-#include <jau/math/vec3f.hpp>
-#include <jau/math/mat4f.hpp>
-#include <jau/math/aabbox3f.hpp>
-#include <jau/math/quaternion.hpp>
-#include <jau/math/geom/frustum.hpp>
-#include <jau/math/geom/plane/affine_transform.hpp>
-
-jau::math::Vec3f& jau::math::Vec3f::set(const Vec4f& o) noexcept {
- x = o.x;
- y = o.y;
- z = o.z;
- return *this;
-}
-
-jau::math::Mat4f& jau::math::Mat4f::setToRotation(const jau::math::Quaternion& q) noexcept {
- // pre-multiply scaled-reciprocal-magnitude to reduce multiplications
- const float norm = q.magnitudeSquared();
- if ( jau::is_zero(norm) ) {
- // identity matrix -> srecip = 0f
- loadIdentity();
- return *this;
- }
- float srecip;
- if ( jau::equals(1.0f, norm) ) {
- srecip = 2.0f;
- } else {
- srecip = 2.0f / norm;
- }
-
- const float x = q.x();
- const float y = q.y();
- const float z = q.z();
- const float w = q.w();
-
- const float xs = srecip * x;
- const float ys = srecip * y;
- const float zs = srecip * z;
-
- const float xx = x * xs;
- const float xy = x * ys;
- const float xz = x * zs;
- const float xw = xs * w;
- const float yy = y * ys;
- const float yz = y * zs;
- const float yw = ys * w;
- const float zz = z * zs;
- const float zw = zs * w;
-
- m00 = 1.0f - ( yy + zz );
- m01 = ( xy - zw );
- m02 = ( xz + yw );
- m03 = 0.0f;
-
- m10 = ( xy + zw );
- m11 = 1.0f - ( xx + zz );
- m12 = ( yz - xw );
- m13 = 0.0f;
-
- m20 = ( xz - yw );
- m21 = ( yz + xw );
- m22 = 1.0f - ( xx + yy );
- m23 = 0.0f;
-
- m30 = m31 = m32 = 0.0f;
- m33 = 1.0f;
- return *this;
-}
-
-jau::math::Quaternion& jau::math::Mat4f::getRotation(jau::math::Quaternion& res) const noexcept {
- return res.setFromMat3(*this);
-}
-
-jau::math::geom::Frustum& jau::math::Mat4f::getFrustum(jau::math::geom::Frustum& frustum) noexcept {
- return frustum.setFromMat4(*this);
-}
-
-std::string& jau::row_to_string(std::string& sb, const std::string& f,
- const float a[],
- const jau::nsize_t rows, const jau::nsize_t columns,
- const bool rowMajorOrder, const jau::nsize_t row) noexcept {
- if(rowMajorOrder) {
- for(jau::nsize_t c=0; c<columns; ++c) {
- sb.append( jau::format_string(f.c_str(), a[ row*columns + c ] ) );
- sb.append(", ");
- }
- } else {
- for(jau::nsize_t r=0; r<columns; ++r) {
- sb.append( jau::format_string(f.c_str(), a[ row + r*rows ] ) );
- sb.append(", ");
- }
- }
- return sb;
-}
-
-std::string& jau::mat_to_string(std::string& sb, const std::string& rowPrefix, const std::string& f,
- const float a[], const jau::nsize_t rows, const jau::nsize_t columns,
- const bool rowMajorOrder) noexcept {
- sb.append(rowPrefix).append("{ ");
- for(jau::nsize_t i=0; i<rows; ++i) {
- if( 0 < i ) {
- sb.append(rowPrefix).append(" ");
- }
- row_to_string(sb, f, a, rows, columns, rowMajorOrder, i);
- sb.append("\n");
- }
- sb.append(rowPrefix).append("}").append("\n");
- return sb;
-}
-
-std::string jau::math::Mat4f::toString(const std::string& rowPrefix, const std::string& f) const noexcept {
- std::string sb;
- float tmp[16];
- get(tmp);
- return jau::mat_to_string(sb, rowPrefix, f, tmp, 4, 4, false /* rowMajorOrder */); // creates a copy-out!
-}
-
-jau::math::AABBox3f& jau::math::AABBox3f::resize(const jau::math::AABBox3f& newBox, const jau::math::geom::plane::AffineTransform& t, Vec3f& tmpV3) noexcept {
- /** test low */
- {
- const jau::math::Vec3f& newBL = t.transform(newBox.low(), tmpV3);
- if (newBL.x < m_lo.x) {
- m_lo.x = newBL.x;
- }
- if (newBL.y < m_lo.y) {
- m_lo.y = newBL.y;
- }
- if (newBL.z < m_lo.z) {
- m_lo.z = newBL.z;
- }
- }
-
- /** test high */
- {
- const jau::math::Vec3f& newTR = t.transform(newBox.high(), tmpV3);
- if (newTR.x > m_hi.x) {
- m_hi.x = newTR.x;
- }
- if (newTR.y > m_hi.y) {
- m_hi.y = newTR.y;
- }
- if (newTR.z > m_hi.z) {
- m_hi.z = newTR.z;
- }
- }
- computeCenter();
- return *this;
-}
diff --git a/test/test_math_quaternion.cpp b/test/test_math_quaternion.cpp
index f9410f7..1a57305 100644
--- a/test/test_math_quaternion.cpp
+++ b/test/test_math_quaternion.cpp
@@ -48,7 +48,7 @@ static const float HALF_PI = (float)M_PI_2;
static const float QUARTER_PI = (float)M_PI_4;
static const float EPSILON = std::numeric_limits<float>::epsilon();
-static const Quaternion QUAT_IDENT = Quaternion(0, 0, 0, 1);
+static const Quat4f QUAT_IDENT = Quat4f(0, 0, 0, 1);
static const Vec3f ZERO = Vec3f ( 0, 0, 0 );
static const Vec3f ONE = Vec3f ( 1, 1, 1 );
@@ -68,14 +68,14 @@ static const Vec4f ONE_v4 = Vec4f ( 1, 1, 1, 0 );
//
TEST_CASE( "Test 01 Normalize", "[quaternion][linear_algebra][math]" ) {
- const Quaternion quat(0, 1, 2, 3);
- const Quaternion quat2 = Quaternion(quat).normalize();
+ const Quat4f quat(0, 1, 2, 3);
+ const Quat4f quat2 = Quat4f(quat).normalize();
// Assert.assertTrue(Math.abs(1 - quat2.magnitude()) <= MACH_EPSILON);
REQUIRE( true == jau::equals( 0.0f, std::abs( 1 - quat2.magnitude() ) ) ) ;
}
TEST_CASE( "Test 02 Rotate Zero Vector", "[quaternion][linear_algebra][math]" ) {
- Quaternion quat;
+ Quat4f quat;
const Vec3f rotVec0 = quat.rotateVector(ZERO);
REQUIRE( ZERO == rotVec0 );
}
@@ -83,22 +83,22 @@ TEST_CASE( "Test 02 Rotate Zero Vector", "[quaternion][linear_algebra][math]" )
TEST_CASE( "Test 03 Invert and Conugate", "[quaternion][linear_algebra][math]" ) {
// inversion check
{
- const Quaternion quat0(0, 1, 2, 3);
- Quaternion quat0Inv = Quaternion(quat0).invert();
+ const Quat4f quat0(0, 1, 2, 3);
+ Quat4f quat0Inv = Quat4f(quat0).invert();
REQUIRE( quat0 == quat0Inv.invert() );
}
// conjugate check
{
- const Quaternion quat0(-1, -2, -3, 4);
- const Quaternion quat0Conj = Quaternion( 1, 2, 3, 4).conjugate();
+ const Quat4f quat0(-1, -2, -3, 4);
+ const Quat4f quat0Conj = Quat4f( 1, 2, 3, 4).conjugate();
REQUIRE( quat0 == quat0Conj );
}
}
TEST_CASE( "Test 04 Dot", "[quaternion][linear_algebra][math]" ) {
- const Quaternion quat(7, 2, 5, -1);
+ const Quat4f quat(7, 2, 5, -1);
REQUIRE( 35.0f == quat.dot(3, 1, 2, -2));
- REQUIRE( -11.0f == quat.dot(Quaternion(-1, 1, -1, 1)));
+ REQUIRE( -11.0f == quat.dot(Quat4f(-1, 1, -1, 1)));
}
//
@@ -106,8 +106,8 @@ TEST_CASE( "Test 04 Dot", "[quaternion][linear_algebra][math]" ) {
//
TEST_CASE( "Test 10 Angle Axis", "[quaternion][linear_algebra][math]" ) {
- Quaternion quat1 = Quaternion().setFromAngleAxis(HALF_PI, Vec3f ( 2, 0, 0 ));
- Quaternion quat2 = Quaternion().setFromAngleNormalAxis(HALF_PI, Vec3f ( 1, 0, 0 ) );
+ Quat4f quat1 = Quat4f().setFromAngleAxis(HALF_PI, Vec3f ( 2, 0, 0 ));
+ Quat4f quat2 = Quat4f().setFromAngleNormalAxis(HALF_PI, Vec3f ( 1, 0, 0 ) );
REQUIRE( quat2 == quat1 );
REQUIRE( true == jau::equals(0.0f, 1 - quat2.magnitude()));
@@ -136,10 +136,10 @@ TEST_CASE( "Test 10 Angle Axis", "[quaternion][linear_algebra][math]" ) {
TEST_CASE( "Test 11 From Vec to Vec", "[quaternion][linear_algebra][math]" ) {
Vec3f vecOut;
- Quaternion quat;
+ Quat4f quat;
quat.setFromVectors(UNIT_Z, UNIT_X);
- Quaternion quat2;
+ Quat4f quat2;
quat2.setFromNormalVectors(UNIT_Z, UNIT_X);
REQUIRE( quat == quat2 );
@@ -148,19 +148,19 @@ TEST_CASE( "Test 11 From Vec to Vec", "[quaternion][linear_algebra][math]" ) {
quat.setFromVectors(UNIT_Z, UNIT_Z_NEG);
vecOut = quat.rotateVector(UNIT_Z);
- REQUIRE_THAT( std::abs( UNIT_Z_NEG.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ REQUIRE_THAT( std::abs( UNIT_Z_NEG.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
quat.setFromVectors(UNIT_X, UNIT_X_NEG);
vecOut = quat.rotateVector(UNIT_X);
- REQUIRE_THAT( std::abs( UNIT_X_NEG.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ REQUIRE_THAT( std::abs( UNIT_X_NEG.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
quat.setFromVectors(UNIT_Y, UNIT_Y_NEG);
vecOut = quat.rotateVector(UNIT_Y);
- REQUIRE_THAT( std::abs( UNIT_Y_NEG.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ REQUIRE_THAT( std::abs( UNIT_Y_NEG.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
quat.setFromVectors(ONE, ONE_NEG);
vecOut = quat.rotateVector(ONE);
- REQUIRE_THAT( std::abs( ONE_NEG.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ REQUIRE_THAT( std::abs( ONE_NEG.dist(vecOut) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
quat.setFromVectors(ZERO, ZERO);
REQUIRE( QUAT_IDENT == quat );
@@ -169,7 +169,7 @@ TEST_CASE( "Test 11 From Vec to Vec", "[quaternion][linear_algebra][math]" ) {
TEST_CASE( "Test 12 From and to Euler Angles", "[quaternion][linear_algebra][math]" ) {
// Y.Z.X -> X.Y.Z
- Quaternion quat;
+ Quat4f quat;
Vec3f angles0Exp( 0, HALF_PI, 0 );
quat.setFromEuler(angles0Exp);
REQUIRE_THAT( quat.magnitude(), Catch::Matchers::WithinAbs(1.0f, EPSILON) );
@@ -177,7 +177,7 @@ TEST_CASE( "Test 12 From and to Euler Angles", "[quaternion][linear_algebra][mat
Vec3f angles0Has = quat.toEuler();
REQUIRE( angles0Exp == angles0Has );
- Quaternion quat2;
+ Quat4f quat2;
quat2.setFromEuler(angles0Has);
REQUIRE( quat == quat2 );
@@ -207,7 +207,7 @@ TEST_CASE( "Test 12 From and to Euler Angles", "[quaternion][linear_algebra][mat
}
TEST_CASE( "Test 13 From Euler Angles and Rotate Vec", "[quaternion][linear_algebra][math]" ) {
- Quaternion quat;
+ Quat4f quat;
quat.setFromEuler(0, HALF_PI, 0); // 90 degrees y-axis
REQUIRE_THAT( quat.magnitude(), Catch::Matchers::WithinAbs(1.0f, EPSILON) );
@@ -231,14 +231,15 @@ TEST_CASE( "Test 14 Matrix", "[matrix][quaternion][linear_algebra][math]" ) {
// Vec4f vecOut4;
Mat4f mat1;
Mat4f mat2;
- Quaternion quat;
+ Quat4f quat1;
+ Quat4f quat2;
//
// IDENTITY CHECK
//
mat1.loadIdentity();
- quat.set(0, 0, 0, 0);
- quat.toMatrix(mat2);
+ quat1.set(0, 0, 0, 0);
+ quat1.toMatrix(mat2);
// mat2 = quat.toMatrix();
REQUIRE( mat1 == mat2 );
@@ -261,11 +262,13 @@ TEST_CASE( "Test 14 Matrix", "[matrix][quaternion][linear_algebra][math]" ) {
}
{
// Validate Matrix via Euler rotation on Quaternion!
- quat.setFromEuler(a, 0, 0);
+ quat1.setFromEuler(a, 0, 0);
{
- quat.toMatrix(mat2);
+ quat1.toMatrix(mat2);
// mat2 = quat.toMatrix();
REQUIRE( mat1 == mat2 );
+ quat2.setFromMat(mat1);
+ REQUIRE( quat1 == quat2 );
float mat2_0[16];
mat2.get(mat2_0);
@@ -274,32 +277,32 @@ TEST_CASE( "Test 14 Matrix", "[matrix][quaternion][linear_algebra][math]" ) {
REQUIRE( mat2 == mat2c );
REQUIRE( mat1 == mat2c );
}
- vecHas = quat.rotateVector(UNIT_Y);
- REQUIRE_THAT( std::abs( UNIT_Z.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ vecHas = quat1.rotateVector(UNIT_Y);
+ REQUIRE_THAT( std::abs( UNIT_Z.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
+ }
+ {
+ quat1.toMatrix(mat1);
+ quat2.setFromMat(mat1);
+ REQUIRE( quat1 == quat2 );
}
- mat1.getRotation(quat);
- quat.setFromMat3(mat1);
- vecHas = quat.rotateVector(UNIT_Y);
- REQUIRE_THAT( std::abs( UNIT_Z.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ vecHas = quat1.rotateVector(UNIT_Y);
+ REQUIRE_THAT( std::abs( UNIT_Z.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
- quat.toMatrix(mat2);
+ quat1.toMatrix(mat2);
REQUIRE( mat1 == mat2 );
- vecHas = quat.rotateVector(ONE_NEG);
+ vecHas = quat1.rotateVector(ONE_NEG);
{
// use Vec3f math
mat2.mulVec3f(ONE_NEG, vecOut3);
- REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
REQUIRE( vecHas == vecOut3);
}
{
// use Vec4f math
+ (mat2 * ONE_NEG_v4).getVec3(vecOut3);
- // mat2.mulVec4f(ONE_NEG_v4, vecOut4);
- // vecOut3.set(vecOut4);
- vecOut3.set( mat2 * ONE_NEG_v4 ); // simpler
-
- REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
REQUIRE( vecHas == vecOut3);
}
@@ -317,24 +320,22 @@ TEST_CASE( "Test 14 Matrix", "[matrix][quaternion][linear_algebra][math]" ) {
}
{
// Validate Matrix via Euler rotation on Quaternion!
- quat.setFromEuler(a, 0, 0);
- quat.toMatrix(mat2);
+ quat1.setFromEuler(a, 0, 0);
+ quat1.toMatrix(mat2);
REQUIRE(mat1 == mat2);
- vecHas = quat.rotateVector(UNIT_Y);
- REQUIRE_THAT( std::abs( UNIT_Y_NEG.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ vecHas = quat1.rotateVector(UNIT_Y);
+ REQUIRE_THAT( std::abs( UNIT_Y_NEG.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
}
- quat.setFromMat3(mat1);
- vecHas = quat.rotateVector(UNIT_Y);
- REQUIRE_THAT( std::abs( UNIT_Y_NEG.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ quat1.setFromMat(mat1);
+ vecHas = quat1.rotateVector(UNIT_Y);
+ REQUIRE_THAT( std::abs( UNIT_Y_NEG.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
- quat.toMatrix(mat2);
+ quat1.toMatrix(mat2);
REQUIRE(mat1 == mat2);
- vecHas = quat.rotateVector(ONE);
- // mat2.mulVec4f(ONE_v4, vecOut4);
- // vecOut3.set(vecOut4);
- vecOut3.set( mat2 * ONE_v4 ); // simpler
- REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ vecHas = quat1.rotateVector(ONE);
+ (mat2 * ONE_v4).getVec3(vecOut3);
+ REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
//
// 180 degrees rotation on Y
@@ -350,25 +351,23 @@ TEST_CASE( "Test 14 Matrix", "[matrix][quaternion][linear_algebra][math]" ) {
}
{
// Validate Matrix via Euler rotation on Quaternion!
- quat.setFromEuler(0, a, 0);
- quat.toMatrix(mat2);
+ quat1.setFromEuler(0, a, 0);
+ quat1.toMatrix(mat2);
REQUIRE(mat1 == mat2);
- vecHas = quat.rotateVector(UNIT_X);
- REQUIRE_THAT( std::abs( UNIT_X_NEG.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ vecHas = quat1.rotateVector(UNIT_X);
+ REQUIRE_THAT( std::abs( UNIT_X_NEG.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
}
- quat.setFromMat3(mat1);
- vecHas = quat.rotateVector(UNIT_X);
- REQUIRE_THAT( std::abs( UNIT_X_NEG.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ quat1.setFromMat(mat1);
+ vecHas = quat1.rotateVector(UNIT_X);
+ REQUIRE_THAT( std::abs( UNIT_X_NEG.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
- quat.toMatrix(mat2);
+ quat1.toMatrix(mat2);
REQUIRE(mat1 == mat2);
- vecHas = quat.rotateVector(ONE_NEG);
- // mat2.mulVec4f(ONE_NEG_v4, vecOut4);
- // vecOut3.set(vecOut4);
- vecOut3.set( mat2 * ONE_NEG_v4 ); // simpler
- REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ vecHas = quat1.rotateVector(ONE_NEG);
+ (mat2 * ONE_NEG_v4).getVec3(vecOut3);
+ REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
//
// 180 degrees rotation on Z
@@ -384,24 +383,23 @@ TEST_CASE( "Test 14 Matrix", "[matrix][quaternion][linear_algebra][math]" ) {
}
{
// Validate Matrix via Euler rotation on Quaternion!
- quat.setFromEuler(0, 0, a);
- quat.toMatrix(mat2);
+ quat1.setFromEuler(0, 0, a);
+ quat1.toMatrix(mat2);
REQUIRE(mat1 == mat2);
- vecHas = quat.rotateVector(UNIT_X);
- REQUIRE_THAT( std::abs( UNIT_X_NEG.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ vecHas = quat1.rotateVector(UNIT_X);
+ REQUIRE_THAT( std::abs( UNIT_X_NEG.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
}
- quat.setFromMat3(mat1);
- vecHas = quat.rotateVector(UNIT_X);
- REQUIRE_THAT( std::abs( UNIT_X_NEG.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ quat1.setFromMat(mat1);
+ vecHas = quat1.rotateVector(UNIT_X);
+ REQUIRE_THAT( std::abs( UNIT_X_NEG.dist(vecHas) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
- quat.toMatrix(mat2);
+ quat1.toMatrix(mat2);
REQUIRE(mat1 == mat2);
- vecHas = quat.rotateVector(ONE);
- // mat2.mulVec4f(ONE_v4, vecOut4);
- // vecOut3.set(vecOut4);
- vecOut3.set( mat2 * ONE_v4 ); // simpler
- REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quaternion::ALLOWED_DEVIANCE) );
+ vecHas = quat1.rotateVector(ONE);
+ vecOut3 = to_vec3( mat2 * ONE_v4 );
+ // (mat2 * ONE_v4).getVec3(vecOut3);
+ REQUIRE_THAT( std::abs( vecHas.dist(vecOut3) ), Catch::Matchers::WithinAbs(0.0f, Quat4f::allowed_deviation) );
//
// Test Matrix-Columns
diff --git a/test/test_math_vec.cpp b/test/test_math_vec.cpp
index 02f4852..641cae6 100644
--- a/test/test_math_vec.cpp
+++ b/test/test_math_vec.cpp
@@ -28,6 +28,8 @@
#include <jau/test/catch2_ext.hpp>
+#include <jau/int_math.hpp>
+#include <jau/float_math.hpp>
#include <jau/math/vec2f.hpp>
#include <jau/math/vec2i.hpp>
#include <jau/math/vec3f.hpp>
@@ -39,16 +41,59 @@
#include <jau/math/mat4f.hpp>
#include <jau/math/recti.hpp>
#include <jau/math/math_error.hpp>
+#include <jau/math/util/sstack.hpp>
using namespace jau;
using namespace jau::math;
+using namespace jau::math::util;
+
+template<class T, class U>
+void test_vec(std::ostream& out, const char* prefix) {
+ out << "Test: " << std::string(prefix) << ", sizeof(U) = " << sizeof(U) << std::endl;
+ T a( U(1) ), b( U(2) );
+ out << "- a: " << a << ", len = " << std::to_string(a.length()) << ", len(normal(a)) = " << std::to_string(T(a).normalize().length()) << std::endl;
+ out << "- b: " << b << ", len = " << std::to_string(b.length()) << ", len(normal(b)) = " << std::to_string(T(b).normalize().length()) << std::endl;
+
+ REQUIRE( T(U(2)) == a * U(2) );
+ REQUIRE( T(U(1)) == b / U(2) );
+
+ a.normalize();
+ b.normalize();
+
+ REQUIRE( jau::equals(U(1), a.length() ) );
+ REQUIRE( jau::equals(U(1), b.length() ) );
+}
+
+template<class T>
+void dump_align_props(std::ostream& out, const char* prefix) {
+ out << std::string(prefix) << "{size " << std::to_string( sizeof(T) ) << ", alignment " << std::to_string( alignof(T) )
+ << " }" << std::endl;
+}
TEST_CASE( "Math Vec Test 00", "[vec][linear_algebra][math]" ) {
+ static_assert(alignof(int) == alignof(Vec2i));
+ static_assert(alignof(float) == alignof(Vec2f));
+ static_assert(alignof(float) == alignof(Vec3f));
+ static_assert(alignof(float) == alignof(Vec4f));
+ static_assert(alignof(float) == alignof(Mat4f));
+
+ dump_align_props<int>(std::cout, "int");
+ dump_align_props<float>(std::cout, "float");
+ dump_align_props<Vec2i>(std::cout, "Vec2i");
+ dump_align_props<Vec2f>(std::cout, "Vec2f");
+ dump_align_props<Vec3f>(std::cout, "Vec3f");
+ dump_align_props<Vec4f>(std::cout, "Vec4f");
+ dump_align_props<Mat4f>(std::cout, "Mat4f");
+
std::cout << "A v2 " << Vec2f(1, 2) << std::endl;
std::cout << "A v3 " << Vec3f(1, 2, 3) << std::endl;
std::cout << "A v4 " << Vec4f(1, 2, 3, 4) << std::endl;
{
- const float mf[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f };
+ const float mf[] = { 1.0f, 2.0f, 3.0f, 4.0f, // column 0
+ 5.0f, 6.0f, 7.0f, 8.0f, // column 1
+ 9.0f, 10.0f, 11.0f, 12.0f, // column 2
+ 13.0f, 14.0f, 15.0f, 16.0f // column 3
+ };
std::cout << "A mat4 " << Mat4f(mf) << std::endl;
}
@@ -56,6 +101,18 @@ TEST_CASE( "Math Vec Test 00", "[vec][linear_algebra][math]" ) {
REQUIRE( Vec3f() == Vec3f(0, 0, 0) );
REQUIRE( 0.0f == Vec2f().length() );
REQUIRE( 0.0f == Vec3f().length() );
+
+ test_vec<Vector2I<int>, int>(std::cout, "Vector2I<int>");
+ test_vec<Vector2I<long>, long>(std::cout, "Vector2I<long>");
+
+ test_vec<Vector2F<float>, float>(std::cout, "Vector2F<float>");
+ test_vec<Vector2F<double>, double>(std::cout, "Vector2F<double>");
+
+ test_vec<Vector3F<float>, float>(std::cout, "Vector3F<float>");
+ test_vec<Vector3F<double>, double>(std::cout, "Vector3F<double>");
+
+ test_vec<Vector4F<float>, float>(std::cout, "Vector4F<float>");
+ test_vec<Vector4F<double>, double>(std::cout, "Vector4F<double>");
}
TEST_CASE( "Math Vec Normalize Test 01", "[vec][linear_algebra][math]" ) {