diff --git a/src/db/db/db.pro b/src/db/db/db.pro index b6c9cb53a..df5648b48 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -72,6 +72,7 @@ SOURCES = \ dbRecursiveInstanceIterator.cc \ dbRecursiveShapeIterator.cc \ dbRegion.cc \ + dbRegionCheckUtils.cc \ dbRegionLocalOperations.cc \ dbSaveLayoutOptions.cc \ dbShape.cc \ @@ -290,6 +291,7 @@ HEADERS = \ dbRecursiveInstanceIterator.h \ dbRecursiveShapeIterator.h \ dbRegion.h \ + dbRegionCheckUtils.h \ dbRegionLocalOperations.h \ dbSaveLayoutOptions.h \ dbShape.h \ diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 0362a5088..9efc43576 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1150,7 +1150,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord size_t n = 0; for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - poly_check.enter (*p, n); + poly_check.single (*p, n); n += 2; } diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc index 081d77a92..3b8fd6b03 100644 --- a/src/db/db/dbCellVariants.cc +++ b/src/db/db/dbCellVariants.cc @@ -22,7 +22,7 @@ #include "dbCellVariants.h" -#include "dbRegionUtils.h" +#include "dbPolygonTools.h" #include "tlUtils.h" namespace db diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index b536941c8..bfe1938c2 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1725,7 +1725,7 @@ DeepRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, c s->polygon (poly); do { - poly_check.enter (poly, 0); + poly_check.single (poly, 0); } while (edge_check.prepare_next_pass ()); } diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index 0343e4828..98718791e 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -23,7 +23,7 @@ #include "dbLayoutUtils.h" #include "dbCellVariants.h" -#include "dbRegionUtils.h" +#include "dbPolygonTools.h" #include "tlProgress.h" namespace db diff --git a/src/db/db/dbPolygonTools.cc b/src/db/db/dbPolygonTools.cc index dc252fc9b..4c7e22c55 100644 --- a/src/db/db/dbPolygonTools.cc +++ b/src/db/db/dbPolygonTools.cc @@ -2827,5 +2827,93 @@ decompose_trapezoids (const db::SimplePolygon &sp, TrapezoidDecompositionMode mo } } +// ------------------------------------------------------------------------------------- +// Polygon snapping + +db::Polygon +snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector &heap) +{ + db::Polygon pnew; + + for (size_t i = 0; i < poly.holes () + 1; ++i) { + + heap.clear (); + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = poly.begin_hull (); + e = poly.end_hull (); + } else { + b = poly.begin_hole ((unsigned int) (i - 1)); + e = poly.end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { + heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy))); + } + + if (i == 0) { + pnew.assign_hull (heap.begin (), heap.end ()); + } else { + pnew.insert_hole (heap.begin (), heap.end ()); + } + + } + + return pnew; +} + +db::Polygon +scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector &heap) +{ + db::Polygon pnew; + + int64_t dgx = int64_t (gx) * int64_t (dx); + int64_t dgy = int64_t (gy) * int64_t (dy); + + for (size_t i = 0; i < poly.holes () + 1; ++i) { + + heap.clear (); + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = poly.begin_hull (); + e = poly.end_hull (); + } else { + b = poly.begin_hole ((unsigned int) (i - 1)); + e = poly.end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { + int64_t x = snap_to_grid (int64_t ((*pt).x ()) * mx + int64_t (ox), dgx) / int64_t (dx); + int64_t y = snap_to_grid (int64_t ((*pt).y ()) * my + int64_t (oy), dgy) / int64_t (dy); + heap.push_back (db::Point (db::Coord (x), db::Coord (y))); + } + + if (i == 0) { + pnew.assign_hull (heap.begin (), heap.end ()); + } else { + pnew.insert_hole (heap.begin (), heap.end ()); + } + + } + + return pnew; +} + +db::Vector +scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy) +{ + int64_t dgx = int64_t (gx) * int64_t (dx); + int64_t dgy = int64_t (gy) * int64_t (dy); + + int64_t x = snap_to_grid (int64_t (v.x ()) * mx + int64_t (ox), dgx) / int64_t (dx); + int64_t y = snap_to_grid (int64_t (v.y ()) * my + int64_t (oy), dgy) / int64_t (dy); + + return db::Vector (db::Coord (x), db::Coord (y)); +} + } diff --git a/src/db/db/dbPolygonTools.h b/src/db/db/dbPolygonTools.h index c90691ba4..0a4bc11d6 100644 --- a/src/db/db/dbPolygonTools.h +++ b/src/db/db/dbPolygonTools.h @@ -760,6 +760,37 @@ void DB_PUBLIC decompose_trapezoids (const db::Polygon &p, TrapezoidDecompositio */ void DB_PUBLIC decompose_trapezoids (const db::SimplePolygon &p, TrapezoidDecompositionMode mode, SimplePolygonSink &sink); +template +static inline C snap_to_grid (C c, C g) +{ + // This form of snapping always snaps g/2 to right/top. + if (c < 0) { + c = -g * ((-c + (g - 1) / 2) / g); + } else { + c = g * ((c + g / 2) / g); + } + return c; +} + +/** + * @brief Snaps a polygon to the given grid + * Heap is a vector of points reused for the point list + */ +DB_PUBLIC db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector &heap); + +/** + * @brief Scales and snaps a polygon to the given grid + * Heap is a vector of points reused for the point list + * The coordinate transformation is q = ((p * m + o) snap (g * d)) / d. + */ +DB_PUBLIC db::Polygon scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector &heap); + +/** + * @brief Scales and snaps a vector to the given grid + * The coordinate transformation is q = ((p * m + o) snap (g * d)) / d. + */ +DB_PUBLIC db::Vector scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy); + } #endif diff --git a/src/db/db/dbRegionCheckUtils.cc b/src/db/db/dbRegionCheckUtils.cc new file mode 100644 index 000000000..38cfe9f51 --- /dev/null +++ b/src/db/db/dbRegionCheckUtils.cc @@ -0,0 +1,601 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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 "dbRegionCheckUtils.h" +#include "dbPolygonTools.h" +#include "dbEdgeBoolean.h" +#include "tlSelect.h" + +namespace db +{ + +// ------------------------------------------------------------------------------------- +// Edge2EdgeCheckBase implementation + +Edge2EdgeCheckBase::Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) + : mp_check (&check), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons), + m_first_pseudo (std::numeric_limits::max ()), + m_with_shielding (with_shielding), + m_symmetric_edges (symmetric_edges), + m_has_edge_pair_output (true), + m_has_negative_edge_output (false), + m_pass (0) +{ + m_distance = check.distance (); +} + +bool +Edge2EdgeCheckBase::prepare_next_pass () +{ + ++m_pass; + + if (m_pass == 1) { + + m_first_pseudo = m_ep.size (); + + if (m_with_shielding && ! m_ep.empty ()) { + + m_ep_discarded.resize (m_ep.size (), false); + + // second pass: + return true; + + } else if (m_has_negative_edge_output) { + + // second pass: + return true; + + } + + } + + if (! m_ep.empty () && m_has_edge_pair_output) { + + std::vector::const_iterator d = m_ep_discarded.begin (); + std::vector::const_iterator i = m_ep_intra_polygon.begin (); + std::vector::const_iterator ep = m_ep.begin (); + while (ep != m_ep.end () && size_t (ep - m_ep.begin ()) < m_first_pseudo) { + bool use_result = true; + if (d != m_ep_discarded.end ()) { + use_result = ! *d; + ++d; + } + if (use_result) { + put (*ep, *i); + } + ++ep; + ++i; + } + + } + + return false; +} + +static inline bool shields (const db::EdgePair &ep, const db::Edge &q) +{ + db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ()); + db::Edge pe2 (ep.second ().p1 (), ep.first ().p2 ()); + + std::pair ip1 = pe1.intersect_point (q); + std::pair ip2 = pe2.intersect_point (q); + + if (ip1.first && ip2.first) { + return ip1.second != ip2.second || (pe1.side_of (q.p1 ()) != 0 && pe2.side_of (q.p2 ()) != 0); + } else { + return false; + } +} + +void +Edge2EdgeCheckBase::finish (const Edge *o, size_t p) +{ + if (m_has_negative_edge_output && m_pass == 1 && m_pseudo_edges.find (std::make_pair (*o, p)) == m_pseudo_edges.end ()) { + + std::pair k (*o, p); + std::multimap, size_t>::const_iterator i0 = m_e2ep.find (k); + + bool fully_removed = false; + bool any = false; + for (std::multimap, size_t>::const_iterator i = i0; ! fully_removed && i != m_e2ep.end () && i->first == k; ++i) { + size_t n = i->second / 2; + if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) { + any = true; + fully_removed = (((i->second & 1) == 0 ? m_ep [n].first () : m_ep [n].second ()) == *o); + } + } + + if (! any) { + + put_negative (*o, (int) p); + + } else if (! fully_removed) { + + std::set partial_edges; + + db::EdgeBooleanCluster > ec (&partial_edges, db::EdgeNot); + ec.add (o, 0); + + for (std::multimap, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) { + size_t n = i->second / 2; + if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) { + ec.add (((i->second & 1) == 0 ? &m_ep [n].first () : &m_ep [n].second ()), 1); + } + } + + ec.finish (); + + for (std::set::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) { + put_negative (*e, (int) p); + } + + } + + } +} + +bool +Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner &scanner) +{ + if (m_pass == 1) { + for (std::set >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) { + scanner.insert (&e->first, e->second); + } + return ! m_pseudo_edges.empty (); + } else { + return false; + } +} + +inline bool edges_considered (bool requires_different_polygons, bool requires_different_layers, size_t p1, size_t p2) +{ + if (p1 == p2) { + if (requires_different_polygons) { + return false; + } else if ((p1 & size_t (1)) != 0) { + // edges from the same polygon are only considered on first layer. + // Reasoning: this case happens when "intruder" polygons are put on layer 1 + // while "subject" polygons are put on layer 0. We don't want "intruders" + // to generate intra-polygon markers. + return false; + } + } + + if (((p1 ^ p2) & size_t (1)) == 0) { + if (requires_different_layers) { + return false; + } else if ((p1 & size_t (1)) != 0) { + // edges on the same layer are only considered on first layer. + // Reasoning: this case happens when "intruder" polygons are put on layer 1 + // while "subject" polygons are put on layer 0. We don't want "intruders" + // to generate inter-polygon markers between them. + return false; + } + } + + return true; +} + +void +Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) +{ + if (m_pass == 0) { + + // Overlap or inside checks require input from different layers + if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) { + + // ensure that the first check argument is of layer 1 and the second of + // layer 2 (unless both are of the same layer) + int l1 = int (p1 & size_t (1)); + int l2 = int (p2 & size_t (1)); + + if (l1 > l2) { + std::swap (o1, o2); + std::swap (p1, p2); + } + + db::EdgePair ep; + if (mp_check->check (*o1, *o2, &ep)) { + + ep.set_symmetric (m_symmetric_edges); + + // found a violation: store inside the local buffer for now. In the second + // pass we will eliminate those which are shielded completely (with shielding) + // and/or compute the negative edges. + size_t n = m_ep.size (); + + m_ep.push_back (ep); + m_ep_intra_polygon.push_back (p1 == p2); + + m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2)); + m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1)); + + if (m_has_negative_edge_output) { + m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p1)); + m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p1)); + if (p1 != p2) { + m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p2)); + m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p2)); + } + } + + } + + } + + } else { + + // set the discarded flags for shielded output + if (m_with_shielding) { + + // a simple (complete) shielding implementation which is based on the + // assumption that shielding is relevant as soon as a foreign edge cuts through + // both of the edge pair's connecting edges. + + // TODO: this implementation does not take into account the nature of the + // EdgePair - because of "whole_edge" it may not reflect the part actually + // violating the distance. + + std::vector n1, n2; + + for (unsigned int p = 0; p < 2; ++p) { + + std::pair k (*o1, p1); + for (std::multimap, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) { + size_t n = i->second / 2; + if (n < m_first_pseudo && ! m_ep_discarded [n]) { + n1.push_back (n); + } + } + + std::sort (n1.begin (), n1.end ()); + + std::swap (o1, o2); + std::swap (p1, p2); + n1.swap (n2); + + } + + for (unsigned int p = 0; p < 2; ++p) { + + std::vector nn; + std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn)); + + for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { + db::EdgePair ep = m_ep [*i].normalized (); + if (shields (ep, *o2)) { + m_ep_discarded [*i] = true; + } + } + + std::swap (o1, o2); + std::swap (p1, p2); + n1.swap (n2); + + } + + } + + // for negative output edges are cancelled by short interactions perpendicular to them + // For this we have generated "pseudo edges" running along the sides of the original violation. We now check a real + // edge vs. a pseudo edge with the same conditions as the normal interaction and add them to the results. In the + // negative case this means we cancel a real edge. + + if (m_has_negative_edge_output && + (m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end ()) != (m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) { + + // Overlap or inside checks require input from different layers + if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) { + + // ensure that the first check argument is of layer 1 and the second of + // layer 2 (unless both are of the same layer) + int l1 = int (p1 & size_t (1)); + int l2 = int (p2 & size_t (1)); + + if (l1 > l2) { + std::swap (o1, o2); + std::swap (p1, p2); + } + + db::EdgePair ep; + if (mp_check->check (*o1, *o2, &ep)) { + + size_t n = m_ep.size (); + + m_ep.push_back (ep); + m_ep_intra_polygon.push_back (p1 == p2); // not really required, but there for consistency + + m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2)); + m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1)); + + } + + } + + } + + } + +} + +/** + * @brief Gets a value indicating whether the check requires different layers + */ +bool +Edge2EdgeCheckBase::requires_different_layers () const +{ + return m_requires_different_layers; +} + +/** + * @brief Sets a value indicating whether the check requires different layers + */ +void +Edge2EdgeCheckBase::set_requires_different_layers (bool f) +{ + m_requires_different_layers = f; +} + +/** + * @brief Gets a value indicating whether the check requires different layers + */ +bool +Edge2EdgeCheckBase::different_polygons () const +{ + return m_different_polygons; +} + +/** + * @brief Sets a value indicating whether the check requires different layers + */ +void +Edge2EdgeCheckBase::set_different_polygons (bool f) +{ + m_different_polygons = f; +} + +/** + * @brief Gets the distance value + */ +EdgeRelationFilter::distance_type +Edge2EdgeCheckBase::distance () const +{ + return m_distance; +} + +// ------------------------------------------------------------------------------------- +// Poly2PolyCheckBase implementation + +template +poly2poly_check::poly2poly_check (Edge2EdgeCheckBase &output) + : mp_output (& output) +{ + // .. nothing yet .. +} + +template +poly2poly_check::poly2poly_check () + : mp_output (0) +{ + // .. nothing yet .. +} + +static size_t vertices (const db::Polygon &p) +{ + return p.vertices (); +} + +static size_t vertices (const db::PolygonRef &p) +{ + return p.obj ().vertices (); +} + +template +void +poly2poly_check::single (const PolygonType &o, size_t p) +{ + tl_assert (! mp_output->requires_different_layers () && ! mp_output->different_polygons ()); + + // finally we check the polygons vs. itself for checks involving intra-polygon interactions + + m_scanner.clear (); + m_scanner.reserve (vertices (o)); + + m_edge_heap.clear (); + + for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) { + m_edge_heap.push_back (*e); + m_scanner.insert (& m_edge_heap.back (), p); + } + + mp_output->feed_pseudo_edges (m_scanner); + + m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); +} + +template +void +poly2poly_check::connect (Edge2EdgeCheckBase &output) +{ + mp_output = &output; + clear (); +} + +template +void +poly2poly_check::clear () +{ + m_scanner.clear (); + m_edge_heap.clear (); +} + +template +void +poly2poly_check::enter (const PolygonType &o, size_t p) +{ + for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) { + m_edge_heap.push_back (*e); + m_scanner.insert (& m_edge_heap.back (), p); + } +} + +template +void +poly2poly_check::process () +{ + mp_output->feed_pseudo_edges (m_scanner); + m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); +} + +// explicit instantiations +template class poly2poly_check; +template class poly2poly_check; + +// ------------------------------------------------------------------------------------- +// RegionToEdgeInteractionFilterBase implementation + +template +region_to_edge_interaction_filter_base::region_to_edge_interaction_filter_base (bool inverse, bool get_all) + : m_inverse (inverse), m_get_all (get_all) +{ + // .. nothing yet .. +} + +template +void +region_to_edge_interaction_filter_base::preset (const OutputType *s) +{ + m_seen.insert (s); +} + +template +void +region_to_edge_interaction_filter_base::add (const PolygonType *p, size_t, const EdgeType *e, size_t) +{ + const OutputType *o = 0; + tl::select (o, p, e); + + if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) { + + // A polygon and an edge interact if the edge is either inside completely + // of at least one edge of the polygon intersects with the edge + bool interacts = false; + if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) { + interacts = true; + } else { + for (typename PolygonType::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) { + if ((*pe).intersect (*e)) { + interacts = true; + } + } + } + + if (interacts) { + if (m_inverse) { + m_seen.erase (o); + } else { + if (! m_get_all) { + m_seen.insert (o); + } + put (*o); + } + } + + } +} + +template +void +region_to_edge_interaction_filter_base::fill_output () +{ + for (typename std::set::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) { + put (**s); + } +} + +// explicit instantiations +template class region_to_edge_interaction_filter_base; +template class region_to_edge_interaction_filter_base; +template class region_to_edge_interaction_filter_base; +template class region_to_edge_interaction_filter_base; + +// ------------------------------------------------------------------------------------- +// RegionToTextInteractionFilterBase implementation + +template +region_to_text_interaction_filter_base::region_to_text_interaction_filter_base (bool inverse, bool get_all) + : m_inverse (inverse), m_get_all (get_all) +{ + // .. nothing yet .. +} + +template +void +region_to_text_interaction_filter_base::preset (const OutputType *s) +{ + m_seen.insert (s); +} + +template +void +region_to_text_interaction_filter_base::add (const PolygonType *p, size_t, const TextType *t, size_t) +{ + const OutputType *o = 0; + tl::select (o, p, t); + + if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) { + + // A polygon and an text interact if the text is either inside completely + // of at least one text of the polygon intersects with the text + db::Point pt = db::box_convert () (*t).p1 (); + if (p->box ().contains (pt) && db::inside_poly (p->begin_edge (), pt) >= 0) { + if (m_inverse) { + m_seen.erase (o); + } else { + if (! m_get_all) { + m_seen.insert (o); + } + put (*o); + } + } + + } +} + +template +void +region_to_text_interaction_filter_base::fill_output () +{ + for (typename std::set::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) { + put (**s); + } +} + +// explicit instantiations +template class region_to_text_interaction_filter_base; +template class region_to_text_interaction_filter_base; +template class region_to_text_interaction_filter_base; +template class region_to_text_interaction_filter_base; +template class region_to_text_interaction_filter_base; + +} diff --git a/src/db/db/dbRegionCheckUtils.h b/src/db/db/dbRegionCheckUtils.h new file mode 100644 index 000000000..d8311980d --- /dev/null +++ b/src/db/db/dbRegionCheckUtils.h @@ -0,0 +1,424 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 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_dbRegionCheckUtils +#define HDR_dbRegionCheckUtils + +#include "dbCommon.h" +#include "dbCellVariants.h" +#include "dbBoxScanner.h" +#include "dbEdgePairRelations.h" + +namespace db { + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + */ +class DB_PUBLIC Edge2EdgeCheckBase + : public db::box_scanner_receiver +{ +public: + Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges); + + /** + * @brief Call this to initiate a new pass until the return value is false + */ + bool prepare_next_pass (); + + /** + * @brief Before the scanner is run, this method must be called to feed additional edges into the scanner + * (required for negative edge output - cancellation of perpendicular edges) + */ + bool feed_pseudo_edges (db::box_scanner &scanner); + + /** + * @brief Reimplementation of the box_scanner_receiver interface + */ + void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2); + + /** + * @brief Reimplementation of the box_scanner_receiver interface + */ + void finish (const Edge *o, size_t); + + /** + * @brief Gets a value indicating whether the check requires different layers + */ + bool requires_different_layers () const; + + /** + * @brief Sets a value indicating whether the check requires different layers + */ + void set_requires_different_layers (bool f); + + /** + * @brief Gets a value indicating whether the check requires different layers + */ + bool different_polygons () const; + + /** + * @brief Sets a value indicating whether the check requires different layers + */ + void set_different_polygons (bool f); + + /** + * @brief Sets a flag indicating that this class wants negative edge output + */ + void set_has_negative_edge_output (bool f) + { + m_has_negative_edge_output = f; + } + + /** + * @brief Gets a flag indicating that this class wants negative edge output + */ + bool has_negative_edge_output () const + { + return m_has_negative_edge_output; + } + + /** + * @brief Sets a flag indicating that this class wants normal edge pair output + */ + void set_has_edge_pair_output (bool f) + { + m_has_edge_pair_output = f; + } + + /** + * @brief Gets a flag indicating that this class wants normal edge pair output + */ + bool has_edge_pair_output () const + { + return m_has_edge_pair_output; + } + + /** + * @brief Gets the distance value + */ + EdgeRelationFilter::distance_type distance () const; + +protected: + /** + * @brief Normal edge pair output (violations) + */ + virtual void put (const db::EdgePair & /*edge*/, bool /*intra-polygon*/) const { } + + /** + * @brief Negative edge output + */ + virtual void put_negative (const db::Edge & /*edge*/, int /*layer*/) const { } + +private: + const EdgeRelationFilter *mp_check; + bool m_requires_different_layers; + bool m_different_polygons; + EdgeRelationFilter::distance_type m_distance; + std::vector m_ep; + std::multimap, size_t> m_e2ep; + std::set > m_pseudo_edges; + size_t m_first_pseudo; + std::vector m_ep_discarded, m_ep_intra_polygon; + bool m_with_shielding; + bool m_symmetric_edges; + bool m_has_edge_pair_output; + bool m_has_negative_edge_output; + unsigned int m_pass; +}; + +/** + * @brief A helper class for the DRC functionality + * + * This class implements the edge-to-edge part of the polygon DRC. + */ +template +class DB_PUBLIC_TEMPLATE edge2edge_check + : public Edge2EdgeCheckBase +{ +public: + edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) + : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output), mp_output_intra (0) + { + // .. nothing yet .. + } + + edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) + : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output_inter), mp_output_intra (&output_intra) + { + // .. nothing yet .. + } + +protected: + void put (const db::EdgePair &edge, bool inter_polygon) const + { + if (! inter_polygon || ! mp_output_intra) { + mp_output_inter->insert (edge); + } else { + mp_output_intra->insert (edge); + } + } + +private: + Output *mp_output_inter; + Output *mp_output_intra; +}; + +/** + * @brief A helper class for the DRC functionality + * + * This class implements the edge-to-edge part of the polygon DRC. + * This version allows delivery of the negative edges. + */ +template +class DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output + : public edge2edge_check +{ +public: + edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) + : edge2edge_check (check, output, different_polygons, requires_different_layers, with_shielding, symmetric_edges), + mp_l1_negative_output (&l1_negative_output), + mp_l2_negative_output (&l2_negative_output) + { + edge2edge_check::set_has_negative_edge_output (true); + } + + edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) + : edge2edge_check (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric_edges), + mp_l1_negative_output (&l1_negative_output), + mp_l2_negative_output (&l2_negative_output) + { + edge2edge_check::set_has_negative_edge_output (true); + } + +protected: + void put_negative (const db::Edge &edge, int layer) const + { + if (layer == 0) { + mp_l1_negative_output->insert (edge); + } + if (layer == 1) { + mp_l2_negative_output->insert (edge); + } + } + +private: + NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output; +}; + +/** + * @brief A helper class for the DRC functionality + * + * This class implements the edge-to-edge part of the polygon DRC. + * This version has only negative edge output. + */ +template +class DB_PUBLIC_TEMPLATE edge2edge_check_negative + : public Edge2EdgeCheckBase +{ +public: + edge2edge_check_negative (const EdgeRelationFilter &check, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding) + : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, false), + mp_l1_negative_output (&l1_negative_output), + mp_l2_negative_output (&l2_negative_output) + { + set_has_negative_edge_output (true); + set_has_edge_pair_output (false); + } + +protected: + void put_negative (const db::Edge &edge, int layer) const + { + if (layer == 0) { + mp_l1_negative_output->insert (edge); + } + if (layer == 1) { + mp_l2_negative_output->insert (edge); + } + } + +private: + NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output; +}; + +/** + * @brief A helper class for the DRC functionality + * + * This class implements the edge-to-edge part of the polygon DRC. + * This version has positive or negative output. Negative output is mapped to edge pairs + * as well. + */ +template +class DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive + : public edge2edge_check +{ +public: + edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric) + : edge2edge_check (check, output, different_polygons, requires_different_layers, with_shielding, symmetric) + { + edge2edge_check::set_has_negative_edge_output (negative_output); + edge2edge_check::set_has_edge_pair_output (! negative_output); + } + + edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric) + : edge2edge_check (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric) + { + edge2edge_check::set_has_negative_edge_output (negative_output); + edge2edge_check::set_has_edge_pair_output (! negative_output); + } + +protected: + void put_negative (const db::Edge &edge, int layer) const + { + if (layer == 0) { + edge2edge_check::put (db::EdgePair (edge, edge.swapped_points ()), false); + } +#if 0 + // NOTE: second-input negative edge output isn't worth a lot as the second input often is not merged, hence + // the outer edges to not represent the actual contour. + if (layer == 1) { + edge2edge_check::put (db::EdgePair (edge.swapped_points (), edge), false); + } +#endif + } +}; + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + */ +template +class DB_PUBLIC poly2poly_check +{ +public: + poly2poly_check (Edge2EdgeCheckBase &output); + poly2poly_check (); + + void clear (); + + void single (const PolygonType&o, size_t p); + + void connect (Edge2EdgeCheckBase &output); + void enter (const PolygonType &o, size_t p); + void process (); + +private: + db::Edge2EdgeCheckBase *mp_output; + db::box_scanner m_scanner; + std::list m_edge_heap; +}; + +/** + * @brief A helper class for the region to edge interaction functionality + */ +template +class DB_PUBLIC region_to_edge_interaction_filter_base + : public db::box_scanner_receiver2 +{ +public: + region_to_edge_interaction_filter_base (bool inverse, bool get_all); + + void preset (const OutputType *s); + void add (const PolygonType *p, size_t, const EdgeType *e, size_t); + void fill_output (); + +protected: + virtual void put (const OutputType &s) const = 0; + +private: + std::set m_seen; + bool m_inverse, m_get_all; +}; + +/** + * @brief A helper class for the region to edge interaction functionality + */ +template +class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter + : public region_to_edge_interaction_filter_base +{ +public: + region_to_edge_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false) + : region_to_edge_interaction_filter_base (inverse, get_all), mp_output (&output) + { + // .. nothing yet .. + } + +protected: + virtual void put (const OutputType &res) const + { + mp_output->insert (res); + } + +private: + OutputContainer *mp_output; +}; + +/** + * @brief A helper class for the region to text interaction functionality + */ +template +class DB_PUBLIC region_to_text_interaction_filter_base + : public db::box_scanner_receiver2 +{ +public: + region_to_text_interaction_filter_base (bool inverse, bool get_all); + + void preset (const OutputType *s); + void add (const PolygonType *p, size_t, const TextType *e, size_t); + void fill_output (); + +protected: + virtual void put (const OutputType &s) const = 0; + +private: + std::set m_seen; + bool m_inverse, m_get_all; +}; + +/** + * @brief A helper class for the region to text interaction functionality + */ +template +class DB_PUBLIC_TEMPLATE region_to_text_interaction_filter + : public region_to_text_interaction_filter_base +{ +public: + region_to_text_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false) + : region_to_text_interaction_filter_base (inverse, get_all), mp_output (&output) + { + // .. nothing yet .. + } + +protected: + virtual void put (const OutputType &poly) const + { + mp_output->insert (poly); + } + +private: + OutputContainer *mp_output; +}; + +} // namespace db + +#endif + diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 0a09c411e..339f5e2dd 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -21,8 +21,8 @@ */ -#include "dbRegionUtils.h" #include "dbRegionLocalOperations.h" +#include "dbRegionUtils.h" #include "dbLocalOperationUtils.h" #include "dbHierProcessor.h" @@ -123,7 +123,7 @@ static bool shields_interaction (const db::EdgePair &ep, const P &poly) template check_local_operation::check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options) - : m_check (check), m_different_polygons (different_polygons), m_has_other (has_other), m_other_is_merged (other_is_merged), m_options (options) + : m_check (check), m_different_polygons (different_polygons), m_has_other (has_other), m_other_is_merged (other_is_merged), m_options (options), m_poly_check () { // .. nothing yet .. } @@ -206,10 +206,9 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape bool symmetric_edge_pairs = ! m_has_other && m_options.opposite_filter == db::NoOppositeFilter && m_options.rect_filter == RectFilter::NoRectFilter; edge2edge_check_negative_or_positive > edge_check (m_check, result, intra_polygon_result, m_options.negative, m_different_polygons, m_has_other, m_options.shielded, symmetric_edge_pairs); - poly2poly_check poly_check (edge_check); + m_poly_check.connect (edge_check); std::list heap; - db::box_scanner scanner; std::unordered_set polygons; std::set ids; @@ -224,7 +223,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape size_t n = 0; for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { const TS &subject = interactions.subject_shape (i->first); - scanner.insert (push_polygon_to_heap (layout, subject, heap), n); + m_poly_check.enter (subject, n); n += 2; } @@ -262,7 +261,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape n = 1; for (typename std::unordered_set::const_iterator o = polygons.begin (); o != polygons.end (); ++o) { - scanner.insert (push_polygon_to_heap (layout, *o, heap), n); + m_poly_check.enter (*o, n); n += 2; } @@ -270,7 +269,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape n = 1; for (std::set::const_iterator id = ids.begin (); id != ids.end (); ++id) { - scanner.insert (push_polygon_to_heap (layout, interactions.intruder_shape (*id).second, heap), n); + m_poly_check.enter (interactions.intruder_shape (*id).second, n); n += 2; } @@ -286,7 +285,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape // we can't directly insert because TS may be != TI const TS &ts = interactions.subject_shape (i->first); insert_into_hash (polygons, ts); - scanner.insert (push_polygon_to_heap (layout, ts, heap), n); + m_poly_check.enter (ts, n); n += 2; } @@ -295,7 +294,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape for (std::set::const_iterator id = ids.begin (); id != ids.end (); ++id) { const TI &ti = interactions.intruder_shape (*id).second; if (polygons.find (ti) == polygons.end ()) { - scanner.insert (push_polygon_to_heap (layout, ti, heap), n); + m_poly_check.enter (ti, n); n += 2; } } @@ -303,7 +302,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape } do { - scanner.process (poly_check, m_check.distance (), db::box_convert ()); + m_poly_check.process (); } while (edge_check.prepare_next_pass ()); // detect and remove parts of the result which have or do not have results "opposite" diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index f4bec55f0..b8f8b8b3f 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -28,6 +28,7 @@ #include "dbEdgePairRelations.h" #include "dbLocalOperation.h" #include "dbEdgeProcessor.h" +#include "dbRegionCheckUtils.h" #include #include @@ -218,6 +219,7 @@ private: bool m_has_other; bool m_other_is_merged; db::RegionCheckOptions m_options; + mutable poly2poly_check m_poly_check; }; typedef check_local_operation CheckLocalOperation; diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc index e4e33e563..4c8acd690 100644 --- a/src/db/db/dbRegionUtils.cc +++ b/src/db/db/dbRegionUtils.cc @@ -22,6 +22,7 @@ #include "dbRegionUtils.h" +#include "dbRegionCheckUtils.h" #include "dbPolygonTools.h" #include "dbEdgeBoolean.h" #include "tlSelect.h" @@ -29,496 +30,6 @@ namespace db { -// ------------------------------------------------------------------------------------- -// Edge2EdgeCheckBase implementation - -Edge2EdgeCheckBase::Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) - : mp_check (&check), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons), - m_first_pseudo (std::numeric_limits::max ()), - m_with_shielding (with_shielding), - m_symmetric_edges (symmetric_edges), - m_has_edge_pair_output (true), - m_has_negative_edge_output (false), - m_pass (0) -{ - m_distance = check.distance (); -} - -bool -Edge2EdgeCheckBase::prepare_next_pass () -{ - ++m_pass; - - if (m_pass == 1) { - - m_first_pseudo = m_ep.size (); - - if (m_with_shielding && ! m_ep.empty ()) { - - m_ep_discarded.resize (m_ep.size (), false); - - // second pass: - return true; - - } else if (m_has_negative_edge_output) { - - // second pass: - return true; - - } - - } - - if (! m_ep.empty () && m_has_edge_pair_output) { - - std::vector::const_iterator d = m_ep_discarded.begin (); - std::vector::const_iterator i = m_ep_intra_polygon.begin (); - std::vector::const_iterator ep = m_ep.begin (); - while (ep != m_ep.end () && size_t (ep - m_ep.begin ()) < m_first_pseudo) { - bool use_result = true; - if (d != m_ep_discarded.end ()) { - use_result = ! *d; - ++d; - } - if (use_result) { - put (*ep, *i); - } - ++ep; - ++i; - } - - } - - return false; -} - -static inline bool shields (const db::EdgePair &ep, const db::Edge &q) -{ - db::Edge pe1 (ep.first ().p1 (), ep.second ().p2 ()); - db::Edge pe2 (ep.second ().p1 (), ep.first ().p2 ()); - - std::pair ip1 = pe1.intersect_point (q); - std::pair ip2 = pe2.intersect_point (q); - - if (ip1.first && ip2.first) { - return ip1.second != ip2.second || (pe1.side_of (q.p1 ()) != 0 && pe2.side_of (q.p2 ()) != 0); - } else { - return false; - } -} - -void -Edge2EdgeCheckBase::finish (const Edge *o, size_t p) -{ - if (m_has_negative_edge_output && m_pass == 1 && m_pseudo_edges.find (std::make_pair (*o, p)) == m_pseudo_edges.end ()) { - - std::pair k (*o, p); - std::multimap, size_t>::const_iterator i0 = m_e2ep.find (k); - - bool fully_removed = false; - bool any = false; - for (std::multimap, size_t>::const_iterator i = i0; ! fully_removed && i != m_e2ep.end () && i->first == k; ++i) { - size_t n = i->second / 2; - if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) { - any = true; - fully_removed = (((i->second & 1) == 0 ? m_ep [n].first () : m_ep [n].second ()) == *o); - } - } - - if (! any) { - - put_negative (*o, (int) p); - - } else if (! fully_removed) { - - std::set partial_edges; - - db::EdgeBooleanCluster > ec (&partial_edges, db::EdgeNot); - ec.add (o, 0); - - for (std::multimap, size_t>::const_iterator i = i0; i != m_e2ep.end () && i->first == k; ++i) { - size_t n = i->second / 2; - if (n >= m_ep_discarded.size () || !m_ep_discarded [n]) { - ec.add (((i->second & 1) == 0 ? &m_ep [n].first () : &m_ep [n].second ()), 1); - } - } - - ec.finish (); - - for (std::set::const_iterator e = partial_edges.begin (); e != partial_edges.end (); ++e) { - put_negative (*e, (int) p); - } - - } - - } -} - -bool -Edge2EdgeCheckBase::feed_pseudo_edges (db::box_scanner &scanner) -{ - if (m_pass == 1) { - for (std::set >::const_iterator e = m_pseudo_edges.begin (); e != m_pseudo_edges.end (); ++e) { - scanner.insert (&e->first, e->second); - } - return ! m_pseudo_edges.empty (); - } else { - return false; - } -} - -inline bool edges_considered (bool requires_different_polygons, bool requires_different_layers, size_t p1, size_t p2) -{ - if (p1 == p2) { - if (requires_different_polygons) { - return false; - } else if ((p1 & size_t (1)) != 0) { - // edges from the same polygon are only considered on first layer. - // Reasoning: this case happens when "intruder" polygons are put on layer 1 - // while "subject" polygons are put on layer 0. We don't want "intruders" - // to generate intra-polygon markers. - return false; - } - } - - if (((p1 ^ p2) & size_t (1)) == 0) { - if (requires_different_layers) { - return false; - } else if ((p1 & size_t (1)) != 0) { - // edges on the same layer are only considered on first layer. - // Reasoning: this case happens when "intruder" polygons are put on layer 1 - // while "subject" polygons are put on layer 0. We don't want "intruders" - // to generate inter-polygon markers between them. - return false; - } - } - - return true; -} - -void -Edge2EdgeCheckBase::add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2) -{ - if (m_pass == 0) { - - // Overlap or inside checks require input from different layers - if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) { - - // ensure that the first check argument is of layer 1 and the second of - // layer 2 (unless both are of the same layer) - int l1 = int (p1 & size_t (1)); - int l2 = int (p2 & size_t (1)); - - if (l1 > l2) { - std::swap (o1, o2); - std::swap (p1, p2); - } - - db::EdgePair ep; - if (mp_check->check (*o1, *o2, &ep)) { - - ep.set_symmetric (m_symmetric_edges); - - // found a violation: store inside the local buffer for now. In the second - // pass we will eliminate those which are shielded completely (with shielding) - // and/or compute the negative edges. - size_t n = m_ep.size (); - - m_ep.push_back (ep); - m_ep_intra_polygon.push_back (p1 == p2); - - m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2)); - m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1)); - - if (m_has_negative_edge_output) { - m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p1)); - m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p1)); - if (p1 != p2) { - m_pseudo_edges.insert (std::make_pair (db::Edge (ep.first ().p1 (), ep.second ().p2 ()), p2)); - m_pseudo_edges.insert (std::make_pair (db::Edge (ep.second ().p1 (), ep.first ().p2 ()), p2)); - } - } - - } - - } - - } else { - - // set the discarded flags for shielded output - if (m_with_shielding) { - - // a simple (complete) shielding implementation which is based on the - // assumption that shielding is relevant as soon as a foreign edge cuts through - // both of the edge pair's connecting edges. - - // TODO: this implementation does not take into account the nature of the - // EdgePair - because of "whole_edge" it may not reflect the part actually - // violating the distance. - - std::vector n1, n2; - - for (unsigned int p = 0; p < 2; ++p) { - - std::pair k (*o1, p1); - for (std::multimap, size_t>::const_iterator i = m_e2ep.find (k); i != m_e2ep.end () && i->first == k; ++i) { - size_t n = i->second / 2; - if (n < m_first_pseudo && ! m_ep_discarded [n]) { - n1.push_back (n); - } - } - - std::sort (n1.begin (), n1.end ()); - - std::swap (o1, o2); - std::swap (p1, p2); - n1.swap (n2); - - } - - for (unsigned int p = 0; p < 2; ++p) { - - std::vector nn; - std::set_difference (n1.begin (), n1.end (), n2.begin (), n2.end (), std::back_inserter (nn)); - - for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { - db::EdgePair ep = m_ep [*i].normalized (); - if (shields (ep, *o2)) { - m_ep_discarded [*i] = true; - } - } - - std::swap (o1, o2); - std::swap (p1, p2); - n1.swap (n2); - - } - - } - - // for negative output edges are cancelled by short interactions perpendicular to them - // For this we have generated "pseudo edges" running along the sides of the original violation. We now check a real - // edge vs. a pseudo edge with the same conditions as the normal interaction and add them to the results. In the - // negative case this means we cancel a real edge. - - if (m_has_negative_edge_output && - (m_pseudo_edges.find (std::make_pair (*o1, p1)) != m_pseudo_edges.end ()) != (m_pseudo_edges.find (std::make_pair (*o2, p2)) != m_pseudo_edges.end ())) { - - // Overlap or inside checks require input from different layers - if (edges_considered (m_different_polygons, m_requires_different_layers, p1, p2)) { - - // ensure that the first check argument is of layer 1 and the second of - // layer 2 (unless both are of the same layer) - int l1 = int (p1 & size_t (1)); - int l2 = int (p2 & size_t (1)); - - if (l1 > l2) { - std::swap (o1, o2); - std::swap (p1, p2); - } - - db::EdgePair ep; - if (mp_check->check (*o1, *o2, &ep)) { - - size_t n = m_ep.size (); - - m_ep.push_back (ep); - m_ep_intra_polygon.push_back (p1 == p2); // not really required, but there for consistency - - m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n * 2)); - m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n * 2 + 1)); - - } - - } - - } - - } - -} - -/** - * @brief Gets a value indicating whether the check requires different layers - */ -bool -Edge2EdgeCheckBase::requires_different_layers () const -{ - return m_requires_different_layers; -} - -/** - * @brief Sets a value indicating whether the check requires different layers - */ -void -Edge2EdgeCheckBase::set_requires_different_layers (bool f) -{ - m_requires_different_layers = f; -} - -/** - * @brief Gets a value indicating whether the check requires different layers - */ -bool -Edge2EdgeCheckBase::different_polygons () const -{ - return m_different_polygons; -} - -/** - * @brief Sets a value indicating whether the check requires different layers - */ -void -Edge2EdgeCheckBase::set_different_polygons (bool f) -{ - m_different_polygons = f; -} - -/** - * @brief Gets the distance value - */ -EdgeRelationFilter::distance_type -Edge2EdgeCheckBase::distance () const -{ - return m_distance; -} - -// ------------------------------------------------------------------------------------- -// Poly2PolyCheckBase implementation - -template -poly2poly_check::poly2poly_check (Edge2EdgeCheckBase &output) - : mp_output (& output) -{ - // .. nothing yet .. -} - -template -void -poly2poly_check::finish (const PolygonType *o, size_t p) -{ - enter (*o, p); -} - -static size_t vertices (const db::Polygon &p) -{ - return p.vertices (); -} - -static size_t vertices (const db::PolygonRef &p) -{ - return p.obj ().vertices (); -} - -template -void -poly2poly_check::enter (const PolygonType &o, size_t p) -{ - if (! mp_output->requires_different_layers () && ! mp_output->different_polygons ()) { - - // finally we check the polygons vs. itself for checks involving intra-polygon interactions - - m_scanner.clear (); - m_scanner.reserve (vertices (o)); - - m_edges.clear (); - m_edges.reserve (vertices (o)); - - for (typename PolygonType::polygon_edge_iterator e = o.begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p); - } - - mp_output->feed_pseudo_edges (m_scanner); - - tl_assert (m_edges.size () == vertices (o)); - - m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); - - } -} - -template -void -poly2poly_check::add (const PolygonType *o1, size_t p1, const PolygonType *o2, size_t p2) -{ - enter (*o1, p1, *o2, p2); -} - -static bool interact (const db::Box &box, const db::Edge &e) -{ - if (! e.bbox ().touches (box)) { - return false; - } else if (e.is_ortho ()) { - return true; - } else { - return e.clipped (box).first; - } -} - -template -void -poly2poly_check::enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2) -{ - if (p1 != p2 && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { - - bool take_all = mp_output->has_negative_edge_output (); - - db::Box common_box; - if (! take_all) { - db::Vector e (mp_output->distance (), mp_output->distance ()); - common_box = o1.box ().enlarged (e) & o2.box ().enlarged (e); - if (common_box.empty ()) { - return; - } - } - - m_scanner.clear (); - m_scanner.reserve (vertices (o1) + vertices (o2)); - - m_edges.clear (); - m_edges.reserve (vertices (o1) + vertices (o2)); - - bool any_o1 = false, any_o2 = false; - - for (typename PolygonType::polygon_edge_iterator e = o1.begin_edge (); ! e.at_end (); ++e) { - if (take_all || interact (common_box, *e)) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p1); - any_o1 = true; - } - } - - for (typename PolygonType::polygon_edge_iterator e = o2.begin_edge (); ! e.at_end (); ++e) { - if (take_all || interact (common_box, *e)) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p2); - any_o2 = true; - } - } - - if (! take_all && (! any_o1 || ! any_o2)) { - return; - } - - mp_output->feed_pseudo_edges (m_scanner); - - // temporarily disable intra-polygon check in that step .. we do that later in finish() - // if required (#650). - bool no_intra = mp_output->different_polygons (); - mp_output->set_different_polygons (true); - - m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); - - mp_output->set_different_polygons (no_intra); - - } -} - -// explicit instantiations -template class poly2poly_check; -template class poly2poly_check; - // ------------------------------------------------------------------------------------- // RegionPerimeterFilter implementation @@ -859,7 +370,7 @@ SinglePolygonCheck::process (const db::Polygon &polygon, std::vector poly_check (edge_check); do { - poly_check.enter (polygon, 0); + poly_check.single (polygon, 0); } while (edge_check.prepare_next_pass ()); res.insert (res.end (), result.begin (), result.end ()); @@ -967,219 +478,4 @@ HullExtractionProcessor::process (const db::Polygon &poly, std::vector -region_to_edge_interaction_filter_base::region_to_edge_interaction_filter_base (bool inverse, bool get_all) - : m_inverse (inverse), m_get_all (get_all) -{ - // .. nothing yet .. -} - -template -void -region_to_edge_interaction_filter_base::preset (const OutputType *s) -{ - m_seen.insert (s); -} - -template -void -region_to_edge_interaction_filter_base::add (const PolygonType *p, size_t, const EdgeType *e, size_t) -{ - const OutputType *o = 0; - tl::select (o, p, e); - - if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) { - - // A polygon and an edge interact if the edge is either inside completely - // of at least one edge of the polygon intersects with the edge - bool interacts = false; - if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) { - interacts = true; - } else { - for (typename PolygonType::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) { - if ((*pe).intersect (*e)) { - interacts = true; - } - } - } - - if (interacts) { - if (m_inverse) { - m_seen.erase (o); - } else { - if (! m_get_all) { - m_seen.insert (o); - } - put (*o); - } - } - - } -} - -template -void -region_to_edge_interaction_filter_base::fill_output () -{ - for (typename std::set::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) { - put (**s); - } -} - -// explicit instantiations -template class region_to_edge_interaction_filter_base; -template class region_to_edge_interaction_filter_base; -template class region_to_edge_interaction_filter_base; -template class region_to_edge_interaction_filter_base; - -// ------------------------------------------------------------------------------------- -// RegionToTextInteractionFilterBase implementation - -template -region_to_text_interaction_filter_base::region_to_text_interaction_filter_base (bool inverse, bool get_all) - : m_inverse (inverse), m_get_all (get_all) -{ - // .. nothing yet .. -} - -template -void -region_to_text_interaction_filter_base::preset (const OutputType *s) -{ - m_seen.insert (s); -} - -template -void -region_to_text_interaction_filter_base::add (const PolygonType *p, size_t, const TextType *t, size_t) -{ - const OutputType *o = 0; - tl::select (o, p, t); - - if (m_get_all || (m_seen.find (o) == m_seen.end ()) != m_inverse) { - - // A polygon and an text interact if the text is either inside completely - // of at least one text of the polygon intersects with the text - db::Point pt = db::box_convert () (*t).p1 (); - if (p->box ().contains (pt) && db::inside_poly (p->begin_edge (), pt) >= 0) { - if (m_inverse) { - m_seen.erase (o); - } else { - if (! m_get_all) { - m_seen.insert (o); - } - put (*o); - } - } - - } -} - -template -void -region_to_text_interaction_filter_base::fill_output () -{ - for (typename std::set::const_iterator s = m_seen.begin (); s != m_seen.end (); ++s) { - put (**s); - } -} - -// explicit instantiations -template class region_to_text_interaction_filter_base; -template class region_to_text_interaction_filter_base; -template class region_to_text_interaction_filter_base; -template class region_to_text_interaction_filter_base; -template class region_to_text_interaction_filter_base; - -// ------------------------------------------------------------------------------------- -// Polygon snapping - -db::Polygon -snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector &heap) -{ - db::Polygon pnew; - - for (size_t i = 0; i < poly.holes () + 1; ++i) { - - heap.clear (); - - db::Polygon::polygon_contour_iterator b, e; - - if (i == 0) { - b = poly.begin_hull (); - e = poly.end_hull (); - } else { - b = poly.begin_hole ((unsigned int) (i - 1)); - e = poly.end_hole ((unsigned int) (i - 1)); - } - - for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { - heap.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy))); - } - - if (i == 0) { - pnew.assign_hull (heap.begin (), heap.end ()); - } else { - pnew.insert_hole (heap.begin (), heap.end ()); - } - - } - - return pnew; -} - -db::Polygon -scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector &heap) -{ - db::Polygon pnew; - - int64_t dgx = int64_t (gx) * int64_t (dx); - int64_t dgy = int64_t (gy) * int64_t (dy); - - for (size_t i = 0; i < poly.holes () + 1; ++i) { - - heap.clear (); - - db::Polygon::polygon_contour_iterator b, e; - - if (i == 0) { - b = poly.begin_hull (); - e = poly.end_hull (); - } else { - b = poly.begin_hole ((unsigned int) (i - 1)); - e = poly.end_hole ((unsigned int) (i - 1)); - } - - for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { - int64_t x = snap_to_grid (int64_t ((*pt).x ()) * mx + int64_t (ox), dgx) / int64_t (dx); - int64_t y = snap_to_grid (int64_t ((*pt).y ()) * my + int64_t (oy), dgy) / int64_t (dy); - heap.push_back (db::Point (db::Coord (x), db::Coord (y))); - } - - if (i == 0) { - pnew.assign_hull (heap.begin (), heap.end ()); - } else { - pnew.insert_hole (heap.begin (), heap.end ()); - } - - } - - return pnew; -} - -db::Vector -scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy) -{ - int64_t dgx = int64_t (gx) * int64_t (dx); - int64_t dgy = int64_t (gy) * int64_t (dy); - - int64_t x = snap_to_grid (int64_t (v.x ()) * mx + int64_t (ox), dgx) / int64_t (dx); - int64_t y = snap_to_grid (int64_t (v.y ()) * my + int64_t (oy), dgy) / int64_t (dy); - - return db::Vector (db::Coord (x), db::Coord (y)); -} - } diff --git a/src/db/db/dbRegionUtils.h b/src/db/db/dbRegionUtils.h index 62fca5cd3..61eb467bf 100644 --- a/src/db/db/dbRegionUtils.h +++ b/src/db/db/dbRegionUtils.h @@ -583,298 +583,6 @@ public: virtual bool result_must_not_be_merged () const { return false; } }; -/** - * @brief A helper class for the DRC functionality which acts as an edge pair receiver - */ -class DB_PUBLIC Edge2EdgeCheckBase - : public db::box_scanner_receiver -{ -public: - Edge2EdgeCheckBase (const EdgeRelationFilter &check, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges); - - /** - * @brief Call this to initiate a new pass until the return value is false - */ - bool prepare_next_pass (); - - /** - * @brief Before the scanner is run, this method must be called to feed additional edges into the scanner - * (required for negative edge output - cancellation of perpendicular edges) - */ - bool feed_pseudo_edges (db::box_scanner &scanner); - - /** - * @brief Reimplementation of the box_scanner_receiver interface - */ - void add (const db::Edge *o1, size_t p1, const db::Edge *o2, size_t p2); - - /** - * @brief Reimplementation of the box_scanner_receiver interface - */ - void finish (const Edge *o, size_t); - - /** - * @brief Gets a value indicating whether the check requires different layers - */ - bool requires_different_layers () const; - - /** - * @brief Sets a value indicating whether the check requires different layers - */ - void set_requires_different_layers (bool f); - - /** - * @brief Gets a value indicating whether the check requires different layers - */ - bool different_polygons () const; - - /** - * @brief Sets a value indicating whether the check requires different layers - */ - void set_different_polygons (bool f); - - /** - * @brief Sets a flag indicating that this class wants negative edge output - */ - void set_has_negative_edge_output (bool f) - { - m_has_negative_edge_output = f; - } - - /** - * @brief Gets a flag indicating that this class wants negative edge output - */ - bool has_negative_edge_output () const - { - return m_has_negative_edge_output; - } - - /** - * @brief Sets a flag indicating that this class wants normal edge pair output - */ - void set_has_edge_pair_output (bool f) - { - m_has_edge_pair_output = f; - } - - /** - * @brief Gets a flag indicating that this class wants normal edge pair output - */ - bool has_edge_pair_output () const - { - return m_has_edge_pair_output; - } - - /** - * @brief Gets the distance value - */ - EdgeRelationFilter::distance_type distance () const; - -protected: - /** - * @brief Normal edge pair output (violations) - */ - virtual void put (const db::EdgePair & /*edge*/, bool /*intra-polygon*/) const { } - - /** - * @brief Negative edge output - */ - virtual void put_negative (const db::Edge & /*edge*/, int /*layer*/) const { } - -private: - const EdgeRelationFilter *mp_check; - bool m_requires_different_layers; - bool m_different_polygons; - EdgeRelationFilter::distance_type m_distance; - std::vector m_ep; - std::multimap, size_t> m_e2ep; - std::set > m_pseudo_edges; - size_t m_first_pseudo; - std::vector m_ep_discarded, m_ep_intra_polygon; - bool m_with_shielding; - bool m_symmetric_edges; - bool m_has_edge_pair_output; - bool m_has_negative_edge_output; - unsigned int m_pass; -}; - -/** - * @brief A helper class for the DRC functionality - * - * This class implements the edge-to-edge part of the polygon DRC. - */ -template -class DB_PUBLIC_TEMPLATE edge2edge_check - : public Edge2EdgeCheckBase -{ -public: - edge2edge_check (const EdgeRelationFilter &check, Output &output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) - : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output), mp_output_intra (0) - { - // .. nothing yet .. - } - - edge2edge_check (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) - : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, symmetric_edges), mp_output_inter (&output_inter), mp_output_intra (&output_intra) - { - // .. nothing yet .. - } - -protected: - void put (const db::EdgePair &edge, bool inter_polygon) const - { - if (! inter_polygon || ! mp_output_intra) { - mp_output_inter->insert (edge); - } else { - mp_output_intra->insert (edge); - } - } - -private: - Output *mp_output_inter; - Output *mp_output_intra; -}; - -/** - * @brief A helper class for the DRC functionality - * - * This class implements the edge-to-edge part of the polygon DRC. - * This version allows delivery of the negative edges. - */ -template -class DB_PUBLIC_TEMPLATE edge2edge_check_with_negative_output - : public edge2edge_check -{ -public: - edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) - : edge2edge_check (check, output, different_polygons, requires_different_layers, with_shielding, symmetric_edges), - mp_l1_negative_output (&l1_negative_output), - mp_l2_negative_output (&l2_negative_output) - { - edge2edge_check::set_has_negative_edge_output (true); - } - - edge2edge_check_with_negative_output (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric_edges) - : edge2edge_check (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric_edges), - mp_l1_negative_output (&l1_negative_output), - mp_l2_negative_output (&l2_negative_output) - { - edge2edge_check::set_has_negative_edge_output (true); - } - -protected: - void put_negative (const db::Edge &edge, int layer) const - { - if (layer == 0) { - mp_l1_negative_output->insert (edge); - } - if (layer == 1) { - mp_l2_negative_output->insert (edge); - } - } - -private: - NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output; -}; - -/** - * @brief A helper class for the DRC functionality - * - * This class implements the edge-to-edge part of the polygon DRC. - * This version has only negative edge output. - */ -template -class DB_PUBLIC_TEMPLATE edge2edge_check_negative - : public Edge2EdgeCheckBase -{ -public: - edge2edge_check_negative (const EdgeRelationFilter &check, NegativeEdgeOutput &l1_negative_output, NegativeEdgeOutput &l2_negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding) - : Edge2EdgeCheckBase (check, different_polygons, requires_different_layers, with_shielding, false), - mp_l1_negative_output (&l1_negative_output), - mp_l2_negative_output (&l2_negative_output) - { - set_has_negative_edge_output (true); - set_has_edge_pair_output (false); - } - -protected: - void put_negative (const db::Edge &edge, int layer) const - { - if (layer == 0) { - mp_l1_negative_output->insert (edge); - } - if (layer == 1) { - mp_l2_negative_output->insert (edge); - } - } - -private: - NegativeEdgeOutput *mp_l1_negative_output, *mp_l2_negative_output; -}; - -/** - * @brief A helper class for the DRC functionality - * - * This class implements the edge-to-edge part of the polygon DRC. - * This version has positive or negative output. Negative output is mapped to edge pairs - * as well. - */ -template -class DB_PUBLIC_TEMPLATE edge2edge_check_negative_or_positive - : public edge2edge_check -{ -public: - edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric) - : edge2edge_check (check, output, different_polygons, requires_different_layers, with_shielding, symmetric) - { - edge2edge_check::set_has_negative_edge_output (negative_output); - edge2edge_check::set_has_edge_pair_output (! negative_output); - } - - edge2edge_check_negative_or_positive (const EdgeRelationFilter &check, Output &output_inter, Output &output_intra, bool negative_output, bool different_polygons, bool requires_different_layers, bool with_shielding, bool symmetric) - : edge2edge_check (check, output_inter, output_intra, different_polygons, requires_different_layers, with_shielding, symmetric) - { - edge2edge_check::set_has_negative_edge_output (negative_output); - edge2edge_check::set_has_edge_pair_output (! negative_output); - } - -protected: - void put_negative (const db::Edge &edge, int layer) const - { - if (layer == 0) { - edge2edge_check::put (db::EdgePair (edge, edge.swapped_points ()), false); - } -#if 0 - // NOTE: second-input negative edge output isn't worth a lot as the second input often is not merged, hence - // the outer edges to not represent the actual contour. - if (layer == 1) { - edge2edge_check::put (db::EdgePair (edge.swapped_points (), edge), false); - } -#endif - } -}; - -/** - * @brief A helper class for the DRC functionality which acts as an edge pair receiver - */ -template -class DB_PUBLIC poly2poly_check - : public db::box_scanner_receiver -{ -public: - poly2poly_check (Edge2EdgeCheckBase &output); - - void finish (const PolygonType *o, size_t p); - void enter (const PolygonType&o, size_t p); - void add (const PolygonType *o1, size_t p1, const PolygonType *o2, size_t p2); - void enter (const PolygonType &o1, size_t p1, const PolygonType &o2, size_t p2); - -private: - db::Edge2EdgeCheckBase *mp_output; - db::box_scanner m_scanner; - std::vector m_edges; -}; - /** * @brief A class wrapping the single-polygon checks into a polygon-to-edge pair processor */ @@ -892,129 +600,6 @@ private: db::RegionCheckOptions m_options; }; -/** - * @brief A helper class for the region to edge interaction functionality - */ -template -class DB_PUBLIC region_to_edge_interaction_filter_base - : public db::box_scanner_receiver2 -{ -public: - region_to_edge_interaction_filter_base (bool inverse, bool get_all); - - void preset (const OutputType *s); - void add (const PolygonType *p, size_t, const EdgeType *e, size_t); - void fill_output (); - -protected: - virtual void put (const OutputType &s) const = 0; - -private: - std::set m_seen; - bool m_inverse, m_get_all; -}; - -/** - * @brief A helper class for the region to edge interaction functionality - */ -template -class DB_PUBLIC_TEMPLATE region_to_edge_interaction_filter - : public region_to_edge_interaction_filter_base -{ -public: - region_to_edge_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false) - : region_to_edge_interaction_filter_base (inverse, get_all), mp_output (&output) - { - // .. nothing yet .. - } - -protected: - virtual void put (const OutputType &res) const - { - mp_output->insert (res); - } - -private: - OutputContainer *mp_output; -}; - -/** - * @brief A helper class for the region to text interaction functionality - */ -template -class DB_PUBLIC region_to_text_interaction_filter_base - : public db::box_scanner_receiver2 -{ -public: - region_to_text_interaction_filter_base (bool inverse, bool get_all); - - void preset (const OutputType *s); - void add (const PolygonType *p, size_t, const TextType *e, size_t); - void fill_output (); - -protected: - virtual void put (const OutputType &s) const = 0; - -private: - std::set m_seen; - bool m_inverse, m_get_all; -}; - -/** - * @brief A helper class for the region to text interaction functionality - */ -template -class DB_PUBLIC_TEMPLATE region_to_text_interaction_filter - : public region_to_text_interaction_filter_base -{ -public: - region_to_text_interaction_filter (OutputContainer &output, bool inverse, bool get_all = false) - : region_to_text_interaction_filter_base (inverse, get_all), mp_output (&output) - { - // .. nothing yet .. - } - -protected: - virtual void put (const OutputType &poly) const - { - mp_output->insert (poly); - } - -private: - OutputContainer *mp_output; -}; - -template -static inline C snap_to_grid (C c, C g) -{ - // This form of snapping always snaps g/2 to right/top. - if (c < 0) { - c = -g * ((-c + (g - 1) / 2) / g); - } else { - c = g * ((c + g / 2) / g); - } - return c; -} - -/** - * @brief Snaps a polygon to the given grid - * Heap is a vector of points reused for the point list - */ -DB_PUBLIC db::Polygon snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord gy, std::vector &heap); - -/** - * @brief Scales and snaps a polygon to the given grid - * Heap is a vector of points reused for the point list - * The coordinate transformation is q = ((p * m + o) snap (g * d)) / d. - */ -DB_PUBLIC db::Polygon scaled_and_snapped_polygon (const db::Polygon &poly, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy, std::vector &heap); - -/** - * @brief Scales and snaps a vector to the given grid - * The coordinate transformation is q = ((p * m + o) snap (g * d)) / d. - */ -DB_PUBLIC db::Vector scaled_and_snapped_vector (const db::Vector &v, db::Coord gx, db::Coord mx, db::Coord dx, db::Coord ox, db::Coord gy, db::Coord my, db::Coord dy, db::Coord oy); - } // namespace db #endif diff --git a/src/db/unit_tests/dbRegionUtilsTests.cc b/src/db/unit_tests/dbRegionCheckUtilsTests.cc similarity index 94% rename from src/db/unit_tests/dbRegionUtilsTests.cc rename to src/db/unit_tests/dbRegionCheckUtilsTests.cc index c27a41e3d..cfad79fdd 100644 --- a/src/db/unit_tests/dbRegionUtilsTests.cc +++ b/src/db/unit_tests/dbRegionCheckUtilsTests.cc @@ -25,7 +25,7 @@ #include "tlUnitTest.h" #include "tlStringEx.h" -#include "dbRegionUtils.h" +#include "dbRegionCheckUtils.h" TEST(1_SimpleLShape) { @@ -52,7 +52,7 @@ TEST(1_SimpleLShape) do { // single polygon check - poly_check.enter (poly, 0); + poly_check.single (poly, 0); } while (e2e.prepare_next_pass ()); EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0),(2000,1000;1000,1000)|(1000,2000;2000,2000)"); @@ -85,7 +85,7 @@ TEST(1s_SimpleLShape) do { // single polygon check - poly_check.enter (poly, 0); + poly_check.single (poly, 0); } while (e2e.prepare_next_pass ()); EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)/(1000,1000;1000,0),(1000,2000;2000,2000)/(2000,1000;1000,1000)"); @@ -118,7 +118,7 @@ TEST(2_SimpleLWithBigPart) do { // single polygon check - poly_check.enter (poly, 0); + poly_check.single (poly, 0); } while (e2e.prepare_next_pass ()); EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0)"); @@ -153,7 +153,7 @@ TEST(3_SimpleTWithBigPart) do { // single polygon check - poly_check.enter (poly, 0); + poly_check.single (poly, 0); } while (e2e.prepare_next_pass ()); EXPECT_EQ (tl::to_string (ep), "(0,0;0,1000)|(1000,1000;1000,0),(0,2500;0,3500)|(1000,3500;1000,2500)"); @@ -188,7 +188,7 @@ TEST(4_SimpleNotch) do { // single polygon check - poly_check.enter (poly, 0); + poly_check.single (poly, 0); } while (e2e.prepare_next_pass ()); EXPECT_EQ (tl::to_string (ep), "(1000,1000;2000,1000)|(2000,2000;1000,2000)"); @@ -225,7 +225,7 @@ TEST(5_LShapeNotch) do { // single polygon check - poly_check.enter (poly, 0); + poly_check.single (poly, 0); } while (e2e.prepare_next_pass ()); EXPECT_EQ (tl::to_string (ep), "(1500,500;2000,500)|(2000,1500;1500,1500),(1500,1500;1500,2500)|(500,2500;500,1500)"); @@ -264,14 +264,12 @@ TEST(6_SeparationLvsBox) db::Polygon poly2; poly2.assign_hull (pts2, pts2 + sizeof (pts2) / sizeof (pts2[0])); - db::box_scanner scanner; - scanner.insert (&poly1, 0); // layer 0 - scanner.insert (&poly2, 1); // layer 1 - db::poly2poly_check poly_check (e2e); + poly_check.enter (poly1, 0); // layer 0 + poly_check.enter (poly2, 1); // layer 1 do { - scanner.process (poly_check, er.distance (), db::box_convert ()); + poly_check.process (); } while (e2e.prepare_next_pass ()); EXPECT_EQ (tl::to_string (ep), "(1000,1000;1000,0)/(2000,0;2000,1000),(3000,2000;2000,2000)/(2000,1000;3000,1000)"); diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 1fc2fbe1d..8601dfb65 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -10,7 +10,7 @@ SOURCES = \ dbCompoundOperationTests.cc \ dbFillToolTests.cc \ dbRecursiveInstanceIteratorTests.cc \ - dbRegionUtilsTests.cc \ + dbRegionCheckUtilsTests.cc \ dbUtilsTests.cc \ dbWriterTools.cc \ dbLoadLayoutOptionsTests.cc \