Merge pull request #2389 from KLayout/wip

Wip
This commit is contained in:
Matthias Köfferlein 2026-07-25 09:45:27 +02:00 committed by GitHub
commit eeb908669a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
57 changed files with 1362 additions and 265 deletions

View File

@ -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): 0.30.9 (2026-05-25):
* Bugfix: %GITHUB%/issues/2331 Screen size error (Assertion tlPixelBuffer.cc:260 n < m_height was not true) * 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 * Enhancement: %GITHUB%/issues/2335 Editor Options (F3) behaviour has changed

View File

@ -1,3 +1,10 @@
klayout (0.30.10-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Mon, 20 Jul 2026 23:20:04 +0200
klayout (0.30.9-1) unstable; urgency=low klayout (0.30.9-1) unstable; urgency=low
* New features and bugfixes * New features and bugfixes

View File

@ -289,6 +289,11 @@ module RBA
alias_method :_layout_base, :layout alias_method :_layout_base, :layout
end 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 # import the Type... constants from PCellParameterDeclaration
PCellParameterDeclaration.constants.each do |c| PCellParameterDeclaration.constants.each do |c|
if !const_defined?(c) if !const_defined?(c)
@ -595,7 +600,7 @@ module RBA
# default implementation # default implementation
def cell_name_impl def cell_name_impl
self.name _name_base()
end end
# default implementation # default implementation

View File

@ -631,14 +631,14 @@ namespace {
class OutputPairHolder class OutputPairHolder
{ {
public: public:
OutputPairHolder (InteractingOutputMode output_mode, bool merged_semantics) OutputPairHolder (InteractingOutputMode output_mode, bool merged_semantics, bool min_coherence)
{ {
if (output_mode == None) { if (output_mode == None) {
return; return;
} }
if (output_mode == Positive || output_mode == Negative || output_mode == PositiveAndNegative) { 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 ()); m_results.push_back (& m_positive->raw_polygons ());
} else { } else {
m_results.push_back ((db::Shapes *) 0); m_results.push_back ((db::Shapes *) 0);
@ -667,7 +667,7 @@ private:
std::pair<RegionDelegate *, RegionDelegate *> std::pair<RegionDelegate *, RegionDelegate *>
AsIfFlatRegion::in_and_out_generic (const Region &other, InteractingOutputMode output_mode) const 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) { if (output_mode == None) {
return oph.region_pair (); return oph.region_pair ();
@ -717,7 +717,7 @@ AsIfFlatRegion::in_and_out_generic (const Region &other, InteractingOutputMode o
std::pair<RegionDelegate *, RegionDelegate *> std::pair<RegionDelegate *, RegionDelegate *>
AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const 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) { if (output_mode == None) {
return oph.region_pair (); return oph.region_pair ();
@ -756,7 +756,7 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOut
std::vector<generic_shape_iterator<db::Edge> > others; std::vector<generic_shape_iterator<db::Edge> > others;
others.push_back (counting ? other.begin_merged () : other.begin ()); others.push_back (counting ? other.begin_merged () : other.begin ());
std::unique_ptr<FlatRegion> output (new FlatRegion (merged_semantics ())); std::unique_ptr<FlatRegion> output (new FlatRegion (merged_semantics (), 0.0, 0, min_coherence ()));
std::vector<db::Shapes *> results; std::vector<db::Shapes *> results;
results.push_back (&output->raw_polygons ()); results.push_back (&output->raw_polygons ());
@ -768,7 +768,7 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOut
std::pair<RegionDelegate *, RegionDelegate *> std::pair<RegionDelegate *, RegionDelegate *>
AsIfFlatRegion::selected_interacting_generic (const Texts &other, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const 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) { if (output_mode == None) {
return oph.region_pair (); return oph.region_pair ();
@ -814,7 +814,7 @@ AsIfFlatRegion::selected_interacting_generic (const Texts &other, InteractingOut
std::pair<RegionDelegate *, RegionDelegate *> std::pair<RegionDelegate *, RegionDelegate *>
AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, bool touching, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const 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) { if (output_mode == None) {
return oph.region_pair (); return oph.region_pair ();
@ -941,7 +941,7 @@ AsIfFlatRegion::pull_generic (const Region &other, int mode, bool touching) cons
std::vector<generic_shape_iterator<db::Polygon> > others; std::vector<generic_shape_iterator<db::Polygon> > others;
others.push_back (other.begin_merged ()); others.push_back (other.begin_merged ());
std::unique_ptr<FlatRegion> output (new FlatRegion (other.merged_semantics () || other.is_merged ())); std::unique_ptr<FlatRegion> output (new FlatRegion (other.merged_semantics () || other.is_merged (), 0.0, 0, min_coherence ()));
std::vector<db::Shapes *> results; std::vector<db::Shapes *> results;
results.push_back (&output->raw_polygons ()); 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"))); throw tl::Exception (tl::to_string (tr ("Scale and snap requires positive and non-null magnification or divisor values")));
} }
std::unique_ptr<FlatRegion> new_region (new FlatRegion (merged_semantics ())); std::unique_ptr<FlatRegion> new_region (new FlatRegion (merged_semantics (), 0.0, 0, min_coherence ()));
gx = std::max (db::Coord (1), gx); gx = std::max (db::Coord (1), gx);
gy = std::max (db::Coord (1), gy); gy = std::max (db::Coord (1), gy);
@ -1184,13 +1184,7 @@ AsIfFlatRegion::width_check (db::Coord d, const RegionCheckOptions &options) con
EdgePairsDelegate * EdgePairsDelegate *
AsIfFlatRegion::space_or_isolated_check (db::Coord d, const RegionCheckOptions &options, bool isolated) const 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) { return run_check (db::SpaceRelation, isolated, 0, d, options);
// 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);
}
} }
EdgePairsDelegate * 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 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 // 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; 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 ()); if (! merged_semantics ()) {
bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged (); 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); EdgeRelationFilter check (rel, d, options);
std::vector<db::RegionIterator> others; std::vector<db::RegionIterator> others;
std::vector<bool> foreign; std::vector<bool> foreign;
bool has_other = false;
bool other_is_merged = true; bool other_is_merged = true;
if (other == subject_regionptr () || other == foreign_regionptr ()) { if (! other) {
foreign.push_back (other == foreign_regionptr ());
foreign.push_back (true);
others.push_back (polygons); others.push_back (polygons);
other_is_merged = primary_is_merged; other_is_merged = primary_is_merged;
} else { } else {
foreign.push_back (false); foreign.push_back (false);
if (! other->merged_semantics ()) { if (! other->merged_semantics ()) {
others.push_back (other->begin ()); others.push_back (other->begin ());
other_is_merged = true; other_is_merged = true;
@ -1272,7 +1281,13 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
others.push_back (other->begin ()); others.push_back (other->begin ());
other_is_merged = other->is_merged (); 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<FlatEdgePairs> output (new FlatEdgePairs ()); std::unique_ptr<FlatEdgePairs> output (new FlatEdgePairs ());
@ -1282,7 +1297,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
if (pc_skip (options.prop_constraint)) { if (pc_skip (options.prop_constraint)) {
db::check_local_operation<db::Polygon, db::Polygon> op (check, different_polygons, primary_is_merged, has_other, other_is_merged, options); db::check_local_operation<db::Polygon, db::Polygon> op (check, different_polygons, primary_is_merged, other != 0, other_is_merged, options);
db::local_processor<db::Polygon, db::Polygon, db::EdgePair> proc; db::local_processor<db::Polygon, db::Polygon, db::EdgePair> proc;
proc.set_base_verbosity (base_verbosity ()); proc.set_base_verbosity (base_verbosity ());
@ -1293,7 +1308,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
} else { } else {
db::check_local_operation_with_properties<db::Polygon, db::Polygon> op (check, different_polygons, primary_is_merged, has_other, other_is_merged, options); db::check_local_operation_with_properties<db::Polygon, db::Polygon> op (check, different_polygons, primary_is_merged, other != 0, other_is_merged, options);
db::local_processor<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgePairWithProperties> proc; db::local_processor<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgePairWithProperties> proc;
proc.set_base_verbosity (base_verbosity ()); proc.set_base_verbosity (base_verbosity ());
@ -1334,7 +1349,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord
} }
RegionDelegate * 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 ()) { if (empty ()) {
@ -1351,8 +1366,8 @@ AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc, bool join_prope
} else { } else {
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true)); std::unique_ptr<FlatRegion> new_region (new FlatRegion (true, 0, 0, min_coh));
merge_polygons_to (new_region->raw_polygons (), min_coherence, min_wc, join_properties_on_merge); merge_polygons_to (new_region->raw_polygons (), min_coh, min_wc, join_properties_on_merge);
return new_region.release (); return new_region.release ();
@ -1695,7 +1710,7 @@ AsIfFlatRegion::and_or_not_with (bool is_and, const Region &other, PropertyConst
ep.insert (*p, n); ep.insert (*p, n);
} }
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true)); std::unique_ptr<FlatRegion> new_region (new FlatRegion (true, 0.0, 0, min_coherence ()));
db::BooleanOp op (is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); db::BooleanOp op (is_and ? db::BooleanOp::And : db::BooleanOp::ANotB);
db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); 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); ep.insert (*p, n);
} }
std::unique_ptr<FlatRegion> new_region1 (new FlatRegion (true)); std::unique_ptr<FlatRegion> new_region1 (new FlatRegion (true, 0.0, 0, min_coherence ()));
db::BooleanOp op1 (db::BooleanOp::And); db::BooleanOp op1 (db::BooleanOp::And);
db::ShapeGenerator pc1 (new_region1->raw_polygons (), true /*clear*/); db::ShapeGenerator pc1 (new_region1->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg1 (pc1, false /*don't resolve holes*/, min_coherence ()); db::PolygonGenerator pg1 (pc1, false /*don't resolve holes*/, min_coherence ());
std::unique_ptr<FlatRegion> new_region2 (new FlatRegion (true)); std::unique_ptr<FlatRegion> new_region2 (new FlatRegion (true, 0.0, 0, min_coherence ()));
db::BooleanOp op2 (db::BooleanOp::ANotB); db::BooleanOp op2 (db::BooleanOp::ANotB);
db::ShapeGenerator pc2 (new_region2->raw_polygons (), true /*clear*/); db::ShapeGenerator pc2 (new_region2->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg2 (pc2, false /*don't resolve holes*/, min_coherence ()); 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); ep.insert (*p, n);
} }
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true)); std::unique_ptr<FlatRegion> new_region (new FlatRegion (true, 0.0, 0, min_coherence ()));
db::BooleanOp op (db::BooleanOp::Xor); db::BooleanOp op (db::BooleanOp::Xor);
db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); 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); ep.insert (*p, n);
} }
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true)); std::unique_ptr<FlatRegion> new_region (new FlatRegion (true, 0.0, 0, min_coherence ()));
db::BooleanOp op (db::BooleanOp::Or); db::BooleanOp op (db::BooleanOp::Or);
db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/); db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ()); db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ());

