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):
* 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

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
* New features and bugfixes

View File

@ -289,6 +289,11 @@ module RBA
alias_method :_layout_base, :layout
end
# makes PCellDeclaration's "name" method available
if ! self.method_defined?(:_name_base)
alias_method :_name_base, :name
end
# import the Type... constants from PCellParameterDeclaration
PCellParameterDeclaration.constants.each do |c|
if !const_defined?(c)
@ -595,7 +600,7 @@ module RBA
# default implementation
def cell_name_impl
self.name
_name_base()
end
# default implementation

View File

@ -631,14 +631,14 @@ namespace {
class OutputPairHolder
{
public:
OutputPairHolder (InteractingOutputMode output_mode, bool merged_semantics)
OutputPairHolder (InteractingOutputMode output_mode, bool merged_semantics, bool min_coherence)
{
if (output_mode == None) {
return;
}
if (output_mode == Positive || output_mode == Negative || output_mode == PositiveAndNegative) {
m_positive.reset (new FlatRegion (merged_semantics));
m_positive.reset (new FlatRegion (merged_semantics, 0.0, 0, min_coherence));
m_results.push_back (& m_positive->raw_polygons ());
} else {
m_results.push_back ((db::Shapes *) 0);
@ -667,7 +667,7 @@ private:
std::pair<RegionDelegate *, RegionDelegate *>
AsIfFlatRegion::in_and_out_generic (const Region &other, InteractingOutputMode output_mode) const
{
OutputPairHolder oph (output_mode, merged_semantics ());
OutputPairHolder oph (output_mode, merged_semantics (), min_coherence ());
if (output_mode == None) {
return oph.region_pair ();
@ -717,7 +717,7 @@ AsIfFlatRegion::in_and_out_generic (const Region &other, InteractingOutputMode o
std::pair<RegionDelegate *, RegionDelegate *>
AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const
{
OutputPairHolder oph (output_mode, merged_semantics () || is_merged ());
OutputPairHolder oph (output_mode, merged_semantics () || is_merged (), min_coherence ());
if (output_mode == None) {
return oph.region_pair ();
@ -756,7 +756,7 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOut
std::vector<generic_shape_iterator<db::Edge> > others;
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;
results.push_back (&output->raw_polygons ());
@ -768,7 +768,7 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOut
std::pair<RegionDelegate *, RegionDelegate *>
AsIfFlatRegion::selected_interacting_generic (const Texts &other, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const
{
OutputPairHolder oph (output_mode, merged_semantics () || is_merged ());
OutputPairHolder oph (output_mode, merged_semantics () || is_merged (), min_coherence ());
if (output_mode == None) {
return oph.region_pair ();
@ -814,7 +814,7 @@ AsIfFlatRegion::selected_interacting_generic (const Texts &other, InteractingOut
std::pair<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
{
OutputPairHolder oph (output_mode, merged_semantics () || is_merged ());
OutputPairHolder oph (output_mode, merged_semantics () || is_merged (), min_coherence ());
if (output_mode == None) {
return oph.region_pair ();
@ -941,7 +941,7 @@ AsIfFlatRegion::pull_generic (const Region &other, int mode, bool touching) cons
std::vector<generic_shape_iterator<db::Polygon> > others;
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;
results.push_back (&output->raw_polygons ());
@ -1106,7 +1106,7 @@ AsIfFlatRegion::scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db
throw tl::Exception (tl::to_string (tr ("Scale and snap requires positive and non-null magnification or divisor values")));
}
std::unique_ptr<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);
gy = std::max (db::Coord (1), gy);
@ -1184,13 +1184,7 @@ AsIfFlatRegion::width_check (db::Coord d, const RegionCheckOptions &options) con
EdgePairsDelegate *
AsIfFlatRegion::space_or_isolated_check (db::Coord d, const RegionCheckOptions &options, bool isolated) const
{
if (options.opposite_filter != NoOppositeFilter || options.rect_filter != NoRectFilter || options.shielded) {
// NOTE: we have to use the "foreign" scheme with a filter because only this scheme
// guarantees that all subject shapes are visited.
return run_check (db::SpaceRelation, isolated, foreign_regionptr (), d, options);
} else {
return run_check (db::SpaceRelation, isolated, subject_regionptr (), d, options);
}
return run_check (db::SpaceRelation, isolated, 0, d, options);
}
EdgePairsDelegate *
@ -1239,28 +1233,43 @@ EdgePairsDelegate *
AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, const RegionCheckOptions &options) const
{
// force different polygons in the different properties case to skip intra-polygon checks
if (pc_always_different (options.prop_constraint)) {
if (! other && pc_always_different (options.prop_constraint)) {
different_polygons = true;
}
bool needs_merged_primary = different_polygons || options.needs_merged ();
bool needs_merged_primary = (! other && different_polygons) || options.needs_merged ();
bool primary_is_merged = is_merged ();
db::RegionIterator polygons;
db::RegionIterator polygons (needs_merged_primary ? begin_merged () : begin ());
bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged ();
if (! merged_semantics ()) {
primary_is_merged = true; // means: don't merge again
needs_merged_primary = false;
polygons = begin ();
} else if (! needs_merged_primary) {
// The implementation may run faster if the primary is not merged
primary_is_merged = false;
polygons = begin_unmerged ();
} else {
primary_is_merged = true;
polygons = begin_merged ();
}
EdgeRelationFilter check (rel, d, options);
std::vector<db::RegionIterator> others;
std::vector<bool> foreign;
bool has_other = false;
bool other_is_merged = true;
if (other == subject_regionptr () || other == foreign_regionptr ()) {
foreign.push_back (other == foreign_regionptr ());
if (! other) {
foreign.push_back (true);
others.push_back (polygons);
other_is_merged = primary_is_merged;
} else {
foreign.push_back (false);
if (! other->merged_semantics ()) {
others.push_back (other->begin ());
other_is_merged = true;
@ -1272,7 +1281,13 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
others.push_back (other->begin ());
other_is_merged = other->is_merged ();
}
has_other = true;
// adds another intruder section to implement subject merging ("primary_intruders")
if (! primary_is_merged) {
foreign.push_back (true);
others.push_back (polygons);
}
}
std::unique_ptr<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)) {
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;
proc.set_base_verbosity (base_verbosity ());
@ -1293,7 +1308,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
} else {
db::check_local_operation_with_properties<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;
proc.set_base_verbosity (base_verbosity ());
@ -1334,7 +1349,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord
}
RegionDelegate *
AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const
AsIfFlatRegion::merged (bool min_coh, unsigned int min_wc, bool join_properties_on_merge) const
{
if (empty ()) {
@ -1351,8 +1366,8 @@ AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc, bool join_prope
} else {
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true));
merge_polygons_to (new_region->raw_polygons (), min_coherence, min_wc, join_properties_on_merge);
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true, 0, 0, min_coh));
merge_polygons_to (new_region->raw_polygons (), min_coh, min_wc, join_properties_on_merge);
return new_region.release ();
@ -1695,7 +1710,7 @@ AsIfFlatRegion::and_or_not_with (bool is_and, const Region &other, PropertyConst
ep.insert (*p, n);
}
std::unique_ptr<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::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ());
@ -1772,12 +1787,12 @@ AsIfFlatRegion::andnot_with (const Region &other, PropertyConstraint property_co
ep.insert (*p, n);
}
std::unique_ptr<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::ShapeGenerator pc1 (new_region1->raw_polygons (), true /*clear*/);
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::ShapeGenerator pc2 (new_region2->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg2 (pc2, false /*don't resolve holes*/, min_coherence ());
@ -1860,7 +1875,7 @@ AsIfFlatRegion::xor_with (const Region &other, PropertyConstraint prop_constrain
ep.insert (*p, n);
}
std::unique_ptr<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::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ());
@ -1916,7 +1931,7 @@ AsIfFlatRegion::or_with (const Region &other, PropertyConstraint /*prop_constrai
ep.insert (*p, n);
}
std::unique_ptr<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::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
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)
{
if (id < m_pin_by_id.size () && ! tl::is_null_iterator (m_pin_by_id [id])) {
connect_pin (id, 0);
m_pins.erase (m_pin_by_id [id]);
m_pin_by_id [id] = pin_list::iterator ();
}

View File

@ -541,31 +541,58 @@ CommonReaderBase::open_dl_uncached (db::Layout &layout, const LDPair &dl)
}
unsigned int nl = layout.insert_layer (lp);
// change OASIS layer name if a layer already exists by layer/datatype, but has a different name
if (! lp.name.empty () && layout.get_properties (nl).name != lp.name) {
layout.set_properties (nl, lp);
}
m_layer_map_out.map (dl, nl, lp);
m_layers_created.insert (nl);
return std::make_pair (true, nl);
} else if (li.size () == 1) {
m_layer_map_out.map (dl, *li.begin (), layout.get_properties (*li.begin ()));
return std::make_pair (true, *li.begin ());
} else {
for (std::set<unsigned int>::const_iterator i = li.begin (); i != li.end (); ++i) {
m_layer_map_out.mmap (dl, *i, layout.get_properties (*i));
for (auto i = li.begin (); i != li.end (); ++i) {
// change OASIS layer name if a layer exists by layer/datatype, but has a different name
const tl::interval_map <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 (mmp == m_multi_mapping_placeholders.end ()) {
// create a placeholder layer
mmp = m_multi_mapping_placeholders.insert (std::make_pair (li, layout.insert_layer ())).first;
}
if (li.size () == 1) {
return std::make_pair (true, mmp->second);
m_layer_map_out.map (dl, *li.begin (), layout.get_properties (*li.begin ()));
return std::make_pair (true, *li.begin ());
} else {
for (std::set<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)
: MutableRegion (other), DeepShapeCollectionDelegateBase (other),
m_merged_polygons_valid (other.m_merged_polygons_valid),
m_merged_polygons_min_coherence (other.m_merged_polygons_min_coherence),
m_merged_polygons_boc_hash (other.m_merged_polygons_boc_hash),
m_is_merged (other.m_is_merged)
m_is_merged (other.m_is_merged),
m_is_merged_min_coherence (other.m_is_merged_min_coherence)
{
if (m_merged_polygons_valid) {
m_merged_polygons = other.m_merged_polygons.copy ();
@ -196,8 +198,10 @@ DeepRegion::operator= (const DeepRegion &other)
DeepShapeCollectionDelegateBase::operator= (other);
m_merged_polygons_valid = other.m_merged_polygons_valid;
m_merged_polygons_min_coherence = other.m_merged_polygons_min_coherence;
m_merged_polygons_boc_hash = other.m_merged_polygons_boc_hash;
m_is_merged = other.m_is_merged;
m_is_merged_min_coherence = other.m_is_merged_min_coherence;
if (m_merged_polygons_valid) {
m_merged_polygons = other.m_merged_polygons.copy ();
}
@ -210,9 +214,11 @@ DeepRegion::operator= (const DeepRegion &other)
void DeepRegion::init ()
{
m_merged_polygons_valid = false;
m_merged_polygons_min_coherence = false;
m_merged_polygons_boc_hash = 0;
m_merged_polygons = db::DeepLayer ();
m_is_merged = false;
m_is_merged_min_coherence = false;
}
RegionDelegate *
@ -228,7 +234,7 @@ void DeepRegion::merged_semantics_changed ()
void DeepRegion::min_coherence_changed ()
{
set_is_merged (false);
// merged status is tracked in separate variables and is validated on inquiry
}
void DeepRegion::join_properties_on_merge_changed ()
@ -468,7 +474,7 @@ DeepRegion::empty () const
bool
DeepRegion::is_merged () const
{
return m_is_merged;
return m_is_merged && (m_is_merged_min_coherence == min_coherence ());
}
const db::Polygon *
@ -739,18 +745,21 @@ DeepRegion::merged_deep_layer () const
bool
DeepRegion::merged_polygons_available () const
{
return m_is_merged || (m_merged_polygons_valid && m_merged_polygons_boc_hash == deep_layer ().breakout_cells_hash ());
return (m_is_merged && m_is_merged_min_coherence == min_coherence ()) ||
(m_merged_polygons_valid && m_merged_polygons_min_coherence == min_coherence () && m_merged_polygons_boc_hash == deep_layer ().breakout_cells_hash ());
}
void
DeepRegion::ensure_merged_polygons_valid () const
{
if (! m_merged_polygons_valid || (! m_is_merged && m_merged_polygons_boc_hash != deep_layer ().breakout_cells_hash ())) {
if (! m_merged_polygons_valid || m_merged_polygons_min_coherence != min_coherence () ||
((! m_is_merged || m_is_merged_min_coherence != min_coherence ()) && m_merged_polygons_boc_hash != deep_layer ().breakout_cells_hash ())) {
if (m_is_merged) {
if (m_is_merged && m_is_merged_min_coherence == min_coherence ()) {
// NOTE: this will reuse the deep layer reference
m_merged_polygons = deep_layer ();
m_merged_polygons_min_coherence = min_coherence ();
} else {
@ -796,6 +805,7 @@ DeepRegion::ensure_merged_polygons_valid () const
}
m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = min_coherence ();
m_merged_polygons_boc_hash = deep_layer ().breakout_cells_hash ();
}
@ -813,7 +823,9 @@ DeepRegion::ensure_unmerged_polygons_valid () const
db::DeepLayer &polygons = const_cast<db::DeepLayer &> (deep_layer ());
m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = m_is_merged_min_coherence;
m_is_merged = false;
m_is_merged_min_coherence = min_coherence ();
m_merged_polygons_boc_hash = deep_layer ().breakout_cells_hash ();
db::Layout &layout = polygons.layout ();
@ -845,6 +857,7 @@ void
DeepRegion::set_is_merged (bool f)
{
m_is_merged = f;
m_is_merged_min_coherence = min_coherence ();
m_merged_polygons_valid = false;
m_merged_polygons_boc_hash = 0;
m_merged_polygons = db::DeepLayer ();
@ -2026,6 +2039,7 @@ DeepRegion::merged () const
res->deep_layer ().layer ();
res->set_min_coherence (min_coherence ());
res->set_is_merged (true);
return res.release ();
}
@ -2370,48 +2384,75 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
{
if (empty ()) {
return new db::DeepEdgePairs (deep_layer ().derived ());
} else if (other && ! is_subject_regionptr (other) && other->empty () && ! options.negative) {
} else if (other && other->empty () && ! options.negative) {
return new db::DeepEdgePairs (deep_layer ().derived ());
}
// force different polygons in the different properties case to skip intra-polygon checks
if (pc_always_different (options.prop_constraint)) {
// TODO: this forces merged primaries, so maybe that is not a good optimization?
different_polygons = true;
}
// delegate to AsIfFlatRegion if the other region is not a deep one
const db::DeepRegion *other_deep = 0;
unsigned int other_layer = 0;
bool other_is_merged = true;
bool needs_merged_primary = different_polygons || options.needs_merged ();
bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged ();
if (other == subject_regionptr ()) {
other_layer = subject_idlayer ();
other_is_merged = primary_is_merged;
} else if (other == foreign_regionptr ()) {
other_layer = foreign_idlayer ();
other_is_merged = primary_is_merged;
} else {
if (other) {
other_deep = dynamic_cast<const db::DeepRegion *> (other->delegate ());
if (! other_deep) {
return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, options);
}
}
// force different polygons in the different properties case to skip intra-polygon checks
if (! other_deep && pc_always_different (options.prop_constraint)) {
// TODO: this forces merged primaries, so maybe that is not a good optimization?
different_polygons = true;
}
// primary input
bool needs_merged_primary = (! other_deep && different_polygons) || options.needs_merged ();
bool primary_is_merged = false;
db::DeepLayer polygons;
if (! merged_semantics ()) {
primary_is_merged = true; // means: don't merge again
polygons = deep_layer ();
} else if (! needs_merged_primary) {
// The implementation may run faster if the primary is not merged
primary_is_merged = false;
ensure_unmerged_polygons_valid ();
polygons = deep_layer ();
} else {
primary_is_merged = true;
polygons = merged_deep_layer ();
}
// other input
std::vector<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 ()) {
other_layer = other_deep->deep_layer ().layer ();
other_layers.push_back (other_deep->deep_layer ().layer ());
other_is_merged = true;
} else if (options.whole_edges) {
// NOTE: whole edges needs both inputs merged
other_layer = other_deep->merged_deep_layer ().layer ();
other_layers.push_back (other_deep->merged_deep_layer ().layer ());
other_is_merged = true;
} else {
other_layer = other_deep->deep_layer ().layer ();
other_layers.push_back (other_deep->deep_layer ().layer ());
other_is_merged = other->is_merged ();
}
}
const db::DeepLayer &polygons = needs_merged_primary ? merged_deep_layer () : deep_layer ();
// adds another intruder section to implement subject merging ("primary_intruders")
if (! primary_is_merged) {
other_layers.push_back (foreign_idlayer ());
}
}
EdgeRelationFilter check (rel, d, options);
@ -2435,7 +2476,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
configure_proc (proc);
proc.set_threads (polygons.store ()->threads ());
proc.run (&op, polygons.layer (), other_layer, res->deep_layer ().layer ());
proc.run (&op, polygons.layer (), other_layers, res->deep_layer ().layer ());
} else {
@ -2448,7 +2489,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
configure_proc (proc);
proc.set_threads (polygons.store ()->threads ());
proc.run (&op, polygons.layer (), other_layer, res->deep_layer ().layer ());
proc.run (&op, polygons.layer (), other_layers, res->deep_layer ().layer ());
}

View File

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

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)
{
init ();
set_min_coherence (other.min_coherence ());
m_is_merged = other.m_is_merged;
m_is_merged_min_coherence = other.m_is_merged_min_coherence;
m_merged_polygons_valid = other.m_merged_polygons_valid;
m_merged_polygons_min_coherence = other.m_merged_polygons_min_coherence;
}
FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged, double area_ratio, size_t max_vertex_count)
FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged, double area_ratio, size_t max_vertex_count, bool min_coh)
: MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)),
m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{
init ();
set_min_coherence (min_coh);
m_is_merged = is_merged;
m_is_merged_min_coherence = min_coh;
}
FlatRegion::FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged, double area_ratio, size_t max_vertex_count)
FlatRegion::FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged, double area_ratio, size_t max_vertex_count, bool min_coh)
: MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)),
m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{
init ();
set_min_coherence (min_coh);
m_is_merged = is_merged;
m_is_merged_min_coherence = min_coh;
transform_generic (trans);
set_merged_semantics (merged_semantics);
}
FlatRegion::FlatRegion (bool is_merged, double area_ratio, size_t max_vertex_count)
FlatRegion::FlatRegion (bool is_merged, double area_ratio, size_t max_vertex_count, bool min_coh)
: MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)),
m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{
init ();
set_min_coherence (min_coh);
m_is_merged = is_merged;
m_is_merged_min_coherence = min_coh;
}
FlatRegion::~FlatRegion ()
@ -83,6 +92,7 @@ FlatRegion::~FlatRegion ()
void FlatRegion::set_is_merged (bool m)
{
m_is_merged = m;
m_is_merged_min_coherence = min_coherence ();
}
void FlatRegion::invalidate_cache ()
@ -95,7 +105,9 @@ void FlatRegion::invalidate_cache ()
void FlatRegion::init ()
{
m_is_merged = false;
m_is_merged_min_coherence = false;
m_merged_polygons_valid = false;
m_merged_polygons_min_coherence = false;
}
void FlatRegion::merged_semantics_changed ()
@ -112,9 +124,7 @@ void FlatRegion::join_properties_on_merge_changed ()
void FlatRegion::min_coherence_changed ()
{
m_is_merged = false;
mp_merged_polygons->clear ();
m_merged_polygons_valid = false;
// merged status is tracked in separate variables and is validated on inquiry
}
void FlatRegion::reserve (size_t n)
@ -122,12 +132,25 @@ void FlatRegion::reserve (size_t n)
mp_polygons->reserve (db::Polygon::tag (), n);
}
bool
FlatRegion::merged_polygons_valid () const
{
return m_merged_polygons_valid && m_merged_polygons_min_coherence == min_coherence ();
}
bool
FlatRegion::merged_polygons_available () const
{
return is_merged () || merged_polygons_valid ();
}
void
FlatRegion::ensure_merged_polygons_valid () const
{
if (! m_merged_polygons_valid) {
if (! merged_polygons_valid ()) {
merge_polygons_to (*mp_merged_polygons, min_coherence (), 0, join_properties_on_merge ());
m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = min_coherence ();
}
}
@ -140,7 +163,9 @@ FlatRegion::ensure_unmerged_polygons_valid () const
mp_merged_polygons.reset (new db::Shapes (*mp_polygons));
m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = m_is_merged_min_coherence;
m_is_merged = false;
m_is_merged_min_coherence = min_coherence ();
break_polygons (*mp_polygons, m_max_vertex_count, m_area_ratio);
}
@ -152,7 +177,7 @@ RegionIteratorDelegate *FlatRegion::begin () const
RegionIteratorDelegate *FlatRegion::begin_merged () const
{
if (! merged_semantics () || m_is_merged) {
if (! merged_semantics () || is_merged ()) {
return begin ();
} else {
ensure_merged_polygons_valid ();
@ -173,7 +198,7 @@ std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_iter ()
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 ();
} else {
ensure_merged_polygons_valid ();
@ -204,7 +229,7 @@ size_t FlatRegion::hier_count () const
bool FlatRegion::is_merged () const
{
return m_is_merged;
return m_is_merged && m_is_merged_min_coherence == min_coherence ();
}
Box FlatRegion::compute_bbox () const
@ -246,6 +271,7 @@ RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter)
mp_merged_polygons->clear ();
invalidate_cache ();
m_is_merged = filter.requires_raw_input () ? false : merged_semantics ();
m_is_merged_min_coherence = min_coherence ();
return this;
}
@ -276,6 +302,7 @@ RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter
mp_merged_polygons->clear ();
invalidate_cache ();
m_is_merged = filter.result_is_merged () && merged_semantics ();
m_is_merged_min_coherence = min_coherence ();
if (filter.result_must_not_be_merged ()) {
set_merged_semantics (false);
@ -286,14 +313,15 @@ RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter
RegionDelegate *FlatRegion::merged_in_place ()
{
if (! m_is_merged) {
if (! is_merged ()) {
if (m_merged_polygons_valid) {
if (merged_polygons_valid ()) {
db::Shapes &merged_polygons = *mp_merged_polygons;
mp_polygons->swap (merged_polygons);
merged_polygons.clear ();
m_is_merged = true;
m_is_merged_min_coherence = min_coherence ();
return this;
} else {
@ -305,7 +333,7 @@ RegionDelegate *FlatRegion::merged_in_place ()
}
}
RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge)
RegionDelegate *FlatRegion::merged_in_place (bool min_coh, unsigned int min_wc, bool join_properties_on_merge)
{
if (empty ()) {
@ -322,9 +350,11 @@ RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int mi
} else {
invalidate_cache ();
merge_polygons_to (*mp_polygons, min_coherence, min_wc, join_properties_on_merge);
merge_polygons_to (*mp_polygons, min_coh, min_wc, join_properties_on_merge);
set_min_coherence (min_coh);
m_is_merged = true;
m_is_merged_min_coherence = min_coh;
}
@ -333,19 +363,35 @@ RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int mi
RegionDelegate *FlatRegion::merged () const
{
if (! m_is_merged) {
if (! is_merged ()) {
if (m_merged_polygons_valid) {
return new FlatRegion (*mp_merged_polygons, true);
} else {
return AsIfFlatRegion::merged (min_coherence (), 0, join_properties_on_merge ());
if (! merged_polygons_valid ()) {
ensure_merged_polygons_valid ();
}
std::unique_ptr<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 {
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
{
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;
FlatRegion (double area_ratio = 0.0, size_t max_vertex_count = 0);
FlatRegion (const db::Shapes &polygons, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0);
FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0);
FlatRegion (bool is_merged, double area_ratio = 0.0, size_t max_vertex_count = 0);
FlatRegion (const db::Shapes &polygons, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false);
FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false);
FlatRegion (bool is_merged, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false);
FlatRegion (const FlatRegion &other);
@ -85,10 +85,9 @@ public:
virtual RegionDelegate *merged_in_place ();
virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge);
virtual RegionDelegate *merged () const;
virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const
{
return db::AsIfFlatRegion::merged (min_coherence, min_wc, join_properties_on_merge);
}
virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const;
bool merged_polygons_available () const;
virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter);
virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter);
@ -138,6 +137,7 @@ protected:
virtual Box compute_bbox () const;
void invalidate_cache ();
void set_is_merged (bool m);
bool merged_polygons_valid () const;
private:
friend class AsIfFlatRegion;
@ -146,9 +146,11 @@ private:
FlatRegion &operator= (const FlatRegion &other);
mutable bool m_is_merged;
mutable bool m_is_merged_min_coherence;
mutable tl::copy_on_write_ptr<db::Shapes> mp_polygons;
mutable tl::copy_on_write_ptr<db::Shapes> mp_merged_polygons;
mutable bool m_merged_polygons_valid;
mutable bool m_merged_polygons_min_coherence;
double m_area_ratio;
size_t m_max_vertex_count;
@ -156,6 +158,16 @@ private:
void ensure_merged_polygons_valid () const;
void ensure_unmerged_polygons_valid () const;
void set_area_ratio (double ar)
{
m_area_ratio = ar;
}
void set_max_vertex_count (size_t mc)
{
m_max_vertex_count = mc;
}
template <class 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 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)
: mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result), m_rt (subject_layout)
interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, const std::map<unsigned int, db::Coord> &override_distance, interactions_type *result)
: mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), m_override_distance (override_distance), mp_result (result), m_rt (subject_layout)
{
// nothing yet ..
}
void add (const db::CellInstArray *inst, unsigned int, const TI *ref, unsigned int layer)
{
collect_instance_shape_interactions (inst, layer, *ref, m_dist);
db::Coord d = m_dist;
auto od = m_override_distance.find (layer);
if (od != m_override_distance.end ()) {
d = std::min (d, od->second);
}
collect_instance_shape_interactions (inst, layer, *ref, d);
}
private:
db::Layout *mp_subject_layout;
unsigned int m_subject_layer;
db::Coord m_dist;
std::map<unsigned int, db::Coord> m_override_distance;
interactions_type *mp_result;
db::shape_reference_translator_with_trans<TI, db::ICplxTrans> m_rt;
@ -681,11 +688,11 @@ private:
// LocalProcessorContextComputationTask implementation
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 (),
mp_proc (proc), mp_contexts (&contexts), mp_parent_context (parent_context),
mp_subject_parent (subject_parent), mp_subject_cell (subject_cell), m_subject_cell_inst (subject_cell_inst),
mp_intruder_cell (intruder_cell), m_dist (dist)
mp_intruder_cell (intruder_cell), m_dist (dist), m_override_distance (override_distance)
{
// This is quick, but will take away the intruders from the caller
m_intruders.swap (intruders);
@ -695,7 +702,7 @@ template <class TS, class TI, class TR>
void
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_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;
issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist ());
issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist (), override_distance);
if (mp_cc_job.get ()) {
mp_cc_job->start ();
@ -922,14 +939,15 @@ void local_processor<TS, TI, TR>::issue_compute_contexts (local_processor_contex
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
db::Coord dist,
const std::map<unsigned int, db::Coord> &override_distance) const
{
bool is_small_job = subject_cell->begin ().at_end ();
if (! is_small_job && mp_cc_job.get ()) {
mp_cc_job->schedule (new local_processor_context_computation_task<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 {
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::Cell *intruder_cell,
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)
@ -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?
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_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;
@ -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::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) {
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 *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
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 ());
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;
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);
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
db::Coord dist = op->dist ();
db::Coord dist_global = op->dist ();
auto override_distance = op->override_distance ();
db::Box subjects_box = safe_box_enlarged (subjects.bbox (), dist, dist);
db::Box subjects_box = safe_box_enlarged (subjects.bbox (), dist_global, dist_global);
db::Box intruders_box;
for (typename std::vector<generic_shape_iterator<TI> >::const_iterator il = intruders.begin (); il != intruders.end (); ++il) {
intruders_box += il->bbox ();
}
intruders_box = safe_box_enlarged (intruders_box, dist, dist);
intruders_box = safe_box_enlarged (intruders_box, dist_global, dist_global);
db::Box common_box = intruders_box & subjects_box;
if (common_box.empty () || common_box.width () == 0 || common_box.height () == 0) {
@ -1670,6 +1704,12 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
bool ff = foreign.size () > il_index && foreign [il_index];
db::Coord dist = dist_global;
auto od = override_distance.find (il_index);
if (od != override_distance.end ()) {
dist = std::min (dist, od->second);
}
if (*il == subjects && ! ff) {
interaction_registration_shape1_scanner_combo<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];
db::Coord dist = dist_global;
auto od = override_distance.find (il_index);
if (od != override_distance.end ()) {
dist = std::min (dist, od->second);
}
if (*il == subjects && ! ff) {
interaction_registration_shape1_scanner_combo<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:
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 ();
private:
@ -372,6 +372,7 @@ private:
const db::Cell *mp_intruder_cell;
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type m_intruders;
db::Coord m_dist;
std::map<unsigned int, db::Coord> m_override_distance;
};
template <class TS, class TI, class TR>
@ -578,8 +579,8 @@ private:
void next () 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 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 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 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;
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)
{
layer = db::any_ld ();
@ -207,6 +207,8 @@ LayerProperties::read (tl::Extractor &ex, bool as_target)
layer = l;
datatype = d;
return true;
} else if (ex.try_read_word_or_quoted (name)) {
if (ex.test ("(")) {
@ -223,6 +225,10 @@ LayerProperties::read (tl::Extractor &ex, bool as_target)
}
return true;
} else {
return false;
}
}

View File

@ -91,8 +91,10 @@ struct DB_PUBLIC LayerProperties
* relative layer/datatype specifications in the format "*+1" or "*-100".
* "*" for layer or datatype is for "don't care" (on input) or "leave as is"
* (for output).
*
* Returns true, if a layer was read successfully.
*/
void read (tl::Extractor &ex, bool as_target = false);
bool read (tl::Extractor &ex, bool as_target = false);
/**
* @brief "Logical" equality

View File

@ -118,6 +118,14 @@ public:
*/
virtual db::Coord dist () const { return 0; }
/**
* @brief Gets a override distance for a specific input layer
* The input layers are numbered from 0 to n-1, where n is the allowed number of intruder layers
* The override distance is used insted of the global distance ("dist()") for the given
* input layer. The override distance cannot be bigger than the global distance.
*/
virtual std::map<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
*/

View File

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

View File

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

View File

@ -211,7 +211,7 @@ check_local_operation_base<TS, TI>::check_local_operation_base (const EdgeRelati
template <class TS, class TI>
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
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;
ep.set_base_verbosity (50);
bool take_all = edge_check.has_negative_edge_output () || intruders.empty ();
bool take_all = edge_check.has_negative_edge_output () || (intruders.empty () && primary_intruders.empty ());
db::Box common_box;
bool subjects_are_fully_covered = false;
@ -259,6 +259,14 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
all_intruders_box += intruder_box.enlarged (e);
}
for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) {
db::Box intruder_box = db::box_convert<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;
}
@ -269,7 +277,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
size_t n = 0;
if (m_is_merged || (subjects.size () == 1 && subjects.front ()->is_box ())) {
if (m_is_merged || (subjects.size () == 1 && subjects.front ()->is_box () && primary_intruders.empty ())) {
for (auto i = subjects.begin (); i != subjects.end (); ++i) {
if (! take_all) {
@ -280,9 +288,10 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
n += 2;
}
} else {
} else if (primary_intruders.empty ()) {
// merge needed for the subject shapes
// Here we don't have other subjects to consider. This is the simple case.
ep.clear ();
size_t nn = 0;
@ -310,6 +319,79 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
n += 2;
}
} else {
// merge needed for the subject and primary intruder shapes ("other subjects") - we merge both and then
// reclaim original subject edges that are "outer" edges and not covered by other subjects shapes.
ep.clear ();
size_t nn = 0;
for (auto i = subjects.begin (); i != subjects.end (); ++i) {
for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) {
ep.insert (*e, nn);
}
++nn;
}
for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) {
for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) {
ep.insert (*e, nn);
}
++nn;
}
std::vector<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
@ -384,11 +466,11 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
} else {
if (m_is_merged || (subjects.size () == 1 && intruders.empty () && subjects.front ()->is_box ())) {
if (m_is_merged || (subjects.size () == 1 && primary_intruders.empty () && subjects.front ()->is_box ())) {
// no merge required
// NOTE: we need to eliminate identical shapes from intruders and subjects because those will shield
// NOTE: we need to eliminate identical shapes from primary intruders and subjects because those will shield
size_t n = 0;
std::unordered_set<TI> subjects_hash;
@ -406,7 +488,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
n = 1;
for (auto i = intruders.begin (); i != intruders.end (); ++i) {
for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) {
if (subjects_hash.find (**i) == subjects_hash.end ()) {
if (! take_all) {
poly_check.enter (**i, n, common_box);
@ -416,9 +498,9 @@ check_local_operation_base<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;
@ -451,7 +533,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
} else {
// merge needed for the subject and intruder shapes - we merge both and then
// merge needed for the subject and primary intruder shapes ("other subjects") - we merge both and then
// separate edges into those from the subject and those from intruder shapes.
ep.clear ();
@ -464,7 +546,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
++nn;
}
for (auto i = intruders.begin (); i != intruders.end (); ++i) {
for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) {
for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) {
ep.insert (*e, nn);
}
@ -727,12 +809,40 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, db::Cell *s
subjects.reserve (interactions.size ());
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) {
subjects.push_back (&interactions.subject_shape (i->first));
for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) {
intruders.insert (&interactions.intruder_shape (*ii).second);
const auto &is = interactions.intruder_shape (*ii);
if (is.first == primary_intruder_layer) {
primary_intruders.insert (&is.second);
} else {
intruders.insert (&is.second);
}
}
}
tl_assert (results.size () == 1);
@ -740,7 +850,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, db::Cell *s
std::unordered_set<db::EdgePair> result, intra_polygon_result;
// 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"
// ("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>
db::Coord
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);
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);
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;
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
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"
// ("opposite" is defined by the projection of edges "through" the subject shape)
@ -2281,8 +2414,11 @@ PolygonToEdgeLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell
db::properties_id_type prop_id = shapes_by_prop_id->first;
ep.clear ();
size_t p = 0;
for (auto s = shapes_by_prop_id->second.first.begin (); s != shapes_by_prop_id->second.first.end (); ++s) {
ep.insert (**s);
ep.insert (**s, p++);
}
db::property_injector<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 ();
for (auto s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) {
ep.insert (s->second);
size_t p = 0;
for (auto s = shapes_by_prop_id->second.first.begin (); s != shapes_by_prop_id->second.first.end (); ++s) {
ep.insert (**s, ++p);
}
for (auto i = interactions.begin_intruders (); i != interactions.end_intruders (); ++i) {
ep.insert (i->second.second);
for (auto s = shapes_by_prop_id->second.second.begin (); s != shapes_by_prop_id->second.second.end (); ++s) {
ep.insert (*(s->second), ++p);
}
std::vector<Edge> edges2;
db::EdgeContainer ec2 (edges2);
ep.process (ec2, op);
// Runs the boolean AND between the result with and without intruders
// Runs the boolean AND between the result with and without intruders - this identifies outside edges
db::box_scanner<db::Edge, size_t> scanner;
scanner.reserve (edges1.size () + edges2.size ());

View File

@ -194,7 +194,7 @@ protected:
bool m_other_is_merged;
db::RegionCheckOptions m_options;
void compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector<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_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);
virtual db::Coord dist () const;
virtual std::map<unsigned int, db::Coord> override_distance () const;
virtual OnEmptyIntruderHint on_empty_intruder_hint () const;
virtual bool requests_single_subjects () const { return true; }
virtual std::string description () const;

View File

@ -236,7 +236,7 @@ Shapes::do_insert (const Shapes &d, unsigned int flags)
if (layout () == d.layout ()) {
// both shape containers reside in the same repository space - simply copy
if (m_layers.empty ()) {
if (m_layers.empty () && is_editable () == d.is_editable ()) {
m_layers.reserve (d.m_layers.size ());
for (tl::vector<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)
| ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0)
| ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts)
| ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta
| db::layout_diff::f_smart_cell_mapping)
| ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta)
| ((norm & WithoutLayerNames) == 0 ? 0 : db::layout_diff::f_no_layer_names)
| db::layout_diff::f_smart_cell_mapping
/*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/
, tolerance, 100 /*max diff lines*/);
@ -176,6 +177,7 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std::
| ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0)
| ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts)
| ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta)
| ((norm & WithoutLayerNames) == 0 ? 0 : db::layout_diff::f_no_layer_names)
/*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/
, tolerance, 100 /*max diff lines*/);

View File

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

View File

@ -892,14 +892,38 @@ static db::Layout *layout_default_ctor()
return new db::Layout (true);
}
static db::Layout *editable_layout_ctor_with_manager(bool editable, db::Manager &manager)
static db::Layout *editable_layout_ctor_with_manager(bool editable, db::Manager *manager)
{
return new db::Layout (editable, &manager);
return new db::Layout (editable, manager);
}
static db::Layout *editable_layout_default_ctor(bool editable)
static db::Layout *layout_ctor_from_cell(const db::Cell &source_cell, tl::Variant editable, db::Manager *manager)
{
return new db::Layout (editable);
const db::Layout *source_layout = source_cell.layout ();
if (! source_layout) {
throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout")));
}
bool editable_flag = source_layout->is_editable ();
if (! editable.is_nil ()) {
editable_flag = editable.to_bool ();
}
std::unique_ptr<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)
@ -1286,7 +1310,7 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"always editable. Before that version, they inherited the editable flag from "
"the application."
) +
gsi::constructor ("new", &editable_layout_ctor_with_manager, gsi::arg ("editable"), gsi::arg ("manager"),
gsi::constructor ("new", &editable_layout_ctor_with_manager, gsi::arg ("editable"), gsi::arg ("manager", (db::Manager *) 0, "nil"),
"@brief Creates a layout object attached to a manager\n"
"\n"
"This constructor specifies a manager object which is used to "
@ -1294,16 +1318,21 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"the layout is editable. In editable mode, some optimizations are disabled "
"and the layout can be manipulated through a variety of methods.\n"
"\n"
"The manager object can be nil - in that case, undo/redo is not supported.\n"
"\n"
"This method was introduced in version 0.22.\n"
) +
gsi::constructor ("new", &editable_layout_default_ctor, gsi::arg ("editable"),
"@brief Creates a layout object\n"
gsi::constructor ("new", &layout_ctor_from_cell, gsi::arg ("source_cell"), gsi::arg ("editable", tl::Variant (), "nil"), gsi::arg ("manager", (db::Manager *) 0, "nil"),
"@brief Creates a layout object as a copy of another cell\n"
"\n"
"This constructor specifies whether "
"the layout is editable. In editable mode, some optimizations are disabled "
"and the layout can be manipulated through a variety of methods.\n"
"This convenience constructor creates a new layout object as a hierarchical copy of the source cell including all "
"child cells and shapes.\n"
"\n"
"This method was introduced in version 0.22.\n"
"If 'editable' is a boolean value, the new layout object will be made editable depending on that value. "
"If 'nil' is used for 'editable', the editable attribute is copied from the layout the source cell lives in.\n"
"'manager' can be a \\Manager object to which the new layout will be attached.\n"
"\n"
"This method was introduced in version 0.30.10.\n"
) +
gsi::method ("library", &db::Layout::library,
"@brief Gets the library this layout lives in or nil if the layout is not part of a library\n"

View File

@ -2033,6 +2033,243 @@ TEST(44_SizeWithProperties)
db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au44.gds");
}
TEST(45_min_coherence_and_merge)
{
// NOTE: with an explicit area ratio and max vertex count we can play with unmerged input
db::Region r1 (new db::FlatRegion (3.0, 32));
r1.insert (db::Box (0, 0, 1000, 2000));
r1.insert (db::Box (1000, 2000, 10000, 3000));
r1.insert (db::Box (9000, 3000, 10000, 10000));
EXPECT_EQ (r1.min_coherence (), false);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<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)
{
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");
}
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);
}
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)
: lay::BackgroundViewObject (view->canvas ()),
lay::ViewService (view->canvas ()),
lay::Editable (view),
lay::Plugin (view),
db::Object (manager),
@ -424,6 +425,9 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view)
m_images_visible (true),
m_visibility_cache_valid (false)
{
// for receiving mouse_move events to get the pixel value
ui ()->grab_mouse (this, false);
// place images behind the grid
z_order (-1);
@ -465,6 +469,9 @@ void
Service::show_images (bool f)
{
if (m_images_visible != f) {
if (! f) {
clear_selection ();
}
m_images_visible = f;
view ()->redraw_deco_layer ();
}
@ -596,9 +603,52 @@ dragging_what (const img::Object *iobj, const db::DBox &search_dbox, img::Servic
}
bool
Service::mouse_move_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool /*prio*/)
Service::mouse_move_event (const db::DPoint &pos, unsigned int /*buttons*/, bool prio)
{
// .. nothing yet ..
if (! prio && (m_selected_image_views.size () == size_t (1) || (mp_transient_view && ! editables ()->has_selection ()))) {
const img::Object *image = 0;
bool is_transient = false;
if (! m_selected_image_views.empty ()) {
image = m_selected_image_views.front ()->image_object ();
} else if (mp_transient_view) {
image = mp_transient_view->image_object ();
is_transient = true;
}
if (image) {
std::string data_string;
db::DPoint pixel = image->matrix ().inverted ().trans (pos);
if (pixel.x () > image->width () * -0.5 - 0.5 + db::epsilon && pixel.x () < image->width () * 0.5 + 0.5 - db::epsilon &&
pixel.y () > image->height () * -0.5 - 0.5 + db::epsilon && pixel.y () < image->height () * 0.5 + 0.5 - db::epsilon) {
db::Point pixel_index = db::Point (pixel + db::DVector (image->width () * 0.5 - 0.5, image->height () * 0.5 - 0.5));
// check once again to account to rounding issues
if (pixel_index.x () >= 0 && pixel_index.x () < image->width () &&
pixel_index.y () >= 0 && pixel_index.y () < image->height ()) {
if (image->is_color ()) {
data_string = tl::sprintf ("RGB: %.5g,%.5g,%.5g",
image->pixel (pixel_index.x (), pixel_index.y (), 0),
image->pixel (pixel_index.x (), pixel_index.y (), 1),
image->pixel (pixel_index.x (), pixel_index.y (), 2));
} else {
data_string = tl::sprintf ("%.5g", image->pixel (pixel_index.x (), pixel_index.y ()));
}
}
}
display_status (is_transient, data_string);
}
}
return false;
}
@ -1309,7 +1359,6 @@ Service::transient_select (const db::DPoint &pos)
clear_transient_selection ();
bool any_selected = false;
std::string data_string;
// compute search box
double l = catch_distance ();
@ -1337,41 +1386,12 @@ Service::transient_select (const db::DPoint &pos)
mp_transient_view = new img::View (this, imin, img::View::mode_transient);
}
if (mp_transient_view->image_object ()) {
const img::Object *image = mp_transient_view->image_object ();
db::DPoint pixel = image->matrix ().inverted ().trans (pos);
if (pixel.x () > image->width () * -0.5 - 0.5 + db::epsilon && pixel.x () < image->width () * 0.5 + 0.5 - db::epsilon &&
pixel.y () > image->height () * -0.5 - 0.5 + db::epsilon && pixel.y () < image->height () * 0.5 + 0.5 - db::epsilon) {
db::Point pixel_index = db::Point (pixel + db::DVector (image->width () * 0.5 - 0.5, image->height () * 0.5 - 0.5));
// check once again to account to rounding issues
if (pixel_index.x () >= 0 && pixel_index.x () < image->width () &&
pixel_index.y () >= 0 && pixel_index.y () < image->height ()) {
if (image->is_color ()) {
data_string = tl::sprintf ("RGB: %.5g,%.5g,%.5g",
image->pixel (pixel_index.x (), pixel_index.y (), 0),
image->pixel (pixel_index.x (), pixel_index.y (), 1),
image->pixel (pixel_index.x (), pixel_index.y (), 2));
} else {
data_string = tl::sprintf ("%.5g", image->pixel (pixel_index.x (), pixel_index.y ()));
}
}
}
}
any_selected = true;
}
if (any_selected && ! editables ()->has_selection ()) {
display_status (true, data_string);
display_status (true);
}
return any_selected;
@ -1474,6 +1494,7 @@ Service::select (const db::DBox &box, lay::Editable::SelectionMode mode)
select (mp_view->annotation_shapes ().iterator_from_pointer (robj), mode);
m_previous_selection.insert (mp_view->annotation_shapes ().iterator_from_pointer (robj));
needs_update = true;
any_selected = true;
}
}

