Merge pull request #1601 from KLayout/drc-procs-and-filters

Region/Edges/EdgePairs/Texts operators and filters
This commit is contained in:
Matthias Köfferlein 2024-03-09 18:10:37 +01:00 committed by GitHub
commit 230bacf725
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 2053 additions and 87 deletions

View File

@ -147,6 +147,24 @@ void AsIfFlatEdgePairs::invalidate_bbox ()
m_bbox_valid = false;
}
EdgePairsDelegate *
AsIfFlatEdgePairs::processed (const EdgePairProcessorBase &filter) const
{
std::unique_ptr<FlatEdgePairs> edge_pairs (new FlatEdgePairs ());
std::vector<db::EdgePair> res_edge_pairs;
for (EdgePairsIterator e = begin (); ! e.at_end (); ++e) {
res_edge_pairs.clear ();
filter.process (*e, res_edge_pairs);
for (std::vector<db::EdgePair>::const_iterator er = res_edge_pairs.begin (); er != res_edge_pairs.end (); ++er) {
edge_pairs->insert (*er);
}
}
return edge_pairs.release ();
}
RegionDelegate *
AsIfFlatEdgePairs::processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const
{

View File

@ -53,8 +53,14 @@ public:
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const;
virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const;
virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &filter) const;
virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &proc)
{
return processed (proc);
}
virtual EdgePairsDelegate *processed (const EdgePairProcessorBase &proc) const;
virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &proc) const;
virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &proc) const;
virtual EdgePairsDelegate *add_in_place (const EdgePairs &other)
{

View File

@ -164,6 +164,24 @@ AsIfFlatTexts::filtered (const TextFilterBase &filter) const
return new_texts.release ();
}
TextsDelegate *
AsIfFlatTexts::processed (const TextProcessorBase &filter) const
{
std::unique_ptr<FlatTexts> texts (new FlatTexts ());
std::vector<db::Text> res_texts;
for (TextsIterator e = begin (); ! e.at_end (); ++e) {
res_texts.clear ();
filter.process (*e, res_texts);
for (std::vector<db::Text>::const_iterator er = res_texts.begin (); er != res_texts.end (); ++er) {
texts->insert (*er);
}
}
return texts.release ();
}
RegionDelegate *
AsIfFlatTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) const
{

View File

@ -55,6 +55,12 @@ public:
virtual TextsDelegate *filtered (const TextFilterBase &) const;
virtual TextsDelegate *process_in_place (const TextProcessorBase &proc)
{
return processed (proc);
}
virtual TextsDelegate *processed (const TextProcessorBase &proc) const;
virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &filter) const;
virtual TextsDelegate *add_in_place (const Texts &other)

View File

@ -451,6 +451,18 @@ DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const
return res.release ();
}
EdgePairsDelegate *DeepEdgePairs::process_in_place (const EdgePairProcessorBase &filter)
{
// TODO: implement to be really in-place
return processed (filter);
}
EdgePairsDelegate *
DeepEdgePairs::processed (const EdgePairProcessorBase &filter) const
{
return shape_collection_processed_impl<db::EdgePair, db::EdgePair, db::DeepEdgePairs> (deep_layer (), filter);
}
RegionDelegate *
DeepEdgePairs::processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const
{

View File

@ -78,6 +78,8 @@ public:
virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter);
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const;
virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &);
virtual EdgePairsDelegate *processed (const EdgePairProcessorBase &) const;
virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const;
virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &filter) const;

View File

@ -182,8 +182,6 @@ private:
std::pair<DeepLayer, DeepLayer> and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const;
DeepRegion *apply_filter (const PolygonFilterBase &filter) const;
template <class Result, class OutputContainer> OutputContainer *processed_impl (const polygon_processor<Result> &filter) const;
template <class Proc>
void configure_proc (Proc &proc) const
{

View File

@ -474,6 +474,18 @@ DeepTexts *DeepTexts::apply_filter (const TextFilterBase &filter) const
return res.release ();
}
TextsDelegate *DeepTexts::process_in_place (const TextProcessorBase &filter)
{
// TODO: implement to be really in-place
return processed (filter);
}
TextsDelegate *
DeepTexts::processed (const TextProcessorBase &filter) const
{
return shape_collection_processed_impl<db::Text, db::Text, db::DeepTexts> (deep_layer (), filter);
}
RegionDelegate *
DeepTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) const
{

View File

@ -80,6 +80,8 @@ public:
virtual TextsDelegate *filter_in_place (const TextFilterBase &filter);
virtual TextsDelegate *filtered (const TextFilterBase &) const;
virtual TextsDelegate *process_in_place (const TextProcessorBase &);
virtual TextsDelegate *processed (const TextProcessorBase &) const;
virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &filter) const;
virtual TextsDelegate *add_in_place (const Texts &other);

View File

@ -169,14 +169,19 @@ EdgePairs::properties_repository ()
return *r;
}
void EdgePairs::processed (Region &output, const EdgePairToPolygonProcessorBase &filter) const
EdgePairs EdgePairs::processed (const EdgePairProcessorBase &proc) const
{
output = Region (mp_delegate->processed_to_polygons (filter));
return EdgePairs (mp_delegate->processed (proc));
}
void EdgePairs::processed (Edges &output, const EdgePairToEdgeProcessorBase &filter) const
void EdgePairs::processed (Region &output, const EdgePairToPolygonProcessorBase &proc) const
{
output = Edges (mp_delegate->processed_to_edges (filter));
output = Region (mp_delegate->processed_to_polygons (proc));
}
void EdgePairs::processed (Edges &output, const EdgePairToEdgeProcessorBase &proc) const
{
output = Edges (mp_delegate->processed_to_edges (proc));
}
void EdgePairs::polygons (Region &output, db::Coord e) const

View File

@ -328,13 +328,31 @@ public:
return EdgePairs (mp_delegate->filtered (filter));
}
/**
* @brief Processes the edge pairs in-place
*
* This method will run the processor over all edge pairs and replace the collection by the results.
*/
EdgePairs &process (const EdgePairProcessorBase &proc)
{
set_delegate (mp_delegate->process_in_place (proc));
return *this;
}
/**
* @brief Processes the edge pairs
*
* This method will run the processor over all edge pairs return a new edge pair collection with the results.
*/
EdgePairs processed (const EdgePairProcessorBase &proc) const;
/**
* @brief Processes the edge pairs into polygons
*
* This method will run the processor over all edge pairs and return a region
* with the outputs of the processor.
*/
void processed (Region &output, const EdgePairToPolygonProcessorBase &filter) const;
void processed (Region &output, const EdgePairToPolygonProcessorBase &proc) const;
/**
* @brief Processes the edge pairs into edges
@ -342,7 +360,7 @@ public:
* This method will run the processor over all edge pairs and return a edge collection
* with the outputs of the processor.
*/
void processed (Edges &output, const EdgePairToEdgeProcessorBase &filter) const;
void processed (Edges &output, const EdgePairToEdgeProcessorBase &proc) const;
/**
* @brief Transforms the edge pair set

View File

@ -39,6 +39,7 @@ class RegionDelegate;
class EdgesDelegate;
class Layout;
typedef shape_collection_processor<db::EdgePair, db::EdgePair> EdgePairProcessorBase;
typedef shape_collection_processor<db::EdgePair, db::Polygon> EdgePairToPolygonProcessorBase;
typedef shape_collection_processor<db::EdgePair, db::Edge> EdgePairToEdgeProcessorBase;
@ -194,8 +195,10 @@ public:
virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter) = 0;
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &filter) const = 0;
virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const = 0;
virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &filter) const = 0;
virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &proc) = 0;
virtual EdgePairsDelegate *processed (const EdgePairProcessorBase &proc) const = 0;
virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &proc) const = 0;
virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &proc) const = 0;
virtual RegionDelegate *polygons (db::Coord e) const = 0;
virtual EdgesDelegate *edges () const = 0;

View File

@ -56,6 +56,8 @@ public:
virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &) { return this; }
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const { return new EmptyEdgePairs (); }
virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &) { return this; }
virtual EdgePairsDelegate *processed (const EdgePairProcessorBase &) const { return new EmptyEdgePairs (); }
virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const;
virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &filter) const;

View File

@ -57,6 +57,8 @@ public:
virtual TextsDelegate *filter_in_place (const TextFilterBase &) { return this; }
virtual TextsDelegate *filtered (const TextFilterBase &) const { return new EmptyTexts (); }
virtual TextsDelegate *process_in_place (const TextProcessorBase &) { return this; }
virtual TextsDelegate *processed (const TextProcessorBase &) const { return new EmptyTexts (); }
virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &) const;
virtual RegionDelegate *polygons (db::Coord e) const;

View File

@ -106,62 +106,6 @@ public:
virtual bool wants_variants () const = 0;
};
/**
* @brief A template base class for polygon processors
*
* A polygon processor can turn a polygon into something else.
*/
template <class Result>
class DB_PUBLIC polygon_processor
{
public:
/**
* @brief Constructor
*/
polygon_processor () { }
/**
* @brief Destructor
*/
virtual ~polygon_processor () { }
/**
* @brief Performs the actual processing
* This method will take the input polygon from "polygon" and puts the results into "res".
* "res" can be empty - in this case, the polygon will be skipped.
*/
virtual void process (const db::Polygon &polygon, std::vector<Result> &res) const = 0;
/**
* @brief Returns the transformation reducer for building cell variants
* This method may return 0. In this case, not cell variants are built.
*/
virtual const TransformationReducer *vars () const = 0;
/**
* @brief Returns true, if the result of this operation can be regarded "merged" always.
*/
virtual bool result_is_merged () const = 0;
/**
* @brief Returns true, if the result of this operation must not be merged.
* This feature can be used, if the result represents "degenerated" objects such
* as point-like edges. These must not be merged. Otherwise they disappear.
*/
virtual bool result_must_not_be_merged () const = 0;
/**
* @brief Returns true, if the processor wants raw (not merged) input
*/
virtual bool requires_raw_input () const = 0;
/**
* @brief Returns true, if the processor wants to build variants
* If not true, the processor accepts shape propagation as variant resolution.
*/
virtual bool wants_variants () const = 0;
};
typedef shape_collection_processor<db::Polygon, db::Polygon> PolygonProcessorBase;
typedef shape_collection_processor<db::Polygon, db::Edge> PolygonToEdgeProcessorBase;
typedef shape_collection_processor<db::Polygon, db::EdgePair> PolygonToEdgePairProcessorBase;

