- Quaternions

- Started Tests for Quaternions
 - Fixed some style issues with constructors
This commit is contained in:
2025-07-16 23:16:54 -04:00
parent f1552b89b1
commit c72d1afe32
17 changed files with 690 additions and 133 deletions

View File

@@ -129,6 +129,7 @@ add_library(fennec STATIC
include/fennec/fproc/filesystem/file.h source/fproc/filesystem/file.cpp
include/fennec/fproc/filesystem/path.h source/fproc/filesystem/path.cpp
include/fennec/math/ext/transform.h
include/fennec/math/ext/quaternion.h
)
# add metaprogramming templates as a dependency and also force documentation to be generated when fennec is compiled

View File

@@ -124,7 +124,8 @@ public:
}
constexpr _string(_string&& str) noexcept
: _str(fennec::move(str._str)) { }
: _str(fennec::move(str._str)) {
}
///
/// \brief String Destructor, cleans up the underlying allocation

View File

@@ -66,7 +66,9 @@ public:
///
/// \brief Default Constructor, initializes with nullptr
constexpr wcstring()
: _str(nullptr), _size(0), _const(true) {
: _str(nullptr)
, _size(0)
, _const(true) {
}
///

View File

@@ -124,7 +124,8 @@ public:
}
constexpr _wstring(_wstring&& str) noexcept
: _str(fennec::move(str._str)) { }
: _str(fennec::move(str._str)) {
}
///
/// \brief String Destructor, cleans up the underlying allocation

View File