View File

@ -353,6 +353,7 @@ Pin &Circuit::add_pin (const std::string &name)
void Circuit::remove_pin (size_t id) void Circuit::remove_pin (size_t id)
{ {
if (id < m_pin_by_id.size () && ! tl::is_null_iterator (m_pin_by_id [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_pins.erase (m_pin_by_id [id]);
m_pin_by_id [id] = pin_list::iterator (); m_pin_by_id [id] = pin_list::iterator ();
} }

View File

@ -541,31 +541,58 @@ CommonReaderBase::open_dl_uncached (db::Layout &layout, const LDPair &dl)
} }
unsigned int nl = layout.insert_layer (lp); 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_layer_map_out.map (dl, nl, lp);
m_layers_created.insert (nl); m_layers_created.insert (nl);
return std::make_pair (true, 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 { } else {
for (std::set<unsigned int>::const_iterator i = li.begin (); i != li.end (); ++i) { for (auto i = li.begin (); i != li.end (); ++i) {
m_layer_map_out.mmap (dl, *i, layout.get_properties (*i));
// change OASIS layer name if a layer exists by layer/datatype, but has a different name
const tl::interval_map <db::ld_type, std::string> *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<std::set<unsigned int>, unsigned int>::iterator mmp = m_multi_mapping_placeholders.find (li); if (li.size () == 1) {
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); m_layer_map_out.map (dl, *li.begin (), layout.get_properties (*li.begin ()));
return std::make_pair (true, *li.begin ());
} else {
for (std::set<unsigned int>::const_iterator i = li.begin (); i != li.end (); ++i) {
m_layer_map_out.mmap (dl, *i, layout.get_properties (*i));
}
std::map<std::set<unsigned int>, 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);
}
} }
} }

View File

