Browse Source

Math: implemented intersect() and intersects() for Range.

One returns the intersection range, the other a boolean.
pull/272/head
Vladimír Vondruš 8 years ago
parent
commit
6cb57534e6
  1. 2
      doc/changelog.dox
  2. 36
      src/Magnum/Math/Range.h
  3. 54
      src/Magnum/Math/Test/RangeTest.cpp

2
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()

36
src/Magnum/Math/Range.h

@ -129,6 +129,7 @@ template<UnsignedInt dimensions, class T> 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<UnsignedInt dimensions, class T> 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 T> 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<UnsignedInt dimensions, class T> inline Range<dimensions, T> join(const Range<dimensions, T>& a, const Range<dimensions, T>& b) {
@ -571,6 +573,38 @@ template<UnsignedInt dimensions, class T> inline Range<dimensions, T> 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<UnsignedInt dimensions, class T> inline Range<dimensions, T> intersect(const Range<dimensions, T>& a, const Range<dimensions, T>& 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<UnsignedInt dimensions, class T> inline bool intersects(const Range<dimensions, T>& a, const Range<dimensions, T>& b) {
return (a.max() > b.min()).all() && (a.min() < b.max()).all();
}
/** @debugoperator{Range} */
template<UnsignedInt dimensions, class T> Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, const Range<dimensions, T>& value) {
debug << "Range({" << Corrade::Utility::Debug::nospace << value.min()[0];

54
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}};

Loading…
Cancel
Save