@@ -0,0 +1,369 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#ifndef FENNEC_MATH_EXT_QUATERNION_H
#define FENNEC_MATH_EXT_QUATERNION_H
#include <fennec/math/trigonometric.h>
#include <fennec/math/vector_base.h>
namespace fennec
{
template<typename ScalarT> struct quaternion;
using quat = quaternion<float>;
using dquat = quaternion<double>;
///
/// \brief Dot Function
/// \param q The Quaternion
/// \returns The square sum of all components.
template<typename genType>
constexpr genType dot(const quaternion<genType>& x, const quaternion<genType>& y) {
return x.w*y.w + x.x*y.x + x.y*y.y + x.z*y.z;
}
///
/// \brief Square Norm Function
/// \param q The Quaternion
/// \returns The square sum of all components.
template<typename genType>
constexpr genType sqnorm(const quaternion<genType>& q) {
return fennec::dot(q, q);
}
///
/// \brief Norm Function
/// \param q The Quaternion
/// \returns The Hamilton Tensor of `q`
template<typename genType>
constexpr genType norm(const quaternion<genType>& q) {
return fennec::sqrt(sqnorm(q));
}
///
/// \brief Unit Function (Versor)
/// \param q The Quaternion
/// \returns A Quaternion of `q` with norm `1`
template<typename genType>
constexpr quaternion<genType> unit(const quaternion<genType>& q) {
genType n = fennec::norm(q);
return q * (genType(1) / n);
}
///
/// \brief Reciprocal Function
/// \param q The Quaternion
/// \returns The quaternion \f${q}^{-1}\f$
template<typename genType>
constexpr quaternion<genType> reciprocal(const quaternion<genType>& q) {
return ~q / fennec::sqnorm(q);
}
template<typename genType>
constexpr quaternion<genType> mix(const quaternion<genType>& x, const quaternion<genType>& y, genType a) {
genType cT = fennec::dot(x, y);
quaternion<genType> z = cT < genType(0) ? -y : y;
cT = cT < genType(0) ? -cT : cT;
if (cT > genType(1) - numeric_limits<genType>::epsilon()) {
return x * (genType(1) - a) + y * a;
}
genType t = fennec::acos(cT);
return (fennec::sin(x * (genType(1) - a) * t) + z * (fennec::sin(a * t))) / fennec::sin(t);
}
template<typename ScalarT>
struct quaternion : detail::vector_base_type<ScalarT, 4>
{
public:
// Typedefs ============================================================================================================
using base_type = detail::vector_base_type<ScalarT, 4>;
using scalar_t = ScalarT;
using quat_t = quaternion;
using vec3_t = vec<scalar_t, 3>;
using vec4_t = vec<scalar_t, 4>;
using base_type::data;
using base_type::x, base_type::y, base_type::z, base_type::w;
// Constructors ========================================================================================================
///
/// \brief Default Constructor, creates a quaternion such that the real part is `1` and all others are `0`
constexpr quaternion() {
data = { 0, 0, 0, 1 };
}
///
/// \brief Component Constructor
/// \param w The real component
/// \param x The coefficient of the `i` component
/// \param y The coefficient of the `j` component
/// \param z The coefficient of the `k` component
constexpr quaternion(scalar_t w, scalar_t x, scalar_t y, scalar_t z) {
data = { x, y, z, w };
}
///
/// \brief Vector Constructor
/// \param v A 4d vector
constexpr quaternion(const vec<scalar_t, 4>& v) {
data = v.data;
}
///
/// \brief Swizzle Constructor
/// \param s A 4d swizzle
template<typename DataT, size_t...IndicesV> requires(sizeof...(IndicesV) == 4)
constexpr quaternion(const detail::swizzle_storage<DataT, scalar_t, IndicesV...>& s) {
data[0] = s[0];
data[1] = s[1];
data[2] = s[2];
data[3] = s[3];
}
///
/// \brief Copy Constructor
/// \param q The quaternion to copy
constexpr quaternion(const quaternion& q) {
data = q.data;
}
///
/// \brief Move Constructor
/// \param q The quaternion to move
constexpr quaternion(quaternion&& q) noexcept {
data = q.data;
}
// Assignment Operators ================================================================================================
///
/// \brief Copy Assignment Operator
/// \param q The quaternion to copy
constexpr quat_t& operator=(const quat_t& q) {
data = q.data;
return *this;
}
///
/// \brief Move Assignment Operator
/// \param q The quaternion to move
constexpr quat_t& operator=(quat_t&& q) noexcept {
data = q.data;
return *this;
}
// Comparison Operators ================================================================================================
///
/// \brief Equality Operator
/// \param lhs the left hand side of the expression
/// \param rhs the right hand side of the expression
/// \returns `true` when all components of `lhs` and `rhs` are equal, false otherwise
constexpr friend bool operator==(const quat_t& lhs, const quat_t& rhs) {
return lhs.data == rhs.data;
}
///
/// \brief Inequality Operator
/// \param lhs the left hand side of the expression
/// \param rhs the right hand side of the expression
/// \returns `true` when any component of `lhs` and `rhs` are not equal, false otherwise
constexpr friend bool operator!=(const quat_t& lhs, const quat_t& rhs) {
return lhs.data != rhs.data;
}
// Unary Operators =====================================================================================================
///
/// \brief Unary Negation Operator
/// \param rhs The quaternion to negate
/// \returns A quaternion with each component of `rhs` negated.
constexpr friend quat_t operator-(const quat_t& rhs) {
return quat_t(-rhs.w, -rhs.x, -rhs.y, -rhs.z);
}
///
/// \brief Unary Conjugation Operator
/// \param rhs The quaternion to conjugate
/// \returns A quaternion with each vector component of `rhs` negated.
constexpr friend quat_t operator~(const quat_t& rhs) {
return quat_t(rhs.w, -rhs.x, -rhs.y, -rhs.z);
}
// Quaternion Scalar Arithmetic Operators ==============================================================================
///
/// \brief Quaternion-Scalar Multiplication Operator
/// \param lhs The quaternion
/// \param rhs The scalar
/// \returns A quaternion with each component of `lhs` multiplied by `rhs`
constexpr friend quat_t operator*(const quat_t& lhs, scalar_t rhs) {
return quat_t(lhs.w * rhs, lhs.x * rhs, lhs.y * rhs, lhs.z * rhs);
}
///
/// \brief Scalar-Quaternion Multiplication Operator
/// \param lhs The scalar
/// \param rhs The quaternion
/// \returns A quaternion with each component of `rhs` multiplied by `lhs`
constexpr friend quat_t operator*(scalar_t lhs, const quat_t& rhs) {
return quat_t(lhs * rhs.w, lhs * rhs.x, lhs * rhs.y, lhs * rhs.z);
}
///
/// \brief Quaternion-Scalar Division Operator
/// \param lhs The quaternion
/// \param rhs The scalar
/// \returns A quaternion with each component of `lhs` divided by `rhs`
constexpr friend quat_t operator/(const quat_t& lhs, scalar_t rhs) {
return quat_t(lhs.w / rhs, lhs.x / rhs, lhs.y / rhs, lhs.z / rhs);
}
///
/// \brief Scalar-Quaternion Division Operator
/// \param lhs The scalar
/// \param rhs The quaternion
/// \returns A quaternion with each component of `rhs` divided by `lhs`
constexpr friend quat_t operator/(scalar_t lhs, const quat_t& rhs) {
return quat_t(lhs / rhs.w, lhs / rhs.x, lhs / rhs.y, lhs / rhs.z);
}
// Quaternion Scalar Arithmetic Assignment Operators ===================================================================
///
/// \brief Quaternion-Scalar Multiplication Assignment Operator
/// \param lhs The quaternion
/// \param rhs The scalar
/// \returns `lhs` with each component multiplied by `rhs`
constexpr friend quat_t& operator*=(quat_t& lhs, scalar_t rhs) {
lhs.x *= rhs;
lhs.y *= rhs;
lhs.z *= rhs;
lhs.w *= rhs;
return lhs;
}
///
/// \brief Quaternion-Scalar Division Assignment Operator
/// \param lhs The quaternion
/// \param rhs The scalar
/// \returns `lhs` with each component divided by `rhs`
constexpr friend quat_t& operator/=(const quat_t& lhs, scalar_t rhs) {
lhs.x /= rhs;
lhs.y /= rhs;
lhs.z /= rhs;
lhs.w /= rhs;
return lhs;
}
// Quaternion Vector Arithmetic Operators ==============================================================================
/// \brief
constexpr friend vec3_t operator*(const quat_t& q, const vec3_t& v) {
const vec3_t u = q.xyz;
const vec3_t uv = fennec::cross(u, v);
const vec3_t uuv = fennec::cross(u, uv);
return v + (uv*q.w + uuv) * scalar_t(2);
}
constexpr friend vec3_t operator*(const vec3_t& v, const quat_t& q) {
return fennec::reciprocal(q) * v;
}
constexpr friend vec4_t operator*(const quat_t& q, const vec4_t& v) {
return vec4_t(q * v.xyz, v.w);
}
constexpr friend vec4_t operator*(const vec4_t& v, const quat_t& q) {
return fennec::reciprocal(q) * v;
}
// Quaternion Quaternion Arithmetic Operators ==========================================================================
constexpr friend quaternion operator+(const quaternion& lhs, const quaternion& rhs) {
return quaternion(
lhs.w + rhs.w,
lhs.x + rhs.x,
lhs.y + rhs.y,
lhs.z + rhs.z
);
}
constexpr friend quaternion operator-(const quaternion& lhs, const quaternion& rhs) {
return quaternion(
lhs.w - rhs.w,
lhs.x - rhs.x,
lhs.y - rhs.y,
lhs.z - rhs.z
);
}
constexpr friend quaternion operator*(const quaternion& lhs, const quaternion& rhs) {
return quaternion(
lhs.w*rhs.w - lhs.x*rhs.x - lhs.y*rhs.y - lhs.z * rhs.z,
lhs.w*rhs.x + lhs.x*rhs.w + lhs.y*rhs.z - lhs.z * rhs.y,
lhs.w*rhs.y - lhs.x*rhs.z + lhs.y*rhs.w + lhs.z * rhs.x,
lhs.w*rhs.z - lhs.x*rhs.y - lhs.y*rhs.x + lhs.z * rhs.w
);
}
// Quaternion Quaternion Arithmetic Assignment Operators ===============================================================
constexpr friend quaternion& operator+=(quaternion& lhs, const quaternion& rhs) {
lhs.w += rhs.w;
lhs.x += rhs.x;
lhs.y += rhs.y;
lhs.z += rhs.z;
return lhs;
}
constexpr friend quaternion& operator-=(quaternion& lhs, const quaternion& rhs) {
lhs.w -= rhs.w;
lhs.x -= rhs.x;
lhs.y -= rhs.y;
lhs.z -= rhs.z;
return lhs;
}
constexpr friend quaternion& operator*=(quaternion& lhs, const quaternion& rhs) {
return lhs = lhs * rhs;
}
private:
};
}
#endif // FENNEC_MATH_EXT_QUATERNION_H

