#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2015 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_GEOMETRY_VECTOR2_H #define NUCLEX_GEOMETRY_VECTOR2_H #include "Nuclex/Geometry/Config.h" #include "Nuclex/Geometry/Math.h" #include "Nuclex/Geometry/Axis2.h" #include #include namespace Nuclex { namespace Geometry { // ------------------------------------------------------------------------------------------- // /// A direction in 2D space template struct Vector2 { /// A vector whose components have been initialized to zero public: static const Vector2 Zero; /// A vector whose components have been initialized to one public: static const Vector2 One; /// A vector pointing towards the negative X axis public: static const Vector2 Left; /// A vector pointing towards the positive X axis public: static const Vector2 Right; /// A vector pointing towards the negative Y axis public: static const Vector2 Down; /// A vector pointing towards the positive Y axis public: static const Vector2 Up; /// Constructs new uninitialized vector public: Vector2() {} /// Initializes a new vector pointing in the specified direction /// X offset the vector is pointing at /// Y offset the vector is pointing at public: Vector2(TScalar x, TScalar y) : X(x), Y(y) {} /// Creates a unit vector pointing in the direction specified by the angle /// Angle the vector will be pointing towards /// A unit vector pointing towards the specified angle public: static Vector2 FromAngle(TScalar angleInRadians) { return Vector2( Math::Cosine(angleInRadians), Math::Sine(angleInRadians) ); } /// Returns the squared length of the vector /// The squared length of the vector public: TScalar GetSquaredLength() const { return (this->X * this->X) + (this->Y * this->Y); } /// Returns the length (magnitude) of the vector /// The length (magnitude) of the vector public: TScalar GetLength() const { return Math::SquareRoot(GetSquaredLength()); } /// Normalizes the vector, ensuring it has a length of 1.0 public: void Normalize() { TScalar length = GetSquaredLength(); if(length == 0) { this->X = 0; this->Y = 0; } else { length = Math::SquareRoot(length); this->X /= length; this->Y /= length; } } /// Returns a normalized copy of the vector /// Vector of which a normalized copy will be created /// A normalized copy of the vector public: static Vector2 Normalize(const Vector2 &vector) { TScalar length = vector.GetSquaredLength(); if(length == 0) { return Vector2::Zero; } else { length = Math::SquareRoot(length); return Vector2(vector.X / length, vector.Y / length); } } /// Initializes a normalized vector /// X coordinate that will be normalized to build the vector /// Y coordinate that will be normalized to build the vector /// A vector initialized with the normalized coordinates public: static Vector2 Normalize(TScalar x, TScalar y) { TScalar length = (x * x) + (y * y); if(length == 0) { return Vector2::Zero; } else { length = Math::SquareRoot(length); return Vector2(x / length, y / length); } } /// Determines the absolute angle of the vector /// The vector's absolute angle in radians public: TScalar GetAngle() const { EnforceNormalization(); return Math::ArcTangent(this->X, this->Y); } /// Determines the absolute (unsigned) angle between two directions /// Vector from which the angle will be calculated /// Vector to which the angle will be calculated /// The absolute angle between the two directions public: static TScalar AngleBetween(const Vector2 &first, const Vector2 &second) { first.EnforceNormalization(); second.EnforceNormalization(); return Math::ArcCosine(Vector2::Dot(first, second)); } /// Calculates the dot product between two vectors /// First vector from which the dot product will be calculated /// Second vector from which the dot product will be calculated /// The dot product of the two vectors public: static TScalar Dot(const Vector2 &first, const Vector2 &second) { return (first.X * second.X) + (first.Y * second.Y); } /// Returns the absolute product of the input vector /// Vector of which the absolute product will be returned /// The absolute product of the vector public: static Vector2 Abs(const Vector2 &vector) { return Vector2(Math::Abs(vector.X), Math::Abs(vector.Y)); } /// Accesses one of the vector's components by index /// Index of the component that will be accessed /// The component at the specified index public: TScalar &operator[](std::size_t index) { switch(index) { case Axis2::X: { return this->X; } case Axis2::Y: { return this->Y; } default: { throw std::out_of_range("Invalid Vector2 component index"); } } } /// Accesses one of the vector's components by index /// Index of the component that will be accessed /// The component at the specified index public: const TScalar &operator[](std::size_t index) const { switch(index) { case Axis2::X: { return this->X; } case Axis2::Y: { return this->Y; } default: { throw std::out_of_range("Invalid Vector2 component index"); } } } /// Returns an orthogonal up vector for the vector /// A vector that is orthogonal to this vector and points up /// /// This method uses a simple method to obtain a right-angled vector which prefers /// the up axis (Y+) in all cases. /// public: Vector2 GetOrthogonalUp() const { switch(GetLongestComponentIndex()) { case Axis2::X: { if(this->X > 0) { return Vector2(-this->Y, this->X); } else { return Vector2(this->Y, -this->X); } } case Axis2::Y: { if(this->Y > 0) { return Vector2(this->Y, -this->X); } else { return Vector2(-this->Y, this->X); } } default: { throw std::logic_error("GetLongestComponentIndex() malfunctioned"); } } } /// Rotates the vector by the specified angle /// Angle by which the vector will be rotated public: void Rotate(TScalar angleInRadians) { TScalar sine = Math::Sine(angleInRadians); TScalar cosine = Math::Cosine(angleInRadians); TScalar temp = this->X * sine + this->Y * cosine; this->X = this->X * cosine - this->Y * sine; this->Y = temp; } /// Returns a rotated copy of the vector /// Angle by which the vector copy will be rotated /// The rotated copy of the vector public: Vector2 GetRotated(TScalar angleInRadians) const { TScalar sine = Math::Sine(angleInRadians); TScalar cosine = Math::Cosine(angleInRadians); return Vector2( this->X * cosine - this->Y * sine, this->X * sine + this->Y * cosine ); } /// Negates the components of the vector /// The negated vector public: Vector2 operator -() { return Vector2(-this->X, -this->Y); } /// Subtracts another vector from this one and returns the result /// Other vector that will be subtracted /// The vector resulting from the subtraction public: Vector2 operator -(const Vector2 &other) const { return Vector2(this->X - other.X, this->Y - other.Y); } /// Subtracts another vector from this one /// Other vector that will be subtracted /// This vector after the subtraction public: Vector2 &operator -=(const Vector2 &other) { this->X -= other.X; this->Y -= other.Y; return *this; } /// Adds another vector to this one and returns the result /// Other vector that will be added /// The vector resulting from the addition public: Vector2 operator +(const Vector2 &other) const { return Vector2(this->X + other.X, this->Y + other.Y); } /// Adds another vector to this one /// Other vector that will be added /// This vector after the addition public: Vector2 &operator +=(const Vector2 &other) { this->X += other.X; this->Y += other.Y; return *this; } /// Multiplies this vector with another vector and returns the result /// Other vector this vector will be multiplied with /// The vector resulting from the multiplication public: Vector2 operator *(const Vector2 &other) const { return Vector2(this->X * other.X, this->Y * other.Y); } /// Multiplies this vector with another vector /// Other vector with which to multiply /// This vector after the multiplication public: Vector2 &operator *=(const Vector2 &other) { this->X *= other.X; this->Y *= other.Y; return *this; } /// Divides this vector by another vector and returns the result /// Other vector this vector will be divided by /// The vector resulting from the division public: Vector2 operator /(const Vector2 &other) const { return Vector2(this->X / other.X, this->Y / other.Y); } /// Divides this vector by another vector /// Other vector by which to divide /// This vector after the division public: Vector2 &operator /=(const Vector2 &other) { this->X /= other.X; this->Y /= other.Y; return *this; } /// Multiplies this vector by a factor and returns the result /// Factor by which the vector will be multiplied /// The result of the vector multiplication public: Vector2 operator *(TScalar factor) const { return Vector2(this->X * factor, this->Y * factor); } /// Multiplies this vector with a factor /// Factor with which to multiply /// This vector after the multiplication public: Vector2 &operator *=(TScalar factor) { this->X *= factor; this->Y *= factor; return *this; } /// Divides this vector through a divisor and returns the result /// Divisor through which the vector will be divided /// The result of the vector division public: Vector2 operator /(TScalar factor) const { return Vector2(this->X / factor, this->Y / factor); } /// Multiplies this vector by a divisor /// Divisor by which to divide /// This vector after the division public: Vector2 &operator /=(TScalar divisor) { this->X /= divisor; this->Y /= divisor; return *this; } /// Returns the index of the longest component in the vector /// The index of the longest component stored in the vector public: std::size_t GetLongestComponentIndex() const { if(Math::Abs(this->X) > Math::Abs(this->Y)) { return Axis2::X; } else { return Axis2::Y; } } /// Checks whether the vector is normalized /// True if the vector is normalized, false otherwise public: bool IsNormalized() const { return (Math::Abs(GetSquaredLength() - 1) * 1000000 < 1); } /// Fails an assertion if the vector is not normalized public: void EnforceNormalization() const { using namespace std; assert(IsNormalized() && "Vector2 has to be normalized"); } /// Offset of the vector's direction on the X axis public: TScalar X; /// Offset of the vector's direction on the Y axis public: TScalar Y; }; // ------------------------------------------------------------------------------------------- // /// Multiplies this vector by a factor and returns the result /// Factor by which the vector will be multiplied /// The result of the vector multiplication template Vector2 operator *(TScalar factor, const Vector2 &vector) { return Vector2(factor * vector.X, factor * vector.Y); } // ------------------------------------------------------------------------------------------- // template const Vector2 Vector2::Zero(0, 0); // ------------------------------------------------------------------------------------------- // template const Vector2 Vector2::One(1, 1); // ------------------------------------------------------------------------------------------- // template const Vector2 Vector2::Left(-1, 0); // ------------------------------------------------------------------------------------------- // template const Vector2 Vector2::Right(1, 0); // ------------------------------------------------------------------------------------------- // template const Vector2 Vector2::Down(0, -1); // ------------------------------------------------------------------------------------------- // template const Vector2 Vector2::Up(0, 1); // ------------------------------------------------------------------------------------------- // }} // namespace Nuclex::Geometry #endif // NUCLEX_GEOMETRY_VECTOR2_H