400 lines
21 KiB
C++
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);
|
|
|
|
}
|