View File

@@ -33,9 +33,9 @@ namespace fennec
template<typename genType>
constexpr mat<genType, 3, 3> translation(const vec<genType, 2>& x) {
return mat<genType, 3, 3>(
1, 0, 0
, 0, 1, 0
, x, 1
1, 0, 0,
0, 1, 0,
x, 1
);
}
@@ -46,10 +46,10 @@ constexpr mat<genType, 3, 3> translation(const vec<genType, 2>& x) {
template<typename genType>
constexpr mat<genType, 4, 4> translation(const vec<genType, 3>& x) {
return mat<genType, 4, 4>(
1, 0, 0, 0
, 0, 1, 0, 0
, 0, 0, 1, 0
, x, 1
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
x, 1
);
}
@@ -60,9 +60,9 @@ constexpr mat<genType, 4, 4> translation(const vec<genType, 3>& x) {
template<typename genType>
constexpr mat<genType, 3, 3> scaling(const vec<genType, 2>& x) {
return mat<genType, 3, 3>(
x.x, 0, 0
, 0, x.y, 0
, 0, 0, 1
x.x, 0, 0,
0, x.y, 0,
0, 0, 1
);
}
@@ -73,10 +73,10 @@ constexpr mat<genType, 3, 3> scaling(const vec<genType, 2>& x) {
template<typename genType>
constexpr mat<genType, 4, 4> scaling(const vec<genType, 3>& x) {
return mat<genType, 4, 4>(
x.x, 0, 0, 0
, 0, x.y, 0, 0
, 0, 0, x.z, 0
, 0, 0, 0, 1
x.x, 0, 0, 0,
0, x.y, 0, 0,
0, 0, x.z, 0,
0, 0, 0, 1
);
}
@@ -93,9 +93,9 @@ constexpr mat<genType, 3, 3> rotation(const genType a) {
// Calculate the matrix
return mat<genType, 3, 3>(
c, -s, 0
, s, c, 0
, 0, 0, 1
c, -s, 0,
s, c, 0,
0, 0, 1
);
}
@@ -118,63 +118,83 @@ constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& A, genType a) {
// Calculate the Matrix
return mat<genType, 4, 4>(
A.x * u.x + c, A.x * u.y + v.z, A.x * u.z - v.y, 0
, A.x * u.y - v.z, A.y * u.y + c, A.y * u.z + v.x, 0
, A.x * u.z + v.y, A.y * u.z - v.x, A.z * u.z + c, 0
, 0, 0, 0, 1
A.x * u.x + c, A.x * u.y + v.z, A.x * u.z - v.y, 0,
A.x * u.y - v.z, A.y * u.y + c, A.y * u.z + v.x, 0,
A.x * u.z + v.y, A.y * u.z - v.x, A.z * u.z + c, 0,
0, 0, 0, 1
);
}
template<typename genType>
constexpr mat<genType, 4, 4> rotation_x(genType a) {
/// \brief enum to denote the unit-axis of rotation
enum rot_
{
rot_x = 0
, rot_y
, rot_z
};
///
/// \brief Create a 3d rotation matrix for an angle about a cartesian unit axis
/// \tparam axis the unit axis to rotate around
/// \param a the angle, in radians
/// \returns a matrix that rotates vectors around a unit axis
template<typename genType, rot_ axis>
constexpr mat<genType, 4, 4> rotation(genType a) {
// https://en.wikipedia.org/wiki/Rotation_matrix#Basic_3D_rotations
// Calculate sin and cos terms
const genType c = fennec::cos(a);
const genType s = fennec::sin(a);
// Calculate the matrix
if constexpr(axis == rot_x) {
return mat<genType, 4, 4>(
1, 0, 0, 0
, 0, c, -s, 0
, 0, s, c, 0
, 0, 0, 0, 1
1, 0, 0, 0,
0, c, -s, 0,
0, s, c, 0,
0, 0, 0, 1
);
}
else if constexpr(axis == rot_y) {
return mat<genType, 4, 4>(
c, 0, s, 0,
0, 0, 0, 0,
-s, 0, c, 0,
0, 0, 0, 1
);
}
else if constexpr(axis == rot_z) {
return mat<genType, 4, 4>(
c, -s, 0, 0,
s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
}
return mat<genType, 4, 4>();
}
template<typename genType>
constexpr mat<genType, 4, 4> rotation_y(genType a) {
///
/// \brief enum to denote the order rotations are applied in
enum order_
{
order_xyz
, order_xzy
, order_yxz
, order_yzx
, order_zxy
, order_zyx
};
// Calculate sin and cos terms
const genType c = fennec::cos(a);
const genType s = fennec::sin(a);
// Calculate the matrix
return mat<genType, 4, 4>(
c, 0, s, 0
, 0, 0, 0, 0
, -s, 0, c, 0
, 0, 0, 0, 1
);
}
template<typename genType>
constexpr mat<genType, 4, 4> rotation_z(const genType a) {
// Calculate sin and cos terms
const genType c = fennec::cos(a);
const genType s = fennec::sin(a);
// Calculate the matrix
return mat<genType, 4, 4>(
c, -s, 0, 0
, s, c, 0, 0
, 0, 0, 1, 0
, 0, 0, 0, 1
);
}
template<typename genType>
///
/// \brief Create 3d rotation matrix from euler angles
/// \tparam order the order in which to apply the rotations
/// \param E the euler angles, taken as tait-bryan (pitch, yaw, roll)
/// \returns a matrix that rotates vectors
template<typename genType, order_ order = order_zxy>
constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& E) {
// expanded using a CAS
// Calculate sin and cos terms
const genType ca = fennec::cos(E.x);
@@ -185,12 +205,56 @@ constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& E) {
const genType sg = fennec::sin(E.z);
// Calculate the matrix
if constexpr(order == order_xyz) {
return mat<genType, 4, 4>(
ca*cb, sa*sb, -sb, 0
, ca*sb*sg - sa*cg, sa*sb*sg + ca*cg, cb*sg, 0
, ca*sb*cg + sa*sg, sa*sb*cg - ca*sg, cb*cg, 0
, 0, 0, 0, 1
cb*cg, -cg*sg, sb, 0,
ca*sg + cg*sa*sb, ca*cg - sa*sb*sg, -ca*sa, 0,
sa*sg - ca*cg*sb, ca*sb*sg + cg*sa, ca*cb, 0,
0, 0, 0, 1
);
}
else if constexpr(order == order_xzy) {
return mat<genType, 4, 4>(
cb*cg, -sg, cg*sb, 0,
ca*cb*sg + sa*sb, ca*cg, cb*sa*sg - ca*sb, 0,
cb*sa*sg - ca*sb, cg*sa, ca*cb + sa*sb*sg, 0,
0, 0, 0, 1
);
}
else if constexpr(order == order_yxz) {
return mat<genType, 4, 4>(
cb*cg + sa*sb*sg, cg*sa*sb - cb*sg, ca*sb, 0,
ca*sg, ca*cg, -sa, 0,
cb*sa*sg - cg*sb, cb*cg*sa + sb*sg, ca*cb, 0,
0, 0, 0, 1
);
}
else if constexpr(order == order_yzx) {
return mat<genType, 4, 4>(
cb*cg, sa*sb - ca*cb*sg, ca*sb + cb*sa*sg, 0,
sg, ca*cg, -cg*sa, 0,
-cg*sb, ca*sb*sg + cb*sa, ca*cb - sa*sb*sg, 0,
0, 0, 0, 1
);
}
else if constexpr(order == order_zxy) {
return mat<genType, 4, 4>(
cb*cg - sa*sb*sg, -ca*sg, cb*sa*sg + cg*sb, 0,
cb*sg + cg*sa*sb, ca*cg, sb*sg - cb*cg*sa, 0,
-ca*sb, sa, ca*cb, 0,
0, 0, 0, 1
);
}
else if constexpr(order == order_zyx) {
return mat<genType, 4, 4>(
cb*cg, cg*sa*sb - ca*sg, ca*cg*sb + sa*sg, 0,
cb*sg, ca*cg + sa*sb*sg, ca*sb*sg - cg*sa, 0,
-sb, cb*sa, ca*cb, 0,
0, 0, 0, 1
);
}
return mat<genType, 4, 4>();
}
}

View File

@@ -227,7 +227,7 @@ constexpr vector<genType, i...> cross(const vector<genType, i...>& x, const vect
/// \param x
template<typename genType, size_t...i>
constexpr vector<genType, i...> normalize(const vector<genType, i...>& x) {
return x / fennec::length(x);
return x * fennec::inversesqrt(fennec::dot(x, x));
}

View File

@@ -257,8 +257,7 @@ struct vector : detail::vector_base_type<ScalarT, sizeof...(IndicesV)>
///
/// \details
/// \param s scalar value to initialize with
explicit constexpr vector(scalar_t s)
: vector() {
explicit constexpr vector(scalar_t s) {
((data[IndicesV] = s), ...);
}
@@ -268,8 +267,7 @@ struct vector : detail::vector_base_type<ScalarT, sizeof...(IndicesV)>
///
/// \details
/// \param s scalar value to initialize with
explicit constexpr vector(int_t s) requires(not is_same_v<ScalarT, int_t>)
: vector() {
explicit constexpr vector(int_t s) requires(not is_same_v<ScalarT, int_t>) {
if constexpr (N == 1) data[0] = ScalarT(s);
else ((data[IndicesV] = ScalarT(s)), ...);
}
@@ -280,8 +278,7 @@ struct vector : detail::vector_base_type<ScalarT, sizeof...(IndicesV)>
///
/// \details
/// \param s scalar value to initialize with
explicit constexpr vector(double_t s) requires(not is_same_v<ScalarT, double_t>)
: vector() {
explicit constexpr vector(double_t s) requires(not is_same_v<ScalarT, double_t>) {
if constexpr (N == 1) data[0] = ScalarT(s);
else ((data[IndicesV] = ScalarT(s)), ...);
}

View File

@@ -327,7 +327,9 @@ public:
/// \brief Sized Constructor, initializes the allocation with a block of size `n * sizeof(T)` bytes
/// \param n The number of elements of type `T` to allocate for
constexpr allocation(size_t n, align_t align) noexcept
: _data(nullptr), _capacity(0), _alignment(zero<align_t>()) {
: _data(nullptr)
, _capacity(0)
, _alignment(zero<align_t>()) {
allocate(n, align);
}
@@ -347,7 +349,9 @@ public:
///
/// \details This constructor should be used when the type `AllocT` needs internal data.
constexpr allocation(const alloc_t& alloc) noexcept
: _alloc(alloc), _data(nullptr), _capacity(0) {
: _alloc(alloc)
, _data(nullptr)
, _capacity(0) {
}
///
@@ -357,7 +361,9 @@ public:
///
/// \details This constructor should be used when the type `AllocT` needs internal data.
constexpr allocation(size_t n, const alloc_t& alloc) noexcept
: _alloc(alloc), _data(nullptr), _capacity(0) {
: _alloc(alloc)
, _data(nullptr)
, _capacity(0) {
allocate(n);
}
@@ -381,7 +387,10 @@ public:
///
/// \details This constructor should be used when the type `AllocT` needs internal data.
constexpr allocation(size_t n, align_t align, const alloc_t& alloc) noexcept
: _alloc(alloc), _data(nullptr), _capacity(0), _alignment(zero<align_t>()) {
: _alloc(alloc)
, _data(nullptr)
, _capacity(0)
, _alignment(zero<align_t>()) {
allocate(n, align);
}
@@ -402,7 +411,9 @@ public:
/// \brief Copy Constructor, creates an allocation of equal size and performs a byte-wise copy
/// \param alloc The allocation to copy
constexpr allocation(const allocation& alloc) noexcept
: _alloc(alloc._alloc), _data(_alloc.allocate(alloc._capacity)), _capacity(alloc._capacity) {
: _alloc(alloc._alloc)
, _data(_alloc.allocate(alloc._capacity))
, _capacity(alloc._capacity) {
fennec::memcpy(_data, alloc._data, alloc._capacity * sizeof(T));
}
@@ -411,7 +422,9 @@ public:
/// can safely destruct
/// \param alloc The allocation to move
constexpr allocation(allocation&& alloc) noexcept
: _alloc(alloc._alloc), _data(alloc._data), _capacity(alloc._capacity) {
: _alloc(alloc._alloc)
, _data(alloc._data)
, _capacity(alloc._capacity) {
alloc._data = nullptr; alloc._capacity = 0;
}

View File

@@ -107,7 +107,8 @@ public:
/// \param ptr The resource to own
/// \param del The deleter
explicit constexpr unique_ptr(pointer_t ptr, const delete_t& del)
: _delete(del), _handle(ptr) {
: _delete(del)
, _handle(ptr) {
}
///
@@ -115,7 +116,8 @@ public:
/// \param ptr The resource to own
/// \param del The deleter
explicit constexpr unique_ptr(pointer_t ptr, delete_t&& del)
: _delete(del), _handle(ptr) {
: _delete(del)
, _handle(ptr) {
}
///

View File

@@ -25,6 +25,8 @@ add_executable(fennec-test main.cpp
tests/test_fproc.h
tests/fproc/test_io.h
printing.h
tests/math/test_ext.h
tests/math/ext/test_quaternion.h
)
target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME}"

View File

@@ -26,6 +26,7 @@
#include <fennec/math/common.h>
#include <fennec/math/matrix.h>
#include <fennec/math/relational.h>
#include <fennec/math/ext/quaternion.h>
namespace fennec
{
@@ -52,6 +53,11 @@ inline std::ostream& operator<<(std::ostream& os, const matrix<ScalarT, RowsV, C
return os;
}
template<typename ScalarT>
inline std::ostream& operator<<(std::ostream& os, const quaternion<ScalarT>& q) {
return os << "< " << q.w << " + " << q.x << " i + " << q.y << " j + " << q.z << " k >";
}
// Helper for printing strings
inline std::ostream& operator<<(std::ostream& os, const cstring& str) {
return os << *str;

View File

@@ -0,0 +1,47 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#ifndef FENNEC_TEST_MATH_EXT_QUATERNION_H
#define FENNEC_TEST_MATH_EXT_QUATERNION_H
#include <fennec/math/ext/quaternion.h>
#include "../../../test.h"
namespace fennec
{
namespace test
{
inline void fennec_test_math_quaternion() {
fennec_test_section("constructors");
fennec_test_spacer(1);
fennec_test_run(quat(), quat(1, 0, 0, 0));
fennec_test_spacer(2);
}
}
}
#endif // FENNEC_TEST_MATH_EXT_QUATERNION_H

View File

@@ -0,0 +1,45 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#include "../../test.h"
#include "./ext/test_quaternion.h"
#ifndef FENNEC_TEST_MATH_EXT_H
#define FENNEC_TEST_MATH_EXT_H
namespace fennec
{
namespace test
{
inline void fennec_test_math_ext() {
fennec_test_subheader("quaternion tests");
fennec_test_spacer(2);
fennec_test_math_quaternion();
fennec_test_spacer(3);
}
}
}
#endif // FENNEC_TEST_MATH_EXT_H

View File

@@ -29,6 +29,8 @@
#include "math/test_relational.h"
#include "math/test_trigonometric.h"
#include "math/test_ext.h"
namespace fennec
{
@@ -76,6 +78,11 @@ inline void fennec_test_math()
fennec_test_spacer(2);
fennec_test_math_trigonometric();
fennec_test_spacer(3);
fennec_test_header("math extension tests");
fennec_test_spacer(2);
fennec_test_math_ext();
fennec_test_spacer(3);
}
}