From ede15ffcc00e562a26325e3ad814d1b7165c6104 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Jul 2025 19:48:38 +0200 Subject: [PATCH] WIP --- src/db/db/dbEdgePairs.h | 2 + src/db/db/dbEdgesDelegate.h | 2 + src/db/db/dbMeasure.cc | 480 ++-------------------------- src/db/db/dbRegionDelegate.h | 2 + src/db/db/dbTexts.h | 2 + src/db/db/gsiDeclDbMeasureHelpers.h | 436 +++++++++++++++++++++++++ src/db/db/gsiDeclDbRegion.cc | 59 ++++ src/tl/tl/tlExpression.h | 17 +- 8 files changed, 544 insertions(+), 456 deletions(-) create mode 100644 src/db/db/gsiDeclDbMeasureHelpers.h diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 0cffe8960..e72376d4d 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -55,6 +55,8 @@ class EdgePairs; class DB_PUBLIC EdgePairFilterBase { public: + typedef db::EdgePair shape_type; + EdgePairFilterBase () { } virtual ~EdgePairFilterBase () { } diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 91aea92ee..a1f39d671 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -45,6 +45,8 @@ namespace db { class DB_PUBLIC EdgeFilterBase { public: + typedef db::Edge shape_type; + /** * @brief Constructor */ diff --git a/src/db/db/dbMeasure.cc b/src/db/db/dbMeasure.cc index f5cd594f8..c1e0a4b13 100644 --- a/src/db/db/dbMeasure.cc +++ b/src/db/db/dbMeasure.cc @@ -20,434 +20,15 @@ */ -#include "dbMeasure.h" -#include "dbRegion.h" -#include "dbEdges.h" -#include "dbEdgePairs.h" -#include "dbTexts.h" -#include "tlExpression.h" -#include "gsiClassBase.h" -#include "gsiDeclDbContainerHelpers.h" +#include "gsiDeclDbMeasureHelpers.h" -namespace db +namespace gsi { -// ------------------------------------------------------------------------------------- -// Some utilities - -namespace -{ - -/** - * @brief A class collecting the properties names from the shapes delivered by a RecursiveShapeIterator - * - * This class implements the "RecursiveShapeReceiver" interface and will collect all property names - * present in the shapes delivered by a RecursiveShapeIterator. Use this class as a target for - * the RecursiveShapeIterator's "push" method. After this, "names" will give you a set with the - * property names found. - */ -class PropertyNamesCollector - : public db::RecursiveShapeReceiver -{ -public: - PropertyNamesCollector () - : m_names (), m_name_ids () - { - // .. nothing yet .. - } - - const std::set &names () const - { - return m_names; - } - - virtual void enter_cell (const db::RecursiveShapeIterator * /*iter*/, const db::Cell *cell, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) - { - m_cell_ids.insert (cell->cell_index ()); - } - - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator * /*iter*/, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) - { - if (m_cell_ids.find (inst.object ().cell_index ()) != m_cell_ids.end ()) { - return NI_skip; - } else { - return NI_single; - } - } - - virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) - { - auto pid = shape.prop_id (); - if (pid != 0 && m_pids.find (pid) == m_pids.end ()) { - m_pids.insert (pid); - const db::PropertiesSet &ps = db::properties (pid); - for (auto i = ps.begin (); i != ps.end (); ++i) { - if (m_name_ids.find (i->first) == m_name_ids.end ()) { - m_name_ids.insert (i->first); - m_names.insert (db::property_name (i->first)); - } - } - } - } - -private: - std::set m_names; - std::unordered_set m_name_ids; - std::unordered_set m_pids; - std::set m_cell_ids; -}; - -/** - * @brief An evaluation context for the expressions - * - * This class provides the methods, functions and variables for the expressions. - */ -class MeasureEval - : public tl::Eval -{ -public: - MeasureEval () - : m_shape_type (None), m_prop_id (0) - { - mp_shape.any = 0; - } - - void init (const std::set &names) - { - define_function ("shape", new ShapesFunction (this)); - define_function ("value", new ValueFunction (this)); - define_function ("values", new ValuesFunction (this)); - - for (auto n = names.begin (); n != names.end (); ++n) { - if (n->is_a_string ()) { - // TODO: should check, if the name is a word - define_function (n->to_string (), new PropertyFunction (this, n->to_string ())); - } - } - } - - void reset_shape () const - { - m_shape_type = None; - mp_shape.any = 0; - m_prop_id = 0; - } - - void set_shape (const db::Polygon *poly) const - { - m_shape_type = Polygon; - mp_shape.poly = poly; - } - - void set_shape (const db::PolygonRef *poly) const - { - m_shape_type = PolygonRef; - mp_shape.poly_ref = poly; - } - - void set_shape (const db::Edge *edge) const - { - m_shape_type = Edge; - mp_shape.edge = edge; - } - - void set_shape (const db::EdgePair *edge_pair) const - { - m_shape_type = EdgePair; - mp_shape.edge_pair = edge_pair; - } - - void set_shape (const db::Text *text) const - { - m_shape_type = Text; - mp_shape.text = text; - } - - void set_prop_id (db::properties_id_type prop_id) const - { - m_prop_id = prop_id; - } - -private: - class ShapesFunction - : public tl::EvalFunction - { - public: - ShapesFunction (MeasureEval *eval) - : mp_eval (eval) - { - // .. nothing yet .. - } - - virtual void execute (const tl::ExpressionParserContext &context, tl::Variant &out, const std::vector &args, const std::map * /*kwargs*/) const - { - if (args.size () != 0) { - throw tl::EvalError (tl::to_string (tr ("'shape' function does not take arguments")), context); - } - out = mp_eval->shape_func (); - } - - private: - MeasureEval *mp_eval; - }; - - class ValueFunction - : public tl::EvalFunction - { - public: - ValueFunction (MeasureEval *eval) - : mp_eval (eval) - { - // .. nothing yet .. - } - - virtual void execute (const tl::ExpressionParserContext &context, tl::Variant &out, const std::vector &args, const std::map * /*kwargs*/) const - { - if (args.size () != 1) { - throw tl::EvalError (tl::to_string (tr ("'value' function takes one argument")), context); - } - out = mp_eval->value_func (args [0]); - } - - private: - MeasureEval *mp_eval; - }; - - class ValuesFunction - : public tl::EvalFunction - { - public: - ValuesFunction (MeasureEval *eval) - : mp_eval (eval) - { - // .. nothing yet .. - } - - virtual void execute (const tl::ExpressionParserContext &context, tl::Variant &out, const std::vector &args, const std::map * /*kwargs*/) const - { - if (args.size () != 1) { - throw tl::EvalError (tl::to_string (tr ("'values' function takes one argument")), context); - } - out = mp_eval->values_func (args [0]); - } - - private: - MeasureEval *mp_eval; - }; - - class PropertyFunction - : public tl::EvalFunction - { - public: - PropertyFunction (MeasureEval *eval, const tl::Variant &name) - : mp_eval (eval), m_name (name) - { - // .. nothing yet .. - } - - virtual void execute (const tl::ExpressionParserContext &context, tl::Variant &out, const std::vector &args, const std::map * /*kwargs*/) const - { - if (args.size () != 0) { - throw tl::EvalError (tl::to_string (tr ("Property getter function does not take arguments")), context); - } - out = mp_eval->value_func (m_name); - } - - private: - MeasureEval *mp_eval; - tl::Variant m_name; - }; - - union ShapeRef - { - const db::Polygon *poly; - const db::PolygonRef *poly_ref; - const db::Edge *edge; - const db::EdgePair *edge_pair; - const db::Text *text; - void *any; - }; - - enum ShapeType - { - None, - Polygon, - PolygonRef, - Edge, - EdgePair, - Text - }; - - mutable ShapeType m_shape_type; - mutable ShapeRef mp_shape; - mutable db::properties_id_type m_prop_id; - - tl::Variant shape_func () const - { - switch (m_shape_type) - { - case None: - default: - return tl::Variant (); - case Polygon: - return tl::Variant (mp_shape.poly); - case PolygonRef: - return tl::Variant (mp_shape.poly_ref); - case Edge: - return tl::Variant (mp_shape.edge); - case EdgePair: - return tl::Variant (mp_shape.edge_pair); - case Text: - return tl::Variant (mp_shape.text); - } - } - - tl::Variant value_func (const tl::Variant &name) const - { - const db::PropertiesSet &ps = db::properties (m_prop_id); - for (auto i = ps.begin (); i != ps.end (); ++i) { - if (db::property_name (i->first) == name) { - return db::property_value (i->second); - } - } - - return tl::Variant (); - } - - tl::Variant values_func (const tl::Variant &name) const - { - tl::Variant res = tl::Variant::empty_list (); - - const db::PropertiesSet &ps = db::properties (m_prop_id); - for (auto i = ps.begin (); i != ps.end (); ++i) { - if (db::property_name (i->first) == name) { - res.push (db::property_value (i->second)); - } - } - - return res; - } -}; - -static db::RecursiveShapeIterator -begin_iter (db::Region *region) -{ - return region->merged_semantics () ? region->begin_merged_iter ().first : region->begin_iter ().first; -} - -static bool -is_merged (db::Region *region) -{ - return region->merged_semantics (); -} - -static db::RecursiveShapeIterator -begin_iter (db::Edges *edges) -{ - return edges->merged_semantics () ? edges->begin_merged_iter ().first : edges->begin_iter ().first; -} - -static bool -is_merged (db::Edges *edges) -{ - return edges->merged_semantics (); -} - -static db::RecursiveShapeIterator -begin_iter (db::EdgePairs *edge_pairs) -{ - return edge_pairs->begin_iter ().first; -} - -static bool -is_merged (db::EdgePairs *) -{ - return false; -} - -static db::RecursiveShapeIterator -begin_iter (db::Texts *texts) -{ - return texts->begin_iter ().first; -} - -static bool -is_merged (db::Texts *) -{ - return false; -} - -/** - * @brief A specialization of the shape processor - * - * This class provides the evaluation of the expressions in the context of - * a specific shape and shape properties. It allows creating properties with - * a computed value. - */ -template -class property_computation_processor - : public gsi::shape_processor_base -{ -public: - typedef typename ProcessorBase::shape_type shape_type; - typedef typename ProcessorBase::result_type result_type; - - property_computation_processor (Container *container, const std::map &expressions, bool copy_properties) - : m_eval (), m_copy_properties (copy_properties) - { - PropertyNamesCollector names_collector; - if (container) { - - db::RecursiveShapeIterator iter = begin_iter (container); - iter.push (&names_collector); - - this->set_result_is_merged (is_merged (container)); - - } - - m_eval.init (names_collector.names ()); - - // compile the expressions - for (auto e = expressions.begin (); e != expressions.end (); ++e) { - m_expressions.push_back (std::make_pair (db::property_names_id (e->first), tl::Expression ())); - tl::Extractor ex (e->second.c_str ()); - m_eval.parse (m_expressions.back ().second, ex); - } - } - - virtual void process (const db::object_with_properties &shape, std::vector > &res) const - { - res.push_back (shape); - - m_eval.set_prop_id (shape.properties_id ()); - m_eval.set_shape (&shape); - - db::PropertiesSet ps; - if (m_copy_properties) { - ps = db::properties (shape.properties_id ()); - } - - for (auto e = m_expressions.begin (); e != m_expressions.end (); ++e) { - ps.insert (e->first, e->second.execute ()); - } - - res.back ().properties_id (db::properties_id (ps)); - } - -public: - MeasureEval m_eval; - std::vector > m_expressions; - bool m_copy_properties; -}; - -} - -// ------------------------------------------------------------------------------------- - /** * @brief Provides methods to handle measurement functions on various containers */ -template +template struct measure_methods { /** @@ -485,72 +66,63 @@ struct measure_methods * If inverse is false, all shapes are selected for which the condition renders true. If * inverse is true, all shapes are selected for which the condition renders false. */ - Container selected_if (const Container &container, const std::string &condition_expression, bool inverse); + Container selected_if (const Container *container, const std::string &condition_expression, bool inverse); /** * @brief In-place version of "selected_if" */ - void select_if (const Container &container, const std::string &condition_expression, bool inverse); + void select_if (Container *container, const std::string &condition_expression, bool inverse); /** * @brief Splits the container into one for which is the condition is true and one with the other shapes */ - std::pair split_if (const Container &container, const std::string &condition_expression); + std::pair split_if (const Container *container, const std::string &condition_expression); }; -template +template Container -measure_methods::computed_properties (Container *container, const std::map &expressions, bool clear_properties) +measure_methods::computed_properties (Container *container, const std::map &expressions, bool clear_properties) { property_computation_processor proc (container, expressions, !clear_properties); return container->processed (proc); } -template +template void -measure_methods::compute_properties_in_place (Container *container, const std::map &expressions, bool clear_properties) +measure_methods::compute_properties_in_place (Container *container, const std::map &expressions, bool clear_properties) { property_computation_processor proc (container, expressions, !clear_properties); container->process (proc); } -template +template Container -measure_methods::selected_if (const Container &container, const std::string &condition_expression, bool inverse) +measure_methods::selected_if (const Container *container, const std::string &condition_expression, bool inverse) { - // - collect property names - // - define tl::Eval functions for property names (properties_id -> value) - // - define tl::Eval function for shape - // - compile expression - // - launch filter (Region::filtered) + expression_filter filter (condition_expression, inverse); + return container->filtered (filter); } -template +template void -measure_methods::select_if (const Container &container, const std::string &condition_expression, bool inverse) +measure_methods::select_if (Container *container, const std::string &condition_expression, bool inverse) { - // - collect property names - // - define tl::Eval functions for property names (properties_id -> value) - // - define tl::Eval function for shape - // - compile expression - // - launch filter (Region::filtered) + expression_filter filter (condition_expression, inverse); + container->filter (filter); } -template +template std::pair -measure_methods::split_if (const Container &container, const std::string &condition_expression) +measure_methods::split_if (const Container *container, const std::string &condition_expression) { - // - collect property names - // - define tl::Eval functions for property names (properties_id -> value) - // - define tl::Eval function for shape - // - compile expression - // - launch filter (Region::filtered) + expression_filter filter (condition_expression, false); + return container->split_filter (filter); } // explicit instantiations -template struct measure_methods >; -template struct measure_methods >; -template struct measure_methods >; -template struct measure_methods >; +template struct measure_methods, db::AllMustMatchFilter>; +template struct measure_methods, db::AllEdgesMustMatchFilter>; +template struct measure_methods, db::EdgePairFilterBase>; +template struct measure_methods, db::TextFilterBase>; } diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index 47c8802bb..0c5dd3aae 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -57,6 +57,8 @@ class Net; class DB_PUBLIC PolygonFilterBase { public: + typedef db::Polygon shape_type; + /** * @brief Constructor */ diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 51b05a5d6..b9f47a5fc 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -54,6 +54,8 @@ class Texts; class DB_PUBLIC TextFilterBase { public: + typedef db::Text shape_type; + TextFilterBase () { } virtual ~TextFilterBase () { } diff --git a/src/db/db/gsiDeclDbMeasureHelpers.h b/src/db/db/gsiDeclDbMeasureHelpers.h new file mode 100644 index 000000000..af300f0de --- /dev/null +++ b/src/db/db/gsiDeclDbMeasureHelpers.h @@ -0,0 +1,436 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_gsiDeclDbMeasureHelpers +#define HDR_gsiDeclDbMeasureHelpers + +#include "dbRegion.h" +#include "dbEdges.h" +#include "dbEdgePairs.h" +#include "dbTexts.h" +#include "dbRegionUtils.h" +#include "dbEdgesUtils.h" +#include "tlExpression.h" +#include "gsiClassBase.h" +#include "gsiDeclDbContainerHelpers.h" + +namespace gsi +{ + +// ------------------------------------------------------------------------------------- +// Some utilities + +/** + * @brief An evaluation context for the expressions + * + * This class provides the methods, functions and variables for the expressions. + */ +class MeasureEval + : public tl::Eval +{ +public: + MeasureEval () + : m_shape_type (None), m_prop_id (0) + { + mp_shape.any = 0; + } + + void init () + { + define_function ("shape", new ShapesFunction (this)); + define_function ("value", new ValueFunction (this)); + define_function ("values", new ValuesFunction (this)); + } + + void reset_shape () const + { + m_shape_type = None; + mp_shape.any = 0; + m_prop_id = 0; + } + + void set_shape (const db::Polygon *poly) const + { + m_shape_type = Polygon; + mp_shape.poly = poly; + } + + void set_shape (const db::PolygonRef *poly) const + { + m_shape_type = PolygonRef; + mp_shape.poly_ref = poly; + } + + void set_shape (const db::Edge *edge) const + { + m_shape_type = Edge; + mp_shape.edge = edge; + } + + void set_shape (const db::EdgePair *edge_pair) const + { + m_shape_type = EdgePair; + mp_shape.edge_pair = edge_pair; + } + + void set_shape (const db::Text *text) const + { + m_shape_type = Text; + mp_shape.text = text; + } + + void set_prop_id (db::properties_id_type prop_id) const + { + m_prop_id = prop_id; + } + +protected: + virtual void resolve_name (const std::string &name, const tl::EvalFunction *&function, const tl::Variant *&value, tl::Variant *&var) + { + tl::Eval::resolve_name (name, function, value, var); + + if (!function && !value && !var) { + // connect the name with a function getting the property value + tl::EvalFunction *f = new PropertyFunction (this, name); + define_function (name, f); + function = f; + } + } + +private: + class ShapesFunction + : public tl::EvalFunction + { + public: + ShapesFunction (MeasureEval *eval) + : mp_eval (eval) + { + // .. nothing yet .. + } + + virtual void execute (const tl::ExpressionParserContext &context, tl::Variant &out, const std::vector &args, const std::map * /*kwargs*/) const + { + if (args.size () != 0) { + throw tl::EvalError (tl::to_string (tr ("'shape' function does not take arguments")), context); + } + out = mp_eval->shape_func (); + } + + private: + MeasureEval *mp_eval; + }; + + class ValueFunction + : public tl::EvalFunction + { + public: + ValueFunction (MeasureEval *eval) + : mp_eval (eval) + { + // .. nothing yet .. + } + + virtual void execute (const tl::ExpressionParserContext &context, tl::Variant &out, const std::vector &args, const std::map * /*kwargs*/) const + { + if (args.size () != 1) { + throw tl::EvalError (tl::to_string (tr ("'value' function takes one argument")), context); + } + out = mp_eval->value_func (args [0]); + } + + private: + MeasureEval *mp_eval; + }; + + class ValuesFunction + : public tl::EvalFunction + { + public: + ValuesFunction (MeasureEval *eval) + : mp_eval (eval) + { + // .. nothing yet .. + } + + virtual void execute (const tl::ExpressionParserContext &context, tl::Variant &out, const std::vector &args, const std::map * /*kwargs*/) const + { + if (args.size () != 1) { + throw tl::EvalError (tl::to_string (tr ("'values' function takes one argument")), context); + } + out = mp_eval->values_func (args [0]); + } + + private: + MeasureEval *mp_eval; + }; + + class PropertyFunction + : public tl::EvalFunction + { + public: + PropertyFunction (MeasureEval *eval, const tl::Variant &name) + : mp_eval (eval), m_name (name) + { + // .. nothing yet .. + } + + virtual void execute (const tl::ExpressionParserContext &context, tl::Variant &out, const std::vector &args, const std::map * /*kwargs*/) const + { + if (args.size () != 0) { + throw tl::EvalError (tl::to_string (tr ("Property getter function does not take arguments")), context); + } + out = mp_eval->value_func (m_name); + } + + private: + MeasureEval *mp_eval; + tl::Variant m_name; + }; + + union ShapeRef + { + const db::Polygon *poly; + const db::PolygonRef *poly_ref; + const db::Edge *edge; + const db::EdgePair *edge_pair; + const db::Text *text; + void *any; + }; + + enum ShapeType + { + None, + Polygon, + PolygonRef, + Edge, + EdgePair, + Text + }; + + mutable ShapeType m_shape_type; + mutable ShapeRef mp_shape; + mutable db::properties_id_type m_prop_id; + + tl::Variant shape_func () const + { + switch (m_shape_type) + { + case None: + default: + return tl::Variant (); + case Polygon: + return tl::Variant (mp_shape.poly); + case PolygonRef: + return tl::Variant (mp_shape.poly_ref); + case Edge: + return tl::Variant (mp_shape.edge); + case EdgePair: + return tl::Variant (mp_shape.edge_pair); + case Text: + return tl::Variant (mp_shape.text); + } + } + + tl::Variant value_func (const tl::Variant &name) const + { + const db::PropertiesSet &ps = db::properties (m_prop_id); + for (auto i = ps.begin (); i != ps.end (); ++i) { + if (db::property_name (i->first) == name) { + return db::property_value (i->second); + } + } + + return tl::Variant (); + } + + tl::Variant values_func (const tl::Variant &name) const + { + tl::Variant res = tl::Variant::empty_list (); + + const db::PropertiesSet &ps = db::properties (m_prop_id); + for (auto i = ps.begin (); i != ps.end (); ++i) { + if (db::property_name (i->first) == name) { + res.push (db::property_value (i->second)); + } + } + + return res; + } +}; + +inline db::RecursiveShapeIterator +begin_iter (const db::Region *region) +{ + return region->merged_semantics () ? region->begin_merged_iter ().first : region->begin_iter ().first; +} + +inline bool +is_merged (const db::Region *region) +{ + return region->merged_semantics (); +} + +inline db::RecursiveShapeIterator +begin_iter (const db::Edges *edges) +{ + return edges->merged_semantics () ? edges->begin_merged_iter ().first : edges->begin_iter ().first; +} + +inline bool +is_merged (const db::Edges *edges) +{ + return edges->merged_semantics (); +} + +inline db::RecursiveShapeIterator +begin_iter (const db::EdgePairs *edge_pairs) +{ + return edge_pairs->begin_iter ().first; +} + +inline bool +is_merged (const db::EdgePairs *) +{ + return false; +} + +inline db::RecursiveShapeIterator +begin_iter (const db::Texts *texts) +{ + return texts->begin_iter ().first; +} + +inline bool +is_merged (const db::Texts *) +{ + return false; +} + +/** + * @brief A specialization of the shape processor + * + * This class provides the evaluation of the expressions in the context of + * a specific shape and shape properties. It allows creating properties with + * a computed value. + */ +template +class property_computation_processor + : public gsi::shape_processor_base +{ +public: + typedef typename ProcessorBase::shape_type shape_type; + typedef typename ProcessorBase::result_type result_type; + + property_computation_processor (const Container *container, const std::map &expressions, bool copy_properties) + : m_eval (), m_copy_properties (copy_properties) + { + if (container) { + this->set_result_is_merged (is_merged (container)); + } + + m_eval.init (); + + // compile the expressions + for (auto e = expressions.begin (); e != expressions.end (); ++e) { + m_expressions.push_back (std::make_pair (db::property_names_id (e->first), tl::Expression ())); + tl::Extractor ex (e->second.c_str ()); + m_eval.parse (m_expressions.back ().second, ex); + } + } + + virtual void process (const db::object_with_properties &shape, std::vector > &res) const + { + res.push_back (shape); + + m_eval.set_prop_id (shape.properties_id ()); + m_eval.set_shape (&shape); + + db::PropertiesSet ps; + if (m_copy_properties) { + ps = db::properties (shape.properties_id ()); + } + + for (auto e = m_expressions.begin (); e != m_expressions.end (); ++e) { + ps.insert (e->first, e->second.execute ()); + } + + res.back ().properties_id (db::properties_id (ps)); + } + +public: + MeasureEval m_eval; + std::vector > m_expressions; + bool m_copy_properties; +}; + +/** + * @brief A specialization of the shape processor + * + * This class provides the evaluation of the expressions in the context of + * a specific shape and shape properties. It allows creating properties with + * a computed value. + */ +template +class expression_filter + : public gsi::shape_filter_impl +{ +public: + typedef typename FilterBase::shape_type shape_type; + + expression_filter (const std::string &expression, bool inverse) + : m_eval (), m_inverse (inverse) + { + m_eval.init (); + + // compile the expression + tl::Extractor ex (expression.c_str ()); + m_eval.parse (m_expression, ex); + } + + 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); + + bool res = m_expression.execute ().to_bool (); + return m_inverse ? !res : res; + } + + // 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); + + bool res = m_expression.execute ().to_bool (); + return m_inverse ? !res : res; + } + +public: + MeasureEval m_eval; + tl::Expression m_expression; + bool m_inverse; +}; + +} + +#endif diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 53da87933..102bfdcdb 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -41,6 +41,7 @@ #include "tlGlobPattern.h" #include "gsiDeclDbContainerHelpers.h" +#include "gsiDeclDbMeasureHelpers.h" #include #include @@ -108,6 +109,11 @@ static gsi::PolygonFilterBase *make_pg (const tl::Variant &name, const std::stri return new PolygonPropertiesFilter (name, pattern, inverse); } +static gsi::PolygonFilterBase *make_pe (const std::string &expression, bool inverse) +{ + return new gsi::expression_filter (expression, inverse); +} + Class decl_PolygonFilterBase ("db", "PolygonFilterBase", gsi::PolygonFilterBase::method_decls (true) + gsi::constructor ("property_glob", &make_pg, gsi::arg ("name"), gsi::arg ("pattern"), gsi::arg ("inverse", false), gsi::arg ("case_sensitive", true), @@ -148,6 +154,24 @@ Class decl_PolygonFilterBase ("db", "PolygonFilterBase", "Apply this filter with \\Region#filtered. See \\property_glob for an example.\n" "\n" "This feature has been introduced in version 0.30." + ) + + gsi::constructor ("expression_filter", &make_pe, gsi::arg ("expression"), gsi::arg ("inverse", false), + "@brief Creates an expression-based filter\n" + "@param expression The expression to evaluate.\n" + "@param inverse If true, inverts the selection - i.e. all polygons without a property with the given name and value range are selected.\n" + "\n" + "Creates a filter that will evaluate the given expression on every shape and select the shape " + "when the expression renders a boolean true value. " + "The expression may use the following variables and functions:\n" + "\n" + "@ul\n" + "@li @b shape @/b: The current shape @/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" + "@/ul\n" + "\n" + "This feature has been introduced in version 0.30.3." ), "@hide" ); @@ -248,6 +272,41 @@ Class > decl_PolygonOperator ("db "This class has been introduced in version 0.29.\n" ); +static +property_computation_processor * +new_pcp (const db::Region *container, const std::map &expressions, bool copy_properties) +{ + return new property_computation_processor (container, expressions, copy_properties); +} + +Class > decl_PolygonPropertiesExpressions ("db", "PolygonPropertiesExpressions", + property_computation_processor::method_decls (true) + + gsi::constructor ("new", &new_pcp, gsi::arg ("region"), gsi::arg ("expressions"), gsi::arg ("copy_properties", false), + "@brief Creates a new properties expressions operator\n" + "\n" + "@param region The region, the processor will be used on. Can be nil, but if given, allows some optimization.\n" + "@param expressions A map of property names and expressions used to generate the values of the properties (see class description for details).\n" + "@param copy_properties If true, new properties will be added to existing ones.\n" + ), + "@brief An operator attaching computed properties to the polygons\n" + "\n" + "This operator will execute a number of expressions and attach the results as new properties. " + "The expression inputs can be taken either from the polygons themselves or from existing properties.\n" + "\n" + "A number of expressions can be supplied with a name. The expressions will be evaluated and the result " + "is attached to the output polygons as user properties with the given names.\n" + "The expression may use the following variables and functions:\n" + "\n" + "@ul\n" + "@li @b shape @/b: The current shape @/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" + "@/ul\n" + "\n" + "This class has been introduced in version 0.30.3.\n" +); + Class > decl_PolygonToEdgeProcessor ("db", "PolygonToEdgeOperator", shape_processor_impl::method_decls (true), "@brief A generic polygon-to-edge operator\n" diff --git a/src/tl/tl/tlExpression.h b/src/tl/tl/tlExpression.h index a7afeef2e..a1439a4f0 100644 --- a/src/tl/tl/tlExpression.h +++ b/src/tl/tl/tlExpression.h @@ -579,6 +579,21 @@ public: return mp_parent; } +protected: + /** + * @brief Resolves a name into a function, a constant value or a variable + * + * Can be overloaded to change the resolution scheme + */ + virtual void resolve_name (const std::string &name, const EvalFunction *&function, const tl::Variant *&value, tl::Variant *&var); + + /** + * @brief Resolves a name into a variable + * + * Can be overloaded to change the resolution scheme + */ + virtual void resolve_var_name (const std::string &name, tl::Variant *&value); + private: friend class Expression; @@ -601,8 +616,6 @@ private: void eval_unary (ExpressionParserContext &context, std::unique_ptr &v); void eval_atomic (ExpressionParserContext &context, std::unique_ptr &v, int am); void eval_suffix (ExpressionParserContext &context, std::unique_ptr &v); - void resolve_name (const std::string &name, const EvalFunction *&function, const tl::Variant *&value, tl::Variant *&var); - void resolve_var_name (const std::string &name, tl::Variant *&value); static Eval m_global; };