diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 1a8115b65..3b0261fe8 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -1629,6 +1629,7 @@ public: void reset () { + mp_es->reset_stop (); mp_op->reset (); } @@ -1637,6 +1638,11 @@ public: return mp_op->is_reset (); } + bool can_stop () + { + return mp_es->can_stop (); + } + void reserve (size_t n) { mp_op->reserve (n); @@ -1941,6 +1947,19 @@ public: return true; } + /** + * @brief Gets a value indicating whether the generator wants to stop + */ + bool can_stop () + { + for (std::vector::iterator s = m_states.begin (); s != m_states.end (); ++s) { + if (s->can_stop ()) { + return true; + } + } + return false; + } + /** * @brief Reserve memory n edges */ @@ -2414,7 +2433,7 @@ EdgeProcessor::redo_or_process (const std::vectorbegin (); - for (std::vector ::iterator current = mp_work_edges->begin (); current != mp_work_edges->end (); ) { + for (std::vector ::iterator current = mp_work_edges->begin (); current != mp_work_edges->end () && ! gs.can_stop (); ) { if (m_report_progress) { double p = double (std::distance (mp_work_edges->begin (), current)) / double (mp_work_edges->size ()); diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index b58d03a32..b10f2e5c5 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -56,7 +56,7 @@ public: /** * @brief Constructor */ - EdgeSink () { } + EdgeSink () : m_can_stop (false) { } /** * @brief Destructor @@ -123,6 +123,38 @@ public: * @brief Signal the end of a scanline at the given y coordinate */ virtual void end_scanline (db::Coord /*y*/) { } + + /** + * @brief Gets a value indicating that the generator wants to stop + */ + bool can_stop () const + { + return m_can_stop; + } + + /** + * @brief Resets the stop request + */ + void reset_stop () + { + m_can_stop = false; + } + +protected: + /** + * @brief Sets the stop request + * + * The scanner can choose to stop once the request is set. + * This is useful for implementing receivers that can stop once a + * specific condition is found. + */ + void request_stop () + { + m_can_stop = true; + } + +private: + bool m_can_stop; }; /** diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index 8210a988e..4558e53ed 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -274,4 +274,96 @@ EdgeOrientationFilter::selected (const db::Edge &edge) const } } +// ------------------------------------------------------------------------------------------------------------- +// Edge to Edge relation implementation + +bool edge_interacts (const db::Edge &a, const db::Edge &b) +{ + return a.intersect (b); +} + +bool edge_is_inside (const db::Edge &a, const db::Edge &b) +{ + return b.contains (a.p1 ()) && b.contains (a.p2 ()); +} + +bool edge_is_outside (const db::Edge &a, const db::Edge &b) +{ + if (a.parallel (b)) { + return ! a.coincident (b); + } else { + auto pt = a.intersect_point (b); + if (! pt.first) { + // no intersection -> outside + return true; + } + return ! b.contains_excl (pt.second); + } +} + +// ------------------------------------------------------------------------------------------------------------- +// Edge to Polygon relation implementation + +bool edge_interacts (const db::Edge &a, const db::Polygon &b) +{ + return db::interact (b, a); +} + +namespace { + +struct DetectTagEdgeSink + : public db::EdgeSink +{ + DetectTagEdgeSink (int tag) + : fail_tag (tag), result (true) { } + + virtual void put (const db::Edge &, int tag) + { + if (tag == fail_tag) { + result = false; + request_stop (); + } + } + + int fail_tag; + bool result; +}; + +} + +static bool +edge_is_inside_or_outside (bool outside, const db::Edge &a, const db::Polygon &b) +{ + db::EdgeProcessor ep; + ep.insert (b, 0); + + ep.insert (a, 1); + + DetectTagEdgeSink es (outside ? 1 : 2); // 2 is the "outside" tag in "Both" mode -> this makes inside fail + db::EdgePolygonOp op (db::EdgePolygonOp::Both, true /*include borders*/); + ep.process (es, op); + + return es.result; +} + +bool edge_is_inside (const db::Edge &a, const db::Polygon &b) +{ + // shortcuts + if (!a.bbox ().inside (b.box ())) { + return false; + } + + return edge_is_inside_or_outside (false, a, b); +} + +bool edge_is_outside (const db::Edge &a, const db::Polygon &b) +{ + // shortcuts + if (! a.bbox ().overlaps (b.box ())) { + return true; + } + + return edge_is_inside_or_outside (true, a, b); +} + } diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index 96b1a7ac5..84d073f94 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -246,6 +246,21 @@ private: EdgeAngleChecker m_checker; }; +/** + * @brief A predicate defining edge a interacts with b + */ +DB_PUBLIC bool edge_interacts (const db::Edge &a, const db::Edge &b); + +/** + * @brief A predicate defining edge a is "inside" b + */ +DB_PUBLIC bool edge_is_inside (const db::Edge &a, const db::Edge &b); + +/** + * @brief A predicate defining edge a is "outside" b + */ +DB_PUBLIC bool edge_is_outside (const db::Edge &a, const db::Edge &b); + /** * @brief A helper class for the edge interaction functionality which acts as an edge pair receiver */ @@ -266,8 +281,9 @@ public: if (p1 != p2) { const db::Edge *o = p1 > p2 ? o2 : o1; const db::Edge *oo = p1 > p2 ? o1 : o2; - // @@@ - if (o->intersect (*oo)) { + if ((m_mode == EdgesInteract && db::edge_interacts (*o, *oo)) || + (m_mode == EdgesInside && db::edge_is_inside (*o, *oo)) || + (m_mode == EdgesOutside && db::edge_is_outside (*o, *oo))) { if (m_seen.insert (o).second) { mp_output->insert (*o); } @@ -281,6 +297,21 @@ private: EdgeInteractionMode m_mode; }; +/** + * @brief A predicate defining edge a interacts with polygon b + */ +DB_PUBLIC bool edge_interacts (const db::Edge &a, const db::Polygon &b); + +/** + * @brief A predicate defining edge a is "inside" polygon b + */ +DB_PUBLIC bool edge_is_inside (const db::Edge &a, const db::Polygon &b); + +/** + * @brief A predicate defining edge a is "outside" polygon b + */ +DB_PUBLIC bool edge_is_outside (const db::Edge &a, const db::Polygon &b); + /** * @brief A helper class for the edge to region interaction functionality which acts as an edge pair receiver * @@ -307,8 +338,9 @@ public: tl::select (ep, e, p); if (m_seen.find (ep) == m_seen.end ()) { - // @@@ - if (db::interact (*p, *e)) { + if ((m_mode == EdgesInteract && db::edge_interacts (*e, *p)) || + (m_mode == EdgesInside && db::edge_is_inside (*e, *p)) || + (m_mode == EdgesOutside && db::edge_is_outside (*e, *p))) { m_seen.insert (ep); mp_output->insert (*ep); } diff --git a/src/db/unit_tests/dbEdgesUtilsTests.cc b/src/db/unit_tests/dbEdgesUtilsTests.cc new file mode 100644 index 000000000..7808903cc --- /dev/null +++ b/src/db/unit_tests/dbEdgesUtilsTests.cc @@ -0,0 +1,86 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2022 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlUnitTest.h" + +#include "dbEdges.h" +#include "dbEdgesUtils.h" + +TEST(1) +{ + EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 10))), true); + EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (10, 0), db::Point (20, 00)), db::Edge (db::Point (0, 0), db::Point (10, 0))), true); + EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (11, 0), db::Point (20, 00)), db::Edge (db::Point (0, 0), db::Point (10, 0))), false); + EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 1), db::Point (10, 10))), true); + EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (1, 0), db::Point (1, 10))), true); + EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 1), db::Point (0, 10))), false); + + EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 10), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (0, 11), db::Point (10, 11)), db::Polygon (db::Box (0, 0, 10, 10))), false); + EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (10, 20), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_interacts (db::Edge (db::Point (10, 20), db::Point (10, 11)), db::Polygon (db::Box (0, 0, 10, 10))), false); + + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 10))), true); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 20))), false); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (-10, -10), db::Point (10, 20))), false); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (5, 5), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 20))), false); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (20, 20))), true); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 2), db::Point (20, 22))), false); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (0, 20))), false); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (1, 1), db::Point (20, 20))), false); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (30, 30), db::Point (20, 20))), false); + + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Polygon ()), false); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (15, 15)), db::Polygon (db::Box (0, 0, 10, 10))), false); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 0), db::Point (10, 0)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (-10, 0), db::Point (10, 0)), db::Polygon (db::Box (0, 0, 10, 10))), false); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 10), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 11), db::Point (10, 11)), db::Polygon (db::Box (0, 0, 10, 10))), false); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (0, 5), db::Point (10, 5)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_is_inside (db::Edge (db::Point (-5, 5), db::Point (15, 5)), db::Polygon (db::Box (0, 0, 10, 10))), false); + + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 10))), false); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 20))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (10, 10), db::Point (20, 10))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (-10, -10), db::Point (10, 20))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (5, 5), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (10, 20))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (20, 20))), false); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 2), db::Point (20, 22))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (0, 0), db::Point (0, 20))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (1, 1), db::Point (20, 20))), false); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Edge (db::Point (30, 30), db::Point (20, 20))), true); + + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Polygon ()), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), false); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (15, 15)), db::Polygon (db::Box (0, 0, 10, 10))), false); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 0), db::Point (10, 0)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 0), db::Point (0, 0)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-10, 0), db::Point (10, 0)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 10), db::Point (10, 10)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 11), db::Point (10, 11)), db::Polygon (db::Box (0, 0, 10, 10))), true); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (0, 5), db::Point (10, 5)), db::Polygon (db::Box (0, 0, 10, 10))), false); + EXPECT_EQ (db::edge_is_outside (db::Edge (db::Point (-5, 5), db::Point (15, 5)), db::Polygon (db::Box (0, 0, 10, 10))), false); +} + diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index ffe564cb9..50d493b51 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -71,6 +71,7 @@ SOURCES = \ dbEdgePairRelationsTests.cc \ dbEdgePairTests.cc \ dbEdgeTests.cc \ + dbEdgesUtilsTests.cc \ dbClipTests.cc \ dbCellMappingTests.cc \ dbCellHullGeneratorTests.cc \