View File

@ -195,6 +195,7 @@ private:
class IMG_PUBLIC Service
: public lay::BackgroundViewObject,
public lay::ViewService,
public lay::Editable,
public lay::Plugin,
public db::Object
@ -436,7 +437,15 @@ public:
/**
* @brief Obtain the lay::Editable interface
*/
lay::Editable *editable_interface ()
virtual lay::Editable *editable_interface ()
{
return this;
}
/**
* @brief Obtain the lay::ViewService interface
*/
virtual lay::ViewService *view_service_interface ()
{
return this;
}

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"))));
if (m_any_move_operation) {
if (m_any_move_operation || v != db::DVector ()) {
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);
do_update_layer_sources ();
goto_view (state);
}

View File

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

View File

@ -131,3 +131,23 @@ TEST(variants)
run_test (_this, tl::testdata (), "variants.lstr", "variants_au.oas");
}
TEST(reload_layer_props)
{
db::LoadLayoutOptions options;
db::Layout layout;
auto l1 = layout.insert_layer (db::LayerProperties (1, 0, "A"));
auto l2 = layout.insert_layer (db::LayerProperties (2, 0, "B"));
{
std::string fn (tl::testdata ());
fn += "/lstream/layers.lstr";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (layout, options);
}
// layer 1 got renamed
EXPECT_EQ (layout.get_properties (l1).to_string (), "ONE (1/0)");
EXPECT_EQ (layout.get_properties (l2).to_string (), "B (2/0)");
}

