diff --git a/src/db/db/gsiDeclDbContainerHelpers.h b/src/db/db/gsiDeclDbContainerHelpers.h index e16e91be8..ee6112948 100644 --- a/src/db/db/gsiDeclDbContainerHelpers.h +++ b/src/db/db/gsiDeclDbContainerHelpers.h @@ -25,6 +25,7 @@ #define HDR_gsiDeclDbContainerHelpers #include "dbPropertiesRepository.h" +#include "dbCellVariants.h" #include "tlVariant.h" #include "gsiDecl.h" @@ -100,6 +101,143 @@ make_property_methods () ); } +// --------------------------------------------------------------------------------- +// Generic shape filter declarations + +template +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; +}; + } #endif diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 870991c4d..3f5639720 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -36,7 +36,83 @@ namespace gsi { -static db::EdgePairs *new_v () +// --------------------------------------------------------------------------------- +// EdgePairFilter binding + +class EdgePairFilterImpl + : public shape_filter_impl +{ +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::issue_selected, edge_pair); + } else { + return db::EdgePairFilterBase::selected (edge_pair); + } + } + + gsi::Callback f_selected; +}; + +Class 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" +); + +// --------------------------------------------------------------------------------- +// EdgePairs binding + +static db::EdgePairs *new_v () { return new db::EdgePairs (); } @@ -181,6 +257,16 @@ static size_t id (const db::EdgePairs *ep) return tl::id_of (ep->delegate ()); } +static db::EdgePairs filtered (const db::EdgePairs *r, const EdgePairFilterImpl *f) +{ + return r->filtered (*f); +} + +static void filter (db::EdgePairs *r, const EdgePairFilterImpl *f) +{ + r->filter (*f); +} + static db::EdgePairs with_distance1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse) { db::EdgePairFilterByDistance ef (length, length + 1, inverse); @@ -619,6 +705,18 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "The boxes will not be merged, so it is possible to determine overlaps " "of these boxes for example.\n" ) + + method_ext ("filter", &filter, gsi::arg ("filter"), + "@brief Applies a generic filter in place (replacing the edge pairs from the EdgePair collection)\n" + "See \\EdgePairFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("filtered", &filtered, gsi::arg ("filtered"), + "@brief Applies a generic filter and returns a filtered copy\n" + "See \\EdgePairFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"), "@brief Filters the edge pairs by length of one of their edges\n" "Filters the edge pairs in the edge pair collection by length of at least one of their edges. If \"inverse\" is false, only " diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 718b6d16b..8640749e4 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -37,6 +37,95 @@ namespace gsi { +// --------------------------------------------------------------------------------- +// EdgeFilter binding + +class EdgeFilterImpl + : public shape_filter_impl +{ +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::issue_selected, edge); + } else { + return db::EdgeFilterBase::selected (edge); + } + } + + // Returns true if all edges match the criterion + virtual bool selected (const std::unordered_set &edges) const + { + for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { + if (! selected (*e)) { + return false; + } + } + return true; + } + + gsi::Callback f_selected; +}; + +Class 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 \\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" +); + +// --------------------------------------------------------------------------------- +// Edges binding + static inline std::vector as_2edges_vector (const std::pair &rp) { std::vector res; @@ -204,6 +293,16 @@ static db::Edges moved_xy (const db::Edges *r, db::Coord x, db::Coord y) return r->transformed (db::Disp (db::Vector (x, y))); } +static db::Edges filtered (const db::Edges *r, const EdgeFilterImpl *f) +{ + return r->filtered (*f); +} + +static void filter (db::Edges *r, const EdgeFilterImpl *f) +{ + r->filter (*f); +} + static db::Edges with_length1 (const db::Edges *r, db::Edges::distance_type length, bool inverse) { db::EdgeLengthFilter f (length, length + 1, inverse); @@ -621,6 +720,18 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "This method has been introduced in version 0.26." ) + + method_ext ("filter", &filter, gsi::arg ("filter"), + "@brief Applies a generic filter in place (replacing the edges from the Edges collection)\n" + "See \\EdgeFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("filtered", &filtered, gsi::arg ("filtered"), + "@brief Applies a generic filter and returns a filtered copy\n" + "See \\EdgeFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + method_ext ("with_length", with_length1, gsi::arg ("length"), gsi::arg ("inverse"), "@brief Filters the edges by length\n" "Filters the edges in the edge collection by length. If \"inverse\" is false, only " diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index b42f8874b..b6c00648c 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -47,143 +47,6 @@ namespace gsi { -// --------------------------------------------------------------------------------- -// Generic shape filter declarations - -template -class shape_filter_impl - : public Base -{ -public: - shape_filter_impl () - { - mp_vars = &m_mag_and_orient; - m_wants_variants = true; - m_requires_raw_input = false; - } - - // overrides virtual method - const db::TransformationReducer *vars () const - { - return mp_vars; - } - - // maybe overrides virtual method - 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 - 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; -}; - // --------------------------------------------------------------------------------- // PolygonFilter binding @@ -217,7 +80,7 @@ public: gsi::Callback f_selected; }; -Class decl_PluginFactory ("db", "PolygonFilter", +Class decl_PolygonFilterImpl ("db", "PolygonFilter", PolygonFilterImpl::method_decls (true) + callback ("selected", &PolygonFilterImpl::issue_selected, &PolygonFilterImpl::f_selected, gsi::arg ("polygon"), "@brief Selects a polygon\n" @@ -264,7 +127,6 @@ Class decl_PluginFactory ("db", "PolygonFilter", "This class has been introduced in version 0.29.\n" ); - // --------------------------------------------------------------------------------- // Region binding diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index b105c5d82..d186c2c32 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -33,6 +33,83 @@ namespace gsi { +// --------------------------------------------------------------------------------- +// TextFilter binding + +class TextFilterImpl + : public shape_filter_impl +{ +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::issue_selected, text); + } else { + return db::TextFilterBase::selected (text); + } + } + + gsi::Callback f_selected; +}; + +Class 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" +); + +// --------------------------------------------------------------------------------- +// Texts binding + static db::Texts *new_v () { return new db::Texts (); @@ -152,6 +229,16 @@ static size_t id (const db::Texts *t) return tl::id_of (t->delegate ()); } +static db::Texts filtered (const db::Texts *r, const TextFilterImpl *f) +{ + return r->filtered (*f); +} + +static void filter (db::Texts *r, const TextFilterImpl *f) +{ + r->filter (*f); +} + static db::Texts with_text (const db::Texts *r, const std::string &text, bool inverse) { db::TextStringFilter f (text, inverse); @@ -401,6 +488,18 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "@brief Converts the edge pairs to polygons\n" "This method creates polygons from the texts. This is equivalent to calling \\extents." ) + + method_ext ("filter", &filter, gsi::arg ("filter"), + "@brief Applies a generic filter in place (replacing the texts from the Texts collection)\n" + "See \\TextFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + method_ext ("filtered", &filtered, gsi::arg ("filtered"), + "@brief Applies a generic filter and returns a filtered copy\n" + "See \\TextFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + method_ext ("with_text", with_text, gsi::arg ("text"), gsi::arg ("inverse"), "@brief Filter the text by text string\n" "If \"inverse\" is false, this method returns the texts with the given string.\n" diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index fa1ccad3c..b5db1f866 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -30,6 +30,20 @@ def csort(s) s.split(/(?<=\));(?=\()/).sort.join(";") end +class PerpendicularEdgesFilter < RBA::EdgePairFilter + + # Constructor + def initialize + self.is_isotropic_and_scale_invariant # orientation and scale do not matter + end + + # Select edge pairs where the edges are perpendicular + def selected(edge_pair) + return edge_pair.first.d.sprod_sign(edge_pair.second.d) == 0 + end + +end + class DBEdgePairs_TestClass < TestBase # Basics @@ -331,6 +345,35 @@ class DBEdgePairs_TestClass < TestBase end + # Generic filters + def test_generic_filters + + # Some basic tests for the filter class + + f = PerpendicularEdgesFilter::new + assert_equal(f.wants_variants?, true) + f.wants_variants = false + assert_equal(f.wants_variants?, false) + + # Smoke test + f.is_isotropic + f.is_scale_invariant + + # Some application + + f = PerpendicularEdgesFilter::new + + edge_pairs = RBA::EdgePairs::new + edge_pairs.insert(RBA::EdgePair::new([0, 0, 100, 0], [0, 100, 0, 300 ])) + edge_pairs.insert(RBA::EdgePair::new([200, 0, 300, 0], [200, 100, 220, 300 ])) + + assert_equal(edge_pairs.filtered(f).to_s, "(0,0;100,0)/(0,100;0,300)") + assert_equal(edge_pairs.to_s, "(0,0;100,0)/(0,100;0,300);(200,0;300,0)/(200,100;220,300)") + edge_pairs.filter(f) + assert_equal(edge_pairs.to_s, "(0,0;100,0)/(0,100;0,300)") + + end + end diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 1a0910a9d..5d3eb0660 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -30,6 +30,21 @@ def csort(s) s.split(/(?<=\));(?=\()/).sort.join(";") end +class ParallelFilter < RBA::EdgeFilter + + # Constructor + def initialize(ref_edge) + self.is_scale_invariant # orientation matters, but scale does not + @ref_edge = ref_edge + end + + # Select only parallel ones + def selected(edge) + return edge.is_parallel?(@ref_edge) + end + +end + class DBEdges_TestClass < TestBase # Basics @@ -749,6 +764,38 @@ class DBEdges_TestClass < TestBase end + # Generic filters + def test_generic_filters + + # Some basic tests for the filter class + + f = ParallelFilter::new(RBA::Edge::new(0, 0, 100, 100)) + assert_equal(f.wants_variants?, true) + f.wants_variants = false + assert_equal(f.wants_variants?, false) + assert_equal(f.requires_raw_input, false) + f.requires_raw_input = false + assert_equal(f.requires_raw_input, false) + + # Smoke test + f.is_isotropic + f.is_scale_invariant + + # Some application + + f = ParallelFilter::new(RBA::Edge::new(0, 0, 100, 100)) + + edges = RBA::Edges::new + edges.insert(RBA::Edge::new(100, 0, 200, 100)) + edges.insert(RBA::Edge::new(100, 100, 100, 200)) + + assert_equal(edges.filtered(f).to_s, "(100,0;200,100)") + assert_equal(edges.to_s, "(100,0;200,100);(100,100;100,200)") + edges.filter(f) + assert_equal(edges.to_s, "(100,0;200,100)") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 2f33b63df..e0f2f07c9 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1227,6 +1227,9 @@ class DBRegion_TestClass < TestBase 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 diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index 7b9a65288..385c50a00 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -30,6 +30,21 @@ def csort(s) s.split(/(?<=\));(?=\()/).sort.join(";") end +class TextStringLengthFilter < RBA::TextFilter + + # Constructor + def initialize(string_length) + self.is_isotropic_and_scale_invariant # orientation and scale do not matter + @string_length = string_length + end + + # Select texts with given string length + def selected(text) + return text.string.size == @string_length + end + +end + class DBTexts_TestClass < TestBase # Basics @@ -324,6 +339,36 @@ class DBTexts_TestClass < TestBase end + # Generic filters + def test_generic_filters + + # Some basic tests for the filter class + + f = TextStringLengthFilter::new(3) + assert_equal(f.wants_variants?, true) + f.wants_variants = false + assert_equal(f.wants_variants?, false) + + # Smoke test + f.is_isotropic + f.is_scale_invariant + + # Some application + + f = TextStringLengthFilter::new(3) + + texts = RBA::Texts::new + texts.insert(RBA::Text::new("long", [ RBA::Trans::M45, 10, 0 ])) + texts.insert(RBA::Text::new("tla", [ RBA::Trans::R0, 0, 0 ])) + texts.insert(RBA::Text::new("00", [ RBA::Trans::R90, 0, 20 ])) + + assert_equal(texts.filtered(f).to_s, "('tla',r0 0,0)") + assert_equal(texts.to_s, "('long',m45 10,0);('tla',r0 0,0);('00',r90 0,20)") + texts.filter(f) + assert_equal(texts.to_s, "('tla',r0 0,0)") + + end + end