From d8f3c522c93c0ce22aa6273c1154e15b6bb7bec6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Jun 2022 20:02:15 +0200 Subject: [PATCH 01/15] Removed a performance bottleneck (do not invalidate cache that often of inst to shape interaction computation) --- src/db/db/dbHierProcessor.cc | 38 +++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 87ca8a900..fc96f6cd9 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -189,12 +189,19 @@ 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 ()); @@ -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); @@ -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) { @@ -1203,6 +1221,8 @@ private: db::box_convert inst_bc (*mp_subject_layout, m_subject_layer); db::Box rbox = db::box_convert () (ref); + db::shape_reference_translator_with_trans rt (mp_subject_layout); + for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) { db::ICplxTrans tn = inst->complex_trans (*n); @@ -1211,7 +1231,7 @@ private: if (! cbox.empty ()) { db::ICplxTrans tni = tn.inverted (); - db::shape_reference_translator_with_trans rt (mp_subject_layout, tni); + rt.set_trans (tni); std::set *shapes = 0; From 959b5fe7997a9aed5c9e9f7b24402f6ef45dc09d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Jun 2022 20:02:41 +0200 Subject: [PATCH 02/15] Faster hash of shape references (need to see what that is doing to test results) --- src/db/db/dbHash.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/db/db/dbHash.h b/src/db/db/dbHash.h index 6d511285b..59d9b882b 100644 --- a/src/db/db/dbHash.h +++ b/src/db/db/dbHash.h @@ -514,13 +514,13 @@ namespace std template size_t hfunc (const db::shape_ref &o, size_t h) { - return hfunc (*o.ptr (), hfunc (o.trans (), h)); + return hfunc ((size_t) o.ptr (), hfunc (o.trans (), h)); } template size_t hfunc (const db::shape_ref &o) { - return hfunc (*o.ptr (), hfunc (o.trans ())); + return hfunc ((size_t) o.ptr (), hfunc (o.trans ())); } template @@ -539,13 +539,13 @@ namespace std template size_t hfunc (const db::polygon_ref &o, size_t h) { - return hfunc (*o.ptr (), hfunc (o.trans (), h)); + return hfunc ((size_t) o.ptr (), hfunc (o.trans (), h)); } template size_t hfunc (const db::polygon_ref &o) { - return hfunc (*o.ptr (), hfunc (o.trans ())); + return hfunc ((size_t) o.ptr (), hfunc (o.trans ())); } template @@ -564,13 +564,13 @@ namespace std template size_t hfunc (const db::path_ref &o, size_t h) { - return hfunc (*o.ptr (), hfunc (o.trans (), h)); + return hfunc ((size_t) o.ptr (), hfunc (o.trans (), h)); } template size_t hfunc (const db::path_ref &o) { - return hfunc (*o.ptr (), hfunc (o.trans ())); + return hfunc ((size_t) o.ptr (), hfunc (o.trans ())); } template @@ -589,13 +589,13 @@ namespace std template size_t hfunc (const db::text_ref &o, size_t h) { - return hfunc (*o.ptr (), hfunc (o.trans (), h)); + return hfunc ((size_t) o.ptr (), hfunc (o.trans (), h)); } template size_t hfunc (const db::text_ref &o) { - return hfunc (*o.ptr (), hfunc (o.trans ())); + return hfunc ((size_t) o.ptr (), hfunc (o.trans ())); } template From 1930018f8fce23a0fe966c112c87c2a513a6097e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Jun 2022 21:40:44 +0200 Subject: [PATCH 03/15] First attempt to implement DRC checks without merging - still required with certain options and not enabled to generic DRC function yet. --- src/db/db/dbAsIfFlatRegion.cc | 12 +- src/db/db/dbCompoundOperation.cc | 13 ++- src/db/db/dbDeepRegion.cc | 13 ++- src/db/db/dbRegionLocalOperations.cc | 145 ++++++++++++++++++------ src/db/db/dbRegionLocalOperations.h | 16 ++- src/db/db/gsiDeclDbCompoundOperation.cc | 2 +- 6 files changed, 157 insertions(+), 44 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 47a87419e..0001342df 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1037,7 +1037,10 @@ 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 (); + + 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 +1062,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 +1078,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/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/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index c2a399411..cd4f9ea1c 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 .. } @@ -210,6 +210,10 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape 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) { @@ -245,14 +249,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) { + + 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); + } + n += 2; } - n += 2; + + } else { + + // merge needed for the subject shapes + + ep.clear (); + size_t nn = 0; + + for (typename shape_interactions::iterator 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 (typename std::unordered_set::const_iterator 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,18 +306,16 @@ 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) { 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); + ep.insert (*e, nn); } - ++i; + ++nn; } polygons.clear (); @@ -317,30 +355,71 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape size_t n = 0; - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { - // 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; - } + if (m_is_merged) { - n = 1; - - 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 (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 (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; } + + 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 ()) { + if (! take_all) { + poly_check.enter (ti, n, common_box); + } else { + poly_check.enter (ti, n); + } + ++n; + } + } + + } else { + + // merge needed for the subject shapes + + ep.clear (); + size_t nn = 0; + + for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + const TS &ts = interactions.subject_shape (i->first); + for (typename TS::polygon_edge_iterator e = ts.begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, nn); + } + ++nn; + } + + for (std::set::const_iterator id = ids.begin (); id != ids.end (); ++id) { + const TI &ti = interactions.intruder_shape (*id).second; + for (typename TI::polygon_edge_iterator e = ti.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 (typename std::unordered_set::const_iterator o = spolygons.begin (); o != spolygons.end (); ++o) { + if (! take_all) { + poly_check.enter (*o, n, common_box); + } else { + poly_check.enter (*o, n); + } + ++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 8d6ed008e..cefa9a1b8 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 (), From 43b63923a709ddf1fa43b40127381cf627846ab4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 26 Jun 2022 01:00:33 +0200 Subject: [PATCH 04/15] WIP --- src/db/db/dbAsIfFlatRegion.cc | 1 + src/db/db/dbHash.h | 43 ++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 0001342df..e0bb9dd4e 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1038,6 +1038,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, #if defined(USE_LOCAL_PROCESSOR) 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 (); diff --git a/src/db/db/dbHash.h b/src/db/db/dbHash.h index 59d9b882b..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; } @@ -514,13 +535,13 @@ namespace std template size_t hfunc (const db::shape_ref &o, size_t h) { - return hfunc ((size_t) o.ptr (), hfunc (o.trans (), h)); + return hfunc (*o.ptr (), hfunc (o.trans (), h)); } template size_t hfunc (const db::shape_ref &o) { - return hfunc ((size_t) o.ptr (), hfunc (o.trans ())); + return hfunc (*o.ptr (), hfunc (o.trans ())); } template @@ -539,13 +560,13 @@ namespace std template size_t hfunc (const db::polygon_ref &o, size_t h) { - return hfunc ((size_t) o.ptr (), hfunc (o.trans (), h)); + return hfunc (*o.ptr (), hfunc (o.trans (), h)); } template size_t hfunc (const db::polygon_ref &o) { - return hfunc ((size_t) o.ptr (), hfunc (o.trans ())); + return hfunc (*o.ptr (), hfunc (o.trans ())); } template @@ -564,13 +585,13 @@ namespace std template size_t hfunc (const db::path_ref &o, size_t h) { - return hfunc ((size_t) o.ptr (), hfunc (o.trans (), h)); + return hfunc (*o.ptr (), hfunc (o.trans (), h)); } template size_t hfunc (const db::path_ref &o) { - return hfunc ((size_t) o.ptr (), hfunc (o.trans ())); + return hfunc (*o.ptr (), hfunc (o.trans ())); } template @@ -589,13 +610,13 @@ namespace std template size_t hfunc (const db::text_ref &o, size_t h) { - return hfunc ((size_t) o.ptr (), hfunc (o.trans (), h)); + return hfunc (*o.ptr (), hfunc (o.trans (), h)); } template size_t hfunc (const db::text_ref &o) { - return hfunc ((size_t) o.ptr (), hfunc (o.trans ())); + return hfunc (*o.ptr (), hfunc (o.trans ())); } template From 91c85172e3124aa4c28f0d18b900fcd28df7fa80 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 26 Jun 2022 01:33:49 +0200 Subject: [PATCH 05/15] WIP --- src/db/db/dbDeepRegion.cc | 1 + src/db/db/dbHash.h | 9 ++++++--- src/db/db/dbHierProcessor.cc | 11 ++++++----- src/db/db/dbRegionLocalOperations.cc | 4 ++-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index f38d94228..b76dc34ef 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1648,6 +1648,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons bool other_is_merged = true; bool needs_merged_primary = different_polygons || options.needs_merged (); +needs_merged_primary = true; // @@@ bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged (); if (other == subject_regionptr ()) { diff --git a/src/db/db/dbHash.h b/src/db/db/dbHash.h index 9ed98ae0b..19913d2c0 100644 --- a/src/db/db/dbHash.h +++ b/src/db/db/dbHash.h @@ -253,7 +253,8 @@ namespace std 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; + // @@@ unsigned int n = 20; + unsigned int n = std::numeric_limits::max (); // @@@ for (typename db::path::iterator p = o.begin (); p != o.end (); ++p) { if (--n == 0) { h = hfunc (o.points (), h); @@ -288,7 +289,8 @@ namespace std 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; + // @@@ unsigned int n = 20; + unsigned int n = std::numeric_limits::max (); // @@@ for (typename db::polygon_contour::simple_iterator i = o.begin (); i != o.end (); ++i) { if (--n == 0) { h = hfunc (o.size (), h); @@ -324,7 +326,8 @@ namespace std { h = hfunc (o.hull (), h); // NOTE: using too many points for the hash function just slows down the code. - unsigned int n = 20; + // @@@ unsigned int n = 20; + unsigned int n = std::numeric_limits::max (); // @@@ for (size_t i = 0; i < o.holes (); ++i) { if (--n == 0) { h = hfunc (o.holes (), h); diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index fc96f6cd9..1afbff8ca 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -342,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 @@ -1221,6 +1221,7 @@ private: db::box_convert inst_bc (*mp_subject_layout, m_subject_layer); db::Box rbox = db::box_convert () (ref); +// @@@ make member of receiver? db::shape_reference_translator_with_trans rt (mp_subject_layout); for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) { diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index cd4f9ea1c..4be8ca7a1 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -250,7 +250,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape size_t n = 0; - if (m_is_merged) { + if (m_is_merged || (interactions.size () == 1 && interactions.subject_shape (interactions.begin ()->first).is_box ())) { for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { const TS &subject = interactions.subject_shape (i->first); @@ -355,7 +355,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape size_t n = 0; - if (m_is_merged) { + if (m_is_merged || (interactions.size () == 1 && ids.empty () && interactions.subject_shape (interactions.begin ()->first).is_box ())) { for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { // we can't directly insert because TS may be != TI From 77b42980b6e9b6db3e56a7d2aaf0aa216ef89db6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 26 Jun 2022 01:50:45 +0200 Subject: [PATCH 06/15] WIP --- src/db/db/dbHierProcessor.cc | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 1afbff8ca..044c454c2 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -204,7 +204,7 @@ public: 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); @@ -221,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); @@ -233,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 @@ -1198,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 .. } @@ -1213,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) @@ -1221,9 +1222,6 @@ private: db::box_convert inst_bc (*mp_subject_layout, m_subject_layer); db::Box rbox = db::box_convert () (ref); -// @@@ make member of receiver? - db::shape_reference_translator_with_trans rt (mp_subject_layout); - for (db::CellInstArray::iterator n = inst->begin_touching (safe_box_enlarged (rbox, dist - 1, dist - 1), inst_bc); ! n.at_end (); ++n) { db::ICplxTrans tn = inst->complex_trans (*n); @@ -1232,7 +1230,7 @@ private: if (! cbox.empty ()) { db::ICplxTrans tni = tn.inverted (); - rt.set_trans (tni); + m_rt.set_trans (tni); std::set *shapes = 0; @@ -1244,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)); } } From bed9ac94b0642fbffef42df55e7c43716c3baceb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 26 Jun 2022 18:03:15 +0200 Subject: [PATCH 07/15] WIP --- src/db/db/dbDeepRegion.cc | 1 - src/db/db/dbRegionLocalOperations.cc | 131 +++++++++++++++++++-------- 2 files changed, 91 insertions(+), 41 deletions(-) diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index b76dc34ef..f38d94228 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1648,7 +1648,6 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons bool other_is_merged = true; bool needs_merged_primary = different_polygons || options.needs_merged (); -needs_merged_primary = true; // @@@ bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged (); if (other == subject_regionptr ()) { diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 4be8ca7a1..fcd0e98da 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -208,16 +208,16 @@ 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; + std::unordered_set subjects; 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); } } @@ -230,13 +230,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); @@ -252,7 +252,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape if (m_is_merged || (interactions.size () == 1 && interactions.subject_shape (interactions.begin ()->first).is_box ())) { - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + 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); @@ -269,7 +269,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.clear (); size_t nn = 0; - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + 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); @@ -284,7 +284,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape db::SimpleMerge op (1 /*wc>0*/); ep.process (pg, op); - for (typename std::unordered_set::const_iterator o = spolygons.begin (); o != spolygons.end (); ++o) { + for (auto o = spolygons.begin (); o != spolygons.end (); ++o) { if (! take_all) { poly_check.enter (*o, n, common_box); } else { @@ -310,9 +310,9 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.clear (); 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) { + for (auto e = is.begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } ++nn; @@ -326,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 { @@ -338,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 { @@ -353,32 +353,22 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape // NOTE: we need to eliminate identical shapes from intruders and subjects because those will shield + // subject handling + size_t n = 0; - if (m_is_merged || (interactions.size () == 1 && ids.empty () && interactions.subject_shape (interactions.begin ()->first).is_box ())) { + if (m_is_merged || (interactions.size () == 1 && interactions.subject_shape (interactions.begin ()->first).is_box ())) { - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + 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 (polygons, ts); + insert_into_hash (subjects, ts); if (! take_all) { poly_check.enter (ts, n, common_box); } else { poly_check.enter (ts, n); } - ++n; - } - - 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 ()) { - if (! take_all) { - poly_check.enter (ti, n, common_box); - } else { - poly_check.enter (ti, n); - } - ++n; - } + n += 2; } } else { @@ -388,17 +378,9 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.clear (); size_t nn = 0; - for (typename shape_interactions::iterator i = interactions.begin (); i != interactions.end (); ++i) { + for (auto i = interactions.begin (); i != interactions.end (); ++i) { const TS &ts = interactions.subject_shape (i->first); - for (typename TS::polygon_edge_iterator e = ts.begin_edge (); ! e.at_end (); ++e) { - ep.insert (*e, nn); - } - ++nn; - } - - for (std::set::const_iterator id = ids.begin (); id != ids.end (); ++id) { - const TI &ti = interactions.intruder_shape (*id).second; - for (typename TI::polygon_edge_iterator e = ti.begin_edge (); ! e.at_end (); ++e) { + for (auto e = ts.begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } ++nn; @@ -411,13 +393,82 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape db::SimpleMerge op (1 /*wc>0*/); ep.process (pg, op); - for (typename std::unordered_set::const_iterator o = spolygons.begin (); o != spolygons.end (); ++o) { + for (auto o = spolygons.begin (); o != spolygons.end (); ++o) { + insert_into_hash (subjects, *o); if (! take_all) { poly_check.enter (*o, n, common_box); } else { poly_check.enter (*o, n); } - ++n; + n += 2; + } + + } + + // intruder handling + + n = 1; + + if (ids.empty ()) { + + // nothing to supply + + } else if (! m_is_merged) { + + // merge needed for intruders - we do a boolean NOT with the subject shape to + // avoid shielding + + ep.clear (); + size_t nn = 0; + + for (auto id = ids.begin (); id != ids.end (); ++id) { + const TI &ti = interactions.intruder_shape (*id).second; + if (subjects.find (ti) == subjects.end ()) { + for (auto e = ti.begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, nn); + } + nn += 2; + } + } + + if (nn > 0) { + + nn = 1; + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = i->begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, nn); + } + nn += 2; + } + + polygons.clear (); + + db::polygon_ref_generator ps (layout, polygons); + db::PolygonGenerator pg (ps, false /*don't resolve holes*/, false); + db::BooleanOp op (db::BooleanOp::ANotB); + ep.process (pg, op); + + for (auto o = polygons.begin (); o != polygons.end (); ++o) { + if (! take_all) { + poly_check.enter (*o, n, common_box); + } else { + poly_check.enter (*o, n); + } + } + + } + + } else { + + 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); + } + } } } From 47c9243c044f421a6750a2f72e7693aa1e8ccc63 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 26 Jun 2022 21:16:34 +0200 Subject: [PATCH 08/15] WIP --- src/db/db/dbPolygon.h | 1 + src/db/db/dbRegionCheckUtils.cc | 18 ++++ src/db/db/dbRegionCheckUtils.h | 3 + src/db/db/dbRegionLocalOperations.cc | 153 ++++++++++++++++----------- 4 files changed, 113 insertions(+), 62 deletions(-) diff --git a/src/db/db/dbPolygon.h b/src/db/db/dbPolygon.h index 1b59dfc5f..d00a099c7 100644 --- a/src/db/db/dbPolygon.h +++ b/src/db/db/dbPolygon.h @@ -3075,6 +3075,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 fcd0e98da..d6aa19b9e 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -210,7 +210,6 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape std::unordered_set polygons; std::unordered_set spolygons; - std::unordered_set subjects; db::EdgeProcessor ep; ep.set_base_verbosity (50); @@ -318,6 +317,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ++nn; } +// @@@ Use edges directly polygons.clear (); db::polygon_ref_generator ps (layout, polygons); @@ -351,13 +351,14 @@ 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 ())) { - // subject handling + // no merge required - size_t n = 0; + // NOTE: we need to eliminate identical shapes from intruders and subjects because those will shield - if (m_is_merged || (interactions.size () == 1 && interactions.subject_shape (interactions.begin ()->first).is_box ())) { + size_t n = 0; + std::unordered_set subjects; for (auto i = interactions.begin (); i != interactions.end (); ++i) { // we can't directly insert because TS may be != TI @@ -371,9 +372,24 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape n += 2; } - } else { + n = 1; - // merge needed for the subject shapes + 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; @@ -386,6 +402,7 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ++nn; } +// @@@ Use edges directly spolygons.clear (); db::polygon_ref_generator ps (layout, spolygons); @@ -394,7 +411,6 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape ep.process (pg, op); for (auto o = spolygons.begin (); o != spolygons.end (); ++o) { - insert_into_hash (subjects, *o); if (! take_all) { poly_check.enter (*o, n, common_box); } else { @@ -403,72 +419,85 @@ check_local_operation::do_compute_local (db::Layout *layout, const shape n += 2; } - } + } else { - // intruder handling - - n = 1; - - if (ids.empty ()) { - - // nothing to supply - - } else if (! m_is_merged) { - - // merge needed for intruders - we do a boolean NOT with the subject shape to - // avoid shielding + // 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 id = ids.begin (); id != ids.end (); ++id) { - const TI &ti = interactions.intruder_shape (*id).second; - if (subjects.find (ti) == subjects.end ()) { - for (auto e = ti.begin_edge (); ! e.at_end (); ++e) { - ep.insert (*e, nn); - } - nn += 2; + 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; } - if (nn > 0) { - - nn = 1; - for (auto i = subjects.begin (); i != subjects.end (); ++i) { - for (auto e = i->begin_edge (); ! e.at_end (); ++e) { - ep.insert (*e, nn); - } - nn += 2; - } - - polygons.clear (); - - db::polygon_ref_generator ps (layout, polygons); - db::PolygonGenerator pg (ps, false /*don't resolve holes*/, false); - db::BooleanOp op (db::BooleanOp::ANotB); - ep.process (pg, op); - - for (auto o = polygons.begin (); o != polygons.end (); ++o) { - if (! take_all) { - poly_check.enter (*o, n, common_box); - } else { - poly_check.enter (*o, n); - } - } - - } - - } else { - 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); + 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); + } + } + } } From 0edeac2efa00ed733e5e0f682d8ca9fdeec1c613 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 26 Jun 2022 23:06:29 +0200 Subject: [PATCH 09/15] WIP --- src/db/db/dbHash.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbHash.h b/src/db/db/dbHash.h index 19913d2c0..9ed98ae0b 100644 --- a/src/db/db/dbHash.h +++ b/src/db/db/dbHash.h @@ -253,8 +253,7 @@ namespace std 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; - unsigned int n = std::numeric_limits::max (); // @@@ + unsigned int n = 20; for (typename db::path::iterator p = o.begin (); p != o.end (); ++p) { if (--n == 0) { h = hfunc (o.points (), h); @@ -289,8 +288,7 @@ namespace std 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; - unsigned int n = std::numeric_limits::max (); // @@@ + unsigned int n = 20; for (typename db::polygon_contour::simple_iterator i = o.begin (); i != o.end (); ++i) { if (--n == 0) { h = hfunc (o.size (), h); @@ -326,8 +324,7 @@ namespace std { h = hfunc (o.hull (), h); // NOTE: using too many points for the hash function just slows down the code. - // @@@ unsigned int n = 20; - unsigned int n = std::numeric_limits::max (); // @@@ + unsigned int n = 20; for (size_t i = 0; i < o.holes (); ++i) { if (--n == 0) { h = hfunc (o.holes (), h); From b02d3b24a818df72b7c84599e66f6b408f65a22f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 11 Nov 2022 23:46:45 +0100 Subject: [PATCH 10/15] WIP: first draft of angle-class selectors for edges and edge pairs for simplifying edge orientation checks --- src/db/db/dbCellVariants.cc | 20 +++++++ src/db/db/dbCellVariants.h | 10 ++++ src/db/db/dbEdgesUtils.cc | 67 +++++++++++++++++++++ src/db/db/dbEdgesUtils.h | 70 ++++++++++++++++++++++ src/db/db/gsiDeclDbEdgePairs.cc | 55 +++++++++++++++++ src/db/db/gsiDeclDbEdges.cc | 43 +++++++++++-- src/drc/drc/built-in-macros/_drc_engine.rb | 12 ++++ src/drc/drc/built-in-macros/_drc_layer.rb | 33 ++++++++-- src/drc/drc/built-in-macros/_drc_tags.rb | 32 +++++++++- 9 files changed, 333 insertions(+), 9 deletions(-) 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/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/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 89516e8fa..c60b21c43 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); @@ -677,6 +692,17 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\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 \\OrthoEdges, \\DiagonalEgdes or \\OrthoDiagonalEdges types.\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 " @@ -702,6 +728,17 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\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 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, \\DiagonalEgdes or \\OrthoDiagonalEdges types.\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 " @@ -838,4 +875,22 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "This class has been introduced in version 0.23.\n" ); +gsi::EnumIn decl_EdgePairsEdgeFilterType ("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 EdgePairs: +gsi::ClassExt decl_EdgePairsEdgeFilterType_into_parent (decl_EdgePairsEdgeFilterType.defs ()); + } diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 911bf9ac6..f2ded4b5c 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" @@ -637,7 +643,7 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "@/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, \\DiagonalEgdes 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/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..8d420316c 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 @@ -907,6 +910,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 +930,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 @@ -998,6 +1017,12 @@ 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::Edges) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.e_value, #{inv.inspect})) + else + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.ep_value, #{inv.inspect})) + end 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..522148a97 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -16,10 +16,40 @@ 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 ep_value + RBA::EdgePairs::OrthoEdges + end + def e_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 ep_value + RBA::EdgePairs::DiagonalEdges + end + def e_value + RBA::Edges::DiagonalEdges + end + end + + # A wrapper for the "diagonal edges" flag for Edges#with_angle or EdgePairs#with_angle + class DRCDiagonalEdges + def ep_value + RBA::EdgePairs::OrthoDiagonalEdges + end + def e_value + RBA::Edges::OrthoDiagonalEdges + end + end + # A wrapper for the padding modes of with_density class DRCDensityPadding attr_accessor :value From 234117006507bc3dc6873927ba9c2cb5880eb6c1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 12 Nov 2022 00:29:50 +0100 Subject: [PATCH 11/15] First tests --- src/db/db/gsiDeclDbEdgePairs.cc | 22 ++------------- src/drc/drc/built-in-macros/_drc_layer.rb | 9 +++---- src/drc/drc/built-in-macros/_drc_tags.rb | 15 +++-------- src/drc/unit_tests/drcSimpleTests.cc | 10 +++++++ testdata/drc/drcSimpleTests_56.drc | 31 ++++++++++++++++++++++ testdata/drc/drcSimpleTests_56.gds | Bin 0 -> 10262 bytes testdata/drc/drcSimpleTests_au56.gds | Bin 0 -> 25346 bytes 7 files changed, 50 insertions(+), 37 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_56.drc create mode 100644 testdata/drc/drcSimpleTests_56.gds create mode 100644 testdata/drc/drcSimpleTests_au56.gds diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index c60b21c43..da526ed66 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -699,7 +699,7 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "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 \\OrthoEdges, \\DiagonalEgdes or \\OrthoDiagonalEdges types.\n" + "and are specified using one of the \\Edges#OrthoEdges, \\Edges#DiagonalEgdes or \\Edges#OrthoDiagonalEdges types.\n" "\n" "This method has been added in version 0.28.\n" ) + @@ -735,7 +735,7 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "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 \\OrthoEdges, \\DiagonalEgdes or \\OrthoDiagonalEdges types.\n" + "and are specified using one of the \\Edges#OrthoEdges, \\Edges#DiagonalEgdes or \\Edges#OrthoDiagonalEdges types.\n" "\n" "This method has been added in version 0.28.\n" ) + @@ -875,22 +875,4 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "This class has been introduced in version 0.23.\n" ); -gsi::EnumIn decl_EdgePairsEdgeFilterType ("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 EdgePairs: -gsi::ClassExt decl_EdgePairsEdgeFilterType_into_parent (decl_EdgePairsEdgeFilterType.defs ()); - } diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 8d420316c..0b1b41634 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -1000,7 +1000,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 @@ -1018,11 +1018,10 @@ CODE 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::Edges) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.e_value, #{inv.inspect})) - else - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.ep_value, #{inv.inspect})) + 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 522148a97..677d45852 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -22,30 +22,21 @@ module DRC # A wrapper for the "ortho edges" flag for Edges#with_angle or EdgePairs#with_angle class DRCOrthoEdges - def ep_value - RBA::EdgePairs::OrthoEdges - end - def e_value + 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 ep_value - RBA::EdgePairs::DiagonalEdges - end - def e_value + 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 ep_value - RBA::EdgePairs::OrthoDiagonalEdges - end - def e_value + def value RBA::Edges::OrthoDiagonalEdges end end 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/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 0000000000000000000000000000000000000000..a9ab615dbd0d4a14e79aa2dc8ea7160ff88b8ffb GIT binary patch literal 10262 zcmb7~O=w(I6vzLWB$Jp%6WjEICfL+ex{%ryq*}0&#)u6qwl-PRqCun@i-=aSbrY$& zXo@bf5iKbwf*%X=e|278Rq@oaiE;(%>CVS=f3;? z=i^0n)oMpH(oyUANvqT^sY`v!->bJ)KQJ{R4RvFAK6UT()a2dj>Xqu(*3_Z;bv5&FV6anKbwTRhYX7s>;P-{y@L}=!mN{woh~#$b zpyZ#s^>O~WbD8hD-d5Z)|D?}9K32@R<1K061Z!usXDb@9IoI=#`XUt%`Vt{J<_pb(v1VssVlb5{nCl|i|_NeG>;9Vo$9UEXAT~+-_a-a zt#0mGzk8D!jeovXnq|ee^^G0&W&d#ZUfY-S>L2Nz*w%bsu+q-6<*bcAx_tbLwyV27 zD#l-vu2|=#e8uXUZFkf=kXEE2pWT0F&sW8Mm`y9Jru-`OnOC>#CZm*Jvl~7yu9?}O zFTbMecNSOHPD_yT9r3f}uQdx%%85rNi`g-s)Y2q=- z?cv_&3XeZm5GVE}8}v1At1};RYd(XN`>^vT_ZdBhjmGbY8yRF}r_MB=o{#@-gN|~f+?vVL1`^-#~+PEM+|DANx zo@9f+Yn$DXCof8FXMc?P%A>XZiT^{*+RC2%+6c3!N29(T`BFObiK&jA5wpSH_4GTo zhliy{KTS4BJ-b7C?*Yl}xs@yYrCbkP?_Cc)F0B1w#R`GSqf-5=Gf>IbJlE$ zQXZjfbW^#P?*^=Jd@Mzvq}Zn1K`QHeinc9^4Zb`kyUu6j4*K%Zu7!D}K`N`K-Cce_vc4-TcaX~ZF3&|zeiQYTM=R@n(AOQNHm46t+buer znUDI)qjk&A(&AE-%A2)K?y@{QH8tF*ho`db{+{25D(1 z>MM`damzVAI3K0*=oDcs(gs(UD98_ig6=^DRgzT#1w99#AQuG+Iu#X^^)=dc zDu9B#7bxg&R8ZCpX_qYn1zi!KAjbv@a%`ZW0|OLfus}iI=GXH45c)PiLEi=_=-U7V zeH);lx&aER8=#=N0Sc-cprB3x3hES~piTh_>Xa+x=MX9mprFqN6jT8~L7xpM$mfCb ze7+X~ z_1S=e+y*GfH-LgZ8&Hq~00lV!P!P?3E7wDa%RoU~1`6UbP!N}af_@ZG&{+ZsdPG1$ zw+AR!?55VYJmUZgdMQ9bR|F{NZvX|I3ZS6p02Fi&fPy|iDb;+A(tJX)q6Z5U)aIpB z@w~j`xj9f!F9QXYFHlg!0tMA*1yxdw0tIy?P*5QP1yvhRP;meS6$emIaR3Ds2T)LP i00k8XP*8CI1(k0Fb#B!DGQ$4Zlb_U0wZByB>e7F2Mabm< literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au56.gds b/testdata/drc/drcSimpleTests_au56.gds new file mode 100644 index 0000000000000000000000000000000000000000..2a9a9f4e64bedec5d189a19a34f9c4386efb4de9 GIT binary patch literal 25346 zcmbW8f2`M48OI+k*L$zrL_xinmkEf3tw_|S>A)d@UY3C%Ev^MI0kwETa*z3a&2Cwa49 z^PK0YZv1Nhvp&xJpPA!lHFl?OcdaM7oY(#EABYw#_3a{lHhX?ZPp ze_##KH9sIa^bpaz{z`P@IMI8*LUfGRcl!-Qk6ua??yDQ8djC5fB)Z{SL>r&y{omu; zbpu51y1OF^kK@{d z3wi&)A$s7Xzdvww<5v40<1voDhiLO=LjxC1@&y!9$#+snxeZ2NcOm77Im zwz7~)ENTlXwZF~#N~~MqT!{@Vr0OhW^9mEY7|*pWZx0hMTSz57xsXaccp;Ve`$8&F zC54o;^O1Hbq!NWwNY&|}@Rq^*5*r`vec@d0?nv_$Qi(Dwq!RsDNF^$zkaFH3(lUiq zqIe3aL>CoNsrpoQKKqu>(Wpgpougz6DXn+QR@hOmqIyf`;&T{j<3cJ?(1lc@OA9Gy z=Ogu6NVyXgY1~38QN)E*5*HLwNfc2?B{4@Kl|(3oR9!sNY!8ztp>VDwrYNM62&9m5 zemN7TY|r~;5T^hNcs)RY=%#_n_)0*5m*+0i8 zs)5RQb3nOyGO-?@fCc~*h!7hnx5^MH7CsFSSAw~K-T)McMjNP%N&yszVF3lS4WK~e z+dxH`qQdtD;^pjIP)7ku=lsgPFQ~r2Tp%{jQc$x23Pkh`R5!C(_B87Apw0F$$%+=9 zjU4T0t#d*8mLUB0#HEr0t)0e8mNp~1{BDO019YqKmm;nD3E~x z6i~2$0(qOUygmeZ8$f})4WK~Y22dbx11J#P018AmfCAACpg?p3C=jOr3dAXZ0&xnU zK%8<<{v3jc15hB(1}G2(01D*U041tCDD>D90m|K-QRaXG8XHg`&ju)w5?pg=AK zP#`M;D3IR(6v$Kn3gjFB1+oW#0(pQe1%EdO6o}2Ul&=JeDuN2n2Ke0^&Bf<0svatw z3&gNsE)b12P?=~HP#~@Z6o?Q31)?@UfrtZ8AmRWNh&TWRA`U=-hyzd{;s6wgH~KEA-dRe^sZ_jqj^e0?#Y^4(qDp)N>Qg&F6mSk$Ib*TNM$W<@(RL zL{olGv~n8PfBu;3Kqq_~`m^)jyq!(qQKIX(R&e0zP(RB2TCU!6^`oIb%P)SCv#O(9 zhsy2HO3yiez2>Xk>)fB^Z@Y}=e<|?1?@EpE{Hq@$I{*Ddp+C#V`*-_Z`k(Ipd*2r7 zUeD&|A3QzDe~6}Xy=VWKP@8uv(fqgb{`vfd{_On28@jx|pMUoWydSUX&+^;&tlfN! zYeKnJ)n)hHS31h|y$?(NNx{%`bS;8Zpe^-nY}UB zkd6*Xe~u6N>cDqmyTEs{-+S`>&>sHgdB4;79XOA?;4fUi%eA(PKIH3nLw{!dA!2*2 z%U9gKQU;i8Wv;4jV zJa6Fleb0{*^PxY>?>@`5!dxHi<~cAo)FJa8_VaU`;QH>+pXIlIpWi$0zlt@Pd*ErJ zPxE_lt-BujvwWo|R&5Tq zn)GKDvi-W&*Ncb#EWc%(>on$c`HI`<`I9_m=&$3eeapO+7UE|p^M>nnT(xg^Up%k- zzD|XCrRDxC|FJ8HzH|Z6NBLbn)gS8Q!~AaC(4XbU_#XG%WZmP}5q)fB=y{{U4&bJpI=Kp`f--@(yJXfT*Q+)& zfADi-zFo)PO6BK5yF6UyHp}nW%is7n`T23X@ddUUtGcaR=QhK8KR0sFw|9Ns`#EXl z`pCgN|K>5)80_y{yNTa>@5yc}*Ovl+SNXX$-e2wdfcF#9j)m*oX4cRCrrv zzrz0VmToK8k-rjq>)JzXZ{72gHe+vJjCH<2Co4Ya*^W*w(7TF{^j+vaJ6v*u|OZ^+Xq-k_I- zZ=0tP&YGuDydh7cctf7%XR*#Q=oH0^PHXYIkm6&1Tc|e_Z_pcxH|Pz;8}x?ajfQj^Yh*j^Yh*j^Yh*j^Yh*PQ;tyoQOBYIf^&LIf^&L zIf^&LIf^&LIf^&LIf^&LIf^&LIf^&LIf@sZ)grH|c!R%Gyy%4%an4Jz{Wa)3#T#^< z;te`a@dlkId|RC-oK@#3KDPUXeoyfRzoqzi{zAW_c!S+iyuofM-e9*BZ?HRxH`pD; z8|;SS<-2bYCn(+!Cn(+!Cn(-v_k?d7CkSVa6BKWV6BKWV6NGOYCkSVa6BKWV6BKWV z6BKXAhef<89~SYZe3;@5`7p&B@?jBg%7-c5kPnM^Q$9@bhJ2Xf#oo5ahy6O%Nrv+m t@uu?^@uu^qc*FTqyy5&syy^TY-f;dR-gN#HZ#aL7Z*%_oIV|m^{{!yxPmcfq literal 0 HcmV?d00001 From 4467089f2a7c5121af0b5114ecd9b48c75843eb0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 12 Nov 2022 00:32:15 +0100 Subject: [PATCH 12/15] More test data --- testdata/drc/drcSimpleTests_au56d.gds | Bin 0 -> 17250 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_au56d.gds diff --git a/testdata/drc/drcSimpleTests_au56d.gds b/testdata/drc/drcSimpleTests_au56d.gds new file mode 100644 index 0000000000000000000000000000000000000000..46d20057960fb7c4d14167951c839dc347465de5 GIT binary patch literal 17250 zcmeI4&ud*p6vt0q@?O&9wRzPd!57q;&>$!_B1#3NDX9=z8>_|Ef(j)zDG_U+lC8SY z{sjtdR0KDQ-I;}JY5lQLT)3$VLBSv3LR;x3o|!xIa?bVM@0&SYxXFWZ(|7OZ&adox1 zlYCEiboUQj)qNjy_dUa0`#RC{Z}I(xiI$d#?j9#v|DEf3glO&K;(WNy--jKR``f2!1fiBzeV5~)&OCsM9~A_5Yr(l{ql zRioX%4@)sC%X+bXyh;(ANV(n+x=5s44TX9Vsggz#sVXIgJ_Nl{rH}qpNnMFGENL^5 zDk(dWD)~YpRdSz1s^npkI$Ixyz&8K|q8CsgdI1GI3Q!>C0R>_nP$1?31!5jhzg}}@cHF>eF2XG6!0iO0gnO{@Tisgb^!+f6mS4Q z0WAXxXc0{Q|J&=;V9ivkL` zD4>9r0R_AlP*r{y=5(VW4)5*F>Glx~{!+|=d?c>$JnJ5w0KFaNZ2QQ2cNOmy8Tfep zbl}~bE1a+84^D0Jhv#^A5e@Tv?(is={Tt8q){0{|U&}vomgknA6Fu-9(ayy%M`Zpu z&$`^RC!DY4_rK3`&DVIY$;a+X1?S%37eCzkp3m3vk56#G!TVzzU&I8I#RwGLj? zh{yPS=W2bs$GELc){Q$ly>y@5I>zt$SX+M=uY7+Mx4rlc-reJ;${02b$>;UpX3w!Pw|HKD_)jwseja8hHt6z4ZfxL=o=ZnrFesH zMLyTJB4_n2!JB+b@P%JRe@gT%#T$IUcZ_th6qi3f`n!#T)wn8`=Mc=a%9P z&n?9pd_(bu=a%9P@uzshb4&5E{fWM(_!z$#zNdJD?P{5-A1w0B+K+Avvssxl9sO^0V+zEbXAkY`s6#!fmP{0oX z1^f_Dz@t{e`&r&TNk9QD0}8}Epg_z63dB61z|#Rxz~=!4;_zJkcmc--lf&@&Kc9Ug$?S@Z0+q zw)ZW>b;<303w~dLe~Yfv-T%CAL9XK^=07e=-5b^+6)($ATt`&AVI5BKaUC+oYr?)2 z!JF361#enMSG-|8?}{8hgYPTe;QNY~D?Y|= z#yYy<4eRKNH>{&8-k{&e=dPni&bp4S^3Dd`DBhqO#ml%!)Q#c|x>3AAH;OmtM)9)! ziMmm|A?_3(b(itn5WMNRA$ZesL-B^^hRUDE_GkEx;tjr|c!TdK-Z1`=&-IreJ;tjfwe6H>zXVtynP0tI#o1PbnH#{#y-rZDx!JF!@_?-Ip$@`DAS|=8t zYR%{^@h?2I-EU72ZEoK9p~#u3v#r|)_+-1=oa5iA@SpZ%^U!_q=Yl1gy;#rk^{3_a nZ*ac-p-a8%+p#A67ZmbK>-6YiFGE#p%GZQaP`$V(hlc21Kj!hT literal 0 HcmV?d00001 From 30ab1a13ca690b912af72792d2cbfb1693e9ad6a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 12 Nov 2022 18:16:57 +0100 Subject: [PATCH 13/15] Added tests, documentation, doc bug fixes for angle-class edge selectors --- src/db/db/gsiDeclDbEdgePairs.cc | 58 +++++++++++++++++++++-- src/db/db/gsiDeclDbEdges.cc | 4 +- src/doc/doc/about/drc_ref_layer.xml | 31 ++++++++++-- src/drc/drc/built-in-macros/_drc_layer.rb | 4 ++ testdata/ruby/dbEdgePairsTest.rb | 21 ++++++++ testdata/ruby/dbEdgesTest.rb | 13 +++++ 6 files changed, 120 insertions(+), 11 deletions(-) diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index da526ed66..d374dd526 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -676,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" @@ -690,6 +698,14 @@ 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"), @@ -699,7 +715,15 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "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#DiagonalEgdes or \\Edges#OrthoDiagonalEdges types.\n" + "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" ) + @@ -712,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" @@ -726,16 +758,32 @@ 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 are returned.\n" + "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#DiagonalEgdes or \\Edges#OrthoDiagonalEdges types.\n" + "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" ) + diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index f2ded4b5c..e2977296f 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -639,7 +639,7 @@ 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), @@ -661,7 +661,7 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "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, \\DiagonalEgdes or \\OrthoDiagonalEdges types.\n" + "and are specified using one of the \\OrthoEdges, \\DiagonalEdges or \\OrthoDiagonalEdges types.\n" "\n" "This method has been added in version 0.28.\n" ) + 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_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 0b1b41634..8e3c13d69 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -896,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: # 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 From d8f91733a733290a8012acff4c3d455a46765f9d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 19 Nov 2022 00:08:27 +0100 Subject: [PATCH 14/15] Updated two DRC tests after performance enhancement The performance enhancement targets towards not merging shapes before width/space check. This means that with an overlapping input, more DRC violation markers are created and markers appear in different places in the hierarchy. However, the geometry stays the same. --- testdata/drc/drcSimpleTests_au25d.gds | Bin 8094 -> 8990 bytes testdata/drc/drcSimpleTests_au48d.gds | Bin 5162 -> 5290 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/testdata/drc/drcSimpleTests_au25d.gds b/testdata/drc/drcSimpleTests_au25d.gds index 554b845d26679117b5c3bd7e57e6df2e5c560492..a8e351c22dc7b52a6fb1e4651be49c0266e4a9f7 100644 GIT binary patch delta 1143 zcmaJEd6+tF36j7@SA&U}(h;(BJTC!7$1}TD@ zf}Ew$b|;kCh#Tp$i!NPt;mUR;5<-zuib!$1FT>bYYzJn}dw1?V_uTW|N_pjGodP6D zbCmT-L*GcD5h5C(Q)?QQ0(Wm$iQ@i2>&Ba_vs>?fT$}lHY3*ITM%lqrxhFZ^*6-_Q zP=454^WjS{FDDiq=U0&^u?R^v(Niku1ywoq^*9_dVQUuFwdAz_5v%+AIL^@u4MbV* z5S^#vV@KKvprPC=WahXgMI`XIN;I*3jBXsCiES)v23lGQOG*Z-n)1?3h>f1t zo+viz&OS^O&?I_7fsn-3lp=mtu2+B77-!bwG3!{;F2a^0MQFs_^crS)@ zuZS4h8(w(r{{N@Zu5t%maU+5q-&5q3GJhY+Wvqt7*b7;(!xqeN1;}uoMQ35gHqnb#u@|$@ioSq3vWZfhuV0)i5?qa%F-oSUkkq;sy$9H|aWWy<%guWcvI8EKA=vW_(C6Ucg=_w_U~ ptS7@eF$?k5QXtMPgx02!wGm$dX{1fJKmi+wL%sHoGi~?Uvz` z0ksPZ771xm1xb;=VU`YM+ z*}JtkP*bm8`9jD<(tUCLbolG@?X$u6&f?Q}Br3^!c^}7b%zGri{#+=vwD=$U+bXe6 zVRAUdnQGyKJYv?-He{}Fx!wMKDR{QqKcij7JJm**>0+)CBD-+3AtKGk@_~m~bFs;K z2p+OGJcNdesNBPGLPu;2@Wt+6ozd|m;UbVdOpLvT@*by|KJSIrhm+S4=BSk;H}4^0 zy~+Cs^DbKKcRWXWn9Ctr#VMk~05|dn;(~`|afQ(CVr^;&O!6%9nOhe9l&EQPZ^=ho zq89tW8sL%r9FavY?Mx9f+70fl6UA6IxxYc(#uQ7=!zO2S{#o{L4=vr&au{L4=vr&au Date: Mon, 21 Nov 2022 22:57:44 +0100 Subject: [PATCH 15/15] Don't crash netlist browser when there is no netlist --- src/layui/layui/layIndexedNetlistModel.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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