diff --git a/doc/changelog.dox b/doc/changelog.dox index 75d5d34e8..adc678297 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -528,6 +528,12 @@ See also: working to fail with an assertion. See the new @ref Math::Matrix3::rotationShear() and @ref Math::Matrix4::rotationShear() accessors for a possible solution. +- Since one-dimensional @ref Math::Range is now used on many new places such + as the @ref Animation library, its underlying type is just @cpp T @ce + instead of @ref Math::Vector "Math::Vector<1, T>" to make it easier to use. + This may break rare existing code that was using one-dimensional ranges or + generic code that's always expecting a vector type. See + @ref Math-Range-generic for possible solutions. - @ref SceneGraph::Camera::setViewport() and @ref SceneGraph::Camera::draw() are no longer @cpp virtual @ce functions. If you need to override their functionality, simply call the subclass implementation directly instead of diff --git a/doc/snippets/MagnumMath.cpp b/doc/snippets/MagnumMath.cpp index 39f0584cb..c91bd31d8 100644 --- a/doc/snippets/MagnumMath.cpp +++ b/doc/snippets/MagnumMath.cpp @@ -873,6 +873,19 @@ Debug{} << Math::Vector3{a}; // prints {16968, 48552, 15993} /* [Half-usage-vector] */ } +{ +Range1D range, a, b; +constexpr UnsignedInt dimensions = 1; +typedef Float T; +/* [Range-generic] */ +Math::Vector min = range.min(); // works for 1D, 2D and 3D + +T c = Math::max(a.size(), b.size()).product(); // vector max() +/* [Range-generic] */ +static_cast(min); +static_cast(c); +} + { /* [Range-construct-minmax] */ Vector3 a, b, c; diff --git a/src/Magnum/Animation/Player.hpp b/src/Magnum/Animation/Player.hpp index 5774dff18..1f933bb6e 100644 --- a/src/Magnum/Animation/Player.hpp +++ b/src/Magnum/Animation/Player.hpp @@ -250,7 +250,7 @@ template std::pair Player::elapsed(const T startTime = _startTime; T pauseTime = _stopPauseTime; State state = _state; - const K duration = _duration.size()[0]; + const K duration = _duration.size(); const Containers::Optional> elapsed = Implementation::playerElapsed(duration, _playCount, _scaler, time, startTime, pauseTime, state); if(elapsed) return *elapsed; @@ -282,12 +282,12 @@ template std::pair Player::elapsed(const template Player& Player::advance(const T time) { /* Get the elapsed time. If we shouldn't advance anything (player already stopped / not yet playing, quit */ - Containers::Optional> elapsed = Implementation::playerElapsed(_duration.size()[0], _playCount, _scaler, time, _startTime, _stopPauseTime, _state); + Containers::Optional> elapsed = Implementation::playerElapsed(_duration.size(), _playCount, _scaler, time, _startTime, _stopPauseTime, _state); if(!elapsed) return *this; /* Advance all tracks. Properly handle durations that don't start at 0. */ for(Track& t: _tracks) - t.advancer(t.track, _duration.min()[0] + elapsed->second, t.hint, t.destination, t.userCallback, t.userCallbackData); + t.advancer(t.track, _duration.min() + elapsed->second, t.hint, t.destination, t.userCallback, t.userCallbackData); return *this; } diff --git a/src/Magnum/Animation/Test/PlayerCustomTest.cpp b/src/Magnum/Animation/Test/PlayerCustomTest.cpp index 38c19b27c..66334000b 100644 --- a/src/Magnum/Animation/Test/PlayerCustomTest.cpp +++ b/src/Magnum/Animation/Test/PlayerCustomTest.cpp @@ -67,7 +67,7 @@ void PlayerCustomTest::test() { player.add(Track, value) .play(2000000ull); - CORRADE_COMPARE(player.duration().size()[0], 24*3); + CORRADE_COMPARE(player.duration().size(), 24*3); /* 1.75 secs in */ player.advance(3750000ull); diff --git a/src/Magnum/Animation/Test/PlayerTest.cpp b/src/Magnum/Animation/Test/PlayerTest.cpp index 0c84640c6..a4a066479 100644 --- a/src/Magnum/Animation/Test/PlayerTest.cpp +++ b/src/Magnum/Animation/Test/PlayerTest.cpp @@ -1230,7 +1230,7 @@ void PlayerTest::runFor100YearsFloat() { /* The track must fit an integer number of times into the day for this test to work (3 seconds do fit) */ - CORRADE_COMPARE(player.duration().size()[0], 3.0f); + CORRADE_COMPARE(player.duration().size(), 3.0f); CORRADE_COMPARE(player.state(), State::Playing); CORRADE_COMPARE(value, -1.0f); @@ -1277,7 +1277,7 @@ void PlayerTest::runFor100YearsChrono() { /* The track must fit an integer number of times into the day for this test to work (3 seconds do fit) */ - CORRADE_COMPARE(player.duration().size()[0], 3.0f); + CORRADE_COMPARE(player.duration().size(), 3.0f); CORRADE_COMPARE(player.state(), State::Playing); CORRADE_COMPARE(value, -1.0f); diff --git a/src/Magnum/GL/AbstractTexture.cpp b/src/Magnum/GL/AbstractTexture.cpp index b6850aaae..d2ab7450e 100644 --- a/src/Magnum/GL/AbstractTexture.cpp +++ b/src/Magnum/GL/AbstractTexture.cpp @@ -1795,7 +1795,7 @@ template void AbstractTexture::subImage(const GLint leve const Math::Vector size = range.size(); const std::size_t dataSize = Magnum::Implementation::imageDataSizeFor(image, size); - const Vector3i paddedOffset = Vector3i::pad(range.min()); + const Vector3i paddedOffset = Vector3i::pad(range.min()); const Vector3i paddedSize = Vector3i::pad(size, 1); /* Reallocate only if needed */ @@ -1818,7 +1818,7 @@ template void AbstractTexture::subImage(const GLint leve const Math::Vector size = range.size(); const std::size_t dataSize = Magnum::Implementation::imageDataSizeFor(image, size); - const Vector3i paddedOffset = Vector3i::pad(range.min()); + const Vector3i paddedOffset = Vector3i::pad(range.min()); const Vector3i paddedSize = Vector3i::pad(size, 1); /* Reallocate only if needed */ @@ -1848,7 +1848,7 @@ template void AbstractTexture::compressedSubImage(const createIfNotAlready(); const Math::Vector size = range.size(); - const Vector3i paddedOffset = Vector3i::pad(range.min()); + const Vector3i paddedOffset = Vector3i::pad(range.min()); const Vector3i paddedSize = Vector3i::pad(size, 1); /* Internal texture format */ @@ -1882,7 +1882,7 @@ template void AbstractTexture::compressedSubImage(const createIfNotAlready(); const Math::Vector size = range.size(); - const Vector3i paddedOffset = Vector3i::pad(range.min()); + const Vector3i paddedOffset = Vector3i::pad(range.min()); const Vector3i paddedSize = Vector3i::pad(size, 1); /* Internal texture format */ diff --git a/src/Magnum/GL/Renderer.cpp b/src/Magnum/GL/Renderer.cpp index 302b5076b..e6636faee 100644 --- a/src/Magnum/GL/Renderer.cpp +++ b/src/Magnum/GL/Renderer.cpp @@ -40,7 +40,7 @@ Range1D Renderer::lineWidthRange() { Range1D& value = state.lineWidthRange; /* Get the value, if not already cached */ - if(value.max().isZero()) + if(!value.max()) value = state.lineWidthRangeImplementation(); return value; @@ -55,7 +55,7 @@ Range1D Renderer::lineWidthRangeImplementationDefault() { #ifndef MAGNUM_TARGET_GLES Range1D Renderer::lineWidthRangeImplementationMesaForwardCompatible() { Range1D value = lineWidthRangeImplementationDefault(); - value.max() = Math::min(1.0f, value.max()[0]); + value.max() = Math::min(1.0f, value.max()); return value; } #endif diff --git a/src/Magnum/GL/Test/RendererGLTest.cpp b/src/Magnum/GL/Test/RendererGLTest.cpp index 3cb9586f7..526b6e54c 100644 --- a/src/Magnum/GL/Test/RendererGLTest.cpp +++ b/src/Magnum/GL/Test/RendererGLTest.cpp @@ -55,7 +55,7 @@ void RendererGLTest::maxLineWidth() { MAGNUM_VERIFY_NO_GL_ERROR(); - Renderer::setLineWidth(lineWidthRange.max()[0]); + Renderer::setLineWidth(lineWidthRange.max()); MAGNUM_VERIFY_NO_GL_ERROR(); } diff --git a/src/Magnum/Math/Range.h b/src/Magnum/Math/Range.h index 367a12870..4be27c574 100644 --- a/src/Magnum/Math/Range.h +++ b/src/Magnum/Math/Range.h @@ -37,7 +37,7 @@ namespace Magnum { namespace Math { namespace Implementation { template struct RangeTraits; - template struct RangeTraits<1, T> { typedef Vector<1, T> Type; }; + template struct RangeTraits<1, T> { typedef T Type; }; template struct RangeTraits<2, T> { typedef Vector2 Type; }; template struct RangeTraits<3, T> { typedef Vector3 Type; }; @@ -47,9 +47,19 @@ namespace Implementation { /** @brief N-dimensional range -Axis-aligned line (in 1D), rectangle (in 2D) or cube (in 3D). Minimal +Axis-aligned line (in 1D), rectangle (in 2D) or cube (in 3D). The minimal coordinate is inclusive, maximal exclusive. See @ref Range1D, @ref Range2D and @ref Range3D specializations for given dimension count. + +@section Math-Range-generic Use in generic code + +While @ref Range2D and @ref Range3D have a vector underlying type, @ref Range1D +is just a scalar. This makes common usage simpler, but may break in generic +code that expects a vector type for any dimension. Solution is to +unconditionally cast the value to a vector type or explicitly specify template +parameters to choose a vector function overload. For example: + +@snippet MagnumMath.cpp Range-generic */ template class Range { template friend class Range; @@ -58,8 +68,8 @@ template class Range { /** * @brief Underlying vector type * - * @ref Math::Vector "Vector<1, T>" in 1D, @ref Math::Vector2 "Vector2" - * in 2D, @ref Math::Vector3 "Vector3" in 3D. + * @cpp T @ce in 1D, @ref Math::Vector2 "Vector2" in 2D, + * @ref Math::Vector3 "Vector3" in 3D. */ typedef typename Implementation::RangeTraits::Type VectorType; @@ -91,10 +101,10 @@ template class Range { * * Construct zero-size range positioned at origin. */ - constexpr /*implicit*/ Range(ZeroInitT = ZeroInit) noexcept: _min{ZeroInit}, _max{ZeroInit} {} + constexpr /*implicit*/ Range(ZeroInitT = ZeroInit) noexcept: Range{ZeroInit, typename std::conditional::type{}} {} /** @brief Construct without initializing the contents */ - explicit Range(NoInitT) noexcept: _min{NoInit}, _max{NoInit} {} + explicit Range(NoInitT) noexcept: Range{NoInit, typename std::conditional::type{}} {} /** @brief Construct a range from minimal and maximal coordinates */ constexpr /*implicit*/ Range(const VectorType& min, const VectorType& max) noexcept: _min{min}, _max{max} {} @@ -143,12 +153,10 @@ template class Range { } /** @brief Equality comparison */ - constexpr bool operator==(const Range& other) const { - return _min == other._min && _max == other._max; - } + bool operator==(const Range& other) const; /** @brief Non-equality comparison */ - constexpr bool operator!=(const Range& other) const { + bool operator!=(const Range& other) const { return !operator==(other); } @@ -156,8 +164,12 @@ template class Range { * @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 */ + T* data() { + return dataInternal(typename std::conditional::type{}); + } + constexpr const T* data() const { + return dataInternal(typename std::conditional::type{}); + } /**< @overload */ /** * @brief Minimal coordinates (inclusive) @@ -258,7 +270,8 @@ template class Range { * @ref min(), @ref max() */ bool contains(const VectorType& b) const { - return (b >= _min).all() && (b < _max).all(); + return (Vector{b} >= _min).all() && + (Vector{b} < _max).all(); } /** @@ -276,10 +289,27 @@ template class Range { * @ref min(), @ref max() */ bool contains(const Range& b) const { - return (b._min >= _min).all() && (b._max <= _max).all(); + return (Vector{b._min} >= _min).all() && + (Vector{b._max} <= _max).all(); } private: + /* Called from Range(ZeroInit), either using the ZeroInit constructor + (if available) or passing zero directly (for scalar types) */ + constexpr explicit Range(ZeroInitT, ZeroInitT*) noexcept: _min{ZeroInit}, _max{ZeroInit} {} + constexpr explicit Range(ZeroInitT, void*) noexcept: _min{T(0)}, _max{T(0)} {} + + /* Called from Range(NoInit), either using the NoInit constructor (if + available) or not doing anything */ + explicit Range(NoInitT, NoInitT*) noexcept: _min{NoInit}, _max{NoInit} {} + explicit Range(NoInitT, void*) noexcept {} + + /* Called from data(), always returning a T* */ + constexpr const VectorType* dataInternal(void*) const { return &_min; } + VectorType* dataInternal(void*) { return &_min; } + constexpr const T* dataInternal(T*) const { return _min.data(); } + T* dataInternal(T*) { return _min.data(); } + VectorType _min, _max; }; @@ -693,21 +723,29 @@ are undefined if any range has a negative size. @ref Range::max() */ template inline bool intersects(const Range& a, const Range& b) { - return (a.max() > b.min()).all() && (a.min() < b.max()).all(); + return (Vector{a.max()} > b.min()).all() && + (Vector{a.min()} < b.max()).all(); } /** @debugoperator{Range} */ template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, const Range& value) { - debug << "Range({" << Corrade::Utility::Debug::nospace << value.min()[0]; + debug << "Range({" << Corrade::Utility::Debug::nospace << Vector{value.min()}[0]; for(UnsignedInt i = 1; i != dimensions; ++i) - debug << Corrade::Utility::Debug::nospace << "," << value.min()[i]; + debug << Corrade::Utility::Debug::nospace << "," << Vector{value.min()}[i]; debug << Corrade::Utility::Debug::nospace << "}, {" - << Corrade::Utility::Debug::nospace << value.max()[0]; + << Corrade::Utility::Debug::nospace << Vector{value.max()}[0]; for(UnsignedInt i = 1; i != dimensions; ++i) - debug << Corrade::Utility::Debug::nospace << "," << value.max()[i]; + debug << Corrade::Utility::Debug::nospace << "," << Vector{value.max()}[i]; return debug << Corrade::Utility::Debug::nospace << "})"; } +template inline bool Range::operator==(const Range& other) const { + /* For non-scalar types default implementation of TypeTraits would be used, + which is just operator== */ + return TypeTraits::equals(_min, other._min) && + TypeTraits::equals(_max, other._max); +} + /* Explicit instantiation for commonly used types */ #ifndef DOXYGEN_GENERATING_OUTPUT extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Range<1, Float>&); diff --git a/src/Magnum/Math/Test/RangeTest.cpp b/src/Magnum/Math/Test/RangeTest.cpp index c8f3e0459..f3f0cb958 100644 --- a/src/Magnum/Math/Test/RangeTest.cpp +++ b/src/Magnum/Math/Test/RangeTest.cpp @@ -52,9 +52,9 @@ template<> struct RangeConverter<1, Float, Dim> { } constexpr static Dim to(const Range<1, Float>& other) { - return {other.min()[0], + return {other.min(), /* Doing it this way to preserve constexpr */ - other.max()[0] - other.min()[0]}; + other.max() - other.min()}; } }; @@ -113,15 +113,26 @@ struct RangeTest: Corrade::TestSuite::Tester { void size(); void center(); + /* Testing 1D separately because it's a scalar, not vector. The above + functions test all dimensions explicitly. */ + void translated(); + void translated1D(); void padded(); + void padded1D(); void scaled(); + void scaled1D(); void scaledFromCenter(); + void scaledFromCenter1D(); - void containsVector(); + void containsPoint(); + void containsPoint1D(); void containsRange(); + void containsRange1D(); void intersectIntersects(); + void intersectIntersects1D(); void join(); + void join1D(); void subclassTypes(); void subclass(); @@ -157,14 +168,22 @@ RangeTest::RangeTest() { &RangeTest::center, &RangeTest::translated, + &RangeTest::translated1D, &RangeTest::padded, + &RangeTest::padded1D, &RangeTest::scaled, + &RangeTest::scaled1D, &RangeTest::scaledFromCenter, + &RangeTest::scaledFromCenter1D, - &RangeTest::containsVector, + &RangeTest::containsPoint, + &RangeTest::containsPoint1D, &RangeTest::containsRange, + &RangeTest::containsRange1D, &RangeTest::intersectIntersects, + &RangeTest::intersectIntersects1D, &RangeTest::join, + &RangeTest::join1D, &RangeTest::subclassTypes, &RangeTest::subclass, @@ -255,11 +274,9 @@ void RangeTest::constructPair() { Vector2i b{30, 18}; Vector2i c{20, 25}; - Range1Di bounds1a{Math::minmax>({a.x(), b.x(), c.x()})}; - Range1Di bounds1b{std::pair, Math::Vector<1, Int>>{10, 30}}; + Range1Di bounds1a{Math::minmax({a.x(), b.x(), c.x()})}; Range1Di bounds1c{10, 30}; CORRADE_COMPARE(bounds1a, bounds1c); - CORRADE_COMPARE(bounds1b, bounds1c); Range2Di bounds2a{Math::minmax({a, b, c})}; Range2Di bounds2b{std::pair, Math::Vector<2, Int>>{{10, 18}, {30, 25}}}; @@ -372,6 +389,16 @@ void RangeTest::access() { constexpr Range2Di crect({34, 23}, {47, 30}); constexpr Range3Di ccube({34, 23, -17}, {47, 30, 12}); + CORRADE_COMPARE(line.data(), static_cast(&line)); + CORRADE_COMPARE(rect.data(), static_cast(&rect)); + CORRADE_COMPARE(cube.data(), static_cast(&cube)); + constexpr Int lineData = *cline.data(); + constexpr Int rectData = *crect.data(); + constexpr Int cubeData = *ccube.data(); + CORRADE_COMPARE(lineData, 34); + CORRADE_COMPARE(rectData, 34); + CORRADE_COMPARE(cubeData, 34); + CORRADE_COMPARE(line.min(), 34); CORRADE_COMPARE(cline.min(), 34); CORRADE_COMPARE(line.max(), 47); @@ -520,6 +547,14 @@ void RangeTest::translated() { CORRADE_COMPARE(a.size(), b.size()); } +void RangeTest::translated1D() { + Range1Di a(34, 47); + Range1Di b(17, 30); + + CORRADE_COMPARE(a.translated(-17), b); + CORRADE_COMPARE(a.size(), b.size()); +} + void RangeTest::padded() { Range2Di a({34, 23}, {47, 30}); Range2Di b({31, 28}, {50, 25}); @@ -528,6 +563,14 @@ void RangeTest::padded() { CORRADE_COMPARE(a.center(), b.center()); } +void RangeTest::padded1D() { + Range1Di a{34, 47}; + Range1Di b{31, 50}; + + CORRADE_COMPARE(a.padded(3), b); + CORRADE_COMPARE(a.center(), b.center()); +} + void RangeTest::scaled() { Range2Di a({34, 23}, {47, 30}); Range2Di b({68, -69}, {94, -90}); @@ -536,6 +579,14 @@ void RangeTest::scaled() { CORRADE_COMPARE((a.size()*Vector2i{2, -3}), b.size()); } +void RangeTest::scaled1D() { + Range1Di a{34, 47}; + Range1Di b{68, 94}; + + CORRADE_COMPARE(a.scaled(2), b); + CORRADE_COMPARE(a.size()*2, b.size()); +} + void RangeTest::scaledFromCenter() { Range2Di a{{34, 22}, {48, 30}}; Range2Di b{{27, 38}, {55, 14}}; @@ -545,7 +596,16 @@ void RangeTest::scaledFromCenter() { CORRADE_COMPARE((a.size()*Vector2i{2, -3}), b.size()); } -void RangeTest::containsVector() { +void RangeTest::scaledFromCenter1D() { + Range1Di a{34, 48}; + Range1Di b{27, 55}; + + CORRADE_COMPARE(a.scaledFromCenter(2), b); + CORRADE_COMPARE(a.center(), b.center()); + CORRADE_COMPARE(a.size()*2, b.size()); +} + +void RangeTest::containsPoint() { Range2Di a({34, 23}, {47, 30}); CORRADE_VERIFY(a.contains({40, 23})); @@ -557,6 +617,17 @@ void RangeTest::containsVector() { CORRADE_VERIFY(!a.contains({47, 30})); } +void RangeTest::containsPoint1D() { + Range1Di a{34, 47}; + + CORRADE_VERIFY(a.contains(40)); + CORRADE_VERIFY(!a.contains(33)); + + /* Contains a point at min, but not at max */ + CORRADE_VERIFY(a.contains(34)); + CORRADE_VERIFY(!a.contains(47)); +} + void RangeTest::containsRange() { Range2Di a({34, 23}, {47, 30}); @@ -593,6 +664,42 @@ void RangeTest::containsRange() { CORRADE_VERIFY(!a.contains(j)); } +void RangeTest::containsRange1D() { + Range1Di a{34, 47}; + + /* Contains whole range with a gap, not the other way around */ + Range1Di b{35, 40}; + CORRADE_VERIFY(a.contains(b)); + CORRADE_VERIFY(!b.contains(a)); + + /* Contains itself, empty range contains itself as well */ + Range1Di c; + CORRADE_VERIFY(a.contains(a)); + CORRADE_VERIFY(b.contains(b)); + CORRADE_VERIFY(c.contains(c)); + + /* Contains zero-sized range inside but not outside */ + Range1Di d{34, 34}; + Range1Di e{33, 33}; + Range1Di f{47, 47}; + Range1Di g{48, 48}; + CORRADE_VERIFY(a.contains(d)); + CORRADE_VERIFY(!a.contains(e)); + CORRADE_VERIFY(a.contains(f)); + CORRADE_VERIFY(!a.contains(g)); + + /* Doesn't contain a range that overlaps */ + Range1Di h{30, 35}; + CORRADE_VERIFY(!a.contains(h)); + CORRADE_VERIFY(!h.contains(a)); + + /* Doesn't contain a range touching the edges from the outside */ + Range1Di i{20, 34}; + Range1Di j{47, 60}; + CORRADE_VERIFY(!a.contains(i)); + CORRADE_VERIFY(!a.contains(j)); +} + void RangeTest::intersectIntersects() { Range2Di a({34, 23}, {47, 30}); @@ -645,6 +752,48 @@ void RangeTest::intersectIntersects() { CORRADE_COMPARE(Math::intersect(l, a), Range2Di{}); } +void RangeTest::intersectIntersects1D() { + Range1Di a(34, 47); + + /* Intersects itself */ + CORRADE_VERIFY(Math::intersects(a, a)); + CORRADE_COMPARE(Math::intersect(a, a), a); + + /* Non-empty intersection */ + Range1Di b{30, 35}; + Range1Di c{34, 35}; + CORRADE_VERIFY(Math::intersects(a, b)); + CORRADE_VERIFY(Math::intersects(b, a)); + CORRADE_COMPARE(Math::intersect(a, b), c); + CORRADE_COMPARE(Math::intersect(b, a), c); + + /* Intersecting with an empty range outside produces a default-constructed range */ + Range1Di d{130, 130}; + CORRADE_VERIFY(!Math::intersects(a, d)); + CORRADE_VERIFY(!Math::intersects(d, a)); + CORRADE_COMPARE(Math::intersect(a, d), Range1Di{}); + CORRADE_COMPARE(Math::intersect(d, a), Range1Di{}); + + /* Intersecting with an empty range inside produces an empty range */ + Range1Di e{40, 40}; + CORRADE_VERIFY(Math::intersects(a, e)); + CORRADE_VERIFY(Math::intersects(e, a)); + CORRADE_COMPARE(Math::intersect(a, e), e); + CORRADE_COMPARE(Math::intersect(e, a), e); + + /* Doesn't intersect a range touching the edges from the outside */ + Range1Di i{20, 34}; + Range1Di j{47, 60}; + CORRADE_VERIFY(!Math::intersects(a, i)); + CORRADE_VERIFY(!Math::intersects(a, j)); + CORRADE_VERIFY(!Math::intersects(i, a)); + CORRADE_VERIFY(!Math::intersects(j, a)); + CORRADE_COMPARE(Math::intersect(a, i), Range1Di{}); + CORRADE_COMPARE(Math::intersect(a, j), Range1Di{}); + CORRADE_COMPARE(Math::intersect(i, a), Range1Di{}); + CORRADE_COMPARE(Math::intersect(j, a), Range1Di{}); +} + void RangeTest::join() { Range2Di a{{12, 20}, {15, 35}}; Range2Di b{{10, 25}, {17, 105}}; @@ -657,6 +806,18 @@ void RangeTest::join() { CORRADE_COMPARE(Math::join(c, a), a); } +void RangeTest::join1D() { + Range1Di a{12, 15}; + Range1Di b{10, 17}; + Range1Di c{130, 130}; + Range1Di d{10, 17}; + + CORRADE_COMPARE(Math::join(a, b), d); + CORRADE_COMPARE(Math::join(b, a), d); + CORRADE_COMPARE(Math::join(a, c), a); + CORRADE_COMPARE(Math::join(c, a), a); +} + template class BasicRect: public Math::Range<2, T> { public: template constexpr BasicRect(U&&... args): Math::Range<2, T>{args...} {}