diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index f370532e0..0f09068b5 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -743,9 +743,17 @@ AsIfFlatEdges::filtered_pair (const EdgeFilterBase &filter) const for (EdgesIterator p (begin_merged ()); ! p.at_end (); ++p) { if (filter.selected (*p, p.prop_id ())) { - new_region_true->insert (*p); + if (p.prop_id () != 0) { + new_region_true->insert (db::EdgeWithProperties (*p, p.prop_id ())); + } else { + new_region_true->insert (*p); + } } else { - new_region_false->insert (*p); + if (p.prop_id () != 0) { + new_region_false->insert (db::EdgeWithProperties (*p, p.prop_id ())); + } else { + new_region_false->insert (*p); + } } } diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 8747324ff..56e5d608b 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -526,9 +526,17 @@ AsIfFlatRegion::filtered_pair (const PolygonFilterBase &filter) const for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { if (filter.selected (*p, p.prop_id ())) { - new_region_true->insert (*p); + if (p.prop_id () != 0) { + new_region_true->insert (db::PolygonWithProperties (*p, p.prop_id ())); + } else { + new_region_true->insert (*p); + } } else { - new_region_false->insert (*p); + if (p.prop_id () != 0) { + new_region_false->insert (db::PolygonWithProperties (*p, p.prop_id ())); + } else { + new_region_false->insert (*p); + } } } diff --git a/src/db/db/dbAsIfFlatTexts.cc b/src/db/db/dbAsIfFlatTexts.cc index d4fe8cad8..f8dd3e086 100644 --- a/src/db/db/dbAsIfFlatTexts.cc +++ b/src/db/db/dbAsIfFlatTexts.cc @@ -179,9 +179,17 @@ AsIfFlatTexts::filtered_pair (const TextFilterBase &filter) const for (TextsIterator p (begin ()); ! p.at_end (); ++p) { if (filter.selected (*p, p.prop_id ())) { - new_texts_true->insert (*p); + if (p.prop_id () != 0) { + new_texts_true->insert (db::TextWithProperties (*p, p.prop_id ())); + } else { + new_texts_true->insert (*p); + } } else { - new_texts_false->insert (*p); + if (p.prop_id () != 0) { + new_texts_false->insert (db::TextWithProperties (*p, p.prop_id ())); + } else { + new_texts_false->insert (*p); + } } } diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 856e889cc..6c26ae12f 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -2105,12 +2105,18 @@ LayoutToNetlist::measure_net (const db::Region &primary, const std::map &args, const std::map * /*kwargs*/) const + { + if (args.size () != 1) { + throw tl::EvalError (tl::to_string (tr ("'skip' function takes one argument (flag)")), context); + } + mp_eval->skip_func (args [0].to_bool ()); + } + +private: + MeasureEval *mp_eval; +}; + class ValueFunction : public tl::EvalFunction { @@ -144,7 +166,7 @@ private: // MeasureEval implementation MeasureEval::MeasureEval (double dbu, bool with_put) - : m_shape_type (None), m_prop_id (0), m_dbu (dbu), m_with_put (with_put) + : m_shape_type (None), m_prop_id (0), m_skip (false), m_dbu (dbu), m_with_put (with_put) { mp_shape.any = 0; } @@ -154,6 +176,7 @@ MeasureEval::init () { if (m_with_put) { define_function ("put", new PutFunction (this)); + define_function ("skip", new SkipFunction (this)); } define_function ("shape", new ShapeFunction (this)); @@ -161,14 +184,6 @@ MeasureEval::init () define_function ("values", new ValuesFunction (this)); } -void -MeasureEval::reset_shape () const -{ - m_shape_type = None; - mp_shape.any = 0; - m_prop_id = 0; -} - void MeasureEval::set_shape (const db::Polygon *poly) const { @@ -205,9 +220,10 @@ MeasureEval::set_shape (const db::Text *text) const } void -MeasureEval::set_prop_id (db::properties_id_type prop_id) const +MeasureEval::reset (db::properties_id_type prop_id) const { m_prop_id = prop_id; + m_skip = false; } void @@ -223,6 +239,12 @@ MeasureEval::resolve_name (const std::string &name, const tl::EvalFunction *&fun } } +void +MeasureEval::skip_func (bool f) const +{ + m_skip = f; +} + tl::Variant MeasureEval::shape_func () const { diff --git a/src/db/db/dbMeasureEval.h b/src/db/db/dbMeasureEval.h index aaf899138..0abbc0140 100644 --- a/src/db/db/dbMeasureEval.h +++ b/src/db/db/dbMeasureEval.h @@ -49,13 +49,17 @@ public: void init (); - void reset_shape () const; + void reset (db::properties_id_type prop_id) const; void set_shape (const db::Polygon *poly) const; void set_shape (const db::PolygonRef *poly) const; void set_shape (const db::Edge *edge) const; void set_shape (const db::EdgePair *edge_pair) const; void set_shape (const db::Text *text) const; - void set_prop_id (db::properties_id_type prop_id) const; + + bool skip () const + { + return m_skip; + } db::PropertiesSet &prop_set_out () const { @@ -67,6 +71,7 @@ protected: private: friend class ShapeFunction; + friend class SkipFunction; friend class ValueFunction; friend class ValuesFunction; friend class PropertyFunction; @@ -95,6 +100,7 @@ private: mutable ShapeType m_shape_type; mutable ShapeRef mp_shape; mutable db::properties_id_type m_prop_id; + mutable bool m_skip; mutable db::PropertiesSet m_prop_set_out; double m_dbu; bool m_with_put; @@ -104,6 +110,7 @@ private: tl::Variant value_func (const tl::Variant &name) const; tl::Variant values_func (const tl::Variant &name) const; void put_func (const tl::Variant &name, const tl::Variant &value) const; + void skip_func (bool f) const; }; /** diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index b5c6c4747..3c83fe30c 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -300,13 +300,14 @@ Class > "is attached to the output edge pairs as user properties with the given names.\n" "\n" "Alternatively, a single expression can be given. In that case, 'put' needs to be used to attach properties " - "to the output shape.\n" + "to the output shape. You can also use 'skip' to drop shapes in that case.\n" "\n" "The expression may use the following variables and functions:\n" "\n" "@ul\n" "@li @b shape @/b: The current shape (i.e. 'EdgePair' without DBU specified or 'DEdgePair' otherwise) @/li\n" "@li @b put(, ) @/b: Attaches the given value as a property with name 'name' to the output shape @/li\n" + "@li @b skip() @/b: If called with a 'true' value, the shape is dropped from the output @/li\n" "@li @b value() @/b: The value of the property with the given name (the first one if there are multiple properties with the same name) @/li\n" "@li @b values() @/b: All values of the properties with the given name (returns a list) @/li\n" "@li @b @/b: A shortcut for 'value()' ( is used as a symbol) @/li\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 9a98cfb89..109101272 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -305,13 +305,14 @@ Class > decl_Ed "is attached to the output edge pairs as user properties with the given names.\n" "\n" "Alternatively, a single expression can be given. In that case, 'put' needs to be used to attach properties " - "to the output shape.\n" + "to the output shape. You can also use 'skip' to drop shapes in that case.\n" "\n" "The expression may use the following variables and functions:\n" "\n" "@ul\n" "@li @b shape @/b: The current shape (i.e. 'Edge' without DBU specified or 'DEdge' otherwise) @/li\n" "@li @b put(, ) @/b: Attaches the given value as a property with name 'name' to the output shape @/li\n" + "@li @b skip() @/b: If called with a 'true' value, the shape is dropped from the output @/li\n" "@li @b value() @/b: The value of the property with the given name (the first one if there are multiple properties with the same name) @/li\n" "@li @b values() @/b: All values of the properties with the given name (returns a list) @/li\n" "@li @b @/b: A shortcut for 'value()' ( is used as a symbol) @/li\n" diff --git a/src/db/db/gsiDeclDbMeasureHelpers.h b/src/db/db/gsiDeclDbMeasureHelpers.h index d31445288..80bc65bdf 100644 --- a/src/db/db/gsiDeclDbMeasureHelpers.h +++ b/src/db/db/gsiDeclDbMeasureHelpers.h @@ -121,36 +121,44 @@ public: virtual void process (const db::object_with_properties &shape, std::vector > &res) const { - res.push_back (shape); + try { - m_eval.set_prop_id (shape.properties_id ()); - m_eval.set_shape (&shape); + m_eval.reset (shape.properties_id ()); + m_eval.set_shape (&shape); + + db::PropertiesSet &ps_out = m_eval.prop_set_out (); + if (m_copy_properties) { + ps_out = db::properties (shape.properties_id ()); + for (auto e = m_expressions.begin (); e != m_expressions.end (); ++e) { + if (e->first != db::property_names_id_type (0)) { + ps_out.erase (e->first); + } + } + } else { + ps_out.clear (); + } - db::PropertiesSet &ps_out = m_eval.prop_set_out (); - if (m_copy_properties) { - ps_out = db::properties (shape.properties_id ()); for (auto e = m_expressions.begin (); e != m_expressions.end (); ++e) { if (e->first != db::property_names_id_type (0)) { - ps_out.erase (e->first); + ps_out.insert (e->first, e->second.execute ()); } } - } else { - ps_out.clear (); - } - for (auto e = m_expressions.begin (); e != m_expressions.end (); ++e) { - if (e->first != db::property_names_id_type (0)) { - ps_out.insert (e->first, e->second.execute ()); + for (auto e = m_expressions.begin (); e != m_expressions.end (); ++e) { + if (e->first == db::property_names_id_type (0)) { + e->second.execute (); + } } - } - for (auto e = m_expressions.begin (); e != m_expressions.end (); ++e) { - if (e->first == db::property_names_id_type (0)) { - e->second.execute (); + if (! m_eval.skip ()) { + res.push_back (shape); + res.back ().properties_id (db::properties_id (ps_out)); } - } - res.back ().properties_id (db::properties_id (ps_out)); + } catch (tl::Exception &ex) { + tl::warn << ex.msg (); + res.clear (); + } } public: @@ -190,21 +198,35 @@ public: virtual bool selected (const shape_type &shape, db::properties_id_type prop_id) const { - m_eval.set_prop_id (prop_id); - m_eval.set_shape (&shape); + try { - bool res = m_expression.execute ().to_bool (); - return m_inverse ? !res : res; + m_eval.reset (prop_id); + m_eval.set_shape (&shape); + + bool res = m_expression.execute ().to_bool (); + return m_inverse ? !res : res; + + } catch (tl::Exception &ex) { + tl::warn << ex.msg (); + return false; + } } // only needed for PolygonFilterBase virtual bool selected (const db::PolygonRef &shape, db::properties_id_type prop_id) const { - m_eval.set_prop_id (prop_id); - m_eval.set_shape (&shape); + try { - bool res = m_expression.execute ().to_bool (); - return m_inverse ? !res : res; + m_eval.reset (prop_id); + m_eval.set_shape (&shape); + + bool res = m_expression.execute ().to_bool (); + return m_inverse ? !res : res; + + } catch (tl::Exception &ex) { + tl::warn << ex.msg (); + return false; + } } public: diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 496e6fd02..3e72eef94 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -320,13 +320,14 @@ Class > dec "is attached to the output edge pairs as user properties with the given names.\n" "\n" "Alternatively, a single expression can be given. In that case, 'put' needs to be used to attach properties " - "to the output shape.\n" + "to the output shape. You can also use 'skip' to drop shapes in that case.\n" "\n" "The expression may use the following variables and functions:\n" "\n" "@ul\n" "@li @b shape @/b: The current shape (i.e. 'Polygon' without DBU specified or 'DPolygon' otherwise) @/li\n" "@li @b put(, ) @/b: Attaches the given value as a property with name 'name' to the output shape @/li\n" + "@li @b skip() @/b: If called with a 'true' value, the shape is dropped from the output @/li\n" "@li @b value() @/b: The value of the property with the given name (the first one if there are multiple properties with the same name) @/li\n" "@li @b values() @/b: All values of the properties with the given name (returns a list) @/li\n" "@li @b @/b: A shortcut for 'value()' ( is used as a symbol) @/li\n" diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 8cb899b5a..476813cf7 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -299,13 +299,14 @@ Class > decl_Te "is attached to the output edge pairs as user properties with the given names.\n" "\n" "Alternatively, a single expression can be given. In that case, 'put' needs to be used to attach properties " - "to the output shape.\n" + "to the output shape. You can also use 'skip' to drop shapes in that case.\n" "\n" "The expression may use the following variables and functions:\n" "\n" "@ul\n" "@li @b shape @/b: The current shape (i.e. 'Text' without DBU specified or 'DText' otherwise) @/li\n" "@li @b put(, ) @/b: Attaches the given value as a property with name 'name' to the output shape @/li\n" + "@li @b skip() @/b: If called with a 'true' value, the shape is dropped from the output @/li\n" "@li @b value() @/b: The value of the property with the given name (the first one if there are multiple properties with the same name) @/li\n" "@li @b values() @/b: All values of the properties with the given name (returns a list) @/li\n" "@li @b @/b: A shortcut for 'value()' ( is used as a symbol) @/li\n" diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 600d1b053..40f770d77 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -453,6 +453,19 @@ module DRC DRCPropertyName::new(k) end end + + def aniso(expression) + DRCTransformationVariantHint::new(expression, :is_scale_invariant) + end + + def scales(expression) + DRCTransformationVariantHint::new(expression, :is_isotropic) + end + + def aniso_and_scales(expression) + DRCTransformationVariantHint::new(expression, nil) + end + # %DRC% # @brief Specifies "same properties" for operations supporting user properties constraints diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index 4f40794b0..acfbba82f 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -5176,7 +5176,7 @@ CODE # %DRC% # @name evaluate # @brief Evaluate expressions on the shapes of the layer - # @synopsis layer.evaluate(expression [, variables]) + # @synopsis layer.evaluate(expression [, variables [, keep_properties ]]) # # Evaluates the given expression on the shapes of the layer. # The expression needs to be written in the KLayout expressions @@ -5185,12 +5185,14 @@ CODE # The expressions can place properties on the shapes using the # "put" function. As input, the expressions will receive the # (merged) shapes in micrometer units (e.g. RBA::DPolygon type) - # by calling the "shape" function. + # by calling the "shape" function. You can also call 'skip' with + # a 'true' value to drop the shape from the output entirely. # # Available functions are: # # @ul - # @li "put(name, value)": creates a property with name 'name' and 'value' value @/li + # @li "put(name, value)": creates a property with the given name and value @/li + # @li "skip(flag)": if called with a 'true' value, the shape will be dropped from the output @/li # @li "shape": the current shape in micrometer units @/li # @li "value(name)": the value of a property with name 'name' or nil if the current shape does not have a property with this name @/li # @ul @@ -5202,50 +5204,106 @@ CODE # becomes available as variables in the expression. This eliminates the need # to build expression strings to pass variable values. # - # The following example computes the area of the shapes and puts them - # into a property 'AREA': + # If 'keep_properties' is true, the existing properties of the shape will be kept. + # Otherwise (the default), existing properties will be removed before adding new + # ones with 'put'. + # + # The expressions require a hint + # whether the they make use of anisotropic or scale-dependent properties. + # For example, the height of a box is an anisotropic property. If a check is made + # inside a rotated cell, the height transforms to a width and the check renders + # different results for the same cell if the cell is placed rotated and non-rotated. + # The solution is cell variant formation. + # + # Similarly, if a check is made against physical dimensions, the check will have + # different results for cells placed with different magnifications. Such a check + # is not scale-invariant. + # + # By default it is assumed that the expressions are isotropic and scale invariant. + # You can mark an expression as anisotropic and/or scale dependent using the following + # expression modifiers: # # @code - # layer.evalute("put('AREA', shape.area)") + # # isotropic and scale invariant + # layer.evaluate("put('holes', shape.holes)") + # + # # anisotropic, but scale invariant + # layer.evaluate(aniso("put('aspect_ratio', shape.bbox.height/shape.bbox.width)")) + # + # # isotropic, but not scale invariant + # layer.evaluate(scales("put('area', shape.area)")) + # + # # anisotropic and not scale invariant + # layer.evaluate(aniso_and_scales("put('width', shape.bbox.width)")) # @/code # + # If you forget to specify this hint, the expression will use the local + # shape properties and fail to correctly produce the results in the presence + # of deep mode and rotated or magnified cell instances. + # + # The following example computes the area of the shapes and puts them + # into a property 'area': + # + # @code + # layer.evaluate(scales("put('area', shape.area)")) + # @/code + # + # NOTE: GDS does not support properties with string names, so + # either save to OASIS or use integer numbers for the property names. + # # This version modifies the input layer. A version that returns # a new layer is \evaluated. # %DRC% # @name evaluated # @brief Evaluate expressions on the shapes of the layer and returns a new layer - # @synopsis layer.evaluated(expression [, variables]) + # @synopsis layer.evaluated(expression [, variables [, keep_properties]]) # # This method is the out-of-place version of \evaluate. - def _make_proc(expression, variables) + def _make_proc(expression, variables, keep_properties) - expression.is_a?(String) || raise("'expression' must be a string") + if expression.is_a?(String) + expression = DRCTransformationVariantHint::new(expression) + elsif expression.is_a?(DRCTransformationVariantHint) + expression.expression.is_a?(String) || raise("'expression' must be a string") + else + raise("'expression' must be a string or a string decorated with a transformation variant hint") + end variables.is_a?(Hash) || raise("'variables' must be a hash") if data.is_a?(RBA::Region) - RBA::PolygonPropertiesExpressions::new(data, expression, dbu: @engine.dbu, variables: variables) + pr = RBA::PolygonPropertiesExpressions::new(data, expression.expression, copy_properties: keep_properties, dbu: @engine.dbu, variables: variables) elsif data.is_a?(RBA::Edges) - RBA::EdgePropertiesExpressions::new(data, expression, dbu: @engine.dbu, variables: variables) + pr = RBA::EdgePropertiesExpressions::new(data, expression.expression, copy_properties: keep_properties, dbu: @engine.dbu, variables: variables) elsif data.is_a?(RBA::EdgePairs) - RBA::EdgePairPropertiesExpressions::new(data, expression, dbu: @engine.dbu, variables: variables) + pr = RBA::EdgePairPropertiesExpressions::new(data, expression.expression, copy_properties: keep_properties, dbu: @engine.dbu, variables: variables) elsif data.is_a?(RBA::Texts) - RBA::TextPropertiesExpressions::new(data, expression, dbu: @engine.dbu, variables: variables) + pr = RBA::TextPropertiesExpressions::new(data, expression.expression, copy_properties: keep_properties, dbu: @engine.dbu, variables: variables) else - nil + pr = nil end + pr && expression.apply(pr) + pr + end - def evaluate(expression, variables = {}) + def evaluate(expression, variables = {}, keep_properties = false) @engine._context("evaluate") do - pr = _make_proc(expression, variables) + pr = _make_proc(expression, variables, keep_properties) @engine._tcmd(self.data, 0, self.data.class, :process, pr) self end end + def evaluated(expression, variables = {}, keep_properties = false) + @engine._context("evaluated") do + pr = _make_proc(expression, variables, keep_properties) + DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :processed, pr)) + end + end + # %DRC% # @name select_if # @brief Selects shapes of a layer based on the evaluation of an expression @@ -5255,6 +5313,8 @@ CODE # If the evaluation gives a 'true' value, the shape is selected. Otherwise # it is discarded. # + # The expression is written in KLayout expression notation. + # # As input, the expressions will receive the # (merged) shapes in micrometer units (e.g. RBA::DPolygon type) # by calling the "shape" function. @@ -5269,6 +5329,39 @@ CODE # Properties with well-formed names (e.g. "VALUE") are available as # variables in the expressions as a shortcut. # + # The expressions require a hint + # whether the they make use of anisotropic or scale-dependent properties. + # For example, the height of a box is an anisotropic property. If a check is made + # inside a rotated cell, the height transforms to a width and the check renders + # different results for the same cell if the cell is placed rotated and non-rotated. + # The solution is cell variant formation. + # + # Similarly, if a check is made against physical dimensions, the check will have + # different results for cells placed with different magnifications. Such a check + # is not scale-invariant. + # + # By default it is assumed that the expressions are isotropic and scale invariant. + # You can mark an expression as anisotropic and/or scale dependent using the following + # expression modifiers: + # + # @code + # # isotropic and scale invariant + # layer.select_if("shape.holes > 0") + # + # # anisotropic, but scale invariant + # layer.select_if(aniso("shape.bbox.height/shape.bbox.width > 2")) + # + # # isotropic, but not scale invariant + # layer.select_if(scales("shape.area > 10.0")) + # + # # anisotropic and not scale invariant + # layer.select_if(aniso_and_scales("shape.bbox.width > 10.0")) + # @/code + # + # If you forget to specify this hint, the expression will use the local + # shape properties and fail to correctly produce the results in the presence + # of deep mode and rotated or magnified cell instances. + # # 'variables' can be a hash of arbitrary names and values. Each of these values # becomes available as variables in the expression. This eliminates the need # to build expression strings to pass variable values. @@ -5277,7 +5370,7 @@ CODE # less than 10 square micrometers: # # @code - # layer.select("shape.area < 10.0") + # layer.select(scales("shape.area < 10.0")) # @/code # # This version modifies the input layer. A version that returns @@ -5302,30 +5395,39 @@ CODE # less than 10 square micrometers and one with the shapes with a bigger area: # # @code - # (smaller, bigger) = layer.split_if("shape.area < 10.0") + # (smaller, bigger) = layer.split_if(scales("shape.area < 10.0")) # @/code def _make_filter(expression, variables) - expression.is_a?(String) || raise("'expression' must be a string") + if expression.is_a?(String) + expression = DRCTransformationVariantHint::new(expression) + elsif expression.is_a?(DRCTransformationVariantHint) + expression.expression.is_a?(String) || raise("'expression' must be a string") + else + raise("'expression' must be a string or a string decorated with a transformation variant hint") + end variables.is_a?(Hash) || raise("'variables' must be a hash") if data.is_a?(RBA::Region) - RBA::PolygonFilterBase::expression_filter(expression, variables: variables) + f = RBA::PolygonFilterBase::expression_filter(expression.expression, dbu: @engine.dbu, variables: variables) elsif data.is_a?(RBA::Edges) - RBA::EdgeFilterBase::expression_filter(expression, variables: variables) + f = RBA::EdgeFilterBase::expression_filter(expression.expression, dbu: @engine.dbu, variables: variables) elsif data.is_a?(RBA::EdgePairs) - RBA::EdgeFilterBase::expression_filter(expression, variables: variables) + f = RBA::EdgeFilterBase::expression_filter(expression.expression, dbu: @engine.dbu, variables: variables) elsif data.is_a?(RBA::Texts) - RBA::TextFilterBase::expression_filter(expression, variables: variables) + f = RBA::TextFilterBase::expression_filter(expression.expression, dbu: @engine.dbu, variables: variables) else - nil + f = nil end + f && expression.apply(f) + f + end def select_if(expression, variables = {}) - @engine._context("evaluate") do + @engine._context("select_if") do f = _make_filter(expression, variables) @engine._tcmd(self.data, 0, self.data.class, :filter, f) self @@ -5333,16 +5435,17 @@ CODE end def selected_if(expression, variables = {}) - @engine._context("evaluated") do + @engine._context("selected_if") do f = _make_filter(expression, variables) DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :filtered, f)) end end def split_if(expression, variables = {}) - @engine._context("evaluated") do + @engine._context("split_if") do f = _make_filter(expression, variables) - DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :split_filter, f)) + res = @engine._tcmd(self.data, 0, self.data.class, :split_filter, f) + [ DRCLayer::new(@engine, res[0]), DRCLayer::new(@engine, res[1]) ] end end diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 554723391..8c009b57c 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -833,6 +833,9 @@ module DRC # # antenna_errors = evaluate_nets(gate, { "MET" => metal }, expression, variables) # @/code + # + # NOTE: GDS does not support properties with string names, so + # either save to OASIS or use integer numbers for the property names. def evaluate_nets(primary, secondary, expression, variables = {}) diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index 710626f53..923ff803e 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -575,5 +575,20 @@ module DRC end end + class DRCTransformationVariantHint + def initialize(expression, mode = :is_isotropic_and_scale_invariant) + @expression = expression + @mode = mode + end + def expression + @expression + end + def apply(pr) + if @mode + pr.send(@mode) + end + end + end + end diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 0f698a1ff..cd25b1774 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -2024,3 +2024,13 @@ TEST(142d_evaluate_nets) run_test (_this, "142", true); } +TEST(143_evaluate_and_filter) +{ + run_test (_this, "143", false); +} + +TEST(143d_evaluate_and_filter) +{ + run_test (_this, "143", true); +} + diff --git a/testdata/drc/drcSimpleTests_143.drc b/testdata/drc/drcSimpleTests_143.drc new file mode 100644 index 000000000..df9a4f129 --- /dev/null +++ b/testdata/drc/drcSimpleTests_143.drc @@ -0,0 +1,51 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0, enable_props) +l2 = input(2, 0, enable_props) + +l1.output(1, 0) +l2.output(2, 0) + +l2.evaluated("put(2, to_f(value(17))*2.0)").output(10, 0) +l2.evaluated("put(2, to_f(value(17))*factor)", { "factor" => 3.0 }, true).output(11, 0) +l1.evaluated(aniso("put(2, shape.bbox.height/shape.bbox.width)")).output(12, 0) +l1.evaluated(scales("put(2, shape.area)")).output(13, 0) +l1.evaluated(aniso_and_scales("put(2, shape.bbox.width)")).output(14, 0) + +d = l2.dup +d.evaluate("put(2, to_f(value(17))*2.0)").output(20, 0) +d = l2.dup +d.evaluate("put(2, to_f(value(17))*factor)", { "factor" => 3.0 }, true).output(21, 0) +d = l1.dup +d.evaluate(aniso("put(2, shape.bbox.height/shape.bbox.width)")).output(22, 0) +d = l1.dup +d.evaluate(scales("put(2, shape.area)")).output(23, 0) +d = l1.dup +d.evaluate(aniso_and_scales("put(2, shape.bbox.width)")).output(24, 0) + +l2.selected_if("to_f(value(17))<3.0").output(30, 0) +l2.selected_if("to_f(value(17)) 3.0 }).output(31, 0) +l1.selected_if(aniso("shape.bbox.height/shape.bbox.width<1.0")).output(32, 0) +l1.selected_if(scales("shape.area<20.0")).output(33, 0) +l1.selected_if(aniso_and_scales("shape.bbox.width<=5")).output(34, 0) + +d = l2.dup +d.select_if("to_f(value(17))<3.0").output(40, 0) +d = l2.dup +d.select_if("to_f(value(17)) 3.0 }).output(41, 0) +d = l1.dup +d.select_if(aniso("shape.bbox.height/shape.bbox.width<1.0")).output(42, 0) +d = l1.dup +d.select_if(scales("shape.area<20.0")).output(43, 0) +d = l1.dup +d.select_if(aniso_and_scales("shape.bbox.width<=5")).output(44, 0) + +l2.split_if("to_f(value(17))<3.0")[0].output(50, 0) +l2.split_if("to_f(value(17))<3.0")[1].output(51, 0) + diff --git a/testdata/drc/drcSimpleTests_143.gds b/testdata/drc/drcSimpleTests_143.gds new file mode 100644 index 000000000..6f18d6afb Binary files /dev/null and b/testdata/drc/drcSimpleTests_143.gds differ diff --git a/testdata/drc/drcSimpleTests_au143.gds b/testdata/drc/drcSimpleTests_au143.gds new file mode 100644 index 000000000..7159c09c4 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au143.gds differ diff --git a/testdata/drc/drcSimpleTests_au143d.gds b/testdata/drc/drcSimpleTests_au143d.gds new file mode 100644 index 000000000..d8a824d25 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au143d.gds differ