mirror of https://github.com/KLayout/klayout.git
Merge pull request #1601 from KLayout/drc-procs-and-filters
Region/Edges/EdgePairs/Texts operators and filters
This commit is contained in:
commit
230bacf725
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 "
|
||||
|
|
|
|||
|
|
@ -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 "
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue