diff --git a/src/OpenMesh/Core/Mesh/SmartRange.hh b/src/OpenMesh/Core/Mesh/SmartRange.hh index 60fefdd8..04053153 100644 --- a/src/OpenMesh/Core/Mesh/SmartRange.hh +++ b/src/OpenMesh/Core/Mesh/SmartRange.hh @@ -65,10 +65,17 @@ struct Identity } +template +struct FilteredSmartRangeT; + /// Base class for all smart range types template struct SmartRangeT { + using Handle = HandleT; + using SmartRange = SmartRangeT; + using Range = RangeT; + // TODO: Someone with better c++ knowledge may improve the code below. /** @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 + auto filtered(Functor&& f) -> FilteredSmartRangeT::type> + { + auto range = static_cast(this); + auto b = (*range).begin(); + auto e = (*range).end(); + return FilteredSmartRangeT::type>(f, b, e); + } + +}; + + +/// Class which applies a filter when iterating over elements +template +struct FilteredSmartRangeT : public SmartRangeT, HandleT> +{ + using BaseRange = SmartRangeT, HandleT>; + using BaseIterator = decltype((std::declval().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_; }; diff --git a/src/Unittests/unittests_smart_handles.cc b/src/Unittests/unittests_smart_handles.cc index bfd8ca22..63122c79 100644 --- a/src/Unittests/unittests_smart_handles.cc +++ b/src/Unittests/unittests_smart_handles.cc @@ -415,10 +415,10 @@ TEST_F(OpenMeshSmartHandles, ComplicatedNavigtaion) */ TEST_F(OpenMeshSmartHandles, Performance) { -#if DEBUG - int n_tests = 300000; -#else +#if NDEBUG int n_tests = 10000000; +#else + int n_tests = 300000; #endif auto t_before_old = std::chrono::high_resolution_clock::now(); diff --git a/src/Unittests/unittests_smart_ranges.cc b/src/Unittests/unittests_smart_ranges.cc index c9c3bed9..7d1d228e 100644 --- a/src/Unittests/unittests_smart_ranges.cc +++ b/src/Unittests/unittests_smart_ranges.cc @@ -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 vertices; + OpenMesh::VProp 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"; + +} + + }