From 6cb57534e69f8d7e49536a7edef7b056752b0410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 13 Aug 2018 20:45:34 +0200 Subject: [PATCH] Math: implemented intersect() and intersects() for Range. One returns the intersection range, the other a boolean. --- doc/changelog.dox | 2 ++ src/Magnum/Math/Range.h | 36 +++++++++++++++++++- src/Magnum/Math/Test/RangeTest.cpp | 54 ++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index df1273c28..fa6ff7716 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -69,6 +69,8 @@ See also: - Added @ref Math::Range2D::x(), @ref Math::Range3D::x(), @ref Math::Range2D::y(), @ref Math::Range3D::y(), @ref Math::Range3D::z() 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::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 8d1c95148..329c53ce2 100644 --- a/src/Magnum/Math/Range.h +++ b/src/Magnum/Math/Range.h @@ -129,6 +129,7 @@ template class Range { /** * @brief Minimal coordinates (inclusive) * + * Denoted as @f$ \operatorname{min}(A) @f$ in related math equations. * @see @ref size(), @ref Range2D::bottomLeft(), * @ref Range3D::backBottomLeft() */ @@ -138,6 +139,7 @@ template class Range { /** * @brief Maximal coordinates (exclusive) * + * Denoted as @f$ \operatorname{max}(A) @f$ in related math equations. * @see @ref size(), @ref Range2D::topRight(), * @ref Range3D::frontTopRight() */ @@ -562,7 +564,7 @@ template class Range3D: public Range<3, T> { @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 +only the other is returned. Results are undefined if any range has a negative size. */ template inline Range join(const Range& a, const Range& b) { @@ -571,6 +573,38 @@ template inline Range join(const return {min(a.min(), b.min()), max(a.max(), b.max())}; } +/** @relatesalso Range +@brief Intersect two ranges + +Returns a range that covers the intersection of both ranges. If the +intersection is empty, a default-constructed range is returned. The range +minimum is interpreted as inclusive, maximum as exclusive. Results are +undefined if any range has a negative size. +@see @ref intersects() +*/ +template inline Range intersect(const Range& a, const Range& b) { + if(!intersects(a, b)) return {}; + return {max(a.min(), b.min()), min(a.max(), b.max())}; +} + +/** @relatesalso Range +@brief Whether two ranges intersect + +Returns @cpp true @ce if the following holds for all dimensions @f$ i @f$, +@cpp false @ce otherwise. @f[ + \bigwedge_i + (\operatorname{max}(A)_i > \operatorname{min}(B)_i) \land + (\operatorname{min}(A)_i < \operatorname{max}(B)_i) +@f] +The range minimum is interpreted as inclusive, maximum as exclusive. Results +are undefined if any range has a negative size. +@see @ref Range::contains(), @ref intersect(), @ref join(), @ref Range::min(), + @ref Range::max() +*/ +template inline bool intersects(const Range& a, const Range& b) { + return (a.max() > b.min()).all() && (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]; diff --git a/src/Magnum/Math/Test/RangeTest.cpp b/src/Magnum/Math/Test/RangeTest.cpp index 33d2c00f7..35dfb9e0d 100644 --- a/src/Magnum/Math/Test/RangeTest.cpp +++ b/src/Magnum/Math/Test/RangeTest.cpp @@ -116,6 +116,7 @@ struct RangeTest: Corrade::TestSuite::Tester { void scaled(); void contains(); + void intersectIntersects(); void join(); void subclassTypes(); @@ -154,6 +155,7 @@ RangeTest::RangeTest() { &RangeTest::scaled, &RangeTest::contains, + &RangeTest::intersectIntersects, &RangeTest::join, &RangeTest::subclassTypes, @@ -506,6 +508,58 @@ void RangeTest::contains() { CORRADE_VERIFY(!a.contains({40, 30})); } +void RangeTest::intersectIntersects() { + Range2Di a({34, 23}, {47, 30}); + + /* Intersects itself */ + CORRADE_VERIFY(Math::intersects(a, a)); + CORRADE_COMPARE(Math::intersect(a, a), a); + + /* Non-empty intersection */ + Range2Di b{{30, 25}, {35, 105}}; + Range2Di c{{34, 25}, {35, 30}}; + 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 */ + Range2Di d{{130, -15}, {130, -15}}; + CORRADE_VERIFY(!Math::intersects(a, d)); + CORRADE_VERIFY(!Math::intersects(d, a)); + CORRADE_COMPARE(Math::intersect(a, d), Range2Di{}); + CORRADE_COMPARE(Math::intersect(d, a), Range2Di{}); + + /* Intersecting with an empty range inside produces an empty range */ + Range2Di e{{40, 25}, {40, 25}}; + 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 */ + Range2Di i{{20, 30}, {34, 40}}; + Range2Di j{{47, 20}, {60, 23}}; + Range2Di k{{20, 20}, {34, 23}}; + Range2Di l{{47, 30}, {60, 40}}; + CORRADE_VERIFY(!Math::intersects(a, i)); + CORRADE_VERIFY(!Math::intersects(a, j)); + CORRADE_VERIFY(!Math::intersects(a, k)); + CORRADE_VERIFY(!Math::intersects(a, l)); + CORRADE_VERIFY(!Math::intersects(i, a)); + CORRADE_VERIFY(!Math::intersects(j, a)); + CORRADE_VERIFY(!Math::intersects(k, a)); + CORRADE_VERIFY(!Math::intersects(l, a)); + CORRADE_COMPARE(Math::intersect(a, i), Range2Di{}); + CORRADE_COMPARE(Math::intersect(a, j), Range2Di{}); + CORRADE_COMPARE(Math::intersect(a, k), Range2Di{}); + CORRADE_COMPARE(Math::intersect(a, l), Range2Di{}); + CORRADE_COMPARE(Math::intersect(i, a), Range2Di{}); + CORRADE_COMPARE(Math::intersect(j, a), Range2Di{}); + CORRADE_COMPARE(Math::intersect(k, a), Range2Di{}); + CORRADE_COMPARE(Math::intersect(l, a), Range2Di{}); +} + void RangeTest::join() { Range2Di a{{12, 20}, {15, 35}}; Range2Di b{{10, 25}, {17, 105}};