View File

@ -85,7 +85,7 @@ void run_test (tl::TestBase *_this, const std::string &file, const db::NetTracer
fn += file_au;
CHECKPOINT ();
db::compare_layouts (_this, layout_nets, fn, db::NormalizationMode (db::WriteOAS | db::AsPolygons));
db::compare_layouts (_this, layout_nets, fn, db::NormalizationMode (db::WriteOAS | db::AsPolygons | db::WithoutLayerNames));
}
TEST(1)

View File

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

View File

@ -16161,8 +16161,7 @@ class DText:
Setter:
@brief Sets the vertical alignment
This property specifies how the text is aligned relative to the anchor point.
This property has been introduced in version 0.22 and extended to enums in 0.28.
This is the version accepting integer values. It's provided for backward compatibility.
"""
x: float
r"""
@ -39453,23 +39452,14 @@ class Layout:
...
@overload
@classmethod
def new(cls, editable: bool) -> Layout:
r"""
@brief Creates a layout object
This constructor specifies whether the layout is editable. In editable mode, some optimizations are disabled and the layout can be manipulated through a variety of methods.
This method was introduced in version 0.22.
"""
...
@overload
@classmethod
def new(cls, editable: bool, manager: Manager) -> Layout:
def new(cls, editable: bool, manager: Optional[Manager] = ...) -> Layout:
r"""
@brief Creates a layout object attached to a manager
This constructor specifies a manager object which is used to store undo information for example. It also allows one to specify whether the layout is editable. In editable mode, some optimizations are disabled and the layout can be manipulated through a variety of methods.
The manager object can be nil - in that case, undo/redo is not supported.
This method was introduced in version 0.22.
"""
...
@ -39484,6 +39474,20 @@ class Layout:
Starting with version 0.25, layouts created with the default constructor are always editable. Before that version, they inherited the editable flag from the application.
"""
...
@overload
@classmethod
def new(cls, source_cell: Cell, editable: Optional[Any] = ..., manager: Optional[Manager] = ...) -> Layout:
r"""
@brief Creates a layout object as a copy of another cell
This convenience constructor creates a new layout object as a hierarchical copy of the source cell including all child cells and shapes.
If 'editable' is a boolean value, the new layout object will be made editable depending on that value. If 'nil' is used for 'editable', the editable attribute is copied from the layout the source cell lives in.
'manager' can be a \Manager object to which the new layout will be attached.
This method was introduced in version 0.30.10.
"""
...
@classmethod
def properties_array(cls, properties_id: int) -> Any:
r"""
@ -39591,22 +39595,14 @@ class Layout:
"""
...
@overload
def __init__(self, editable: bool) -> None:
r"""
@brief Creates a layout object
This constructor specifies whether the layout is editable. In editable mode, some optimizations are disabled and the layout can be manipulated through a variety of methods.
This method was introduced in version 0.22.
"""
...
@overload
def __init__(self, editable: bool, manager: Manager) -> None:
def __init__(self, editable: bool, manager: Optional[Manager] = ...) -> None:
r"""
@brief Creates a layout object attached to a manager
This constructor specifies a manager object which is used to store undo information for example. It also allows one to specify whether the layout is editable. In editable mode, some optimizations are disabled and the layout can be manipulated through a variety of methods.
The manager object can be nil - in that case, undo/redo is not supported.
This method was introduced in version 0.22.
"""
...
@ -39620,6 +39616,19 @@ class Layout:
Starting with version 0.25, layouts created with the default constructor are always editable. Before that version, they inherited the editable flag from the application.
"""
...
@overload
def __init__(self, source_cell: Cell, editable: Optional[Any] = ..., manager: Optional[Manager] = ...) -> None:
r"""
@brief Creates a layout object as a copy of another cell
This convenience constructor creates a new layout object as a hierarchical copy of the source cell including all child cells and shapes.
If 'editable' is a boolean value, the new layout object will be made editable depending on that value. If 'nil' is used for 'editable', the editable attribute is copied from the layout the source cell lives in.
'manager' can be a \Manager object to which the new layout will be attached.
This method was introduced in version 0.30.10.
"""
...
def _const_cast(self) -> Layout:
r"""
@brief Returns a non-const reference to self.
@ -49955,17 +49964,17 @@ class Netlist:
@overload
def circuit_by_cell_index(self, cell_index: int) -> Circuit:
r"""
@brief Gets the circuit object for a given cell index (const version).
@brief Gets the circuit object for a given cell index.
If the cell index is not valid or no circuit is registered with this index, nil is returned.
This constness variant has been introduced in version 0.26.8.
"""
...
@overload
def circuit_by_cell_index(self, cell_index: int) -> Circuit:
r"""
@brief Gets the circuit object for a given cell index.
@brief Gets the circuit object for a given cell index (const version).
If the cell index is not valid or no circuit is registered with this index, nil is returned.
This constness variant has been introduced in version 0.26.8.
"""
...
@overload
@ -66555,10 +66564,10 @@ class Shape:
Applies to texts only. Will throw an exception if the object is not a text.
Setter:
@brief Sets the text transformation
@brief Sets the text transformation in micrometer units
Applies to texts only. Will throw an exception if the object is not a text.
This method has been introduced in version 0.23.
This method has been introduced in version 0.25.
"""
text_valign: int
r"""
@ -71393,8 +71402,7 @@ class Text:
Setter:
@brief Sets the vertical alignment
This property specifies how the text is aligned relative to the anchor point.
This property has been introduced in version 0.22 and extended to enums in 0.28.
This is the version accepting integer values. It's provided for backward compatibility.
"""
x: int
r"""

View File

@ -10956,6 +10956,16 @@ class PixelBuffer:
By default, the pixel buffer does not support an alpha channel.
"""
@classmethod
def from_bytes(cls, data: bytes) -> PixelBuffer:
r"""
@brief Reconstructs a pixel buffer from a byte stream produced by \to_bytes
The width and height are taken from the header and the stream length is checked against them. The dimensions are capped at 65536 x 65536; a header exceeding this limit raises an error.
This method has been added in version 0.30.10.
"""
...
@classmethod
def from_png_data(cls, data: bytes) -> PixelBuffer:
r"""
@brief Reads the pixel buffer from a PNG byte stream
@ -11142,6 +11152,15 @@ class PixelBuffer:
@brief Swaps data with another PixelBuffer object
"""
...
def to_bytes(self) -> bytes:
r"""
@brief Converts the pixel buffer to a raw byte stream
The stream starts with an 8-byte header (width and height as 32-bit unsigned integers) followed by the raw ARGB32 pixel data with 4 bytes per pixel in row-major order, top to bottom. Unlike \to_png_data this method has zero encoding overhead. Use \from_bytes to reconstruct the pixel buffer.
This method has been added in version 0.30.10.
"""
...
def to_png_data(self) -> bytes:
r"""
@brief Converts the pixel buffer to a PNG byte stream

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.height = self.shape.box.height() * self.layout.dbu
# A PCell with a parameter named "name"
class PCellWithNameParameter(pya.PCellDeclarationHelper):
def __init__(self):
super(PCellWithNameParameter, self).__init__()
self.param("name", self.TypeString, "Name", default = "")
def produce_impl(self):
self.cell.shapes(self.layout.layer(1, 0)).insert(pya.Text(self.name, pya.Trans()))
class PCellTestLib2(pya.Library):
def __init__(self):
@ -143,6 +153,7 @@ if "PCellDeclarationHelper" in pya.__dict__:
# create the PCell declarations
self.layout().register_pcell("Box2", BoxPCell2())
self.layout().register_pcell("PCellWithNameParameter", PCellWithNameParameter())
# register us with the name "PCellTestLib2"
self.register("PCellTestLib2")
@ -588,6 +599,22 @@ class DBPCellTests(unittest.TestCase):
self.assertEqual(c2.display_title(), "PCellTestLib3.RecursivePCell(L=1/0,E=(0,0;20,0),LVL=4")
self.assertEqual(str(c1.dbbox()), "(0,0;20,5.774)")
# PCell with "name" parameter
def test_15(self):
# instantiate and register the library
tl = PCellTestLib2()
lib = pya.Library.library_by_name("PCellTestLib2")
pcell_decl_id = lib.layout().pcell_id("PCellWithNameParameter")
param = { "name": "xyz" }
pcell_var_id = lib.layout().add_pcell_variant(pcell_decl_id, param)
self.assertEqual(lib.layout().cell(pcell_var_id).name, "PCellWithNameParameter")
self.assertEqual(lib.layout().begin_shapes(pcell_var_id, lib.layout().layer(1, 0)).shape().to_s(), "text ('xyz',r0 0,0)")
# run unit tests
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(DBPCellTests)

View File

@ -2518,6 +2518,40 @@ class DBLayoutTests1_TestClass < TestBase
end
# Create layout from a cell
def test_newLayoutFromCell
[ true, false ].each do |editable|
ly = RBA::Layout::new(editable)
top = ly.create_cell("TOP")
a = ly.create_cell("A")
b = ly.create_cell("B")
l1 = ly.layer(1, 0)
a.shapes(l1).insert(RBA::Box::new(0, 0, 1000, 2000))
b.shapes(l1).insert(RBA::Box::new(0, 0, 100, 200))
top.insert(RBA::CellInstArray::new(a, RBA::Trans::new))
a.insert(RBA::CellInstArray::new(b, RBA::Trans::new(1, 2)))
ly2 = RBA::Layout::new(a)
assert_equal(ly2.is_editable?, ly.is_editable?)
assert_equal(ly2.top_cell.name, "A")
assert_equal(collect(ly2.top_cell.begin_shapes_rec(ly2.layer(1, 0)), ly2), "[A](0,0;1000,2000)/[B](1,2;101,202)")
ly2 = RBA::Layout::new(a, false)
assert_equal(ly2.is_editable?, false)
assert_equal(ly2.top_cell.name, "A")
assert_equal(collect(ly2.top_cell.begin_shapes_rec(ly2.layer(1, 0)), ly2), "[A](0,0;1000,2000)/[B](1,2;101,202)")
ly2 = RBA::Layout::new(a, true)
assert_equal(ly2.is_editable?, true)
assert_equal(ly2.top_cell.name, "A")
assert_equal(collect(ly2.top_cell.begin_shapes_rec(ly2.layer(1, 0)), ly2), "[A](0,0;1000,2000)/[B](1,2;101,202)")
end
end
end
load("test_epilogue.rb")

View File

@ -213,6 +213,15 @@ class DBNetlist_TestClass < TestBase
assert_equal(p1.name, "X")
assert_equal(p2.name, "B")
n1 = c.create_net("NET1")
n2 = c.create_net("NET2")
c.connect_pin(p1.id, n1)
assert_equal(n1.pin_count, 1)
c.connect_pin(p2.id, n2)
assert_equal(n2.pin_count, 1)
assert_equal(p1.property(17), nil)
p1.set_property(17, 42)
assert_equal(p1.property(17), 42)
@ -245,6 +254,9 @@ class DBNetlist_TestClass < TestBase
c.remove_pin(0)
assert_equal(n1.pin_count, 0)
assert_equal(n2.pin_count, 1)
names = []
c.each_pin { |p| names << p.name }
assert_equal(names, [ "B" ])
@ -253,6 +265,13 @@ class DBNetlist_TestClass < TestBase
assert_equal(c.pin_by_id(1) == nil, false)
assert_equal(c.pin_by_id(1).name, "B")
c.remove_pin(1)
assert_equal(n1.pin_count, 0)
assert_equal(n2.pin_count, 0)
assert_equal(c.pin_by_id(1) == nil, true)
end
def test_4_Device

View File

@ -173,6 +173,20 @@ if RBA.constants.member?(:PCellDeclarationHelper)
end
# A PCell with a parameter named "name"
class PCellWithNameParameter < RBA::PCellDeclarationHelper
def initialize
super()
param(:name, TypeString, "Name", :default => "")
end
def produce_impl
cell.shapes(layout.layer(1, 0)).insert(RBA::Text::new(name, RBA::Trans::new))
end
end
class PCellTestLib2 < RBA::Library
def initialize
@ -182,6 +196,7 @@ if RBA.constants.member?(:PCellDeclarationHelper)
# create the PCell declarations
layout.register_pcell("Box2", BoxPCell2::new)
layout.register_pcell("PCellWithNameParameter", PCellWithNameParameter::new)
# register us with the name "MyLib"
self.register("PCellTestLib2")
@ -1012,6 +1027,25 @@ class DBPCell_TestClass < TestBase
end
# PCell with "name" parameter
def test_15
# instantiate and register the library
tl = PCellTestLib2::new
lib = RBA::Library::library_by_name("PCellTestLib2")
pcell_decl_id = lib.layout.pcell_id("PCellWithNameParameter")
param = { "name" => "xyz" }
pcell_var_id = lib.layout.add_pcell_variant(pcell_decl_id, param)
assert_equal(lib.layout.cell(pcell_var_id).name, "PCellWithNameParameter")
assert_equal(first_shape(lib.layout.begin_shapes(pcell_var_id, lib.layout.layer(1, 0))).to_s, "text ('xyz',r0 0,0)")
tl._destroy
end
end
class DBPCellParameterStates_TestClass < TestBase

View File

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