Files
j3ml/include/J3ML/LinearAlgebra/Vector4.hpp

279 lines
15 KiB
C++

#pragma once
#include <cstddef>
#include <cmath>
#include <J3ML/LinearAlgebra/Forward.hpp>
namespace J3ML::LinearAlgebra {
/// A 3D vector of form (x,y,z,w) in a 4D homogeneous coordinate space.
/** This class has two sets of member functions. The functions ending in a suffix '3' operate only on the
(x, y, z) part, ignoring the w component (or assuming a value of 0 or 1, where expectable). The functions
without the '3' suffix operate on all four elements of the vector. */
class Vector4 {
public:
enum { Size = 4 };
public:
static const Vector4 Zero;
static const Vector4 NaN;
public:
/// The default constructor does not initialize any members of this class.
/** This means that the values of the members x, y, z, and w are all undefined after creating a new Vector4 using
this default constructor. Remember to assign to them before use.
@see x, y, z, w. */
Vector4();
/// Constructs a new Vector4 with x,y,z values from a Vector3
explicit Vector4(const Vector3& xyz, float w = 0);
/// Constructs a new Vector4 with the value (X, Y, Z, W)
/** @note If you are constructing a float4 from an array of consecutive values, always prefer calling "float4(ptr);" instead of "float4(ptr[0], ptr[1], ptr[2], ptr[3]);"
because there is a considerable SIMD performance benefit in the first form.
@see x, y, z, w. */
Vector4(float X, float Y, float Z, float W);
/// The Vector4 copy constructor.
Vector4(const Vector4& copy) { Set(copy); }
Vector4(Vector4&& move) = default;
Vector4& operator=(const Vector4& rhs);
/// Constructs this Vector4 from a C array, to the value (data[0], data[1], data[2], data[3]).
/** @param data An array containing four elements for x, y, z, and w. This pointer may not be null. */
explicit Vector4(const float* data);
/// Casts this Vector4 to a C array.
/** This function does not allocate new memory or make a copy of this Vector4. This function simply
returns a C pointer view to this data structure. Use ptr()[0] to access the x component of this Vector4,
ptr()[1] to access y, ptr()[2] to access z, and ptr()[3] to access the w component of this Vector4.
@note Since the returned pointer points to this class, do not dereference the pointer after this
Vector 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 [] of this
class to access the elements of this vector by index. */
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, 1 for y, 2 for z and 3 for w.
@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. */
float operator[](std::size_t index) const;
float &operator[](std::size_t index);
/// Accesses an element of this vector.
/** @param index The element to get. Pass in 0 for x, 1 for y, 2 for z and 3 for w.
@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.At(1) = 10.f; would set the y-component of this vector. */
float At(int index) const;
float& At(int index);
/// Returns the (x,y) part of this vector.
[[nodiscard]] Vector2 XY() const;
/// Returns the (x,y,z) part of this vector.
[[nodiscard]] Vector3 XYZ() const;
[[nodiscard]] float GetX() const { return x; }
[[nodiscard]] float GetY() const { return y; }
[[nodiscard]] float GetZ() const { return z; }
[[nodiscard]] float GetW() const { return w; }
void SetX(float newX) { x = newX;}
void SetY(float newY) { y = newY;}
void SetZ(float newZ) { z = newZ;}
void SetW(float newW) { w = newW;}
/// Sets all elements of this vector.
/** @see x, y, z, w, At(). */
void Set(float x, float y, float z, float w);
void Set(const Vector4& rhs);
[[nodiscard]] bool IsWithinMarginOfError(const Vector4& rhs, float margin=0.0001f) const;
/// Computes the squared length of the (x,y,z) part of this vector.
[[nodiscard]] float LengthSqXYZ() const;
/// Tests if the length of the (x, y, z) part of this vector is one, up to the given epsilon.
/** @see NormalizeW(), IsWZeroOrOne(), IsZero3(), IsZero4(), IsNormalized4(). */
[[nodiscard]] bool IsNormalized3(float epsilonSq = 1e-5f) const;
/// Returns true if the length of this vector is 1, up to the given epsilon.
/** This function takes into account all the four components of this vector when calculating the norm.
@see NormalizeW(), IsWZeroOrOne(), IsZero3(), IsZero4(), IsNormalized3(). */
[[nodiscard]] bool IsNormalized4(float epsilonSq = 1e-5f) const;
[[nodiscard]] bool IsNormalized(float epsilonSq = 1e-5f) const;
/// Tests if the (x, y, z) part of this vector is equal to (0,0,0), up to the given epsilon.
/** @see NormalizeW(), IsWZeroOrOne(), IsZero4(), IsNormalized3(), IsNormalized4(). */
[[nodiscard]] bool IsZero3(float epsilonSq = 1e-6f) const;
[[nodiscard]] bool IsZero(float epsilonSq = 1e-6f) const;
[[nodiscard]] bool IsZero4(float epsilonSq = 1e-6f) const;
/// Tests if this vector contains valid finite elements.
[[nodiscard]] bool IsFinite() const;
/// Tests if the (x, y, z) parts of two vectors are perpendicular to each other.
[[nodiscard]] bool IsPerpendicular(const Vector4& other, float epsilonSq=1e-5f) const;
[[nodiscard]] bool IsPerpendicular3(const Vector4& other, float epsilonSq = 1e-5f) const;
/// Divides each element by w to produce a Vector4 of form (x, y, z, 1).
/** This function performs the <b>perspective divide</b> or the <b>homogeneous divide</b> on this vector, which is the
process of dividing each element of this vector by w. If the w component of this vector is zero before division, the
result of this vector will be undefined.
@note This function operates in-place.
@see IsWZeroOrOne(). */
void NormalizeW();
bool operator==(const Vector4& rhs) const;
bool operator!=(const Vector4& rhs) const;
[[nodiscard]] bool Equals(const Vector4& rhs, float epsilon = 1e-3f) const;
[[nodiscard]] bool Equals(float _x, float _y, float _z, float _w, float epsilon = 1e-3f) const;
/// Returns an element-wise minimum of this and the vector (ceil, ceil, ceil, ceil).
/** Each element that is larger than ceil is replaced by ceil. */
[[nodiscard]] Vector4 Min(float ceil) const;
/// Returns an element-wise minimum of this and the given vector.
/** Each element that is larger than ceil is replaced by ceil.
@see Max(), Clamp(). */
[[nodiscard]] Vector4 Min(const Vector4& ceil) const;
/// Returns an element-wise maximum of this and the vector (floor, floor, floor, floor).
/** Each element that is smaller than floor is replaced by floor. */
[[nodiscard]] Vector4 Max(float floor) const;
/// Returns an element-wise maximum of this and the given vector.
/** Each element that is smaller than floor is replaced by floor.
@see Min(), Clamp(). */
[[nodiscard]] Vector4 Max(const Vector4& floor) const;
/// Returns a vector that has floor <= this[i] <= ceil for each element.
[[nodiscard]] Vector4 Clamp(float floor, float ceil) const;
/// Limits each element of this vector between the corresponding elements in floor and ceil.
/** @see Min(), Max(), Clamp01(). */
[[nodiscard]] Vector4 Clamp(const Vector4& floor, const Vector4& ceil) const;
/// Limits each element of this vector in the range [0, 1].
/** @see Min(), Max(), Clamp(). */
[[nodiscard]] Vector4 Clamp01() const;
[[nodiscard]] float Distance(const Vector4& to) const;
/// Computes the length of this vector.
/** @return Sqrt(x*x + y*y + z*z + w*w).
@see LengthSq3(), Length3(), LengthSq4(), Normalize3(), Normalize4(). */
[[nodiscard]] float Length4() const;
[[nodiscard]] float Length() const;
/// Computes the squared length of this vector.
/** Calling this function is faster than calling Length4(), 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 LengthSq4(), instead of Length4(), since Sqrt() is an order-preserving
(monotonous and non-decreasing) function.
@return x*x + y*y + z*z + w*w.
@see Length3(), LengthSq3(), Length4(), Normalize3(), Normalize4(). */
[[nodiscard]] float LengthSquared4() const;
[[nodiscard]] float LengthSquared() const;
[[nodiscard]] float LengthSq4() const;
[[nodiscard]] float LengthSq() const;
/// Computes the length of the (x, y, z) part of this vector.
/** @note This function ignores the w component of this vector.
@return Sqrt(x*x + y*y + z*z).
@see LengthSq3(), LengthSq4(), Length4(), Normalize3(), Normalize4(). */
[[nodiscard]] float Length3() const { return std::sqrt(x*x + y*y + z*z); }
/// Computes the squared length of the (x, y, z) part of this vector.
/** Calling this function is faster than calling Length3(), 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 LengthSq3(), instead of Length3(), since Sqrt() is an order-preserving
(monotonous and non-decreasing) function.
@note This function ignores the w component of this vector.
@return x*x + y*y + z*z.
@see Length3(), LengthSq4(), Length4(), Normalize3(), Normalize4(). */
[[nodiscard]] float LengthSq3() const;
[[nodiscard]] float LengthSquared3() const;
[[nodiscard]] float Magnitude() const;
[[nodiscard]] float Dot(const Vector4& rhs) const;
[[nodiscard]] float Dot4(const Vector4& rhs) const { return this->Dot(rhs); }
/// Computes the dot product of the (x, y, z) parts of this and the given float4.
/** @note This function ignores the w component of this vector (assumes w=0).
@see Dot4(), Cross3(). */
[[nodiscard]] float Dot3(const Vector3& rhs) const;
[[nodiscard]] float Dot3(const Vector4& rhs) const;
[[nodiscard]] Vector4 Project(const Vector4& rhs) const;
/// While it is feasable to compute a cross-product in four dimensions the cross product only has the orthogonality property in 3 and 7 dimensions.
/// You should consider instead looking at Gram-Schmidt Orthogonalization to find orthonormal vectors.
[[nodiscard]] Vector4 Cross3(const Vector3& rhs) const;
[[nodiscard]] Vector4 Cross3(const Vector4& rhs) const;
[[nodiscard]] Vector4 Cross(const Vector4& rhs) const;
[[nodiscard]] Vector4 Normalized() const;
[[nodiscard]] Vector4 Lerp(const Vector4& goal, float alpha) const;
/// Returns the angle between this vector and the specified vector, in radians.
/// @note This function takes into account that this vector or the other vector can be un-normalized, and normalizes the computations.
/// @see Dot3(), AngleBetween3(), AngleBetweenNorm3(), AngleBetweenNorm().
[[nodiscard]] float AngleBetween(const Vector4& rhs) const;
[[nodiscard]] float AngleBetween4(const Vector4& rhs) const;
/// Adds two vectors. [indexTitle: operators +,-,*,/]
/** This function is identical to the member function Add().
@return float4(x + v.x, y + v.y, z + v.z, w + v.w); */
Vector4 operator +(const Vector4& rhs) const;
/// Adds a vector to this vector. [IndexTitle: Add/Sub/Mul/Div]
/// @return (x+v.x, y+v.y, z+v.z, w+v.w).
[[nodiscard]] Vector4 Add(const Vector4& rhs) const;
static Vector4 Add(const Vector4& lhs, const Vector4& rhs);
/// Subtracts the given vector from this vector. [similarOverload: operator+] [hideIndex]
/** This function is identical to the member function Sub().
@return float4(x - v.x, y - v.y, z - v.z, w - v.w); */
Vector4 operator -(const Vector4& rhs) const;
[[nodiscard]] Vector4 Sub(const Vector4& rhs) const;
static Vector4 Sub(const Vector4& lhs, const Vector4& rhs);
/// Multiplies this vector by a scalar. [similarOverload: operator+] [hideIndex]
/** This function is identical to the member function Mul().
@return float4(x * scalar, y * scalar, z * scalar, w * scalar); */
Vector4 operator *(float rhs) const;
[[nodiscard]] Vector4 Mul(float scalar) const { return *this * scalar;}
static Vector4 Mul(const Vector4& lhs, float rhs) {return lhs * rhs; }
/// Divides this vector by a scalar. [similarOverload: operator+] [hideIndex]
/** This function is identical to the member function Div().
@return float4(x / scalar, y / scalar, z / scalar, w * scalar); */
Vector4 operator /(float rhs) const;
[[nodiscard]] Vector4 Div(float scalar) const { return *this / scalar; }
static Vector4 Div(const Vector4& rhs, float scalar) { return rhs / scalar; }
/// Divides this vector by a vector, element-wise.
/// @note Mathematically, the division of two vectors is not defined in linear space structures,
/// but this function is provided here for syntactical convenience.
Vector4 Div(const Vector4& rhs) const;
static Vector4 Div(const Vector4& lhs, const Vector4& rhs);
Vector4 operator +() const { return *this;} // Unary + Operator
/// Performs an unary negation of this vector. [similarOverload: operator+] [hideIndex]
/** This function is identical to the member function Neg().
@return float4(-x, -y, -z, -w). */
Vector4 operator -() const;
public:
float x = 0;
float y = 0;
float z = 0;
float w = 0;
void Normalize();
};
}