Compare commits

...

12 Commits
3.4.6 ... main

Author SHA1 Message Date
c7919a0928 Yee
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 23s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 28s
2025-06-27 20:49:30 -05:00
db7078ff46 It keeps getting more stupid.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m31s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 26s
2025-06-12 02:46:49 -05:00
0d4255e759 Ridiculous Generic Vector class header - WIP.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Has started running
Build Docs With Doxygen / Explore-Gitea-Actions (push) Has been cancelled
2025-06-12 02:45:32 -05:00
9ecb64a2fe Messing with matrices.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m32s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 30s
2025-06-11 08:12:31 -05:00
2886bbb397 V2 & V2i constructable from std::pair
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m25s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 24s
2025-06-04 15:40:39 -04:00
966f6fc77d Update Transform2D.cpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m25s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 26s
Fix non-building
2025-06-04 15:26:37 -04:00
1bbe237510 Further Transform2D
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m28s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 27s
2025-04-22 00:28:10 -04:00
d3527ab32c Transform2D
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m21s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 27s
2025-04-21 19:24:11 -04:00
e4dd7058e1 Implement Geometry::Rect2D class, which will work in tandem with 2D geometry tools such as Transform2D, AABB2D, OBB2D, Polygon2D
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m31s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 23s
2025-04-18 15:31:22 -04:00
3259a8e980 Expand Matrix4x4 Tests, still have to correct several. 2025-04-18 15:01:32 -04:00
b17e49d1df Document & Implement Vector4::Add, AngleBetween, IsZero4, IsZero, Sub, Div, Clamp01 2025-04-18 14:58:42 -04:00
16419b2476 Add Vector2i to LinearAlgebra header file. 2025-04-18 14:57:47 -04:00
21 changed files with 743 additions and 99 deletions

View File

