Region to edge interactions

With this fix, regions can be interaction-tested with edge collections.
Only "normal" interaction is available - select_overlapping is not. This
is still confined to region to region interactions.
This commit is contained in:
Matthias Koefferlein 2017-07-12 22:41:11 +02:00
parent df46aabb2e
commit 4ad20fa4ce
5 changed files with 362 additions and 31 deletions

View File

@ -651,7 +651,7 @@ public:
p = reinterpret_cast<const db::Polygon *> (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<char, size_t> 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<char, size_t> scanner (m_report_progress, m_progress_desc);
scanner.reserve (size () + other.size ());

View File

@ -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 OutputContainer>
class region_to_edge_interaction_filter
: public db::box_scanner_receiver<char, size_t>
{
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 &region)
: 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<const db::Edge *> (o1);
p = reinterpret_cast<const db::Polygon *> (o2 - 1);
} else if (p1 == 1 && p2 == 0) {
e = reinterpret_cast<const db::Edge *> (o2);
p = reinterpret_cast<const db::Polygon *> (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 db::Polygon *>::const_iterator p = m_seen.begin (); p != m_seen.end (); ++p) {
mp_output->insert (**p);
}
}
private:
OutputContainer *mp_output;
std::set<const db::Polygon *> 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<const db::Polygon *> (cp - 1))->box ();
} else {
// it's an edge
const db::Edge *e = reinterpret_cast<const db::Edge *> (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<char, size_t> 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<Region> filter (output);
scanner.process (filter, 1, bc);
} else {
region_to_edge_interaction_filter<Region> 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<char, size_t> 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<db::Shapes> filter (output);
scanner.process (filter, 1, bc);
} else {
region_to_edge_interaction_filter<db::Shapes> 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;

View File

@ -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);
};
/**

View File

@ -1312,7 +1312,7 @@ Class<db::Region> 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<db::Region> 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<db::Region> 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<db::Region> 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<db::Region> 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"

View File

@ -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)");
}