diff --git a/doc/changelog.dox b/doc/changelog.dox index fa6ff7716..13ffe7190 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -71,6 +71,8 @@ See also: and @ref Math::Range3D::y() for slicing ranges into lower dimensions - Added @ref Math::intersect() and @ref Math::intersects() as a counterpart to @ref Math::join() for operating with @ref Math::Range instances +- Added @ref Math::Range::contains() overloading taking a range, in addition + to vector - Added @ref Math::Constants::piQuarter() - Added a convenience function @ref Math::select() as a constant interpolation counterpart to @ref Math::lerp() diff --git a/src/Magnum/Math/Range.h b/src/Magnum/Math/Range.h index 329c53ce2..6ba1a3702 100644 --- a/src/Magnum/Math/Range.h +++ b/src/Magnum/Math/Range.h @@ -190,9 +190,41 @@ template class Range { */ 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(); + /** + * @brief Whether given point is contained inside the range + * + * Returns @cpp true @ce if the following holds for all dimensions + * @f$ i @f$, @cpp false @ce otherwise. @f[ + * \bigwedge_i + * (b_i \ge \operatorname{min}(A)_i) \land + * (b_i < \operatorname{max}(A)_i) + * @f] + * + * The range minimum is interpreted as inclusive, maximum as exclusive. + * Results are undefined if the range has negative size. + * @see @ref intersects(), @ref contains(const Range&) const, + * @ref min(), @ref max() + */ + bool contains(const VectorType& b) const { + return (b >= _min).all() && (b < _max).all(); + } + + /** + * @brief Whether another range is fully contained inside this range + * + * Returns @cpp true @ce if the following holds for all dimensions + * @f$ i @f$, @cpp false @ce otherwise. @f[ + * \bigwedge_i + * (\operatorname{min}(B)_i \ge \operatorname{min}(A)_i) \land + * (\operatorname{max}(B)_i \le \operatorname{max}(A)_i) + * @f] + * + * Results are undefined if the range has negative size. + * @see @ref intersects(), @ref contains(const VectorType&) const, + * @ref min(), @ref max() + */ + bool contains(const Range& b) const { + return (b._min >= _min).all() && (b._max <= _max).all(); } private: diff --git a/src/Magnum/Math/Test/RangeTest.cpp b/src/Magnum/Math/Test/RangeTest.cpp index 35dfb9e0d..9d9de731b 100644 --- a/src/Magnum/Math/Test/RangeTest.cpp +++ b/src/Magnum/Math/Test/RangeTest.cpp @@ -115,7 +115,8 @@ struct RangeTest: Corrade::TestSuite::Tester { void padded(); void scaled(); - void contains(); + void containsVector(); + void containsRange(); void intersectIntersects(); void join(); @@ -154,7 +155,8 @@ RangeTest::RangeTest() { &RangeTest::padded, &RangeTest::scaled, - &RangeTest::contains, + &RangeTest::containsVector, + &RangeTest::containsRange, &RangeTest::intersectIntersects, &RangeTest::join, @@ -500,12 +502,52 @@ void RangeTest::scaled() { CORRADE_COMPARE(a.scaled({2, -3}), b); } -void RangeTest::contains() { +void RangeTest::containsVector() { Range2Di a({34, 23}, {47, 30}); CORRADE_VERIFY(a.contains({40, 23})); CORRADE_VERIFY(!a.contains({33, 23})); CORRADE_VERIFY(!a.contains({40, 30})); + + /* Contains a point at min, but not at max */ + CORRADE_VERIFY(a.contains({34, 23})); + CORRADE_VERIFY(!a.contains({47, 30})); +} + +void RangeTest::containsRange() { + Range2Di a({34, 23}, {47, 30}); + + /* Contains whole range with a gap, not the other way around */ + Range2Di b{{35, 25}, {40, 28}}; + CORRADE_VERIFY(a.contains(b)); + CORRADE_VERIFY(!b.contains(a)); + + /* Contains itself, empty range contains itself as well */ + Range2Di c; + CORRADE_VERIFY(a.contains(a)); + CORRADE_VERIFY(b.contains(b)); + CORRADE_VERIFY(c.contains(c)); + + /* Contains zero-sized range inside but not outside */ + Range2Di d{{34, 23}, {34, 23}}; + Range2Di e{{33, 23}, {33, 23}}; + Range2Di f{{47, 30}, {47, 30}}; + Range2Di g{{47, 31}, {47, 31}}; + 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 */ + Range2Di h{{30, 25}, {35, 105}}; + CORRADE_VERIFY(!a.contains(h)); + CORRADE_VERIFY(!h.contains(a)); + + /* Doesn't contain a range touching the edges from the outside */ + Range2Di i{{20, 30}, {34, 40}}; + Range2Di j{{47, 20}, {60, 23}}; + CORRADE_VERIFY(!a.contains(i)); + CORRADE_VERIFY(!a.contains(j)); } void RangeTest::intersectIntersects() {