#include "region.hpp" #include "point.hpp" #include "tap.hpp" #include //----------------------------------------------------------------------------- int main (int, char **) { cruft::TAP::logger tap; // check that two overlapping regions successfully test for intersection { const cruft::point2d ap { 32.7, -6.09703 }; const cruft::extent2d ae { 0.8, 2. }; const cruft::region2d a (ap, ae); const cruft::point2d bp {33.5, -4.5}; const cruft::extent2d be { 0.5, 0.5 }; const cruft::region2d b (bp, be); tap.expect (!a.intersects (b), "simple 2d intersection"); } { using p_t = cruft::point2i; using e_t = cruft::extent2i; struct { cruft::region2i a; cruft::region2i b; bool intersection; char const *message; } const TESTS[] = { { .a = { p_t { 58, 22 }, e_t { 13, 8 } }, .b = { p_t { 58, 22 }, e_t { 7, 7 } }, .intersection = true, .message = "Coaligned base", }, { .a = { p_t { 21, 32 }, e_t { 13, 8 } }, .b = { p_t { 27, 33 }, e_t { 24, 24 } }, .intersection = true, .message = "corner overlap", } }; for (auto const &[a, b, res, msg]: TESTS) { tap.expect_eq (a.intersects (b), res, "intersection a-on-b: {}", msg); tap.expect_eq (b.intersects (a), res, "intersection b-on-a: {}", msg); } } // check various floating point maximums successfully test for // intersection. the concern is that infinities are incorrectly handled // in some comparisons. tap.expect ( cruft::region2d::max ().intersects ( cruft::region2d::unit () ), "maximal region2d intersection" ); tap.expect ( cruft::region2f::max ().intersects ( cruft::region2f::unit () ), "maximal region2f intersection" ); // ensure unit regions are... unit sized... tap.expect_eq (cruft::region2d::unit ().area (), 1.0, "unit region2d area"); tap.expect_eq (cruft::region2f::unit ().area (), 1.0f, "unit region2f area"); // test boundary cases of includes and contains { const cruft::point2f p0 { 0 }; const cruft::extent2f e0 { 2 }; const cruft::region2f r {p0, e0}; tap.expect (!r.inclusive (cruft::point2f {-1, 1}), "region/point inclusive, invalid x"); tap.expect (!r.inclusive (cruft::point2f { 1, 3}), "region/point inclusive, invalid y "); tap.expect (r.inclusive (cruft::point2f {1, 1}), "region/point inclusive, centre"); tap.expect (r.inclusive (cruft::point2f {0, 0}), "region/point inclusive, base"); tap.expect (r.inclusive (cruft::point2f {2, 2}), "region/point inclusive, corner"); } // ensure union operator behaves as expected { const cruft::point2f p { -1 }; const cruft::extent2f e { 2 }; const cruft::region2f r { p, e }; tap.expect_eq (r | cruft::point2f { 0, 0 }, r, "identity union"); tap.expect_eq ( r | cruft::point2f { 2, 3 }, cruft::region2f { p, cruft::extent2f { 3, 4 } }, "positive expanding union" ); tap.expect_eq ( r | cruft::point2f { -3, -2 }, cruft::region2f { cruft::point2f { -3, -2 }, cruft::extent2f { 4, 3 } }, "negative expanding union" ); }; // Check that 'covers' behaves as expected. ie, a covers b. { using p = cruft::point2i; using e = cruft::extent2i; struct { cruft::region2i a; cruft::region2i b; bool expected; char const *message; } TESTS[] = { { .a = { p { 0, 0, }, e { 32, 32, } }, .b = { p { 27, 4, }, e { 8, 13, } }, .expected = false, .message = "a exceeds +x margin", } }; for (auto const &[a, b, expected, msg]: TESTS) tap.expect_eq (a.covers (b), expected, "covers: {}", msg); } // ensure make_region covers the expected values { const cruft::region2i REGION { cruft::point2i { 0, 0 }, cruft::point2i { 3, 2 } }; const cruft::point2i EXPECTED[] = { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 0, 1 }, { 1, 1 }, { 2, 1 }, }; std::vector values; std::copy (std::cbegin (REGION.step ()), std::cend (REGION.step ()), std::back_inserter (values)); bool success = values.size () == std::size (EXPECTED) && std::equal (std::cbegin (values), std::cend (values), std::cbegin (EXPECTED), std::cend (EXPECTED)); tap.expect (success, "make_range(region2i)"); }; // test distance2 for points { struct { cruft::region2f r; cruft::point2f p; float distance2; char const *message; } const TESTS[] = { { { cruft::point2f { 1, 1 }, cruft::point2f { 3, 5 } }, { 0, 0 }, 2.f, "origin to offset rect" }, { { cruft::point2f { 1, 1 }, cruft::point2f { 3, 5 } }, { 2, 2 }, 0.f, "point inside region" }, { { cruft::point2f { 1, 1 }, cruft::point2f { 3, 5 } }, { 4, 7 }, 1 + 4, "offset point to offset rect" }, }; for (auto const &t: TESTS) { auto const d2 = distance2 (t.r, t.p); tap.expect_eq (d2, t.distance2, "region-point distance2: {}", t.message); } }; // Test expected results of 2 region interesection { using p_t = cruft::point2i; struct { cruft::region2i a; cruft::region2i b; cruft::region2i res; char const *msg; } const TESTS[] = { { .a = { p_t { 0, 0 }, p_t { 1, 1 } }, .b = { p_t { 0, 0 }, p_t { 1, 1 } }, .res = { p_t { 0, 0 }, p_t { 1, 1 } }, .msg = "identical, unit sizes", }, { .a = { p_t { 0, 0 }, p_t { 1, 1 } }, .b = { p_t { 1, 1 }, p_t { 2, 2 } }, .res = { p_t { 1, 1 }, p_t { 1, 1 } }, .msg = "unit sized, shared corner", }, { .a = { p_t { -4, -5 }, p_t { 5, 3 } }, .b = { p_t { -1, -2 }, p_t { 2, 1 } }, .res = { p_t { -1, -2 }, p_t { 2, 1 } }, .msg = "a contains b", }, { .a = { p_t { -1, -2 }, p_t { 2, 1 } }, .b = { p_t { -4, -5 }, p_t { 5, 3 } }, .res = { p_t { -1, -2 }, p_t { 2, 1 } }, .msg = "b contains a", }, { .a = { p_t { -3, -1 }, p_t { 4, 8 } }, .b = { p_t { -1, -4 }, p_t { 6, 2 } }, .res = { p_t { -1, -1 }, p_t { 4, 2 } }, .msg = "partial overlap", }, }; for (auto const &[a, b, res, message]: TESTS) tap.expect_eq (intersection (a, b), res, "region-region interesection: {}", message); } // Test rotate behaves as expected. // // It's important that the following constraints apply to the test data: // * no point lies at the origin because we want to catch // rotate-about-point errors that need a translation. // * the size is non-square so we can differentiate between rotated // values // * use floating point values so we can test for loss of precision. { cruft::point2f const base { 1, 2 }; cruft::extent2f const size { 3, 4 }; cruft::region2f const orig { base, size }; struct { int rotation; cruft::region2f res; } const TESTS[] { { .rotation = 0, .res = orig, }, { .rotation = 1, .res = { cruft::point2f { -3, 2 }, cruft::extent2f { 4, 3 }, }, }, { .rotation = 2, .res = { cruft::point2f { 1, -2 }, cruft::extent2f { 3, 4 }, }, }, { .rotation = 3, .res = { cruft::point2f { 1, 2 }, cruft::extent2f { 4, 3 }, }, } }; for (auto const &obj: TESTS) { auto const computed = rotate90 (orig, obj.rotation); tap.expect_eq (computed, obj.res, "{} rotation", obj.rotation); } } return tap.status (); }