@ -179,8 +179,10 @@ DeepRegion::~DeepRegion ()
DeepRegion::DeepRegion (const DeepRegion &other) DeepRegion::DeepRegion (const DeepRegion &other)
: MutableRegion (other), DeepShapeCollectionDelegateBase (other), : MutableRegion (other), DeepShapeCollectionDelegateBase (other),
m_merged_polygons_valid (other.m_merged_polygons_valid), 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_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) { if (m_merged_polygons_valid) {
m_merged_polygons = other.m_merged_polygons.copy (); m_merged_polygons = other.m_merged_polygons.copy ();
@ -196,8 +198,10 @@ DeepRegion::operator= (const DeepRegion &other)
DeepShapeCollectionDelegateBase::operator= (other); DeepShapeCollectionDelegateBase::operator= (other);
m_merged_polygons_valid = other.m_merged_polygons_valid; 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_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) { if (m_merged_polygons_valid) {
m_merged_polygons = other.m_merged_polygons.copy (); m_merged_polygons = other.m_merged_polygons.copy ();
} }
@ -210,9 +214,11 @@ DeepRegion::operator= (const DeepRegion &other)
void DeepRegion::init () void DeepRegion::init ()
{ {
m_merged_polygons_valid = false; m_merged_polygons_valid = false;
m_merged_polygons_min_coherence = false;
m_merged_polygons_boc_hash = 0; m_merged_polygons_boc_hash = 0;
m_merged_polygons = db::DeepLayer (); m_merged_polygons = db::DeepLayer ();
m_is_merged = false; m_is_merged = false;
m_is_merged_min_coherence = false;
} }
RegionDelegate * RegionDelegate *
@ -228,7 +234,7 @@ void DeepRegion::merged_semantics_changed ()
void DeepRegion::min_coherence_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 () void DeepRegion::join_properties_on_merge_changed ()
@ -468,7 +474,7 @@ DeepRegion::empty () const
bool bool
DeepRegion::is_merged () const DeepRegion::is_merged () const
{ {
return m_is_merged; return m_is_merged && (m_is_merged_min_coherence == min_coherence ());
} }
const db::Polygon * const db::Polygon *
@ -739,18 +745,21 @@ DeepRegion::merged_deep_layer () const
bool bool
DeepRegion::merged_polygons_available () const 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 void
DeepRegion::ensure_merged_polygons_valid () const 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 // NOTE: this will reuse the deep layer reference
m_merged_polygons = deep_layer (); m_merged_polygons = deep_layer ();
m_merged_polygons_min_coherence = min_coherence ();
} else { } else {
@ -796,6 +805,7 @@ DeepRegion::ensure_merged_polygons_valid () const
} }
m_merged_polygons_valid = true; m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = min_coherence ();
m_merged_polygons_boc_hash = deep_layer ().breakout_cells_hash (); 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<db::DeepLayer &> (deep_layer ()); db::DeepLayer &polygons = const_cast<db::DeepLayer &> (deep_layer ());
m_merged_polygons_valid = true; m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = m_is_merged_min_coherence;
m_is_merged = false; m_is_merged = false;
m_is_merged_min_coherence = min_coherence ();
m_merged_polygons_boc_hash = deep_layer ().breakout_cells_hash (); m_merged_polygons_boc_hash = deep_layer ().breakout_cells_hash ();
db::Layout &layout = polygons.layout (); db::Layout &layout = polygons.layout ();
@ -845,6 +857,7 @@ void
DeepRegion::set_is_merged (bool f) DeepRegion::set_is_merged (bool f)
{ {
m_is_merged = f; m_is_merged = f;
m_is_merged_min_coherence = min_coherence ();
m_merged_polygons_valid = false; m_merged_polygons_valid = false;
m_merged_polygons_boc_hash = 0; m_merged_polygons_boc_hash = 0;
m_merged_polygons = db::DeepLayer (); m_merged_polygons = db::DeepLayer ();
@ -2026,6 +2039,7 @@ DeepRegion::merged () const
res->deep_layer ().layer (); res->deep_layer ().layer ();
res->set_min_coherence (min_coherence ());
res->set_is_merged (true); res->set_is_merged (true);
return res.release (); return res.release ();
} }
@ -2370,48 +2384,75 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
{ {
if (empty ()) { if (empty ()) {
return new db::DeepEdgePairs (deep_layer ().derived ()); 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 ()); return new db::DeepEdgePairs (deep_layer ().derived ());
} }
// force different polygons in the different properties case to skip intra-polygon checks // delegate to AsIfFlatRegion if the other region is not a deep one
if (pc_always_different (options.prop_constraint)) {
// TODO: this forces merged primaries, so maybe that is not a good optimization?
different_polygons = true;
}
const db::DeepRegion *other_deep = 0; const db::DeepRegion *other_deep = 0;
unsigned int other_layer = 0; if (other) {
bool other_is_merged = true;
bool needs_merged_primary = different_polygons || options.needs_merged ();
bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged ();
if (other == subject_regionptr ()) {
other_layer = subject_idlayer ();
other_is_merged = primary_is_merged;
} else if (other == foreign_regionptr ()) {
other_layer = foreign_idlayer ();
other_is_merged = primary_is_merged;
} else {
other_deep = dynamic_cast<const db::DeepRegion *> (other->delegate ()); other_deep = dynamic_cast<const db::DeepRegion *> (other->delegate ());
if (! other_deep) { if (! other_deep) {
return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, options); 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<unsigned int> 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 ()) { if (! other->merged_semantics ()) {
other_layer = other_deep->deep_layer ().layer (); other_layers.push_back (other_deep->deep_layer ().layer ());
other_is_merged = true; other_is_merged = true;
} else if (options.whole_edges) { } else if (options.whole_edges) {
// NOTE: whole edges needs both inputs merged // 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; other_is_merged = true;
} else { } else {
other_layer = other_deep->deep_layer ().layer (); other_layers.push_back (other_deep->deep_layer ().layer ());
other_is_merged = other->is_merged (); 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); EdgeRelationFilter check (rel, d, options);
@ -2435,7 +2476,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
configure_proc (proc); configure_proc (proc);
proc.set_threads (polygons.store ()->threads ()); 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 { } else {
@ -2448,7 +2489,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
configure_proc (proc); configure_proc (proc);
proc.set_threads (polygons.store ()->threads ()); 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 ());
} }

View File

@ -179,8 +179,10 @@ private:
mutable DeepLayer m_merged_polygons; mutable DeepLayer m_merged_polygons;
mutable bool m_merged_polygons_valid; mutable bool m_merged_polygons_valid;
mutable bool m_merged_polygons_min_coherence;
mutable size_t m_merged_polygons_boc_hash; mutable size_t m_merged_polygons_boc_hash;
mutable bool m_is_merged; mutable bool m_is_merged;
mutable bool m_is_merged_min_coherence;
void init (); void init ();
void ensure_merged_polygons_valid () const; void ensure_merged_polygons_valid () const;

View File

@ -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) m_area_ratio (other.m_area_ratio), m_max_vertex_count (other.m_max_vertex_count)
{ {
init (); init ();
set_min_coherence (other.min_coherence ());
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;
m_merged_polygons_valid = other.m_merged_polygons_valid; 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)), : 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) m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{ {
init (); init ();
set_min_coherence (min_coh);
m_is_merged = is_merged; 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)), : 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) m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{ {
init (); init ();
set_min_coherence (min_coh);
m_is_merged = is_merged; m_is_merged = is_merged;
m_is_merged_min_coherence = min_coh;
transform_generic (trans); transform_generic (trans);
set_merged_semantics (merged_semantics); 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)), : 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) m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{ {
init (); init ();
set_min_coherence (min_coh);
m_is_merged = is_merged; m_is_merged = is_merged;
m_is_merged_min_coherence = min_coh;
} }
FlatRegion::~FlatRegion () FlatRegion::~FlatRegion ()
@ -83,6 +92,7 @@ FlatRegion::~FlatRegion ()
void FlatRegion::set_is_merged (bool m) void FlatRegion::set_is_merged (bool m)
{ {
m_is_merged = m; m_is_merged = m;
m_is_merged_min_coherence = min_coherence ();
} }
void FlatRegion::invalidate_cache () void FlatRegion::invalidate_cache ()
@ -95,7 +105,9 @@ void FlatRegion::invalidate_cache ()
void FlatRegion::init () void FlatRegion::init ()
{ {
m_is_merged = false; m_is_merged = false;
m_is_merged_min_coherence = false;
m_merged_polygons_valid = false; m_merged_polygons_valid = false;
m_merged_polygons_min_coherence = false;
} }
void FlatRegion::merged_semantics_changed () void FlatRegion::merged_semantics_changed ()
@ -112,9 +124,7 @@ void FlatRegion::join_properties_on_merge_changed ()
void FlatRegion::min_coherence_changed () void FlatRegion::min_coherence_changed ()
{ {
m_is_merged = false; // merged status is tracked in separate variables and is validated on inquiry
mp_merged_polygons->clear ();
m_merged_polygons_valid = false;
} }
void FlatRegion::reserve (size_t n) void FlatRegion::reserve (size_t n)
@ -122,12 +132,25 @@ void FlatRegion::reserve (size_t n)
mp_polygons->reserve (db::Polygon::tag (), 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 void
FlatRegion::ensure_merged_polygons_valid () const 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 ()); merge_polygons_to (*mp_merged_polygons, min_coherence (), 0, join_properties_on_merge ());
m_merged_polygons_valid = true; 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)); mp_merged_polygons.reset (new db::Shapes (*mp_polygons));
m_merged_polygons_valid = true; m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = m_is_merged_min_coherence;
m_is_merged = false; m_is_merged = false;
m_is_merged_min_coherence = min_coherence ();
break_polygons (*mp_polygons, m_max_vertex_count, m_area_ratio); break_polygons (*mp_polygons, m_max_vertex_count, m_area_ratio);
} }
@ -152,7 +177,7 @@ RegionIteratorDelegate *FlatRegion::begin () const
RegionIteratorDelegate *FlatRegion::begin_merged () const RegionIteratorDelegate *FlatRegion::begin_merged () const
{ {
if (! merged_semantics () || m_is_merged) { if (! merged_semantics () || is_merged ()) {
return begin (); return begin ();
} else { } else {
ensure_merged_polygons_valid (); ensure_merged_polygons_valid ();
@ -173,7 +198,7 @@ std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_iter ()
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_merged_iter () const std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_merged_iter () const
{ {
if (! merged_semantics () || m_is_merged) { if (! merged_semantics () || is_merged ()) {
return begin_iter (); return begin_iter ();
} else { } else {
ensure_merged_polygons_valid (); ensure_merged_polygons_valid ();
@ -204,7 +229,7 @@ size_t FlatRegion::hier_count () const
bool FlatRegion::is_merged () 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 Box FlatRegion::compute_bbox () const
@ -246,6 +271,7 @@ RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter)
mp_merged_polygons->clear (); mp_merged_polygons->clear ();
invalidate_cache (); invalidate_cache ();
m_is_merged = filter.requires_raw_input () ? false : merged_semantics (); m_is_merged = filter.requires_raw_input () ? false : merged_semantics ();
m_is_merged_min_coherence = min_coherence ();
return this; return this;
} }
@ -276,6 +302,7 @@ RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter
mp_merged_polygons->clear (); mp_merged_polygons->clear ();
invalidate_cache (); invalidate_cache ();
m_is_merged = filter.result_is_merged () && merged_semantics (); m_is_merged = filter.result_is_merged () && merged_semantics ();
m_is_merged_min_coherence = min_coherence ();
if (filter.result_must_not_be_merged ()) { if (filter.result_must_not_be_merged ()) {
set_merged_semantics (false); set_merged_semantics (false);
@ -286,14 +313,15 @@ RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter
RegionDelegate *FlatRegion::merged_in_place () 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; db::Shapes &merged_polygons = *mp_merged_polygons;
mp_polygons->swap (merged_polygons); mp_polygons->swap (merged_polygons);
merged_polygons.clear (); merged_polygons.clear ();
m_is_merged = true; m_is_merged = true;
m_is_merged_min_coherence = min_coherence ();
return this; return this;
} else { } 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 ()) { if (empty ()) {
@ -322,9 +350,11 @@ RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int mi
} else { } else {
invalidate_cache (); 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 = 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 RegionDelegate *FlatRegion::merged () const
{ {
if (! m_is_merged) { if (! is_merged ()) {
if (m_merged_polygons_valid) { if (! merged_polygons_valid ()) {
return new FlatRegion (*mp_merged_polygons, true); ensure_merged_polygons_valid ();
} else {
return AsIfFlatRegion::merged (min_coherence (), 0, join_properties_on_merge ());
} }
std::unique_ptr<FlatRegion> 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 { } else {
return clone (); return clone ();
} }
} }
RegionDelegate *FlatRegion::merged (bool min_coh, unsigned int min_wc, bool jp) const
{
std::unique_ptr<FlatRegion> 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 RegionDelegate *FlatRegion::add (const Region &other) const
{ {
std::unique_ptr<FlatRegion> new_region (new FlatRegion (*this)); std::unique_ptr<FlatRegion> new_region (new FlatRegion (*this));

View File

@ -52,9 +52,9 @@ public:
typedef polygon_layer_wp_type::iterator polygon_iterator_wp_type; typedef polygon_layer_wp_type::iterator polygon_iterator_wp_type;
FlatRegion (double area_ratio = 0.0, size_t max_vertex_count = 0); 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, 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); 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); FlatRegion (bool is_merged, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false);
FlatRegion (const FlatRegion &other); FlatRegion (const FlatRegion &other);
@ -85,10 +85,9 @@ public:
virtual RegionDelegate *merged_in_place (); 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_in_place (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge);
virtual RegionDelegate *merged () const; virtual RegionDelegate *merged () const;
virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) 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); bool merged_polygons_available () const;
}
virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter); virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter);
virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter); virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter);
@ -138,6 +137,7 @@ protected:
virtual Box compute_bbox () const; virtual Box compute_bbox () const;
void invalidate_cache (); void invalidate_cache ();
void set_is_merged (bool m); void set_is_merged (bool m);
bool merged_polygons_valid () const;
private: private:
friend class AsIfFlatRegion; friend class AsIfFlatRegion;
@ -146,9 +146,11 @@ private:
FlatRegion &operator= (const FlatRegion &other); FlatRegion &operator= (const FlatRegion &other);
mutable bool m_is_merged; mutable bool m_is_merged;
mutable bool m_is_merged_min_coherence;
mutable tl::copy_on_write_ptr<db::Shapes> mp_polygons; mutable tl::copy_on_write_ptr<db::Shapes> mp_polygons;
mutable tl::copy_on_write_ptr<db::Shapes> mp_merged_polygons; mutable tl::copy_on_write_ptr<db::Shapes> mp_merged_polygons;
mutable bool m_merged_polygons_valid; mutable bool m_merged_polygons_valid;
mutable bool m_merged_polygons_min_coherence;
double m_area_ratio; double m_area_ratio;
size_t m_max_vertex_count; size_t m_max_vertex_count;
@ -156,6 +158,16 @@ private:
void ensure_merged_polygons_valid () const; void ensure_merged_polygons_valid () const;
void ensure_unmerged_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 <class Trans> template <class Trans>
void transform_generic (const Trans &trans) void transform_generic (const Trans &trans)
{ {

View File

@ -622,21 +622,28 @@ public:
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type; typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type; typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, interactions_type *result) interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, const std::map<unsigned int, db::Coord> &override_distance, interactions_type *result)
: mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result), m_rt (subject_layout) : 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 .. // nothing yet ..
} }
void add (const db::CellInstArray *inst, unsigned int, const TI *ref, unsigned int layer) 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: private:
db::Layout *mp_subject_layout; db::Layout *mp_subject_layout;
unsigned int m_subject_layer; unsigned int m_subject_layer;
db::Coord m_dist; db::Coord m_dist;
std::map<unsigned int, db::Coord> m_override_distance;
interactions_type *mp_result; interactions_type *mp_result;
db::shape_reference_translator_with_trans<TI, db::ICplxTrans> m_rt; db::shape_reference_translator_with_trans<TI, db::ICplxTrans> m_rt;
@ -681,11 +688,11 @@ private:
// LocalProcessorContextComputationTask implementation // LocalProcessorContextComputationTask implementation
template <class TS, class TI, class TR> template <class TS, class TI, class TR>
local_processor_context_computation_task<TS, TI, TR>::local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *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<TS, TI, TR>::context_key_type &intruders, db::Coord dist) local_processor_context_computation_task<TS, TI, TR>::local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *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<TS, TI, TR>::context_key_type &intruders, db::Coord dist, const std::map<unsigned int, db::Coord> &override_distance)
: tl::Task (), : tl::Task (),
mp_proc (proc), mp_contexts (&contexts), mp_parent_context (parent_context), 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_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 // This is quick, but will take away the intruders from the caller
m_intruders.swap (intruders); m_intruders.swap (intruders);
@ -695,7 +702,7 @@ template <class TS, class TI, class TR>
void void
local_processor_context_computation_task<TS, TI, TR>::perform () local_processor_context_computation_task<TS, TI, TR>::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<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
contexts.set_intruder_layers (intruder_layers); contexts.set_intruder_layers (intruder_layers);
contexts.set_subject_layer (subject_layer); contexts.set_subject_layer (subject_layer);
// collect override distances per layer ID
auto od = op->override_distance ();
std::map<unsigned int, db::Coord> 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<TS, TI, TR>::context_key_type intruders; typename local_processor_cell_contexts<TS, TI, TR>::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 ()) { if (mp_cc_job.get ()) {
mp_cc_job->start (); mp_cc_job->start ();
@ -922,14 +939,15 @@ void local_processor<TS, TI, TR>::issue_compute_contexts (local_processor_contex
const db::ICplxTrans &subject_cell_inst, const db::ICplxTrans &subject_cell_inst,
const db::Cell *intruder_cell, const db::Cell *intruder_cell,
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders,
db::Coord dist) const db::Coord dist,
const std::map<unsigned int, db::Coord> &override_distance) const
{ {
bool is_small_job = subject_cell->begin ().at_end (); bool is_small_job = subject_cell->begin ().at_end ();
if (! is_small_job && mp_cc_job.get ()) { if (! is_small_job && mp_cc_job.get ()) {
mp_cc_job->schedule (new local_processor_context_computation_task<TS, TI, TR> (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<TS, TI, TR> (this, contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist, override_distance));
} else { } 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<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
const db::ICplxTrans &subject_cell_inst, const db::ICplxTrans &subject_cell_inst,
const db::Cell *intruder_cell, const db::Cell *intruder_cell,
const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders,
db::Coord dist_top) const db::Coord dist_top,
const std::map<unsigned int, db::Coord> &override_distance) const
{ {
CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts) CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts)
@ -1041,10 +1060,16 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
// TODO: can we shortcut this if interactions is empty? // TODO: can we shortcut this if interactions is empty?
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) { for (std::vector<unsigned int>::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 <db::CellInstArray, true> inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il)); db::box_convert <db::CellInstArray, true> inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il));
db::box_scanner2<db::CellInstArray, int, db::CellInstArray, int> scanner; db::box_scanner2<db::CellInstArray, int, db::CellInstArray, int> scanner;
interaction_registration_inst2inst<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions); interaction_registration_inst2inst<TS, TI, TR> 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; unsigned int id = 0;
@ -1095,7 +1120,7 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
db::box_scanner2<db::CellInstArray, int, TI, int> scanner; db::box_scanner2<db::CellInstArray, int, TI, int> scanner;
db::addressable_object_from_shape<TI> heap; db::addressable_object_from_shape<TI> heap;
interaction_registration_inst2shape<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions); interaction_registration_inst2shape<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), dist, override_distance, &interactions);
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) { 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 ())) { if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
@ -1127,7 +1152,7 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
db::Cell *subject_child_cell = &mp_subject_layout->cell (i->first.first); db::Cell *subject_child_cell = &mp_subject_layout->cell (i->first.first);
db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? subject_child_cell : 0); 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 <class TS, class TI, class TR>
void void
local_processor<TS, TI, TR>::compute_local_cell (const db::local_processor_contexts<TS, TI, TR> &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation<TS, TI, TR> *op, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, std::vector<std::unordered_set<TR> > &result) const local_processor<TS, TI, TR>::compute_local_cell (const db::local_processor_contexts<TS, TI, TR> &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation<TS, TI, TR> *op, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, std::vector<std::unordered_set<TR> > &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 ()); const db::Shapes *subject_shapes = &subject_cell->shapes (contexts.subject_layer ());
db::shape_to_object<TS> s2o; db::shape_to_object<TS> s2o;
@ -1412,6 +1438,13 @@ local_processor<TS, TI, TR>::compute_local_cell (const db::local_processor_conte
unsigned int il_index = 0; unsigned int il_index = 0;
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il, ++il_index) { for (std::vector<unsigned int>::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); unsigned int ail = contexts.actual_intruder_layer (*il);
bool foreign = contexts.is_foreign (*il); bool foreign = contexts.is_foreign (*il);
@ -1633,15 +1666,16 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
// build the subjects in the intruders list // 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; db::Box intruders_box;
for (typename std::vector<generic_shape_iterator<TI> >::const_iterator il = intruders.begin (); il != intruders.end (); ++il) { for (typename std::vector<generic_shape_iterator<TI> >::const_iterator il = intruders.begin (); il != intruders.end (); ++il) {
intruders_box += il->bbox (); 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; db::Box common_box = intruders_box & subjects_box;
if (common_box.empty () || common_box.width () == 0 || common_box.height () == 0) { if (common_box.empty () || common_box.width () == 0 || common_box.height () == 0) {
@ -1670,6 +1704,12 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
bool ff = foreign.size () > il_index && foreign [il_index]; 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) { if (*il == subjects && ! ff) {
interaction_registration_shape1_scanner_combo<TS, TI> scanner (&interactions, il_index, report_progress (), scan_description); interaction_registration_shape1_scanner_combo<TS, TI> scanner (&interactions, il_index, report_progress (), scan_description);
@ -1736,6 +1776,12 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
bool ff = foreign.size () > il_index && foreign [il_index]; 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) { if (*il == subjects && ! ff) {
interaction_registration_shape1_scanner_combo<TS, TI> scanner (&interactions, il_index, report_progress (), scan_description); interaction_registration_shape1_scanner_combo<TS, TI> scanner (&interactions, il_index, report_progress (), scan_description);
@ -1789,6 +1835,7 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
} }
} }
} }
} }

