Property filter for edges too.

This commit is contained in:
Matthias Koefferlein 2025-02-16 13:35:49 +01:00
parent a219576296
commit 1fed2767e8
12 changed files with 513 additions and 162 deletions

View File

@ -79,6 +79,7 @@ SOURCES = \
dbPolygonNeighborhood.cc \
dbPolygonTools.cc \
dbPolygonGenerators.cc \
dbPropertiesFilter.cc \
dbPropertiesRepository.cc \
dbReader.cc \
dbRecursiveInstanceIterator.cc \
@ -315,6 +316,7 @@ HEADERS = \
dbPolygonNeighborhood.h \
dbPolygonTools.h \
dbPolygonGenerators.h \
dbPropertiesFilter.h \
dbPropertiesRepository.h \
dbPropertyConstraint.h \
dbReader.h \

View File

@ -1064,7 +1064,7 @@ private:
child (0)->compute_local (cache, layout, cell, interactions, one, proc);
if (m_sum_of) {
if (mp_filter->selected (one.front ())) {
if (mp_filter->selected_set (one.front ())) {
results.front ().insert (one.front ().begin (), one.front ().end ());
}
} else {

View File

@ -62,7 +62,7 @@ public:
* @brief Filters the edge set
* If this method returns true, the edges are kept. Otherwise they are discarded.
*/
virtual bool selected (const std::unordered_set<db::EdgeWithProperties> &edge) const = 0;
virtual bool selected_set (const std::unordered_set<db::EdgeWithProperties> &edge) const = 0;
/**
* @brief Returns the transformation reducer for building cell variants

View File

@ -79,7 +79,7 @@ struct DB_PUBLIC EdgeLengthFilter
/**
* @brief Returns true if the total edge length matches the criterion
*/
bool selected (const std::unordered_set<db::EdgeWithProperties> &edges) const
bool selected_set (const std::unordered_set<db::EdgeWithProperties> &edges) const
{
length_type l = 0;
for (std::unordered_set<db::EdgeWithProperties>::const_iterator e = edges.begin (); e != edges.end (); ++e) {
@ -209,7 +209,7 @@ struct DB_PUBLIC EdgeOrientationFilter
/**
* @brief Returns true if all edge orientations match the criterion
*/
virtual bool selected (const std::unordered_set<db::EdgeWithProperties> &edges) const
virtual bool selected_set (const std::unordered_set<db::EdgeWithProperties> &edges) const
{
for (std::unordered_set<db::EdgeWithProperties>::const_iterator e = edges.begin (); e != edges.end (); ++e) {
if (! selected (*e, e->properties_id ())) {
@ -279,7 +279,7 @@ struct DB_PUBLIC SpecialEdgeOrientationFilter
/**
* @brief Returns true if all edge orientations match the criterion
*/
virtual bool selected (const std::unordered_set<db::EdgeWithProperties> &edges) const
virtual bool selected_set (const std::unordered_set<db::EdgeWithProperties> &edges) const
{
for (std::unordered_set<db::EdgeWithProperties>::const_iterator e = edges.begin (); e != edges.end (); ++e) {
if (! selected (*e, e->properties_id ())) {
@ -319,6 +319,29 @@ private:
db::OrthogonalTransformationReducer m_vars;
};
/**
* @brief A filter implementation which implements the set filters through "all must match"
*/
struct DB_PUBLIC AllEdgesMustMatchFilter
: public EdgeFilterBase
{
/**
* @brief Constructor
*/
AllEdgesMustMatchFilter () { }
virtual bool selected_set (const std::unordered_set<db::EdgeWithProperties> &edges) const
{
for (std::unordered_set<db::EdgeWithProperties>::const_iterator p = edges.begin (); p != edges.end (); ++p) {
if (! selected (*p, p->properties_id ())) {
return false;
}
}
return true;
}
};
/**
* @brief A predicate defining edge a interacts with b
*/

View File

@ -0,0 +1,82 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbPropertiesFilter.h"
namespace db
{
PropertiesFilter::PropertiesFilter (const tl::Variant &name, const tl::Variant &value, bool inverse)
: m_name_id (db::property_names_id (name)), m_value_from (value), m_exact (true), m_glob (false), m_inverse (inverse)
{
// .. nothing yet ..
}
PropertiesFilter::PropertiesFilter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
: m_name_id (db::property_names_id (name)), m_value_from (from), m_value_to (to), m_exact (false), m_glob (false), m_inverse (inverse)
{
// .. nothing yet ..
}
PropertiesFilter::PropertiesFilter (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse)
: m_name_id (db::property_names_id (name)), m_pattern (pattern), m_exact (true), m_glob (true), m_inverse (inverse)
{
// .. nothing yet ..
}
bool
PropertiesFilter::prop_selected (db::properties_id_type prop_id) const
{
tl::MutexLocker locker (&m_lock);
auto c = m_cache.find (prop_id);
if (c != m_cache.end ()) {
return c->second;
}
bool res = prop_selected_impl (prop_id);
m_cache.insert (std::make_pair (prop_id, res));
return res;
}
bool
PropertiesFilter::prop_selected_impl (db::properties_id_type prop_id) const
{
const db::PropertiesSet &ps = db::properties (prop_id);
if (ps.has_value (m_name_id)) {
const tl::Variant &value = ps.value (m_name_id);
if (m_glob) {
return m_pattern.match (value.to_string ()) != m_inverse;
} else if (m_exact) {
return (value == m_value_from) != m_inverse;
} else {
return ((m_value_from.is_nil () || ! (value < m_value_from)) && (m_value_to.is_nil () || value < m_value_to)) != m_inverse;
}
} else {
return m_inverse;
}
}
}

View File

@ -0,0 +1,132 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_dbPropertiesFilter
#define HDR_dbPropertiesFilter
#include "dbCommon.h"
#include "dbPropertiesRepository.h"
#include "dbPolygon.h"
#include "tlVariant.h"
#include "tlGlobPattern.h"
#include "tlThreads.h"
#include <map>
namespace db
{
/**
* @brief A properties filter
*
* This is a base class for PolygonFilters, EdgeFilters etc. for selecting
* Polygons from Regions by property.
*/
class DB_PUBLIC PropertiesFilter
{
public:
PropertiesFilter (const tl::Variant &name, const tl::Variant &value, bool inverse);
PropertiesFilter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse);
PropertiesFilter (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse);
bool prop_selected (db::properties_id_type prop_id) const;
private:
bool prop_selected_impl (db::properties_id_type prop_id) const;
mutable std::map<db::properties_id_type, bool> m_cache;
db::property_names_id_type m_name_id;
tl::Variant m_value_from, m_value_to;
tl::GlobPattern m_pattern;
bool m_exact;
bool m_glob;
bool m_inverse;
mutable tl::Mutex m_lock;
};
template <class PolygonFilter>
class polygon_properties_filter
: public PolygonFilter, public PropertiesFilter
{
public:
polygon_properties_filter<PolygonFilter> (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse)
: PropertiesFilter (name, pattern, inverse)
{
// .. nothing yet ..
}
polygon_properties_filter<PolygonFilter> (const tl::Variant &name, const tl::Variant &value, bool inverse)
: PropertiesFilter (name, value, inverse)
{
// .. nothing yet ..
}
polygon_properties_filter<PolygonFilter> (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
: PropertiesFilter (name, from, to, inverse)
{
// .. nothing yet ..
}
bool selected (const db::Polygon &, db::properties_id_type prop_id) const
{
return PropertiesFilter::prop_selected (prop_id);
}
bool selected (const db::PolygonRef &, db::properties_id_type prop_id) const
{
return PropertiesFilter::prop_selected (prop_id);
}
};
template <class BasicFilter, class ShapeType>
class generic_properties_filter
: public BasicFilter, public PropertiesFilter
{
public:
generic_properties_filter<BasicFilter, ShapeType> (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse)
: PropertiesFilter (name, pattern, inverse)
{
// .. nothing yet ..
}
generic_properties_filter<BasicFilter, ShapeType> (const tl::Variant &name, const tl::Variant &value, bool inverse)
: PropertiesFilter (name, value, inverse)
{
// .. nothing yet ..
}
generic_properties_filter<BasicFilter, ShapeType> (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
: PropertiesFilter (name, from, to, inverse)
{
// .. nothing yet ..
}
bool selected (const ShapeType &, db::properties_id_type prop_id) const
{
return PropertiesFilter::prop_selected (prop_id);
}
};
}
#endif

View File

@ -31,6 +31,7 @@
#include "dbRegion.h"
#include "dbOriginalLayerRegion.h"
#include "dbLayoutUtils.h"
#include "dbPropertiesFilter.h"
#include "gsiDeclDbContainerHelpers.h"
@ -40,8 +41,10 @@ namespace gsi
// ---------------------------------------------------------------------------------
// EdgeFilter binding
typedef shape_filter_impl<db::AllEdgesMustMatchFilter> EdgeFilterBase;
class EdgeFilterImpl
: public shape_filter_impl<db::EdgeFilterBase>
: public gsi::EdgeFilterBase
{
public:
EdgeFilterImpl () { }
@ -60,25 +63,6 @@ public:
}
}
// Returns true if all edges match the criterion
virtual bool selected (const std::unordered_set<db::EdgeWithProperties> &edges) const
{
if (f_selected.can_issue ()) {
for (std::unordered_set<db::EdgeWithProperties>::const_iterator e = edges.begin (); e != edges.end (); ++e) {
if (! f_selected.issue<EdgeFilterImpl, bool, const db::EdgeWithProperties &> (&EdgeFilterImpl::issue_selected, *e)) {
return false;
}
}
} else {
for (std::unordered_set<db::EdgeWithProperties>::const_iterator e = edges.begin (); e != edges.end (); ++e) {
if (! issue_selected (*e)) {
return false;
}
}
}
return true;
}
gsi::Callback f_selected;
private:
@ -87,8 +71,70 @@ private:
EdgeFilterImpl (const EdgeFilterImpl &);
};
Class<gsi::EdgeFilterImpl> decl_EdgeFilterImpl ("db", "EdgeFilter",
EdgeFilterImpl::method_decls (true) +
typedef db::generic_properties_filter<gsi::EdgeFilterBase, db::Edge> EdgePropertiesFilter;
static gsi::EdgeFilterBase *make_ppf1 (const tl::Variant &name, const tl::Variant &value, bool inverse)
{
return new EdgePropertiesFilter (name, value, inverse);
}
static gsi::EdgeFilterBase *make_ppf2 (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
{
return new EdgePropertiesFilter (name, from, to, inverse);
}
static gsi::EdgeFilterBase *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 EdgePropertiesFilter (name, pattern, inverse);
}
Class<gsi::EdgeFilterBase> decl_EdgeFilterBase ("db", "EdgeFilterBase",
gsi::EdgeFilterBase::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 edges 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 \\Edges#filtered:\n"
"\n"
"@code\n"
"# edges is a Edges object\n"
"# filtered_edges contains all edges where the 'net' property starts with 'C':\n"
"filtered_edges = edges.filtered(RBA::EdgeFilterBase::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 edges without a property with the given name and value are selected.\n"
"\n"
"Apply this filter with \\Edges#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 edges 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 \\Edges#filtered. See \\property_glob for an example.\n"
"\n"
"This feature has been introduced in version 0.30."
),
"@hide"
);
Class<gsi::EdgeFilterImpl> decl_EdgeFilterImpl (decl_EdgeFilterBase, "db", "EdgeFilter",
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"
@ -455,12 +501,12 @@ 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)
static db::Edges filtered (const db::Edges *r, const gsi::EdgeFilterBase *f)
{
return r->filtered (*f);
}
static void filter (db::Edges *r, const EdgeFilterImpl *f)
static void filter (db::Edges *r, const gsi::EdgeFilterBase *f)
{
r->filter (*f);
}

View File

@ -37,6 +37,7 @@
#include "dbCompoundOperation.h"
#include "dbLayoutToNetlist.h"
#include "dbPropertiesRepository.h"
#include "dbPropertiesFilter.h"
#include "tlGlobPattern.h"
#include "gsiDeclDbContainerHelpers.h"
@ -51,12 +52,7 @@ namespace gsi
// ---------------------------------------------------------------------------------
// PolygonFilter binding
class PolygonFilterBase
: public shape_filter_impl<db::AllMustMatchFilter>
{
public:
PolygonFilterBase () { }
};
typedef shape_filter_impl<db::AllMustMatchFilter> PolygonFilterBase;
class PolygonFilterImpl
: public PolygonFilterBase
@ -93,128 +89,41 @@ private:
PolygonFilterImpl (const PolygonFilterImpl &);
};
/**
* @brief A properties filter
*/
class PropertiesFilter
{
public:
PropertiesFilter (const tl::Variant &name, const tl::Variant &value, bool inverse)
: m_name_id (db::property_names_id (name)), m_value_from (value), m_exact (true), m_glob (false), m_inverse (inverse)
{
// .. nothing yet ..
}
typedef db::polygon_properties_filter<gsi::PolygonFilterBase> PolygonPropertiesFilter;
PropertiesFilter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
: m_name_id (db::property_names_id (name)), m_value_from (from), m_value_to (to), m_exact (false), m_glob (false), m_inverse (inverse)
{
// .. nothing yet ..
}
PropertiesFilter (const tl::Variant &name, const std::string &pattern, bool inverse)
: m_name_id (db::property_names_id (name)), m_pattern (pattern), m_exact (true), m_glob (true), m_inverse (inverse)
{
// .. nothing yet ..
}
bool prop_selected (db::properties_id_type prop_id) const
{
auto c = m_cache.find (prop_id);
if (c != m_cache.end ()) {
return c->second;
}
bool res = prop_selected_impl (prop_id);
m_cache.insert (std::make_pair (prop_id, res));
return res;
}
private:
bool prop_selected_impl (db::properties_id_type prop_id) const
{
const db::PropertiesSet &ps = db::properties (prop_id);
if (ps.has_value (m_name_id)) {
const tl::Variant &value = ps.value (m_name_id);
if (m_glob) {
return m_pattern.match (value.to_string ()) != m_inverse;
} else if (m_exact) {
return (value == m_value_from) != m_inverse;
} else {
return ((m_value_from.is_nil () || ! (value < m_value_from)) && (m_value_to.is_nil () || value < m_value_to)) != m_inverse;
}
} else {
return m_inverse;
}
}
mutable std::map<db::properties_id_type, bool> m_cache;
db::property_names_id_type m_name_id;
tl::Variant m_value_from, m_value_to;
tl::GlobPattern m_pattern;
bool m_exact;
bool m_glob;
bool m_inverse;
};
class PolygonPropertiesFilter
: public PolygonFilterBase, public PropertiesFilter
{
public:
PolygonPropertiesFilter (const tl::Variant &name, const std::string &pattern, bool inverse)
: PropertiesFilter (name, pattern, inverse)
{
// .. nothing yet ..
}
PolygonPropertiesFilter (const tl::Variant &name, const tl::Variant &value, bool inverse)
: PropertiesFilter (name, value, inverse)
{
// .. nothing yet ..
}
PolygonPropertiesFilter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
: PropertiesFilter (name, from, to, inverse)
{
// .. nothing yet ..
}
bool selected (const db::Polygon &, db::properties_id_type prop_id) const
{
return PropertiesFilter::prop_selected (prop_id);
}
bool selected (const db::PolygonRef &, db::properties_id_type prop_id) const
{
return PropertiesFilter::prop_selected (prop_id);
}
};
static PolygonFilterBase *make_ppf1 (const tl::Variant &name, const tl::Variant &value, bool inverse)
static gsi::PolygonFilterBase *make_ppf1 (const tl::Variant &name, const tl::Variant &value, bool inverse)
{
return new PolygonPropertiesFilter (name, value, inverse);
}
static PolygonFilterBase *make_ppf2 (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
static gsi::PolygonFilterBase *make_ppf2 (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
{
return new PolygonPropertiesFilter (name, from, to, inverse);
}
static PolygonFilterBase *make_pg (const tl::Variant &name, const std::string &glob, bool inverse)
static gsi::PolygonFilterBase *make_pg (const tl::Variant &name, const std::string &glob, bool inverse, bool case_sensitive)
{
return new PolygonPropertiesFilter (name, glob, inverse);
tl::GlobPattern pattern (glob);
pattern.set_case_sensitive (case_sensitive);
return new PolygonPropertiesFilter (name, pattern, inverse);
}
Class<gsi::PolygonFilterBase> decl_PolygonFilterBase ("db", "PolygonFilterBase",
gsi::constructor ("property_glob", &make_pg, gsi::arg ("name"), gsi::arg ("pattern"), gsi::arg ("inverse", false),
gsi::PolygonFilterBase::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 polygons 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 \\Region#filtered.\n"
"Apply this filter with \\Region#filtered:\n"
"\n"
"@code\n"
"# region is a Region object\n"
"# filtered_region contains all polygons where the 'net' property starts with 'C':\n"
"filtered_region = region.filtered(RBA::PolygonFilterBase::property_glob('net', 'C*'))\n"
"@/code\n"
"\n"
"This feature has been introduced in version 0.30."
) +
@ -224,7 +133,7 @@ Class<gsi::PolygonFilterBase> decl_PolygonFilterBase ("db", "PolygonFilterBase",
"@param value The value against which the property is checked (exact match).\n"
"@param inverse If true, inverts the selection - i.e. all polygons without a property with the given name and value are selected.\n"
"\n"
"Apply this filter with \\Region#filtered.\n"
"Apply this filter with \\Region#filtered. See \\property_glob for an example.\n"
"\n"
"This feature has been introduced in version 0.30."
) +
@ -236,7 +145,7 @@ Class<gsi::PolygonFilterBase> decl_PolygonFilterBase ("db", "PolygonFilterBase",
"@param inverse If true, inverts the selection - i.e. all polygons 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 \\Region#filtered.\n"
"Apply this filter with \\Region#filtered. See \\property_glob for an example.\n"
"\n"
"This feature has been introduced in version 0.30."
),
@ -244,7 +153,6 @@ Class<gsi::PolygonFilterBase> decl_PolygonFilterBase ("db", "PolygonFilterBase",
);
Class<gsi::PolygonFilterImpl> decl_PolygonFilterImpl (decl_PolygonFilterBase, "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"

View File

@ -0,0 +1,103 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbPropertiesFilter.h"
#include "tlUnitTest.h"
namespace {
/**
* @brief Installs a temporary repository instance for testing
*
* By using a temp instance, we do not disturb other tests.
*/
class TempPropertiesRepository
{
public:
TempPropertiesRepository ()
{
db::PropertiesRepository::replace_instance_temporarily (&m_temp);
}
~TempPropertiesRepository ()
{
db::PropertiesRepository::replace_instance_temporarily (0);
}
private:
db::PropertiesRepository m_temp;
};
}
TEST(1)
{
TempPropertiesRepository temp_pr;
db::PropertiesSet ps;
ps.insert ("net", 17);
auto net17 = properties_id (ps);
ps.clear ();
ps.insert ("net", 1);
auto net1 = properties_id (ps);
ps.clear ();
ps.insert ("net", 42);
ps.insert ("not", "never");
auto net42 = properties_id (ps);
EXPECT_EQ (db::PropertiesFilter ("not", tl::Variant ("never"), false).prop_selected (net17), false);
EXPECT_EQ (db::PropertiesFilter ("not", tl::Variant ("never"), true).prop_selected (net17), true);
EXPECT_EQ (db::PropertiesFilter ("not", tl::Variant ("never"), false).prop_selected (net1), false);
EXPECT_EQ (db::PropertiesFilter ("not", tl::Variant ("never"), true).prop_selected (net1), true);
EXPECT_EQ (db::PropertiesFilter ("not", tl::Variant ("never"), false).prop_selected (net42), true);
EXPECT_EQ (db::PropertiesFilter ("not", tl::Variant ("never"), true).prop_selected (net42), false);
EXPECT_EQ (db::PropertiesFilter ("doesnotexist", tl::Variant ("never"), false).prop_selected (net42), false);
EXPECT_EQ (db::PropertiesFilter ("doesnotexist", tl::Variant ("never"), true).prop_selected (net42), true);
EXPECT_EQ (db::PropertiesFilter ("net", 17, false).prop_selected (net17), true);
EXPECT_EQ (db::PropertiesFilter ("net", 17, true).prop_selected (net17), false);
EXPECT_EQ (db::PropertiesFilter ("net", 17, false).prop_selected (net1), false);
EXPECT_EQ (db::PropertiesFilter ("net", 17, true).prop_selected (net1), true);
EXPECT_EQ (db::PropertiesFilter ("net", tl::Variant (), 17, false).prop_selected (net1), true);
EXPECT_EQ (db::PropertiesFilter ("net", tl::Variant (), 17, true).prop_selected (net1), false);
EXPECT_EQ (db::PropertiesFilter ("net", tl::Variant (), 1, false).prop_selected (net1), false);
EXPECT_EQ (db::PropertiesFilter ("net", tl::Variant (), 1, true).prop_selected (net1), true);
EXPECT_EQ (db::PropertiesFilter ("net", 0, 2, false).prop_selected (net1), true);
EXPECT_EQ (db::PropertiesFilter ("net", 0, 2, true).prop_selected (net1), false);
EXPECT_EQ (db::PropertiesFilter ("net", 0, 1, false).prop_selected (net1), false);
EXPECT_EQ (db::PropertiesFilter ("net", 0, 1, true).prop_selected (net1), true);
EXPECT_EQ (db::PropertiesFilter ("net", 1, 2, false).prop_selected (net1), true);
EXPECT_EQ (db::PropertiesFilter ("net", 1, 2, true).prop_selected (net1), false);
EXPECT_EQ (db::PropertiesFilter ("net", -1, tl::Variant (), false).prop_selected (net1), true);
EXPECT_EQ (db::PropertiesFilter ("net", -1, tl::Variant (), true).prop_selected (net1), false);
EXPECT_EQ (db::PropertiesFilter ("net", 2, tl::Variant (), false).prop_selected (net1), false);
EXPECT_EQ (db::PropertiesFilter ("net", 2, tl::Variant (), true).prop_selected (net1), true);
EXPECT_EQ (db::PropertiesFilter ("net", tl::GlobPattern ("1*"), false).prop_selected (net1), true);
EXPECT_EQ (db::PropertiesFilter ("net", tl::GlobPattern ("1*"), true).prop_selected (net1), false);
EXPECT_EQ (db::PropertiesFilter ("not", tl::GlobPattern ("1*"), false).prop_selected (net1), false);
EXPECT_EQ (db::PropertiesFilter ("not", tl::GlobPattern ("1*"), true).prop_selected (net1), true);
EXPECT_EQ (db::PropertiesFilter ("net", tl::GlobPattern ("1*"), false).prop_selected (net17), true);
EXPECT_EQ (db::PropertiesFilter ("net", tl::GlobPattern ("1*"), true).prop_selected (net17), false);
EXPECT_EQ (db::PropertiesFilter ("net", tl::GlobPattern ("1*"), false).prop_selected (net42), false);
EXPECT_EQ (db::PropertiesFilter ("net", tl::GlobPattern ("1*"), true).prop_selected (net42), true);
}

View File

@ -13,6 +13,7 @@ SOURCES = \
dbLogTests.cc \
dbObjectWithPropertiesTests.cc \
dbPolygonNeighborhoodTests.cc \
dbPropertiesFilterTests.cc \
dbRecursiveInstanceIteratorTests.cc \
dbRegionCheckUtilsTests.cc \
dbTriangleTests.cc \

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 ParallelFilter < RBA::EdgeFilter
@ -1037,6 +1037,56 @@ class DBEdges_TestClass < TestBase
end
# properties
def test_prop_filters
r = RBA::Edges::new
r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 200), { "one" => -1 }))
r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(1, 1, 101, 201), { "one" => 17 }))
r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(2, 2, 102, 202), { "one" => 42 }))
assert_equal(r.filtered(RBA::EdgeFilter::property_filter("one", 11)).to_s, "")
assert_equal(r.filtered(RBA::EdgeFilter::property_filter("two", 17)).to_s, "")
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter("one", 17)).to_s), csort("(1,1;101,201){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter("one", 17, true)).to_s), csort("(0,0;100,200){one=>-1};(2,2;102,202){one=>42}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", 17, nil)).to_s), csort("(2,2;102,202){one=>42};(1,1;101,201){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", 17, 18)).to_s), csort("(1,1;101,201){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("(2,2;102,202){one=>42};(0,0;100,200){one=>-1}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", nil, 18)).to_s), csort("(1,1;101,201){one=>17};(0,0;100,200){one=>-1}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_glob("one", "1*")).to_s), csort("(1,1;101,201){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_glob("one", "1*", true)).to_s), csort("(2,2;102,202){one=>42};(0,0;100,200){one=>-1}"))
ly = RBA::Layout::new
top = ly.create_cell("TOP")
l1 = ly.layer(1, 0)
s = top.shapes(l1)
s.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 200), { "one" => -1 }))
s.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(1, 1, 101, 201), { "one" => 17 }))
s.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(2, 2, 102, 202), { "one" => 42 }))
dss = RBA::DeepShapeStore::new
iter = top.begin_shapes_rec(l1)
iter.enable_properties()
r = RBA::Edges::new(iter, dss)
assert_equal(r.filtered(RBA::EdgeFilter::property_filter("one", 11)).to_s, "")
assert_equal(r.filtered(RBA::EdgeFilter::property_filter("two", 17)).to_s, "")
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter("one", 17)).to_s), csort("(1,1;101,201){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter("one", 17, true)).to_s), csort("(0,0;100,200){one=>-1};(2,2;102,202){one=>42}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", 17, nil)).to_s), csort("(2,2;102,202){one=>42};(1,1;101,201){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", 17, 18)).to_s), csort("(1,1;101,201){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("(2,2;102,202){one=>42};(0,0;100,200){one=>-1}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", nil, 18)).to_s), csort("(1,1;101,201){one=>17};(0,0;100,200){one=>-1}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_glob("one", "1*")).to_s), csort("(1,1;101,201){one=>17}"))
assert_equal(csort(r.filtered(RBA::EdgeFilter::property_glob("one", "1*", true)).to_s), csort("(2,2;102,202){one=>42};(0,0;100,200){one=>-1}"))
rr = r.dup
rr.filter(RBA::EdgeFilter::property_filter("one", 17))
assert_equal(csort(rr.to_s), csort("(1,1;101,201){one=>17}"))
end
end
load("test_epilogue.rb")

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 TriangleFilter < RBA::PolygonFilter
@ -1593,14 +1593,14 @@ class DBRegion_TestClass < TestBase
assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 11)).to_s, "")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter("two", 17)).to_s, "")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 17)).to_s, "(1,1;1,201;101,201;101,1){one=>17}")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 17, true)).to_s, "(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, nil)).to_s, "(1,1;1,201;101,201;101,1){one=>17};(2,2;2,202;102,202;102,2){one=>42}")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18)).to_s, "(1,1;1,201;101,201;101,1){one=>17}")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18, true)).to_s, "(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", nil, 18)).to_s, "(1,1;1,201;101,201;101,1){one=>17};(0,0;0,200;100,200;100,0){one=>-1}")
assert_equal(r.filtered(RBA::PolygonFilter::property_glob("one", "1*")).to_s, "(1,1;1,201;101,201;101,1){one=>17}")
assert_equal(r.filtered(RBA::PolygonFilter::property_glob("one", "1*", true)).to_s, "(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}")
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter("one", 17)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter("one", 17, true)).to_s), csort("(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, nil)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17};(2,2;2,202;102,202;102,2){one=>42}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", nil, 18)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17};(0,0;0,200;100,200;100,0){one=>-1}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_glob("one", "1*")).to_s), csort("(1,1;1,201;101,201;101,1){one=>17}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_glob("one", "1*", true)).to_s), csort("(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}"))
ly = RBA::Layout::new
top = ly.create_cell("TOP")
@ -1618,14 +1618,18 @@ class DBRegion_TestClass < TestBase
assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 11)).to_s, "")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter("two", 17)).to_s, "")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 17)).to_s, "(1,1;1,201;101,201;101,1){one=>17}")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 17, true)).to_s, "(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, nil)).to_s, "(1,1;1,201;101,201;101,1){one=>17};(2,2;2,202;102,202;102,2){one=>42}")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18)).to_s, "(1,1;1,201;101,201;101,1){one=>17}")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18, true)).to_s, "(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}")
assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", nil, 18)).to_s, "(0,0;0,200;100,200;100,0){one=>-1};(1,1;1,201;101,201;101,1){one=>17}")
assert_equal(r.filtered(RBA::PolygonFilter::property_glob("one", "1*")).to_s, "(1,1;1,201;101,201;101,1){one=>17}")
assert_equal(r.filtered(RBA::PolygonFilter::property_glob("one", "1*", true)).to_s, "(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}")
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter("one", 17)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter("one", 17, true)).to_s), csort("(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, nil)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17};(2,2;2,202;102,202;102,2){one=>42}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", nil, 18)).to_s), csort("(0,0;0,200;100,200;100,0){one=>-1};(1,1;1,201;101,201;101,1){one=>17}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_glob("one", "1*")).to_s), csort("(1,1;1,201;101,201;101,1){one=>17}"))
assert_equal(csort(r.filtered(RBA::PolygonFilter::property_glob("one", "1*", true)).to_s), csort("(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}"))
rr = r.dup
rr.filter(RBA::PolygonFilter::property_filter("one", 17))
assert_equal(csort(rr.to_s), csort("(1,1;1,201;101,201;101,1){one=>17}"))
end