geom/segment: add segment-segment intersection test
This commit is contained in:
parent
33816fab94
commit
0c49eb5845
@ -69,6 +69,72 @@ cruft::geom::intersects (cruft::geom::segment2i seg, cruft::region2i rect)
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <>
|
||||
bool
|
||||
cruft::geom::intersects (cruft::geom::segment2f ab, cruft::point2f p)
|
||||
{
|
||||
auto const hi = max (ab.a, ab.b);
|
||||
auto const lo = min (ab.a, ab.b);
|
||||
|
||||
return all (p <= hi) and all (p >= lo);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// segment-segment intersection in 2D is derived from
|
||||
// https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect
|
||||
//
|
||||
// The idea is to check the winding of 4 triangles created by the segment.
|
||||
//
|
||||
// Co-linear segments need some additional tests to determine if points of one segment lie on the other.
|
||||
|
||||
|
||||
/// \return 0 if colinear, 1 for clockwise, -1 for counter-clockwise
|
||||
static int
|
||||
winding (
|
||||
cruft::point2f const p,
|
||||
cruft::point2f const q,
|
||||
cruft::point2f const r
|
||||
) {
|
||||
auto const qp = q - p;
|
||||
auto const rq = r - q;
|
||||
|
||||
auto const c = cross (qp, rq);
|
||||
|
||||
if (cruft::exactly_zero (c))
|
||||
return 0;
|
||||
else if (c > 0)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <>
|
||||
bool
|
||||
cruft::geom::intersects (
|
||||
cruft::geom::segment2f const s0,
|
||||
cruft::geom::segment2f const s1
|
||||
) {
|
||||
auto const w0 = winding (s0.a, s0.b, s1.a);
|
||||
auto const w1 = winding (s0.a, s0.b, s1.b);
|
||||
auto const w2 = winding (s1.a, s1.b, s0.a);
|
||||
auto const w3 = winding (s1.a, s1.b, s0.b);
|
||||
|
||||
if (w0 != w1 and w2 != w3)
|
||||
return true;
|
||||
|
||||
if (w0 == 0 and intersects (s0, s1.a)) return true;
|
||||
if (w1 == 0 and intersects (s0, s1.b)) return true;
|
||||
if (w2 == 0 and intersects (s1, s0.a)) return true;
|
||||
if (w3 == 0 and intersects (s1, s0.b)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <>
|
||||
cruft::geom::aabb3f
|
||||
|
@ -6,7 +6,8 @@
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void test_point_distance (cruft::TAP::logger &tap)
|
||||
void
|
||||
test_point_distance (cruft::TAP::logger &tap)
|
||||
{
|
||||
static struct {
|
||||
cruft::point3f a, b;
|
||||
@ -27,8 +28,9 @@ void test_point_distance (cruft::TAP::logger &tap)
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void test_region_intersection (cruft::TAP::logger &tap)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void
|
||||
test_region_intersection (cruft::TAP::logger &tap)
|
||||
{
|
||||
using p = cruft::point2i;
|
||||
|
||||
@ -70,7 +72,58 @@ void test_region_intersection (cruft::TAP::logger &tap)
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void test_bresenham (cruft::TAP::logger &tap)
|
||||
void
|
||||
test_segment_intersection (cruft::TAP::logger &tap)
|
||||
{
|
||||
using cruft::geom::segment2f;
|
||||
using cruft::point2f;
|
||||
|
||||
static struct {
|
||||
segment2f a;
|
||||
segment2f b;
|
||||
bool expected;
|
||||
char const *message;
|
||||
} TESTS[] = {
|
||||
{
|
||||
.a = { { 1, 1 }, { 10, 1 } },
|
||||
.b = { { 1, 2 }, { 10, 2 } },
|
||||
.expected = false,
|
||||
.message = "parallel"
|
||||
},
|
||||
{
|
||||
.a = { { 10, 0 }, { 0, 10 } },
|
||||
.b = { { 0, 0 }, { 10, 10 } },
|
||||
.expected = true,
|
||||
.message = "right angles"
|
||||
},
|
||||
{
|
||||
.a = { { -5, -5 }, { 0, 0 } },
|
||||
.b = { { 1, 1 }, { 10, 10 } },
|
||||
.expected = false,
|
||||
.message = "co-linear stopping early",
|
||||
},
|
||||
{
|
||||
.a = { { -5, -5 }, { 5, 5 } },
|
||||
.b = { { 0, 0 }, { 10, 10 } },
|
||||
.expected = true,
|
||||
.message = "co-linear stopping midway",
|
||||
},
|
||||
};
|
||||
|
||||
for (auto const &t: TESTS) {
|
||||
tap.expect_eq (
|
||||
t.expected,
|
||||
intersects (t.a, t.b),
|
||||
"segment-segment {}",
|
||||
t.message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void
|
||||
test_bresenham (cruft::TAP::logger &tap)
|
||||
{
|
||||
static struct {
|
||||
cruft::point2i a;
|
||||
@ -120,6 +173,7 @@ main (int, char**)
|
||||
cruft::TAP::logger tap;
|
||||
test_point_distance (tap);
|
||||
test_region_intersection (tap);
|
||||
test_segment_intersection (tap);
|
||||
test_bresenham (tap);
|
||||
return tap.status ();
|
||||
}
|
Loading…
Reference in New Issue
Block a user