Compare commits

...

39 Commits
3.4.4 ... 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
1285b85fbd Add AxisAngle members and fill out more unit tests.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 2m2s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 33s
2025-03-05 02:12:53 -05:00
0f5070783e Added some more Quaternion unit tests
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m17s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 28s
2025-03-04 16:32:12 -06:00
28f87dd189 Add legacy Carmack implementation of Reciprocal Sqrt 2025-03-04 16:31:47 -06:00
21f809481d Remove usages of CoordinateFrame 2025-03-04 16:31:14 -06:00
7af631d7e5 Remove CoordinateFrame cpp file 2025-03-04 16:30:40 -06:00
51d51a9cc7 Add AxisAngle members and fill out more unit tests.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m2s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 26s
2025-03-03 23:33:03 -05:00
b161250052 Remove EulerAngle because it's dumb 2025-03-03 23:32:23 -05:00
073b1518fe Unit test Quaternion::Quaternion(Matrix3x3).
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 4m39s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 21s
2025-03-03 03:37:05 -06:00
b4e3bf4a7d Fix Quaternion::Quaternion(Matrix3x3) with more correct algorithm.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 4m29s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 24s
2025-03-03 03:29:12 -06:00
2bd27c5f3e Testing RoundTrip for new rotation type conversions
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m18s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 33s
2025-03-02 05:22:13 -05:00
7e5207d194 Fiddle with cmake 2025-03-02 05:21:53 -05:00
152e5f4ebd Implement Axisangle::ToQuaternion, and AxisAngle constructor from matrix 2025-03-02 05:21:41 -05:00
179bf277b5 Fixed potential circular include 2025-03-02 05:20:58 -05:00
c5b843f086 Fixed potential circular include 2025-03-02 05:20:51 -05:00
75e2229126 Implemented Quaternion::Equals 2025-03-02 05:20:35 -05:00
7592dcdd39 Merge remote-tracking branch 'origin/main'
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m34s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 27s
# Conflicts:
#	tests/LinearAlgebra/Matrix3x3Tests.hpp
2025-02-02 22:11:49 -05:00
0460aede0d Comment out tests for Matrix3x3-To-EulerAngle which is no longer supported at the moment. 2025-02-02 22:11:40 -05:00
777601a7c7 Add conversion from Vector2-float to Vector2i 2025-02-02 22:11:14 -05:00
8e8a0a37d4 Add Vector2::PreciselyEquals(), 2025-02-02 22:11:02 -05:00
174f6068e7 Fix incorrect usage of Normalized() vs Normalize() 2025-02-02 22:10:02 -05:00
1a5737a356 Deprecate BitTwiddling module, fix obsolete standard header inclusion 2025-02-02 22:09:43 -05:00
437113437f Implemented Matrix3x3::ForwardDir, BackwardDir, LeftDir, RightDir, UpDir, DownDir return normalized relative direction vectors. 2025-02-02 22:08:48 -05:00
250c745969 Remove DirectionVector class 2025-02-02 22:06:43 -05:00
245c6c6eb4 Update Vector2.cpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m17s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 28s
change == and != to not check epsilon. ~= should check epsilon.
2025-01-27 02:42:42 -05:00
Redacted
58bd078b05 Update include/J3ML/LinearAlgebra/Vector4.hpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m51s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 31s
Fix weird compilation issue on gcc?
2025-01-16 13:49:32 -05:00
4cbfef1706 Fix build error on Matrix3x3tests.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m26s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 23s
2025-01-15 23:39:53 -05:00
43191a9857 Fix USE_LOOKUP_TABLES enabled when lookup table implementation is not complete!
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m15s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 27s
2024-12-25 17:01:02 -05:00
48 changed files with 1251 additions and 485 deletions

View File

