#ifndef Magnum_Math_Range_h #define Magnum_Math_Range_h /* This file is part of Magnum. Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 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::Range, @ref Magnum::Math::Range2D, @ref Magnum::Math::Range3D, alias @ref Magnum::Math::Range1D */ #include "Magnum/Math/Functions.h" #include "Magnum/Math/Vector3.h" namespace Magnum { namespace Math { namespace Implementation { template struct RangeTraits; template struct RangeTraits<1, T> { typedef Vector<1, T> Type; }; template struct RangeTraits<2, T> { typedef Vector2 Type; }; template struct RangeTraits<3, T> { typedef Vector3 Type; }; template struct RangeConverter; } /** @brief N-dimensional range Axis-aligned line (in 1D), rectangle (in 2D) or cube (in 3D). Minimal coordinate is inclusive, maximal exclusive. See @ref Range1D, @ref Range2D and @ref Range3D specializations for given dimension count. */ template class Range { template friend class Range; public: /** * @brief Underlying vector type * * `T` in 1D, @ref Vector2 in 2D, @ref Vector3 in 3D. */ typedef typename Implementation::RangeTraits::Type VectorType; /** * @brief Create range from minimal coordinates and size * @param min Minimal coordinates * @param size Range size */ static Range fromSize(const VectorType& min, const VectorType& size) { return {min, min+size}; } /** * @brief Construct zero range * * Construct zero-size range positioned at origin. */ constexpr /*implicit*/ Range(ZeroInitT = ZeroInit) noexcept: _min{ZeroInit}, _max{ZeroInit} {} /** @brief Construct without initializing the contents */ explicit Range(NoInitT) noexcept: _min{NoInit}, _max{NoInit} {} /** @brief Construct range from minimal and maximal coordinates */ constexpr /*implicit*/ Range(const VectorType& min, const VectorType& max) noexcept: _min{min}, _max{max} {} /** * @brief Construct range from another of different type * * Performs only default casting on the values, no rounding or * anything else. Example usage: * @code * Range2D floatingPoint({1.3f, 2.7f}, {-15.0f, 7.0f}); * Range2D integral(floatingPoint); // {{1, 2}, {-15, 7}} * @endcode */ template constexpr explicit Range(const Range& other) noexcept: _min(other._min), _max(other._max) {} /** @brief Construct range from external representation */ template::from(std::declval()))> constexpr explicit Range(const U& other): Range{Implementation::RangeConverter::from(other)} {} /** @brief Copy constructor */ constexpr /*implicit*/ Range(const Range&) noexcept = default; /** @brief Convert range to external representation */ template::to(std::declval>()))> constexpr explicit operator U() const { return Implementation::RangeConverter::to(*this); } /** @brief Equality comparison */ constexpr bool operator==(const Range& other) const { return _min == other._min && _max == other._max; } /** @brief Non-equality comparison */ constexpr bool operator!=(const Range& other) const { return !operator==(other); } /** * @brief Raw data * @return One-dimensional array of `dimensions`*2 length. */ T* data() { return _min.data(); } constexpr const T* data() const { return _min.data(); } /**< @overload */ /** * @brief Minimal coordinates (inclusive) * * @see @ref size(), @ref Range2D::bottomLeft(), * @ref Range3D::backBottomLeft() */ VectorType& min() { return _min; } constexpr const VectorType min() const { return _min; } /**< @overload */ /** * @brief Maximal coordinates (exclusive) * * @see @ref size(), @ref Range2D::topRight(), * @ref Range3D::frontTopRight() */ VectorType& max() { return _max; } constexpr const VectorType max() const { return _max; } /**< @overload */ /** * @brief Range size * * @see @ref min(), @ref max(), @ref Range2D::sizeX(), * @ref Range2D::sizeY(), @ref Range3D::sizeX(), * @ref Range3D::sizeY(), @ref Range3D::sizeZ(), @ref center() */ VectorType size() const { return _max - _min; } /** * @brief Range center * * @see @ref Range2D::centerX(), @ref Range2D::centerY(), * @ref Range3D::centerX(), @ref Range3D::centerY(), * @ref Range3D::centerZ(), @ref size() */ VectorType center() const { return (_min + _max)/T(2); } /** * @brief Translated range * * Translates the minimal and maximal coordinates by given amount. Size * remains the same. * @see @ref padded() */ Range translated(const VectorType& vector) const; /** * @brief Padded rage * * Translates the minimal and maximal coordinates by given amount. * Center remains the same. * @see @ref translated() */ Range padded(const VectorType& padding) const; /** * @brief Scaled range * * Multiplies the minimal and maximal coordinates by given amount. * @see @ref padded() */ Range scaled(const VectorType& scaling) const; /** @brief Whether given point is contained inside the range */ constexpr bool contains(const VectorType& a) const { return (a >= _min).all() && (a < _max).all(); } private: VectorType _min, _max; }; #ifndef DOXYGEN_GENERATING_OUTPUT #define MAGNUM_RANGE_SUBCLASS_IMPLEMENTATION(dimensions, Type, VectorType) \ static Type fromSize(const VectorType& min, const VectorType& size) { \ return Range::fromSize(min, size); \ } \ Type translated(const VectorType& vector) const { \ return Range::translated(vector); \ } \ Type padded(const VectorType& padding) const { \ return Range::padded(padding); \ } \ Type scaled(const VectorType& scaling) const { \ return Range::scaled(scaling); \ } #endif /** @brief One-dimensional range Convenience alternative to `Range<1, T>`. See @ref Range for more information. */ #ifndef CORRADE_MSVC2015_COMPATIBILITY /* Multiple definitions still broken */ template using Range1D = Range<1, T>; #endif /** @brief Two-dimensional range See @ref Range for more information. @see @ref Range1D, @ref Range3D */ template class Range2D: public Range<2, T> { public: /** @copydoc Range(ZeroInitT) */ constexpr /*implicit*/ Range2D(ZeroInitT = ZeroInit) noexcept /** @todoc remove workaround when doxygen is sane */ #ifndef DOXYGEN_GENERATING_OUTPUT /* MSVC 2015 can't handle {} here */ : Range<2, T>(ZeroInit) #endif {} /** @copydoc Range(NoInitT) */ explicit Range2D(NoInitT) noexcept /** @todoc remove workaround when doxygen is sane */ #ifndef DOXYGEN_GENERATING_OUTPUT /* MSVC 2015 can't handle {} here */ : Range<2, T>(NoInit) #endif {} /** @copydoc Range(const VectorType&, const VectorType&) */ constexpr /*implicit*/ Range2D(const Vector2& min, const Vector2& max) noexcept: Range<2, T>(min, max) {} /** @copydoc Range(const Range&) */ template constexpr explicit Range2D(const Range2D& other) noexcept: Range<2, T>(other) {} /** * @brief Construct range from external representation * @todoc Remove workaround when Doxygen no longer chokes on that line */ template::from(std::declval())) #else decltype(Implementation::RangeConverter<2, T, U>()) #endif > constexpr explicit Range2D(const U& other) #ifndef DOXYGEN_GENERATING_OUTPUT /* MSVC 2015 can't handle {} here */ : Range<2, T>(Implementation::RangeConverter<2, T, U>::from(other)) #endif {} /** @copydoc Range(const Range&) */ constexpr /*implicit*/ Range2D(const Range<2, T>& other) noexcept: Range<2, T>(other) {} /** * @brief Bottom left corner * * Equivalent to @ref min(). */ Vector2& bottomLeft() { return Range<2, T>::min(); } constexpr Vector2 bottomLeft() const { return Range<2, T>::min(); } /**< @overload */ /** @brief Bottom right corner */ constexpr Vector2 bottomRight() const { return {Range<2, T>::max().x(), Range<2, T>::min().y()}; } /** @brief Top left corner */ constexpr Vector2 topLeft() const { return {Range<2, T>::min().x(), Range<2, T>::max().y()}; } /** * @brief Top right corner * * Equivalent to @ref max(). */ Vector2& topRight() { return Range<2, T>::max(); } constexpr Vector2 topRight() const { return Range<2, T>::max(); } /**< @overload */ /** @brief Left edge */ T& left() { return Range<2, T>::min().x(); } constexpr T left() const { return Range<2, T>::min().x(); } /**< @overload */ /** @brief Right edge */ T& right() { return Range<2, T>::max().x(); } constexpr T right() const { return Range<2, T>::max().x(); } /**< @overload */ /** @brief Bottom edge */ T& bottom() { return Range<2, T>::min().y(); } constexpr T bottom() const { return Range<2, T>::min().y(); } /**< @overload */ /** @brief Top edge */ T& top() { return Range<2, T>::max().y(); } constexpr T top() const { return Range<2, T>::max().y(); } /**< @overload */ /** * @brief Range width * * @see @ref size() */ T sizeX() const { return Range<2, T>::max().x() - Range<2, T>::min().x(); } /** * @brief Range height * * @see @ref size() */ T sizeY() const { return Range<2, T>::max().y() - Range<2, T>::min().y(); } /** * @brief Range center on X axis * * @see @ref center() */ T centerX() const { return (Range<2, T>::min().x() + Range<2, T>::max().x())/T(2); } /** * @brief Range center on Y axis * * @see @ref center() */ T centerY() const { return (Range<2, T>::min().y() + Range<2, T>::max().y())/T(2); } MAGNUM_RANGE_SUBCLASS_IMPLEMENTATION(2, Range2D, Vector2) }; /** @brief Two-dimensional range See @ref Range for more information. @see @ref Range1D, @ref Range2D */ template class Range3D: public Range<3, T> { public: /** @copydoc Range(ZeroInitT) */ constexpr /*implicit*/ Range3D(ZeroInitT = ZeroInit) noexcept /** @todoc remove workaround when doxygen is sane */ #ifndef DOXYGEN_GENERATING_OUTPUT /* MSVC 2015 can't handle {} here */ : Range<3, T>(ZeroInit) #endif {} /** @copybrief Range(NoInitT) */ explicit Range3D(NoInitT) noexcept /** @todoc remove workaround when doxygen is sane */ #ifndef DOXYGEN_GENERATING_OUTPUT /* MSVC 2015 can't handle {} here */ : Range<3, T>(NoInit) #endif {} /** @copydoc Range(const VectorType&, const VectorType&) */ constexpr /*implicit*/ Range3D(const Vector3& min, const Vector3& max) noexcept: Range<3, T>(min, max) {} /** @copydoc Range(const Range&) */ template constexpr explicit Range3D(const Range3D& other) noexcept: Range<3, T>(other) {} /** * @brief Construct range from external representation * @todoc Remove workaround when Doxygen no longer chokes on that line */ template::from(std::declval()))> constexpr explicit Range3D(const U& other) noexcept #ifndef DOXYGEN_GENERATING_OUTPUT /* MSVC 2015 can't handle {} here */ : Range<3, T>(Implementation::RangeConverter<3, T, U>::from(other)) #endif {} /** @copydoc Range(const Range&) */ constexpr /*implicit*/ Range3D(const Range<3, T>& other) noexcept: Range<3, T>(other) {} /** * @brief Back bottom left corner * * Equivalent to @ref min(). */ Vector3& backBottomLeft() { return Range<3, T>::min(); } constexpr Vector3 backBottomLeft() const { return Range<3, T>::min(); } /**< @overload */ /** @brief Back bottom right corner */ constexpr Vector3 backBottomRight() const { return {Range<3, T>::max().x(), Range<3, T>::min().y(), Range<3, T>::min().z()}; } /** @brief Back top right corner */ constexpr Vector3 backTopLeft() const { return {Range<3, T>::min().x(), Range<3, T>::max().y(), Range<3, T>::min().z()}; } /** @brief Back top right corner */ constexpr Vector3 backTopRight() const { return {Range<3, T>::max().x(), Range<3, T>::max().y(), Range<3, T>::min().z()}; } /** * @brief Front top right corner * * Equivalent to @ref max(). */ Vector3& frontTopRight() { return Range<3, T>::max(); } constexpr Vector3 frontTopRight() const { return Range<3, T>::max(); } /**< @overload */ /** @brief Front top left corner */ constexpr Vector3 frontTopLeft() const { return {Range<3, T>::min().x(), Range<3, T>::max().y(), Range<3, T>::max().z()}; } /** @brief Front bottom right corner */ constexpr Vector3 frontBottomRight() const { return {Range<3, T>::max().x(), Range<3, T>::min().y(), Range<3, T>::max().z()}; } /** @brief Front bottom left corner */ constexpr Vector3 frontBottomLeft() const { return {Range<3, T>::min().x(), Range<3, T>::min().y(), Range<3, T>::max().z()}; } /** @brief Left edge */ T& left() { return Range<3, T>::min().x(); } constexpr T left() const { return Range<3, T>::min().x(); } /**< @overload */ /** @brief Right edge */ T& right() { return Range<3, T>::max().x(); } constexpr T right() const { return Range<3, T>::max().x(); } /**< @overload */ /** @brief Bottom edge */ T& bottom() { return Range<3, T>::min().y(); } constexpr T bottom() const { return Range<3, T>::min().y(); } /**< @overload */ /** @brief Top edge */ T& top() { return Range<3, T>::max().y(); } constexpr T top() const { return Range<3, T>::max().y(); } /**< @overload */ /** @brief Back edge */ T& back() { return Range<3, T>::min().z(); } constexpr T back() const { return Range<3, T>::min().z(); } /**< @overload */ /** @brief Front edge */ T& front() { return Range<3, T>::max().z(); } constexpr T front() const { return Range<3, T>::max().z(); } /**< @overload */ /** * @brief Range width * * @see @ref size() */ T sizeX() const { return Range<3, T>::max().x() - Range<3, T>::min().x(); } /** * @brief Range height * * @see @ref size() */ T sizeY() const { return Range<3, T>::max().y() - Range<3, T>::min().y(); } /** * @brief Range depth * * @see @ref size() */ T sizeZ() const { return Range<3, T>::max().z() - Range<3, T>::min().z(); } /** * * @brief Range center on X axis * * @see @ref center() */ T centerX() const { return (Range<3, T>::min().x() + Range<3, T>::max().x())/T(2); } /** * @brief Range center on Y axis * * @see @ref center() */ T centerY() const { return (Range<3, T>::min().y() + Range<3, T>::max().y())/T(2); } /** * @brief Range center on Z axis * * @see @ref center() */ T centerZ() const { return (Range<3, T>::min().z() + Range<3, T>::max().z())/T(2); } MAGNUM_RANGE_SUBCLASS_IMPLEMENTATION(3, Range3D, Vector3) }; /** @relates Range @brief Join two ranges Returns a range that contains both input ranges. If one of the ranges is empty, only the other is returned. Results are undefined if any range has negative size. */ template inline Range join(const Range& a, const Range& b) { if(a.min() == a.max()) return b; if(b.min() == b.max()) return a; return {min(a.min(), b.min()), max(a.max(), b.max())}; } /** @debugoperator{Magnum::Math::Range} */ template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, const Range& value) { debug << "Range({" << Corrade::Utility::Debug::nospace << value.min()[0]; for(UnsignedInt i = 1; i != dimensions; ++i) debug << Corrade::Utility::Debug::nospace << "," << value.min()[i]; debug << Corrade::Utility::Debug::nospace << "}, {" << Corrade::Utility::Debug::nospace << value.max()[0]; for(UnsignedInt i = 1; i != dimensions; ++i) debug << Corrade::Utility::Debug::nospace << "," << value.max()[i]; return debug << Corrade::Utility::Debug::nospace << "})"; } template Range Range::translated(const VectorType& vector) const { return {_min + vector, _max + vector}; } template Range Range::padded(const VectorType& padding) const { return {_min - padding, _max + padding}; } template Range Range::scaled(const VectorType& scaling) const { return {_min*scaling, _max*scaling}; } }} namespace Corrade { namespace Utility { /** @configurationvalue{Magnum::Math::Range} */ template struct ConfigurationValue> { ConfigurationValue() = delete; /** @brief Writes elements separated with spaces */ static std::string toString(const Magnum::Math::Range& value, const ConfigurationValueFlags flags) { return ConfigurationValue>::toString( reinterpret_cast&>(value), flags); } /** @brief Reads elements separated with whitespace */ static Magnum::Math::Range fromString(const std::string& stringValue, const ConfigurationValueFlags flags) { const auto vec = ConfigurationValue>::fromString(stringValue, flags); return *reinterpret_cast*>(vec.data()); } }; /** @configurationvalue{Magnum::Math::Range2D} */ template struct ConfigurationValue>: public ConfigurationValue> {}; /** @configurationvalue{Magnum::Math::Range3D} */ template struct ConfigurationValue>: public ConfigurationValue> {}; #if !defined(DOXYGEN_GENERATING_OUTPUT) && !defined(__MINGW32__) 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>; #endif }} #endif