Compare commits

..

55 Commits

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
4de703209c Fix build errors and migrate packages to latest release.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m48s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 20s
2024-12-25 16:35:33 -05:00
6aa4a33121 Merge remote-tracking branch 'origin/main'
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m28s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 30s
# Conflicts:
#	include/J3ML/J3ML.hpp
#	src/J3ML/LinearAlgebra/Matrix3x3.cpp
#	src/J3ML/LinearAlgebra/Vector2.cpp
#	tests/LinearAlgebra/Matrix3x3Tests.hpp
2024-12-11 01:36:25 -05:00
b24328488b Fix includes 2024-12-11 01:34:38 -05:00
1e3e2f42f2 Implement Demo of new Trigonometrics 2024-12-11 01:34:22 -05:00
88dad23e50 Implement Trigonometric::SignOfSin, Trigonometric::SignOfCos, Trigonometric::SignOfTan,
Trigonometric::SignOfSin, and Trigonometric::QuadrantOf
2024-12-11 01:34:12 -05:00
f4d8523bdc Implement Vector2 Less-Than and Greater-Than operators 2024-12-11 01:31:31 -05:00
df2c8b31bf More operators
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m0s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 20s
2024-12-04 11:46:14 -05:00
d715391d2a Vector2i operators.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m18s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 22s
2024-12-03 19:47:30 -05:00
13a68eea45 right-handed DirectionVector
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m49s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 30s
2024-11-27 19:46:58 -05:00
79e617b780 RoundTrip angle conversion.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m23s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 31s
2024-11-19 15:39:06 -05:00
aaea5ff53e AxisAngle FromQuaternion
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m52s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 24s
2024-11-19 09:13:37 -05:00
2caa4c8412 Quaternion from EulerAngleXYZ
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m14s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 21s
2024-11-18 21:51:40 -05:00
bb1b1b5a13 Remove EulerAngle & Add EulerAngleXYZ.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m38s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 26s
2024-11-18 20:39:08 -05:00
fa6d2fefcc ToEulerAngle
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m30s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 45s
2024-11-14 23:57:33 -05:00
80b752128c Update Vector3.cpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m24s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 30s
Added missing Vector3::One
2024-10-31 02:09:23 -04:00
3fc9ca3954 Update Vector3.hpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m36s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 27s
fixed a documentation copy paste mistake
2024-10-31 02:02:03 -04:00
50 changed files with 1978 additions and 1011 deletions

View File

