mirror of https://github.com/KLayout/klayout.git
commit
eeb908669a
28
Changelog
28
Changelog
|
|
@ -1,3 +1,31 @@
|
|||
0.30.10 (2026-07-22):
|
||||
* Bugfix: %GITHUB%/issues/2384 Deep DRC failure with scaled instances
|
||||
* Enhancement: %GITHUB%/issues/2391 Grid Features: switching between coarser and finier grids with keyboard shortcut
|
||||
* Bugfix: %GITHUB%/issues/2392 Wrong PCell instantiations when switching technologies
|
||||
* Bugfix: %GITHUB%/issues/2396 Python objects did nott support weak refs
|
||||
* Bugfix: Region#edges was not working properly when used on Regions with properties
|
||||
enabled. Now, with properties enabled, the regions are consistently formed from polygons with
|
||||
identical properties before generating the edges.
|
||||
* Enhancement: DRC two-layer checks now do not necessarily require merging for the
|
||||
first input. This in general speeds up the operation in certain cases.
|
||||
As a side effect, the error markers may be split according to partial input
|
||||
polygons, as is the case for plain "space" check already.
|
||||
* Enhancement: Use multiple drawing workers for image_with_options rendering
|
||||
* Bugfix: Update layer names in layer list (when used in addition to layer/datatypes) on reload
|
||||
As a side effect, the layer properties nodes will update the "real source" according to
|
||||
the actual name if the name is used in addition to layer/datatypes. Also, the layout readers
|
||||
will update layer names in the layer properties of the Layout object.
|
||||
* Bugfix: Marek's bug: move interactive/copy interactive did not do anything when the mouse wasn't moved
|
||||
* Bugfix: Entering '16:17' (or other nonsense) into 'new layout' layer list made KLayout hang
|
||||
* Bugfix: API: removing a pin from a Circuit does not crash if the pin was not disconnected before.
|
||||
Now it's disconnected automatically
|
||||
* Enhancement: Improved the image pixel value display: favors selected image, and follows mouse instantly
|
||||
* Bugfix: When images are selected and images are turned off, the selection no longer persists
|
||||
* Bugfix: Using PCell parameter name 'name' won't break PCell's cell_name_impl default implementation
|
||||
* Enhancement: New API functions
|
||||
* Create a Layout as a deep copy of a cell (Layout#new)
|
||||
* PixelBuffer#to_bytes and PixelBuffer#from_bytes for zero-overhead RGBA pixel access
|
||||
|
||||
0.30.9 (2026-05-25):
|
||||
* Bugfix: %GITHUB%/issues/2331 Screen size error (Assertion tlPixelBuffer.cc:260 n < m_height was not true)
|
||||
* Enhancement: %GITHUB%/issues/2335 Editor Options (F3) behaviour has changed
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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*/);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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> ¶meters)
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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[] = {
|
||||
|
|
|
|||
|
|
@ -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)");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3383,6 +3383,7 @@ LayoutViewBase::reload_layout (unsigned int cv_index)
|
|||
}
|
||||
|
||||
set_properties (new_props);
|
||||
do_update_layer_sources ();
|
||||
|
||||
goto_view (state);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@ class _PCellDeclarationHelperMixin:
|
|||
"""
|
||||
default implementation
|
||||
"""
|
||||
return self.name()
|
||||
return super(_PCellDeclarationHelperMixin, self).name()
|
||||
|
||||
def coerce_parameters_impl(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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)
|
||||
|
||||
Binary file not shown.
|
|
@ -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)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in New Issue