From a4b396535fe82529a79a474986ecf8a3261330ab Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 24 Apr 2018 20:56:09 +0200 Subject: [PATCH] Fixed #116 (polygon cutting issue) Last step: polygon cut algorithm with fallback: merge before cut on invalid polygons. --- src/db/db/dbPolygonTools.cc | 159 +++++++++++++++++++++++-- src/db/unit_tests/dbPolygonTools.cc | 172 ++++++++++++++++++++++++++++ 2 files changed, 324 insertions(+), 7 deletions(-) diff --git a/src/db/db/dbPolygonTools.cc b/src/db/db/dbPolygonTools.cc index 515d6c47f..77f7c9dcd 100644 --- a/src/db/db/dbPolygonTools.cc +++ b/src/db/db/dbPolygonTools.cc @@ -30,6 +30,7 @@ #include #include +#include namespace db { @@ -187,7 +188,7 @@ public: }; template -void cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line) +static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line) { typedef typename PolygonType::point_type point_type; typedef typename PolygonType::coord_type coord_type; @@ -268,7 +269,7 @@ void cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygo if (nc == 0) { // the hull is fully on the right side -> just output the input polygon and that's it. right_of_line->put (&input); - return; + return true; } else { // remember hole contours for later assignment hole_polygons.push_back (PolygonType ()); @@ -310,7 +311,7 @@ void cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygo } } if (jj == loose_ends.end ()) { - tl_assert (false); // @@@ cannot cut + return false; // cannot cut (self-overlapping, self-intersecting) } std::swap (*jj, *i); } @@ -454,12 +455,156 @@ void cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygo right_of_line->put (&*hole); } } + + return true; +} + +namespace +{ + + template + struct get_sink_type; + + template <> + struct get_sink_type { typedef db::PolygonSink result; }; + + template <> + struct get_sink_type { typedef db::SimplePolygonSink result; }; + + /** + * @brief A polygon sink for the edge processor that feeds the polygon into the cut algorithm + */ + template + struct cut_polygon_sink + : public Sink + { + cut_polygon_sink (const Edge &_line, CutPolygonReceiverBase *_right_of_line) + : line (_line), right_of_line (_right_of_line) + { + // .. nothing yet .. + } + + virtual void put (const PolygonType &poly) + { + tl_assert (_cut_polygon_internal (poly, line, right_of_line)); + } + + Edge line; + CutPolygonReceiverBase *right_of_line; + }; + + /** + * @brief The cut algorithm driver with fallback to polygon merging when the cut fails + * TODO: this is kind of inefficient as we will first merge all and the cut just a half. + * This means for producing both parts we do this twice. But remember, this is just a + * fallback. + */ + template + void cut_polygon_internal_int (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line) + { + bool ok = _cut_polygon_internal (input, line, right_of_line); + if (! ok) { + + // If the cut operation fails on the plain input, merge the input polygon and try again + + db::EdgeProcessor ep; + ep.insert_sequence (input.begin_edge ()); + db::SimpleMerge op; + + cut_polygon_sink::result, PolygonType, Edge> sink (line, right_of_line); + db::PolygonGenerator pg (sink); + ep.process (pg, op); + + } + + } + + /** + * A transforming receiver that is put between a int cut algorithm and the double output receiver + */ + template + class cut_polygon_receiver_double_impl + : public CutPolygonReceiverBase + { + public: + cut_polygon_receiver_double_impl () + : mp_next (0) + { } + + void set_next (CutPolygonReceiverBase *next) + { + mp_next = next; + } + + void set_trans (const db::CplxTrans &tr) + { + m_tr = tr; + } + + virtual void put (const void *p) + { + PolygonType pp = ((const IPolygonType *) p)->transformed (m_tr, false); + mp_next->put ((void *) &pp); + } + + private: + CutPolygonReceiverBase *mp_next; + db::CplxTrans m_tr; + }; + + template + class cut_polygon_receiver_double; + + // template specializations for the double types + template <> class cut_polygon_receiver_double : public cut_polygon_receiver_double_impl { }; + template <> class cut_polygon_receiver_double : public cut_polygon_receiver_double_impl { }; + + /** + * @brief The cut algorithm driver for double types + * This driver will first try to guess a database unit (based on the bounding box) and then + * transform the polygon to int. On output, the polygon is transformed back to double. + */ + template + void cut_polygon_internal_double (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line) + { + db::DBox bbox = input.box (); + bbox += db::DBox (0, 0, 0, 0); + bbox += line.bbox (); + + // guess DBU + double dbu = std::max (1e-10, std::max (bbox.width (), bbox.height ()) / (std::numeric_limits::max () / 2)); + dbu = pow (10.0, ceil (log10 (dbu))); + + db::CplxTrans tr (dbu); + cut_polygon_receiver_double rec; + rec.set_trans (tr); + rec.set_next (right_of_line); + + cut_polygon_internal_int (input.transformed (tr.inverted (), false), line.transformed (tr.inverted ()), &rec); + } + +} + +template<> DB_PUBLIC void cut_polygon_internal (const db::Polygon &polygon, const db::Polygon::edge_type &line, CutPolygonReceiverBase *right_of_line) +{ + cut_polygon_internal_int (polygon, line, right_of_line); +} + +template<> DB_PUBLIC void cut_polygon_internal (const db::SimplePolygon &polygon, const db::SimplePolygon::edge_type &line, CutPolygonReceiverBase *right_of_line) +{ + cut_polygon_internal_int (polygon, line, right_of_line); +} + +template<> DB_PUBLIC void cut_polygon_internal (const db::DPolygon &polygon, const db::DPolygon::edge_type &line, CutPolygonReceiverBase *right_of_line) +{ + cut_polygon_internal_double (polygon, line, right_of_line); +} + +template<> DB_PUBLIC void cut_polygon_internal (const db::DSimplePolygon &polygon, const db::DSimplePolygon::edge_type &line, CutPolygonReceiverBase *right_of_line) +{ + cut_polygon_internal_double (polygon, line, right_of_line); } -template DB_PUBLIC void cut_polygon_internal<> (const db::Polygon &polygon, const db::Polygon::edge_type &line, CutPolygonReceiverBase *right_of_line); -template DB_PUBLIC void cut_polygon_internal<> (const db::SimplePolygon &polygon, const db::SimplePolygon::edge_type &line, CutPolygonReceiverBase *right_of_line); -template DB_PUBLIC void cut_polygon_internal<> (const db::DPolygon &polygon, const db::DPolygon::edge_type &line, CutPolygonReceiverBase *right_of_line); -template DB_PUBLIC void cut_polygon_internal<> (const db::DSimplePolygon &polygon, const db::DSimplePolygon::edge_type &line, CutPolygonReceiverBase *right_of_line); // ------------------------------------------------------------------------- // Implementation of split_polygon diff --git a/src/db/unit_tests/dbPolygonTools.cc b/src/db/unit_tests/dbPolygonTools.cc index 55660b24f..a690897f3 100644 --- a/src/db/unit_tests/dbPolygonTools.cc +++ b/src/db/unit_tests/dbPolygonTools.cc @@ -2069,3 +2069,175 @@ TEST(322) ); } +// cut self-overlapping polygon +TEST(400) +{ + std::vector c; + c.push_back (db::Point (0, 0)); + c.push_back (db::Point (0, 100)); + c.push_back (db::Point (1000, 100)); + c.push_back (db::Point (1000, 1000)); + c.push_back (db::Point (0, 1000)); + c.push_back (db::Point (0, 900)); + c.push_back (db::Point (900, 900)); + c.push_back (db::Point (900, 0)); + + { + db::Polygon in; + in.assign_hull (c.begin (), c.end ()); + std::vector right_of; + + db::cut_polygon (in, db::Edge (db::Point (500, 0), db::Point (500, 1)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (2)); + EXPECT_EQ (right_of[0].to_string (), "(500,0;500,100;900,100;900,0)"); + EXPECT_EQ (right_of[1].to_string (), "(900,100;900,900;500,900;500,1000;1000,1000;1000,100)"); + + right_of.clear (); + db::cut_polygon (in, db::Edge (db::Point (500, 1), db::Point (500, 0)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (2)); + EXPECT_EQ (right_of[0].to_string (), "(0,0;0,100;500,100;500,0)"); + EXPECT_EQ (right_of[1].to_string (), "(0,900;0,1000;500,1000;500,900)"); + } + + { + db::SimplePolygon in; + in.assign_hull (c.begin (), c.end ()); + std::vector right_of; + + db::cut_polygon (in, db::Edge (db::Point (500, 0), db::Point (500, 1)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (2)); + EXPECT_EQ (right_of[0].to_string (), "(500,0;500,100;900,100;900,0)"); + EXPECT_EQ (right_of[1].to_string (), "(900,100;900,900;500,900;500,1000;1000,1000;1000,100)"); + + right_of.clear (); + db::cut_polygon (in, db::Edge (db::Point (500, 1), db::Point (500, 0)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (2)); + EXPECT_EQ (right_of[0].to_string (), "(0,0;0,100;500,100;500,0)"); + EXPECT_EQ (right_of[1].to_string (), "(0,900;0,1000;500,1000;500,900)"); + } +} + +// cut self-overlapping polygon (with double types) +TEST(401) +{ + std::vector c; + c.push_back (db::DPoint (0, 0)); + c.push_back (db::DPoint (0, 100)); + c.push_back (db::DPoint (1000, 100)); + c.push_back (db::DPoint (1000, 1000)); + c.push_back (db::DPoint (0, 1000)); + c.push_back (db::DPoint (0, 900)); + c.push_back (db::DPoint (900, 900)); + c.push_back (db::DPoint (900, 0)); + + { + db::DPolygon in; + in.assign_hull (c.begin (), c.end ()); + std::vector right_of; + + db::cut_polygon (in, db::DEdge (db::DPoint (500, 0), db::DPoint (500, 1)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (2)); + EXPECT_EQ (right_of[0].to_string (), "(500,0;500,100;900,100;900,0)"); + EXPECT_EQ (right_of[1].to_string (), "(900,100;900,900;500,900;500,1000;1000,1000;1000,100)"); + + right_of.clear (); + db::cut_polygon (in, db::DEdge (db::DPoint (500, 1), db::DPoint (500, 0)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (2)); + EXPECT_EQ (right_of[0].to_string (), "(0,0;0,100;500,100;500,0)"); + EXPECT_EQ (right_of[1].to_string (), "(0,900;0,1000;500,1000;500,900)"); + } + + { + db::DSimplePolygon in; + in.assign_hull (c.begin (), c.end ()); + std::vector right_of; + + db::cut_polygon (in, db::DEdge (db::DPoint (500, 0), db::DPoint (500, 1)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (2)); + EXPECT_EQ (right_of[0].to_string (), "(500,0;500,100;900,100;900,0)"); + EXPECT_EQ (right_of[1].to_string (), "(900,100;900,900;500,900;500,1000;1000,1000;1000,100)"); + + right_of.clear (); + db::cut_polygon (in, db::DEdge (db::DPoint (500, 1), db::DPoint (500, 0)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (2)); + EXPECT_EQ (right_of[0].to_string (), "(0,0;0,100;500,100;500,0)"); + EXPECT_EQ (right_of[1].to_string (), "(0,900;0,1000;500,1000;500,900)"); + } +} + +// cut empty polygons +TEST(402) +{ + { + db::Polygon in; + std::vector right_of; + db::cut_polygon (in, db::Edge (db::Point (500, 0), db::Point (500, 1)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (0)); + } + { + db::SimplePolygon in; + std::vector right_of; + db::cut_polygon (in, db::Edge (db::Point (500, 0), db::Point (500, 1)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (0)); + } + { + db::DPolygon in; + std::vector right_of; + db::cut_polygon (in, db::DEdge (db::DPoint (500, 0), db::DPoint (500, 1)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (0)); + } + { + db::DSimplePolygon in; + std::vector right_of; + db::cut_polygon (in, db::DEdge (db::DPoint (500, 0), db::DPoint (500, 1)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (0)); + } +} + +// cut point-like polygons +TEST(403) +{ + { + db::Polygon in (db::Box (1000, 0, 1000, 0)); + std::vector right_of; + db::cut_polygon (in, db::Edge (db::Point (500, 0), db::Point (500, 1)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (1)); + EXPECT_EQ (right_of[0].to_string (), "()"); // bad, but no contour available :-( + right_of.clear (); + db::cut_polygon (in, db::Edge (db::Point (500, 1), db::Point (500, 0)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (0)); + } + + { + db::SimplePolygon in (db::Box (1000, 0, 1000, 0)); + std::vector right_of; + db::cut_polygon (in, db::Edge (db::Point (500, 0), db::Point (500, 1)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (1)); + EXPECT_EQ (right_of[0].to_string (), "()"); // bad, but no contour available :-( + right_of.clear (); + db::cut_polygon (in, db::Edge (db::Point (500, 1), db::Point (500, 0)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (0)); + } + + { + db::DPolygon in (db::DBox (1000, 0, 1000, 0)); + std::vector right_of; + db::cut_polygon (in, db::DEdge (db::DPoint (500, 0), db::DPoint (500, 1)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (1)); + EXPECT_EQ (right_of[0].to_string (), "()"); // bad, but no contour available :-( + right_of.clear (); + db::cut_polygon (in, db::DEdge (db::DPoint (500, 1), db::DPoint (500, 0)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (0)); + } + + { + db::DSimplePolygon in (db::DBox (1000, 0, 1000, 0)); + std::vector right_of; + db::cut_polygon (in, db::DEdge (db::DPoint (500, 0), db::DPoint (500, 1)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (1)); + EXPECT_EQ (right_of[0].to_string (), "()"); // bad, but no contour available :-( + right_of.clear (); + db::cut_polygon (in, db::DEdge (db::DPoint (500, 1), db::DPoint (500, 0)), std::back_inserter (right_of)); + EXPECT_EQ (right_of.size (), size_t (0)); + } +}