#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_VECTOR4_H #define NUCLEX_GEOMETRY_VECTOR4_H #include "Nuclex/Geometry/Config.h" #include "Nuclex/Geometry/Math.h" #include "Nuclex/Geometry/Axis4.h" #include #include namespace Nuclex { namespace Geometry { // ------------------------------------------------------------------------------------------- // /// A direction in 4D space template struct Vector4 { /// A vector whose components have been initialized to zero public: static const Vector4 Zero; /// A vector whose components have been initialized to one public: static const Vector4 One; /// Constructs new uninitialized vector public: Vector4() {} /// Initializes a new vector pointing in the specified direciton /// X offset the vector is pointing at /// Y offset the vector is pointing at /// Z offset the vector is pointing at /// W offset the vector is pointing at public: Vector4(TScalar x, TScalar y, TScalar z, TScalar w) : X(x), Y(y), Z(z), W(w) {} /// 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) + (this->Z * this->Z) + (this->W * this->W); } /// 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; this->Z = 0; this->W = 0; } else { length = Math::SquareRoot(length); this->X /= length; this->Y /= length; this->Z /= length; this->W /= length; } } /// Returns a normalized copy of the vector /// A normalized copy of the vector public: Vector4 GetNormalized() const { TScalar length = GetSquaredLength(); if(length == 0) { return Vector4(0, 0, 0, 0); } else { length = Math::SquareRoot(length); return Vector4(this->X / length, this->Y / length, this->Z / length, this->W / length); } } /// 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 Vector4 &first, const Vector4 &second) { first.EnforceNormalization(); second.EnforceNormalization(); return Math::ArcCosine(Vector4::Dot(first, second)); } /// Calculates the cross product between three vectors /// First vector the cross product is calculated with /// Second vector the cross product is calculated with /// Third vector the cross product is calculated with /// The cross product of the three vectors public: static Vector4 Cross( const Vector4 first, const Vector4 &second, const Vector4 &third ) { // TODO: Implement cross product for 4D vectors. } /// Calculates the dot product between two vectors /// First vector of which the dot product will be calculated /// Second vector of which the dot product will be calculated /// The dot product of the two vectors public: static TScalar Dot(const Vector4 &first, const Vector4 &second) { return (first.X * second.X) + (first.Y * second.Y) + (first.Z * second.Z) + (first.W * second.W); } /// 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 Vector4 Abs(const Vector4 &vector) { return Vector4( Math::Abs(vector.X), Math::Abs(vector.Y), Math::Abs(vector.Z), Math::Abs(vector.W) ); } /// 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 Axis4::X: { return this->X; } case Axis4::Y: { return this->Y; } case Axis4::Z: { return this->Z; } case Axis4::W: { return this->W; } default: { throw std::out_of_range("Invalid Vector4 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 Axis4::X: { return this->X; } case Axis4::Y: { return this->Y; } case Axis4::Z: { return this->Z; } case Axis4::W: { return this->W; } default: { throw std::out_of_range("Invalid Vector4 component index"); } } } #if 0 /// Returns an orthogonal up vector for the vector /// A vector that is orthogonal to this vector and points up public: Vector4 GetOrthogonalUp() const { // TODO: GetOrthogonalUp() is not correct for 4D vectors switch(GetLongestComponentIndex()) { case Axis4::X: { if(this->X > 0) { return Vector4(-this->Z, 0, this->X); } else { return Vector4(this->Z, 0, -this->X); } } case Axis4::Y: { if(this->Y > 0) { return Vector4(0, -this->Z, this->Y); } else { return Vector4(0, this->Z, -this->Y); } } case Axis4::Z: { if(this->X > 0) { return Vector4(-this->Z, 0, this->X); } else { return Vector4(this->Z, 0, -this->X); } } default: { throw std::logic_error("GetLongestComponentIndex() malfunctioned"); } } } #endif /// Negates the components of the vector /// The negated vector public: Vector4 operator -() { return Vector4(-this->X, -this->Y, -this->Z, -this->W); } /// Subtracts another vector from this one and returns the result /// Other vector that will be subtracted /// The vector resulting from the subtraction public: Vector4 operator -(const Vector4 &other) const { return Vector4( this->X - other.X, this->Y - other.Y, this->Z - other.Z, this->W - other.W ); } /// Subtracts another vector from this one /// Other vector that will be subtracted /// This vector after the subtraction public: Vector4 &operator -=(const Vector4 &other) { this->X -= other.X; this->Y -= other.Y; this->Z -= other.Z; this->W -= other.W; 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: Vector4 operator +(const Vector4 &other) const { return Vector4( this->X + other.X, this->Y + other.Y, this->Z + other.Z, this->W + other.W ); } /// Adds another vector to this one /// Other vector that will be added /// This vector after the addition public: Vector4 &operator +=(const Vector4 &other) { this->X += other.X; this->Y += other.Y; this->Z += other.Z; this->W += other.W; 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: Vector4 operator *(const Vector4 &other) const { return Vector4( this->X * other.X, this->Y * other.Y, this->Z * other.Z, this->W * other.W ); } /// Multiplies this vector with another vector /// Other vector with which to multiply /// This vector after the multiplication public: Vector4 &operator *=(const Vector4 &other) { this->X *= other.X; this->Y *= other.Y; this->Z *= other.Z; this->W *= other.W; 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: Vector4 operator /(const Vector4 &other) const { return Vector4( this->X / other.X, this->Y / other.Y, this->Z / other.Z, this->W / other.W ); } /// Divides this vector by another vector /// Other vector by which to divide /// This vector after the division public: Vector4 &operator /=(const Vector4 &other) { this->X /= other.X; this->Y /= other.Y; this->Z /= other.Z; this->W /= other.W; 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: Vector4 operator *(TScalar factor) const { return Vector4(this->X * factor, this->Y * factor, this->Z * factor, this->W * factor); } /// Multiplies this vector with a factor /// Factor with which to multiply /// This vector after the multiplication public: Vector4 &operator *=(TScalar factor) { this->X *= factor; this->Y *= factor; this->Z *= factor; this->W *= 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: Vector4 operator /(TScalar factor) const { return Vector4(this->X / factor, this->Y / factor, this->Z / factor, this->W / factor); } /// Multiplies this vector by a divisor /// Divisor by which to divide /// This vector after the division public: Vector4 &operator /=(TScalar divisor) { this->X /= divisor; this->Y /= divisor; this->Z /= divisor; this->W /= 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 { TScalar absX = Math::Abs(this->X); TScalar absY = Math::Abs(this->Y); TScalar absZ = Math::Abs(this->Z); if(absY > absX) { if(absZ > absY) { if(Math::Abs(this->W) > absZ) { return Axis4::W; } else { return Axis4::Z; } } else { if(Math::Abs(this->W) > absY) { return Axis4::W; } else { return Axis4::Y; } } } else { if(absZ > absX) { if(Math::Abs(this->W) > absZ) { return Axis4::W; } else { return Axis4::Z; } } else { if(Math::Abs(this->W) > absX) { return Axis4::W; } else { return Axis4::X; } } } } /// 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; /// Offset of the vector's direction on the Z axis public: TScalar Z; /// Offset of the vector's direction on the W axis public: TScalar W; }; // ------------------------------------------------------------------------------------------- // /// 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 Vector4 operator *(TScalar factor, const Vector4 &vector) { return Vector4( factor * vector.X, factor * vector.Y, factor * vector.Z, factor * vector.W ); } // ------------------------------------------------------------------------------------------- // template const Vector4 Vector4::Zero(0, 0, 0, 0); // ------------------------------------------------------------------------------------------- // template const Vector4 Vector4::One(1, 1, 1, 1); // ------------------------------------------------------------------------------------------- // }} // namespace Nuclex::Geometry #endif // NUCLEX_GEOMETRY_VECTOR4_H