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;
|
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 *
|
RegionDelegate *
|
||||||
AsIfFlatEdgePairs::processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const
|
AsIfFlatEdgePairs::processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,14 @@ public:
|
||||||
|
|
||||||
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const;
|
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const;
|
||||||
|
|
||||||
virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const;
|
virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &proc)
|
||||||
virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &filter) const;
|
{
|
||||||
|
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)
|
virtual EdgePairsDelegate *add_in_place (const EdgePairs &other)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,24 @@ AsIfFlatTexts::filtered (const TextFilterBase &filter) const
|
||||||
return new_texts.release ();
|
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 *
|
RegionDelegate *
|
||||||
AsIfFlatTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) const
|
AsIfFlatTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,12 @@ public:
|
||||||
|
|
||||||
virtual TextsDelegate *filtered (const TextFilterBase &) const;
|
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 RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &filter) const;
|
||||||
|
|
||||||
virtual TextsDelegate *add_in_place (const Texts &other)
|
virtual TextsDelegate *add_in_place (const Texts &other)
|
||||||
|
|
|
||||||
|
|
@ -451,6 +451,18 @@ DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const
|
||||||
return res.release ();
|
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 *
|
RegionDelegate *
|
||||||
DeepEdgePairs::processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const
|
DeepEdgePairs::processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,8 @@ public:
|
||||||
|
|
||||||
virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter);
|
virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter);
|
||||||
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const;
|
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 RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const;
|
||||||
virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &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;
|
std::pair<DeepLayer, DeepLayer> and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const;
|
||||||
DeepRegion *apply_filter (const PolygonFilterBase &filter) 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>
|
template <class Proc>
|
||||||
void configure_proc (Proc &proc) const
|
void configure_proc (Proc &proc) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -474,6 +474,18 @@ DeepTexts *DeepTexts::apply_filter (const TextFilterBase &filter) const
|
||||||
return res.release ();
|
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 *
|
RegionDelegate *
|
||||||
DeepTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) const
|
DeepTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,8 @@ public:
|
||||||
virtual TextsDelegate *filter_in_place (const TextFilterBase &filter);
|
virtual TextsDelegate *filter_in_place (const TextFilterBase &filter);
|
||||||
virtual TextsDelegate *filtered (const TextFilterBase &) const;
|
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 RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &filter) const;
|
||||||
|
|
||||||
virtual TextsDelegate *add_in_place (const Texts &other);
|
virtual TextsDelegate *add_in_place (const Texts &other);
|
||||||
|
|
|
||||||
|
|
@ -169,14 +169,19 @@ EdgePairs::properties_repository ()
|
||||||
return *r;
|
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
|
void EdgePairs::polygons (Region &output, db::Coord e) const
|
||||||
|
|
|
||||||
|
|
@ -328,13 +328,31 @@ public:
|
||||||
return EdgePairs (mp_delegate->filtered (filter));
|
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
|
* @brief Processes the edge pairs into polygons
|
||||||
*
|
*
|
||||||
* This method will run the processor over all edge pairs and return a region
|
* This method will run the processor over all edge pairs and return a region
|
||||||
* with the outputs of the processor.
|
* 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
|
* @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
|
* This method will run the processor over all edge pairs and return a edge collection
|
||||||
* with the outputs of the processor.
|
* 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
|
* @brief Transforms the edge pair set
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ class RegionDelegate;
|
||||||
class EdgesDelegate;
|
class EdgesDelegate;
|
||||||
class Layout;
|
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::Polygon> EdgePairToPolygonProcessorBase;
|
||||||
typedef shape_collection_processor<db::EdgePair, db::Edge> EdgePairToEdgeProcessorBase;
|
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 *filter_in_place (const EdgePairFilterBase &filter) = 0;
|
||||||
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &filter) const = 0;
|
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &filter) const = 0;
|
||||||
virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const = 0;
|
virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &proc) = 0;
|
||||||
virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &filter) const = 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 RegionDelegate *polygons (db::Coord e) const = 0;
|
||||||
virtual EdgesDelegate *edges () const = 0;
|
virtual EdgesDelegate *edges () const = 0;
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,8 @@ public:
|
||||||
|
|
||||||
virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &) { return this; }
|
virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &) { return this; }
|
||||||
virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const { return new EmptyEdgePairs (); }
|
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 RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const;
|
||||||
virtual EdgesDelegate *processed_to_edges (const EdgePairToEdgeProcessorBase &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 *filter_in_place (const TextFilterBase &) { return this; }
|
||||||
virtual TextsDelegate *filtered (const TextFilterBase &) const { return new EmptyTexts (); }
|
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 *processed_to_polygons (const TextToPolygonProcessorBase &) const;
|
||||||
|
|
||||||
virtual RegionDelegate *polygons (db::Coord e) const;
|
virtual RegionDelegate *polygons (db::Coord e) const;
|
||||||
|
|
|
||||||
|
|
@ -106,62 +106,6 @@ public:
|
||||||
virtual bool wants_variants () const = 0;
|
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::Polygon> PolygonProcessorBase;
|
||||||
typedef shape_collection_processor<db::Polygon, db::Edge> PolygonToEdgeProcessorBase;
|
typedef shape_collection_processor<db::Polygon, db::Edge> PolygonToEdgeProcessorBase;
|
||||||
typedef shape_collection_processor<db::Polygon, db::EdgePair> PolygonToEdgePairProcessorBase;
|
typedef shape_collection_processor<db::Polygon, db::EdgePair> PolygonToEdgePairProcessorBase;
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,9 @@ class DB_PUBLIC_TEMPLATE shape_collection_processor
|
||||||
: public tl::Object
|
: public tl::Object
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
typedef Shape shape_type;
|
||||||
|
typedef Result result_type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructor
|
* @brief Constructor
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,11 @@ MutableTexts *Texts::mutable_texts ()
|
||||||
return 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
|
void Texts::processed (Region &output, const TextToPolygonProcessorBase &filter) const
|
||||||
{
|
{
|
||||||
output = Region (mp_delegate->processed_to_polygons (filter));
|
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
|
* This method will run the processor over all edges and return a region
|
||||||
* with the outputs of the processor.
|
* with the outputs of the processor.
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ class RegionDelegate;
|
||||||
class EdgesDelegate;
|
class EdgesDelegate;
|
||||||
class Layout;
|
class Layout;
|
||||||
|
|
||||||
|
typedef shape_collection_processor<db::Text, db::Text> TextProcessorBase;
|
||||||
typedef shape_collection_processor<db::Text, db::Polygon> TextToPolygonProcessorBase;
|
typedef shape_collection_processor<db::Text, db::Polygon> TextToPolygonProcessorBase;
|
||||||
|
|
||||||
typedef db::generic_shape_iterator_delegate_base <db::Text> TextsIteratorDelegate;
|
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 *filter_in_place (const TextFilterBase &filter) = 0;
|
||||||
virtual TextsDelegate *filtered (const TextFilterBase &filter) const = 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 RegionDelegate *polygons (db::Coord e) const = 0;
|
||||||
virtual EdgesDelegate *edges () const = 0;
|
virtual EdgesDelegate *edges () const = 0;
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
#define HDR_gsiDeclDbContainerHelpers
|
#define HDR_gsiDeclDbContainerHelpers
|
||||||
|
|
||||||
#include "dbPropertiesRepository.h"
|
#include "dbPropertiesRepository.h"
|
||||||
|
#include "dbCellVariants.h"
|
||||||
#include "tlVariant.h"
|
#include "tlVariant.h"
|
||||||
#include "gsiDecl.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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,181 @@
|
||||||
namespace gsi
|
namespace gsi
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------
|
||||||
|
// 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 ()
|
static db::EdgePairs *new_v ()
|
||||||
{
|
{
|
||||||
return new db::EdgePairs ();
|
return new db::EdgePairs ();
|
||||||
|
|
@ -181,6 +356,40 @@ static size_t id (const db::EdgePairs *ep)
|
||||||
return tl::id_of (ep->delegate ());
|
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)
|
static db::EdgePairs with_distance1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse)
|
||||||
{
|
{
|
||||||
db::EdgePairFilterByDistance ef (length, length + 1, 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 "
|
"The boxes will not be merged, so it is possible to determine overlaps "
|
||||||
"of these boxes for example.\n"
|
"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"),
|
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"
|
"@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 "
|
"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
|
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)
|
static inline std::vector<db::Edges> as_2edges_vector (const std::pair<db::Edges, db::Edges> &rp)
|
||||||
{
|
{
|
||||||
std::vector<db::Edges> res;
|
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)));
|
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)
|
static db::Edges with_length1 (const db::Edges *r, db::Edges::distance_type length, bool inverse)
|
||||||
{
|
{
|
||||||
db::EdgeLengthFilter f (length, length + 1, inverse);
|
db::EdgeLengthFilter f (length, length + 1, inverse);
|
||||||
|
|
@ -627,6 +849,42 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
|
||||||
"\n"
|
"\n"
|
||||||
"This method has been introduced in version 0.26."
|
"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"),
|
method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"),
|
||||||
"@brief Filters the edges by length\n"
|
"@brief Filters the edges by length\n"
|
||||||
"Filters the edges in the edge collection by length. If \"inverse\" is false, only "
|
"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) {
|
if (c->hull ().size () > p) {
|
||||||
return c->hull ()[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 ();
|
return c->hull ().size ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_empty (C *c)
|
static bool is_empty (const C *c)
|
||||||
{
|
{
|
||||||
return c->hull ().size () == 0;
|
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 ();
|
return c->vertices ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_empty (C *c)
|
static bool is_empty (const C *c)
|
||||||
{
|
{
|
||||||
return c->vertices () == 0;
|
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) {
|
if (c->hull ().size () > p) {
|
||||||
return c->hull ()[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) {
|
if (c->holes () > n && c->contour (n + 1).size () > p) {
|
||||||
return c->contour (n + 1)[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 ();
|
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 ();
|
return c->contour (n + 1).size ();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,190 @@
|
||||||
namespace gsi
|
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)
|
static inline std::vector<db::Region> as_2region_vector (const std::pair<db::Region, db::Region> &rp)
|
||||||
{
|
{
|
||||||
std::vector<db::Region> res;
|
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));
|
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)
|
static db::Region with_perimeter1 (const db::Region *r, db::Region::perimeter_type perimeter, bool inverse)
|
||||||
{
|
{
|
||||||
db::RegionPerimeterFilter f (perimeter, perimeter + 1, inverse);
|
db::RegionPerimeterFilter f (perimeter, perimeter + 1, inverse);
|
||||||
|
|
@ -2363,6 +2577,42 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
|
||||||
"\n"
|
"\n"
|
||||||
"This method has been introduced in version 0.28.\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,
|
method_ext ("rectangles", &rectangles,
|
||||||
"@brief Returns all polygons which are rectangles\n"
|
"@brief Returns all polygons which are rectangles\n"
|
||||||
"This method returns all polygons in self which are rectangles."
|
"This method returns all polygons in self which are rectangles."
|
||||||
|
|
|
||||||
|
|
@ -98,17 +98,17 @@ struct text_defs
|
||||||
t->font (db::Font (f));
|
t->font (db::Font (f));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_font (C *t)
|
static int get_font (const C *t)
|
||||||
{
|
{
|
||||||
return t->font ();
|
return t->font ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static point_type get_pos (C *t)
|
static point_type get_pos (const C *t)
|
||||||
{
|
{
|
||||||
return t->trans () * point_type ();
|
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);
|
point_type p = get_pos (t);
|
||||||
return box_type (p, p);
|
return box_type (p, p);
|
||||||
|
|
@ -124,7 +124,7 @@ struct text_defs
|
||||||
t->halign (db::HAlign (f));
|
t->halign (db::HAlign (f));
|
||||||
}
|
}
|
||||||
|
|
||||||
static db::HAlign get_halign (C *t)
|
static db::HAlign get_halign (const C *t)
|
||||||
{
|
{
|
||||||
return t->halign ();
|
return t->halign ();
|
||||||
}
|
}
|
||||||
|
|
@ -139,12 +139,12 @@ struct text_defs
|
||||||
t->valign (db::VAlign (f));
|
t->valign (db::VAlign (f));
|
||||||
}
|
}
|
||||||
|
|
||||||
static db::VAlign get_valign (C *t)
|
static db::VAlign get_valign (const C *t)
|
||||||
{
|
{
|
||||||
return t->valign ();
|
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));
|
return c->transformed (simple_trans_type (p));
|
||||||
}
|
}
|
||||||
|
|
@ -155,7 +155,7 @@ struct text_defs
|
||||||
return *c;
|
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)));
|
return c->transformed (simple_trans_type (vector_type (dx, dy)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,160 @@
|
||||||
namespace gsi
|
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 ()
|
static db::Texts *new_v ()
|
||||||
{
|
{
|
||||||
return new db::Texts ();
|
return new db::Texts ();
|
||||||
|
|
@ -152,6 +306,33 @@ static size_t id (const db::Texts *t)
|
||||||
return tl::id_of (t->delegate ());
|
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)
|
static db::Texts with_text (const db::Texts *r, const std::string &text, bool inverse)
|
||||||
{
|
{
|
||||||
db::TextStringFilter f (text, 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"
|
"@brief Converts the edge pairs to polygons\n"
|
||||||
"This method creates polygons from the texts. This is equivalent to calling \\extents."
|
"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"),
|
method_ext ("with_text", with_text, gsi::arg ("text"), gsi::arg ("inverse"),
|
||||||
"@brief Filter the text by text string\n"
|
"@brief Filter the text by text string\n"
|
||||||
"If \"inverse\" is false, this method returns the texts with the given 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(";")
|
s.split(/(?<=\));(?=\()/).sort.join(";")
|
||||||
end
|
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
|
class DBEdgePairs_TestClass < TestBase
|
||||||
|
|
||||||
# Basics
|
# Basics
|
||||||
|
|
@ -331,6 +385,90 @@ class DBEdgePairs_TestClass < TestBase
|
||||||
|
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,64 @@ def csort(s)
|
||||||
s.split(/(?<=\));(?=\()/).sort.join(";")
|
s.split(/(?<=\));(?=\()/).sort.join(";")
|
||||||
end
|
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
|
class DBEdges_TestClass < TestBase
|
||||||
|
|
||||||
# Basics
|
# Basics
|
||||||
|
|
@ -791,6 +849,105 @@ class DBEdges_TestClass < TestBase
|
||||||
|
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
load("test_epilogue.rb")
|
load("test_epilogue.rb")
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,63 @@ def csort(s)
|
||||||
s.split(/(?<=\));(?=\()/).sort.join(";")
|
s.split(/(?<=\));(?=\()/).sort.join(";")
|
||||||
end
|
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
|
class DBRegion_TestClass < TestBase
|
||||||
|
|
||||||
# Basics
|
# Basics
|
||||||
|
|
@ -1226,6 +1283,104 @@ class DBRegion_TestClass < TestBase
|
||||||
|
|
||||||
end
|
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
|
# rasterize
|
||||||
def test_rasterize
|
def test_rasterize
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,52 @@ def csort(s)
|
||||||
s.split(/(?<=\));(?=\()/).sort.join(";")
|
s.split(/(?<=\));(?=\()/).sort.join(";")
|
||||||
end
|
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
|
class DBTexts_TestClass < TestBase
|
||||||
|
|
||||||
# Basics
|
# Basics
|
||||||
|
|
@ -324,6 +370,79 @@ class DBTexts_TestClass < TestBase
|
||||||
|
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue