Compare commits

...

8 Commits

Author SHA1 Message Date
85aac1c192 Update CMakeLists.txt
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m44s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 24s
Get rid of event cmake warning.
2024-10-10 12:14:21 -04:00
29db4f0792 Implement rest of LineSegment2D members.
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 27s
2024-10-07 15:36:48 -04:00
143b7e7279 Implementing LineSegment2D class
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m30s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 28s
2024-10-06 22:25:01 -04:00
3861741ff2 Remove inlining of Capsule::AnyPointFast() (seems to prevent compilation on -O3 optimization level)
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m16s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 28s
2024-10-04 12:36:43 -04:00
262603a496 Merge remote-tracking branch 'origin/main'
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m25s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 23s
2024-10-01 10:38:56 -04:00
5e0e4b8349 Implementing LineSegment2D class 2024-10-01 10:38:49 -04:00
Redacted
756316169e Update README.md
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m15s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 20s
2024-09-17 14:46:53 -04:00
237c318bf1 fix https://git.redacted.cc/Redacted/ReWindow.git using rebitch
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 21s
2024-08-26 19:49:12 -04:00
8 changed files with 408 additions and 17 deletions

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.18...3.28)
cmake_minimum_required(VERSION 3.18...3.27)
PROJECT(J3ML
VERSION 1.1
LANGUAGES CXX
@@ -34,7 +34,7 @@ set_target_properties(J3ML PROPERTIES LINKER_LANGUAGE CXX)
CPMAddPackage(
NAME jtest
URL https://git.redacted.cc/josh/jtest/archive/Release-1.2.zip
URL https://git.redacted.cc/josh/jtest/archive/Release-1.4.zip
)
target_include_directories(J3ML PUBLIC ${jtest_SOURCE_DIR}/include)
@@ -58,4 +58,4 @@ target_link_libraries(MathDemo ${PROJECT_NAME})
if(WIN32)
#target_compile_options(MathDemo PRIVATE -mwindows)
endif()
endif()

View File

@@ -4,7 +4,7 @@
Yet Another C++ Math Standard
J3ML is a "Modern C++" C++ library designed to provide comprehensive support for 3D mathematical operations commonly used in computer graphics, game development, physics simulations, and related fields. It offers a wide range of functionalities to simplify the implementation of complex mathematical operations in your projects.
J3ML is a "Modern C++" library designed to provide comprehensive support for 3D mathematical operations commonly used in computer graphics, game development, physics simulations, and related fields. It offers a wide range of functionalities to simplify the implementation of complex mathematical operations in your projects.
![Static Badge](https://img.shields.io/badge/Lit-Based-%20)
@@ -50,7 +50,7 @@ Documentation is automatically generated from latest commit and is hosted at htt
# Contributing
Contributions to J3ML are welcome! If you find a bug, have a feature request, or would like to contribute code, please submit an issue or pull request to the GitHub repository.
Contributions to J3ML are welcome! If you find a bug, have a feature request, or would like to contribute code, please submit an issue or pull request to the repository.
# License

View File

@@ -16,6 +16,15 @@
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
/// See Christer Ericson's Real-time Collision Detection, p. 87, or
/// James Arvo's "Transforming Axis-aligned Bounding Boxes" in Graphics Gems 1, pp. 548-550.
/// http://www.graphicsgems.org/
template <typename Matrix>
void AABB2DTransformAsAABB2D(AABB2D& aabb, Matrix& m);
namespace J3ML::Geometry
{
using LinearAlgebra::Vector2;
@@ -31,28 +40,30 @@ namespace J3ML::Geometry
minPoint(min), maxPoint(max)
{}
float Width() const;
float Height() const;
[[nodiscard]] float Width() const;
[[nodiscard]] float Height() const;
float DistanceSq(const Vector2& pt) const;
Vector2 Centroid();
[[nodiscard]] float DistanceSq(const Vector2& pt) const;
void SetNegativeInfinity();
void Enclose(const Vector2& point);
bool Intersects(const AABB2D& rhs) const;
[[nodiscard]] bool Intersects(const AABB2D& rhs) const;
bool Contains(const AABB2D& rhs) const;
[[nodiscard]] bool Contains(const AABB2D& rhs) const;
bool Contains(const Vector2& pt) const;
[[nodiscard]] bool Contains(const Vector2& pt) const;
bool Contains(int x, int y) const;
[[nodiscard]] bool Contains(int x, int y) const;
bool IsDegenerate() const;
[[nodiscard]] bool IsDegenerate() const;
bool HasNegativeVolume() const;
[[nodiscard]] bool HasNegativeVolume() const;
bool IsFinite() const;
[[nodiscard]] bool IsFinite() const;
Vector2 PosInside(const Vector2 &normalizedPos) const;
@@ -62,5 +73,30 @@ namespace J3ML::Geometry
AABB2D operator-(const Vector2& pt) const;
Vector2 CornerPoint(int cornerIndex);
void TransformAsAABB(const Matrix3x3& transform);
void TransformAsAABB(const Matrix4x4& transform);
};
template<typename Matrix>
void AABB2DTransformAsAABB2D(AABB2D &aabb, Matrix &m) {
float ax = m[0][0] * aabb.minPoint.x;
float bx = m[0][0] * aabb.maxPoint.x;
float ay = m[0][1] * aabb.minPoint.y;
float by = m[0][1] * aabb.maxPoint.y;
float ax2 = m[1][0] * aabb.minPoint.x;
float bx2 = m[1][0] * aabb.maxPoint.x;
float ay2 = m[1][1] * aabb.minPoint.y;
float by2 = m[1][1] * aabb.maxPoint.y;
aabb.minPoint.x = J3ML::Math::Min(ax, bx) + J3ML::Math::Min(ay, by) + m[0][3];
aabb.maxPoint.x = J3ML::Math::Max(ax, bx) + J3ML::Math::Max(ay, by) + m[0][3];
aabb.minPoint.y = J3ML::Math::Min(ax2, bx2) + J3ML::Math::Min(ay2, by2) + m[1][3];
aabb.maxPoint.y = J3ML::Math::Max(ax2, bx2) + J3ML::Math::Max(ay2, by2) + m[1][3];
}
}

View File

@@ -54,7 +54,7 @@ namespace J3ML::Geometry
/// Quickly returns an arbitrary point inside this Capsule. Used in GJK intersection test.
[[nodiscard]] inline Vector3 AnyPointFast() const;
[[nodiscard]] Vector3 AnyPointFast() const;
/// Generates a point that perhaps lies inside this capsule.
/** @param height A normalized value between [0,1]. This specifies the point position along the height line of this capsule.

View File

@@ -83,7 +83,7 @@ namespace J3ML::Geometry
/// Specifies whether this frustum is a perspective or an orthographic frustum.
FrustumType type;
/// Specifies whether the [-1, 1] or [0, 1] range is used for the post-projective depth range.
FrustumProjectiveSpace projectiveSpace;
FrustumProjectiveSpace projectiveSpace ;
/// Specifies the chirality of world and view spaces
FrustumHandedness handedness;
/// The eye point of this frustum

View File

@@ -0,0 +1,174 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
#include <J3ML/LinearAlgebra/Matrix4x4.hpp>
namespace J3ML::Geometry
{
/// A line segment in 2D space is a finite line with a start and end point.
class LineSegment2D {
public:
/// The starting point of this line segment.
Vector2 A;
/// The end point of this line segment.
Vector2 B;
/// The default constructor does not initialize any members of this class.
/** This means that the values of the members a and b are undefined after creating a new LineSegment2D using this
default constructor. Remember to assign to them before use.
@see A, B. */
LineSegment2D() {}
/// Constructs a line segment through the given end points.
LineSegment2D(const Vector2 &a, const Vector2 &b);
/// Returns a point on the line.
/** @param d The normalized distance along the line segment to compute. If a value in the range [0, 1] is passed, then the
returned point lies along this line segment. If some other value is specified, the returned point lies on the line
defined by this line segment, but *not* inside the interval from a to b.
@note The meaning of d here differs from Line2D::GetPoint and Ray2D::GetPoint. For the class LineSegment2D,
GetPoint(0) returns a, and getPoint(1) returns b. This means that GetPoint(1) will not generally be exactly one unit
away from the starting point of this line segment, as is the case with Line2D and Ray2D.
@return (1-d)*a + d*b;
@see a, b, Line2D::GetPoint(), Ray2D::GetPoint() */
Vector2 GetPoint(float d) const;
/// Returns the center point of this line segment.
/** This function is the same as calling GetPoint(0.5f), but provided here as convenience.
@see GetPoint(). */
Vector2 CenterPoint() const;
Vector2 Centroid() const;
/// Reverses the direction of this line segment.
/** This function swaps the start and end points of this line segment so that it runs from b to a.
This does not have an effect on the set of points represented by this line segment, but it reverses
the direction of the vector returned by Dir().
@note This function operates in-place.
@see a, b, Dir(). */
void Reverse();
/// Returns the normalized direction vector that points in the direction a->b.
/** @note The returned vector is normalized, meaning that its length is 1, not |b-a|.
@see a, b. */
Vector2 Dir() const;
/// Quickly returns an arbitrary point inside this LineSegment2D. Used in GJK intersection test.
Vector2 AnyPointFast() const;
/// Computes an extreme point of this LineSegment2D in the given direction.
/** An extreme point is a farthest point along this LineSegment2D in the given direction. Given a direction,
this point is not necessarily unique.
@param direction The direction vector of the direction to find the extreme point. This vector may
be un-normalized, but may not be null.
@return An extreme point of this LineSegment2D in the given direction. The returned point is always
either a or b.
@see a, b. **/
Vector2 ExtremePoint(const Vector2 &direction) const;
Vector2 ExtremePoint(const Vector2 &direction, float &projectionDistance) const;
/// Translates this LineSegment2D in world space.
/** @param offset The amount of displacement to apply to this LineSegment2D, in world space coordinates.
@see Transform(). */
void Translate(const Vector2& offset);
/// Applies a transformation to this line.
/** This function operates in-place.
@see Translate(), Transform(), classes Matrix3x3, Matrix4x4, Quaternion*/
void Transform(const Matrix3x3& transform);
void Transform(const Matrix4x4& transform);
void Transform(const Quaternion& transform);
/// Computes the length of this line segment.
/** @return |b - a|
@see a, b. */
float Length() const;
/// Computes the squared length of this line segment.
/** Calling this function is faster than calling Length(), since this function avoids computing a square root.
If you only need to compare lengths to each other and are not interested in the actual length values,
you can compare using LengthSq(), instead of Length(), since Sqrt() is an order-preserving,
(monotonous and non-decreasing) function. */
float LengthSq() const;
float LengthSquared() const;
/// Tests if this line segment is finite
/** A line segment is finite if its endpoints a and b do not contain floating-point NaNs for +/- infs
in them.
@return True if both a and b have finite floating-point values. */
bool IsFinite() const;
/// Tests if this line segment represents the same set of points than the the given line segment.
/** @param epsilon Specifies how much distance threshold to allow in the comparison.
@return True if a == rhs.a && b == rhs.b, or, a == rhs.b && b == rhs.a, within the given epsilon. */
bool Equals(const LineSegment2D& rhs, float epsilon = 1e-3f) const;
/// Tests if the given point or line segment is contained on this line segment.
/** @param epsilon Because a line segment is a one-dimensional object in 3D space, an epsilon value
is used as a threshold for this test. This effectively transforms this line segment to a capsule with
the radius indicated by this value.
@return True if this line segment contains the given point or line segment.
@see Intersects, ClosestPoint(), Distance(). */
bool Contains(const Vector2& point, float epsilon = 1e-3f) const;
bool Contains(const LineSegment2D& rhs, float epsilon = 1e-3f) const;
/// Computes the closest point on this line segment to the given object.
/** @param d If specified, this parameter receives the normalized distance along
this line segment which specifies the closest point on this line segment to
the specified point.
@return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector2 ClosestPoint(const Vector2& point) const;
Vector2 ClosestPoint(const Vector2& point, float& d) const;
/** @param d2 [out] If specified, this parameter receives the (normalized, in case of line segment)
distance along the other line object which specifies the closest point on that line to
this line segment. */
Vector2 ClosestPoint(const LineSegment2D& other) const;
Vector2 ClosestPoint(const LineSegment2D& other, float& d) const;
Vector2 ClosestPoint(const LineSegment2D& other, float& d, float& d2) const;
/// Computes the distance between this line segment and the given object.
/** @param d [out] If specified, this parameter receives the normalized distance along
this line segment which specifies the closest point on this line segment to
the specified point.
@return The distance between this line segment and the given object.
@see */
float Distance(const Vector2& point) const;
float Distance(const Vector2& point, float& d) const;
/** @param d2 [out] If specified, this parameter receives the (normalized, in case of line segment)
distance along the other line object which specifies the closest point on that line to
this line segment. */
float Distance(const LineSegment2D& other) const;
float Distance(const LineSegment2D& other, float& d) const;
float Distance(const LineSegment2D& other, float& d, float& d2) const;
float DistanceSq(const Vector2& point) const;
float DistanceSq(const LineSegment2D& other) const;
/** @param epsilon If testing intersection between two line segments, a distance threshold value is used to account
for floating point inaccuracies. */
bool Intersects(const LineSegment2D& lineSegment, float epsilon = 1e-3f) const;
/// Projects this LineSegment2D onto the given 1D axis direction vector.
/** This function collapses this LineSegment2D onto a 1D axis for the purposes of e.g. separate axis test computations.
This function returns a 1D range [outMin, outNax] denoting the interval of the projection.
@param direction The 1D axis to project to. This vector may be unnormalized, in which case the output
of this function gets scaled by the length of this vector.
@param outMin [out] Returns the minimum extent of this vector along the projection axis.
@param outMax [out] Returns the maximum extent of this vector along the projection axis. */
void ProjectToAxis(const Vector2& direction, float& outMin, float& outMax) const;
protected:
private:
};
LineSegment2D operator * (const Matrix3x3& transform, const LineSegment2D& line);
LineSegment2D operator * (const Matrix4x4& transform, const LineSegment2D& line);
LineSegment2D operator * (const Quaternion& transform, const LineSegment2D& line);
}

