mirror of https://github.com/KLayout/klayout.git
WIP: some bug fixes, tests for deep mode - some refinement needed.
This commit is contained in:
parent
6c9d16c221
commit
fe1d520d0c
|
|
@ -57,9 +57,10 @@ DeepShapeCollectionDelegateBase::apply_property_translator (const db::Properties
|
|||
db::Shapes &shapes = c->shapes (m_deep_layer.layer ());
|
||||
|
||||
db::Shapes new_shapes (shapes.is_editable ());
|
||||
new_shapes.assign (shapes, pt);
|
||||
shapes.swap (new_shapes);
|
||||
|
||||
shapes.assign (new_shapes, pt);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -172,6 +172,32 @@ static db::Region complex_region (const db::RecursiveShapeIterator *iter)
|
|||
}
|
||||
}
|
||||
|
||||
static void enable_properties (db::RecursiveShapeIterator *c)
|
||||
{
|
||||
c->apply_property_translator (db::PropertiesTranslator::make_pass_all ());
|
||||
}
|
||||
|
||||
static void remove_properties (db::RecursiveShapeIterator *c)
|
||||
{
|
||||
c->apply_property_translator (db::PropertiesTranslator::make_remove_all ());
|
||||
}
|
||||
|
||||
static void filter_properties (db::RecursiveShapeIterator *c, const std::vector<tl::Variant> &keys)
|
||||
{
|
||||
if (c->layout ()) {
|
||||
std::set<tl::Variant> kf;
|
||||
kf.insert (keys.begin (), keys.end ());
|
||||
c->apply_property_translator (db::PropertiesTranslator::make_filter (const_cast<db::Layout *> (c->layout ())->properties_repository (), kf));
|
||||
}
|
||||
}
|
||||
|
||||
static void map_properties (db::RecursiveShapeIterator *c, const std::map<tl::Variant, tl::Variant> &map)
|
||||
{
|
||||
if (c->layout ()) {
|
||||
c->apply_property_translator (db::PropertiesTranslator::make_key_mapper (const_cast<db::Layout *> (c->layout ())->properties_repository (), map));
|
||||
}
|
||||
}
|
||||
|
||||
Class<db::RecursiveShapeIterator> decl_RecursiveShapeIterator ("db", "RecursiveShapeIterator",
|
||||
gsi::constructor ("new", &new_si1, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layer"),
|
||||
"@brief Creates a recursive, single-layer shape iterator.\n"
|
||||
|
|
@ -547,6 +573,20 @@ Class<db::RecursiveShapeIterator> decl_RecursiveShapeIterator ("db", "RecursiveS
|
|||
"\n"
|
||||
"This method has been introduced in version 0.25.3."
|
||||
) +
|
||||
gsi::method ("prop_id", &db::RecursiveShapeIterator::prop_id,
|
||||
"@brief Gets the effective properties ID\n"
|
||||
"The shape iterator supports property filtering and translation. This method will deliver "
|
||||
"the effective property ID after translation. The original property ID can be obtained from "
|
||||
"'shape.prop_id' and is not changed by installing filters or mappers.\n"
|
||||
"\n"
|
||||
"\\prop_id is evaluated by \\Region objects for example, when they are created "
|
||||
"from a shape iterator.\n"
|
||||
"\n"
|
||||
"See \\enable_properties, \\filter_properties, \\remove_properties and \\map_properties for "
|
||||
"details on this feature.\n"
|
||||
"\n"
|
||||
"This attribute has been introduced in version 0.28.4."
|
||||
) +
|
||||
gsi::method ("shape", &db::RecursiveShapeIterator::shape,
|
||||
"@brief Gets the current shape\n"
|
||||
"\n"
|
||||
|
|
@ -592,6 +632,51 @@ Class<db::RecursiveShapeIterator> decl_RecursiveShapeIterator ("db", "RecursiveS
|
|||
"@brief Comparison of iterators - inequality\n"
|
||||
"\n"
|
||||
"Two iterators are not equal if they do not point to the same shape.\n"
|
||||
) +
|
||||
gsi::method_ext ("enable_properties", &enable_properties,
|
||||
"@brief Enables properties for the given iterator.\n"
|
||||
"Afer enabling properties, \\prop_id will deliver the effective properties ID for the current shape. "
|
||||
"By default, properties are not enabled and \\prop_id will always return 0 (no properties attached). "
|
||||
"Alternatively you can apply \\filter_properties "
|
||||
"or \\map_properties to enable properties with a specific name key.\n"
|
||||
"\n"
|
||||
"Note that property filters/mappers are additive and act in addition (after) the currently installed filter.\n"
|
||||
"\n"
|
||||
"This feature has been introduced in version 0.28.4."
|
||||
) +
|
||||
gsi::method_ext ("remove_properties", &remove_properties,
|
||||
"@brief Removes properties for the given container.\n"
|
||||
"This will remove all properties and \\prop_id will deliver 0 always (no properties attached).\n"
|
||||
"Alternatively you can apply \\filter_properties "
|
||||
"or \\map_properties to enable properties with a specific name key.\n"
|
||||
"\n"
|
||||
"Note that property filters/mappers are additive and act in addition (after) the currently installed filter.\n"
|
||||
"So effectively after 'remove_properties' you cannot get them back.\n"
|
||||
"\n"
|
||||
"This feature has been introduced in version 0.28.4."
|
||||
) +
|
||||
gsi::method_ext ("filter_properties", &filter_properties, gsi::arg ("keys"),
|
||||
"@brief Filters properties by certain keys.\n"
|
||||
"Calling this method will reduce the properties to values with name keys from the 'keys' list.\n"
|
||||
"As a side effect, this method enables properties.\n"
|
||||
"As with \\enable_properties or \\remove_properties, this filter has an effect on the value returned "
|
||||
"by \\prop_id, not on the properties ID attached to the shape directly.\n"
|
||||
"\n"
|
||||
"Note that property filters/mappers are additive and act in addition (after) the currently installed filter.\n"
|
||||
"\n"
|
||||
"This feature has been introduced in version 0.28.4."
|
||||
) +
|
||||
gsi::method_ext ("map_properties", &map_properties, gsi::arg ("key_map"),
|
||||
"@brief Maps properties by name key.\n"
|
||||
"Calling this method will reduce the properties to values with name keys from the 'keys' hash and "
|
||||
"renames the properties. Property values with keys not listed in the key map will be removed.\n"
|
||||
"As a side effect, this method enables properties.\n"
|
||||
"As with \\enable_properties or \\remove_properties, this filter has an effect on the value returned "
|
||||
"by \\prop_id, not on the properties ID attached to the shape directly.\n"
|
||||
"\n"
|
||||
"Note that property filters/mappers are additive and act in addition (after) the currently installed filter.\n"
|
||||
"\n"
|
||||
"This feature has been introduced in version 0.28.4."
|
||||
),
|
||||
"@brief An iterator delivering shapes recursively\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -323,6 +323,30 @@ module DRC
|
|||
def negative
|
||||
DRCNegative::new
|
||||
end
|
||||
|
||||
def enable_props
|
||||
DRCPropertySelector::new(:enable_properties)
|
||||
end
|
||||
|
||||
def remove_props
|
||||
DRCPropertySelector::new(:remove_properties)
|
||||
end
|
||||
|
||||
def select_props(*keys)
|
||||
self._context("select_props") do
|
||||
keys.each do |k|
|
||||
k.is_a?(String) || k.is_a?(1.class) || raise("Key values need to be integers or strings (got '#{k.inspect}')")
|
||||
end
|
||||
DRCPropertySelector::new(:filter_properties, keys)
|
||||
end
|
||||
end
|
||||
|
||||
def map_props(hash)
|
||||
self._context("map_props") do
|
||||
hash.is_a?(Hash) || raise("Argument needs to be a hash (got '#{hash.inspect}')")
|
||||
DRCPropertySelector::new(:map_properties, hash)
|
||||
end
|
||||
end
|
||||
|
||||
def pattern(p)
|
||||
self._context("pattern") do
|
||||
|
|
@ -2806,7 +2830,7 @@ CODE
|
|||
end
|
||||
end
|
||||
|
||||
def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags, global_trans, cls)
|
||||
def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags, global_trans, prop_sel, cls)
|
||||
|
||||
if layers.empty? && ! @deep
|
||||
|
||||
|
|
@ -2819,6 +2843,7 @@ CODE
|
|||
else
|
||||
iter = RBA::RecursiveShapeIterator::new(layout, layout.cell(cell_index), layers)
|
||||
end
|
||||
prop_sel.each { |p| p.apply_to(iter) }
|
||||
iter.shape_flags = shape_flags
|
||||
iter.global_dtrans = global_trans
|
||||
|
||||
|
|
|
|||
|
|
@ -4783,37 +4783,35 @@ CODE
|
|||
|
||||
# %DRC%
|
||||
# @name select_props
|
||||
# @brief Enables or selects properties from original or property-annotated layers
|
||||
# @synopsis layer.select_props
|
||||
# @brief Enables or selects properties from a property-annotated layer
|
||||
# @synopsis layer.select_props(keys)
|
||||
#
|
||||
# This method will enable user properties or select specific property keys
|
||||
# from layers. It returns a new layer with properties enabled. The
|
||||
# This method will select specific property keys
|
||||
# from layers. It returns a new layer with the new properties. The
|
||||
# original layer is not modified.
|
||||
#
|
||||
# When used on original layers, this method will enable properties on input.
|
||||
# By default, properties are not read:
|
||||
#
|
||||
# @code
|
||||
# layer1 = input(1, 0)
|
||||
# layer1_with_props = input(1, 0).select_props
|
||||
# @/code
|
||||
#
|
||||
# You can specify the user property keys (names) to use. As user properties
|
||||
# in general are a set of key/value pairs and may carry multiple information
|
||||
# in general are a set of key/value pairs and may carry multiple values
|
||||
# under different keys, this feature can be handy to filter out a specific
|
||||
# aspect. To get only the values from key 1 (integer), use:
|
||||
#
|
||||
# @code
|
||||
# layer1_with_props = input(1, 0).select_props(1)
|
||||
# layer1 = input(1, 0, enable_properties)
|
||||
# layer1_filtered = layer1.select_props(1)
|
||||
# @/code
|
||||
#
|
||||
# To get the combined key 1 and 2 properties, use:
|
||||
#
|
||||
# @code
|
||||
# layer1_with_props = input(1, 0).select_props(1, 2)
|
||||
# layer1 = input(1, 0, enable_properties)
|
||||
# layer1_filtered = layer1.select_props(1, 2)
|
||||
# @/code
|
||||
#
|
||||
# Without any arguments, this method will remove all properties.
|
||||
# Note that you can directly filter or map properties on input
|
||||
# which is more efficient than first loading all and then selecting some
|
||||
# properties. See \DRCSource#input for details.
|
||||
#
|
||||
# \map_props is a way to change property keys and \remove_props
|
||||
# will entirely remove all user properties.
|
||||
|
||||
|
|
@ -4822,7 +4820,7 @@ CODE
|
|||
# @brief Selects properties with certain keys and allows key mapping
|
||||
# @synopsis layer.map_props({ key => key_new, .. })
|
||||
#
|
||||
# Similar to \select_props, this method will enable user properties
|
||||
# Similar to \select_props, this method will map or filter properties and
|
||||
# and take the values from certain keys. In addition, this method allows
|
||||
# mapping keys to new keys. Specify a hash argument with old to new keys.
|
||||
#
|
||||
|
|
@ -4835,7 +4833,8 @@ CODE
|
|||
# use:
|
||||
#
|
||||
# @code
|
||||
# layer1_with_props = input(1, 0).map_props({ 2 => 1 })
|
||||
# layer1 = input(1, 0, enable_properties)
|
||||
# layer1_mapped = layer1.map_props({ 2 => 1 })
|
||||
# @/code
|
||||
#
|
||||
# See also \select_props and \remove_props.
|
||||
|
|
|
|||
|
|
@ -374,6 +374,7 @@ CODE
|
|||
# @synopsis source.input(layer, datatype)
|
||||
# @synopsis source.input(layer_into)
|
||||
# @synopsis source.input(filter, ...)
|
||||
# @synopsis source.input(props_spec, ...)
|
||||
# Creates a layer with the shapes from the given layer of the source.
|
||||
# The layer can be specified by layer and optionally datatype, by a RBA::LayerInfo
|
||||
# object or by a sequence of filters.
|
||||
|
|
@ -410,12 +411,27 @@ CODE
|
|||
#
|
||||
# "input" without any arguments will create a new, empty original layer.
|
||||
#
|
||||
# If you want to use user properties - for example with properties constraints in DRC checks -
|
||||
# you need to enable properties on input:
|
||||
#
|
||||
# @code
|
||||
# input1_with_props = input(1, 0, enable_props)
|
||||
# @/code
|
||||
#
|
||||
# You can also filter or map property keys, similar to the functions available on
|
||||
# layers (\DRCLayer#map_props, \DRCLayer#select_props). For example to select
|
||||
# property values with key 17 (numerical) only, use:
|
||||
#
|
||||
# @code
|
||||
# input1_with_props = input(1, 0, select_props(17))
|
||||
# @/code
|
||||
#
|
||||
# Use the global version of "input" without a source object to address the default source.
|
||||
|
||||
def input(*args)
|
||||
@engine._context("input") do
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, RBA::Region))
|
||||
layers, prop_selectors = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, prop_selectors, RBA::Region))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -441,8 +457,8 @@ CODE
|
|||
|
||||
def labels(*args)
|
||||
@engine._context("labels") do
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, @global_trans, RBA::Texts))
|
||||
layers, prop_selectors = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, @global_trans, prop_selectors, RBA::Texts))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -467,8 +483,8 @@ CODE
|
|||
|
||||
def polygons(*args)
|
||||
@engine._context("polygons") do
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, @global_trans, RBA::Region))
|
||||
layers, prop_selectors = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, @global_trans, prop_selectors, RBA::Region))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -496,8 +512,8 @@ CODE
|
|||
|
||||
def edges(*args)
|
||||
@engine._context("edges") do
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, @global_trans, RBA::Edges))
|
||||
layers, prop_selectors = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, @global_trans, prop_selectors, RBA::Edges))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -525,8 +541,8 @@ CODE
|
|||
|
||||
def edge_pairs(*args)
|
||||
@engine._context("edge_pairs") do
|
||||
layers = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, @global_trans, RBA::EdgePairs))
|
||||
layers, prop_selectors = parse_input_layers(*args)
|
||||
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, @global_trans, prop_selectors, RBA::EdgePairs))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -581,7 +597,10 @@ CODE
|
|||
def parse_input_layers(*args)
|
||||
|
||||
layers = []
|
||||
|
||||
prop_selectors = args.select { |a| a.is_a?(DRCPropertySelector) }
|
||||
|
||||
args = args.select { |a| !a.is_a?(DRCPropertySelector) }
|
||||
|
||||
if args.size == 0
|
||||
|
||||
li = @layout.insert_layer(RBA::LayerInfo::new)
|
||||
|
|
@ -615,7 +634,7 @@ CODE
|
|||
|
||||
end
|
||||
|
||||
layers
|
||||
[ layers, prop_selectors ]
|
||||
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -158,6 +158,19 @@ module DRC
|
|||
def initialize
|
||||
end
|
||||
end
|
||||
|
||||
# Property selector for "input"
|
||||
class DRCPropertySelector
|
||||
attr_accessor :method
|
||||
attr_accessor :args
|
||||
def apply_to(iter)
|
||||
iter.send(self.method, *self.args)
|
||||
end
|
||||
def initialize(method, *args)
|
||||
self.method = method
|
||||
self.args = args
|
||||
end
|
||||
end
|
||||
|
||||
# A wrapper for a pair of limit values
|
||||
# This class is used to identify projection limits for DRC
|
||||
|
|
|
|||
|
|
@ -1401,3 +1401,8 @@ TEST(70_props)
|
|||
{
|
||||
run_test (_this, "70", false);
|
||||
}
|
||||
|
||||
TEST(70d_props)
|
||||
{
|
||||
run_test (_this, "70", true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,28 +8,37 @@ if $drc_test_deep
|
|||
deep
|
||||
end
|
||||
|
||||
l1 = input(1, 0)
|
||||
l2 = input(2, 0)
|
||||
l3 = input(3, 0)
|
||||
l4 = input(4, 0)
|
||||
# properties on input
|
||||
l1 = input(1, 0)
|
||||
l2 = input(2, 0)
|
||||
l3_wp = input(3, 0, enable_props)
|
||||
l3_wp1_input = input(3, 0, select_props(1))
|
||||
l3_wp2as1_input = input(3, 0, map_props({ 2 => 1 }))
|
||||
l3 = input(3, 0)
|
||||
l4_wp = input(4, 0, enable_props)
|
||||
l4 = input(4, 0)
|
||||
|
||||
# derived properties
|
||||
l3_wp1 = l3_wp.select_props(1)
|
||||
l3_wp2as1 = l3_wp.map_props({ 2 => 1 })
|
||||
l3_nowp = l3_wp.remove_props
|
||||
|
||||
# dump to output
|
||||
|
||||
l1.output(1, 0)
|
||||
l2.output(2, 0)
|
||||
l3.output(3, 0)
|
||||
l4.output(4, 0)
|
||||
|
||||
l3_wp = l3.select_props
|
||||
l3_wp1 = l3.select_props(1)
|
||||
l3_wp2as1 = l3.map_props({ 2 => 1 })
|
||||
l3_nowp = l3_wp.remove_props
|
||||
|
||||
l4_wp = l4.select_props
|
||||
|
||||
l3_wp.output(10, 0)
|
||||
l3_wp1.output(11, 0)
|
||||
l3_wp2as1.output(12, 0)
|
||||
l3_nowp.output(13, 0)
|
||||
l4_wp.output(14, 0)
|
||||
l3_wp1_input.output(14, 0)
|
||||
l3_wp2as1_input.output(15, 0)
|
||||
l4_wp.output(16, 0)
|
||||
|
||||
# booleans with properties constraints
|
||||
|
||||
l3_wp.and(l4_wp, props_eq).output(20, 0)
|
||||
l3_wp1.and(l4_wp, props_eq).output(21, 0)
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue