diff --git a/src/db/dbEdges.cc b/src/db/dbEdges.cc index a2f47f4a2..99236480d 100644 --- a/src/db/dbEdges.cc +++ b/src/db/dbEdges.cc @@ -651,7 +651,7 @@ public: p = reinterpret_cast (o1 - 1); } - if (e && p) { + if (e && p && m_seen.find (e) == m_seen.end ()) { // A polygon and an edge interact if the edge is either inside completely // of at least one edge of the polygon intersects with the edge @@ -666,7 +666,8 @@ public: } } - if (interacts && m_seen.insert (e).second) { + if (interacts) { + m_seen.insert (e); mp_output->insert (*e); } @@ -706,6 +707,14 @@ struct EdgeOrRegionBoxConverter Edges & Edges::select_interacting (const Region &other) { + // shortcuts + if (other.empty ()) { + clear (); + return *this; + } else if (empty ()) { + return *this; + } + db::box_scanner scanner (m_report_progress, m_progress_desc); scanner.reserve (size () + other.size ()); @@ -731,6 +740,11 @@ Edges::select_interacting (const Region &other) Edges & Edges::select_not_interacting (const Region &other) { + // shortcuts + if (other.empty () || empty ()) { + return *this; + } + db::box_scanner scanner (m_report_progress, m_progress_desc); scanner.reserve (size () + other.size ()); diff --git a/src/db/dbRegion.cc b/src/db/dbRegion.cc index e2d93a95f..11a05e3f2 100644 --- a/src/db/dbRegion.cc +++ b/src/db/dbRegion.cc @@ -929,7 +929,201 @@ Region::select_interacting_generic (const Region &other, int mode, bool touching set_valid_polygons (); } -EdgePairs +namespace +{ + +/** + * @brief A helper class for the region to edge interaction functionality + * + * Note: This special scanner uses pointers to two different objects: edges and polygons. + * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate + * pointers to edges. + * + * There is a special box converter which is able to sort that out as well. + */ +template +class region_to_edge_interaction_filter + : public db::box_scanner_receiver +{ +public: + region_to_edge_interaction_filter (OutputContainer &output) + : mp_output (&output), m_inverse (false) + { + // .. nothing yet .. + } + + region_to_edge_interaction_filter (OutputContainer &output, const db::Region ®ion) + : mp_output (&output), m_inverse (true) + { + for (db::Region::const_iterator p = region.begin_merged (); ! p.at_end (); ++p) { + m_seen.insert (&*p); + } + } + + void add (const char *o1, size_t p1, const char *o2, size_t p2) + { + const db::Edge *e = 0; + const db::Polygon *p = 0; + + // Note: edges have property 0 and have even-valued pointers. + // Polygons have property 1 and odd-valued pointers. + if (p1 == 0 && p2 == 1) { + e = reinterpret_cast (o1); + p = reinterpret_cast (o2 - 1); + } else if (p1 == 1 && p2 == 0) { + e = reinterpret_cast (o2); + p = reinterpret_cast (o1 - 1); + } + + if (e && p && (m_seen.find (p) == m_seen.end ()) != m_inverse) { + + // A polygon and an edge interact if the edge is either inside completely + // of at least one edge of the polygon intersects with the edge + bool interacts = false; + if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) { + interacts = true; + } else { + for (db::Polygon::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) { + if ((*pe).intersect (*e)) { + interacts = true; + } + } + } + + if (interacts) { + if (m_inverse) { + m_seen.erase (p); + } else { + m_seen.insert (p); + mp_output->insert (*p); + } + } + + } + } + + void fill_output () + { + for (std::set::const_iterator p = m_seen.begin (); p != m_seen.end (); ++p) { + mp_output->insert (**p); + } + } + +private: + OutputContainer *mp_output; + std::set m_seen; + bool m_inverse; +}; + +/** + * @brief A special box converter that splits the pointers into polygon and edge pointers + */ +struct EdgeOrRegionBoxConverter +{ + typedef db::Box box_type; + + db::Box operator() (const char &c) const + { + // Note: edges have property 0 and have even-valued pointers. + // Polygons have property 1 and odd-valued pointers. + const char *cp = &c; + if ((size_t (cp) & 1) == 1) { + // it's a polygon + return (reinterpret_cast (cp - 1))->box (); + } else { + // it's an edge + const db::Edge *e = reinterpret_cast (cp); + return db::Box (e->p1 (), e->p2 ()); + } + } +}; + +} + +Region +Region::selected_interacting_generic (const Edges &other, bool inverse) const +{ + if (other.empty ()) { + if (! inverse) { + return Region (); + } else { + return *this; + } + } else if (empty ()) { + return *this; + } + + db::box_scanner scanner (m_report_progress, m_progress_desc); + scanner.reserve (size () + other.size ()); + + ensure_valid_polygons (); + for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { + scanner.insert ((char *) &*p + 1, 1); + } + + other.ensure_valid_merged_edges (); + for (Edges::const_iterator e = other.begin (); ! e.at_end (); ++e) { + scanner.insert ((char *) &*e, 0); + } + + Region output; + EdgeOrRegionBoxConverter bc; + + if (! inverse) { + region_to_edge_interaction_filter filter (output); + scanner.process (filter, 1, bc); + } else { + region_to_edge_interaction_filter filter (output, *this); + scanner.process (filter, 1, bc); + filter.fill_output (); + } + + return output; +} + +void +Region::select_interacting_generic (const Edges &other, bool inverse) +{ + // shortcut + if (other.empty ()) { + if (! inverse) { + clear (); + } + return; + } else if (empty ()) { + return; + } + + db::box_scanner scanner (m_report_progress, m_progress_desc); + scanner.reserve (size () + other.size ()); + + ensure_valid_polygons (); + for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { + scanner.insert ((char *) &*p + 1, 1); + } + + other.ensure_valid_merged_edges (); + for (Edges::const_iterator e = other.begin (); ! e.at_end (); ++e) { + scanner.insert ((char *) &*e, 0); + } + + db::Shapes output (false); + EdgeOrRegionBoxConverter bc; + + if (! inverse) { + region_to_edge_interaction_filter filter (output); + scanner.process (filter, 1, bc); + } else { + region_to_edge_interaction_filter filter (output, *this); + scanner.process (filter, 1, bc); + filter.fill_output (); + } + + m_polygons.swap (output); + set_valid_polygons (); +} + +EdgePairs Region::grid_check (db::Coord gx, db::Coord gy) const { EdgePairs out; diff --git a/src/db/dbRegion.h b/src/db/dbRegion.h index b26cfc71f..801aedcbd 100644 --- a/src/db/dbRegion.h +++ b/src/db/dbRegion.h @@ -1199,9 +1199,6 @@ public: /** * @brief Selects all polygons of this region which are completly outside polygons from the other region * - * This method does not merge the polygons before using them. If whole connected - * regions shall be selected, merge the region first. - * * Merged semantics applies. */ Region &select_outside (const Region &other) @@ -1213,9 +1210,6 @@ public: /** * @brief Selects all polygons of this region which are not completly outside polygons from the other region * - * This method does not merge the polygons before using them. If whole connected - * regions shall be selected, merge the region first. - * * Merged semantics applies. */ Region &select_not_outside (const Region &other) @@ -1251,9 +1245,6 @@ public: /** * @brief Selects all polygons of this region which are completly inside polygons from the other region * - * This method does not merge the polygons before using them. If whole connected - * regions shall be selected, merge the region first. - * * Merged semantics applies. */ Region &select_inside (const Region &other) @@ -1265,9 +1256,6 @@ public: /** * @brief Selects all polygons of this region which are not completly inside polygons from the other region * - * This method does not merge the polygons before using them. If whole connected - * regions shall be selected, merge the region first. - * * Merged semantics applies. */ Region &select_not_inside (const Region &other) @@ -1303,9 +1291,6 @@ public: /** * @brief Selects all polygons of this region which overlap or touch polygons from the other region * - * This method does not merge the polygons before using them. If whole connected - * regions shall be selected, merge the region first. - * * Merged semantics applies. */ Region &select_interacting (const Region &other) @@ -1317,9 +1302,6 @@ public: /** * @brief Selects all polygons of this region which do not overlap or touch polygons from the other region * - * This method does not merge the polygons before using them. If whole connected - * regions shall be selected, merge the region first. - * * Merged semantics applies. */ Region &select_not_interacting (const Region &other) @@ -1353,10 +1335,53 @@ public: } /** - * @brief Selects all polygons of this region which overlap polygons from the other region + * @brief Selects all polygons of this region which overlap or touch edges from the given edge collection * - * This method does not merge the polygons before using them. If whole connected - * regions shall be selected, merge the region first. + * Merged semantics applies to both operators. + */ + Region &select_interacting (const Edges &other) + { + select_interacting_generic (other, false); + return *this; + } + + /** + * @brief Selects all polygons of this region which do not overlap or touch edges from the edge collection + * + * Merged semantics applies to both operators. + */ + Region &select_not_interacting (const Edges &other) + { + select_interacting_generic (other, true); + return *this; + } + + /** + * @brief Returns all polygons of this which overlap or touch edges from the edge collection + * + * This method is an out-of-place version of select_interacting. + * + * Merged semantics applies to both operators. + */ + Region selected_interacting (const Edges &other) const + { + return selected_interacting_generic (other, false); + } + + /** + * @brief Returns all polygons of this which do not overlap or touch polygons from the other region + * + * This method is an out-of-place version of select_not_interacting. + * + * Merged semantics applies to both operators. + */ + Region selected_not_interacting (const Edges &other) const + { + return selected_interacting_generic (other, true); + } + + /** + * @brief Selects all polygons of this region which overlap polygons from the other region * * Merged semantics applies. */ @@ -1369,9 +1394,6 @@ public: /** * @brief Selects all polygons of this region which do not overlap polygons from the other region * - * This method does not merge the polygons before using them. If whole connected - * regions shall be selected, merge the region first. - * * Merged semantics applies. */ Region &select_not_overlapping (const Region &other) @@ -1563,6 +1585,8 @@ private: EdgePairs run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; void select_interacting_generic (const Region &other, int mode, bool touching, bool inverse); Region selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const; + Region selected_interacting_generic (const Edges &other, bool inverse) const; + void select_interacting_generic (const Edges &other, bool inverse); }; /** diff --git a/src/db/gsiDeclDbRegion.cc b/src/db/gsiDeclDbRegion.cc index 39ebc5122..36a7e8b35 100644 --- a/src/db/gsiDeclDbRegion.cc +++ b/src/db/gsiDeclDbRegion.cc @@ -1312,7 +1312,7 @@ Class decl_Region ("Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" ) + - method ("interacting", &db::Region::selected_interacting, + method ("interacting", (db::Region (db::Region::*) (const db::Region &) const) &db::Region::selected_interacting, "@brief Returns the polygons of this region which overlap or touch polygons from the other region\n" "\n" "@args other\n" @@ -1320,7 +1320,7 @@ Class decl_Region ("Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" ) + - method ("not_interacting", &db::Region::selected_not_interacting, + method ("not_interacting", (db::Region (db::Region::*) (const db::Region &) const) &db::Region::selected_not_interacting, "@brief Returns the polygons of this region which do not overlap or touch polygons from the other region\n" "\n" "@args other\n" @@ -1328,7 +1328,7 @@ Class decl_Region ("Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" ) + - method ("select_interacting", &db::Region::select_interacting, + method ("select_interacting", (db::Region &(db::Region::*) (const db::Region &)) &db::Region::select_interacting, "@brief Selects the polygons from this region which overlap or touch polygons from the other region\n" "\n" "@args other\n" @@ -1336,7 +1336,7 @@ Class decl_Region ("Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" ) + - method ("select_not_interacting", &db::Region::select_not_interacting, + method ("select_not_interacting", (db::Region &(db::Region::*) (const db::Region &)) &db::Region::select_not_interacting, "@brief Selects the polygons from this region which do not overlap or touch polygons from the other region\n" "\n" "@args other\n" @@ -1344,6 +1344,46 @@ Class decl_Region ("Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" ) + + method ("interacting", (db::Region (db::Region::*) (const db::Edges &) const) &db::Region::selected_interacting, + "@brief Returns the polygons of this region which overlap or touch edges from the edge collection\n" + "\n" + "@args other\n" + "@return A new region containing the polygons overlapping or touching edges from the edge collection\n" + "\n" + "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" + "\n" + "This method has been introduced in version 0.25\n" + ) + + method ("not_interacting", (db::Region (db::Region::*) (const db::Edges &) const) &db::Region::selected_not_interacting, + "@brief Returns the polygons of this region which do not overlap or touch edges from the edge collection\n" + "\n" + "@args other\n" + "@return A new region containing the polygons not overlapping or touching edges from the edge collection\n" + "\n" + "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" + "\n" + "This method has been introduced in version 0.25\n" + ) + + method ("select_interacting", (db::Region &(db::Region::*) (const db::Edges &)) &db::Region::select_interacting, + "@brief Selects the polygons from this region which overlap or touch edges from the edge collection\n" + "\n" + "@args other\n" + "@return The region after the polygons have been selected (self)\n" + "\n" + "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" + "\n" + "This method has been introduced in version 0.25\n" + ) + + method ("select_not_interacting", (db::Region &(db::Region::*) (const db::Edges &)) &db::Region::select_not_interacting, + "@brief Selects the polygons from this region which do not overlap or touch edges from the edge collection\n" + "\n" + "@args other\n" + "@return The region after the polygons have been selected (self)\n" + "\n" + "Merged semantics applies for this method (see \\merged_semantics= of merged semantics)\n" + "\n" + "This method has been introduced in version 0.25\n" + ) + method ("overlapping", &db::Region::selected_overlapping, "@brief Returns the polygons of this region which overlap polygons from the other region\n" "\n" diff --git a/src/unit_tests/dbRegion.cc b/src/unit_tests/dbRegion.cc index 4c4ab93d9..f94829e7e 100644 --- a/src/unit_tests/dbRegion.cc +++ b/src/unit_tests/dbRegion.cc @@ -1169,3 +1169,62 @@ TEST(29) EXPECT_EQ (b.perimeter (), 8000000000.0); } +TEST(30a) +{ + db::Region r; + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (20, 20), db::Point (30, 30)))).to_string (), ""); + r.insert (db::Box (db::Point (0, 0), db::Point (100, 200))); + r.insert (db::Box (db::Point (-100, -100), db::Point (0, 0))); + r.set_merged_semantics (false); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (20, 20), db::Point (30, 30)))).to_string (), "(0,0;0,200;100,200;100,0)"); + EXPECT_EQ (r.selected_not_interacting (db::Edges (db::Edge (db::Point (20, 20), db::Point (30, 30)))).to_string (), "(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (-20, -20), db::Point (30, 30)))).to_string (), "(0,0;0,200;100,200;100,0);(-100,-100;-100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (-200, -200), db::Point (-190, -190)))).to_string (), ""); + db::Region rr = r; + r.select_interacting (db::Edges (db::Edge (db::Point (-20, -20), db::Point (-10, -10)))); + EXPECT_EQ (r.to_string (), "(-100,-100;-100,0;0,0;0,-100)"); + rr.select_not_interacting (db::Edges (db::Edge (db::Point (-20, -20), db::Point (-10, -10)))); + EXPECT_EQ (rr.to_string (), "(0,0;0,200;100,200;100,0)"); + + r.clear (); + r.insert(db::Box (db::Point (1000, 0), db::Point (6000, 4000))); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (0, 4000), db::Point (2000, 6000)))).to_string (), ""); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (1000, 4000), db::Point (2000, 6000)))).to_string (), "(1000,0;1000,4000;6000,4000;6000,0)"); + EXPECT_EQ (db::Edges (db::Edge (db::Point (0, 4000), db::Point (2000, 6000))).selected_interacting (r).to_string (), ""); + EXPECT_EQ (db::Edges (db::Edge (db::Point (1000, 4000), db::Point (2000, 6000))).selected_interacting (r).to_string (), "(1000,4000;2000,6000)"); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (1000, 4001), db::Point (2000, 6000)))).to_string (), ""); + EXPECT_EQ (db::Edges (db::Edge (db::Point (1000, 4001), db::Point (2000, 6000))).selected_interacting (r).to_string (), ""); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (1000, 3999), db::Point (1000, 6000)))).to_string (), "(1000,0;1000,4000;6000,4000;6000,0)"); + EXPECT_EQ (db::Edges (db::Edge (db::Point (1000, 3999), db::Point (1000, 6000))).selected_interacting (r).to_string (), "(1000,3999;1000,6000)"); +} + +TEST(30b) +{ + db::Region r; + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (20, 20), db::Point (30, 30)))).to_string (), ""); + r.insert (db::Box (db::Point (0, 0), db::Point (100, 200))); + r.insert (db::Box (db::Point (-100, -100), db::Point (0, 0))); + r.set_merged_semantics (true); + r.set_min_coherence (true); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (20, 20), db::Point (30, 30)))).to_string (), "(0,0;0,200;100,200;100,0)"); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (-20, -20), db::Point (30, 30)))).to_string (), "(-100,-100;-100,0;0,0;0,-100);(0,0;0,200;100,200;100,0)"); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (-200, -200), db::Point (-190, -190)))).to_string (), ""); + r.select_interacting (db::Edges (db::Edge (db::Point (-20, -20), db::Point (-10, -10)))); + EXPECT_EQ (r.to_string (), "(-100,-100;-100,0;0,0;0,-100)"); +} + +TEST(30c) +{ + db::Region r; + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (20, 20), db::Point (30, 30)))).to_string (), ""); + r.insert (db::Box (db::Point (0, 0), db::Point (100, 200))); + r.insert (db::Box (db::Point (-100, -100), db::Point (0, 0))); + r.set_merged_semantics (true); + r.set_min_coherence (false); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (20, 20), db::Point (30, 30)))).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (-20, -20), db::Point (30, 30)))).to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); + EXPECT_EQ (r.selected_interacting (db::Edges (db::Edge (db::Point (-200, -200), db::Point (-190, -190)))).to_string (), ""); + r.select_interacting (db::Edges (db::Edge (db::Point (-20, -20), db::Point (-10, -10)))); + EXPECT_EQ (r.to_string (), "(-100,-100;-100,0;0,0;0,200;100,200;100,0;0,0;0,-100)"); +} +