Finalized Vector2
Some checks are pending
Run tests / Explore-Gitea-Actions (push) Waiting to run
Build Docs With Doxygen / Explore-Gitea-Actions (push) Waiting to run

This commit is contained in:
2024-06-03 17:04:44 -04:00
parent 9f965c3d17
commit ab6b2b7972
2 changed files with 390 additions and 62 deletions

View File

@@ -8,12 +8,45 @@ namespace J3ML::LinearAlgebra {
/// 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:
/// Default Constructor - Initializes values to zero
/// @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);
@@ -24,19 +57,15 @@ namespace J3ML::LinearAlgebra {
Vector2(const Vector2& rhs); // Copy Constructor
//Vector2(Vector2&&) = default; // Move Constructor
static const Vector2 Zero;
static const Vector2 Up;
static const Vector2 Left;
static const Vector2 Down;
static const Vector2 Right;
static const Vector2 NaN;
static const Vector2 Infinity;
float GetX() const;
float GetY() const;
[[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
@@ -49,144 +78,297 @@ namespace J3ML::LinearAlgebra {
@return A pointer to the first float element of this class. The data is contiguous in memory.
@see operator [](), At(). */
float* ptr();
const float *ptr() const;
[[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);
const float At(std::size_t index) 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(), operator [](). */
[[nodiscard]] const float At(std::size_t index) const;
float &At(std::size_t index);
Vector2 Abs() const;
[[nodiscard]] Vector2 Abs() const;
bool IsWithinMarginOfError(const Vector2& rhs, float margin=0.001f) const;
[[nodiscard]] bool IsWithinMarginOfError(const Vector2& rhs, float margin=0.001f) const;
bool IsNormalized(float epsilonSq = 1e-5f) const;
bool IsZero(float epsilonSq = 1e-6f) const;
bool IsPerpendicular(const Vector2& other, float epsilonSq=1e-5f) 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;
bool operator == (const Vector2& rhs) const;
bool operator != (const Vector2& rhs) const;
Vector2 Min(const Vector2& min) 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);
Vector2 Max(const Vector2& max) const;
/// Returns an element-wise maximum between two vectors.
[[nodiscard]] Vector2 Max(const Vector2& max) const;
static Vector2 Max(const Vector2& value, const Vector2& maximum);
Vector2 Clamp(const Vector2& min, const Vector2& max) const;
/// 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.
float Distance(const Vector2& to) const;
/// @see DistanceSq(), Length(), LengthSquared()
[[nodiscard]] float Distance(const Vector2& to) const;
static float Distance(const Vector2& from, const Vector2& to);
float DistanceSq(const Vector2& to) const;
[[nodiscard]] float DistanceSq(const Vector2& to) const;
static float DistanceSq(const Vector2& from, const Vector2& to);
float MinElement() const;
/// Returns the smaller element of the vector.
[[nodiscard]] float MinElement() const;
float MaxElement() const;
/// Returns the larger element of the vector.
[[nodiscard]] float MaxElement() const;
float Length() 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);
float LengthSquared() const;
/// 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)
float Magnitude() const;
[[nodiscard]] float Magnitude() const;
static float Magnitude(const Vector2& of);
bool IsFinite() const;
[[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.
float Dot(const Vector2& rhs) const;
[[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)
Vector2 Project(const Vector2& rhs) const;
[[nodiscard]] Vector2 Project(const Vector2& rhs) const;
/// @see Project
static Vector2 Project(const Vector2& lhs, const Vector2& rhs);
/// Returns a copy of this vector, resized to have a magnitude of 1, while preserving "direction"
Vector2 Normalize() const;
static Vector2 Normalize(const Vector2& of);
/// 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
Normalize() function instead.
@see Normalize() */
[[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).
Vector2 Lerp(const Vector2& rhs, float alpha) const;
[[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!
float AngleBetween(const Vector2& rhs) const;
[[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;
Vector2 Add(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;
Vector2 Sub(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;
Vector2 Mul(float scalar) 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
{
}
Vector2 Mul(const Vector2& v) const;
Vector2 operator *(const Vector2& rhs) const;
[[nodiscard]] Vector2 Mul(const Vector2& v) const;
/// Divides this vector by a scalar.
Vector2 operator /(float rhs) const;
Vector2 Div(float scalar) 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 Div(const Vector2& v) const;
Vector2 operator / (const Vector2& rhs) const;
[[nodiscard]] Vector2 Div(const Vector2& v) const;
/// Unary operator +
Vector2 operator +() const; // TODO: Implement
Vector2 operator +() const { return *this;}
Vector2 operator -() const;
/// Assigns a vector to another
/// 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 &);
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);
};
static Vector2 operator*(float lhs, const Vector2 &rhs)
{
return rhs * lhs;
}
Vector2 operator*(float lhs, const Vector2 &rhs);
}

View File

@@ -6,7 +6,7 @@
namespace J3ML::LinearAlgebra {
Vector2::Vector2(): x(0), y(0)
Vector2::Vector2()
{}
Vector2::Vector2(float X, float Y): x(X), y(Y)
@@ -95,15 +95,12 @@ namespace J3ML::LinearAlgebra {
return rhs * scalar;
}
Vector2 Vector2::Normalize() const
Vector2 Vector2::Normalized() const
{
if (Length() > 0)
return {
x / Length(),
y / Length()
};
else
return {0,0};
Vector2 copy = *this;
float oldLength = copy.Normalize();
assert(oldLength > 0.f && "Vector2::Normalized() failed!");
return copy;
}
Vector2 Vector2::Lerp(const Vector2& rhs, float alpha) const
@@ -153,6 +150,9 @@ namespace J3ML::LinearAlgebra {
}
const Vector2 Vector2::Zero = {0, 0};
const Vector2 Vector2::One = {1, 1};
const Vector2 Vector2::UnitX = {1, 0};
const Vector2 Vector2::UnitY = {0, 1};
const Vector2 Vector2::Up = {0, -1};
const Vector2 Vector2::Down = {0, 1};
const Vector2 Vector2::Left = {-1, 0};
@@ -189,7 +189,7 @@ namespace J3ML::LinearAlgebra {
return dot*dot <= epsilonSq * LengthSquared() * other.LengthSquared();
}
Vector2 Vector2::Normalize(const Vector2 &of) { return of.Normalize(); }
Vector2 Vector2::Project(const Vector2 &lhs, const Vector2 &rhs) { return lhs.Project(rhs); }
@@ -341,4 +341,150 @@ namespace J3ML::LinearAlgebra {
return (a.x-c.x)*(b.y-c.y) - (a.y-c.y)*(b.x-c.x) >= 0.f;
}
Vector2 Vector2::operator*(const Vector2 &rhs) const {
return this->Mul(rhs);
}
Vector2 Vector2::operator/(const Vector2 &rhs) const { return this->Div(rhs);}
Vector2 Vector2::Clamp(float floor, float ceil) const {
return {std::clamp(this->x, floor, ceil),
std::clamp(this->y, floor, ceil)};
}
Vector2 Vector2::Clamp(const Vector2 &value, float floor, float ceil) {
return value.Clamp(floor, ceil);
}
Vector2 Vector2::Clamp01() const {
return Clamp(0, 1);
}
Vector2 Vector2::Clamp01(const Vector2 &value) {
return value.Clamp01();
}
Vector2 Vector2::Clamp(const Vector2 &min, const Vector2 &middle, const Vector2 &max) {
return middle.Clamp(min, max);
}
float Vector2::Distance(const Vector2 &from, const Vector2 &to) {
return from.Distance(to);
}
float Vector2::SumOfElements() const { return x+y;}
float Vector2::ProductOfElements() const { return x*y;}
float Vector2::AverageOfElements() const { return (x+y)/2; }
Vector2 Vector2::Neg() const { return {-x, -y};}
Vector2 Vector2::Recip() const { return {1.f/x, 1.f/y}; }
float Vector2::AimedAngle() const {
assert(!IsZero());
return std::atan2(y, x);
}
Vector2 Vector2::ToPolarCoordinates() const {
float radius = Length();
if (radius > 1e-4f)
return {std::atan2(y, x), radius};
else
return Vector2::Zero;
}
Vector2 Vector2::Perp() const {
return {-y, x};
}
float Vector2::PerpDot(const Vector2 &rhs) const {
return x * rhs.y - y * rhs.x;
}
void Vector2::Rotate90CW() {
float oldX = x;
x = y;
y = -oldX;
}
Vector2 Vector2::Rotated90CW() const {
return {y, -x};
}
void Vector2::Rotate90CCW() {
float oldX = x;
x = -y;
y = oldX;
}
Vector2 Vector2::Rotated90CCW() const {
return {-y, x};
}
Vector2 Vector2::Reflect(const Vector2 &normal) const {
assert(normal.IsNormalized());
return 2.f * this->ProjectToNorm(normal) - *this;
}
Vector2
Vector2::Refract(const Vector2 &normal, float negativeSideRefractionIndex, float positiveSideRefractionIndex) const {
// Implementation from https://www.flipcode.com/archives/reflection_transmission.pdf.
// This code is duplicated in Vector3::Refract
float n = negativeSideRefractionIndex / positiveSideRefractionIndex;
float cosI = this->Dot(normal);
float sinT2 = n*n*(1.f - cosI*cosI);
if (sinT2 > 1.f) // Total internal reflection occurs?
return (-*this).Reflect(normal);
return n * *this - (n + std::sqrt(1.f - sinT2)) * normal;
}
Vector2 Vector2::ProjectTo(const Vector2 &direction) const {
assert(!direction.IsZero());
return direction * this->Dot(direction) / direction.LengthSquared();
}
Vector2 Vector2::ProjectToNorm(const Vector2 &direction) const {
assert(direction.IsNormalized());
return direction * this->Dot(direction);
}
void Vector2::Orthogonalize(const Vector2 &a, Vector2 &b) {
assert(!a.IsZero());
b -= a.Dot(b) / a.Length() * a;
}
void Vector2::Orthonormalize(Vector2 &a, Vector2 &b) {
assert(!a.IsZero());
a.Normalize();
b -= a.Dot(b) * a;
}
bool Vector2::AreOrthogonal(const Vector2 &a, const Vector2 &b, float epsilon) {
return a.IsPerpendicular(b, epsilon);
}
void Vector2::Set(float newX, float newY) {
x = newX;
y = newY;
}
float Vector2::Normalize() {
assert(IsFinite());
float lengthSq = LengthSquared();
if (lengthSq > 1e-6f)
{
float length = std::sqrt(lengthSq);
*this *= 1.f / length;
return length;
} else {
Set(1.f, 0.f); // We will always produce a normalized vector.
return 0; // But signal failure, so user knows we have generated an arbitrary normalization.
}
}
Vector2 operator*(float lhs, const Vector2 &rhs) {
return {lhs * rhs.x, lhs * rhs.y};
}
}