DRC binding for property computation, filtering

This commit is contained in:
Matthias Koefferlein 2025-08-03 13:55:14 +02:00
parent 2193f28e2a
commit c8568e8aed
20 changed files with 362 additions and 82 deletions

View File

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

View File

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

View File

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

View File

@ -2105,12 +2105,18 @@ LayoutToNetlist::measure_net (const db::Region &primary, const std::map<std::str
continue;
}
eval.reset (*cid, *c);
compiled_expr.execute ();
try {
if (! eval.skip ()) {
db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ());
get_merged_shapes_of_net (*cid, *c, primary_layer, shapes, db::properties_id (eval.prop_set_out ()));
eval.reset (*cid, *c);
compiled_expr.execute ();
if (! eval.skip ()) {
db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ());
get_merged_shapes_of_net (*cid, *c, primary_layer, shapes, db::properties_id (eval.prop_set_out ()));
}
} catch (tl::Exception &ex) {
tl::warn << ex.msg ();
}
}

View File

@ -51,6 +51,28 @@ private:
MeasureEval *mp_eval;
};
class SkipFunction
: public tl::EvalFunction
{
public:
SkipFunction (MeasureEval *eval)
: mp_eval (eval)
{
// .. nothing yet ..
}
virtual void execute (const tl::ExpressionParserContext &context, tl::Variant & /*out*/, const std::vector<tl::Variant> &args, const std::map<std::string, tl::Variant> * /*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
{

View File

@ -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;
};
/**

View File

@ -300,13 +300,14 @@ Class<property_computation_processor<db::EdgePairProcessorBase, db::EdgePairs> >
"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(<name>, <value>) @/b: Attaches the given value as a property with name 'name' to the output shape @/li\n"
"@li @b skip(<flag>) @/b: If called with a 'true' value, the shape is dropped from the output @/li\n"
"@li @b value(<name>) @/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(<name>) @/b: All values of the properties with the given name (returns a list) @/li\n"
"@li @b <name> @/b: A shortcut for 'value(<name>)' (<name> is used as a symbol) @/li\n"

View File

@ -305,13 +305,14 @@ Class<property_computation_processor<db::EdgeProcessorBase, db::Edges> > 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(<name>, <value>) @/b: Attaches the given value as a property with name 'name' to the output shape @/li\n"
"@li @b skip(<flag>) @/b: If called with a 'true' value, the shape is dropped from the output @/li\n"
"@li @b value(<name>) @/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(<name>) @/b: All values of the properties with the given name (returns a list) @/li\n"
"@li @b <name> @/b: A shortcut for 'value(<name>)' (<name> is used as a symbol) @/li\n"

View File

@ -121,36 +121,44 @@ public:
virtual void process (const db::object_with_properties<shape_type> &shape, std::vector<db::object_with_properties<shape_type> > &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:

View File

@ -320,13 +320,14 @@ Class<property_computation_processor<db::PolygonProcessorBase, db::Region> > 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(<name>, <value>) @/b: Attaches the given value as a property with name 'name' to the output shape @/li\n"
"@li @b skip(<flag>) @/b: If called with a 'true' value, the shape is dropped from the output @/li\n"
"@li @b value(<name>) @/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(<name>) @/b: All values of the properties with the given name (returns a list) @/li\n"
"@li @b <name> @/b: A shortcut for 'value(<name>)' (<name> is used as a symbol) @/li\n"

View File

@ -299,13 +299,14 @@ Class<property_computation_processor<db::TextProcessorBase, db::Texts> > 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(<name>, <value>) @/b: Attaches the given value as a property with name 'name' to the output shape @/li\n"
"@li @b skip(<flag>) @/b: If called with a 'true' value, the shape is dropped from the output @/li\n"
"@li @b value(<name>) @/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(<name>) @/b: All values of the properties with the given name (returns a list) @/li\n"
"@li @b <name> @/b: A shortcut for 'value(<name>)' (<name> is used as a symbol) @/li\n"

View File

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

View File

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

View File

@ -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 = {})

View File

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

View File

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

51
testdata/drc/drcSimpleTests_143.drc vendored Normal file
View File

@ -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))<thr)", { "thr" => 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))<thr)", { "thr" => 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)

BIN
testdata/drc/drcSimpleTests_143.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au143.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au143d.gds vendored Normal file

Binary file not shown.