From 54945105efe544c12a6ccd3b467423a1eeeb5779 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 Nov 2018 22:56:08 +0100 Subject: [PATCH] WIP: first part of region refactoring. --- src/db/db/dbEdges.h | 18 +- src/db/db/dbRegion.cc | 2462 ++++++++++++++++++++++++++++++++++++----- src/db/db/dbRegion.h | 1992 +++++++++++++++++++++++++++++++-- 3 files changed, 4148 insertions(+), 324 deletions(-) diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 537a1e8ce..fae01830e 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -41,6 +41,18 @@ namespace db { class Edges; +/** + * @brief A base class for polygon filters + */ +class DB_PUBLIC EdgeFilterBase +{ +public: + EdgeFilterBase () { } + virtual ~EdgeFilterBase () { } + + virtual bool selected (const db::Edge &edge) const = 0; +}; + /** * @brief An edge length filter for use with Edges::filter or Edges::filtered * @@ -51,6 +63,7 @@ class Edges; */ struct DB_PUBLIC EdgeLengthFilter + : public EdgeFilterBase { typedef db::Edge::distance_type length_type; @@ -70,7 +83,7 @@ struct DB_PUBLIC EdgeLengthFilter /** * @brief Returns true if the edge length matches the criterion */ - bool operator() (const db::Edge &edge) const + bool selected (const db::Edge &edge) const { length_type l = edge.length (); if (! m_inverse) { @@ -96,6 +109,7 @@ private: */ struct DB_PUBLIC EdgeOrientationFilter + : public EdgeFilterBase { /** * @brief Constructor @@ -132,7 +146,7 @@ struct DB_PUBLIC EdgeOrientationFilter /** * @brief Returns true if the edge orientation matches the criterion */ - bool operator() (const db::Edge &edge) const + bool selected (const db::Edge &edge) const { int smin = db::vprod_sign (m_emin, db::DVector (edge.d ())); if (m_exact) { diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 65554fab4..4a2761b77 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -40,6 +40,2207 @@ namespace db { +// ------------------------------------------------------------------------------------------------------------- + +RegionDelegate::RegionDelegate () +{ + m_report_progress = false; + m_merged_semantics = true; + m_strict_handling = false; + m_merge_min_coherence = false; +} + +RegionDelegate::~RegionDelegate () +{ + // .. nothing yet .. +} + +void RegionDelegate::enable_progress (const std::string &progress_desc) +{ + m_report_progress = true; + m_progress_desc = progress_desc; +} + +void RegionDelegate::disable_progress () +{ + m_report_progress = false; +} + +void RegionDelegate::set_min_coherence (bool f) +{ + m_merge_min_coherence = f; +} + +void RegionDelegate::set_merged_semantics (bool f) +{ + if (f != m_merged_semantics) { + m_merged_semantics = f; + merged_semantics_changed (); + } +} + +void RegionDelegate::set_strict_handling (bool f) +{ + m_strict_handling = f; +} + +// ------------------------------------------------------------------------------------------------------------- +// EmptyRegion implementation + +EmptyRegion::EmptyRegion () +{ + // .. nothing yet .. +} + +EmptyRegion::~EmptyRegion () +{ + // .. nothing yet .. +} + +EmptyRegion::EmptyRegion (const EmptyRegion &other) + : RegionDelegate (other) +{ + // .. nothing yet .. +} + +RegionDelegate * +EmptyRegion::clone () const +{ + return new EmptyRegion (*this); +} + +RegionDelegate * +EmptyRegion::xor_with (const Region &other) const +{ + return other.delegate ()->clone (); +} + +RegionDelegate * +EmptyRegion::or_with (const Region &other) const +{ + return other.delegate ()->clone (); +} + +RegionDelegate * +EmptyRegion::add_in_place (const Region &other) +{ + return add (other); +} + +RegionDelegate * +EmptyRegion::add (const Region &other) const +{ + return other.delegate ()->clone (); +} + +// ------------------------------------------------------------------------------------------------------------- +// AsIfFlagRegion implementation + +AsIfFlatRegion::AsIfFlatRegion () + : RegionDelegate (), m_bbox_valid (false) +{ + // .. nothing yet .. +} + +AsIfFlatRegion::~AsIfFlatRegion () +{ + // .. nothing yet .. +} + +std::string +AsIfFlatRegion::to_string (size_t nmax) const +{ + std::ostringstream os; + RegionIterator p (begin ()); + bool first = true; + for ( ; ! p.at_end () && nmax != 0; ++p, --nmax) { + if (! first) { + os << ";"; + } + first = false; + os << p->to_string (); + } + if (! p.at_end ()) { + os << "..."; + } + return os.str (); +} + +Edges +AsIfFlatRegion::edges (const EdgeFilterBase *filter) const +{ + Edges edges; + + size_t n = 0; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + edges.reserve (n); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + if (! filter || filter->selected (*e)) { + edges.insert (*e); + } + } + } + + return edges; +} + +RegionDelegate * +AsIfFlatRegion::hulls () const +{ + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + db::Polygon h; + h.assign_hull (p->begin_hull (), p->end_hull ()); + new_region->insert (h); + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::holes () const +{ + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + for (size_t i = 0; i < p->holes (); ++i) { + db::Polygon h; + h.assign_hull (p->begin_hole ((unsigned int) i), p->end_hole ((unsigned int) i)); + new_region->insert (h); + } + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::rounded_corners (double rinner, double router, unsigned int n) const +{ + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + new_region->insert (db::compute_rounded (*p, rinner, router, n)); + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::smoothed (coord_type d) const +{ + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + new_region->insert (db::smooth (*p, d)); + } + + return new_region.release (); +} + +RegionDelegate * +AsIfFlatRegion::in (const Region &other, bool invert) const +{ + std::set op; + for (RegionIterator o (other.begin_merged ()); ! o.at_end (); ++o) { + op.insert (*o); + } + + std::auto_ptr new_region (new FlatRegion (false)); + + for (RegionIterator o (begin_merged ()); ! o.at_end (); ++o) { + if ((op.find (*o) == op.end ()) == invert) { + new_region->insert (*o); + } + } + + return new_region.release (); +} + +bool +AsIfFlatRegion::is_box () const +{ + RegionIterator p (begin ()); + if (p.at_end ()) { + return false; + } else { + const db::Polygon &poly = *p; + ++p; + if (! p.at_end ()) { + return false; + } else { + return poly.is_box (); + } + } +} + +AsIfFlatRegion::area_type +AsIfFlatRegion::area (const db::Box &box) const +{ + area_type a = 0; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (box.empty () || p->box ().inside (box)) { + a += p->area (); + } else { + std::vector clipped; + clip_poly (*p, box, clipped); + for (std::vector::const_iterator c = clipped.begin (); c != clipped.end (); ++c) { + a += c->area (); + } + } + } + + return a; +} + +AsIfFlatRegion::perimeter_type +AsIfFlatRegion::perimeter (const db::Box &box) const +{ + perimeter_type d = 0; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + if (box.empty () || p->box ().inside (box)) { + d += p->perimeter (); + } else { + + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + + if (box.empty ()) { + d += (*e).length (); + } else { + + std::pair ce = (*e).clipped (box); + if (ce.first) { + + db::Coord dx = ce.second.dx (); + db::Coord dy = ce.second.dy (); + db::Coord x = ce.second.p1 ().x (); + db::Coord y = ce.second.p1 ().y (); + if ((dx == 0 && x == box.left () && dy < 0) || + (dx == 0 && x == box.right () && dy > 0) || + (dy == 0 && y == box.top () && dx < 0) || + (dy == 0 && y == box.bottom () && dx > 0)) { + // not counted -> box is at outside side of the edge + } else { + d += ce.second.length (); + } + + } + + } + + } + + } + + } + + return d; +} + +Box AsIfFlatRegion::bbox () const +{ + if (! m_bbox_valid) { + m_bbox = db::Box (); + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + m_bbox += p->box (); + } + m_bbox_valid = true; + } + + return m_bbox; +} + +void AsIfFlatRegion::update_bbox (const db::Box &b) +{ + m_bbox = b; + m_bbox_valid = true; +} + +RegionDelegate * +AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const +{ + std::auto_ptr new_region (new FlatRegion ()); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + new_region->insert (*p); + } + } + + return new_region.release (); +} + +namespace +{ + +/** + * @brief A helper class for the region to edge interaction functionality + * + * Note: This special scanner uses pointers to two different objects: edges and polygons. + * It uses odd value pointers to indicate pointers to polygons and even value pointers to indicate + * pointers to edges. + * + * There is a special box converter which is able to sort that out as well. + */ +template +class region_to_edge_interaction_filter + : public db::box_scanner_receiver +{ +public: + region_to_edge_interaction_filter (OutputContainer &output) + : mp_output (&output), m_inverse (false) + { + // .. nothing yet .. + } + + region_to_edge_interaction_filter (OutputContainer &output, const db::RegionIterator &polygons) + : mp_output (&output), m_inverse (true) + { + for (db::RegionIterator p = polygons; ! p.at_end (); ++p) { + m_seen.insert (&*p); + } + } + + void add (const char *o1, size_t p1, const char *o2, size_t p2) + { + const db::Edge *e = 0; + const db::Polygon *p = 0; + + // Note: edges have property 0 and have even-valued pointers. + // Polygons have property 1 and odd-valued pointers. + if (p1 == 0 && p2 == 1) { + e = reinterpret_cast (o1); + p = reinterpret_cast (o2 - 1); + } else if (p1 == 1 && p2 == 0) { + e = reinterpret_cast (o2); + p = reinterpret_cast (o1 - 1); + } + + if (e && p && (m_seen.find (p) == m_seen.end ()) != m_inverse) { + + // A polygon and an edge interact if the edge is either inside completely + // of at least one edge of the polygon intersects with the edge + bool interacts = false; + if (p->box ().contains (e->p1 ()) && db::inside_poly (p->begin_edge (), e->p1 ()) >= 0) { + interacts = true; + } else { + for (db::Polygon::polygon_edge_iterator pe = p->begin_edge (); ! pe.at_end () && ! interacts; ++pe) { + if ((*pe).intersect (*e)) { + interacts = true; + } + } + } + + if (interacts) { + if (m_inverse) { + m_seen.erase (p); + } else { + m_seen.insert (p); + mp_output->insert (*p); + } + } + + } + } + + void fill_output () + { + for (std::set::const_iterator p = m_seen.begin (); p != m_seen.end (); ++p) { + mp_output->insert (**p); + } + } + +private: + OutputContainer *mp_output; + std::set m_seen; + bool m_inverse; +}; + +/** + * @brief A special box converter that splits the pointers into polygon and edge pointers + */ +struct EdgeOrRegionBoxConverter +{ + typedef db::Box box_type; + + db::Box operator() (const char &c) const + { + // Note: edges have property 0 and have even-valued pointers. + // Polygons have property 1 and odd-valued pointers. + const char *cp = &c; + if ((size_t (cp) & 1) == 1) { + // it's a polygon + return (reinterpret_cast (cp - 1))->box (); + } else { + // it's an edge + const db::Edge *e = reinterpret_cast (cp); + return db::Box (e->p1 (), e->p2 ()); + } + } +}; + +} + +RegionDelegate * +AsIfFlatRegion::selected_interacting_generic (const Edges &other, bool inverse) const +{ + if (other.empty ()) { + if (! inverse) { + return new EmptyRegion (); + } else { + return clone (); + } + } else if (empty ()) { + return clone (); + } + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + other.size ()); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + scanner.insert ((char *) &*p + 1, 1); + } + + other.ensure_valid_merged_edges (); + for (Edges::const_iterator e = other.begin (); ! e.at_end (); ++e) { + scanner.insert ((char *) &*e, 0); + } + + std::auto_ptr output (new FlatRegion (false)); + EdgeOrRegionBoxConverter bc; + + if (! inverse) { + region_to_edge_interaction_filter filter (output->raw_polygons ()); + scanner.process (filter, 1, bc); + } else { + region_to_edge_interaction_filter filter (output->raw_polygons (), RegionIterator (begin_merged ())); + scanner.process (filter, 1, bc); + filter.fill_output (); + } + + return output.release (); +} + +RegionDelegate * +AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const +{ + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // shortcut + if (empty ()) { + return clone (); + } else if (other.empty ()) { + // clear, if b is empty and + // * mode is inside or interacting and inverse is false ("inside" or "interacting") + // * mode is outside and inverse is true ("not outside") + if ((mode <= 0) != inverse) { + return new EmptyRegion (); + } else { + return clone (); + } + } + + for (RegionIterator p = other.begin (); ! p.at_end (); ++p) { + if (p->box ().touches (bbox ())) { + ep.insert (*p, 0); + } + } + + size_t n = 1; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p, ++n) { + if (mode > 0 || p->box ().touches (other.bbox ())) { + ep.insert (*p, n); + } + } + + db::InteractionDetector id (mode, 0); + id.set_include_touching (touching); + db::EdgeSink es; + ep.process (es, id); + id.finish (); + + std::auto_ptr output (new FlatRegion (false)); + + n = 0; + std::set selected; + for (db::InteractionDetector::iterator i = id.begin (); i != id.end () && i->first == 0; ++i) { + ++n; + selected.insert (i->second); + } + + output->reserve (n); + + n = 1; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p, ++n) { + if ((selected.find (n) == selected.end ()) == inverse) { + output->raw_polygons ().insert (*p); + } + } + + return output.release (); +} + +EdgePairs +AsIfFlatRegion::grid_check (db::Coord gx, db::Coord gy) const +{ + EdgePairs out; + + gx = std::max (db::Coord (1), gx); + gy = std::max (db::Coord (1), gy); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + for (size_t i = 0; i < p->holes () + 1; ++i) { + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = p->begin_hull (); + e = p->end_hull (); + } else { + b = p->begin_hole ((unsigned int) (i - 1)); + e = p->end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { + if (((*pt).x () % gx) != 0 || ((*pt).y () % gy) != 0) { + out.insert (EdgePair (db::Edge (*pt, *pt), db::Edge (*pt, *pt))); + } + } + + } + + } + + return out; +} + +static bool ac_less (double cos_a, bool gt180_a, double cos_b, bool gt180_b) +{ + if (gt180_a != gt180_b) { + return gt180_a < gt180_b; + } else { + if (gt180_a) { + return cos_a < cos_b - 1e-10; + } else { + return cos_a > cos_b + 1e-10; + } + } +} + +EdgePairs +AsIfFlatRegion::angle_check (double min, double max, bool inverse) const +{ + EdgePairs out; + + double cos_min = cos (std::max (0.0, std::min (360.0, min)) / 180.0 * M_PI); + double cos_max = cos (std::max (0.0, std::min (360.0, max)) / 180.0 * M_PI); + bool gt180_min = min > 180.0; + bool gt180_max = max > 180.0; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + for (size_t i = 0; i < p->holes () + 1; ++i) { + + const db::Polygon::contour_type *h = 0; + if (i == 0) { + h = &p->hull (); + } else { + h = &p->hole ((unsigned int) (i - 1)); + } + + size_t np = h->size (); + + for (size_t j = 0; j < np; ++j) { + + db::Edge e ((*h) [j], (*h) [(j + 1) % np]); + db::Edge ee (e.p2 (), (*h) [(j + 2) % np]); + double le = e.double_length (); + double lee = ee.double_length (); + + double cos_a = -db::sprod (e, ee) / (le * lee); + bool gt180_a = db::vprod_sign (e, ee) > 0; + + if ((ac_less (cos_a, gt180_a, cos_max, gt180_max) && !ac_less (cos_a, gt180_a, cos_min, gt180_min)) == !inverse) { + out.insert (EdgePair (e, ee)); + } + + } + + } + + } + + return out; +} + +static inline db::Coord snap_to_grid (db::Coord c, db::Coord 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; +} + +RegionDelegate * +AsIfFlatRegion::snapped (db::Coord gx, db::Coord gy) +{ + std::auto_ptr new_region (new FlatRegion (merged_semantics ())); + + gx = std::max (db::Coord (1), gx); + gy = std::max (db::Coord (1), gy); + + std::vector pts; + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + + db::Polygon pnew; + + for (size_t i = 0; i < p->holes () + 1; ++i) { + + pts.clear (); + + db::Polygon::polygon_contour_iterator b, e; + + if (i == 0) { + b = p->begin_hull (); + e = p->end_hull (); + } else { + b = p->begin_hole ((unsigned int) (i - 1)); + e = p->end_hole ((unsigned int) (i - 1)); + } + + for (db::Polygon::polygon_contour_iterator pt = b; pt != e; ++pt) { + pts.push_back (db::Point (snap_to_grid ((*pt).x (), gx), snap_to_grid ((*pt).y (), gy))); + } + + if (i == 0) { + pnew.assign_hull (pts.begin (), pts.end ()); + } else { + pnew.insert_hole (pts.begin (), pts.end ()); + } + + } + + new_region->raw_polygons ().insert (pnew); + + } + + return new_region.release (); +} + +namespace +{ + /** + * @brief A helper class to implement the strange polygon detector + */ + struct StrangePolygonInsideFunc + { + inline bool operator() (int wc) const + { + return wc < 0 || wc > 1; + } + }; +} + +RegionDelegate * +AsIfFlatRegion::strange_polygon_check () const +{ + EdgeProcessor ep; + std::auto_ptr new_region (new FlatRegion (merged_semantics ())); + + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + + ep.clear (); + ep.insert (*p); + + StrangePolygonInsideFunc inside; + db::GenericMerge op (inside); + db::ShapeGenerator pc (new_region->raw_polygons (), false); + db::PolygonGenerator pg (pc, false, false); + ep.process (pg, op); + } + + return new_region.release (); +} + + +namespace { + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + */ +class Edge2EdgeCheck + : public db::box_scanner_receiver +{ +public: + Edge2EdgeCheck (const EdgeRelationFilter &check, EdgePairs &output, bool different_polygons, bool requires_different_layers) + : mp_check (&check), mp_output (&output), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons), + m_pass (0) + { + m_distance = check.distance (); + } + + bool prepare_next_pass () + { + ++m_pass; + + if (m_pass == 1) { + + if (! m_ep.empty ()) { + m_ep_discarded.resize (m_ep.size (), false); + return true; + } + + } else if (m_pass == 2) { + + std::vector::const_iterator d = m_ep_discarded.begin (); + std::vector::const_iterator ep = m_ep.begin (); + while (ep != m_ep.end ()) { + tl_assert (d != m_ep_discarded.end ()); + if (! *d) { + mp_output->insert (*ep); + } + ++d; + ++ep; + } + + } + + return false; + } + + void 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 ((! m_different_polygons || p1 != p2) && (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0)) { + + // 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)); + + db::EdgePair ep; + if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { + + // found a violation: store inside the local buffer for now. In the second + // pass we will eliminate those which are shielded completely. + size_t n = m_ep.size (); + m_ep.push_back (ep); + m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n)); + m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n)); + + } + + } + + } else { + + // 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) { + n1.push_back (i->second); + } + + 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) { + if (! m_ep_discarded [*i]) { + db::EdgePair ep = m_ep [*i].normalized (); + if (db::Edge (ep.first ().p1 (), ep.second ().p2 ()).intersect (*o2) && + db::Edge (ep.second ().p1 (), ep.first ().p2 ()).intersect (*o2)) { + m_ep_discarded [*i] = true; + } + } + } + + std::swap (o1, o2); + std::swap (p1, p2); + n1.swap (n2); + + } + + } + + } + + /** + * @brief Gets a value indicating whether the check requires different layers + */ + bool requires_different_layers () const + { + return m_requires_different_layers; + } + + /** + * @brief Sets a value indicating whether the check requires different layers + */ + void set_requires_different_layers (bool f) + { + m_requires_different_layers = f; + } + + /** + * @brief Gets a value indicating whether the check requires different layers + */ + bool different_polygons () const + { + return m_different_polygons; + } + + /** + * @brief Sets a value indicating whether the check requires different layers + */ + void set_different_polygons (bool f) + { + m_different_polygons = f; + } + + /** + * @brief Gets the distance value + */ + EdgeRelationFilter::distance_type distance () const + { + return m_distance; + } + +private: + const EdgeRelationFilter *mp_check; + EdgePairs *mp_output; + 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::vector m_ep_discarded; + unsigned int m_pass; +}; + +/** + * @brief A helper class for the DRC functionality which acts as an edge pair receiver + */ +class Poly2PolyCheck + : public db::box_scanner_receiver +{ +public: + Poly2PolyCheck (Edge2EdgeCheck &output) + : mp_output (&output) + { + // .. nothing yet .. + } + + void finish (const db::Polygon *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 (o->vertices ()); + + m_edges.clear (); + m_edges.reserve (o->vertices ()); + + for (db::Polygon::polygon_edge_iterator e = o->begin_edge (); ! e.at_end (); ++e) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p); + } + + tl_assert (m_edges.size () == o->vertices ()); + + m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); + + } + } + + void add (const db::Polygon *o1, size_t p1, const db::Polygon *o2, size_t p2) + { + if ((! mp_output->different_polygons () || p1 != p2) && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { + + m_scanner.clear (); + m_scanner.reserve (o1->vertices () + o2->vertices ()); + + m_edges.clear (); + m_edges.reserve (o1->vertices () + o2->vertices ()); + + for (db::Polygon::polygon_edge_iterator e = o1->begin_edge (); ! e.at_end (); ++e) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p1); + } + + for (db::Polygon::polygon_edge_iterator e = o2->begin_edge (); ! e.at_end (); ++e) { + m_edges.push_back (*e); + m_scanner.insert (& m_edges.back (), p2); + } + + tl_assert (m_edges.size () == o1->vertices () + o2->vertices ()); + + // 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); + + } + } + +private: + db::box_scanner m_scanner; + Edge2EdgeCheck *mp_output; + std::vector m_edges; +}; + +} + +EdgePairs +AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + EdgePairs result; + + db::box_scanner scanner (report_progress (), progress_desc ()); + scanner.reserve (size () + (other ? other->size () : 0)); + + size_t n = 0; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + scanner.insert (&*p, n); + n += 2; + } + + if (other) { + n = 1; + for (RegionIterator p (other->begin_merged ()); ! p.at_end (); ++p) { + scanner.insert (&*p, n); + n += 2; + } + } + + EdgeRelationFilter check (rel, d, metrics); + check.set_include_zero (other != 0); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + Edge2EdgeCheck edge_check (check, result, different_polygons, other != 0); + Poly2PolyCheck poly_check (edge_check); + + do { + scanner.process (poly_check, d, db::box_convert ()); + } while (edge_check.prepare_next_pass ()); + + return result; +} + +EdgePairs +AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const +{ + EdgePairs result; + + EdgeRelationFilter check (rel, d, metrics); + check.set_whole_edges (whole_edges); + check.set_ignore_angle (ignore_angle); + check.set_min_projection (min_projection); + check.set_max_projection (max_projection); + + Edge2EdgeCheck edge_check (check, result, false, false); + Poly2PolyCheck poly_check (edge_check); + + do { + size_t n = 0; + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + poly_check.finish (&*p, n); + n += 2; + } + } while (edge_check.prepare_next_pass ()); + + return result; +} + +RegionDelegate * +AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc) const +{ + if (empty ()) { + + return new EmptyRegion (); + + } else if (is_box ()) { + + // take box only if min_wc == 0, otherwise clear + if (min_wc > 0) { + return new EmptyRegion (); + } else { + return clone (); + } + + } else { + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + + // and run the merge step + db::MergeOp op (min_wc); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::region_from_box (const db::Box &b) +{ + if (! b.empty () && b.width () > 0 && b.height () > 0) { + FlatRegion *new_region = new FlatRegion (); + new_region->insert (b); + return new_region; + } else { + return new EmptyRegion (); + } +} + +RegionDelegate * +AsIfFlatRegion::sized (coord_type d, unsigned int mode) const +{ + return sized (d, d, mode); +} + +RegionDelegate * +AsIfFlatRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const +{ + if (empty ()) { + + // ignore empty + return new EmptyRegion (); + + } else if (is_box () && mode >= 2) { + + // simplified handling for a box + db::Box b = bbox ().enlarged (db::Vector (dx, dy)); + return region_from_box (b); + + } else if (! merged_semantics ()) { + + // Generic case + std::auto_ptr new_region (new FlatRegion (false /*output isn't merged*/)); + + db::ShapeGenerator pc (new_region->raw_polygons (), false); + db::PolygonGenerator pg (pc, false, true); + db::SizingPolygonFilter sf (pg, dx, dy, mode); + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + sf.put (*p); + } + + return new_region.release (); + + } else { + + // Generic case - the size operation will merge first + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (false /*output isn't merged*/)); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg2 (pc, false /*don't resolve holes*/, true /*min. coherence*/); + db::SizingPolygonFilter siz (pg2, dx, dy, mode); + db::PolygonGenerator pg (siz, false /*don't resolve holes*/, false /*min. coherence*/); + db::BooleanOp op (db::BooleanOp::Or); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::and_with (const Region &other) const +{ + if (empty () || other.empty ()) { + + // Nothing to do + return new EmptyRegion (); + + } else if (is_box () && other.is_box ()) { + + // Simplified handling for boxes + db::Box b = bbox (); + b &= other.bbox (); + return region_from_box (b); + + } else if (is_box () && ! other.strict_handling ()) { + + // map AND with box to clip .. + db::Box b = bbox (); + std::auto_ptr new_region (new FlatRegion ()); + + std::vector clipped; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + clipped.clear (); + clip_poly (*p, b, clipped); + new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); + } + + return new_region.release (); + + } else if (other.is_box () && ! strict_handling ()) { + + // map AND with box to clip .. + db::Box b = other.bbox (); + std::auto_ptr new_region (new FlatRegion ()); + + std::vector clipped; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + clipped.clear (); + clip_poly (*p, b, clipped); + new_region->raw_polygons ().insert (clipped.begin (), clipped.end ()); + } + + return new_region.release (); + + } else if (! bbox ().overlaps (other.bbox ())) { + + // Result will be nothing + return new EmptyRegion (); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::And); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::not_with (const Region &other) const +{ + if (empty ()) { + + // Nothing to do + return new EmptyRegion (); + + } else if (other.empty () && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::ANotB); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::xor_with (const Region &other) const +{ + if (empty () && ! other.strict_handling ()) { + + return other.delegate ()->clone (); + + } else if (other.empty () && ! strict_handling ()) { + + return clone (); + + } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) { + + // Simplified handling for disjunct case + return or_with (other); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::Xor); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::or_with (const Region &other) const +{ + if (empty () && ! other.strict_handling ()) { + + return other.delegate ()->clone (); + + } else if (other.empty () && ! strict_handling ()) { + + // Nothing to do + return clone (); + + } else if (! bbox ().overlaps (other.bbox ()) && ! strict_handling () && ! other.strict_handling ()) { + + // Simplified handling for disjunct case + return add (other); + + } else { + + // Generic case + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + n = 1; + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p, n += 2) { + ep.insert (*p, n); + } + + std::auto_ptr new_region (new FlatRegion (true)); + db::BooleanOp op (db::BooleanOp::Or); + db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + return new_region.release (); + + } +} + +RegionDelegate * +AsIfFlatRegion::add (const Region &other) const +{ + FlatRegion *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + std::auto_ptr new_region (new FlatRegion (*other_flat)); + + size_t n = new_region->raw_polygons ().size () + size (); + + new_region->reserve (n); + + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + + return new_region.release (); + + } else { + + std::auto_ptr new_region (new FlatRegion (false /*not merged*/)); + + size_t n = size () + other.size (); + + new_region->reserve (n); + + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + + return new_region.release (); + + } +} + +bool +AsIfFlatRegion::equals (const Region &other) const +{ + if (empty () != other.empty ()) { + return false; + } + if (size () != other.size ()) { + return false; + } + RegionIterator o1 (begin ()); + RegionIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return false; + } + ++o1; + ++o2; + } + return true; +} + +bool +AsIfFlatRegion::less (const Region &other) const +{ + if (empty () != other.empty ()) { + return empty () < other.empty (); + } + if (size () != other.size ()) { + return (size () < other.size ()); + } + RegionIterator o1 (begin ()); + RegionIterator o2 (other.begin ()); + while (! o1.at_end () && ! o2.at_end ()) { + if (*o1 != *o2) { + return *o1 < *o2; + } + ++o1; + ++o2; + } + return false; +} + +// ------------------------------------------------------------------------------------------------------------- +// FlatRegion implementation + +namespace +{ + + class FlatRegionIterator + : public RegionIteratorDelegate + { + public: + typedef db::layer polygon_layer_type; + typedef polygon_layer_type::iterator iterator_type; + + FlatRegionIterator (iterator_type from, iterator_type to) + : m_from (from), m_to (to) + { + // .. nothing yet .. + } + + virtual bool at_end () const + { + return m_from == m_to; + } + + virtual void increment () + { + ++m_from; + } + + virtual const value_type *get () const + { + return m_from.operator-> (); + } + + virtual RegionIteratorDelegate *clone () const + { + return new FlatRegionIterator (*this); + } + + private: + friend class Region; + + iterator_type m_from, m_to; + }; + +} + + +FlatRegion::FlatRegion () + : AsIfFlatRegion () +{ + init (); +} + +FlatRegion::~FlatRegion () +{ + // .. nothing yet .. +} + +FlatRegion::FlatRegion (const FlatRegion &other) + : AsIfFlatRegion (other), m_polygons (false), m_merged_polygons (false) +{ + init (); + + m_is_merged = other.m_is_merged; + m_polygons = other.m_polygons; + m_merged_polygons = other.m_merged_polygons; + m_merged_polygons_valid = other.m_merged_polygons_valid; +} + +FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) + : AsIfFlatRegion (), m_polygons (polygons), m_merged_polygons (false) +{ + init (); + + m_is_merged = is_merged; +} + +FlatRegion::FlatRegion (bool is_merged) + : AsIfFlatRegion (), m_polygons (false), m_merged_polygons (false) +{ + init (); + + m_is_merged = is_merged; +} + +void FlatRegion::invalidate_cache () +{ + invalidate_bbox (); + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + +void FlatRegion::init () +{ + m_is_merged = true; + m_merged_polygons_valid = false; +} + +void FlatRegion::merged_semantics_changed () +{ + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + +void FlatRegion::reserve (size_t n) +{ + m_polygons.reserve (db::Polygon::tag (), n); +} + +void +FlatRegion::ensure_merged_polygons_valid () const +{ + if (! m_merged_polygons_valid) { + + m_merged_polygons.clear (); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + // and run the merge step + db::MergeOp op (0); + db::ShapeGenerator pc (m_merged_polygons); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); + ep.process (pg, op); + + m_merged_polygons_valid = true; + + } +} + +RegionIteratorDelegate *FlatRegion::begin () const +{ + return new FlatRegionIterator (m_polygons.get_layer ().begin (), m_polygons.get_layer ().end ()); +} + +RegionIteratorDelegate *FlatRegion::begin_merged () const +{ + if (! merged_semantics () || m_is_merged) { + return begin (); + } else { + ensure_merged_polygons_valid (); + return new FlatRegionIterator (m_merged_polygons.get_layer ().begin (), m_merged_polygons.get_layer ().end ()); + } +} + +std::pair FlatRegion::begin_iter () const +{ + return std::make_pair (db::RecursiveShapeIterator (m_polygons), db::ICplxTrans ()); +} + +std::pair FlatRegion::begin_merged_iter () const +{ + if (! merged_semantics () || m_is_merged) { + return begin_iter (); + } else { + ensure_merged_polygons_valid (); + return std::make_pair (db::RecursiveShapeIterator (m_merged_polygons), db::ICplxTrans ()); + } +} + +bool FlatRegion::empty () const +{ + return m_polygons.empty (); +} + +size_t FlatRegion::size () const +{ + return m_polygons.size (); +} + +bool FlatRegion::is_merged () const +{ + return m_is_merged; +} + +void FlatRegion::set_box (const db::Box &b) +{ + m_polygons.clear (); + if (! b.empty () && b.width () > 0 && b.height () > 0 ) { + m_polygons.insert (db::Polygon (b)); + } + + m_is_merged = true; + update_bbox (b); + + m_merged_polygons.clear (); + m_merged_polygons_valid = false; +} + +RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter) +{ + polygon_iterator_type pw = m_polygons.get_layer ().begin (); + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + if (pw == m_polygons.get_layer ().end ()) { + m_polygons.get_layer ().insert (*p); + pw = m_polygons.get_layer ().end (); + } else { + m_polygons.get_layer ().replace (pw++, *p); + } + } + } + + m_polygons.get_layer ().erase (pw, m_polygons.get_layer ().end ()); + m_merged_polygons.clear (); + m_is_merged = merged_semantics (); + + return this; +} + +RegionDelegate *FlatRegion::merged_in_place () +{ + if (! m_is_merged) { + + if (m_merged_polygons_valid) { + + m_polygons.swap (m_merged_polygons); + m_merged_polygons.clear (); + m_is_merged = true; + return this; + + } else { + return merged_in_place (min_coherence (), 0); + } + + } else { + return this; + } +} + +RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int min_wc) +{ + if (empty ()) { + + // ignore empty + return new EmptyRegion (); + + } else if (is_box ()) { + + // take box only if min_wc == 0, otherwise clear + if (min_wc > 0) { + return new EmptyRegion (); + } + + } else { + + invalidate_cache (); + + db::EdgeProcessor ep (report_progress (), progress_desc ()); + + // count edges and reserve memory + size_t n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + // insert the polygons into the processor + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++n) { + ep.insert (*p, n); + } + + // and run the merge step + db::MergeOp op (min_wc); + db::ShapeGenerator pc (m_polygons, true /*clear*/); + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); + ep.process (pg, op); + + m_is_merged = true; + + } + + return this; +} + +RegionDelegate *FlatRegion::merged () const +{ + if (! m_is_merged) { + + if (m_merged_polygons_valid) { + return new FlatRegion (m_merged_polygons, true); + } else { + return AsIfFlatRegion::merged (min_coherence (), 0); + } + + } else { + return clone (); + } +} + +RegionDelegate *FlatRegion::add (const Region &other) const +{ + std::auto_ptr new_region (new FlatRegion (*this)); + + FlatRegion *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + new_region->raw_polygons ().insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); + + } else { + + size_t n = new_region->raw_polygons ().size (); + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + new_region->raw_polygons ().reserve (db::Polygon::tag (), n); + + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + new_region->raw_polygons ().insert (*p); + } + + } + + return new_region.release (); +} + +RegionDelegate *FlatRegion::add_in_place (const Region &other) +{ + invalidate_cache (); + + FlatRegion *other_flat = dynamic_cast (other.delegate ()); + if (other_flat) { + + m_polygons.insert (other_flat->raw_polygons ().get_layer ().begin (), other_flat->raw_polygons ().get_layer ().end ()); + + } else { + + size_t n = m_polygons.size (); + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + ++n; + } + + m_polygons.reserve (db::Polygon::tag (), n); + + for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { + m_polygons.insert (*p); + } + + } + + m_is_merged = false; + + return this; +} + +const db::Polygon *FlatRegion::nth (size_t n) const +{ + return n < m_polygons.size () ? &m_polygons.get_layer ().begin () [n] : 0; +} + +bool FlatRegion::has_valid_polygons () const +{ + return true; +} + +const db::RecursiveShapeIterator *FlatRegion::iter () const +{ + return 0; +} + +void +FlatRegion::insert (const db::Box &box) +{ + if (! box.empty () && box.width () > 0 && box.height () > 0) { + m_polygons.insert (db::Polygon (box)); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::Path &path) +{ + if (path.points () > 0) { + m_polygons.insert (path.polygon ()); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::Polygon &polygon) +{ + if (polygon.holes () > 0 || polygon.vertices () > 0) { + m_polygons.insert (polygon); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::SimplePolygon &polygon) +{ + if (polygon.vertices () > 0) { + db::Polygon poly; + poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); + m_polygons.insert (poly); + m_is_merged = false; + invalidate_cache (); + } +} + +void +FlatRegion::insert (const db::Shape &shape) +{ + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + db::Polygon poly; + shape.polygon (poly); + m_polygons.insert (poly); + m_is_merged = false; + invalidate_cache (); + } +} + +// ------------------------------------------------------------------------------------------------------------- + +#if 0 // @@@ ORIGINAL +/** + * @brief A region iterator + * + * The iterator delivers the polygons of the region + */ + +class DB_PUBLIC RegionIterator +{ +public: + typedef db::Polygon value_type; + typedef const db::Polygon &reference; + typedef const db::Polygon *pointer; + typedef std::forward_iterator_tag iterator_category; + typedef void difference_type; + + /** + * @Returns true, if the iterator is at the end + */ + bool at_end () const + { + return m_from == m_to && m_rec_iter.at_end (); + } + + /** + * @brief Increment + */ + RegionIterator &operator++ () + { + inc (); + set (); + return *this; + } + + /** + * @brief Access + */ + reference operator* () const + { + if (m_rec_iter.at_end ()) { + return *m_from; + } else { + return m_polygon; + } + } + + /** + * @brief Access + */ + pointer operator-> () const + { + if (m_rec_iter.at_end ()) { + return &*m_from; + } else { + return &m_polygon; + } + } + +private: + friend class Region; + + typedef db::layer polygon_layer_type; + typedef polygon_layer_type::iterator iterator_type; + + db::RecursiveShapeIterator m_rec_iter; + db::ICplxTrans m_iter_trans; + db::Polygon m_polygon; + iterator_type m_from, m_to; + + /** + * @brief ctor from a recursive shape iterator + */ + RegionIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) + : m_rec_iter (iter), m_iter_trans (trans), m_from (), m_to () + { + // NOTE: the following initialization appears to be required on some compilers + // (specifically MacOS/clang) to ensure the proper initialization of the iterators + m_from = m_to; + set (); + } + + /** + * @brief ctor from a range of polygons inside a vector + */ + RegionIterator (iterator_type from, iterator_type to) + : m_from (from), m_to (to) + { + // no required yet: set (); + } + + /** + * @brief Establish the iterator at the current position + */ + void set () + { + while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_polygon () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { + inc (); + } + if (! m_rec_iter.at_end ()) { + m_rec_iter.shape ().polygon (m_polygon); + m_polygon.transform (m_iter_trans * m_rec_iter.trans (), false); + } + } + + /** + * @brief Increment the iterator + */ + void inc () + { + if (! m_rec_iter.at_end ()) { + ++m_rec_iter; + } else { + ++m_from; + } + } +}; +#endif + +#if 0 // @@@ TODO +// .......................................................... + + + +/** + * @brief An original layerregion based on a RecursiveShapeIterator + */ +class DB_PUBLIC OriginalLayerRegion + : public RegionDelegate +{ +public: + OriginalLayerRegion () { } + virtual ~OriginalLayerRegion () { } + + RegionDelegate *clone () const; + + virtual void enable_progress (const std::string &progress_desc); + virtual void disable_progress (); + + virtual RegionIteratorDelegate *begin () const; + virtual RegionIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + virtual size_t size () const; + + virtual bool is_box () const; + virtual bool is_merged () const; + virtual area_type area (const db::Box &box = db::Box ()) const; + virtual perimeter_type perimeter (const db::Box &box = db::Box ()) const; + + virtual Box bbox () const; + + virtual EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs grid_check (db::Coord gx, db::Coord gy) const; + virtual EdgePairs angle_check (double min, double max, bool inverse) const; + virtual void snap (db::Coord gx, db::Coord gy); + virtual Region strange_polygon_check () const; + + virtual Edges edges () const; + + virtual void transform (const db::ICplxTrans &trans); + virtual void transform (const db::Trans &trans); + virtual void merge (); + virtual void merge (bool min_coherence, unsigned int min_wc); + + virtual RegionDelegate *sized (coord_type d, unsigned int mode); + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode); + + virtual RegionDelegate *and_with (const Region &other) const; + virtual RegionDelegate *not_with (const Region &other) const; + virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; + virtual RegionDelegate *add (const Region &other) const; + + virtual RegionDelegate *selected_outside (const Region &other) const; + virtual RegionDelegate *selected_not_outside (const Region &other) const; + virtual RegionDelegate *selected_inside (const Region &other) const; + virtual RegionDelegate *selected_not_inside (const Region &other) const; + virtual RegionDelegate *selected_interacting (const Region &other) const; + virtual RegionDelegate *selected_not_interacting (const Region &other) const; + virtual RegionDelegate *selected_interacting (const Edges &other) const; + virtual RegionDelegate *selected_not_interacting (const Edges &other) const; + virtual RegionDelegate *selected_overlapping (const Region &other) const; + virtual RegionDelegate *selected_not_overlapping (const Region &other) const; + + virtual RegionDelegate *holes () const; + virtual RegionDelegate *hulls () const; + virtual RegionDelegate *in (const Region &other, bool invert) const; + virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const; + virtual RegionDelegate *smoothed (coord_type d) const; + + virtual const db::Polygon *nth (size_t n) const; + virtual bool has_valid_polygons () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; +}; + +#endif + +// ------------------------------------------------------------------------------------------------------------- +// Region implementation + +Region::Region () + : mp_delegate (new EmptyRegion ()) +{ + // .. nothing yet .. +} + +Region::Region (RegionDelegate *delegate) + : mp_delegate (delegate) +{ + // .. nothing yet .. +} + +Region::Region (const Region &other) + : mp_delegate (other.mp_delegate->clone ()) +{ + // .. nothing yet .. +} + +Region::~Region () +{ + delete mp_delegate; + mp_delegate = 0; +} + +Region &Region::operator= (const Region &other) +{ + if (this != &other) { + set_delegate (other.mp_delegate->clone ()); + } + return *this; +} + +Region::Region (const RecursiveShapeIterator &si) +{ + mp_delegate = new OriginalLayerRegion (si); +} + +Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics) +{ + mp_delegate = new OriginalLayerRegion (si, trans, merged_semantics); +} + +const db::RecursiveShapeIterator & +Region::iter () const +{ + static db::RecursiveShapeIterator def_iter; + const db::RecursiveShapeIterator *i = mp_delegate->iter (); + return *(i ? i : &def_iter); +} + +// ------------------------------------------------------------------------------------------------------------- + +#if 0 +@@@@ original + Region::Region (const RecursiveShapeIterator &si) : m_polygons (false), m_merged_polygons (false), m_iter (si) { @@ -155,7 +2356,7 @@ Region::to_string (size_t nmax) const return os.str (); } -Region::area_type +Region::area_type Region::area (const db::Box &box) const { area_type a = 0; @@ -1066,7 +3267,6 @@ Region::selected_interacting_generic (const Edges &other, bool inverse) const db::box_scanner scanner (m_report_progress, m_progress_desc); scanner.reserve (size () + other.size ()); - ensure_valid_polygons (); for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { scanner.insert ((char *) &*p + 1, 1); } @@ -1107,7 +3307,6 @@ Region::select_interacting_generic (const Edges &other, bool inverse) db::box_scanner scanner (m_report_progress, m_progress_desc); scanner.reserve (size () + other.size ()); - ensure_valid_polygons (); for (const_iterator p = begin_merged (); ! p.at_end (); ++p) { scanner.insert ((char *) &*p + 1, 1); } @@ -1546,260 +3745,6 @@ Region::clear () m_iter_trans = db::ICplxTrans (); } -namespace { - -/** - * @brief A helper class for the DRC functionality which acts as an edge pair receiver - */ -class Edge2EdgeCheck - : public db::box_scanner_receiver -{ -public: - Edge2EdgeCheck (const EdgeRelationFilter &check, EdgePairs &output, bool different_polygons, bool requires_different_layers) - : mp_check (&check), mp_output (&output), m_requires_different_layers (requires_different_layers), m_different_polygons (different_polygons), - m_pass (0) - { - m_distance = check.distance (); - } - - bool prepare_next_pass () - { - ++m_pass; - - if (m_pass == 1) { - - if (! m_ep.empty ()) { - m_ep_discarded.resize (m_ep.size (), false); - return true; - } - - } else if (m_pass == 2) { - - std::vector::const_iterator d = m_ep_discarded.begin (); - std::vector::const_iterator ep = m_ep.begin (); - while (ep != m_ep.end ()) { - tl_assert (d != m_ep_discarded.end ()); - if (! *d) { - mp_output->insert (*ep); - } - ++d; - ++ep; - } - - } - - return false; - } - - void 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 ((! m_different_polygons || p1 != p2) && (! m_requires_different_layers || ((p1 ^ p2) & 1) != 0)) { - - // 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)); - - db::EdgePair ep; - if (mp_check->check (l1 <= l2 ? *o1 : *o2, l1 <= l2 ? *o2 : *o1, &ep)) { - - // found a violation: store inside the local buffer for now. In the second - // pass we will eliminate those which are shielded completely. - size_t n = m_ep.size (); - m_ep.push_back (ep); - m_e2ep.insert (std::make_pair (std::make_pair (*o1, p1), n)); - m_e2ep.insert (std::make_pair (std::make_pair (*o2, p2), n)); - - } - - } - - } else { - - // 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) { - n1.push_back (i->second); - } - - 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) { - if (! m_ep_discarded [*i]) { - db::EdgePair ep = m_ep [*i].normalized (); - if (db::Edge (ep.first ().p1 (), ep.second ().p2 ()).intersect (*o2) && - db::Edge (ep.second ().p1 (), ep.first ().p2 ()).intersect (*o2)) { - m_ep_discarded [*i] = true; - } - } - } - - std::swap (o1, o2); - std::swap (p1, p2); - n1.swap (n2); - - } - - } - - } - - /** - * @brief Gets a value indicating whether the check requires different layers - */ - bool requires_different_layers () const - { - return m_requires_different_layers; - } - - /** - * @brief Sets a value indicating whether the check requires different layers - */ - void set_requires_different_layers (bool f) - { - m_requires_different_layers = f; - } - - /** - * @brief Gets a value indicating whether the check requires different layers - */ - bool different_polygons () const - { - return m_different_polygons; - } - - /** - * @brief Sets a value indicating whether the check requires different layers - */ - void set_different_polygons (bool f) - { - m_different_polygons = f; - } - - /** - * @brief Gets the distance value - */ - EdgeRelationFilter::distance_type distance () const - { - return m_distance; - } - -private: - const EdgeRelationFilter *mp_check; - EdgePairs *mp_output; - 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::vector m_ep_discarded; - unsigned int m_pass; -}; - -/** - * @brief A helper class for the DRC functionality which acts as an edge pair receiver - */ -class Poly2PolyCheck - : public db::box_scanner_receiver -{ -public: - Poly2PolyCheck (Edge2EdgeCheck &output) - : mp_output (&output) - { - // .. nothing yet .. - } - - void finish (const db::Polygon *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 (o->vertices ()); - - m_edges.clear (); - m_edges.reserve (o->vertices ()); - - for (db::Polygon::polygon_edge_iterator e = o->begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p); - } - - tl_assert (m_edges.size () == o->vertices ()); - - m_scanner.process (*mp_output, mp_output->distance (), db::box_convert ()); - - } - } - - void add (const db::Polygon *o1, size_t p1, const db::Polygon *o2, size_t p2) - { - if ((! mp_output->different_polygons () || p1 != p2) && (! mp_output->requires_different_layers () || ((p1 ^ p2) & 1) != 0)) { - - m_scanner.clear (); - m_scanner.reserve (o1->vertices () + o2->vertices ()); - - m_edges.clear (); - m_edges.reserve (o1->vertices () + o2->vertices ()); - - for (db::Polygon::polygon_edge_iterator e = o1->begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p1); - } - - for (db::Polygon::polygon_edge_iterator e = o2->begin_edge (); ! e.at_end (); ++e) { - m_edges.push_back (*e); - m_scanner.insert (& m_edges.back (), p2); - } - - tl_assert (m_edges.size () == o1->vertices () + o2->vertices ()); - - // 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); - - } - } - -private: - db::box_scanner m_scanner; - Edge2EdgeCheck *mp_output; - std::vector m_edges; -}; - -} - EdgePairs Region::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const { @@ -1866,6 +3811,9 @@ Region::run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool return result; } +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@q +#endif + } namespace tl diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 80e4b08de..9346cb7aa 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -43,6 +43,20 @@ namespace db { +class EdgeFilterBase; + +/** + * @brief A base class for polygon filters + */ +class DB_PUBLIC PolygonFilterBase +{ +public: + PolygonFilterBase () { } + virtual ~PolygonFilterBase () { } + + virtual bool selected (const db::Polygon &polgon) const = 0; +}; + /** * @brief A perimeter filter for use with Region::filter or Region::filtered * @@ -53,6 +67,7 @@ namespace db { */ struct DB_PUBLIC RegionPerimeterFilter + : public PolygonFilterBase { typedef db::coord_traits::perimeter_type perimeter_type; @@ -72,7 +87,7 @@ struct DB_PUBLIC RegionPerimeterFilter /** * @brief Returns true if the polygon's perimeter matches the criterion */ - bool operator() (const db::Polygon &poly) const + virtual bool selected (const db::Polygon &poly) const { perimeter_type p = 0; for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end () && p < m_pmax; ++e) { @@ -101,6 +116,7 @@ private: */ struct DB_PUBLIC RegionAreaFilter + : public PolygonFilterBase { typedef db::Polygon::area_type area_type; @@ -120,7 +136,7 @@ struct DB_PUBLIC RegionAreaFilter /** * @brief Returns true if the polygon's area matches the criterion */ - bool operator() (const db::Polygon &poly) const + virtual bool selected (const db::Polygon &poly) const { area_type a = poly.area (); if (! m_inverse) { @@ -142,6 +158,7 @@ private: */ struct DB_PUBLIC RectilinearFilter + : public PolygonFilterBase { /** * @brief Constructor @@ -156,7 +173,7 @@ struct DB_PUBLIC RectilinearFilter /** * @brief Returns true if the polygon's area matches the criterion */ - bool operator() (const db::Polygon &poly) const + virtual bool selected (const db::Polygon &poly) const { return poly.is_rectilinear () != m_inverse; } @@ -172,6 +189,7 @@ private: */ struct DB_PUBLIC RectangleFilter + : public PolygonFilterBase { /** * @brief Constructor @@ -186,7 +204,7 @@ struct DB_PUBLIC RectangleFilter /** * @brief Returns true if the polygon's area matches the criterion */ - bool operator() (const db::Polygon &poly) const + virtual bool selected (const db::Polygon &poly) const { return poly.is_box () != m_inverse; } @@ -212,6 +230,7 @@ private: */ struct DB_PUBLIC RegionBBoxFilter + : public PolygonFilterBase { typedef db::Box::distance_type value_type; @@ -242,7 +261,7 @@ struct DB_PUBLIC RegionBBoxFilter /** * @brief Returns true if the polygon's area matches the criterion */ - bool operator() (const db::Polygon &poly) const + virtual bool selected (const db::Polygon &poly) const { value_type v = 0; db::Box box = poly.box (); @@ -270,36 +289,102 @@ private: parameter_type m_parameter; }; +/** + * @brief The region iterator delegate + */ +class DB_PUBLIC RegionIteratorDelegate +{ +public: + RegionIteratorDelegate () { } + virtual ~RegionIteratorDelegate () { } + + typedef db::Polygon value_type; + + virtual bool at_end () const = 0; + virtual void increment () = 0; + virtual const value_type *get () const = 0; + virtual RegionIteratorDelegate *clone () const = 0; +}; + /** * @brief A region iterator * * The iterator delivers the polygons of the region */ - class DB_PUBLIC RegionIterator { public: - typedef db::Polygon value_type; - typedef const db::Polygon &reference; - typedef const db::Polygon *pointer; + typedef RegionIteratorDelegate::value_type value_type; + typedef const value_type &reference; + typedef const value_type *pointer; typedef std::forward_iterator_tag iterator_category; typedef void difference_type; + /** + * @brief Default constructor + */ + RegionIterator () + : mp_delegate (0) + { + // .. nothing yet .. + } + + /** + * @brief Constructor from a delegate + * The iterator will take ownership over the delegate + */ + RegionIterator (RegionIteratorDelegate *delegate) + : mp_delegate (delegate) + { + // .. nothing yet .. + } + + /** + * @brief Destructor + */ + ~RegionIterator () + { + delete mp_delegate; + mp_delegate = 0; + } + + /** + * @brief Copy constructor and assignment + */ + RegionIterator (const RegionIterator &other) + : mp_delegate (0) + { + operator= (other); + } + + /** + * @brief Assignment + */ + RegionIterator &operator= (const RegionIterator &other) + { + if (this != &other) { + delete mp_delegate; + mp_delegate = other.mp_delegate ? other.mp_delegate->clone () : 0; + } + return *this; + } + /** * @Returns true, if the iterator is at the end */ bool at_end () const { - return m_from == m_to && m_rec_iter.at_end (); + return mp_delegate == 0 || mp_delegate->at_end (); } /** * @brief Increment */ - RegionIterator &operator++ () + RegionIterator &operator++ () { - inc (); - set (); + if (mp_delegate) { + mp_delegate->increment (); + } return *this; } @@ -308,11 +393,9 @@ public: */ reference operator* () const { - if (m_rec_iter.at_end ()) { - return *m_from; - } else { - return m_polygon; - } + const value_type *value = operator-> (); + tl_assert (value != 0); + return *value; } /** @@ -320,72 +403,1851 @@ public: */ pointer operator-> () const { - if (m_rec_iter.at_end ()) { - return &*m_from; - } else { - return &m_polygon; - } + return mp_delegate ? mp_delegate->get () : 0; } private: - friend class Region; + RegionIteratorDelegate *mp_delegate; +}; +/** + * @brief The delegate for the actual region implementation + */ +class DB_PUBLIC RegionDelegate +{ +public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; + typedef db::Polygon polygon_type; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + typedef coord_traits::distance_type distance_type; + typedef coord_traits::perimeter_type perimeter_type; + typedef coord_traits::area_type area_type; + + RegionDelegate (); + virtual ~RegionDelegate (); + + virtual RegionDelegate *clone () const = 0; + + void enable_progress (const std::string &progress_desc); + void disable_progress (); + + void set_min_coherence (bool f); + bool min_coherence () const + { + return m_merge_min_coherence; + } + + void set_merged_semantics (bool f); + bool merged_semantics () const + { + return m_merged_semantics; + } + + void set_strict_handling (bool f); + bool strict_handling () const + { + return m_strict_handling; + } + + virtual std::string to_string (size_t nmax) const = 0; + + virtual RegionIteratorDelegate *begin () const = 0; + virtual RegionIteratorDelegate *begin_merged () const = 0; + + virtual std::pair begin_iter () const = 0; + virtual std::pair begin_merged_iter () const = 0; + + virtual bool empty () const = 0; + virtual bool is_box () const = 0; + virtual bool is_merged () const = 0; + virtual size_t size () const = 0; + + virtual area_type area (const db::Box &box) const = 0; + virtual perimeter_type perimeter (const db::Box &box) const = 0; + virtual Box bbox () const = 0; + + virtual EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const = 0; + virtual EdgePairs grid_check (db::Coord gx, db::Coord gy) const = 0; + virtual EdgePairs angle_check (double min, double max, bool inverse) const = 0; + + virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) = 0; + virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy) = 0; + + virtual Edges edges (const EdgeFilterBase *filter) const = 0; + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) = 0; + virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const = 0; + + virtual RegionDelegate *merged_in_place () = 0; + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc) = 0; + virtual RegionDelegate *merged () const = 0; + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const = 0; + + virtual RegionDelegate *strange_polygon_check () const = 0; + + virtual RegionDelegate *sized (coord_type d, unsigned int mode) const = 0; + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const = 0; + + virtual RegionDelegate *and_with (const Region &other) const = 0; + virtual RegionDelegate *not_with (const Region &other) const = 0; + virtual RegionDelegate *xor_with (const Region &other) const = 0; + virtual RegionDelegate *or_with (const Region &other) const = 0; + virtual RegionDelegate *add_in_place (const Region &other) = 0; + virtual RegionDelegate *add (const Region &other) const = 0; + + virtual RegionDelegate *selected_outside (const Region &other) const = 0; + virtual RegionDelegate *selected_not_outside (const Region &other) const = 0; + virtual RegionDelegate *selected_inside (const Region &other) const = 0; + virtual RegionDelegate *selected_not_inside (const Region &other) const = 0; + virtual RegionDelegate *selected_interacting (const Region &other) const = 0; + virtual RegionDelegate *selected_not_interacting (const Region &other) const = 0; + virtual RegionDelegate *selected_interacting (const Edges &other) const = 0; + virtual RegionDelegate *selected_not_interacting (const Edges &other) const = 0; + virtual RegionDelegate *selected_overlapping (const Region &other) const = 0; + virtual RegionDelegate *selected_not_overlapping (const Region &other) const = 0; + + virtual RegionDelegate *holes () const = 0; + virtual RegionDelegate *hulls () const = 0; + virtual RegionDelegate *in (const Region &other, bool invert) const = 0; + virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const = 0; + virtual RegionDelegate *smoothed (coord_type d) const = 0; + + virtual const db::Polygon *nth (size_t n) const = 0; + virtual bool has_valid_polygons () const = 0; + + virtual const db::RecursiveShapeIterator *iter () const = 0; + + virtual bool equals (const Region &other) const = 0; + virtual bool less (const Region &other) const = 0; + +protected: + const std::string &progress_desc () const + { + return m_progress_desc; + } + + bool report_progress () const + { + return m_report_progress; + } + + virtual void merged_semantics_changed () { } + +private: + bool m_merged_semantics; + bool m_strict_handling; + bool m_merge_min_coherence; + bool m_report_progress; + std::string m_progress_desc; +}; + +/** + * @brief An empty Region + */ +class DB_PUBLIC EmptyRegion + : public RegionDelegate +{ +public: + EmptyRegion (); + virtual ~EmptyRegion (); + + EmptyRegion (const EmptyRegion &other); + RegionDelegate *clone () const; + + virtual RegionIteratorDelegate *begin () const { return 0; } + virtual RegionIteratorDelegate *begin_merged () const { return 0; } + + virtual std::pair begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + virtual std::pair begin_merged_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); } + + virtual bool empty () const { return true; } + virtual size_t size () const { return 0; } + virtual std::string to_string (size_t) const { return std::string (); } + + virtual bool is_box () const { return false; } + virtual bool is_merged () const { return true; } + virtual area_type area (const db::Box &) const { return 0; } + virtual perimeter_type perimeter (const db::Box &) const { return 0; } + + virtual Box bbox () const { return Box (); } + + virtual EdgePairs width_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs space_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs isolated_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs notch_check (db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + virtual EdgePairs separation_check (const Region &, db::Coord, bool , metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs inside_check (const Region &, db::Coord, bool, metrics_type, double, distance_type, distance_type) const { return EdgePairs (); } + virtual EdgePairs grid_check (db::Coord, db::Coord) const { return EdgePairs (); } + virtual EdgePairs angle_check (double, double, bool) const { return EdgePairs (); } + + virtual RegionDelegate *snapped_in_place (db::Coord, db::Coord) { return this; } + virtual RegionDelegate *snapped (db::Coord, db::Coord) { return new EmptyRegion (); } + + virtual RegionDelegate *strange_polygon_check () const { return new EmptyRegion (); } + + virtual Edges edges (const EdgeFilterBase *) const { return db::Edges (); } + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &) { return this; } + virtual RegionDelegate *filtered (const PolygonFilterBase &) const { return new EmptyRegion (); } + + virtual RegionDelegate *merged_in_place () { return this; } + virtual RegionDelegate *merged_in_place (bool, unsigned int) { return this; } + virtual RegionDelegate *merged () const { return new EmptyRegion (); } + virtual RegionDelegate *merged (bool, unsigned int) const { return new EmptyRegion (); } + + virtual RegionDelegate *sized (coord_type, unsigned int) const { return new EmptyRegion (); } + virtual RegionDelegate *sized (coord_type, coord_type, unsigned int) const { return new EmptyRegion (); } + + virtual RegionDelegate *and_with (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *not_with (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; + virtual RegionDelegate *add_in_place (const Region &other); + virtual RegionDelegate *add (const Region &other) const; + + virtual RegionDelegate *selected_outside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_outside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_inside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_inside (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_interacting (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_interacting (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_interacting (const Edges &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_interacting (const Edges &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_overlapping (const Region &) const { return new EmptyRegion (); } + virtual RegionDelegate *selected_not_overlapping (const Region &) const { return new EmptyRegion (); } + + virtual RegionDelegate *holes () const { return new EmptyRegion (); } + virtual RegionDelegate *hulls () const { return new EmptyRegion (); } + virtual RegionDelegate *in (const Region &, bool) const { return new EmptyRegion (); } + virtual RegionDelegate *rounded_corners (double, double, unsigned int) const { return new EmptyRegion (); } + virtual RegionDelegate *smoothed (coord_type) const { return new EmptyRegion (); } + + virtual bool has_valid_polygons () const { return false; } + virtual const db::Polygon *nth (size_t) const { tl_assert (false); } + + virtual const db::RecursiveShapeIterator *iter () const { return 0; } + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; + +private: + EmptyRegion &operator= (const EmptyRegion &other); +}; + +/** + * @brief Provides default flat implementations + */ +class DB_PUBLIC AsIfFlatRegion + : public RegionDelegate +{ +public: + AsIfFlatRegion (); + virtual ~AsIfFlatRegion (); + + virtual bool is_box () const; + + virtual area_type area (const db::Box &box) const; + virtual perimeter_type perimeter (const db::Box &box) const; + virtual Box bbox () const; + + virtual std::string to_string (size_t nmax) const; + + EdgePairs width_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_single_polygon_check (db::WidthRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs space_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, false, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs isolated_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, true, 0, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs notch_check (db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_single_polygon_check (db::SpaceRelation, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::OverlapRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::WidthRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::SpaceRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const + { + return run_check (db::InsideRelation, true, &other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + virtual EdgePairs grid_check (db::Coord gx, db::Coord gy) const; + virtual EdgePairs angle_check (double min, double max, bool inverse) const; + + virtual RegionDelegate *snapped_in_place (db::Coord gx, db::Coord gy) + { + return snapped (gx, gy); + } + + virtual RegionDelegate *snapped (db::Coord gx, db::Coord gy); + + virtual Edges edges (const EdgeFilterBase *) const; + + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) + { + return filtered (filter); + } + + virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const; + + virtual RegionDelegate *merged_in_place () + { + return merged (); + } + + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc) + { + return merged (min_coherence, min_wc); + } + + virtual RegionDelegate *merged () const + { + return merged (min_coherence (), 0); + } + + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const; + + virtual RegionDelegate *strange_polygon_check () const; + + virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; + + virtual RegionDelegate *and_with (const Region &other) const; + virtual RegionDelegate *not_with (const Region &other) const; + virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; + + virtual RegionDelegate *add_in_place (const Region &other) + { + return add (other); + } + + virtual RegionDelegate *add (const Region &other) const; + + virtual RegionDelegate *selected_outside (const Region &other) const + { + return selected_interacting_generic (other, 1, false, false); + } + + virtual RegionDelegate *selected_not_outside (const Region &other) const + { + return selected_interacting_generic (other, 1, false, true); + } + + virtual RegionDelegate *selected_inside (const Region &other) const + { + return selected_interacting_generic (other, -1, true, false); + } + + virtual RegionDelegate *selected_not_inside (const Region &other) const + { + return selected_interacting_generic (other, -1, true, true); + } + + virtual RegionDelegate *selected_interacting (const Region &other) const + { + return selected_interacting_generic (other, 0, true, false); + } + + virtual RegionDelegate *selected_not_interacting (const Region &other) const + { + return selected_interacting_generic (other, 0, true, true); + } + + virtual RegionDelegate *selected_interacting (const Edges &other) const + { + return selected_interacting_generic (other, false); + } + + virtual RegionDelegate *selected_not_interacting (const Edges &other) const + { + return selected_interacting_generic (other, true); + } + + virtual RegionDelegate *selected_overlapping (const Region &other) const + { + return selected_interacting_generic (other, 0, false, false); + } + + virtual RegionDelegate *selected_not_overlapping (const Region &other) const + { + return selected_interacting_generic (other, 0, false, true); + } + + virtual RegionDelegate *holes () const; + virtual RegionDelegate *hulls () const; + virtual RegionDelegate *in (const Region &other, bool invert) const; + virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const; + virtual RegionDelegate *smoothed (coord_type d) const; + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; + +protected: + void update_bbox (const db::Box &box); + void invalidate_bbox (); + +private: + AsIfFlatRegion &operator= (const AsIfFlatRegion &other); + + mutable bool m_bbox_valid; + mutable db::Box m_bbox; + + void ensure_bbox_valid (); + static RegionDelegate *region_from_box (const db::Box &b); + + EdgePairs run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + EdgePairs run_single_polygon_check (db::edge_relation_type rel, db::Coord d, bool whole_edges, metrics_type metrics, double ignore_angle, distance_type min_projection, distance_type max_projection) const; + RegionDelegate *selected_interacting_generic (const Region &other, int mode, bool touching, bool inverse) const; + RegionDelegate *selected_interacting_generic (const Edges &other, bool inverse) const; +}; + +/** + * @brief A flat, polygon-set delegate + */ +class DB_PUBLIC FlatRegion + : public AsIfFlatRegion +{ +public: typedef db::layer polygon_layer_type; - typedef polygon_layer_type::iterator iterator_type; + typedef polygon_layer_type::iterator polygon_iterator_type; - db::RecursiveShapeIterator m_rec_iter; - db::ICplxTrans m_iter_trans; - db::Polygon m_polygon; - iterator_type m_from, m_to; + FlatRegion (); + FlatRegion (const db::Shapes &polygons, bool is_merged); + FlatRegion (bool is_merged); - /** - * @brief ctor from a recursive shape iterator - */ - RegionIterator (const db::RecursiveShapeIterator &iter, const db::ICplxTrans &trans) - : m_rec_iter (iter), m_iter_trans (trans), m_from (), m_to () - { - // NOTE: the following initialization appears to be required on some compilers - // (specifically MacOS/clang) to ensure the proper initialization of the iterators - m_from = m_to; - set (); - } + FlatRegion (const FlatRegion &other); - /** - * @brief ctor from a range of polygons inside a vector - */ - RegionIterator (iterator_type from, iterator_type to) - : m_from (from), m_to (to) - { - // no required yet: set (); - } + virtual ~FlatRegion (); - /** - * @brief Establish the iterator at the current position - */ - void set () + RegionDelegate *clone () const { - while (! m_rec_iter.at_end () && ! (m_rec_iter.shape ().is_polygon () || m_rec_iter.shape ().is_path () || m_rec_iter.shape ().is_box ())) { - inc (); + return new FlatRegion (*this); + } + + void reserve (size_t); + + virtual RegionIteratorDelegate *begin () const; + virtual RegionIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + virtual size_t size () const; + virtual bool is_merged () const; + + virtual Box bbox () const; + + virtual RegionDelegate *merged_in_place (); + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); + virtual RegionDelegate *merged () const; + + virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter); + + virtual RegionDelegate *add_in_place (const Region &other); + virtual RegionDelegate *add (const Region &other) const; + + virtual const db::Polygon *nth (size_t n) const; + virtual bool has_valid_polygons () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + void insert (const db::Box &box); + void insert (const db::Path &path); + void insert (const db::SimplePolygon &polygon); + void insert (const db::Polygon &polygon); + void insert (const db::Shape &shape); + + template + void insert (const db::Shape &shape, const T &trans) + { + if (shape.is_polygon () || shape.is_path () || shape.is_box ()) { + db::Polygon poly; + shape.polygon (poly); + poly.transform (trans); + insert (poly); } - if (! m_rec_iter.at_end ()) { - m_rec_iter.shape ().polygon (m_polygon); - m_polygon.transform (m_iter_trans * m_rec_iter.trans (), false); - } + } + + template + void insert (const Iter &b, const Iter &e) + { + reserve (size () + (e - b)); + for (Iter i = b; i != e; ++i) { + insert (*i); + } + } + + template + void insert_seq (const Iter &seq) + { + for (Iter i = seq; ! i.at_end (); ++i) { + insert (*i); + } + } + + template + void transform (const Trans &trans) + { + if (! trans.is_unity ()) { + for (polygon_iterator_type p = m_polygons.get_layer ().begin (); p != m_polygons.get_layer ().end (); ++p) { + m_polygons.get_layer ().replace (p, p->transformed (trans)); + } + invalidate_cache (); + } + } + + db::Shapes &raw_polygons (); + +protected: + virtual void merged_semantics_changed (); + void set_box (const db::Box &box); + void invalidate_cache (); + +private: + FlatRegion &operator= (const FlatRegion &other); + + bool m_is_merged; + mutable db::Shapes m_polygons; + mutable db::Shapes m_merged_polygons; + mutable bool m_merged_polygons_valid; + + void init (); + void ensure_merged_polygons_valid () const; +}; + +/** + * @brief An original layerregion based on a RecursiveShapeIterator + */ +class DB_PUBLIC OriginalLayerRegion + : public AsIfFlatRegion +{ +public: + OriginalLayerRegion (); + OriginalLayerRegion (const RecursiveShapeIterator &si); + OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics); + virtual ~OriginalLayerRegion (); + + RegionDelegate *clone () const; + + virtual void enable_progress (const std::string &progress_desc); + virtual void disable_progress (); + + virtual RegionIteratorDelegate *begin () const; + virtual RegionIteratorDelegate *begin_merged () const; + + virtual std::pair begin_iter () const; + virtual std::pair begin_merged_iter () const; + + virtual bool empty () const; + virtual size_t size () const; + + virtual bool is_box () const; + virtual bool is_merged () const; + virtual area_type area (const db::Box &box = db::Box ()) const; + virtual perimeter_type perimeter (const db::Box &box = db::Box ()) const; + + virtual Box bbox () const; + + virtual RegionDelegate *merged_in_place (); + virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); + virtual RegionDelegate *merged () const; + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc) const; + + virtual RegionDelegate *sized (coord_type d, unsigned int mode) const; + virtual RegionDelegate *sized (coord_type dx, coord_type dy, unsigned int mode) const; + + virtual RegionDelegate *and_with (const Region &other) const; + virtual RegionDelegate *not_with (const Region &other) const; + virtual RegionDelegate *xor_with (const Region &other) const; + virtual RegionDelegate *or_with (const Region &other) const; + virtual RegionDelegate *add (const Region &other) const; + + virtual RegionDelegate *selected_outside (const Region &other) const; + virtual RegionDelegate *selected_not_outside (const Region &other) const; + virtual RegionDelegate *selected_inside (const Region &other) const; + virtual RegionDelegate *selected_not_inside (const Region &other) const; + virtual RegionDelegate *selected_interacting (const Region &other) const; + virtual RegionDelegate *selected_not_interacting (const Region &other) const; + virtual RegionDelegate *selected_interacting (const Edges &other) const; + virtual RegionDelegate *selected_not_interacting (const Edges &other) const; + virtual RegionDelegate *selected_overlapping (const Region &other) const; + virtual RegionDelegate *selected_not_overlapping (const Region &other) const; + + virtual RegionDelegate *holes () const; + virtual RegionDelegate *hulls () const; + virtual RegionDelegate *in (const Region &other, bool invert) const; + virtual RegionDelegate *rounded_corners (double rinner, double router, unsigned int n) const; + virtual RegionDelegate *smoothed (coord_type d) const; + + virtual const db::Polygon *nth (size_t n) const; + virtual bool has_valid_polygons () const; + + virtual const db::RecursiveShapeIterator *iter () const; + + virtual bool equals (const Region &other) const; + virtual bool less (const Region &other) const; +}; + +/** + * @brief A region + * + * A region basically is a set of polygons. It supports a variety of operations, i.e. + * boolean operations with other regions, sizing etc. + * + * Regions can have different states. Specifically a region can be merged (no overlapping + * polygons are present, touching polygons are merged, self-intersections of polygons are + * removed) or non-merged (polygons may overlap or polygons may be self-intersecting). In + * merged state, the wrap count at every point is either zero or 1, in non-merged state + * it can be every value. + * + * Polygons inside the region may contain holes if the region is merged. + */ +class DB_PUBLIC Region + : public gsi::ObjectBase +{ +public: + typedef db::Coord coord_type; + typedef db::coord_traits coord_traits; + typedef db::Polygon polygon_type; + typedef db::Vector vector_type; + typedef db::Point point_type; + typedef db::Box box_type; + typedef coord_traits::distance_type distance_type; + typedef coord_traits::perimeter_type perimeter_type; + typedef coord_traits::area_type area_type; + typedef RegionIterator const_iterator; + + /** + * @brief Default constructor + * + * Creates an empty region. + */ + Region (); + + /** + * @brief Destructor + */ + ~Region (); + + /** + * @brief Constructor from a delegate + * + * The region will take ownership of the delegate. + */ + Region (RegionDelegate *delegate); + + /** + * @brief Copy constructor + */ + Region (const Region &other); + + /** + * @brief Assignment + */ + Region &operator= (const Region &other); + + /** + * @brief Constructor from an object + * + * Creates a region representing a single instance of that object + */ + template + Region (const Sh &s) + { + FlatRegion *delegate = new FlatRegion (); + mp_delegate = delegate; + delegate->insert (s); } /** - * @brief Increment the iterator + * @brief Sequence constructor + * + * Creates a region from a sequence of objects. The objects can be boxes, + * polygons, paths or shapes. This version accepts iterators of the begin ... end + * style. */ - void inc () + template + Region (const Iter &b, const Iter &e) { - if (! m_rec_iter.at_end ()) { - ++m_rec_iter; - } else { - ++m_from; + FlatRegion *delegate = new FlatRegion (); + mp_delegate = delegate; + delegate->insert (b, e); + } + + /** + * @brief Constructor from a RecursiveShapeIterator + * + * Creates a region from a recursive shape iterator. This allows to feed a region + * from a hierarchy of cells. + */ + Region (const RecursiveShapeIterator &si); + + /** + * @brief Constructor from a RecursiveShapeIterator with a transformation + * + * Creates a region from a recursive shape iterator. This allows to feed a region + * from a hierarchy of cells. The transformation is useful to scale to a specific + * DBU for example. + */ + Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true); + + /** + * @brief Gets the underlying delegate object + */ + RegionDelegate *delegate () const + { + return mp_delegate; + } + + /** + * @brief Enable progress reporting + * + * @param progress_text The description text of the progress object + */ + void enable_progress (const std::string &desc = std::string ()) + { + mp_delegate->enable_progress (desc); + } + + /** + * @brief Disable progress reporting + */ + void disable_progress () + { + mp_delegate->disable_progress (); + } + + /** + * @brief Iterator of the region + * + * The iterator delivers the polygons of the region. + * It follows the at_end semantics. + */ + const_iterator begin () const + { + return RegionIterator (mp_delegate->begin ()); + } + + /** + * @brief Returns the merged polygons if merge semantics applies + * + * If merge semantics is not enabled, this iterator delivers the individual polygons. + */ + const_iterator begin_merged () const + { + return RegionIterator (mp_delegate->begin_merged ()); + } + + /** + * @brief Delivers a RecursiveShapeIterator pointing to the polygons plus the necessary transformation + */ + std::pair begin_iter () const + { + return mp_delegate->begin_iter (); + } + + /** + * @brief Delivers a RecursiveShapeIterator pointing to the merged polygons plus the necessary transformation + */ + std::pair begin_merged_iter () const + { + return mp_delegate->begin_merged_iter (); + } + + /** + * @brief Inserts the given shape (working object) into the region + */ + template + void insert (const Sh &shape) + { + flat_region ()->insert (shape); + } + + /** + * @brief Insert a shape reference into the region + */ + void insert (const db::Shape &shape) + { + flat_region ()->insert (shape); + } + + /** + * @brief Insert a transformed shape into the region + */ + template + void insert (const db::Shape &shape, const T &trans) + { + flat_region ()->insert (shape, trans); + } + + /** + * @brief Returns true if the region is empty + */ + bool empty () const + { + return mp_delegate->empty (); + } + + /** + * @brief Returns the number of polygons in the region + */ + size_t size () const + { + return mp_delegate->size (); + } + + /** + * @brief Returns a string representing the region + * + * nmax specifies how many polygons are included (set to std::numeric_limits::max() for "all". + */ + std::string to_string (size_t nmax = 10) const + { + return mp_delegate->to_string (nmax); + } + + /** + * @brief Clear the region + */ + void clear () + { + set_delegate (new EmptyRegion ()); + } + + /** + * @brief Reserve memory for the given number of polygons + */ + void reserve (size_t n) + { + flat_region ()->reserve (n); + } + + /** + * @brief Sets the minimum-coherence flag + * + * If minimum coherence is set, the merge operations (explicit merge with \merge or + * implicit merge through merged_semantics) are performed using minimum coherence mode. + * The coherence mode determines how kissing-corner situations are resolved. If + * minimum coherence is selected, they are resolved such that multiple polygons are + * created which touch at a corner). + */ + void set_min_coherence (bool f) + { + mp_delegate->set_min_coherence (f); + } + + /** + * @brief Gets the minimum coherence flag + */ + bool min_coherence () const + { + return mp_delegate->min_coherence (); + } + + /** + * @brief Sets the merged-semantics flag + * + * If merged semantics is enabled (the default), coherent polygons will be considered + * as single regions and artificial edges such as cut-lines will not be considered. + * Merged semantics thus is equivalent to considering coherent areas rather than + * single polygons. + */ + void set_merged_semantics (bool f) + { + mp_delegate->set_merged_semantics (f); + } + + /** + * @brief Gets the merged-semantics flag + */ + bool merged_semantics () const + { + return mp_delegate->merged_semantics (); + } + + /** + * @brief Enables or disables strict handling + * + * Strict handling means to leave away some optimizations. Specifically the + * output of boolean operations will be merged even if one input is empty. + * Without strict handling, the operation will be optimized and output + * won't be merged. + * + * Strict handling is disabled by default. + */ + void set_strict_handling (bool f) + { + mp_delegate->set_strict_handling (f); + } + + /** + * @brief Gets a valid indicating whether strict handling is enabled + */ + bool strict_handling () const + { + return mp_delegate->strict_handling (); + } + + /** + * @brief Returns true if the region is a single box + * + * If the region is not merged, this method may return false even + * if the merged region would be a box. + */ + bool is_box () const + { + return mp_delegate->is_box (); + } + + /** + * @brief Returns true if the region is merged + */ + bool is_merged () const + { + return mp_delegate->is_merged (); + } + + /** + * @brief Returns the area of the region + * + * This method returns the area sum over all polygons. + * Merged semantics applies. In merged semantics, the area is the correct area covered by the + * polygons. Without merged semantics, overlapping parts are counted twice. + * + * If a box is given, the computation is restricted to that box. + */ + area_type area (const db::Box &box = db::Box ()) const + { + return mp_delegate->area (box); + } + + /** + * @brief Returns the perimeter sum of the region + * + * This method returns the perimeter sum over all polygons. + * Merged semantics applies. In merged semantics, the perimeter is the true perimeter. + * Without merged semantics, inner edges contribute to the perimeter. + * + * If a box is given, the computation is restricted to that box. + * Edges coincident with the box edges are counted only if the form outer edges at the box edge. + */ + perimeter_type perimeter (const db::Box &box = db::Box ()) const + { + return mp_delegate->perimeter (box); + } + + /** + * @brief Returns the bounding box of the region + */ + Box bbox () const + { + return mp_delegate->bbox (); + } + + /** + * @brief Filters the polygons + * + * This method will keep all polygons for which the filter returns true. + * Merged semantics applies. In merged semantics, the filter will run over + * all merged polygons. + */ + Region &filter (const PolygonFilterBase &filter) + { + set_delegate (mp_delegate->filter_in_place (filter)); + return *this; + } + + /** + * @brief Returns the filtered polygons + * + * This method will return a new region with only those polygons which + * conform to the filter criterion. + */ + Region filtered (const PolygonFilterBase &filter) const + { + return Region (mp_delegate->filtered (filter)); + } + + /** + * @brief Applies a width check and returns EdgePairs which correspond to violation markers + * + * The width check will create a edge pairs if the width of the area between the + * edges is less than the specified threshold d. Without "whole_edges", the parts of + * the edges are returned which violate the condition. If "whole_edges" is true, the + * result will contain the complete edges participating in the result. + * + * The metrics parameter specifies which metrics to use. "Euclidian", "Square" and "Projected" + * metrics are available. + * + * ingore_angle allows specification of a maximum angle that connected edges can have to not participate + * in the check. By choosing 90 degree, edges with angles of 90 degree and larger are not checked, + * but acute corners are for example. + * + * With min_projection and max_projection it is possible to specify how edges must be related + * to each other. If the length of the projection of either edge on the other is >= min_projection + * or < max_projection, the edges are considered for the check. + * + * The order of the edges in the resulting edge pairs is undefined. + * + * Merged semantics applies. + */ + EdgePairs width_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->width_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a space check and returns EdgePairs which correspond to violation markers + * + * For the parameters see \width_check. The space check reports edges for which the space is + * less than the specified threshold d. + * + * Merged semantics applies. + */ + EdgePairs space_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->space_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a isolation check (space of polygon vs. other polygons) and returns EdgePairs which correspond to violation markers + * + * For the parameters see \width_check. The space check reports edges for which the notch space is + * less than the specified threshold d. + * + * Merged semantics applies. + */ + EdgePairs isolated_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->isolated_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a notch check (space in polygon vs. itself) and returns EdgePairs which correspond to violation markers + * + * For the parameters see \width_check. The space check reports edges for which the notch space is + * less than the specified threshold d. + * + * Merged semantics applies. + */ + EdgePairs notch_check (db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->notch_check (d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a enclosed check and returns EdgePairs which correspond to violation markers + * + * The check will return true, where this region is enclosing the polygons of the other + * region by less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs enclosing_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->enclosing_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a overlap check and returns EdgePairs which correspond to violation markers + * + * The check will return true, where this region overlaps the polygons of the other + * region by less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs overlap_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->overlap_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a separation check and returns EdgePairs which correspond to violation markers + * + * The check will return true, where this region is separated by polygons of the other + * region by less than the specified threshold d. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs separation_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->separation_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Applies a inside check and returns EdgePairs which correspond to violation markers + * + * The check will return true, where this region is inside by less than the threshold d inside the polygons of the other + * region. + * + * The first edges of the edge pairs will be the ones from "this", the second edges will be those of "other". + * + * For the other parameters see \width_check. + * + * Merged semantics applies. + */ + EdgePairs inside_check (const Region &other, db::Coord d, bool whole_edges = false, metrics_type metrics = db::Euclidian, double ignore_angle = 90, distance_type min_projection = 0, distance_type max_projection = std::numeric_limits::max ()) const + { + return mp_delegate->inside_check (other, d, whole_edges, metrics, ignore_angle, min_projection, max_projection); + } + + /** + * @brief Returns an edge set containing all edges of the polygons in this region + * + * Merged semantics applies. In merged semantics, only full, outer edges are delivered. + */ + Edges edges () const + { + return mp_delegate->edges (0); + } + + /** + * @brief Returns an edge set containing all edges of the polygons in this region + * + * This version allows to specify a filter by which the edges are filtered before they are + * returned. + * + * Merged semantics applies. In merged semantics, only full, outer edges are delivered. + */ + Edges edges (const EdgeFilterBase &filter) const + { + return mp_delegate->edges (&filter); + } + + /** + * @brief Transform the region + */ + template + Region &transform (const T &trans) + { + flat_region ()->transform (trans); + return *this; + } + + /** + * @brief Returns the transformed region + */ + template + Region transformed (const T &trans) const + { + Region d (*this); + d.transform (trans); + return d; + } + + /** + * @brief Performs an off-grid check on the polygons inside the region + * + * The method returns single-point edge pairs for each vertex found off-grid. + * The grid can be specified differently in x and y direction. + */ + EdgePairs grid_check (db::Coord gx, db::Coord gy) const + { + return mp_delegate->grid_check (gx, gy); + } + + /** + * @brief Performs an angle check + * + * The method returns edge pairs for each connected edges having + * an angle between min and max (exclusive max, in degree) or + * not between min and max (if inverse is true). + */ + EdgePairs angle_check (double min, double max, bool inverse) const + { + return mp_delegate->angle_check (min, max, inverse); + } + + /** + * @brief Grid-snaps the region + * + * Snaps the vertices of the polygons to the specified grid. + * different grids can be specified int x and y direction. + */ + void snap (db::Coord gx, db::Coord gy) + { + set_delegate (mp_delegate->snapped_in_place (gx, gy)); + } + + /** + * @brief Returns the snapped region + */ + Region snapped (db::Coord gx, db::Coord gy) const + { + return Region (mp_delegate->snapped (gx, gy)); + } + + /** + * @brief Performs a check for "strange" polygons + * + * This check will return a region with all self-overlapping or + * non-orientable parts of polygons. + * + * Naturally this method will ignore the merged_semantics setting. + */ + Region strange_polygon_check () const + { + return Region (mp_delegate->strange_polygon_check ()); + } + + /** + * @brief Swap with the other region + */ + void swap (db::Region &other) + { + std::swap (other.mp_delegate, mp_delegate); + } + + /** + * @brief Merge the region + * + * This method merges the polygons of the region if they are not merged already. + * It returns a reference to this region. + * An out-of-place merge version is "merged". + */ + Region &merge () + { + set_delegate (mp_delegate->merged_in_place ()); + return *this; + } + + /** + * @brief Returns the merged region + * + * This is the out-of-place merge. It returns a new region but does not modify + * the region it is called on. An in-place version is "merge". + */ + Region merged () const + { + return Region (mp_delegate->merged ()); + } + + /** + * @brief Merge the region with options + * + * This will merge the region and provides some options while doing so. + * + * A result is generated if the wrap count (wc) + * of a point is larger than the given min_wrapcount value. + * A min_wrapcount of 1 will produce output where at least two polygons overlap. + * + * This method will always execute the merge, even if the region is already merged. + * + * @param min_coherence Set this parameter to true to get minimum polygons (kissing corner problem) + * @param min_wrapcount See the description above + * @return A reference to this region + */ + Region &merge (bool min_coherence, unsigned int min_wc = 0) + { + set_delegate (mp_delegate->merged_in_place (min_coherence, min_wc)); + return *this; + } + + /** + * @brief Returns the merged region with options + * + * This is the out-of-place version of "merge" with options (see there). + */ + Region merged (bool min_coherence, unsigned int min_wc = 0) const + { + return Region (mp_delegate->merged (min_coherence, min_wc)); + } + + /** + * @brief Size the region + * + * This method applies a sizing to the region. Before the sizing is done, the + * region is merged if this is not the case already. + * + * The result is a set of polygons which may be overlapping, but are not self- + * intersecting. + * + * Merged semantics applies. + * + * @param d The (isotropic) sizing value + * @param mode The sizing mode (see EdgeProcessor) for a description of the sizing mode which controls the miter distance. + * @return A reference to self + */ + Region &size (coord_type d, unsigned int mode = 2) + { + set_delegate (mp_delegate->sized (d, mode)); + return *this; + } + + /** + * @brief Anisotropic sizing + * + * This version provides anisotropic sizing by allowing to specify a distance int x and y + * direction. + * + * Merged semantics applies. + * + * @param dx The horizontal sizing + * @param dy The vertical sizing + * @param mode The sizing mode (see EdgeProcessor) for a description of the sizing mode which controls the miter distance. + */ + Region &size (coord_type dx, coord_type dy, unsigned int mode = 2) + { + set_delegate (mp_delegate->sized (dx, dy, mode)); + return *this; + } + + /** + * @brief Returns the sized region + * + * This is an out-of-place version of the size method with isotropic sizing + * "merged polygon" semantics applies if merged_polygon_semantics is true (see set_auto_merge). + * + * Merged semantics applies. + */ + Region sized (coord_type d, unsigned int mode = 2) const + { + return Region (mp_delegate->sized (d, mode)); + } + + /** + * @brief Returns the sized region + * + * This is an out-of-place version of the size method with anisotropic sizing + * "merged polygon" semantics applies if merged_polygon_semantics is true (see set_auto_merge). + * + * Merged semantics applies. + */ + Region sized (coord_type dx, coord_type dy, unsigned int mode = 2) const + { + return Region (mp_delegate->sized (dx, dy, mode)); + } + + /** + * @brief Boolean AND operator + */ + Region operator& (const Region &other) const + { + return Region (mp_delegate->and_with (other)); + } + + /** + * @brief In-place boolean AND operator + * + * This method does not necessarily merge the region. To ensure the region + * is merged, call merge afterwards. + */ + Region &operator&= (const Region &other) + { + set_delegate (mp_delegate->and_with (other)); + return *this; + } + + /** + * @brief Boolean NOT operator + */ + Region operator- (const Region &other) const + { + return Region (mp_delegate->not_with (other)); + } + + /** + * @brief In-place boolean NOT operator + * + * This method does not necessarily merge the region. To ensure the region + * is merged, call merge afterwards. + */ + Region &operator-= (const Region &other) + { + set_delegate (mp_delegate->not_with (other)); + return *this; + } + + /** + * @brief Boolean XOR operator + */ + Region operator^ (const Region &other) const + { + return Region (mp_delegate->xor_with (other)); + } + + /** + * @brief In-place boolean XOR operator + * + * This method does not necessarily merge the region. To ensure the region + * is merged, call merge afterwards. + */ + Region &operator^= (const Region &other) + { + set_delegate (mp_delegate->xor_with (other)); + return *this; + } + + /** + * @brief Boolean OR operator + * + * This method merges the polygons of both regions. + */ + Region operator| (const Region &other) const + { + return Region (mp_delegate->or_with (other)); + } + + /** + * @brief In-place boolean OR operator + */ + Region &operator|= (const Region &other) + { + set_delegate (mp_delegate->or_with (other)); + return *this; + } + + /** + * @brief Joining of regions + * + * This method joins the regions but does not merge them afterwards. + */ + Region operator+ (const Region &other) const + { + return Region (mp_delegate->add (other)); + } + + /** + * @brief In-place region joining + */ + Region &operator+= (const Region &other) + { + set_delegate (mp_delegate->add_in_place (other)); + return *this; + } + + /** + * @brief Selects all polygons of this region which are completly outside polygons from the other region + * + * Merged semantics applies. + */ + Region &select_outside (const Region &other) + { + set_delegate (mp_delegate->selected_outside (other)); + return *this; + } + + /** + * @brief Selects all polygons of this region which are not completly outside polygons from the other region + * + * Merged semantics applies. + */ + Region &select_not_outside (const Region &other) + { + set_delegate (mp_delegate->selected_not_outside (other)); + return *this; + } + + /** + * @brief Returns all polygons of this which are completly outside polygons from the other region + * + * This method is an out-of-place version of select_outside. + * + * Merged semantics applies. + */ + Region selected_outside (const Region &other) const + { + return Region (mp_delegate->selected_outside (other)); + } + + /** + * @brief Returns all polygons of this which are not completly outside polygons from the other region + * + * This method is an out-of-place version of select_not_outside. + * + * Merged semantics applies. + */ + Region selected_not_outside (const Region &other) const + { + return Region (mp_delegate->selected_not_outside (other)); + } + + /** + * @brief Selects all polygons of this region which are completly inside polygons from the other region + * + * Merged semantics applies. + */ + Region &select_inside (const Region &other) + { + set_delegate (mp_delegate->selected_inside (other)); + return *this; + } + + /** + * @brief Selects all polygons of this region which are not completly inside polygons from the other region + * + * Merged semantics applies. + */ + Region &select_not_inside (const Region &other) + { + set_delegate (mp_delegate->selected_not_inside (other)); + return *this; + } + + /** + * @brief Returns all polygons of this which are completly inside polygons from the other region + * + * This method is an out-of-place version of select_inside. + * + * Merged semantics applies. + */ + Region selected_inside (const Region &other) const + { + return Region (mp_delegate->selected_inside (other)); + } + + /** + * @brief Returns all polygons of this which are not completly inside polygons from the other region + * + * This method is an out-of-place version of select_not_inside. + * + * Merged semantics applies. + */ + Region selected_not_inside (const Region &other) const + { + return Region (mp_delegate->selected_not_inside (other)); + } + + /** + * @brief Selects all polygons of this region which overlap or touch polygons from the other region + * + * Merged semantics applies. + */ + Region &select_interacting (const Region &other) + { + set_delegate (mp_delegate->selected_interacting (other)); + return *this; + } + + /** + * @brief Selects all polygons of this region which do not overlap or touch polygons from the other region + * + * Merged semantics applies. + */ + Region &select_not_interacting (const Region &other) + { + set_delegate (mp_delegate->selected_not_interacting (other)); + return *this; + } + + /** + * @brief Returns all polygons of this which overlap or touch polygons from the other region + * + * This method is an out-of-place version of select_interacting. + * + * Merged semantics applies. + */ + Region selected_interacting (const Region &other) const + { + return Region (mp_delegate->selected_interacting (other)); + } + + /** + * @brief Returns all polygons of this which do not overlap or touch polygons from the other region + * + * This method is an out-of-place version of select_not_interacting. + * + * Merged semantics applies. + */ + Region selected_not_interacting (const Region &other) const + { + return Region (mp_delegate->selected_not_interacting (other)); + } + + /** + * @brief Selects all polygons of this region which overlap or touch edges from the given edge collection + * + * Merged semantics applies to both operators. + */ + Region &select_interacting (const Edges &other) + { + set_delegate (mp_delegate->selected_interacting (other)); + return *this; + } + + /** + * @brief Selects all polygons of this region which do not overlap or touch edges from the edge collection + * + * Merged semantics applies to both operators. + */ + Region &select_not_interacting (const Edges &other) + { + set_delegate (mp_delegate->selected_not_interacting (other)); + return *this; + } + + /** + * @brief Returns all polygons of this which overlap or touch edges from the edge collection + * + * This method is an out-of-place version of select_interacting. + * + * Merged semantics applies to both operators. + */ + Region selected_interacting (const Edges &other) const + { + return Region (mp_delegate->selected_interacting (other)); + } + + /** + * @brief Returns all polygons of this which do not overlap or touch polygons from the other region + * + * This method is an out-of-place version of select_not_interacting. + * + * Merged semantics applies to both operators. + */ + Region selected_not_interacting (const Edges &other) const + { + return Region (mp_delegate->selected_not_interacting (other)); + } + + /** + * @brief Selects all polygons of this region which overlap polygons from the other region + * + * Merged semantics applies. + */ + Region &select_overlapping (const Region &other) + { + set_delegate (mp_delegate->selected_overlapping (other)); + return *this; + } + + /** + * @brief Selects all polygons of this region which do not overlap polygons from the other region + * + * Merged semantics applies. + */ + Region &select_not_overlapping (const Region &other) + { + set_delegate (mp_delegate->selected_not_overlapping (other)); + return *this; + } + + /** + * @brief Returns all polygons of this which overlap polygons from the other region + * + * This method is an out-of-place version of select_overlapping. + * + * Merged semantics applies. + */ + Region selected_overlapping (const Region &other) const + { + return Region (mp_delegate->selected_overlapping (other)); + } + + /** + * @brief Returns all polygons of this which do not overlap polygons from the other region + * + * This method is an out-of-place version of select_not_overlapping. + * + * Merged semantics applies. + */ + Region selected_not_overlapping (const Region &other) const + { + return Region (mp_delegate->selected_not_overlapping (other)); + } + + /** + * @brief Returns the holes + * + * This method returns the holes of the polygons. + * + * Merged semantics applies. + */ + Region holes () const + { + return Region (mp_delegate->holes ()); + } + + /** + * @brief Returns the hulls + * + * This method returns the hulls of the polygons. It does not merge the + * polygons before the hulls are derived. + * + * Merged semantics applies. + */ + Region hulls () const + { + return Region (mp_delegate->hulls ()); + } + + /** + * @brief Returns all polygons which are in the other region + * + * This method will return all polygons which are part of another region. + * The match is done exactly. + * The "invert" flag can be used to invert the sense, i.e. with + * "invert" set to true, this method will return all polygons not + * in the other region. + * + * Merged semantics applies. + */ + Region in (const Region &other, bool invert = false) const + { + return Region (mp_delegate->in (other, invert)); + } + + /** + * @brief Round corners (in-place) + * + * @param rinner The inner radius in DBU units + * @param router The outer radius in DBU units + * @param n The number of points to use per circle + */ + void round_corners (double rinner, double router, unsigned int n) + { + set_delegate (mp_delegate->rounded_corners (rinner, router, n)); + } + + /** + * @brief Returns a new region with rounded corners (out of place) + */ + Region rounded_corners (double rinner, double router, unsigned int n) const + { + return Region (mp_delegate->rounded_corners (rinner, router, n)); + } + + /** + * @brief Smoothes the region (in-place) + */ + void smooth (coord_type d) + { + set_delegate (mp_delegate->smoothed (d)); + } + + /** + * @brief Returns the smoothed region + * + * @param d The smoothing accuracy + */ + Region smoothed (coord_type d) const + { + return Region (mp_delegate->smoothed (d)); + } + + /** + * @brief Returns the nth polygon + * + * This operation is only cheap if "has_valid_polygons" is true. Otherwise, the + * complexity is O(n). + */ + const db::Polygon *nth (size_t n) const + { + return mp_delegate->nth (n); + } + + /** + * @brief Returns true, if the region has valid polygons stored within itself + */ + bool has_valid_polygons () const + { + return mp_delegate->has_valid_polygons (); + } + + /** + * @brief Gets the internal iterator + * + * This method is intended for users who know what they are doing + */ + const db::RecursiveShapeIterator &iter () const; + + /** + * @brief Equality + */ + bool operator== (const db::Region &other) const + { + return mp_delegate->equals (other); + } + + /** + * @brief Inequality + */ + bool operator!= (const db::Region &other) const + { + return ! mp_delegate->equals (other); + } + + /** + * @brief Less operator + */ + bool operator< (const db::Region &other) const + { + return mp_delegate->less (other); + } + +private: + RegionDelegate *mp_delegate; + + void set_delegate (RegionDelegate *delegate) + { + if (delegate != mp_delegate) { + delete mp_delegate; + mp_delegate = delegate; } } + + FlatRegion *flat_region () + { + FlatRegion *region = dynamic_cast (mp_delegate); + if (! region) { + region = new FlatRegion (); + region->insert_seq (begin ()); + set_delegate (region); + } + + return region; + } }; + +// ...................................................................................................... + +#if 0 // ORIGINAL /** * @brief A region * @@ -400,7 +2262,6 @@ private: * * Polygons inside the region may contain holes if the region is merged. */ - class DB_PUBLIC Region : public gsi::ObjectBase { @@ -1632,6 +3493,7 @@ private: Region *mp_region; bool m_clear; }; +#endif } // namespace db