@@ -34,12 +34,12 @@ set_target_properties(J3ML PROPERTIES LINKER_LANGUAGE CXX)
CPMAddPackage(
NAME jtest
URL https://git.redacted.cc/josh/jtest/archive/Release-1.4.zip
URL https://git.redacted.cc/josh/jtest/archive/Release-1.5.zip
)
target_include_directories(J3ML PUBLIC ${jtest_SOURCE_DIR}/include)
target_link_libraries(J3ML PUBLIC jtest)
target_link_libraries(J3ML PUBLIC jlog jtest)
if(WIN32)
#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
* <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.
* **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.
@@ -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.
* **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
#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>
int main() {
// Create two 3D vectors
Vector3 v1(1.0, 2.0, 3.0);
Vector3 v2(4.0, 5.0, 6.0);
// Perform vector addition
Vector3 result = v1 + v2;
// Output the result
std::cout << "Result: " << result << std::endl;
return 0;
}
Matrix3x3 mRotation = Matrix3x3::RotateX(Math::PiOverTwo);
Quaternion qRotation(mRotation); // Convert to Quaternion
AxisAngle aRotation(qRotation); // Convert to AxisAngle
EulerAngleXYZ eRotation(aRotation); // Convert to Euler Angle (XYZ)
```
For more detailed usage instructions and examples, please refer to the documentation.

View File

@@ -17,7 +17,7 @@
// Transcribed from here: explicit form and derivative
// https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves
#include "J3ML/LinearAlgebra/Vector2.hpp"
#include <J3ML/LinearAlgebra/Vector2.hpp>
namespace J3ML::Algorithm
{

View File

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

View File

@@ -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
if the defaults were uninitialized. */
Frustum();
static Frustum CreateFrustumFromCamera(const CoordinateFrame& cam, float aspect, float fovY, float zNear, float zFar);
public:
/// Quickly returns an arbitrary point inside this Frustum. Used in GJK intersection test.
[[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 {
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

@@ -17,18 +17,114 @@
#include <cassert>
#include <vector>
/// 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.
#undef USE_LOOKUP_TABLES /// Pre-computed lookup tables.
#undef USE_SSE /// Streaming SIMD Extensions (x86)
#undef USE_NEON /// ARM Vector Processing
#undef USE_AVX /// Advanced Vector Extensions (x86)
/// TODO: Implement lookup tables.
/// TODO: Implement constexpr Trigonometric LUT generators that are parameterized (samples, samples-per-period, etc.)
#ifdef USE_LOOKUP_TABLES
static float fast_cossin_table[MAX_CIRCLE_ANGLE];
#define LUT_SAMPLES 1024
#pragma region Trigonometric Lookup Tables
// Formula: sin(2*pi*t/T)
/** Generated using Dr LUT - Free Lookup Table Generator
* https://github.com/ppelikan/drlut
**/
// Formula: sin(2*pi*t/T)
const uint8_t u8_sin_lut[1024] = {
127,128,129,129,130,131,132,132,133,134,135,136,136,
137,138,139,139,140,141,142,143,143,144,145,146,146,
147,148,149,149,150,151,152,153,153,154,155,156,156,
157,158,159,159,160,161,162,162,163,164,165,165,166,
167,168,168,169,170,171,171,172,173,173,174,175,176,
176,177,178,178,179,180,181,181,182,183,183,184,185,
185,186,187,188,188,189,190,190,191,192,192,193,194,
194,195,196,196,197,198,198,199,199,200,201,201,202,
203,203,204,205,205,206,206,207,208,208,209,209,210,
211,211,212,212,213,213,214,215,215,216,216,217,217,
218,218,219,220,220,221,221,222,222,223,223,224,224,
225,225,226,226,227,227,228,228,229,229,229,230,230,
231,231,232,232,233,233,233,234,234,235,235,236,236,
236,237,237,238,238,238,239,239,239,240,240,240,241,
241,241,242,242,242,243,243,243,244,244,244,245,245,
245,245,246,246,246,247,247,247,247,248,248,248,248,
249,249,249,249,249,250,250,250,250,250,251,251,251,
251,251,251,252,252,252,252,252,252,252,253,253,253,
253,253,253,253,253,253,253,253,254,254,254,254,254,
254,254,254,254,254,254,254,254,254,254,254,254,254,
254,254,254,254,254,254,254,254,254,254,254,253,253,
253,253,253,253,253,253,253,253,253,252,252,252,252,
252,252,252,251,251,251,251,251,251,250,250,250,250,
250,249,249,249,249,249,248,248,248,248,247,247,247,
247,246,246,246,245,245,245,245,244,244,244,243,243,
243,242,242,242,241,241,241,240,240,240,239,239,239,
238,238,238,237,237,236,236,236,235,235,234,234,233,
233,233,232,232,231,231,230,230,229,229,229,228,228,
227,227,226,226,225,225,224,224,223,223,222,222,221,
221,220,220,219,218,218,217,217,216,216,215,215,214,
213,213,212,212,211,211,210,209,209,208,208,207,206,
206,205,205,204,203,203,202,201,201,200,199,199,198,
198,197,196,196,195,194,194,193,192,192,191,190,190,
189,188,188,187,186,185,185,184,183,183,182,181,181,
180,179,178,178,177,176,176,175,174,173,173,172,171,
171,170,169,168,168,167,166,165,165,164,163,162,162,
161,160,159,159,158,157,156,156,155,154,153,153,152,
151,150,149,149,148,147,146,146,145,144,143,143,142,
141,140,139,139,138,137,136,136,135,134,133,132,132,
131,130,129,129,128,127,126,125,125,124,123,122,122,
121,120,119,118,118,117,116,115,115,114,113,112,111,
111,110,109,108,108,107,106,105,105,104,103,102,101,
101,100, 99, 98, 98, 97, 96, 95, 95, 94, 93, 92, 92,
91, 90, 89, 89, 88, 87, 86, 86, 85, 84, 83, 83, 82,
81, 81, 80, 79, 78, 78, 77, 76, 76, 75, 74, 73, 73,
72, 71, 71, 70, 69, 69, 68, 67, 66, 66, 65, 64, 64,
63, 62, 62, 61, 60, 60, 59, 58, 58, 57, 56, 56, 55,
55, 54, 53, 53, 52, 51, 51, 50, 49, 49, 48, 48, 47,
46, 46, 45, 45, 44, 43, 43, 42, 42, 41, 41, 40, 39,
39, 38, 38, 37, 37, 36, 36, 35, 34, 34, 33, 33, 32,
32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26,
25, 25, 25, 24, 24, 23, 23, 22, 22, 21, 21, 21, 20,
20, 19, 19, 18, 18, 18, 17, 17, 16, 16, 16, 15, 15,
15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11,
10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 7, 7, 7,
7, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4,
4, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2,
2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6,
6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9,
10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14,
14, 14, 15, 15, 15, 16, 16, 16, 17, 17, 18, 18, 18,
19, 19, 20, 20, 21, 21, 21, 22, 22, 23, 23, 24, 24,
25, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30,
31, 31, 32, 32, 33, 33, 34, 34, 35, 36, 36, 37, 37,
38, 38, 39, 39, 40, 41, 41, 42, 42, 43, 43, 44, 45,
45, 46, 46, 47, 48, 48, 49, 49, 50, 51, 51, 52, 53,
53, 54, 55, 55, 56, 56, 57, 58, 58, 59, 60, 60, 61,
62, 62, 63, 64, 64, 65, 66, 66, 67, 68, 69, 69, 70,
71, 71, 72, 73, 73, 74, 75, 76, 76, 77, 78, 78, 79,
80, 81, 81, 82, 83, 83, 84, 85, 86, 86, 87, 88, 89,
89, 90, 91, 92, 92, 93, 94, 95, 95, 96, 97, 98, 98,
99,100,101,101,102,103,104,105,105,106,107,108,108,
109,110,111,111,112,113,114,115,115,116,117,118,118,
119,120,121,122,122,123,124,125,125,126 };
#pragma endregion
#endif
#include <J3ML/Algorithm/Reinterpret.hpp>
/// Swaps two elements in-place without copying their data.
template <typename T>
void Swap(T &a, T &b)
@@ -38,79 +134,82 @@ void Swap(T &a, T &b)
b = std::move(temp);
}
/// Clean symbolic names for integers of specific size.
namespace J3ML::SizedIntegralTypes
{
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using s8 = int8_t;
using s16 = int16_t;
using s32 = int32_t;
using s64 = int64_t;
}
namespace J3ML::SizedFloatTypes
{
// TODO: Use C++23 <stdfloat>
using f16 = float;
using f32 = float;
using f64 = double;
using f128 = long double;
namespace J3ML {
/// Clean symbolic names for integers of specific size.
namespace SizedIntegralTypes {
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using s8 = int8_t;
using s16 = int16_t;
using s32 = int32_t;
using s64 = int64_t;
}
//using namespace SizedIntegralTypes; // Bring into J3ML namespace.
namespace SizedFloatTypes { // TODO: Use C++23 <stdfloat>
using f16 = float;
using f32 = float;
using f64 = double;
using f128 = long double;
}
//using namespace SizedFloatTypes; // Bring into J3ML namespace.
}
using namespace J3ML::SizedIntegralTypes;
using namespace J3ML::SizedFloatTypes;
namespace J3ML::Math::BitTwiddling
{
namespace J3ML::BitTwiddling {
/// Parses a string of form "011101010" to a u32
u32 BinaryStringToValue(const char* s);
/// Returns the number of 1's set in the given value.
inline int CountBitsSet(u32 value);
//inline int CountBitsSet(u32 value);
}
namespace J3ML::Math {
enum class Quadrant { I, II, III, IV };
// Zero technically isn't a sign, but zero also isn't positive, or negative, so bite me.
enum class Sign { ZERO, POSITIVE, NEGATIVE};
// TODO: Implement "Wrappers" for most standard math functions.
// We want to later-on implement lookup tables and SSE as conditional macros.
namespace J3ML::Math::Constants {
/// sqrt(2pi) ^ -1
constexpr float RecipSqrt2Pi = 0.3989422804014326779399460599343818684758586311649346576659258296706579258993018385012523339073069364;
/// pi - https://www.mathsisfun.com/numbers/pi.html
constexpr float Pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
/// e - https://www.mathsisfun.com/numbers/e-eulers-number.html
constexpr float EulersNumber = 2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274;
/// 2pi - The ratio of a circle's circumferecne to its radius, and the number of radians in one turn.
constexpr float Tau = 6.28318530717958647692;
/// sqrt(2)
constexpr float PythagorasConstant = 1.41421356237309504880;
/// sqrt(3)
constexpr float TheodorusConstant = 1.73205080756887729352;
/// Golden Ratio
constexpr float Phi = 1.61803398874989484820;
/// ln 2
constexpr float NaturalLog2 = 0.6931471805599453094172321214581765680755001343602552541206800094933936219696947156058633269964186875;
/// ln 10
constexpr float NaturalLog10 = 2.3025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983;
constexpr float Infinity = INFINITY;
constexpr float NegativeInfinity = -INFINITY;
constexpr float NotANumber = NAN;
}
/// 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.
#undef USE_LOOKUP_TABLES /// Pre-computed lookup tables.
#undef USE_SSE /// Streaming SIMD Extensions (x86)
#undef USE_NEON /// ARM Vector Processing
#undef USE_AVX /// Advanced Vector Extensions (x86)
namespace J3ML::Math::Constants { // TODO: Consider double precision for these.
/// sqrt(2pi) ^ -1
constexpr float RecipSqrt2Pi = 0.3989422804014326779399460599343818684758586311649346576659258296706579258993018385012523339073069364;
/// pi - https://www.mathsisfun.com/numbers/pi.html
constexpr float Pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
constexpr float TwoPi = Pi*2.0;
constexpr float PiOverTwo = Pi/2.0;
constexpr float ThreePiOverTwo = 3.0*Pi/2.0;
/// e - https://www.mathsisfun.com/numbers/e-eulers-number.html
constexpr float EulersNumber = 2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274;
/// 2pi - The ratio of a circle's circumferecne to its radius, and the number of radians in one turn.
constexpr float Tau = 6.28318530717958647692;
/// sqrt(2)
constexpr float PythagorasConstant = 1.41421356237309504880;
/// sqrt(3)
constexpr float TheodorusConstant = 1.73205080756887729352;
/// Golden Ratio
constexpr float Phi = 1.61803398874989484820;
/// ln 2
constexpr float NaturalLog2 = 0.6931471805599453094172321214581765680755001343602552541206800094933936219696947156058633269964186875;
/// ln 10
constexpr float NaturalLog10 = 2.3025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983;
constexpr float Infinity = INFINITY;
constexpr float NegativeInfinity = -INFINITY;
constexpr float NotANumber = NAN;
}
namespace J3ML::Math {
using namespace Constants; // Bring into J3ML::Math namespace.
}
namespace J3ML::Math::Functions {
// TODO: Implement "Wrappers" for most standard math functions.
// We want to later-on implement lookup tables and SSE as conditional macros.
/// Clamps the given input value to the range [min, max].
/** @see Clamp01(), Min(), Max(). */
@@ -221,26 +320,37 @@ namespace J3ML::Math::Functions {
inline bool IsInfinite(double d) { return (ReinterpretAs<u64>(d) << 1) == 0xFFE0000000000000ULL; }
namespace Trigonometric {
Sign SignOfSin(float radians);
Sign SignOfCos(float radians);
Sign SignOfTan(float radians);
float Radians(float deg); /// Converts the given amount of degrees into radians.
float Degrees(float rad); /// Converts the given amount of radians into degrees.
Quadrant QuadrantOf(float radians);
float Sin(float x); /// Computes the sine of x, in radians.
float Cos(float x); /// Computes the cosine of x, in radians.
float Tan(float x); /// Computes the tangent of x, in radians.
/// Simultaneously computes both sine and cosine of x, in radians.
/// This yields a small performance increase over computing them separately.
/// @see Sin(), Cos().
void SinCos(float x, float& outSin, float& outCos);
float Radians(float deg); /// Converts the given amount of degrees into radians.
float Degrees(float rad); /// Converts the given amount of radians into degrees.
float Asin(float x); /// Computes the inverse sine of x, in radians.
float Acos(float x); /// Computes the inverse cosine of x, in radians.
float Atan(float x); /// Computes the inverse tangent of x, in radians.
float Atan2(float y, float x); /// Computes the signed (principal value) inverse tangent of y/x, in radians.
float Sinh(float x); /// Computes the hyperbolic sine of x, in radians.
float Cosh(float x); /// Computes the hyperbolic cosine of x, in radians.
float Tanh(float x); /// Computes the hyperbolic tangent of x, in radians.
float Sin(float x); /// Computes the sine of x, in radians.
float Cos(float x); /// Computes the cosine of x, in radians.
float Tan(float x); /// Computes the tangent of x, in radians.
/// Simultaneously computes both sine and cosine of x, in radians.
/// This yields a small performance increase over computing them separately.
/// @see Sin(), Cos().
void SinCos(float x, float& outSin, float& outCos);
float Asin(float x); /// Computes the inverse sine of x, in radians.
float Acos(float x); /// Computes the inverse cosine of x, in radians.
float Atan(float x); /// Computes the inverse tangent of x, in radians.
float Atan2(float y, float x); /// Computes the signed (principal value) inverse tangent of y/x, in radians.
float Sinh(float x); /// Computes the hyperbolic sine of x, in radians.
float Cosh(float x); /// Computes the hyperbolic cosine of x, in radians.
float Tanh(float x); /// Computes the hyperbolic tangent of x, in radians.
}
using namespace Trigonometric;
bool IsPow2(u32 number); /// Returns true if the given number is a power of 2.
bool IsPow2(u64 number); /// Returns true if the given number is a power of 2.
@@ -272,10 +382,6 @@ namespace J3ML::Math::Functions {
/// 2241 -> 2.2k, 55421 -> 55.4k, 1000000 -> 1.0M
std::string Truncate(float input);
float RecipFast(float x);
@@ -331,47 +437,50 @@ namespace J3ML::Math::Functions {
/// Returns the fractional part of x.
/** @see Lerp(), LerpMod(), InvLerp(), Step(), SmoothStep(), PingPongMod(), Mod(), ModPos(). */
float Frac(float x);
float Sqrt(float x); /// Returns the square root of x.
float FastSqrt(float x); /// Computes a fast approximation of the square root of x.
float RSqrt(float x); /// Returns 1/Sqrt(x). (The reciprocal of the square root of x)
float FastRSqrt(float x); /// SSE implementation of reciprocal square root.
float Recip(float x); /// Returns 1/x, the reciprocal of x.
float RecipFast(float x); /// Returns 1/x, the reciprocal of x, using a fast approximation (SSE rcp instruction).
namespace Interp
{
inline float SmoothStart(float t);
}
/// Returns the square root of x.
float Sqrt(float x);
/// Computes a fast approximation of the square root of x.
float FastSqrt(float x);
/// Returns 1/Sqrt(x). (The reciprocal of the square root of x)
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
{
inline float SmoothStart(float t);
}
namespace J3ML::Math {
using namespace Math::Constants;
using namespace Math::Functions;
using namespace Functions;
}
namespace J3ML::Math::Types {
struct Rotation
{
public:
Rotation();
Rotation(float value);
float valueInRadians;
float ValueInRadians() const;
float ValueInDegrees() const;
Rotation operator+(const Rotation& rhs);
struct Radians { // TODO: Fill in with relevant members.
float value;
float operator()() const { return value; }
};
struct Degrees { // TODO: Fill in with relevant members.
float value;
float operator()() const { return value; }
};
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
/// @file LinearAlgebra.h
/// @file LinearAlgebra.hpp
/// @description Includes all LinearAlgebra classes and functions
/// @author Josh O'Leary, William Tomasine II
/// @copyright 2024 Redacted Software
@@ -9,22 +9,15 @@
#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/Vector3.hpp"
#include "J3ML/LinearAlgebra/Vector4.hpp"
#include "J3ML/LinearAlgebra/Quaternion.hpp"
#include "J3ML/LinearAlgebra/AxisAngle.hpp"
#include "J3ML/LinearAlgebra/EulerAngle.hpp"
#include "J3ML/LinearAlgebra/Matrix2x2.hpp"
#include "J3ML/LinearAlgebra/Matrix3x3.hpp"
#include "J3ML/LinearAlgebra/Matrix4x4.hpp"
#include "J3ML/LinearAlgebra/Transform2D.hpp"
#include "J3ML/LinearAlgebra/CoordinateFrame.hpp"
#include "J3ML/LinearAlgebra/Vector2i.hpp"
using namespace J3ML::LinearAlgebra;

View File

@@ -1,29 +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
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/AxisAngle.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Forward.hpp>
namespace J3ML::LinearAlgebra
{
namespace J3ML::LinearAlgebra {
class AxisAngle;
}
/// Transitional datatype, not useful for internal representation of rotation
/// But has uses for conversion and manipulation.
class AxisAngle {
public:
Vector3 axis;
float angle;
public:
AxisAngle();
explicit AxisAngle(const Quaternion& q);
explicit AxisAngle(const EulerAngle& e);
/// @class AxisAngle
/// @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 {
public:
/// The
Vector3 axis;
/// Radians.
float angle;
public:
/// 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);
/// 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;
EulerAngle ToEulerAngleXYZ() const;
/// Returns the axis component of this AxisAngle rotation.
Vector3 Axis() const;
float Angle() const;
Quaternion ToQuaternion() const;
static AxisAngle FromEulerAngleXYZ(const EulerAngle&);
};
}
/// 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,52 +0,0 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/AxisAngle.hpp>
namespace J3ML::LinearAlgebra {
class AxisAngle;
// Essential Reading:
// http://www.essentialmath.com/GDC2012/GDC2012_JMV_Rotations.pdf
class EulerAngle {
public:
EulerAngle();
EulerAngle(float pitch, float yaw, float roll);
EulerAngle(const Vector3& vec) : pitch(vec.x), yaw(vec.y), roll(vec.z) {}
AxisAngle ToAxisAngle() const;
[[nodiscard]] Quaternion ToQuaternion() const;
explicit EulerAngle(const Quaternion& rhs);
explicit EulerAngle(const AxisAngle& rhs);
/// TODO: Implement separate upper and lower bounds
/// Preserves internal value of euler angles, normalizes and clamps the output.
/// This does not solve gimbal lock!!!
float GetPitch(float pitch_limit) const;
float GetYaw(float yaw_limit) const;
float GetRoll(float roll_limit) const;
bool operator==(const EulerAngle& a) const;
void clamp();
// TODO: Euler Angles do not represent a vector, length doesn't apply, nor is this information meaningful for this data type.
// If you need a meaningful representation of length in 3d space, use a vector!!
[[nodiscard]] float length() const {
return 0;
}
// TODO: Implement
Vector3 unitVector() const;
EulerAngle movementAngle() const;
public:
float pitch;
float yaw;
float roll;
};
}

View File

@@ -4,10 +4,10 @@
namespace J3ML::LinearAlgebra
{
class Vector2; // A type representing a position in a 2-dimensional coordinate space.
class Vector2i;
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 Angle2D; // Uses x,y components to represent a 2D rotation.
class EulerAngle; // Uses pitch,yaw,roll components to represent a 3D orientation.
class AxisAngle; //
class CoordinateFrame; //
class Matrix2x2;
@@ -15,6 +15,7 @@ namespace J3ML::LinearAlgebra
class Matrix4x4;
class Transform2D;
class Transform3D;
class DirectionVectorRH; // A type representing a direction in 3D space.
class Quaternion;

View File

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

View File

@@ -5,7 +5,6 @@
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
#include <J3ML/Algorithm/RNG.hpp>
using namespace J3ML::Algorithm;
@@ -62,8 +61,9 @@ namespace J3ML::LinearAlgebra {
Matrix3x3(const Vector3& col0, const Vector3& col1, const Vector3& col2);
/// Constructs this matrix3x3 from the given quaternion.
explicit Matrix3x3(const Quaternion& orientation);
/// Constructs this matrix3x3 from the given euler angle.
explicit Matrix3x3(const EulerAngle& 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.
explicit Matrix3x3(const float *data);
@@ -153,10 +153,19 @@ namespace J3ML::LinearAlgebra {
/// Sets this matrix to perform a rotation about the given axis and angle.
void SetRotatePart(const Vector3& a, float angle);
void SetRotatePart(const AxisAngle& axisAngle);
/// Sets this matrix to perform the rotation expressed by the given quaternion.
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.
/** @param row The zero-based index [0, 2] of the row to get. */
Vector3 GetRow(int index) const;
@@ -239,17 +248,10 @@ namespace J3ML::LinearAlgebra {
inline float* ptr() { return &elems[0][0];}
[[nodiscard]] inline const float* ptr() const {return &elems[0][0];}
/// Convers this rotation matrix to a quaternion.
/// This function assumes that the matrix is orthonormal (no shear or scaling) and does not perform any mirroring (determinant > 0)
[[nodiscard]] Quaternion ToQuat() const;
/// Attempts to convert this matrix to a quaternion. Returns false if the conversion cannot succeed (this matrix was not a rotation
/// matrix, and there is scaling ,shearing, or mirroring in this matrix)
bool TryConvertToQuat(Quaternion& q) const;
/// Converts this rotation matrix to an Euler Angle.
[[nodiscard]] EulerAngle ToEulerAngle() const;
/// Returns the main diagonal.
/// The main diagonal consists of the elements at m[0][0], m[1][1], m[2][2]
[[nodiscard]] Vector3 Diagonal() const;

View File

@@ -5,6 +5,8 @@
#include <J3ML/Algorithm/RNG.hpp>
#include <algorithm>
#include <iostream>
#include <bits/ostream.tcc>
using namespace J3ML::Algorithm;
@@ -71,8 +73,7 @@ namespace J3ML::LinearAlgebra {
/// Constructs this Matrix4x4 from the given quaternion.
explicit Matrix4x4(const Quaternion& orientation);
/// Constructs this Matrix4x4 from the given Euler Angle.
explicit Matrix4x4(const EulerAngle& orientation);
/// Constructs this float4x4 from the given quaternion and translation.
/// Logically, the translation occurs after the rotation has been performed.
@@ -567,15 +568,18 @@ namespace J3ML::LinearAlgebra {
[[nodiscard]] Quaternion ToQuat() const;
[[nodiscard]] EulerAngle ToEulerAngle() const;
/// Returns true if this Matrix4x4 is equal to the given Matrix4x4, up to given per-element epsilon.
bool Equals(const Matrix4x4& other, float epsilon = 1e-3f) const;
[[nodiscard]] std::string ToString() const;
protected:
float elems[4][4];
Vector3 TransformDir(float tx, float ty, float tz) const;
};
}
std::ostream& operator << (std::ostream& out, const Matrix4x4& rhs);
}

View File

@@ -4,258 +4,262 @@
#include <J3ML/Algorithm/RNG.hpp>
#include <cmath>
namespace J3ML::LinearAlgebra
{
class Quaternion {
public:
/// The identity quaternion performs no rotation when applied to a vector.
static const Quaternion Identity;
/// A compile-time constant Quaternion with the value (NAN, NAN, NAN, NAN).
/// For this constant, each element has the value of quiet NAN, or Not-A-Number.
/// @note Never compare a Quaternion to this value! Due to how IEEE floats work, "nan == nan" returns false!
/// That is, nothing is equal to NaN, not even NaN itself!
static const Quaternion NaN;
public:
/// The default constructor does not initialize any member values.
Quaternion();
/// Copy constructor
Quaternion(const Quaternion &rhs) = default;
/// Constructs a quaternion from the given data buffer.
/// @param data An array of four floats to use for the quaternion, in the order 'x,y,z,w.' (== 'i,j,k,w')
explicit Quaternion(const float *data);
namespace J3ML::LinearAlgebra {
class Quaternion;
}
explicit Quaternion(const Matrix3x3 &rotationMtrx);
explicit Quaternion(const Matrix4x4 &rotationMtrx);
class J3ML::LinearAlgebra::Quaternion {
public:
float x;
float y;
float z;
float w;
public:
/// The identity quaternion performs no rotation when applied to a vector.
static const Quaternion Identity;
/// A compile-time constant Quaternion with the value (NAN, NAN, NAN, NAN).
/// For this constant, each element has the value of quiet NAN, or Not-A-Number.
/// @note Never compare a Quaternion to this value! Due to how IEEE floats work, "nan == nan" returns false!
/// That is, nothing is equal to NaN, not even NaN itself!
static const Quaternion NaN;
public:
/// The default constructor does not initialize any member values.
Quaternion() = default;
/// @param x The factor of i.
/// @param y The factor of j.
/// @param z The factor of k.
/// @param w The scalar factor (or 'w').
/// @note The input data is not normalized after construction, this has to be done manually.
Quaternion(float X, float Y, float Z, float W);
/// Copy constructor
Quaternion(const Quaternion &rhs);
/// Constructs this quaternion by specifying a rotation axis and the amount of rotation to be performed about that axis
/// @param rotationAxis The normalized rotation axis to rotate about. If using Vector4 version of the constructor, the w component of this vector must be 0.
/// @param rotationAngleRadians The angle to rotate by, in radians. For example, Pi/4.f equals to 45 degrees, Pi/2.f is 90 degrees, etc.
/// @see DegToRad()
Quaternion(const Vector3 &rotationAxis, float rotationAngleRadians);
Quaternion(const Vector4 &rotationAxis, float rotationAngleRadians);
/// Quaternion from Matrix3x3
explicit Quaternion(const Matrix3x3 &ro_mat);
explicit Quaternion(const Vector4& vector4);
explicit Quaternion(const EulerAngle& angle);
explicit Quaternion(const AxisAngle& angle);
/// Creates a LookAt quaternion.
/** A LookAt quaternion is a quaternion that orients an object to face towards a specified target direction.
@param localForward Specifies the forward direction in the local space of the object. This is the direction
the model is facing at in its own local/object space, often +X(1,0,0), +Y(0,1,0), or +Z(0,0,1).
The vector to pass in here depends on the conventions you or your modeling software is using, and it is best
to pick one convention for all your objects, and be consistent.
This input parameter must be a normalized vector.
@param targetDirection Specifies the desired world space direction the object should look at. This function
will compute a quaternion which will rotate the localForward vector to orient towards this targetDirection
vector. This input parameter must be a normalized vector.
@param localUp Specifies the up direction in the local space of the object. This is the up direction the model
was authored in, often +Y (0,1,0) or +Z (0,0,1). The vector to pass in here depends on the conventions you
or your modeling software is using, and it is best to pick one convention for all your objects, and be
consistent. This input parameter must be a normalized vector. This vector must be perpendicular to the
vector localForward, i.e. localForward.Dot(localUp) == 0.
@param worldUp Specifies the global up direction of the scene in world space. Simply rotating one vector to
coincide with another (localForward->targetDirection) would cause the up direction of the resulting
orientation to drift (e.g. the model could be looking at its target its head slanted sideways). To keep
the up direction straight, this function orients the localUp direction of the model to point towards
the specified worldUp direction (as closely as possible). The worldUp and targetDirection vectors cannot be
collinear, but they do not need to be perpendicular either.
@return A quaternion that maps the given local space forward direction vector to point towards the given target
direction, and the given local up direction towards the given target world up direction. For the returned
quaternion Q it holds that M * localForward = targetDirection, and M * localUp lies in the plane spanned
by the vectors targetDirection and worldUp.
@see RotateFromTo() */
static Quaternion LookAt(const Vector3& localForward, const Vector3& targetDirection, const Vector3& localUp, const Vector3& worldUp);
/// Creates a new Quaternion that rotates about the given axis by the given angle.
static Quaternion RotateAxisAngle(const AxisAngle& axisAngle);
/// Creates a new quaternion that rotates about the positive X axis by the given rotation.
static Quaternion RotateX(float angleRadians);
/// Creates a new quaternion that rotates about the positive Y axis by the given rotation.
static Quaternion RotateY(float angleRadians);
/// Creates a new quaternion that rotates about the positive Z axis by the given rotation.
static Quaternion RotateZ(float angleRadians);
/// Creates a new quaternion that rotates sourceDirection vector (in world space) to coincide with the
/// 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);
/// Quaternion from Matrix4x4 RotatePart.
explicit Quaternion(const Matrix4x4 &ro_mat);
/// Returns a uniformly random unitary quaternion.
static Quaternion RandomRotation(RNG &rng);
public:
void SetFromAxisAngle(const Vector3 &vector3, float between);
/// Quaternion from AxisAngle.
explicit Quaternion(const AxisAngle &angle);
void SetFromAxisAngle(const Vector4 &vector4, float between);
void SetFrom(const AxisAngle& angle);
/// Quaternion from Vector4 (no conversion).
explicit Quaternion(const Vector4 &vector4);
/// Inverses this quaternion in-place.
/// @note For optimization purposes, this function assumes that the quaternion is unitary, in which
/// case the inverse of the quaternion is simply just the same as its conjugate.
/// This function does not detect whether the operation succeeded or failed.
void Inverse();
/// @param x The factor of i.
/// @param y The factor of j.
/// @param z The factor of k.
/// @param w The scalar factor (or 'w').
/// @note The input data is not normalized after construction, this has to be done manually.
Quaternion(float X, float Y, float Z, float W);
/// Returns an inverted copy of this quaternion.
[[nodiscard]] Quaternion Inverted() const;
/// Computes the conjugate of this quaternion in-place.
void Conjugate();
/// Returns a conjugated copy of this quaternion.
[[nodiscard]] Quaternion Conjugated() const;
/// Creates a LookAt quaternion.
/** A LookAt quaternion is a quaternion that orients an object to face towards a specified target direction.
@param localForward Specifies the forward direction in the local space of the object. This is the direction
the model is facing at in its own local/object space, often +X(1,0,0), +Y(0,1,0), or +Z(0,0,1).
The vector to pass in here depends on the conventions you or your modeling software is using, and it is best
to pick one convention for all your objects, and be consistent.
This input parameter must be a normalized vector.
@param targetDirection Specifies the desired world space direction the object should look at. This function
will compute a quaternion which will rotate the localForward vector to orient towards this targetDirection
vector. This input parameter must be a normalized vector.
@param localUp Specifies the up direction in the local space of the object. This is the up direction the model
was authored in, often +Y (0,1,0) or +Z (0,0,1). The vector to pass in here depends on the conventions you
or your modeling software is using, and it is best to pick one convention for all your objects, and be
consistent. This input parameter must be a normalized vector. This vector must be perpendicular to the
vector localForward, i.e. localForward.Dot(localUp) == 0.
@param worldUp Specifies the global up direction of the scene in world space. Simply rotating one vector to
coincide with another (localForward->targetDirection) would cause the up direction of the resulting
orientation to drift (e.g. the model could be looking at its target its head slanted sideways). To keep
the up direction straight, this function orients the localUp direction of the model to point towards
the specified worldUp direction (as closely as possible). The worldUp and targetDirection vectors cannot be
collinear, but they do not need to be perpendicular either.
@return A quaternion that maps the given local space forward direction vector to point towards the given target
direction, and the given local up direction towards the given target world up direction. For the returned
quaternion Q it holds that M * localForward = targetDirection, and M * localUp lies in the plane spanned
by the vectors targetDirection and worldUp.
@see RotateFromTo() */
static Quaternion LookAt(const Vector3 &localForward, const Vector3 &targetDirection, const Vector3 &localUp, const Vector3 &worldUp);
/// Inverses this quaternion in-place.
/// Call this function when the quaternion is not known beforehand to be normalized.
/// This function computes the inverse proper, and normalizes the result.
/// @note Because of the normalization, it does not necessarily hold that q * q.InverseAndNormalize() == id.
/// @return Returns the old length of this quaternion (not the old length of the inverse quaternion).
float InverseAndNormalize();
/// Creates a new quaternion that rotates about the positive X axis by the given rotation.
static Quaternion RotateX(float rad);
/// 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;
/// 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;
/// 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;
/// Creates a new quaternion that rotates about the positive Y axis by the given rotation.
static Quaternion RotateY(float rad);
/// Returns the axis of rotation for this quaternion.
[[nodiscard]] Vector3 Axis() const;
/// Creates a new quaternion that rotates about the positive Z axis by the given rotation.
static Quaternion RotateZ(float rad);
/// Returns the angle of rotation for this quaternion, in radians.
[[nodiscard]] float Angle() const;
/// Creates a new quaternion that rotates sourceDirection vector (in world space) to coincide with the
/// 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);
[[nodiscard]] float LengthSquared() const;
[[nodiscard]] float Length() const;
static Quaternion RotateFromTo(const Vector4 &sourceDirection, const Vector4 &targetDirection);
[[nodiscard]] EulerAngle ToEulerAngle() const;
/// 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);
[[nodiscard]] Matrix3x3 ToMatrix3x3() const;
[[nodiscard]] Matrix4x4 ToMatrix4x4() const;
/// Returns a uniformly random unitary quaternion.
static Quaternion RandomRotation(RNG &rng);
[[nodiscard]] Matrix4x4 ToMatrix4x4(const Vector3 &translation) const;
public:
/// Inverses this quaternion in-place.
/// @note For optimization purposes, this function assumes that the quaternion is unitary, in which
/// case the inverse of the quaternion is simply just the same as its conjugate.
/// This function does not detect whether the operation succeeded or failed.
void Inverse();
[[nodiscard]] Vector3 Transform(const Vector3& vec) 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
[[nodiscard]] Vector4 Transform(const Vector4& vec) const;
[[nodiscard]] Vector4 Transform(float X, float Y, float Z, float W) const;
/// Returns an inverted copy of this quaternion.
[[nodiscard]] Quaternion Inverted() 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 Slerp(const Quaternion &source, const Quaternion& target, float t);
/// Computes the conjugate of this quaternion in-place.
void Conjugate();
/// 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.
@param from A normalized direction vector specifying the direction of rotation at t=0
@param to A normalized direction vector specifying the direction of rotation at t=1
@param t The interpolation time parameter, in the range [0, 1]. Input values outside this range are
silently clamped to the [0, 1] interval.
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
static Vector3 SlerpVector(const Vector3& from, const Vector3& to, float t);
/// Returns a conjugated copy of this quaternion.
[[nodiscard]] Quaternion Conjugated() const;
/// 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.
@param from A normalized direction vector specifying the direction of rotation at angleRadians=0.
@param to A normalized direction vector specifying the target direction to rotate towards.
@param angleRadians The maximum angle to rotate the 'from' vector by, in the range [0, pi]. If the
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.
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
static Vector3 SlerpVectorAbs(const Vector3 &from, const Vector3& to, float angleRadians);
/// Inverses this quaternion in-place.
/// Call this function when the quaternion is not known beforehand to be normalized.
/// This function computes the inverse proper, and normalizes the result.
/// @note Because of the normalization, it does not necessarily hold that q * q.InverseAndNormalize() == id.
/// @return Returns the old length of this quaternion (not the old length of the inverse quaternion).
float InverseAndNormalize();
/// Normalizes this quaternion in-place.
/// Returns the old length of this quaternion, or 0 if normalization failed.
float Normalize();
/// Returns a normalized copy of this quaternion.
[[nodiscard]] Quaternion Normalized() const;
/// 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;
/// Returns true if the length of this quaternion is one.
[[nodiscard]] bool IsNormalized(float epsilon = 1e-5f) const;
[[nodiscard]] bool IsInvertible(float epsilon = 1e-3f) 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.
[[nodiscard]] Vector3 WorldY() const;
/// Returns true if the entries of this quaternion are all finite.
[[nodiscard]] bool IsFinite() 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.
[[nodiscard]] Vector3 WorldZ() const;
/// Returns true if this quaternion equals rhs, up to the given epsilon.
[[nodiscard]] bool Equals(const Quaternion& rhs, float epsilon = 1e-3f) const;
/// Returns the axis of rotation for this quaternion.
[[nodiscard]] Vector3 Axis() const;
/// 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.
bool BitEquals(const Quaternion& rhs) const;
/// Returns the angle of rotation for this quaternion, in radians.
[[nodiscard]] float Angle() const;
/// @return A pointer to the first element (x). The data is contiguous in memory.
/// ptr[0] gives x, ptr[1] gives y, ptr[2] gives z, ptr[3] gives w.
inline float *ptr() { return &x; }
[[nodiscard]] inline const float *ptr() const { return &x; }
[[nodiscard]] float LengthSquared() const;
[[nodiscard]] float Length() const;
[[nodiscard]] Matrix3x3 ToMatrix3x3() const;
[[nodiscard]] Matrix4x4 ToMatrix4x4() const;
[[nodiscard]] Matrix4x4 ToMatrix4x4(const Vector3 &translation) const;
[[nodiscard]] Vector3 Transform(const Vector3 &vec) 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
[[nodiscard]] Vector4 Transform(const Vector4 &vec) const;
[[nodiscard]] Vector4 Transform(float X, float Y, float Z, float W) 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 Slerp(const Quaternion &source, const Quaternion &target, float t);
/// 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.
@param from A normalized direction vector specifying the direction of rotation at t=0
@param to A normalized direction vector specifying the direction of rotation at t=1
@param t The interpolation time parameter, in the range [0, 1]. Input values outside this range are
silently clamped to the [0, 1] interval.
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
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.
/** This function slerps the given 'from' vector towards the 'to' vector.
@param from A normalized direction vector specifying the direction of rotation at angleRadians=0.
@param to A normalized direction vector specifying the target direction to rotate towards.
@param angleRadians The maximum angle to rotate the 'from' vector by, in the range [0, pi]. If the
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.
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
static Vector3 SlerpVectorAbs(const Vector3 &from, const Vector3 &to, float angleRadians);
/// Normalizes this quaternion in-place.
/// @returns false if failure, true if success.
[[nodiscard]] bool Normalize();
/// Returns a normalized copy of this quaternion.
[[nodiscard]] Quaternion Normalized() const;
/// Returns true if the length of this quaternion is one.
[[nodiscard]] bool IsNormalized(float epsilon = 1e-5f) const;
[[nodiscard]] bool IsInvertible(float epsilon = 1e-3f) const;
/// Returns true if the entries of this quaternion are all finite.
[[nodiscard]] bool IsFinite() const;
/// Returns true if this quaternion equals rhs, up to the given epsilon.
[[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.
/// @note Prefer using this over e.g. memcmp, since there can be SSE-related padding in the structures.
bool BitEquals(const Quaternion& rhs) const;
/// @return A pointer to the first element (x). The data is contiguous in memory.
/// ptr[0] gives x, ptr[1] gives y, ptr[2] gives z, ptr[3] gives w.
inline float *ptr() { return &x; }
[[nodiscard]] inline const float *ptr() const { return &x; }
// Multiplies two quaternions together.
// The product q1 * q2 returns a quaternion that concatenates the two orientation rotations.
// The rotation q2 is applied first before q1.
Quaternion operator * (const Quaternion& rhs) const;
// Multiplies two quaternions together.
// The product q1 * q2 returns a quaternion that concatenates the two orientation rotations.
// The rotation q2 is applied first before q1.
Quaternion operator * (const Quaternion& rhs) const;
// Unsafe
Quaternion operator * (float scalar) const;
// Unsafe
Quaternion operator * (float scalar) const;
// Unsafe
Quaternion operator / (float scalar) const;
// Unsafe
Quaternion operator / (float scalar) const;
// Transforms the given vector by this Quaternion.
Vector3 operator * (const Vector3& rhs) const;
// Transforms the given vector by this Quaternion.
Vector3 operator * (const Vector3& rhs) const;
Vector4 operator * (const Vector4& rhs) const;
Vector4 operator * (const Vector4& rhs) const;
// Divides a quaternion by another. Divison "a / b" results in a quaternion that rotates the orientation b to coincide with orientation of
Quaternion operator / (const Quaternion& rhs) const;
Quaternion operator + (const Quaternion& rhs) const;
// Divides a quaternion by another. Divison "a / b" results in a quaternion that rotates the orientation b to coincide with orientation of
Quaternion operator / (const Quaternion& rhs) const;
Quaternion operator + (const Quaternion& rhs) const;
Quaternion operator + () const;
Quaternion operator - () const;
Quaternion operator + () const;
Quaternion operator - () const;
/// Computes the dot product of this and the given quaternion.
/// Dot product is commutative.
[[nodiscard]] float Dot(const Quaternion &quaternion) const;
/// Computes the dot product of this and the given quaternion.
/// Dot product is commutative.
[[nodiscard]] float Dot(const Quaternion &quaternion) const;
/// Returns the angle between this and the target orientation (the shortest route) in radians.
[[nodiscard]] float AngleBetween(const Quaternion& target) const;
/// Returns the axis of rotation to get from this orientation to target orientation (the shortest route).
[[nodiscard]] Vector3 AxisFromTo(const Quaternion& target) const;
/// Returns the angle between this and the target orientation (the shortest route) in radians.
[[nodiscard]] float AngleBetween(const Quaternion& target) const;
/// Returns the axis of rotation to get from this orientation to target orientation (the shortest route).
[[nodiscard]] Vector3 AxisFromTo(const Quaternion& target) const;
[[nodiscard]] AxisAngle ToAxisAngle() const;
void SetFromAxisAngle(const AxisAngle& axisAngle);
/// Sets this quaternion to represent the same rotation as the given matrix.
void Set(const Matrix3x3& matrix);
void Set(const Matrix4x4& matrix);
void Set(float x, float y, float z, float w);
void Set(const Quaternion& q);
void Set(const Vector4& v);
/// Sets this quaternion to represent the same rotation as the given matrix.
void Set(const Matrix3x3& matrix);
void Set(const Matrix4x4& matrix);
void Set(float x, float y, float z, float w);
void Set(const Quaternion& q);
void Set(const Vector4& v);
public:
float x;
float y;
float z;
float w;
};
}
};

View File

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

View File

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

View File

@@ -55,11 +55,12 @@ namespace J3ML::LinearAlgebra {
Vector2(float X, float Y);
/// Constructs this float2 from a C array, to the value (data[0], data[1]).
explicit Vector2(const float* data);
// Constructs a new Vector2 with the value {scalar, scalar}
/// Constructs a new Vector2 with the value {scalar, scalar}
explicit Vector2(float scalar);
Vector2(const Vector2& rhs); // Copy Constructor
//Vector2(Vector2&&) = default; // Move Constructor
explicit Vector2(const Vector2i& rhs);
/// Constructs a new Vector2 from std::pair<float, float>,.
explicit Vector2(const std::pair<float, float>& rhs) : x(rhs.first), y(rhs.second) {}
[[nodiscard]] float GetX() const;
[[nodiscard]] float GetY() const;
@@ -110,17 +111,15 @@ namespace J3ML::LinearAlgebra {
/// Tests if two vectors are equal, up to the given epsilon.
/** @see IsPerpendicular(). */
bool Equals(const Vector2& rhs, float epsilon = 1e-3f) const {
return Math::EqualAbs(x, rhs.x, epsilon) &&
Math::EqualAbs(y, rhs.y, epsilon);
}
bool Equals(float x_, float y_, float epsilon = 1e-3f) const {
return Math::EqualAbs(x, x_, epsilon) &&
Math::EqualAbs(y, y_, epsilon);
}
bool Equals(const Vector2& rhs, float epsilon = 1e-3f) const;
bool Equals(float x_, float y_, float epsilon = 1e-3f) const;
bool PreciselyEquals(const Vector2& rhs) const;
bool operator == (const Vector2& rhs) const;
bool operator != (const Vector2& rhs) const;
bool operator > (const Vector2& rhs) const;
bool operator < (const Vector2& rhs) const;
/// Returns an element-wise minimum between two vectors.
[[nodiscard]] Vector2 Min(const Vector2& min) const;
@@ -385,6 +384,7 @@ namespace J3ML::LinearAlgebra {
static Vector2 RandomBox(Algorithm::RNG& rng, float minElem, float maxElem);
[[nodiscard]] std::string ToString() const;
};
Vector2 operator*(float lhs, const Vector2 &rhs);

View File

@@ -1,11 +1,34 @@
#pragma once
#include <string>
#include "Vector2.hpp"
namespace J3ML::LinearAlgebra
{
class Vector2i
{
public:
int x;
int y;
};
}
namespace J3ML::LinearAlgebra {
class Vector2i;
}
class J3ML::LinearAlgebra::Vector2i {
public:
int x, y;
public:
Vector2i();
Vector2i(int x, int y) : x(x), y(y) {}
explicit Vector2i(int rhs) : x(rhs), y(rhs) {}
explicit Vector2i(const Vector2& rhs) : x(rhs.x), y(rhs.y) { }
explicit Vector2i(const std::pair<int, int>& rhs) : x(rhs.first), y(rhs.second) {}
public:
bool operator == (const Vector2i& rhs) const;
bool operator != (const Vector2i& rhs) const;
Vector2i& operator =(const Vector2i& rhs);
Vector2i& operator +=(const Vector2i& rhs);
Vector2i& operator -=(const Vector2i& rhs);
Vector2i& operator *=(const Vector2i& rhs);
Vector2i& operator /=(const Vector2i& rhs);
Vector2i operator +(const Vector2i& rhs) const;
Vector2i operator -(const Vector2i& rhs) const;
Vector2i operator *(const Vector2i& rhs) const;
Vector2i operator *(int rhs) const;
Vector2i operator /(const Vector2i& rhs) const;
Vector2i operator /(int rhs) const;
public:
[[nodiscard]] std::string ToString() const;
};

View File

@@ -68,15 +68,15 @@ public:
/** @note Due to static data initialization order being undefined in C++, do NOT use this
member to initialize other static data in other compilation units! */
static const Vector3 NegativeInfinity;
/// Specifies a compile-time constant Vector3 with value (1,1,1).
/// Specifies a compile-time constant Vector3 with value (1,0,0).
/** @note Due to static data initialization order being undefined in C++, do NOT use this
member to initialize other static data in other compilation units! */
static const Vector3 UnitX;
/// Specifies a compile-time constant Vector3 with value (1,1,1).
/// Specifies a compile-time constant Vector3 with value (0,1,0).
/** @note Due to static data initialization order being undefined in C++, do NOT use this
member to initialize other static data in other compilation units! */
static const Vector3 UnitY;
/// Specifies a compile-time constant Vector3 with value (1,1,1).
/// Specifies a compile-time constant Vector3 with value (0,0,1).
/** @note Due to static data initialization order being undefined in C++, do NOT use this
member to initialize other static data in other compilation units! */
static const Vector3 UnitZ;

View File

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

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

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

View File

@@ -11,21 +11,91 @@
#include <iostream>
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry.hpp>
#include "J3ML/J3ML.hpp"
#include <J3ML/J3ML.hpp>
#include <jlog/Logger.hpp>
#include <J3ML/LinearAlgebra/Matrix.hpp>
#include <J3ML/LinearAlgebra/Vector.hpp>
#include "J3ML/Rotation.hpp"
int main(int argc, char** argv)
{
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;
// Test quadrant
for (float r = 0; r < TwoPi; r+=0.25f)
{
Quadrant q = QuadrantOf(r);
if (q == Quadrant::I)
std::cout << "I" << std::endl;
if (q == Quadrant::II)
std::cout << "II" << std::endl;
if (q == Quadrant::III)
std::cout << "III" << std::endl;
if (q == Quadrant::IV)
std::cout << "IV" << std::endl;
}
for (int i = 10; i < 9999999; i*=1.5f) {
std::cout << J3ML::Math::Functions::Truncate(i) << std::endl;
}
Ray a({420, 0, 0}, {1, 0, 0});
std::cout << a << std::endl;
Matrix4x4 A {
1, 2, 0, 1,
3, 1, 2, 0,
0, 4, 1, 2,
2, 0, 3, 1
};
Matrix4x4 B {
0, 1, 2, 3,
1, 0, 1, 0,
2, 3, 0, 1,
1, 2, 1, 0
};
auto C = A*B;
using Matrix2x3f = Matrix<2, 3, float>;
std::cout << C << std::endl;
std::cout << "j3ml demo coming soon" << std::endl;
v2f _v2f{1.f};
v3f _v3f{1.f};
v4f _v4f(1);
v2i ipair (420, 420);
v3i ipair3(0,0,0);
v4i ipair4(1,2,3,4);
using namespace J3ML::Math;
Rotation my_rot = 25_degrees;
return 0;
}

View File

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

View File

@@ -129,23 +129,6 @@ namespace J3ML::Geometry
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> frustumVolume;
frustumVolume.p[0] = NearPlane();

View File

@@ -372,7 +372,7 @@ namespace J3ML::Geometry
Vector4 b = Vector4(poly.v[face.v[1]], 1.f);
Vector4 c = Vector4(poly.v[face.v[2]], 1.f);
Vector4 normal = (b-a).Cross(c-a);
normal.Normalized();
normal.Normalize();
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();
}
@@ -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
v0 = v1;
}
normal.Normalized();
normal.Normalize();
return normal;
#if 0
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 <iomanip>
#include <strstream>
#include "J3ML/J3ML.hpp"
#include <J3ML/J3ML.hpp>
#include <sstream>
@@ -38,22 +37,104 @@ float PowUInt(float base, u32 exponent)
}
namespace J3ML
{
namespace J3ML::Math::Functions::Trigonometric {
enum Sign SignOfSin(float radians) {
enum Quadrant q = QuadrantOf(radians);
if (q == Quadrant::I || q == Quadrant::II)
return Sign::POSITIVE;
float Math::Functions::Radians(float degrees) { return degrees * (Pi/180.f); }
// ReSharper disable once CppDFAConstantConditions
if (q == Quadrant::II || q == Quadrant::IV)
return Sign::NEGATIVE;
float Math::Functions::Degrees(float radians) { return radians * (180.f/Pi); }
// ReSharper disable once CppDFAUnreachableCode
return Sign::ZERO;
}
enum Sign SignOfCos(float radians) {
enum Quadrant q = QuadrantOf(radians);
if (q == Quadrant::I || q == Quadrant::IV)
return Sign::POSITIVE;
// ReSharper disable once CppDFAConstantConditions
if (q == Quadrant::II || q == Quadrant::III)
return Sign::NEGATIVE;
Math::Rotation Math::operator ""_degrees(long double rads) { return {Functions::Radians((float)rads)}; }
// ReSharper disable once CppDFAUnreachableCode
return Sign::ZERO;
}
Math::Rotation Math::operator ""_deg(long double rads) { return {Functions::Radians((float)rads)}; }
enum Sign SignOfTan(float radians) {
enum Quadrant q = QuadrantOf(radians);
if (q == Quadrant::I || q == Quadrant::III)
return Sign::POSITIVE;
Math::Rotation Math::operator ""_radians(long double rads) { return {(float)rads}; }
// ReSharper disable once CppDFAConstantConditions
if (q == Quadrant::II || q == Quadrant::IV)
return Sign::NEGATIVE;
Math::Rotation Math::operator ""_rad(long double rads) { return {(float)rads}; }
// ReSharper disable once CppDFAUnreachableCode
return Sign::ZERO;
}
Quadrant QuadrantOf(float radians) {
if (radians > ThreePiOverTwo) {
return Quadrant::IV;
} else if (radians >= Pi) {
return Quadrant::III;
} else if (radians >= PiOverTwo) {
return Quadrant::II;
} else {
return Quadrant::I;;
}
}
float Radians(float degrees) { return degrees * (Pi/180.f); }
float Degrees(float radians) { return radians * (180.f/Pi); }
float Sin(float x) {
#ifdef USE_LOOKUP_TABLES
#elif USE_SSE
#else
return std::sin(x);
#endif
}
float Cos(float x) {
#ifdef USE_LOOKUP_TABLES
#elif USE_SSE
#else
return std::cos(x);
#endif
}
float Tan(float x) { return std::tan(x); }
void SinCos(float x, float &outSin, float &outCos) {
outSin = Sin(x);
outCos = Cos(x);
}
float Asin(float x) { return std::asin(x); }
float Acos(float x) { return std::acos(x); }
float Atan(float x) { return std::atan(x); }
float Atan2(float y, float x) { return std::atan2(y, x); }
float Sinh(float x) { return std::sinh(x); }
float Cosh(float x) { return std::cosh(x); }
float Tanh(float x) { return std::tanh(x); }
}
namespace J3ML::Math::Functions { }
namespace J3ML {
float Math::Functions::FastRSqrt(float x) {
return 1.f / FastSqrt(x);
@@ -77,9 +158,6 @@ namespace J3ML
return 1.f / Sqrt(x);
}
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};
@@ -149,6 +227,20 @@ namespace J3ML
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::LerpMod(float a, float b, float mod, float t) {
@@ -207,60 +299,9 @@ namespace J3ML
Math::Rotation::Rotation() : valueInRadians(0) {}
Math::Rotation::Rotation(float value) : valueInRadians(value) {}
Math::Rotation Math::Rotation::operator+(const Math::Rotation &rhs) {
return {valueInRadians + rhs.valueInRadians};
}
float Math::Interp::SmoothStart(float t) {
assert(t >= 0.f && t <= 1.f);
return t*t;
}
int Math::BitTwiddling::CountBitsSet(u32 value) {
}
// int BitTwiddling::CountBitsSet(u32 value) { }
namespace Math::Functions {
float Sin(float x) {
#ifdef USE_LOOKUP_TABLES
#elif USE_SSE
#else
return std::sin(x);
#endif
}
float Cos(float x) {
#ifdef USE_LOOKUP_TABLES
#elif USE_SSE
#else
return std::cos(x);
#endif
}
float Tan(float x) { return std::tan(x); }
void SinCos(float x, float &outSin, float &outCos) {
outSin = Sin(x);
outCos = Cos(x);
}
float Asin(float x) { return std::asin(x); }
float Acos(float x) { return std::acos(x); }
float Atan(float x) { return std::atan(x); }
float Atan2(float y, float x) { return std::atan2(y, x); }
float Sinh(float x) { return std::sinh(x); }
float Cosh(float x) { return std::cosh(x); }
float Tanh(float x) { return std::tanh(x); }
bool IsPow2(u32 number) {
return (number & (number - 1)) == 0;
@@ -311,3 +352,10 @@ namespace J3ML
}
}
namespace J3ML::Math::Functions::Interpolation {
float SmoothStart(float t) {
assert(t >= 0.f && t <= 1.f);
return t*t;
}
}

View File

@@ -1,61 +1,82 @@
#include <J3ML/LinearAlgebra/AxisAngle.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
namespace J3ML::LinearAlgebra {
AxisAngle::AxisAngle() : axis(Vector3::Zero) {}
AxisAngle::AxisAngle(const Vector3 &axis, float angle) : axis(axis), angle(angle) {}
AxisAngle::AxisAngle(const Vector3& axis, float angle) : axis(axis), angle(angle) {}
Quaternion AxisAngle::ToQuaternion() const {
return {
axis.x * std::sin(angle/2),
axis.y * std::sin(angle/2),
axis.z * std::sin(angle/2),
std::cos(angle/2)
};
AxisAngle::AxisAngle(const Quaternion& rhs) {
float halfAngle = std::acos(rhs.w);
angle = halfAngle * 2.f;
float reciprocalSinAngle = 1.f / std::sqrt(1.f - rhs.w*rhs.w);
axis = { rhs.x*reciprocalSinAngle, rhs.y*reciprocalSinAngle, rhs.z*reciprocalSinAngle };
}
AxisAngle::AxisAngle(const Quaternion &q) {
auto theta = std::acos(q.w) * 2.f;
auto ax = q.x / std::sin(std::acos(theta));
auto ay = q.y / std::sin(std::acos(theta));
auto az = q.z / std::sin(std::acos(theta));
Quaternion AxisAngle::ToQuaternion() const { return Quaternion(*this); }
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);
}
AxisAngle::AxisAngle(const EulerAngle &e) {
Matrix3x3 AxisAngle::ToMatrix3x3() const {
return Matrix3x3(*this);
}
// Assuming the angles are in radians
void AxisAngle::Inverse() {
angle = -angle;
}
float heading = e.pitch;
float attitude = e.yaw;
float bank = e.roll;
AxisAngle AxisAngle::Inverted() const {
return {axis, -angle};
}
float c1 = std::cos(heading / 2.f);
float s1 = std::sin(heading / 2.f);
float c2 = std::cos(attitude / 2.f);
float s2 = std::sin(attitude / 2.f);
float c3 = std::cos(bank / 2.f);
float s3 = std::sin(bank / 2.f);
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};
}
float w = c1*c2*c3 - s1*s2*s3;
float x = c1*c2*c3 + s1*s2*s3;
float y = s1*c2*c3 + c1*s2*s3;
float z = c1*s2*c3 - s1*c2*s3;
AxisAngle AxisAngle::Slerp(const AxisAngle &rhs, float t) {
Quaternion a(*this);
Quaternion b(rhs);
angle = 2.f * std::acos(w);
Quaternion intermediate = a.Slerp(b, t);
double norm = x*x + y*y + z*z;
if (norm < 0.001) { // when all euler angles are zero angle=0, so
// we can set axis to anything to avoid divide by zero
x = 1;
y = z = 0;
} else {
norm = std::sqrt(norm);
x /= norm;
y /= norm;
z /= norm;
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;
}
axis = {x, y, z};
}
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,147 +0,0 @@
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
#include <cmath>
#include <algorithm>
namespace J3ML::LinearAlgebra {
EulerAngle::EulerAngle(float pitch, float yaw, float roll): pitch(pitch), yaw(yaw), roll(roll)
{}
float EulerAngle::GetPitch(float pitch_limit) const
{ return std::clamp( std::remainderf(pitch,360.f), -pitch_limit, pitch_limit); }
float EulerAngle::GetYaw(float yaw_limit) const
{ return std::clamp(std::remainderf(yaw, 360.f), -yaw_limit, yaw_limit); }
float EulerAngle::GetRoll(float pitch_limit) const
{ return std::clamp( std::remainderf(pitch,360.f), -pitch_limit, pitch_limit); }
bool EulerAngle::operator==(const EulerAngle& a) const
{
return (pitch == a.pitch) && (yaw == a.yaw) && (roll == a.roll);
}
void EulerAngle::clamp()
{
if (this->pitch > 89.0f)
this->pitch = 89.0f;
if (this->pitch <= -89.0f)
this->pitch = -89.0f;
//TODO: Make this entirely seamless by getting the amount they rotated passed -180 and +180 by.
if (this->yaw <= -180.0f)
this->yaw = 180.0f;
if (this->yaw >= 180.01f)
this->yaw = -179.9f;
if (this->roll >= 360.0f)
this->roll = 0.0;
if (this->roll <= -360.0f)
this->roll = 0.0;
}
EulerAngle EulerAngle::movementAngle() const
{
EulerAngle a;
a.pitch = (cos(Math::Radians(yaw)) * cos(Math::Radians(pitch)));
a.yaw = -sin(Math::Radians(pitch));
a.roll = (sin(Math::Radians(yaw)) * cos(Math::Radians(pitch)));
return a;
}
EulerAngle::EulerAngle() : pitch(0), yaw(0), roll(0) {}
EulerAngle::EulerAngle(const AxisAngle &rhs) {
float x = rhs.axis.x;
float y = rhs.axis.y;
float z = rhs.axis.z;
float angle = rhs.angle;
double s = std::sin(rhs.angle);
double c = std::cos(rhs.angle);
double t = 1-c;
// if axis is not already normalized then uncomment this
// double magnitude = std::sqrt(x*x + y*y + z*z);
// if (magnitude == 0) throw error;
// x /= magnitude;
// y /= magnitude;
// z /= magnitude;
if ((x*y*t + z*s) > 0.998) { // North pole singularity detected
pitch = 2 * std::atan2(x * std::sin(angle/2.f), std::cos(angle/2.f));
yaw = Math::Pi / 2.f;
roll = 0;
return;
}
if ((x*y*t + z*s) < -0.998) { // South pole singularity detected
pitch = -2 * std::atan2(x * std::sin(angle/2.f), std::cos(angle/2.f));
yaw = -Math::Pi / 2.f;
roll = 0;
return;
}
pitch = std::atan2(y * s-x * z * t, 1 - (y*y + z*z) * t);
yaw = std::asin(x * y * t + z * s);
roll = std::atan2(x * s - y * z * t, 1 - (x*x + z*z) * t);
}
AxisAngle EulerAngle::ToAxisAngle() const {
auto c1 = std::cos(yaw / 2);
auto c2 = std::cos(pitch / 2);
auto c3 = std::cos(roll / 2);
auto s1 = std::sin(yaw / 2);
auto s2 = std::sin(pitch / 2);
auto s3 = std::sin(roll / 2);
auto angle = 2 * std::acos(c1*c2*c3 - s1*s2*s3);
auto x = s1*s2*c3 + c1*c2*s3;
auto y = s1*c2*c3 + c1*s2*s3;
auto z = c1*s2*c3 - s1*c2*s3;
// todo: normalize?
// sqrt(x^2 + y^2 + z^2) = sqrt((s1 s2 c3 +c1 c2 s3)^2+(s1 c2 c3 + c1 s2 s3)^2+(c1 s2 c3 - s1 c2 s3)^2)
return {{x,y,z}, angle};
}
Quaternion EulerAngle::ToQuaternion() const {
auto c1 = std::cos(yaw / 2);
auto c2 = std::cos(pitch / 2);
auto c3 = std::cos(roll / 2);
auto s1 = std::sin(yaw / 2);
auto s2 = std::sin(pitch / 2);
auto s3 = std::sin(roll / 2);
auto w = c1*c2*c3 - s1*s2*s3;
auto x = s1*s2*c3 + c1*c2*s3;
auto y = s1*c2*c3 + c1*s2*s3;
auto z = c1*s2*c3 - s1*c2*s3;
return {w,x,y,z};
}
EulerAngle::EulerAngle(const Quaternion &rhs) {
double test = rhs.x * rhs.y + rhs.z * rhs.w;
if (test > 0.499) { // Singularity at north pole
pitch = 2 * std::atan2(rhs.x, rhs.w);
yaw = Math::Pi / 2.f;
roll = 0;
return;
}
if (test < -0.499) { // Singularity at south pole
pitch = -2 * std::atan2(rhs.x, rhs.y);
yaw = - Math::Pi / 2.f;
roll = 0;
return;
}
float sqx = rhs.x * rhs.x;
float sqy = rhs.y * rhs.y;
float sqz = rhs.z * rhs.z;
}
}

View File

@@ -109,27 +109,8 @@ namespace J3ML::LinearAlgebra {
//this->elems[2][2] = r3.z;
}
Matrix3x3::Matrix3x3(const Quaternion &orientation) {
SetRotatePart(orientation);
}
Matrix3x3::Matrix3x3(const Quaternion& orientation) {
Matrix3x3::Matrix3x3(const EulerAngle &orientation) {
auto sa = std::sin(orientation.pitch);
auto ca = std::cos(orientation.pitch);
auto sb = std::sin(orientation.roll);
auto cb = std::cos(orientation.roll);
auto sh = std::sin(orientation.yaw);
auto ch = std::cos(orientation.yaw);
At(0, 0) = ch*ca;
At(0, 1) = -ch*sa*cb + sh*sh;
At(0, 2) = ch*sa*sb + sh*cb;
At(1, 0) = sa;
At(1, 1) = ca*cb;
At(1, 2) = -ca*cb;
At(2, 0) = -sh*ca;
At(2, 1) = sh*sa*cb + ch*sb;
At(2, 2) = -sh*sa*sb + ch*cb;
}
float Matrix3x3::Determinant() const {
@@ -207,7 +188,7 @@ namespace J3ML::LinearAlgebra {
};
}
Quaternion Matrix3x3::ToQuat() const {
/*Quaternion Matrix3x3::ToQuat() const {
auto m00 = At(0,0);
auto m01 = At(0, 1);
auto m02 = At(0, 2);
@@ -226,7 +207,7 @@ namespace J3ML::LinearAlgebra {
(m10 - m01) / w4,
w
};
}
}*/
void Matrix3x3::SetRotatePart(const Vector3 &a, float angle) {
float s = std::sin(angle);
@@ -992,25 +973,12 @@ namespace J3ML::LinearAlgebra {
bool Matrix3x3::TryConvertToQuat(Quaternion &q) const {
if (IsColOrthogonal() && HasUnitaryScale() && !HasNegativeScale()) {
q = ToQuat();
q = Quaternion(*this);
return true;
}
return false;
}
EulerAngle Matrix3x3::ToEulerAngle() const {
auto heading = std::atan2(-At(2, 0), At(0, 0));
auto attitude = std::asin(At(1, 0));
auto bank = std::atan2(-At(1,2), At(1,1));
if (At(1, 0) == 1 || At(1, 0) == -1) // North Pole || South Pole
{
heading = std::atan2(At(0, 2), At(2,2));
bank = 0;
}
return {attitude, heading, bank};
}
void Matrix3x3::BatchTransform(Vector3 *pointArray, int numPoints, int stride) const {
assert(pointArray || numPoints == 0);
assert(stride >= (int)sizeof(Vector3));
@@ -1119,6 +1087,18 @@ namespace J3ML::LinearAlgebra {
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/Vector4.hpp>
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/Matrices.inl>
#include <iostream>
namespace J3ML::LinearAlgebra {
@@ -86,10 +87,6 @@ namespace J3ML::LinearAlgebra {
Set3x3Part(Matrix3x3(orientation));
}
Matrix4x4::Matrix4x4(const EulerAngle &orientation) {
Set3x3Part(Matrix3x3(orientation));
}
void Matrix4x4::SetTranslatePart(float translateX, float translateY, float translateZ) {
elems[0][3] = translateX;
elems[1][3] = translateY;
@@ -683,6 +680,11 @@ namespace J3ML::LinearAlgebra {
}
std::ostream & operator<<(std::ostream &out, const Matrix4x4 &rhs) {
out << rhs.ToString();
return out;
}
void Matrix4x4::InverseOrthonormal()
{
//assert(!ContainsProjection());
@@ -777,19 +779,6 @@ namespace J3ML::LinearAlgebra {
};
}
EulerAngle Matrix4x4::ToEulerAngle() const {
auto heading = std::atan2(-At(2, 0), At(0, 0));
auto attitude = std::asin(At(1, 0));
auto bank = std::atan2(-At(1,2), At(1,1));
if (At(1, 0) == 1 || At(1, 0) == -1) // North Pole || South Pole
{
heading = std::atan2(At(0, 2), At(2,2));
bank = 0;
}
return {attitude, heading, bank};
}
bool Matrix4x4::InverseOrthogonalUniformScale() {
assert(!ContainsProjection());
assert(IsColOrthogonal(1e-3f));
@@ -1031,6 +1020,46 @@ namespace J3ML::LinearAlgebra {
return true;
}
std::string trim_trailing_zeros(const std::string &value) {
std::string str = value;
// Ensure that there is a decimal point somewhere (there should be)
if(str.find('.') != std::string::npos)
{
// Remove trailing zeroes
str = str.substr(0, str.find_last_not_of('0')+1);
// If the decimal point is now the last character, remove that as well
if(str.find('.') == str.size()-1)
{
str = str.substr(0, str.size()-1);
}
}
return str;
}
std::string Matrix4x4::ToString() const {
// Determine the maximum width for any element in the matrix.
size_t max_width = 0;
for (size_t row = 0; row < 4; ++row) {
for (size_t col = 0; col < 4; ++col) {
std::string s = std::to_string(At(row, col));
s = trim_trailing_zeros(s);
max_width = std::max(max_width, s.length());
}
}
for (size_t row = 0; row < 4; ++row) {
for (size_t col = 0; col < 4; ++col) {
auto val = this->At(row, col);
std::cout << std::right << std::setw(static_cast<int>(max_width)) << val << " ";
}
std::cout << std::endl;
}
}
void
Matrix4x4::Set(float _00, float _01, float _02, float _03, float _10, float _11, float _12, float _13, float _20,
float _21, float _22, float _23, float _30, float _31, float _32, float _33) {

View File

@@ -1,23 +1,81 @@
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Vector4.hpp>
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
#include <J3ML/LinearAlgebra/Matrix4x4.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/AxisAngle.hpp>
#include <J3ML/LinearAlgebra/Vector4.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp>
namespace J3ML::LinearAlgebra {
const Quaternion Quaternion::Identity = Quaternion(0.f, 0.f, 0.f, 1.f);
const Quaternion Quaternion::NaN = Quaternion(NAN, NAN, NAN, NAN);
Quaternion Quaternion::operator-() const
{
Quaternion Quaternion::operator-() const {
return {-x, -y, -z, -w};
}
Quaternion::Quaternion(const Matrix3x3 &rotationMtrx) {}
Quaternion::Quaternion(const Matrix3x3& ro_mat) {
// https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
Quaternion::Quaternion(const Matrix4x4 &rotationMtrx) {}
auto m = ro_mat;//.Transposed();
auto m00 = m.At(0,0);
auto m01 = m.At(0, 1);
auto m02 = m.At(0, 2);
auto m10 = m.At(1, 0);
auto m11 = m.At(1, 1);
auto m12 = m.At(1, 2);
auto m20 = m.At(2, 0);
auto m21 = m.At(2, 1);
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;
float w4 = (4.f * field_w);
x = (m21 - m12) / w4;
y = (m02 - m20) / w4;
z = (m10 - m01) / w4;
w = field_w;
bool success = Normalize();
// TODO: Validate normalization success.
*/
}
Quaternion::Quaternion(const Matrix4x4& ro_mat) {
auto q = Quaternion(ro_mat.GetRotatePart());
x = q.x; y = q.y; z = q.z; w = q.w;
}
Vector3 Quaternion::WorldX() const { return Transform(1.f, 0.f, 0.f); }
@@ -50,24 +108,8 @@ namespace J3ML::LinearAlgebra {
return (*this * (t - 1.f) + b * t).Normalized();
}
void Quaternion::SetFromAxisAngle(const Vector3 &axis, float angle) {
float sinz, cosz;
sinz = std::sin(angle*0.5f);
cosz = std::cos(angle*0.5f);
x = axis.x * sinz;
y = axis.y * sinz;
z = axis.z * sinz;
w = cosz;
}
void Quaternion::SetFromAxisAngle(const Vector4 &axis, float angle)
{
SetFromAxisAngle(Vector3(axis.x, axis.y, axis.z), angle);
}
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 {
@@ -82,11 +124,8 @@ namespace J3ML::LinearAlgebra {
Quaternion Quaternion::operator+() const { return *this; }
Quaternion::Quaternion() {}
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 {
return x * rhs.x + y * rhs.y + z * rhs.z + w * rhs.w;
}
@@ -148,20 +187,6 @@ namespace J3ML::LinearAlgebra {
return (*this * (a * sign) + q2 * b).Normalized();
}
AxisAngle Quaternion::ToAxisAngle() const {
float halfAngle = std::acos(w);
float angle = halfAngle * 2.f;
// TODO: Can Implement Fast Inverted Sqrt Here
float reciprocalSinAngle = 1.f / std::sqrt(1.f - w*w);
Vector3 axis = {
x*reciprocalSinAngle,
y*reciprocalSinAngle,
z*reciprocalSinAngle
};
return AxisAngle(axis, angle);
}
float Quaternion::AngleBetween(const Quaternion &target) const {
Quaternion delta = target / *this;
return delta.Normalized().Angle();
@@ -212,47 +237,14 @@ namespace J3ML::LinearAlgebra {
return Vector3(x, y, z) * rcpSinAngle;
}
Quaternion::Quaternion(const Vector3 &rotationAxis, float rotationAngleRadians) {
SetFromAxisAngle(rotationAxis, rotationAngleRadians);
}
Quaternion::Quaternion(const Vector4 &rotationAxis, float rotationAngleRadians) {
SetFromAxisAngle(rotationAxis, rotationAngleRadians);
}
Quaternion::Quaternion(const AxisAngle &angle) {
double s = std::sin(angle.angle / 2);
Quaternion::Quaternion(const AxisAngle& angle) {
float s = Math::Sin(angle.angle / 2.f);
x = angle.axis.x * s;
y = angle.axis.y * s;
z = angle.axis.z * s;
w = std::cos(angle.angle / 2);
}
Quaternion::Quaternion(const EulerAngle &angle) {
// Abbreviations for the various angular functions
double cr = std::cos(angle.roll * 0.5);
double sr = std::sin(angle.roll * 0.5);
double cp = std::cos(angle.pitch * 0.5);
double sp = std::sin(angle.pitch * 0.5);
double cy = std::cos(angle.yaw * 0.5);
double sy = std::sin(angle.yaw * 0.5);
w = cr * cp * cy + sr * sp * sy;
x = sr * cp * cy - cr * sp * sy;
y = cr * sp * cy + sr * cp * sy;
z = cr * cp * sy - sr * sp * cy;
}
void Quaternion::SetFrom(const AxisAngle &angle) {
double s = std::sin(angle.angle / 2);
x = angle.axis.x * s;
y = angle.axis.y * s;
z = angle.axis.z * s;
w = std::cos(angle.angle / 2);
}
EulerAngle Quaternion::ToEulerAngle() const {
return EulerAngle(*this);
w = Math::Cos(angle.angle / 2);
bool success = Normalize();
// TODO: Validate normalization success.
}
Quaternion Quaternion::RandomRotation(RNG &rng) {
@@ -271,16 +263,16 @@ namespace J3ML::LinearAlgebra {
return Quaternion::Identity;
}
float Quaternion::Normalize() {
bool Quaternion::Normalize() {
float length = Length();
if (length < 1e-4f)
return 0.f;
return false;
float rcpLength = 1.f / length;
x *= rcpLength;
y *= rcpLength;
z *= rcpLength;
w *= rcpLength;
return length;
return true;
}
bool Quaternion::IsNormalized(float epsilon) const {
@@ -325,9 +317,10 @@ namespace J3ML::LinearAlgebra {
Quaternion Quaternion::LookAt(const Vector3 &localForward, const Vector3 &targetDirection, const Vector3 &localUp,
const Vector3 &worldUp) {
return Matrix3x3::LookAt(localForward, targetDirection, localUp, worldUp).ToQuat();
return Quaternion(Matrix3x3::LookAt(localForward, targetDirection, localUp, worldUp));
}
/*
Quaternion Quaternion::RotateX(float angleRadians) {
return {{1,0,0}, angleRadians};
}
@@ -343,6 +336,7 @@ namespace J3ML::LinearAlgebra {
Quaternion Quaternion::RotateAxisAngle(const AxisAngle &axisAngle) {
return {axisAngle.axis, axisAngle.angle};
}
*/
Quaternion Quaternion::RotateFromTo(const Vector3 &sourceDirection, const Vector3 &targetDirection) {
assert(sourceDirection.IsNormalized());
@@ -369,14 +363,6 @@ namespace J3ML::LinearAlgebra {
return Quaternion::RotateFromTo(sourceDirection.XYZ(), targetDirection.XYZ());
}
Quaternion::Quaternion(const float *data) {
assert(data);
x = data[0];
y = data[1];
z = data[2];
w = data[3];
}
Quaternion Quaternion::Lerp(const Quaternion &source, const Quaternion &target, float t) { return source.Lerp(target, t);}
Quaternion Quaternion::Slerp(const Quaternion &source, const Quaternion &target, float t) { return source.Slerp(target, t);}
@@ -401,4 +387,30 @@ namespace J3ML::LinearAlgebra {
float Quaternion::LengthSquared() const { return x*x + y*y + z*z + w*w;}
float Quaternion::Length() const { return std::sqrt(LengthSquared()); }
Quaternion::Quaternion(const Quaternion& rhs) {
x = rhs.x;
y = rhs.y;
z = rhs.z;
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 {
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

@@ -7,6 +7,7 @@
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
#include <J3ML/LinearAlgebra/Matrix4x4.hpp>
#include <J3ML/LinearAlgebra/Vector4.hpp>
#include <J3ML/LinearAlgebra/Vector2i.hpp>
namespace J3ML::LinearAlgebra {
@@ -36,12 +37,12 @@ namespace J3ML::LinearAlgebra {
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
{
return this->IsWithinMarginOfError(rhs) == false;
return !(*this == rhs);
}
Vector2 Vector2::Min(const Vector2& min) const
@@ -499,6 +500,33 @@ namespace J3ML::LinearAlgebra {
return std::format("{},{}", x, y);
}
bool Vector2::operator>(const Vector2 &rhs) const {
return this->Magnitude() > rhs.Magnitude();
}
bool Vector2::operator<(const Vector2 &rhs) const {
return this->Magnitude() < rhs.Magnitude();
}
Vector2::Vector2(const Vector2i& rhs) {
x = rhs.x;
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) {
return {lhs * rhs.x, lhs * rhs.y};
}

View File

@@ -0,0 +1,79 @@
#include <J3ML/LinearAlgebra/Vector2i.hpp>
J3ML::LinearAlgebra::Vector2i &J3ML::LinearAlgebra::Vector2i::operator =(const Vector2i& rhs) {
x = rhs.x;
y = rhs.y;
return *this;
}
bool J3ML::LinearAlgebra::Vector2i::operator ==(const Vector2i& rhs) const {
return (x == rhs.x && y == rhs.y);
}
bool J3ML::LinearAlgebra::Vector2i::operator !=(const Vector2i& rhs) const {
return !(*this == rhs);
}
J3ML::LinearAlgebra::Vector2i& J3ML::LinearAlgebra::Vector2i::operator +=(const Vector2i& rhs) {
x += rhs.x;
y += rhs.y;
return *this;
}
J3ML::LinearAlgebra::Vector2i& J3ML::LinearAlgebra::Vector2i::operator -=(const Vector2i& rhs) {
x -= rhs.x;
y -= rhs.y;
return *this;
}
J3ML::LinearAlgebra::Vector2i& J3ML::LinearAlgebra::Vector2i::operator *=(const Vector2i& rhs) {
x *= rhs.x;
y *=rhs.y;
return *this;
}
J3ML::LinearAlgebra::Vector2i& J3ML::LinearAlgebra::Vector2i::operator /=(const Vector2i& rhs) {
x /= rhs.x;
y /=rhs.y;
return *this;
}
J3ML::LinearAlgebra::Vector2i J3ML::LinearAlgebra::Vector2i::operator +(const Vector2i& rhs) const {
return {x + rhs.x, y + rhs.y};
}
J3ML::LinearAlgebra::Vector2i J3ML::LinearAlgebra::Vector2i::operator -(const Vector2i& rhs) const {
return {x - rhs.x, y - rhs.y};
}
J3ML::LinearAlgebra::Vector2i J3ML::LinearAlgebra::Vector2i::operator *(const Vector2i& rhs) const {
return {x * rhs.x, y * rhs.y};
}
J3ML::LinearAlgebra::Vector2i J3ML::LinearAlgebra::Vector2i::operator /(const Vector2i& rhs) const {
return {x / rhs.x, y / rhs.y};
}
std::string J3ML::LinearAlgebra::Vector2i::ToString() const {
return std::string(std::to_string(x) + " " + std::to_string(y));
}
J3ML::LinearAlgebra::Vector2i::Vector2i() {
x = 0;
y = 0;
}
J3ML::LinearAlgebra::Vector2i J3ML::LinearAlgebra::Vector2i::operator *(const int rhs) const {
return {x * rhs, y * rhs};
}
J3ML::LinearAlgebra::Vector2i J3ML::LinearAlgebra::Vector2i::operator/(int rhs) const {
return {x / rhs, y / rhs};
}

View File

@@ -10,6 +10,7 @@
namespace J3ML::LinearAlgebra {
const Vector3 Vector3::Zero = {0,0,0};
const Vector3 Vector3::One = {1, 1, 1};
const Vector3 Vector3::Up = {0, -1, 0};
const Vector3 Vector3::Down = {0, 1, 0};
const Vector3 Vector3::Left = {-1, 0, 0};

View File

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

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

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

View File

@@ -53,19 +53,19 @@ namespace TriangleTests {
);
// 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
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
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
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
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
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
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(
{0.0f, 0.0f, 0.0f},
@@ -74,11 +74,11 @@ namespace TriangleTests {
);
// Should collide on V0-V1 edge
jtest::check(Intersects(yxTriangle, yxTriangle));
//jtest::check(Intersects(yxTriangle, yxTriangle));
// 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
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(
{0.0f, 1.0f, -1.0f},
@@ -86,11 +86,11 @@ namespace TriangleTests {
{0.0f, 1.0f, 1.0f}
);
// Should collide exactly on V1
jtest::check(Intersects(xyTriangle, zyInvertedTriangle));
//jtest::check(Intersects(xyTriangle, zyInvertedTriangle));
// 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
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

@@ -0,0 +1,46 @@
#include <jtest/jtest.hpp>
#include <jtest/Unit.hpp>
jtest::Unit AxisAngleUnit {"AxisAngle"};
namespace AxisAngleTests {
inline void Define() {
using namespace jtest;
AxisAngleUnit += Test("CtorFromQuaternion", [] {
AxisAngle expected_result({0.3860166, 0.4380138, 0.8118714}, 0.6742209);
Quaternion q(0.1276794, 0.1448781, 0.2685358, 0.9437144);
AxisAngle from_quaternion(q);
jtest::check(expected_result.Equals(from_quaternion));
});
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() {
AxisAngleUnit.RunAll();
}
}

View File

@@ -1,21 +0,0 @@
//
// Created by josh on 12/26/2023.
//
#include <jtest/jtest.hpp>
#include <jtest/Unit.hpp>
jtest::Unit EulerAngleUnit {"EulerAngle"};
namespace EulerAngleTests {
inline void Define() {
using namespace jtest;
EulerAngleUnit += Test("Not Implemented", [] {
throw("Not Implemented");
});
}
inline void Run() {
EulerAngleUnit.RunAll();
}
}

View File

@@ -11,6 +11,42 @@ namespace Matrix3x3Tests
using namespace jtest;
using namespace J3ML::LinearAlgebra;
/*Matrix3x3Unit += Test("AngleTypeRound-TripConversion", [] {
EulerAngleXYZ expected_result(8, 60, -27);
Matrix3x3 m(expected_result);
AxisAngle a(expected_result);
Quaternion q(a);
Matrix3x3 m2(q);
Quaternion q2(m2);
AxisAngle a2(q2);
EulerAngleXYZ round_trip(a2);
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.yaw), Math::Radians(round_trip.yaw), 1e-6f));
});*/
/*Matrix3x3Unit += Test("From_EulerAngleXYZ", []{
Matrix3x3 expected_result(Vector3(0.4455033, 0.2269952, 0.8660254),
Vector3(-0.3421816, 0.9370536, -0.0695866),
Vector3(-0.8273081, -0.2653369, 0.4951340)
);
EulerAngleXYZ e(8, 60, -27);
Matrix3x3 from_euler(e);
jtest::check(Math::EqualAbs(expected_result.At(0, 0), from_euler.At(0, 0), 1e-6f));
jtest::check(Math::EqualAbs(expected_result.At(0, 1), from_euler.At(0, 1), 1e-6f));
jtest::check(Math::EqualAbs(expected_result.At(0, 2), from_euler.At(0, 2), 1e-6f));
jtest::check(Math::EqualAbs(expected_result.At(1, 0), from_euler.At(1, 0), 1e-6f));
jtest::check(Math::EqualAbs(expected_result.At(1, 1), from_euler.At(1, 1), 1e-6f));
jtest::check(Math::EqualAbs(expected_result.At(1, 2), from_euler.At(1, 2), 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, 2), from_euler.At(2, 2), 1e-6f));
});*/
Matrix3x3Unit += Test("Add_Unary", []
{
Matrix3x3 m(1,2,3, 4,5,6, 7,8,9);

View File

@@ -11,8 +11,7 @@ namespace Matrix4x4Tests {
using namespace J3ML::LinearAlgebra;
using namespace J3ML::Math;
Matrix4x4Unit += Test("Add_Unary", []
{
Matrix4x4Unit += Test("Add_Unary", [] {
Matrix4x4 m(1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16);
Matrix4x4 m2 = +m;
jtest::check(m.Equals(m2));
@@ -38,6 +37,13 @@ namespace Matrix4x4Tests {
}
});
Matrix4x4Unit += Test("CtorFromRotationMatrix", []{
Matrix3x3 m = Matrix3x3::RotateX(40);
Matrix4x4 from3x3(m);
});
Matrix4x4Unit += Test("Ctor", []{
RNG rng;
Matrix3x3 m = Matrix3x3::RandomGeneral(rng, -10.f, 10.f);
@@ -46,16 +52,16 @@ namespace Matrix4x4Tests {
for (int y = 0; y < 3; ++y)
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[2][3], 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][2], 0.f));
jtest::check(Math::EqualAbs(m2[3][3], 0.f));
jtest::check(Math::EqualAbs(m2[3][3], 0.f));*/
});
Matrix4x4Unit += Test("SetRow", []
@@ -104,27 +110,52 @@ namespace Matrix4x4Tests {
});
Matrix4x4Unit += Test("CtorFromQuatTrans", [] {});
Matrix4x4Unit += Test("Translate", [] {});
Matrix4x4Unit += Test("Scale", [] {});
Matrix4x4Unit += Test("InverseOrthogonalUniformScale", [] {});
Matrix4x4Unit += Test("InverseOrthonormal", [] {});
Matrix4x4Unit += Test("DeterminantCorrectness", [] { });
Matrix4x4Unit += Test("MulMat3x3", [] {});
Matrix4x4Unit += Test("CtorFromQuatTrans", [] {
RNG rng;
constexpr float SCALE = 1e2f;
Vector3 t = Vector3::RandomBox(rng, Vector3(-SCALE, -SCALE, -SCALE), Vector3(SCALE, SCALE, SCALE));
Quaternion q = Quaternion::RandomRotation(rng);
Matrix4x4 m (q, t);
Vector3 v = Vector3(-1, 5, 20.f);
Vector3 v1 = q * v + t;
Vector3 v2 = m.Transform(v);
jtest::check(v1.Equals(v2));
Matrix4x4Unit += Test("AngleTypeRoundtripConversion", [] {
Matrix4x4 matrix;
EulerAngle a(Math::Radians(45), Math::Radians(45), Math::Radians(45));
Quaternion q(a);
matrix.SetRotatePart(q);
//matrix.SetRotatePartX(a.pitch);
//matrix.SetRotatePartY(a.yaw);
//matrix.SetRotatePartZ(a.roll);
EulerAngle fromMatrix = matrix.GetRotatePart().ToQuat().ToEulerAngle();
jtest::check(a == fromMatrix);
});
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() {

View File

@@ -5,9 +5,13 @@
#include <jtest/Unit.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/Algorithm/RNG.hpp>
#include <J3ML/Math.hpp>
jtest::Unit QuaternionUnit {"Quaternion"};
namespace QuaternionTests {
// This is here to check the accuracy of the Slerp inside the Quaternion class.
// Although you don't jtest::check anything :shrug: - Redacted.
Quaternion PreciseSlerp(const Quaternion &a, const Quaternion& b, float t)
{
double angle = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
@@ -69,13 +73,65 @@ namespace QuaternionTests {
Quaternion lerp = q.Lerp(q2, t);
}
});
QuaternionUnit += Test("Mat4x4Conversion", [] { throw("Not Implemented"); });
QuaternionUnit += Test("MulOpQuat", [] { throw("Not Implemented"); });
QuaternionUnit += Test("Constants", [] {
Quaternion id {0.f, 0.f, 0.f, 1.f};
jtest::check(id.Equals(Quaternion::Identity));
});
QuaternionUnit += Test("From_AxisAngle", [] {
Quaternion expected_result(0.0579133, 0.0782044, 0.1765667, 0.9794664);
AxisAngle a({0.2872573, 0.3879036, 0.8757934}, 0.4059981);
Quaternion from_axis(a);
jtest::check(Math::EqualAbs(expected_result.x, from_axis.x, 1e-6f));
jtest::check(Math::EqualAbs(expected_result.y, from_axis.y, 1e-6f));
jtest::check(Math::EqualAbs(expected_result.z, from_axis.z, 1e-6f));
jtest::check(Math::EqualAbs(expected_result.w, from_axis.w, 1e-6f));
});
QuaternionUnit += Test("Ctor_FromMatrix3x3", [] {
// 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("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("Transform", [] { throw("Not Implemented"); });
}
inline void Run()

View File

@@ -35,7 +35,7 @@ namespace Vector4Tests
jtest::check_float_eq(Input.w, 1);
});
Vector4Unit += Test("Vector4::Addition_Op", [] {
Vector4Unit += Test("Addition_Op", [] {
Vector4 A (1, 1, 1, 1);
Vector4 B (2, 2, 2, 2);
@@ -43,6 +43,19 @@ namespace Vector4Tests
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()

View File

@@ -15,7 +15,7 @@
#include "Geometry/AABBTests.hpp"
#include "Geometry/FrustumTests.hpp"
#include "LinearAlgebra/EulerAngleTests.hpp"
#include "LinearAlgebra/AxisAngleTests.hpp"
#include "LinearAlgebra/Matrix2x2Tests.hpp"
#include "LinearAlgebra/Matrix3x3Tests.hpp"
#include "LinearAlgebra/Matrix4x4Tests.hpp"
@@ -64,10 +64,10 @@ namespace LinearAlgebraTests
{
void Define()
{
EulerAngleTests::Define();
Vector2Tests::Define();
Vector3Tests::Define();
Vector4Tests::Define();
AxisAngleTests::Define();
QuaternionTests::Define();
Matrix2x2Tests::Define();
Matrix3x3Tests::Define();
@@ -76,10 +76,10 @@ namespace LinearAlgebraTests
}
void Run()
{
EulerAngleTests::Run();
Vector2Tests::Run();
Vector3Tests::Run();
Vector4Tests::Run();
AxisAngleTests::Run();
QuaternionTests::Run();
Matrix2x2Tests::Run();
Matrix3x3Tests::Run();