diff --git a/source/runtime/core/public/maths/base/Scalar.hpp b/source/runtime/core/public/maths/base/Scalar.hpp index 615ecb0d..9505c357 100644 --- a/source/runtime/core/public/maths/base/Scalar.hpp +++ b/source/runtime/core/public/maths/base/Scalar.hpp @@ -7,6 +7,7 @@ #include "concepts/Concepts.hpp" #include "CoreMinimal.hpp" // IWYU pragma: keep #include "maths/base/Constants.hpp" +#include namespace gp::math { @@ -230,4 +231,24 @@ GP_NODISCARD constexpr T nextPowerOfTwo(T value) noexcept return value + 1; } +/// @brief Returns the square root of a value. +/// @tparam T Floating-point type. +/// @param[in] value The value to compute the square root of. +/// @return The square root of @p value. +template +GP_NODISCARD constexpr T sqrt(const T value) noexcept +{ + return std::sqrt(value); +} + +/// @brief Returns the reciprocal of the square root of a value. +/// @tparam T Floating-point type. +/// @param[in] value The value to compute the inverse square root of. +/// @return The reciprocal of the square root of @p value. +template +GP_NODISCARD constexpr T inverseSqrt(const T value) noexcept +{ + return static_cast(1) / sqrt(value); +} + } // namespace gp::math diff --git a/source/runtime/core/public/maths/base/Tresholds.hpp b/source/runtime/core/public/maths/base/Tresholds.hpp new file mode 100644 index 00000000..38468770 --- /dev/null +++ b/source/runtime/core/public/maths/base/Tresholds.hpp @@ -0,0 +1,34 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "concepts/Concepts.hpp" +#include "CoreMinimal.hpp" // IWYU pragma: keep + +namespace gp::math +{ + +/// @brief A struct containing threshold values for various mathematical operations. +/// @tparam T The floating-point type for the threshold values. +template +struct tresholds final +{ + static constexpr T pointOnPlane = static_cast(0.10); + static constexpr T pointOnSide = static_cast(0.20); + static constexpr T pointsAreSame = static_cast(0.00002); + static constexpr T pointsAreNear = static_cast(0.015); + static constexpr T normalsAreSame = static_cast(0.00002); + static constexpr T uvsAreSame = static_cast(0.0009765625); + static constexpr T vectorsAreNear = static_cast(0.0004); + static constexpr T splitPolyWithPlane = static_cast(0.25); + static constexpr T splitPolyPrecisely = static_cast(0.01); + static constexpr T zeroNormSquared = static_cast(0.0001); + static constexpr T normalsAreParallel = static_cast(0.999845); + static constexpr T normalsAreOrthogonal = static_cast(0.017455); + static constexpr T vectorNormalized = static_cast(0.01); + static constexpr T quatNormalized = static_cast(0.01); +}; + +} // namespace gp::math diff --git a/source/runtime/core/public/maths/vector/Vector3.hpp b/source/runtime/core/public/maths/vector/Vector3.hpp index a73c5ce2..b7c41b01 100644 --- a/source/runtime/core/public/maths/vector/Vector3.hpp +++ b/source/runtime/core/public/maths/vector/Vector3.hpp @@ -6,6 +6,9 @@ #include "concepts/Concepts.hpp" #include "CoreMinimal.hpp" +#include "maths/base/Constants.hpp" +#include "maths/base/Scalar.hpp" +#include "maths/base/Tresholds.hpp" #include "maths/MathForward.hpp" namespace gp::math @@ -17,9 +20,87 @@ template struct Vector3 { public: - T x; - T y; - T z; + T x; // zero() + { + return { T{ 0 }, T{ 0 }, T{ 0 } }; + } + + /// @brief Returns a vector with all components set to one. + /// @return A vector with all components set to one. + GP_NODISCARD static inline constexpr Vector3 one() + { + return { T{ 1 }, T{ 1 }, T{ 1 } }; + } + + /// @brief Returns a vector pointing upwards. + /// @return A vector pointing upwards. + GP_NODISCARD static inline constexpr Vector3 up() + { + return { T{ 0 }, T{ 1 }, T{ 0 } }; + } + + /// @brief Returns a vector pointing downwards. + /// @return A vector pointing downwards. + GP_NODISCARD static inline constexpr Vector3 down() + { + return { T{ 0 }, T{ -1 }, T{ 0 } }; + } + + /// @brief Returns a vector pointing to the left. + /// @return A vector pointing to the left. + GP_NODISCARD static inline constexpr Vector3 left() + { + return { T{ -1 }, T{ 0 }, T{ 0 } }; + } + + /// @brief Returns a vector pointing to the right. + /// @return A vector pointing to the right. + GP_NODISCARD static inline constexpr Vector3 right() + { + return { T{ 1 }, T{ 0 }, T{ 0 } }; + } + + /// @brief Returns a vector pointing forward. + /// @return A vector pointing forward. + GP_NODISCARD static inline constexpr Vector3 forward() + { + return { T{ 0 }, T{ 0 }, T{ 1 } }; + } + + /// @brief Returns a vector pointing backward. + /// @return A vector pointing backward. + GP_NODISCARD static inline constexpr Vector3 backward() + { + return { T{ 0 }, T{ 0 }, T{ -1 } }; + } + + /// @brief Returns a unit vector along the x-axis. + /// @return A unit vector along the x-axis. + GP_NODISCARD static inline constexpr Vector3 unitX() + { + return { T{ 1 }, T{ 0 }, T{ 0 } }; + } + + /// @brief Returns a unit vector along the y-axis. + /// @return A unit vector along the y-axis. + GP_NODISCARD static inline constexpr Vector3 unitY() + { + return { T{ 0 }, T{ 1 }, T{ 0 } }; + } + + /// @brief Returns a unit vector along the z-axis. + /// @return A unit vector along the z-axis. + GP_NODISCARD static inline constexpr Vector3 unitZ() + { + return { T{ 0 }, T{ 0 }, T{ 1 } }; + } public: /// @brief Default constructor initializes to (0, 0, 0). @@ -78,9 +159,683 @@ struct Vector3 /// @brief Constructor that initializes the vector from a Vector4 by discarding the w component. /// @param[in] vec The input Vector4 to initialize the x, y, and z components of the Vector3. GP_NODISCARD explicit constexpr Vector3(const Vector4& vec) noexcept; + +public: + /// @brief Component-wise cross product of this vector with another vector. + /// @param[in] other The other vector to compute the cross product with. + /// @return The cross product of this vector and the other vector. + GP_NODISCARD constexpr Vector3 operator^(const Vector3& other) const noexcept + { + return Vector3(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x); + } + + /// @brief Component-wise dot product of this vector with another vector. + /// @param[in] other The other vector to compute the dot product with. + /// @return The dot product of this vector and the other vector. + GP_NODISCARD constexpr T operator|(const Vector3& other) const noexcept + { + return x * other.x + y * other.y + z * other.z; + } + + /// @brief Negation operator, returns a vector with all components negated. + /// @return A vector with all components negated. + GP_NODISCARD constexpr Vector3 operator-() const noexcept + { + return Vector3(-x, -y, -z); + } + + /// @brief Unary plus operator, returns the vector itself. + /// @return The vector itself. + GP_NODISCARD constexpr Vector3 operator+() const noexcept + { + return *this; + } + + /// @brief Component-wise addition of this vector with another vector. + /// @param[in] other The other vector to add to this vector. + /// @return The component-wise sum of this vector and the other vector. + GP_NODISCARD constexpr Vector3 operator+(const Vector3& other) const noexcept + { + return Vector3(x + other.x, y + other.y, z + other.z); + } + + /// @brief Component-wise addition of a scalar bias to this vector. + /// @param[in] bias The scalar bias to add to each component of the vector. + /// @return The component-wise sum of this vector and the scalar bias. + template + GP_NODISCARD constexpr Vector3 operator+(const U bias) const noexcept + { + return Vector3(x + static_cast(bias), y + static_cast(bias), z + static_cast(bias)); + } + + /// @brief Component-wise subtraction of another vector from this vector. + /// @param[in] other The other vector to subtract from this vector. + /// @return The component-wise difference of this vector and the other vector. + GP_NODISCARD constexpr Vector3 operator-(const Vector3& other) const noexcept + { + return Vector3(x - other.x, y - other.y, z - other.z); + } + + /// @brief Component-wise subtraction of a scalar bias from this vector. + /// @param[in] bias The scalar bias to subtract from each component of the vector. + /// @return The component-wise difference of this vector and the scalar bias. + template + GP_NODISCARD constexpr Vector3 operator-(const U bias) const noexcept + { + return Vector3(x - static_cast(bias), y - static_cast(bias), z - static_cast(bias)); + } + + /// @brief Component-wise multiplication of this vector with another vector. + /// @param[in] other The other vector to multiply with this vector. + /// @return The component-wise product of this vector and the other vector. + GP_NODISCARD constexpr Vector3 operator*(const Vector3& other) const noexcept + { + return Vector3(x * other.x, y * other.y, z * other.z); + } + + /// @brief Component-wise multiplication of this vector by a scalar scale factor. + /// @param[in] scale The scalar scale factor to multiply each component of the vector by. + /// @return The component-wise product of this vector and the scalar scale factor. + template + GP_NODISCARD constexpr Vector3 operator*(const U scale) const noexcept + { + return Vector3(x * static_cast(scale), y * static_cast(scale), z * static_cast(scale)); + } + + /// @brief Component-wise division of this vector by another vector. + /// @param[in] other The other vector to divide this vector by. + /// @return The component-wise quotient of this vector and the other vector. + GP_NODISCARD constexpr Vector3 operator/(const Vector3& other) const noexcept + { + return Vector3(x / other.x, y / other.y, z / other.z); + } + + /// @brief Component-wise division of this vector by a scalar scale factor. + /// @param[in] scale The scalar scale factor to divide each component of the vector by. + /// @return The component-wise quotient of this vector and the scalar scale factor. + /// @details + /// This operator computes the inverse of the scalar scale factor and multiplies it with each component + /// of the vector for improved performance, especially when the scalar is a constant or can be optimized by the + /// compiler. + template + GP_NODISCARD constexpr Vector3 operator/(const U scale) const noexcept + { + const T invScale = static_cast(1) / static_cast(scale); + return Vector3(x * invScale, y * invScale, z * invScale); + } + + /// @brief Component-wise equality comparison of this vector with another vector. + /// @param[in] other The other vector to compare with this vector. + /// @return True if all components of this vector are equal to the corresponding components of the other vector, + /// false otherwise. + GP_NODISCARD constexpr bool operator==(const Vector3& other) const noexcept + { + return x == other.x && y == other.y && z == other.z; + } + + /// @brief Component-wise inequality comparison of this vector with another vector. + /// @param[in] other The other vector to compare with this vector. + /// @return True if any component of this vector is not equal to the corresponding component of the other vector, + /// false otherwise. + GP_NODISCARD constexpr bool operator!=(const Vector3& other) const noexcept + { + return x != other.x || y != other.y || z != other.z; + } + + /// @brief Subscript operator for non-const access to vector components by index. + /// @param[in] index The index of the component to access (0 for x, 1 for y, 2 for z). + /// @return A reference to the component at the specified index. + /// @note The behavior is undefined if the index is out of range (not 0, 1, or 2). + GP_NODISCARD constexpr T& operator[](const Int32 index) noexcept + { + return this->component(index); + } + + /// @brief Subscript operator for const access to vector components by index. + /// @param[in] index The index of the component to access (0 for x, 1 for y, 2 for z). + /// @return A const reference to the component at the specified index. + /// @note The behavior is undefined if the index is out of range (not 0, 1, or 2). + GP_NODISCARD constexpr const T& operator[](const Int32 index) const noexcept + { + return this->component(index); + } + + /// @brief In-place component-wise addition of another vector to this vector. + /// @param[in] other The other vector to add to this vector. + /// @return A reference to this vector after the addition. + constexpr Vector3& operator+=(const Vector3& other) noexcept + { + x += other.x; + y += other.y; + z += other.z; + return *this; + } + + /// @brief In-place component-wise addition of a scalar bias to this vector. + /// @tparam U The arithmetic type of the scalar bias. + /// @param[in] bias The scalar bias to add to each component of the vector. + /// @return A reference to this vector after the addition. + template + constexpr Vector3& operator+=(const U bias) noexcept + { + x += static_cast(bias); + y += static_cast(bias); + z += static_cast(bias); + return *this; + } + + /// @brief In-place component-wise subtraction of another vector from this vector. + /// @param[in] other The other vector to subtract from this vector. + /// @return A reference to this vector after the subtraction. + constexpr Vector3& operator-=(const Vector3& other) noexcept + { + x -= other.x; + y -= other.y; + z -= other.z; + return *this; + } + + /// @brief In-place component-wise subtraction of a scalar bias from this vector. + /// @tparam U The arithmetic type of the scalar bias. + /// @param[in] bias The scalar bias to subtract from each component of the vector. + /// @return A reference to this vector after the subtraction. + template + constexpr Vector3& operator-=(const U bias) noexcept + { + x -= static_cast(bias); + y -= static_cast(bias); + z -= static_cast(bias); + return *this; + } + + /// @brief In-place component-wise multiplication of this vector with another vector. + /// @param[in] other The other vector to multiply with this vector. + /// @return A reference to this vector after the multiplication. + constexpr Vector3& operator*=(const Vector3& other) noexcept + { + x *= other.x; + y *= other.y; + z *= other.z; + return *this; + } + + /// @brief In-place component-wise multiplication of this vector by a scalar scale factor. + /// @tparam U The arithmetic type of the scalar scale factor. + /// @param[in] scale The scalar scale factor to multiply each component of the vector by. + /// @return A reference to this vector after the multiplication. + template + constexpr Vector3& operator*=(const U scale) noexcept + { + x *= static_cast(scale); + y *= static_cast(scale); + z *= static_cast(scale); + return *this; + } + + /// @brief In-place component-wise division of this vector by another vector. + /// @param[in] other The other vector to divide this vector by. + /// @return A reference to this vector after the division. + constexpr Vector3& operator/=(const Vector3& other) noexcept + { + x /= other.x; + y /= other.y; + z /= other.z; + return *this; + } + + /// @brief In-place component-wise division of this vector by a scalar scale factor. + /// @tparam U The arithmetic type of the scalar scale factor. + /// @param[in] scale The scalar scale factor to divide each component of the vector by. + /// @return A reference to this vector after the division. + template + constexpr Vector3& operator/=(const U scale) noexcept + { + x /= static_cast(scale); + y /= static_cast(scale); + z /= static_cast(scale); + return *this; + } + +public: + /// @brief Gets a reference to a component of the vector by index. + /// @param[in] index The index of the component to access (0 for x, 1 for y, 2 for z). + /// @return A reference to the component at the specified index. + /// @note The behavior is undefined if the index is out of range (not 0, 1, or 2). + GP_NODISCARD constexpr T& component(const Int32 index) noexcept + { + GP_ASSERT(index >= 0 && index < 3, "Index out of range"); + return *(&x + index); + } + + /// @brief Gets a const reference to a component of the vector by index. + /// @param[in] index The index of the component to access (0 for x, 1 for y, 2 for z). + /// @return A const reference to the component at the specified index. + /// @note The behavior is undefined if the index is out of range (not 0, 1, or 2). + GP_NODISCARD constexpr const T& component(const Int32 index) const noexcept + { + GP_ASSERT(index >= 0 && index < 3, "Index out of range"); + return *(&x + index); + } + + /// @brief Component-wise cross product of this vector with another vector. + /// @param[in] other The other vector to compute the cross product with. + /// @return The cross product of this vector and the other vector. + GP_NODISCARD constexpr Vector3 cross(const Vector3& other) const noexcept + { + return *this ^ other; + } + + /// @brief Component-wise dot product of this vector with another vector. + /// @param[in] other The other vector to compute the dot product with. + /// @return The dot product of this vector and the other vector. + GP_NODISCARD constexpr T dot(const Vector3& other) const noexcept + { + return *this | other; + } + + /// @brief Checks if this vector is equal to another vector within a given tolerance. + /// @param[in] other The other vector to compare with this vector. + /// @param[in] tolerance The tolerance for the comparison. + /// @return True if all components of this vector are equal to the corresponding components of the other vector + /// within the tolerance, false otherwise. + GP_NODISCARD constexpr bool + equals(const Vector3& other, const T tolerance = constants::kindaSmallNumber) const noexcept + { + return math::abs(x - other.x) <= tolerance && math::abs(y - other.y) <= tolerance && + math::abs(z - other.z) <= tolerance; + } + + /// @brief Checks if all components of this vector are equal within a given tolerance. + /// @param[in] tolerance The tolerance for the comparison. + /// @return True if all components of this vector are equal within the tolerance, false otherwise. + GP_NODISCARD constexpr bool isAllComponentsEqual(const T tolerance = constants::kindaSmallNumber) const noexcept + { + return math::abs(x - y) <= tolerance && math::abs(x - z) <= tolerance && math::abs(y - z) <= tolerance; + } + + /// @brief Checks if the vector is nearly zero within a given tolerance. + /// @param[in] tolerance The tolerance for the comparison. + /// @return True if all components of the vector are nearly zero within the tolerance, false otherwise. + GP_NODISCARD constexpr bool isNearlyZero(const T tolerance = constants::kindaSmallNumber) const noexcept + { + return math::abs(x) <= tolerance && math::abs(y) <= tolerance && math::abs(z) <= tolerance; + } + + /// @brief Checks if the vector is exactly zero. + /// @return True if all components of the vector are exactly zero, false otherwise. + GP_NODISCARD constexpr bool isZero() const noexcept + { + return x == T{ 0 } && y == T{ 0 } && z == T{ 0 }; + } + + /// @brief Checks if the vector is a unit vector within a given tolerance. + /// @param[in] tolerance The tolerance for the comparison. + /// @return True if the vector is a unit vector within the tolerance, false otherwise. + GP_NODISCARD constexpr bool isUnit(const T tolerance = constants::kindaSmallNumber) const noexcept + { + return math::abs(T{ 1 } - lengthSquared()) <= tolerance; + } + + /// @brief Checks if the vector is normalized within a given tolerance. + /// @param[in] tolerance The tolerance for the comparison. + /// @return True if the vector is normalized within the tolerance, false otherwise. + GP_NODISCARD constexpr bool isNormalized(const T tolerance = tresholds::vectorNormalized) const noexcept + { + return isUnit(tolerance); + } + + /// @brief Checks if all components of the vector are uniform within a given tolerance. + /// @param[in] tolerance The tolerance for the comparison. + /// @return True if all components of the vector are uniform within the tolerance, false otherwise. + GP_NODISCARD constexpr bool isUniform(const T tolerance = constants::kindaSmallNumber) const noexcept + { + return isAllComponentsEqual(tolerance); + } + + /// @brief Get the maximum component value of the vector. + /// @return The maximum component value among x, y, and z. + GP_NODISCARD constexpr T getMax() const noexcept + { + return math::max(x, math::max(y, z)); + } + + /// @brief Get the minimum component value of the vector. + /// @return The minimum component value among x, y, and z. + GP_NODISCARD constexpr T getMin() const noexcept + { + return math::min(x, math::min(y, z)); + } + + /// @brief Get the maximum absolute component value of the vector. + /// @return The maximum absolute component value among x, y, and z. + GP_NODISCARD constexpr T getAbsMax() const noexcept + { + return math::max(math::abs(x), math::max(math::abs(y), math::abs(z))); + } + + /// @brief Get the minimum absolute component value of the vector. + /// @return The minimum absolute component value among x, y, and z. + GP_NODISCARD constexpr T getAbsMin() const noexcept + { + return math::min(math::abs(x), math::min(math::abs(y), math::abs(z))); + } + + /// @brief Get the component-wise minimum of this vector and another vector. + /// @param[in] other The other vector to compare with this vector. + /// @return A vector containing the minimum of each component between this vector and the other vector. + GP_NODISCARD constexpr Vector3 getComponentMin(const Vector3& other) const noexcept + { + return Vector3(math::min(x, other.x), math::min(y, other.y), math::min(z, other.z)); + } + + /// @brief Get the component-wise maximum of this vector and another vector. + /// @param[in] other The other vector to compare with this vector. + /// @return A vector containing the maximum of each component between this vector and the other vector. + GP_NODISCARD constexpr Vector3 getComponentMax(const Vector3& other) const noexcept + { + return Vector3(math::max(x, other.x), math::max(y, other.y), math::max(z, other.z)); + } + + /// @brief Get the component-wise minimum of the absolute values of this vector and another vector. + /// @param[in] other The other vector to compare with this vector. + /// @return A vector containing the minimum of the absolute values of each component between this vector and the + /// other vector. + GP_NODISCARD constexpr Vector3 getComponentAbsMin(const Vector3& other) const noexcept + { + return Vector3( + math::min(math::abs(x), math::abs(other.x)), + math::min(math::abs(y), math::abs(other.y)), + math::min(math::abs(z), math::abs(other.z)) + ); + } + + /// @brief Get the component-wise maximum of the absolute values of this vector and another vector. + /// @param[in] other The other vector to compare with this vector. + /// @return A vector containing the maximum of the absolute values of each component between this vector and the + /// other vector. + GP_NODISCARD constexpr Vector3 getComponentAbsMax(const Vector3& other) const noexcept + { + return Vector3( + math::max(math::abs(x), math::abs(other.x)), + math::max(math::abs(y), math::abs(other.y)), + math::max(math::abs(z), math::abs(other.z)) + ); + } + + /// @brief Get a vector containing the absolute values of each component of this vector. + /// @return A vector with the absolute values of each component of this vector. + GP_NODISCARD constexpr Vector3 getAbsolute() const noexcept + { + return Vector3(math::abs(x), math::abs(y), math::abs(z)); + } + + /// @brief Get the length of the vector. + /// @return The length of the vector. + GP_NODISCARD constexpr T length() const noexcept + { + return math::sqrt(x * x + y * y + z * z); + } + + /// @brief Get the squared length of the vector. + /// @return The squared length of the vector. + GP_NODISCARD constexpr T lengthSquared() const noexcept + { + return x * x + y * y + z * z; + } + + /// @brief Normalize the vector in place if its length is greater than a given tolerance. + /// @param[in] tolerance The tolerance for the length check to avoid division by zero or very small numbers. + /// @return True if the vector was successfully normalized, false if the length was too small and the vector was not + /// modified. + constexpr bool normalize(const T tolerance = constants::smallNumber) noexcept + { + const T squareSum = x * x + y * y + z * z; + if (squareSum > tolerance) + { + const T scale = math::inverseSqrt(squareSum); + x *= scale; + y *= scale; + z *= scale; + return true; + } + return false; + } + + /// @brief Get a normalized version of this vector without modifying the original vector unsafely, assuming the + /// length of the vector is greater than zero. + /// @return A normalized version of this vector if its length is greater than zero, or an undefined result if the + /// length is zero (caller must ensure the length is greater than zero). + GP_NODISCARD constexpr Vector3 getUnsafeNormal() const + { + const T scale = math::inverseSqrt(x * x + y * y + z * z); + return Vector3(x * scale, y * scale, z * scale); + } + + /// @brief Get a normalized version of this vector, returning a zero vector if the length is less than or equal to a + /// given tolerance to avoid division by zero or very small numbers. + /// @param[in] tolerance The tolerance for the length check to determine if the vector is too small to normalize. + /// @return A normalized version of this vector if its length is greater than the tolerance, or a zero vector if the + /// length is less than or equal to the tolerance. + GP_NODISCARD constexpr Vector3 getSafeNormal(const T tolerance = constants::smallNumber) const noexcept + { + const T squareSum = x * x + y * y + z * z; + + if (squareSum == T{ 1 }) + { + return *this; + } + else if (squareSum < tolerance) + { + return Vector3::zero(); + } + + const T scale = math::inverseSqrt(squareSum); + return Vector3(x * scale, y * scale, z * scale); + } + + /// @brief Get a vector containing the sign of each component of this vector. + /// @return A vector with the sign of each component of this vector, where each component is -1, 0, or 1 depending + /// on whether the original component is negative, zero, or positive, respectively. + GP_NODISCARD constexpr Vector3 getSignVector() const noexcept + { + return Vector3(math::sign(x), math::sign(y), math::sign(z)); + } + + /// @brief Get the projection of this vector onto the xy-plane by dividing the x and y components by the z component + /// @return A vector containing the projected x and y components, with the z component set to 1. + GP_NODISCARD constexpr Vector3 getProjection() const noexcept + { + const T invZ = T{ 1 } / z; + return Vector3(x * invZ, y * invZ, T{ 1 }); + } + + /// @brief Get the component-wise reciprocal of this vector, where each component is replaced by its reciprocal. + /// @return A vector containing the reciprocal of each component of this vector, where each component is 1 divided + /// by the original component. + GP_NODISCARD constexpr Vector3 getReciprocal() const noexcept + { + return Vector3( + x != T{ 0 } ? T{ 1 } / x : constants::bigNumber, + y != T{ 0 } ? T{ 1 } / y : constants::bigNumber, + z != T{ 0 } ? T{ 1 } / z : constants::bigNumber + ); + } + + /// @brief Project this vector onto another vector. + /// @param[in] other The vector to project onto. + /// @return The projected vector. + GP_NODISCARD constexpr Vector3 projectOnTo(const Vector3& other) const noexcept + { + return other * ((*this | other) / (other | other)); + } + + /// @brief Project this vector onto a normal vector. + /// @param[in] normal The normal vector to project onto. + /// @return The projected vector. + GP_NODISCARD constexpr Vector3 projectOnToNormal(const Vector3& normal) const noexcept + { + return normal * (*this | normal); + } + + /// @brief Mirror this vector by another vector. + /// @param[in] normal The vector to mirror by. + /// @return The mirrored vector. + GP_NODISCARD Vector3 mirrorByVector(const Vector3& normal) const noexcept + { + return *this - normal * (T{ 2 } * (*this | normal)); + } + +public: + /// @brief Component-wise cross product of two vectors. + /// @param[in] lhs The first vector to compute the cross product with. + /// @param[in] rhs The second vector to compute the cross product with. + /// @return The cross product of the two vectors. + GP_NODISCARD constexpr static Vector3 cross(const Vector3& lhs, const Vector3& rhs) noexcept + { + return lhs ^ rhs; + } + + /// @brief Component-wise dot product of two vectors. + /// @param[in] lhs The first vector to compute the dot product with. + /// @param[in] rhs The second vector to compute the dot product with. + /// @return The dot product of the two vectors. + GP_NODISCARD constexpr static T dot(const Vector3& lhs, const Vector3& rhs) noexcept + { + return lhs | rhs; + } + + /// @brief Get the component-wise minimum of two vectors. + /// @param[in] a The first vector to compare. + /// @param[in] b The second vector to compare. + /// @return A vector containing the minimum of each component between the two vectors. + GP_NODISCARD constexpr static inline Vector3 min(const Vector3& a, const Vector3& b) noexcept + { + return Vector3(math::min(a.x, b.x), math::min(a.y, b.y), math::min(a.z, b.z)); + } + + /// @brief Get the component-wise maximum of two vectors. + /// @param[in] a The first vector to compare. + /// @param[in] b The second vector to compare. + /// @return A vector containing the maximum of each component between the two vectors. + GP_NODISCARD constexpr static inline Vector3 max(const Vector3& a, const Vector3& b) noexcept + { + return Vector3(math::max(a.x, b.x), math::max(a.y, b.y), math::max(a.z, b.z)); + } + + /// @brief Get the component-wise minimum of three vectors. + /// @param[in] a The first vector to compare. + /// @param[in] b The second vector to compare. + /// @param[in] c The third vector to compare. + /// @return A vector containing the minimum of each component between the three vectors. + GP_NODISCARD constexpr static inline Vector3 + min(const Vector3& a, const Vector3& b, const Vector3& c) noexcept + { + return Vector3(math::min(a.x, b.x, c.x), math::min(a.y, b.y, c.y), math::min(a.z, b.z, c.z)); + } + + /// @brief Get the component-wise maximum of three vectors. + /// @param[in] a The first vector to compare. + /// @param[in] b The second vector to compare. + /// @param[in] c The third vector to compare. + /// @return A vector containing the maximum of each component between the three vectors. + GP_NODISCARD constexpr static inline Vector3 + max(const Vector3& a, const Vector3& b, const Vector3& c) noexcept + { + return Vector3(math::max(a.x, b.x, c.x), math::max(a.y, b.y, c.y), math::max(a.z, b.z, c.z)); + } + + /// @brief Clamp a vector between two other vectors. + /// @param[in] value The vector to clamp. + /// @param[in] minVec The minimum vector. + /// @param[in] maxVec The maximum vector. + /// @return The clamped vector. + GP_NODISCARD constexpr static inline Vector3 + clamp(const Vector3& value, const Vector3& minVec, const Vector3& maxVec) noexcept + { + return Vector3( + math::clamp(value.x, minVec.x, maxVec.x), + math::clamp(value.y, minVec.y, maxVec.y), + math::clamp(value.z, minVec.z, maxVec.z) + ); + } }; +/// @brief Get the component-wise minimum of two vectors. +/// @param[in] a The first vector to compare. +/// @param[in] b The second vector to compare. +/// @return A vector containing the minimum of each component between the two vectors. +template +GP_NODISCARD constexpr Vector3 min(const Vector3& a, const Vector3& b) noexcept +{ + return Vector3::min(a, b); +} + +/// @brief Get the component-wise maximum of two vectors. +/// @param[in] a The first vector to compare. +/// @param[in] b The second vector to compare. +/// @return A vector containing the maximum of each component between the two vectors. +template +GP_NODISCARD constexpr Vector3 max(const Vector3& a, const Vector3& b) noexcept +{ + return Vector3::max(a, b); +} + +/// @brief Get the component-wise minimum of three vectors. +/// @param[in] a The first vector to compare. +/// @param[in] b The second vector to compare. +/// @param[in] c The third vector to compare. +/// @return A vector containing the minimum of each component between the three vectors. +template +GP_NODISCARD constexpr Vector3 min(const Vector3& a, const Vector3& b, const Vector3& c) noexcept +{ + return Vector3::min(a, b, c); +} + +/// @brief Get the component-wise maximum of three vectors. +/// @param[in] a The first vector to compare. +/// @param[in] b The second vector to compare. +/// @param[in] c The third vector to compare. +/// @return A vector containing the maximum of each component between the three vectors. +template +GP_NODISCARD constexpr Vector3 max(const Vector3& a, const Vector3& b, const Vector3& c) noexcept +{ + return Vector3::max(a, b, c); +} + +/// @brief Clamp a vector between two other vectors. +/// @param[in] value The vector to clamp. +/// @param[in] minVec The minimum vector. +/// @param[in] maxVec The maximum vector. +/// @return The clamped vector. +template +GP_NODISCARD constexpr Vector3 + clamp(const Vector3& value, const Vector3& minVec, const Vector3& maxVec) noexcept +{ + return Vector3::clamp(value, minVec, maxVec); +} + } // namespace gp::math +/// @brief Component-wise addition of a scalar bias to a vector, with the scalar on the left-hand side of the operator. +/// @param[in] bias The scalar bias to add to each component of the vector. +/// @param[in] vec The vector to add the bias to. +/// @return A vector containing the result of the component-wise addition. +template +GP_NODISCARD constexpr gp::math::Vector3 operator+(const U bias, const gp::math::Vector3& vec) noexcept +{ + return vec + bias; +} + +/// @brief Component-wise multiplication of a vector by a scalar scale factor, with the scalar on the left-hand side of +/// the operator. +/// @param[in] scale The scalar scale factor to multiply each component of the vector by. +/// @param[in] vec The vector to multiply by the scale factor. +/// @return A vector containing the result of the component-wise multiplication. +template +GP_NODISCARD constexpr gp::math::Vector3 operator*(const U scale, const gp::math::Vector3& vec) noexcept +{ + return vec * scale; +} + // Include the implementation of the Vector3 template #include "maths/vector/Vector3.inl" diff --git a/source/runtime/core/tests/maths/vector/Vector3.tests.cpp b/source/runtime/core/tests/maths/vector/Vector3.tests.cpp index f2debf86..6ccc437e 100644 --- a/source/runtime/core/tests/maths/vector/Vector3.tests.cpp +++ b/source/runtime/core/tests/maths/vector/Vector3.tests.cpp @@ -20,6 +20,7 @@ class Vector3Test : public ::testing::Test // Helper constants to ensure correct types during assertions const T zero = T{ 0 }; const T one = T{ 1 }; + const T minusOne = T{ -1 }; }; // Specify the types you want to test (must satisfy concepts::IsFloatingPoint) @@ -110,4 +111,699 @@ TYPED_TEST(Vector3Test, Vector4Constructor) EXPECT_EQ(vecDefaultW.z, vec4.z); } +TYPED_TEST(Vector3Test, Constants) +{ + Vector3 zeroVec = Vector3::zero(); + EXPECT_EQ(zeroVec.x, this->zero); + EXPECT_EQ(zeroVec.y, this->zero); + EXPECT_EQ(zeroVec.z, this->zero); + + Vector3 oneVec = Vector3::one(); + EXPECT_EQ(oneVec.x, this->one); + EXPECT_EQ(oneVec.y, this->one); + EXPECT_EQ(oneVec.z, this->one); + + Vector3 unitX = Vector3::unitX(); + EXPECT_EQ(unitX.x, this->one); + EXPECT_EQ(unitX.y, this->zero); + EXPECT_EQ(unitX.z, this->zero); + + Vector3 unitY = Vector3::unitY(); + EXPECT_EQ(unitY.x, this->zero); + EXPECT_EQ(unitY.y, this->one); + EXPECT_EQ(unitY.z, this->zero); + + Vector3 unitZ = Vector3::unitZ(); + EXPECT_EQ(unitZ.x, this->zero); + EXPECT_EQ(unitZ.y, this->zero); + EXPECT_EQ(unitZ.z, this->one); + + Vector3 forward = Vector3::forward(); + EXPECT_EQ(forward.x, this->zero); + EXPECT_EQ(forward.y, this->zero); + EXPECT_EQ(forward.z, this->one); + + Vector3 backward = Vector3::backward(); + EXPECT_EQ(backward.x, this->zero); + EXPECT_EQ(backward.y, this->zero); + EXPECT_EQ(backward.z, this->minusOne); + + Vector3 left = Vector3::left(); + EXPECT_EQ(left.x, this->minusOne); + EXPECT_EQ(left.y, this->zero); + EXPECT_EQ(left.z, this->zero); + + Vector3 right = Vector3::right(); + EXPECT_EQ(right.x, this->one); + EXPECT_EQ(right.y, this->zero); + EXPECT_EQ(right.z, this->zero); + + Vector3 up = Vector3::up(); + EXPECT_EQ(up.x, this->zero); + EXPECT_EQ(up.y, this->one); + EXPECT_EQ(up.z, this->zero); + + Vector3 down = Vector3::down(); + EXPECT_EQ(down.x, this->zero); + EXPECT_EQ(down.y, this->minusOne); + EXPECT_EQ(down.z, this->zero); +} + +TYPED_TEST(Vector3Test, CrossProductOperator) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 0.0 }, TypeParam{ 0.0 }); + Vector3 vecB(TypeParam{ 0.0 }, TypeParam{ 1.0 }, TypeParam{ 0.0 }); + + Vector3 cross = vecA ^ vecB; + + EXPECT_EQ(cross.x, this->zero); + EXPECT_EQ(cross.y, this->zero); + EXPECT_EQ(cross.z, this->one); +} + +TYPED_TEST(Vector3Test, DotProductOperator) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 4.0 }, TypeParam{ 5.0 }, TypeParam{ 6.0 }); + + TypeParam dot = vecA | vecB; + + EXPECT_EQ(dot, TypeParam{ 32.0 }); // 1*4 + 2*5 + 3*6 = 32 +} + +TYPED_TEST(Vector3Test, NegationOperator) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ -2.0 }, TypeParam{ 3.0 }); + + Vector3 negated = -vec; + + EXPECT_EQ(negated.x, -vec.x); + EXPECT_EQ(negated.y, -vec.y); + EXPECT_EQ(negated.z, -vec.z); +} + +TYPED_TEST(Vector3Test, UnaryPlusOperator) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ -2.0 }, TypeParam{ 3.0 }); + + Vector3 result = +vec; + + EXPECT_EQ(result.x, vec.x); + EXPECT_EQ(result.y, vec.y); + EXPECT_EQ(result.z, vec.z); +} + +TYPED_TEST(Vector3Test, EqualityOperator) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecC(TypeParam{ 4.0 }, TypeParam{ 5.0 }, TypeParam{ 6.0 }); + + EXPECT_TRUE(vecA == vecB); + EXPECT_FALSE(vecA == vecC); +} + +TYPED_TEST(Vector3Test, InequalityOperator) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecC(TypeParam{ 4.0 }, TypeParam{ 5.0 }, TypeParam{ 6.0 }); + + EXPECT_FALSE(vecA != vecB); + EXPECT_TRUE(vecA != vecC); +} + +TYPED_TEST(Vector3Test, SubscriptOperator) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + + EXPECT_EQ(vec[0], vec.x); + EXPECT_EQ(vec[1], vec.y); + EXPECT_EQ(vec[2], vec.z); + + const Vector3 constVec(TypeParam{ 4.0 }, TypeParam{ 5.0 }, TypeParam{ 6.0 }); + EXPECT_EQ(constVec[0], constVec.x); + EXPECT_EQ(constVec[1], constVec.y); + EXPECT_EQ(constVec[2], constVec.z); + + Vector3 modifiableVec(TypeParam{ 7.0 }, TypeParam{ 8.0 }, TypeParam{ 9.0 }); + modifiableVec[0] = TypeParam{ 10.0 }; + modifiableVec[1] = TypeParam{ 11.0 }; + modifiableVec[2] = TypeParam{ 12.0 }; + EXPECT_EQ(modifiableVec.x, TypeParam{ 10.0 }); + EXPECT_EQ(modifiableVec.y, TypeParam{ 11.0 }); + EXPECT_EQ(modifiableVec.z, TypeParam{ 12.0 }); +} + +TYPED_TEST(Vector3Test, VectorAdditionOperator) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 4.0 }, TypeParam{ 5.0 }, TypeParam{ 6.0 }); + + Vector3 sum = vecA + vecB; + + EXPECT_NEAR(sum.x, vecA.x + vecB.x, TypeParam{ 1e-6 }); + EXPECT_NEAR(sum.y, vecA.y + vecB.y, TypeParam{ 1e-6 }); + EXPECT_NEAR(sum.z, vecA.z + vecB.z, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, ScalarAdditionOperator) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + TypeParam bias = TypeParam{ 5.0 }; + + Vector3 result = vec + bias; + + EXPECT_NEAR(result.x, vec.x + bias, TypeParam{ 1e-6 }); + EXPECT_NEAR(result.y, vec.y + bias, TypeParam{ 1e-6 }); + EXPECT_NEAR(result.z, vec.z + bias, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, ScalarAdditionOperatorCommutative) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + TypeParam bias = TypeParam{ 5.0 }; + + Vector3 result = bias + vec; + + EXPECT_NEAR(result.x, vec.x + bias, TypeParam{ 1e-6 }); + EXPECT_NEAR(result.y, vec.y + bias, TypeParam{ 1e-6 }); + EXPECT_NEAR(result.z, vec.z + bias, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, VectorSubtractionOperator) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 4.0 }, TypeParam{ 5.0 }, TypeParam{ 6.0 }); + + Vector3 difference = vecA - vecB; + + EXPECT_NEAR(difference.x, vecA.x - vecB.x, TypeParam{ 1e-6 }); + EXPECT_NEAR(difference.y, vecA.y - vecB.y, TypeParam{ 1e-6 }); + EXPECT_NEAR(difference.z, vecA.z - vecB.z, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, ScalarSubtractionOperator) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + TypeParam bias = TypeParam{ 5.0 }; + + Vector3 result = vec - bias; + + EXPECT_NEAR(result.x, vec.x - bias, TypeParam{ 1e-6 }); + EXPECT_NEAR(result.y, vec.y - bias, TypeParam{ 1e-6 }); + EXPECT_NEAR(result.z, vec.z - bias, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, VectorMultiplicationOperator) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 4.0 }, TypeParam{ 5.0 }, TypeParam{ 6.0 }); + + Vector3 product = vecA * vecB; + + EXPECT_NEAR(product.x, vecA.x * vecB.x, TypeParam{ 1e-6 }); + EXPECT_NEAR(product.y, vecA.y * vecB.y, TypeParam{ 1e-6 }); + EXPECT_NEAR(product.z, vecA.z * vecB.z, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, ScalarMultiplicationOperator) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + TypeParam scalar = TypeParam{ 5.0 }; + + Vector3 result = vec * scalar; + + EXPECT_NEAR(result.x, vec.x * scalar, TypeParam{ 1e-6 }); + EXPECT_NEAR(result.y, vec.y * scalar, TypeParam{ 1e-6 }); + EXPECT_NEAR(result.z, vec.z * scalar, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, ScalarMultiplicationOperatorCommutative) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + TypeParam scalar = TypeParam{ 5.0 }; + + Vector3 result = scalar * vec; + + EXPECT_NEAR(result.x, vec.x * scalar, TypeParam{ 1e-6 }); + EXPECT_NEAR(result.y, vec.y * scalar, TypeParam{ 1e-6 }); + EXPECT_NEAR(result.z, vec.z * scalar, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, VectorDivisionOperator) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 4.0 }, TypeParam{ 5.0 }, TypeParam{ 6.0 }); + + Vector3 quotient = vecA / vecB; + + EXPECT_NEAR(quotient.x, vecA.x / vecB.x, TypeParam{ 1e-6 }); + EXPECT_NEAR(quotient.y, vecA.y / vecB.y, TypeParam{ 1e-6 }); + EXPECT_NEAR(quotient.z, vecA.z / vecB.z, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, ScalarDivisionOperator) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + TypeParam scalar = TypeParam{ 5.0 }; + + Vector3 result = vec / scalar; + + EXPECT_NEAR(result.x, vec.x / scalar, TypeParam{ 1e-6 }); + EXPECT_NEAR(result.y, vec.y / scalar, TypeParam{ 1e-6 }); + EXPECT_NEAR(result.z, vec.z / scalar, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, CompoundAssignmentOperators) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 other(TypeParam{ 4.0 }, TypeParam{ 5.0 }, TypeParam{ 6.0 }); + TypeParam scalar = TypeParam{ 5.0 }; + + vec += other; + EXPECT_NEAR(vec.x, TypeParam{ 5.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(vec.y, TypeParam{ 7.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(vec.z, TypeParam{ 9.0 }, TypeParam{ 1e-6 }); + + vec -= other; + EXPECT_NEAR(vec.x, TypeParam{ 1.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(vec.y, TypeParam{ 2.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(vec.z, TypeParam{ 3.0 }, TypeParam{ 1e-6 }); + + vec *= scalar; + EXPECT_NEAR(vec.x, TypeParam{ 5.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(vec.y, TypeParam{ 10.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(vec.z, TypeParam{ 15.0 }, TypeParam{ 1e-6 }); + + vec /= scalar; + EXPECT_NEAR(vec.x, TypeParam{ 1.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(vec.y, TypeParam{ 2.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(vec.z, TypeParam{ 3.0 }, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, ComponentMethod) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + EXPECT_EQ(vec.component(0), TypeParam{ 1.0 }); + EXPECT_EQ(vec.component(1), TypeParam{ 2.0 }); + EXPECT_EQ(vec.component(2), TypeParam{ 3.0 }); + + const Vector3 constVec(TypeParam{ 4.0 }, TypeParam{ 5.0 }, TypeParam{ 6.0 }); + EXPECT_EQ(constVec.component(0), TypeParam{ 4.0 }); + EXPECT_EQ(constVec.component(1), TypeParam{ 5.0 }); + EXPECT_EQ(constVec.component(2), TypeParam{ 6.0 }); +} + +TYPED_TEST(Vector3Test, CrossMethod) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 0.0 }, TypeParam{ 0.0 }); + Vector3 vecB(TypeParam{ 0.0 }, TypeParam{ 1.0 }, TypeParam{ 0.0 }); + Vector3 cross = vecA.cross(vecB); + EXPECT_EQ(cross.x, this->zero); + EXPECT_EQ(cross.y, this->zero); + EXPECT_EQ(cross.z, this->one); +} + +TYPED_TEST(Vector3Test, DotMethod) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 4.0 }, TypeParam{ 5.0 }, TypeParam{ 6.0 }); + EXPECT_EQ(vecA.dot(vecB), TypeParam{ 32.0 }); +} + +TYPED_TEST(Vector3Test, EqualsMethod) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecC(TypeParam{ 1.1 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + + EXPECT_TRUE(vecA.equals(vecB)); + EXPECT_FALSE(vecA.equals(vecC)); + EXPECT_TRUE(vecA.equals(vecC, TypeParam{ 0.2 })); +} + +TYPED_TEST(Vector3Test, IsAllComponentsEqualMethod) +{ + Vector3 vecA(TypeParam{ 2.0 }, TypeParam{ 2.0 }, TypeParam{ 2.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ 2.0 }, TypeParam{ 2.1 }); + + EXPECT_TRUE(vecA.isAllComponentsEqual()); + EXPECT_FALSE(vecB.isAllComponentsEqual()); + EXPECT_TRUE(vecB.isAllComponentsEqual(TypeParam{ 0.2 })); +} + +TYPED_TEST(Vector3Test, IsNearlyZeroMethod) +{ + Vector3 vecA = Vector3::zero(); + Vector3 vecB(TypeParam{ 0.00001 }, TypeParam{ 0.0 }, TypeParam{ 0.0 }); + + EXPECT_TRUE(vecA.isNearlyZero()); + EXPECT_FALSE(vecB.isNearlyZero(TypeParam{ 1e-6 })); + EXPECT_TRUE(vecB.isNearlyZero(TypeParam{ 1e-4 })); +} + +TYPED_TEST(Vector3Test, IsZeroMethod) +{ + Vector3 vecA = Vector3::zero(); + Vector3 vecB(TypeParam{ 0.00001 }, TypeParam{ 0.0 }, TypeParam{ 0.0 }); + + EXPECT_TRUE(vecA.isZero()); + EXPECT_FALSE(vecB.isZero()); +} + +TYPED_TEST(Vector3Test, IsUnitMethod) +{ + Vector3 vecA = Vector3::unitX(); + Vector3 vecB(TypeParam{ 0.5 }, TypeParam{ 0.5 }, TypeParam{ 0.5 }); + + EXPECT_TRUE(vecA.isUnit()); + EXPECT_FALSE(vecB.isUnit()); +} + +TYPED_TEST(Vector3Test, IsNormalizedMethod) +{ + Vector3 vecA = Vector3::unitY(); + Vector3 vecB(TypeParam{ 1.0 }, TypeParam{ 1.0 }, TypeParam{ 0.0 }); + + EXPECT_TRUE(vecA.isNormalized()); + EXPECT_FALSE(vecB.isNormalized()); +} + +TYPED_TEST(Vector3Test, IsUniformMethod) +{ + Vector3 vecA(TypeParam{ 5.0 }, TypeParam{ 5.0 }, TypeParam{ 5.0 }); + Vector3 vecB(TypeParam{ 5.0 }, TypeParam{ 4.0 }, TypeParam{ 5.0 }); + + EXPECT_TRUE(vecA.isUniform()); + EXPECT_FALSE(vecB.isUniform()); +} + +TYPED_TEST(Vector3Test, GetMaxMethod) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ 5.0 }, TypeParam{ 3.0 }); + EXPECT_EQ(vec.getMax(), TypeParam{ 5.0 }); +} + +TYPED_TEST(Vector3Test, GetMinMethod) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ 5.0 }, TypeParam{ -3.0 }); + EXPECT_EQ(vec.getMin(), TypeParam{ -3.0 }); +} + +TYPED_TEST(Vector3Test, GetAbsMaxMethod) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ -5.0 }, TypeParam{ 3.0 }); + EXPECT_EQ(vec.getAbsMax(), TypeParam{ 5.0 }); +} + +TYPED_TEST(Vector3Test, GetAbsMinMethod) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ -5.0 }, TypeParam{ 0.5 }); + EXPECT_EQ(vec.getAbsMin(), TypeParam{ 0.5 }); +} + +TYPED_TEST(Vector3Test, GetComponentMinMethod) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 5.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ 4.0 }, TypeParam{ 6.0 }); + Vector3 minVec = vecA.getComponentMin(vecB); + EXPECT_EQ(minVec.x, TypeParam{ 1.0 }); + EXPECT_EQ(minVec.y, TypeParam{ 4.0 }); + EXPECT_EQ(minVec.z, TypeParam{ 3.0 }); +} + +TYPED_TEST(Vector3Test, GetComponentMaxMethod) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 5.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ 4.0 }, TypeParam{ 6.0 }); + Vector3 maxVec = vecA.getComponentMax(vecB); + EXPECT_EQ(maxVec.x, TypeParam{ 2.0 }); + EXPECT_EQ(maxVec.y, TypeParam{ 5.0 }); + EXPECT_EQ(maxVec.z, TypeParam{ 6.0 }); +} + +TYPED_TEST(Vector3Test, GetComponentAbsMinMethod) +{ + Vector3 vecA(TypeParam{ -1.0 }, TypeParam{ 5.0 }, TypeParam{ -3.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ -4.0 }, TypeParam{ 6.0 }); + Vector3 minVec = vecA.getComponentAbsMin(vecB); + EXPECT_EQ(minVec.x, TypeParam{ 1.0 }); + EXPECT_EQ(minVec.y, TypeParam{ 4.0 }); + EXPECT_EQ(minVec.z, TypeParam{ 3.0 }); +} + +TYPED_TEST(Vector3Test, GetComponentAbsMaxMethod) +{ + Vector3 vecA(TypeParam{ -1.0 }, TypeParam{ 5.0 }, TypeParam{ -3.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ -4.0 }, TypeParam{ 6.0 }); + Vector3 maxVec = vecA.getComponentAbsMax(vecB); + EXPECT_EQ(maxVec.x, TypeParam{ 2.0 }); + EXPECT_EQ(maxVec.y, TypeParam{ 5.0 }); + EXPECT_EQ(maxVec.z, TypeParam{ 6.0 }); +} + +TYPED_TEST(Vector3Test, GetAbsoluteMethod) +{ + Vector3 vec(TypeParam{ -1.0 }, TypeParam{ 2.0 }, TypeParam{ -3.0 }); + Vector3 absVec = vec.getAbsolute(); + EXPECT_EQ(absVec.x, TypeParam{ 1.0 }); + EXPECT_EQ(absVec.y, TypeParam{ 2.0 }); + EXPECT_EQ(absVec.z, TypeParam{ 3.0 }); +} + +TYPED_TEST(Vector3Test, LengthMethod) +{ + Vector3 vec(TypeParam{ 0.0 }, TypeParam{ 3.0 }, TypeParam{ 4.0 }); + EXPECT_NEAR(vec.length(), TypeParam{ 5.0 }, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, LengthSquaredMethod) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + EXPECT_NEAR(vec.lengthSquared(), TypeParam{ 14.0 }, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, NormalizeMethod) +{ + Vector3 vec(TypeParam{ 0.0 }, TypeParam{ 3.0 }, TypeParam{ 4.0 }); + EXPECT_TRUE(vec.normalize()); + EXPECT_NEAR(vec.x, TypeParam{ 0.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(vec.y, TypeParam{ 0.6 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(vec.z, TypeParam{ 0.8 }, TypeParam{ 1e-6 }); + + Vector3 zeroVec = Vector3::zero(); + EXPECT_FALSE(zeroVec.normalize()); +} + +TYPED_TEST(Vector3Test, GetUnsafeNormalMethod) +{ + Vector3 vec(TypeParam{ 0.0 }, TypeParam{ 3.0 }, TypeParam{ 4.0 }); + Vector3 norm = vec.getUnsafeNormal(); + EXPECT_NEAR(norm.x, TypeParam{ 0.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(norm.y, TypeParam{ 0.6 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(norm.z, TypeParam{ 0.8 }, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, GetSafeNormalMethod) +{ + Vector3 vec(TypeParam{ 0.0 }, TypeParam{ 3.0 }, TypeParam{ 4.0 }); + Vector3 norm = vec.getSafeNormal(); + EXPECT_NEAR(norm.x, TypeParam{ 0.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(norm.y, TypeParam{ 0.6 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(norm.z, TypeParam{ 0.8 }, TypeParam{ 1e-6 }); + + Vector3 zeroVec = Vector3::zero(); + Vector3 zeroNorm = zeroVec.getSafeNormal(); + EXPECT_EQ(zeroNorm.x, TypeParam{ 0.0 }); + EXPECT_EQ(zeroNorm.y, TypeParam{ 0.0 }); + EXPECT_EQ(zeroNorm.z, TypeParam{ 0.0 }); +} + +TYPED_TEST(Vector3Test, GetSignVectorMethod) +{ + Vector3 vec(TypeParam{ -5.0 }, TypeParam{ 0.0 }, TypeParam{ 3.0 }); + Vector3 signVec = vec.getSignVector(); + EXPECT_EQ(signVec.x, TypeParam{ -1.0 }); + EXPECT_EQ(signVec.y, TypeParam{ 0.0 }); + EXPECT_EQ(signVec.z, TypeParam{ 1.0 }); +} + +TYPED_TEST(Vector3Test, GetProjectionMethod) +{ + Vector3 vec(TypeParam{ 2.0 }, TypeParam{ 4.0 }, TypeParam{ 2.0 }); + Vector3 proj = vec.getProjection(); + EXPECT_NEAR(proj.x, TypeParam{ 1.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(proj.y, TypeParam{ 2.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(proj.z, TypeParam{ 1.0 }, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, GetReciprocalMethod) +{ + Vector3 vec(TypeParam{ 2.0 }, TypeParam{ 4.0 }, TypeParam{ 5.0 }); + Vector3 rec = vec.getReciprocal(); + EXPECT_NEAR(rec.x, TypeParam{ 0.5 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(rec.y, TypeParam{ 0.25 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(rec.z, TypeParam{ 0.2 }, TypeParam{ 1e-6 }); + + Vector3 vecZero = Vector3::zero(); + Vector3 recZero = vecZero.getReciprocal(); + EXPECT_EQ(recZero.x, constants::bigNumber); + EXPECT_EQ(recZero.y, constants::bigNumber); + EXPECT_EQ(recZero.z, constants::bigNumber); +} + +TYPED_TEST(Vector3Test, ProjectOnToMethod) +{ + Vector3 vec(TypeParam{ 2.0 }, TypeParam{ 2.0 }, TypeParam{ 0.0 }); + Vector3 onto(TypeParam{ 1.0 }, TypeParam{ 0.0 }, TypeParam{ 0.0 }); + Vector3 proj = vec.projectOnTo(onto); + EXPECT_NEAR(proj.x, TypeParam{ 2.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(proj.y, TypeParam{ 0.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(proj.z, TypeParam{ 0.0 }, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, ProjectOnToNormalMethod) +{ + Vector3 vec(TypeParam{ 2.0 }, TypeParam{ 2.0 }, TypeParam{ 0.0 }); + Vector3 normal(TypeParam{ 1.0 }, TypeParam{ 0.0 }, TypeParam{ 0.0 }); + Vector3 proj = vec.projectOnToNormal(normal); + EXPECT_NEAR(proj.x, TypeParam{ 2.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(proj.y, TypeParam{ 0.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(proj.z, TypeParam{ 0.0 }, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, MirrorByVectorMethod) +{ + Vector3 vec(TypeParam{ 1.0 }, TypeParam{ -1.0 }, TypeParam{ 0.0 }); + Vector3 normal(TypeParam{ 0.0 }, TypeParam{ 1.0 }, TypeParam{ 0.0 }); + Vector3 mirrored = vec.mirrorByVector(normal); + EXPECT_NEAR(mirrored.x, TypeParam{ 1.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(mirrored.y, TypeParam{ 1.0 }, TypeParam{ 1e-6 }); + EXPECT_NEAR(mirrored.z, TypeParam{ 0.0 }, TypeParam{ 1e-6 }); +} + +TYPED_TEST(Vector3Test, StaticCrossMethod) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 0.0 }, TypeParam{ 0.0 }); + Vector3 vecB(TypeParam{ 0.0 }, TypeParam{ 1.0 }, TypeParam{ 0.0 }); + Vector3 cross = Vector3::cross(vecA, vecB); + EXPECT_EQ(cross.x, this->zero); + EXPECT_EQ(cross.y, this->zero); + EXPECT_EQ(cross.z, this->one); +} + +TYPED_TEST(Vector3Test, StaticDotMethod) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 2.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 4.0 }, TypeParam{ 5.0 }, TypeParam{ 6.0 }); + EXPECT_EQ(Vector3::dot(vecA, vecB), TypeParam{ 32.0 }); +} + +TYPED_TEST(Vector3Test, StaticMin2Method) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 5.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ 4.0 }, TypeParam{ 6.0 }); + Vector3 minVec = Vector3::min(vecA, vecB); + EXPECT_EQ(minVec.x, TypeParam{ 1.0 }); + EXPECT_EQ(minVec.y, TypeParam{ 4.0 }); + EXPECT_EQ(minVec.z, TypeParam{ 3.0 }); +} + +TYPED_TEST(Vector3Test, StaticMax2Method) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 5.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ 4.0 }, TypeParam{ 6.0 }); + Vector3 maxVec = Vector3::max(vecA, vecB); + EXPECT_EQ(maxVec.x, TypeParam{ 2.0 }); + EXPECT_EQ(maxVec.y, TypeParam{ 5.0 }); + EXPECT_EQ(maxVec.z, TypeParam{ 6.0 }); +} + +TYPED_TEST(Vector3Test, StaticMin3Method) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 5.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ 4.0 }, TypeParam{ 6.0 }); + Vector3 vecC(TypeParam{ 0.0 }, TypeParam{ 7.0 }, TypeParam{ 4.0 }); + Vector3 minVec = Vector3::min(vecA, vecB, vecC); + EXPECT_EQ(minVec.x, TypeParam{ 0.0 }); + EXPECT_EQ(minVec.y, TypeParam{ 4.0 }); + EXPECT_EQ(minVec.z, TypeParam{ 3.0 }); +} + +TYPED_TEST(Vector3Test, StaticMax3Method) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 5.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ 4.0 }, TypeParam{ 6.0 }); + Vector3 vecC(TypeParam{ 0.0 }, TypeParam{ 7.0 }, TypeParam{ 4.0 }); + Vector3 maxVec = Vector3::max(vecA, vecB, vecC); + EXPECT_EQ(maxVec.x, TypeParam{ 2.0 }); + EXPECT_EQ(maxVec.y, TypeParam{ 7.0 }); + EXPECT_EQ(maxVec.z, TypeParam{ 6.0 }); +} + +TYPED_TEST(Vector3Test, StaticClampMethod) +{ + Vector3 vec(TypeParam{ -1.0 }, TypeParam{ 5.0 }, TypeParam{ 10.0 }); + Vector3 minVec(TypeParam{ 0.0 }, TypeParam{ 0.0 }, TypeParam{ 0.0 }); + Vector3 maxVec(TypeParam{ 2.0 }, TypeParam{ 2.0 }, TypeParam{ 2.0 }); + Vector3 clamped = Vector3::clamp(vec, minVec, maxVec); + EXPECT_EQ(clamped.x, TypeParam{ 0.0 }); + EXPECT_EQ(clamped.y, TypeParam{ 2.0 }); + EXPECT_EQ(clamped.z, TypeParam{ 2.0 }); +} + +TYPED_TEST(Vector3Test, GlobalMin2Function) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 5.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ 4.0 }, TypeParam{ 6.0 }); + Vector3 minVec = gp::math::min(vecA, vecB); + EXPECT_EQ(minVec.x, TypeParam{ 1.0 }); + EXPECT_EQ(minVec.y, TypeParam{ 4.0 }); + EXPECT_EQ(minVec.z, TypeParam{ 3.0 }); +} + +TYPED_TEST(Vector3Test, GlobalMax2Function) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 5.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ 4.0 }, TypeParam{ 6.0 }); + Vector3 maxVec = gp::math::max(vecA, vecB); + EXPECT_EQ(maxVec.x, TypeParam{ 2.0 }); + EXPECT_EQ(maxVec.y, TypeParam{ 5.0 }); + EXPECT_EQ(maxVec.z, TypeParam{ 6.0 }); +} + +TYPED_TEST(Vector3Test, GlobalMin3Function) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 5.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ 4.0 }, TypeParam{ 6.0 }); + Vector3 vecC(TypeParam{ 0.0 }, TypeParam{ 7.0 }, TypeParam{ 4.0 }); + Vector3 minVec = gp::math::min(vecA, vecB, vecC); + EXPECT_EQ(minVec.x, TypeParam{ 0.0 }); + EXPECT_EQ(minVec.y, TypeParam{ 4.0 }); + EXPECT_EQ(minVec.z, TypeParam{ 3.0 }); +} + +TYPED_TEST(Vector3Test, GlobalMax3Function) +{ + Vector3 vecA(TypeParam{ 1.0 }, TypeParam{ 5.0 }, TypeParam{ 3.0 }); + Vector3 vecB(TypeParam{ 2.0 }, TypeParam{ 4.0 }, TypeParam{ 6.0 }); + Vector3 vecC(TypeParam{ 0.0 }, TypeParam{ 7.0 }, TypeParam{ 4.0 }); + Vector3 maxVec = gp::math::max(vecA, vecB, vecC); + EXPECT_EQ(maxVec.x, TypeParam{ 2.0 }); + EXPECT_EQ(maxVec.y, TypeParam{ 7.0 }); + EXPECT_EQ(maxVec.z, TypeParam{ 6.0 }); +} + +TYPED_TEST(Vector3Test, GlobalClampFunction) +{ + Vector3 vec(TypeParam{ -1.0 }, TypeParam{ 5.0 }, TypeParam{ 10.0 }); + Vector3 minVec(TypeParam{ 0.0 }, TypeParam{ 0.0 }, TypeParam{ 0.0 }); + Vector3 maxVec(TypeParam{ 2.0 }, TypeParam{ 2.0 }, TypeParam{ 2.0 }); + Vector3 clamped = gp::math::clamp(vec, minVec, maxVec); + EXPECT_EQ(clamped.x, TypeParam{ 0.0 }); + EXPECT_EQ(clamped.y, TypeParam{ 2.0 }); + EXPECT_EQ(clamped.z, TypeParam{ 2.0 }); +} + } // namespace gp::math::tests