diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 441b85bcd..ee55133a2 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -29,6 +29,7 @@ SOURCES = \ dbEdgePairs.cc \ dbEdgeProcessor.cc \ dbEdges.cc \ + dbEdgesLocalOperations.cc \ dbFillTool.cc \ dbFuzzyCellMapping.cc \ dbGenericShapeIterator.cc \ @@ -243,6 +244,7 @@ HEADERS = \ dbEdgePairs.h \ dbEdgeProcessor.h \ dbEdges.h \ + dbEdgesLocalOperations.h \ dbEdgesToContours.h \ dbFillTool.h \ dbFuzzyCellMapping.h \ diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index 499096a24..06fb10204 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -35,6 +35,7 @@ #include "dbLocalOperation.h" #include "dbLocalOperationUtils.h" #include "dbRegionLocalOperations.h" // for db::ContainedEdgesLocalOperation +#include "dbEdgesLocalOperations.h" #include "dbHierProcessor.h" #include "dbEmptyEdges.h" diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index dafed0cea..a95b2f808 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1282,125 +1282,6 @@ DeepRegion::snapped (db::Coord gx, db::Coord gy) return res.release (); } -namespace -{ - -template -static -std::map, std::vector > > -separate_by_same_properties (const shape_interactions, db::object_with_properties > &interactions, db::PropertyMapper &pms, db::PropertyMapper &pmi) -{ - std::map, std::vector > > by_prop_id; - - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - - const db::object_with_properties &subject = interactions.subject_shape (i->first); - - std::pair, std::vector > &s2p = by_prop_id [pms (subject.properties_id ())]; - s2p.first.push_back (&subject); - - for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { - - const std::pair > &intruder = interactions.intruder_shape (*ii); - - if (subject.properties_id () == pmi (intruder.second.properties_id ())) { - s2p.second.push_back (&intruder.second); - } - - } - - } - - return by_prop_id; -} - -class PolygonToEdgeLocalOperation - : public local_operation -{ -public: - PolygonToEdgeLocalOperation (db::PropertiesRepository *target_pr, const db::PropertiesRepository *source_pr) - : local_operation (), m_pm (target_pr, source_pr) - { - // .. nothing yet .. - } - - virtual db::Coord dist () const { return 1; } - virtual bool requests_single_subjects () const { return true; } - virtual std::string description () const { return std::string ("polygon to edges"); } - - virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const - { - db::EdgeProcessor ep; - ep.set_base_verbosity (50); - - auto by_prop_id = separate_by_same_properties (interactions, m_pm, m_pm); - for (auto shapes_by_prop_id = by_prop_id.begin (); shapes_by_prop_id != by_prop_id.end (); ++shapes_by_prop_id) { - - db::properties_id_type prop_id = shapes_by_prop_id->first; - - for (auto s = shapes_by_prop_id->second.first.begin (); s != shapes_by_prop_id->second.first.end (); ++s) { - ep.insert (**s); - } - - db::property_injector > results_with_properties (&results.front (), prop_id); - - if (shapes_by_prop_id->second.second.empty ()) { - - db::edge_to_edge_set_generator > > eg (results_with_properties, prop_id); - db::MergeOp op (0); - ep.process (eg, op); - - } else { - - // With intruders: to compute our local contribution we take the edges without and with intruders - // and deliver what is in both sets - - db::MergeOp op (0); - - std::vector edges1; - db::EdgeContainer ec1 (edges1); - ep.process (ec1, op); - - ep.clear (); - - for (auto s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) { - ep.insert (s->second); - } - for (auto i = interactions.begin_intruders (); i != interactions.end_intruders (); ++i) { - ep.insert (i->second.second); - } - - std::vector edges2; - db::EdgeContainer ec2 (edges2); - ep.process (ec2, op); - - // Runs the boolean AND between the result with and without intruders - - db::box_scanner scanner; - scanner.reserve (edges1.size () + edges2.size ()); - - for (std::vector::const_iterator i = edges1.begin (); i != edges1.end (); ++i) { - scanner.insert (i.operator-> (), 0); - } - for (std::vector::const_iterator i = edges2.begin (); i != edges2.end (); ++i) { - scanner.insert (i.operator-> (), 1); - } - - EdgeBooleanClusterCollector > > cluster_collector (&results_with_properties, EdgeAnd); - scanner.process (cluster_collector, 1, db::box_convert ()); - - } - - } - - } - -private: - mutable db::PropertyMapper m_pm; -}; - -} - EdgesDelegate * DeepRegion::edges (const EdgeFilterBase *filter) const { diff --git a/src/db/db/dbEdgesLocalOperations.cc b/src/db/db/dbEdgesLocalOperations.cc new file mode 100644 index 000000000..cd69d034f --- /dev/null +++ b/src/db/db/dbEdgesLocalOperations.cc @@ -0,0 +1,203 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 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 "dbEdgesLocalOperations.h" +#include "dbHierProcessor.h" +#include "dbLocalOperationUtils.h" + +namespace db +{ + +// --------------------------------------------------------------------------------------------- +// EdgeBoolAndOrNotLocalOperation implementation + +EdgeBoolAndOrNotLocalOperation::EdgeBoolAndOrNotLocalOperation (EdgeBoolOp op) + : m_op (op) +{ + // .. nothing yet .. +} + +OnEmptyIntruderHint +EdgeBoolAndOrNotLocalOperation::on_empty_intruder_hint () const +{ + return (m_op == EdgeAnd || m_op == EdgeIntersections) ? Drop : Copy; +} + +std::string +EdgeBoolAndOrNotLocalOperation::description () const +{ + if (m_op == EdgeIntersections) { + return tl::to_string (tr ("Edge INTERSECTION operation")); + } else if (m_op == EdgeAnd) { + return tl::to_string (tr ("Edge AND operation")); + } else if (m_op == EdgeNot) { + return tl::to_string (tr ("Edge NOT operation")); + } else { + return std::string (); + } +} + +void +EdgeBoolAndOrNotLocalOperation::do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + tl_assert (results.size () == size_t (m_op == EdgeAndNot ? 2 : 1)); + + std::unordered_set &result = results.front (); + + std::unordered_set *result2 = 0; + if (results.size () > 1) { + result2 = &results[1]; + } + + EdgeBooleanClusterCollector > cluster_collector (&result, m_op, result2); + + db::box_scanner scanner; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + bool any_subject = false; + bool is_and = (m_op == EdgeAnd || m_op == EdgeAndNot || m_op == EdgeIntersections); + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::Edge &subject = interactions.subject_shape (i->first); + if (others.find (subject) != others.end ()) { + if (is_and) { + result.insert (subject); + } + } else if (i->second.empty ()) { + // shortcut (not: keep, and: drop) + if (! is_and) { + result.insert (subject); + } + } else { + scanner.insert (&subject, 0); + any_subject = true; + } + + } + + if (! others.empty () || any_subject) { + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + scanner.insert (o.operator-> (), 1); + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + } +} + +// --------------------------------------------------------------------------------------------- +// EdgeToPolygonLocalOperation implementation + +EdgeToPolygonLocalOperation::EdgeToPolygonLocalOperation (EdgePolygonOp::mode_t op, bool include_borders) + : m_op (op), m_include_borders (include_borders) +{ + // .. nothing yet .. +} + +OnEmptyIntruderHint +EdgeToPolygonLocalOperation::on_empty_intruder_hint () const +{ + return m_op == EdgePolygonOp::Inside ? Drop : (m_op == EdgePolygonOp::Outside ? Copy : CopyToSecond); +} + +std::string +EdgeToPolygonLocalOperation::description () const +{ + if (m_op == EdgePolygonOp::Inside) { + return tl::to_string (tr ("Edge to polygon AND/INSIDE")); + } else if (m_op == EdgePolygonOp::Outside) { + return tl::to_string (tr ("Edge to polygon NOT/OUTSIDE")); + } else { + return tl::to_string (tr ("Edge to polygon ANDNOT/INOUTSIDE")); + } +} + +void +EdgeToPolygonLocalOperation::do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + tl_assert (results.size () == size_t (m_op == EdgePolygonOp::Both ? 2 : 1)); + + std::unordered_set &result = results.front (); + + std::unordered_set *result2 = 0; + if (results.size () > 1) { + result2 = &results[1]; + } + + db::EdgeProcessor ep; + + std::set others; + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + bool any_subject = false; + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + const db::Edge &subject = interactions.subject_shape (i->first); + if (i->second.empty ()) { + // shortcut (outside: keep, otherwise: drop) + if (m_op == db::EdgePolygonOp::Outside) { + result.insert (subject); + } else if (m_op == db::EdgePolygonOp::Both) { + result2->insert (subject); + } + } else { + ep.insert (subject, 1); + any_subject = true; + } + + } + + if (! others.empty () || any_subject) { + + for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { + for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, 0); + } + } + + std::unique_ptr cc_second; + if (result2) { + cc_second.reset (new db::EdgeToEdgeSetGenerator (*result2, 2 /*second tag*/)); + } + + db::EdgeToEdgeSetGenerator cc (result, 1 /*first tag*/, cc_second.get ()); + db::EdgePolygonOp op (m_op, m_include_borders); + ep.process (cc, op); + + } +} + +} + diff --git a/src/db/db/dbEdgesLocalOperations.h b/src/db/db/dbEdgesLocalOperations.h new file mode 100644 index 000000000..163bf5a59 --- /dev/null +++ b/src/db/db/dbEdgesLocalOperations.h @@ -0,0 +1,84 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 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 + +*/ + + + +#ifndef HDR_dbEdgesLocalOperation +#define HDR_dbEdgesLocalOperation + +#include "dbCommon.h" + +#include "dbLayout.h" +#include "dbEdgeBoolean.h" +#include "dbEdgeProcessor.h" +#include "dbLocalOperation.h" + +namespace db +{ + +/** + * @brief Implements a boolean AND or NOT operation between edges + */ +class DB_PUBLIC EdgeBoolAndOrNotLocalOperation + : public local_operation +{ +public: + EdgeBoolAndOrNotLocalOperation (db::EdgeBoolOp op); + + virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + + // edge interaction distance is 1 to force overlap between edges and edge/boxes + virtual db::Coord dist () const { return 1; } + +private: + db::EdgeBoolOp m_op; +}; + +/** + * @brief Implements a boolean AND or NOT operation between edges and polygons (polygons as intruders) + * + * "AND" is implemented by "outside == false", "NOT" by "outside == true" with "include_borders == true". + * With "include_borders == false" the operations are "INSIDE" and "OUTSIDE". + */ +class DB_PUBLIC EdgeToPolygonLocalOperation + : public local_operation +{ +public: + EdgeToPolygonLocalOperation (EdgePolygonOp::mode_t op, bool include_borders); + + virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + + // edge interaction distance is 1 to force overlap between edges and edge/boxes + virtual db::Coord dist () const { return m_include_borders ? 1 : 0; } + +private: + db::EdgePolygonOp::mode_t m_op; + bool m_include_borders; +}; + +} + +#endif diff --git a/src/db/db/dbLocalOperation.cc b/src/db/db/dbLocalOperation.cc index b95ebea92..2c7283cd1 100644 --- a/src/db/db/dbLocalOperation.cc +++ b/src/db/db/dbLocalOperation.cc @@ -27,7 +27,6 @@ #include "dbBoxConvert.h" #include "dbPolygonGenerators.h" #include "dbPolygonTools.h" -#include "dbLocalOperationUtils.h" #include "dbEdgeBoolean.h" #include "dbLayoutUtils.h" #include "tlLog.h" @@ -115,656 +114,5 @@ template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; template class DB_PUBLIC local_operation; -// --------------------------------------------------------------------------------------------- -// BoolAndOrNotLocalOperation implementation - -template -bool_and_or_not_local_operation::bool_and_or_not_local_operation (bool is_and) - : m_is_and (is_and) -{ - // .. nothing yet .. -} - -template -OnEmptyIntruderHint -bool_and_or_not_local_operation::on_empty_intruder_hint () const -{ - return m_is_and ? Drop : Copy; -} - -template -std::string -bool_and_or_not_local_operation::description () const -{ - return m_is_and ? tl::to_string (tr ("AND operation")) : tl::to_string (tr ("NOT operation")); -} - -template -void -bool_and_or_not_local_operation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const -{ - tl_assert (results.size () == 1); - std::unordered_set &result = results.front (); - - db::EdgeProcessor ep; - - size_t p1 = 0, p2 = 1; - - std::set others; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - for (auto j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - - const TR &subject = interactions.subject_shape (i->first); - if (others.find (subject) != others.end ()) { - if (m_is_and) { - result.insert (subject); - } - } else if (i->second.empty ()) { - // shortcut (not: keep, and: drop) - if (! m_is_and) { - result.insert (subject); - } - } else { - for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p1); - } - p1 += 2; - } - - } - - if (! others.empty () && p1 > 0) { - - for (auto o = others.begin (); o != others.end (); ++o) { - for (auto e = o->begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p2); - } - p2 += 2; - } - - db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); - db::polygon_ref_generator pr (layout, result); - db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); - db::PolygonGenerator pg (splitter, true, true); - ep.set_base_verbosity (50); - ep.process (pg, op); - - } -} - -template class DB_PUBLIC bool_and_or_not_local_operation; -template class DB_PUBLIC bool_and_or_not_local_operation; - -// --------------------------------------------------------------------------------------------- -// BoolAndOrNotLocalOperationWithProperties implementation - -template -bool_and_or_not_local_operation_with_properties::bool_and_or_not_local_operation_with_properties (bool is_and, db::PropertiesRepository *target_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr, db::PropertyConstraint property_constraint) - : m_is_and (is_and), m_property_constraint (property_constraint), m_pms (target_pr, subject_pr), m_pmi (target_pr, intruder_pr) -{ - // .. nothing yet .. -} - -template -OnEmptyIntruderHint -bool_and_or_not_local_operation_with_properties::on_empty_intruder_hint () const -{ - return m_is_and ? Drop : Copy; -} - -template -std::string -bool_and_or_not_local_operation_with_properties::description () const -{ - return m_is_and ? tl::to_string (tr ("AND operation")) : tl::to_string (tr ("NOT operation")); -} - -template -void -bool_and_or_not_local_operation_with_properties::do_compute_local (db::Layout *layout, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &results, size_t max_vertex_count, double area_ratio) const -{ - tl_assert (results.size () == 1); - std::unordered_set > &result = results.front (); - - db::EdgeProcessor ep; - - std::map, std::set > > by_prop_id; - - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - - const db::object_with_properties &subject = interactions.subject_shape (i->first); - - if (i->second.empty ()) { - - if (! m_is_and) { - result.insert (db::object_with_properties (subject, m_pms (subject.properties_id ()))); - } - - } else { - - db::properties_id_type prop_id_s = m_pms (subject.properties_id ()); - - auto &shapes_by_prop = by_prop_id [prop_id_s]; - shapes_by_prop.first.push_front (subject); - - for (auto j = i->second.begin (); j != i->second.end (); ++j) { - const db::object_with_properties &intruder = interactions.intruder_shape (*j).second; - db::properties_id_type prop_id_i = (m_property_constraint != db::NoPropertyConstraint ? m_pmi (intruder.properties_id ()) : prop_id_s); - if ((prop_id_i != prop_id_s) == (m_property_constraint == db::DifferentPropertiesConstraint)) { - shapes_by_prop.second.insert (intruder); - } - } - - } - - } - - for (auto p2s = by_prop_id.begin (); p2s != by_prop_id.end (); ++p2s) { - - ep.clear (); - size_t p1 = 0, p2 = 1; - - const std::set &others = p2s->second.second; - db::properties_id_type prop_id = p2s->first; - - for (auto s = p2s->second.first.begin (); s != p2s->second.first.end (); ++s) { - - const TS &subject = *s; - if (others.find (subject) != others.end ()) { - if (m_is_and) { - result.insert (db::object_with_properties (subject, prop_id)); - } - } else if (others.empty ()) { - // shortcut (not: keep, and: drop) - if (! m_is_and) { - result.insert (db::object_with_properties (subject, prop_id)); - } - } else { - for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p1); - } - p1 += 2; - } - - } - - if (! others.empty () && p1 > 0) { - - for (auto o = others.begin (); o != others.end (); ++o) { - for (auto e = o->begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p2); - } - p2 += 2; - } - - db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); - db::polygon_ref_generator_with_properties > pr (layout, result, prop_id); - db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); - db::PolygonGenerator pg (splitter, true, true); - ep.set_base_verbosity (50); - ep.process (pg, op); - - } - - } -} - -template class DB_PUBLIC bool_and_or_not_local_operation_with_properties; -template class DB_PUBLIC bool_and_or_not_local_operation_with_properties; - -// --------------------------------------------------------------------------------------------- -// TwoBoolAndNotLocalOperation implementation - -template -two_bool_and_not_local_operation::two_bool_and_not_local_operation () - : db::local_operation () -{ - // .. nothing yet .. -} - -template -void -two_bool_and_not_local_operation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const -{ - tl_assert (results.size () == 2); - - db::EdgeProcessor ep; - - std::unordered_set &result0 = results [0]; - std::unordered_set &result1 = results [1]; - - size_t p1 = 0, p2 = 1; - - std::set others; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - for (auto j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - - const TS &subject = interactions.subject_shape (i->first); - if (others.find (subject) != others.end ()) { - result0.insert (subject); - } else if (i->second.empty ()) { - // shortcut (not: keep, and: drop) - result1.insert (subject); - } else { - for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p1); - } - p1 += 2; - } - - } - - if (! others.empty () && p1 > 0) { - - for (auto o = others.begin (); o != others.end (); ++o) { - for (auto e = o->begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p2); - } - p2 += 2; - } - - db::BooleanOp op0 (db::BooleanOp::And); - db::polygon_ref_generator pr0 (layout, result0); - db::PolygonSplitter splitter0 (pr0, area_ratio, max_vertex_count); - db::PolygonGenerator pg0 (splitter0, true, true); - - db::BooleanOp op1 (db::BooleanOp::ANotB); - db::polygon_ref_generator pr1 (layout, result1); - db::PolygonSplitter splitter1 (pr1, area_ratio, max_vertex_count); - db::PolygonGenerator pg1 (splitter1, true, true); - - ep.set_base_verbosity (50); - - std::vector > procs; - procs.push_back (std::make_pair (&pg0, &op0)); - procs.push_back (std::make_pair (&pg1, &op1)); - ep.process (procs); - - } - -} - -template -std::string two_bool_and_not_local_operation::description () const -{ - return tl::to_string (tr ("ANDNOT operation")); -} - -template class DB_PUBLIC two_bool_and_not_local_operation; -template class DB_PUBLIC two_bool_and_not_local_operation; - -// --------------------------------------------------------------------------------------------- -// TwoBoolAndNotLocalOperationWithProperties implementation - -template -two_bool_and_not_local_operation_with_properties::two_bool_and_not_local_operation_with_properties (db::PropertiesRepository *target1_pr, db::PropertiesRepository *target2_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr, db::PropertyConstraint property_constraint) - : db::local_operation, db::object_with_properties, db::object_with_properties > (), - m_property_constraint (property_constraint), m_pms (target1_pr, subject_pr), m_pmi (target1_pr, intruder_pr), m_pm12 (target2_pr, target1_pr) -{ - // .. nothing yet .. -} - -template -void -two_bool_and_not_local_operation_with_properties::do_compute_local (db::Layout *layout, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &results, size_t max_vertex_count, double area_ratio) const -{ - tl_assert (results.size () == 2); - std::unordered_set > &result0 = results [0]; - std::unordered_set > &result1 = results [1]; - - db::EdgeProcessor ep; - - std::map, std::set > > by_prop_id; - - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - - const db::object_with_properties &subject = interactions.subject_shape (i->first); - - if (i->second.empty ()) { - - result1.insert (db::object_with_properties (subject, m_pms (subject.properties_id ()))); - - } else { - - db::properties_id_type prop_id_s = m_pms (subject.properties_id ()); - - auto &shapes_by_prop = by_prop_id [prop_id_s]; - shapes_by_prop.first.push_front (subject); - - for (auto j = i->second.begin (); j != i->second.end (); ++j) { - const db::object_with_properties &intruder = interactions.intruder_shape (*j).second; - db::properties_id_type prop_id_i = (m_property_constraint != db::NoPropertyConstraint ? m_pmi (intruder.properties_id ()) : prop_id_s); - if ((prop_id_i != prop_id_s) == (m_property_constraint == db::DifferentPropertiesConstraint)) { - shapes_by_prop.second.insert (intruder); - } - } - - } - - } - - for (auto p2s = by_prop_id.begin (); p2s != by_prop_id.end (); ++p2s) { - - ep.clear (); - size_t p1 = 0, p2 = 1; - - const std::set &others = p2s->second.second; - db::properties_id_type prop_id = p2s->first; - - for (auto s = p2s->second.first.begin (); s != p2s->second.first.end (); ++s) { - - const TR &subject = *s; - if (others.find (subject) != others.end ()) { - result0.insert (db::object_with_properties (subject, prop_id)); - } else if (others.empty ()) { - // shortcut (not: keep, and: drop) - result1.insert (db::object_with_properties (subject, m_pm12 (prop_id))); - } else { - for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p1); - } - p1 += 2; - } - - } - - if (! others.empty () && p1 > 0) { - - for (auto o = others.begin (); o != others.end (); ++o) { - for (auto e = o->begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p2); - } - p2 += 2; - } - - std::unordered_set result0_wo_props; - std::unordered_set result1_wo_props; - - db::BooleanOp op0 (db::BooleanOp::And); - db::polygon_ref_generator pr0 (layout, result0_wo_props); - db::PolygonSplitter splitter0 (pr0, area_ratio, max_vertex_count); - db::PolygonGenerator pg0 (splitter0, true, true); - - db::BooleanOp op1 (db::BooleanOp::ANotB); - db::polygon_ref_generator pr1 (layout, result1_wo_props); - db::PolygonSplitter splitter1 (pr1, area_ratio, max_vertex_count); - db::PolygonGenerator pg1 (splitter1, true, true); - - ep.set_base_verbosity (50); - - std::vector > procs; - procs.push_back (std::make_pair (&pg0, &op0)); - procs.push_back (std::make_pair (&pg1, &op1)); - ep.process (procs); - - for (auto r = result0_wo_props.begin (); r != result0_wo_props.end (); ++r) { - result0.insert (db::object_with_properties (*r, prop_id)); - } - for (auto r = result1_wo_props.begin (); r != result1_wo_props.end (); ++r) { - result1.insert (db::object_with_properties (*r, m_pm12 (prop_id))); - } - - } - - } -} - -template -std::string two_bool_and_not_local_operation_with_properties::description () const -{ - return tl::to_string (tr ("ANDNOT operation")); -} - -template class DB_PUBLIC two_bool_and_not_local_operation_with_properties; -template class DB_PUBLIC two_bool_and_not_local_operation_with_properties; - -// --------------------------------------------------------------------------------------------- - -SelfOverlapMergeLocalOperation::SelfOverlapMergeLocalOperation (unsigned int wrap_count) - : m_wrap_count (wrap_count) -{ - // .. nothing yet .. -} - -void -SelfOverlapMergeLocalOperation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const -{ - tl_assert (results.size () == 1); - std::unordered_set &result = results.front (); - - if (m_wrap_count == 0) { - return; - } - - db::EdgeProcessor ep; - - size_t p1 = 0, p2 = 1; - std::set seen; - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - - if (seen.find (i->first) == seen.end ()) { - seen.insert (i->first); - const db::PolygonRef &subject = interactions.subject_shape (i->first); - for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p1); - } - p1 += 2; - } - - for (db::shape_interactions::iterator2 o = i->second.begin (); o != i->second.end (); ++o) { - // don't take the same (really the same, not an identical one) shape twice - the interaction - // set does not take care to list just one copy of the same item on the intruder side. - if (seen.find (*o) == seen.end ()) { - seen.insert (*o); - const db::PolygonRef &intruder = interactions.intruder_shape (*o).second; - for (db::PolygonRef::polygon_edge_iterator e = intruder.begin_edge (); ! e.at_end(); ++e) { - ep.insert (*e, p2); - } - p2 += 2; - } - } - - } - - db::MergeOp op (m_wrap_count - 1); - db::PolygonRefGenerator pr (layout, result); - db::PolygonGenerator pg (pr, true, true); - ep.set_base_verbosity (50); - ep.process (pg, op); -} - -OnEmptyIntruderHint SelfOverlapMergeLocalOperation::on_empty_intruder_hint () const -{ - return m_wrap_count > 1 ? Drop : Copy; -} - -std::string SelfOverlapMergeLocalOperation::description () const -{ - return tl::sprintf (tl::to_string (tr ("Self-overlap (wrap count %d)")), int (m_wrap_count)); -} - -// --------------------------------------------------------------------------------------------- -// EdgeBoolAndOrNotLocalOperation implementation - -EdgeBoolAndOrNotLocalOperation::EdgeBoolAndOrNotLocalOperation (EdgeBoolOp op) - : m_op (op) -{ - // .. nothing yet .. -} - -OnEmptyIntruderHint -EdgeBoolAndOrNotLocalOperation::on_empty_intruder_hint () const -{ - return (m_op == EdgeAnd || m_op == EdgeIntersections) ? Drop : Copy; -} - -std::string -EdgeBoolAndOrNotLocalOperation::description () const -{ - if (m_op == EdgeIntersections) { - return tl::to_string (tr ("Edge INTERSECTION operation")); - } else if (m_op == EdgeAnd) { - return tl::to_string (tr ("Edge AND operation")); - } else if (m_op == EdgeNot) { - return tl::to_string (tr ("Edge NOT operation")); - } else { - return std::string (); - } -} - -void -EdgeBoolAndOrNotLocalOperation::do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const -{ - tl_assert (results.size () == size_t (m_op == EdgeAndNot ? 2 : 1)); - - std::unordered_set &result = results.front (); - - std::unordered_set *result2 = 0; - if (results.size () > 1) { - result2 = &results[1]; - } - - EdgeBooleanClusterCollector > cluster_collector (&result, m_op, result2); - - db::box_scanner scanner; - - std::set others; - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - bool any_subject = false; - bool is_and = (m_op == EdgeAnd || m_op == EdgeAndNot || m_op == EdgeIntersections); - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - - const db::Edge &subject = interactions.subject_shape (i->first); - if (others.find (subject) != others.end ()) { - if (is_and) { - result.insert (subject); - } - } else if (i->second.empty ()) { - // shortcut (not: keep, and: drop) - if (! is_and) { - result.insert (subject); - } - } else { - scanner.insert (&subject, 0); - any_subject = true; - } - - } - - if (! others.empty () || any_subject) { - - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - scanner.insert (o.operator-> (), 1); - } - - scanner.process (cluster_collector, 1, db::box_convert ()); - - } -} - -// --------------------------------------------------------------------------------------------- -// EdgeToPolygonLocalOperation implementation - -EdgeToPolygonLocalOperation::EdgeToPolygonLocalOperation (EdgePolygonOp::mode_t op, bool include_borders) - : m_op (op), m_include_borders (include_borders) -{ - // .. nothing yet .. -} - -OnEmptyIntruderHint -EdgeToPolygonLocalOperation::on_empty_intruder_hint () const -{ - return m_op == EdgePolygonOp::Inside ? Drop : (m_op == EdgePolygonOp::Outside ? Copy : CopyToSecond); -} - -std::string -EdgeToPolygonLocalOperation::description () const -{ - if (m_op == EdgePolygonOp::Inside) { - return tl::to_string (tr ("Edge to polygon AND/INSIDE")); - } else if (m_op == EdgePolygonOp::Outside) { - return tl::to_string (tr ("Edge to polygon NOT/OUTSIDE")); - } else { - return tl::to_string (tr ("Edge to polygon ANDNOT/INOUTSIDE")); - } -} - -void -EdgeToPolygonLocalOperation::do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const -{ - tl_assert (results.size () == size_t (m_op == EdgePolygonOp::Both ? 2 : 1)); - - std::unordered_set &result = results.front (); - - std::unordered_set *result2 = 0; - if (results.size () > 1) { - result2 = &results[1]; - } - - db::EdgeProcessor ep; - - std::set others; - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { - others.insert (interactions.intruder_shape (*j).second); - } - } - - bool any_subject = false; - - for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - - const db::Edge &subject = interactions.subject_shape (i->first); - if (i->second.empty ()) { - // shortcut (outside: keep, otherwise: drop) - if (m_op == db::EdgePolygonOp::Outside) { - result.insert (subject); - } else if (m_op == db::EdgePolygonOp::Both) { - result2->insert (subject); - } - } else { - ep.insert (subject, 1); - any_subject = true; - } - - } - - if (! others.empty () || any_subject) { - - for (std::set::const_iterator o = others.begin (); o != others.end (); ++o) { - for (db::PolygonRef::polygon_edge_iterator e = o->begin_edge (); ! e.at_end (); ++e) { - ep.insert (*e, 0); - } - } - - std::unique_ptr cc_second; - if (result2) { - cc_second.reset (new db::EdgeToEdgeSetGenerator (*result2, 2 /*second tag*/)); - } - - db::EdgeToEdgeSetGenerator cc (result, 1 /*first tag*/, cc_second.get ()); - db::EdgePolygonOp op (m_op, m_include_borders); - ep.process (cc, op); - - } -} - } diff --git a/src/db/db/dbLocalOperation.h b/src/db/db/dbLocalOperation.h index 93b12d9ff..34f45e4d7 100644 --- a/src/db/db/dbLocalOperation.h +++ b/src/db/db/dbLocalOperation.h @@ -131,156 +131,6 @@ protected: virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const = 0; }; -/** - * @brief Implements a boolean AND or NOT operation - */ - -template -class DB_PUBLIC bool_and_or_not_local_operation - : public local_operation -{ -public: - bool_and_or_not_local_operation (bool is_and); - - virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; - virtual OnEmptyIntruderHint on_empty_intruder_hint () const; - virtual std::string description () const; - -private: - bool m_is_and; -}; - -typedef bool_and_or_not_local_operation BoolAndOrNotLocalOperation; - -/** - * @brief Implements a boolean AND or NOT operation with property handling - */ -template -class DB_PUBLIC bool_and_or_not_local_operation_with_properties - : public local_operation, db::object_with_properties, db::object_with_properties > -{ -public: - bool_and_or_not_local_operation_with_properties (bool is_and, db::PropertiesRepository *target_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr, db::PropertyConstraint property_constraint); - - virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &result, size_t max_vertex_count, double area_ratio) const; - virtual OnEmptyIntruderHint on_empty_intruder_hint () const; - virtual std::string description () const; - -private: - bool m_is_and; - db::PropertyConstraint m_property_constraint; - mutable db::PropertyMapper m_pms; - mutable db::PropertyMapper m_pmi; -}; - -typedef bool_and_or_not_local_operation_with_properties BoolAndOrNotLocalOperationWithProperties; - -/** - * @brief Implements a boolean AND plus NOT operation - * - * This processor delivers two outputs: the first one having the AND result, the second - * one having the NOT result. - */ - -template -class DB_PUBLIC two_bool_and_not_local_operation - : public local_operation -{ -public: - two_bool_and_not_local_operation (); - - virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; - virtual std::string description () const; -}; - -typedef two_bool_and_not_local_operation TwoBoolAndNotLocalOperation; - -/** - * @brief Implements a boolean AND plus NOT operation - * - * This processor delivers two outputs: the first one having the AND result, the second - * one having the NOT result. - */ -template -class DB_PUBLIC two_bool_and_not_local_operation_with_properties - : public local_operation, db::object_with_properties, db::object_with_properties > -{ -public: - two_bool_and_not_local_operation_with_properties (db::PropertiesRepository *target1_pr, db::PropertiesRepository *target2_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr, db::PropertyConstraint property_constraint); - - virtual void do_compute_local (db::Layout *layout, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &result, size_t max_vertex_count, double area_ratio) const; - virtual std::string description () const; - -private: - db::PropertyConstraint m_property_constraint; - mutable db::PropertyMapper m_pms, m_pmi, m_pm12; -}; - -typedef two_bool_and_not_local_operation_with_properties TwoBoolAndNotLocalOperationWithProperties; - -/** - * @brief Implements a merge operation with an overlap count - * With a given wrap_count, the result will only contains shapes where - * the original shapes overlap at least "wrap_count" times. - */ -class DB_PUBLIC SelfOverlapMergeLocalOperation - : public local_operation -{ -public: - SelfOverlapMergeLocalOperation (unsigned int wrap_count); - - virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; - virtual OnEmptyIntruderHint on_empty_intruder_hint () const; - virtual std::string description () const; - -private: - unsigned int m_wrap_count; -}; - -/** - * @brief Implements a boolean AND or NOT operation between edges - */ -class DB_PUBLIC EdgeBoolAndOrNotLocalOperation - : public local_operation -{ -public: - EdgeBoolAndOrNotLocalOperation (db::EdgeBoolOp op); - - virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; - virtual OnEmptyIntruderHint on_empty_intruder_hint () const; - virtual std::string description () const; - - // edge interaction distance is 1 to force overlap between edges and edge/boxes - virtual db::Coord dist () const { return 1; } - -private: - db::EdgeBoolOp m_op; -}; - -/** - * @brief Implements a boolean AND or NOT operation between edges and polygons (polygons as intruders) - * - * "AND" is implemented by "outside == false", "NOT" by "outside == true" with "include_borders == true". - * With "include_borders == false" the operations are "INSIDE" and "OUTSIDE". - */ -class DB_PUBLIC EdgeToPolygonLocalOperation - : public local_operation -{ -public: - EdgeToPolygonLocalOperation (EdgePolygonOp::mode_t op, bool include_borders); - - virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; - virtual OnEmptyIntruderHint on_empty_intruder_hint () const; - virtual std::string description () const; - - // edge interaction distance is 1 to force overlap between edges and edge/boxes - virtual db::Coord dist () const { return m_include_borders ? 1 : 0; } - -private: - db::EdgePolygonOp::mode_t m_op; - bool m_include_borders; -}; - } #endif diff --git a/src/db/db/dbLocalOperationUtils.h b/src/db/db/dbLocalOperationUtils.h index 42484a243..390ebb6c9 100644 --- a/src/db/db/dbLocalOperationUtils.h +++ b/src/db/db/dbLocalOperationUtils.h @@ -29,6 +29,7 @@ #include "dbLayout.h" #include "dbPolygonGenerators.h" +#include "dbLocalOperation.h" #include "dbHash.h" #include "tlThreads.h" @@ -303,6 +304,45 @@ private: db::properties_id_type m_prop_id; }; +/** + * @brief Separates the interacting shapes by property relation + * + * Returns a map of property ID, subject shapes and intruder shapes belonging to the subject shapes. + * Depending on the property constraint the intruders will either be ones with and properties (NoPropertyConstraint), + * the same properties than the subject (SamePropertiesConstraint) or different properties (DifferentPropertiesConstraint). + */ +template +DB_PUBLIC +std::map, std::set > > +separate_interactions_by_properties (const shape_interactions, db::object_with_properties > &interactions, db::PropertyConstraint property_constraint, db::PropertyMapper &pms, db::PropertyMapper &pmi) +{ + std::map, std::set > > by_prop_id; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + + const db::object_with_properties &subject = interactions.subject_shape (i->first); + + db::properties_id_type prop_id = pms (subject.properties_id ()); + + std::pair, std::set > &s2p = by_prop_id [prop_id]; + s2p.first.push_back (&subject); + + for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { + + const std::pair > &intruder = interactions.intruder_shape (*ii); + db::properties_id_type intruder_prop_id = (property_constraint == db::NoPropertyConstraint ? prop_id : pmi (intruder.second.properties_id ())); + + if ((property_constraint == db::DifferentPropertiesConstraint) == (prop_id != intruder_prop_id)) { + s2p.second.insert (&intruder.second); + } + + } + + } + + return by_prop_id; +} + } #endif diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 90d0399ec..5e018163f 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -95,6 +95,9 @@ private: // --------------------------------------------------------------------------------------------------------------- +namespace +{ + static inline bool shields_interaction (const db::EdgePair &ep, const db::Edge &q) { db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ()); @@ -121,16 +124,6 @@ static bool shields_interaction (const db::EdgePair &ep, const P &poly) return false; } -template -check_local_operation::check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options) - : m_check (check), m_different_polygons (different_polygons), m_is_merged (is_merged), m_has_other (has_other), m_other_is_merged (other_is_merged), m_options (options) -{ - // .. nothing yet .. -} - -namespace -{ - template void insert_into_hash (std::unordered_set &, const S &) { @@ -143,8 +136,6 @@ void insert_into_hash (std::unordered_set &hash, const T &shape) hash.insert (shape); } -} - template static uint32_t compute_error_pattern (const TS &subject, std::unordered_set &result, std::map &edges_with_errors) @@ -195,13 +186,20 @@ static bool rect_filter_can_be_waived (uint32_t error_pattern, uint32_t rect_fil return can_be_waived; } +} + + +template +check_local_operation_base::check_local_operation_base (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options) + : m_check (check), m_different_polygons (different_polygons), m_is_merged (is_merged), m_has_other (has_other), m_other_is_merged (other_is_merged), m_options (options) +{ + // .. nothing yet .. +} + template void -check_local_operation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +check_local_operation_base::compute_results (db::Layout *layout, const std::vector &subjects, const std::set &intruders, std::unordered_set &result, std::unordered_set &intra_polygon_result) const { - tl_assert (results.size () == 1); - std::unordered_set result, intra_polygon_result; - // NOTE: the rectangle and opposite filters are unsymmetric bool symmetric_edge_pairs = ! m_has_other && m_options.opposite_filter == db::NoOppositeFilter && m_options.rect_filter == RectFilter::NoRectFilter; @@ -214,14 +212,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape db::EdgeProcessor ep; ep.set_base_verbosity (50); - std::set ids; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - for (auto j = i->second.begin (); j != i->second.end (); ++j) { - ids.insert (*j); - } - } - - bool take_all = edge_check.has_negative_edge_output () || interactions.num_intruders () == 0; + bool take_all = edge_check.has_negative_edge_output () || intruders.empty (); db::Box common_box; if (! take_all) { @@ -229,14 +220,14 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape db::Vector e (edge_check.distance (), edge_check.distance ()); db::Box subject_box; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - subject_box += db::box_convert () (interactions.subject_shape (i->first)); + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + subject_box += db::box_convert () (**i); } if (edge_check.requires_different_layers ()) { db::Box intruder_box; - for (auto id = ids.begin (); id != ids.end (); ++id) { - intruder_box += db::box_convert () (interactions.intruder_shape (*id).second); + for (auto i = intruders.begin (); i != intruders.end (); ++i) { + intruder_box += db::box_convert () (**i); } common_box = subject_box.enlarged (e) & intruder_box.enlarged (e); } else { @@ -249,14 +240,13 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape size_t n = 0; - if (m_is_merged || (interactions.size () == 1 && interactions.subject_shape (interactions.begin ()->first).is_box ())) { + if (m_is_merged || (subjects.size () == 1 && subjects.front ()->is_box ())) { - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - const TS &subject = interactions.subject_shape (i->first); + for (auto i = subjects.begin (); i != subjects.end (); ++i) { if (! take_all) { - poly_check.enter (subject, n, common_box); + poly_check.enter (**i, n, common_box); } else { - poly_check.enter (subject, n); + poly_check.enter (**i, n); } n += 2; } @@ -268,9 +258,8 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.clear (); size_t nn = 0; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - const TS &is = interactions.subject_shape (i->first); - for (typename TS::polygon_edge_iterator e = is.begin_edge (); ! e.at_end (); ++e) { + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } ++nn; @@ -296,11 +285,11 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape // merge the intruders to remove inner edges - if (ids.empty ()) { + if (intruders.empty ()) { // empty intruders - } else if (! m_other_is_merged && (ids.size () > 1 || ! interactions.intruder_shape (*ids.begin ()).second.is_box ())) { + } else if (! m_other_is_merged && (intruders.size () > 1 || ! (*intruders.begin ())->is_box ())) { // NOTE: this local merge is not necessarily giving the same results than a global merge before running // the processor. Reason: the search range is limited, hence not all necessary components may have been @@ -309,9 +298,8 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.clear (); size_t nn = 0; - for (auto id = ids.begin (); id != ids.end (); ++id) { - const TI &is = interactions.intruder_shape (*id).second; - for (auto e = is.begin_edge (); ! e.at_end (); ++e) { + for (auto i = intruders.begin (); i != intruders.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } ++nn; @@ -338,11 +326,11 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape } else { n = 1; - for (auto id = ids.begin (); id != ids.end (); ++id) { + for (auto i = intruders.begin (); i != intruders.end (); ++i) { if (! take_all) { - poly_check.enter (interactions.intruder_shape (*id).second, n, common_box); + poly_check.enter (**i, n, common_box); } else { - poly_check.enter (interactions.intruder_shape (*id).second, n); + poly_check.enter (**i, n); } n += 2; } @@ -351,41 +339,39 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape } else { - if (m_is_merged || (interactions.size () == 1 && ids.empty () && interactions.subject_shape (interactions.begin ()->first).is_box ())) { + if (m_is_merged || (subjects.size () == 1 && intruders.empty () && subjects.front ()->is_box ())) { // no merge required // NOTE: we need to eliminate identical shapes from intruders and subjects because those will shield size_t n = 0; - std::unordered_set subjects; + std::unordered_set subjects_hash; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { + for (auto i = subjects.begin (); i != subjects.end (); ++i) { // we can't directly insert because TS may be != TI - const TS &ts = interactions.subject_shape (i->first); - insert_into_hash (subjects, ts); + insert_into_hash (subjects_hash, **i); if (! take_all) { - poly_check.enter (ts, n, common_box); + poly_check.enter (**i, n, common_box); } else { - poly_check.enter (ts, n); + poly_check.enter (**i, n); } n += 2; } n = 1; - for (auto id = ids.begin (); id != ids.end (); ++id) { - const TI &ti = interactions.intruder_shape (*id).second; - if (subjects.find (ti) == subjects.end ()) { + for (auto i = intruders.begin (); i != intruders.end (); ++i) { + if (subjects_hash.find (**i) == subjects_hash.end ()) { if (! take_all) { - poly_check.enter (ti, n, common_box); + poly_check.enter (**i, n, common_box); } else { - poly_check.enter (ti, n); + poly_check.enter (**i, n); } } } - } else if (ids.empty ()) { + } else if (intruders.empty ()) { // merge needed for the subject shapes - no intruders present so this is the simple case @@ -394,9 +380,8 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.clear (); size_t nn = 0; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - const TS &ts = interactions.subject_shape (i->first); - for (auto e = ts.begin_edge (); ! e.at_end (); ++e) { + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } ++nn; @@ -427,17 +412,15 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.clear (); size_t nn = 0; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - const TS &ts = interactions.subject_shape (i->first); - for (auto e = ts.begin_edge (); ! e.at_end (); ++e) { + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } ++nn; } - for (auto id = ids.begin (); id != ids.end (); ++id) { - const TI &ti = interactions.intruder_shape (*id).second; - for (auto e = ti.begin_edge (); ! e.at_end (); ++e) { + for (auto i = intruders.begin (); i != intruders.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } ++nn; @@ -453,16 +436,14 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape std::vector subject_edges; size_t sz = 0; - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - const TS &ts = interactions.subject_shape (i->first); - sz += ts.vertices (); + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + sz += (*i)->vertices (); } subject_edges.reserve (sz); - for (auto i = interactions.begin (); i != interactions.end (); ++i) { - const TS &ts = interactions.subject_shape (i->first); - for (auto e = ts.begin_edge (); ! e.at_end (); ++e) { + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { subject_edges.push_back (*e); } } @@ -507,201 +488,245 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape do { poly_check.process (); } while (edge_check.prepare_next_pass ()); +} - // detect and remove parts of the result which have or do not have results "opposite" - // ("opposite" is defined by the projection of edges "through" the subject shape) - if (m_options.opposite_filter != db::NoOppositeFilter && (! result.empty () || ! intra_polygon_result.empty ())) { +template +void +check_local_operation_base::apply_opposite_filter (const std::vector &subjects, std::unordered_set &result, std::unordered_set &intra_polygon_result) const +{ + db::EdgeRelationFilter opp (db::WidthRelation, std::numeric_limits::max (), db::Projection); - db::EdgeRelationFilter opp (db::WidthRelation, std::numeric_limits::max (), db::Projection); + std::unordered_set cleaned_result; - std::unordered_set cleaned_result; + if (m_has_other) { - if (m_has_other) { + tl_assert (intra_polygon_result.empty ()); - tl_assert (intra_polygon_result.empty ()); + // filter out opposite edges: this is the case of two-layer checks where we can maintain the edge pairs but + // strip them of the filtered-out part. - // filter out opposite edges: this is the case of two-layer checks where we can maintain the edge pairs but - // strip them of the filtered-out part. + std::vector projections; + for (std::unordered_set::const_iterator ep1 = result.begin (); ep1 != result.end (); ++ep1) { - std::vector projections; - for (std::unordered_set::const_iterator ep1 = result.begin (); ep1 != result.end (); ++ep1) { + projections.clear (); - projections.clear (); + for (std::unordered_set::const_iterator ep2 = result.begin (); ep2 != result.end (); ++ep2) { - for (std::unordered_set::const_iterator ep2 = result.begin (); ep2 != result.end (); ++ep2) { + if (ep1 == ep2) { + continue; + } - if (ep1 == ep2) { - continue; + db::EdgePair ep_opp; + if (opp.check (ep1->first (), ep2->first (), &ep_opp)) { + + bool shielded = false; + for (auto i = subjects.begin (); i != subjects.end () && ! shielded; ++i) { + shielded = shields_interaction (ep_opp, **i); } - db::EdgePair ep_opp; - if (opp.check (ep1->first (), ep2->first (), &ep_opp)) { - - bool shielded = false; - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end () && ! shielded; ++i) { - shielded = shields_interaction (ep_opp, interactions.subject_shape (i->first)); - } - - if (! shielded) { - projections.push_back (ep_opp.first ()); - } - + if (! shielded) { + projections.push_back (ep_opp.first ()); } } - if (! projections.empty ()) { - db::Edges ce; - if (m_options.opposite_filter == db::OnlyOpposite) { - ce = db::Edges (ep1->first ()) & db::Edges (projections.begin (), projections.end ()); - } else if (m_options.opposite_filter == db::NotOpposite) { - ce = db::Edges (ep1->first ()) - db::Edges (projections.begin (), projections.end ()); - } - for (db::Edges::const_iterator re = ce.begin (); ! re.at_end (); ++re) { - cleaned_result.insert (db::EdgePair (*re, ep1->second ())); - } + } + + if (! projections.empty ()) { + db::Edges ce; + if (m_options.opposite_filter == db::OnlyOpposite) { + ce = db::Edges (ep1->first ()) & db::Edges (projections.begin (), projections.end ()); } else if (m_options.opposite_filter == db::NotOpposite) { - cleaned_result.insert (*ep1); + ce = db::Edges (ep1->first ()) - db::Edges (projections.begin (), projections.end ()); } - - } - - } else { - - // this is the single-layer case where we cannot maintain the edge pairs as we don't know how the - // other side will be filtered. For the filtering we only need the first edges and both edges of the - // intra-polygon checks - - std::unordered_set edges; - - for (std::unordered_set::const_iterator ep = result.begin (); ep != result.end (); ++ep) { - edges.insert (ep->first ()); - } - - for (std::unordered_set::const_iterator ep = intra_polygon_result.begin (); ep != intra_polygon_result.end (); ++ep) { - edges.insert (ep->first ()); - edges.insert (ep->second ()); - } - - // filter out opposite edges - std::vector projections; - for (std::unordered_set::const_iterator e1 = edges.begin (); e1 != edges.end (); ++e1) { - - projections.clear (); - - for (std::unordered_set::const_iterator e2 = edges.begin (); e2 != edges.end (); ++e2) { - - if (e1 == e2) { - continue; - } - - db::EdgePair ep_opp; - if (opp.check (*e1, *e2, &ep_opp)) { - - bool shielded = false; - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end () && ! shielded; ++i) { - shielded = shields_interaction (ep_opp, interactions.subject_shape (i->first)); - } - - if (! shielded) { - projections.push_back (ep_opp.first ()); - } - - } - + for (db::Edges::const_iterator re = ce.begin (); ! re.at_end (); ++re) { + cleaned_result.insert (db::EdgePair (*re, ep1->second ())); } - - if (! projections.empty ()) { - db::Edges ce; - if (m_options.opposite_filter == db::OnlyOpposite) { - ce = db::Edges (*e1) & db::Edges (projections.begin (), projections.end ()); - } else if (m_options.opposite_filter == db::NotOpposite) { - ce = db::Edges (*e1) - db::Edges (projections.begin (), projections.end ()); - } - for (db::Edges::const_iterator re = ce.begin (); ! re.at_end (); ++re) { - cleaned_result.insert (db::EdgePair (*re, re->swapped_points ())); - } - } else if (m_options.opposite_filter == db::NotOpposite) { - cleaned_result.insert (db::EdgePair (*e1, e1->swapped_points ())); - } - + } else if (m_options.opposite_filter == db::NotOpposite) { + cleaned_result.insert (*ep1); } } - result.swap (cleaned_result); - } else { - result.insert (intra_polygon_result.begin (), intra_polygon_result.end ()); + // this is the single-layer case where we cannot maintain the edge pairs as we don't know how the + // other side will be filtered. For the filtering we only need the first edges and both edges of the + // intra-polygon checks - } + std::unordered_set edges; - // implements error filtering on rectangles - if (m_options.rect_filter != RectFilter::NoRectFilter && ! result.empty ()) { + for (std::unordered_set::const_iterator ep = result.begin (); ep != result.end (); ++ep) { + edges.insert (ep->first ()); + } - std::unordered_set waived; + for (std::unordered_set::const_iterator ep = intra_polygon_result.begin (); ep != intra_polygon_result.end (); ++ep) { + edges.insert (ep->first ()); + edges.insert (ep->second ()); + } - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + // filter out opposite edges + std::vector projections; + for (std::unordered_set::const_iterator e1 = edges.begin (); e1 != edges.end (); ++e1) { - const TS &subject = interactions.subject_shape (i->first); - if (! subject.is_box ()) { - continue; - } + projections.clear (); - std::map edges_with_errors; - unsigned int error_pattern = compute_error_pattern (subject, result, edges_with_errors); + for (std::unordered_set::const_iterator e2 = edges.begin (); e2 != edges.end (); ++e2) { - if (rect_filter_can_be_waived (error_pattern, (uint32_t) m_options.rect_filter)) { + if (e1 == e2) { + continue; + } - for (std::unordered_set::const_iterator ep = result.begin (); ep != result.end (); ++ep) { - if (edges_with_errors.find (ep->first ()) != edges_with_errors.end ()) { - waived.insert (*ep); + db::EdgePair ep_opp; + if (opp.check (*e1, *e2, &ep_opp)) { + + bool shielded = false; + for (auto i = subjects.begin (); i != subjects.end () && ! shielded; ++i) { + shielded = shields_interaction (ep_opp, **i); } + + if (! shielded) { + projections.push_back (ep_opp.first ()); + } + } } - } - - if (! waived.empty ()) { - for (std::unordered_set::const_iterator i = waived.begin (); i != waived.end (); ++i) { - result.erase (*i); + if (! projections.empty ()) { + db::Edges ce; + if (m_options.opposite_filter == db::OnlyOpposite) { + ce = db::Edges (*e1) & db::Edges (projections.begin (), projections.end ()); + } else if (m_options.opposite_filter == db::NotOpposite) { + ce = db::Edges (*e1) - db::Edges (projections.begin (), projections.end ()); + } + for (db::Edges::const_iterator re = ce.begin (); ! re.at_end (); ++re) { + cleaned_result.insert (db::EdgePair (*re, re->swapped_points ())); + } + } else if (m_options.opposite_filter == db::NotOpposite) { + cleaned_result.insert (db::EdgePair (*e1, e1->swapped_points ())); } + } - if (! m_has_other) { + } - // this is the case of single-layer interaction. We need to separate the results - // from edge pairs into single edges (basically returning the first edge only) - // Reasoning: we cannot say what's going to happen on the other side of the - // error - it may not be waived and we cannot waive half of an edge pair. + result.swap (cleaned_result); +} - for (std::unordered_set::const_iterator i = result.begin (); i != result.end (); ++i) { - results.front ().insert (db::EdgePair (i->first (), i->first ().swapped_points ())); +template +void +check_local_operation_base::apply_rectangle_filter (const std::vector &subjects, std::unordered_set &result) const +{ + std::unordered_set waived; + + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + + const TS &subject = **i; + if (! subject.is_box ()) { + continue; + } + + std::map edges_with_errors; + unsigned int error_pattern = compute_error_pattern (subject, result, edges_with_errors); + + if (rect_filter_can_be_waived (error_pattern, (uint32_t) m_options.rect_filter)) { + + for (std::unordered_set::const_iterator ep = result.begin (); ep != result.end (); ++ep) { + if (edges_with_errors.find (ep->first ()) != edges_with_errors.end ()) { + waived.insert (*ep); + } } - result.clear (); } } + if (! waived.empty ()) { + for (std::unordered_set::const_iterator i = waived.begin (); i != waived.end (); ++i) { + result.erase (*i); + } + } + + if (! m_has_other) { + + // this is the case of single-layer interaction. We need to separate the results + // from edge pairs into single edges (basically returning the first edge only) + // Reasoning: we cannot say what's going to happen on the other side of the + // error - it may not be waived and we cannot waive half of an edge pair. + + std::unordered_set filtered; + for (std::unordered_set::const_iterator i = result.begin (); i != result.end (); ++i) { + filtered.insert (db::EdgePair (i->first (), i->first ().swapped_points ())); + } + + result.swap (filtered); + + } +} + +// --------------------------------------------------------------------------------------------------------------- + +template +check_local_operation::check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options) + : check_local_operation_base (check, different_polygons, is_merged, has_other, other_is_merged, options) +{ + // .. nothing yet .. +} + +template +void +check_local_operation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + std::vector subjects; + subjects.reserve (interactions.size ()); + + std::set intruders; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + subjects.push_back (&interactions.subject_shape (i->first)); + for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { + intruders.insert (&interactions.intruder_shape (*ii).second); + } + } + + tl_assert (results.size () == 1); + + std::unordered_set result, intra_polygon_result; + + // perform the basic check + check_local_operation_base::compute_results (layout, subjects, intruders, result, intra_polygon_result); + + // detect and remove parts of the result which have or do not have results "opposite" + // ("opposite" is defined by the projection of edges "through" the subject shape) + if (check_local_operation_base::m_options.opposite_filter != db::NoOppositeFilter && (! result.empty () || ! intra_polygon_result.empty ())) { + check_local_operation_base::apply_opposite_filter (subjects, result, intra_polygon_result); + } else { + result.insert (intra_polygon_result.begin (), intra_polygon_result.end ()); + } + + // implements error filtering on rectangles + if (check_local_operation_base::m_options.rect_filter != RectFilter::NoRectFilter && ! result.empty ()) { + check_local_operation_base::apply_rectangle_filter (subjects, result); + } + results.front ().insert (result.begin (), result.end ()); } + template db::Coord check_local_operation::dist () const { // TODO: will the distance be sufficient? Or should we take somewhat more? - return m_check.distance (); + return check_local_operation_base::m_check.distance (); } template OnEmptyIntruderHint check_local_operation::on_empty_intruder_hint () const { - return m_different_polygons ? OnEmptyIntruderHint::Drop : OnEmptyIntruderHint::Ignore; + return check_local_operation_base::m_different_polygons ? OnEmptyIntruderHint::Drop : OnEmptyIntruderHint::Ignore; } template @@ -717,6 +742,77 @@ template class DB_PUBLIC check_local_operation; // --------------------------------------------------------------------------------------------------------------- +template +check_local_operation_with_properties::check_local_operation_with_properties (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options, db::PropertiesRepository *target_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr) + : check_local_operation_base (check, different_polygons, is_merged, has_other, other_is_merged, options), m_pms (target_pr, subject_pr), m_pmi (target_pr, intruder_pr) +{ + // .. nothing yet .. +} + +template +void +check_local_operation_with_properties::do_compute_local (db::Layout *layout, const shape_interactions, db::object_with_properties > &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + tl_assert (results.size () == 1); + + auto by_prop_id = separate_interactions_by_properties (interactions, check_local_operation_base::m_options.prop_constraint, m_pms, m_pmi); + + for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) { + + std::unordered_set result, intra_polygon_result; + + const std::vector &subjects = s2p->second.first; + const std::set &intruders = s2p->second.second; + + // perform the basic check + check_local_operation_base::compute_results (layout, subjects, intruders, result, intra_polygon_result); + + // detect and remove parts of the result which have or do not have results "opposite" + // ("opposite" is defined by the projection of edges "through" the subject shape) + if (check_local_operation_base::m_options.opposite_filter != db::NoOppositeFilter && (! result.empty () || ! intra_polygon_result.empty ())) { + check_local_operation_base::apply_opposite_filter (subjects, result, intra_polygon_result); + } else { + result.insert (intra_polygon_result.begin (), intra_polygon_result.end ()); + } + + // implements error filtering on rectangles + if (check_local_operation_base::m_options.rect_filter != RectFilter::NoRectFilter && ! result.empty ()) { + check_local_operation_base::apply_rectangle_filter (subjects, result); + } + + results.front ().insert (result.begin (), result.end ()); + + } +} + +template +db::Coord +check_local_operation_with_properties::dist () const +{ + // TODO: will the distance be sufficient? Or should we take somewhat more? + return check_local_operation_base::m_check.distance (); +} + +template +OnEmptyIntruderHint +check_local_operation_with_properties::on_empty_intruder_hint () const +{ + return check_local_operation_base::m_different_polygons ? OnEmptyIntruderHint::Drop : OnEmptyIntruderHint::Ignore; +} + +template +std::string +check_local_operation_with_properties::description () const +{ + return tl::to_string (tr ("Generic DRC check")); +} + +// explicit instantiations +template class DB_PUBLIC check_local_operation_with_properties; +template class DB_PUBLIC check_local_operation_with_properties; + +// --------------------------------------------------------------------------------------------------------------- + namespace { class PolygonToEdgeProcessor @@ -1430,4 +1526,565 @@ std::string interacting_with_text_local_operation::description () co template class DB_PUBLIC interacting_with_text_local_operation; template class DB_PUBLIC interacting_with_text_local_operation; +// --------------------------------------------------------------------------------------------- +// BoolAndOrNotLocalOperation implementation + +template +bool_and_or_not_local_operation::bool_and_or_not_local_operation (bool is_and) + : m_is_and (is_and) +{ + // .. nothing yet .. +} + +template +OnEmptyIntruderHint +bool_and_or_not_local_operation::on_empty_intruder_hint () const +{ + return m_is_and ? Drop : Copy; +} + +template +std::string +bool_and_or_not_local_operation::description () const +{ + return m_is_and ? tl::to_string (tr ("AND operation")) : tl::to_string (tr ("NOT operation")); +} + +template +void +bool_and_or_not_local_operation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const +{ + tl_assert (results.size () == 1); + std::unordered_set &result = results.front (); + + db::EdgeProcessor ep; + + size_t p1 = 0, p2 = 1; + + std::set others; + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + for (auto j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + + const TR &subject = interactions.subject_shape (i->first); + if (others.find (subject) != others.end ()) { + if (m_is_and) { + result.insert (subject); + } + } else if (i->second.empty ()) { + // shortcut (not: keep, and: drop) + if (! m_is_and) { + result.insert (subject); + } + } else { + for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + } + + if (! others.empty () && p1 > 0) { + + for (auto o = others.begin (); o != others.end (); ++o) { + for (auto e = o->begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); + db::polygon_ref_generator pr (layout, result); + db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); + db::PolygonGenerator pg (splitter, true, true); + ep.set_base_verbosity (50); + ep.process (pg, op); + + } +} + +template class DB_PUBLIC bool_and_or_not_local_operation; +template class DB_PUBLIC bool_and_or_not_local_operation; + +// --------------------------------------------------------------------------------------------- +// BoolAndOrNotLocalOperationWithProperties implementation + +template +bool_and_or_not_local_operation_with_properties::bool_and_or_not_local_operation_with_properties (bool is_and, db::PropertiesRepository *target_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr, db::PropertyConstraint property_constraint) + : m_is_and (is_and), m_property_constraint (property_constraint), m_pms (target_pr, subject_pr), m_pmi (target_pr, intruder_pr) +{ + // .. nothing yet .. +} + +template +OnEmptyIntruderHint +bool_and_or_not_local_operation_with_properties::on_empty_intruder_hint () const +{ + return m_is_and ? Drop : Copy; +} + +template +std::string +bool_and_or_not_local_operation_with_properties::description () const +{ + return m_is_and ? tl::to_string (tr ("AND operation")) : tl::to_string (tr ("NOT operation")); +} + +template +void +bool_and_or_not_local_operation_with_properties::do_compute_local (db::Layout *layout, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &results, size_t max_vertex_count, double area_ratio) const +{ + tl_assert (results.size () == 1); + std::unordered_set > &result = results.front (); + + db::EdgeProcessor ep; + + std::map, std::set > > by_prop_id; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + + const db::object_with_properties &subject = interactions.subject_shape (i->first); + + if (i->second.empty ()) { + + if (! m_is_and) { + result.insert (db::object_with_properties (subject, m_pms (subject.properties_id ()))); + } + + } else { + + db::properties_id_type prop_id_s = m_pms (subject.properties_id ()); + + auto &shapes_by_prop = by_prop_id [prop_id_s]; + shapes_by_prop.first.push_front (subject); + + for (auto j = i->second.begin (); j != i->second.end (); ++j) { + const db::object_with_properties &intruder = interactions.intruder_shape (*j).second; + db::properties_id_type prop_id_i = (m_property_constraint != db::NoPropertyConstraint ? m_pmi (intruder.properties_id ()) : prop_id_s); + if ((prop_id_i != prop_id_s) == (m_property_constraint == db::DifferentPropertiesConstraint)) { + shapes_by_prop.second.insert (intruder); + } + } + + } + + } + + for (auto p2s = by_prop_id.begin (); p2s != by_prop_id.end (); ++p2s) { + + ep.clear (); + size_t p1 = 0, p2 = 1; + + const std::set &others = p2s->second.second; + db::properties_id_type prop_id = p2s->first; + + for (auto s = p2s->second.first.begin (); s != p2s->second.first.end (); ++s) { + + const TS &subject = *s; + if (others.find (subject) != others.end ()) { + if (m_is_and) { + result.insert (db::object_with_properties (subject, prop_id)); + } + } else if (others.empty ()) { + // shortcut (not: keep, and: drop) + if (! m_is_and) { + result.insert (db::object_with_properties (subject, prop_id)); + } + } else { + for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + } + + if (! others.empty () && p1 > 0) { + + for (auto o = others.begin (); o != others.end (); ++o) { + for (auto e = o->begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); + db::polygon_ref_generator_with_properties > pr (layout, result, prop_id); + db::PolygonSplitter splitter (pr, area_ratio, max_vertex_count); + db::PolygonGenerator pg (splitter, true, true); + ep.set_base_verbosity (50); + ep.process (pg, op); + + } + + } +} + +template class DB_PUBLIC bool_and_or_not_local_operation_with_properties; +template class DB_PUBLIC bool_and_or_not_local_operation_with_properties; + +// --------------------------------------------------------------------------------------------- +// TwoBoolAndNotLocalOperation implementation + +template +two_bool_and_not_local_operation::two_bool_and_not_local_operation () + : db::local_operation () +{ + // .. nothing yet .. +} + +template +void +two_bool_and_not_local_operation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const +{ + tl_assert (results.size () == 2); + + db::EdgeProcessor ep; + + std::unordered_set &result0 = results [0]; + std::unordered_set &result1 = results [1]; + + size_t p1 = 0, p2 = 1; + + std::set others; + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + for (auto j = i->second.begin (); j != i->second.end (); ++j) { + others.insert (interactions.intruder_shape (*j).second); + } + } + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + + const TS &subject = interactions.subject_shape (i->first); + if (others.find (subject) != others.end ()) { + result0.insert (subject); + } else if (i->second.empty ()) { + // shortcut (not: keep, and: drop) + result1.insert (subject); + } else { + for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + } + + if (! others.empty () && p1 > 0) { + + for (auto o = others.begin (); o != others.end (); ++o) { + for (auto e = o->begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + db::BooleanOp op0 (db::BooleanOp::And); + db::polygon_ref_generator pr0 (layout, result0); + db::PolygonSplitter splitter0 (pr0, area_ratio, max_vertex_count); + db::PolygonGenerator pg0 (splitter0, true, true); + + db::BooleanOp op1 (db::BooleanOp::ANotB); + db::polygon_ref_generator pr1 (layout, result1); + db::PolygonSplitter splitter1 (pr1, area_ratio, max_vertex_count); + db::PolygonGenerator pg1 (splitter1, true, true); + + ep.set_base_verbosity (50); + + std::vector > procs; + procs.push_back (std::make_pair (&pg0, &op0)); + procs.push_back (std::make_pair (&pg1, &op1)); + ep.process (procs); + + } + +} + +template +std::string two_bool_and_not_local_operation::description () const +{ + return tl::to_string (tr ("ANDNOT operation")); +} + +template class DB_PUBLIC two_bool_and_not_local_operation; +template class DB_PUBLIC two_bool_and_not_local_operation; + +// --------------------------------------------------------------------------------------------- +// TwoBoolAndNotLocalOperationWithProperties implementation + +template +two_bool_and_not_local_operation_with_properties::two_bool_and_not_local_operation_with_properties (db::PropertiesRepository *target1_pr, db::PropertiesRepository *target2_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr, db::PropertyConstraint property_constraint) + : db::local_operation, db::object_with_properties, db::object_with_properties > (), + m_property_constraint (property_constraint), m_pms (target1_pr, subject_pr), m_pmi (target1_pr, intruder_pr), m_pm12 (target2_pr, target1_pr) +{ + // .. nothing yet .. +} + +template +void +two_bool_and_not_local_operation_with_properties::do_compute_local (db::Layout *layout, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &results, size_t max_vertex_count, double area_ratio) const +{ + tl_assert (results.size () == 2); + std::unordered_set > &result0 = results [0]; + std::unordered_set > &result1 = results [1]; + + db::EdgeProcessor ep; + + std::map, std::set > > by_prop_id; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + + const db::object_with_properties &subject = interactions.subject_shape (i->first); + + if (i->second.empty ()) { + + result1.insert (db::object_with_properties (subject, m_pms (subject.properties_id ()))); + + } else { + + db::properties_id_type prop_id_s = m_pms (subject.properties_id ()); + + auto &shapes_by_prop = by_prop_id [prop_id_s]; + shapes_by_prop.first.push_front (subject); + + for (auto j = i->second.begin (); j != i->second.end (); ++j) { + const db::object_with_properties &intruder = interactions.intruder_shape (*j).second; + db::properties_id_type prop_id_i = (m_property_constraint != db::NoPropertyConstraint ? m_pmi (intruder.properties_id ()) : prop_id_s); + if ((prop_id_i != prop_id_s) == (m_property_constraint == db::DifferentPropertiesConstraint)) { + shapes_by_prop.second.insert (intruder); + } + } + + } + + } + + for (auto p2s = by_prop_id.begin (); p2s != by_prop_id.end (); ++p2s) { + + ep.clear (); + size_t p1 = 0, p2 = 1; + + const std::set &others = p2s->second.second; + db::properties_id_type prop_id = p2s->first; + + for (auto s = p2s->second.first.begin (); s != p2s->second.first.end (); ++s) { + + const TR &subject = *s; + if (others.find (subject) != others.end ()) { + result0.insert (db::object_with_properties (subject, prop_id)); + } else if (others.empty ()) { + // shortcut (not: keep, and: drop) + result1.insert (db::object_with_properties (subject, m_pm12 (prop_id))); + } else { + for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + } + + if (! others.empty () && p1 > 0) { + + for (auto o = others.begin (); o != others.end (); ++o) { + for (auto e = o->begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + + std::unordered_set result0_wo_props; + std::unordered_set result1_wo_props; + + db::BooleanOp op0 (db::BooleanOp::And); + db::polygon_ref_generator pr0 (layout, result0_wo_props); + db::PolygonSplitter splitter0 (pr0, area_ratio, max_vertex_count); + db::PolygonGenerator pg0 (splitter0, true, true); + + db::BooleanOp op1 (db::BooleanOp::ANotB); + db::polygon_ref_generator pr1 (layout, result1_wo_props); + db::PolygonSplitter splitter1 (pr1, area_ratio, max_vertex_count); + db::PolygonGenerator pg1 (splitter1, true, true); + + ep.set_base_verbosity (50); + + std::vector > procs; + procs.push_back (std::make_pair (&pg0, &op0)); + procs.push_back (std::make_pair (&pg1, &op1)); + ep.process (procs); + + for (auto r = result0_wo_props.begin (); r != result0_wo_props.end (); ++r) { + result0.insert (db::object_with_properties (*r, prop_id)); + } + for (auto r = result1_wo_props.begin (); r != result1_wo_props.end (); ++r) { + result1.insert (db::object_with_properties (*r, m_pm12 (prop_id))); + } + + } + + } +} + +template +std::string two_bool_and_not_local_operation_with_properties::description () const +{ + return tl::to_string (tr ("ANDNOT operation")); +} + +template class DB_PUBLIC two_bool_and_not_local_operation_with_properties; +template class DB_PUBLIC two_bool_and_not_local_operation_with_properties; + +// --------------------------------------------------------------------------------------------- + +SelfOverlapMergeLocalOperation::SelfOverlapMergeLocalOperation (unsigned int wrap_count) + : m_wrap_count (wrap_count) +{ + // .. nothing yet .. +} + +void +SelfOverlapMergeLocalOperation::do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + tl_assert (results.size () == 1); + std::unordered_set &result = results.front (); + + if (m_wrap_count == 0) { + return; + } + + db::EdgeProcessor ep; + + size_t p1 = 0, p2 = 1; + std::set seen; + + for (shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + + if (seen.find (i->first) == seen.end ()) { + seen.insert (i->first); + const db::PolygonRef &subject = interactions.subject_shape (i->first); + for (db::PolygonRef::polygon_edge_iterator e = subject.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p1); + } + p1 += 2; + } + + for (db::shape_interactions::iterator2 o = i->second.begin (); o != i->second.end (); ++o) { + // don't take the same (really the same, not an identical one) shape twice - the interaction + // set does not take care to list just one copy of the same item on the intruder side. + if (seen.find (*o) == seen.end ()) { + seen.insert (*o); + const db::PolygonRef &intruder = interactions.intruder_shape (*o).second; + for (db::PolygonRef::polygon_edge_iterator e = intruder.begin_edge (); ! e.at_end(); ++e) { + ep.insert (*e, p2); + } + p2 += 2; + } + } + + } + + db::MergeOp op (m_wrap_count - 1); + db::PolygonRefGenerator pr (layout, result); + db::PolygonGenerator pg (pr, true, true); + ep.set_base_verbosity (50); + ep.process (pg, op); +} + +OnEmptyIntruderHint SelfOverlapMergeLocalOperation::on_empty_intruder_hint () const +{ + return m_wrap_count > 1 ? Drop : Copy; +} + +std::string SelfOverlapMergeLocalOperation::description () const +{ + return tl::sprintf (tl::to_string (tr ("Self-overlap (wrap count %d)")), int (m_wrap_count)); +} + +// --------------------------------------------------------------------------------------------- + +PolygonToEdgeLocalOperation::PolygonToEdgeLocalOperation (db::PropertiesRepository *target_pr, const db::PropertiesRepository *source_pr) + : local_operation (), m_pm (target_pr, source_pr) +{ + // .. nothing yet .. +} + +std::string +PolygonToEdgeLocalOperation::description () const +{ + return std::string ("polygon to edges"); +} + +void +PolygonToEdgeLocalOperation::do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const +{ + db::EdgeProcessor ep; + ep.set_base_verbosity (50); + + auto by_prop_id = separate_interactions_by_properties (interactions, db::SamePropertiesConstraint, m_pm, m_pm); + for (auto shapes_by_prop_id = by_prop_id.begin (); shapes_by_prop_id != by_prop_id.end (); ++shapes_by_prop_id) { + + db::properties_id_type prop_id = shapes_by_prop_id->first; + + for (auto s = shapes_by_prop_id->second.first.begin (); s != shapes_by_prop_id->second.first.end (); ++s) { + ep.insert (**s); + } + + db::property_injector > results_with_properties (&results.front (), prop_id); + + if (shapes_by_prop_id->second.second.empty ()) { + + db::edge_to_edge_set_generator > > eg (results_with_properties, prop_id); + db::MergeOp op (0); + ep.process (eg, op); + + } else { + + // With intruders: to compute our local contribution we take the edges without and with intruders + // and deliver what is in both sets + + db::MergeOp op (0); + + std::vector edges1; + db::EdgeContainer ec1 (edges1); + ep.process (ec1, op); + + ep.clear (); + + for (auto s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) { + ep.insert (s->second); + } + for (auto i = interactions.begin_intruders (); i != interactions.end_intruders (); ++i) { + ep.insert (i->second.second); + } + + std::vector edges2; + db::EdgeContainer ec2 (edges2); + ep.process (ec2, op); + + // Runs the boolean AND between the result with and without intruders + + db::box_scanner scanner; + scanner.reserve (edges1.size () + edges2.size ()); + + for (std::vector::const_iterator i = edges1.begin (); i != edges1.end (); ++i) { + scanner.insert (i.operator-> (), 0); + } + for (std::vector::const_iterator i = edges2.begin (); i != edges2.end (); ++i) { + scanner.insert (i.operator-> (), 1); + } + + EdgeBooleanClusterCollector > > cluster_collector (&results_with_properties, EdgeAnd); + scanner.process (cluster_collector, 1, db::box_convert ()); + + } + + } + +} + } diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index 60289d003..4e2542110 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -27,6 +27,7 @@ #include "dbCommon.h" #include "dbEdgePairRelations.h" #include "dbLocalOperation.h" +#include "dbLocalOperationUtils.h" #include "dbEdgeProcessor.h" #include "dbRegionCheckUtils.h" #include "dbPropertyConstraint.h" @@ -125,7 +126,8 @@ struct DB_PUBLIC RegionCheckOptions bool _shielded = true, OppositeFilter _opposite_filter = NoOppositeFilter, RectFilter _rect_filter = NoRectFilter, - bool _negative = false) + bool _negative = false, + PropertyConstraint _prop_constraint = IgnoreProperties) : whole_edges (_whole_edges), metrics (_metrics), ignore_angle (_ignore_angle), @@ -134,7 +136,8 @@ struct DB_PUBLIC RegionCheckOptions shielded (_shielded), opposite_filter (_opposite_filter), rect_filter (_rect_filter), - negative (_negative) + negative (_negative), + prop_constraint (_prop_constraint) { } /** @@ -199,6 +202,11 @@ struct DB_PUBLIC RegionCheckOptions */ bool negative; + /** + * @brief Specifies a property constraint - e.g. checking only shapes with the same properties + */ + PropertyConstraint prop_constraint; + /** * @brief Gets a value indicating whether merged primary input is required */ @@ -213,9 +221,28 @@ struct DB_PUBLIC RegionCheckOptions } }; +template +class check_local_operation_base +{ +public: + check_local_operation_base (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options); + +protected: + EdgeRelationFilter m_check; + bool m_different_polygons; + bool m_is_merged; + bool m_has_other; + bool m_other_is_merged; + db::RegionCheckOptions m_options; + + void compute_results (db::Layout *layout, const std::vector &subjects, const std::set &intruders, std::unordered_set &result, std::unordered_set &intra_polygon_result) const; + void apply_opposite_filter (const std::vector &subjects, std::unordered_set &result, std::unordered_set &intra_polygon_result) const; + void apply_rectangle_filter (const std::vector &subjects, std::unordered_set &result) const; +}; + template class check_local_operation - : public local_operation + : public local_operation, public check_local_operation_base { public: check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options); @@ -226,14 +253,24 @@ public: virtual std::string description () const; virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const; +}; + +template +class check_local_operation_with_properties + : public local_operation, db::object_with_properties, db::EdgePair>, public check_local_operation_base +{ +public: + check_local_operation_with_properties (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options, db::PropertiesRepository *target_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr); + + virtual db::Coord dist () const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual bool requests_single_subjects () const { return true; } + virtual std::string description () const; + + virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions, db::object_with_properties > &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const; private: - EdgeRelationFilter m_check; - bool m_different_polygons; - bool m_is_merged; - bool m_has_other; - bool m_other_is_merged; - db::RegionCheckOptions m_options; + mutable db::PropertyMapper m_pms, m_pmi; }; typedef check_local_operation CheckLocalOperation; @@ -372,6 +409,131 @@ typedef contained_local_operation ContainedEdgesLocalOperation; +/** + * @brief Implements a boolean AND or NOT operation + */ + +template +class DB_PUBLIC bool_and_or_not_local_operation + : public local_operation +{ +public: + bool_and_or_not_local_operation (bool is_and); + + virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + bool m_is_and; +}; + +typedef bool_and_or_not_local_operation BoolAndOrNotLocalOperation; + +/** + * @brief Implements a boolean AND or NOT operation with property handling + */ +template +class DB_PUBLIC bool_and_or_not_local_operation_with_properties + : public local_operation, db::object_with_properties, db::object_with_properties > +{ +public: + bool_and_or_not_local_operation_with_properties (bool is_and, db::PropertiesRepository *target_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr, db::PropertyConstraint property_constraint); + + virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &result, size_t max_vertex_count, double area_ratio) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + bool m_is_and; + db::PropertyConstraint m_property_constraint; + mutable db::PropertyMapper m_pms; + mutable db::PropertyMapper m_pmi; +}; + +typedef bool_and_or_not_local_operation_with_properties BoolAndOrNotLocalOperationWithProperties; + +/** + * @brief Implements a boolean AND plus NOT operation + * + * This processor delivers two outputs: the first one having the AND result, the second + * one having the NOT result. + */ + +template +class DB_PUBLIC two_bool_and_not_local_operation + : public local_operation +{ +public: + two_bool_and_not_local_operation (); + + virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; + virtual std::string description () const; +}; + +typedef two_bool_and_not_local_operation TwoBoolAndNotLocalOperation; + +/** + * @brief Implements a boolean AND plus NOT operation + * + * This processor delivers two outputs: the first one having the AND result, the second + * one having the NOT result. + */ +template +class DB_PUBLIC two_bool_and_not_local_operation_with_properties + : public local_operation, db::object_with_properties, db::object_with_properties > +{ +public: + two_bool_and_not_local_operation_with_properties (db::PropertiesRepository *target1_pr, db::PropertiesRepository *target2_pr, const db::PropertiesRepository *subject_pr, const db::PropertiesRepository *intruder_pr, db::PropertyConstraint property_constraint); + + virtual void do_compute_local (db::Layout *layout, const shape_interactions, db::object_with_properties > &interactions, std::vector > > &result, size_t max_vertex_count, double area_ratio) const; + virtual std::string description () const; + +private: + db::PropertyConstraint m_property_constraint; + mutable db::PropertyMapper m_pms, m_pmi, m_pm12; +}; + +typedef two_bool_and_not_local_operation_with_properties TwoBoolAndNotLocalOperationWithProperties; + +/** + * @brief Implements a merge operation with an overlap count + * With a given wrap_count, the result will only contains shapes where + * the original shapes overlap at least "wrap_count" times. + */ +class DB_PUBLIC SelfOverlapMergeLocalOperation + : public local_operation +{ +public: + SelfOverlapMergeLocalOperation (unsigned int wrap_count); + + virtual void do_compute_local (db::Layout *layout, const shape_interactions &interactions, std::vector > &result, size_t max_vertex_count, double area_ratio) const; + virtual OnEmptyIntruderHint on_empty_intruder_hint () const; + virtual std::string description () const; + +private: + unsigned int m_wrap_count; +}; + +/** + * @brief Converts polygons to edges + */ +class DB_PUBLIC PolygonToEdgeLocalOperation + : public local_operation +{ +public: + PolygonToEdgeLocalOperation (db::PropertiesRepository *target_pr, const db::PropertiesRepository *source_pr); + + virtual db::Coord dist () const { return 1; } + virtual bool requests_single_subjects () const { return true; } + virtual std::string description () const; + + virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions &interactions, std::vector > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const; + +private: + mutable db::PropertyMapper m_pm; +}; + } // namespace db #endif diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index d98e48c76..7e9fe165f 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -30,6 +30,7 @@ #include "dbEdgeProcessor.h" #include "dbPolygonGenerators.h" #include "dbLocalOperationUtils.h" +#include "dbRegionLocalOperations.h" #include "dbPolygon.h" static std::string testdata (const std::string &fn)