diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 47a87419e..e0bb9dd4e 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1037,7 +1037,11 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, { #if defined(USE_LOCAL_PROCESSOR) - db::RegionIterator polygons (begin_merged ()); + bool needs_merged_primary = different_polygons || options.needs_merged (); +needs_merged_primary = true; // @@@ + + db::RegionIterator polygons (needs_merged_primary ? begin_merged () : begin ()); + bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged (); EdgeRelationFilter check (rel, d, options.metrics); check.set_include_zero (false); @@ -1059,9 +1063,12 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, if (other == subject_regionptr () || other == foreign_regionptr ()) { foreign.push_back (other == foreign_regionptr ()); others.push_back (begin_merged ()); + other_is_merged = primary_is_merged; } else { foreign.push_back (false); - if (options.whole_edges) { + if (! other->merged_semantics ()) { + other_is_merged = true; + } else if (options.whole_edges) { // NOTE: whole edges needs both inputs merged others.push_back (other->begin_merged ()); other_is_merged = true; @@ -1072,7 +1079,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, has_other = true; } - db::check_local_operation op (check, different_polygons, has_other, other_is_merged, options); + db::check_local_operation op (check, different_polygons, primary_is_merged, has_other, other_is_merged, options); std::unique_ptr output (new FlatEdgePairs ()); std::vector results; diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc index 5f36cc7b7..97f3c8c0d 100644 --- a/src/db/db/dbCellVariants.cc +++ b/src/db/db/dbCellVariants.cc @@ -45,6 +45,26 @@ db::Trans OrientationReducer::reduce (const db::Trans &trans) const // ------------------------------------------------------------------------------------------ +db::ICplxTrans OrthogonalTransformationReducer::reduce (const db::ICplxTrans &trans) const +{ + if (trans.is_ortho ()) { + return db::ICplxTrans (); + } else { + db::ICplxTrans res; + double a = trans.angle (); + double a90 = floor (a / 90.0 + 0.5) * 90.0; + res.angle (a - a90); + return res; + } +} + +db::Trans OrthogonalTransformationReducer::reduce (const db::Trans &trans) const +{ + return db::Trans (); +} + +// ------------------------------------------------------------------------------------------ + db::ICplxTrans MagnificationReducer::reduce (const db::ICplxTrans &trans) const { return db::ICplxTrans (trans.mag ()); diff --git a/src/db/db/dbCellVariants.h b/src/db/db/dbCellVariants.h index fb945586b..8bbc3f1e9 100644 --- a/src/db/db/dbCellVariants.h +++ b/src/db/db/dbCellVariants.h @@ -69,6 +69,16 @@ struct DB_PUBLIC OrientationReducer db::Trans reduce (const db::Trans &trans) const; }; +/** + * @brief A reducer for invariance against orthogonal transformations (rotations of multiple of 90 degree) + */ +struct DB_PUBLIC OrthogonalTransformationReducer + : public TransformationReducer +{ + db::ICplxTrans reduce (const db::ICplxTrans &trans) const; + db::Trans reduce (const db::Trans &trans) const; +}; + /** * @brief A magnification reducer * diff --git a/src/db/db/dbCompoundOperation.cc b/src/db/db/dbCompoundOperation.cc index 1a569a7b9..11122a2ae 100644 --- a/src/db/db/dbCompoundOperation.cc +++ b/src/db/db/dbCompoundOperation.cc @@ -1555,10 +1555,13 @@ CompoundRegionCheckOperationNode::CompoundRegionCheckOperationNode (CompoundRegi m_check.set_max_projection (options.max_projection); } -CompoundRegionCheckOperationNode::CompoundRegionCheckOperationNode (CompoundRegionOperationNode * /*input*/, CompoundRegionOperationNode *other, db::edge_relation_type rel, bool different_polygons, db::Coord d, const db::RegionCheckOptions &options) +CompoundRegionCheckOperationNode::CompoundRegionCheckOperationNode (CompoundRegionOperationNode *input, CompoundRegionOperationNode *other, db::edge_relation_type rel, bool different_polygons, db::Coord d, const db::RegionCheckOptions &options) : CompoundRegionMultiInputOperationNode (other), m_check (rel, d, options.metrics), m_different_polygons (different_polygons), m_options (options) { + tl_assert (input == 0); // input is a dummy parameter + m_has_other = other->has_external_inputs (); +// @@@ needs a concept to deal with merged/non-merged inputs m_is_other_merged = other->is_merged (); set_description ("check"); @@ -1585,7 +1588,9 @@ CompoundRegionCheckOperationNode::computed_dist () const void CompoundRegionCheckOperationNode::do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const { - db::check_local_operation op (m_check, m_different_polygons, m_has_other, m_is_other_merged, m_options); +// @@@ needs a concept to deal with merged/non-merged primary +bool is_merged = true; + db::check_local_operation op (m_check, m_different_polygons, is_merged, m_has_other, m_is_other_merged, m_options); tl_assert (results.size () == 1); if (results.front ().empty ()) { @@ -1601,7 +1606,9 @@ CompoundRegionCheckOperationNode::do_compute_local (CompoundRegionOperationCache void CompoundRegionCheckOperationNode::do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout *layout, const shape_interactions &interactions, std::vector > &results, size_t max_vertex_count, double area_ratio) const { - db::check_local_operation op (m_check, m_different_polygons, m_has_other, m_is_other_merged, m_options); +// @@@ needs a concept to deal with merged/non-merged primary +bool is_merged = true; + db::check_local_operation op (m_check, m_different_polygons, is_merged, m_has_other, m_is_other_merged, m_options); tl_assert (results.size () == 1); if (results.front ().empty ()) { diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index f0886c723..f38d94228 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1647,16 +1647,23 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons unsigned int other_layer = 0; bool other_is_merged = true; + bool needs_merged_primary = different_polygons || options.needs_merged (); + bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged (); + if (other == subject_regionptr ()) { other_layer = subject_idlayer (); + other_is_merged = primary_is_merged; } else if (other == foreign_regionptr ()) { other_layer = foreign_idlayer (); + other_is_merged = primary_is_merged; } else { other_deep = dynamic_cast (other->delegate ()); if (! other_deep) { return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, options); } - if (options.whole_edges) { + if (! other->merged_semantics ()) { + other_is_merged = true; + } else if (options.whole_edges) { // NOTE: whole edges needs both inputs merged other_layer = other_deep->merged_deep_layer ().layer (); other_is_merged = true; @@ -1666,7 +1673,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons } } - const db::DeepLayer &polygons = merged_deep_layer (); + const db::DeepLayer &polygons = needs_merged_primary ? merged_deep_layer () : deep_layer (); EdgeRelationFilter check (rel, d, options.metrics); check.set_include_zero (false); @@ -1677,7 +1684,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons std::unique_ptr res (new db::DeepEdgePairs (polygons.derived ())); - db::CheckLocalOperation op (check, different_polygons, other_deep != 0, other_is_merged, options); + db::CheckLocalOperation op (check, different_polygons, primary_is_merged, other_deep != 0, other_is_merged, options); db::local_processor proc (const_cast (&polygons.layout ()), const_cast (&polygons.initial_cell ()), diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index df0ca4dfd..cf7bcb709 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -274,6 +274,73 @@ EdgeOrientationFilter::selected (const db::Edge &edge) const } } +// ------------------------------------------------------------------------------------------------------------- +// SpecialEdgeOrientationFilter implementation + +SpecialEdgeOrientationFilter::SpecialEdgeOrientationFilter (FilterType type, bool inverse) + : m_type (type), m_inverse (inverse) +{ + // .. nothing yet .. +} + +static EdgeAngleChecker s_ortho_checkers [] = { + EdgeAngleChecker (0.0, true, 0.0, true), + EdgeAngleChecker (90.0, true, 90.0, true) +}; + +static EdgeAngleChecker s_diagonal_checkers [] = { + EdgeAngleChecker (-45.0, true, -45.0, true), + EdgeAngleChecker (45.0, true, 45.0, true) +}; + +static EdgeAngleChecker s_orthodiagonal_checkers [] = { + EdgeAngleChecker (-45.0, true, -45.0, true), + EdgeAngleChecker (0.0, true, 0.0, true), + EdgeAngleChecker (45.0, true, 45.0, true), + EdgeAngleChecker (90.0, true, 90.0, true) +}; + +bool +SpecialEdgeOrientationFilter::selected (const db::Edge &edge) const +{ + const EdgeAngleChecker *eb, *ee; + + switch (m_type) { + case Ortho: + eb = s_ortho_checkers; + ee = s_ortho_checkers + sizeof (s_ortho_checkers) / sizeof (s_ortho_checkers [0]); + break; + case Diagonal: + eb = s_diagonal_checkers; + ee = s_diagonal_checkers + sizeof (s_diagonal_checkers) / sizeof (s_diagonal_checkers [0]); + break; + case OrthoDiagonal: + default: + eb = s_orthodiagonal_checkers; + ee = s_orthodiagonal_checkers + sizeof (s_orthodiagonal_checkers) / sizeof (s_orthodiagonal_checkers [0]); + break; + } + + db::Vector en, ev; + en = db::Vector (edge.ortho_length (), 0); + + // NOTE: this edge normalization confines the angle to a range between (-90 .. 90] (-90 excluded). + // A horizontal edge has 0 degree, a vertical one has 90 degree. + if (edge.dx () < 0 || (edge.dx () == 0 && edge.dy () < 0)) { + ev = -edge.d (); + } else { + ev = edge.d (); + } + + for (auto ec = eb; ec != ee; ++ec) { + if ((*ec) (en, ev)) { + return ! m_inverse; + } + } + + return m_inverse; +} + // ------------------------------------------------------------------------------------------------------------- // Edge to Edge relation implementation diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index 055d78eae..4577397a4 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -246,6 +246,76 @@ private: EdgeAngleChecker m_checker; }; +/** + * @brief An edge orientation filter for use with Edges::filter or Edges::filtered + * + * This filter renders edges with special orientations. + */ + +struct DB_PUBLIC SpecialEdgeOrientationFilter + : public EdgeFilterBase +{ + enum FilterType { + Ortho = 0, // 0 and 90 degree + Diagonal = 1, // -45 and 45 degree + OrthoDiagonal = 2 // both Ortho and Diagonal + }; + + /** + * @brief Constructor + * + * @param type The type of filter + */ + SpecialEdgeOrientationFilter (FilterType type, bool inverse); + + /** + * @brief Returns true if the edge orientation matches the criterion + */ + virtual bool selected (const db::Edge &edge) const; + + /** + * @brief Returns true if all edge orientations match the criterion + */ + virtual bool selected (const std::unordered_set &edges) const + { + for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { + if (! selected (*e)) { + return false; + } + } + return true; + } + + /** + * @brief This filter is not isotropic + */ + virtual const TransformationReducer *vars () const + { + return &m_vars; + } + + /** + * @brief Requires merged input + */ + virtual bool requires_raw_input () const + { + return false; + } + + /** + * @brief Wants to build variants + */ + virtual bool wants_variants () const + { + return true; + } + +private: + FilterType m_type; + bool m_inverse; + db::OrthogonalTransformationReducer m_vars; +}; + /** * @brief A predicate defining edge a interacts with b */ diff --git a/src/db/db/dbHash.h b/src/db/db/dbHash.h index 6d511285b..9ed98ae0b 100644 --- a/src/db/db/dbHash.h +++ b/src/db/db/dbHash.h @@ -252,8 +252,15 @@ namespace std h = hfunc_coord (o.bgn_ext (), h); h = hfunc_coord (o.end_ext (), h); h = hfunc_coord (o.width (), h); + // NOTE: using too many points for the hash function just slows down the code. + unsigned int n = 20; for (typename db::path::iterator p = o.begin (); p != o.end (); ++p) { - h = hfunc (*p, h); + if (--n == 0) { + h = hfunc (o.points (), h); + break; + } else { + h = hfunc (*p, h); + } } return h; } @@ -280,8 +287,15 @@ namespace std template size_t hfunc (const db::polygon_contour &o, size_t h) { + // NOTE: using too many points for the hash function just slows down the code. + unsigned int n = 20; for (typename db::polygon_contour::simple_iterator i = o.begin (); i != o.end (); ++i) { - h = hfunc (*i, h); + if (--n == 0) { + h = hfunc (o.size (), h); + break; + } else { + h = hfunc (*i, h); + } } return h; } @@ -309,8 +323,15 @@ namespace std size_t hfunc (const db::polygon &o, size_t h) { h = hfunc (o.hull (), h); + // NOTE: using too many points for the hash function just slows down the code. + unsigned int n = 20; for (size_t i = 0; i < o.holes (); ++i) { - h = hfunc (o.hole (int (i)), h); + if (--n == 0) { + h = hfunc (o.holes (), h); + break; + } else { + h = hfunc (o.hole (int (i)), h); + } } return h; } diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index a52f8bef2..35582d39b 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -189,15 +189,22 @@ public: typedef typename Ref::shape_type shape_type; typedef typename Ref::trans_type ref_trans_type; - shape_reference_translator_with_trans_from_shape_ref (db::Layout *target_layout, const Trans &trans) - : mp_layout (target_layout), m_trans (trans), m_ref_trans (trans), m_bare_trans (Trans (m_ref_trans.inverted ()) * trans) + shape_reference_translator_with_trans_from_shape_ref (db::Layout *target_layout) + : mp_layout (target_layout) { // .. nothing yet .. } + void set_trans (const Trans &trans) + { + m_trans = trans; + m_ref_trans = ref_trans_type (trans); + m_bare_trans = Trans (m_ref_trans.inverted ()) * trans; + } + Ref operator() (const Ref &ref) const { - typename std::unordered_map >::const_iterator m = m_cache.find (ref.ptr ()); + auto m = m_cache.find (std::make_pair (ref.ptr (), m_bare_trans)); if (m != m_cache.end ()) { return Ref (m->second.first, ref_trans_type (m_trans * Trans (ref.trans ())) * m->second.second); @@ -214,7 +221,7 @@ public: ptr = mp_layout->shape_repository ().repository (typename shape_type::tag ()).insert (sh); } - m_cache[ref.ptr ()] = std::make_pair (ptr, red_trans); + m_cache[std::make_pair (ref.ptr (), m_bare_trans)] = std::make_pair (ptr, red_trans); return Ref (ptr, ref_trans_type (m_trans * Trans (ref.trans ())) * red_trans); @@ -226,7 +233,7 @@ private: Trans m_trans; ref_trans_type m_ref_trans; Trans m_bare_trans; - mutable std::unordered_map > m_cache; + mutable std::unordered_map, std::pair > m_cache; }; template @@ -234,8 +241,8 @@ class shape_reference_translator_with_trans : public shape_reference_translator_with_trans_from_shape_ref { public: - shape_reference_translator_with_trans (db::Layout *target_layout, const Trans &trans) - : shape_reference_translator_with_trans_from_shape_ref (target_layout, trans) + shape_reference_translator_with_trans (db::Layout *target_layout) + : shape_reference_translator_with_trans_from_shape_ref (target_layout) { // .. nothing yet .. } @@ -247,12 +254,16 @@ class shape_reference_translator_with_trans public: typedef Sh shape_type; - shape_reference_translator_with_trans (db::Layout * /*target_layout*/, const Trans &trans) - : m_trans (trans) + shape_reference_translator_with_trans (db::Layout * /*target_layout*/) { // .. nothing yet .. } + void set_trans (const Trans &trans) + { + m_trans = trans; + } + shape_type operator() (const shape_type &s) const { return s.transformed (m_trans); @@ -331,11 +342,11 @@ template local_processor_cell_context & local_processor_cell_context::operator= (const local_processor_cell_context &other) { - if (this != &other) { - m_propagated = other.m_propagated; - m_drops = other.m_drops; - } - return *this; + if (this != &other) { + m_propagated = other.m_propagated; + m_drops = other.m_drops; + } + return *this; } template @@ -353,13 +364,20 @@ local_processor_cell_context::propagate (unsigned int output_layer, return; } + db::Layout *subject_layout = 0; + shape_reference_translator_with_trans rt (subject_layout); + for (typename std::vector >::const_iterator d = m_drops.begin (); d != m_drops.end (); ++d) { tl_assert (d->parent_context != 0); tl_assert (d->parent != 0); - db::Layout *subject_layout = d->parent->layout (); - shape_reference_translator_with_trans rt (subject_layout, d->cell_inst); + if (subject_layout != d->parent->layout ()) { + subject_layout = d->parent->layout (); + rt = shape_reference_translator_with_trans (subject_layout); + } + + rt.set_trans (d->cell_inst); std::vector new_refs; new_refs.reserve (res.size ()); for (typename std::unordered_set::const_iterator r = res.begin (); r != res.end (); ++r) { @@ -1180,7 +1198,7 @@ public: typedef std::unordered_map, interactions_value_type> interactions_type; interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, interactions_type *result) - : mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result) + : mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result), m_rt (subject_layout) { // nothing yet .. } @@ -1195,6 +1213,7 @@ private: unsigned int m_subject_layer; db::Coord m_dist; interactions_type *mp_result; + db::shape_reference_translator_with_trans m_rt; void collect_instance_shape_interactions (const db::CellInstArray *inst, unsigned int layer, const TI &ref, db::Coord dist) @@ -1211,7 +1230,7 @@ private: if (! cbox.empty ()) { db::ICplxTrans tni = tn.inverted (); - db::shape_reference_translator_with_trans rt (mp_subject_layout, tni); + m_rt.set_trans (tni); std::set *shapes = 0; @@ -1223,7 +1242,7 @@ private: if (! shapes) { shapes = & (*mp_result) [std::make_pair (cell.cell_index (), tn)].second [layer]; } - shapes->insert (rt (ref)); + shapes->insert (m_rt (ref)); } } diff --git a/src/db/db/dbPolygon.h b/src/db/db/dbPolygon.h index 3d7657ba2..d715f8c4b 100644 --- a/src/db/db/dbPolygon.h +++ b/src/db/db/dbPolygon.h @@ -3076,6 +3076,7 @@ public: typedef typename Poly::coord_type coord_type; typedef typename Poly::point_type point_type; typedef typename Poly::box_type box_type; + typedef typename Poly::edge_type edge_type; typedef Trans trans_type; typedef Poly polygon_type; typedef db::polygon_edge_iterator polygon_edge_iterator; diff --git a/src/db/db/dbRegionCheckUtils.cc b/src/db/db/dbRegionCheckUtils.cc index c8183fa46..e09fa60ee 100644 --- a/src/db/db/dbRegionCheckUtils.cc +++ b/src/db/db/dbRegionCheckUtils.cc @@ -468,6 +468,14 @@ poly2poly_check::enter (const PolygonType &o, size_t p) } } +template +void +poly2poly_check::enter (const poly2poly_check::edge_type &e, size_t p) +{ + m_edge_heap.push_back (e); + m_scanner.insert (& m_edge_heap.back (), p); +} + // TODO: move to generic header static bool interact (const db::Box &box, const db::Edge &e) { @@ -496,6 +504,16 @@ poly2poly_check::enter (const PolygonType &o, size_t p, const poly2 } } +template +void +poly2poly_check::enter (const poly2poly_check::edge_type &e, size_t p, const poly2poly_check::box_type &box) +{ + if (! box.empty () && interact (box, e)) { + m_edge_heap.push_back (e); + m_scanner.insert (& m_edge_heap.back (), p); + } +} + template void poly2poly_check::process () diff --git a/src/db/db/dbRegionCheckUtils.h b/src/db/db/dbRegionCheckUtils.h index 6c19ed0c3..41ad724d6 100644 --- a/src/db/db/dbRegionCheckUtils.h +++ b/src/db/db/dbRegionCheckUtils.h @@ -310,6 +310,7 @@ class DB_PUBLIC poly2poly_check { public: typedef typename PolygonType::box_type box_type; + typedef typename PolygonType::edge_type edge_type; poly2poly_check (Edge2EdgeCheckBase &output); poly2poly_check (); @@ -321,6 +322,8 @@ public: void connect (Edge2EdgeCheckBase &output); void enter (const PolygonType &o, size_t p); void enter (const PolygonType &o, size_t p, const box_type &search_box); + void enter (const edge_type &o, size_t p); + void enter (const edge_type &o, size_t p, const box_type &search_box); void process (); private: diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index c2a399411..d6aa19b9e 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -122,8 +122,8 @@ static bool shields_interaction (const db::EdgePair &ep, const P &poly) } template -check_local_operation::check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options) - : m_check (check), m_different_polygons (different_polygons), m_has_other (has_other), m_other_is_merged (other_is_merged), m_options (options) +check_local_operation::check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options) + : m_check (check), m_different_polygons (different_polygons), m_is_merged (is_merged), m_has_other (has_other), m_other_is_merged (other_is_merged), m_options (options) { // .. nothing yet .. } @@ -208,12 +208,15 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape edge2edge_check_negative_or_positive > edge_check (m_check, result, intra_polygon_result, m_options.negative, m_different_polygons, m_has_other, m_options.shielded, symmetric_edge_pairs); poly2poly_check poly_check (edge_check); - std::list heap; std::unordered_set polygons; + std::unordered_set spolygons; + + db::EdgeProcessor ep; + ep.set_base_verbosity (50); std::set ids; - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - for (typename shape_interactions::iterator2 j = i->second.begin (); j != i->second.end (); ++j) { + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + for (auto j = i->second.begin (); j != i->second.end (); ++j) { ids.insert (*j); } } @@ -226,13 +229,13 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape db::Vector e (edge_check.distance (), edge_check.distance ()); db::Box subject_box; - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (auto i = interactions.begin (); i != interactions.end (); ++i) { subject_box += db::box_convert () (interactions.subject_shape (i->first)); } if (edge_check.requires_different_layers ()) { db::Box intruder_box; - for (std::set::const_iterator id = ids.begin (); id != ids.end (); ++id) { + for (auto id = ids.begin (); id != ids.end (); ++id) { intruder_box += db::box_convert () (interactions.intruder_shape (*id).second); } common_box = subject_box.enlarged (e) & intruder_box.enlarged (e); @@ -245,14 +248,50 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape if (m_has_other) { size_t n = 0; - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - const TS &subject = interactions.subject_shape (i->first); - if (! take_all) { - poly_check.enter (subject, n, common_box); - } else { - poly_check.enter (subject, n); + + if (m_is_merged || (interactions.size () == 1 && interactions.subject_shape (interactions.begin ()->first).is_box ())) { + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + const TS &subject = interactions.subject_shape (i->first); + if (! take_all) { + poly_check.enter (subject, n, common_box); + } else { + poly_check.enter (subject, n); + } + n += 2; } - n += 2; + + } else { + + // merge needed for the subject shapes + + ep.clear (); + size_t nn = 0; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + const TS &is = interactions.subject_shape (i->first); + for (typename TS::polygon_edge_iterator e = is.begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, nn); + } + ++nn; + } + + spolygons.clear (); + + db::polygon_ref_generator ps (layout, spolygons); + db::PolygonGenerator pg (ps, false /*don't resolve holes*/, false); + db::SimpleMerge op (1 /*wc>0*/); + ep.process (pg, op); + + for (auto o = spolygons.begin (); o != spolygons.end (); ++o) { + if (! take_all) { + poly_check.enter (*o, n, common_box); + } else { + poly_check.enter (*o, n); + } + n += 2; + } + } // merge the intruders to remove inner edges @@ -266,20 +305,19 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape // NOTE: this local merge is not necessarily giving the same results than a global merge before running // the processor. Reason: the search range is limited, hence not all necessary components may have been // captured. - db::EdgeProcessor ep; - ep.set_base_verbosity (50); ep.clear (); - size_t i = 0; + size_t nn = 0; - for (std::set::const_iterator id = ids.begin (); id != ids.end (); ++id) { + for (auto id = ids.begin (); id != ids.end (); ++id) { const TI &is = interactions.intruder_shape (*id).second; - for (typename TI::polygon_edge_iterator e = is.begin_edge (); ! e.at_end (); ++e) { - ep.insert (*e, i); + for (auto e = is.begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, nn); } - ++i; + ++nn; } +// @@@ Use edges directly polygons.clear (); db::polygon_ref_generator ps (layout, polygons); @@ -288,7 +326,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.process (pg, op); n = 1; - for (typename std::unordered_set::const_iterator o = polygons.begin (); o != polygons.end (); ++o) { + for (auto o = polygons.begin (); o != polygons.end (); ++o) { if (! take_all) { poly_check.enter (*o, n, common_box); } else { @@ -300,7 +338,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape } else { n = 1; - for (std::set::const_iterator id = ids.begin (); id != ids.end (); ++id) { + for (auto id = ids.begin (); id != ids.end (); ++id) { if (! take_all) { poly_check.enter (interactions.intruder_shape (*id).second, n, common_box); } else { @@ -313,34 +351,155 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape } else { - // NOTE: we need to eliminate identical shapes from intruders and subjects because those will shield + if (m_is_merged || (interactions.size () == 1 && ids.empty () && interactions.subject_shape (interactions.begin ()->first).is_box ())) { - size_t n = 0; + // no merge required - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - // we can't directly insert because TS may be != TI - const TS &ts = interactions.subject_shape (i->first); - insert_into_hash (polygons, ts); - if (! take_all) { - poly_check.enter (ts, n, common_box); - } else { - poly_check.enter (ts, n); - } - n += 2; - } + // NOTE: we need to eliminate identical shapes from intruders and subjects because those will shield - n = 1; + size_t n = 0; + std::unordered_set subjects; - for (std::set::const_iterator id = ids.begin (); id != ids.end (); ++id) { - const TI &ti = interactions.intruder_shape (*id).second; - if (polygons.find (ti) == polygons.end ()) { + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + // we can't directly insert because TS may be != TI + const TS &ts = interactions.subject_shape (i->first); + insert_into_hash (subjects, ts); if (! take_all) { - poly_check.enter (ti, n, common_box); + poly_check.enter (ts, n, common_box); } else { - poly_check.enter (ti, n); + poly_check.enter (ts, n); } n += 2; } + + n = 1; + + for (auto id = ids.begin (); id != ids.end (); ++id) { + const TI &ti = interactions.intruder_shape (*id).second; + if (subjects.find (ti) == subjects.end ()) { + if (! take_all) { + poly_check.enter (ti, n, common_box); + } else { + poly_check.enter (ti, n); + } + } + } + + } else if (ids.empty ()) { + + // merge needed for the subject shapes - no intruders present so this is the simple case + + size_t n = 0; + + ep.clear (); + size_t nn = 0; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + const TS &ts = interactions.subject_shape (i->first); + for (auto e = ts.begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, nn); + } + ++nn; + } + +// @@@ Use edges directly + spolygons.clear (); + + db::polygon_ref_generator ps (layout, spolygons); + db::PolygonGenerator pg (ps, false /*don't resolve holes*/, false); + db::SimpleMerge op (1 /*wc>0*/); + ep.process (pg, op); + + for (auto o = spolygons.begin (); o != spolygons.end (); ++o) { + if (! take_all) { + poly_check.enter (*o, n, common_box); + } else { + poly_check.enter (*o, n); + } + n += 2; + } + + } else { + + // merge needed for the subject and intruder shapes - we merge both and then + // separate edges into those from the subject and those from intruder shapes. + + ep.clear (); + size_t nn = 0; + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + const TS &ts = interactions.subject_shape (i->first); + for (auto e = ts.begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, nn); + } + ++nn; + } + + for (auto id = ids.begin (); id != ids.end (); ++id) { + const TI &ti = interactions.intruder_shape (*id).second; + for (auto e = ti.begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, nn); + } + ++nn; + } + + std::vector edges; + + db::EdgeContainer ee (edges); + db::SimpleMerge op (1 /*wc>0*/); + ep.process (ee, op); + ep.clear (); + + std::vector subject_edges; + + size_t sz = 0; + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + const TS &ts = interactions.subject_shape (i->first); + sz += ts.vertices (); + } + + subject_edges.reserve (sz); + + for (auto i = interactions.begin (); i != interactions.end (); ++i) { + const TS &ts = interactions.subject_shape (i->first); + for (auto e = ts.begin_edge (); ! e.at_end (); ++e) { + subject_edges.push_back (*e); + } + } + + for (size_t n = 0; n <= 1; ++n) { + + std::set partial_edges; + + EdgeBooleanClusterCollector > cluster_collector (&partial_edges, n == 0 ? db::EdgeAnd : db::EdgeNot); + + db::box_scanner scanner; + scanner.reserve (edges.size () + subject_edges.size ()); + + for (auto i = edges.begin (); i != edges.end (); ++i) { + if (! i->is_degenerate ()) { + scanner.insert (i.operator-> (), 0); + } + } + + for (auto i = subject_edges.begin (); i != subject_edges.end (); ++i) { + if (! i->is_degenerate ()) { + scanner.insert (i.operator-> (), 1); + } + } + + scanner.process (cluster_collector, 1, db::box_convert ()); + + for (auto e = partial_edges.begin (); e != partial_edges.end (); ++e) { + if (! take_all) { + poly_check.enter (*e, n, common_box); + } else { + poly_check.enter (*e, n); + } + } + + } + } } diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index d5734850f..6d81c2b0d 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -197,6 +197,19 @@ struct DB_PUBLIC RegionCheckOptions * @brief Specifies whether to produce negative output */ bool negative; + + /** + * @brief Gets a value indicating whether merged primary input is required + */ + bool needs_merged () const + { + return negative + || rect_filter != NoRectFilter + || opposite_filter != NoOppositeFilter + || max_projection != std::numeric_limits::max () + || min_projection != 0 + || whole_edges; + } }; template @@ -204,7 +217,7 @@ class check_local_operation : public local_operation { public: - check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options); + check_local_operation (const EdgeRelationFilter &check, bool different_polygons, bool is_merged, bool has_other, bool other_is_merged, const db::RegionCheckOptions &options); virtual db::Coord dist () const; virtual OnEmptyIntruderHint on_empty_intruder_hint () const; @@ -216,6 +229,7 @@ public: private: EdgeRelationFilter m_check; bool m_different_polygons; + bool m_is_merged; bool m_has_other; bool m_other_is_merged; db::RegionCheckOptions m_options; diff --git a/src/db/db/gsiDeclDbCompoundOperation.cc b/src/db/db/gsiDeclDbCompoundOperation.cc index 2fc3924fd..84468d936 100644 --- a/src/db/db/gsiDeclDbCompoundOperation.cc +++ b/src/db/db/gsiDeclDbCompoundOperation.cc @@ -390,7 +390,7 @@ static db::CompoundRegionOperationNode *new_edge_pair_to_second_edges (db::Compo static db::CompoundRegionOperationNode *new_check_node (db::CompoundRegionOperationNode *other, db::edge_relation_type rel, bool different_polygons, db::Coord d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, bool shielded, db::OppositeFilter opposite_filter, db::RectFilter rect_filter, bool negative) { check_non_null (other, "other"); - return new db::CompoundRegionCheckOperationNode (new_primary (), other, rel, different_polygons, d, + return new db::CompoundRegionCheckOperationNode (0, other, rel, different_polygons, d, db::RegionCheckOptions (whole_edges, metrics, ignore_angle.is_nil () ? 90 : ignore_angle.to_double (), diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 89516e8fa..d374dd526 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -22,6 +22,7 @@ #include "gsiDecl.h" +#include "gsiEnums.h" #include "dbEdgePairs.h" #include "dbEdges.h" @@ -232,6 +233,13 @@ static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double am return r->filtered (ef); } +static db::EdgePairs with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse) +{ + db::SpecialEdgeOrientationFilter f (type, inverse); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return r->filtered (ef); +} + static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool inverse) { db::EdgeOrientationFilter f (a, inverse); @@ -246,6 +254,13 @@ static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, doub return r->filtered (ef); } +static db::EdgePairs with_angle_both3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse) +{ + db::SpecialEdgeOrientationFilter f (type, inverse); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return r->filtered (ef); +} + static db::EdgePairs with_internal_angle1 (const db::EdgePairs *r, double a, bool inverse) { db::InternalAngleEdgePairFilter f (a, inverse); @@ -661,7 +676,15 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "This will filter edge pairs with at least one horizontal edge:\n" "\n" "@code\n" - "horizontal = edge_pairs.with_orientation(0, false)\n" + "horizontal = edge_pairs.with_angle(0, false)\n" + "@/code\n" + "\n" + "Note that the inverse @b result @/b of \\with_angle is delivered by \\with_angle_both with the inverse flag set as edge pairs are unselected when both edges fail to meet the criterion.\n" + "I.e\n" + "\n" + "@code\n" + "result = edge_pairs.with_angle(0, false)\n" + "others = edge_pairs.with_angle_both(0, true)\n" "@/code\n" "\n" "This method has been added in version 0.27.1.\n" @@ -675,8 +698,35 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the " "minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n" "\n" + "Note that the inverse @b result @/b of \\with_angle is delivered by \\with_angle_both with the inverse flag set as edge pairs are unselected when both edges fail to meet the criterion.\n" + "I.e\n" + "\n" + "@code\n" + "result = edge_pairs.with_angle(0, 45, false)\n" + "others = edge_pairs.with_angle_both(0, 45, true)\n" + "@/code\n" + "\n" "This method has been added in version 0.27.1.\n" ) + + method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"), + "@brief Filter the edge pairs by orientation of their edges\n" + "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " + "edge pairs with at least one edge having an angle of the given type are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion are returned.\n" + "\n" + "This version allows specifying an edge type instead of an angle. Edge types include multiple distinct orientations " + "and are specified using one of the \\Edges#OrthoEdges, \\Edges#DiagonalEdges or \\Edges#OrthoDiagonalEdges types.\n" + "\n" + "Note that the inverse @b result @/b of \\with_angle is delivered by \\with_angle_both with the inverse flag set as edge pairs are unselected when both edges fail to meet the criterion.\n" + "I.e\n" + "\n" + "@code\n" + "result = edge_pairs.with_angle(RBA::Edges::Ortho, false)\n" + "others = edge_pairs.with_angle_both(RBA::Edges::Ortho, true)\n" + "@/code\n" + "\n" + "This method has been added in version 0.28.\n" + ) + method_ext ("with_angle_both", with_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Filter the edge pairs by orientation of both of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " @@ -686,7 +736,15 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "This will filter edge pairs with at least one horizontal edge:\n" "\n" "@code\n" - "horizontal = edge_pairs.with_orientation(0, false)\n" + "horizontal = edge_pairs.with_angle_both(0, false)\n" + "@/code\n" + "\n" + "Note that the inverse @b result @/b of \\with_angle_both is delivered by \\with_angle with the inverse flag set as edge pairs are unselected when one edge fails to meet the criterion.\n" + "I.e\n" + "\n" + "@code\n" + "result = edge_pairs.with_angle_both(0, false)\n" + "others = edge_pairs.with_angle(0, true)\n" "@/code\n" "\n" "This method has been added in version 0.27.1.\n" @@ -700,8 +758,35 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the " "minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n" "\n" + "Note that the inverse @b result @/b of \\with_angle_both is delivered by \\with_angle with the inverse flag set as edge pairs are unselected when one edge fails to meet the criterion.\n" + "I.e\n" + "\n" + "@code\n" + "result = edge_pairs.with_angle_both(0, 45, false)\n" + "others = edge_pairs.with_angle(0, 45, true)\n" + "@/code\n" + "\n" "This method has been added in version 0.27.1.\n" ) + + method_ext ("with_angle_both", with_angle_both3, gsi::arg ("type"), gsi::arg ("inverse"), + "@brief Filter the edge pairs by orientation of their edges\n" + "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " + "edge pairs with both edges having an angle of the given type are returned. If \"inverse\" is true, " + "edge pairs not fulfilling this criterion for both edges are returned.\n" + "\n" + "This version allows specifying an edge type instead of an angle. Edge types include multiple distinct orientations " + "and are specified using one of the \\Edges#OrthoEdges, \\Edges#DiagonalEdges or \\Edges#OrthoDiagonalEdges types.\n" + "\n" + "Note that the inverse @b result @/b of \\with_angle_both is delivered by \\with_angle with the inverse flag set as edge pairs are unselected when one edge fails to meet the criterion.\n" + "I.e\n" + "\n" + "@code\n" + "result = edge_pairs.with_angle_both(RBA::Edges::Ortho, false)\n" + "others = edge_pairs.with_angle(RBA::Edges::Ortho, true)\n" + "@/code\n" + "\n" + "This method has been added in version 0.28.\n" + ) + method_ext ("with_area", with_area1, gsi::arg ("area"), gsi::arg ("inverse"), "@brief Filters the edge pairs by the enclosed area\n" "Filters the edge pairs in the edge pair collection by enclosed area. If \"inverse\" is false, only " diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 911bf9ac6..e2977296f 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -212,6 +212,12 @@ static db::Edges with_angle2 (const db::Edges *r, double amin, double amax, bool return r->filtered (f); } +static db::Edges with_angle3 (const db::Edges *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse) +{ + db::SpecialEdgeOrientationFilter f (type, inverse); + return r->filtered (f); +} + static db::EdgePairs width2 (const db::Edges *r, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection) { return r->width_check (d, db::EdgesCheckOptions (whole_edges, @@ -609,13 +615,13 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "This method has been introduced in version 0.26." ) + method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"), - "@brief Filter the edges by length\n" + "@brief Filters the edges by length\n" "Filters the edges in the edge collection by length. If \"inverse\" is false, only " "edges which have the given length are returned. If \"inverse\" is true, " "edges not having the given length are returned.\n" ) + method_ext ("with_length", with_length2, gsi::arg ("min_length"), gsi::arg ("max_length"), gsi::arg ("inverse"), - "@brief Filter the edges by length\n" + "@brief Filters the edges by length\n" "Filters the edges in the edge collection by length. If \"inverse\" is false, only " "edges which have a length larger or equal to \"min_length\" and less than \"max_length\" are " "returned. If \"inverse\" is true, " @@ -625,7 +631,7 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "If you don't want to specify a lower or upper limit, pass nil to that parameter.\n" ) + method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), - "@brief Filter the edges by orientation\n" + "@brief Filters the edges by orientation\n" "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " "edges which have the given angle to the x-axis are returned. If \"inverse\" is true, " "edges not having the given angle are returned.\n" @@ -633,11 +639,11 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "This will select horizontal edges:\n" "\n" "@code\n" - "horizontal = edges.with_orientation(0, false)\n" + "horizontal = edges.with_angle(0, false)\n" "@/code\n" ) + method_ext ("with_angle", with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), - "@brief Filter the edges by orientation\n" + "@brief Filters the edges by orientation\n" "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " "edges which have an angle to the x-axis larger or equal to \"min_angle\" (depending on \"include_min_angle\") and equal or less than \"max_angle\" (depending on \"include_max_angle\") are " "returned. If \"inverse\" is true, " @@ -648,6 +654,17 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "The two \"include..\" arguments have been added in version 0.27." ) + + method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"), + "@brief Filters the edges by orientation type\n" + "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " + "edges which have an angle of the given type are returned. If \"inverse\" is true, " + "edges which do not conform to this criterion are returned.\n" + "\n" + "This version allows specifying an edge type instead of an angle. Edge types include multiple distinct orientations " + "and are specified using one of the \\OrthoEdges, \\DiagonalEdges or \\OrthoDiagonalEdges types.\n" + "\n" + "This method has been added in version 0.28.\n" + ) + method ("insert", (void (db::Edges::*)(const db::Edge &)) &db::Edges::insert, gsi::arg ("edge"), "@brief Inserts an edge\n" "\n" @@ -1749,5 +1766,23 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "This class has been introduced in version 0.23.\n" ); +gsi::EnumIn decl_EdgesEdgeFilterType ("db", "EdgeType", + gsi::enum_const ("OrthoEdges", db::SpecialEdgeOrientationFilter::Ortho, + "@brief Horizontal and vertical edges are selected\n" + ) + + gsi::enum_const ("DiagonalEdges", db::SpecialEdgeOrientationFilter::Diagonal, + "@brief Diagonal edges are selected (-45 and 45 degree)\n" + ) + + gsi::enum_const ("OrthoDiagonalEdges", db::SpecialEdgeOrientationFilter::OrthoDiagonal, + "@brief Diagonal or orthogonal edges are selected (0, 90, -45 and 45 degree)\n" + ), + "@brief This enum specifies the the edge type for edge angle filters.\n" + "\n" + "This enum was introduced in version 0.28.\n" +); + +// Inject the db::SpecialEdgeOrientationFilter::FilterType declarations into Edges: +gsi::ClassExt decl_EdgesEdgeFilterType_into_parent (decl_EdgesEdgeFilterType.defs ()); + } diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index 4cc344d45..323b77638 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -3290,7 +3290,10 @@ Shielding is enabled by default, but can be switched off with the "transparent"
  • layer.with_angle(min .. max)
  • layer.with_angle(value)
  • layer.with_angle(min, max)
  • -
  • edge_pair_layer.with_angle(min, max [, both])
  • +
  • layer.with_angle(ortho)
  • +
  • layer.with_angle(diagonal)
  • +
  • layer.with_angle(diagonal_only)
  • +
  • edge_pair_layer.with_angle(... [, both])
  • When called on an edge layer, the method selects edges by their angle, @@ -3303,7 +3306,7 @@ an angle of 90 degee. The angle range is from -90 (exclusive) to 90 degree (incl The first version of this method selects edges with a angle larger or equal to min and less than max (but not equal). The second version selects edges with exactly the given angle. The third -version is identical to the first one. +version is identical to the first one.

    When called on an edge pair layer, this method selects edge pairs with one or both edges meeting the angle criterion. In this case an additional argument is accepted which can be @@ -3320,6 +3323,10 @@ ep2 = edge_pairs.with_angle(90, both)

    A method delivering all objects not matching the angle criterion is without_angle. +Note that for edge pairs, in order to get the inverse result, you have to add or drop "both" +on without_angle. This is because without_angle without both returns edge pairs where +one edge does not match the criterion. The logical opposite of "one edge matches" however is +"both edges do not match".

    The following images demonstrate some use cases of with_angle and without_angle:

    @@ -3334,6 +3341,17 @@ The following images demonstrate some use cases of with_an

    +Specifying "ortho", "diagonal" or "diagonal_only" instead of the angle values will select +0 and 90 degree edges (ortho), -45 and 45 degree edges (diagonal_only) and both types (diagonal). +This simplifies the implementation of selectors for manhattan or half-manhattan features: +

    +

    +ortho_edges = edges.with_angle(ortho)
    +
    +# which is equivalent to, but more efficient as:
    +ortho_edges = edges.with_angle(0) + edges.with_angle(90)
    +
    +

    Note that in former versions, with_angle could be used on polygon layers selecting corners with specific angles. This feature has been deprecated. Use corners instead.

    @@ -3663,12 +3681,17 @@ This method is available for polygon layers only.
  • layer.without_angle(min .. max)
  • layer.without_angle(value)
  • layer.without_angle(min, max)
  • -
  • edge_pair_layer.without_angle(min, max [, both])
  • +
  • layer.without_angle(ortho)
  • +
  • layer.without_angle(diagonal)
  • +
  • layer.without_angle(diagonal_only)
  • +
  • edge_pair_layer.without_angle(... [, both])
  • The method basically is the inverse of with_angle. It selects all edges of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle -is not inside the given interval (first and third form). When called on edge pairs, it selects +is not inside the given interval (first and third form) or of the given type (other forms). +

    +When called on edge pairs, it selects edge pairs by the angles of their edges.

    A note on the "both" modifier (without_angle called on edge pairs): "both" means that diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 7dce0a451..fc024aa13 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -77,6 +77,18 @@ module DRC DRCBothEdges::new end + def ortho + DRCOrthoEdges::new + end + + def diagonal_only + DRCDiagonalOnlyEdges::new + end + + def diagonal + DRCDiagonalEdges::new + end + def shift(x, y) self._context("shift") do RBA::DCplxTrans::new(RBA::DVector::new(_make_value(x) * self.dbu, _make_value(y) * self.dbu)) diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index c8e2731a5..8e3c13d69 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -864,7 +864,10 @@ CODE # @synopsis layer.with_angle(min .. max) # @synopsis layer.with_angle(value) # @synopsis layer.with_angle(min, max) - # @synopsis edge_pair_layer.with_angle(min, max [, both]) + # @synopsis layer.with_angle(ortho) + # @synopsis layer.with_angle(diagonal) + # @synopsis layer.with_angle(diagonal_only) + # @synopsis edge_pair_layer.with_angle(... [, both]) # # When called on an edge layer, the method selects edges by their angle, # measured against the horizontal axis in the mathematical sense. @@ -876,7 +879,7 @@ CODE # The first version of this method selects # edges with a angle larger or equal to min and less than max (but not equal). # The second version selects edges with exactly the given angle. The third - # version is identical to the first one. + # version is identical to the first one. # # When called on an edge pair layer, this method selects edge pairs with one or both edges # meeting the angle criterion. In this case an additional argument is accepted which can be @@ -893,6 +896,10 @@ CODE # @/code # # A method delivering all objects not matching the angle criterion is \without_angle. + # Note that for edge pairs, in order to get the inverse result, you have to add or drop "both" + # on \without_angle. This is because \without_angle without both returns edge pairs where + # one edge does not match the criterion. The logical opposite of "one edge matches" however is + # "both edges do not match". # # The following images demonstrate some use cases of \with_angle and \without_angle: # @@ -907,6 +914,17 @@ CODE # @/tr # @/table # + # Specifying "ortho", "diagonal" or "diagonal_only" instead of the angle values will select + # 0 and 90 degree edges (ortho), -45 and 45 degree edges (diagonal_only) and both types (diagonal). + # This simplifies the implementation of selectors for manhattan or half-manhattan features: + # + # @code + # ortho_edges = edges.with_angle(ortho) + # + # # which is equivalent to, but more efficient as: + # ortho_edges = edges.with_angle(0) + edges.with_angle(90) + # @/code + # # Note that in former versions, with_angle could be used on polygon layers selecting corners with specific angles. # This feature has been deprecated. Use \corners instead. @@ -916,11 +934,16 @@ CODE # @synopsis layer.without_angle(min .. max) # @synopsis layer.without_angle(value) # @synopsis layer.without_angle(min, max) - # @synopsis edge_pair_layer.without_angle(min, max [, both]) + # @synopsis layer.without_angle(ortho) + # @synopsis layer.without_angle(diagonal) + # @synopsis layer.without_angle(diagonal_only) + # @synopsis edge_pair_layer.without_angle(... [, both]) # # The method basically is the inverse of \with_angle. It selects all edges # of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle - # is not inside the given interval (first and third form). When called on edge pairs, it selects + # is not inside the given interval (first and third form) or of the given type (other forms). + # + # When called on edge pairs, it selects # edge pairs by the angles of their edges. # # A note on the "both" modifier (without_angle called on edge pairs): "both" means that @@ -981,7 +1004,7 @@ CODE args = args.select do |a| if a.is_a?(DRCBothEdges) if !self.data.is_a?(RBA::EdgePairs) - raise("'both' keyword only available for edge pair layers") + raise("'both' keyword is only available for edge pair layers") end f = :with_#{f}_both false @@ -998,6 +1021,11 @@ CODE a = args[0] if a.is_a?(Range) DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect})) + elsif a.is_a?(DRCOrthoEdges) || a.is_a?(DRCDiagonalOnlyEdges) || a.is_a?(DRCDiagonalEdges) + if self.data.is_a?(RBA::Region) + raise("'ortho', 'diagonal' or 'diagonal_only' keyword is only available for edge or edge pair layers") + end + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.value, #{inv.inspect})) else DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a, #{inv.inspect})) end diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index 86fd67e8d..677d45852 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -16,10 +16,31 @@ module DRC end end - # A wrapper for the "both edges" flag for EdgePair#with_length or EdgePair#with_angle + # A wrapper for the "both edges" flag for EdgePairs#with_length or EdgePairs#with_angle class DRCBothEdges end + # A wrapper for the "ortho edges" flag for Edges#with_angle or EdgePairs#with_angle + class DRCOrthoEdges + def value + RBA::Edges::OrthoEdges + end + end + + # A wrapper for the "diagonal only edges" flag for Edges#with_angle or EdgePairs#with_angle + class DRCDiagonalOnlyEdges + def value + RBA::Edges::DiagonalEdges + end + end + + # A wrapper for the "diagonal edges" flag for Edges#with_angle or EdgePairs#with_angle + class DRCDiagonalEdges + def value + RBA::Edges::OrthoDiagonalEdges + end + end + # A wrapper for the padding modes of with_density class DRCDensityPadding attr_accessor :value diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 741646c9e..2ee8dd3a2 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1328,3 +1328,13 @@ TEST(55d_drccount) run_test (_this, "55", true); } +TEST(56_angle_classes) +{ + run_test (_this, "56", false); +} + +TEST(56d_angle_classes) +{ + run_test (_this, "56", true); +} + diff --git a/src/layui/layui/layIndexedNetlistModel.cc b/src/layui/layui/layIndexedNetlistModel.cc index 35c1f67a4..29ff0dc85 100644 --- a/src/layui/layui/layIndexedNetlistModel.cc +++ b/src/layui/layui/layIndexedNetlistModel.cc @@ -225,13 +225,13 @@ static size_t index_from_attr (const std::pair &attr size_t SingleIndexedNetlistModel::circuit_count () const { - return mp_netlist->circuit_count (); + return mp_netlist ? mp_netlist->circuit_count () : 0; } size_t SingleIndexedNetlistModel::top_circuit_count () const { - return mp_netlist->top_circuit_count (); + return mp_netlist ? mp_netlist->top_circuit_count () : 0; } size_t diff --git a/testdata/drc/drcSimpleTests_56.drc b/testdata/drc/drcSimpleTests_56.drc new file mode 100644 index 000000000..778b5fad0 --- /dev/null +++ b/testdata/drc/drcSimpleTests_56.drc @@ -0,0 +1,31 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) + +l1.output(1, 0) + +l1e = l1.edges +l1e.with_angle(ortho).output(100, 0) +l1e.without_angle(ortho).output(101, 0) +l1e.with_angle(diagonal).output(102, 0) +l1e.without_angle(diagonal).output(103, 0) +l1e.with_angle(diagonal_only).output(104, 0) +l1e.without_angle(diagonal_only).output(105, 0) + +l1ee = l1.width(100.um, projection) +l1ee.output(10, 0) + +l1ee.with_angle(ortho).output(200, 0) +l1ee.without_angle(ortho).output(201, 0) +l1ee.with_angle(diagonal).output(202, 0) +l1ee.without_angle(diagonal).output(203, 0) +l1ee.with_angle(diagonal_only).output(204, 0) +l1ee.without_angle(diagonal_only).output(205, 0) +l1ee.with_angle(ortho, both).output(210, 0) + diff --git a/testdata/drc/drcSimpleTests_56.gds b/testdata/drc/drcSimpleTests_56.gds new file mode 100644 index 000000000..a9ab615db Binary files /dev/null and b/testdata/drc/drcSimpleTests_56.gds differ diff --git a/testdata/drc/drcSimpleTests_au25d.gds b/testdata/drc/drcSimpleTests_au25d.gds index 554b845d2..a8e351c22 100644 Binary files a/testdata/drc/drcSimpleTests_au25d.gds and b/testdata/drc/drcSimpleTests_au25d.gds differ diff --git a/testdata/drc/drcSimpleTests_au48d.gds b/testdata/drc/drcSimpleTests_au48d.gds index dfc7a8ada..a0a418801 100644 Binary files a/testdata/drc/drcSimpleTests_au48d.gds and b/testdata/drc/drcSimpleTests_au48d.gds differ diff --git a/testdata/drc/drcSimpleTests_au56.gds b/testdata/drc/drcSimpleTests_au56.gds new file mode 100644 index 000000000..2a9a9f4e6 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au56.gds differ diff --git a/testdata/drc/drcSimpleTests_au56d.gds b/testdata/drc/drcSimpleTests_au56d.gds new file mode 100644 index 000000000..46d200579 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au56d.gds differ diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index db5570d45..d44b25977 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -303,6 +303,27 @@ class DBEdgePairs_TestClass < TestBase assert_equal(r1.with_internal_angle(0, 45, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(20,10;30,0)") assert_equal(r1.with_internal_angle(0, 45, true, true, true).to_s, "") + ep1 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 0, 20, 0)) + ep2 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 0, 20, 10)) + ep3 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 20), RBA::Edge::new(10, 20, 10, 0)) + ep4 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 0, 15, 10)) + + r = RBA::EdgePairs::new([ ep1, ep2, ep3, ep4 ]) + + assert_equal(r.with_angle(RBA::Edges::OrthoEdges, false).to_s, "(0,0;0,10)/(10,0;20,0);(0,0;0,10)/(10,0;20,10);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;15,10)") + assert_equal(r.with_angle(RBA::Edges::OrthoEdges, true).to_s, "(0,0;0,10)/(10,0;20,10);(0,0;0,10)/(10,0;15,10)") + assert_equal(r.with_angle(RBA::Edges::DiagonalEdges, false).to_s, "(0,0;0,10)/(10,0;20,10)") + assert_equal(r.with_angle(RBA::Edges::DiagonalEdges, true).to_s, "(0,0;0,10)/(10,0;20,0);(0,0;0,10)/(10,0;20,10);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;15,10)") + assert_equal(r.with_angle(RBA::Edges::OrthoDiagonalEdges, false).to_s, "(0,0;0,10)/(10,0;20,0);(0,0;0,10)/(10,0;20,10);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;15,10)") + assert_equal(r.with_angle(RBA::Edges::OrthoDiagonalEdges, true).to_s, "(0,0;0,10)/(10,0;15,10)") + + assert_equal(r.with_angle_both(RBA::Edges::OrthoEdges, false).to_s, "(0,0;0,10)/(10,0;20,0);(0,0;0,20)/(10,20;10,0)") + assert_equal(r.with_angle_both(RBA::Edges::OrthoEdges, true).to_s, "") + assert_equal(r.with_angle_both(RBA::Edges::DiagonalEdges, false).to_s, "") + assert_equal(r.with_angle_both(RBA::Edges::DiagonalEdges, true).to_s, "(0,0;0,10)/(10,0;20,0);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;15,10)") + assert_equal(r.with_angle_both(RBA::Edges::OrthoDiagonalEdges, false).to_s, "(0,0;0,10)/(10,0;20,0);(0,0;0,10)/(10,0;20,10);(0,0;0,20)/(10,20;10,0)") + assert_equal(r.with_angle_both(RBA::Edges::OrthoDiagonalEdges, true).to_s, "") + end end diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index c199c0c10..680a60568 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -474,6 +474,19 @@ class DBEdges_TestClass < TestBase assert_equal(r.with_length(100, 200, true).to_s, "(100,0;100,50)") assert_equal(r.with_length(nil, 100, false).to_s, "(100,0;100,50)") + r = RBA::Edges::new + r.insert(RBA::Edge::new(0, 0, 100, 0)) + r.insert(RBA::Edge::new(100, 0, 100, 50)) + r.insert(RBA::Edge::new(0, 0, 100, 100)) + r.insert(RBA::Edge::new(0, 0, 100, -100)) + r.insert(RBA::Edge::new(0, 0, 100, 120)) + assert_equal(r.with_angle(RBA::Edges::OrthoEdges, false).to_s, "(0,0;100,0);(100,0;100,50)") + assert_equal(r.with_angle(RBA::Edges::OrthoEdges, true).to_s, "(0,0;100,100);(0,0;100,-100);(0,0;100,120)") + assert_equal(r.with_angle(RBA::Edges::DiagonalEdges, false).to_s, "(0,0;100,100);(0,0;100,-100)") + assert_equal(r.with_angle(RBA::Edges::DiagonalEdges, true).to_s, "(0,0;100,0);(100,0;100,50);(0,0;100,120)") + assert_equal(r.with_angle(RBA::Edges::OrthoDiagonalEdges, false).to_s, "(0,0;100,0);(100,0;100,50);(0,0;100,100);(0,0;100,-100)") + assert_equal(r.with_angle(RBA::Edges::OrthoDiagonalEdges, true).to_s, "(0,0;100,120)") + end # interact