mirror of https://github.com/KLayout/klayout.git
DRC binding for property computation, filtering
This commit is contained in:
parent
2193f28e2a
commit
c8568e8aed
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = {})
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue