Files
j3ml/include/J3ML/LinearAlgebra/Vector2.hpp
josh 9ecb64a2fe
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
Messing with matrices.
2025-06-11 08:12:31 -05:00

400 lines
21 KiB
C++

#pragma once
#include <cstddef>
#include <J3ML/Algorithm/RNG.hpp>
#include <J3ML/LinearAlgebra/Forward.hpp>
namespace J3ML::LinearAlgebra {
using namespace J3ML;
/// A 2D (x, y) ordered pair.
class Vector2 {
public:
/// The x component.
/// A Vector2 is 8 bytes in size. This element lies in the memory offsets 0-3 of this class
float x = 0;
/// The y component.
/// This element is packed to the memory offsets 4-7 of this class.
float y = 0;
public:
/// Specifies the number of elements in this vector.
enum {Dimensions = 2};
public:
/// @note Due to static data initialization order being undefined in C++,
/// do NOT use these members to initialize other static data in other compilation units!
/// Specifies a compile-time constant Vector2 with value (0,0).
static const Vector2 Zero;
/// Specifies a compile-time constant Vector2 with value (1,1)
static const Vector2 One;
/// Specifies a compile-time constant Vector2 with value (1,0).
static const Vector2 UnitX;
/// Specifies a compile-time constant Vector2 with value (0,1).
static const Vector2 UnitY;
/// Specifies a compile-time constant Vector2 with value (0,-1).
static const Vector2 Up;
/// Specifies a compile-time constant Vector2 with value (-1,0).
static const Vector2 Left;
/// Specifies a compile-time constant Vector2 with value (0,1).
static const Vector2 Down;
/// Specifies a compile-time constant Vector2 with value (1,0).
static const Vector2 Right;
/// Specifies a compile-time constant Vector2 with value (NaN, NaN).
/// For this constant, each element has the value of quiet NaN, or Not-A-Number.
/// @note Never compare a Vector2 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 Vector2 NaN;
/// Specifies a compile-time constant Vector2 with value (+infinity, +infinity).
static const Vector2 Infinity;
public:
/// Default Constructor does not initialize any members of this class.
Vector2();
/// Constructs a new Vector2 with the value (X, Y)
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}
explicit Vector2(float scalar);
Vector2(const Vector2& rhs); // Copy 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;
void SetX(float newX);
void SetY(float newY);
/// Sets all elements of this vector.
void Set(float newX, float newY);
/// Casts this float2 to a C array.
/** This function does not allocate new memory or make a copy of this float2. This function simply
returns a C pointer view to this data structure. Use ptr()[0] to access the x component of this float2
and ptr()[1] to access the y component.
@note Since the returned pointer points to this class, do not dereference the pointer after this
float2 has been deleted. You should never store a copy of the returned pointer.
@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 []
or the At() function to access the elements of this vector by index.
@return A pointer to the first float element of this class. The data is contiguous in memory.
@see operator [](), At(). */
float* ptr();
[[nodiscard]] const float *ptr() const;
/// Accesses an element of this vector using array notation.
/** @param index The element to get. Pass in 0 for x and 1 for y.
@note If you have a non-const instance of this class, you can use this notation to set the elements of
this vector as well, e.g. vec[1] = 10.f; would set the y-component of this vector.
@see ptr(), At(). */
float operator[](std::size_t index) const;
float &operator[](std::size_t index);
/// Accesses an element of this vector using array notation.
/** @param index The element to get. Pass in 0 for x and 1 for y.
@note If you have a non-const instance of this class, you can use this notation to set the elements of
this vector as well, e.g. vec[1] = 10.f; would set the y-component of this vector.
@see ptr(), operator [](). */
[[nodiscard]] const float At(std::size_t index) const;
float &At(std::size_t index);
[[nodiscard]] Vector2 Abs() const;
[[nodiscard]] bool IsWithinMarginOfError(const Vector2& rhs, float margin=0.001f) const;
[[nodiscard]] bool IsNormalized(float epsilonSq = 1e-5f) const;
[[nodiscard]] bool IsZero(float epsilonSq = 1e-6f) const;
[[nodiscard]] bool IsPerpendicular(const Vector2& other, float epsilonSq=1e-5f) const;
/// Tests if two vectors are equal, up to the given epsilon.
/** @see IsPerpendicular(). */
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;
static Vector2 Min(const Vector2& value, const Vector2& minimum);
/// Returns an element-wise maximum between two vectors.
[[nodiscard]] Vector2 Max(const Vector2& max) const;
static Vector2 Max(const Vector2& value, const Vector2& maximum);
/// Returns a Vector2 that has floor <= this[i] <= ceil for each element.
[[nodiscard]] Vector2 Clamp(float floor, float ceil) const;
static Vector2 Clamp(const Vector2& value, float floor, float ceil);
/// Limits each element of this vector between the corresponding elements in floor and ceil.
[[nodiscard]] Vector2 Clamp(const Vector2& min, const Vector2& max) const;
static Vector2 Clamp(const Vector2& min, const Vector2& middle, const Vector2& max);
/// Limits each element of this vector in the range [0, 1].
[[nodiscard]] Vector2 Clamp01() const;
static Vector2 Clamp01(const Vector2& value);
/// Returns the magnitude between the two vectors.
/// @see DistanceSq(), Length(), LengthSquared()
[[nodiscard]] float Distance(const Vector2& to) const;
static float Distance(const Vector2& from, const Vector2& to);
[[nodiscard]] float DistanceSq(const Vector2& to) const;
static float DistanceSq(const Vector2& from, const Vector2& to);
/// Returns the smaller element of the vector.
[[nodiscard]] float MinElement() const;
/// Returns the larger element of the vector.
[[nodiscard]] float MaxElement() const;
/// Computes the length of this vector
/// @return sqrt(x*x + y*y)
/// @see LengthSquared(), Distance(), DistanceSq()
[[nodiscard]] float Length() const;
static float Length(const Vector2& of);
/// Computes the squared length of this vector.
/** Calling this function is faster than using Length(), since this function avoids computing a square root.
If you only need to compare lengths to each other, but are not interested in the actual length values,
you can compare by using LengthSquared(), instead of Length(), since Sqrt() is an order-preserving
(monotonous and non-decreasing) function.
@return x*x + y*y
@see LengthSquared(), Distance(), DistanceSq() */
[[nodiscard]] float LengthSquared() const;
static float LengthSquared(const Vector2& of);
/// Returns the length of the vector, which is sqrt(x^2 + y^2)
[[nodiscard]] float Magnitude() const;
static float Magnitude(const Vector2& of);
[[nodiscard]] bool IsFinite() const;
static bool IsFinite(const Vector2& v);
/// @return x+y;
[[nodiscard]] float SumOfElements() const;
/// @return x*y;
[[nodiscard]] float ProductOfElements() const;
/// @return (x+y)/2
[[nodiscard]] float AverageOfElements() const;
/// Returns a copy of this vector with each element negated.
/** This function returns a new vector where each element x of the original vector is replaced by the value -x.
@return Vector2(-x, -y)
@see Abs(); */
[[nodiscard]] Vector2 Neg() const;
/// Computes the element-wise reciprocal of this vector.
/** This function returns a new vector where each element x of the original vector is replaced by the value 1/x.
@return Vector2(1/x, 1/y). */
[[nodiscard]] Vector2 Recip() const;
/// Returns the aimed angle direction of this vector, in radians.
/** The aimed angle of a 2D vector corresponds to the theta part (or azimuth) of the polar coordinate representation of this vector. Essentially,
describes the direction this vector is pointing at. A vector pointing towards +X returns 0, vector pointing towards +Y returns pi/2, vector
pointing towards -X returns pi, and a vector pointing towards -Y returns -pi/2 (equal to 3pi/2).
@note This vector does not need to be normalized for this function to work, but it DOES need to be non-zero (unlike the function ToPolarCoordinates).
@return The aimed angle in the range ]-pi/2, pi/2].
@see ToPolarCoordinates, FromPolarCoordinates, SetFromPolarCoordinates */
[[nodiscard]] float AimedAngle() const;
/// Converts this euclidian (x,y) Vector2 to polar coordinates representation in the form (theta, lenght).
/** @note It is valid for the magnitude of this vector to be (very close to) zero, in which case the return value is the zero vector.
@return A vector2 that has the first component (x) representing the aimed angle (azimuth) of this direction vector, in radians,
and is equal to atan2(y, x). The x component has a range of ]-pi/2, pi/2]. The second component (y) of the returned vector
stores the length (radius) of this vector.
@see SetFromPolarCoordinates, FromPolarCoordinates, AimedAngle */
[[nodiscard]] Vector2 ToPolarCoordinates() const;
/// Returns a float value equal to the magnitudes of the two vectors multiplied together and then multiplied by the cosine of the angle between them.
/// For normalized vectors, dot returns 1 if they point in exactly the same direction,
/// -1 if they point in completely opposite directions, and 0 if the vectors are perpendicular.
[[nodiscard]] float Dot(const Vector2& rhs) const;
static float Dot(const Vector2& lhs, const Vector2& rhs);
/// Projects one vector onto another and returns the result. (IDK)
[[nodiscard]] Vector2 Project(const Vector2& rhs) const;
/// @see Project
static Vector2 Project(const Vector2& lhs, const Vector2& rhs);
/// Normalizes this Vector2
/** In the case of failure, this vector is set to (1, 0), so calling this function will never result in an un-normalized vector.
@note If this function fials to normalize the vector, no error message is printed, the vector is set to (1, 0) and
an error code of 0 is returned. This is different than the behavior of the Normalized() function, which prints an
error if normalization fails.
@note This function operates in-place.
@return The old length of this vector, or 0 if normalization failed.
@see Normalized() */
float Normalize();
/// Returns a normalized copy of this vector.
/** @note If the vector is zero and cannot be normalized, the vector (1, 0) is returned, and an error message is printed.
If you do not want to generate an error message on failure, but want to handle the failure yourself, use the
Normalized() function instead.
@see Normalized() */
[[nodiscard]] Vector2 Normalized() const;
/// Linearly interpolates between two points.
/// Interpolates between the points and b by the interpolant t.
/// The parameter is (TODO: SHOULD BE!) clamped to the range[0, 1].
/// This is most commonly used to find a point some fraction of the wy along a line between two endpoints (eg. to move an object gradually between those points).
[[nodiscard]] Vector2 Lerp(const Vector2& rhs, float alpha) const;
/// @see Lerp
static Vector2 Lerp(const Vector2& lhs, const Vector2& rhs, float alpha);
/// Note: Input vectors MUST be normalized first!
[[nodiscard]] float AngleBetween(const Vector2& rhs) const;
static float AngleBetween(const Vector2& lhs, const Vector2& rhs);
/// Adds two vectors.
Vector2 operator +(const Vector2& rhs) const;
[[nodiscard]] Vector2 Add(const Vector2& rhs) const;
static Vector2 Add(const Vector2& lhs, const Vector2& rhs);
/// Subtracts two vectors.
Vector2 operator -(const Vector2& rhs) const;
[[nodiscard]] Vector2 Sub(const Vector2& rhs) const;
static Vector2 Sub(const Vector2& lhs, const Vector2& rhs);
/// Multiplies this vector by a scalar value.
Vector2 operator *(float rhs) const;
[[nodiscard]] Vector2 Mul(float scalar) const;
static Vector2 Mul(const Vector2& lhs, float rhs);
/// Multiplies this vector by a vector, element-wise
/// @note Mathematically, the multiplication of two vectors is not defined in linear space structures,
/// but this function is provided here for syntactical convenience.
Vector2 operator *(const Vector2& rhs) const;
[[nodiscard]] Vector2 Mul(const Vector2& v) const;
/// Divides this vector by a scalar.
Vector2 operator /(float rhs) const;
[[nodiscard]] Vector2 Div(float scalar) const;
static Vector2 Div(const Vector2& lhs, float rhs);
/// Divides this vector by a vector, element-wise
/// @note Mathematically, the multiplication of two vectors is not defined in linear space structures,
/// but this function is provided here for syntactical convenience
Vector2 operator / (const Vector2& rhs) const;
[[nodiscard]] Vector2 Div(const Vector2& v) const;
/// Unary operator +
Vector2 operator +() const { return *this;}
Vector2 operator -() const;
/// Assigns a vector to another.
Vector2 &operator =(const Vector2 &rhs);
/// Adds a vector to this vector, in-place.
Vector2& operator+=(const Vector2& rhs);
/// Subtracts a vector from this vector, in-place.
Vector2& operator-=(const Vector2& rhs);
/// Multiplies this vector by a scalar, in-place.
Vector2& operator*=(float scalar);
/// Divides this vector by a scalar, in-place
Vector2& operator/=(float scalar);
/// Returns this vector with the "perp" operator applied to it.
/** The perp operator rotates a vector 90 degrees ccw (around the "z axis"), i.e.
for a 2D vector (x,y), this function returns the vector (-y, x).
@note This function is identical to Rotate90CCW().
@return (-y, x). The returned vector is perpendicular to this vector.
@see PerpDot(), Rotated90CCW() */
[[nodiscard]] Vector2 Perp() const;
/// Computes the perp-dot product of this and the given Vector2 in the order this^perp (dot) rhs.
/// @see Dot(), Perp()
[[nodiscard]] float PerpDot(const Vector2& rhs) const;
/// Rotates this vector 90 degrees clock-wise
/// This rotation is interpreted in a coordinate system on a plane where +x extends to the right, and +y extends upwards.
/// @see Perp(), Rotated90CW(), Rotate90CCW(), Rotated90CCW();
void Rotate90CW();
/// Returns a vector that is perpendicular to this vector (rotated 90 degrees clock-wise).
/// @note This function is identical to Perp().
/// @see Perp(), Rotate90CW(), Rotate90CCW(), Rotated90CCW()
[[nodiscard]] Vector2 Rotated90CW() const;
/// Rotates this vector 90 degrees counterclock-wise.
/// This is a coordinate system on a plane +x extends to the right, and +y extends upwards.
/// @see Perp(), Rotate90CW(), Rotated90CW(), Rotated90CCW();
void Rotate90CCW();
/// Returns a vector that is perpendicular to this vector (rotated 90 degrees counter-clock-wise)
/// @see Perp(), Rotate90CW(), Rotated90CW(), Rotate90CCW()
[[nodiscard]] Vector2 Rotated90CCW() const;
/// Returns this vector reflected about a plane with the given normal.
/// By convention, both this and the reflected vector
[[nodiscard]] Vector2 Reflect(const Vector2& normal) const;
/// Refracts this vector about a plane with the given normal.
/** By convention, the this vector points towards the plane, and the returned vector points away from the plane.
When the ray is going from a denser material to a lighter one, total internal reflection can occur.
In this case, this function will just return a reflected vector from a call to Reflect().
@param normal Specifies the plane normal direction
@param negativeSideRefractionIndex The refraction index of the material we are exiting.
@param positiveSideRefractionIndex The refraction index of the material we are entering.
@see Reflect(). */
[[nodiscard]] Vector2 Refract(const Vector2& normal, float negativeSideRefractionIndex, float positiveSideRefractionIndex) const;
/// Projects this vector onto the given un-normalized direction vector.
/// @param direction The direction vector to project this vector onto.
/// This function will normalize the vector, so you can pass in an un-normalized vector.
/// @see ProjectToNorm
[[nodiscard]] Vector2 ProjectTo(const Vector2& direction) const;
/// Projects this vector onto the given normalized direction vector.
/// @param direction The vector to project onto.
[[nodiscard]] Vector2 ProjectToNorm(const Vector2& direction) const;
/// Tests if the triangle a->b->c is oriented counter-clockwise.
/** Returns true if the triangle a->b->c is oriented counter-clockwise, when viewed in the XY-plane
where x spans to the right and y spans up.
Another way to think of this is that this function returns true, if the point C lies to the left
of the directed line AB. */
static bool OrientedCCW(const Vector2&, const Vector2&, const Vector2&);
/// Makes the given vectors linearly independent.
/** This function directly follows the Gram-Schmidt procedure on the input vectors.
The vector a is kept unmodified, and vector B is modified to be perpendicular to a.
@note If any of the input vectors is zero, then the resulting set of vectors cannot be made orthogonal.
@see AreOrthogonal(), Orthonormalize(), AreOrthonormal() */
static void Orthogonalize(const Vector2& a, Vector2& b);
/// Returns true if the given vectors are orthogonal to each other.
/// @see Orthogonalize(), Orthonormalize(), AreOrthonormal()
static bool AreOrthogonal(const Vector2& a, const Vector2& b, float epsilon = 1e-3f);
/// Makes the given vectors linearly independent and normalized in length.
/** This function directly follows the Gram-Schmidt procedure on the input vectors.
The vector a is first normalized, and vector b is modified to be perpendicular to a, and also normalized.
@note If either of the input vectors is zero, then the resulting set of vectors cannot be made orthonormal.
@see Orthogonalize(), AreOrthogonal(), AreOrthonormal() */
static void Orthonormalize(Vector2& a, Vector2& b);
/// Returns a random Vector2 with each entry randomized between the range [minElem, maxElem].
static Vector2 RandomBox(Algorithm::RNG& rng, float minElem, float maxElem);
[[nodiscard]] std::string ToString() const;
};
Vector2 operator*(float lhs, const Vector2 &rhs);
std::ostream& operator << (std::ostream& out, const Vector2& rhs);
Vector2 Mul2D(const Matrix3x3& transform, const Vector2& v);
Vector2 MulPos2D(const Matrix4x4& transform, const Vector2& v);
Vector2 MulDir2D(const Matrix4x4& transform, const Vector2& v);
}