This commit is contained in:
Matthias Koefferlein 2025-07-27 19:48:38 +02:00
parent 427ac0ae16
commit ede15ffcc0
8 changed files with 544 additions and 456 deletions

View File

@ -55,6 +55,8 @@ class EdgePairs;
class DB_PUBLIC EdgePairFilterBase
{
public:
typedef db::EdgePair shape_type;
EdgePairFilterBase () { }
virtual ~EdgePairFilterBase () { }

View File

@ -45,6 +45,8 @@ namespace db {
class DB_PUBLIC EdgeFilterBase
{
public:
typedef db::Edge shape_type;
/**
* @brief Constructor
*/

View File

@ -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<tl::Variant> &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<tl::Variant> m_names;
std::unordered_set<db::property_names_id_type> m_name_ids;
std::unordered_set<db::properties_id_type> m_pids;
std::set<db::cell_index_type> 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<tl::Variant> &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<tl::Variant> &args, const std::map<std::string, tl::Variant> * /*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<tl::Variant> &args, const std::map<std::string, tl::Variant> * /*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<tl::Variant> &args, const std::map<std::string, tl::Variant> * /*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<tl::Variant> &args, const std::map<std::string, tl::Variant> * /*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 ProcessorBase, class Container>
class property_computation_processor
: public gsi::shape_processor_base<ProcessorBase>
{
public:
typedef typename ProcessorBase::shape_type shape_type;
typedef typename ProcessorBase::result_type result_type;
property_computation_processor (Container *container, const std::map<tl::Variant, std::string> &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_type> &shape, std::vector<db::object_with_properties<shape_type> > &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<std::pair<db::property_names_id_type, tl::Expression> > m_expressions;
bool m_copy_properties;
};
}
// -------------------------------------------------------------------------------------
/**
* @brief Provides methods to handle measurement functions on various containers
*/
template <class Container, class ProcessorBase>
template <class Container, class ProcessorBase, class FilterBase>
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<Container, Container> split_if (const Container &container, const std::string &condition_expression);
std::pair<Container, Container> split_if (const Container *container, const std::string &condition_expression);
};
template <class Container, class ProcessorBase>
template <class Container, class ProcessorBase, class FilterBase>
Container
measure_methods<Container, ProcessorBase>::computed_properties (Container *container, const std::map<tl::Variant, std::string> &expressions, bool clear_properties)
measure_methods<Container, ProcessorBase, FilterBase>::computed_properties (Container *container, const std::map<tl::Variant, std::string> &expressions, bool clear_properties)
{
property_computation_processor<ProcessorBase, Container> proc (container, expressions, !clear_properties);
return container->processed (proc);
}
template <class Container, class ProcessorBase>
template <class Container, class ProcessorBase, class FilterBase>
void
measure_methods<Container, ProcessorBase>::compute_properties_in_place (Container *container, const std::map<tl::Variant, std::string> &expressions, bool clear_properties)
measure_methods<Container, ProcessorBase, FilterBase>::compute_properties_in_place (Container *container, const std::map<tl::Variant, std::string> &expressions, bool clear_properties)
{
property_computation_processor<ProcessorBase, Container> proc (container, expressions, !clear_properties);
container->process (proc);
}
template <class Container, class ProcessorBase>
template <class Container, class ProcessorBase, class FilterBase>
Container
measure_methods<Container, ProcessorBase>::selected_if (const Container &container, const std::string &condition_expression, bool inverse)
measure_methods<Container, ProcessorBase, FilterBase>::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<FilterBase, Container> filter (condition_expression, inverse);
return container->filtered (filter);
}
template <class Container, class ProcessorBase>
template <class Container, class ProcessorBase, class FilterBase>
void
measure_methods<Container, ProcessorBase>::select_if (const Container &container, const std::string &condition_expression, bool inverse)
measure_methods<Container, ProcessorBase, FilterBase>::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<FilterBase, Container> filter (condition_expression, inverse);
container->filter (filter);
}
template <class Container, class ProcessorBase>
template <class Container, class ProcessorBase, class FilterBase>
std::pair<Container, Container>
measure_methods<Container, ProcessorBase>::split_if (const Container &container, const std::string &condition_expression)
measure_methods<Container, ProcessorBase, FilterBase>::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<FilterBase, Container> filter (condition_expression, false);
return container->split_filter (filter);
}
// explicit instantiations
template struct measure_methods<db::Region, db::shape_collection_processor<db::Polygon, db::Polygon> >;
template struct measure_methods<db::Edges, db::shape_collection_processor<db::Edge, db::Edge> >;
template struct measure_methods<db::EdgePairs, db::shape_collection_processor<db::EdgePair, db::EdgePair> >;
template struct measure_methods<db::Texts, db::shape_collection_processor<db::Text, db::Text> >;
template struct measure_methods<db::Region, db::shape_collection_processor<db::Polygon, db::Polygon>, db::AllMustMatchFilter>;
template struct measure_methods<db::Edges, db::shape_collection_processor<db::Edge, db::Edge>, db::AllEdgesMustMatchFilter>;
template struct measure_methods<db::EdgePairs, db::shape_collection_processor<db::EdgePair, db::EdgePair>, db::EdgePairFilterBase>;
template struct measure_methods<db::Texts, db::shape_collection_processor<db::Text, db::Text>, db::TextFilterBase>;
}

