diff --git a/src/db/db/dbEdgeNeighborhood.cc b/src/db/db/dbEdgeNeighborhood.cc index 88c6ca56e..e4c9fbe20 100644 --- a/src/db/db/dbEdgeNeighborhood.cc +++ b/src/db/db/dbEdgeNeighborhood.cc @@ -165,9 +165,9 @@ private: return; } + // compute normal and unit vector along edge db::DVector e = db::DVector (edge.d ()); e = e * (1.0 / e.double_length ()); - db::DVector ne (-e.y (), e.x ()); // transform on the edge @@ -183,11 +183,37 @@ private: db::Coord xmin = -m_bext - 1; db::Coord xmax = ref_edge.dx () + m_eext + 1; + db::SimplePolygon per_edge_clip_box (db::Box (xmin, -m_din - 1, xmax, m_dout + 1)); + + // compute the merged neighbors + std::map > merged_neighbors; + + db::EdgeProcessor ep; for (auto n = neighbors.begin (); n != neighbors.end (); ++n) { + + ep.clear (); + + size_t id = 0; + for (auto nn = n->second.begin (); nn != n->second.end (); ++nn) { + for (auto e = (*nn)->begin_edge (); ! e.at_end (); ++e) { + ep.insert (itrans * (move * *e), id); + } + id += 2; + } + + ep.insert (per_edge_clip_box, size_t (1)); + + db::BooleanOp and_op (db::BooleanOp::And); + db::PolygonContainer pc (merged_neighbors [n->first]); + db::PolygonGenerator pg (pc, false); + ep.process (pg, and_op); + + } + + for (auto n = merged_neighbors.begin (); n != merged_neighbors.end (); ++n) { for (auto p = n->second.begin (); p != n->second.end (); ++p) { - db::Polygon poly = itrans * (move * **p); - for (auto p = poly.begin_edge (); ! p.at_end (); ++p) { - db::Edge e = *p; + for (auto pe = p->begin_edge (); ! pe.at_end (); ++pe) { + db::Edge e = *pe; xpos.insert (std::max (xmin, std::min (xmax, e.p1 ().x ()))); xpos.insert (std::max (xmin, std::min (xmax, e.p2 ().x ()))); } @@ -210,12 +236,12 @@ private: db::Box clip_box (xfrom, -m_din - 1, xto, m_dout + 1); // NOTE: this could be more efficient if we had a multi-layer capable trapezoid decomposition tool - for (auto n = neighbors.begin (); n != neighbors.end (); ++n) { + for (auto n = merged_neighbors.begin (); n != merged_neighbors.end (); ++n) { + + EdgeNeighborhoodVisitor::neighbor_shapes_type polygons; - EdgeNeighborhoodVisitor::neighbor_shapes_type polygons; for (auto p = n->second.begin (); p != n->second.end (); ++p) { - db::Polygon poly = itrans * (move * **p); - db::clip_poly (poly, clip_box, polygons, false); + db::clip_poly (*p, clip_box, polygons, false); } if (!polygons.empty ()) { diff --git a/src/db/db/gsiDeclDbEdgeNeighborhood.cc b/src/db/db/gsiDeclDbEdgeNeighborhood.cc index b3b770d7b..ede33d277 100644 --- a/src/db/db/gsiDeclDbEdgeNeighborhood.cc +++ b/src/db/db/gsiDeclDbEdgeNeighborhood.cc @@ -218,9 +218,9 @@ gsi::ClassExt decl_CompoundRegionOperationNode_ "@param children The inputs to use. The first one in the primary input, the others are neighbors.\n" "@param visitor The visitor object (see \\EdgeNeighborhoodVisitor) receiving the edge events.\n" "@param bext The search window extension to use at the edge beginning.\n" - "@param eext The search window extension to use at the edge beginning.\n" - "@param din The search window extension to use at the edge beginning.\n" - "@param dout The search window extension to use at the edge beginning.\n" + "@param eext The search window extension to use at the edge end.\n" + "@param din The search window extension to the 'outside' of the edge.\n" + "@param dout The search window extension to the 'inside' of the edge.\n" "\n" "This constructor has been introduced in version 0.29.9.\n" ) diff --git a/src/db/unit_tests/dbEdgeNeighborhoodTests.cc b/src/db/unit_tests/dbEdgeNeighborhoodTests.cc new file mode 100644 index 000000000..27bebd33c --- /dev/null +++ b/src/db/unit_tests/dbEdgeNeighborhoodTests.cc @@ -0,0 +1,171 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2024 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "tlUnitTest.h" + +#include "dbRegion.h" +#include "dbEdgeNeighborhood.h" +#include "dbReader.h" +#include "dbTestSupport.h" + +#include "tlStream.h" + +#include + +namespace +{ + +class ENPrimaryCopyVisitor + : public db::EdgeNeighborhoodVisitor +{ +public: + ENPrimaryCopyVisitor () + { + set_result_type (db::CompoundRegionOperationNode::ResultType::Region); + } + + void begin_polygon (const db::Layout *, const db::Cell *, const db::Polygon &polygon) + { + output_polygon (polygon); + } +}; + +class ENPrimaryCopyIntruderVisitor + : public db::EdgeNeighborhoodVisitor +{ +public: + ENPrimaryCopyIntruderVisitor (unsigned int input) + { + set_result_type (db::CompoundRegionOperationNode::ResultType::Region); + m_input = input; + } + + void on_edge (const db::Layout * /*layout*/, const db::Cell * /*cell*/, const db::Edge &edge, const neighbors_type &neighbors) + { + // Compute transformation to original edge + db::DVector e = db::DVector (edge.d ()); + e = e * (1.0 / e.double_length ()); + db::DVector ne (-e.y (), e.x ()); + + db::IMatrix2d trans (e.x (), ne.x (), e.y (), ne.y ()); + db::Disp move (edge.p1 () - db::Point ()); + + for (auto n = neighbors.begin (); n != neighbors.end (); ++n) { + for (auto nn = n->second.begin (); nn != n->second.end (); ++nn) { + if (nn->first == m_input) { + for (auto p = nn->second.begin (); p != nn->second.end (); ++p) { + output_polygon (move * (trans * *p)); + } + } + } + } + } + +private: + unsigned int m_input; +}; + +} + +static void prep_layer (db::Layout &ly, int gds_layer, db::Region &r, db::DeepShapeStore &dss, bool deep) +{ + unsigned int li = ly.get_layer (db::LayerProperties (gds_layer, 0)); + if (deep) { + r = db::Region (db::RecursiveShapeIterator (ly, ly.cell (*ly.begin_top_down ()), li), dss); + } else { + r = db::Region (db::RecursiveShapeIterator (ly, ly.cell (*ly.begin_top_down ()), li)); + } +} + +static void run_test (tl::TestBase *_this, db::EdgeNeighborhoodVisitor &visitor, const std::string &au_name, bool deep = true, db::Coord bext = 0, db::Coord eext = 0, db::Coord din = 0, db::Coord dout = 0) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/edge_neighborhood.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::DeepShapeStore dss; + + db::Region r1, r2, r3; + prep_layer (ly, 1, r1, dss, deep); + prep_layer (ly, 2, r2, dss, deep); + prep_layer (ly, 3, r3, dss, deep); + + std::vector children; + children.push_back (new db::CompoundRegionOperationPrimaryNode ()); + children.push_back (new db::CompoundRegionOperationForeignNode ()); + children.push_back (new db::CompoundRegionOperationSecondaryNode (&r2)); + children.push_back (new db::CompoundRegionOperationSecondaryNode (&r3)); + + db::EdgeNeighborhoodCompoundOperationNode en_node (children, &visitor, bext, eext, din, dout); + + unsigned int l100 = ly.get_layer (db::LayerProperties (100, 0)); + + if (en_node.result_type () == db::CompoundRegionOperationNode::ResultType::Region) { + auto res = r1.cop_to_region (en_node); + res.insert_into (&ly, *ly.begin_top_down (), l100); + } else if (en_node.result_type () == db::CompoundRegionOperationNode::ResultType::Edges) { + auto res = r1.cop_to_edges (en_node); + res.insert_into (&ly, *ly.begin_top_down (), l100); + } else if (en_node.result_type () == db::CompoundRegionOperationNode::ResultType::EdgePairs) { + auto res = r1.cop_to_edge_pairs (en_node); + res.insert_into (&ly, *ly.begin_top_down (), l100); + } + + db::compare_layouts (_this, ly, tl::testdata () + au_name); +} + +TEST(1) +{ + ENPrimaryCopyVisitor visitor; + run_test (_this, visitor, "/algo/edge_neighborhood_au1.gds"); +} + + +TEST(2) +{ + ENPrimaryCopyIntruderVisitor visitor (0); + run_test (_this, visitor, "/algo/edge_neighborhood_au2.gds", true, 100, 100, 100, 2000); +} + +TEST(3) +{ + ENPrimaryCopyIntruderVisitor visitor (1); + run_test (_this, visitor, "/algo/edge_neighborhood_au3.gds", true, 100, 100, 100, 2000); +} + +TEST(4) +{ + ENPrimaryCopyIntruderVisitor visitor (2); + run_test (_this, visitor, "/algo/edge_neighborhood_au4.gds", true, 100, 100, 100, 2000); +} + +TEST(5) +{ + ENPrimaryCopyIntruderVisitor visitor (3); + run_test (_this, visitor, "/algo/edge_neighborhood_au5.gds", true, 100, 100, 100, 2000); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index a5ef9690e..67e131894 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ dbCompoundOperationTests.cc \ + dbEdgeNeighborhoodTests.cc \ dbFillToolTests.cc \ dbLogTests.cc \ dbRecursiveInstanceIteratorTests.cc \ diff --git a/testdata/algo/edge_neighborhood.gds b/testdata/algo/edge_neighborhood.gds new file mode 100644 index 000000000..75c723e1c Binary files /dev/null and b/testdata/algo/edge_neighborhood.gds differ diff --git a/testdata/algo/edge_neighborhood_au1.gds b/testdata/algo/edge_neighborhood_au1.gds new file mode 100644 index 000000000..8c206b741 Binary files /dev/null and b/testdata/algo/edge_neighborhood_au1.gds differ diff --git a/testdata/algo/edge_neighborhood_au2.gds b/testdata/algo/edge_neighborhood_au2.gds new file mode 100644 index 000000000..209b56eb0 Binary files /dev/null and b/testdata/algo/edge_neighborhood_au2.gds differ diff --git a/testdata/algo/edge_neighborhood_au3.gds b/testdata/algo/edge_neighborhood_au3.gds new file mode 100644 index 000000000..563bc7322 Binary files /dev/null and b/testdata/algo/edge_neighborhood_au3.gds differ diff --git a/testdata/algo/edge_neighborhood_au4.gds b/testdata/algo/edge_neighborhood_au4.gds new file mode 100644 index 000000000..451dd3bd1 Binary files /dev/null and b/testdata/algo/edge_neighborhood_au4.gds differ diff --git a/testdata/algo/edge_neighborhood_au5.gds b/testdata/algo/edge_neighborhood_au5.gds new file mode 100644 index 000000000..fda0698d3 Binary files /dev/null and b/testdata/algo/edge_neighborhood_au5.gds differ