#ifndef Magnum_Math_Vector_h #define Magnum_Math_Vector_h /* This file is part of Magnum. Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** @file * @brief Class Magnum::Math::Vector */ #include #include #include #include #include #include "Math/Angle.h" #include "Math/BoolVector.h" #include "Math/TypeTraits.h" #include "magnumVisibility.h" namespace Magnum { namespace Math { namespace Implementation { template struct VectorConverter; } /** @brief %Vector @tparam size %Vector size @tparam T Underlying data type See @ref matrix-vector for brief introduction. @configurationvalueref{Magnum::Math::Vector} */ template class Vector { static_assert(size != 0, "Vector cannot have zero elements"); template friend class Vector; public: typedef T Type; /**< @brief Underlying data type */ const static std::size_t Size = size; /**< @brief %Vector size */ /** * @brief %Vector from array * @return Reference to the data as if it was Vector, thus doesn't * perform any copying. * * @attention Use with caution, the function doesn't check whether the * array is long enough. */ constexpr static Vector& from(T* data) { return *reinterpret_cast*>(data); } /** @overload */ constexpr static const Vector& from(const T* data) { return *reinterpret_cast*>(data); } /** * @brief Dot product * * Returns `0` if two vectors are orthogonal, `1` if two *normalized* * vectors are parallel and `-1` if two *normalized* vectors are * antiparallel. @f[ * \boldsymbol a \cdot \boldsymbol b = \sum_{i=0}^{n-1} \boldsymbol a_i \boldsymbol b_i * @f] * @see dot() const, operator-(), Vector2::perpendicular() */ static T dot(const Vector& a, const Vector& b) { return (a*b).sum(); } /** * @brief Angle between normalized vectors * * Expects that both vectors are normalized. @f[ * \theta = acos \left( \frac{\boldsymbol a \cdot \boldsymbol b}{|\boldsymbol a| |\boldsymbol b|} \right) = acos (\boldsymbol a \cdot \boldsymbol b) * @f] * @see isNormalized(), Quaternion::angle(), Complex::angle() */ static Rad angle(const Vector& normalizedA, const Vector& normalizedB); /** * @brief Default constructor * * @f[ * \boldsymbol v = \boldsymbol 0 * @f] */ constexpr /*implicit*/ Vector(): _data() {} /** @todo Creating Vector from combination of vector and scalar types */ /** * @brief Construct vector from values * @param first First value * @param next Next values */ #ifdef DOXYGEN_GENERATING_OUTPUT template constexpr /*implicit*/ Vector(T first, U... next); #else template::type> constexpr /*implicit*/ Vector(T first, U... next): _data{first, next...} {} #endif /** @brief Construct vector with one value for all fields */ #ifdef DOXYGEN_GENERATING_OUTPUT constexpr explicit Vector(T value); #else #ifndef CORRADE_GCC46_COMPATIBILITY template::value && size != 1, T>::type> constexpr explicit Vector(U value): Vector(typename Implementation::GenerateSequence::Type(), value) {} #else template::value && size != 1, T>::type> explicit Vector(U value) { *this = Vector(typename Implementation::GenerateSequence::Type(), value); } #endif #endif /** * @brief Construct vector from another of different type * * Performs only default casting on the values, no rounding or * anything else. Example usage: * @code * Vector<4, Float> floatingPoint(1.3f, 2.7f, -15.0f, 7.0f); * Vector<4, Byte> integral(floatingPoint); * // integral == {1, 2, -15, 7} * @endcode */ #ifndef CORRADE_GCC46_COMPATIBILITY template constexpr explicit Vector(const Vector& other): Vector(typename Implementation::GenerateSequence::Type(), other) {} #else template explicit Vector(const Vector& other) { *this = Vector(typename Implementation::GenerateSequence::Type(), other); } #endif /** @brief Construct vector from external representation */ #ifndef CORRADE_GCC46_COMPATIBILITY template::from(std::declval()))> constexpr explicit Vector(const U& other): Vector(Implementation::VectorConverter::from(other)) {} #else template::from(std::declval()))> explicit Vector(const U& other) { *this = Implementation::VectorConverter::from(other); } #endif /** @brief Copy constructor */ constexpr Vector(const Vector&) = default; /** @brief Assignment operator */ Vector& operator=(const Vector&) = default; /** @brief Convert vector to external representation */ template::to(std::declval>()))> constexpr explicit operator U() const { /** @bug Why this is not constexpr under GCC 4.6? */ return Implementation::VectorConverter::to(*this); } /** * @brief Raw data * @return One-dimensional array of `size*size` length. * * @see operator[]() */ T* data() { return _data; } constexpr const T* data() const { return _data; } /**< @overload */ /** * @brief Value at given position * * @see data() */ T& operator[](std::size_t pos) { return _data[pos]; } constexpr T operator[](std::size_t pos) const { return _data[pos]; } /**< @overload */ /** @brief Equality comparison */ bool operator==(const Vector& other) const { for(std::size_t i = 0; i != size; ++i) if(!TypeTraits::equals(_data[i], other._data[i])) return false; return true; } /** @brief Non-equality comparison */ bool operator!=(const Vector& other) const { return !operator==(other); } /** @brief Component-wise less than */ BoolVector operator<(const Vector& other) const; /** @brief Component-wise less than or equal */ BoolVector operator<=(const Vector& other) const; /** @brief Component-wise greater than or equal */ BoolVector operator>=(const Vector& other) const; /** @brief Component-wise greater than */ BoolVector operator>(const Vector& other) const; /** * @brief Whether the vector is zero * * @f[ * |\boldsymbol a \cdot \boldsymbol a - 0| < \epsilon^2 \cong \epsilon * @f] * @see dot(), normalized() */ bool isZero() const { return Implementation::isZeroSquared(dot()); } /** * @brief Whether the vector is normalized * * The vector is normalized if it has unit length: @f[ * |\boldsymbol a \cdot \boldsymbol a - 1| < 2 \epsilon + \epsilon^2 \cong 2 \epsilon * @f] * @see dot(), normalized() */ bool isNormalized() const { return Implementation::isNormalizedSquared(dot()); } /** * @brief Negated vector * * @f[ * \boldsymbol b_i = -\boldsymbol a_i * @f] * @see Vector2::perpendicular() */ Vector operator-() const; /** * @brief Add and assign vector * * The computation is done in-place. @f[ * \boldsymbol a_i = \boldsymbol a_i + \boldsymbol b_i * @f] */ Vector& operator+=(const Vector& other) { for(std::size_t i = 0; i != size; ++i) _data[i] += other._data[i]; return *this; } /** * @brief Add vector * * @see operator+=(), sum() */ Vector operator+(const Vector& other) const { return Vector(*this) += other; } /** * @brief Subtract and assign vector * * The computation is done in-place. @f[ * \boldsymbol a_i = \boldsymbol a_i - \boldsymbol b_i * @f] */ Vector& operator-=(const Vector& other) { for(std::size_t i = 0; i != size; ++i) _data[i] -= other._data[i]; return *this; } /** * @brief Subtract vector * * @see operator-=() */ Vector operator-(const Vector& other) const { return Vector(*this) -= other; } /** * @brief Multiply vector with number and assign * * The computation is done in-place. @f[ * \boldsymbol a_i = b \boldsymbol a_i * @f] */ #ifdef DOXYGEN_GENERATING_OUTPUT template Vector& operator*=(U number) { #else template typename std::enable_if::value, Vector&>::type operator*=(U number) { #endif for(std::size_t i = 0; i != size; ++i) _data[i] *= number; return *this; } /** * @brief Multiply vector with number * * @see operator*=(U), operator*(U, const Vector&) */ #ifdef DOXYGEN_GENERATING_OUTPUT template Vector operator*(U number) const { #else template typename std::enable_if::value, Vector>::type operator*(U number) const { #endif return Vector(*this) *= number; } /** * @brief Divide vector with number and assign * * The computation is done in-place. @f[ * \boldsymbol a_i = \frac{\boldsymbol a_i} b * @f] */ #ifdef DOXYGEN_GENERATING_OUTPUT template Vector& operator/=(U number) { #else template typename std::enable_if::value, Vector&>::type operator/=(U number) { #endif for(std::size_t i = 0; i != size; ++i) _data[i] /= number; return *this; } /** * @brief Divide vector with number * * @see operator/=(), operator/(U, const Vector&) */ #ifdef DOXYGEN_GENERATING_OUTPUT template Vector operator/(U number) const { #else template typename std::enable_if::value, Vector>::type operator/(U number) const { #endif return Vector(*this) /= number; } /** * @brief Multiply vector component-wise and assign * * The computation is done in-place. @f[ * \boldsymbol a_i = \boldsymbol a_i \boldsymbol b_i * @f] */ template Vector& operator*=(const Vector& other) { for(std::size_t i = 0; i != size; ++i) _data[i] *= other._data[i]; return *this; } /** * @brief Multiply vector component-wise * * @see operator*=(const Vector&), product() */ template Vector operator*(const Vector& other) const { return Vector(*this) *= other; } /** * @brief Divide vector component-wise and assign * * The computation is done in-place. @f[ * \boldsymbol a_i = \frac{\boldsymbol a_i}{\boldsymbol b_i} * @f] */ template Vector& operator/=(const Vector& other) { for(std::size_t i = 0; i != size; ++i) _data[i] /= other._data[i]; return *this; } /** * @brief Divide vector component-wise * * @see operator/=(const Vector&) */ template Vector operator/(const Vector& other) const { return Vector(*this) /= other; } /** * @brief Dot product of the vector * * Should be used instead of length() for comparing vector length with * other values, because it doesn't compute the square root. @f[ * \boldsymbol a \cdot \boldsymbol a = \sum_{i=0}^{n-1} \boldsymbol a_i^2 * @f] * @see dot(const Vector&, const Vector&), isNormalized() */ T dot() const { return dot(*this, *this); } /** * @brief %Vector length * * See also dot() const which is faster for comparing length with other * values. @f[ * |\boldsymbol a| = \sqrt{\boldsymbol a \cdot \boldsymbol a} * @f] * @see lengthInverted(), Math::sqrt(), normalized(), resized() * @todo something like std::hypot() for possibly better precision? */ T length() const { return std::sqrt(dot()); } /** * @brief Inverse vector length * * @f[ * \frac{1}{|\boldsymbol a|} = \frac{1}{\sqrt{\boldsymbol a \cdot \boldsymbol a}} * @f] * @see length(), Math::sqrtInverted(), normalized(), resized() */ T lengthInverted() const { return T(1)/length(); } /** * @brief Normalized vector (of unit length) * * @see isNormalized(), lengthInverted(), resized() */ Vector normalized() const { return *this*lengthInverted(); } /** * @brief Resized vector * * Convenience equivalent to the following code. Due to operation order * this function is faster than the obvious way of sizing normalized() * vector. * @code * vec*(vec.lengthInverted()*length) // the brackets are important * @endcode * @see normalized() */ Vector resized(T length) const { return *this*(lengthInverted()*length); } /** * @brief %Vector projected onto line * * Returns vector projected onto @p line. @f[ * \boldsymbol a_1 = \frac{\boldsymbol a \cdot \boldsymbol b}{\boldsymbol b \cdot \boldsymbol b} \boldsymbol b * @f] * @see dot(), projectedOntoNormalized() */ Vector projected(const Vector& line) const { return line*dot(*this, line)/line.dot(); } /** * @brief %Vector projected onto normalized line * * Slightly faster alternative to projected(), expects @p line to be * normalized. @f[ * \boldsymbol a_1 = \frac{\boldsymbol a \cdot \boldsymbol b}{\boldsymbol b \cdot \boldsymbol b} \boldsymbol b = * (\boldsymbol a \cdot \boldsymbol b) \boldsymbol b * @f] * @see dot() */ Vector projectedOntoNormalized(const Vector& line) const; /** * @brief Sum of values in the vector * * @see operator+() */ T sum() const; /** * @brief Product of values in the vector * * @see operator*(const Vector&) */ T product() const; /** * @brief Minimal value in the vector * * @see Math::min() */ T min() const; /** * @brief Maximal value in the vector * * @see Math::max() */ T max() const; private: /* Implementation for Vector::Vector(const Vector&) */ template constexpr explicit Vector(Implementation::Sequence, const Vector& vector): _data{T(vector._data[sequence])...} {} /* Implementation for Vector::Vector(U) */ template constexpr explicit Vector(Implementation::Sequence, T value): _data{Implementation::repeat(value, sequence)...} {} T _data[size]; }; /** @relates Vector @brief Multiply number with vector Same as Vector::operator*(U) const. */ #ifdef DOXYGEN_GENERATING_OUTPUT template inline Vector operator*(U number, const Vector& vector) { #else template inline typename std::enable_if::value, Vector>::type operator*(U number, const Vector& vector) { #endif return vector*number; } /** @relates Vector @brief Divide vector with number and invert @f[ \boldsymbol c_i = \frac b {\boldsymbol a_i} @f] @see Vector::operator/() */ #ifdef DOXYGEN_GENERATING_OUTPUT template inline Vector operator/(U number, const Vector& vector) { #else template inline typename std::enable_if::value, Vector>::type operator/(U number, const Vector& vector) { #endif Vector out; for(std::size_t i = 0; i != size; ++i) out[i] = number/vector[i]; return out; } /** @debugoperator{Magnum::Math::Vector} */ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Vector& value) { debug << "Vector("; debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); for(std::size_t i = 0; i != size; ++i) { if(i != 0) debug << ", "; debug << value[i]; } debug << ")"; debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, true); return debug; } /* Explicit instantiation for types used in OpenGL */ #ifndef DOXYGEN_GENERATING_OUTPUT extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Vector<2, Float>&); extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Vector<3, Float>&); extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Vector<4, Float>&); extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Vector<2, Int>&); extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Vector<3, Int>&); extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Vector<4, Int>&); extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Vector<2, UnsignedInt>&); extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Vector<3, UnsignedInt>&); extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Vector<4, UnsignedInt>&); #ifndef MAGNUM_TARGET_GLES extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Vector<2, Double>&); extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Vector<3, Double>&); extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Vector<4, Double>&); #endif #endif #ifndef DOXYGEN_GENERATING_OUTPUT #define MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(Type, size) \ constexpr static Type& from(T* data) { \ return *reinterpret_cast*>(data); \ } \ constexpr static const Type& from(const T* data) { \ return *reinterpret_cast*>(data); \ } \ \ Type& operator=(const Type& other) { \ Math::Vector::operator=(other); \ return *this; \ } \ \ Type operator-() const { \ return Math::Vector::operator-(); \ } \ Type& operator+=(const Math::Vector& other) { \ Math::Vector::operator+=(other); \ return *this; \ } \ Type operator+(const Math::Vector& other) const { \ return Math::Vector::operator+(other); \ } \ Type& operator-=(const Math::Vector& other) { \ Math::Vector::operator-=(other); \ return *this; \ } \ Type operator-(const Math::Vector& other) const { \ return Math::Vector::operator-(other); \ } \ template typename std::enable_if::value, Type&>::type operator*=(U number) { \ Math::Vector::operator*=(number); \ return *this; \ } \ template typename std::enable_if::value, Type>::type operator*(U number) const { \ return Math::Vector::operator*(number); \ } \ template typename std::enable_if::value, Type&>::type operator/=(U number) { \ Math::Vector::operator/=(number); \ return *this; \ } \ template typename std::enable_if::value, Type>::type operator/(U number) const { \ return Math::Vector::operator/(number); \ } \ template Type& operator*=(const Math::Vector& other) { \ Math::Vector::operator*=(other); \ return *this; \ } \ template Type operator*(const Math::Vector& other) const { \ return Math::Vector::operator*(other); \ } \ template Type& operator/=(const Math::Vector& other) { \ Math::Vector::operator/=(other); \ return *this; \ } \ template Type operator/(const Math::Vector& other) const { \ return Math::Vector::operator/(other); \ } \ \ Type normalized() const { \ return Math::Vector::normalized(); \ } \ Type resized(T length) const { \ return Math::Vector::resized(length); \ } \ Type projected(const Math::Vector& other) const { \ return Math::Vector::projected(other); \ } #define MAGNUM_VECTOR_SUBCLASS_OPERATOR_IMPLEMENTATION(Type, size) \ template inline typename std::enable_if::value, Type>::type operator*(U number, const Type& vector) { \ return number*Math::Vector(vector); \ } \ template inline typename std::enable_if::value, Type>::type operator/(U number, const Type& vector) { \ return number/Math::Vector(vector); \ } #endif template inline Rad Vector::angle(const Vector& normalizedA, const Vector& normalizedB) { CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), "Math::Vector::angle(): vectors must be normalized", Rad(std::numeric_limits::quiet_NaN())); return Rad(std::acos(dot(normalizedA, normalizedB))); } template inline BoolVector Vector::operator<(const Vector& other) const { BoolVector out; for(std::size_t i = 0; i != size; ++i) out.set(i, _data[i] < other._data[i]); return out; } template inline BoolVector Vector::operator<=(const Vector& other) const { BoolVector out; for(std::size_t i = 0; i != size; ++i) out.set(i, _data[i] <= other._data[i]); return out; } template inline BoolVector Vector::operator>=(const Vector& other) const { BoolVector out; for(std::size_t i = 0; i != size; ++i) out.set(i, _data[i] >= other._data[i]); return out; } template inline BoolVector Vector::operator>(const Vector& other) const { BoolVector out; for(std::size_t i = 0; i != size; ++i) out.set(i, _data[i] > other._data[i]); return out; } template inline Vector Vector::operator-() const { Vector out; for(std::size_t i = 0; i != size; ++i) out._data[i] = -_data[i]; return out; } template inline Vector Vector::projectedOntoNormalized(const Vector& line) const { CORRADE_ASSERT(line.isNormalized(), "Math::Vector::projectedOntoNormalized(): line must be normalized", (Vector(std::numeric_limits::quiet_NaN()))); return line*dot(*this, line); } template inline T Vector::sum() const { T out(_data[0]); for(std::size_t i = 1; i != size; ++i) out += _data[i]; return out; } template inline T Vector::product() const { T out(_data[0]); for(std::size_t i = 1; i != size; ++i) out *= _data[i]; return out; } template inline T Vector::min() const { T out(_data[0]); for(std::size_t i = 1; i != size; ++i) out = std::min(out, _data[i]); return out; } template inline T Vector::max() const { T out(_data[0]); for(std::size_t i = 1; i != size; ++i) out = std::max(out, _data[i]); return out; } }} namespace Corrade { namespace Utility { /** @configurationvalue{Magnum::Math::RectangularMatrix} */ template struct ConfigurationValue> { ConfigurationValue() = delete; /** @brief Writes elements separated with spaces */ static std::string toString(const Magnum::Math::Vector& value, ConfigurationValueFlags flags) { std::string output; for(std::size_t i = 0; i != size; ++i) { if(!output.empty()) output += ' '; output += ConfigurationValue::toString(value[i], flags); } return output; } /** @brief Reads elements separated with whitespace */ static Magnum::Math::Vector fromString(const std::string& stringValue, ConfigurationValueFlags flags) { Magnum::Math::Vector result; std::size_t oldpos = 0, pos = std::string::npos, i = 0; do { pos = stringValue.find(' ', oldpos); std::string part = stringValue.substr(oldpos, pos-oldpos); if(!part.empty()) { result[i] = ConfigurationValue::fromString(part, flags); ++i; } oldpos = pos+1; } while(pos != std::string::npos); return result; } }; #ifndef DOXYGEN_GENERATING_OUTPUT /* Vectors */ extern template struct MAGNUM_EXPORT ConfigurationValue>; extern template struct MAGNUM_EXPORT ConfigurationValue>; extern template struct MAGNUM_EXPORT ConfigurationValue>; extern template struct MAGNUM_EXPORT ConfigurationValue>; extern template struct MAGNUM_EXPORT ConfigurationValue>; extern template struct MAGNUM_EXPORT ConfigurationValue>; extern template struct MAGNUM_EXPORT ConfigurationValue>; extern template struct MAGNUM_EXPORT ConfigurationValue>; extern template struct MAGNUM_EXPORT ConfigurationValue>; #ifndef MAGNUM_TARGET_GLES extern template struct MAGNUM_EXPORT ConfigurationValue>; extern template struct MAGNUM_EXPORT ConfigurationValue>; extern template struct MAGNUM_EXPORT ConfigurationValue>; #endif #endif }} #endif