@@ -39,7 +39,7 @@ CPMAddPackage(
target_include_directories(J3ML PUBLIC ${jtest_SOURCE_DIR}/include) target_include_directories(J3ML PUBLIC ${jtest_SOURCE_DIR}/include)
target_link_libraries(J3ML PUBLIC jtest) target_link_libraries(J3ML PUBLIC jlog jtest)
if(WIN32) if(WIN32)
#target_compile_options(J3ML PRIVATE -Wno-multichar) #target_compile_options(J3ML PRIVATE -Wno-multichar)

View File

@@ -10,7 +10,7 @@ J3ML is a "Modern C++" library designed to provide comprehensive support for 3D
## Features ## Features
* <b>Vector Operations:</b> Comprehensive support for 3D vector operations including addition, subtraction, scalar multiplication, dot product, cross product, normalization, and more. * **Vector Operations:** Comprehensive support for 3D vector operations including addition, subtraction, scalar multiplication, dot product, cross product, normalization, and more.
* **Matrix Operations:** Efficient implementation of 3x3 and 4x4 matrices with support for common operations such as multiplication, transpose, determinant calculation, and inverse calculation. * **Matrix Operations:** Efficient implementation of 3x3 and 4x4 matrices with support for common operations such as multiplication, transpose, determinant calculation, and inverse calculation.
* **Quaternion Operations:** Quaternion manipulation functions including conversion to/from axis-angle representation, quaternion multiplication, normalization, and interpolation (slerp). * **Quaternion Operations:** Quaternion manipulation functions including conversion to/from axis-angle representation, quaternion multiplication, normalization, and interpolation (slerp).
* **Transformation Functions:** Functions for transforming points, vectors, and normals using matrices and quaternions. * **Transformation Functions:** Functions for transforming points, vectors, and normals using matrices and quaternions.
@@ -18,29 +18,68 @@ J3ML is a "Modern C++" library designed to provide comprehensive support for 3D
* **Algorithms:** Implementation of various algorithms including Gilbert-Johnson-Keerthi (GJK) algorithm for collision detection, random number generator, and more. * **Algorithms:** Implementation of various algorithms including Gilbert-Johnson-Keerthi (GJK) algorithm for collision detection, random number generator, and more.
* **Utility Functions:** Additional utilities such as conversion between degrees and radians, random number generation, and common constants. * **Utility Functions:** Additional utilities such as conversion between degrees and radians, random number generation, and common constants.
# Usage ## Coming Soon
To use J3ML in your C++ project, simply include the necessary header files and link against the library. Here's a basic example of how to use the library to perform vector addition: * **SIMD:** (Single-instruction, multiple-data) utilizes a vectorized instruction set to compute operations on multiple values at once. This is particularly useful in matrix maths.
* **LUTs:** Compute Lookup-tables for common operations, and bake them into your program via constexpr. (Sin, Cos, Tan, Sqrt, FastInverseSqrt)
# Installation
We support integration via CMake Package Manager (CPM). It's quite clean and flexible. It's a single CMake script too.
Here's what we recommend:
Install CPM.cmake to a `cmake` directory in your project root, and add the lines below into your CMakeLists.txt
To integrate the package manager:
```cmake
include("cmake/CPM.cmake")
```
To automatically download and build J3ML version 3.4.5 (Check releases for new versions!):
```cmake
CPMAddPackage(
NAME J3ML
URL https::/git.redacted.cc/josh/J3ML/archive/3.4.5.zip
)
```
Then you should be able to link J3ML to your project like any other library:
```cmake
target_include_directories(MyProgramOrLib PUBLIC ${J3ML_SOURCE_DIR}/include)
###
target_link_libraries(MyProgramOrLib PUBLIC J3ML)
```
# Usage Samples
## 2D Vector Operations
```cpp ```cpp
#include <J3ML/LinearAlgebra.hpp>
#include <iostream> Vector2 position {10.f, 10.f};
Vector2 velocity {5.f, 1.5f};
float step = 1.f/60.f;
void doStep() {
position = position + (velocity * step);
velocity = velocity.Lerp(Vector2::Zero, step);
float speed = velocity.Length();
}
```
## Matrix3x3 and Rotation Types
```cpp
#include <j3ml/LinearAlgebra.h> #include <j3ml/LinearAlgebra.h>
int main() { Matrix3x3 mRotation = Matrix3x3::RotateX(Math::PiOverTwo);
// Create two 3D vectors Quaternion qRotation(mRotation); // Convert to Quaternion
Vector3 v1(1.0, 2.0, 3.0); AxisAngle aRotation(qRotation); // Convert to AxisAngle
Vector3 v2(4.0, 5.0, 6.0); EulerAngleXYZ eRotation(aRotation); // Convert to Euler Angle (XYZ)
// Perform vector addition
Vector3 result = v1 + v2;
// Output the result
std::cout << "Result: " << result << std::endl;
return 0;
}
``` ```
For more detailed usage instructions and examples, please refer to the documentation. For more detailed usage instructions and examples, please refer to the documentation.

View File

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

View File

@@ -149,11 +149,8 @@ namespace J3ML::Geometry
is because the Frustum class implements a caching mechanism where world, projection and viewProj matrices are recomputed on demand, which does not work nicely together is because the Frustum class implements a caching mechanism where world, projection and viewProj matrices are recomputed on demand, which does not work nicely together
if the defaults were uninitialized. */ if the defaults were uninitialized. */
Frustum(); Frustum();
static Frustum CreateFrustumFromCamera(const CoordinateFrame& cam, float aspect, float fovY, float zNear, float zFar);
public: public:
/// Quickly returns an arbitrary point inside this Frustum. Used in GJK intersection test. /// Quickly returns an arbitrary point inside this Frustum. Used in GJK intersection test.
[[nodiscard]] Vector3 AnyPointFast() const { return CornerPoint(0); } [[nodiscard]] Vector3 AnyPointFast() const { return CornerPoint(0); }

View File

@@ -52,7 +52,7 @@ namespace J3ML::Geometry {
} }
} }
AABB2D ComputeAABB() {} AABB2D ComputeAABB() { return AABB2D(); }
float DistanceSq(const Vector2 &point) const { float DistanceSq(const Vector2 &point) const {
Vector2 centered = point - center; Vector2 centered = point - center;

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

@@ -19,7 +19,7 @@
/// This set of functions may be set to use lookup tables or SIMD operations. /// This set of functions may be set to use lookup tables or SIMD operations.
/// If no options are set, they will default to using standard library implementation. /// If no options are set, they will default to using standard library implementation.
#define USE_LOOKUP_TABLES /// Pre-computed lookup tables. #undef USE_LOOKUP_TABLES /// Pre-computed lookup tables.
#undef USE_SSE /// Streaming SIMD Extensions (x86) #undef USE_SSE /// Streaming SIMD Extensions (x86)
#undef USE_NEON /// ARM Vector Processing #undef USE_NEON /// ARM Vector Processing
#undef USE_AVX /// Advanced Vector Extensions (x86) #undef USE_AVX /// Advanced Vector Extensions (x86)
@@ -165,7 +165,7 @@ namespace J3ML::BitTwiddling {
u32 BinaryStringToValue(const char* s); u32 BinaryStringToValue(const char* s);
/// Returns the number of 1's set in the given value. /// Returns the number of 1's set in the given value.
inline int CountBitsSet(u32 value); //inline int CountBitsSet(u32 value);
} }
namespace J3ML::Math { namespace J3ML::Math {
@@ -382,10 +382,6 @@ namespace J3ML::Math::Functions {
/// 2241 -> 2.2k, 55421 -> 55.4k, 1000000 -> 1.0M /// 2241 -> 2.2k, 55421 -> 55.4k, 1000000 -> 1.0M
std::string Truncate(float input); std::string Truncate(float input);
float RecipFast(float x); float RecipFast(float x);
@@ -441,13 +437,23 @@ namespace J3ML::Math::Functions {
/// Returns the fractional part of x. /// Returns the fractional part of x.
/** @see Lerp(), LerpMod(), InvLerp(), Step(), SmoothStep(), PingPongMod(), Mod(), ModPos(). */ /** @see Lerp(), LerpMod(), InvLerp(), Step(), SmoothStep(), PingPongMod(), Mod(), ModPos(). */
float Frac(float x); float Frac(float x);
float Sqrt(float x); /// Returns the square root of x. /// Returns the square root of x.
float FastSqrt(float x); /// Computes a fast approximation of the square root of x. float Sqrt(float x);
float RSqrt(float x); /// Returns 1/Sqrt(x). (The reciprocal of the square root of x) /// Computes a fast approximation of the square root of x.
float FastRSqrt(float x); /// SSE implementation of reciprocal square root. float FastSqrt(float x);
float Recip(float x); /// Returns 1/x, the reciprocal of x. /// Returns 1/Sqrt(x). (The reciprocal of the square root of x)
float RecipFast(float x); /// Returns 1/x, the reciprocal of x, using a fast approximation (SSE rcp instruction). float RSqrt(float x);
/// SSE implementation of reciprocal square root.
float FastRSqrt(float x);
/// Returns 1/x, the reciprocal of x.
float Recip(float x);
/// Returns 1/x, the reciprocal of x, using a fast approximation (SSE rcp instruction).
float RecipFast(float x);
/// Carmack (Quake) implementation of inverse (reciprocal) square root.
/// Relies on funky type-casting hacks to avoid floating-point division and sqrt, which is very slow on legacy hardware.
/// This technique is superseded by modern processors having built-in support, but is included for its historical significance.
/// https://en.wikipedia.org/wiki/Fast_inverse_square_root
float QRSqrt(float x);
} }
namespace J3ML::Math::Functions::Interpolation namespace J3ML::Math::Functions::Interpolation
@@ -476,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

@@ -1,5 +1,5 @@
//// Dawsh Linear Algebra Library - Everything you need for 3D math //// Dawsh Linear Algebra Library - Everything you need for 3D math
/// @file LinearAlgebra.h /// @file LinearAlgebra.hpp
/// @description Includes all LinearAlgebra classes and functions /// @description Includes all LinearAlgebra classes and functions
/// @author Josh O'Leary, William Tomasine II /// @author Josh O'Leary, William Tomasine II
/// @copyright 2024 Redacted Software /// @copyright 2024 Redacted Software
@@ -9,22 +9,15 @@
#pragma once #pragma once
// TODO: Enforce Style Consistency (Function Names use MicroSoft Case)
// TODO: Implement Templated Linear Algebra
// Library Code //
#include "J3ML/LinearAlgebra/Vector2.hpp" #include "J3ML/LinearAlgebra/Vector2.hpp"
#include "J3ML/LinearAlgebra/Vector3.hpp" #include "J3ML/LinearAlgebra/Vector3.hpp"
#include "J3ML/LinearAlgebra/Vector4.hpp" #include "J3ML/LinearAlgebra/Vector4.hpp"
#include "J3ML/LinearAlgebra/Quaternion.hpp" #include "J3ML/LinearAlgebra/Quaternion.hpp"
#include "J3ML/LinearAlgebra/AxisAngle.hpp" #include "J3ML/LinearAlgebra/AxisAngle.hpp"
#include "J3ML/LinearAlgebra/EulerAngle.hpp"
#include "J3ML/LinearAlgebra/Matrix2x2.hpp" #include "J3ML/LinearAlgebra/Matrix2x2.hpp"
#include "J3ML/LinearAlgebra/Matrix3x3.hpp" #include "J3ML/LinearAlgebra/Matrix3x3.hpp"
#include "J3ML/LinearAlgebra/Matrix4x4.hpp" #include "J3ML/LinearAlgebra/Matrix4x4.hpp"
#include "J3ML/LinearAlgebra/Transform2D.hpp" #include "J3ML/LinearAlgebra/Transform2D.hpp"
#include "J3ML/LinearAlgebra/CoordinateFrame.hpp" #include "J3ML/LinearAlgebra/Vector2i.hpp"
using namespace J3ML::LinearAlgebra; using namespace J3ML::LinearAlgebra;

View File

@@ -1,24 +1,73 @@
//// Dawsh Linear Algebra Library - Everything you need for 3D math
/// @file AxisAngle.hpp
/// @description Defines the AxisAngle class for representing rotations.
/// @author Josh O'Leary, William Tomasine II
/// @copyright 2025 Redacted Software
/// @license Unlicense - Public Domain
/// @edited 2025-03-04
#pragma once #pragma once
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp> #include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Forward.hpp>
namespace J3ML::LinearAlgebra { namespace J3ML::LinearAlgebra {
class AxisAngle; class AxisAngle;
} }
/// Transitional datatype, not useful for internal representation of rotation /// @class AxisAngle
/// But has uses for conversion and manipulation. /// @brief Represents a rotation using an axis and an angle.
/// This class encapsulates a rotation in 3D space using an axis-angle representation.
/// It provides methods for converting between AxisAngle and other rotation representations,
/// as well as interpolation and other useful rotation operations.
/// @note There are many different ways you can represent a rotation in 3D space,
/// and we provide some of the more common types.
/// @see class Matrix3x3, class Quaternion
class J3ML::LinearAlgebra::AxisAngle { class J3ML::LinearAlgebra::AxisAngle {
public: public:
/// The
Vector3 axis; Vector3 axis;
// Radians. /// Radians.
float angle; float angle;
public: public:
AxisAngle(); /// The default constructor does not initialize any member values.
AxisAngle() = default;
/// Returns an AxisAngle created by converting from the given Quaternions rotation representation.
explicit AxisAngle(const Quaternion& q); explicit AxisAngle(const Quaternion& q);
explicit AxisAngle(const EulerAngleXYZ& e); /// This constructor derives the Quaternion equivalent of the given matrix, and converts that to AxisAngle representation.
explicit AxisAngle(const Matrix3x3& m);
AxisAngle(const Vector3& axis, float angle); AxisAngle(const Vector3& axis, float angle);
[[nodiscard]] Quaternion ToQuaternion() const;
[[nodiscard]] Matrix3x3 ToMatrix3x3() const;
/// Returns the axis component of this AxisAngle rotation.
Vector3 Axis() const;
float Angle() const;
/// Normalize this rotation in-place.
void Normalize();
/// Return a normalized copy of this rotation.
[[nodiscard]] AxisAngle Normalized() const;
/// Checks if the rotation is an identity rotation (angle is 0).
bool IsIdentity();
/// Inverts this rotation in-place.
void Inverse();
/// Returns an inverted copy of this rotation.
[[nodiscard]] AxisAngle Inverted() const;
/// Performs a direct Linear Interpolation on the members of the inputs.
AxisAngle Lerp(const AxisAngle& rhs, float t);
/// Performs a Spherical Linear Interpolation by converting the inputs to Quaternions.
AxisAngle Slerp(const AxisAngle& rhs, float t);
bool Equals(const AxisAngle& rhs, float epsilon = 1e-3f);
bool operator == (const AxisAngle& rhs);
}; };

View File

@@ -1,17 +0,0 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.hpp>
namespace J3ML::LinearAlgebra
{
/// The CFrame is fundamentally 4 vectors (position, forward, right, up vector)
class CoordinateFrame
{
public:
Vector3 Position;
Vector3 Front;
Vector3 Right;
Vector3 Up;
};
}

View File

@@ -1,20 +0,0 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.hpp>
namespace J3ML::LinearAlgebra {
class DirectionVectorRH;
}
/// Direction vector of a given Matrix3x3 RotationMatrix in a Right-handed coordinate space.
class J3ML::LinearAlgebra::DirectionVectorRH : public Vector3 {
private:
// This is purposefully not exposed because these types aren't usually convertable.
explicit DirectionVectorRH(const Vector3& rhs);
public:
static DirectionVectorRH Forward(const Matrix3x3& rhs);
static DirectionVectorRH Backward(const Matrix3x3& rhs);
static DirectionVectorRH Left(const Matrix3x3& rhs);
static DirectionVectorRH Right(const Matrix3x3& rhs);
static DirectionVectorRH Up(const Matrix3x3& rhs);
static DirectionVectorRH Down(const Matrix3x3& rhs);
};

View File

@@ -1,23 +0,0 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/AxisAngle.hpp>
namespace J3ML::LinearAlgebra {
class EulerAngleXYZ;
}
class J3ML::LinearAlgebra::EulerAngleXYZ {
public:
public:
float roll = 0; // X
float pitch = 0; // Y
float yaw = 0; // Z
public:
EulerAngleXYZ(float roll, float pitch, float yaw);
public:
explicit EulerAngleXYZ(const Quaternion& rhs);
explicit EulerAngleXYZ(const AxisAngle& rhs);
explicit EulerAngleXYZ(const Matrix3x3& rhs);
};

View File

@@ -8,7 +8,6 @@ namespace J3ML::LinearAlgebra
class Vector3; // A type representing a position in a 3-dimensional coordinate space. class Vector3; // A type representing a position in a 3-dimensional coordinate space.
class Vector4; // A type representing a position in a 4-dimensional coordinate space. class Vector4; // A type representing a position in a 4-dimensional coordinate space.
class Angle2D; // Uses x,y components to represent a 2D rotation. class Angle2D; // Uses x,y components to represent a 2D rotation.
class EulerAngleXYZ; // Uses pitch,yaw,roll components to represent a 3D orientation.
class AxisAngle; // class AxisAngle; //
class CoordinateFrame; // class CoordinateFrame; //
class Matrix2x2; class Matrix2x2;

View File

@@ -15,6 +15,9 @@
#include <cstddef> #include <cstddef>
#include <cstdlib> #include <cstdlib>
#include <algorithm> #include <algorithm>
#include <ranges>
#include <initializer_list>
#include "Vector.hpp" #include "Vector.hpp"
namespace J3ML::LinearAlgebra namespace J3ML::LinearAlgebra
@@ -22,8 +25,8 @@ namespace J3ML::LinearAlgebra
template <uint ROWS, uint COLS, typename T> template <uint ROWS, uint COLS, typename T>
class Matrix class Matrix {
{ public:
static constexpr uint Diag = std::min(ROWS, COLS); static constexpr uint Diag = std::min(ROWS, COLS);
using RowVector = Vector<ROWS, T>; using RowVector = Vector<ROWS, T>;
@@ -33,6 +36,22 @@ namespace J3ML::LinearAlgebra
enum { Rows = ROWS }; enum { Rows = ROWS };
enum { Cols = COLS }; 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) void AssertRowSize(uint rows)
{ {
assert(rows < Rows && ""); assert(rows < Rows && "");

View File

@@ -5,7 +5,6 @@
#include <J3ML/LinearAlgebra/Vector2.hpp> #include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp> #include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp> #include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
#include <J3ML/Algorithm/RNG.hpp> #include <J3ML/Algorithm/RNG.hpp>
using namespace J3ML::Algorithm; using namespace J3ML::Algorithm;
@@ -62,11 +61,9 @@ namespace J3ML::LinearAlgebra {
Matrix3x3(const Vector3& col0, const Vector3& col1, const Vector3& col2); Matrix3x3(const Vector3& col0, const Vector3& col1, const Vector3& col2);
/// Constructs this matrix3x3 from the given quaternion. /// Constructs this matrix3x3 from the given quaternion.
explicit Matrix3x3(const Quaternion& orientation); explicit Matrix3x3(const Quaternion& orientation);
/// Constructs this matrix3x3 from the given euler angle.
//explicit Matrix3x3(const EulerAngleXYZ& orientation);
//explicit Matrix3x3(const AxisAngle& orientation); //explicit Matrix3x3(const AxisAngle& orientation);
explicit Matrix3x3(const AxisAngle& orientation) : Matrix3x3(Quaternion(orientation)) {};
/// Constructs this Matrix3x3 from a pointer to an array of floats. /// Constructs this Matrix3x3 from a pointer to an array of floats.
explicit Matrix3x3(const float *data); explicit Matrix3x3(const float *data);
@@ -161,6 +158,14 @@ namespace J3ML::LinearAlgebra {
/// Sets this matrix to perform the rotation expressed by the given quaternion. /// Sets this matrix to perform the rotation expressed by the given quaternion.
void SetRotatePart(const Quaternion& quat); void SetRotatePart(const Quaternion& quat);
Vector3 ForwardDir() const;
Vector3 BackwardDir() const;
Vector3 LeftDir() const;
Vector3 RightDir() const;
Vector3 UpDir() const;
Vector3 DownDir() const;
/// Returns the given row. /// Returns the given row.
/** @param row The zero-based index [0, 2] of the row to get. */ /** @param row The zero-based index [0, 2] of the row to get. */
Vector3 GetRow(int index) const; Vector3 GetRow(int index) const;

View File

@@ -5,6 +5,8 @@
#include <J3ML/Algorithm/RNG.hpp> #include <J3ML/Algorithm/RNG.hpp>
#include <algorithm> #include <algorithm>
#include <iostream>
#include <bits/ostream.tcc>
using namespace J3ML::Algorithm; 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. /// 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; bool Equals(const Matrix4x4& other, float epsilon = 1e-3f) const;
[[nodiscard]] std::string ToString() const;
protected: protected:
float elems[4][4]; float elems[4][4];
Vector3 TransformDir(float tx, float ty, float tz) const; Vector3 TransformDir(float tx, float ty, float tz) const;
}; };
}
std::ostream& operator << (std::ostream& out, const Matrix4x4& rhs);
}

View File

@@ -25,18 +25,22 @@ public:
public: public:
/// The default constructor does not initialize any member values. /// The default constructor does not initialize any member values.
Quaternion() = default; Quaternion() = default;
/// Copy constructor /// Copy constructor
Quaternion(const Quaternion &rhs); Quaternion(const Quaternion &rhs);
/// Quaternion from Matrix3x3 /// Quaternion from Matrix3x3
explicit Quaternion(const Matrix3x3& ro_mat); explicit Quaternion(const Matrix3x3 &ro_mat);
/// Quaternion from Matrix4x4 RotatePart. /// Quaternion from Matrix4x4 RotatePart.
explicit Quaternion(const Matrix4x4& ro_mat); explicit Quaternion(const Matrix4x4 &ro_mat);
/// Quaternion from EulerAngleXYZ.
explicit Quaternion(const EulerAngleXYZ& rhs);
/// Quaternion from AxisAngle. /// Quaternion from AxisAngle.
explicit Quaternion(const AxisAngle& angle); explicit Quaternion(const AxisAngle &angle);
/// Quaternion from Vector4 (no conversion). /// Quaternion from Vector4 (no conversion).
explicit Quaternion(const Vector4& vector4); explicit Quaternion(const Vector4 &vector4);
/// @param x The factor of i. /// @param x The factor of i.
/// @param y The factor of j. /// @param y The factor of j.
@@ -71,33 +75,39 @@ public:
quaternion Q it holds that M * localForward = targetDirection, and M * localUp lies in the plane spanned quaternion Q it holds that M * localForward = targetDirection, and M * localUp lies in the plane spanned
by the vectors targetDirection and worldUp. by the vectors targetDirection and worldUp.
@see RotateFromTo() */ @see RotateFromTo() */
static Quaternion LookAt(const Vector3& localForward, const Vector3& targetDirection, const Vector3& localUp, const Vector3& worldUp); static Quaternion LookAt(const Vector3 &localForward, const Vector3 &targetDirection, const Vector3 &localUp, const Vector3 &worldUp);
/// Creates a new quaternion that rotates about the positive X axis by the given rotation. /// Creates a new quaternion that rotates about the positive X axis by the given rotation.
static Quaternion RotateX(float rad); static Quaternion RotateX(float rad);
/// Creates a new quaternion that rotates about the positive Y axis by the given rotation.
static Quaternion RotateY(float rad);
/// Creates a new quaternion that rotates about the positive Z axis by the given rotation.
static Quaternion RotateZ(float rad);
/// Creates a new quaternion that rotates sourceDirection vector (in world space) to coincide with the /// Creates a new quaternion that rotates about the positive Y axis by the given rotation.
/// targetDirection vector (in world space). static Quaternion RotateY(float rad);
/// Rotation is performed about the origin.
/// The vectors sourceDirection and targetDirection are assumed to be normalized.
/// @note There are multiple such rotations - this function returns the rotation that has the shortest angle
/// (when decomposed to axis-angle notation).
static Quaternion RotateFromTo(const Vector3& sourceDirection, const Vector3& targetDirection);
static Quaternion RotateFromTo(const Vector4& sourceDirection, const Vector4& targetDirection);
/// Creates a new quaternion that /// Creates a new quaternion that rotates about the positive Z axis by the given rotation.
/// 1. rotates sourceDirection vector to coincide with the targetDirection vector, and then static Quaternion RotateZ(float rad);
/// 2. rotates sourceDirection2 (which was transformed by 1.) to targetDirection2, but keeping the constraint that
/// sourceDirection must look at targetDirection /// Creates a new quaternion that rotates sourceDirection vector (in world space) to coincide with the
static Quaternion RotateFromTo(const Vector3& sourceDirection, const Vector3& targetDirection, const Vector3& sourceDirection2, const Vector3& targetDirection2); /// targetDirection vector (in world space).
/// Rotation is performed about the origin.
/// The vectors sourceDirection and targetDirection are assumed to be normalized.
/// @note There are multiple such rotations - this function returns the rotation that has the shortest angle
/// (when decomposed to axis-angle notation).
static Quaternion RotateFromTo(const Vector3 &sourceDirection, const Vector3 &targetDirection);
static Quaternion RotateFromTo(const Vector4 &sourceDirection, const Vector4 &targetDirection);
/// Creates a new quaternion that
/// 1. rotates sourceDirection vector to coincide with the targetDirection vector, and then
/// 2. rotates sourceDirection2 (which was transformed by 1.) to targetDirection2, but keeping the constraint that
/// sourceDirection must look at targetDirection
static Quaternion
RotateFromTo(const Vector3 &sourceDirection, const Vector3 &targetDirection, const Vector3 &sourceDirection2,
const Vector3 &targetDirection2);
/// Returns a uniformly random unitary quaternion. /// Returns a uniformly random unitary quaternion.
static Quaternion RandomRotation(RNG &rng); static Quaternion RandomRotation(RNG &rng);
public: public:
/// Inverses this quaternion in-place. /// Inverses this quaternion in-place.
/// @note For optimization purposes, this function assumes that the quaternion is unitary, in which /// @note For optimization purposes, this function assumes that the quaternion is unitary, in which
@@ -107,8 +117,10 @@ public:
/// Returns an inverted copy of this quaternion. /// Returns an inverted copy of this quaternion.
[[nodiscard]] Quaternion Inverted() const; [[nodiscard]] Quaternion Inverted() const;
/// Computes the conjugate of this quaternion in-place. /// Computes the conjugate of this quaternion in-place.
void Conjugate(); void Conjugate();
/// Returns a conjugated copy of this quaternion. /// Returns a conjugated copy of this quaternion.
[[nodiscard]] Quaternion Conjugated() const; [[nodiscard]] Quaternion Conjugated() const;
@@ -121,10 +133,13 @@ public:
/// Returns the local +X axis in the post-transformed coordinate space. This is the same as transforming the vector (1,0,0) by this quaternion. /// Returns the local +X axis in the post-transformed coordinate space. This is the same as transforming the vector (1,0,0) by this quaternion.
[[nodiscard]] Vector3 WorldX() const; [[nodiscard]] Vector3 WorldX() const;
/// Returns the local +Y axis in the post-transformed coordinate space. This is the same as transforming the vector (0,1,0) by this quaternion. /// Returns the local +Y axis in the post-transformed coordinate space. This is the same as transforming the vector (0,1,0) by this quaternion.
[[nodiscard]] Vector3 WorldY() const; [[nodiscard]] Vector3 WorldY() const;
/// Returns the local +Z axis in the post-transformed coordinate space. This is the same as transforming the vector (0,0,1) by this quaternion. /// Returns the local +Z axis in the post-transformed coordinate space. This is the same as transforming the vector (0,0,1) by this quaternion.
[[nodiscard]] Vector3 WorldZ() const; [[nodiscard]] Vector3 WorldZ() const;
/// Returns the axis of rotation for this quaternion. /// Returns the axis of rotation for this quaternion.
[[nodiscard]] Vector3 Axis() const; [[nodiscard]] Vector3 Axis() const;
@@ -132,23 +147,31 @@ public:
[[nodiscard]] float Angle() const; [[nodiscard]] float Angle() const;
[[nodiscard]] float LengthSquared() const; [[nodiscard]] float LengthSquared() const;
[[nodiscard]] float Length() const; [[nodiscard]] float Length() const;
[[nodiscard]] Matrix3x3 ToMatrix3x3() const; [[nodiscard]] Matrix3x3 ToMatrix3x3() const;
[[nodiscard]] Matrix4x4 ToMatrix4x4() const; [[nodiscard]] Matrix4x4 ToMatrix4x4() const;
[[nodiscard]] Matrix4x4 ToMatrix4x4(const Vector3 &translation) const; [[nodiscard]] Matrix4x4 ToMatrix4x4(const Vector3 &translation) const;
[[nodiscard]] Vector3 Transform(const Vector3& vec) const; [[nodiscard]] Vector3 Transform(const Vector3 &vec) const;
[[nodiscard]] Vector3 Transform(float X, float Y, float Z) const; [[nodiscard]] Vector3 Transform(float X, float Y, float Z) const;
// Note: We only transform the x,y,z components of 4D vectors, w is left untouched // Note: We only transform the x,y,z components of 4D vectors, w is left untouched
[[nodiscard]] Vector4 Transform(const Vector4& vec) const; [[nodiscard]] Vector4 Transform(const Vector4 &vec) const;
[[nodiscard]] Vector4 Transform(float X, float Y, float Z, float W) const; [[nodiscard]] Vector4 Transform(float X, float Y, float Z, float W) const;
[[nodiscard]] Quaternion Lerp(const Quaternion& b, float t) const; [[nodiscard]] Quaternion Lerp(const Quaternion &b, float t) const;
static Quaternion Lerp(const Quaternion &source, const Quaternion& target, float t);
[[nodiscard]] Quaternion Slerp(const Quaternion& q2, float t) const; static Quaternion Lerp(const Quaternion &source, const Quaternion &target, float t);
static Quaternion Slerp(const Quaternion &source, const Quaternion& target, float t);
[[nodiscard]] Quaternion Slerp(const Quaternion &q2, float t) const;
static Quaternion Slerp(const Quaternion &source, const Quaternion &target, float t);
/// Returns the 'from' vector rotated towards the 'to' vector by the given normalized time parameter. /// Returns the 'from' vector rotated towards the 'to' vector by the given normalized time parameter.
/** This function slerps the given 'form' vector toward the 'to' vector. /** This function slerps the given 'form' vector toward the 'to' vector.
@@ -157,7 +180,7 @@ public:
@param t The interpolation time parameter, in the range [0, 1]. Input values outside this range are @param t The interpolation time parameter, in the range [0, 1]. Input values outside this range are
silently clamped to the [0, 1] interval. silently clamped to the [0, 1] interval.
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */ @return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
static Vector3 SlerpVector(const Vector3& from, const Vector3& to, float t); static Vector3 SlerpVector(const Vector3 &from, const Vector3 &to, float t);
/// Returns the 'from' vector rotated towards the 'to' vector by the given absolute angle, in radians. /// Returns the 'from' vector rotated towards the 'to' vector by the given absolute angle, in radians.
/** This function slerps the given 'from' vector towards the 'to' vector. /** This function slerps the given 'from' vector towards the 'to' vector.
@@ -167,23 +190,25 @@ public:
angle between 'from' and 'to' is smaller than this angle, then the vector 'to' is returned. angle between 'from' and 'to' is smaller than this angle, then the vector 'to' is returned.
Input values outside this range are silently clamped to the [0, pi] interval. Input values outside this range are silently clamped to the [0, pi] interval.
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */ @return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
static Vector3 SlerpVectorAbs(const Vector3 &from, const Vector3& to, float angleRadians); static Vector3 SlerpVectorAbs(const Vector3 &from, const Vector3 &to, float angleRadians);
/// Normalizes this quaternion in-place. /// Normalizes this quaternion in-place.
/// @returns false if failure, true if success. /// @returns false if failure, true if success.
[[nodiscard]] bool Normalize(); [[nodiscard]] bool Normalize();
/// Returns a normalized copy of this quaternion. /// Returns a normalized copy of this quaternion.
[[nodiscard]] Quaternion Normalized() const; [[nodiscard]] Quaternion Normalized() const;
/// Returns true if the length of this quaternion is one. /// Returns true if the length of this quaternion is one.
[[nodiscard]] bool IsNormalized(float epsilon = 1e-5f) const; [[nodiscard]] bool IsNormalized(float epsilon = 1e-5f) const;
[[nodiscard]] bool IsInvertible(float epsilon = 1e-3f) const; [[nodiscard]] bool IsInvertible(float epsilon = 1e-3f) const;
/// Returns true if the entries of this quaternion are all finite. /// Returns true if the entries of this quaternion are all finite.
[[nodiscard]] bool IsFinite() const; [[nodiscard]] bool IsFinite() const;
/// Returns true if this quaternion equals rhs, up to the given epsilon. /// Returns true if this quaternion equals rhs, up to the given epsilon.
[[nodiscard]] bool Equals(const Quaternion& rhs, float epsilon = 1e-3f) const; [[nodiscard]] bool Equals(const Quaternion &rhs, float epsilon = 1e-3f) const;
/// Compares whether this Quaternion and the given Quaternion are identical bit-by-bit in the underlying representation. /// Compares whether this Quaternion and the given Quaternion are identical bit-by-bit in the underlying representation.
/// @note Prefer using this over e.g. memcmp, since there can be SSE-related padding in the structures. /// @note Prefer using this over e.g. memcmp, since there can be SSE-related padding in the structures.

View File

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

View File

@@ -1,15 +1,163 @@
#pragma once #pragma once
#include <cstddef>
#include <cstdlib> #include <cstdlib>
namespace J3ML::LinearAlgebra namespace J3ML::LinearAlgebra
{ {
template <uint DIMS, typename T>
template <size_t D, typename T>
class Vector { class Vector {
static_assert(D > 1, "A vector cannot be of 1-dimension, it would be just a scalar!");
public: public:
enum { Dimensions = DIMS}; static constexpr bool IsAtLeast1D = D >= 1; // Should always be true for a proper vector.
T elems[DIMS]; 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,12 +55,12 @@ namespace J3ML::LinearAlgebra {
Vector2(float X, float Y); Vector2(float X, float Y);
/// Constructs this float2 from a C array, to the value (data[0], data[1]). /// Constructs this float2 from a C array, to the value (data[0], data[1]).
explicit Vector2(const float* data); 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); explicit Vector2(float scalar);
Vector2(const Vector2& rhs); // Copy Constructor Vector2(const Vector2& rhs); // Copy Constructor
explicit Vector2(const Vector2i& rhs); 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 GetX() const;
[[nodiscard]] float GetY() const; [[nodiscard]] float GetY() const;
@@ -111,14 +111,10 @@ namespace J3ML::LinearAlgebra {
/// Tests if two vectors are equal, up to the given epsilon. /// Tests if two vectors are equal, up to the given epsilon.
/** @see IsPerpendicular(). */ /** @see IsPerpendicular(). */
bool Equals(const Vector2& rhs, float epsilon = 1e-3f) const { bool Equals(const Vector2& rhs, float epsilon = 1e-3f) const;
return Math::EqualAbs(x, rhs.x, epsilon) && bool Equals(float x_, float y_, float epsilon = 1e-3f) const;
Math::EqualAbs(y, rhs.y, epsilon);
} bool PreciselyEquals(const Vector2& rhs) const;
bool Equals(float x_, float y_, float epsilon = 1e-3f) const {
return Math::EqualAbs(x, x_, epsilon) &&
Math::EqualAbs(y, y_, epsilon);
}
bool operator == (const Vector2& rhs) const; bool operator == (const Vector2& rhs) const;
bool operator != (const Vector2& rhs) const; bool operator != (const Vector2& rhs) const;
@@ -388,6 +384,7 @@ namespace J3ML::LinearAlgebra {
static Vector2 RandomBox(Algorithm::RNG& rng, float minElem, float maxElem); static Vector2 RandomBox(Algorithm::RNG& rng, float minElem, float maxElem);
[[nodiscard]] std::string ToString() const; [[nodiscard]] std::string ToString() const;
}; };
Vector2 operator*(float lhs, const Vector2 &rhs); Vector2 operator*(float lhs, const Vector2 &rhs);

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <string> #include <string>
#include "Vector2.hpp"
namespace J3ML::LinearAlgebra { namespace J3ML::LinearAlgebra {
class Vector2i; class Vector2i;
@@ -12,6 +13,8 @@ public:
Vector2i(); Vector2i();
Vector2i(int x, int y) : x(x), y(y) {} Vector2i(int x, int y) : x(x), y(y) {}
explicit Vector2i(int rhs) : x(rhs), y(rhs) {} 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: public:
bool operator == (const Vector2i& rhs) const; bool operator == (const Vector2i& rhs) const;
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. because there is a considerable SIMD performance benefit in the first form.
@see x, y, z, w. */ @see x, y, z, w. */
Vector4(float X, float Y, float Z, float 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. /// The Vector4 copy constructor.
Vector4(const Vector4& copy) { Set(copy); } Vector4(const Vector4& copy) { Set(copy); }
Vector4(Vector4&& move) = default; Vector4(Vector4&& move) = default;
@@ -48,7 +51,7 @@ namespace J3ML::LinearAlgebra {
@note This function is provided for compatibility with other APIs which require raw C pointer access @note This function is provided for compatibility with other APIs which require raw C pointer access
to vectors. Avoid using this function in general, and instead always use the operator [] of this to vectors. Avoid using this function in general, and instead always use the operator [] of this
class to access the elements of this vector by index. */ class to access the elements of this vector by index. */
inline float* ptr(); float* ptr();
[[nodiscard]] const float* ptr() const; [[nodiscard]] const float* ptr() const;
/// Accesses an element of this vector using array notation. /// Accesses an element of this vector using array notation.
@@ -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. /// 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(). */ /** @see NormalizeW(), IsWZeroOrOne(), IsZero4(), IsNormalized3(), IsNormalized4(). */
[[nodiscard]] bool IsZero3(float epsilonSq = 1e-6f) const; [[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 IsZero(float epsilonSq = 1e-6f) const;
[[nodiscard]] bool IsZero4(float epsilonSq = 1e-6f) const; [[nodiscard]] bool IsZero4(float epsilonSq = 1e-6f) const;
/// Tests if this vector contains valid finite elements. /// Tests if this vector contains valid finite elements.
[[nodiscard]] bool IsFinite() const; [[nodiscard]] bool IsFinite() const;
@@ -202,6 +202,7 @@ namespace J3ML::LinearAlgebra {
[[nodiscard]] float Magnitude() const; [[nodiscard]] float Magnitude() const;
[[nodiscard]] float Dot(const Vector4& rhs) 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. /// 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). /** @note This function ignores the w component of this vector (assumes w=0).
@see Dot4(), Cross3(). */ @see Dot4(), Cross3(). */
@@ -209,10 +210,9 @@ namespace J3ML::LinearAlgebra {
[[nodiscard]] float Dot3(const Vector4& rhs) const; [[nodiscard]] float Dot3(const Vector4& rhs) const;
[[nodiscard]] Vector4 Project(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 /// 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 /// You should consider instead looking at Gram-Schmidt Orthogonalization to find orthonormal vectors.
// to find orthonormal vectors.
[[nodiscard]] Vector4 Cross3(const Vector3& rhs) const; [[nodiscard]] Vector4 Cross3(const Vector3& rhs) const;
[[nodiscard]] Vector4 Cross3(const Vector4& rhs) const; [[nodiscard]] Vector4 Cross3(const Vector4& rhs) const;
[[nodiscard]] Vector4 Cross(const Vector4& rhs) const; [[nodiscard]] Vector4 Cross(const Vector4& rhs) const;
@@ -220,7 +220,11 @@ namespace J3ML::LinearAlgebra {
[[nodiscard]] Vector4 Normalized() const; [[nodiscard]] Vector4 Normalized() const;
[[nodiscard]] Vector4 Lerp(const Vector4& goal, float alpha) 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 AngleBetween(const Vector4& rhs) const;
[[nodiscard]] float AngleBetween4(const Vector4& rhs) const;
/// Adds two vectors. [indexTitle: operators +,-,*,/] /// Adds two vectors. [indexTitle: operators +,-,*,/]
/** This function is identical to the member function Add(). /** 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(). /** This function is identical to the member function Mul().
@return float4(x * scalar, y * scalar, z * scalar, w * scalar); */ @return float4(x * scalar, y * scalar, z * scalar, w * scalar); */
Vector4 operator *(float rhs) const; Vector4 operator *(float rhs) const;
[[nodiscard]] Vector4 Mul(float scalar) const; [[nodiscard]] Vector4 Mul(float scalar) const { return *this * scalar;}
static Vector4 Mul(const Vector4& lhs, float rhs); static Vector4 Mul(const Vector4& lhs, float rhs) {return lhs * rhs; }
/// Divides this vector by a scalar. [similarOverload: operator+] [hideIndex] /// Divides this vector by a scalar. [similarOverload: operator+] [hideIndex]
/** This function is identical to the member function Div(). /** This function is identical to the member function Div().
@return float4(x / scalar, y / scalar, z / scalar, w * scalar); */ @return float4(x / scalar, y / scalar, z / scalar, w * scalar); */
Vector4 operator /(float rhs) const; Vector4 operator /(float rhs) const;
[[nodiscard]] Vector4 Div(float scalar) const; [[nodiscard]] Vector4 Div(float scalar) const { return *this / scalar; }
static Vector4 Div(const Vector4& rhs, float 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] /// Performs an unary negation of this vector. [similarOverload: operator+] [hideIndex]
/** This function is identical to the member function Neg(). /** This function is identical to the member function Neg().
@return float4(-x, -y, -z, -w). */ @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

@@ -11,13 +11,27 @@
#include <iostream> #include <iostream>
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry.hpp> #include <J3ML/Geometry.hpp>
#include <J3ML/J3ML.hpp> #include <J3ML/J3ML.hpp>
#include <jlog/Logger.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) int main(int argc, char** argv)
{ {
Matrix3x3 matrix_rep = {
0.9902160, 0.0000000, 0.1395431,
0.1273699, 0.4084874, -0.9038334,
-0.0570016, 0.9127640, 0.4044908};
Quaternion quat_rep = {0.5425029, 0.0586955, 0.0380374, 0.8371371};
AxisAngle aa_rep = { {0.9917912, 0.1073057, 0.069539}, 1.1575362};
using namespace J3ML::Math; using namespace J3ML::Math;
@@ -40,10 +54,48 @@ int main(int argc, char** argv)
} }
Ray a({420, 0, 0}, {1, 0, 0}); Ray a({420, 0, 0}, {1, 0, 0});
std::cout << a << std::endl; 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; 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; return 0;
} }

View File

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

View File

@@ -129,23 +129,6 @@ namespace J3ML::Geometry
return mostExtreme; return mostExtreme;
} }
Frustum Frustum::CreateFrustumFromCamera(const CoordinateFrame &cam, float aspect, float fovY, float zNear, float zFar) {
Frustum frustum;
const float halfVSide = zFar * tanf(fovY * 0.5f);
const float halfHSide = halfVSide * aspect;
const Vector3 frontMultFar = cam.Front * zFar;
// frustum.NearFace = Plane{cam.Position + cam.Front * zNear, cam.Front};
// frustum.FarFace = Plane{cam.Position + frontMultFar, -cam.Front};
// frustum.RightFace = Plane{cam.Position, Vector3::Cross(frontMultFar - cam.Right * halfHSide, cam.Up)};
// frustum.LeftFace = Plane{cam.Position, Vector3::Cross(cam.Up, frontMultFar+cam.Right*halfHSide)};
// frustum.TopFace = Plane{cam.Position, Vector3::Cross(cam.Right, frontMultFar - cam.Up * halfVSide)};
// frustum.BottomFace = Plane{cam.Position, Vector3::Cross(frontMultFar + cam.Up * halfVSide, cam.Right)};
return frustum;
}
PBVolume<6> Frustum::ToPBVolume() const { PBVolume<6> Frustum::ToPBVolume() const {
PBVolume<6> frustumVolume; PBVolume<6> frustumVolume;
frustumVolume.p[0] = NearPlane(); frustumVolume.p[0] = NearPlane();

View File

@@ -372,7 +372,7 @@ namespace J3ML::Geometry
Vector4 b = Vector4(poly.v[face.v[1]], 1.f); Vector4 b = Vector4(poly.v[face.v[1]], 1.f);
Vector4 c = Vector4(poly.v[face.v[2]], 1.f); Vector4 c = Vector4(poly.v[face.v[2]], 1.f);
Vector4 normal = (b-a).Cross(c-a); Vector4 normal = (b-a).Cross(c-a);
normal.Normalized(); normal.Normalize();
return normal; return normal;
// return ((vec)v[face.v[1]]-(vec)v[face.v[0]]).Cross((vec)v[face.v[2]]-(vec)v[face.v[0]]).Normalized(); // return ((vec)v[face.v[1]]-(vec)v[face.v[0]]).Cross((vec)v[face.v[2]]-(vec)v[face.v[0]]).Normalized();
} }
@@ -390,7 +390,7 @@ namespace J3ML::Geometry
normal.z += (double(poly.v[v0].x) - poly.v[v1].x) * (double(poly.v[v0].y) + poly.v[v1].y); // Project on xy normal.z += (double(poly.v[v0].x) - poly.v[v1].x) * (double(poly.v[v0].y) + poly.v[v1].y); // Project on xy
v0 = v1; v0 = v1;
} }
normal.Normalized(); normal.Normalize();
return normal; return normal;
#if 0 #if 0
cv bestNormal; cv bestNormal;

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

@@ -10,8 +10,7 @@
#include <format> #include <format>
#include <iomanip> #include <iomanip>
#include <strstream> #include <J3ML/J3ML.hpp>
#include "J3ML/J3ML.hpp"
#include <sstream> #include <sstream>
@@ -133,20 +132,10 @@ namespace J3ML::Math::Functions::Trigonometric {
} }
namespace J3ML::Math::Functions { namespace J3ML::Math::Functions { }
}
namespace J3ML { 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) { float Math::Functions::FastRSqrt(float x) {
return 1.f / FastSqrt(x); return 1.f / FastSqrt(x);
} }
@@ -169,9 +158,6 @@ namespace J3ML {
return 1.f / Sqrt(x); return 1.f / Sqrt(x);
} }
int SigFigsTable[] = {0,0,0,1,0,0,1,0,0,1}; int SigFigsTable[] = {0,0,0,1,0,0,1,0,0,1};
int DivBy[] = {1,1,1, 1000,1000,1000, 1000000, 1000000, 1000000, 1000000000, 1000000000,1000000000}; int DivBy[] = {1,1,1, 1000,1000,1000, 1000000, 1000000, 1000000, 1000000000, 1000000000,1000000000};
@@ -241,6 +227,20 @@ namespace J3ML {
return 1.f / x; return 1.f / x;
} }
float Math::Functions::QRSqrt(float x) {
long i;
float x2, y;
const float threehalfs = 1.5f;
x2 = x * 0.5f;
y = x;
i = *(long*) &y; // evil floating point bit level hacking
i = 0x5f3759df - (i >> 1); // what the fuck?
y = *(float*) &i;
y = y * (threehalfs - (x2 * y * y)); // increase precision of approximation via Newton's Method
return y;
}
float Math::Functions::Lerp(float a, float b, float t) { return a + t * (b-a);} float Math::Functions::Lerp(float a, float b, float t) { return a + t * (b-a);}
float Math::Functions::LerpMod(float a, float b, float mod, float t) { float Math::Functions::LerpMod(float a, float b, float mod, float t) {
@@ -299,31 +299,10 @@ namespace J3ML {
Math::Rotation::Rotation() : valueInRadians(0) {} // int BitTwiddling::CountBitsSet(u32 value) { }
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) {
}
namespace Math::Functions { namespace Math::Functions {
bool IsPow2(u32 number) { bool IsPow2(u32 number) {
return (number & (number - 1)) == 0; return (number & (number - 1)) == 0;
} }

View File

@@ -1,8 +1,8 @@
#include <J3ML/LinearAlgebra/AxisAngle.hpp> #include <J3ML/LinearAlgebra/AxisAngle.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp> #include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
namespace J3ML::LinearAlgebra { namespace J3ML::LinearAlgebra {
AxisAngle::AxisAngle() : axis(Vector3::Zero), angle(0) {}
AxisAngle::AxisAngle(const Vector3& axis, float angle) : axis(axis), angle(angle) {} AxisAngle::AxisAngle(const Vector3& axis, float angle) : axis(axis), angle(angle) {}
@@ -15,9 +15,68 @@ namespace J3ML::LinearAlgebra {
axis = { rhs.x*reciprocalSinAngle, rhs.y*reciprocalSinAngle, rhs.z*reciprocalSinAngle }; axis = { rhs.x*reciprocalSinAngle, rhs.y*reciprocalSinAngle, rhs.z*reciprocalSinAngle };
} }
AxisAngle::AxisAngle(const EulerAngleXYZ& e) {
auto a = AxisAngle(Quaternion(e)); Quaternion AxisAngle::ToQuaternion() const { return Quaternion(*this); }
axis = a.axis;
angle = a.angle; AxisAngle::AxisAngle(const Matrix3x3 &m) : AxisAngle(Quaternion(m)) { }
bool AxisAngle::Equals(const AxisAngle &rhs, float epsilon) {
return this->axis.Equals(rhs.axis, epsilon) && Math::Equal(angle, rhs.angle, epsilon);
} }
Matrix3x3 AxisAngle::ToMatrix3x3() const {
return Matrix3x3(*this);
}
void AxisAngle::Inverse() {
angle = -angle;
}
AxisAngle AxisAngle::Inverted() const {
return {axis, -angle};
}
AxisAngle AxisAngle::Lerp(const AxisAngle &rhs, float t) {
auto new_axis = axis.Lerp(rhs.axis, t);
float new_angle = Math::Lerp(angle, rhs.angle, t);
return {new_axis, new_angle};
}
AxisAngle AxisAngle::Slerp(const AxisAngle &rhs, float t) {
Quaternion a(*this);
Quaternion b(rhs);
Quaternion intermediate = a.Slerp(b, t);
return AxisAngle(intermediate);
}
bool AxisAngle::operator==(const AxisAngle &rhs) {
return Equals(rhs);
}
AxisAngle AxisAngle::Normalized() const {
AxisAngle copy(*this);
copy.Normalize();
return copy;
}
bool AxisAngle::IsIdentity() { return Math::Equal(angle, 0); }
void AxisAngle::Normalize() {
float axisLength = axis.Length();
if (axisLength > 0.0f)
axis /= axisLength;
else {
// Handle the case where the axis is a zero vector.
// You might want to set it to a default axis (e.g., (0,1,0))
axis = Vector3(0, 1, 0);
angle = 0;
}
}
Vector3 AxisAngle::Axis() const { return axis;}
float AxisAngle::Angle() const { return angle;}
} }

View File

@@ -1 +0,0 @@
#include <J3ML/LinearAlgebra/CoordinateFrame.hpp>

View File

@@ -1,38 +0,0 @@
#include <J3ML/LinearAlgebra/DirectionVector.hpp>
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
DirectionVectorRH::DirectionVectorRH(const Vector3& rhs) {
x = rhs.x;
y = rhs.y;
z = rhs.z;
}
DirectionVectorRH DirectionVectorRH::Forward(const Matrix3x3& rhs) {
return DirectionVectorRH(rhs.Col(2));
}
DirectionVectorRH DirectionVectorRH::Backward(const Matrix3x3& rhs) {
return DirectionVectorRH(-rhs.Col(2));
}
DirectionVectorRH DirectionVectorRH::Left(const Matrix3x3& rhs) {
return DirectionVectorRH(-rhs.Col(0));
}
DirectionVectorRH DirectionVectorRH::Right(const Matrix3x3& rhs) {
return DirectionVectorRH(rhs.Col(0));
}
DirectionVectorRH DirectionVectorRH::Up(const Matrix3x3 &rhs) {
return DirectionVectorRH(rhs.Col(1));
}
DirectionVectorRH DirectionVectorRH::Down(const Matrix3x3& rhs) {
return DirectionVectorRH(-rhs.Col(1));
}

View File

@@ -1,45 +0,0 @@
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
#include <cmath>
#include <algorithm>
namespace J3ML::LinearAlgebra {
EulerAngleXYZ::EulerAngleXYZ(float roll, float pitch, float yaw) {
this->roll = roll;
this->pitch = pitch;
this->yaw = yaw;
}
EulerAngleXYZ::EulerAngleXYZ(const AxisAngle& rhs) {
*this = EulerAngleXYZ(Quaternion(rhs));
}
EulerAngleXYZ::EulerAngleXYZ(const Quaternion& q) {
float sy = 2 * q.x * q.z + 2 * q.y * q.w;
bool gimbal_lock = std::abs(sy) > 0.99999f;
if (!gimbal_lock)
roll = Math::Degrees(std::atan2(-(2 * q.y * q.z - 2 * q.x * q.w),2 * q.w * q.w + 2 * q.z * q.z - 1));
else
roll = Math::Degrees(std::atan2(2 * q.y * q.z + 2 * q.x * q.w,2 * q.w * q.w + 2 * q.y * q.y - 1));
pitch = Math::Degrees(std::asin(sy));
if (!gimbal_lock)
yaw = Math::Degrees(std::atan2(-(2 * q.x * q.y - 2 * q.z * q.w),2 * q.w * q.w + 2 * q.x * q.x - 1));
else
yaw = 0;
}
EulerAngleXYZ::EulerAngleXYZ(const Matrix3x3& rhs) {
auto m = rhs.Transposed();
auto sy = m.At(0, 2);
auto unlocked = std::abs(sy) < 0.99999f;
roll = Math::Degrees(unlocked ? std::atan2(-m.At(1, 2), m.At(2, 2)) : std::atan2(m.At(2, 1), m.At(1, 1)));
pitch = Math::Degrees(std::asin(sy));
yaw = Math::Degrees(unlocked ? std::atan2(-m.At(0, 1), m.At(0, 0)) : 0);
}
}

View File

@@ -110,7 +110,7 @@ namespace J3ML::LinearAlgebra {
} }
Matrix3x3::Matrix3x3(const Quaternion& orientation) { Matrix3x3::Matrix3x3(const Quaternion& orientation) {
//*this = Matrix3x3(EulerAngleXYZ(orientation));
} }
float Matrix3x3::Determinant() const { float Matrix3x3::Determinant() const {
@@ -1087,6 +1087,18 @@ namespace J3ML::LinearAlgebra {
return m; return m;
} }
Vector3 Matrix3x3::ForwardDir() const { return Col(2).Normalized(); }
Vector3 Matrix3x3::BackwardDir() const { return -Col(2).Normalized(); }
Vector3 Matrix3x3::LeftDir() const { return -Col(0).Normalized(); }
Vector3 Matrix3x3::RightDir() const { return Col(0).Normalized(); }
Vector3 Matrix3x3::UpDir() const { return Col(1).Normalized(); }
Vector3 Matrix3x3::DownDir() const { return -Col(1).Normalized(); }
} }

View File

@@ -1,9 +1,10 @@
#include <iomanip>
#include <J3ML/LinearAlgebra/Matrix4x4.hpp> #include <J3ML/LinearAlgebra/Matrix4x4.hpp>
#include <J3ML/LinearAlgebra/Vector4.hpp> #include <J3ML/LinearAlgebra/Vector4.hpp>
#include <J3ML/LinearAlgebra/Matrix3x3.hpp> #include <J3ML/LinearAlgebra/Matrix3x3.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp> #include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/Matrices.inl> #include <J3ML/LinearAlgebra/Matrices.inl>
#include <iostream>
namespace J3ML::LinearAlgebra { 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() void Matrix4x4::InverseOrthonormal()
{ {
//assert(!ContainsProjection()); //assert(!ContainsProjection());
@@ -1014,6 +1020,46 @@ namespace J3ML::LinearAlgebra {
return true; 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 void
Matrix4x4::Set(float _00, float _01, float _02, float _03, float _10, float _11, float _12, float _13, float _20, 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) { float _21, float _22, float _23, float _30, float _31, float _32, float _33) {

View File

@@ -2,7 +2,6 @@
#include <J3ML/LinearAlgebra/Matrix4x4.hpp> #include <J3ML/LinearAlgebra/Matrix4x4.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp> #include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/AxisAngle.hpp> #include <J3ML/LinearAlgebra/AxisAngle.hpp>
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
#include <J3ML/LinearAlgebra/Vector4.hpp> #include <J3ML/LinearAlgebra/Vector4.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp> #include <J3ML/LinearAlgebra/Vector3.hpp>
@@ -16,17 +15,51 @@ namespace J3ML::LinearAlgebra {
} }
Quaternion::Quaternion(const Matrix3x3& ro_mat) { Quaternion::Quaternion(const Matrix3x3& ro_mat) {
auto m = ro_mat.Transposed(); // https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
auto m = ro_mat;//.Transposed();
auto m00 = m.At(0,0); auto m00 = m.At(0,0);
auto m01 = m.At(0, 1); auto m01 = m.At(0, 1);
auto m02 = m.At(0, 2); auto m02 = m.At(0, 2);
auto m10 = m.At(1,0); auto m10 = m.At(1, 0);
auto m11 = m.At(1, 1); auto m11 = m.At(1, 1);
auto m12 = m.At(1, 2); auto m12 = m.At(1, 2);
auto m20 = m.At(2,0); auto m20 = m.At(2, 0);
auto m21 = m.At(2, 1); auto m21 = m.At(2, 1);
auto m22 = m.At(2, 2); auto m22 = m.At(2, 2);
float tr = m00 + m11 + m22;
if (tr > 0) {
float S = Math::Sqrt(tr + 1.f) * 2; // S = 4*qw
w = 0.25f * S;
x = (m21 - m12) / S;
y = (m02 - m20) / S;
z = (m10 - m01) / S;
} else {
if (m00 > m11 && m00 > m22) {
float S = 2.f * Math::Sqrt(1.f + m00 - m11 - m22);
w = (m21 - m12) / S;
x = 0.25f * S;
y = (m01 + m10) / S;
z = (m02 + m20) / S;
} else if (m11 > m22) {
float s = 2.f * Math::Sqrt(1.f + m11 - m00 - m22);
w = (m02 - m20) / s;
x = (m01 + m10) / s;
y = 0.25f * s;
z = (m12 + m21) / s;
} else {
float s = 2.f * Math::Sqrt(1.f + m22 - m00 - m11);
w = (m10 - m01) / s;
x = (m02 + m20) / s;
y = (m12 + m21) / s;
z = 0.25f * s;
}
}
/*
auto field_w = std::sqrt(1.f + m00 + m11 + m22) / 2.f; auto field_w = std::sqrt(1.f + m00 + m11 + m22) / 2.f;
float w4 = (4.f * field_w); float w4 = (4.f * field_w);
@@ -34,7 +67,9 @@ namespace J3ML::LinearAlgebra {
y = (m02 - m20) / w4; y = (m02 - m20) / w4;
z = (m10 - m01) / w4; z = (m10 - m01) / w4;
w = field_w; w = field_w;
Normalize(); bool success = Normalize();
// TODO: Validate normalization success.
*/
} }
Quaternion::Quaternion(const Matrix4x4& ro_mat) { Quaternion::Quaternion(const Matrix4x4& ro_mat) {
@@ -74,7 +109,7 @@ namespace J3ML::LinearAlgebra {
} }
Quaternion Quaternion::operator*(float scalar) const { Quaternion Quaternion::operator*(float scalar) const {
return Quaternion(x * scalar, y * scalar, z * scalar, w * scalar); return {x * scalar, y * scalar, z * scalar, w * scalar};
} }
Quaternion Quaternion::operator/(float scalar) const { Quaternion Quaternion::operator/(float scalar) const {
@@ -91,7 +126,6 @@ namespace J3ML::LinearAlgebra {
Quaternion::Quaternion(float X, float Y, float Z, float W) : x(X), y(Y), z(Z), w(W) {} Quaternion::Quaternion(float X, float Y, float Z, float W) : x(X), y(Y), z(Z), w(W) {}
// TODO: implement
float Quaternion::Dot(const Quaternion &rhs) const { float Quaternion::Dot(const Quaternion &rhs) const {
return x * rhs.x + y * rhs.y + z * rhs.z + w * rhs.w; return x * rhs.x + y * rhs.y + z * rhs.z + w * rhs.w;
} }
@@ -204,12 +238,13 @@ namespace J3ML::LinearAlgebra {
} }
Quaternion::Quaternion(const AxisAngle& angle) { Quaternion::Quaternion(const AxisAngle& angle) {
double s = std::sin(angle.angle / 2); float s = Math::Sin(angle.angle / 2.f);
x = angle.axis.x * s; x = angle.axis.x * s;
y = angle.axis.y * s; y = angle.axis.y * s;
z = angle.axis.z * s; z = angle.axis.z * s;
w = std::cos(angle.angle / 2); w = Math::Cos(angle.angle / 2);
Normalize(); bool success = Normalize();
// TODO: Validate normalization success.
} }
Quaternion Quaternion::RandomRotation(RNG &rng) { Quaternion Quaternion::RandomRotation(RNG &rng) {
@@ -353,27 +388,29 @@ namespace J3ML::LinearAlgebra {
float Quaternion::Length() const { return std::sqrt(LengthSquared()); } float Quaternion::Length() const { return std::sqrt(LengthSquared()); }
Quaternion::Quaternion(const EulerAngleXYZ& rhs) {
float cos_roll = Math::Cos(0.5f * Math::Radians(rhs.roll));
float sin_roll = Math::Sin(0.5f * Math::Radians(rhs.roll));
float cos_pitch = Math::Cos(0.5f * Math::Radians(rhs.pitch));
float sin_pitch = Math::Sin(0.5f * Math::Radians(rhs.pitch));
float cos_yaw = Math::Cos(0.5f * Math::Radians(rhs.yaw));
float sin_yaw = Math::Sin(0.5f * Math::Radians(rhs.yaw));
x = cos_roll * sin_pitch * sin_yaw + sin_roll * cos_pitch * cos_yaw;
y = -sin_roll * cos_pitch * sin_yaw + cos_roll * sin_pitch * cos_yaw;
z = cos_roll * cos_pitch * sin_yaw + sin_roll * sin_pitch * cos_yaw;
w = -sin_roll * sin_pitch * sin_yaw + cos_roll * cos_pitch * cos_yaw;
Normalize();
}
Quaternion::Quaternion(const Quaternion& rhs) { Quaternion::Quaternion(const Quaternion& rhs) {
x = rhs.x; x = rhs.x;
y = rhs.y; y = rhs.y;
z = rhs.z; z = rhs.z;
w = rhs.w; w = rhs.w;
} }
bool Quaternion::Equals(const Quaternion &rhs, float epsilon) const {
return Math::Equal(this->x, rhs.x, epsilon) &&
Math::Equal(this->y, rhs.y, epsilon) &&
Math::Equal(this->z, rhs.z, epsilon) &&
Math::Equal(this->w, rhs.w, epsilon);
}
Quaternion Quaternion::RotateX(float rad) {
return Quaternion(AxisAngle({1,0,0}, rad));
}
Quaternion Quaternion::RotateY(float rad) {
return Quaternion(AxisAngle({0,1,0}, rad));
}
Quaternion Quaternion::RotateZ(float rad) {
return Quaternion(AxisAngle({0,0,1}, rad));
}
} }

View File

@@ -27,4 +27,73 @@ namespace J3ML::LinearAlgebra {
Transform2D Transform2D::Translate(const LinearAlgebra::Vector2 &input) const { Transform2D Transform2D::Translate(const LinearAlgebra::Vector2 &input) const {
return Translate(input.x, input.y); 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

@@ -37,12 +37,12 @@ namespace J3ML::LinearAlgebra {
bool Vector2::operator==(const Vector2& rhs) const bool Vector2::operator==(const Vector2& rhs) const
{ {
return this->IsWithinMarginOfError(rhs); return x == rhs.x && y == rhs.y;
} }
bool Vector2::operator!=(const Vector2& rhs) const bool Vector2::operator!=(const Vector2& rhs) const
{ {
return this->IsWithinMarginOfError(rhs) == false; return !(*this == rhs);
} }
Vector2 Vector2::Min(const Vector2& min) const Vector2 Vector2::Min(const Vector2& min) const
@@ -513,6 +513,20 @@ namespace J3ML::LinearAlgebra {
y = rhs.y; y = rhs.y;
} }
bool Vector2::Equals(const Vector2 &rhs, float epsilon) const {
return Math::EqualAbs(x, rhs.x, epsilon) &&
Math::EqualAbs(y, rhs.y, epsilon);
}
bool Vector2::Equals(float x_, float y_, float epsilon) const {
return Math::EqualAbs(x, x_, epsilon) &&
Math::EqualAbs(y, y_, epsilon);
}
bool Vector2::PreciselyEquals(const Vector2 &rhs) const {
return this->x == rhs.x && this->y == rhs.y;
}
Vector2 operator*(float lhs, const Vector2 &rhs) { Vector2 operator*(float lhs, const Vector2 &rhs) {
return {lhs * rhs.x, lhs * rhs.y}; return {lhs * rhs.x, lhs * rhs.y};
} }

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::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 #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

@@ -53,19 +53,19 @@ namespace TriangleTests {
); );
// Should collide exactly on V0 // Should collide exactly on V0
jtest::check(Intersects(xyTriangle, zxTriangle)); //jtest::check(Intersects(xyTriangle, zxTriangle));
// Should collide across xyTriangle's edge and zxTriangle's face // Should collide across xyTriangle's edge and zxTriangle's face
jtest::check(Intersects(xyTriangle, zxTriangle.Translated(Vector3(0.0f, 0.0f, -1.0)))); //jtest::check(Intersects(xyTriangle, zxTriangle.Translated(Vector3(0.0f, 0.0f, -1.0))));
// Should collide exactly on V1 // Should collide exactly on V1
jtest::check(Intersects(xyTriangle, zxTriangle.Translated(Vector3(0.0f, 0.0f, -2.0)))); //jtest::check(Intersects(xyTriangle, zxTriangle.Translated(Vector3(0.0f, 0.0f, -2.0))));
// xyTriangle's face should be poked by zxTriangle's V0 // xyTriangle's face should be poked by zxTriangle's V0
jtest::check(Intersects(xyTriangle, zxTriangle.Translated(Vector3(1.0f, 1.0f, 0.0f)))); //jtest::check(Intersects(xyTriangle, zxTriangle.Translated(Vector3(1.0f, 1.0f, 0.0f))));
// xyTriangle's face should be cut by zxTriangle // xyTriangle's face should be cut by zxTriangle
jtest::check(Intersects(xyTriangle, zxTriangle.Translated(Vector3(1.0f, 1.0f, -0.5f)))); //jtest::check(Intersects(xyTriangle, zxTriangle.Translated(Vector3(1.0f, 1.0f, -0.5f))));
// Should not collide // Should not collide
jtest::check(!Intersects(xyTriangle, zxTriangle.Translated(Vector3(1.0f, 1.0f, 1.0f)))); //jtest::check(!Intersects(xyTriangle, zxTriangle.Translated(Vector3(1.0f, 1.0f, 1.0f))));
// Should not collide // Should not collide
jtest::check(!Intersects(xyTriangle, zxTriangle.Translated(Vector3(0.0f, 0.0f, -3.0f)))); //jtest::check(!Intersects(xyTriangle, zxTriangle.Translated(Vector3(0.0f, 0.0f, -3.0f))));
Triangle yxTriangle( Triangle yxTriangle(
{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f},
@@ -74,11 +74,11 @@ namespace TriangleTests {
); );
// Should collide on V0-V1 edge // Should collide on V0-V1 edge
jtest::check(Intersects(yxTriangle, yxTriangle)); //jtest::check(Intersects(yxTriangle, yxTriangle));
// Should not collide // Should not collide
jtest::check(!Intersects(xyTriangle, yxTriangle.Translated(Vector3(0.0f, 1.0f, 0.0f)))); //jtest::check(!Intersects(xyTriangle, yxTriangle.Translated(Vector3(0.0f, 1.0f, 0.0f))));
// Should not collide // Should not collide
jtest::check(!Intersects(yxTriangle, yxTriangle.Translated(Vector3(0.0f, 0.0f, 1.0f)))); //jtest::check(!Intersects(yxTriangle, yxTriangle.Translated(Vector3(0.0f, 0.0f, 1.0f))));
Triangle zyInvertedTriangle( Triangle zyInvertedTriangle(
{0.0f, 1.0f, -1.0f}, {0.0f, 1.0f, -1.0f},
@@ -86,11 +86,11 @@ namespace TriangleTests {
{0.0f, 1.0f, 1.0f} {0.0f, 1.0f, 1.0f}
); );
// Should collide exactly on V1 // Should collide exactly on V1
jtest::check(Intersects(xyTriangle, zyInvertedTriangle)); //jtest::check(Intersects(xyTriangle, zyInvertedTriangle));
// Should not collide // Should not collide
jtest::check(!Intersects(xyTriangle, zyInvertedTriangle.Translated(Vector3(0.0f, 1.0f, 0.0f)))); //jtest::check(!Intersects(xyTriangle, zyInvertedTriangle.Translated(Vector3(0.0f, 1.0f, 0.0f))));
// Should not collide // Should not collide
jtest::check(!Intersects(xyTriangle, zyInvertedTriangle.Translated(Vector3(0.25f, 0.75f, 0.0f)))); //jtest::check(!Intersects(xyTriangle, zyInvertedTriangle.Translated(Vector3(0.25f, 0.75f, 0.0f))));
}); });
} }

View File

@@ -7,17 +7,38 @@ namespace AxisAngleTests {
inline void Define() { inline void Define() {
using namespace jtest; using namespace jtest;
AxisAngleUnit += Test("From_Quaternion", [] { AxisAngleUnit += Test("CtorFromQuaternion", [] {
AxisAngle expected_result({0.3860166, 0.4380138, 0.8118714}, 0.6742209); AxisAngle expected_result({0.3860166, 0.4380138, 0.8118714}, 0.6742209);
Quaternion q(0.1276794, 0.1448781, 0.2685358, 0.9437144); Quaternion q(0.1276794, 0.1448781, 0.2685358, 0.9437144);
AxisAngle from_quaternion(q); AxisAngle from_quaternion(q);
jtest::check(Math::EqualAbs(expected_result.axis.x, from_quaternion.axis.x, 1e-6f)); jtest::check(expected_result.Equals(from_quaternion));
jtest::check(Math::EqualAbs(expected_result.axis.y, from_quaternion.axis.y, 1e-6f));
jtest::check(Math::EqualAbs(expected_result.axis.z, from_quaternion.axis.z, 1e-6f));
jtest::check(Math::EqualAbs(expected_result.angle, from_quaternion.angle, 1e-6f));
}); });
AxisAngleUnit += Test("CtorFromMatrix3x3", [] {
Matrix3x3 m {
0.9811029, -0.1925617, 0.0188971,
0.1925617, 0.9622058, -0.1925617,
0.0188971, 0.1925617, 0.9811029 };
AxisAngle expected { {0.7071068, 0, 0.7071068}, 0.2758069};
AxisAngle from_matrix(m);
jtest::check(expected.Equals(from_matrix));
});
AxisAngleUnit += Test("ToQuaternion", [] {});
AxisAngleUnit += Test("ToMatrix3x3", [] {});
AxisAngleUnit += Test("Normalize", [] {});
AxisAngleUnit += Test("Inverse", [] {});
} }
inline void Run() { inline void Run() {
AxisAngleUnit.RunAll(); AxisAngleUnit.RunAll();

View File

@@ -1,28 +0,0 @@
//
// Created by josh on 12/26/2023.
//
#include <jtest/jtest.hpp>
#include <jtest/Unit.hpp>
jtest::Unit EulerAngleUnit {"EulerAngle_XYZ"};
namespace EulerAngleTests {
inline void Define() {
using namespace jtest;
EulerAngleUnit += Test("From_Quaternion", [] {
EulerAngleXYZ expected_result(-170, 88, -160);
Quaternion q(0.1840604, 0.6952024, 0.1819093, 0.6706149);
EulerAngleXYZ from_quaternion(q);
jtest::check(Math::EqualAbs(Math::Radians(expected_result.roll), Math::Radians(from_quaternion.roll), 1e-5f));
jtest::check(Math::EqualAbs(Math::Radians(expected_result.pitch), Math::Radians(from_quaternion.pitch), 1e-5f));
jtest::check(Math::EqualAbs(Math::Radians(expected_result.yaw), Math::Radians(from_quaternion.yaw), 1e-5f));
});
}
inline void Run() {
EulerAngleUnit.RunAll();
}
}

View File

@@ -11,7 +11,7 @@ namespace Matrix3x3Tests
using namespace jtest; using namespace jtest;
using namespace J3ML::LinearAlgebra; using namespace J3ML::LinearAlgebra;
Matrix3x3Unit += Test("AngleTypeRound-TripConversion", [] { /*Matrix3x3Unit += Test("AngleTypeRound-TripConversion", [] {
EulerAngleXYZ expected_result(8, 60, -27); EulerAngleXYZ expected_result(8, 60, -27);
Matrix3x3 m(expected_result); Matrix3x3 m(expected_result);
@@ -25,9 +25,9 @@ namespace Matrix3x3Tests
jtest::check(Math::EqualAbs(Math::Radians(expected_result.roll), Math::Radians(round_trip.roll), 1e-6f)); jtest::check(Math::EqualAbs(Math::Radians(expected_result.roll), Math::Radians(round_trip.roll), 1e-6f));
jtest::check(Math::EqualAbs(Math::Radians(expected_result.pitch), Math::Radians(round_trip.pitch), 1e-6f)); jtest::check(Math::EqualAbs(Math::Radians(expected_result.pitch), Math::Radians(round_trip.pitch), 1e-6f));
jtest::check(Math::EqualAbs(Math::Radians(expected_result.yaw), Math::Radians(round_trip.yaw), 1e-6f)); jtest::check(Math::EqualAbs(Math::Radians(expected_result.yaw), Math::Radians(round_trip.yaw), 1e-6f));
}); });*/
Matrix3x3Unit += Test("From_EulerAngleXYZ", []{ /*Matrix3x3Unit += Test("From_EulerAngleXYZ", []{
Matrix3x3 expected_result(Vector3(0.4455033, 0.2269952, 0.8660254), Matrix3x3 expected_result(Vector3(0.4455033, 0.2269952, 0.8660254),
Vector3(-0.3421816, 0.9370536, -0.0695866), Vector3(-0.3421816, 0.9370536, -0.0695866),
Vector3(-0.8273081, -0.2653369, 0.4951340) Vector3(-0.8273081, -0.2653369, 0.4951340)
@@ -45,7 +45,7 @@ namespace Matrix3x3Tests
jtest::check(Math::EqualAbs(expected_result.At(2, 0), from_euler.At(2, 0), 1e-6f)); jtest::check(Math::EqualAbs(expected_result.At(2, 0), from_euler.At(2, 0), 1e-6f));
jtest::check(Math::EqualAbs(expected_result.At(2, 1), from_euler.At(2, 1), 1e-6f)); jtest::check(Math::EqualAbs(expected_result.At(2, 1), from_euler.At(2, 1), 1e-6f));
jtest::check(Math::EqualAbs(expected_result.At(2, 2), from_euler.At(2, 2), 1e-6f)); jtest::check(Math::EqualAbs(expected_result.At(2, 2), from_euler.At(2, 2), 1e-6f));
}); });*/
Matrix3x3Unit += Test("Add_Unary", [] Matrix3x3Unit += Test("Add_Unary", []
{ {

View File

@@ -11,8 +11,7 @@ namespace Matrix4x4Tests {
using namespace J3ML::LinearAlgebra; using namespace J3ML::LinearAlgebra;
using namespace J3ML::Math; 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 m(1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16);
Matrix4x4 m2 = +m; Matrix4x4 m2 = +m;
jtest::check(m.Equals(m2)); jtest::check(m.Equals(m2));
@@ -38,6 +37,13 @@ namespace Matrix4x4Tests {
} }
}); });
Matrix4x4Unit += Test("CtorFromRotationMatrix", []{
Matrix3x3 m = Matrix3x3::RotateX(40);
Matrix4x4 from3x3(m);
});
Matrix4x4Unit += Test("Ctor", []{ Matrix4x4Unit += Test("Ctor", []{
RNG rng; RNG rng;
Matrix3x3 m = Matrix3x3::RandomGeneral(rng, -10.f, 10.f); Matrix3x3 m = Matrix3x3::RandomGeneral(rng, -10.f, 10.f);
@@ -46,16 +52,16 @@ namespace Matrix4x4Tests {
for (int y = 0; y < 3; ++y) for (int y = 0; y < 3; ++y)
for (int x = 0; x < 3; ++x) for (int x = 0; x < 3; ++x)
assert(Math::EqualAbs(m.At(y, x), m2.At(y, x))); check(Math::EqualAbs(m.At(y, x), m2.At(y, x)));
jtest::check(Math::EqualAbs(m2[0][3], 0.f)); /*jtest::check(Math::EqualAbs(m2[0][3], 0.f));
jtest::check(Math::EqualAbs(m2[1][3], 0.f)); jtest::check(Math::EqualAbs(m2[1][3], 0.f));
jtest::check(Math::EqualAbs(m2[2][3], 0.f)); jtest::check(Math::EqualAbs(m2[2][3], 0.f));
jtest::check(Math::EqualAbs(m2[3][0], 0.f)); jtest::check(Math::EqualAbs(m2[3][0], 0.f));
jtest::check(Math::EqualAbs(m2[3][1], 0.f)); jtest::check(Math::EqualAbs(m2[3][1], 0.f));
jtest::check(Math::EqualAbs(m2[3][2], 0.f)); jtest::check(Math::EqualAbs(m2[3][2], 0.f));
jtest::check(Math::EqualAbs(m2[3][3], 0.f)); jtest::check(Math::EqualAbs(m2[3][3], 0.f));*/
}); });
Matrix4x4Unit += Test("SetRow", [] Matrix4x4Unit += Test("SetRow", []
@@ -104,13 +110,52 @@ namespace Matrix4x4Tests {
}); });
Matrix4x4Unit += Test("CtorFromQuatTrans", [] {}); Matrix4x4Unit += Test("CtorFromQuatTrans", [] {
Matrix4x4Unit += Test("Translate", [] {}); RNG rng;
Matrix4x4Unit += Test("Scale", [] {}); constexpr float SCALE = 1e2f;
Matrix4x4Unit += Test("InverseOrthogonalUniformScale", [] {}); Vector3 t = Vector3::RandomBox(rng, Vector3(-SCALE, -SCALE, -SCALE), Vector3(SCALE, SCALE, SCALE));
Matrix4x4Unit += Test("InverseOrthonormal", [] {}); Quaternion q = Quaternion::RandomRotation(rng);
Matrix4x4Unit += Test("DeterminantCorrectness", [] { });
Matrix4x4Unit += Test("MulMat3x3", [] {}); 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() { inline void Run() {

View File

@@ -4,8 +4,8 @@
#include <jtest/jtest.hpp> #include <jtest/jtest.hpp>
#include <jtest/Unit.hpp> #include <jtest/Unit.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp> #include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
#include <J3ML/Algorithm/RNG.hpp> #include <J3ML/Algorithm/RNG.hpp>
#include <J3ML/Math.hpp>
jtest::Unit QuaternionUnit {"Quaternion"}; jtest::Unit QuaternionUnit {"Quaternion"};
namespace QuaternionTests { namespace QuaternionTests {
@@ -73,15 +73,11 @@ namespace QuaternionTests {
Quaternion lerp = q.Lerp(q2, t); Quaternion lerp = q.Lerp(q2, t);
} }
}); });
QuaternionUnit += Test("From_EulerAngleXYZ", [] {
Quaternion expected_result(0.1819093, 0.6706149, 0.1840604, 0.6952024);
EulerAngleXYZ e(10, 88, 20);
Quaternion from_euler(e);
jtest::check(Math::EqualAbs(expected_result.x, from_euler.x, 1e-6f)); QuaternionUnit += Test("Constants", [] {
jtest::check(Math::EqualAbs(expected_result.y, from_euler.y, 1e-6f)); Quaternion id {0.f, 0.f, 0.f, 1.f};
jtest::check(Math::EqualAbs(expected_result.z, from_euler.z, 1e-6f));
jtest::check(Math::EqualAbs(expected_result.w, from_euler.w, 1e-6f)); jtest::check(id.Equals(Quaternion::Identity));
}); });
QuaternionUnit += Test("From_AxisAngle", [] { QuaternionUnit += Test("From_AxisAngle", [] {
@@ -95,13 +91,47 @@ namespace QuaternionTests {
jtest::check(Math::EqualAbs(expected_result.w, from_axis.w, 1e-6f)); jtest::check(Math::EqualAbs(expected_result.w, from_axis.w, 1e-6f));
}); });
QuaternionUnit += Test("Mat4x4Conversion", [] { throw("Not Implemented"); }); QuaternionUnit += Test("Ctor_FromMatrix3x3", [] {
QuaternionUnit += Test("MulOpQuat", [] { throw("Not Implemented"); }); // TODO: Test multiple rotation values.
// https://www.andre-gaschler.com/rotationconverter/
Matrix3x3 matrix_rep = {
0.9902160, 0.0000000, 0.1395431,
0.1273699, 0.4084874, -0.9038334,
-0.0570016, 0.9127640, 0.4044908};
Quaternion expected = {0.5425029, 0.0586955, 0.0380374, 0.8371371};
Quaternion from_mat = Quaternion(matrix_rep);
jtest::check(expected.Equals(from_mat));
});
QuaternionUnit += Test("Ctor_FromMatrix4x4", [] {
Matrix4x4 matrix_rep = {
0.9799671, 0.1991593, -0.0000000, 0,
-0.1977032, 0.9728020, -0.1207050, 0,
-0.0240395, 0.1182869, 0.9926884, 0,
0, 0, 0, 0};
Quaternion expected {0.0601595, 0.0060513, -0.0998991, 0.9931588};
Quaternion q (matrix_rep);
jtest::check(q.Equals(expected));
});
QuaternionUnit += Test("MulOpQuat", [] {
//Quaternion a =
});
QuaternionUnit += Test("DivOpQuat", [] { throw("Not Implemented"); }); QuaternionUnit += Test("DivOpQuat", [] { throw("Not Implemented"); });
QuaternionUnit += Test("Lerp", [] { throw("Not Implemented"); }); QuaternionUnit += Test("Lerp", [] {
Quaternion a = Quaternion::RotateX(Math::PiOverTwo);
Quaternion b = Quaternion::RotateX(-Math::PiOverTwo);
Quaternion expected {0,0,0,0};
Quaternion result = a.Lerp(b, 0.5f);
});
QuaternionUnit += Test("RotateFromTo", [] { throw("Not Implemented"); }); QuaternionUnit += Test("RotateFromTo", [] { throw("Not Implemented"); });
QuaternionUnit += Test("Transform", [] { throw("Not Implemented"); }); QuaternionUnit += Test("Transform", [] { throw("Not Implemented"); });
} }
inline void Run() inline void Run()

View File

@@ -35,7 +35,7 @@ namespace Vector4Tests
jtest::check_float_eq(Input.w, 1); jtest::check_float_eq(Input.w, 1);
}); });
Vector4Unit += Test("Vector4::Addition_Op", [] { Vector4Unit += Test("Addition_Op", [] {
Vector4 A (1, 1, 1, 1); Vector4 A (1, 1, 1, 1);
Vector4 B (2, 2, 2, 2); Vector4 B (2, 2, 2, 2);
@@ -43,6 +43,19 @@ namespace Vector4Tests
jtest::check_v4_eq(A + B, ExpectedResult); jtest::check_v4_eq(A + B, ExpectedResult);
}); });
Vector4Unit += Test("Addition_Method", [] {
Vector4 A (1, 2, 3, 4);
Vector4 B (2, 2, 2, 2);
Vector4 Expected(3, 4, 5, 6);
jtest::check_v4_eq(A.Add(B), Expected);
});
Vector4Unit += Test("Addition_Static", [] {
});
} }
inline void Run() inline void Run()

View File

@@ -15,7 +15,6 @@
#include "Geometry/AABBTests.hpp" #include "Geometry/AABBTests.hpp"
#include "Geometry/FrustumTests.hpp" #include "Geometry/FrustumTests.hpp"
#include "LinearAlgebra/EulerAngleTests.hpp"
#include "LinearAlgebra/AxisAngleTests.hpp" #include "LinearAlgebra/AxisAngleTests.hpp"
#include "LinearAlgebra/Matrix2x2Tests.hpp" #include "LinearAlgebra/Matrix2x2Tests.hpp"
#include "LinearAlgebra/Matrix3x3Tests.hpp" #include "LinearAlgebra/Matrix3x3Tests.hpp"
@@ -69,7 +68,6 @@ namespace LinearAlgebraTests
Vector3Tests::Define(); Vector3Tests::Define();
Vector4Tests::Define(); Vector4Tests::Define();
AxisAngleTests::Define(); AxisAngleTests::Define();
EulerAngleTests::Define();
QuaternionTests::Define(); QuaternionTests::Define();
Matrix2x2Tests::Define(); Matrix2x2Tests::Define();
Matrix3x3Tests::Define(); Matrix3x3Tests::Define();
@@ -82,7 +80,6 @@ namespace LinearAlgebraTests
Vector3Tests::Run(); Vector3Tests::Run();
Vector4Tests::Run(); Vector4Tests::Run();
AxisAngleTests::Run(); AxisAngleTests::Run();
EulerAngleTests::Run();
QuaternionTests::Run(); QuaternionTests::Run();
Matrix2x2Tests::Run(); Matrix2x2Tests::Run();
Matrix3x3Tests::Run(); Matrix3x3Tests::Run();