From 64636de5af86144606939149ca867948745f4453 Mon Sep 17 00:00:00 2001 From: AndriyAndreyev Date: Tue, 26 Apr 2022 21:46:11 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=A1onsider=20that=20LargeHole=5FDontFill?= =?UTF-8?q?=20is=20true=20if=20angle=20is=20negative?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise, a wrong triangle can be generated Signed-off-by: AndriyAndreyev --- poly2tri/sweep/sweep.cc | 37 +++++++++++++++++++++++++++++++++++++ poly2tri/sweep/sweep.h | 1 + 2 files changed, 38 insertions(+) diff --git a/poly2tri/sweep/sweep.cc b/poly2tri/sweep/sweep.cc index 4f02115..48e8bee 100644 --- a/poly2tri/sweep/sweep.cc +++ b/poly2tri/sweep/sweep.cc @@ -262,6 +262,35 @@ void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) } // True if HoleAngle exceeds 90 degrees. +// LargeHole_DontFill checks if the advancing front has a large hole. +// A "Large hole" is a triangle formed by a sequence of points in the advancing +// front where three neighbor points form a triangle. +// And angle between left-top, bottom, and right-top points is more than 90 degrees. +// The first part of the algorithm reviews only three neighbor points, e.g. named A, B, C. +// Additional part of this logic reviews a sequence of 5 points - +// additionally reviews one point before and one after the sequence of three (A, B, C), +// e.g. named X and Y. +// In this case, angles are XBC and ABY and this if angles are negative or more +// than 90 degrees LargeHole_DontFill returns true. +// But there is a configuration when ABC has a negative angle but XBC or ABY is less +// than 90 degrees and positive. +// Then function LargeHole_DontFill return false and initiates filling. +// This filling creates a triangle ABC and adds it to the advancing front. +// But in the case when angle ABC is negative this triangle goes inside the advancing front +// and can intersect previously created triangles. +// This triangle leads to making wrong advancing front and problems in triangulation in the future. +// Looks like such a triangle should not be created. +// The simplest way to check and fix it is to check an angle ABC. +// If it is negative LargeHole_DontFill should return true and +// not initiate creating the ABC triangle in the advancing front. +// X______A Y +// \ / +// \ / +// \ B / +// | / +// | / +// |/ +// C bool Sweep::LargeHole_DontFill(const Node* node) const { const Node* nextNode = node->next; @@ -269,6 +298,9 @@ bool Sweep::LargeHole_DontFill(const Node* node) const { if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point)) return false; + if (AngleIsNegative(node->point, nextNode->point, prevNode->point)) + return true; + // Check additional points on front. const Node* next2Node = nextNode->next; // "..Plus.." because only want angles on same side as point being added. @@ -283,6 +315,11 @@ bool Sweep::LargeHole_DontFill(const Node* node) const { return true; } +bool Sweep::AngleIsNegative(const Point* origin, const Point* pa, const Point* pb) const { + const double angle = Angle(origin, pa, pb); + return angle < 0; +} + bool Sweep::AngleExceeds90Degrees(const Point* origin, const Point* pa, const Point* pb) const { const double angle = Angle(origin, pa, pb); return ((angle > PI_div2) || (angle < -PI_div2)); diff --git a/poly2tri/sweep/sweep.h b/poly2tri/sweep/sweep.h index 895b0e8..d12720e 100644 --- a/poly2tri/sweep/sweep.h +++ b/poly2tri/sweep/sweep.h @@ -172,6 +172,7 @@ private: // Decision-making about when to Fill hole. // Contributed by ToolmakerSteve2 bool LargeHole_DontFill(const Node* node) const; + bool AngleIsNegative(const Point* origin, const Point* pa, const Point* pb) const; bool AngleExceeds90Degrees(const Point* origin, const Point* pa, const Point* pb) const; bool AngleExceedsPlus90DegreesOrIsNegative(const Point* origin, const Point* pa, const Point* pb) const; double Angle(const Point* origin, const Point* pa, const Point* pb) const; From 1bf8fad5a2bfef4915fe64c1279e1fd02eb7d340 Mon Sep 17 00:00:00 2001 From: AndriyAndreyev Date: Tue, 26 Apr 2022 21:46:45 +0300 Subject: [PATCH 2/3] Add test case to check stack overflow crash Without additional check is angle negative or not in the LargeHole_DontFill function this test case leads to stack overflow. Signed-off-by: AndriyAndreyev --- testbed/data/stalactite.dat | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 testbed/data/stalactite.dat diff --git a/testbed/data/stalactite.dat b/testbed/data/stalactite.dat new file mode 100644 index 0000000..5aae3d0 --- /dev/null +++ b/testbed/data/stalactite.dat @@ -0,0 +1,14 @@ +450 2250 +450 1750 +400 1700 +350 1650 +350 500 +1050 1700 +HOLE +980 1636 +950 1600 +650 1230 +625 1247 +600 1250 +591 1350 +550 2050 From 563239da078b2557faf0dc4a61bd0b71087ea19f Mon Sep 17 00:00:00 2001 From: AndriyAndreyev Date: Fri, 6 May 2022 00:49:31 +0300 Subject: [PATCH 3/3] Add unittest to check stack overflow crash Signed-off-by: AndriyAndreyev --- unittest/main.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/unittest/main.cpp b/unittest/main.cpp index 2a1f5b4..69c49aa 100644 --- a/unittest/main.cpp +++ b/unittest/main.cpp @@ -188,6 +188,41 @@ BOOST_AUTO_TEST_CASE(PolygonTest03) } } +BOOST_AUTO_TEST_CASE(PolygonTest04) +{ + std::vector polyline { + new p2t::Point(450, 2250), + new p2t::Point(450, 1750), + new p2t::Point(400, 1700), + new p2t::Point(350, 1650), + new p2t::Point(350, 500), + new p2t::Point(1050, 1700) + }; + + std::vector hole { + new p2t::Point(980, 1636), + new p2t::Point(950, 1600), + new p2t::Point(650, 1230), + new p2t::Point(625, 1247), + new p2t::Point(600, 1250), + new p2t::Point(591, 1350), + new p2t::Point(550, 2050) + }; + + p2t::CDT cdt{ polyline }; + cdt.AddHole(hole); + + BOOST_CHECK_NO_THROW(cdt.Triangulate()); + const auto result = cdt.GetTriangles(); + BOOST_REQUIRE_EQUAL(result.size(), 13); + 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" }) {