diff --git a/poly2tri/common/utils.h b/poly2tri/common/utils.h index f71334c..6a20a00 100644 --- a/poly2tri/common/utils.h +++ b/poly2tri/common/utils.h @@ -68,7 +68,11 @@ Orientation Orient2d(const Point& pa, const Point& pb, const Point& pc) double detleft = (pa.x - pc.x) * (pb.y - pc.y); double detright = (pa.y - pc.y) * (pb.x - pc.x); double val = detleft - detright; - if (val > -EPSILON && val < EPSILON) { + +// Using a tolerance here fails on concave-by-subepsilon boundaries +// if (val > -EPSILON && val < EPSILON) { +// Using == on double makes -Wfloat-equal warnings yell at us + if (std::fpclassify(val) == FP_ZERO) { return COLLINEAR; } else if (val > 0) { return CCW; diff --git a/unittest/main.cpp b/unittest/main.cpp index 7941eac..dc86f4a 100644 --- a/unittest/main.cpp +++ b/unittest/main.cpp @@ -49,7 +49,8 @@ BOOST_AUTO_TEST_CASE(QuadTest) BOOST_AUTO_TEST_CASE(NarrowQuadTest) { - // Very narrow quad that demonstrates a failure case during triangulation + // Very narrow quad that used to demonstrate a failure case during + // triangulation std::vector polyline { new p2t::Point(0.0, 0.0), new p2t::Point(1.0e-05, 0.0), @@ -57,12 +58,61 @@ BOOST_AUTO_TEST_CASE(NarrowQuadTest) new p2t::Point(1.0e-04, 3.0e-07) }; p2t::CDT cdt{ polyline }; - BOOST_CHECK_THROW(cdt.Triangulate(), std::runtime_error); + BOOST_CHECK_NO_THROW(cdt.Triangulate()); + const auto result = cdt.GetTriangles(); + BOOST_REQUIRE_EQUAL(result.size(), 2); + BOOST_CHECK(p2t::IsDelaunay(result)); for (const auto p : polyline) { delete p; } } +BOOST_AUTO_TEST_CASE(ConcaveBoundaryTest) +{ + // Concave-by-less-than-epsilon boundaries used to potentially fail + // during triangulation + const double eps = 1e-15; // This gave EdgeEvent - null triangle + std::vector polyline { + new p2t::Point(0,0), + new p2t::Point(0.5,eps), + new p2t::Point(1,0), + new p2t::Point(1-eps,0.836541), + new p2t::Point(1,2), + new p2t::Point(.46,1.46+eps), + new p2t::Point(0,1), + new p2t::Point(eps,0.5) + }; + + const double r2o4 = std::sqrt(2.)/4; + std::vector hole { + new p2t::Point(0.5+r2o4,0.5), + new p2t::Point(0.5,0.5+r2o4), + new p2t::Point(0.5-r2o4,0.5), + new p2t::Point(0.5-eps,0.5-r2o4) + }; + + std::vector interior_points + {{0.21,0.79},{0.21,0.21},{0.79,0.21}}; + + p2t::CDT cdt{ polyline }; + cdt.AddHole(hole); + + for (auto & p : interior_points) + cdt.AddPoint(&p); + + BOOST_CHECK_NO_THROW(cdt.Triangulate()); + const auto result = cdt.GetTriangles(); + BOOST_REQUIRE_EQUAL(result.size(), 18); + BOOST_CHECK(p2t::IsDelaunay(result)); + for (const auto p : polyline) { + delete p; + } + for (const auto p : hole) { + delete p; + } +} + + BOOST_AUTO_TEST_CASE(TestbedFilesTest) { for (const auto& filename : { "custom.dat", "diamond.dat", "star.dat", "test.dat" }) {