View File

@@ -77,4 +77,20 @@ namespace J3ML::Geometry
a.maxPoint = maxPoint - pt;
return a;
}
Vector2 AABB2D::Centroid() {
return (minPoint + (maxPoint / 2.f));
}
Vector2 AABB2D::CornerPoint(int cornerIndex) {
assert(0 <= cornerIndex && cornerIndex <= 3);
switch(cornerIndex)
{
default:
case 0: return minPoint;
case 1: return {minPoint.x, maxPoint.y};
case 2: return {maxPoint.x, minPoint.y};
case 3: return maxPoint;
}
}
}

View File

@@ -0,0 +1,165 @@
#include <J3ML/Geometry/LineSegment2D.hpp>
void Line2DClosestPointLineLine(const Vector2& v0, const Vector2& v10, const Vector2& v2, const Vector2& v32, float& d, float& d2) // TODO: Move to Line2D when that exists
{
// assert(!v10.IsZero());
// assert(!v32.IsZero());
Vector2 v02 = v0 - v2;
float d0232 = v02.Dot(v32);
float d3210 = v32.Dot(v10);
float d3232 = v32.Dot(v32);
// assert(d3232 != 0.f); // Don't call with a zero direction vector.
float d0210 = v02.Dot(v10);
float d1010 = v10.Dot(v10);
float denom = d1010*d3232 - d3210*d3210;
d = (denom !=0) ? (d0232*d3210 - d0210*d3232) / denom : 0;
d2 = (d0232 + d * d3210) / d3232;
}
Geometry::LineSegment2D::LineSegment2D(const Vector2 &a, const Vector2 &b)
: A(a), B(b) {}
Vector2 Geometry::LineSegment2D::GetPoint(float d) const {
return (1.f - d) * A + d * B;
}
Vector2 Geometry::LineSegment2D::CenterPoint() const {
return (A + B) * 0.5f;
}
Vector2 Geometry::LineSegment2D::Centroid() const {
return CenterPoint();
}
void Geometry::LineSegment2D::Reverse() {
Swap(A, B);
}
Vector2 Geometry::LineSegment2D::Dir() const {
return (B - A).Normalized();
}
Vector2 Geometry::LineSegment2D::AnyPointFast() const { return A; }
Vector2 Geometry::LineSegment2D::ExtremePoint(const Vector2 &direction) const {
return Vector2::Dot(direction, B - A) >= 0.f ? B : A;
}
Vector2 Geometry::LineSegment2D::ExtremePoint(const Vector2 &direction, float &projectionDistance) const {
Vector2 extremePoint = ExtremePoint(direction);
projectionDistance = extremePoint.Dot(direction);
return extremePoint;
}
void Geometry::LineSegment2D::Translate(const Vector2 &offset) {
A += offset;
B += offset;
}
void Geometry::LineSegment2D::Transform(const Matrix3x3 &transform) {
A = transform.Mul(A);
B = transform.Mul(B);
}
void Geometry::LineSegment2D::Transform(const Matrix4x4 &transform) {
A = transform.Mul(A);
B = transform.Mul(B);
}
void Geometry::LineSegment2D::Transform(const Quaternion &transform) {
//A = transform.Mul(A);
//B = transform.Mul(B);
}
float Geometry::LineSegment2D::Length() const { return A.Distance(B); }
float Geometry::LineSegment2D::LengthSq() const { return A.DistanceSq(B); }
float Geometry::LineSegment2D::LengthSquared() const { return LengthSq(); }
bool Geometry::LineSegment2D::IsFinite() const { return A.IsFinite() && B.IsFinite(); }
bool Geometry::LineSegment2D::Equals(const Geometry::LineSegment2D &rhs, float epsilon) const {
return (A.Equals(rhs.A, epsilon) && B.Equals(rhs.B, epsilon) ||
A.Equals(rhs.B, epsilon) && B.Equals(rhs.A, epsilon));
}
bool Geometry::LineSegment2D::Contains(const Vector2 &point, float epsilon) const {
return ClosestPoint(point).DistanceSq(point) <= epsilon;
}
bool Geometry::LineSegment2D::Contains(const Geometry::LineSegment2D &rhs, float epsilon) const {
return Contains(rhs.A, epsilon) && Contains(rhs.B, epsilon);
}
Vector2 Geometry::LineSegment2D::ClosestPoint(const Vector2 &point) const {
float d;
return ClosestPoint(point, d);
}
Vector2 Geometry::LineSegment2D::ClosestPoint(const Vector2 &point, float &d) const {
Vector2 dir = B - A;
d = J3ML::Math::Clamp01(Vector2::Dot(point - A, dir) / dir.LengthSquared());
return A + d * dir;
}
Vector2 Geometry::LineSegment2D::ClosestPoint(const Geometry::LineSegment2D &other) const {
float d, d2;
return ClosestPoint(other, d, d2);
}
Vector2 Geometry::LineSegment2D::ClosestPoint(const Geometry::LineSegment2D &other, float &d) const {
float d2; return ClosestPoint(other, d, d2);
}
Vector2 Geometry::LineSegment2D::ClosestPoint(const Geometry::LineSegment2D &other, float &d, float &d2) const {
Vector2 dir = B - A;
Line2DClosestPointLineLine(A, B - A, other.A, other.B - other.A, d, d2);
return Vector2::Zero;
}
float Geometry::LineSegment2D::Distance(const Vector2 &point) const { float d; return Distance(point, d); }
float Geometry::LineSegment2D::Distance(const Vector2 &point, float &d) const {
/// See Christer Ericson's Real-Time Collision Detection, p. 130.
Vector2 closestPoint = ClosestPoint(point, d);
return closestPoint.Distance(point);
}
float Geometry::LineSegment2D::Distance(const Geometry::LineSegment2D &other) const { float d, d2; return Distance(other, d, d2);}
float Geometry::LineSegment2D::Distance(const Geometry::LineSegment2D &other, float &d) const { float d2; return Distance(other, d, d2); }
float Geometry::LineSegment2D::Distance(const Geometry::LineSegment2D &other, float &d, float &d2) const {
ClosestPoint(other, d, d2);
return GetPoint(d).Distance(other.GetPoint(d2));
}
float Geometry::LineSegment2D::DistanceSq(const Geometry::LineSegment2D &other) const {
float d, d2;
ClosestPoint(other, d, d2);
return GetPoint(d).DistanceSq(other.GetPoint(d2));
}
float Geometry::LineSegment2D::DistanceSq(const Vector2 &point) const {
float d;
/// See Christer Ericson's Real-Time Collision Detection, p.130.
Vector2 closestPoint = ClosestPoint(point, d);
return closestPoint.DistanceSq(point);
}
bool Geometry::LineSegment2D::Intersects(const Geometry::LineSegment2D &lineSegment, float epsilon) const {
return Distance(lineSegment) <= epsilon;
}
void Geometry::LineSegment2D::ProjectToAxis(const Vector2 &direction, float &outMin, float &outMax) const {
outMin = Vector2::Dot(direction, A);
outMax = Vector2::Dot(direction, B);
if (outMax < outMin)
Swap(outMin, outMax);
}