Properties filters for EdgePairs and Texts too.

This commit is contained in:
Matthias Koefferlein 2025-02-16 13:59:50 +01:00
parent 1fed2767e8
commit ae5ae25000
4 changed files with 244 additions and 14 deletions

View File

@ -30,6 +30,7 @@
#include "dbDeepEdgePairs.h"
#include "dbEdgesUtils.h"
#include "dbEdgePairFilters.h"
#include "dbPropertiesFilter.h"
#include "gsiDeclDbContainerHelpers.h"
@ -39,8 +40,10 @@ namespace gsi
// ---------------------------------------------------------------------------------
// EdgePairFilter binding
typedef shape_filter_impl<db::EdgePairFilterBase> EdgePairFilterBase;
class EdgePairFilterImpl
: public shape_filter_impl<db::EdgePairFilterBase>
: public EdgePairFilterBase
{
public:
EdgePairFilterImpl () { }
@ -67,8 +70,70 @@ private:
EdgePairFilterImpl (const EdgePairFilterImpl &);
};
Class<gsi::EdgePairFilterImpl> decl_EdgePairFilterImpl ("db", "EdgePairFilter",
EdgePairFilterImpl::method_decls (false) +
typedef db::generic_properties_filter<gsi::EdgePairFilterBase, db::EdgePair> EdgePairPropertiesFilter;
static gsi::EdgePairFilterBase *make_ppf1 (const tl::Variant &name, const tl::Variant &value, bool inverse)
{
return new EdgePairPropertiesFilter (name, value, inverse);
}
static gsi::EdgePairFilterBase *make_ppf2 (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
{
return new EdgePairPropertiesFilter (name, from, to, inverse);
}
static gsi::EdgePairFilterBase *make_pg (const tl::Variant &name, const std::string &glob, bool inverse, bool case_sensitive)
{
tl::GlobPattern pattern (glob);
pattern.set_case_sensitive (case_sensitive);
return new EdgePairPropertiesFilter (name, pattern, inverse);
}
Class<gsi::EdgePairFilterBase> decl_EdgePairFilterBase ("db", "EdgePairFilterBase",
gsi::EdgePairFilterBase::method_decls (true) +
gsi::constructor ("property_glob", &make_pg, gsi::arg ("name"), gsi::arg ("pattern"), gsi::arg ("inverse", false), gsi::arg ("case_sensitive", true),
"@brief Creates a single-valued property filter\n"
"@param name The name of the property to use.\n"
"@param value The glob pattern to match the property value against.\n"
"@param inverse If true, inverts the selection - i.e. all edge pairs without a matching property are selected.\n"
"@param case_sensitive If true, the match is case sensitive (the default), if false, the match is not case sensitive.\n"
"\n"
"Apply this filter with \\EdgePairs#filtered:\n"
"\n"
"@code\n"
"# edge_pairs is a EdgePairs object\n"
"# filtered_edge_pairs contains all edge pairs where the 'net' property starts with 'C':\n"
"filtered_edge_pairs = edge_pairs.filtered(RBA::EdgePairFilterBase::property_glob('net', 'C*'))\n"
"@/code\n"
"\n"
"This feature has been introduced in version 0.30."
) +
gsi::constructor ("property_filter", &make_ppf1, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("inverse", false),
"@brief Creates a single-valued property filter\n"
"@param name The name of the property to use.\n"
"@param value The value against which the property is checked (exact match).\n"
"@param inverse If true, inverts the selection - i.e. all edge pairs without a property with the given name and value are selected.\n"
"\n"
"Apply this filter with \\EdgePairs#filtered. See \\property_glob for an example.\n"
"\n"
"This feature has been introduced in version 0.30."
) +
gsi::constructor ("property_filter_bounded", &make_ppf2, gsi::arg ("name"), gsi::arg ("from"), gsi::arg ("to"), gsi::arg ("inverse", false),
"@brief Creates a single-valued property filter\n"
"@param name The name of the property to use.\n"
"@param from The lower value against which the property is checked or 'nil' if no lower bound shall be used.\n"
"@param to The upper value against which the property is checked or 'nil' if no upper bound shall be used.\n"
"@param inverse If true, inverts the selection - i.e. all edge pairs without a property with the given name and value range are selected.\n"
"\n"
"This version does a bounded match. The value of the propery needs to be larger or equal to 'from' and less than 'to'.\n"
"Apply this filter with \\EdgePairs#filtered. See \\property_glob for an example.\n"
"\n"
"This feature has been introduced in version 0.30."
),
"@hide"
);
Class<gsi::EdgePairFilterImpl> decl_EdgePairFilterImpl (decl_EdgePairFilterBase, "db", "EdgePairFilter",
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"
@ -411,12 +476,12 @@ 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)
static db::EdgePairs filtered (const db::EdgePairs *r, const gsi::EdgePairFilterBase *f)
{
return r->filtered (*f);
}
static void filter (db::EdgePairs *r, const EdgePairFilterImpl *f)
static void filter (db::EdgePairs *r, const gsi::EdgePairFilterBase *f)
{
r->filter (*f);
}

View File

@ -27,6 +27,7 @@
#include "dbRegion.h"
#include "dbDeepTexts.h"
#include "dbTextsUtils.h"
#include "dbPropertiesFilter.h"
#include "gsiDeclDbContainerHelpers.h"
@ -36,8 +37,10 @@ namespace gsi
// ---------------------------------------------------------------------------------
// TextFilter binding
typedef shape_filter_impl<db::TextFilterBase> TextFilterBase;
class TextFilterImpl
: public shape_filter_impl<db::TextFilterBase>
: public TextFilterBase
{
public:
TextFilterImpl () { }
@ -64,8 +67,70 @@ private:
TextFilterImpl (const TextFilterImpl &);
};
Class<gsi::TextFilterImpl> decl_TextFilterImpl ("db", "TextFilter",
TextFilterImpl::method_decls (false) +
typedef db::generic_properties_filter<gsi::TextFilterBase, db::Text> TextPropertiesFilter;
static gsi::TextFilterBase *make_ppf1 (const tl::Variant &name, const tl::Variant &value, bool inverse)
{
return new TextPropertiesFilter (name, value, inverse);
}
static gsi::TextFilterBase *make_ppf2 (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
{
return new TextPropertiesFilter (name, from, to, inverse);
}
static gsi::TextFilterBase *make_pg (const tl::Variant &name, const std::string &glob, bool inverse, bool case_sensitive)
{
tl::GlobPattern pattern (glob);
pattern.set_case_sensitive (case_sensitive);
return new TextPropertiesFilter (name, pattern, inverse);
}
Class<gsi::TextFilterBase> decl_TextFilterBase ("db", "TextFilterBase",
gsi::TextFilterBase::method_decls (false) +
gsi::constructor ("property_glob", &make_pg, gsi::arg ("name"), gsi::arg ("pattern"), gsi::arg ("inverse", false), gsi::arg ("case_sensitive", true),
"@brief Creates a single-valued property filter\n"
"@param name The name of the property to use.\n"
"@param value The glob pattern to match the property value against.\n"
"@param inverse If true, inverts the selection - i.e. all texts without a matching property are selected.\n"
"@param case_sensitive If true, the match is case sensitive (the default), if false, the match is not case sensitive.\n"
"\n"
"Apply this filter with \\Texts#filtered:\n"
"\n"
"@code\n"
"# texts is a Texts object\n"
"# filtered_texts contains all texts where the 'net' property starts with 'C':\n"
"filtered_texts = texts.filtered(RBA::TextFilterBase::property_glob('net', 'C*'))\n"
"@/code\n"
"\n"
"This feature has been introduced in version 0.30."
) +
gsi::constructor ("property_filter", &make_ppf1, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("inverse", false),
"@brief Creates a single-valued property filter\n"
"@param name The name of the property to use.\n"
"@param value The value against which the property is checked (exact match).\n"
"@param inverse If true, inverts the selection - i.e. all texts without a property with the given name and value are selected.\n"
"\n"
"Apply this filter with \\Texts#filtered. See \\property_glob for an example.\n"
"\n"
"This feature has been introduced in version 0.30."
) +
gsi::constructor ("property_filter_bounded", &make_ppf2, gsi::arg ("name"), gsi::arg ("from"), gsi::arg ("to"), gsi::arg ("inverse", false),
"@brief Creates a single-valued property filter\n"
"@param name The name of the property to use.\n"
"@param from The lower value against which the property is checked or 'nil' if no lower bound shall be used.\n"
"@param to The upper value against which the property is checked or 'nil' if no upper bound shall be used.\n"
"@param inverse If true, inverts the selection - i.e. all texts without a property with the given name and value range are selected.\n"
"\n"
"This version does a bounded match. The value of the propery needs to be larger or equal to 'from' and less than 'to'.\n"
"Apply this filter with \\Texts#filtered. See \\property_glob for an example.\n"
"\n"
"This feature has been introduced in version 0.30."
),
"@hide"
);
Class<gsi::TextFilterImpl> decl_TextFilterImpl (decl_TextFilterBase, "db", "TextFilter",
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"
@ -318,12 +383,12 @@ 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)
static db::Texts filtered (const db::Texts *r, const gsi::TextFilterBase *f)
{
return r->filtered (*f);
}
static void filter (db::Texts *r, const TextFilterImpl *f)
static void filter (db::Texts *r, const gsi::TextFilterBase *f)
{
r->filter (*f);
}

View File

@ -26,8 +26,8 @@ load("test_prologue.rb")
# normalizes a specification string for region, edges etc.
# such that the order of the objects becomes irrelevant
def csort(s)
# splits at ");(" without consuming the brackets
s.split(/(?<=\));(?=\()/).sort.join(";")
# splits at ");(" or "};(" without consuming the brackets
s.split(/(?<=[\)\}]);(?=\()/).sort.join(";")
end
class PerpendicularEdgesFilter < RBA::EdgePairFilter
@ -598,6 +598,56 @@ class DBEdgePairs_TestClass < TestBase
end
# properties
def test_prop_filters
r = RBA::EdgePairs::new
r.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(0, 10, 100, 210)), { "one" => -1 }))
r.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(1, 1, 101, 201), RBA::Edge::new(1, 11, 101, 211)), { "one" => 17 }))
r.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(2, 2, 102, 202), RBA::Edge::new(2, 12, 102, 212)), { "one" => 42 }))
assert_equal(r.filtered(RBA::EdgePairFilter::property_filter("one", 11)).to_s, "")
assert_equal(r.filtered(RBA::EdgePairFilter::property_filter("two", 17)).to_s, "")
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter("one", 17)).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter("one", 17, true)).to_s), csort("(0,0;100,200)/(0,10;100,210){one=>-1};(2,2;102,202)/(2,12;102,212){one=>42}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", 17, nil)).to_s), csort("(2,2;102,202)/(2,12;102,212){one=>42};(1,1;101,201)/(1,11;101,211){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", 17, 18)).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("(2,2;102,202)/(2,12;102,212){one=>42};(0,0;100,200)/(0,10;100,210){one=>-1}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", nil, 18)).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17};(0,0;100,200)/(0,10;100,210){one=>-1}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_glob("one", "1*")).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_glob("one", "1*", true)).to_s), csort("(2,2;102,202)/(2,12;102,212){one=>42};(0,0;100,200)/(0,10;100,210){one=>-1}"))
ly = RBA::Layout::new
top = ly.create_cell("TOP")
l1 = ly.layer(1, 0)
s = top.shapes(l1)
s.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(0, 10, 100, 210)), { "one" => -1 }))
s.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(1, 1, 101, 201), RBA::Edge::new(1, 11, 101, 211)), { "one" => 17 }))
s.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(2, 2, 102, 202), RBA::Edge::new(2, 12, 102, 212)), { "one" => 42 }))
dss = RBA::DeepShapeStore::new
iter = top.begin_shapes_rec(l1)
iter.enable_properties()
r = RBA::EdgePairs::new(iter, dss)
assert_equal(r.filtered(RBA::EdgePairFilter::property_filter("one", 11)).to_s, "")
assert_equal(r.filtered(RBA::EdgePairFilter::property_filter("two", 17)).to_s, "")
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter("one", 17)).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter("one", 17, true)).to_s), csort("(0,0;100,200)/(0,10;100,210){one=>-1};(2,2;102,202)/(2,12;102,212){one=>42}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", 17, nil)).to_s), csort("(2,2;102,202)/(2,12;102,212){one=>42};(1,1;101,201)/(1,11;101,211){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", 17, 18)).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("(2,2;102,202)/(2,12;102,212){one=>42};(0,0;100,200)/(0,10;100,210){one=>-1}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", nil, 18)).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17};(0,0;100,200)/(0,10;100,210){one=>-1}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_glob("one", "1*")).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_glob("one", "1*", true)).to_s), csort("(2,2;102,202)/(2,12;102,212){one=>42};(0,0;100,200)/(0,10;100,210){one=>-1}"))
rr = r.dup
rr.filter(RBA::EdgePairFilter::property_filter("one", 17))
assert_equal(csort(rr.to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}"))
end
end

View File

@ -26,8 +26,8 @@ load("test_prologue.rb")
# normalizes a specification string for region, edges etc.
# such that the order of the objects becomes irrelevant
def csort(s)
# splits at ");(" without consuming the brackets
s.split(/(?<=\));(?=\()/).sort.join(";")
# splits at ");(" or "};(" without consuming the brackets
s.split(/(?<=[\)\}]);(?=\()/).sort.join(";")
end
class TextStringLengthFilter < RBA::TextFilter
@ -467,6 +467,56 @@ class DBTexts_TestClass < TestBase
end
# properties
def test_prop_filters
r = RBA::Texts::new
r.insert(RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::R0), { "one" => -1 }))
r.insert(RBA::TextWithProperties::new(RBA::Text::new("uvw", RBA::Trans::R0), { "one" => 17 }))
r.insert(RBA::TextWithProperties::new(RBA::Text::new("xyz", RBA::Trans::R0), { "one" => 42 }))
assert_equal(r.filtered(RBA::TextFilter::property_filter("one", 11)).to_s, "")
assert_equal(r.filtered(RBA::TextFilter::property_filter("two", 17)).to_s, "")
assert_equal(csort(r.filtered(RBA::TextFilter::property_filter("one", 17)).to_s), csort("('uvw',r0 0,0){one=>17}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_filter("one", 17, true)).to_s), csort("('abc',r0 0,0){one=>-1};('xyz',r0 0,0){one=>42}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", 17, nil)).to_s), csort("('xyz',r0 0,0){one=>42};('uvw',r0 0,0){one=>17}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", 17, 18)).to_s), csort("('uvw',r0 0,0){one=>17}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("('xyz',r0 0,0){one=>42};('abc',r0 0,0){one=>-1}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", nil, 18)).to_s), csort("('uvw',r0 0,0){one=>17};('abc',r0 0,0){one=>-1}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_glob("one", "1*")).to_s), csort("('uvw',r0 0,0){one=>17}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_glob("one", "1*", true)).to_s), csort("('xyz',r0 0,0){one=>42};('abc',r0 0,0){one=>-1}"))
ly = RBA::Layout::new
top = ly.create_cell("TOP")
l1 = ly.layer(1, 0)
s = top.shapes(l1)
s.insert(RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::R0), { "one" => -1 }))
s.insert(RBA::TextWithProperties::new(RBA::Text::new("uvw", RBA::Trans::R0), { "one" => 17 }))
s.insert(RBA::TextWithProperties::new(RBA::Text::new("xyz", RBA::Trans::R0), { "one" => 42 }))
dss = RBA::DeepShapeStore::new
iter = top.begin_shapes_rec(l1)
iter.enable_properties()
r = RBA::Texts::new(iter, dss)
assert_equal(r.filtered(RBA::TextFilter::property_filter("one", 11)).to_s, "")
assert_equal(r.filtered(RBA::TextFilter::property_filter("two", 17)).to_s, "")
assert_equal(csort(r.filtered(RBA::TextFilter::property_filter("one", 17)).to_s), csort("('uvw',r0 0,0){one=>17}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_filter("one", 17, true)).to_s), csort("('abc',r0 0,0){one=>-1};('xyz',r0 0,0){one=>42}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", 17, nil)).to_s), csort("('xyz',r0 0,0){one=>42};('uvw',r0 0,0){one=>17}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", 17, 18)).to_s), csort("('uvw',r0 0,0){one=>17}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("('xyz',r0 0,0){one=>42};('abc',r0 0,0){one=>-1}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", nil, 18)).to_s), csort("('uvw',r0 0,0){one=>17};('abc',r0 0,0){one=>-1}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_glob("one", "1*")).to_s), csort("('uvw',r0 0,0){one=>17}"))
assert_equal(csort(r.filtered(RBA::TextFilter::property_glob("one", "1*", true)).to_s), csort("('xyz',r0 0,0){one=>42};('abc',r0 0,0){one=>-1}"))
rr = r.dup
rr.filter(RBA::TextFilter::property_filter("one", 17))
assert_equal(csort(rr.to_s), csort("('uvw',r0 0,0){one=>17}"))
end
end