View File

@ -359,7 +359,7 @@ class DB_PUBLIC local_processor_context_computation_task
: public tl::Task : public tl::Task
{ {
public: public:
local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *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<TS, TI, TR>::context_key_type &intruders, db::Coord dist); local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *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<TS, TI, TR>::context_key_type &intruders, db::Coord dist, const std::map<unsigned int, db::Coord> &override_distance);
void perform (); void perform ();
private: private:
@ -372,6 +372,7 @@ private:
const db::Cell *mp_intruder_cell; const db::Cell *mp_intruder_cell;
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type m_intruders; typename local_processor_cell_contexts<TS, TI, TR>::context_key_type m_intruders;
db::Coord m_dist; db::Coord m_dist;
std::map<unsigned int, db::Coord> m_override_distance;
}; };
template <class TS, class TI, class TR> template <class TS, class TI, class TR>
@ -578,8 +579,8 @@ private:
void next () const; void next () const;
size_t get_progress () const; size_t get_progress () const;
void compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *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<TS, TI, TR>::context_key_type &intruders, db::Coord dist) const; void compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *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<TS, TI, TR>::context_key_type &intruders, db::Coord dist, const std::map<unsigned int, Coord> &override_distance) const;
void issue_compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *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<TS, TI, TR>::context_key_type &intruders, db::Coord dist) const; void issue_compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *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<TS, TI, TR>::context_key_type &intruders, db::Coord dist, const std::map<unsigned int, Coord> &override_distance) const;
void compute_local_cell (const db::local_processor_contexts<TS, TI, TR> &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation<TS, TI, TR> *op, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, std::vector<std::unordered_set<TR> > &result) const; void compute_local_cell (const db::local_processor_contexts<TS, TI, TR> &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation<TS, TI, TR> *op, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, std::vector<std::unordered_set<TR> > &result) const;
bool subject_cell_is_breakout (db::cell_index_type ci) const bool subject_cell_is_breakout (db::cell_index_type ci) const

