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

This commit is contained in:
2024-10-06 22:25:01 -04:00
parent 3861741ff2
commit 143b7e7279
2 changed files with 236 additions and 14 deletions

View File

@@ -1,12 +1,13 @@
#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
{
class LineSegment2D {
public:
/// The starting point of this line segment.
Vector2 A;
@@ -20,43 +21,127 @@ namespace J3ML::Geometry
LineSegment2D() {}
/// Constructs a line segment through the given end points.
LineSegment2D(const Vector2& a, const Vector2& b);
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;
Vector2 CenterPoint() const;
Vector2 Centroid() const;
void Reverse();
Vector2 Dir() const;
inline Vector2 AnyPointFast() const { return A; }
Vector2 ExtremePoint(const Vector2& direction) const;
Vector2 ExtremePoint(const Vector2& direction, float& projectionDistance) 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;
bool Equals(const LineSegment2D& rhs, float epsilon = 1e-3f) const;
bool Contains(const Vector2& point, float epsilon = 1e-3f) const;
bool Contains(const LineSegment2D& lineSegment, float epsilon = 1e-3f) 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. */
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;
@@ -64,9 +149,25 @@ namespace J3ML::Geometry
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

@@ -1,2 +1,123 @@
#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;
}