View File

@ -57,6 +57,8 @@ class Net;
class DB_PUBLIC PolygonFilterBase
{
public:
typedef db::Polygon shape_type;
/**
* @brief Constructor
*/

View File

@ -54,6 +54,8 @@ class Texts;
class DB_PUBLIC TextFilterBase
{
public:
typedef db::Text shape_type;
TextFilterBase () { }
virtual ~TextFilterBase () { }

View File

@ -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<tl::Variant> &args, const std::map<std::string, tl::Variant> * /*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<tl::Variant> &args, const std::map<std::string, tl::Variant> * /*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<tl::Variant> &args, const std::map<std::string, tl::Variant> * /*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<tl::Variant> &args, const std::map<std::string, tl::Variant> * /*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 ProcessorBase, class Container>
class property_computation_processor
: public gsi::shape_processor_base<ProcessorBase>
{
public:
typedef typename ProcessorBase::shape_type shape_type;
typedef typename ProcessorBase::result_type result_type;
property_computation_processor (const Container *container, const std::map<tl::Variant, std::string> &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_type> &shape, std::vector<db::object_with_properties<shape_type> > &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<std::pair<db::property_names_id_type, tl::Expression> > 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 FilterBase, class Container>
class expression_filter
: public gsi::shape_filter_impl<FilterBase>
{
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

View File

@ -41,6 +41,7 @@
#include "tlGlobPattern.h"
#include "gsiDeclDbContainerHelpers.h"
#include "gsiDeclDbMeasureHelpers.h"
#include <memory>
#include <vector>
@ -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<gsi::PolygonFilterBase, db::Region> (expression, inverse);
}
Class<gsi::PolygonFilterBase> 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<gsi::PolygonFilterBase> 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(<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"
"@/ul\n"
"\n"
"This feature has been introduced in version 0.30.3."
),
"@hide"
);
@ -248,6 +272,41 @@ Class<shape_processor_impl<db::PolygonProcessorBase> > decl_PolygonOperator ("db
"This class has been introduced in version 0.29.\n"
);
static
property_computation_processor<db::PolygonProcessorBase, db::Region> *
new_pcp (const db::Region *container, const std::map<tl::Variant, std::string> &expressions, bool copy_properties)
{
return new property_computation_processor<db::PolygonProcessorBase, db::Region> (container, expressions, copy_properties);
}
Class<property_computation_processor<db::PolygonProcessorBase, db::Region> > decl_PolygonPropertiesExpressions ("db", "PolygonPropertiesExpressions",
property_computation_processor<db::PolygonProcessorBase, db::Region>::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(<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"
"@/ul\n"
"\n"
"This class has been introduced in version 0.30.3.\n"
);
Class<shape_processor_impl<db::PolygonToEdgeProcessorBase> > decl_PolygonToEdgeProcessor ("db", "PolygonToEdgeOperator",
shape_processor_impl<db::PolygonToEdgeProcessorBase>::method_decls (true),
"@brief A generic polygon-to-edge operator\n"

View File

@ -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<ExpressionNode> &v);
void eval_atomic (ExpressionParserContext &context, std::unique_ptr<ExpressionNode> &v, int am);
void eval_suffix (ExpressionParserContext &context, std::unique_ptr<ExpressionNode> &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;
};