This commit is contained in:
Matthias Koefferlein 2025-07-27 17:43:01 +02:00
parent 1c1555f31c
commit 427ac0ae16
3 changed files with 604 additions and 83 deletions

View File

@ -26,20 +26,531 @@
#include "dbEdgePairs.h"
#include "dbTexts.h"
#include "tlExpression.h"
#include "gsiClassBase.h"
#include "gsiDeclDbContainerHelpers.h"
namespace db
{
// -------------------------------------------------------------------------------------
// Some utilities
namespace
{
template <class Container>
void compute_as_properties (Container *container, const std::map<tl::Variant, std::string> &expressions, bool clear_properties)
/**
* @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>
struct measure_methods
{
/**
* @brief Computes one or many properties from expressions
*
* This method will use the shapes from the "input" container and compute properties from them using the
* given expression from "expressions". This map specifies the name of the target property, the value
* specifies the expression to execute.
*
* The expressions can make use of the following variables and functions:
* * "shape": the shape which is currently seen
* * "<prop-name>": an existing property from the shape currently seen (or nil, if no such property is present).
* This is a shortcut, only for properties with string names that are compatible with variable names
* * "value(<name>)": the value of the property with the given name - if multiple properties with that
* name are present, one value is returned
* * "values(<name>)": a list of values for all properties with the given name
*
* Returns the new container with the computed properties attached.
*/
Container computed_properties (Container *input, const std::map<tl::Variant, std::string> &expressions, bool clear_properties);
/**
* @brief Computes one or many properties from expressions
*
* Like "computed_properties", this method computes properties, but attaches them to the existing shapes.
* As a side effect, the shapes may be merged if "merged_semantics" applies. If "clear_properties" is true,
* any existing properties will be removed. If not, the new properties are added to the existing ones.
*/
void compute_properties_in_place (Container *container, const std::map<tl::Variant, std::string> &expressions, bool clear_properties);
/**
* @brief Selects all shapes for which the condition expression renders true (or the inverse)
*
* The condition expression can use the features as described for "computed_properties".
* 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);
/**
* @brief In-place version of "selected_if"
*/
void select_if (const 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);
};
template <class Container, class ProcessorBase>
Container
measure_methods<Container, ProcessorBase>::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>
void
measure_methods<Container, ProcessorBase>::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>
Container
measure_methods<Container, ProcessorBase>::selected_if (const Container &container, const std::string &condition_expression, bool inverse)
{
// - collect property names
// - define functions for
// - define tl::Eval functions for property names (properties_id -> value)
// - define tl::Eval function for shape
// - compile expression
// - launch filter (Region::filtered)
}
template <class Container, class ProcessorBase>
void
measure_methods<Container, ProcessorBase>::select_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)
}
template <class Container, class ProcessorBase>
std::pair<Container, Container>
measure_methods<Container, ProcessorBase>::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)
}
// 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> >;
}

View File

