// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #pragma once #include "Math.h" #include "Mathd.h" #include "Engine/Core/Formatting.h" #include "Engine/Core/Templates.h" /// /// Represents a two dimensional mathematical vector. /// template API_STRUCT(Template) struct Vector2Base { typedef T Real; static FLAXENGINE_API struct ScriptingTypeInitializer TypeInitializer; union { struct { /// /// The X component of the vector. /// API_FIELD() T X; /// /// The Y component of the vector. /// API_FIELD() T Y; }; /// /// The raw vector values (in XY order). /// T Raw[2]; }; public: // Vector with all components equal 0 static FLAXENGINE_API const Vector2Base Zero; // Vector with all components equal 1 static FLAXENGINE_API const Vector2Base One; // Vector with all components equal 0.5 static FLAXENGINE_API const Vector2Base Half; // Vector X=1, Y=0 static FLAXENGINE_API const Vector2Base UnitX; // Vector X=0, Y=1 static FLAXENGINE_API const Vector2Base UnitY; // Vector with all components equal maximum value. static FLAXENGINE_API const Vector2Base Minimum; // Vector with all components equal minimum value. static FLAXENGINE_API const Vector2Base Maximum; public: /// /// Empty constructor. /// Vector2Base() = default; FORCE_INLINE Vector2Base(T xy) : X(xy) , Y(xy) { } FORCE_INLINE explicit Vector2Base(const T* xy) : X(xy[0]) , Y(xy[1]) { } FORCE_INLINE Vector2Base(T x, T y) : X(x) , Y(y) { } template>::Value>::Type...> FORCE_INLINE Vector2Base(const Vector2Base& xy) : X((T)xy.X) , Y((T)xy.Y) { } FLAXENGINE_API explicit Vector2Base(const Int3& xy); FLAXENGINE_API explicit Vector2Base(const Int4& xy); FLAXENGINE_API explicit Vector2Base(const Float3& xy); FLAXENGINE_API explicit Vector2Base(const Float4& xy); FLAXENGINE_API explicit Vector2Base(const Double3& xy); FLAXENGINE_API explicit Vector2Base(const Double4& xy); FLAXENGINE_API explicit Vector2Base(const Color& color); public: FLAXENGINE_API String ToString() const; public: // Gets a value indicting whether this instance is normalized. bool IsNormalized() const { return Math::IsOne(X * X + Y * Y); } // Gets a value indicting whether this vector is zero. bool IsZero() const { return Math::IsZero(X) && Math::IsZero(Y); } // Gets a value indicting whether any vector component is zero. bool IsAnyZero() const { return Math::IsZero(X) || Math::IsZero(Y); } // Gets a value indicting whether this vector is zero. bool IsOne() const { return Math::IsOne(X) && Math::IsOne(Y); } // Calculates the length of the vector. T Length() const { return Math::Sqrt(X * X + Y * Y); } // Calculates the squared length of the vector. T LengthSquared() const { return X * X + Y * Y; } // Calculates inverted length of the vector (1 / length). T InvLength() const { return 1.0f / Length(); } /// /// Returns the average arithmetic of all the components. /// T AverageArithmetic() const { return (X + Y) * 0.5f; } /// /// Gets the sum of all vector components values. /// T SumValues() const { return X + Y; } /// /// Gets the multiplication result of all vector components values. /// T MulValues() const { return X * Y; } /// /// Returns the minimum value of all the components. /// T MinValue() const { return Math::Min(X, Y); } /// /// Returns the maximum value of all the components. /// T MaxValue() const { return Math::Max(X, Y); } /// /// Returns true if vector has one or more components is not a number (NaN). /// bool IsNaN() const { return isnan(X) || isnan(Y); } /// /// Returns true if vector has one or more components equal to +/- infinity. /// bool IsInfinity() const { return isinf(X) || isinf(Y); } /// /// Returns true if vector has one or more components equal to +/- infinity or NaN. /// bool IsNanOrInfinity() const { return IsInfinity() || IsNaN(); } /// /// Calculates a vector with values being absolute values of that vector. /// Vector2Base GetAbsolute() const { return Vector2Base(Math::Abs(X), Math::Abs(Y)); } /// /// Calculates a vector with values being opposite to values of that vector. /// Vector2Base GetNegative() const { return Vector2Base(-X, -Y); } /// /// Calculates a normalized vector that has length equal to 1. /// Vector2Base GetNormalized() const { Vector2Base result(X, Y); result.Normalize(); return result; } public: /// /// Performs vector normalization (scales vector up to unit length). /// void Normalize() { const T length = Math::Sqrt(X * X + Y * Y); if (length >= ZeroTolerance) { const T invLength = (T)1.0f / length; X *= invLength; Y *= invLength; } } public: Vector2Base operator+(const Vector2Base& b) const { return Vector2Base(X + b.X, Y + b.Y); } Vector2Base operator-(const Vector2Base& b) const { return Vector2Base(X - b.X, Y - b.Y); } Vector2Base operator*(const Vector2Base& b) const { return Vector2Base(X * b.X, Y * b.Y); } Vector2Base operator/(const Vector2Base& b) const { return Vector2Base(X / b.X, Y / b.Y); } Vector2Base operator-() const { return Vector2Base(-X, -Y); } Vector2Base operator+(T b) const { return Vector2Base(X + b, Y + b); } Vector2Base operator-(T b) const { return Vector2Base(X - b, Y - b); } Vector2Base operator*(T b) const { return Vector2Base(X * b, Y * b); } Vector2Base operator/(T b) const { return Vector2Base(X / b, Y / b); } Vector2Base operator+(typename TOtherFloat::Type a) const { T b = (T)a; return Vector2Base(X + b, Y + b); } Vector2Base operator-(typename TOtherFloat::Type a) const { T b = (T)a; return Vector2Base(X - b, Y - b); } Vector2Base operator*(typename TOtherFloat::Type a) const { T b = (T)a; return Vector2Base(X * b, Y * b); } Vector2Base operator/(typename TOtherFloat::Type a) const { T b = (T)a; return Vector2Base(X / b, Y / b); } Vector2Base& operator+=(const Vector2Base& b) { X += b.X; Y += b.Y; return *this; } Vector2Base& operator-=(const Vector2Base& b) { X -= b.X; Y -= b.Y; return *this; } Vector2Base& operator*=(const Vector2Base& b) { X *= b.X; Y *= b.Y; return *this; } Vector2Base& operator/=(const Vector2Base& b) { X /= b.X; Y /= b.Y; return *this; } Vector2Base& operator+=(T b) { X += b; Y += b; return *this; } Vector2Base& operator-=(T b) { X -= b; Y -= b; return *this; } Vector2Base& operator*=(T b) { X *= b; Y *= b; return *this; } Vector2Base& operator/=(T b) { X /= b; Y /= b; return *this; } bool operator==(const Vector2Base& b) const { return X == b.X && Y == b.Y; } bool operator!=(const Vector2Base& b) const { return X != b.X || Y != b.Y; } bool operator>(const Vector2Base& b) const { return X > b.X && Y > b.Y; } bool operator>=(const Vector2Base& b) const { return X >= b.X && Y >= b.Y; } bool operator<(const Vector2Base& b) const { return X < b.X && Y < b.Y; } bool operator<=(const Vector2Base& b) const { return X <= b.X && Y <= b.Y; } public: static bool NearEqual(const Vector2Base& a, const Vector2Base& b) { return Math::NearEqual(a.X, b.X) && Math::NearEqual(a.Y, b.Y); } static bool NearEqual(const Vector2Base& a, const Vector2Base& b, T epsilon) { return Math::NearEqual(a.X, b.X, epsilon) && Math::NearEqual(a.Y, b.Y, epsilon); } public: static T Dot(const Vector2Base& a, const Vector2Base& b) { return a.X * b.X + a.Y * b.Y; } static T Cross(const Vector2Base& a, const Vector2Base& b) { return a.X * b.Y - a.Y * b.X; } static void Add(const Vector2Base& a, const Vector2Base& b, Vector2Base& result) { result = Vector2Base(a.X + b.X, a.Y + b.Y); } static void Subtract(const Vector2Base& a, const Vector2Base& b, Vector2Base& result) { result = Vector2Base(a.X - b.X, a.Y - b.Y); } static void Multiply(const Vector2Base& a, const Vector2Base& b, Vector2Base& result) { result = Vector2Base(a.X * b.X, a.Y * b.Y); } static void Divide(const Vector2Base& a, const Vector2Base& b, Vector2Base& result) { result = Vector2Base(a.X / b.X, a.Y / b.Y); } static void Min(const Vector2Base& a, const Vector2Base& b, Vector2Base& result) { result = Vector2Base(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y); } static void Max(const Vector2Base& a, const Vector2Base& b, Vector2Base& result) { result = Vector2Base(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y); } public: static Vector2Base Min(const Vector2Base& a, const Vector2Base& b) { return Vector2Base(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y); } static Vector2Base Max(const Vector2Base& a, const Vector2Base& b) { return Vector2Base(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y); } static Vector2Base Mod(const Vector2Base& a, const Vector2Base& b) { return Vector2Base(Math::Mod(a.X, b.X), Math::Mod(a.Y, b.Y)); } static Vector2Base Floor(const Vector2Base& v) { return Vector2Base(Math::Floor(v.X), Math::Floor(v.Y)); } static Vector2Base Frac(const Vector2Base& v) { return Vector2Base(v.X - (int32)v.X, v.Y - (int32)v.Y); } static Vector2Base Round(const Vector2Base& v) { return Vector2Base(Math::Round(v.X), Math::Round(v.Y)); } static Vector2Base Ceil(const Vector2Base& v) { return Vector2Base(Math::Ceil(v.X), Math::Ceil(v.Y)); } static Vector2Base Abs(const Vector2Base& v) { return Vector2Base(Math::Abs(v.X), Math::Abs(v.Y)); } public: // Clamp vector values within given range // @param v Vector to clamp // @param min Minimum value // @param max Maximum value // @returns Clamped vector static Vector2Base Clamp(const Vector2Base& v, const Vector2Base& min, const Vector2Base& max) { Vector2Base result; Clamp(v, min, max, result); return result; } // Restricts a value to be within a specified range // @param v The value to clamp // @param min The minimum value, // @param max The maximum value // @param result When the method completes, contains the clamped value static void Clamp(const Vector2Base& v, const Vector2Base& min, const Vector2Base& max, Vector2Base& result) { result = Vector2Base(Math::Clamp(v.X, min.X, max.X), Math::Clamp(v.Y, min.Y, max.Y)); } // Calculates distance between two points in 2D // @param a 1st point // @param b 2nd point // @returns Distance static T Distance(const Vector2Base& a, const Vector2Base& b) { const T x = a.X - b.X; const T y = a.Y - b.Y; return Math::Sqrt(x * x + y * y); } // Calculates the squared distance between two points in 2D // @param a 1st point // @param b 2nd point // @returns Distance static T DistanceSquared(const Vector2Base& a, const Vector2Base& b) { const T x = a.X - b.X; const T y = a.Y - b.Y; return x * x + y * y; } // Performs vector normalization (scales vector up to unit length). static Vector2Base Normalize(const Vector2Base& v) { Vector2Base r = v; const T length = Math::Sqrt(r.X * r.X + r.Y * r.Y); if (length >= ZeroTolerance) { const T inv = (T)1.0f / length; r.X *= inv; r.Y *= inv; } return r; } // Performs a linear interpolation between two vectors // @param start Start vector // @param end End vector // @param amount Value between 0 and 1 indicating the weight of end // @param result When the method completes, contains the linear interpolation of the two vectors static void Lerp(const Vector2Base& start, const Vector2Base& end, T amount, Vector2Base& result) { result.X = Math::Lerp(start.X, end.X, amount); result.Y = Math::Lerp(start.Y, end.Y, amount); } // // Performs a linear interpolation between two vectors. // // @param start Start vector, // @param end End vector, // @param amount Value between 0 and 1 indicating the weight of @paramref end"/>, // @returns The linear interpolation of the two vectors static Vector2Base Lerp(const Vector2Base& start, const Vector2Base& end, T amount) { Vector2Base result; Lerp(start, end, amount, result); return result; } public: /// /// Calculates the area of the triangle. /// /// The first triangle vertex. /// The second triangle vertex. /// The third triangle vertex. /// The triangle area. FLAXENGINE_API static T TriangleArea(const Vector2Base& v0, const Vector2Base& v1, const Vector2Base& v2); /// /// Calculates the angle (in radians) between from and to. This is always the smallest value. /// /// The first vector. /// The second vector. /// The angle (in radians). FLAXENGINE_API static T Angle(const Vector2Base& from, const Vector2Base& to); }; template inline Vector2Base operator+(T a, const Vector2Base& b) { return b + a; } template inline Vector2Base operator-(T a, const Vector2Base& b) { return Vector2Base(a) - b; } template inline Vector2Base operator*(T a, const Vector2Base& b) { return b * a; } template inline Vector2Base operator/(T a, const Vector2Base& b) { return Vector2Base(a) / b; } template inline Vector2Base operator+(typename TOtherFloat::Type a, const Vector2Base& b) { return b + a; } template inline Vector2Base operator-(typename TOtherFloat::Type a, const Vector2Base& b) { return Vector2Base(a) - b; } template inline Vector2Base operator*(typename TOtherFloat::Type a, const Vector2Base& b) { return b * a; } template inline Vector2Base operator/(typename TOtherFloat::Type a, const Vector2Base& b) { return Vector2Base(a) / b; } template inline uint32 GetHash(const Vector2Base& key) { return (*(uint32*)&key.X * 397) ^ *(uint32*)&key.Y; } namespace Math { template FORCE_INLINE static bool NearEqual(const Vector2Base& a, const Vector2Base& b) { return Vector2Base::NearEqual(a, b); } } template<> struct TIsPODType { enum { Value = true }; }; DEFINE_DEFAULT_FORMATTING(Float2, "X:{0} Y:{1}", v.X, v.Y); template<> struct TIsPODType { enum { Value = true }; }; DEFINE_DEFAULT_FORMATTING(Double2, "X:{0} Y:{1}", v.X, v.Y); template<> struct TIsPODType { enum { Value = true }; }; DEFINE_DEFAULT_FORMATTING(Int2, "X:{0} Y:{1}", v.X, v.Y); #if !defined(_MSC_VER) || defined(__clang__) // Forward specializations for Clang template<> FLAXENGINE_API const Float2 Float2::Zero; template<> FLAXENGINE_API const Float2 Float2::One; template<> FLAXENGINE_API const Float2 Float2::UnitX; template<> FLAXENGINE_API const Float2 Float2::UnitY; template<> FLAXENGINE_API const Float2 Float2::Minimum; template<> FLAXENGINE_API const Float2 Float2::Maximum; template<> FLAXENGINE_API ScriptingTypeInitializer Float2::TypeInitializer; template<> FLAXENGINE_API const Double2 Double2::Zero; template<> FLAXENGINE_API const Double2 Double2::One; template<> FLAXENGINE_API const Double2 Double2::UnitX; template<> FLAXENGINE_API const Double2 Double2::UnitY; template<> FLAXENGINE_API const Double2 Double2::Minimum; template<> FLAXENGINE_API const Double2 Double2::Maximum; template<> FLAXENGINE_API ScriptingTypeInitializer Double2::TypeInitializer; template<> FLAXENGINE_API const Int2 Int2::Zero; template<> FLAXENGINE_API const Int2 Int2::One; template<> FLAXENGINE_API const Int2 Int2::UnitX; template<> FLAXENGINE_API const Int2 Int2::UnitY; template<> FLAXENGINE_API const Int2 Int2::Minimum; template<> FLAXENGINE_API const Int2 Int2::Maximum; template<> FLAXENGINE_API ScriptingTypeInitializer Int2::TypeInitializer; #endif