#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_VECTOR3_H #define NUCLEX_GEOMETRY_VECTOR3_H #include "Nuclex/Geometry/Config.h" #include "Nuclex/Geometry/Math.h" #include "Nuclex/Geometry/Axis3.h" #include #include namespace Nuclex { namespace Geometry { // ------------------------------------------------------------------------------------------- // /// A direction in 3D space template struct Vector3 { /// A vector whose components have been initialized to zero public: static const Vector3 Zero; /// A vector whose components have been initialized to one public: static const Vector3 One; /// A vector pointing towards the negative X axis public: static const Vector3 Left; /// A vector pointing towards the positive X axis public: static const Vector3 Right; /// A vector pointing towards the negative Y axis public: static const Vector3 Backward; /// A vector pointing towards the positive Y axis public: static const Vector3 Forward; /// A vector pointing towards the negative Z axis public: static const Vector3 Down; /// A vector pointing towards the positive Z axis public: static const Vector3 Up; /// Constructs new uninitialized vector public: Vector3() {} /// 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 public: Vector3(TScalar x, TScalar y, TScalar z) : X(x), Y(y), Z(z) {} /// 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); } /// 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; } else { length = Math::SquareRoot(length); this->X /= length; this->Y /= length; this->Z /= length; } } /// Builds a normalized copy of the vector /// A normalized copy of the vector public: static Vector3 Normalize(const Vector3 &vector) { TScalar length = vector.GetSquaredLength(); if(length == 0) { return Vector3(0, 0, 0); } else { length = Math::SquareRoot(length); return Vector3(vector.X / length, vector.Y / length, vector.Z / 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 Vector3 &first, const Vector3 &second) { first.EnforceNormalization(); second.EnforceNormalization(); return Math::ArcCosine(Vector3::Dot(first, second)); } /// Calculates the cross product between this vector and another one /// First vector the cross product will be calculated for /// Second vector the cross product will be calculated for /// The cross product of this vector with the other vector public: static Vector3 Cross(const Vector3 first, const Vector3 &second) { return Vector3( (first.Y * second.Z) - (first.Z * second.Y), (first.Z * second.X) - (first.X * second.Z), (first.X * second.Y) - (first.Y * second.X) ); } /// 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 Vector3 &first, const Vector3 &second) { return (first.X * second.X) + (first.Y * second.Y) + (first.Z * second.Z); } /// 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 Vector3 Abs(const Vector3 &vector) { return Vector3( Math::Abs(vector.X), Math::Abs(vector.Y), Math::Abs(vector.Z) ); } /// 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 Axis3::X: { return this->X; } case Axis3::Y: { return this->Y; } case Axis3::Z: { return this->Z; } default: { throw std::out_of_range("Invalid Vector3 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 Axis3::X: { return this->X; } case Axis3::Y: { return this->Y; } case Axis3::Z: { return this->Z; } default: { throw std::out_of_range("Invalid Vector3 component index"); } } } /// Returns an orthogonal up vector for the vector /// A vector that is orthogonal to this vector and points up public: Vector3 GetOrthogonalUp() const { switch(GetLongestComponentIndex()) { case Axis3::X: { if(this->X > 0) { return Vector3(-this->Z, 0, this->X); } else { return Vector3(this->Z, 0, -this->X); } } case Axis3::Y: { if(this->Y > 0) { return Vector3(0, -this->Z, this->Y); } else { return Vector3(0, this->Z, -this->Y); } } case Axis3::Z: { if(this->X > 0) { return Vector3(-this->Z, 0, this->X); } else { return Vector3(this->Z, 0, -this->X); } } default: { throw std::logic_error("GetLongestComponentIndex() malfunctioned"); } } } /// Negates the components of the vector /// The negated vector public: Vector3 operator -() { return Vector3(-this->X, -this->Y, -this->Z); } /// Subtracts another vector from this one and returns the result /// Other vector that will be subtracted /// The vector resulting from the subtraction public: Vector3 operator -(const Vector3 &other) const { return Vector3(this->X - other.X, this->Y - other.Y, this->Z - other.Z); } /// Subtracts another vector from this one /// Other vector that will be subtracted /// This vector after the subtraction public: Vector3 &operator -=(const Vector3 &other) { this->X -= other.X; this->Y -= other.Y; this->Z -= other.Z; 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: Vector3 operator +(const Vector3 &other) const { return Vector3(this->X + other.X, this->Y + other.Y, this->Z + other.Z); } /// Adds another vector to this one /// Other vector that will be added /// This vector after the addition public: Vector3 &operator +=(const Vector3 &other) { this->X += other.X; this->Y += other.Y; this->Z += other.Z; 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: Vector3 operator *(const Vector3 &other) const { return Vector3(this->X * other.X, this->Y * other.Y, this->Z * other.Z); } /// Multiplies this vector with another vector /// Other vector with which to multiply /// This vector after the multiplication public: Vector3 &operator *=(const Vector3 &other) { this->X *= other.X; this->Y *= other.Y; this->Z *= other.Z; 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: Vector3 operator /(const Vector3 &other) const { return Vector3(this->X / other.X, this->Y / other.Y, this->Z / other.Z); } /// Divides this vector by another vector /// Other vector by which to divide /// This vector after the division public: Vector3 &operator /=(const Vector3 &other) { this->X /= other.X; this->Y /= other.Y; this->Z /= other.Z; 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: Vector3 operator *(TScalar factor) const { return Vector3(this->X * factor, this->Y * factor, this->Z * factor); } /// Multiplies this vector with a factor /// Factor with which to multiply /// This vector after the multiplication public: Vector3 &operator *=(TScalar factor) { this->X *= factor; this->Y *= factor; this->Z *= 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: Vector3 operator /(TScalar factor) const { return Vector3(this->X / factor, this->Y / factor, this->Z / factor); } /// Multiplies this vector by a divisor /// Divisor by which to divide /// This vector after the division public: Vector3 &operator /=(TScalar divisor) { this->X /= divisor; this->Y /= divisor; this->Z /= 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); if(absY > absX) { if(Math::Abs(this->Z) > absY) { return Axis3::Z; } else { return Axis3::Y; } } else { if(Math::Abs(this->Z) > absX) { return Axis3::Z; } else { return Axis3::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() && "Vector3 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; }; // ------------------------------------------------------------------------------------------- // /// 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 Vector3 operator *(TScalar factor, const Vector3 &vector) { return Vector3(factor * vector.X, factor * vector.Y, factor * vector.Z); } // ------------------------------------------------------------------------------------------- // template const Vector3 Vector3::Zero(0, 0, 0); // ------------------------------------------------------------------------------------------- // template const Vector3 Vector3::One(1, 1, 1); // ------------------------------------------------------------------------------------------- // template const Vector3 Vector3::Left(-1, 0, 0); // ------------------------------------------------------------------------------------------- // template const Vector3 Vector3::Right(1, 0, 0); // ------------------------------------------------------------------------------------------- // template const Vector3 Vector3::Backward(0, -1, 0); // ------------------------------------------------------------------------------------------- // template const Vector3 Vector3::Forward(0, 1, 0); // ------------------------------------------------------------------------------------------- // template const Vector3 Vector3::Down(0, 0, -1); // ------------------------------------------------------------------------------------------- // template const Vector3 Vector3::Up(0, 0, 1); // ------------------------------------------------------------------------------------------- // }} // namespace Nuclex::Geometry #endif // NUCLEX_GEOMETRY_VECTOR3_H