@@ -7,8 +7,7 @@
/// @file AABB2D.hpp
/// @desc A 2D Axis-Aligned Bounding Box structure.
/// @edit 2024-08-01
/// @note On backlog, low-priority.
/// @edit 2025-04-18
#pragma once
@@ -23,8 +22,6 @@
template <typename Matrix>
void AABB2DTransformAsAABB2D(AABB2D& aabb, Matrix& m);
namespace J3ML::Geometry
{
using LinearAlgebra::Vector2;
@@ -46,7 +43,7 @@ namespace J3ML::Geometry
[[nodiscard]] float Width() const;
[[nodiscard]] float Height() const;
Vector2 Centroid();
Vector2 Centroid() const;
[[nodiscard]] float DistanceSq(const Vector2& pt) const;

View File

@@ -0,0 +1,86 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Rect2D.hpp
/// @desc A 2D AABB, represented by a top-left origin, and a w,h size.
/// @edit 2025-04-18
/// @note On backlog, low-priority.
#pragma once
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Vector2i.hpp>
#include <J3ML/Geometry/Forward.hpp>
#include <J3ML/Geometry/AABB2D.hpp>
namespace J3ML::Geometry
{
/// A specialized type of 2D AABB structure, which represents a box from it's top-left point, and a size as width and height.
/// This is more natural for manipulation in 2D games, for bounding-boxes, sprite-quads, etc.
struct Rect2D {
/// The top-left origin point of this Rect2D.
Vector2 position;
/// The width and height of this Rect2D.
Vector2 size;
/// Constructs a Rect2D from a given Vector2 position, and size.
Rect2D(const Vector2& pos, const Vector2& size);
/// Constructs a Rect2D from a given position {x, y}, and size {w, h}
/// @param x The X-axis of the position.
/// @param y The Y-axis of the position.
/// @param w The width of the Rect2D.
/// @param h The height of the Rect2D.
Rect2D(float x, float y, float w, float h);
/// Constructs a Rect2D from a desired centroid, and a Vector2 specifying half-width, and half-height.
static Rect2D FromCentroidAndRadii(const Vector2& centroid, const Vector2& radii);
[[nodiscard]] float HorizontalRadius() const;
[[nodiscard]] float VerticalRadius() const;
[[nodiscard]] float HalfWidth() const;
[[nodiscard]] float HalfHeight() const;
[[nodiscard]] Vector2 Centroid() const;
[[nodiscard]] float Width() const;
[[nodiscard]] float Height() const;
[[nodiscard]] Vector2 MinPoint() const;
[[nodiscard]] Vector2 MaxPoint() const;
[[nodiscard]] AABB2D GetAsAABB() const;
float Area() const { return size.x * size.y;}
float Perimeter() const { return 2.f * (size.x + size.y); }
bool Intersects(const Rect2D& rhs) const;
bool Intersects(const AABB2D& rhs) const;
bool Contains(const Vector2& rhs) const;
bool Contains(int x, int y) const;
Vector2 PosInside(const Vector2 &normalizedPos) const;
Vector2 ToNormalizedLocalSpace(const Vector2 &pt) const;
Vector2 CornerPoint(int cornerIndex);
bool IsDegenerate() const;
bool HasNegativeVolume() const;
bool IsFinite() const;
Rect2D operator + (const Vector2& pt) const;
Rect2D& operator + (const Vector2& pt);
Rect2D operator - (const Vector2& pt) const;
};
struct Rect2Di {
Vector2i position;
Vector2i size;
};
}

View File

@@ -382,10 +382,6 @@ namespace J3ML::Math::Functions {
/// 2241 -> 2.2k, 55421 -> 55.4k, 1000000 -> 1.0M
std::string Truncate(float input);
float RecipFast(float x);
@@ -486,28 +482,5 @@ namespace J3ML::Math::Types {
}
namespace J3ML::Math {
struct Rotation {
Rotation();
Rotation(float value);
Rotation(const Types::Radians& radians);
Rotation(const Types::Degrees& degrees);
float valueInRadians;
float ValueInRadians() const { return valueInRadians; }
Types::Radians Radians() const { return {valueInRadians}; }
float Degrees() const { return Functions::Degrees(valueInRadians); }
Rotation operator+(const Rotation& rhs);
};
Rotation operator ""_rad(long double rads);
Rotation operator ""_radians(long double rads);
Rotation operator ""_deg(long double rads);
Rotation operator ""_degrees(long double rads);
}

View File

@@ -9,7 +9,6 @@
#pragma once
#include "J3ML/LinearAlgebra/Vector2.hpp"
#include "J3ML/LinearAlgebra/Vector3.hpp"
#include "J3ML/LinearAlgebra/Vector4.hpp"
@@ -19,5 +18,6 @@
#include "J3ML/LinearAlgebra/Matrix3x3.hpp"
#include "J3ML/LinearAlgebra/Matrix4x4.hpp"
#include "J3ML/LinearAlgebra/Transform2D.hpp"
#include "J3ML/LinearAlgebra/Vector2i.hpp"
using namespace J3ML::LinearAlgebra;

View File

@@ -15,6 +15,9 @@
#include <cstddef>
#include <cstdlib>
#include <algorithm>
#include <ranges>
#include <initializer_list>
#include "Vector.hpp"
namespace J3ML::LinearAlgebra
@@ -22,8 +25,8 @@ namespace J3ML::LinearAlgebra
template <uint ROWS, uint COLS, typename T>
class Matrix
{
class Matrix {
public:
static constexpr uint Diag = std::min(ROWS, COLS);
using RowVector = Vector<ROWS, T>;
@@ -33,6 +36,22 @@ namespace J3ML::LinearAlgebra
enum { Rows = ROWS };
enum { Cols = COLS };
Matrix(std::initializer_list<T> arg) {
int iterator = 0;
for (T entry : arg) {
int x = iterator % ROWS;
int y = iterator / ROWS;
elems[x][y] = entry;
iterator++;
}
}
Matrix(const std::vector<T>& entries);
Matrix(const std::vector<RowVector>& rows);
void AssertRowSize(uint rows)
{
assert(rows < Rows && "");

View File

@@ -5,6 +5,8 @@
#include <J3ML/Algorithm/RNG.hpp>
#include <algorithm>
#include <iostream>
#include <bits/ostream.tcc>
using namespace J3ML::Algorithm;
@@ -569,10 +571,15 @@ namespace J3ML::LinearAlgebra {
/// Returns true if this Matrix4x4 is equal to the given Matrix4x4, up to given per-element epsilon.
bool Equals(const Matrix4x4& other, float epsilon = 1e-3f) const;
[[nodiscard]] std::string ToString() const;
protected:
float elems[4][4];
Vector3 TransformDir(float tx, float ty, float tz) const;
};
}
std::ostream& operator << (std::ostream& out, const Matrix4x4& rhs);
}

View File

@@ -4,36 +4,52 @@
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
namespace J3ML::LinearAlgebra {
/// A class that performs and represents translations, rotations, and scaling operations in two dimensions.
class Transform2D {
protected:
// TODO: Verify column-major order (or transpose it) for compatibility with OpenGL.
Matrix3x3 transformation;
public:
const static Transform2D Identity;
const static Transform2D FlipX;
const static Transform2D FlipY;
Transform2D(float rotation, const Vector2& pos);
Transform2D(float px, float py, float sx, float sy, float ox, float oy, float kx, float ky, float rotation)
{
transformation = Matrix3x3(px, py, rotation, sx, sy, ox, oy, kx, ky);
}
/// Default constructor initializes to an Identity transformation.
Transform2D();
Transform2D(const Vector2& pos, const Vector2& scale, const Vector2& origin, const Vector2& skew, float rotation);
Transform2D(const Matrix3x3& transform);
explicit Transform2D(const Matrix3x3& transform);
static Transform2D FromScale(float sx, float sy);
static Transform2D FromScale(const Vector2& scale);
static Transform2D FromRotation(float radians);
static Transform2D FromTranslation(const Vector2& translation);
static Transform2D FromTranslation(float tx, float ty);
/// Returns a Transform2D
Transform2D Translate(const Vector2& offset) const;
Transform2D Translate(float x, float y) const;
Transform2D Scale(float scale); // Perform Uniform Scale
Transform2D Scale(float x, float y); // Perform Nonunform Scale
Transform2D Scale(const Vector2& scales); // Perform Nonuniform Scale
Transform2D Rotate();
Vector2 Transform(const Vector2& input) const;
Transform2D Scale(float scale);
Transform2D Scale(float x, float y);
Transform2D Scale(const Vector2& scales);
Transform2D Rotate(float radians);
/// Transforms a given 2D point by this transformation.
Vector2 Transform(const Vector2& point) const;
Transform2D Inverse() const;
Transform2D AffineInverse() const;
Vector2 ForwardVector() const;
Vector2 UpVector() const;
float Determinant() const;
Vector2 GetOrigin() const;
float& At(int row, int col);
[[nodiscard]] float At(int row, int col) const;
Vector2 GetTranslation() const;
float GetRotation() const;
Vector2 GetScale() const;
float GetSkew() const;
Transform2D OrthoNormalize();
};
}

View File

@@ -1,15 +1,163 @@
#pragma once
#include <cstddef>
#include <cstdlib>
namespace J3ML::LinearAlgebra
{
template <uint DIMS, typename T>
template <size_t D, typename T>
class Vector {
static_assert(D > 1, "A vector cannot be of 1-dimension, it would be just a scalar!");
public:
enum { Dimensions = DIMS};
T elems[DIMS];
static constexpr bool IsAtLeast1D = D >= 1; // Should always be true for a proper vector.
static constexpr bool IsAtLeast2D = D >= 2; // Should always be true for a proper vector.
static constexpr bool IsAtLeast3D = D >= 3;
static constexpr bool IsAtLeast4D = D >= 4;
static constexpr bool Is1D = IsAtLeast1D; // Should always be true for a proper vector.
static constexpr bool Is2D = IsAtLeast2D; // Should always be true for a proper vector.
static constexpr bool Is3D = IsAtLeast3D;
static constexpr bool Is4D = IsAtLeast4D;
static constexpr bool IsExact1D = D == 1; // Should never be true for a proper vector.
static constexpr bool IsExact2D = D == 2;
static constexpr bool IsExact3D = D == 3;
static constexpr bool IsExact4D = D == 4;
static constexpr bool IsAtMost1D = D <= 1; // Should also never be true.
static constexpr bool IsAtMost2D = D <= 2;
static constexpr bool IsAtMost3D = D <= 3;
static constexpr bool IsAtMost4D = D <= 4;
static constexpr bool IsFloatingPoint = std::is_floating_point_v<T>;
static constexpr bool IsIntegral = std::is_integral_v<T>;
using value_type = T;
using self_type = Vector<D, T>;
static const Vector<D, T> Zero;
static const self_type One;
static const self_type UnitX;
static const self_type UnitY;
static const self_type NaN;
static const self_type Infinity;
static const self_type NegativeInfinity;
static self_type GetUnitX() requires Is1D {}
static self_type GetUnitY() requires Is2D {}
static self_type GetUnitZ() requires Is3D {}
static self_type GetUnitW() requires Is4D {}
enum { Dimensions = D};
std::array<T, Dimensions> data;
/// Default constructor initializes all elements to zero.
Vector() : data{} {}
/// Initialize all elements to a single value.
explicit Vector(T value) { data.fill(value); }
Vector(T x, T y) requires Is2D: data{x, y} {}
Vector(T x, T y, T z) requires Is3D: data{x, y, z} {}
Vector(T x, T y, T z, T w) requires Is4D: data{x, y, z, w} {}
Vector(std::initializer_list<T> values);
T& operator[](size_t index) { return data[index]; }
const T& operator[](size_t index) const { return data[index]; }
T X() const requires Is1D { return At(0);}
T Y() const requires Is2D { return At(1);}
T Z() const requires Is3D { return At(2);}
T W() const requires Is4D { return At(3);}
T& X() requires Is1D { return At(0);}
T& Y() requires Is2D { return At(1);}
T& Z() requires Is3D { return At(2);}
T& W() requires Is4D { return At(3);}
Vector<2, T> XX() const requires Is1D { return {X()};}
Vector<2, T> YY() const requires Is2D { return {Y()};}
Vector<2, T> ZZ() const requires Is3D { return {Z()};}
Vector<2, T> WW() const requires Is4D { return {W()};}
Vector<3, T> XXX() const requires Is1D { return {X()};}
Vector<3, T> YYY() const requires Is2D { return {Y()};}
Vector<3, T> ZZZ() const requires Is3D { return {Z()};}
Vector<3, T> WWW() const requires Is4D { return {W()};}
Vector<4, T> XXXX() const requires Is1D { return {X()};}
Vector<4, T> YYYY() const requires Is2D { return {Y()};}
Vector<4, T> ZZZZ() const requires Is3D { return {Z()};}
Vector<4, T> WWWW() const requires Is4D { return {W()};}
Vector<2, T> XY() const requires Is2D { return {X(), Y()};}
Vector<2, T> XYZ() const requires Is3D { return {X(), Y(), Z()};}
Vector<2, T> XYZW() const requires Is4D { return {X(), Y(), Z(), W()};}
self_type& operator+=(const self_type& other);
T* ptr();
[[nodiscard]] const T* ptr() const;
[[nodiscard]]T At(size_t index) const;
T& At(size_t index);
[[nodiscard]] T Dot(const self_type& other) const;
[[nodiscard]] T LengthSquared() const;
[[nodiscard]] T LengthSq() const;
[[nodiscard]] T Length() const;
[[nodiscard]] self_type& Normalized() const;
void Normalize();
template <size_t N> void Normalize();
bool IsNormalized() const;
template <size_t N> bool IsNormalized() const;
bool IsFinite() const;
bool IsZero();
bool IsPerpendicular() const;
bool IsPerp();
self_type Min(const self_type& ceil) const;
self_type Max(const self_type& floor) const;
self_type Min(T ceil) const;
self_type Max(T floor) const;
self_type Clamp(const self_type& floor, const self_type& ceil) const;
self_type Clamp(T floor, T ceil) const;
T Distance(const self_type& other) const requires IsFloatingPoint;
int ManhattanDistance(const self_type& other) const requires IsIntegral;
self_type Cross(const self_type& other) const requires Is3D && IsFloatingPoint {
return {
At(1) * other.At(2) - At(2) * other.At(1),
At(2) * other.At(0) - At(0) * other.At(2),
At(0) * other.At(1) - At(1) * other.At(0),
};
}
static bool AreOrthonormal(const self_type& A, const self_type& B, float epsilon = 1e-3f);
self_type Abs() const;
};
template<size_t DIMS, typename T>
Vector<DIMS, T>::Vector(std::initializer_list<T> values) {
size_t i = 0;
for (const T& value : values) {
if (i < DIMS) {
data[i++] = value;
} else {
break;
}
}
}
template<size_t DIMS, typename T>
Vector<DIMS, T> & Vector<DIMS, T>::operator+=(const Vector &other) {
return {0};
}
using v2f = Vector<2, float>;
using v3f = Vector<3, float>;
using v4f = Vector<4, float>;
using v2d = Vector<2, double>;
using v3d = Vector<3, double>;
using v4d = Vector<4, double>;
using v2i = Vector<2, int>;
using v3i = Vector<3, int>;
using v4i = Vector<4, int>;
template<> const v2f Vector<2, float>::Zero = v2f(0);
template<> const v3f Vector<3, float>::Zero = v3f(0);
template<> const v4f Vector<4, float>::Zero = v4f(0);
template<> const v2f Vector<2, float>::One = v2f(1);
template<> const v3f Vector<3, float>::One = v3f(1);
template<> const v4f Vector<4, float>::One = v4f(1);
}

View File

@@ -55,11 +55,12 @@ namespace J3ML::LinearAlgebra {
Vector2(float X, float Y);
/// Constructs this float2 from a C array, to the value (data[0], data[1]).
explicit Vector2(const float* data);
// Constructs a new Vector2 with the value {scalar, scalar}
/// Constructs a new Vector2 with the value {scalar, scalar}
explicit Vector2(float scalar);
Vector2(const Vector2& rhs); // Copy Constructor
explicit Vector2(const Vector2i& rhs);
//Vector2(Vector2&&) = default; // Move Constructor
/// Constructs a new Vector2 from std::pair<float, float>,.
explicit Vector2(const std::pair<float, float>& rhs) : x(rhs.first), y(rhs.second) {}
[[nodiscard]] float GetX() const;
[[nodiscard]] float GetY() const;
@@ -383,6 +384,7 @@ namespace J3ML::LinearAlgebra {
static Vector2 RandomBox(Algorithm::RNG& rng, float minElem, float maxElem);
[[nodiscard]] std::string ToString() const;
};
Vector2 operator*(float lhs, const Vector2 &rhs);

View File

@@ -14,6 +14,7 @@ public:
Vector2i(int x, int y) : x(x), y(y) {}
explicit Vector2i(int rhs) : x(rhs), y(rhs) {}
explicit Vector2i(const Vector2& rhs) : x(rhs.x), y(rhs.y) { }
explicit Vector2i(const std::pair<int, int>& rhs) : x(rhs.first), y(rhs.second) {}
public:
bool operator == (const Vector2i& rhs) const;
bool operator != (const Vector2i& rhs) const;

View File

@@ -30,6 +30,9 @@ namespace J3ML::LinearAlgebra {
because there is a considerable SIMD performance benefit in the first form.
@see x, y, z, w. */
Vector4(float X, float Y, float Z, float W);
Vector4(float XYZW) : x(XYZW), y(0), z(0), w(0) {}
Vector4(float X, float Y) : x(X), y(Y), z(0), w(0) {}
Vector4(float X, float Y, float Z) : x(X), y(Y), z(Z), w(0) {}
/// The Vector4 copy constructor.
Vector4(const Vector4& copy) { Set(copy); }
Vector4(Vector4&& move) = default;
@@ -106,12 +109,9 @@ namespace J3ML::LinearAlgebra {
/// Tests if the (x, y, z) part of this vector is equal to (0,0,0), up to the given epsilon.
/** @see NormalizeW(), IsWZeroOrOne(), IsZero4(), IsNormalized3(), IsNormalized4(). */
[[nodiscard]] bool IsZero3(float epsilonSq = 1e-6f) const;
/// Returns true if this vector is equal to (0,0,0,0), up to the given epsilon.
/** @see NormalizeW(), IsWZeroOrOne(), IsZero3(), IsNormalized3(), IsNormalized4(). */
[[nodiscard]] bool IsZero(float epsilonSq = 1e-6f) const;
[[nodiscard]] bool IsZero4(float epsilonSq = 1e-6f) const;
/// Tests if this vector contains valid finite elements.
[[nodiscard]] bool IsFinite() const;
@@ -202,6 +202,7 @@ namespace J3ML::LinearAlgebra {
[[nodiscard]] float Magnitude() const;
[[nodiscard]] float Dot(const Vector4& rhs) const;
[[nodiscard]] float Dot4(const Vector4& rhs) const { return this->Dot(rhs); }
/// Computes the dot product of the (x, y, z) parts of this and the given float4.
/** @note This function ignores the w component of this vector (assumes w=0).
@see Dot4(), Cross3(). */
@@ -209,10 +210,9 @@ namespace J3ML::LinearAlgebra {
[[nodiscard]] float Dot3(const Vector4& rhs) const;
[[nodiscard]] Vector4 Project(const Vector4& rhs) const;
// While it is feasable to compute a cross-product in four dimensions
// the cross product only has the orthogonality property in 3 and 7 dimensions
// You should consider instead looking at Gram-Schmidt Orthogonalization
// to find orthonormal vectors.
/// While it is feasable to compute a cross-product in four dimensions the cross product only has the orthogonality property in 3 and 7 dimensions.
/// You should consider instead looking at Gram-Schmidt Orthogonalization to find orthonormal vectors.
[[nodiscard]] Vector4 Cross3(const Vector3& rhs) const;
[[nodiscard]] Vector4 Cross3(const Vector4& rhs) const;
[[nodiscard]] Vector4 Cross(const Vector4& rhs) const;
@@ -220,7 +220,11 @@ namespace J3ML::LinearAlgebra {
[[nodiscard]] Vector4 Normalized() const;
[[nodiscard]] Vector4 Lerp(const Vector4& goal, float alpha) const;
/// Returns the angle between this vector and the specified vector, in radians.
/// @note This function takes into account that this vector or the other vector can be un-normalized, and normalizes the computations.
/// @see Dot3(), AngleBetween3(), AngleBetweenNorm3(), AngleBetweenNorm().
[[nodiscard]] float AngleBetween(const Vector4& rhs) const;
[[nodiscard]] float AngleBetween4(const Vector4& rhs) const;
/// Adds two vectors. [indexTitle: operators +,-,*,/]
/** This function is identical to the member function Add().
@@ -243,17 +247,23 @@ namespace J3ML::LinearAlgebra {
/** This function is identical to the member function Mul().
@return float4(x * scalar, y * scalar, z * scalar, w * scalar); */
Vector4 operator *(float rhs) const;
[[nodiscard]] Vector4 Mul(float scalar) const;
static Vector4 Mul(const Vector4& lhs, float rhs);
[[nodiscard]] Vector4 Mul(float scalar) const { return *this * scalar;}
static Vector4 Mul(const Vector4& lhs, float rhs) {return lhs * rhs; }
/// Divides this vector by a scalar. [similarOverload: operator+] [hideIndex]
/** This function is identical to the member function Div().
@return float4(x / scalar, y / scalar, z / scalar, w * scalar); */
Vector4 operator /(float rhs) const;
[[nodiscard]] Vector4 Div(float scalar) const;
static Vector4 Div(const Vector4& rhs, float scalar);
[[nodiscard]] Vector4 Div(float scalar) const { return *this / scalar; }
static Vector4 Div(const Vector4& rhs, float scalar) { return rhs / scalar; }
Vector4 operator +() const; // Unary + Operator
/// Divides this vector by a vector, element-wise.
/// @note Mathematically, the division of two vectors is not defined in linear space structures,
/// but this function is provided here for syntactical convenience.
Vector4 Div(const Vector4& rhs) const;
static Vector4 Div(const Vector4& lhs, const Vector4& rhs);
Vector4 operator +() const { return *this;} // Unary + Operator
/// Performs an unary negation of this vector. [similarOverload: operator+] [hideIndex]
/** This function is identical to the member function Neg().
@return float4(-x, -y, -z, -w). */

56
include/J3ML/Rotation.hpp Normal file
View File

@@ -0,0 +1,56 @@
#pragma once
#include "J3ML.hpp"
#include "LinearAlgebra/Vector2.hpp"
namespace J3ML::Math {
/// Rotation is a class that represents a single axis of rotation.
/// The class is designed to behave very similarly to a float literal, and
/// primarily help organize code involving rotations by handling common boilerplate
/// and providing mathematical expressions.
struct Rotation {
constexpr Rotation();
constexpr Rotation(float value);
constexpr explicit Rotation(const Vector2& direction_vector);
constexpr Rotation FromDegrees(float degrees);
constexpr Rotation FromRadians(float radians);
//Rotation(const Types::Radians& radians);
//Rotation(const Types::Degrees& degrees);
constexpr float Radians() const { return value;}
//Types::Radians Radians() const { return {value}; }
constexpr float Degrees() const { return Math::Degrees(value); }
constexpr Rotation operator+(const Rotation& rhs) const;
constexpr Rotation operator-(const Rotation& rhs) const;
constexpr Rotation operator*(float scalar) const;
constexpr Rotation operator/(float scalar) const;
constexpr bool operator==(const Rotation& rhs) const = default;
constexpr Rotation operator-() const;
/// Rotates a given Vector2 by this Rotation.
Vector2 Rotate(const Vector2& rhs) const;
float operator()() const { return value; }
Rotation& operator=(const Rotation& rhs) {
this->value = rhs.value;
return *this;
}
protected:
float value;
};
constexpr Rotation operator ""_rad(long double rads);
constexpr Rotation operator ""_radians(long double rads);
constexpr Rotation operator ""_deg(long double rads);
constexpr Rotation operator ""_degrees(long double rads);
}

View File

@@ -15,6 +15,10 @@
#include <J3ML/Geometry.hpp>
#include <J3ML/J3ML.hpp>
#include <jlog/Logger.hpp>
#include <J3ML/LinearAlgebra/Matrix.hpp>
#include <J3ML/LinearAlgebra/Vector.hpp>
#include "J3ML/Rotation.hpp"
int main(int argc, char** argv)
@@ -50,10 +54,48 @@ int main(int argc, char** argv)
}
Ray a({420, 0, 0}, {1, 0, 0});
std::cout << a << std::endl;
Matrix4x4 A {
1, 2, 0, 1,
3, 1, 2, 0,
0, 4, 1, 2,
2, 0, 3, 1
};
Matrix4x4 B {
0, 1, 2, 3,
1, 0, 1, 0,
2, 3, 0, 1,
1, 2, 1, 0
};
auto C = A*B;
using Matrix2x3f = Matrix<2, 3, float>;
std::cout << C << std::endl;
std::cout << "j3ml demo coming soon" << std::endl;
v2f _v2f{1.f};
v3f _v3f{1.f};
v4f _v4f(1);
v2i ipair (420, 420);
v3i ipair3(0,0,0);
v4i ipair4(1,2,3,4);
using namespace J3ML::Math;
Rotation my_rot = 25_degrees;
return 0;
}

View File

@@ -78,7 +78,7 @@ namespace J3ML::Geometry
return a;
}
Vector2 AABB2D::Centroid() {
Vector2 AABB2D::Centroid() const {
return (minPoint + (maxPoint / 2.f));
}

View File

@@ -0,0 +1,50 @@
#include <J3ML/Geometry/Rect2D.hpp>
namespace J3ML::Geometry
{
Rect2D Rect2D::operator+(const J3ML::LinearAlgebra::Vector2 &pt) const {
return {position+pt, size};
}
Rect2D &Rect2D::operator+(const Vector2 &pt) {
position += pt;
return *this;
}
AABB2D Rect2D::GetAsAABB() const { return {MinPoint(), MaxPoint()};}
Rect2D Rect2D::FromCentroidAndRadii(const Vector2 &centroid, const Vector2 &radii) {
return Rect2D(centroid.x - (radii.x), centroid.y - (radii.y),
radii.x*2.f, radii.y*2.f);
}
Rect2D::Rect2D(float x, float y, float w, float h) {
this->position = {x,y};
this->size = {w,h};
}
Rect2D::Rect2D(const Vector2 &pos, const Vector2 &size) {
this->position = pos;
this->size = size;
}
float Rect2D::HorizontalRadius() const { return Width()/2.f;}
float Rect2D::VerticalRadius() const { return Height()/2.f;}
float Rect2D::HalfWidth() const { return HorizontalRadius();}
float Rect2D::HalfHeight() const { return VerticalRadius();}
Vector2 Rect2D::Centroid() const { return position + (size / 2.f);}
float Rect2D::Width() const { return size.x;}
float Rect2D::Height() const { return size.y;}
Vector2 Rect2D::MinPoint() const { return position; }
Vector2 Rect2D::MaxPoint() const { return position + size;}
}

View File

@@ -136,14 +136,6 @@ namespace J3ML::Math::Functions { }
namespace J3ML {
Math::Rotation Math::operator ""_degrees(long double rads) { return {Functions::Radians((float)rads)}; }
Math::Rotation Math::operator ""_deg(long double rads) { return {Functions::Radians((float)rads)}; }
Math::Rotation Math::operator ""_radians(long double rads) { return {(float)rads}; }
Math::Rotation Math::operator ""_rad(long double rads) { return {(float)rads}; }
float Math::Functions::FastRSqrt(float x) {
return 1.f / FastSqrt(x);
}
@@ -305,17 +297,7 @@ namespace J3ML {
return 1.f / x;
}
Math::Rotation::Rotation() : valueInRadians(0) {}
Math::Rotation::Rotation(float value) : valueInRadians(value) {}
Math::Rotation::Rotation(const Types::Radians &radians): valueInRadians(radians.value) {}
Math::Rotation::Rotation(const Types::Degrees &degrees): valueInRadians(Functions::Radians(degrees.value)) {}
Math::Rotation Math::Rotation::operator+(const Math::Rotation &rhs) {
return {valueInRadians + rhs.valueInRadians};
}
// int BitTwiddling::CountBitsSet(u32 value) { }

View File

@@ -1,9 +1,10 @@
#include <iomanip>
#include <J3ML/LinearAlgebra/Matrix4x4.hpp>
#include <J3ML/LinearAlgebra/Vector4.hpp>
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/Matrices.inl>
#include <iostream>
namespace J3ML::LinearAlgebra {
@@ -679,6 +680,11 @@ namespace J3ML::LinearAlgebra {
}
std::ostream & operator<<(std::ostream &out, const Matrix4x4 &rhs) {
out << rhs.ToString();
return out;
}
void Matrix4x4::InverseOrthonormal()
{
//assert(!ContainsProjection());
@@ -1014,6 +1020,46 @@ namespace J3ML::LinearAlgebra {
return true;
}
std::string trim_trailing_zeros(const std::string &value) {
std::string str = value;
// Ensure that there is a decimal point somewhere (there should be)
if(str.find('.') != std::string::npos)
{
// Remove trailing zeroes
str = str.substr(0, str.find_last_not_of('0')+1);
// If the decimal point is now the last character, remove that as well
if(str.find('.') == str.size()-1)
{
str = str.substr(0, str.size()-1);
}
}
return str;
}
std::string Matrix4x4::ToString() const {
// Determine the maximum width for any element in the matrix.
size_t max_width = 0;
for (size_t row = 0; row < 4; ++row) {
for (size_t col = 0; col < 4; ++col) {
std::string s = std::to_string(At(row, col));
s = trim_trailing_zeros(s);
max_width = std::max(max_width, s.length());
}
}
for (size_t row = 0; row < 4; ++row) {
for (size_t col = 0; col < 4; ++col) {
auto val = this->At(row, col);
std::cout << std::right << std::setw(static_cast<int>(max_width)) << val << " ";
}
std::cout << std::endl;
}
}
void
Matrix4x4::Set(float _00, float _01, float _02, float _03, float _10, float _11, float _12, float _13, float _20,
float _21, float _22, float _23, float _30, float _31, float _32, float _33) {

View File

@@ -27,4 +27,73 @@ namespace J3ML::LinearAlgebra {
Transform2D Transform2D::Translate(const LinearAlgebra::Vector2 &input) const {
return Translate(input.x, input.y);
}
Transform2D::Transform2D() {
transformation = Matrix3x3::Identity;
}
Transform2D Transform2D::FromRotation(float radians) {
float c = Math::Cos(radians);
float s = Math::Sin(radians);
return Transform2D(Matrix3x3(
c, s, 0.f,
-s, c, 0.f,
0.f, 0.f, 1.f));
}
Transform2D Transform2D::FromScale(const Vector2 &scale) {
return FromScale(scale.x, scale.y);
}
Transform2D Transform2D::FromScale(float sx, float sy) {
Transform2D s;
s.transformation[0][0] = sx;
s.transformation[1][1] = sy;
return s;
}
Transform2D Transform2D::FromTranslation(const Vector2 &translation) {
return FromTranslation(translation.x, translation.y);
}
Transform2D Transform2D::FromTranslation(float tx, float ty) {
return Transform2D(Matrix3x3(
1.f, 0.f, 0.f,
0.f, 1.f, 0.f,
tx, ty, 1.f));
}
Vector2 Transform2D::GetScale() const {
return {
Math::Sqrt(At(0,0) * At(0,0) + At(0,1) * At(0,1)),
Math::Sqrt(At(1,0) * At(1,0) + At(1,1) * At(1,1))
};
}
float Transform2D::GetRotation() const {
return Math::Atan2(At(1, 0), At(0, 0));
}
Vector2 Transform2D::GetTranslation() const {
return {At(2,0), At(2, 1)};
}
float &Transform2D::At(int row, int col) { return transformation.At(row, col); }
float Transform2D::At(int row, int col) const { return transformation.At(row, col); }
float Transform2D::Determinant() const { return transformation.Determinant(); }
/*
Vector2 Transform2D::Transform(const Vector2 &point) const {
Vector2 result;
result.x = At(0,0) * point.x + At(0,1) * point.y + At(0,2);
result.y = At(1,0) * point.x + At(1,1) * point.y + At(1,2);
return result;
}
*/
Vector2 Transform2D::ForwardVector() const { return {At(0,0), At(1,0)}; }
Vector2 Transform2D::UpVector() const { return {At(0,1), At(1,1)}; }
}

View File

@@ -331,5 +331,63 @@ Vector4 Vector4::operator-(const Vector4& rhs) const
}
Vector4 Vector4::Cross(const Vector4 &rhs) const { return Cross3(rhs); }
Vector4 Vector4::Add(const Vector4 &rhs) const { return *this + rhs;}
Vector4 Vector4::Add(const Vector4 &lhs, const Vector4 &rhs) {
return lhs + rhs;
}
float Vector4::AngleBetween(const Vector4 &rhs) const {
float cosa = this->Dot4(rhs) / Math::Sqrt(LengthSq4() * rhs.LengthSq4());
if (cosa >= 1.f)
return 0.f;
else if (cosa <= -1.f)
return Math::Pi;
else
return Math::Acos(cosa);
}
float Vector4::AngleBetween4(const Vector4 &rhs) const { return AngleBetween(rhs);}
bool Vector4::IsZero4(float epsilonSq) const {
return LengthSq4() <= epsilonSq;
}
bool Vector4::IsZero(float epsilonSq) const { return IsZero4(epsilonSq);}
Vector4 Vector4::Clamp01() const {
return Vector4(
Math::Clamp01(x),
Math::Clamp01(y),
Math::Clamp01(z),
Math::Clamp01(w) );
}
Vector4 Vector4::Sub(const Vector4 &rhs) const { return *this - rhs;}
Vector4 Vector4::Sub(const Vector4 &lhs, const Vector4 &rhs) { return lhs - rhs;}
Vector4 Vector4::operator/(float rhs) const {
float invScalar = 1.f / rhs;
return Vector4(x * invScalar, y * invScalar, z * invScalar, w * invScalar);
}
Vector4 Vector4::Div(const Vector4 &rhs) const {
return Vector4(
x / rhs.x,
y / rhs.y,
z / rhs.z,
w / rhs.w );
}
Vector4 Vector4::Div(const Vector4 &lhs, const Vector4 &rhs) {
return lhs.Div(rhs);
}
Vector4 Vector4::operator-() const {
return Vector4(-x,-y,-z,-w);
}
}
#pragma endregion

44
src/J3ML/Rotation.cpp Normal file
View File

@@ -0,0 +1,44 @@
#include <J3ML/Rotation.hpp>
namespace J3ML {
Math::Rotation Math::operator ""_degrees(long double rads) { return {Functions::Radians((float)rads)}; }
Math::Rotation Math::operator ""_deg(long double rads) { return {Functions::Radians((float)rads)}; }
Math::Rotation Math::operator ""_radians(long double rads) { return {(float)rads}; }
Vector2 Math::Rotation::Rotate(const Vector2 &rhs) const {
float cos_a = Math::Cos(value);
float sin_a = Math::Sin(value);
return Vector2(
rhs.x * cos_a - rhs.y * sin_a,
rhs.x * sin_a + rhs.y * cos_a);
}
Math::Rotation Math::operator ""_rad(long double rads) { return {(float)rads}; }
Math::Rotation::Rotation() : value(0) {}
Math::Rotation::Rotation(float value) : value(value) {}
constexpr Math::Rotation::Rotation(const Vector2 &direction_vector) {
value = Math::Atan2(direction_vector.y, direction_vector.x);
}
constexpr Math::Rotation Math::Rotation::FromDegrees(float degrees) {
return Rotation(Math::Radians(degrees));
}
constexpr Math::Rotation Math::Rotation::FromRadians(float radians) { return Rotation(value);}
Math::Rotation Math::Rotation::operator+(const Math::Rotation &rhs) const {
return {value + rhs.value};
}
Math::Rotation Math::Rotation::operator-(const Math::Rotation &rhs) const {
return {value - rhs.value};
}
}

View File

@@ -11,8 +11,7 @@ namespace Matrix4x4Tests {
using namespace J3ML::LinearAlgebra;
using namespace J3ML::Math;
Matrix4x4Unit += Test("Add_Unary", []
{
Matrix4x4Unit += Test("Add_Unary", [] {
Matrix4x4 m(1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16);
Matrix4x4 m2 = +m;
jtest::check(m.Equals(m2));
@@ -111,13 +110,52 @@ namespace Matrix4x4Tests {
});
Matrix4x4Unit += Test("CtorFromQuatTrans", [] {});
Matrix4x4Unit += Test("Translate", [] {});
Matrix4x4Unit += Test("Scale", [] {});
Matrix4x4Unit += Test("InverseOrthogonalUniformScale", [] {});
Matrix4x4Unit += Test("InverseOrthonormal", [] {});
Matrix4x4Unit += Test("DeterminantCorrectness", [] { });
Matrix4x4Unit += Test("MulMat3x3", [] {});
Matrix4x4Unit += Test("CtorFromQuatTrans", [] {
RNG rng;
constexpr float SCALE = 1e2f;
Vector3 t = Vector3::RandomBox(rng, Vector3(-SCALE, -SCALE, -SCALE), Vector3(SCALE, SCALE, SCALE));
Quaternion q = Quaternion::RandomRotation(rng);
Matrix4x4 m (q, t);
Vector3 v = Vector3(-1, 5, 20.f);
Vector3 v1 = q * v + t;
Vector3 v2 = m.Transform(v);
jtest::check(v1.Equals(v2));
});
Matrix4x4Unit += Test("Translate", [] {
RNG rng;
constexpr float SCALE = 1e2f;
Vector3 t = Vector3::RandomBox(rng, Vector3(-SCALE, -SCALE, -SCALE), Vector3(SCALE, SCALE, SCALE));
Vector3 t2 = Vector3::RandomBox(rng, Vector3(-SCALE, -SCALE, -SCALE), Vector3(SCALE, SCALE, SCALE));
Matrix4x4 m = Matrix4x4::Translate(t);
Matrix4x4 m2 = Matrix4x4::Translate({t.x, t.y, t.z});
Vector3 v = t + t2;
Vector3 v1 = m.Transform(t2);
Vector3 v2 = m2.Transform(t2);
jtest::check(v1.Equals(v2));
jtest::check(v.Equals(v1));
});
Matrix4x4Unit += Test("Scale", [] {
Matrix4x4 m = Matrix4x4::Scale({2, 4, 6});
Matrix4x4 m2(2,0,0,0, 0,4,0,0, 0,0,6,0, 0,0,0,1);
jtest::check(m.Equals(m2));
});
Matrix4x4Unit += Test("MulMat3x3", [] {
RNG rng;
Matrix3x3 m = Matrix3x3::RandomGeneral(rng, -10.f, 10.f);
Matrix4x4 m_ = m;
Matrix4x4 m2 = Matrix4x4::RandomGeneral(rng, -10.f, 10.f);
Matrix4x4 test = m2 * m;
Matrix4x4 correct = m2 * m_;
jtest::check(test.Equals(correct));
});
}
inline void Run() {