@ -237,15 +237,22 @@ private:
// ---------------------------------------------------------------------------------
// Generic shape processor declarations
/**
* @brief A "declarative" base for shape processors
*
* This implementation provides configuration options to tailor the behavior
* of the processor base - specifically with respect to variants and various
* attributes.
*/
template <class ProcessorBase>
class shape_processor_impl
class shape_processor_base
: public ProcessorBase
{
public:
typedef typename ProcessorBase::shape_type shape_type;
typedef typename ProcessorBase::result_type result_type;
typedef typename ProcessorBase::shape_type shape_type;
typedef typename ProcessorBase::result_type result_type;
shape_processor_impl ()
shape_processor_base ()
{
mp_vars = &m_mag_and_orient;
m_wants_variants = true;
@ -319,79 +326,17 @@ public:
mp_vars = 0;
}
virtual void process (const shape_type &shape, std::vector<result_type> &res) const
{
res = do_process (shape);
}
virtual void process (const db::object_with_properties<shape_type> &shape, std::vector<db::object_with_properties<result_type> > &res) const
{
res = do_process (shape);
}
std::vector<result_type> issue_do_process (const shape_type &) const
{
return std::vector<result_type> ();
}
std::vector<db::object_with_properties<result_type> > issue_do_process_wp (const db::object_with_properties<shape_type> &) const
{
return std::vector<db::object_with_properties<result_type> > ();
}
std::vector<result_type> do_process (const shape_type &shape) const
{
if (f_process.can_issue ()) {
return f_process.issue<shape_processor_impl, std::vector<result_type>, const shape_type &> (&shape_processor_impl::issue_do_process, shape);
} else {
return issue_do_process (shape);
}
}
std::vector<db::object_with_properties<result_type> > do_process (const db::object_with_properties<shape_type> &shape) const
{
if (f_process_wp.can_issue ()) {
return f_process.issue<shape_processor_impl, std::vector<db::object_with_properties<result_type> >, const db::object_with_properties<shape_type> &> (&shape_processor_impl::issue_do_process_wp, shape);
} else if (f_process.can_issue ()) {
auto tmp_result = f_process.issue<shape_processor_impl, std::vector<result_type>, const shape_type &> (&shape_processor_impl::issue_do_process, shape);
std::vector<db::object_with_properties<result_type> > result;
for (auto i = tmp_result.begin (); i != tmp_result.end (); ++i) {
result.push_back (db::object_with_properties<result_type> (*i, shape.properties_id ()));
}
return result;
} else {
return issue_do_process_wp (shape);
}
}
gsi::Callback f_process;
gsi::Callback f_process_wp;
static gsi::Methods method_decls (bool with_merged_options)
{
gsi::Methods decls =
callback ("process", &shape_processor_impl::issue_do_process, &shape_processor_impl::f_process, gsi::arg ("shape"),
"@brief Processes a shape\n"
"This method is the actual payload. It needs to be reimplemented in a derived class.\n"
"If needs to process the input shape and deliver a list of output shapes.\n"
"The output list may be empty to entirely discard the input shape. It may also contain more than a single shape.\n"
"In that case, the number of total shapes may grow during application of the processor.\n"
) +
callback ("process_with_properties", &shape_processor_impl::issue_do_process_wp, &shape_processor_impl::f_process_wp, gsi::arg ("shape"),
"@brief Processes a shape with properties\n"
"In scenarios with shapes with properties, this method is called to process the shapes. If the method is not implemented, "
"the property-less 'process' method is called and the properties are copied from the input to the output.\n"
"\n"
"This flavor has been introduced in version 0.30."
);
gsi::Methods decls;
if (with_merged_options) {
decls +=
method ("requires_raw_input?", &shape_processor_impl::requires_raw_input,
method ("requires_raw_input?", &shape_processor_base::requires_raw_input,
"@brief Gets a value indicating whether the processor needs raw (unmerged) input\n"
"See \\requires_raw_input= for details.\n"
) +
method ("requires_raw_input=", &shape_processor_impl::set_requires_raw_input, gsi::arg ("flag"),
method ("requires_raw_input=", &shape_processor_base::set_requires_raw_input, gsi::arg ("flag"),
"@brief Sets a value indicating whether the processor needs raw (unmerged) input\n"
"This flag must be set before using this processor. It tells the processor implementation whether the "
"processor wants to have raw input (unmerged). The default value is 'false', meaning that\n"
@ -401,22 +346,22 @@ public:
"Also, raw input means that strange shapes such as dot-like edges, self-overlapping polygons, "
"empty or degenerated polygons are preserved."
) +
method ("result_is_merged?", &shape_processor_impl::result_is_merged,
method ("result_is_merged?", &shape_processor_base::result_is_merged,
"@brief Gets a value indicating whether the processor delivers merged output\n"
"See \\result_is_merged= for details.\n"
) +
method ("result_is_merged=", &shape_processor_impl::set_result_is_merged, gsi::arg ("flag"),
method ("result_is_merged=", &shape_processor_base::set_result_is_merged, gsi::arg ("flag"),
"@brief Sets a value indicating whether the processor delivers merged output\n"
"This flag must be set before using this processor. If the processor maintains the merged condition\n"
"by design (output is merged if input is), it is a good idea to set this predicate to 'true'.\n"
"This will avoid additional merge steps when the resulting collection is used in further operations\n"
"that need merged input\n."
) +
method ("result_must_not_be_merged?", &shape_processor_impl::result_must_not_be_merged,
method ("result_must_not_be_merged?", &shape_processor_base::result_must_not_be_merged,
"@brief Gets a value indicating whether the processor's output must not be merged\n"
"See \\result_must_not_be_merged= for details.\n"
) +
method ("result_must_not_be_merged=", &shape_processor_impl::set_result_must_not_be_merged, gsi::arg ("flag"),
method ("result_must_not_be_merged=", &shape_processor_base::set_result_must_not_be_merged, gsi::arg ("flag"),
"@brief Sets a value indicating whether the processor's output must not be merged\n"
"This flag must be set before using this processor. The processor can set this flag if it wants to\n"
"deliver shapes that must not be merged - e.g. point-like edges or strange or degenerated polygons.\n."
@ -424,11 +369,11 @@ public:
}
decls +=
method ("wants_variants?", &shape_processor_impl::wants_variants,
method ("wants_variants?", &shape_processor_base::wants_variants,
"@brief Gets a value indicating whether the filter prefers cell variants\n"
"See \\wants_variants= for details.\n"
) +
method ("wants_variants=", &shape_processor_impl::set_wants_variants, gsi::arg ("flag"),
method ("wants_variants=", &shape_processor_base::set_wants_variants, gsi::arg ("flag"),
"@brief Sets a value indicating whether the filter prefers cell variants\n"
"This flag must be set before using this filter for hierarchical applications (deep mode). "
"It tells the filter implementation whether cell variants should be created (true, the default) "
@ -440,7 +385,7 @@ public:
"need to be differentiated. Cell variant formation is one way, shape propagation the other way.\n"
"Typically, cell variant formation is less expensive, but the hierarchy will be modified."
) +
method ("is_isotropic", &shape_processor_impl::is_isotropic,
method ("is_isotropic", &shape_processor_base::is_isotropic,
"@brief Indicates that the filter has isotropic properties\n"
"Call this method before using the filter to indicate that the selection is independent of "
"the orientation of the shape. This helps the filter algorithm optimizing the filter run, specifically in "
@ -449,7 +394,7 @@ public:
"Examples for isotropic (polygon) processors are size or shrink operators. Size or shrink is not dependent "
"on orientation unless size or shrink needs to be different in x and y direction."
) +
method ("is_scale_invariant", &shape_processor_impl::is_scale_invariant,
method ("is_scale_invariant", &shape_processor_base::is_scale_invariant,
"@brief Indicates that the filter is scale invariant\n"
"Call this method before using the filter to indicate that the selection is independent of "
"the scale of the shape. This helps the filter algorithm optimizing the filter run, specifically in "
@ -458,7 +403,7 @@ public:
"An example for a scale invariant (polygon) processor is the rotation operator. Rotation is not depending on scale, "
"but on the original orientation as mirrored versions need to be rotated differently."
) +
method ("is_isotropic_and_scale_invariant", &shape_processor_impl::is_isotropic_and_scale_invariant,
method ("is_isotropic_and_scale_invariant", &shape_processor_base::is_isotropic_and_scale_invariant,
"@brief Indicates that the filter is isotropic and scale invariant\n"
"Call this method before using the filter to indicate that the selection is independent of "
"the scale and orientation of the shape. This helps the filter algorithm optimizing the filter run, specifically in "
@ -481,6 +426,71 @@ private:
bool m_result_is_merged;
bool m_result_must_not_be_merged;
// No copying
shape_processor_base &operator= (const shape_processor_base &);
shape_processor_base (const shape_processor_base &);
};
/**
* @brief The implementation class for the generic shape processor
*
* In addition to the services provided by "shape_processor_base",
* this class provides an overload slot for the "process" virtual method.
*/
template <class ProcessorBase>
class shape_processor_impl
: public shape_processor_base<ProcessorBase>
{
public:
typedef typename ProcessorBase::shape_type shape_type;
typedef typename ProcessorBase::result_type result_type;
shape_processor_impl ()
: shape_processor_base<ProcessorBase> ()
{
// .. nothing yet ..
}
virtual void process (const db::object_with_properties<shape_type> &shape, std::vector<db::object_with_properties<result_type> > &res) const
{
res = do_process (shape);
}
std::vector<db::object_with_properties<result_type> > issue_do_process (const db::object_with_properties<shape_type> &) const
{
return std::vector<db::object_with_properties<result_type> > ();
}
std::vector<db::object_with_properties<result_type> > do_process (const db::object_with_properties<shape_type> &shape) const
{
if (f_process.can_issue ()) {
return f_process.issue<shape_processor_impl, std::vector<db::object_with_properties<result_type> >, const db::object_with_properties<shape_type> &> (&shape_processor_impl::issue_do_process, shape);
} else {
return issue_do_process (shape);
}
}
gsi::Callback f_process;
static gsi::Methods method_decls (bool with_merged_options)
{
gsi::Methods decls =
callback ("process", &shape_processor_impl::issue_do_process, &shape_processor_impl::f_process, gsi::arg ("shape"),
"@brief Processes a shape\n"
"This method is the actual payload. It needs to be reimplemented in a derived class.\n"
"If needs to process the input shape and deliver a list of output shapes.\n"
"The output list may be empty to entirely discard the input shape. It may also contain more than a single shape.\n"
"In that case, the number of total shapes may grow during application of the processor.\n"
"\n"
"Since version 0.30.3, this method will always receive objects with properties and is also able to deliver them - "
"hence modify the properties.\n"
"'process_with_properties' is no longer supported."
);
return decls + shape_processor_base<ProcessorBase>::method_decls (with_merged_options);
}
private:
// No copying
shape_processor_impl &operator= (const shape_processor_impl &);
shape_processor_impl (const shape_processor_impl &);

View File

@ -162,7 +162,7 @@ public:
create_item_from_shape (mp_rdb, m_cell_stack.back ()->id (), mp_cat->id (), m_trans, shape, m_with_properties);
}
public:
private:
rdb::Category *mp_cat;
rdb::Database *mp_rdb;
std::vector<const rdb::Cell *> m_cell_stack;