View File

@ -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) LayerProperties::read (tl::Extractor &ex, bool as_target)
{ {
layer = db::any_ld (); layer = db::any_ld ();
@ -207,6 +207,8 @@ LayerProperties::read (tl::Extractor &ex, bool as_target)
layer = l; layer = l;
datatype = d; datatype = d;
return true;
} else if (ex.try_read_word_or_quoted (name)) { } else if (ex.try_read_word_or_quoted (name)) {
if (ex.test ("(")) { if (ex.test ("(")) {
@ -223,6 +225,10 @@ LayerProperties::read (tl::Extractor &ex, bool as_target)
} }
return true;
} else {
return false;
} }
} }

View File

@ -91,8 +91,10 @@ struct DB_PUBLIC LayerProperties
* relative layer/datatype specifications in the format "*+1" or "*-100". * 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 layer or datatype is for "don't care" (on input) or "leave as is"
* (for output). * (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 * @brief "Logical" equality

View File

@ -118,6 +118,14 @@ public:
*/ */
virtual db::Coord dist () const { return 0; } 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<unsigned int, db::Coord> override_distance () const { return std::map<unsigned int, db::Coord> (); }
/** /**
* @brief Gets the cell variant reducer that indicates whether to build cell variants and which * @brief Gets the cell variant reducer that indicates whether to build cell variants and which
*/ */

View File

@ -376,10 +376,10 @@ private:
*/ */
template <class TS, class TI> template <class TS, class TI>
DB_PUBLIC_TEMPLATE DB_PUBLIC_TEMPLATE
std::map<db::properties_id_type, std::pair<std::vector<const TS *>, std::set<const TI *> > > std::map<db::properties_id_type, std::pair<std::vector<const TS *>, std::set<std::pair<unsigned int, const TI *> > > >
separate_interactions_by_properties (const shape_interactions<db::object_with_properties<TS>, db::object_with_properties<TI> > &interactions, db::PropertyConstraint property_constraint) separate_interactions_by_properties (const shape_interactions<db::object_with_properties<TS>, db::object_with_properties<TI> > &interactions, db::PropertyConstraint property_constraint)
{ {
std::map<db::properties_id_type, std::pair<std::vector<const TS *>, std::set<const TI *> > > by_prop_id; std::map<db::properties_id_type, std::pair<std::vector<const TS *>, std::set<std::pair<unsigned int, const TI *> > > > by_prop_id;
for (auto i = interactions.begin (); i != interactions.end (); ++i) { for (auto i = interactions.begin (); i != interactions.end (); ++i) {
@ -387,7 +387,7 @@ separate_interactions_by_properties (const shape_interactions<db::object_with_pr
db::properties_id_type prop_id = subject.properties_id (); db::properties_id_type prop_id = subject.properties_id ();
std::pair<std::vector<const TS *>, std::set<const TI *> > &s2p = by_prop_id [prop_id]; auto &s2p = by_prop_id [prop_id];
s2p.first.push_back (&subject); s2p.first.push_back (&subject);
for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) {
@ -395,7 +395,7 @@ separate_interactions_by_properties (const shape_interactions<db::object_with_pr
const std::pair<unsigned int, db::object_with_properties<TI> > &intruder = interactions.intruder_shape (*ii); const std::pair<unsigned int, db::object_with_properties<TI> > &intruder = interactions.intruder_shape (*ii);
if (pc_match (property_constraint, prop_id, intruder.second.properties_id ())) { 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));
} }
} }

View File

@ -142,28 +142,34 @@ OriginalLayerRegion::OriginalLayerRegion ()
OriginalLayerRegion::OriginalLayerRegion (const OriginalLayerRegion &other) OriginalLayerRegion::OriginalLayerRegion (const OriginalLayerRegion &other)
: AsIfFlatRegion (other), : AsIfFlatRegion (other),
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),
m_merged_polygons (other.m_merged_polygons), m_merged_polygons (other.m_merged_polygons),
m_merged_polygons_valid (other.m_merged_polygons_valid), 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 (other.m_iter),
m_iter_trans (other.m_iter_trans) m_iter_trans (other.m_iter_trans)
{ {
// .. nothing yet .. // .. 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) : AsIfFlatRegion (), m_merged_polygons (false), m_iter (si)
{ {
init (); init ();
set_min_coherence (min_coh);
m_is_merged = is_merged; 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) : AsIfFlatRegion (), m_merged_polygons (false), m_iter (si), m_iter_trans (trans)
{ {
init (); init ();
set_min_coherence (min_coh);
m_is_merged = is_merged; m_is_merged = is_merged;
m_is_merged_min_coherence = min_coh;
set_merged_semantics (merged_semantics); set_merged_semantics (merged_semantics);
} }
@ -183,6 +189,7 @@ OriginalLayerRegion::merged_semantics_changed ()
{ {
m_merged_polygons.clear (); m_merged_polygons.clear ();
m_merged_polygons_valid = false; m_merged_polygons_valid = false;
m_merged_polygons_min_coherence = false;
} }
void void
@ -190,14 +197,13 @@ OriginalLayerRegion::join_properties_on_merge_changed ()
{ {
m_merged_polygons.clear (); m_merged_polygons.clear ();
m_merged_polygons_valid = false; m_merged_polygons_valid = false;
m_merged_polygons_min_coherence = false;
} }
void void
OriginalLayerRegion::min_coherence_changed () OriginalLayerRegion::min_coherence_changed ()
{ {
m_is_merged = false; // merged status is tracked in separate variables and is validated on inquiry
m_merged_polygons.clear ();
m_merged_polygons_valid = false;
} }
size_t size_t
@ -309,7 +315,7 @@ OriginalLayerRegion::begin () const
RegionIteratorDelegate * RegionIteratorDelegate *
OriginalLayerRegion::begin_merged () const OriginalLayerRegion::begin_merged () const
{ {
if (! merged_semantics () || m_is_merged) { if (! merged_semantics () || is_merged ()) {
return begin (); return begin ();
} else { } else {
ensure_merged_polygons_valid (); ensure_merged_polygons_valid ();
@ -332,7 +338,7 @@ OriginalLayerRegion::begin_iter () const
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> std::pair<db::RecursiveShapeIterator, db::ICplxTrans>
OriginalLayerRegion::begin_merged_iter () const OriginalLayerRegion::begin_merged_iter () const
{ {
if (! merged_semantics () || m_is_merged) { if (! merged_semantics () || is_merged ()) {
return begin_iter (); return begin_iter ();
} else { } else {
ensure_merged_polygons_valid (); ensure_merged_polygons_valid ();
@ -346,6 +352,33 @@ OriginalLayerRegion::begin_unmerged_iter () const
return std::make_pair (m_iter, m_iter_trans); 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<FlatRegion> 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<FlatRegion> 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 bool
OriginalLayerRegion::empty () const OriginalLayerRegion::empty () const
{ {
@ -355,7 +388,13 @@ OriginalLayerRegion::empty () const
bool bool
OriginalLayerRegion::is_merged () const 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 * const db::Polygon *
@ -379,7 +418,7 @@ OriginalLayerRegion::has_valid_polygons () const
bool bool
OriginalLayerRegion::has_valid_merged_polygons () const OriginalLayerRegion::has_valid_merged_polygons () const
{ {
return merged_semantics () && ! m_is_merged; return merged_semantics () && ! is_merged ();
} }
const db::RecursiveShapeIterator * const db::RecursiveShapeIterator *
@ -394,6 +433,7 @@ OriginalLayerRegion::apply_property_translator (const db::PropertiesTranslator &
m_iter.apply_property_translator (pt); m_iter.apply_property_translator (pt);
m_merged_polygons_valid = false; m_merged_polygons_valid = false;
m_merged_polygons_min_coherence = false;
m_merged_polygons.clear (); m_merged_polygons.clear ();
} }
@ -423,7 +463,9 @@ void
OriginalLayerRegion::init () OriginalLayerRegion::init ()
{ {
m_is_merged = false; m_is_merged = false;
m_is_merged_min_coherence = false;
m_merged_polygons_valid = false; m_merged_polygons_valid = false;
m_merged_polygons_min_coherence = false;
} }
namespace { 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 void
OriginalLayerRegion::ensure_merged_polygons_valid () const OriginalLayerRegion::ensure_merged_polygons_valid () const
{ {
if (! m_merged_polygons_valid) { if (! merged_polygons_valid ()) {
m_merged_polygons.clear (); m_merged_polygons.clear ();
merge_polygons_to (m_merged_polygons, min_coherence (), 0, join_properties_on_merge ()); merge_polygons_to (m_merged_polygons, min_coherence (), 0, join_properties_on_merge ());
m_merged_polygons_valid = true; m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = min_coherence ();
} }
} }

View File

@ -43,8 +43,8 @@ class DB_PUBLIC OriginalLayerRegion
public: public:
OriginalLayerRegion (); OriginalLayerRegion ();
OriginalLayerRegion (const OriginalLayerRegion &other); OriginalLayerRegion (const OriginalLayerRegion &other);
OriginalLayerRegion (const RecursiveShapeIterator &si, 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); OriginalLayerRegion (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false, bool min_coh = false);
virtual ~OriginalLayerRegion (); virtual ~OriginalLayerRegion ();
RegionDelegate *clone () const; RegionDelegate *clone () const;
@ -57,6 +57,11 @@ public:
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_merged_iter () const; virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_merged_iter () const;
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_unmerged_iter () const; virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> 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 empty () const;
virtual bool is_merged () const; virtual bool is_merged () const;
@ -85,13 +90,16 @@ private:
OriginalLayerRegion &operator= (const OriginalLayerRegion &other); OriginalLayerRegion &operator= (const OriginalLayerRegion &other);
bool m_is_merged; bool m_is_merged;
mutable bool m_is_merged_min_coherence;
mutable db::Shapes m_merged_polygons; mutable db::Shapes m_merged_polygons;
mutable bool m_merged_polygons_valid; mutable bool m_merged_polygons_valid;
mutable bool m_merged_polygons_min_coherence;
mutable db::RecursiveShapeIterator m_iter; mutable db::RecursiveShapeIterator m_iter;
db::ICplxTrans m_iter_trans; db::ICplxTrans m_iter_trans;
void init (); void init ();
void ensure_merged_polygons_valid () const; void ensure_merged_polygons_valid () const;
bool merged_polygons_valid () const;
}; };
} }

View File

@ -211,7 +211,7 @@ check_local_operation_base<TS, TI>::check_local_operation_base (const EdgeRelati
template <class TS, class TI> template <class TS, class TI>
void void
check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector<const TS *> &subjects, const std::set<const TI *> &intruders, std::unordered_set<db::EdgePair> &result, std::unordered_set<db::EdgePair> &intra_polygon_result, const db::LocalProcessorBase *proc) const check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector<const TS *> &subjects, const std::set<const TI *> &primary_intruders, const std::set<const TI *> &intruders, std::unordered_set<db::EdgePair> &result, std::unordered_set<db::EdgePair> &intra_polygon_result, const db::LocalProcessorBase *proc) const
{ {
// NOTE: the rectangle and opposite filters are unsymmetric // 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; 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<TS, TI>::compute_results (db::Layout *layout, db::Cel
db::EdgeProcessor ep; db::EdgeProcessor ep;
ep.set_base_verbosity (50); 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; db::Box common_box;
bool subjects_are_fully_covered = false; bool subjects_are_fully_covered = false;
@ -259,6 +259,14 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
all_intruders_box += intruder_box.enlarged (e); 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<TI> () (**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; common_box &= all_intruders_box;
} }
@ -269,7 +277,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
size_t n = 0; 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) { for (auto i = subjects.begin (); i != subjects.end (); ++i) {
if (! take_all) { if (! take_all) {
@ -280,9 +288,10 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
n += 2; n += 2;
} }
} else { } else if (primary_intruders.empty ()) {
// merge needed for the subject shapes // merge needed for the subject shapes
// Here we don't have other subjects to consider. This is the simple case.
ep.clear (); ep.clear ();
size_t nn = 0; size_t nn = 0;
@ -310,6 +319,79 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
n += 2; 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<typename TS::edge_type> edges;
db::EdgeContainer ee (edges);
db::SimpleMerge op (1 /*wc>0*/);
ep.process (ee, op);
ep.clear ();
std::vector<typename TS::edge_type> 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<typename TI::edge_type> partial_edges;
EdgeBooleanClusterCollector<std::set<typename TI::edge_type> > cluster_collector (&partial_edges, db::EdgeAnd);
db::box_scanner<typename TI::edge_type, size_t> 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<typename TI::edge_type> ());
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 // merge the intruders to remove inner edges
@ -384,11 +466,11 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
} else { } 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 // 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; size_t n = 0;
std::unordered_set<TI> subjects_hash; std::unordered_set<TI> subjects_hash;
@ -406,7 +488,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
n = 1; 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 (subjects_hash.find (**i) == subjects_hash.end ()) {
if (! take_all) { if (! take_all) {
poly_check.enter (**i, n, common_box); poly_check.enter (**i, n, common_box);
@ -416,9 +498,9 @@ check_local_operation_base<TS, TI>::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; size_t n = 0;
@ -451,7 +533,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
} else { } 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. // separate edges into those from the subject and those from intruder shapes.
ep.clear (); ep.clear ();
@ -464,7 +546,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
++nn; ++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) { for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) {
ep.insert (*e, nn); ep.insert (*e, nn);
} }
@ -727,12 +809,40 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, db::Cell *s
subjects.reserve (interactions.size ()); subjects.reserve (interactions.size ());
std::set<const TI *> intruders; std::set<const TI *> intruders;
std::set<const TI *> primary_intruders;
unsigned int primary_intruder_layer = check_local_operation_base<TS, TI>::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<TS, TI>::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) { for (auto i = interactions.begin (); i != interactions.end (); ++i) {
subjects.push_back (&interactions.subject_shape (i->first)); subjects.push_back (&interactions.subject_shape (i->first));
for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) { 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); tl_assert (results.size () == 1);
@ -740,7 +850,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, db::Cell *s
std::unordered_set<db::EdgePair> result, intra_polygon_result; std::unordered_set<db::EdgePair> result, intra_polygon_result;
// perform the basic check // perform the basic check
check_local_operation_base<TS, TI>::compute_results (layout, subject_cell, subjects, intruders, result, intra_polygon_result, proc); check_local_operation_base<TS, TI>::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" // 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) // ("opposite" is defined by the projection of edges "through" the subject shape)
@ -773,6 +883,19 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, db::Cell *s
} }
} }
template <class TS, class TI>
std::map<unsigned int, db::Coord>
check_local_operation<TS, TI>::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<unsigned int, db::Coord> od;
if (check_local_operation_base<TS, TI>::m_has_other) {
od.insert (std::make_pair (1, 0));
}
return od;
}
template <class TS, class TI> template <class TS, class TI>
db::Coord db::Coord
check_local_operation<TS, TI>::dist () const check_local_operation<TS, TI>::dist () const
@ -816,6 +939,8 @@ check_local_operation_with_properties<TS, TI>::do_compute_local (db::Layout *lay
{ {
tl_assert (results.size () == 1); tl_assert (results.size () == 1);
unsigned int primary_intruder_layer = check_local_operation_base<TS, TI>::m_has_other ? 1 : 0;
auto by_prop_id = separate_interactions_by_properties (interactions, check_local_operation_base<TS, TI>::m_options.prop_constraint); auto by_prop_id = separate_interactions_by_properties (interactions, check_local_operation_base<TS, TI>::m_options.prop_constraint);
for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) { for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) {
@ -823,10 +948,18 @@ check_local_operation_with_properties<TS, TI>::do_compute_local (db::Layout *lay
std::unordered_set<db::EdgePair> result, intra_polygon_result; std::unordered_set<db::EdgePair> result, intra_polygon_result;
const std::vector<const TS *> &subjects = s2p->second.first; const std::vector<const TS *> &subjects = s2p->second.first;
const std::set<const TI *> &intruders = s2p->second.second;
std::set<const TI *> 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 // perform the basic check
check_local_operation_base<TS, TI>::compute_results (layout, subject_cell, subjects, intruders, result, intra_polygon_result, proc); check_local_operation_base<TS, TI>::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" // 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) // ("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; 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) { 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<db::Edge, std::unordered_set<db::EdgeWithProperties> > results_with_properties (&results.front (), prop_id); db::property_injector<db::Edge, std::unordered_set<db::EdgeWithProperties> > results_with_properties (&results.front (), prop_id);
@ -2306,18 +2442,19 @@ PolygonToEdgeLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell
ep.clear (); ep.clear ();
for (auto s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) { size_t p = 0;
ep.insert (s->second); 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) { for (auto s = shapes_by_prop_id->second.second.begin (); s != shapes_by_prop_id->second.second.end (); ++s) {
ep.insert (i->second.second); ep.insert (*(s->second), ++p);
} }
std::vector<Edge> edges2; std::vector<Edge> edges2;
db::EdgeContainer ec2 (edges2); db::EdgeContainer ec2 (edges2);
ep.process (ec2, op); 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<db::Edge, size_t> scanner; db::box_scanner<db::Edge, size_t> scanner;
scanner.reserve (edges1.size () + edges2.size ()); scanner.reserve (edges1.size () + edges2.size ());

View File

@ -194,7 +194,7 @@ protected:
bool m_other_is_merged; bool m_other_is_merged;
db::RegionCheckOptions m_options; db::RegionCheckOptions m_options;
void compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector<const TS *> &subjects, const std::set<const TI *> &intruders, std::unordered_set<db::EdgePair> &result, std::unordered_set<db::EdgePair> &intra_polygon_result, const LocalProcessorBase *proc) const; void compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector<const TS *> &subjects, const std::set<const TI *> &primary_intruders, const std::set<const TI *> &intruders, std::unordered_set<db::EdgePair> &result, std::unordered_set<db::EdgePair> &intra_polygon_result, const LocalProcessorBase *proc) const;
void apply_opposite_filter (const std::vector<const TS *> &subjects, std::unordered_set<db::EdgePair> &result, std::unordered_set<db::EdgePair> &intra_polygon_result) const; void apply_opposite_filter (const std::vector<const TS *> &subjects, std::unordered_set<db::EdgePair> &result, std::unordered_set<db::EdgePair> &intra_polygon_result) const;
void apply_rectangle_filter (const std::vector<const TS *> &subjects, std::unordered_set<db::EdgePair> &result) const; void apply_rectangle_filter (const std::vector<const TS *> &subjects, std::unordered_set<db::EdgePair> &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); 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 db::Coord dist () const;
virtual std::map<unsigned int, db::Coord> override_distance () const;
virtual OnEmptyIntruderHint on_empty_intruder_hint () const; virtual OnEmptyIntruderHint on_empty_intruder_hint () const;
virtual bool requests_single_subjects () const { return true; } virtual bool requests_single_subjects () const { return true; }
virtual std::string description () const; virtual std::string description () const;

View File

@ -236,7 +236,7 @@ Shapes::do_insert (const Shapes &d, unsigned int flags)
if (layout () == d.layout ()) { if (layout () == d.layout ()) {
// both shape containers reside in the same repository space - simply copy // 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 ()); m_layers.reserve (d.m_layers.size ());
for (tl::vector<LayerBase *>::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) { for (tl::vector<LayerBase *>::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) {

View File

@ -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) (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 & 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 & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts)
| ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta | ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta)
| db::layout_diff::f_smart_cell_mapping) | ((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*/ /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/
, tolerance, 100 /*max diff lines*/); , 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 & 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 & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts)
| ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta) | ((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*/ /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/
, tolerance, 100 /*max diff lines*/); , tolerance, 100 /*max diff lines*/);

View File

@ -52,15 +52,16 @@ class Texts;
*/ */
enum NormalizationMode enum NormalizationMode
{ {
NoNormalization = 0, // no normalization - take the test subject as it is NoNormalization = 0, // no normalization - take the test subject as it is
WriteGDS2 = 1, // normalize subject by writing to GDS2 and reading back WriteGDS2 = 1, // normalize subject by writing to GDS2 and reading back
WriteOAS = 2, // normalize subject by writing to OASIS and reading back WriteOAS = 2, // normalize subject by writing to OASIS and reading back
NormFileMask = 7, // bits the extract for file mode NormFileMask = 7, // bits the extract for file mode
NoContext = 8, // write tmp file without context NoContext = 8, // write tmp file without context
AsPolygons = 16, // paths and boxes are treated as polygons AsPolygons = 16, // paths and boxes are treated as polygons
WithArrays = 32, // do not flatten arrays WithArrays = 32, // do not flatten arrays
WithMeta = 64, // with meta info WithMeta = 64, // with meta info
WithoutCellNames = 128 // smart cell name mapping WithoutCellNames = 128, // smart cell name mapping
WithoutLayerNames = 256, // don't compare layer names
}; };
/** /**

View File

@ -892,14 +892,38 @@ static db::Layout *layout_default_ctor()
return new db::Layout (true); 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<db::Layout> 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 <db::cell_index_type> 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<tl::Variant> &parameters) static db::cell_index_type add_lib_pcell_variant (db::Layout *layout, db::Library *lib, db::pcell_id_type pcell_id, const std::vector<tl::Variant> &parameters)
@ -1286,7 +1310,7 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"always editable. Before that version, they inherited the editable flag from " "always editable. Before that version, they inherited the editable flag from "
"the application." "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" "@brief Creates a layout object attached to a manager\n"
"\n" "\n"
"This constructor specifies a manager object which is used to " "This constructor specifies a manager object which is used to "
@ -1294,16 +1318,21 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"the layout is editable. In editable mode, some optimizations are disabled " "the layout is editable. In editable mode, some optimizations are disabled "
"and the layout can be manipulated through a variety of methods.\n" "and the layout can be manipulated through a variety of methods.\n"
"\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" "This method was introduced in version 0.22.\n"
) + ) +
gsi::constructor ("new", &editable_layout_default_ctor, gsi::arg ("editable"), 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\n" "@brief Creates a layout object as a copy of another cell\n"
"\n" "\n"
"This constructor specifies whether " "This convenience constructor creates a new layout object as a hierarchical copy of the source cell including all "
"the layout is editable. In editable mode, some optimizations are disabled " "child cells and shapes.\n"
"and the layout can be manipulated through a variety of methods.\n"
"\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, 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" "@brief Gets the library this layout lives in or nil if the layout is not part of a library\n"

View File

@ -2033,6 +2033,243 @@ TEST(44_SizeWithProperties)
db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au44.gds"); 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<const db::FlatRegion *> (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<const db::FlatRegion *> (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<const db::FlatRegion *> (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<const db::FlatRegion *> (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<const db::FlatRegion *> (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<const db::FlatRegion *> (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<const db::FlatRegion *> (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<const db::FlatRegion *> (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<const db::OriginalLayerRegion *> (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<const db::OriginalLayerRegion *> (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<const db::OriginalLayerRegion *> (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<const db::OriginalLayerRegion *> (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<const db::OriginalLayerRegion *> (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<const db::OriginalLayerRegion *> (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<const db::FlatRegion *> (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<const db::OriginalLayerRegion *> (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) TEST(100_Issue1275)
{ {
db::Point pts[] = { db::Point pts[] = {

View File

@ -3341,3 +3341,133 @@ TEST(deep_region_peel)
db::compare_layouts (_this, ly, tl::testdata () + "/algo/deep_region_peel_au.gds"); 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<const db::DeepRegion *> (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<const db::DeepRegion *> (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<const db::DeepRegion *> (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<const db::DeepRegion *> (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<const db::DeepRegion *> (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<const db::DeepRegion *> (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<const db::DeepRegion *> (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<const db::DeepRegion *> (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)");
}

View File

@ -2123,3 +2123,23 @@ TEST(149d_compound_drc_with_mag)
{ {
run_test (_this, "149", true); 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);
}

View File

@ -412,6 +412,7 @@ View::render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas)
Service::Service (db::Manager *manager, lay::LayoutViewBase *view) Service::Service (db::Manager *manager, lay::LayoutViewBase *view)
: lay::BackgroundViewObject (view->canvas ()), : lay::BackgroundViewObject (view->canvas ()),
lay::ViewService (view->canvas ()),
lay::Editable (view), lay::Editable (view),
lay::Plugin (view), lay::Plugin (view),
db::Object (manager), db::Object (manager),
@ -424,6 +425,9 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view)
m_images_visible (true), m_images_visible (true),
m_visibility_cache_valid (false) 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 // place images behind the grid
z_order (-1); z_order (-1);
@ -465,6 +469,9 @@ void
Service::show_images (bool f) Service::show_images (bool f)
{ {
if (m_images_visible != f) { if (m_images_visible != f) {
if (! f) {
clear_selection ();
}
m_images_visible = f; m_images_visible = f;
view ()->redraw_deco_layer (); view ()->redraw_deco_layer ();
} }
@ -596,9 +603,52 @@ dragging_what (const img::Object *iobj, const db::DBox &search_dbox, img::Servic
} }
bool 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; return false;
} }
@ -1309,7 +1359,6 @@ Service::transient_select (const db::DPoint &pos)
clear_transient_selection (); clear_transient_selection ();
bool any_selected = false; bool any_selected = false;
std::string data_string;
// compute search box // compute search box
double l = catch_distance (); 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); 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; any_selected = true;
} }
if (any_selected && ! editables ()->has_selection ()) { if (any_selected && ! editables ()->has_selection ()) {
display_status (true, data_string); display_status (true);
} }
return any_selected; 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); select (mp_view->annotation_shapes ().iterator_from_pointer (robj), mode);
m_previous_selection.insert (mp_view->annotation_shapes ().iterator_from_pointer (robj)); m_previous_selection.insert (mp_view->annotation_shapes ().iterator_from_pointer (robj));
needs_update = true; needs_update = true;
any_selected = true;
} }
} }

View File

@ -195,6 +195,7 @@ private:
class IMG_PUBLIC Service class IMG_PUBLIC Service
: public lay::BackgroundViewObject, : public lay::BackgroundViewObject,
public lay::ViewService,
public lay::Editable, public lay::Editable,
public lay::Plugin, public lay::Plugin,
public db::Object public db::Object
@ -436,7 +437,15 @@ public:
/** /**
* @brief Obtain the lay::Editable interface * @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; return this;
} }

View File

@ -626,7 +626,7 @@ Editables::end_move (const db::DVector &v, db::Transaction *transaction)
{ {
std::unique_ptr<db::Transaction> trans_holder (transaction ? transaction : new db::Transaction (manager (), tl::to_string (tr ("Move")))); std::unique_ptr<db::Transaction> 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 (); trans_holder->open ();

View File

@ -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);
}
}
} }
} }

View File

@ -3383,6 +3383,7 @@ LayoutViewBase::reload_layout (unsigned int cv_index)
} }
set_properties (new_props); set_properties (new_props);
do_update_layer_sources ();
goto_view (state); goto_view (state);
} }

View File

@ -196,7 +196,9 @@ NewLayoutPropertiesDialog::exec_dialog (std::string &technology, std::string &ce
while (! ex.at_end ()) { while (! ex.at_end ()) {
db::LayerProperties lp; db::LayerProperties lp;
try { try {
lp.read (ex); if (! lp.read (ex)) {
break;
}
} catch (...) { } catch (...) {
break; break;
} }

View File

@ -131,3 +131,23 @@ TEST(variants)
run_test (_this, tl::testdata (), "variants.lstr", "variants_au.oas"); 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)");
}

View File

@ -85,7 +85,7 @@ void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracer
fn += file_au; fn += file_au;
CHECKPOINT (); 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) TEST(1)

View File

@ -352,7 +352,7 @@ class _PCellDeclarationHelperMixin:
""" """
default implementation default implementation
""" """
return self.name() return super(_PCellDeclarationHelperMixin, self).name()
def coerce_parameters_impl(self): def coerce_parameters_impl(self):
""" """

View File

@ -16161,8 +16161,7 @@ class DText:
Setter: Setter:
@brief Sets the vertical alignment @brief Sets the vertical alignment
This property specifies how the text is aligned relative to the anchor point. This is the version accepting integer values. It's provided for backward compatibility.
This property has been introduced in version 0.22 and extended to enums in 0.28.
""" """
x: float x: float
r""" r"""
@ -39453,23 +39452,14 @@ class Layout:
... ...
@overload @overload
@classmethod @classmethod
def new(cls, editable: bool) -> Layout: def new(cls, editable: bool, manager: Optional[Manager] = ...) -> 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:
r""" r"""
@brief Creates a layout object attached to a manager @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. 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. 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. 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 @classmethod
def properties_array(cls, properties_id: int) -> Any: def properties_array(cls, properties_id: int) -> Any:
r""" r"""
@ -39591,22 +39595,14 @@ class Layout:
""" """
... ...
@overload @overload
def __init__(self, editable: bool) -> None: def __init__(self, editable: bool, manager: Optional[Manager] = ...) -> 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:
r""" r"""
@brief Creates a layout object attached to a manager @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. 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. 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. 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: def _const_cast(self) -> Layout:
r""" r"""
@brief Returns a non-const reference to self. @brief Returns a non-const reference to self.
@ -49955,17 +49964,17 @@ class Netlist:
@overload @overload
def circuit_by_cell_index(self, cell_index: int) -> Circuit: def circuit_by_cell_index(self, cell_index: int) -> Circuit:
r""" 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. 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 @overload
def circuit_by_cell_index(self, cell_index: int) -> Circuit: def circuit_by_cell_index(self, cell_index: int) -> Circuit:
r""" 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. 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 @overload
@ -66555,10 +66564,10 @@ class Shape:
Applies to texts only. Will throw an exception if the object is not a text. Applies to texts only. Will throw an exception if the object is not a text.
Setter: 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. 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 text_valign: int
r""" r"""
@ -71393,8 +71402,7 @@ class Text:
Setter: Setter:
@brief Sets the vertical alignment @brief Sets the vertical alignment
This property specifies how the text is aligned relative to the anchor point. This is the version accepting integer values. It's provided for backward compatibility.
This property has been introduced in version 0.22 and extended to enums in 0.28.
""" """
x: int x: int
r""" r"""

View File

@ -10956,6 +10956,16 @@ class PixelBuffer:
By default, the pixel buffer does not support an alpha channel. By default, the pixel buffer does not support an alpha channel.
""" """
@classmethod @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: def from_png_data(cls, data: bytes) -> PixelBuffer:
r""" r"""
@brief Reads the pixel buffer from a PNG byte stream @brief Reads the pixel buffer from a PNG byte stream
@ -11142,6 +11152,15 @@ class PixelBuffer:
@brief Swaps data with another PixelBuffer object @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: def to_png_data(self) -> bytes:
r""" r"""
@brief Converts the pixel buffer to a PNG byte stream @brief Converts the pixel buffer to a PNG byte stream

Binary file not shown.

Binary file not shown.

Binary file not shown.

20
testdata/drc/drcSimpleTests_150.drc vendored Normal file
View File

@ -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)

BIN
testdata/drc/drcSimpleTests_150.gds vendored Normal file

Binary file not shown.

14
testdata/drc/drcSimpleTests_151.drc vendored Normal file
View File

@ -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)

BIN
testdata/drc/drcSimpleTests_151.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au150.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au150d.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au151.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au151d.gds vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
testdata/lstream/layers.lstr vendored Normal file

Binary file not shown.

View File

@ -134,6 +134,16 @@ if "PCellDeclarationHelper" in pya.__dict__:
self.width = self.shape.box.width() * self.layout.dbu self.width = self.shape.box.width() * self.layout.dbu
self.height = self.shape.box.height() * 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): class PCellTestLib2(pya.Library):
def __init__(self): def __init__(self):
@ -143,6 +153,7 @@ if "PCellDeclarationHelper" in pya.__dict__:
# create the PCell declarations # create the PCell declarations
self.layout().register_pcell("Box2", BoxPCell2()) self.layout().register_pcell("Box2", BoxPCell2())
self.layout().register_pcell("PCellWithNameParameter", PCellWithNameParameter())
# register us with the name "PCellTestLib2" # register us with the name "PCellTestLib2"
self.register("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(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)") 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 # run unit tests
if __name__ == '__main__': if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(DBPCellTests) suite = unittest.TestLoader().loadTestsFromTestCase(DBPCellTests)

View File

@ -2518,6 +2518,40 @@ class DBLayoutTests1_TestClass < TestBase
end 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 end
load("test_epilogue.rb") load("test_epilogue.rb")

View File

@ -213,6 +213,15 @@ class DBNetlist_TestClass < TestBase
assert_equal(p1.name, "X") assert_equal(p1.name, "X")
assert_equal(p2.name, "B") 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) assert_equal(p1.property(17), nil)
p1.set_property(17, 42) p1.set_property(17, 42)
assert_equal(p1.property(17), 42) assert_equal(p1.property(17), 42)
@ -245,6 +254,9 @@ class DBNetlist_TestClass < TestBase
c.remove_pin(0) c.remove_pin(0)
assert_equal(n1.pin_count, 0)
assert_equal(n2.pin_count, 1)
names = [] names = []
c.each_pin { |p| names << p.name } c.each_pin { |p| names << p.name }
assert_equal(names, [ "B" ]) 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) == nil, false)
assert_equal(c.pin_by_id(1).name, "B") 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 end
def test_4_Device def test_4_Device

View File

@ -173,6 +173,20 @@ if RBA.constants.member?(:PCellDeclarationHelper)
end 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 class PCellTestLib2 < RBA::Library
def initialize def initialize
@ -182,6 +196,7 @@ if RBA.constants.member?(:PCellDeclarationHelper)
# create the PCell declarations # create the PCell declarations
layout.register_pcell("Box2", BoxPCell2::new) layout.register_pcell("Box2", BoxPCell2::new)
layout.register_pcell("PCellWithNameParameter", PCellWithNameParameter::new)
# register us with the name "MyLib" # register us with the name "MyLib"
self.register("PCellTestLib2") self.register("PCellTestLib2")
@ -1012,6 +1027,25 @@ class DBPCell_TestClass < TestBase
end 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 end
class DBPCellParameterStates_TestClass < TestBase class DBPCellParameterStates_TestClass < TestBase

View File

@ -2,10 +2,10 @@
# This script is sourced to define the main version parameters # This script is sourced to define the main version parameters
# The main version # The main version
KLAYOUT_VERSION="0.30.9" KLAYOUT_VERSION="0.30.10"
# The version used for PyPI (don't use variables here!) # 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 # The build date
KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d") KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")