Merge branch 'SmartRangeFilter' into 'master'
add filtered to SmartRanges See merge request OpenMesh/OpenMesh!254
This commit is contained in:
@@ -65,10 +65,17 @@ struct Identity
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename RangeT, typename HandleT, typename Functor>
|
||||||
|
struct FilteredSmartRangeT;
|
||||||
|
|
||||||
/// Base class for all smart range types
|
/// Base class for all smart range types
|
||||||
template <typename RangeT, typename HandleT>
|
template <typename RangeT, typename HandleT>
|
||||||
struct SmartRangeT
|
struct SmartRangeT
|
||||||
{
|
{
|
||||||
|
using Handle = HandleT;
|
||||||
|
using SmartRange = SmartRangeT<RangeT, HandleT>;
|
||||||
|
using Range = RangeT;
|
||||||
|
|
||||||
// TODO: Someone with better c++ knowledge may improve the code below.
|
// TODO: Someone with better c++ knowledge may improve the code below.
|
||||||
|
|
||||||
/** @brief Computes the sum of elements.
|
/** @brief Computes the sum of elements.
|
||||||
@@ -386,6 +393,63 @@ struct SmartRangeT
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Only iterate over a subset of elements
|
||||||
|
*
|
||||||
|
* Returns a smart range which skips all elements that do not satisfy functor \p f
|
||||||
|
*
|
||||||
|
* @param f Functor that needs to be evaluated to true if the element should not be skipped.
|
||||||
|
*/
|
||||||
|
template <typename Functor>
|
||||||
|
auto filtered(Functor&& f) -> FilteredSmartRangeT<SmartRange, Handle, typename std::decay<Functor>::type>
|
||||||
|
{
|
||||||
|
auto range = static_cast<const RangeT*>(this);
|
||||||
|
auto b = (*range).begin();
|
||||||
|
auto e = (*range).end();
|
||||||
|
return FilteredSmartRangeT<SmartRange, Handle, typename std::decay<Functor>::type>(f, b, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Class which applies a filter when iterating over elements
|
||||||
|
template <typename RangeT, typename HandleT, typename Functor>
|
||||||
|
struct FilteredSmartRangeT : public SmartRangeT<FilteredSmartRangeT<RangeT, HandleT, Functor>, HandleT>
|
||||||
|
{
|
||||||
|
using BaseRange = SmartRangeT<FilteredSmartRangeT<RangeT, HandleT, Functor>, HandleT>;
|
||||||
|
using BaseIterator = decltype((std::declval<typename RangeT::Range>().begin()));
|
||||||
|
|
||||||
|
struct FilteredIterator : public BaseIterator
|
||||||
|
{
|
||||||
|
|
||||||
|
FilteredIterator(Functor f, BaseIterator it, BaseIterator end): BaseIterator(it), f_(f), end_(end)
|
||||||
|
{
|
||||||
|
if (!f_(*(*this))) // if start is not valid go to first valid one
|
||||||
|
operator++();
|
||||||
|
}
|
||||||
|
|
||||||
|
FilteredIterator& operator++()
|
||||||
|
{
|
||||||
|
if (BaseIterator::operator==(end_)) // don't go past end
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
// go to next valid one
|
||||||
|
do
|
||||||
|
BaseIterator::operator++();
|
||||||
|
while (BaseIterator::operator!=(end_) && !f_(*(*this)));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Functor f_;
|
||||||
|
BaseIterator end_;
|
||||||
|
};
|
||||||
|
|
||||||
|
FilteredSmartRangeT(Functor f, BaseIterator begin, BaseIterator end) : f_(f), begin_(begin), end_(end){}
|
||||||
|
FilteredIterator begin() const { return FilteredIterator(f_, begin_, end_); }
|
||||||
|
FilteredIterator end() const { return FilteredIterator(f_, end_, end_); }
|
||||||
|
|
||||||
|
Functor f_;
|
||||||
|
BaseIterator begin_;
|
||||||
|
BaseIterator end_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -415,10 +415,10 @@ TEST_F(OpenMeshSmartHandles, ComplicatedNavigtaion)
|
|||||||
*/
|
*/
|
||||||
TEST_F(OpenMeshSmartHandles, Performance)
|
TEST_F(OpenMeshSmartHandles, Performance)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if NDEBUG
|
||||||
int n_tests = 300000;
|
|
||||||
#else
|
|
||||||
int n_tests = 10000000;
|
int n_tests = 10000000;
|
||||||
|
#else
|
||||||
|
int n_tests = 300000;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto t_before_old = std::chrono::high_resolution_clock::now();
|
auto t_before_old = std::chrono::high_resolution_clock::now();
|
||||||
|
|||||||
@@ -296,5 +296,54 @@ TEST_F(OpenMeshSmartRanges, ForEach)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Test filter
|
||||||
|
*/
|
||||||
|
TEST_F(OpenMeshSmartRanges, Filtered)
|
||||||
|
{
|
||||||
|
using VH = OpenMesh::VertexHandle;
|
||||||
|
|
||||||
|
auto is_even = [](VH vh) { return vh.idx() % 2 == 0; };
|
||||||
|
auto is_odd = [](VH vh) { return vh.idx() % 2 == 1; };
|
||||||
|
auto is_divisible_by_3 = [](VH vh) { return vh.idx() % 3 == 0; };
|
||||||
|
auto to_id = [](VH vh) { return vh.idx(); };
|
||||||
|
|
||||||
|
auto even_vertices = mesh_.vertices().filtered(is_even).to_vector(to_id);
|
||||||
|
EXPECT_EQ(even_vertices.size(), 4);
|
||||||
|
EXPECT_EQ(even_vertices[0], 0);
|
||||||
|
EXPECT_EQ(even_vertices[1], 2);
|
||||||
|
EXPECT_EQ(even_vertices[2], 4);
|
||||||
|
EXPECT_EQ(even_vertices[3], 6);
|
||||||
|
|
||||||
|
auto odd_vertices = mesh_.vertices().filtered(is_odd).to_vector(to_id);
|
||||||
|
EXPECT_EQ(odd_vertices.size(), 4);
|
||||||
|
EXPECT_EQ(odd_vertices[0], 1);
|
||||||
|
EXPECT_EQ(odd_vertices[1], 3);
|
||||||
|
EXPECT_EQ(odd_vertices[2], 5);
|
||||||
|
EXPECT_EQ(odd_vertices[3], 7);
|
||||||
|
|
||||||
|
auto even_3_vertices = mesh_.vertices().filtered(is_even).filtered(is_divisible_by_3).to_vector(to_id);
|
||||||
|
EXPECT_EQ(even_3_vertices.size(), 2);
|
||||||
|
EXPECT_EQ(even_3_vertices[0], 0);
|
||||||
|
EXPECT_EQ(even_3_vertices[1], 6);
|
||||||
|
|
||||||
|
auto odd_3_vertices = mesh_.vertices().filtered(is_odd).filtered(is_divisible_by_3).to_vector(to_id);
|
||||||
|
EXPECT_EQ(odd_3_vertices.size(), 1);
|
||||||
|
EXPECT_EQ(odd_3_vertices[0], 3);
|
||||||
|
|
||||||
|
|
||||||
|
// create a vector of vertices in the order they are visited when iterating over face vertices, but every vertex only once
|
||||||
|
std::vector<VH> vertices;
|
||||||
|
OpenMesh::VProp<bool> to_be_processed(true, mesh_);
|
||||||
|
auto store_vertex = [&](VH vh) { to_be_processed(vh) = false; vertices.push_back(vh); };
|
||||||
|
|
||||||
|
for (auto fh : mesh_.faces())
|
||||||
|
fh.vertices().filtered(to_be_processed).for_each(store_vertex);
|
||||||
|
|
||||||
|
EXPECT_EQ(vertices.size(), mesh_.n_vertices()) << " number of visited vertices not correct";
|
||||||
|
EXPECT_TRUE(mesh_.vertices().all_of([&](VH vh) { return !to_be_processed(vh); })) << "did not visit all vertices";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user