geom/segment: add segment/region intersection test
This commit is contained in:
parent
48b04765d7
commit
39bbaa5a80
@ -8,5 +8,61 @@
|
|||||||
|
|
||||||
#include "segment.hpp"
|
#include "segment.hpp"
|
||||||
|
|
||||||
|
#include "ops.hpp"
|
||||||
|
|
||||||
|
#include "../region.hpp"
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TODO: Replace, ported from https://stackoverflow.com/a/100165
|
||||||
|
template <>
|
||||||
|
bool
|
||||||
|
cruft::geom::intersects (cruft::geom::segment2f seg, cruft::region2f rect)
|
||||||
|
{
|
||||||
|
// Find min and max X for the segment
|
||||||
|
auto [maxX, minX] = cruft::maxmin (seg.a.x, seg.b.x);
|
||||||
|
|
||||||
|
// Find the intersection of the segment's and rectangle's x-projections
|
||||||
|
maxX = cruft::min (maxX, maxX > rect.away ().x);
|
||||||
|
minX = cruft::max (minX, rect.p.x);
|
||||||
|
|
||||||
|
// If their projections do not intersect return false
|
||||||
|
if (minX > maxX)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Find corresponding min and max Y for min and max X we found before
|
||||||
|
auto minY = seg.a.y;
|
||||||
|
auto maxY = seg.b.y;
|
||||||
|
|
||||||
|
auto const dx = seg.b.x - seg.a.x;
|
||||||
|
|
||||||
|
if (cruft::abs (dx) > 1e-6f) {
|
||||||
|
auto const a = (seg.b.y - seg.a.y) / dx;
|
||||||
|
auto const b = seg.a.y - a * seg.a.x;
|
||||||
|
|
||||||
|
minY = a * minX + b;
|
||||||
|
maxY = a * maxX + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minY > maxY)
|
||||||
|
std::swap (minY, maxY);
|
||||||
|
|
||||||
|
// Find the intersection of the segment's and rectangle's y-projections
|
||||||
|
maxY = cruft::min (maxY, rect.away ().y);
|
||||||
|
minY = cruft::max (minY, rect.p.y);
|
||||||
|
|
||||||
|
// If Y-projections do not intersect return false
|
||||||
|
if (minY > maxY)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
bool
|
||||||
|
cruft::geom::intersects (cruft::geom::segment2i seg, cruft::region2i rect)
|
||||||
|
{
|
||||||
|
return intersects (seg.cast<float> (), rect.cast<float> ());
|
||||||
|
}
|
||||||
|
@ -16,6 +16,17 @@ namespace cruft::geom {
|
|||||||
struct segment {
|
struct segment {
|
||||||
cruft::point<S,T> a;
|
cruft::point<S,T> a;
|
||||||
cruft::point<S,T> b;
|
cruft::point<S,T> b;
|
||||||
|
|
||||||
|
// Return a copy of this object with the underlying type casted to
|
||||||
|
// the specified type.
|
||||||
|
template <typename CastT>
|
||||||
|
segment<S,CastT>
|
||||||
|
cast (void) const {
|
||||||
|
return {
|
||||||
|
.a = a.template cast<CastT> (),
|
||||||
|
.b = b.template cast<CastT> ()
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "tap.hpp"
|
#include "tap.hpp"
|
||||||
|
|
||||||
#include "geom/segment.hpp"
|
#include "geom/segment.hpp"
|
||||||
|
#include "geom/ops.hpp"
|
||||||
|
#include "region.hpp"
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@ -25,11 +27,54 @@ void test_point_distance (cruft::TAP::logger &tap)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void test_region_intersection (cruft::TAP::logger &tap)
|
||||||
|
{
|
||||||
|
using p = cruft::point2i;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
cruft::geom::segment2i a;
|
||||||
|
cruft::region2i b;
|
||||||
|
bool expected;
|
||||||
|
char const *message;
|
||||||
|
} const TESTS[] = {
|
||||||
|
{
|
||||||
|
.a = { p { 0, 0 }, p { 4, 4 }, },
|
||||||
|
.b = { p { 1, 1 }, p { 2, 2 }, },
|
||||||
|
.expected = true,
|
||||||
|
.message = "rising into"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.a = { p { 0, 0 }, p { 1, 1 } },
|
||||||
|
.b = { p { -2, -2 }, p { 2, 2 } },
|
||||||
|
.expected = true,
|
||||||
|
.message = "completely internal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.a = { p { 0, 0 }, p { 1, 0 } },
|
||||||
|
.b = { p { 0, 0 }, p { 1, 1 } },
|
||||||
|
.expected = true,
|
||||||
|
.message = "glancing horizontal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.a = { p { 0, 0 }, p { -1, -1 } },
|
||||||
|
.b = { p { 0, 0 }, p { 1, 1 } },
|
||||||
|
.expected = true,
|
||||||
|
.message = "common origin",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto const &t: TESTS)
|
||||||
|
tap.expect_eq (intersects (t.a, t.b), t.expected, "region intersection: %!", t.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
int
|
int
|
||||||
main (int, char**)
|
main (int, char**)
|
||||||
{
|
{
|
||||||
cruft::TAP::logger tap;
|
cruft::TAP::logger tap;
|
||||||
test_point_distance (tap);
|
test_point_distance (tap);
|
||||||
|
test_region_intersection (tap);
|
||||||
return tap.status ();
|
return tap.status ();
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user