diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 80dbd794d..f9fc3d203 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -1060,7 +1060,15 @@ EdgeProcessor::insert (const db::Edge &e, EdgeProcessor::property_type p) } } -void +void +EdgeProcessor::insert (const db::SimplePolygon &q, EdgeProcessor::property_type p) +{ + for (db::SimplePolygon::polygon_edge_iterator e = q.begin_edge (); ! e.at_end (); ++e) { + insert (*e, p); + } +} + +void EdgeProcessor::insert (const db::Polygon &q, EdgeProcessor::property_type p) { for (db::Polygon::polygon_edge_iterator e = q.begin_edge (); ! e.at_end (); ++e) { diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index 9b5f4e380..dad1aa146 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -633,6 +633,11 @@ public: */ void insert (const db::Polygon &q, property_type p = 0); + /** + * @brief Insert a simple polygon + */ + void insert (const db::SimplePolygon &q, property_type p = 0); + /** * @brief Insert a polygon reference */ diff --git a/src/db/db/dbPolygonGenerators.cc b/src/db/db/dbPolygonGenerators.cc index 6c8a80fd4..208e3634b 100644 --- a/src/db/db/dbPolygonGenerators.cc +++ b/src/db/db/dbPolygonGenerators.cc @@ -90,6 +90,8 @@ public: void push_front (const db::Point &p) { m_contour.push_front (p); } void pop_back () { m_contour.pop_back (); } void pop_front () { m_contour.pop_front (); } + iterator erase (iterator i) { return m_contour.erase (i); } + iterator insert (iterator i, const db::Point &p) { return m_contour.insert (i, p); } bool empty () const { return m_contour.empty (); } size_t size () const { return m_contour.size (); } @@ -136,9 +138,12 @@ public: } template - void insert (iterator at, I from, I to) + iterator insert (iterator at, I from, I to) { + // NOTE: in some STL m_contour.insert already returns the new iterator + size_t index_at = at - m_contour.begin (); m_contour.insert (at, from, to); + return m_contour.begin () + index_at; } private: @@ -725,27 +730,56 @@ PolygonGenerator::join_contours (db::Coord x) PGPolyContour &cprev = (*mp_contours) [iprev]; tl_assert (cprev.size () >= 2); + tl_assert (c1.size () >= 2); + + PGPolyContour::iterator ins = cprev.end (); + db::Coord xprev = 0; + db::Edge eprev; + +#if 1 + // shallow analysis: insert the cutline at the end of the sequence - this may + // cut lines collinear with contour edges + + eprev = db::Edge (ins[-2], ins[-1]); + xprev = db::coord_traits::rounded (edge_xaty (db::Edge (ins[-2], ins[-1]), m_y)); +#else + // deep analysis: determine insertion point: pick the one where the cutline is shortest + + for (PGPolyContour::iterator i = ins; i > cprev.begin () + 1; --i) { + + db::Edge ecut (i[-2], i[-1]); + db::Coord xcut = db::coord_traits::rounded (edge_xaty (db::Edge (i[-2], i[-1]), m_y)); + + if (ins == i || (i[-1].y () >= m_y && i[-2].y () < m_y && xcut < c1.back ().x () && xcut > xprev)) { + xprev = xcut; + eprev = ecut; + ins = i; + } + + } +#endif // compute intersection point with next edge - db::Edge eprev (cprev.end ()[-2], cprev.back ()); - db::Coord xprev = db::coord_traits::rounded (edge_xaty (eprev, m_y)); db::Point pprev (xprev, m_y); // remove collinear edges along the cut line - cprev.back () = pprev; - while (cprev.size () > 1 && cprev.end ()[-2].y () == m_y && cprev.end ()[-1].y () == m_y) { - cprev.pop_back (); + ins[-1] = pprev; + while (ins - cprev.begin () > 1 && ins[-2].y () == m_y && ins[-1].y () == m_y) { + ins = cprev.erase (ins - 1); } - tl_assert (c1.size () >= 2); if ((c1.begin () + 1)->y () == m_y) { - cprev.insert (cprev.end (), c1.begin () + 1, c1.end ()); + ins = cprev.insert (ins, c1.begin () + 1, c1.end ()); + ins += c1.size () - 1; } else { - cprev.insert (cprev.end (), c1.begin (), c1.end ()); + ins = cprev.insert (ins, c1.begin (), c1.end ()); + ins += c1.size (); } - cprev.push_back (pprev); + + ins = cprev.insert (ins, pprev); + ++ins; if (eprev.p2 () != pprev) { - cprev.push_back (eprev.p2 ()); + cprev.insert (ins, eprev.p2 ()); } mp_contours->free (i1); diff --git a/src/db/db/dbPolygonTools.cc b/src/db/db/dbPolygonTools.cc index 0c2312e22..1895d69b9 100644 --- a/src/db/db/dbPolygonTools.cc +++ b/src/db/db/dbPolygonTools.cc @@ -185,10 +185,19 @@ public: return db::vprod_sign (edge (), other.edge ()) > 0; } } + + bool operator== (const loose_end_struct &other) const + { + if (! db::coord_traits::equal (proj (), other.proj ())) { + return false; + } else { + return db::vprod_sign (edge (), other.edge ()) == 0; + } + } }; template -static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line) +static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, cut_polygon_receiver_base *right_of_line) { typedef typename PolygonType::point_type point_type; typedef typename PolygonType::coord_type coord_type; @@ -259,6 +268,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C // tie together last and first partial segments. if (cutting_segments[nfirst].segment < 0) { cutting_segments[nfirst].enter = cutting_segments.back ().enter; + cutting_segments[nfirst].segment = cutting_segments.back ().segment; cutting_segments.pop_back (); } @@ -268,7 +278,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C 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); + right_of_line->put (input); return true; } else { // remember hole contours for later assignment @@ -279,7 +289,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C } else { PolygonType poly; poly.assign_hull (contour.begin (), contour.end ()); - right_of_line->put (&poly); + right_of_line->put (poly); } } @@ -297,23 +307,28 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C std::stable_sort (loose_ends.begin (), loose_ends.end ()); - // bring the points in a strict enter/leave order if possible + // we allow single pairs of collinear entry/leave edges (cut lines) and bring them in the right order bool enter = false; + for (typename std::vector >::iterator i = loose_ends.begin (); i != loose_ends.end (); ++i) { + if (i + 1 != loose_ends.end () && i[1] == i[0]) { + if (i + 2 != loose_ends.end () && i[2] == i[0]) { + // triple collinear + return false; + } + if (i[0].enter != enter && i[1].enter == enter) { + std::swap (i[0], i[1]); + } + } + enter = !enter; + } + + // the points now have to be in strict enter/leave order - otherwise fallback to merge + + enter = false; for (typename std::vector >::iterator i = loose_ends.begin (); i != loose_ends.end (); ++i) { if (i->enter != enter) { - typename std::vector >::iterator j = i + 1; - typename std::vector >::iterator jj = loose_ends.end (); - for ( ; j != loose_ends.end () && !(*j < *i) && !(*i < *j); ++j) { - if (j->enter == enter) { - jj = j; - break; - } - } - if (jj == loose_ends.end ()) { - return false; // cannot cut (self-overlapping, self-intersecting) - } - std::swap (*jj, *i); + return false; } enter = !enter; } @@ -410,7 +425,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C // it might happen in some cases, that cut pieces may vanish (i.e. all points on a line). Thus we check, if that // is the case and do not produce a polygon then. if (poly.vertices () > 0) { - right_of_line->put (&poly); + right_of_line->put (poly); } } @@ -441,7 +456,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C } } - right_of_line->put (&*hull); + right_of_line->put (*hull); } @@ -452,7 +467,7 @@ static bool _cut_polygon_internal (const PolygonType &input, const Edge &line, C // we assign to a PolygonType, this check is not possible. for (typename std::vector::iterator hole = hole_polygons.begin (); hole != hole_polygons.end (); ++hole) { if (hole->vertices () > 0) { - right_of_line->put (&*hole); + right_of_line->put (*hole); } } @@ -474,23 +489,22 @@ namespace /** * @brief A polygon sink for the edge processor that feeds the polygon into the cut algorithm */ - template - struct cut_polygon_sink + template + struct cut_polygon_bool_sink : public Sink { - cut_polygon_sink (const Edge &_line, CutPolygonReceiverBase *_right_of_line) - : line (_line), right_of_line (_right_of_line) + cut_polygon_bool_sink (cut_polygon_receiver_base *_right_of_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)); + right_of_line->put (poly); } - Edge line; - CutPolygonReceiverBase *right_of_line; + cut_polygon_receiver_base *right_of_line; }; /** @@ -500,20 +514,31 @@ namespace * fallback. */ template - void cut_polygon_internal_int (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line) + void cut_polygon_internal_int (const PolygonType &input, const Edge &line, cut_polygon_receiver_base *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 + // If the fast cut operation fails, use boolean AND to perform the cut operation - db::EdgeProcessor ep; - ep.insert_sequence (input.begin_edge ()); - db::SimpleMerge op; + PolygonType clip (input.box ()); + std::vector mask; + cut_polygon (clip, line, std::back_inserter (mask)); - cut_polygon_sink::result, PolygonType, Edge> sink (line, right_of_line); - db::PolygonGenerator pg (sink); - ep.process (pg, op); + if (! mask.empty ()) { + + db::EdgeProcessor ep; + ep.insert_sequence (input.begin_edge (), 0); + ep.insert_sequence (mask.begin (), mask.end (), 1); + + db::BooleanOp op (BooleanOp::And); + + cut_polygon_bool_sink::result, PolygonType> sink (right_of_line); + db::PolygonGenerator pg (sink); + ep.process (pg, op); + + + } } @@ -524,14 +549,14 @@ namespace */ template class cut_polygon_receiver_double_impl - : public CutPolygonReceiverBase + : public cut_polygon_receiver_base { public: cut_polygon_receiver_double_impl () : mp_next (0) { } - void set_next (CutPolygonReceiverBase *next) + void set_next (cut_polygon_receiver_base *next) { mp_next = next; } @@ -541,14 +566,14 @@ namespace m_tr = tr; } - virtual void put (const void *p) + virtual void put (const IPolygonType &p) { - PolygonType pp = ((const IPolygonType *) p)->transformed (m_tr, false); - mp_next->put ((void *) &pp); + PolygonType pp = p.transformed (m_tr, false); + mp_next->put (pp); } private: - CutPolygonReceiverBase *mp_next; + cut_polygon_receiver_base *mp_next; db::CplxTrans m_tr; }; @@ -565,7 +590,7 @@ namespace * 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) + void cut_polygon_internal_double (const PolygonType &input, const Edge &line, cut_polygon_receiver_base *right_of_line) { db::DBox bbox = input.box (); bbox += db::DBox (0, 0, 0, 0); @@ -585,22 +610,22 @@ namespace } -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::Polygon &polygon, const db::Polygon::edge_type &line, cut_polygon_receiver_base *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) +template<> DB_PUBLIC void cut_polygon_internal (const db::SimplePolygon &polygon, const db::SimplePolygon::edge_type &line, cut_polygon_receiver_base *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) +template<> DB_PUBLIC void cut_polygon_internal (const db::DPolygon &polygon, const db::DPolygon::edge_type &line, cut_polygon_receiver_base *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) +template<> DB_PUBLIC void cut_polygon_internal (const db::DSimplePolygon &polygon, const db::DSimplePolygon::edge_type &line, cut_polygon_receiver_base *right_of_line) { cut_polygon_internal_double (polygon, line, right_of_line); } diff --git a/src/db/db/dbPolygonTools.h b/src/db/db/dbPolygonTools.h index b3c439126..26f963039 100644 --- a/src/db/db/dbPolygonTools.h +++ b/src/db/db/dbPolygonTools.h @@ -74,25 +74,26 @@ private: // Some helper classes and functions for implementing cut_polygon -class DB_PUBLIC CutPolygonReceiverBase +template +class DB_PUBLIC cut_polygon_receiver_base { public: - virtual ~CutPolygonReceiverBase () { } - virtual void put (const void *) = 0; + virtual ~cut_polygon_receiver_base () { } + virtual void put (const Polygon &) = 0; }; template class cut_polygon_receiver - : public CutPolygonReceiverBase + : public cut_polygon_receiver_base { public: cut_polygon_receiver (const OutputIter &iter) : m_iter (iter) { } - virtual void put (const void *polygon) + virtual void put (const Polygon &polygon) { - *m_iter++ = *((const Polygon *) polygon); + *m_iter++ = polygon; } private: @@ -100,7 +101,7 @@ private: }; template -void DB_PUBLIC cut_polygon_internal (const PolygonType &input, const Edge &line, CutPolygonReceiverBase *right_of_line); +void DB_PUBLIC cut_polygon_internal (const PolygonType &input, const Edge &line, cut_polygon_receiver_base *right_of_line); /** * @brief Polygon cut function diff --git a/src/db/unit_tests/dbEdgeProcessorTests.cc b/src/db/unit_tests/dbEdgeProcessorTests.cc index d342a1db0..82ce06a5b 100644 --- a/src/db/unit_tests/dbEdgeProcessorTests.cc +++ b/src/db/unit_tests/dbEdgeProcessorTests.cc @@ -2570,6 +2570,67 @@ TEST(102) EXPECT_EQ (out[0].to_string (), "(0,0;0,200;100,200;100,100;200,100;200,200;500,200;500,100;600,100;600,200;0,200;0,1000;1000,1000;1000,0)"); } +TEST(103) +{ + db::EdgeProcessor ep; + + { + db::Point pts[] = { + db::Point (0, 0), + db::Point (0, 500), + db::Point (1500, 500), + db::Point (1500, 0), + db::Point (1000, 0), + db::Point (1000, 400), + db::Point (500, 400), + db::Point (500, 0) + }; + db::Polygon p; + p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]); + ep.insert (p, 0); + } + + { + db::Point pts[] = { + db::Point (100, 100), + db::Point (100, 400), + db::Point (400, 400), + db::Point (400, 100) + }; + db::Polygon p; + p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]); + ep.insert (p, 1); + } + + { + db::Point pts[] = { + db::Point (1100, 100), + db::Point (1100, 400), + db::Point (1400, 400), + db::Point (1400, 100) + }; + db::Polygon p; + p.assign_hull (&pts[0], &pts[sizeof(pts) / sizeof(pts[0])]); + ep.insert (p, 1); + } + + std::vector out; + db::PolygonContainer pc (out); + db::PolygonGenerator pg (pc, true, true); + db::BooleanOp op (db::BooleanOp::ANotB); + + ep.process (pg, op); + + EXPECT_EQ (out.size (), size_t (1)); +#if 1 + // fast hole treatment + EXPECT_EQ (out[0].to_string (), "(0,0;0,400;100,400;100,100;400,100;400,400;1100,400;1100,100;1400,100;1400,400;0,400;0,500;1500,500;1500,0;1000,0;1000,400;500,400;500,0)"); +#else + // elaborate hole treatment + EXPECT_EQ (out[0].to_string (), "(0,0;0,400;100,400;100,100;400,100;400,400;0,400;0,500;1500,500;1500,0;1000,0;1000,400;1100,400;1100,100;1400,100;1400,400;500,400;500,0)"); +#endif +} + // Bug 134 TEST(134) { diff --git a/src/db/unit_tests/dbPolygonToolsTests.cc b/src/db/unit_tests/dbPolygonToolsTests.cc index a0e92cc71..156aa6a8d 100644 --- a/src/db/unit_tests/dbPolygonToolsTests.cc +++ b/src/db/unit_tests/dbPolygonToolsTests.cc @@ -559,15 +559,18 @@ TEST(9c) std::vector right_of; db::cut_polygon (in, db::Edge (db::Point (15835, 0), db::Point (15835, 1)), std::back_inserter (right_of)); - EXPECT_EQ (right_of.size (), size_t (3)); - EXPECT_EQ (right_of[0].to_string (), "(17335,8265;16335,9265;15835,9265;15835,9765;17335,9765)"); - EXPECT_EQ (right_of[1].to_string (), "(15835,9765;16002,9932;15835,10015;17335,10015;17335,9765)"); - EXPECT_EQ (right_of[2].to_string (), "(15835,10015;15835,10265;17335,10265;17335,10015)"); + EXPECT_EQ (right_of.size (), size_t (1)); + EXPECT_EQ (right_of[0].to_string (), "(17335,8265;16335,9265;15835,9265;15835,9765;16002,9932;15835,10015;15835,10265;17335,10265)"); right_of.clear (); db::cut_polygon (in, db::Edge (db::Point (15835, 1), db::Point (15835, 0)), std::back_inserter (right_of)); - EXPECT_EQ (right_of.size (), size_t (1)); - EXPECT_EQ (right_of[0].to_string (), "(14335,8265;14335,10265;15335,10265;15335,9765;15668,9932;15335,10265;15835,10265;15835,10015;15668,9932;15835,9765;15335,9765;14335,9265;15335,9265;15335,9765;15835,9765;15835,9265;15335,9265)"); + EXPECT_EQ (right_of.size (), size_t (4)); + if (right_of.size () >= 4) { + EXPECT_EQ (right_of[0].to_string (), "(14335,8265;14335,9265;15335,9265)"); + EXPECT_EQ (right_of[1].to_string (), "(15335,9265;15335,9765;15668,9932;15835,9765;15835,9265)"); + EXPECT_EQ (right_of[2].to_string (), "(14335,9265;14335,10265;15335,10265;15335,9765)"); + EXPECT_EQ (right_of[3].to_string (), "(15668,9932;15335,10265;15835,10265;15835,10015)"); + } } TEST(9d) @@ -2373,6 +2376,23 @@ TEST(404) } } +TEST(405) +{ + db::Polygon poly; + std::string s ("(0,0;0,1126;30,1126;30,30;3044,30;3044,1126;5782,1126;5782,30;8796,30;8796,1126;0,1126;0,1141;3009,1141;3009,1156;3194,1156;3194,1141;8826,1141;8826,0;5742,0;5742,1126;3084,1126;3084,0)"); + tl::Extractor ex (s.c_str ()); + ex.read (poly); + + std::vector sp; + db::split_polygon (poly, sp); + + EXPECT_EQ (sp.size (), size_t (2)); + if (sp.size () >= 2) { + EXPECT_EQ (sp[0].to_string (), "(5742,0;5742,1126;5782,1126;5782,30;8796,30;8796,1126;3194,1126;3194,1141;8826,1141;8826,0)"); + EXPECT_EQ (sp[1].to_string (), "(0,0;0,1126;30,1126;30,30;3044,30;3044,1126;0,1126;0,1141;3009,1141;3009,1156;3194,1156;3194,1126;3084,1126;3084,0)"); + } +} + static db::Polygon str2poly (const std::string &s) { db::Polygon poly; @@ -2382,7 +2402,7 @@ static db::Polygon str2poly (const std::string &s) } // self-overlapping, non-orientable check -TEST(405) +TEST(500) { std::string ps; std::vector parts; diff --git a/testdata/gds/t166_au.gds.gz b/testdata/gds/t166_au.gds.gz index 7766ef28e..b250e67b3 100644 Binary files a/testdata/gds/t166_au.gds.gz and b/testdata/gds/t166_au.gds.gz differ