View File

@ -48,6 +48,9 @@ class DB_PUBLIC_TEMPLATE shape_collection_processor
: public tl::Object
{
public:
typedef Shape shape_type;
typedef Result result_type;
/**
* @brief Constructor
*/

View File

@ -199,6 +199,11 @@ MutableTexts *Texts::mutable_texts ()
return texts;
}
Texts Texts::processed (const TextProcessorBase &proc) const
{
return Texts (mp_delegate->processed (proc));
}
void Texts::processed (Region &output, const TextToPolygonProcessorBase &filter) const
{
output = Region (mp_delegate->processed_to_polygons (filter));

View File

@ -316,7 +316,25 @@ public:
}
/**
* @brief Processes the edges into polygons
* @brief Processes the edge pairs in-place
*
* This method will run the processor over all texts and replace the collection by the results.
*/
Texts &process (const TextProcessorBase &proc)
{
set_delegate (mp_delegate->process_in_place (proc));
return *this;
}
/**
* @brief Processes the texts
*
* This method will run the processor over all texts and return a new text collection with the results.
*/
Texts processed (const TextProcessorBase &proc) const;
/**
* @brief Processes the texts into polygons
*
* This method will run the processor over all edges and return a region
* with the outputs of the processor.

View File

@ -40,6 +40,7 @@ class RegionDelegate;
class EdgesDelegate;
class Layout;
typedef shape_collection_processor<db::Text, db::Text> TextProcessorBase;
typedef shape_collection_processor<db::Text, db::Polygon> TextToPolygonProcessorBase;
typedef db::generic_shape_iterator_delegate_base <db::Text> TextsIteratorDelegate;
@ -94,7 +95,9 @@ public:
virtual TextsDelegate *filter_in_place (const TextFilterBase &filter) = 0;
virtual TextsDelegate *filtered (const TextFilterBase &filter) const = 0;
virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &filter) const = 0;
virtual TextsDelegate *process_in_place (const TextProcessorBase &proc) = 0;
virtual TextsDelegate *processed (const TextProcessorBase &proc) const = 0;
virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &proc) const = 0;
virtual RegionDelegate *polygons (db::Coord e) const = 0;
virtual EdgesDelegate *edges () const = 0;

View File

@ -25,6 +25,7 @@
#define HDR_gsiDeclDbContainerHelpers
#include "dbPropertiesRepository.h"
#include "dbCellVariants.h"
#include "tlVariant.h"
#include "gsiDecl.h"
@ -100,6 +101,361 @@ make_property_methods ()
);
}
// ---------------------------------------------------------------------------------
// Generic shape filter declarations
template <class FilterBase>
class shape_filter_impl
: public FilterBase
{
public:
shape_filter_impl ()
{
mp_vars = &m_mag_and_orient;
m_wants_variants = true;
m_requires_raw_input = false;
}
// overrides virtual method
virtual const db::TransformationReducer *vars () const
{
return mp_vars;
}
// maybe overrides virtual method
virtual bool requires_raw_input () const
{
return m_requires_raw_input;
}
void set_requires_raw_input (bool f)
{
m_requires_raw_input = f;
}
// overrides virtual method
virtual bool wants_variants () const
{
return m_wants_variants;
}
void set_wants_variants (bool f)
{
m_wants_variants = f;
}
void is_isotropic ()
{
mp_vars = &m_mag;
}
void is_scale_invariant ()
{
mp_vars = &m_orientation;
}
void is_isotropic_and_scale_invariant ()
{
mp_vars = 0;
}
static gsi::Methods method_decls (bool with_requires_raw_input)
{
gsi::Methods decls;
if (with_requires_raw_input) {
decls =
method ("requires_raw_input?", &shape_filter_impl::requires_raw_input,
"@brief Gets a value indicating whether the filter needs raw (unmerged) input\n"
"See \\requires_raw_input= for details.\n"
) +
method ("requires_raw_input=", &shape_filter_impl::set_requires_raw_input, gsi::arg ("flag"),
"@brief Sets a value indicating whether the filter needs raw (unmerged) input\n"
"This flag must be set before using this filter. It tells the filter implementation whether the "
"filter wants to have raw input (unmerged). The default value is 'false', meaning that\n"
"the filter will receive merged polygons ('merged semantics').\n"
"\n"
"Setting this value to false potentially saves some CPU time needed for merging the polygons.\n"
"Also, raw input means that strange shapes such as dot-like edges, self-overlapping polygons, "
"empty or degenerated polygons are preserved."
);
}
decls +=
method ("wants_variants?", &shape_filter_impl::wants_variants,
"@brief Gets a value indicating whether the filter prefers cell variants\n"
"See \\wants_variants= for details.\n"
) +
method ("wants_variants=", &shape_filter_impl::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) "
"or shape propagation will be applied (false).\n"
"\n"
"This decision needs to be made, if the filter indicates that it will deliver different results\n"
"for scaled or rotated versions of the shape (see \\is_isotropic and the other hints). If a cell\n"
"is present with different qualities - as seen from the top cell - the respective instances\n"
"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_filter_impl::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 "
"hierarchical mode.\n"
"\n"
"Examples for isotropic (polygon) filters are area or perimeter filters. The area or perimeter of a polygon "
"depends on the scale, but not on the orientation of the polygon."
) +
method ("is_scale_invariant", &shape_filter_impl::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 "
"hierarchical mode.\n"
"\n"
"An example for a scale invariant (polygon) filter is the bounding box aspect ratio (height/width) filter. "
"The definition of heigh and width depends on the orientation, but the ratio is independent on scale."
) +
method ("is_isotropic_and_scale_invariant", &shape_filter_impl::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 "
"hierarchical mode.\n"
"\n"
"An example for such a (polygon) filter is the square selector. Whether a polygon is a square or not does not depend on "
"the polygon's orientation nor scale."
);
return decls;
}
private:
const db::TransformationReducer *mp_vars;
db::OrientationReducer m_orientation;
db::MagnificationReducer m_mag;
db::MagnificationAndOrientationReducer m_mag_and_orient;
bool m_requires_raw_input;
bool m_wants_variants;
};
// ---------------------------------------------------------------------------------
// Generic shape processor declarations
template <class ProcessorBase>
class shape_processor_impl
: public ProcessorBase
{
public:
typedef typename ProcessorBase::shape_type shape_type;
typedef typename ProcessorBase::result_type result_type;
shape_processor_impl ()
{
mp_vars = &m_mag_and_orient;
m_wants_variants = true;
m_requires_raw_input = false;
m_result_is_merged = false;
m_result_must_not_be_merged = false;
}
// overrides virtual method
virtual const db::TransformationReducer *vars () const
{
return mp_vars;
}
// maybe overrides virtual method
virtual bool requires_raw_input () const
{
return m_requires_raw_input;
}
void set_requires_raw_input (bool f)
{
m_requires_raw_input = f;
}
// overrides virtual method
virtual bool wants_variants () const
{
return m_wants_variants;
}
void set_wants_variants (bool f)
{
m_wants_variants = f;
}
// overrides virtual method
virtual bool result_is_merged () const
{
return m_result_is_merged;
}
void set_result_is_merged (bool f)
{
m_result_is_merged = f;
}
// overrides virtual method
virtual bool result_must_not_be_merged () const
{
return m_result_must_not_be_merged;
}
void set_result_must_not_be_merged (bool f)
{
m_result_must_not_be_merged = f;
}
void is_isotropic ()
{
mp_vars = &m_mag;
}
void is_scale_invariant ()
{
mp_vars = &m_orientation;
}
void is_isotropic_and_scale_invariant ()
{
mp_vars = 0;
}
virtual void process (const shape_type &shape, std::vector<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<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);
}
}
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"
);
if (with_merged_options) {
decls +=
method ("requires_raw_input?", &shape_processor_impl::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"),
"@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"
"the processor will receive merged polygons ('merged semantics').\n"
"\n"
"Setting this value to false potentially saves some CPU time needed for merging the polygons.\n"
"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,
"@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"),
"@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,
"@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"),
"@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."
);
}
decls +=
method ("wants_variants?", &shape_processor_impl::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"),
"@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) "
"or shape propagation will be applied (false).\n"
"\n"
"This decision needs to be made, if the filter indicates that it will deliver different results\n"
"for scaled or rotated versions of the shape (see \\is_isotropic and the other hints). If a cell\n"
"is present with different qualities - as seen from the top cell - the respective instances\n"
"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,
"@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 "
"hierarchical mode.\n"
"\n"
"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,
"@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 "
"hierarchical mode.\n"
"\n"
"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,
"@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 "
"hierarchical mode.\n"
"\n"
"An example for such a (polygon) processor is the convex decomposition operator. The decomposition of a polygon into "
"convex parts is an operation that is not depending on scale nor orientation."
);
return decls;
}
private:
const db::TransformationReducer *mp_vars;
db::OrientationReducer m_orientation;
db::MagnificationReducer m_mag;
db::MagnificationAndOrientationReducer m_mag_and_orient;
bool m_requires_raw_input;
bool m_wants_variants;
bool m_result_is_merged;
bool m_result_must_not_be_merged;
// No copying
shape_processor_impl &operator= (const shape_processor_impl &);
shape_processor_impl (const shape_processor_impl &);
};
}
#endif

View File

@ -36,7 +36,182 @@
namespace gsi
{
static db::EdgePairs *new_v ()
// ---------------------------------------------------------------------------------
// EdgePairFilter binding
class EdgePairFilterImpl
: public shape_filter_impl<db::EdgePairFilterBase>
{
public:
EdgePairFilterImpl () { }
bool issue_selected (const db::EdgePair &) const
{
return false;
}
virtual bool selected (const db::EdgePair &edge_pair) const
{
if (f_selected.can_issue ()) {
return f_selected.issue<EdgePairFilterImpl, bool, const db::EdgePair &> (&EdgePairFilterImpl::issue_selected, edge_pair);
} else {
return issue_selected (edge_pair);
}
}
gsi::Callback f_selected;
private:
// No copying
EdgePairFilterImpl &operator= (const EdgePairFilterImpl &);
EdgePairFilterImpl (const EdgePairFilterImpl &);
};
Class<gsi::EdgePairFilterImpl> decl_EdgePairFilterImpl ("db", "EdgePairFilter",
EdgePairFilterImpl::method_decls (false) +
callback ("selected", &EdgePairFilterImpl::issue_selected, &EdgePairFilterImpl::f_selected, gsi::arg ("text"),
"@brief Selects an edge pair\n"
"This method is the actual payload. It needs to be reimplemented in a derived class.\n"
"It needs to analyze the edge pair and return 'true' if it should be kept and 'false' if it should be discarded."
),
"@brief A generic edge pair filter adaptor\n"
"\n"
"EdgePair filters are an efficient way to filter edge pairs from a EdgePairs collection. To apply a filter, derive your own "
"filter class and pass an instance to \\EdgePairs#filter or \\EdgePairs#filtered method.\n"
"\n"
"Conceptually, these methods take each edge pair from the collection and present it to the filter's 'selected' method.\n"
"Based on the result of this evaluation, the edge pair is kept or discarded.\n"
"\n"
"The magic happens when deep mode edge pair collections are involved. In that case, the filter will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the filter behaves. You "
"need to configure the filter by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using the filter.\n"
"\n"
"You can skip this step, but the filter algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"Here is some example that filters edge pairs where the edges are perpendicular:"
"\n"
"@code\n"
"class PerpendicularEdgesFilter < RBA::EdgePairFilter\n"
"\n"
" # Constructor\n"
" def initialize\n"
" self.is_isotropic_and_scale_invariant # orientation and scale do not matter\n"
" end\n"
" \n"
" # Select edge pairs where the edges are perpendicular\n"
" def selected(edge_pair)\n"
" return edge_pair.first.d.sprod_sign(edge_pair.second.d) == 0\n"
" end\n"
"\n"
"end\n"
"\n"
"edge_pairs = ... # some EdgePairs object\n"
"perpendicular_only = edge_pairs.filtered(PerpendicularEdgesFilter::new)\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
// ---------------------------------------------------------------------------------
// EdgePairProcessor binding
Class<shape_processor_impl<db::EdgePairProcessorBase> > decl_EdgePairProcessor ("db", "EdgePairOperator",
shape_processor_impl<db::EdgePairProcessorBase>::method_decls (false),
"@brief A generic edge-pair operator\n"
"\n"
"Edge pair processors are an efficient way to process edge pairs from an edge pair collection. To apply a processor, derive your own "
"operator class and pass an instance to the \\EdgePairs#processed or \\EdgePairs#process method.\n"
"\n"
"Conceptually, these methods take each edge pair from the edge pair collection and present it to the operator's 'process' method.\n"
"The result of this call is a list of zero to many output edge pairs derived from the input edge pair.\n"
"The output edge pair collection is the sum over all these individual results.\n"
"\n"
"The magic happens when deep mode edge pair collections are involved. In that case, the processor will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using it.\n"
"\n"
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"Here is some example that flips the edge pairs (swaps first and second edge):"
"\n"
"@code\n"
"class FlipEdgePairs < RBA::EdgePairOperator\n"
"\n"
" # Constructor\n"
" def initialize\n"
" self.is_isotropic_and_scale_invariant # orientation and scale do not matter\n"
" end\n"
" \n"
" # Flips the edge pair\n"
" def process(edge_pair)\n"
" return [ RBA::EdgePair::new(edge_pair.second, edge_pair.first) ]\n"
" end\n"
"\n"
"end\n"
"\n"
"edge_pairs = ... # some EdgePairs object\n"
"flipped = edge_pairs.processed(FlipEdgePairs::new)\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
Class<shape_processor_impl<db::EdgePairToPolygonProcessorBase> > decl_EdgePairToPolygonProcessor ("db", "EdgePairToPolygonOperator",
shape_processor_impl<db::EdgePairToPolygonProcessorBase>::method_decls (false),
"@brief A generic edge-pair-to-polygon operator\n"
"\n"
"Edge pair processors are an efficient way to process edge pairs from an edge pair collection. To apply a processor, derive your own "
"operator class and pass an instance to the \\EdgePairs#processed method.\n"
"\n"
"Conceptually, these methods take each edge pair from the edge pair collection and present it to the operator's 'process' method.\n"
"The result of this call is a list of zero to many output polygons derived from the input edge pair.\n"
"The output region is the sum over all these individual results.\n"
"\n"
"The magic happens when deep mode edge pair collections are involved. In that case, the processor will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using it.\n"
"\n"
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"For a basic example see the \\EdgeToPolygonOperator class, with the exception that this incarnation receives edge pairs.\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
Class<shape_processor_impl<db::EdgePairToEdgeProcessorBase> > decl_EdgePairToEdgeProcessor ("db", "EdgePairToEdgeOperator",
shape_processor_impl<db::EdgePairToEdgeProcessorBase>::method_decls (false),
"@brief A generic edge-pair-to-edge operator\n"
"\n"
"Edge processors are an efficient way to process edge pairs from an edge pair collection. To apply a processor, derive your own "
"operator class and pass an instance to \\EdgePairs#processed method.\n"
"\n"
"Conceptually, these methods take each edge from the edge collection and present it to the operator's 'process' method.\n"
"The result of this call is a list of zero to many output edges derived from the input edge pair.\n"
"The output edge pair collection is the sum over all these individual results.\n"
"\n"
"The magic happens when deep mode edge pair collections are involved. In that case, the processor will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using it.\n"
"\n"
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"For a basic example see the \\EdgeToEdgePairOperator class, with the exception that this incarnation has to deliver edges and takes edge pairs.\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
// ---------------------------------------------------------------------------------
// EdgePairs binding
static db::EdgePairs *new_v ()
{
return new db::EdgePairs ();
}
@ -181,6 +356,40 @@ static size_t id (const db::EdgePairs *ep)
return tl::id_of (ep->delegate ());
}
static db::EdgePairs filtered (const db::EdgePairs *r, const EdgePairFilterImpl *f)
{
return r->filtered (*f);
}
static void filter (db::EdgePairs *r, const EdgePairFilterImpl *f)
{
r->filter (*f);
}
static db::EdgePairs processed_epep (const db::EdgePairs *r, const shape_processor_impl<db::EdgePairProcessorBase> *f)
{
return r->processed (*f);
}
static void process_epep (db::EdgePairs *r, const shape_processor_impl<db::EdgePairProcessorBase> *f)
{
r->process (*f);
}
static db::Edges processed_epe (const db::EdgePairs *r, const shape_processor_impl<db::EdgePairToEdgeProcessorBase> *f)
{
db::Edges out;
r->processed (out, *f);
return out;
}
static db::Region processed_epp (const db::EdgePairs *r, const shape_processor_impl<db::EdgePairToPolygonProcessorBase> *f)
{
db::Region out;
r->processed (out, *f);
return out;
}
static db::EdgePairs with_distance1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse)
{
db::EdgePairFilterByDistance ef (length, length + 1, inverse);
@ -619,6 +828,42 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"The boxes will not be merged, so it is possible to determine overlaps "
"of these boxes for example.\n"
) +
method_ext ("filter", &filter, gsi::arg ("filter"),
"@brief Applies a generic filter in place (replacing the edge pairs from the EdgePair collection)\n"
"See \\EdgePairFilter for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("filtered", &filtered, gsi::arg ("filtered"),
"@brief Applies a generic filter and returns a filtered copy\n"
"See \\EdgePairFilter for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("process", &process_epep, gsi::arg ("process"),
"@brief Applies a generic edge pair processor in place (replacing the edge pairs from the EdgePairs collection)\n"
"See \\EdgePairProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("processed", &processed_epep, gsi::arg ("processed"),
"@brief Applies a generic edge pair processor and returns a processed copy\n"
"See \\EdgePairProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("processed", &processed_epe, gsi::arg ("processed"),
"@brief Applies a generic edge-pair-to-edge processor and returns an edge collection with the results\n"
"See \\EdgePairToEdgeProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("processed", &processed_epp, gsi::arg ("processed"),
"@brief Applies a generic edge-pair-to-polygon processor and returns an Region with the results\n"
"See \\EdgePairToPolygonProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"),
"@brief Filters the edge pairs by length of one of their edges\n"
"Filters the edge pairs in the edge pair collection by length of at least one of their edges. If \"inverse\" is false, only "

View File

@ -37,6 +37,196 @@
namespace gsi
{
// ---------------------------------------------------------------------------------
// EdgeFilter binding
class EdgeFilterImpl
: public shape_filter_impl<db::EdgeFilterBase>
{
public:
EdgeFilterImpl () { }
bool issue_selected (const db::Edge &) const
{
return false;
}
virtual bool selected (const db::Edge &edge) const
{
if (f_selected.can_issue ()) {
return f_selected.issue<EdgeFilterImpl, bool, const db::Edge &> (&EdgeFilterImpl::issue_selected, edge);
} else {
return issue_selected (edge);
}
}
// Returns true if all edges match the criterion
virtual bool selected (const std::unordered_set<db::Edge> &edges) const
{
for (std::unordered_set<db::Edge>::const_iterator e = edges.begin (); e != edges.end (); ++e) {
if (! selected (*e)) {
return false;
}
}
return true;
}
gsi::Callback f_selected;
private:
// No copying
EdgeFilterImpl &operator= (const EdgeFilterImpl &);
EdgeFilterImpl (const EdgeFilterImpl &);
};
Class<gsi::EdgeFilterImpl> decl_EdgeFilterImpl ("db", "EdgeFilter",
EdgeFilterImpl::method_decls (true) +
callback ("selected", &EdgeFilterImpl::issue_selected, &EdgeFilterImpl::f_selected, gsi::arg ("edge"),
"@brief Selects an edge\n"
"This method is the actual payload. It needs to be reimplemented in a derived class.\n"
"It needs to analyze the edge and return 'true' if it should be kept and 'false' if it should be discarded."
),
"@brief A generic edge filter adaptor\n"
"\n"
"Edge filters are an efficient way to filter edge from a Edges collection. To apply a filter, derive your own "
"filter class and pass an instance to the \\Edges#filter or \\Edges#filtered method.\n"
"\n"
"Conceptually, these methods take each edge from the collection and present it to the filter's 'selected' method.\n"
"Based on the result of this evaluation, the edge is kept or discarded.\n"
"\n"
"The magic happens when deep mode edge collections are involved. In that case, the filter will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the filter behaves. You "
"need to configure the filter by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using the filter.\n"
"\n"
"You can skip this step, but the filter algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"Here is some example that filters edges parallel to a given one:"
"\n"
"@code\n"
"class ParallelFilter < RBA::EdgeFilter\n"
"\n"
" # Constructor\n"
" def initialize(ref_edge)\n"
" self.is_scale_invariant # orientation matters, but scale does not\n"
" @ref_edge = ref_edge\n"
" end\n"
" \n"
" # Select only parallel ones\n"
" def selected(edge)\n"
" return edge.is_parallel?(@ref_edge)\n"
" end\n"
"\n"
"end\n"
"\n"
"edges = ... # some Edges object\n"
"ref_edge = ... # some Edge\n"
"parallel_only = edges.filtered(ParallelFilter::new(ref_edge))\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
// ---------------------------------------------------------------------------------
// EdgeProcessor binding
Class<shape_processor_impl<db::EdgeProcessorBase> > decl_EdgeProcessorBase ("db", "EdgeOperator",
shape_processor_impl<db::EdgeProcessorBase>::method_decls (true),
"@brief A generic edge-to-polygon operator\n"
"\n"
"Edge processors are an efficient way to process edges from an edge collection. To apply a processor, derive your own "
"operator class and pass an instance to the \\Edges#processed method.\n"
"\n"
"Conceptually, these methods take each edge from the edge collection and present it to the operator's 'process' method.\n"
"The result of this call is a list of zero to many output edges derived from the input edge.\n"
"The output edge collection is the sum over all these individual results.\n"
"\n"
"The magic happens when deep mode edge collections are involved. In that case, the processor will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using it.\n"
"\n"
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"Here is some example that shrinks every edge to half of the size, but does not change the position.\n"
"In this example the 'position' is defined by the center of the edge:"
"\n"
"@code\n"
"class ShrinkToHalf < RBA::EdgeOperator\n"
"\n"
" # Constructor\n"
" def initialize\n"
" self.is_isotropic_and_scale_invariant # scale or orientation do not matter\n"
" end\n"
" \n"
" # Shrink to half size\n"
" def process(edge)\n"
" shift = edge.bbox.center - RBA::Point::new # shift vector\n"
" return [ (edge.moved(-shift) * 0.5).moved(shift) ]\n"
" end\n"
"\n"
"end\n"
"\n"
"edges = ... # some Edges collection\n"
"shrinked_to_half = edges.processed(ShrinkToHalf::new)\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
Class<shape_processor_impl<db::EdgeToPolygonProcessorBase> > decl_EdgeToPolygonProcessor ("db", "EdgeToPolygonOperator",
shape_processor_impl<db::EdgeToPolygonProcessorBase>::method_decls (true),
"@brief A generic edge-to-polygon operator\n"
"\n"
"Edge processors are an efficient way to process edges from an edge collection. To apply a processor, derive your own "
"operator class and pass an instance to the \\Edges#processed method.\n"
"\n"
"Conceptually, these methods take each edge from the edge collection and present it to the operator's 'process' method.\n"
"The result of this call is a list of zero to many output polygons derived from the input edge.\n"
"The output region is the sum over all these individual results.\n"
"\n"
"The magic happens when deep mode edge collections are involved. In that case, the processor will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using it.\n"
"\n"
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"For a basic example see the \\EdgeOperator class, with the exception that this incarnation has to deliver edges.\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
Class<shape_processor_impl<db::EdgeToEdgePairProcessorBase> > decl_EdgeToEdgePairProcessor ("db", "EdgeToEdgePairOperator",
shape_processor_impl<db::EdgeToEdgePairProcessorBase>::method_decls (true),
"@brief A generic edge-to-edge-pair operator\n"
"\n"
"Edge processors are an efficient way to process edges from an edge collection. To apply a processor, derive your own "
"operator class and pass an instance to the \\Edges#processed method.\n"
"\n"
"Conceptually, these methods take each edge from the edge collection and present it to the operator's 'process' method.\n"
"The result of this call is a list of zero to many output edge pairs derived from the input edge.\n"
"The output edge pair collection is the sum over all these individual results.\n"
"\n"
"The magic happens when deep mode edge collections are involved. In that case, the processor will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using it.\n"
"\n"
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"For a basic example see the \\EdgeOperator class, with the exception that this incarnation has to deliver edge pairs.\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
// ---------------------------------------------------------------------------------
// Edges binding
static inline std::vector<db::Edges> as_2edges_vector (const std::pair<db::Edges, db::Edges> &rp)
{
std::vector<db::Edges> res;
@ -204,6 +394,38 @@ static db::Edges moved_xy (const db::Edges *r, db::Coord x, db::Coord y)
return r->transformed (db::Disp (db::Vector (x, y)));
}
static db::Edges filtered (const db::Edges *r, const EdgeFilterImpl *f)
{
return r->filtered (*f);
}
static void filter (db::Edges *r, const EdgeFilterImpl *f)
{
r->filter (*f);
}
static db::Edges processed_ee (const db::Edges *r, const shape_processor_impl<db::EdgeProcessorBase> *f)
{
return r->processed (*f);
}
static void process_ee (db::Edges *r, const shape_processor_impl<db::EdgeProcessorBase> *f)
{
r->process (*f);
}
static db::EdgePairs processed_eep (const db::Edges *r, const shape_processor_impl<db::EdgeToEdgePairProcessorBase> *f)
{
return r->processed (*f);
}
static db::Region processed_ep (const db::Edges *r, const shape_processor_impl<db::EdgeToPolygonProcessorBase> *f)
{
db::Region out;
r->processed (out, *f);
return out;
}
static db::Edges with_length1 (const db::Edges *r, db::Edges::distance_type length, bool inverse)
{
db::EdgeLengthFilter f (length, length + 1, inverse);
@ -627,6 +849,42 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"\n"
"This method has been introduced in version 0.26."
) +
method_ext ("filter", &filter, gsi::arg ("filter"),
"@brief Applies a generic filter in place (replacing the edges from the Edges collection)\n"
"See \\EdgeFilter for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("filtered", &filtered, gsi::arg ("filtered"),
"@brief Applies a generic filter and returns a filtered copy\n"
"See \\EdgeFilter for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("process", &process_ee, gsi::arg ("process"),
"@brief Applies a generic edge processor in place (replacing the edges from the Edges collection)\n"
"See \\EdgeProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("processed", &processed_ee, gsi::arg ("processed"),
"@brief Applies a generic edge processor and returns a processed copy\n"
"See \\EdgeProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("processed", &processed_eep, gsi::arg ("processed"),
"@brief Applies a generic edge-to-edge-pair processor and returns an edge pair collection with the results\n"
"See \\EdgeToEdgePairProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("processed", &processed_ep, gsi::arg ("processed"),
"@brief Applies a generic edge-to-polygon processor and returns an edge collection with the results\n"
"See \\EdgeToPolygonProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"),
"@brief Filters the edges by length\n"
"Filters the edges in the edge collection by length. If \"inverse\" is false, only "

View File

@ -61,7 +61,7 @@ struct simple_polygon_defs
}
}
static point_type point (C *c, size_t p)
static point_type point (const C *c, size_t p)
{
if (c->hull ().size () > p) {
return c->hull ()[p];
@ -70,12 +70,12 @@ struct simple_polygon_defs
}
}
static size_t num_points (C *c)
static size_t num_points (const C *c)
{
return c->hull ().size ();
}
static bool is_empty (C *c)
static bool is_empty (const C *c)
{
return c->hull ().size () == 0;
}
@ -868,17 +868,17 @@ struct polygon_defs
}
}
static size_t num_points (C *c)
static size_t num_points (const C *c)
{
return c->vertices ();
}
static bool is_empty (C *c)
static bool is_empty (const C *c)
{
return c->vertices () == 0;
}
static point_type point_hull (C *c, size_t p)
static point_type point_hull (const C *c, size_t p)
{
if (c->hull ().size () > p) {
return c->hull ()[p];
@ -887,7 +887,7 @@ struct polygon_defs
}
}
static point_type point_hole (C *c, unsigned int n, size_t p)
static point_type point_hole (const C *c, unsigned int n, size_t p)
{
if (c->holes () > n && c->contour (n + 1).size () > p) {
return c->contour (n + 1)[p];
@ -896,12 +896,12 @@ struct polygon_defs
}
}
static size_t num_points_hull (C *c)
static size_t num_points_hull (const C *c)
{
return c->hull ().size ();
}
static size_t num_points_hole (C *c, unsigned int n)
static size_t num_points_hole (const C *c, unsigned int n)
{
return c->contour (n + 1).size ();
}

View File

@ -47,6 +47,190 @@
namespace gsi
{
// ---------------------------------------------------------------------------------
// PolygonFilter binding
class PolygonFilterImpl
: public shape_filter_impl<db::AllMustMatchFilter>
{
public:
PolygonFilterImpl () { }
bool issue_selected (const db::Polygon &) const
{
return false;
}
virtual bool selected (const db::Polygon &polygon) const
{
if (f_selected.can_issue ()) {
return f_selected.issue<PolygonFilterImpl, bool, const db::Polygon &> (&PolygonFilterImpl::issue_selected, polygon);
} else {
return issue_selected (polygon);
}
}
virtual bool selected (const db::PolygonRef &polygon) const
{
db::Polygon p;
polygon.instantiate (p);
return selected (p);
}
gsi::Callback f_selected;
private:
// No copying
PolygonFilterImpl &operator= (const PolygonFilterImpl &);
PolygonFilterImpl (const PolygonFilterImpl &);
};
Class<gsi::PolygonFilterImpl> decl_PolygonFilterImpl ("db", "PolygonFilter",
PolygonFilterImpl::method_decls (true) +
callback ("selected", &PolygonFilterImpl::issue_selected, &PolygonFilterImpl::f_selected, gsi::arg ("polygon"),
"@brief Selects a polygon\n"
"This method is the actual payload. It needs to be reimplemented in a derived class.\n"
"It needs to analyze the polygon and return 'true' if it should be kept and 'false' if it should be discarded."
),
"@brief A generic polygon filter adaptor\n"
"\n"
"Polygon filters are an efficient way to filter polygons from a Region. To apply a filter, derive your own "
"filter class and pass an instance to the \\Region#filter or \\Region#filtered method.\n"
"\n"
"Conceptually, these methods take each polygon from the region and present it to the filter's 'selected' method.\n"
"Based on the result of this evaluation, the polygon is kept or discarded.\n"
"\n"
"The magic happens when deep mode regions are involved. In that case, the filter will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the filter behaves. You "
"need to configure the filter by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using the filter.\n"
"\n"
"You can skip this step, but the filter algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"Here is some example that filters triangles:"
"\n"
"@code\n"
"class TriangleFilter < RBA::PolygonFilter\n"
"\n"
" # Constructor\n"
" def initialize\n"
" self.is_isotropic_and_scale_invariant # the triangle nature is not dependent on the scale or orientation\n"
" end\n"
" \n"
" # Select only triangles\n"
" def selected(polygon)\n"
" return polygon.holes == 0 && polygon.num_points == 3\n"
" end\n"
"\n"
"end\n"
"\n"
"region = ... # some Region\n"
"triangles_only = region.filtered(TriangleFilter::new)\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
// ---------------------------------------------------------------------------------
// PolygonProcessor binding
Class<shape_processor_impl<db::PolygonProcessorBase> > decl_PolygonOperator ("db", "PolygonOperator",
shape_processor_impl<db::PolygonProcessorBase>::method_decls (true),
"@brief A generic polygon operator\n"
"\n"
"Polygon processors are an efficient way to process polygons from a Region. To apply a processor, derive your own "
"operator class and pass an instance to the \\Region#process or \\Region#processed method.\n"
"\n"
"Conceptually, these methods take each polygon from the region and present it to the operators' 'process' method.\n"
"The result of this call is a list of zero to many output polygons derived from the input polygon.\n"
"The output region is the sum over all these individual results.\n"
"\n"
"The magic happens when deep mode regions are involved. In that case, the processor will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using it.\n"
"\n"
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"Here is some example that shrinks every polygon to half of the size but does not change the position.\n"
"In this example the 'position' is defined by the center of the bounding box:"
"\n"
"@code\n"
"class ShrinkToHalf < RBA::PolygonOperator\n"
"\n"
" # Constructor\n"
" def initialize\n"
" self.is_isotropic_and_scale_invariant # scale or orientation do not matter\n"
" end\n"
" \n"
" # Shrink to half size\n"
" def process(polygon)\n"
" shift = polygon.bbox.center - RBA::Point::new # shift vector\n"
" return [ (polygon.moved(-shift) * 0.5).moved(shift) ]\n"
" end\n"
"\n"
"end\n"
"\n"
"region = ... # some Region\n"
"shrinked_to_half = region.processed(ShrinkToHalf::new)\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.29.\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"
"\n"
"Polygon processors are an efficient way to process polygons from a Region. To apply a processor, derive your own "
"operator class and pass an instance to the \\Region#processed method.\n"
"\n"
"Conceptually, these methods take each polygon from the region and present it to the operator's 'process' method.\n"
"The result of this call is a list of zero to many output edges derived from the input polygon.\n"
"The output edge collection is the sum over all these individual results.\n"
"\n"
"The magic happens when deep mode regions are involved. In that case, the processor will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using it.\n"
"\n"
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"For a basic example see the \\PolygonOperator class, with the exception that this incarnation has to deliver edges.\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
Class<shape_processor_impl<db::PolygonToEdgePairProcessorBase> > decl_PolygonToEdgePairProcessor ("db", "PolygonToEdgePairOperator",
shape_processor_impl<db::PolygonToEdgePairProcessorBase>::method_decls (true),
"@brief A generic polygon-to-edge-pair operator\n"
"\n"
"Polygon processors are an efficient way to process polygons from a Region. To apply a processor, derive your own "
"operator class and pass an instance to the \\Region#processed method.\n"
"\n"
"Conceptually, these methods take each polygon from the region and present it to the operator's 'process' method.\n"
"The result of this call is a list of zero to many output edge pairs derived from the input polygon.\n"
"The output edge pair collection is the sum over all these individual results.\n"
"\n"
"The magic happens when deep mode regions are involved. In that case, the processor will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using it.\n"
"\n"
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"For a basic example see the \\PolygonOperator class, with the exception that this incarnation has to deliver edge pairs.\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
// ---------------------------------------------------------------------------------
// Region binding
static inline std::vector<db::Region> as_2region_vector (const std::pair<db::Region, db::Region> &rp)
{
std::vector<db::Region> res;
@ -318,6 +502,36 @@ static db::Edges extent_refs_edges (const db::Region *r, double fx1, double fy1,
return r->processed (db::RelativeExtentsAsEdges (fx1, fy1, fx2, fy2));
}
static db::Region filtered (const db::Region *r, const PolygonFilterImpl *f)
{
return r->filtered (*f);
}
static void filter (db::Region *r, const PolygonFilterImpl *f)
{
r->filter (*f);
}
static db::Region processed_pp (const db::Region *r, const shape_processor_impl<db::PolygonProcessorBase> *f)
{
return r->processed (*f);
}
static void process_pp (db::Region *r, const shape_processor_impl<db::PolygonProcessorBase> *f)
{
r->process (*f);
}
static db::EdgePairs processed_pep (const db::Region *r, const shape_processor_impl<db::PolygonToEdgePairProcessorBase> *f)
{
return r->processed (*f);
}
static db::Edges processed_pe (const db::Region *r, const shape_processor_impl<db::PolygonToEdgeProcessorBase> *f)
{
return r->processed (*f);
}
static db::Region with_perimeter1 (const db::Region *r, db::Region::perimeter_type perimeter, bool inverse)
{
db::RegionPerimeterFilter f (perimeter, perimeter + 1, inverse);
@ -2363,6 +2577,42 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"\n"
"This method has been introduced in version 0.28.\n"
) +
method_ext ("filter", &filter, gsi::arg ("filter"),
"@brief Applies a generic filter in place (replacing the polygons from the Region)\n"
"See \\PolygonFilter for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("filtered", &filtered, gsi::arg ("filtered"),
"@brief Applies a generic filter and returns a filtered copy\n"
"See \\PolygonFilter for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("process", &process_pp, gsi::arg ("process"),
"@brief Applies a generic polygon processor in place (replacing the polygons from the Region)\n"
"See \\PolygonProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("processed", &processed_pp, gsi::arg ("processed"),
"@brief Applies a generic polygon processor and returns a processed copy\n"
"See \\PolygonProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("processed", &processed_pep, gsi::arg ("processed"),
"@brief Applies a generic polygon-to-edge-pair processor and returns an edge pair collection with the results\n"
"See \\PolygonToEdgePairProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("processed", &processed_pe, gsi::arg ("processed"),
"@brief Applies a generic polygon-to-edge processor and returns an edge collection with the results\n"
"See \\PolygonToEdgeProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("rectangles", &rectangles,
"@brief Returns all polygons which are rectangles\n"
"This method returns all polygons in self which are rectangles."

View File

@ -98,17 +98,17 @@ struct text_defs
t->font (db::Font (f));
}
static int get_font (C *t)
static int get_font (const C *t)
{
return t->font ();
}
static point_type get_pos (C *t)
static point_type get_pos (const C *t)
{
return t->trans () * point_type ();
}
static box_type get_bbox (C *t)
static box_type get_bbox (const C *t)
{
point_type p = get_pos (t);
return box_type (p, p);
@ -124,7 +124,7 @@ struct text_defs
t->halign (db::HAlign (f));
}
static db::HAlign get_halign (C *t)
static db::HAlign get_halign (const C *t)
{
return t->halign ();
}
@ -139,12 +139,12 @@ struct text_defs
t->valign (db::VAlign (f));
}
static db::VAlign get_valign (C *t)
static db::VAlign get_valign (const C *t)
{
return t->valign ();
}
static C moved (C *c, const vector_type &p)
static C moved (const C *c, const vector_type &p)
{
return c->transformed (simple_trans_type (p));
}
@ -155,7 +155,7 @@ struct text_defs
return *c;
}
static C moved_xy (C *c, coord_type dx, coord_type dy)
static C moved_xy (const C *c, coord_type dx, coord_type dy)
{
return c->transformed (simple_trans_type (vector_type (dx, dy)));
}

View File

@ -33,6 +33,160 @@
namespace gsi
{
// ---------------------------------------------------------------------------------
// TextFilter binding
class TextFilterImpl
: public shape_filter_impl<db::TextFilterBase>
{
public:
TextFilterImpl () { }
bool issue_selected (const db::Text &) const
{
return false;
}
virtual bool selected (const db::Text &text) const
{
if (f_selected.can_issue ()) {
return f_selected.issue<TextFilterImpl, bool, const db::Text &> (&TextFilterImpl::issue_selected, text);
} else {
return issue_selected (text);
}
}
gsi::Callback f_selected;
private:
// No copying
TextFilterImpl &operator= (const TextFilterImpl &);
TextFilterImpl (const TextFilterImpl &);
};
Class<gsi::TextFilterImpl> decl_TextFilterImpl ("db", "TextFilter",
TextFilterImpl::method_decls (false) +
callback ("selected", &TextFilterImpl::issue_selected, &TextFilterImpl::f_selected, gsi::arg ("text"),
"@brief Selects a text\n"
"This method is the actual payload. It needs to be reimplemented in a derived class.\n"
"It needs to analyze the text and return 'true' if it should be kept and 'false' if it should be discarded."
),
"@brief A generic text filter adaptor\n"
"\n"
"Text filters are an efficient way to filter texts from a Texts collection. To apply a filter, derive your own "
"filter class and pass an instance to \\Texts#filter or \\Texts#filtered method.\n"
"\n"
"Conceptually, these methods take each text from the collection and present it to the filter's 'selected' method.\n"
"Based on the result of this evaluation, the text is kept or discarded.\n"
"\n"
"The magic happens when deep mode text collections are involved. In that case, the filter will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the filter behaves. You "
"need to configure the filter by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using the filter.\n"
"\n"
"You can skip this step, but the filter algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"Here is some example that filters texts with a given string length:"
"\n"
"@code\n"
"class TextStringLengthFilter < RBA::TextFilter\n"
"\n"
" # Constructor\n"
" def initialize(string_length)\n"
" self.is_isotropic_and_scale_invariant # orientation and scale do not matter\n"
" @string_length = string_length\n"
" end\n"
" \n"
" # Select texts with given string length\n"
" def selected(text)\n"
" return text.string.size == @string_length\n"
" end\n"
"\n"
"end\n"
"\n"
"texts = ... # some Texts object\n"
"with_length_3 = edges.filtered(TextStringLengthFilter::new(3))\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
// ---------------------------------------------------------------------------------
// TextProcessor binding
Class<shape_processor_impl<db::TextProcessorBase> > decl_TextProcessor ("db", "TextOperator",
shape_processor_impl<db::TextProcessorBase>::method_decls (false),
"@brief A generic text operator\n"
"\n"
"Text processors are an efficient way to process texts from an text collection. To apply a processor, derive your own "
"operator class and pass an instance to the \\Texts#processed or \\Texts#process method.\n"
"\n"
"Conceptually, these methods take each text from the edge pair collection and present it to the operator's 'process' method.\n"
"The result of this call is a list of zero to many output texts derived from the input text.\n"
"The output text collection is the sum over all these individual results.\n"
"\n"
"The magic happens when deep mode text collections are involved. In that case, the processor will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using it.\n"
"\n"
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"Here is some example that replaces the text string:"
"\n"
"@code\n"
"class ReplaceTextString < RBA::TextOperator\n"
"\n"
" # Constructor\n"
" def initialize\n"
" self.is_isotropic_and_scale_invariant # orientation and scale do not matter\n"
" end\n"
" \n"
" # Replaces the string by a number representing the string length\n"
" def process(text)\n"
" new_text = text.dup # need a copy as we cannot modify the text passed\n"
" new_text.string = text.string.size.to_s\n"
" return [ new_text ]\n"
" end\n"
"\n"
"end\n"
"\n"
"texts = ... # some Texts object\n"
"modified = texts.processed(ReplaceTextString::new)\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
Class<shape_processor_impl<db::TextToPolygonProcessorBase> > decl_TextToPolygonProcessor ("db", "TextToPolygonOperator",
shape_processor_impl<db::TextToPolygonProcessorBase>::method_decls (false),
"@brief A generic text-to-polygon operator\n"
"\n"
"Text processors are an efficient way to process texts from an text collection. To apply a processor, derive your own "
"operator class and pass an instance to the \\Texts#processed method.\n"
"\n"
"Conceptually, these methods take each text from the text collection and present it to the operator's 'process' method.\n"
"The result of this call is a list of zero to many output polygons derived from the input text.\n"
"The output region is the sum over all these individual results.\n"
"\n"
"The magic happens when deep mode text collections are involved. In that case, the processor will use as few calls as possible "
"and exploit the hierarchical compression if possible. It needs to know however, how the operator behaves. You "
"need to configure the operator by calling \\is_isotropic, \\is_scale_invariant or \\is_isotropic_and_scale_invariant "
"before using it.\n"
"\n"
"You can skip this step, but the processor algorithm will assume the worst case then. This usually leads to cell variant "
"formation which is not always desired and blows up the hierarchy.\n"
"\n"
"For a basic example see the \\TextOperator class, with the exception that this incarnation delivers polygons.\n"
"\n"
"This class has been introduced in version 0.29.\n"
);
// ---------------------------------------------------------------------------------
// Texts binding
static db::Texts *new_v ()
{
return new db::Texts ();
@ -152,6 +306,33 @@ static size_t id (const db::Texts *t)
return tl::id_of (t->delegate ());
}
static db::Texts filtered (const db::Texts *r, const TextFilterImpl *f)
{
return r->filtered (*f);
}
static void filter (db::Texts *r, const TextFilterImpl *f)
{
r->filter (*f);
}
static db::Texts processed_tt (const db::Texts *r, const shape_processor_impl<db::TextProcessorBase> *f)
{
return r->processed (*f);
}
static void process_tt (db::Texts *r, const shape_processor_impl<db::TextProcessorBase> *f)
{
r->process (*f);
}
static db::Region processed_tp (const db::Texts *r, const shape_processor_impl<db::TextToPolygonProcessorBase> *f)
{
db::Region out;
r->processed (out, *f);
return out;
}
static db::Texts with_text (const db::Texts *r, const std::string &text, bool inverse)
{
db::TextStringFilter f (text, inverse);
@ -401,6 +582,36 @@ Class<db::Texts> decl_Texts (decl_dbShapeCollection, "db", "Texts",
"@brief Converts the edge pairs to polygons\n"
"This method creates polygons from the texts. This is equivalent to calling \\extents."
) +
method_ext ("filter", &filter, gsi::arg ("filter"),
"@brief Applies a generic filter in place (replacing the texts from the Texts collection)\n"
"See \\TextFilter for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("filtered", &filtered, gsi::arg ("filtered"),
"@brief Applies a generic filter and returns a filtered copy\n"
"See \\TextFilter for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("process", &process_tt, gsi::arg ("process"),
"@brief Applies a generic text processor in place (replacing the texts from the text collection)\n"
"See \\TextProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("processed", &processed_tt, gsi::arg ("processed"),
"@brief Applies a generic text processor and returns a processed copy\n"
"See \\TextProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("processed", &processed_tp, gsi::arg ("processed"),
"@brief Applies a generic text-to-polygon processor and returns a region with the results\n"
"See \\TextToPolygonProcessor for a description of this feature.\n"
"\n"
"This method has been introduced in version 0.29.\n"
) +
method_ext ("with_text", with_text, gsi::arg ("text"), gsi::arg ("inverse"),
"@brief Filter the text by text string\n"
"If \"inverse\" is false, this method returns the texts with the given string.\n"

View File

@ -30,6 +30,60 @@ def csort(s)
s.split(/(?<=\));(?=\()/).sort.join(";")
end
class PerpendicularEdgesFilter < RBA::EdgePairFilter
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # orientation and scale do not matter
end
# Select edge pairs where the edges are perpendicular
def selected(edge_pair)
return edge_pair.first.d.sprod_sign(edge_pair.second.d) == 0
end
end
class FlipEdgePair < RBA::EdgePairOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # orientation and scale do not matter
end
# Flips the edge pair
def process(edge_pair)
return [ RBA::EdgePair::new(edge_pair.second, edge_pair.first) ]
end
end
class SomeEdgePairToEdgeOperator < RBA::EdgePairToEdgeOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # scale or orientation do not matter
end
def process(ep)
return [ RBA::Edge::new(ep.first.p1, ep.second.p2) ]
end
end
class SomeEdgePairToPolygonOperator < RBA::EdgePairToPolygonOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # scale or orientation do not matter
end
def process(ep)
return [ RBA::Polygon::new(ep.bbox) ]
end
end
class DBEdgePairs_TestClass < TestBase
# Basics
@ -331,6 +385,90 @@ class DBEdgePairs_TestClass < TestBase
end
# Generic filters
def test_generic_filters
# Some basic tests for the filter class
f = PerpendicularEdgesFilter::new
assert_equal(f.wants_variants?, true)
f.wants_variants = false
assert_equal(f.wants_variants?, false)
# Smoke test
f.is_isotropic
f.is_scale_invariant
# Some application
f = PerpendicularEdgesFilter::new
edge_pairs = RBA::EdgePairs::new
edge_pairs.insert(RBA::EdgePair::new([0, 0, 100, 0], [0, 100, 0, 300 ]))
edge_pairs.insert(RBA::EdgePair::new([200, 0, 300, 0], [200, 100, 220, 300 ]))
assert_equal(edge_pairs.filtered(f).to_s, "(0,0;100,0)/(0,100;0,300)")
assert_equal(edge_pairs.to_s, "(0,0;100,0)/(0,100;0,300);(200,0;300,0)/(200,100;220,300)")
edge_pairs.filter(f)
assert_equal(edge_pairs.to_s, "(0,0;100,0)/(0,100;0,300)")
end
# Generic processors
def test_generic_processors_epep
# Some basic tests for the processor class
f = FlipEdgePair::new
assert_equal(f.wants_variants?, true)
f.wants_variants = false
assert_equal(f.wants_variants?, false)
# Smoke test
f.is_isotropic
f.is_scale_invariant
# Some application
edge_pairs = RBA::EdgePairs::new
edge_pairs.insert(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)))
assert_equal(edge_pairs.processed(FlipEdgePair::new).to_s, "(200,300;200,500)/(0,0;100,100)")
assert_equal(edge_pairs.to_s, "(0,0;100,100)/(200,300;200,500)")
edge_pairs.process(FlipEdgePair::new)
assert_equal(edge_pairs.to_s, "(200,300;200,500)/(0,0;100,100)")
end
# Generic processors
def test_generic_processors_epe
p = SomeEdgePairToEdgeOperator::new
edge_pairs = RBA::EdgePairs::new
edge_pairs.insert(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)))
assert_equal(edge_pairs.processed(p).to_s, "(0,0;200,500)")
assert_equal(edge_pairs.to_s, "(0,0;100,100)/(200,300;200,500)")
end
# Generic processors
def test_generic_processors_epp
p = SomeEdgePairToPolygonOperator::new
edge_pairs = RBA::EdgePairs::new
edge_pairs.insert(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)))
assert_equal(edge_pairs.processed(p).to_s, "(0,0;0,500;200,500;200,0)")
assert_equal(edge_pairs.to_s, "(0,0;100,100)/(200,300;200,500)")
end
end

View File

@ -30,6 +30,64 @@ def csort(s)
s.split(/(?<=\));(?=\()/).sort.join(";")
end
class ParallelFilter < RBA::EdgeFilter
# Constructor
def initialize(ref_edge)
self.is_scale_invariant # orientation matters, but scale does not
@ref_edge = ref_edge
end
# Select only parallel ones
def selected(edge)
return edge.is_parallel?(@ref_edge)
end
end
class ShrinkToHalfEdgeOperator < RBA::EdgeOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # scale or orientation do not matter
end
# Shrink to half size
def process(edge)
shift = edge.bbox.center - RBA::Point::new # shift vector
return [ (edge.moved(-shift) * 0.5).moved(shift) ]
end
end
class SomeEdgeToEdgePairOperator < RBA::EdgeToEdgePairOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # scale or orientation do not matter
end
def process(edge)
box = edge.bbox
return [ RBA::EdgePair::new([ box.left, box.bottom, box.left, box.top ], [ box.right, box.bottom, box.right, box.top ]) ]
end
end
class SomeEdgeToPolygonOperator < RBA::EdgeToPolygonOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # scale or orientation do not matter
end
def process(edge)
box = edge.bbox
return [ RBA::Polygon::new(box) ]
end
end
class DBEdges_TestClass < TestBase
# Basics
@ -791,6 +849,105 @@ class DBEdges_TestClass < TestBase
end
# Generic filters
def test_generic_filters
# Some basic tests for the filter class
f = ParallelFilter::new(RBA::Edge::new(0, 0, 100, 100))
assert_equal(f.wants_variants?, true)
f.wants_variants = false
assert_equal(f.wants_variants?, false)
assert_equal(f.requires_raw_input, false)
f.requires_raw_input = false
assert_equal(f.requires_raw_input, false)
# Smoke test
f.is_isotropic
f.is_scale_invariant
# Some application
f = ParallelFilter::new(RBA::Edge::new(0, 0, 100, 100))
edges = RBA::Edges::new
edges.insert(RBA::Edge::new(100, 0, 200, 100))
edges.insert(RBA::Edge::new(100, 100, 100, 200))
assert_equal(edges.filtered(f).to_s, "(100,0;200,100)")
assert_equal(edges.to_s, "(100,0;200,100);(100,100;100,200)")
edges.filter(f)
assert_equal(edges.to_s, "(100,0;200,100)")
end
# Generic processors
def test_generic_processors_ee
# Some basic tests for the processor class
f = ShrinkToHalfEdgeOperator::new
assert_equal(f.wants_variants?, true)
f.wants_variants = false
assert_equal(f.wants_variants?, false)
assert_equal(f.requires_raw_input, false)
f.requires_raw_input = true
assert_equal(f.requires_raw_input, true)
assert_equal(f.result_is_merged, false)
f.result_is_merged = true
assert_equal(f.result_is_merged, true)
assert_equal(f.result_must_not_be_merged, false)
f.result_must_not_be_merged = true
assert_equal(f.result_must_not_be_merged, true)
# Smoke test
f.is_isotropic
f.is_scale_invariant
# Some application
edges = RBA::Edges::new
edges.insert(RBA::Edge::new(0, 0, 100, 100))
edges.insert(RBA::Edge::new(200, 300, 200, 500))
assert_equal(edges.processed(ShrinkToHalfEdgeOperator::new).to_s, "(25,25;75,75);(200,350;200,450)")
assert_equal(edges.to_s, "(0,0;100,100);(200,300;200,500)")
edges.process(ShrinkToHalfEdgeOperator::new)
assert_equal(edges.to_s, "(25,25;75,75);(200,350;200,450)")
end
# Generic processors
def test_generic_processors_eep
p = SomeEdgeToEdgePairOperator::new
edges = RBA::Edges::new
edges.insert(RBA::Edge::new(0, 0, 100, 100))
edges.insert(RBA::Edge::new(200, 300, 200, 500))
assert_equal(edges.processed(p).to_s, "(0,0;0,100)/(100,0;100,100);(200,300;200,500)/(200,300;200,500)")
assert_equal(edges.to_s, "(0,0;100,100);(200,300;200,500)")
end
# Generic processors
def test_generic_processors_ep
p = SomeEdgeToPolygonOperator::new
edges = RBA::Edges::new
edges.insert(RBA::Edge::new(0, 0, 100, 100))
edges.insert(RBA::Edge::new(200, 300, 200, 500))
assert_equal(edges.processed(p).to_s, "(0,0;0,100;100,100;100,0);(200,300;200,300;200,500;200,500)")
assert_equal(edges.to_s, "(0,0;100,100);(200,300;200,500)")
end
end
load("test_epilogue.rb")

View File

@ -30,6 +30,63 @@ def csort(s)
s.split(/(?<=\));(?=\()/).sort.join(";")
end
class TriangleFilter < RBA::PolygonFilter
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # the triangle nature is not dependent on the scale or orientation
end
# Select only triangles
def selected(polygon)
return polygon.holes == 0 && polygon.num_points == 3
end
end
class ShrinkToHalfOperator < RBA::PolygonOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # scale or orientation do not matter
end
# Shrink to half size
def process(polygon)
shift = polygon.bbox.center - RBA::Point::new # shift vector
return [ (polygon.moved(-shift) * 0.5).moved(shift) ]
end
end
class SomePolygonToEdgePairOperator < RBA::PolygonToEdgePairOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # scale or orientation do not matter
end
def process(polygon)
box = polygon.bbox
return [ RBA::EdgePair::new([ box.left, box.bottom, box.left, box.top ], [ box.right, box.bottom, box.right, box.top ]) ]
end
end
class SomePolygonToEdgeOperator < RBA::PolygonToEdgeOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # scale or orientation do not matter
end
def process(polygon)
box = polygon.bbox
return [ RBA::Edge::new(box.p1, box.p2) ]
end
end
class DBRegion_TestClass < TestBase
# Basics
@ -1226,6 +1283,104 @@ class DBRegion_TestClass < TestBase
end
# Generic filters
def test_generic_filters
# Some basic tests for the filter class
f = TriangleFilter::new
assert_equal(f.wants_variants?, true)
f.wants_variants = false
assert_equal(f.wants_variants?, false)
assert_equal(f.requires_raw_input, false)
f.requires_raw_input = true
assert_equal(f.requires_raw_input, true)
# Smoke test
f.is_isotropic
f.is_scale_invariant
# Some application
region = RBA::Region::new
region.insert(RBA::Polygon::new([[0,0], [100, 100], [100,0]]))
region.insert(RBA::Box::new(200, 0, 300, 100))
assert_equal(region.filtered(TriangleFilter::new).to_s, "(0,0;100,100;100,0)")
assert_equal(region.to_s, "(0,0;100,100;100,0);(200,0;200,100;300,100;300,0)")
region.filter(TriangleFilter::new)
assert_equal(region.to_s, "(0,0;100,100;100,0)")
end
# Generic processors
def test_generic_processors_pp
# Some basic tests for the processor class
f = ShrinkToHalfOperator::new
assert_equal(f.wants_variants?, true)
f.wants_variants = false
assert_equal(f.wants_variants?, false)
assert_equal(f.requires_raw_input, false)
f.requires_raw_input = true
assert_equal(f.requires_raw_input, true)
assert_equal(f.result_is_merged, false)
f.result_is_merged = true
assert_equal(f.result_is_merged, true)
assert_equal(f.result_must_not_be_merged, false)
f.result_must_not_be_merged = true
assert_equal(f.result_must_not_be_merged, true)
# Smoke test
f.is_isotropic
f.is_scale_invariant
# Some application
region = RBA::Region::new
region.insert(RBA::Polygon::new([[0,0], [100, 100], [100,0]]))
region.insert(RBA::Box::new(200, 0, 300, 100))
assert_equal(region.processed(ShrinkToHalfOperator::new).to_s, "(25,25;75,75;75,25);(225,25;225,75;275,75;275,25)")
assert_equal(region.to_s, "(0,0;100,100;100,0);(200,0;200,100;300,100;300,0)")
region.process(ShrinkToHalfOperator::new)
assert_equal(region.to_s, "(25,25;75,75;75,25);(225,25;225,75;275,75;275,25)")
end
# Generic processors
def test_generic_processors_pep
p = SomePolygonToEdgePairOperator::new
region = RBA::Region::new
region.insert(RBA::Polygon::new([[0,0], [100, 100], [100,0]]))
region.insert(RBA::Box::new(200, 0, 300, 100))
assert_equal(region.processed(p).to_s, "(0,0;0,100)/(100,0;100,100);(200,0;200,100)/(300,0;300,100)")
assert_equal(region.to_s, "(0,0;100,100;100,0);(200,0;200,100;300,100;300,0)")
end
# Generic processors
def test_generic_processors_pe
p = SomePolygonToEdgeOperator::new
region = RBA::Region::new
region.insert(RBA::Polygon::new([[0,0], [100, 100], [100,0]]))
region.insert(RBA::Box::new(200, 0, 300, 100))
assert_equal(region.processed(p).to_s, "(0,0;100,100);(200,0;300,100)")
assert_equal(region.to_s, "(0,0;100,100;100,0);(200,0;200,100;300,100;300,0)")
end
# rasterize
def test_rasterize

View File

@ -29,6 +29,52 @@ def csort(s)
# splits at ");(" without consuming the brackets
s.split(/(?<=\));(?=\()/).sort.join(";")
end
class TextStringLengthFilter < RBA::TextFilter
# Constructor
def initialize(string_length)
self.is_isotropic_and_scale_invariant # orientation and scale do not matter
@string_length = string_length
end
# Select texts with given string length
def selected(text)
return text.string.size == @string_length
end
end
class ReplaceTextString < RBA::TextOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # orientation and scale do not matter
end
# Replaces the string by a number representing the string length
def process(text)
new_text = text.dup # need a copy as we cannot modify the text passed
new_text.string = text.string.size.to_s
return [ new_text ]
end
end
class SomeTextToPolygonOperator < RBA::TextToPolygonOperator
# Constructor
def initialize
self.is_isotropic_and_scale_invariant # orientation and scale do not matter
end
# Replaces the string by a number representing the string length
def process(text)
s = text.string.size * 10
return [ RBA::Polygon::new(text.bbox.enlarged(s)) ]
end
end
class DBTexts_TestClass < TestBase
@ -324,6 +370,79 @@ class DBTexts_TestClass < TestBase
end
# Generic filters
def test_generic_filters
# Some basic tests for the filter class
f = TextStringLengthFilter::new(3)
assert_equal(f.wants_variants?, true)
f.wants_variants = false
assert_equal(f.wants_variants?, false)
# Smoke test
f.is_isotropic
f.is_scale_invariant
# Some application
f = TextStringLengthFilter::new(3)
texts = RBA::Texts::new
texts.insert(RBA::Text::new("long", [ RBA::Trans::M45, 10, 0 ]))
texts.insert(RBA::Text::new("tla", [ RBA::Trans::R0, 0, 0 ]))
texts.insert(RBA::Text::new("00", [ RBA::Trans::R90, 0, 20 ]))
assert_equal(texts.filtered(f).to_s, "('tla',r0 0,0)")
assert_equal(texts.to_s, "('long',m45 10,0);('tla',r0 0,0);('00',r90 0,20)")
texts.filter(f)
assert_equal(texts.to_s, "('tla',r0 0,0)")
end
# Generic processors
def test_generic_processors_tt
# Some basic tests for the processor class
f = ReplaceTextString::new
assert_equal(f.wants_variants?, true)
f.wants_variants = false
assert_equal(f.wants_variants?, false)
# Smoke test
f.is_isotropic
f.is_scale_invariant
# Some application
texts = RBA::Texts::new
texts.insert(RBA::Text::new("abc", RBA::Trans::new))
texts.insert(RBA::Text::new("a long text", RBA::Trans::M45))
assert_equal(texts.processed(ReplaceTextString::new).to_s, "('3',r0 0,0);('11',m45 0,0)")
assert_equal(texts.to_s, "('abc',r0 0,0);('a long text',m45 0,0)")
texts.process(ReplaceTextString::new)
assert_equal(texts.to_s, "('3',r0 0,0);('11',m45 0,0)")
end
# Generic processors
def test_generic_processors_tp
p = SomeTextToPolygonOperator::new
texts = RBA::Texts::new
texts.insert(RBA::Text::new("abc", RBA::Trans::new))
texts.insert(RBA::Text::new("a long text", RBA::Trans::M45))
assert_equal(texts.processed(p).to_s, "(-30,-30;-30,30;30,30;30,-30);(-110,-110;-110,110;110,110;110,-110)")
assert_equal(texts.to_s, "('abc',r0 0,0);('a long text',m45 0,0)")
end
end