geom/segment: add segment-segment intersection test

This commit is contained in:
Danny Robson 2024-11-07 12:32:13 +10:00
parent 33816fab94
commit 0c49eb5845
2 changed files with 124 additions and 4 deletions

View File

@ -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

View File

@ -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 ();
}