#ifndef Magnum_Math_Vector_h #define Magnum_Math_Vector_h /* This file is part of Magnum. Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 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 @ref Magnum::Math::Vector, function @ref Magnum::Math::dot(), @ref Magnum::Math::angle() */ #include #ifndef CORRADE_NO_DEBUG #include #endif #include #include #include "Magnum/visibility.h" #include "Magnum/Math/Angle.h" #include "Magnum/Math/BitVector.h" #include "Magnum/Math/TypeTraits.h" namespace Magnum { namespace Math { #ifndef DOXYGEN_GENERATING_OUTPUT /* Documented in Functions.h, defined here because Vector needs them */ template inline typename std::enable_if::value, bool>::type isNan(T value) { return std::isnan(UnderlyingTypeOf(value)); } /* Keeping the same parameter names as in Functions.h so the note about NaN propagation works here too */ template constexpr typename std::enable_if::value, T>::type min(T value, T min) { return min < value ? min : value; } template constexpr typename std::enable_if::value, T>::type max(T value, T max) { return value < max ? max : value; } template constexpr typename std::enable_if::value, T>::type clamp(T value, T min, T max) { return Math::min(Math::max(value, min), max); } #endif namespace Implementation { template struct VectorConverter; /* Needed by DualQuaternion and Functions.h (to avoid dependency between them) */ template T lerp(const T& a, const T& b, U t) { return T((U(1) - t)*a + t*b); } template struct IsZero; template<> struct IsZero { template bool operator()(const Vector& vec) const { /* Proper comparison should be with epsilon^2, but the value is not representable in given precision. Comparing to epsilon instead. */ return std::abs(vec.dot()) < TypeTraits::epsilon(); } }; template<> struct IsZero { template bool operator()(const Vector& vec) const { return vec == Vector{}; } }; /* Used to make friends to speed up debug builds */ template struct MatrixDeterminant; /* To make gather() / scatter() faster */ template struct GatherComponentAt; template struct ScatterComponentOr; template constexpr T scatterRecursive(const T&, const Vector&, std::size_t); } /** @relatesalso Vector @brief Dot product of two vectors Returns `0` when two vectors are perpendicular, `> 0` when two vectors are in the same general direction, `1` when two *normalized* vectors are parallel, `< 0` when two vectors are in opposite general direction and `-1` when two * *normalized* vectors are antiparallel. @f[ \boldsymbol a \cdot \boldsymbol b = \sum_{i=0}^{n-1} \boldsymbol a_i \boldsymbol b_i @f] @see @ref Vector::dot() const, @ref Vector::operator-(), @ref Vector2::perpendicular() */ template inline T dot(const Vector& a, const Vector& b) { T out{}; for(std::size_t i = 0; i != size; ++i) out += a._data[i]*b._data[i]; return out; } /** @relatesalso Vector @brief Angle between normalized vectors Expects that both vectors are normalized. Enabled only for floating-point types. @f[ \theta = \arccos \left( \frac{\boldsymbol a \cdot \boldsymbol b}{|\boldsymbol a| |\boldsymbol b|} \right) = \arccos (\boldsymbol a \cdot \boldsymbol b) @f] To avoid numerical issues when two vectors are very close to each other, the dot product is clamped to the @f$ [-1, +1] @f$ range before being passed to @f$ \arccos @f$. @see @ref Vector::isNormalized(), @ref angle(const Complex&, const Complex&), @ref angle(const Quaternion&, const Quaternion&) */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Rad #else typename std::enable_if::value, Rad>::type #endif angle(const Vector& normalizedA, const Vector& normalizedB) { CORRADE_DEBUG_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), "Math::angle(): vectors" << normalizedA << "and" << normalizedB << "are not normalized", {}); return Rad(std::acos(clamp(dot(normalizedA, normalizedB), FloatingPoint(-1), FloatingPoint(1)))); } /** @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"); public: typedef T Type; /**< @brief Underlying data type */ enum: std::size_t { Size = size /**< Vector size */ }; /** * @brief Vector from an 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. */ static Vector& from(T* data) { return *reinterpret_cast*>(data); } /** @overload */ static const Vector& from(const T* data) { return *reinterpret_cast*>(data); } /** * @brief Pad a vector * * If size of @p a is smaller than @ref Size, it is padded from right * with @p value, otherwise it's cut. * @see @ref Vector4::pad(const Vector&, T, T) */ template constexpr static Vector pad(const Vector& a, T value = T()) { return padInternal(typename Corrade::Containers::Implementation::GenerateSequence::Type{}, a, value); } /** * @brief Default constructor * * Equivalent to @ref Vector(ZeroInitT). */ constexpr /*implicit*/ Vector() noexcept: _data{} {} /** * @brief Construct a zero vector * * @f[ * \boldsymbol v = \boldsymbol 0 * @f] */ constexpr explicit Vector(ZeroInitT) noexcept: _data{} {} /** @brief Construct a vector without initializing the contents */ explicit Vector(Magnum::NoInitT) noexcept {} /** @brief Construct a vector from components */ #ifdef DOXYGEN_GENERATING_OUTPUT template constexpr /*implicit*/ Vector(T first, U... next) noexcept; #else template::type> constexpr /*implicit*/ Vector(T first, U... next) noexcept: _data{first, next...} {} #endif /** @brief Construct a vector with one value for all components */ #ifdef DOXYGEN_GENERATING_OUTPUT constexpr explicit Vector(T value) noexcept; #else template::value && size != 1, T>::type> constexpr explicit Vector(U value) noexcept: Vector(typename Corrade::Containers::Implementation::GenerateSequence::Type{}, value) {} #endif /** * @brief Construct a vector from another of different type * * Performs only default casting on the values, no rounding or * anything else. Example usage: * * @snippet MagnumMath.cpp Vector-conversion */ template constexpr explicit Vector(const Vector& other) noexcept: Vector(typename Corrade::Containers::Implementation::GenerateSequence::Type{}, other) {} /** @brief Construct a vector from external representation */ template::from(std::declval()))> constexpr explicit Vector(const U& other) noexcept: Vector(Implementation::VectorConverter::from(other)) {} /** @brief Convert a vector to external representation */ template::to(std::declval>()))> constexpr explicit operator U() const { return Implementation::VectorConverter::to(*this); } /** * @brief Raw data * * Contrary to what Doxygen shows, returns reference to an * one-dimensional fixed-size array of `size` elements, i.e. * @cpp T(&)[size] @ce. * @see @ref operator[]() * @todoc Fix once there's a possibility to patch the signature in a * post-processing step (https://github.com/mosra/m.css/issues/56) */ #ifdef DOXYGEN_GENERATING_OUTPUT T* data(); constexpr const T* data() const; /**< @overload */ #else auto data() -> T(&)[size] { return _data; } constexpr auto data() const -> const T(&)[size] { return _data; } #endif /** * @brief Value at given position * * @see @ref 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 * * @see @ref Math::equal() */ 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 * * @see @ref Math::notEqual() */ bool operator!=(const Vector& other) const { return !operator==(other); } /** * @brief Component-wise less than comparison * * @m_keyword{lessThan(),GLSL lessThan(),} */ BitVector operator<(const Vector& other) const; /** * @brief Component-wise less than or equal comparison * * @m_keyword{lessThanEqual(),GLSL lessThanEqual(),} */ BitVector operator<=(const Vector& other) const; /** * @brief Component-wise greater than or equal comparison * * @m_keyword{greaterThanEqual(),GLSL greaterThanEqual(),} */ BitVector operator>=(const Vector& other) const; /** * @brief Component-wise greater than comparison * * @m_keyword{greaterThan(),GLSL greaterThan(),} */ BitVector operator>(const Vector& other) const; /** * @brief Whether the vector is zero * * @f[ * |\boldsymbol a \cdot \boldsymbol a - 0| < \epsilon^2 \cong \epsilon * @f] * @see @ref dot(), @ref normalized() */ bool isZero() const { return Implementation::IsZero::value>{}(*this); } /** * @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 @ref dot(), @ref normalized() */ bool isNormalized() const { return Implementation::isNormalizedSquared(dot()); } /** * @brief Promotion * @m_since_latest * * Returns the value as-is. */ Vector operator+() const { return *this; } /** * @brief Negated vector * * Enabled only for signed types. @f[ * \boldsymbol b_i = -\boldsymbol a_i * @f] * @see @ref Vector2::perpendicular() */ #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else template typename std::enable_if::value, Vector>::type #endif operator-() const; /** * @brief Add and assign a 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 a vector * * @see @ref operator+=(), @ref sum() */ Vector operator+(const Vector& other) const { return Vector(*this) += other; } /** * @brief Subtract and assign a 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 a vector * * @see @ref operator-=() */ Vector operator-(const Vector& other) const { return Vector(*this) -= other; } /** * @brief Multiply with a scalar and assign * * The computation is done in-place. @f[ * \boldsymbol a_i = b \boldsymbol a_i * @f] * @see @ref operator*=(const Vector&), * @ref operator*=(Vector&, FloatingPoint) */ Vector& operator*=(T scalar) { for(std::size_t i = 0; i != size; ++i) _data[i] *= scalar; return *this; } /** * @brief Multiply with a scalar * * @see @ref operator*(const Vector&) const, * @ref operator*=(T), @ref operator*(T, const Vector&), * @ref operator*(const Vector&, FloatingPoint) */ Vector operator*(T scalar) const { return Vector(*this) *= scalar; } /** * @brief Divide with a scalar and assign * * The computation is done in-place. @f[ * \boldsymbol a_i = \frac{\boldsymbol a_i} b * @f] * @see @ref operator/=(const Vector&), * @ref operator/=(Vector&, FloatingPoint) */ Vector& operator/=(T scalar) { for(std::size_t i = 0; i != size; ++i) _data[i] /= scalar; return *this; } /** * @brief Divide with a scalar * * @see @ref operator/(const Vector&) const, * @ref operator/=(T), @ref operator/(T, const Vector&), * @ref operator/(const Vector&, FloatingPoint) */ Vector operator/(T scalar) const { return Vector(*this) /= scalar; } /** * @brief Multiply a vector component-wise and assign * * The computation is done in-place. @f[ * \boldsymbol a_i = \boldsymbol a_i \boldsymbol b_i * @f] * @see @ref operator*=(T), * @ref operator*=(Vector&, const Vector&) */ Vector& operator*=(const Vector& other) { for(std::size_t i = 0; i != size; ++i) _data[i] *= other._data[i]; return *this; } /** * @brief Multiply a vector component-wise * * @see @ref operator*(T) const, @ref operator*=(const Vector&), * @ref operator*(const Vector&, const Vector&), * @ref product() */ Vector operator*(const Vector& other) const { return Vector(*this) *= other; } /** * @brief Divide a vector component-wise and assign * * The computation is done in-place. @f[ * \boldsymbol a_i = \frac{\boldsymbol a_i}{\boldsymbol b_i} * @f] * @see @ref operator/=(T), * @ref operator/=(Vector&, const Vector&) */ Vector& operator/=(const Vector& other) { for(std::size_t i = 0; i != size; ++i) _data[i] /= other._data[i]; return *this; } /** * @brief Divide a vector component-wise * * @see @ref operator/(T) const, @ref operator/=(const Vector&), * @ref operator/(const Vector&, const Vector&) */ Vector operator/(const Vector& other) const { return Vector(*this) /= other; } /** * @brief Dot product of the vector * * Should be used instead of @ref 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 @ref dot(const Vector&, const Vector&), * @ref isNormalized(), @ref Distance::pointPointSquared(), * @ref Intersection::pointSphere() */ T dot() const { return Math::dot(*this, *this); } /** * @brief Vector length * * See also @ref dot() const which is faster for comparing length with * other values. @f[ * |\boldsymbol a| = \sqrt{\boldsymbol a \cdot \boldsymbol a} * @f] * * For integral types the result may be imprecise, to get a * floating-point value of desired precision, cast to a floating-point * vector first: * * @snippet MagnumMath.cpp Vector-length-integer * * A [Manhattan length](https://en.wikipedia.org/wiki/Taxicab_geometry) * might be more suitable than @ref length() in certain cases where the * square root is undesirable --- it's a sum of absolute values: * * @snippet MagnumMath.cpp Vector-length-manhattan * * @see @ref lengthInverted(), @ref Math::sqrt(), @ref normalized(), * @ref resized(), @ref Distance::pointPoint(), * @ref Intersection::pointSphere() * @todo something like std::hypot() for possibly better precision? */ T length() const { return T(std::sqrt(dot())); } /** * @brief Inverse vector length * * Enabled only for floating-point types. @f[ * \frac{1}{|\boldsymbol a|} = \frac{1}{\sqrt{\boldsymbol a \cdot \boldsymbol a}} * @f] * @see @ref length(), @ref Math::sqrtInverted(), @ref normalized(), * @ref resized() */ #ifdef DOXYGEN_GENERATING_OUTPUT T #else template typename std::enable_if::value, T>::type #endif lengthInverted() const { return T(1)/length(); } /** * @brief Normalized vector (of unit length) * * Enabled only for floating-point types. * @see @ref isNormalized(), @ref lengthInverted(), @ref resized() * @m_keyword{normalize(),GLSL normalize(),} */ #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else template typename std::enable_if::value, Vector>::type #endif 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 * a @ref normalized() vector. Enabled only for floating-point types. * * @snippet MagnumMath.cpp Vector-resized * * @see @ref normalized() */ #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else template typename std::enable_if::value, Vector>::type #endif resized(T length) const { return *this*(lengthInverted()*length); } /** * @brief Vector projected onto a line * * Returns a vector [projected](https://en.wikipedia.org/wiki/Vector_projection) * onto @p line. Enabled only for floating-point types. @f[ * \operatorname{proj}_{\boldsymbol{b}}\,(\boldsymbol{a}) = \frac{\boldsymbol a \cdot \boldsymbol b}{\boldsymbol b \cdot \boldsymbol b} \boldsymbol b * @f] * @see @ref Math::dot(), @ref projectedOntoNormalized() */ #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else template typename std::enable_if::value, Vector>::type #endif projected(const Vector& line) const { return line*Math::dot(*this, line)/line.dot(); } /** * @brief Vector projected onto a normalized line * * Slightly faster alternative to @ref projected(), expects @p line to * be normalized. Enabled only for floating-point types. @f[ * \operatorname{proj}_{\boldsymbol{b}}\,(\boldsymbol{a}) = \frac{\boldsymbol a \cdot \boldsymbol b}{\boldsymbol b \cdot \boldsymbol b} \boldsymbol b = * (\boldsymbol a \cdot \boldsymbol b) \boldsymbol b * @f] * @see @ref Math::dot() */ #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else template typename std::enable_if::value, Vector>::type #endif projectedOntoNormalized(const Vector& line) const; /** * @brief Flipped vector * * Returns the vector with components in reverse order. * @see @ref RectangularMatrix::flippedCols(), * @ref RectangularMatrix::flippedRows() */ constexpr Vector flipped() const { return flippedInternal(typename Corrade::Containers::Implementation::GenerateSequence::Type{}); } /** * @brief Sum of values in the vector * * @see @ref operator+(), @ref length() */ T sum() const; /** * @brief Product of values in the vector * * @see @ref operator*(const Vector&) const */ T product() const; /** * @brief Minimal value in the vector * * NaNs are ignored, unless the vector is all NaNs. * @see @ref Math::min(), @ref minmax(), @ref Math::isNan() */ T min() const; /** * @brief Maximal value in the vector * * NaNs are ignored, unless the vector is all NaNs. * @see @ref Math::max(), @ref minmax(), @ref Math::isNan() */ T max() const; /** * @brief Minimal and maximal value in the vector * * NaNs are ignored, unless the vector is all NaNs. * @see @ref min(), @ref max(), @ref Math::minmax(), @ref Math::isNan() */ std::pair minmax() const; #ifndef DOXYGEN_GENERATING_OUTPUT protected: #else private: #endif /* So derived classes can avoid the overhead of operator[] in debug builds */ T _data[size]; private: #ifndef DOXYGEN_GENERATING_OUTPUT /* Since I added deprecated aliases to Shaders::VectorGL, this FUCKING DUMPSTER FIRE DOXYGEN CRAP thinks this refers to Shaders::Vector or whatever fucking insane thing. WHAT THE FUCK. */ template friend class Vector; #endif /* These three needed to access _data to speed up debug builds */ template friend class RectangularMatrix; template friend class Matrix; template friend struct Implementation::MatrixDeterminant; /* To make gather() / scatter() faster */ template friend struct Implementation::GatherComponentAt; template friend struct Implementation::ScatterComponentOr; template friend constexpr T_ Implementation::scatterRecursive(const T_&, const Vector&, std::size_t); /* So the out-of-class comparators can access data directly to avoid function call overhead */ template friend BitVector equal(const Vector&, const Vector&); template friend BitVector notEqual(const Vector&, const Vector&); template friend U dot(const Vector&, const Vector&); /* Implementation for Vector::Vector(const Vector&) */ template constexpr explicit Vector(Corrade::Containers::Implementation::Sequence, const Vector& vector) noexcept: _data{T(vector._data[sequence])...} {} /* Implementation for Vector::Vector(U) */ template constexpr explicit Vector(Corrade::Containers::Implementation::Sequence, T value) noexcept: _data{Implementation::repeat(value, sequence)...} {} template constexpr static Vector padInternal(Corrade::Containers::Implementation::Sequence, const Vector& a, T value) { return {sequence < otherSize ? a[sequence] : value...}; } template constexpr Vector flippedInternal(Corrade::Containers::Implementation::Sequence) const { return {_data[size - 1 - sequence]...}; } }; /** @relatesalso Vector @brief Component-wise equality comparison @m_since{2019,10} Unlike @ref Vector::operator==() returns a @ref BitVector instead of a single value. Vector complement to @ref equal(T, T). */ template inline BitVector equal(const Vector& a, const Vector& b) { BitVector out; for(std::size_t i = 0; i != size; ++i) out.set(i, TypeTraits::equals(a._data[i], b._data[i])); return out; } /** @relatesalso Vector @brief Component-wise non-equality comparison @m_since{2019,10} Unlike @ref Vector::operator!=() returns a @ref BitVector instead of a single value. Vector complement to @ref notEqual(T, T). */ template inline BitVector notEqual(const Vector& a, const Vector& b) { BitVector out; for(std::size_t i = 0; i != size; ++i) out.set(i, !TypeTraits::equals(a._data[i], b._data[i])); return out; } /** @relates Vector @brief Multiply a scalar with a vector Same as @ref Vector::operator*(T) const. */ template inline Vector operator*( #ifdef DOXYGEN_GENERATING_OUTPUT T #else typename std::common_type::type #endif scalar, const Vector& vector) { return vector*scalar; } /** @relates Vector @brief Divide a vector with a scalar and invert @f[ \boldsymbol c_i = \frac b {\boldsymbol a_i} @f] @see @ref Vector::operator/(T) const */ template inline Vector operator/( #ifdef DOXYGEN_GENERATING_OUTPUT T #else typename std::common_type::type #endif scalar, const Vector& vector) { Vector out; for(std::size_t i = 0; i != size; ++i) out[i] = scalar/vector[i]; return out; } /** @relates Vector @brief Do modulo of an integral vector and assign The computation is done in-place. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector& #else typename std::enable_if::value, Vector&>::type #endif operator%=(Vector& a, Integral b) { for(std::size_t i = 0; i != size; ++i) a[i] %= b; return a; } /** @relates Vector @brief Modulo of an integral vector */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value, Vector>::type #endif operator%(const Vector& a, Integral b) { Vector copy(a); return copy %= b; } /** @relates Vector @brief Do modulo of two integral vectors and assign The computation is done in-place. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector& #else typename std::enable_if::value, Vector&>::type #endif operator%=(Vector& a, const Vector& b) { for(std::size_t i = 0; i != size; ++i) a[i] %= b[i]; return a; } /** @relates Vector @brief Modulo of two integral vectors */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value, Vector>::type #endif operator%(const Vector& a, const Vector& b) { Vector copy(a); return copy %= b; } /** @relates Vector @brief Bitwise NOT of an integral vector */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value, Vector>::type #endif operator~(const Vector& vector) { Vector out; for(std::size_t i = 0; i != size; ++i) out[i] = ~vector[i]; return out; } /** @relates Vector @brief Do bitwise AND of two integral vectors and assign The computation is done in-place. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector& #else typename std::enable_if::value, Vector&>::type #endif operator&=(Vector& a, const Vector& b) { for(std::size_t i = 0; i != size; ++i) a[i] &= b[i]; return a; } /** @relates Vector @brief Bitwise AND of two integral vectors */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value, Vector>::type #endif operator&(const Vector& a, const Vector& b) { Vector copy(a); return copy &= b; } /** @relates Vector @brief Do bitwise OR of two integral vectors and assign The computation is done in-place. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector& #else typename std::enable_if::value, Vector&>::type #endif operator|=(Vector& a, const Vector& b) { for(std::size_t i = 0; i != size; ++i) a[i] |= b[i]; return a; } /** @relates Vector @brief Bitwise OR of two integral vectors */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value, Vector>::type #endif operator|(const Vector& a, const Vector& b) { Vector copy(a); return copy |= b; } /** @relates Vector @brief Do bitwise XOR of two integral vectors and assign The computation is done in-place. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector& #else typename std::enable_if::value, Vector&>::type #endif operator^=(Vector& a, const Vector& b) { for(std::size_t i = 0; i != size; ++i) a[i] ^= b[i]; return a; } /** @relates Vector @brief Bitwise XOR of two integral vectors */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value, Vector>::type #endif operator^(const Vector& a, const Vector& b) { Vector copy(a); return copy ^= b; } /** @relates Vector @brief Do bitwise left shift of an integral vector and assign The computation is done in-place. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector& #else typename std::enable_if::value, Vector&>::type #endif operator<<=(Vector& vector, #ifdef DOXYGEN_GENERATING_OUTPUT Integral #else typename std::common_type::type #endif shift) { for(std::size_t i = 0; i != size; ++i) vector[i] <<= shift; return vector; } /** @relates Vector @brief Bitwise left shift of an integral vector */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value, Vector>::type #endif operator<<(const Vector& vector, #ifdef DOXYGEN_GENERATING_OUTPUT Integral #else typename std::common_type::type #endif shift) { Vector copy(vector); return copy <<= shift; } /** @relates Vector @brief Do bitwise right shift of an integral vector and assign The computation is done in-place. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector& #else typename std::enable_if::value, Vector&>::type #endif operator>>=(Vector& vector, #ifdef DOXYGEN_GENERATING_OUTPUT Integral #else typename std::common_type::type #endif shift) { for(std::size_t i = 0; i != size; ++i) vector[i] >>= shift; return vector; } /** @relates Vector @brief Bitwise left shift of an integral vector */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value, Vector>::type #endif operator>>(const Vector& vector, #ifdef DOXYGEN_GENERATING_OUTPUT Integral #else typename std::common_type::type #endif shift) { Vector copy(vector); return copy >>= shift; } /** @relates Vector @brief Multiply an integral vector with a floating-point scalar and assign Similar to @ref Vector::operator*=(T), except that the multiplication is done in floating-point. The computation is done in-place. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector& #else typename std::enable_if::value && std::is_floating_point::value, Vector&>::type #endif operator*=(Vector& vector, FloatingPoint scalar) { for(std::size_t i = 0; i != size; ++i) vector[i] = Integral(vector[i]*scalar); return vector; } /** @relates Vector @brief Multiply an integral vector with a floating-point scalar Similar to @ref Vector::operator*(T) const, except that the multiplication is done in floating-point. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value && std::is_floating_point::value, Vector>::type #endif operator*(const Vector& vector, FloatingPoint scalar) { Vector copy(vector); return copy *= scalar; } /** @relates Vector @brief Multiply a floating-point scalar with an integral vector Same as @ref operator*(const Vector&, FloatingPoint). */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value && std::is_floating_point::value, Vector>::type #endif operator*(FloatingPoint scalar, const Vector& vector) { return vector*scalar; } /** @relates Vector @brief Divide an integral vector with a floating-point scalar and assign Similar to @ref Vector::operator/=(T), except that the division is done in floating-point. The computation is done in-place. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector& #else typename std::enable_if::value && std::is_floating_point::value, Vector&>::type #endif operator/=(Vector& vector, FloatingPoint scalar) { for(std::size_t i = 0; i != size; ++i) vector[i] = Integral(vector[i]/scalar); return vector; } /** @relates Vector @brief Divide an integral vector with a floating-point scalar Similar to @ref Vector::operator/(T) const, except that the division is done in floating-point. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value && std::is_floating_point::value, Vector>::type #endif operator/(const Vector& vector, FloatingPoint scalar) { Vector copy(vector); return copy /= scalar; } /** @relates Vector @brief Multiply an integral vector with a floating-point vector component-wise and assign Similar to @ref Vector::operator*=(const Vector&), except that the multiplication is done in floating-point. The computation is done in-place. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector& #else typename std::enable_if::value && std::is_floating_point::value, Vector&>::type #endif operator*=(Vector& a, const Vector& b) { for(std::size_t i = 0; i != size; ++i) a[i] = Integral(a[i]*b[i]); return a; } /** @relates Vector @brief Multiply an integral vector with a floating-point vector component-wise Similar to @ref Vector::operator*(const Vector&) const, except that the multiplication is done in floating-point. The result is always integral vector, convert both arguments to the same floating-point type to have floating-point result. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value && std::is_floating_point::value, Vector>::type #endif operator*(const Vector& a, const Vector& b) { Vector copy(a); return copy *= b; } /** @relates Vector @brief Multiply a floating-point vector with an integral vector component-wise Same as @ref operator*(const Vector&, const Vector&). */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value && std::is_floating_point::value, Vector>::type #endif operator*(const Vector& a, const Vector& b) { return b*a; } /** @relates Vector @brief Divide an integral vector with a floating-point vector component-wise and assign Similar to @ref Vector::operator/=(const Vector&), except that the division is done in floating-point. The computation is done in-place. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector& #else typename std::enable_if::value && std::is_floating_point::value, Vector&>::type #endif operator/=(Vector& a, const Vector& b) { for(std::size_t i = 0; i != size; ++i) a[i] = Integral(a[i]/b[i]); return a; } /** @relates Vector @brief Divide an integral vector with a floating-point vector component-wise Similar to @ref Vector::operator/(const Vector&) const, except that the division is done in floating-point. The result is always integral vector, convert both arguments to the same floating-point type to have floating-point result. */ template inline #ifdef DOXYGEN_GENERATING_OUTPUT Vector #else typename std::enable_if::value && std::is_floating_point::value, Vector>::type #endif operator/(const Vector& a, const Vector& b) { Vector copy(a); return copy /= b; } #ifndef CORRADE_NO_DEBUG /** @debugoperator{Vector} */ template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, const Vector& value) { /** @todo might make sense to propagate the flags also, for hex value printing etc */ const bool packed = debug.immediateFlags() >= Corrade::Utility::Debug::Flag::Packed; debug << (packed ? "{" : "Vector(") << Corrade::Utility::Debug::nospace; for(std::size_t i = 0; i != size; ++i) { if(i != 0) debug << Corrade::Utility::Debug::nospace << ","; debug << value[i]; } return debug << Corrade::Utility::Debug::nospace << (packed ? "}" : ")"); } /* Explicit instantiation for commonly used types */ #ifndef DOXYGEN_GENERATING_OUTPUT extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Vector<2, Float>&); extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Vector<3, Float>&); extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Vector<4, Float>&); extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Vector<2, Int>&); extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Vector<3, Int>&); extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Vector<4, Int>&); extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Vector<2, UnsignedInt>&); extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Vector<3, UnsignedInt>&); extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Vector<4, UnsignedInt>&); extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Vector<2, Double>&); extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Vector<3, Double>&); extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Vector<4, Double>&); #endif #endif #ifndef DOXYGEN_GENERATING_OUTPUT #define MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(size, Type) \ static Type& from(T* data) { \ return *reinterpret_cast*>(data); \ } \ static const Type& from(const T* data) { \ return *reinterpret_cast*>(data); \ } \ template constexpr static Type pad(const Math::Vector& a, T value = T()) { \ return Math::Vector::pad(a, value); \ } \ \ Type operator+() const { \ return Math::Vector::operator+(); \ } \ template typename std::enable_if::value, Type>::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); \ } \ Type& operator*=(T number) { \ Math::Vector::operator*=(number); \ return *this; \ } \ Type operator*(T number) const { \ return Math::Vector::operator*(number); \ } \ Type& operator/=(T number) { \ Math::Vector::operator/=(number); \ return *this; \ } \ Type operator/(T number) const { \ return Math::Vector::operator/(number); \ } \ 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 normalized() const { \ return Math::Vector::normalized(); \ } \ template typename std::enable_if::value, Type>::type resized(T length) const { \ return Math::Vector::resized(length); \ } \ template typename std::enable_if::value, Type>::type projected(const Math::Vector& other) const { \ return Math::Vector::projected(other); \ } \ template typename std::enable_if::value, Type>::type projectedOntoNormalized(const Math::Vector& other) const { \ return Math::Vector::projectedOntoNormalized(other); \ } \ constexpr Type flipped() const { \ return Math::Vector::flipped(); \ } #define MAGNUM_VECTORn_OPERATOR_IMPLEMENTATION(size, Type) \ template inline Type operator*(typename std::common_type::type number, const Type& vector) { \ return number*static_cast&>(vector); \ } \ template inline Type operator/(typename std::common_type::type number, const Type& vector) { \ return number/static_cast&>(vector); \ } \ \ template inline typename std::enable_if::value, Type&>::type operator%=(Type& a, Integral b) { \ static_cast&>(a) %= b; \ return a; \ } \ template inline typename std::enable_if::value, Type>::type operator%(const Type& a, Integral b) { \ return static_cast&>(a) % b; \ } \ template inline typename std::enable_if::value, Type&>::type operator%=(Type& a, const Math::Vector& b) { \ static_cast&>(a) %= b; \ return a; \ } \ template inline typename std::enable_if::value, Type>::type operator%(const Type& a, const Math::Vector& b) { \ return static_cast&>(a) % b; \ } \ \ template inline typename std::enable_if::value, Type>::type operator~(const Type& vector) { \ return ~static_cast&>(vector); \ } \ template inline typename std::enable_if::value, Type&>::type operator&=(Type& a, const Math::Vector& b) { \ static_cast&>(a) &= b; \ return a; \ } \ template inline typename std::enable_if::value, Type>::type operator&(const Type& a, const Math::Vector& b) { \ return static_cast&>(a) & b; \ } \ template inline typename std::enable_if::value, Type&>::type operator|=(Type& a, const Math::Vector& b) { \ static_cast&>(a) |= b; \ return a; \ } \ template inline typename std::enable_if::value, Type>::type operator|(const Type& a, const Math::Vector& b) { \ return static_cast&>(a) | b; \ } \ template inline typename std::enable_if::value, Type&>::type operator^=(Type& a, const Math::Vector& b) { \ static_cast&>(a) ^= b; \ return a; \ } \ template inline typename std::enable_if::value, Type>::type operator^(const Type& a, const Math::Vector& b) { \ return static_cast&>(a) ^ b; \ } \ template inline typename std::enable_if::value, Type&>::type operator<<=(Type& vector, typename std::common_type::type shift) { \ static_cast&>(vector) <<= shift; \ return vector; \ } \ template inline typename std::enable_if::value, Type>::type operator<<(const Type& vector, typename std::common_type::type shift) { \ return static_cast&>(vector) << shift; \ } \ template inline typename std::enable_if::value, Type&>::type operator>>=(Type& vector, typename std::common_type::type shift) { \ static_cast&>(vector) >>= shift; \ return vector; \ } \ template inline typename std::enable_if::value, Type>::type operator>>(const Type& vector, typename std::common_type::type shift) { \ return static_cast&>(vector) >> shift; \ } \ template inline typename std::enable_if::value && std::is_floating_point::value, Type&>::type operator*=(Type& vector, FloatingPoint number) { \ static_cast&>(vector) *= number; \ return vector; \ } \ template inline typename std::enable_if::value && std::is_floating_point::value, Type>::type operator*(const Type& vector, FloatingPoint number) { \ return static_cast&>(vector)*number; \ } \ template inline typename std::enable_if::value && std::is_floating_point::value, Type>::type operator*(FloatingPoint number, const Type& vector) { \ return number*static_cast&>(vector); \ } \ template inline typename std::enable_if::value && std::is_floating_point::value, Type&>::type operator/=(Type& vector, FloatingPoint number) { \ static_cast&>(vector) /= number; \ return vector; \ } \ template inline typename std::enable_if::value && std::is_floating_point::value, Type>::type operator/(const Type& vector, FloatingPoint number) { \ return static_cast&>(vector)/number; \ } \ \ template inline typename std::enable_if::value && std::is_floating_point::value, Type&>::type operator*=(Type& a, const Math::Vector& b) { \ static_cast&>(a) *= b; \ return a; \ } \ template inline typename std::enable_if::value && std::is_floating_point::value, Type>::type operator*(const Type& a, const Math::Vector& b) { \ return static_cast&>(a)*b; \ } \ template inline typename std::enable_if::value && std::is_floating_point::value, Type>::type operator*(const Math::Vector& a, const Type& b) { \ return a*static_cast&>(b); \ } \ template inline typename std::enable_if::value && std::is_floating_point::value, Type&>::type operator/=(Type& a, const Math::Vector& b) { \ static_cast&>(a) /= b; \ return a; \ } \ template inline typename std::enable_if::value && std::is_floating_point::value, Type>::type operator/(const Type& a, const Math::Vector& b) { \ return static_cast&>(a)/b; \ } #endif template inline BitVector Vector::operator<(const Vector& other) const { BitVector out; for(std::size_t i = 0; i != size; ++i) out.set(i, _data[i] < other._data[i]); return out; } template inline BitVector Vector::operator<=(const Vector& other) const { BitVector out; for(std::size_t i = 0; i != size; ++i) out.set(i, _data[i] <= other._data[i]); return out; } template inline BitVector Vector::operator>=(const Vector& other) const { BitVector out; for(std::size_t i = 0; i != size; ++i) out.set(i, _data[i] >= other._data[i]); return out; } template inline BitVector Vector::operator>(const Vector& other) const { BitVector out; for(std::size_t i = 0; i != size; ++i) out.set(i, _data[i] > other._data[i]); return out; } template #ifdef DOXYGEN_GENERATING_OUTPUT inline Vector #else template inline typename std::enable_if::value, Vector>::type #endif Vector::operator-() const { Vector out; for(std::size_t i = 0; i != size; ++i) out._data[i] = -_data[i]; return out; } template #ifdef DOXYGEN_GENERATING_OUTPUT inline Vector #else template inline typename std::enable_if::value, Vector>::type #endif Vector::projectedOntoNormalized(const Vector& line) const { CORRADE_DEBUG_ASSERT(line.isNormalized(), "Math::Vector::projectedOntoNormalized(): line" << line << "is not normalized", {}); return line*Math::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; } namespace Implementation { /* Non-floating-point types, the first is a non-NaN for sure */ template constexpr std::size_t firstNonNan(const T(&)[size], std::false_type) { return 0; } /* Floating-point types, return the first that's not NaN */ template inline std::size_t firstNonNan(const T(&data)[size], std::true_type) { /* Find the first non-NaN value to compare against. If all are NaN, return the last value so the following loop in min/max/minmax() doesn't even execute. */ for(std::size_t i = 0; i != size; ++i) if(!isNan(data[i])) return i; return size - 1; } } template inline T Vector::min() const { std::size_t i = Implementation::firstNonNan(_data, IsFloatingPoint{}); T out(_data[i]); for(++i; i != size; ++i) out = Math::min(out, _data[i]); return out; } template inline T Vector::max() const { std::size_t i = Implementation::firstNonNan(_data, IsFloatingPoint{}); T out(_data[i]); for(++i; i != size; ++i) out = Math::max(out, _data[i]); return out; } template inline std::pair Vector::minmax() const { std::size_t i = Implementation::firstNonNan(_data, IsFloatingPoint{}); T min{_data[i]}, max{_data[i]}; for(++i; i != size; ++i) { if(_data[i] < min) min = _data[i]; else if(_data[i] > max) max = _data[i]; } return {min, max}; } namespace Implementation { template struct StrictWeakOrdering> { bool operator()(const Vector& a, const Vector& b) const { for(std::size_t i = 0; i < size; ++i) { if(a[i] < b[i]) return true; if(a[i] > b[i]) return false; } return false; /* a and b are equivalent */ } }; } }} #endif