// // Created by vlad on 10/28/23. // #pragma once #include "omath/linear_algebra/vector2.hpp" #include "omath/trigonometry/angle.hpp" #include #include #include namespace omath { enum class Vector3Error { IMPOSSIBLE_BETWEEN_ANGLE, }; template requires std::is_arithmetic_v class Vector3 : public Vector2 { public: using ContainedType = Type; Type z = static_cast(0); constexpr Vector3(const Type& x, const Type& y, const Type& z) noexcept: Vector2(x, y), z(z) { } constexpr Vector3() noexcept: Vector2() {}; [[nodiscard]] constexpr bool operator==(const Vector3& other) const noexcept { return Vector2::operator==(other) && (other.z == z); } [[nodiscard]] constexpr bool operator!=(const Vector3& other) const noexcept { return !(*this == other); } constexpr Vector3& operator+=(const Vector3& other) noexcept { Vector2::operator+=(other); z += other.z; return *this; } constexpr Vector3& operator-=(const Vector3& other) noexcept { Vector2::operator-=(other); z -= other.z; return *this; } constexpr Vector3& operator*=(const Type& value) noexcept { Vector2::operator*=(value); z *= value; return *this; } constexpr Vector3& operator*=(const Vector3& other) noexcept { Vector2::operator*=(other); z *= other.z; return *this; } constexpr Vector3& operator/=(const Vector3& other) noexcept { Vector2::operator/=(other); z /= other.z; return *this; } constexpr Vector3& operator+=(const Type& value) noexcept { Vector2::operator+=(value); z += value; return *this; } constexpr Vector3& operator/=(const Type& value) noexcept { Vector2::operator/=(value); z /= value; return *this; } constexpr Vector3& operator-=(const Type& value) noexcept { Vector2::operator-=(value); z -= value; return *this; } constexpr Vector3& abs() noexcept { Vector2::abs(); z = z < 0.f ? -z : z; return *this; } [[nodiscard]] constexpr Type distance_to_sqr(const Vector3& other) const noexcept { return (*this - other).length_sqr(); } [[nodiscard]] constexpr Type dot(const Vector3& other) const noexcept { return Vector2::dot(other) + z * other.z; } #ifndef _MSC_VER [[nodiscard]] constexpr Type length() const { return std::hypot(this->x, this->y, z); } [[nodiscard]] constexpr Type length_2d() const { return Vector2::length(); } [[nodiscard]] Type distance_to(const Vector3& other) const { return (*this - other).length(); } [[nodiscard]] constexpr Vector3 normalized() const { const Type length_value = this->length(); return length_value != 0 ? *this / length_value : *this; } #else [[nodiscard]] Type length() const noexcept { return std::hypot(this->x, this->y, z); } [[nodiscard]] Vector3 normalized() const noexcept { const Type len = this->length(); return len != static_cast(0) ? *this / len : *this; } [[nodiscard]] Type length_2d() const noexcept { return Vector2::length(); } [[nodiscard]] Type distance_to(const Vector3& v_other) const noexcept { return (*this - v_other).length(); } #endif [[nodiscard]] constexpr Type length_sqr() const noexcept { return Vector2::length_sqr() + z * z; } [[nodiscard]] constexpr Vector3 operator-() const noexcept { return {-this->x, -this->y, -z}; } [[nodiscard]] constexpr Vector3 operator+(const Vector3& other) const noexcept { return {this->x + other.x, this->y + other.y, z + other.z}; } [[nodiscard]] constexpr Vector3 operator-(const Vector3& other) const noexcept { return {this->x - other.x, this->y - other.y, z - other.z}; } [[nodiscard]] constexpr Vector3 operator*(const Type& value) const noexcept { return {this->x * value, this->y * value, z * value}; } [[nodiscard]] constexpr Vector3 operator*(const Vector3& other) const noexcept { return {this->x * other.x, this->y * other.y, z * other.z}; } [[nodiscard]] constexpr Vector3 operator/(const Type& value) const noexcept { return {this->x / value, this->y / value, z / value}; } [[nodiscard]] constexpr Vector3 operator/(const Vector3& other) const noexcept { return {this->x / other.x, this->y / other.y, z / other.z}; } [[nodiscard]] constexpr Vector3 cross(const Vector3& other) const noexcept { return {this->y * other.z - z * other.y, z * other.x - this->x * other.z, this->x * other.y - this->y * other.x}; } [[nodiscard]] constexpr Type sum() const noexcept { return sum_2d() + z; } [[nodiscard]] bool point_to_same_direction(const Vector3& other) const { return dot(other) > static_cast(0); } [[nodiscard]] std::expected, Vector3Error> angle_between(const Vector3& other) const noexcept { const auto bottom = length() * other.length(); if (bottom == static_cast(0)) return std::unexpected(Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE); return Angle::from_radians(std::acos(dot(other) / bottom)); } [[nodiscard]] bool is_perpendicular(const Vector3& other, Type epsilon = static_cast(0.0001)) const noexcept { if (const auto angle = angle_between(other)) return std::abs(angle->as_degrees() - static_cast(90)) <= epsilon; return false; } [[nodiscard]] constexpr Type sum_2d() const noexcept { return Vector2::sum(); } [[nodiscard]] constexpr std::tuple as_tuple() const noexcept { return std::make_tuple(this->x, this->y, z); } [[nodiscard]] bool operator<(const Vector3& other) const noexcept { return length() < other.length(); } [[nodiscard]] bool operator>(const Vector3& other) const noexcept { return length() > other.length(); } [[nodiscard]] bool operator<=(const Vector3& other) const noexcept { return length() <= other.length(); } [[nodiscard]] bool operator>=(const Vector3& other) const noexcept { return length() >= other.length(); } [[nodiscard]] constexpr std::array as_array() const noexcept { return {this->x, this->y, z}; } }; } // namespace omath template<> struct std::hash> { [[nodiscard]] std::size_t operator()(const omath::Vector3& vec) const noexcept { std::size_t hash = 0; constexpr std::hash hasher; hash ^= hasher(vec.x) + 0x9e3779b9 + (hash << 6) + (hash >> 2); hash ^= hasher(vec.y) + 0x9e3779b9 + (hash << 6) + (hash >> 2); hash ^= hasher(vec.z) + 0x9e3779b9 + (hash << 6) + (hash >> 2); return hash; } }; template struct std::formatter> // NOLINT(*-dcl58-cpp) { [[nodiscard]] static constexpr auto parse(std::format_parse_context& ctx) { return ctx.begin(); } template [[nodiscard]] static auto format(const omath::Vector3& vec, FormatContext& ctx) { if constexpr (std::is_same_v) return std::format_to(ctx.out(), "[{}, {}, {}]", vec.x, vec.y, vec.z); if constexpr (std::is_same_v) return std::format_to(ctx.out(), L"[{}, {}, {}]", vec.x, vec.y, vec.z); if constexpr (std::is_same_v) return std::format_to(ctx.out(), u8"[{}, {}, {}]", vec.x, vec.y, vec.z); } };