diff --git a/Changelog b/Changelog index 4003ed0b7..8aa8a9287 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,31 @@ +0.30.10 (2026-07-22): +* Bugfix: %GITHUB%/issues/2384 Deep DRC failure with scaled instances +* Enhancement: %GITHUB%/issues/2391 Grid Features: switching between coarser and finier grids with keyboard shortcut +* Bugfix: %GITHUB%/issues/2392 Wrong PCell instantiations when switching technologies +* Bugfix: %GITHUB%/issues/2396 Python objects did nott support weak refs +* Bugfix: Region#edges was not working properly when used on Regions with properties + enabled. Now, with properties enabled, the regions are consistently formed from polygons with + identical properties before generating the edges. +* Enhancement: DRC two-layer checks now do not necessarily require merging for the + first input. This in general speeds up the operation in certain cases. + As a side effect, the error markers may be split according to partial input + polygons, as is the case for plain "space" check already. +* Enhancement: Use multiple drawing workers for image_with_options rendering +* Bugfix: Update layer names in layer list (when used in addition to layer/datatypes) on reload + As a side effect, the layer properties nodes will update the "real source" according to + the actual name if the name is used in addition to layer/datatypes. Also, the layout readers + will update layer names in the layer properties of the Layout object. +* Bugfix: Marek's bug: move interactive/copy interactive did not do anything when the mouse wasn't moved +* Bugfix: Entering '16:17' (or other nonsense) into 'new layout' layer list made KLayout hang +* Bugfix: API: removing a pin from a Circuit does not crash if the pin was not disconnected before. + Now it's disconnected automatically +* Enhancement: Improved the image pixel value display: favors selected image, and follows mouse instantly +* Bugfix: When images are selected and images are turned off, the selection no longer persists +* Bugfix: Using PCell parameter name 'name' won't break PCell's cell_name_impl default implementation +* Enhancement: New API functions + * Create a Layout as a deep copy of a cell (Layout#new) + * PixelBuffer#to_bytes and PixelBuffer#from_bytes for zero-overhead RGBA pixel access + 0.30.9 (2026-05-25): * Bugfix: %GITHUB%/issues/2331 Screen size error (Assertion tlPixelBuffer.cc:260 n < m_height was not true) * Enhancement: %GITHUB%/issues/2335 Editor Options (F3) behaviour has changed diff --git a/Changelog.Debian b/Changelog.Debian index 44cba48fc..0daf6cb6c 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,10 @@ +klayout (0.30.10-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Mon, 20 Jul 2026 23:20:04 +0200 + klayout (0.30.9-1) unstable; urgency=low * New features and bugfixes diff --git a/src/db/db/built-in-macros/pcell_declaration_helper.lym b/src/db/db/built-in-macros/pcell_declaration_helper.lym index 774a33028..4e57ef672 100644 --- a/src/db/db/built-in-macros/pcell_declaration_helper.lym +++ b/src/db/db/built-in-macros/pcell_declaration_helper.lym @@ -289,6 +289,11 @@ module RBA alias_method :_layout_base, :layout end + # makes PCellDeclaration's "name" method available + if ! self.method_defined?(:_name_base) + alias_method :_name_base, :name + end + # import the Type... constants from PCellParameterDeclaration PCellParameterDeclaration.constants.each do |c| if !const_defined?(c) @@ -595,7 +600,7 @@ module RBA # default implementation def cell_name_impl - self.name + _name_base() end # default implementation diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 1886f26ab..37b1708ee 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -631,14 +631,14 @@ namespace { class OutputPairHolder { public: - OutputPairHolder (InteractingOutputMode output_mode, bool merged_semantics) + OutputPairHolder (InteractingOutputMode output_mode, bool merged_semantics, bool min_coherence) { if (output_mode == None) { return; } if (output_mode == Positive || output_mode == Negative || output_mode == PositiveAndNegative) { - m_positive.reset (new FlatRegion (merged_semantics)); + m_positive.reset (new FlatRegion (merged_semantics, 0.0, 0, min_coherence)); m_results.push_back (& m_positive->raw_polygons ()); } else { m_results.push_back ((db::Shapes *) 0); @@ -667,7 +667,7 @@ private: std::pair AsIfFlatRegion::in_and_out_generic (const Region &other, InteractingOutputMode output_mode) const { - OutputPairHolder oph (output_mode, merged_semantics ()); + OutputPairHolder oph (output_mode, merged_semantics (), min_coherence ()); if (output_mode == None) { return oph.region_pair (); @@ -717,7 +717,7 @@ AsIfFlatRegion::in_and_out_generic (const Region &other, InteractingOutputMode o std::pair AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const { - OutputPairHolder oph (output_mode, merged_semantics () || is_merged ()); + OutputPairHolder oph (output_mode, merged_semantics () || is_merged (), min_coherence ()); if (output_mode == None) { return oph.region_pair (); @@ -756,7 +756,7 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOut std::vector > others; others.push_back (counting ? other.begin_merged () : other.begin ()); - std::unique_ptr output (new FlatRegion (merged_semantics ())); + std::unique_ptr output (new FlatRegion (merged_semantics (), 0.0, 0, min_coherence ())); std::vector results; results.push_back (&output->raw_polygons ()); @@ -768,7 +768,7 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOut std::pair AsIfFlatRegion::selected_interacting_generic (const Texts &other, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const { - OutputPairHolder oph (output_mode, merged_semantics () || is_merged ()); + OutputPairHolder oph (output_mode, merged_semantics () || is_merged (), min_coherence ()); if (output_mode == None) { return oph.region_pair (); @@ -814,7 +814,7 @@ AsIfFlatRegion::selected_interacting_generic (const Texts &other, InteractingOut std::pair AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, bool touching, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const { - OutputPairHolder oph (output_mode, merged_semantics () || is_merged ()); + OutputPairHolder oph (output_mode, merged_semantics () || is_merged (), min_coherence ()); if (output_mode == None) { return oph.region_pair (); @@ -941,7 +941,7 @@ AsIfFlatRegion::pull_generic (const Region &other, int mode, bool touching) cons std::vector > others; others.push_back (other.begin_merged ()); - std::unique_ptr output (new FlatRegion (other.merged_semantics () || other.is_merged ())); + std::unique_ptr output (new FlatRegion (other.merged_semantics () || other.is_merged (), 0.0, 0, min_coherence ())); std::vector results; results.push_back (&output->raw_polygons ()); @@ -1106,7 +1106,7 @@ AsIfFlatRegion::scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db throw tl::Exception (tl::to_string (tr ("Scale and snap requires positive and non-null magnification or divisor values"))); } - std::unique_ptr new_region (new FlatRegion (merged_semantics ())); + std::unique_ptr new_region (new FlatRegion (merged_semantics (), 0.0, 0, min_coherence ())); gx = std::max (db::Coord (1), gx); gy = std::max (db::Coord (1), gy); @@ -1184,13 +1184,7 @@ AsIfFlatRegion::width_check (db::Coord d, const RegionCheckOptions &options) con EdgePairsDelegate * AsIfFlatRegion::space_or_isolated_check (db::Coord d, const RegionCheckOptions &options, bool isolated) const { - if (options.opposite_filter != NoOppositeFilter || options.rect_filter != NoRectFilter || options.shielded) { - // NOTE: we have to use the "foreign" scheme with a filter because only this scheme - // guarantees that all subject shapes are visited. - return run_check (db::SpaceRelation, isolated, foreign_regionptr (), d, options); - } else { - return run_check (db::SpaceRelation, isolated, subject_regionptr (), d, options); - } + return run_check (db::SpaceRelation, isolated, 0, d, options); } EdgePairsDelegate * @@ -1239,28 +1233,43 @@ EdgePairsDelegate * AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, const RegionCheckOptions &options) const { // force different polygons in the different properties case to skip intra-polygon checks - if (pc_always_different (options.prop_constraint)) { + if (! other && pc_always_different (options.prop_constraint)) { different_polygons = true; } - bool needs_merged_primary = different_polygons || options.needs_merged (); + bool needs_merged_primary = (! other && different_polygons) || options.needs_merged (); + bool primary_is_merged = is_merged (); + db::RegionIterator polygons; - db::RegionIterator polygons (needs_merged_primary ? begin_merged () : begin ()); - bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged (); + if (! merged_semantics ()) { + primary_is_merged = true; // means: don't merge again + needs_merged_primary = false; + polygons = begin (); + } else if (! needs_merged_primary) { + // The implementation may run faster if the primary is not merged + primary_is_merged = false; + polygons = begin_unmerged (); + } else { + primary_is_merged = true; + polygons = begin_merged (); + } EdgeRelationFilter check (rel, d, options); std::vector others; std::vector foreign; - bool has_other = false; bool other_is_merged = true; - if (other == subject_regionptr () || other == foreign_regionptr ()) { - foreign.push_back (other == foreign_regionptr ()); + if (! other) { + + foreign.push_back (true); others.push_back (polygons); other_is_merged = primary_is_merged; + } else { + foreign.push_back (false); + if (! other->merged_semantics ()) { others.push_back (other->begin ()); other_is_merged = true; @@ -1272,7 +1281,13 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, others.push_back (other->begin ()); other_is_merged = other->is_merged (); } - has_other = true; + + // adds another intruder section to implement subject merging ("primary_intruders") + if (! primary_is_merged) { + foreign.push_back (true); + others.push_back (polygons); + } + } std::unique_ptr output (new FlatEdgePairs ()); @@ -1282,7 +1297,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, if (pc_skip (options.prop_constraint)) { - db::check_local_operation op (check, different_polygons, primary_is_merged, has_other, other_is_merged, options); + db::check_local_operation op (check, different_polygons, primary_is_merged, other != 0, other_is_merged, options); db::local_processor proc; proc.set_base_verbosity (base_verbosity ()); @@ -1293,7 +1308,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, } else { - db::check_local_operation_with_properties op (check, different_polygons, primary_is_merged, has_other, other_is_merged, options); + db::check_local_operation_with_properties op (check, different_polygons, primary_is_merged, other != 0, other_is_merged, options); db::local_processor proc; proc.set_base_verbosity (base_verbosity ()); @@ -1334,7 +1349,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord } RegionDelegate * -AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const +AsIfFlatRegion::merged (bool min_coh, unsigned int min_wc, bool join_properties_on_merge) const { if (empty ()) { @@ -1351,8 +1366,8 @@ AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc, bool join_prope } else { - std::unique_ptr new_region (new FlatRegion (true)); - merge_polygons_to (new_region->raw_polygons (), min_coherence, min_wc, join_properties_on_merge); + std::unique_ptr new_region (new FlatRegion (true, 0, 0, min_coh)); + merge_polygons_to (new_region->raw_polygons (), min_coh, min_wc, join_properties_on_merge); return new_region.release (); @@ -1695,7 +1710,7 @@ AsIfFlatRegion::and_or_not_with (bool is_and, const Region &other, PropertyConst ep.insert (*p, n); } - std::unique_ptr new_region (new FlatRegion (true)); + std::unique_ptr new_region (new FlatRegion (true, 0.0, 0, min_coherence ())); db::BooleanOp op (is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); @@ -1772,12 +1787,12 @@ AsIfFlatRegion::andnot_with (const Region &other, PropertyConstraint property_co ep.insert (*p, n); } - std::unique_ptr new_region1 (new FlatRegion (true)); + std::unique_ptr new_region1 (new FlatRegion (true, 0.0, 0, min_coherence ())); db::BooleanOp op1 (db::BooleanOp::And); db::ShapeGenerator pc1 (new_region1->raw_polygons (), true /*clear*/); db::PolygonGenerator pg1 (pc1, false /*don't resolve holes*/, min_coherence ()); - std::unique_ptr new_region2 (new FlatRegion (true)); + std::unique_ptr new_region2 (new FlatRegion (true, 0.0, 0, min_coherence ())); db::BooleanOp op2 (db::BooleanOp::ANotB); db::ShapeGenerator pc2 (new_region2->raw_polygons (), true /*clear*/); db::PolygonGenerator pg2 (pc2, false /*don't resolve holes*/, min_coherence ()); @@ -1860,7 +1875,7 @@ AsIfFlatRegion::xor_with (const Region &other, PropertyConstraint prop_constrain ep.insert (*p, n); } - std::unique_ptr new_region (new FlatRegion (true)); + std::unique_ptr new_region (new FlatRegion (true, 0.0, 0, min_coherence ())); db::BooleanOp op (db::BooleanOp::Xor); db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); @@ -1916,7 +1931,7 @@ AsIfFlatRegion::or_with (const Region &other, PropertyConstraint /*prop_constrai ep.insert (*p, n); } - std::unique_ptr new_region (new FlatRegion (true)); + std::unique_ptr new_region (new FlatRegion (true, 0.0, 0, min_coherence ())); db::BooleanOp op (db::BooleanOp::Or); db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index db2354046..9a6f2bef4 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -353,6 +353,7 @@ Pin &Circuit::add_pin (const std::string &name) void Circuit::remove_pin (size_t id) { if (id < m_pin_by_id.size () && ! tl::is_null_iterator (m_pin_by_id [id])) { + connect_pin (id, 0); m_pins.erase (m_pin_by_id [id]); m_pin_by_id [id] = pin_list::iterator (); } diff --git a/src/db/db/dbCommonReader.cc b/src/db/db/dbCommonReader.cc index 15cb0fb2e..7d4a1f1f3 100644 --- a/src/db/db/dbCommonReader.cc +++ b/src/db/db/dbCommonReader.cc @@ -541,31 +541,58 @@ CommonReaderBase::open_dl_uncached (db::Layout &layout, const LDPair &dl) } unsigned int nl = layout.insert_layer (lp); + + // change OASIS layer name if a layer already exists by layer/datatype, but has a different name + if (! lp.name.empty () && layout.get_properties (nl).name != lp.name) { + layout.set_properties (nl, lp); + } + m_layer_map_out.map (dl, nl, lp); m_layers_created.insert (nl); return std::make_pair (true, nl); - } else if (li.size () == 1) { - - m_layer_map_out.map (dl, *li.begin (), layout.get_properties (*li.begin ())); - - return std::make_pair (true, *li.begin ()); - } else { - for (std::set::const_iterator i = li.begin (); i != li.end (); ++i) { - m_layer_map_out.mmap (dl, *i, layout.get_properties (*i)); + for (auto i = li.begin (); i != li.end (); ++i) { + + // change OASIS layer name if a layer exists by layer/datatype, but has a different name + const tl::interval_map *names_dmap = m_layer_names.mapped (dl.layer); + if (names_dmap != 0) { + const std::string *name = names_dmap->mapped (dl.datatype); + if (name != 0) { + db::LayerProperties lp_out = layout.get_properties (*i); + if (lp_out.name != *name) { + lp_out.name = *name; + layout.set_properties (*i, lp_out); + } + } + } + } - std::map, unsigned int>::iterator mmp = m_multi_mapping_placeholders.find (li); - if (mmp == m_multi_mapping_placeholders.end ()) { - // create a placeholder layer - mmp = m_multi_mapping_placeholders.insert (std::make_pair (li, layout.insert_layer ())).first; - } + if (li.size () == 1) { - return std::make_pair (true, mmp->second); + m_layer_map_out.map (dl, *li.begin (), layout.get_properties (*li.begin ())); + + return std::make_pair (true, *li.begin ()); + + } else { + + for (std::set::const_iterator i = li.begin (); i != li.end (); ++i) { + m_layer_map_out.mmap (dl, *i, layout.get_properties (*i)); + } + + std::map, unsigned int>::iterator mmp = m_multi_mapping_placeholders.find (li); + if (mmp == m_multi_mapping_placeholders.end ()) { + // create a placeholder layer + mmp = m_multi_mapping_placeholders.insert (std::make_pair (li, layout.insert_layer ())).first; + } + + return std::make_pair (true, mmp->second); + + } } } diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 43eac39bf..bb739ed54 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -179,8 +179,10 @@ DeepRegion::~DeepRegion () DeepRegion::DeepRegion (const DeepRegion &other) : MutableRegion (other), DeepShapeCollectionDelegateBase (other), m_merged_polygons_valid (other.m_merged_polygons_valid), + m_merged_polygons_min_coherence (other.m_merged_polygons_min_coherence), m_merged_polygons_boc_hash (other.m_merged_polygons_boc_hash), - m_is_merged (other.m_is_merged) + m_is_merged (other.m_is_merged), + m_is_merged_min_coherence (other.m_is_merged_min_coherence) { if (m_merged_polygons_valid) { m_merged_polygons = other.m_merged_polygons.copy (); @@ -196,8 +198,10 @@ DeepRegion::operator= (const DeepRegion &other) DeepShapeCollectionDelegateBase::operator= (other); m_merged_polygons_valid = other.m_merged_polygons_valid; + m_merged_polygons_min_coherence = other.m_merged_polygons_min_coherence; m_merged_polygons_boc_hash = other.m_merged_polygons_boc_hash; m_is_merged = other.m_is_merged; + m_is_merged_min_coherence = other.m_is_merged_min_coherence; if (m_merged_polygons_valid) { m_merged_polygons = other.m_merged_polygons.copy (); } @@ -210,9 +214,11 @@ DeepRegion::operator= (const DeepRegion &other) void DeepRegion::init () { m_merged_polygons_valid = false; + m_merged_polygons_min_coherence = false; m_merged_polygons_boc_hash = 0; m_merged_polygons = db::DeepLayer (); m_is_merged = false; + m_is_merged_min_coherence = false; } RegionDelegate * @@ -228,7 +234,7 @@ void DeepRegion::merged_semantics_changed () void DeepRegion::min_coherence_changed () { - set_is_merged (false); + // merged status is tracked in separate variables and is validated on inquiry } void DeepRegion::join_properties_on_merge_changed () @@ -468,7 +474,7 @@ DeepRegion::empty () const bool DeepRegion::is_merged () const { - return m_is_merged; + return m_is_merged && (m_is_merged_min_coherence == min_coherence ()); } const db::Polygon * @@ -739,18 +745,21 @@ DeepRegion::merged_deep_layer () const bool DeepRegion::merged_polygons_available () const { - return m_is_merged || (m_merged_polygons_valid && m_merged_polygons_boc_hash == deep_layer ().breakout_cells_hash ()); + return (m_is_merged && m_is_merged_min_coherence == min_coherence ()) || + (m_merged_polygons_valid && m_merged_polygons_min_coherence == min_coherence () && m_merged_polygons_boc_hash == deep_layer ().breakout_cells_hash ()); } void DeepRegion::ensure_merged_polygons_valid () const { - if (! m_merged_polygons_valid || (! m_is_merged && m_merged_polygons_boc_hash != deep_layer ().breakout_cells_hash ())) { + if (! m_merged_polygons_valid || m_merged_polygons_min_coherence != min_coherence () || + ((! m_is_merged || m_is_merged_min_coherence != min_coherence ()) && m_merged_polygons_boc_hash != deep_layer ().breakout_cells_hash ())) { - if (m_is_merged) { + if (m_is_merged && m_is_merged_min_coherence == min_coherence ()) { // NOTE: this will reuse the deep layer reference m_merged_polygons = deep_layer (); + m_merged_polygons_min_coherence = min_coherence (); } else { @@ -796,6 +805,7 @@ DeepRegion::ensure_merged_polygons_valid () const } m_merged_polygons_valid = true; + m_merged_polygons_min_coherence = min_coherence (); m_merged_polygons_boc_hash = deep_layer ().breakout_cells_hash (); } @@ -813,7 +823,9 @@ DeepRegion::ensure_unmerged_polygons_valid () const db::DeepLayer &polygons = const_cast (deep_layer ()); m_merged_polygons_valid = true; + m_merged_polygons_min_coherence = m_is_merged_min_coherence; m_is_merged = false; + m_is_merged_min_coherence = min_coherence (); m_merged_polygons_boc_hash = deep_layer ().breakout_cells_hash (); db::Layout &layout = polygons.layout (); @@ -845,6 +857,7 @@ void DeepRegion::set_is_merged (bool f) { m_is_merged = f; + m_is_merged_min_coherence = min_coherence (); m_merged_polygons_valid = false; m_merged_polygons_boc_hash = 0; m_merged_polygons = db::DeepLayer (); @@ -2026,6 +2039,7 @@ DeepRegion::merged () const res->deep_layer ().layer (); + res->set_min_coherence (min_coherence ()); res->set_is_merged (true); return res.release (); } @@ -2370,48 +2384,75 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons { if (empty ()) { return new db::DeepEdgePairs (deep_layer ().derived ()); - } else if (other && ! is_subject_regionptr (other) && other->empty () && ! options.negative) { + } else if (other && other->empty () && ! options.negative) { return new db::DeepEdgePairs (deep_layer ().derived ()); } - // force different polygons in the different properties case to skip intra-polygon checks - if (pc_always_different (options.prop_constraint)) { - // TODO: this forces merged primaries, so maybe that is not a good optimization? - different_polygons = true; - } - + // delegate to AsIfFlatRegion if the other region is not a deep one const db::DeepRegion *other_deep = 0; - 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 { + if (other) { other_deep = dynamic_cast (other->delegate ()); if (! other_deep) { return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, options); } + } + + // force different polygons in the different properties case to skip intra-polygon checks + if (! other_deep && pc_always_different (options.prop_constraint)) { + // TODO: this forces merged primaries, so maybe that is not a good optimization? + different_polygons = true; + } + + // primary input + + bool needs_merged_primary = (! other_deep && different_polygons) || options.needs_merged (); + bool primary_is_merged = false; + + db::DeepLayer polygons; + + if (! merged_semantics ()) { + primary_is_merged = true; // means: don't merge again + polygons = deep_layer (); + } else if (! needs_merged_primary) { + // The implementation may run faster if the primary is not merged + primary_is_merged = false; + ensure_unmerged_polygons_valid (); + polygons = deep_layer (); + } else { + primary_is_merged = true; + polygons = merged_deep_layer (); + } + + // other input + + std::vector other_layers; + bool other_is_merged = true; + + if (! other_deep) { + + other_layers.push_back (foreign_idlayer ()); + other_is_merged = primary_is_merged; + + } else { + if (! other->merged_semantics ()) { - other_layer = other_deep->deep_layer ().layer (); + other_layers.push_back (other_deep->deep_layer ().layer ()); 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_layers.push_back (other_deep->merged_deep_layer ().layer ()); other_is_merged = true; } else { - other_layer = other_deep->deep_layer ().layer (); + other_layers.push_back (other_deep->deep_layer ().layer ()); other_is_merged = other->is_merged (); } - } - const db::DeepLayer &polygons = needs_merged_primary ? merged_deep_layer () : deep_layer (); + // adds another intruder section to implement subject merging ("primary_intruders") + if (! primary_is_merged) { + other_layers.push_back (foreign_idlayer ()); + } + + } EdgeRelationFilter check (rel, d, options); @@ -2435,7 +2476,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons configure_proc (proc); proc.set_threads (polygons.store ()->threads ()); - proc.run (&op, polygons.layer (), other_layer, res->deep_layer ().layer ()); + proc.run (&op, polygons.layer (), other_layers, res->deep_layer ().layer ()); } else { @@ -2448,7 +2489,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons configure_proc (proc); proc.set_threads (polygons.store ()->threads ()); - proc.run (&op, polygons.layer (), other_layer, res->deep_layer ().layer ()); + proc.run (&op, polygons.layer (), other_layers, res->deep_layer ().layer ()); } diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 10a9277f1..8a18ef34e 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -179,8 +179,10 @@ private: mutable DeepLayer m_merged_polygons; mutable bool m_merged_polygons_valid; + mutable bool m_merged_polygons_min_coherence; mutable size_t m_merged_polygons_boc_hash; mutable bool m_is_merged; + mutable bool m_is_merged_min_coherence; void init (); void ensure_merged_polygons_valid () const; diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index 720f376de..f32d1d988 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -45,34 +45,43 @@ FlatRegion::FlatRegion (const FlatRegion &other) m_area_ratio (other.m_area_ratio), m_max_vertex_count (other.m_max_vertex_count) { init (); + set_min_coherence (other.min_coherence ()); m_is_merged = other.m_is_merged; + m_is_merged_min_coherence = other.m_is_merged_min_coherence; m_merged_polygons_valid = other.m_merged_polygons_valid; + m_merged_polygons_min_coherence = other.m_merged_polygons_min_coherence; } -FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged, double area_ratio, size_t max_vertex_count) +FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged, double area_ratio, size_t max_vertex_count, bool min_coh) : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)), m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count) { init (); + set_min_coherence (min_coh); m_is_merged = is_merged; + m_is_merged_min_coherence = min_coh; } -FlatRegion::FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged, double area_ratio, size_t max_vertex_count) +FlatRegion::FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged, double area_ratio, size_t max_vertex_count, bool min_coh) : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)), m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count) { init (); + set_min_coherence (min_coh); m_is_merged = is_merged; + m_is_merged_min_coherence = min_coh; transform_generic (trans); set_merged_semantics (merged_semantics); } -FlatRegion::FlatRegion (bool is_merged, double area_ratio, size_t max_vertex_count) +FlatRegion::FlatRegion (bool is_merged, double area_ratio, size_t max_vertex_count, bool min_coh) : MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)), m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count) { init (); + set_min_coherence (min_coh); m_is_merged = is_merged; + m_is_merged_min_coherence = min_coh; } FlatRegion::~FlatRegion () @@ -83,6 +92,7 @@ FlatRegion::~FlatRegion () void FlatRegion::set_is_merged (bool m) { m_is_merged = m; + m_is_merged_min_coherence = min_coherence (); } void FlatRegion::invalidate_cache () @@ -95,7 +105,9 @@ void FlatRegion::invalidate_cache () void FlatRegion::init () { m_is_merged = false; + m_is_merged_min_coherence = false; m_merged_polygons_valid = false; + m_merged_polygons_min_coherence = false; } void FlatRegion::merged_semantics_changed () @@ -112,9 +124,7 @@ void FlatRegion::join_properties_on_merge_changed () void FlatRegion::min_coherence_changed () { - m_is_merged = false; - mp_merged_polygons->clear (); - m_merged_polygons_valid = false; + // merged status is tracked in separate variables and is validated on inquiry } void FlatRegion::reserve (size_t n) @@ -122,12 +132,25 @@ void FlatRegion::reserve (size_t n) mp_polygons->reserve (db::Polygon::tag (), n); } +bool +FlatRegion::merged_polygons_valid () const +{ + return m_merged_polygons_valid && m_merged_polygons_min_coherence == min_coherence (); +} + +bool +FlatRegion::merged_polygons_available () const +{ + return is_merged () || merged_polygons_valid (); +} + void FlatRegion::ensure_merged_polygons_valid () const { - if (! m_merged_polygons_valid) { + if (! merged_polygons_valid ()) { merge_polygons_to (*mp_merged_polygons, min_coherence (), 0, join_properties_on_merge ()); m_merged_polygons_valid = true; + m_merged_polygons_min_coherence = min_coherence (); } } @@ -140,7 +163,9 @@ FlatRegion::ensure_unmerged_polygons_valid () const mp_merged_polygons.reset (new db::Shapes (*mp_polygons)); m_merged_polygons_valid = true; + m_merged_polygons_min_coherence = m_is_merged_min_coherence; m_is_merged = false; + m_is_merged_min_coherence = min_coherence (); break_polygons (*mp_polygons, m_max_vertex_count, m_area_ratio); } @@ -152,7 +177,7 @@ RegionIteratorDelegate *FlatRegion::begin () const RegionIteratorDelegate *FlatRegion::begin_merged () const { - if (! merged_semantics () || m_is_merged) { + if (! merged_semantics () || is_merged ()) { return begin (); } else { ensure_merged_polygons_valid (); @@ -173,7 +198,7 @@ std::pair FlatRegion::begin_iter () std::pair FlatRegion::begin_merged_iter () const { - if (! merged_semantics () || m_is_merged) { + if (! merged_semantics () || is_merged ()) { return begin_iter (); } else { ensure_merged_polygons_valid (); @@ -204,7 +229,7 @@ size_t FlatRegion::hier_count () const bool FlatRegion::is_merged () const { - return m_is_merged; + return m_is_merged && m_is_merged_min_coherence == min_coherence (); } Box FlatRegion::compute_bbox () const @@ -246,6 +271,7 @@ RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter) mp_merged_polygons->clear (); invalidate_cache (); m_is_merged = filter.requires_raw_input () ? false : merged_semantics (); + m_is_merged_min_coherence = min_coherence (); return this; } @@ -276,6 +302,7 @@ RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter mp_merged_polygons->clear (); invalidate_cache (); m_is_merged = filter.result_is_merged () && merged_semantics (); + m_is_merged_min_coherence = min_coherence (); if (filter.result_must_not_be_merged ()) { set_merged_semantics (false); @@ -286,14 +313,15 @@ RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter RegionDelegate *FlatRegion::merged_in_place () { - if (! m_is_merged) { + if (! is_merged ()) { - if (m_merged_polygons_valid) { + if (merged_polygons_valid ()) { db::Shapes &merged_polygons = *mp_merged_polygons; mp_polygons->swap (merged_polygons); merged_polygons.clear (); m_is_merged = true; + m_is_merged_min_coherence = min_coherence (); return this; } else { @@ -305,7 +333,7 @@ RegionDelegate *FlatRegion::merged_in_place () } } -RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) +RegionDelegate *FlatRegion::merged_in_place (bool min_coh, unsigned int min_wc, bool join_properties_on_merge) { if (empty ()) { @@ -322,9 +350,11 @@ RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int mi } else { invalidate_cache (); - merge_polygons_to (*mp_polygons, min_coherence, min_wc, join_properties_on_merge); + merge_polygons_to (*mp_polygons, min_coh, min_wc, join_properties_on_merge); + set_min_coherence (min_coh); m_is_merged = true; + m_is_merged_min_coherence = min_coh; } @@ -333,19 +363,35 @@ RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int mi RegionDelegate *FlatRegion::merged () const { - if (! m_is_merged) { + if (! is_merged ()) { - if (m_merged_polygons_valid) { - return new FlatRegion (*mp_merged_polygons, true); - } else { - return AsIfFlatRegion::merged (min_coherence (), 0, join_properties_on_merge ()); + if (! merged_polygons_valid ()) { + ensure_merged_polygons_valid (); } + std::unique_ptr new_region (new FlatRegion (*mp_merged_polygons, true, 0, 0, min_coherence ())); + + new_region->set_max_vertex_count (m_max_vertex_count); + new_region->set_area_ratio (m_area_ratio); + + return new_region.release (); + } else { return clone (); } } +RegionDelegate *FlatRegion::merged (bool min_coh, unsigned int min_wc, bool jp) const +{ + std::unique_ptr new_region (new FlatRegion (true, 0, 0, min_coh)); + merge_polygons_to (new_region->raw_polygons (), min_coh, min_wc, jp); + + new_region->set_max_vertex_count (m_max_vertex_count); + new_region->set_area_ratio (m_area_ratio); + + return new_region.release (); +} + RegionDelegate *FlatRegion::add (const Region &other) const { std::unique_ptr new_region (new FlatRegion (*this)); diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h index 5610eb061..70d4249b0 100644 --- a/src/db/db/dbFlatRegion.h +++ b/src/db/db/dbFlatRegion.h @@ -52,9 +52,9 @@ public: typedef polygon_layer_wp_type::iterator polygon_iterator_wp_type; FlatRegion (double area_ratio = 0.0, size_t max_vertex_count = 0); - FlatRegion (const db::Shapes &polygons, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0); - FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0); - FlatRegion (bool is_merged, double area_ratio = 0.0, size_t max_vertex_count = 0); + FlatRegion (const db::Shapes &polygons, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false); + FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false); + FlatRegion (bool is_merged, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false); FlatRegion (const FlatRegion &other); @@ -85,10 +85,9 @@ public: virtual RegionDelegate *merged_in_place (); virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge); virtual RegionDelegate *merged () const; - virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const - { - return db::AsIfFlatRegion::merged (min_coherence, min_wc, join_properties_on_merge); - } + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const; + + bool merged_polygons_available () const; virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter); virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter); @@ -138,6 +137,7 @@ protected: virtual Box compute_bbox () const; void invalidate_cache (); void set_is_merged (bool m); + bool merged_polygons_valid () const; private: friend class AsIfFlatRegion; @@ -146,9 +146,11 @@ private: FlatRegion &operator= (const FlatRegion &other); mutable bool m_is_merged; + mutable bool m_is_merged_min_coherence; mutable tl::copy_on_write_ptr mp_polygons; mutable tl::copy_on_write_ptr mp_merged_polygons; mutable bool m_merged_polygons_valid; + mutable bool m_merged_polygons_min_coherence; double m_area_ratio; size_t m_max_vertex_count; @@ -156,6 +158,16 @@ private: void ensure_merged_polygons_valid () const; void ensure_unmerged_polygons_valid () const; + void set_area_ratio (double ar) + { + m_area_ratio = ar; + } + + void set_max_vertex_count (size_t mc) + { + m_max_vertex_count = mc; + } + template void transform_generic (const Trans &trans) { diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 26cc7608e..9399cebc2 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -622,21 +622,28 @@ public: typedef typename local_processor_cell_contexts::context_key_type interactions_value_type; 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), m_rt (subject_layout) + interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, const std::map &override_distance, interactions_type *result) + : mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), m_override_distance (override_distance), mp_result (result), m_rt (subject_layout) { // nothing yet .. } void add (const db::CellInstArray *inst, unsigned int, const TI *ref, unsigned int layer) { - collect_instance_shape_interactions (inst, layer, *ref, m_dist); + db::Coord d = m_dist; + auto od = m_override_distance.find (layer); + if (od != m_override_distance.end ()) { + d = std::min (d, od->second); + } + + collect_instance_shape_interactions (inst, layer, *ref, d); } private: db::Layout *mp_subject_layout; unsigned int m_subject_layer; db::Coord m_dist; + std::map m_override_distance; interactions_type *mp_result; db::shape_reference_translator_with_trans m_rt; @@ -681,11 +688,11 @@ private: // LocalProcessorContextComputationTask implementation template -local_processor_context_computation_task::local_processor_context_computation_task (const local_processor *proc, local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist) +local_processor_context_computation_task::local_processor_context_computation_task (const local_processor *proc, local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist, const std::map &override_distance) : tl::Task (), mp_proc (proc), mp_contexts (&contexts), mp_parent_context (parent_context), mp_subject_parent (subject_parent), mp_subject_cell (subject_cell), m_subject_cell_inst (subject_cell_inst), - mp_intruder_cell (intruder_cell), m_dist (dist) + mp_intruder_cell (intruder_cell), m_dist (dist), m_override_distance (override_distance) { // This is quick, but will take away the intruders from the caller m_intruders.swap (intruders); @@ -695,7 +702,7 @@ template void local_processor_context_computation_task::perform () { - mp_proc->compute_contexts (*mp_contexts, mp_parent_context, mp_subject_parent, mp_subject_cell, m_subject_cell_inst, mp_intruder_cell, m_intruders, m_dist); + mp_proc->compute_contexts (*mp_contexts, mp_parent_context, mp_subject_parent, mp_subject_cell, m_subject_cell_inst, mp_intruder_cell, m_intruders, m_dist, m_override_distance); } // --------------------------------------------------------------------------------------------- @@ -900,8 +907,18 @@ void local_processor::compute_contexts (local_processor_contextsoverride_distance (); + std::map override_distance; + for (auto il = intruder_layers.begin (); il != intruder_layers.end (); ++il) { + auto o = od.find ((unsigned int) (il - intruder_layers.begin ())); + if (o != od.end ()) { + override_distance [*il] = o->second; + } + } + typename local_processor_cell_contexts::context_key_type intruders; - issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist ()); + issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist (), override_distance); if (mp_cc_job.get ()) { mp_cc_job->start (); @@ -922,14 +939,15 @@ void local_processor::issue_compute_contexts (local_processor_contex const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts::context_key_type &intruders, - db::Coord dist) const + db::Coord dist, + const std::map &override_distance) const { bool is_small_job = subject_cell->begin ().at_end (); if (! is_small_job && mp_cc_job.get ()) { - mp_cc_job->schedule (new local_processor_context_computation_task (this, contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist)); + mp_cc_job->schedule (new local_processor_context_computation_task (this, contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist, override_distance)); } else { - compute_contexts (contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist); + compute_contexts (contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist, override_distance); } } @@ -941,7 +959,8 @@ void local_processor::compute_contexts (local_processor_contexts::context_key_type &intruders, - db::Coord dist_top) const + db::Coord dist_top, + const std::map &override_distance) const { CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts) @@ -1041,10 +1060,16 @@ void local_processor::compute_contexts (local_processor_contexts::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) { + auto od = override_distance.find (*il); + db::Coord d = dist; + if (od != override_distance.end ()) { + d = std::min (d, od->second); + } + db::box_convert inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il)); db::box_scanner2 scanner; - interaction_registration_inst2inst rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions); + interaction_registration_inst2inst rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), d, &interactions); unsigned int id = 0; @@ -1095,7 +1120,7 @@ void local_processor::compute_contexts (local_processor_contexts scanner; db::addressable_object_from_shape heap; - interaction_registration_inst2shape rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions); + interaction_registration_inst2shape rec (mp_subject_layout, contexts.subject_layer (), dist, override_distance, &interactions); for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) { @@ -1127,7 +1152,7 @@ void local_processor::compute_contexts (local_processor_contextscell (i->first.first); db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? subject_child_cell : 0); - issue_compute_contexts (contexts, cell_context, subject_cell, subject_child_cell, i->first.second, intruder_child_cell, i->second, dist); + issue_compute_contexts (contexts, cell_context, subject_cell, subject_child_cell, i->first.second, intruder_child_cell, i->second, dist, override_distance); } @@ -1386,7 +1411,8 @@ template void local_processor::compute_local_cell (const db::local_processor_contexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation *op, const typename local_processor_cell_contexts::context_key_type &intruders, std::vector > &result) const { - db::Coord dist = dist_for_cell (subject_cell->cell_index (), op->dist ()); + auto override_distance = op->override_distance (); + db::Coord dist_global = dist_for_cell (subject_cell->cell_index (), op->dist ()); const db::Shapes *subject_shapes = &subject_cell->shapes (contexts.subject_layer ()); db::shape_to_object s2o; @@ -1412,6 +1438,13 @@ local_processor::compute_local_cell (const db::local_processor_conte unsigned int il_index = 0; for (std::vector::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il, ++il_index) { + // compute effective distance + db::Coord dist = dist_global; + auto od = override_distance.find (il_index); + if (od != override_distance.end ()) { + dist = std::min (dist, dist_for_cell (subject_cell->cell_index (), od->second)); + } + unsigned int ail = contexts.actual_intruder_layer (*il); bool foreign = contexts.is_foreign (*il); @@ -1633,15 +1666,16 @@ local_processor::run_flat (const generic_shape_iterator &subject // build the subjects in the intruders list - db::Coord dist = op->dist (); + db::Coord dist_global = op->dist (); + auto override_distance = op->override_distance (); - db::Box subjects_box = safe_box_enlarged (subjects.bbox (), dist, dist); + db::Box subjects_box = safe_box_enlarged (subjects.bbox (), dist_global, dist_global); db::Box intruders_box; for (typename std::vector >::const_iterator il = intruders.begin (); il != intruders.end (); ++il) { intruders_box += il->bbox (); } - intruders_box = safe_box_enlarged (intruders_box, dist, dist); + intruders_box = safe_box_enlarged (intruders_box, dist_global, dist_global); db::Box common_box = intruders_box & subjects_box; if (common_box.empty () || common_box.width () == 0 || common_box.height () == 0) { @@ -1670,6 +1704,12 @@ local_processor::run_flat (const generic_shape_iterator &subject bool ff = foreign.size () > il_index && foreign [il_index]; + db::Coord dist = dist_global; + auto od = override_distance.find (il_index); + if (od != override_distance.end ()) { + dist = std::min (dist, od->second); + } + if (*il == subjects && ! ff) { interaction_registration_shape1_scanner_combo scanner (&interactions, il_index, report_progress (), scan_description); @@ -1736,6 +1776,12 @@ local_processor::run_flat (const generic_shape_iterator &subject bool ff = foreign.size () > il_index && foreign [il_index]; + db::Coord dist = dist_global; + auto od = override_distance.find (il_index); + if (od != override_distance.end ()) { + dist = std::min (dist, od->second); + } + if (*il == subjects && ! ff) { interaction_registration_shape1_scanner_combo scanner (&interactions, il_index, report_progress (), scan_description); @@ -1789,6 +1835,7 @@ local_processor::run_flat (const generic_shape_iterator &subject } } + } } diff --git a/src/db/db/dbHierProcessor.h b/src/db/db/dbHierProcessor.h index 8d446641a..a2a3d8714 100644 --- a/src/db/db/dbHierProcessor.h +++ b/src/db/db/dbHierProcessor.h @@ -359,7 +359,7 @@ class DB_PUBLIC local_processor_context_computation_task : public tl::Task { public: - local_processor_context_computation_task (const local_processor *proc, local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist); + local_processor_context_computation_task (const local_processor *proc, local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist, const std::map &override_distance); void perform (); private: @@ -372,6 +372,7 @@ private: const db::Cell *mp_intruder_cell; typename local_processor_cell_contexts::context_key_type m_intruders; db::Coord m_dist; + std::map m_override_distance; }; template @@ -578,8 +579,8 @@ private: void next () const; size_t get_progress () const; - void compute_contexts (db::local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist) const; - void issue_compute_contexts (db::local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist) const; + void compute_contexts (db::local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist, const std::map &override_distance) const; + void issue_compute_contexts (db::local_processor_contexts &contexts, db::local_processor_cell_context *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts::context_key_type &intruders, db::Coord dist, const std::map &override_distance) const; void compute_local_cell (const db::local_processor_contexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation *op, const typename local_processor_cell_contexts::context_key_type &intruders, std::vector > &result) const; bool subject_cell_is_breakout (db::cell_index_type ci) const diff --git a/src/db/db/dbLayerProperties.cc b/src/db/db/dbLayerProperties.cc index d5a50e497..ccdb42153 100644 --- a/src/db/db/dbLayerProperties.cc +++ b/src/db/db/dbLayerProperties.cc @@ -190,7 +190,7 @@ static bool read_ld (tl::Extractor &ex, ld_type &l, bool with_relative) } } -void +bool LayerProperties::read (tl::Extractor &ex, bool as_target) { layer = db::any_ld (); @@ -207,6 +207,8 @@ LayerProperties::read (tl::Extractor &ex, bool as_target) layer = l; datatype = d; + return true; + } else if (ex.try_read_word_or_quoted (name)) { if (ex.test ("(")) { @@ -223,6 +225,10 @@ LayerProperties::read (tl::Extractor &ex, bool as_target) } + return true; + + } else { + return false; } } diff --git a/src/db/db/dbLayerProperties.h b/src/db/db/dbLayerProperties.h index b9738b339..7ed4494ff 100644 --- a/src/db/db/dbLayerProperties.h +++ b/src/db/db/dbLayerProperties.h @@ -91,8 +91,10 @@ struct DB_PUBLIC LayerProperties * relative layer/datatype specifications in the format "*+1" or "*-100". * "*" for layer or datatype is for "don't care" (on input) or "leave as is" * (for output). + * + * Returns true, if a layer was read successfully. */ - void read (tl::Extractor &ex, bool as_target = false); + bool read (tl::Extractor &ex, bool as_target = false); /** * @brief "Logical" equality diff --git a/src/db/db/dbLocalOperation.h b/src/db/db/dbLocalOperation.h index 5ce9c2db3..8f8651865 100644 --- a/src/db/db/dbLocalOperation.h +++ b/src/db/db/dbLocalOperation.h @@ -118,6 +118,14 @@ public: */ virtual db::Coord dist () const { return 0; } + /** + * @brief Gets a override distance for a specific input layer + * The input layers are numbered from 0 to n-1, where n is the allowed number of intruder layers + * The override distance is used insted of the global distance ("dist()") for the given + * input layer. The override distance cannot be bigger than the global distance. + */ + virtual std::map override_distance () const { return std::map (); } + /** * @brief Gets the cell variant reducer that indicates whether to build cell variants and which */ diff --git a/src/db/db/dbLocalOperationUtils.h b/src/db/db/dbLocalOperationUtils.h index 815439951..c7eced15b 100644 --- a/src/db/db/dbLocalOperationUtils.h +++ b/src/db/db/dbLocalOperationUtils.h @@ -376,10 +376,10 @@ private: */ template DB_PUBLIC_TEMPLATE -std::map, std::set > > +std::map, std::set > > > separate_interactions_by_properties (const shape_interactions, db::object_with_properties > &interactions, db::PropertyConstraint property_constraint) { - std::map, std::set > > by_prop_id; + std::map, std::set > > > by_prop_id; for (auto i = interactions.begin (); i != interactions.end (); ++i) { @@ -387,7 +387,7 @@ separate_interactions_by_properties (const shape_interactions, std::set > &s2p = by_prop_id [prop_id]; + auto &s2p = by_prop_id [prop_id]; s2p.first.push_back (&subject); for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { @@ -395,7 +395,7 @@ separate_interactions_by_properties (const shape_interactions > &intruder = interactions.intruder_shape (*ii); if (pc_match (property_constraint, prop_id, intruder.second.properties_id ())) { - s2p.second.insert (&intruder.second); + s2p.second.insert (std::make_pair (intruder.first, &intruder.second)); } } diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc index 674ff7ec3..f96c783fb 100644 --- a/src/db/db/dbOriginalLayerRegion.cc +++ b/src/db/db/dbOriginalLayerRegion.cc @@ -142,28 +142,34 @@ OriginalLayerRegion::OriginalLayerRegion () OriginalLayerRegion::OriginalLayerRegion (const OriginalLayerRegion &other) : AsIfFlatRegion (other), m_is_merged (other.m_is_merged), + m_is_merged_min_coherence (other.m_is_merged_min_coherence), m_merged_polygons (other.m_merged_polygons), m_merged_polygons_valid (other.m_merged_polygons_valid), + m_merged_polygons_min_coherence (other.m_merged_polygons_min_coherence), m_iter (other.m_iter), m_iter_trans (other.m_iter_trans) { // .. nothing yet .. } -OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged) +OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged, bool min_coh) : AsIfFlatRegion (), m_merged_polygons (false), m_iter (si) { init (); + set_min_coherence (min_coh); m_is_merged = is_merged; + m_is_merged_min_coherence = min_coh; } -OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) +OriginalLayerRegion::OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged, bool min_coh) : AsIfFlatRegion (), m_merged_polygons (false), m_iter (si), m_iter_trans (trans) { init (); + set_min_coherence (min_coh); m_is_merged = is_merged; + m_is_merged_min_coherence = min_coh; set_merged_semantics (merged_semantics); } @@ -183,6 +189,7 @@ OriginalLayerRegion::merged_semantics_changed () { m_merged_polygons.clear (); m_merged_polygons_valid = false; + m_merged_polygons_min_coherence = false; } void @@ -190,14 +197,13 @@ OriginalLayerRegion::join_properties_on_merge_changed () { m_merged_polygons.clear (); m_merged_polygons_valid = false; + m_merged_polygons_min_coherence = false; } void OriginalLayerRegion::min_coherence_changed () { - m_is_merged = false; - m_merged_polygons.clear (); - m_merged_polygons_valid = false; + // merged status is tracked in separate variables and is validated on inquiry } size_t @@ -309,7 +315,7 @@ OriginalLayerRegion::begin () const RegionIteratorDelegate * OriginalLayerRegion::begin_merged () const { - if (! merged_semantics () || m_is_merged) { + if (! merged_semantics () || is_merged ()) { return begin (); } else { ensure_merged_polygons_valid (); @@ -332,7 +338,7 @@ OriginalLayerRegion::begin_iter () const std::pair OriginalLayerRegion::begin_merged_iter () const { - if (! merged_semantics () || m_is_merged) { + if (! merged_semantics () || is_merged ()) { return begin_iter (); } else { ensure_merged_polygons_valid (); @@ -346,6 +352,33 @@ OriginalLayerRegion::begin_unmerged_iter () const return std::make_pair (m_iter, m_iter_trans); } +RegionDelegate * +OriginalLayerRegion::merged () const +{ + if (! is_merged ()) { + + if (! merged_polygons_valid ()) { + ensure_merged_polygons_valid (); + } + + std::unique_ptr new_region (new FlatRegion (m_merged_polygons, true, 0, 0, min_coherence ())); + + return new_region.release (); + + } else { + return clone (); + } +} + +RegionDelegate * +OriginalLayerRegion::merged (bool min_coh, unsigned int min_wc, bool jp) const +{ + std::unique_ptr new_region (new FlatRegion (true, 0, 0, min_coh)); + merge_polygons_to (new_region->raw_polygons (), min_coh, min_wc, jp); + + return new_region.release (); +} + bool OriginalLayerRegion::empty () const { @@ -355,7 +388,13 @@ OriginalLayerRegion::empty () const bool OriginalLayerRegion::is_merged () const { - return m_is_merged; + return m_is_merged && m_is_merged_min_coherence == min_coherence (); +} + +bool +OriginalLayerRegion::merged_polygons_valid () const +{ + return m_merged_polygons_valid && m_merged_polygons_min_coherence == min_coherence (); } const db::Polygon * @@ -379,7 +418,7 @@ OriginalLayerRegion::has_valid_polygons () const bool OriginalLayerRegion::has_valid_merged_polygons () const { - return merged_semantics () && ! m_is_merged; + return merged_semantics () && ! is_merged (); } const db::RecursiveShapeIterator * @@ -394,6 +433,7 @@ OriginalLayerRegion::apply_property_translator (const db::PropertiesTranslator & m_iter.apply_property_translator (pt); m_merged_polygons_valid = false; + m_merged_polygons_min_coherence = false; m_merged_polygons.clear (); } @@ -423,7 +463,9 @@ void OriginalLayerRegion::init () { m_is_merged = false; + m_is_merged_min_coherence = false; m_merged_polygons_valid = false; + m_merged_polygons_min_coherence = false; } namespace { @@ -459,15 +501,22 @@ OriginalLayerRegion::insert_into (Layout *layout, db::cell_index_type into_cell, } } +bool +OriginalLayerRegion::merged_polygons_available () const +{ + return is_merged () || merged_polygons_valid (); +} + void OriginalLayerRegion::ensure_merged_polygons_valid () const { - if (! m_merged_polygons_valid) { + if (! merged_polygons_valid ()) { m_merged_polygons.clear (); merge_polygons_to (m_merged_polygons, min_coherence (), 0, join_properties_on_merge ()); m_merged_polygons_valid = true; + m_merged_polygons_min_coherence = min_coherence (); } } diff --git a/src/db/db/dbOriginalLayerRegion.h b/src/db/db/dbOriginalLayerRegion.h index a72db588b..92d2a90ac 100644 --- a/src/db/db/dbOriginalLayerRegion.h +++ b/src/db/db/dbOriginalLayerRegion.h @@ -43,8 +43,8 @@ class DB_PUBLIC OriginalLayerRegion public: OriginalLayerRegion (); OriginalLayerRegion (const OriginalLayerRegion &other); - OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged = false); - OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false); + OriginalLayerRegion (const RecursiveShapeIterator &si, bool is_merged = false, bool min_coh = false); + OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false, bool min_coh = false); virtual ~OriginalLayerRegion (); RegionDelegate *clone () const; @@ -57,6 +57,11 @@ public: virtual std::pair begin_merged_iter () const; virtual std::pair begin_unmerged_iter () const; + virtual RegionDelegate *merged () const; + virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const; + + bool merged_polygons_available () const; + virtual bool empty () const; virtual bool is_merged () const; @@ -85,13 +90,16 @@ private: OriginalLayerRegion &operator= (const OriginalLayerRegion &other); bool m_is_merged; + mutable bool m_is_merged_min_coherence; mutable db::Shapes m_merged_polygons; mutable bool m_merged_polygons_valid; + mutable bool m_merged_polygons_min_coherence; mutable db::RecursiveShapeIterator m_iter; db::ICplxTrans m_iter_trans; void init (); void ensure_merged_polygons_valid () const; + bool merged_polygons_valid () const; }; } diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 186ed30db..82c17c80e 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -211,7 +211,7 @@ check_local_operation_base::check_local_operation_base (const EdgeRelati template void -check_local_operation_base::compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector &subjects, const std::set &intruders, std::unordered_set &result, std::unordered_set &intra_polygon_result, const db::LocalProcessorBase *proc) const +check_local_operation_base::compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector &subjects, const std::set &primary_intruders, const std::set &intruders, std::unordered_set &result, std::unordered_set &intra_polygon_result, const db::LocalProcessorBase *proc) const { // NOTE: the rectangle and opposite filters are unsymmetric bool symmetric_edge_pairs = ! m_has_other && m_options.opposite_filter == db::NoOppositeFilter && m_options.rect_filter == RectFilter::NoRectFilter; @@ -229,7 +229,7 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel db::EdgeProcessor ep; ep.set_base_verbosity (50); - bool take_all = edge_check.has_negative_edge_output () || intruders.empty (); + bool take_all = edge_check.has_negative_edge_output () || (intruders.empty () && primary_intruders.empty ()); db::Box common_box; bool subjects_are_fully_covered = false; @@ -259,6 +259,14 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel all_intruders_box += intruder_box.enlarged (e); } + for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) { + db::Box intruder_box = db::box_convert () (**i); + if (! subjects_are_fully_covered && (*i)->is_box () && common_box.inside (intruder_box)) { + subjects_are_fully_covered = true; + } + all_intruders_box += intruder_box.enlarged (e); + } + common_box &= all_intruders_box; } @@ -269,7 +277,7 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel size_t n = 0; - if (m_is_merged || (subjects.size () == 1 && subjects.front ()->is_box ())) { + if (m_is_merged || (subjects.size () == 1 && subjects.front ()->is_box () && primary_intruders.empty ())) { for (auto i = subjects.begin (); i != subjects.end (); ++i) { if (! take_all) { @@ -280,9 +288,10 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel n += 2; } - } else { + } else if (primary_intruders.empty ()) { // merge needed for the subject shapes + // Here we don't have other subjects to consider. This is the simple case. ep.clear (); size_t nn = 0; @@ -310,6 +319,79 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel n += 2; } + } else { + + // merge needed for the subject and primary intruder shapes ("other subjects") - we merge both and then + // reclaim original subject edges that are "outer" edges and not covered by other subjects shapes. + + ep.clear (); + size_t nn = 0; + + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { + ep.insert (*e, nn); + } + ++nn; + } + + for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) { + for (auto e = (*i)->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 = subjects.begin (); i != subjects.end (); ++i) { + sz += (*i)->vertices (); + } + + subject_edges.reserve (sz); + + for (auto i = subjects.begin (); i != subjects.end (); ++i) { + for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { + subject_edges.push_back (*e); + } + } + + std::set partial_edges; + + EdgeBooleanClusterCollector > cluster_collector (&partial_edges, db::EdgeAnd); + + 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, 0, common_box); + } else { + poly_check.enter (*e, 0); + } + } + } // merge the intruders to remove inner edges @@ -384,11 +466,11 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel } else { - if (m_is_merged || (subjects.size () == 1 && intruders.empty () && subjects.front ()->is_box ())) { + if (m_is_merged || (subjects.size () == 1 && primary_intruders.empty () && subjects.front ()->is_box ())) { // no merge required - // NOTE: we need to eliminate identical shapes from intruders and subjects because those will shield + // NOTE: we need to eliminate identical shapes from primary intruders and subjects because those will shield size_t n = 0; std::unordered_set subjects_hash; @@ -406,7 +488,7 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel n = 1; - for (auto i = intruders.begin (); i != intruders.end (); ++i) { + for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) { if (subjects_hash.find (**i) == subjects_hash.end ()) { if (! take_all) { poly_check.enter (**i, n, common_box); @@ -416,9 +498,9 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel } } - } else if (intruders.empty ()) { + } else if (primary_intruders.empty ()) { - // merge needed for the subject shapes - no intruders present so this is the simple case + // merge needed for the subject shapes - no primary intruders ("other subjects") present so this is the simple case size_t n = 0; @@ -451,7 +533,7 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel } else { - // merge needed for the subject and intruder shapes - we merge both and then + // merge needed for the subject and primary intruder shapes ("other subjects") - we merge both and then // separate edges into those from the subject and those from intruder shapes. ep.clear (); @@ -464,7 +546,7 @@ check_local_operation_base::compute_results (db::Layout *layout, db::Cel ++nn; } - for (auto i = intruders.begin (); i != intruders.end (); ++i) { + for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) { for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) { ep.insert (*e, nn); } @@ -727,12 +809,40 @@ check_local_operation::do_compute_local (db::Layout *layout, db::Cell *s subjects.reserve (interactions.size ()); std::set intruders; + std::set primary_intruders; + + unsigned int primary_intruder_layer = check_local_operation_base::m_has_other ? 1 : 0; + + // in the case of a two-layer check, return if there is no second layer shape + if (check_local_operation_base::m_has_other) { + + bool any = false; + for (auto i = interactions.begin (); i != interactions.end () && ! any; ++i) { + for (auto ii = i->second.begin (); ii != i->second.end () && ! any; ++ii) { + const auto &is = interactions.intruder_shape (*ii); + any = (is.first != primary_intruder_layer); + } + } + + if (! any) { + return; + } + + } for (auto i = interactions.begin (); i != interactions.end (); ++i) { + subjects.push_back (&interactions.subject_shape (i->first)); + for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { - intruders.insert (&interactions.intruder_shape (*ii).second); + const auto &is = interactions.intruder_shape (*ii); + if (is.first == primary_intruder_layer) { + primary_intruders.insert (&is.second); + } else { + intruders.insert (&is.second); + } } + } tl_assert (results.size () == 1); @@ -740,7 +850,7 @@ check_local_operation::do_compute_local (db::Layout *layout, db::Cell *s std::unordered_set result, intra_polygon_result; // perform the basic check - check_local_operation_base::compute_results (layout, subject_cell, subjects, intruders, result, intra_polygon_result, proc); + check_local_operation_base::compute_results (layout, subject_cell, subjects, primary_intruders, intruders, result, intra_polygon_result, proc); // detect and remove parts of the result which have or do not have results "opposite" // ("opposite" is defined by the projection of edges "through" the subject shape) @@ -773,6 +883,19 @@ check_local_operation::do_compute_local (db::Layout *layout, db::Cell *s } } +template +std::map +check_local_operation::override_distance () const +{ + // makes sure, the "foreign"-type pseudo-intruder used for merging only + // does not use the full search range, but only "touching". + std::map od; + if (check_local_operation_base::m_has_other) { + od.insert (std::make_pair (1, 0)); + } + return od; +} + template db::Coord check_local_operation::dist () const @@ -816,6 +939,8 @@ check_local_operation_with_properties::do_compute_local (db::Layout *lay { tl_assert (results.size () == 1); + unsigned int primary_intruder_layer = check_local_operation_base::m_has_other ? 1 : 0; + auto by_prop_id = separate_interactions_by_properties (interactions, check_local_operation_base::m_options.prop_constraint); for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) { @@ -823,10 +948,18 @@ check_local_operation_with_properties::do_compute_local (db::Layout *lay std::unordered_set result, intra_polygon_result; const std::vector &subjects = s2p->second.first; - const std::set &intruders = s2p->second.second; + + std::set intruders, primary_intruders; + for (auto i = s2p->second.second.begin (); i != s2p->second.second.end (); ++i) { + if (i->first == primary_intruder_layer) { + primary_intruders.insert (i->second); + } else { + intruders.insert (i->second); + } + } // perform the basic check - check_local_operation_base::compute_results (layout, subject_cell, subjects, intruders, result, intra_polygon_result, proc); + check_local_operation_base::compute_results (layout, subject_cell, subjects, primary_intruders, intruders, result, intra_polygon_result, proc); // detect and remove parts of the result which have or do not have results "opposite" // ("opposite" is defined by the projection of edges "through" the subject shape) @@ -2281,8 +2414,11 @@ PolygonToEdgeLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell db::properties_id_type prop_id = shapes_by_prop_id->first; + ep.clear (); + + size_t p = 0; for (auto s = shapes_by_prop_id->second.first.begin (); s != shapes_by_prop_id->second.first.end (); ++s) { - ep.insert (**s); + ep.insert (**s, p++); } db::property_injector > results_with_properties (&results.front (), prop_id); @@ -2306,18 +2442,19 @@ PolygonToEdgeLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell ep.clear (); - for (auto s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) { - ep.insert (s->second); + size_t p = 0; + for (auto s = shapes_by_prop_id->second.first.begin (); s != shapes_by_prop_id->second.first.end (); ++s) { + ep.insert (**s, ++p); } - for (auto i = interactions.begin_intruders (); i != interactions.end_intruders (); ++i) { - ep.insert (i->second.second); + for (auto s = shapes_by_prop_id->second.second.begin (); s != shapes_by_prop_id->second.second.end (); ++s) { + ep.insert (*(s->second), ++p); } std::vector edges2; db::EdgeContainer ec2 (edges2); ep.process (ec2, op); - // Runs the boolean AND between the result with and without intruders + // Runs the boolean AND between the result with and without intruders - this identifies outside edges db::box_scanner scanner; scanner.reserve (edges1.size () + edges2.size ()); diff --git a/src/db/db/dbRegionLocalOperations.h b/src/db/db/dbRegionLocalOperations.h index c1a4e170b..cafe7419e 100644 --- a/src/db/db/dbRegionLocalOperations.h +++ b/src/db/db/dbRegionLocalOperations.h @@ -194,7 +194,7 @@ protected: bool m_other_is_merged; db::RegionCheckOptions m_options; - void compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector &subjects, const std::set &intruders, std::unordered_set &result, std::unordered_set &intra_polygon_result, const LocalProcessorBase *proc) const; + void compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector &subjects, const std::set &primary_intruders, const std::set &intruders, std::unordered_set &result, std::unordered_set &intra_polygon_result, const LocalProcessorBase *proc) const; void apply_opposite_filter (const std::vector &subjects, std::unordered_set &result, std::unordered_set &intra_polygon_result) const; void apply_rectangle_filter (const std::vector &subjects, std::unordered_set &result) const; }; @@ -207,6 +207,7 @@ public: 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 std::map override_distance () const; virtual OnEmptyIntruderHint on_empty_intruder_hint () const; virtual bool requests_single_subjects () const { return true; } virtual std::string description () const; diff --git a/src/db/db/dbShapes.cc b/src/db/db/dbShapes.cc index 8176df950..5cdc5cb5b 100644 --- a/src/db/db/dbShapes.cc +++ b/src/db/db/dbShapes.cc @@ -236,7 +236,7 @@ Shapes::do_insert (const Shapes &d, unsigned int flags) if (layout () == d.layout ()) { // both shape containers reside in the same repository space - simply copy - if (m_layers.empty ()) { + if (m_layers.empty () && is_editable () == d.is_editable ()) { m_layers.reserve (d.m_layers.size ()); for (tl::vector::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) { diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index f055a5980..3d2feb5be 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -163,8 +163,9 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) | ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0) | ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts) - | ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta - | db::layout_diff::f_smart_cell_mapping) + | ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta) + | ((norm & WithoutLayerNames) == 0 ? 0 : db::layout_diff::f_no_layer_names) + | db::layout_diff::f_smart_cell_mapping /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/ , tolerance, 100 /*max diff lines*/); @@ -176,6 +177,7 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: | ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0) | ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts) | ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta) + | ((norm & WithoutLayerNames) == 0 ? 0 : db::layout_diff::f_no_layer_names) /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/ , tolerance, 100 /*max diff lines*/); diff --git a/src/db/db/dbTestSupport.h b/src/db/db/dbTestSupport.h index df013f8a1..1672cf86a 100644 --- a/src/db/db/dbTestSupport.h +++ b/src/db/db/dbTestSupport.h @@ -52,15 +52,16 @@ class Texts; */ enum NormalizationMode { - NoNormalization = 0, // no normalization - take the test subject as it is - WriteGDS2 = 1, // normalize subject by writing to GDS2 and reading back - WriteOAS = 2, // normalize subject by writing to OASIS and reading back - NormFileMask = 7, // bits the extract for file mode - NoContext = 8, // write tmp file without context - AsPolygons = 16, // paths and boxes are treated as polygons - WithArrays = 32, // do not flatten arrays - WithMeta = 64, // with meta info - WithoutCellNames = 128 // smart cell name mapping + NoNormalization = 0, // no normalization - take the test subject as it is + WriteGDS2 = 1, // normalize subject by writing to GDS2 and reading back + WriteOAS = 2, // normalize subject by writing to OASIS and reading back + NormFileMask = 7, // bits the extract for file mode + NoContext = 8, // write tmp file without context + AsPolygons = 16, // paths and boxes are treated as polygons + WithArrays = 32, // do not flatten arrays + WithMeta = 64, // with meta info + WithoutCellNames = 128, // smart cell name mapping + WithoutLayerNames = 256, // don't compare layer names }; /** diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 46329ec13..088254f79 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -892,14 +892,38 @@ static db::Layout *layout_default_ctor() return new db::Layout (true); } -static db::Layout *editable_layout_ctor_with_manager(bool editable, db::Manager &manager) +static db::Layout *editable_layout_ctor_with_manager(bool editable, db::Manager *manager) { - return new db::Layout (editable, &manager); + return new db::Layout (editable, manager); } -static db::Layout *editable_layout_default_ctor(bool editable) +static db::Layout *layout_ctor_from_cell(const db::Cell &source_cell, tl::Variant editable, db::Manager *manager) { - return new db::Layout (editable); + const db::Layout *source_layout = source_cell.layout (); + if (! source_layout) { + throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout"))); + } + + bool editable_flag = source_layout->is_editable (); + if (! editable.is_nil ()) { + editable_flag = editable.to_bool (); + } + + std::unique_ptr target_layout (new db::Layout (editable_flag, manager)); + db::cell_index_type target_cell_index = target_layout->add_cell (source_layout->cell_name (source_cell.cell_index ())); + target_layout->dbu (source_layout->dbu ()); + + db::CellMapping cm; + cm.create_single_mapping_full (*target_layout, target_cell_index, *source_layout, source_cell.cell_index ()); + + db::LayerMapping lm; + lm.create_full (*target_layout, *source_cell.layout ()); + + std::vector source_cells; + source_cells.push_back (source_cell.cell_index ()); + db::copy_shapes (*target_layout, *source_layout, db::ICplxTrans (), source_cells, cm.table (), lm.table ()); + + return target_layout.release (); } static db::cell_index_type add_lib_pcell_variant (db::Layout *layout, db::Library *lib, db::pcell_id_type pcell_id, const std::vector ¶meters) @@ -1286,7 +1310,7 @@ Class decl_Layout ("db", "Layout", "always editable. Before that version, they inherited the editable flag from " "the application." ) + - gsi::constructor ("new", &editable_layout_ctor_with_manager, gsi::arg ("editable"), gsi::arg ("manager"), + gsi::constructor ("new", &editable_layout_ctor_with_manager, gsi::arg ("editable"), gsi::arg ("manager", (db::Manager *) 0, "nil"), "@brief Creates a layout object attached to a manager\n" "\n" "This constructor specifies a manager object which is used to " @@ -1294,16 +1318,21 @@ Class decl_Layout ("db", "Layout", "the layout is editable. In editable mode, some optimizations are disabled " "and the layout can be manipulated through a variety of methods.\n" "\n" + "The manager object can be nil - in that case, undo/redo is not supported.\n" + "\n" "This method was introduced in version 0.22.\n" ) + - gsi::constructor ("new", &editable_layout_default_ctor, gsi::arg ("editable"), - "@brief Creates a layout object\n" + gsi::constructor ("new", &layout_ctor_from_cell, gsi::arg ("source_cell"), gsi::arg ("editable", tl::Variant (), "nil"), gsi::arg ("manager", (db::Manager *) 0, "nil"), + "@brief Creates a layout object as a copy of another cell\n" "\n" - "This constructor specifies whether " - "the layout is editable. In editable mode, some optimizations are disabled " - "and the layout can be manipulated through a variety of methods.\n" + "This convenience constructor creates a new layout object as a hierarchical copy of the source cell including all " + "child cells and shapes.\n" "\n" - "This method was introduced in version 0.22.\n" + "If 'editable' is a boolean value, the new layout object will be made editable depending on that value. " + "If 'nil' is used for 'editable', the editable attribute is copied from the layout the source cell lives in.\n" + "'manager' can be a \\Manager object to which the new layout will be attached.\n" + "\n" + "This method was introduced in version 0.30.10.\n" ) + gsi::method ("library", &db::Layout::library, "@brief Gets the library this layout lives in or nil if the layout is not part of a library\n" diff --git a/src/db/unit_tests/dbAsIfFlatRegionTests.cc b/src/db/unit_tests/dbAsIfFlatRegionTests.cc index 3377a2ab4..1811236ac 100644 --- a/src/db/unit_tests/dbAsIfFlatRegionTests.cc +++ b/src/db/unit_tests/dbAsIfFlatRegionTests.cc @@ -2033,6 +2033,243 @@ TEST(44_SizeWithProperties) db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au44.gds"); } +TEST(45_min_coherence_and_merge) +{ + // NOTE: with an explicit area ratio and max vertex count we can play with unmerged input + db::Region r1 (new db::FlatRegion (3.0, 32)); + + r1.insert (db::Box (0, 0, 1000, 2000)); + r1.insert (db::Box (1000, 2000, 10000, 3000)); + r1.insert (db::Box (9000, 3000, 10000, 10000)); + + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), false); + EXPECT_EQ (r1.to_string (), "(0,0;0,2000;1000,2000;1000,0);(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000)"); + + // forces merged status + r1.sized (1); + + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), true); + + db::Region r2 = r1.merged (); + EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); + + EXPECT_EQ (r2.min_coherence (), false); + + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), true); + + r1.set_min_coherence (true); + + // valid_merged_polygons is invalidated + EXPECT_EQ (r1.min_coherence (), true); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), false); + + r1.set_min_coherence (false); + + // setting min_coherence to false will bring the validation back + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), true); + + r1.set_min_coherence (true); + + // valid_merged_polygons is invalidated again by setting min_coherence to true + EXPECT_EQ (r1.min_coherence (), true); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), false); + + // inserting a box into the region will reset the merged valid flag and not bring it back by resetting min_coherence + db::Region r3 = r1; + r3.insert (db::Box (0, 0, 100, 100)); + EXPECT_EQ (r3.min_coherence (), true); + r3.set_min_coherence (false); + EXPECT_EQ (r3.is_merged (), false); + EXPECT_EQ (dynamic_cast (r3.delegate ())->merged_polygons_available (), false); + + // creates a intrinsically merged copy and tests if the min_coherence + // flag is inherited + r3 = r1.merged (); + EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,0);(1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000)"); + EXPECT_EQ (r3.is_merged (), true); + EXPECT_EQ (r3.min_coherence (), true); + + // merged polygons are validated against min_coherence = true during r1.merged() before, so + // resetting min_coherence to false will invalidat them + r1.set_min_coherence (false); + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), false); + + // creates a intrinsically merged copy and tests if the min_coherence + // flag is inherited + r3 = r1.merged (); + EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); + EXPECT_EQ (r3.is_merged (), true); + EXPECT_EQ (r3.min_coherence (), false); + + // r2 is an intrinsically merged region (with min coherence false) + EXPECT_EQ (r2.min_coherence (), false); + EXPECT_EQ (r2.is_merged (), true); + EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); + + // after changing min_coherence, the merged status is no longer true + r2.set_min_coherence (true); + EXPECT_EQ (r2.min_coherence (), true); + EXPECT_EQ (r2.is_merged (), false); + EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); + + // read-only experiments with r2 + + // inserting a box into the region will reset the merged state + // and not bring it back by resetting min_coherence + r3 = r2; + r3.insert (db::Box (0, 0, 100, 100)); + r3.set_min_coherence (false); + EXPECT_EQ (r3.is_merged (), false); + EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0);(0,0;0,100;100,100;100,0)"); + + // unmerge after resetting min coherence + r3 = r2; + r3.set_min_coherence (false); + EXPECT_EQ (r3.is_merged (), true); + r3.begin_unmerged (); + EXPECT_EQ (r3.is_merged (), false); + EXPECT_EQ (r3.to_string (), "(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000);(0,0;0,2000;1000,2000;1000,0)"); + + // r2 still remembers it is merged, even though it does not say so and + // will unmerge + r3 = r2; + EXPECT_EQ (r3.is_merged (), false); + // unmerge + r3.begin_unmerged (); + EXPECT_EQ (r3.is_merged (), false); + EXPECT_EQ (r3.to_string (), "(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000);(0,0;0,2000;1000,2000;1000,0)"); + + // r2 merged state it can be restored by resetting min_coherence to false + r2.set_min_coherence (false); + EXPECT_EQ (r2.min_coherence (), false); + EXPECT_EQ (r2.is_merged (), true); + EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); +} + +TEST(46_min_coherence_and_merge_from_original) +{ + db::Layout ly; + + auto &top = ly.cell (ly.add_cell ("TOP")); + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + top.shapes (l1).insert (db::Box (0, 0, 1000, 2000)); + top.shapes (l1).insert (db::Box (1000, 2000, 10000, 3000)); + top.shapes (l1).insert (db::Box (9000, 3000, 10000, 10000)); + + db::Region r1 (db::RecursiveShapeIterator (ly, top, l1)); + + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), false); + EXPECT_EQ (r1.to_string (), "(0,0;0,2000;1000,2000;1000,0);(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000)"); + + // forces merged status + r1.sized (1); + + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), true); + + db::Region r2 = r1.merged (); + EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); + + EXPECT_EQ (r2.min_coherence (), false); + + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), true); + + r1.set_min_coherence (true); + + // valid_merged_polygons is invalidated + EXPECT_EQ (r1.min_coherence (), true); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), false); + + r1.set_min_coherence (false); + + // setting min_coherence to false will bring the validation back + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), true); + + r1.set_min_coherence (true); + + // valid_merged_polygons is invalidated again by setting min_coherence to true + EXPECT_EQ (r1.min_coherence (), true); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), false); + + // inserting a box into the region will reset the merged valid flag and not bring it back by resetting min_coherence + db::Region r3 = r1; + r3.insert (db::Box (0, 0, 100, 100)); + EXPECT_EQ (r3.min_coherence (), true); + r3.set_min_coherence (false); + EXPECT_EQ (r3.is_merged (), false); + EXPECT_EQ (dynamic_cast (r3.delegate ())->merged_polygons_available (), false); + + // creates a intrinsically merged copy and tests if the min_coherence + // flag is inherited + r3 = r1.merged (); + EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,0);(1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000)"); + EXPECT_EQ (r3.is_merged (), true); + EXPECT_EQ (r3.min_coherence (), true); + + // merged polygons are validated against min_coherence = true during r1.merged() before, so + // resetting min_coherence to false will invalidat them + r1.set_min_coherence (false); + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), false); + + // creates a intrinsically merged copy and tests if the min_coherence + // flag is inherited + r3 = r1.merged (); + EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); + EXPECT_EQ (r3.is_merged (), true); + EXPECT_EQ (r3.min_coherence (), false); + + // r2 is an intrinsically merged region (with min coherence false) + EXPECT_EQ (r2.min_coherence (), false); + EXPECT_EQ (r2.is_merged (), true); + EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); + + // after changing min_coherence, the merged status is no longer true + r2.set_min_coherence (true); + EXPECT_EQ (r2.min_coherence (), true); + EXPECT_EQ (r2.is_merged (), false); + EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); + + // read-only experiments with r2 + + // inserting a box into the region will reset the merged state + // and not bring it back by resetting min_coherence + r3 = r2; + r3.insert (db::Box (0, 0, 100, 100)); + r3.set_min_coherence (false); + EXPECT_EQ (r3.is_merged (), false); + EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0);(0,0;0,100;100,100;100,0)"); + + // r2 merged state it can be restored by resetting min_coherence to false + r2.set_min_coherence (false); + EXPECT_EQ (r2.min_coherence (), false); + EXPECT_EQ (r2.is_merged (), true); + EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); +} + TEST(100_Issue1275) { db::Point pts[] = { diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 108b5ed4b..0a0ff2a66 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -3341,3 +3341,133 @@ TEST(deep_region_peel) db::compare_layouts (_this, ly, tl::testdata () + "/algo/deep_region_peel_au.gds"); } +TEST(min_coherence_and_merge) +{ + db::Layout ly; + + auto &top = ly.cell (ly.add_cell ("TOP")); + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + top.shapes (l1).insert (db::Box (0, 0, 1000, 2000)); + top.shapes (l1).insert (db::Box (1000, 2000, 10000, 3000)); + top.shapes (l1).insert (db::Box (9000, 3000, 10000, 10000)); + + db::DeepShapeStore dss; + + db::Region r1 (db::RecursiveShapeIterator (ly, top, l1), dss); + + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), false); + EXPECT_EQ (r1.to_string (), "(0,0;0,2000;1000,2000;1000,0);(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000)"); + + // forces merged status + r1.sized (1); + + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), true); + + db::Region r2 = r1.merged (); + EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); + + EXPECT_EQ (r2.min_coherence (), false); + + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), true); + + r1.set_min_coherence (true); + + // valid_merged_polygons is invalidated + EXPECT_EQ (r1.min_coherence (), true); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), false); + + r1.set_min_coherence (false); + + // setting min_coherence to false will bring the validation back + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), true); + + r1.set_min_coherence (true); + + // valid_merged_polygons is invalidated again by setting min_coherence to true + EXPECT_EQ (r1.min_coherence (), true); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), false); + + // inserting a box into the region will reset the merged valid flag and not bring it back by resetting min_coherence + db::Region r3 = r1; + EXPECT_EQ (r3.min_coherence (), true); + r3.insert (db::Box (0, 0, 100, 100)); + r3.set_min_coherence (false); + EXPECT_EQ (r3.is_merged (), false); + EXPECT_EQ (dynamic_cast (r3.delegate ())->merged_polygons_available (), false); + + // creates a intrinsically merged copy and tests if the min_coherence + // flag is inherited + r3 = r1.merged (); + EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,0);(1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000)"); + EXPECT_EQ (r3.is_merged (), true); + EXPECT_EQ (r3.min_coherence (), true); + + // merged polygons are validated against min_coherence = true during r1.merged() before, so + // resetting min_coherence to false will invalidat them + r1.set_min_coherence (false); + EXPECT_EQ (r1.min_coherence (), false); + EXPECT_EQ (r1.is_merged (), false); + EXPECT_EQ (dynamic_cast (r1.delegate ())->merged_polygons_available (), false); + + // creates a intrinsically merged copy and tests if the min_coherence + // flag is inherited + r3 = r1.merged (); + EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); + EXPECT_EQ (r3.is_merged (), true); + EXPECT_EQ (r3.min_coherence (), false); + + // r2 is an intrinsically merged region (with min coherence false) + EXPECT_EQ (r2.min_coherence (), false); + EXPECT_EQ (r2.is_merged (), true); + EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); + + // after changing min_coherence, the merged status is no longer true + r2.set_min_coherence (true); + EXPECT_EQ (r2.min_coherence (), true); + EXPECT_EQ (r2.is_merged (), false); + EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); + + // read-only experiments with r2 + + // inserting a box into the region will reset the merged state + // and not bring it back by resetting min_coherence + r3 = r2; + r3.insert (db::Box (0, 0, 100, 100)); + r3.set_min_coherence (false); + EXPECT_EQ (r3.is_merged (), false); + EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0);(0,0;0,100;100,100;100,0)"); + + // unmerge after resetting min coherence + r3 = r2; + r3.set_min_coherence (false); + EXPECT_EQ (r3.is_merged (), true); + r3.begin_unmerged (); + EXPECT_EQ (r3.is_merged (), false); + EXPECT_EQ (r3.to_string (), "(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000);(0,0;0,2000;1000,2000;1000,0)"); + + // r2 still remembers it is merged, even though it does not say so and + // will unmerge + r3 = r2; + EXPECT_EQ (r3.is_merged (), false); + // unmerge + r3.begin_unmerged (); + EXPECT_EQ (r3.is_merged (), false); + EXPECT_EQ (r3.to_string (), "(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000);(0,0;0,2000;1000,2000;1000,0)"); + + // r2 merged state it can be restored by resetting min_coherence to false + r2.set_min_coherence (false); + EXPECT_EQ (r2.min_coherence (), false); + EXPECT_EQ (r2.is_merged (), true); + EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)"); +} diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 40fcf0376..9928e45de 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -2123,3 +2123,23 @@ TEST(149d_compound_drc_with_mag) { run_test (_this, "149", true); } + +TEST(150_optimized_separation) +{ + run_test (_this, "150", false); +} + +TEST(150d_optimized_separation) +{ + run_test (_this, "150", true); +} + +TEST(151_edges_from_region_with_properties) +{ + run_test (_this, "151", false); +} + +TEST(151d_edges_from_region_with_properties) +{ + run_test (_this, "151", true); +} diff --git a/src/img/img/imgService.cc b/src/img/img/imgService.cc index dd8ca8b04..90391d355 100644 --- a/src/img/img/imgService.cc +++ b/src/img/img/imgService.cc @@ -412,6 +412,7 @@ View::render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas) Service::Service (db::Manager *manager, lay::LayoutViewBase *view) : lay::BackgroundViewObject (view->canvas ()), + lay::ViewService (view->canvas ()), lay::Editable (view), lay::Plugin (view), db::Object (manager), @@ -424,6 +425,9 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view) m_images_visible (true), m_visibility_cache_valid (false) { + // for receiving mouse_move events to get the pixel value + ui ()->grab_mouse (this, false); + // place images behind the grid z_order (-1); @@ -465,6 +469,9 @@ void Service::show_images (bool f) { if (m_images_visible != f) { + if (! f) { + clear_selection (); + } m_images_visible = f; view ()->redraw_deco_layer (); } @@ -596,9 +603,52 @@ dragging_what (const img::Object *iobj, const db::DBox &search_dbox, img::Servic } bool -Service::mouse_move_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool /*prio*/) +Service::mouse_move_event (const db::DPoint &pos, unsigned int /*buttons*/, bool prio) { - // .. nothing yet .. + if (! prio && (m_selected_image_views.size () == size_t (1) || (mp_transient_view && ! editables ()->has_selection ()))) { + + const img::Object *image = 0; + bool is_transient = false; + if (! m_selected_image_views.empty ()) { + image = m_selected_image_views.front ()->image_object (); + } else if (mp_transient_view) { + image = mp_transient_view->image_object (); + is_transient = true; + } + + if (image) { + + std::string data_string; + + db::DPoint pixel = image->matrix ().inverted ().trans (pos); + if (pixel.x () > image->width () * -0.5 - 0.5 + db::epsilon && pixel.x () < image->width () * 0.5 + 0.5 - db::epsilon && + pixel.y () > image->height () * -0.5 - 0.5 + db::epsilon && pixel.y () < image->height () * 0.5 + 0.5 - db::epsilon) { + + db::Point pixel_index = db::Point (pixel + db::DVector (image->width () * 0.5 - 0.5, image->height () * 0.5 - 0.5)); + + // check once again to account to rounding issues + if (pixel_index.x () >= 0 && pixel_index.x () < image->width () && + pixel_index.y () >= 0 && pixel_index.y () < image->height ()) { + + if (image->is_color ()) { + data_string = tl::sprintf ("RGB: %.5g,%.5g,%.5g", + image->pixel (pixel_index.x (), pixel_index.y (), 0), + image->pixel (pixel_index.x (), pixel_index.y (), 1), + image->pixel (pixel_index.x (), pixel_index.y (), 2)); + } else { + data_string = tl::sprintf ("%.5g", image->pixel (pixel_index.x (), pixel_index.y ())); + } + + } + + } + + display_status (is_transient, data_string); + + } + + } + return false; } @@ -1309,7 +1359,6 @@ Service::transient_select (const db::DPoint &pos) clear_transient_selection (); bool any_selected = false; - std::string data_string; // compute search box double l = catch_distance (); @@ -1337,41 +1386,12 @@ Service::transient_select (const db::DPoint &pos) mp_transient_view = new img::View (this, imin, img::View::mode_transient); } - if (mp_transient_view->image_object ()) { - - const img::Object *image = mp_transient_view->image_object (); - - db::DPoint pixel = image->matrix ().inverted ().trans (pos); - if (pixel.x () > image->width () * -0.5 - 0.5 + db::epsilon && pixel.x () < image->width () * 0.5 + 0.5 - db::epsilon && - pixel.y () > image->height () * -0.5 - 0.5 + db::epsilon && pixel.y () < image->height () * 0.5 + 0.5 - db::epsilon) { - - db::Point pixel_index = db::Point (pixel + db::DVector (image->width () * 0.5 - 0.5, image->height () * 0.5 - 0.5)); - - // check once again to account to rounding issues - if (pixel_index.x () >= 0 && pixel_index.x () < image->width () && - pixel_index.y () >= 0 && pixel_index.y () < image->height ()) { - - if (image->is_color ()) { - data_string = tl::sprintf ("RGB: %.5g,%.5g,%.5g", - image->pixel (pixel_index.x (), pixel_index.y (), 0), - image->pixel (pixel_index.x (), pixel_index.y (), 1), - image->pixel (pixel_index.x (), pixel_index.y (), 2)); - } else { - data_string = tl::sprintf ("%.5g", image->pixel (pixel_index.x (), pixel_index.y ())); - } - - } - - } - - } - any_selected = true; } if (any_selected && ! editables ()->has_selection ()) { - display_status (true, data_string); + display_status (true); } return any_selected; @@ -1474,6 +1494,7 @@ Service::select (const db::DBox &box, lay::Editable::SelectionMode mode) select (mp_view->annotation_shapes ().iterator_from_pointer (robj), mode); m_previous_selection.insert (mp_view->annotation_shapes ().iterator_from_pointer (robj)); needs_update = true; + any_selected = true; } } diff --git a/src/img/img/imgService.h b/src/img/img/imgService.h index a709ab2ca..bceb60b1d 100644 --- a/src/img/img/imgService.h +++ b/src/img/img/imgService.h @@ -195,6 +195,7 @@ private: class IMG_PUBLIC Service : public lay::BackgroundViewObject, + public lay::ViewService, public lay::Editable, public lay::Plugin, public db::Object @@ -436,7 +437,15 @@ public: /** * @brief Obtain the lay::Editable interface */ - lay::Editable *editable_interface () + virtual lay::Editable *editable_interface () + { + return this; + } + + /** + * @brief Obtain the lay::ViewService interface + */ + virtual lay::ViewService *view_service_interface () { return this; } diff --git a/src/laybasic/laybasic/layEditable.cc b/src/laybasic/laybasic/layEditable.cc index e826ebc32..b364a7c02 100644 --- a/src/laybasic/laybasic/layEditable.cc +++ b/src/laybasic/laybasic/layEditable.cc @@ -626,7 +626,7 @@ Editables::end_move (const db::DVector &v, db::Transaction *transaction) { std::unique_ptr trans_holder (transaction ? transaction : new db::Transaction (manager (), tl::to_string (tr ("Move")))); - if (m_any_move_operation) { + if (m_any_move_operation || v != db::DVector ()) { trans_holder->open (); diff --git a/src/laybasic/laybasic/layLayerProperties.cc b/src/laybasic/laybasic/layLayerProperties.cc index 1758ad72b..0e899efad 100644 --- a/src/laybasic/laybasic/layLayerProperties.cc +++ b/src/laybasic/laybasic/layLayerProperties.cc @@ -640,6 +640,13 @@ LayerProperties::do_realize (const LayoutViewBase *view) const } + if (m_layer_index >= 0 && cv->layout ().is_valid_layer (m_layer_index)) { + const auto &lp = cv->layout ().get_properties (m_layer_index); + if (! lp.name.empty () && m_source_real.name () != lp.name) { + m_source_real.name (lp.name); + } + } + } } diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index d893ea79b..0a07c65cc 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -3383,6 +3383,7 @@ LayoutViewBase::reload_layout (unsigned int cv_index) } set_properties (new_props); + do_update_layer_sources (); goto_view (state); } diff --git a/src/layui/layui/layDialogs.cc b/src/layui/layui/layDialogs.cc index a303c6dd0..61d16c46c 100644 --- a/src/layui/layui/layDialogs.cc +++ b/src/layui/layui/layDialogs.cc @@ -196,7 +196,9 @@ NewLayoutPropertiesDialog::exec_dialog (std::string &technology, std::string &ce while (! ex.at_end ()) { db::LayerProperties lp; try { - lp.read (ex); + if (! lp.read (ex)) { + break; + } } catch (...) { break; } diff --git a/src/plugins/streamers/lstream/unit_tests/dbLStreamReaderTests.cc b/src/plugins/streamers/lstream/unit_tests/dbLStreamReaderTests.cc index 6c4a92d2b..5ce038752 100644 --- a/src/plugins/streamers/lstream/unit_tests/dbLStreamReaderTests.cc +++ b/src/plugins/streamers/lstream/unit_tests/dbLStreamReaderTests.cc @@ -131,3 +131,23 @@ TEST(variants) run_test (_this, tl::testdata (), "variants.lstr", "variants_au.oas"); } +TEST(reload_layer_props) +{ + db::LoadLayoutOptions options; + + db::Layout layout; + auto l1 = layout.insert_layer (db::LayerProperties (1, 0, "A")); + auto l2 = layout.insert_layer (db::LayerProperties (2, 0, "B")); + + { + std::string fn (tl::testdata ()); + fn += "/lstream/layers.lstr"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (layout, options); + } + + // layer 1 got renamed + EXPECT_EQ (layout.get_properties (l1).to_string (), "ONE (1/0)"); + EXPECT_EQ (layout.get_properties (l2).to_string (), "B (2/0)"); +} diff --git a/src/plugins/tools/net_tracer/unit_tests/dbTraceAllNets.cc b/src/plugins/tools/net_tracer/unit_tests/dbTraceAllNets.cc index 7b7ad5259..0cc6d3e03 100644 --- a/src/plugins/tools/net_tracer/unit_tests/dbTraceAllNets.cc +++ b/src/plugins/tools/net_tracer/unit_tests/dbTraceAllNets.cc @@ -85,7 +85,7 @@ void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracer fn += file_au; CHECKPOINT (); - db::compare_layouts (_this, layout_nets, fn, db::NormalizationMode (db::WriteOAS | db::AsPolygons)); + db::compare_layouts (_this, layout_nets, fn, db::NormalizationMode (db::WriteOAS | db::AsPolygons | db::WithoutLayerNames)); } TEST(1) diff --git a/src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py b/src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py index 6a89fc446..8edc5a808 100644 --- a/src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py +++ b/src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py @@ -352,7 +352,7 @@ class _PCellDeclarationHelperMixin: """ default implementation """ - return self.name() + return super(_PCellDeclarationHelperMixin, self).name() def coerce_parameters_impl(self): """ diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index f18782fc3..bf92a578a 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -16161,8 +16161,7 @@ class DText: Setter: @brief Sets the vertical alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ x: float r""" @@ -39453,23 +39452,14 @@ class Layout: ... @overload @classmethod - def new(cls, editable: bool) -> Layout: - r""" - @brief Creates a layout object - - This constructor specifies whether the layout is editable. In editable mode, some optimizations are disabled and the layout can be manipulated through a variety of methods. - - This method was introduced in version 0.22. - """ - ... - @overload - @classmethod - def new(cls, editable: bool, manager: Manager) -> Layout: + def new(cls, editable: bool, manager: Optional[Manager] = ...) -> Layout: r""" @brief Creates a layout object attached to a manager This constructor specifies a manager object which is used to store undo information for example. It also allows one to specify whether the layout is editable. In editable mode, some optimizations are disabled and the layout can be manipulated through a variety of methods. + The manager object can be nil - in that case, undo/redo is not supported. + This method was introduced in version 0.22. """ ... @@ -39484,6 +39474,20 @@ class Layout: Starting with version 0.25, layouts created with the default constructor are always editable. Before that version, they inherited the editable flag from the application. """ ... + @overload + @classmethod + def new(cls, source_cell: Cell, editable: Optional[Any] = ..., manager: Optional[Manager] = ...) -> Layout: + r""" + @brief Creates a layout object as a copy of another cell + + This convenience constructor creates a new layout object as a hierarchical copy of the source cell including all child cells and shapes. + + If 'editable' is a boolean value, the new layout object will be made editable depending on that value. If 'nil' is used for 'editable', the editable attribute is copied from the layout the source cell lives in. + 'manager' can be a \Manager object to which the new layout will be attached. + + This method was introduced in version 0.30.10. + """ + ... @classmethod def properties_array(cls, properties_id: int) -> Any: r""" @@ -39591,22 +39595,14 @@ class Layout: """ ... @overload - def __init__(self, editable: bool) -> None: - r""" - @brief Creates a layout object - - This constructor specifies whether the layout is editable. In editable mode, some optimizations are disabled and the layout can be manipulated through a variety of methods. - - This method was introduced in version 0.22. - """ - ... - @overload - def __init__(self, editable: bool, manager: Manager) -> None: + def __init__(self, editable: bool, manager: Optional[Manager] = ...) -> None: r""" @brief Creates a layout object attached to a manager This constructor specifies a manager object which is used to store undo information for example. It also allows one to specify whether the layout is editable. In editable mode, some optimizations are disabled and the layout can be manipulated through a variety of methods. + The manager object can be nil - in that case, undo/redo is not supported. + This method was introduced in version 0.22. """ ... @@ -39620,6 +39616,19 @@ class Layout: Starting with version 0.25, layouts created with the default constructor are always editable. Before that version, they inherited the editable flag from the application. """ ... + @overload + def __init__(self, source_cell: Cell, editable: Optional[Any] = ..., manager: Optional[Manager] = ...) -> None: + r""" + @brief Creates a layout object as a copy of another cell + + This convenience constructor creates a new layout object as a hierarchical copy of the source cell including all child cells and shapes. + + If 'editable' is a boolean value, the new layout object will be made editable depending on that value. If 'nil' is used for 'editable', the editable attribute is copied from the layout the source cell lives in. + 'manager' can be a \Manager object to which the new layout will be attached. + + This method was introduced in version 0.30.10. + """ + ... def _const_cast(self) -> Layout: r""" @brief Returns a non-const reference to self. @@ -49955,17 +49964,17 @@ class Netlist: @overload def circuit_by_cell_index(self, cell_index: int) -> Circuit: r""" - @brief Gets the circuit object for a given cell index (const version). + @brief Gets the circuit object for a given cell index. If the cell index is not valid or no circuit is registered with this index, nil is returned. - - This constness variant has been introduced in version 0.26.8. """ ... @overload def circuit_by_cell_index(self, cell_index: int) -> Circuit: r""" - @brief Gets the circuit object for a given cell index. + @brief Gets the circuit object for a given cell index (const version). If the cell index is not valid or no circuit is registered with this index, nil is returned. + + This constness variant has been introduced in version 0.26.8. """ ... @overload @@ -66555,10 +66564,10 @@ class Shape: Applies to texts only. Will throw an exception if the object is not a text. Setter: - @brief Sets the text transformation + @brief Sets the text transformation in micrometer units Applies to texts only. Will throw an exception if the object is not a text. - This method has been introduced in version 0.23. + This method has been introduced in version 0.25. """ text_valign: int r""" @@ -71393,8 +71402,7 @@ class Text: Setter: @brief Sets the vertical alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ x: int r""" diff --git a/src/pymod/distutils_src/klayout/laycore.pyi b/src/pymod/distutils_src/klayout/laycore.pyi index e0354dfaa..9787e56c6 100644 --- a/src/pymod/distutils_src/klayout/laycore.pyi +++ b/src/pymod/distutils_src/klayout/laycore.pyi @@ -10956,6 +10956,16 @@ class PixelBuffer: By default, the pixel buffer does not support an alpha channel. """ @classmethod + def from_bytes(cls, data: bytes) -> PixelBuffer: + r""" + @brief Reconstructs a pixel buffer from a byte stream produced by \to_bytes + + The width and height are taken from the header and the stream length is checked against them. The dimensions are capped at 65536 x 65536; a header exceeding this limit raises an error. + + This method has been added in version 0.30.10. + """ + ... + @classmethod def from_png_data(cls, data: bytes) -> PixelBuffer: r""" @brief Reads the pixel buffer from a PNG byte stream @@ -11142,6 +11152,15 @@ class PixelBuffer: @brief Swaps data with another PixelBuffer object """ ... + def to_bytes(self) -> bytes: + r""" + @brief Converts the pixel buffer to a raw byte stream + + The stream starts with an 8-byte header (width and height as 32-bit unsigned integers) followed by the raw ARGB32 pixel data with 4 bytes per pixel in row-major order, top to bottom. Unlike \to_png_data this method has zero encoding overhead. Use \from_bytes to reconstruct the pixel buffer. + + This method has been added in version 0.30.10. + """ + ... def to_png_data(self) -> bytes: r""" @brief Converts the pixel buffer to a PNG byte stream diff --git a/testdata/algo/deep_region_au18.gds b/testdata/algo/deep_region_au18.gds index 35edb6a15..8782b745e 100644 Binary files a/testdata/algo/deep_region_au18.gds and b/testdata/algo/deep_region_au18.gds differ diff --git a/testdata/algo/deep_region_au41.gds b/testdata/algo/deep_region_au41.gds index 7e84f976d..0c79103bd 100644 Binary files a/testdata/algo/deep_region_au41.gds and b/testdata/algo/deep_region_au41.gds differ diff --git a/testdata/algo/flat_region_au18.gds b/testdata/algo/flat_region_au18.gds index 8c7800243..f7dafc3e3 100644 Binary files a/testdata/algo/flat_region_au18.gds and b/testdata/algo/flat_region_au18.gds differ diff --git a/testdata/drc/drcSimpleTests_150.drc b/testdata/drc/drcSimpleTests_150.drc new file mode 100644 index 000000000..3bcc8e15c --- /dev/null +++ b/testdata/drc/drcSimpleTests_150.drc @@ -0,0 +1,20 @@ +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +deep + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +l1.space(1.um).output(100, 0) +l1.sep(l2, 1.um).output(101, 0) +(l1 + l2).space(1.um, transparent).output(102, 0) +(l1 + l2).space(1.um).output(103, 0) + diff --git a/testdata/drc/drcSimpleTests_150.gds b/testdata/drc/drcSimpleTests_150.gds new file mode 100644 index 000000000..d433eb8bb Binary files /dev/null and b/testdata/drc/drcSimpleTests_150.gds differ diff --git a/testdata/drc/drcSimpleTests_151.drc b/testdata/drc/drcSimpleTests_151.drc new file mode 100644 index 000000000..154464951 --- /dev/null +++ b/testdata/drc/drcSimpleTests_151.drc @@ -0,0 +1,14 @@ +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +deep + +l1 = input(1, 0, enable_props) +l1.output(1, 0) + +l1.edges.output(100, 0) + diff --git a/testdata/drc/drcSimpleTests_151.gds b/testdata/drc/drcSimpleTests_151.gds new file mode 100644 index 000000000..9750b9468 Binary files /dev/null and b/testdata/drc/drcSimpleTests_151.gds differ diff --git a/testdata/drc/drcSimpleTests_au150.gds b/testdata/drc/drcSimpleTests_au150.gds new file mode 100644 index 000000000..9e46df4e8 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au150.gds differ diff --git a/testdata/drc/drcSimpleTests_au150d.gds b/testdata/drc/drcSimpleTests_au150d.gds new file mode 100644 index 000000000..9e46df4e8 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au150d.gds differ diff --git a/testdata/drc/drcSimpleTests_au151.gds b/testdata/drc/drcSimpleTests_au151.gds new file mode 100644 index 000000000..75622fd4b Binary files /dev/null and b/testdata/drc/drcSimpleTests_au151.gds differ diff --git a/testdata/drc/drcSimpleTests_au151d.gds b/testdata/drc/drcSimpleTests_au151d.gds new file mode 100644 index 000000000..75622fd4b Binary files /dev/null and b/testdata/drc/drcSimpleTests_au151d.gds differ diff --git a/testdata/drc/drcSimpleTests_au48.gds b/testdata/drc/drcSimpleTests_au48.gds index 6d6b3325d..f7d37b0cf 100644 Binary files a/testdata/drc/drcSimpleTests_au48.gds and b/testdata/drc/drcSimpleTests_au48.gds differ diff --git a/testdata/drc/drcSimpleTests_au48d.gds b/testdata/drc/drcSimpleTests_au48d.gds index a0a418801..5ce5ee703 100644 Binary files a/testdata/drc/drcSimpleTests_au48d.gds and b/testdata/drc/drcSimpleTests_au48d.gds differ diff --git a/testdata/lstream/layers.lstr b/testdata/lstream/layers.lstr new file mode 100644 index 000000000..0bc26c5c0 Binary files /dev/null and b/testdata/lstream/layers.lstr differ diff --git a/testdata/python/dbPCells.py b/testdata/python/dbPCells.py index 0384e3253..885ad1e29 100644 --- a/testdata/python/dbPCells.py +++ b/testdata/python/dbPCells.py @@ -134,6 +134,16 @@ if "PCellDeclarationHelper" in pya.__dict__: self.width = self.shape.box.width() * self.layout.dbu self.height = self.shape.box.height() * self.layout.dbu + # A PCell with a parameter named "name" + class PCellWithNameParameter(pya.PCellDeclarationHelper): + + def __init__(self): + super(PCellWithNameParameter, self).__init__() + self.param("name", self.TypeString, "Name", default = "") + + def produce_impl(self): + self.cell.shapes(self.layout.layer(1, 0)).insert(pya.Text(self.name, pya.Trans())) + class PCellTestLib2(pya.Library): def __init__(self): @@ -143,6 +153,7 @@ if "PCellDeclarationHelper" in pya.__dict__: # create the PCell declarations self.layout().register_pcell("Box2", BoxPCell2()) + self.layout().register_pcell("PCellWithNameParameter", PCellWithNameParameter()) # register us with the name "PCellTestLib2" self.register("PCellTestLib2") @@ -588,6 +599,22 @@ class DBPCellTests(unittest.TestCase): self.assertEqual(c2.display_title(), "PCellTestLib3.RecursivePCell(L=1/0,E=(0,0;20,0),LVL=4") self.assertEqual(str(c1.dbbox()), "(0,0;20,5.774)") + # PCell with "name" parameter + def test_15(self): + + # instantiate and register the library + tl = PCellTestLib2() + + lib = pya.Library.library_by_name("PCellTestLib2") + pcell_decl_id = lib.layout().pcell_id("PCellWithNameParameter") + + param = { "name": "xyz" } + pcell_var_id = lib.layout().add_pcell_variant(pcell_decl_id, param) + + self.assertEqual(lib.layout().cell(pcell_var_id).name, "PCellWithNameParameter") + self.assertEqual(lib.layout().begin_shapes(pcell_var_id, lib.layout().layer(1, 0)).shape().to_s(), "text ('xyz',r0 0,0)") + + # run unit tests if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(DBPCellTests) diff --git a/testdata/ruby/dbLayoutTests1.rb b/testdata/ruby/dbLayoutTests1.rb index 97c903762..fd52659de 100644 --- a/testdata/ruby/dbLayoutTests1.rb +++ b/testdata/ruby/dbLayoutTests1.rb @@ -2518,6 +2518,40 @@ class DBLayoutTests1_TestClass < TestBase end + # Create layout from a cell + def test_newLayoutFromCell + + [ true, false ].each do |editable| + + ly = RBA::Layout::new(editable) + top = ly.create_cell("TOP") + a = ly.create_cell("A") + b = ly.create_cell("B") + l1 = ly.layer(1, 0) + a.shapes(l1).insert(RBA::Box::new(0, 0, 1000, 2000)) + b.shapes(l1).insert(RBA::Box::new(0, 0, 100, 200)) + top.insert(RBA::CellInstArray::new(a, RBA::Trans::new)) + a.insert(RBA::CellInstArray::new(b, RBA::Trans::new(1, 2))) + + ly2 = RBA::Layout::new(a) + assert_equal(ly2.is_editable?, ly.is_editable?) + assert_equal(ly2.top_cell.name, "A") + assert_equal(collect(ly2.top_cell.begin_shapes_rec(ly2.layer(1, 0)), ly2), "[A](0,0;1000,2000)/[B](1,2;101,202)") + + ly2 = RBA::Layout::new(a, false) + assert_equal(ly2.is_editable?, false) + assert_equal(ly2.top_cell.name, "A") + assert_equal(collect(ly2.top_cell.begin_shapes_rec(ly2.layer(1, 0)), ly2), "[A](0,0;1000,2000)/[B](1,2;101,202)") + + ly2 = RBA::Layout::new(a, true) + assert_equal(ly2.is_editable?, true) + assert_equal(ly2.top_cell.name, "A") + assert_equal(collect(ly2.top_cell.begin_shapes_rec(ly2.layer(1, 0)), ly2), "[A](0,0;1000,2000)/[B](1,2;101,202)") + + end + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 4bdd285a9..6447e0bdc 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -213,6 +213,15 @@ class DBNetlist_TestClass < TestBase assert_equal(p1.name, "X") assert_equal(p2.name, "B") + n1 = c.create_net("NET1") + n2 = c.create_net("NET2") + + c.connect_pin(p1.id, n1) + assert_equal(n1.pin_count, 1) + + c.connect_pin(p2.id, n2) + assert_equal(n2.pin_count, 1) + assert_equal(p1.property(17), nil) p1.set_property(17, 42) assert_equal(p1.property(17), 42) @@ -245,6 +254,9 @@ class DBNetlist_TestClass < TestBase c.remove_pin(0) + assert_equal(n1.pin_count, 0) + assert_equal(n2.pin_count, 1) + names = [] c.each_pin { |p| names << p.name } assert_equal(names, [ "B" ]) @@ -253,6 +265,13 @@ class DBNetlist_TestClass < TestBase assert_equal(c.pin_by_id(1) == nil, false) assert_equal(c.pin_by_id(1).name, "B") + c.remove_pin(1) + + assert_equal(n1.pin_count, 0) + assert_equal(n2.pin_count, 0) + + assert_equal(c.pin_by_id(1) == nil, true) + end def test_4_Device diff --git a/testdata/ruby/dbPCells.rb b/testdata/ruby/dbPCells.rb index 864b82ead..82e6f5624 100644 --- a/testdata/ruby/dbPCells.rb +++ b/testdata/ruby/dbPCells.rb @@ -173,6 +173,20 @@ if RBA.constants.member?(:PCellDeclarationHelper) end + # A PCell with a parameter named "name" + class PCellWithNameParameter < RBA::PCellDeclarationHelper + + def initialize + super() + param(:name, TypeString, "Name", :default => "") + end + + def produce_impl + cell.shapes(layout.layer(1, 0)).insert(RBA::Text::new(name, RBA::Trans::new)) + end + + end + class PCellTestLib2 < RBA::Library def initialize @@ -182,6 +196,7 @@ if RBA.constants.member?(:PCellDeclarationHelper) # create the PCell declarations layout.register_pcell("Box2", BoxPCell2::new) + layout.register_pcell("PCellWithNameParameter", PCellWithNameParameter::new) # register us with the name "MyLib" self.register("PCellTestLib2") @@ -1012,6 +1027,25 @@ class DBPCell_TestClass < TestBase end + # PCell with "name" parameter + def test_15 + + # instantiate and register the library + tl = PCellTestLib2::new + + lib = RBA::Library::library_by_name("PCellTestLib2") + pcell_decl_id = lib.layout.pcell_id("PCellWithNameParameter") + + param = { "name" => "xyz" } + pcell_var_id = lib.layout.add_pcell_variant(pcell_decl_id, param) + + assert_equal(lib.layout.cell(pcell_var_id).name, "PCellWithNameParameter") + assert_equal(first_shape(lib.layout.begin_shapes(pcell_var_id, lib.layout.layer(1, 0))).to_s, "text ('xyz',r0 0,0)") + + tl._destroy + + end + end class DBPCellParameterStates_TestClass < TestBase diff --git a/version.sh b/version.sh index 664c35ca2..7ef614d84 100644 --- a/version.sh +++ b/version.sh @@ -2,10 +2,10 @@ # This script is sourced to define the main version parameters # The main version -KLAYOUT_VERSION="0.30.9" +KLAYOUT_VERSION="0.30.10" # The version used for PyPI (don't use variables here!) -KLAYOUT_PYPI_VERSION="0.30.9" +KLAYOUT_PYPI_VERSION="0.30.10" # The build date KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")