From 2667c669c4218157d62fce150f795b038f951721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20M=C3=B6bius?= Date: Fri, 9 Oct 2020 09:31:19 +0200 Subject: [PATCH 01/18] Change stl reader behaviour on extension .stl , Don't check for the solid keyword explicitly --- src/OpenMesh/Core/IO/reader/STLReader.cc | 31 +++--------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/src/OpenMesh/Core/IO/reader/STLReader.cc b/src/OpenMesh/Core/IO/reader/STLReader.cc index c5a12917..e8d5dd85 100644 --- a/src/OpenMesh/Core/IO/reader/STLReader.cc +++ b/src/OpenMesh/Core/IO/reader/STLReader.cc @@ -451,32 +451,7 @@ _STLReader_:: check_stl_type(const std::string& _filename) const { - // open file - std::ifstream ifs (_filename.c_str(), std::ifstream::binary); - if(!ifs.good()) - { - omerr() << "could not open file" << _filename << std::endl; - return NONE; - } - - //find first non whitespace character - std::string line = ""; - std::size_t firstChar; - while(line.empty() && ifs.good()) - { - std::getline(ifs,line); - firstChar = line.find_first_not_of("\t "); - } - - //check for ascii keyword solid - if(strnicmp("solid",&line[firstChar],5) == 0) - { - return STLA; - } - ifs.close(); - - //if the file does not start with solid it is probably STLB - //check the file size to verify it. + // Check the file size if it matches the binary value given after the header. //open the file FILE* in = fopen(_filename.c_str(), "rb"); @@ -504,8 +479,8 @@ check_stl_type(const std::string& _filename) const file_size += fread(dummy, 1, 100, in); fclose(in); - // if sizes match -> it's STLB - return (binary_size == file_size ? STLB : NONE); + // if sizes match -> it's STLB otherwise STLA is assumed + return (binary_size == file_size ? STLB : STLA); } From 5f3f57e8cba059a52518507a62d1d3d5ee48af4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20M=C3=B6bius?= Date: Fri, 9 Oct 2020 09:33:28 +0200 Subject: [PATCH 02/18] Change stl reader behaviour on extension .stl , Don't check for the solid keyword explicitly --- Doc/changelog.docu | 5 +++++ src/OpenMesh/Core/IO/reader/STLReader.cc | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Doc/changelog.docu b/Doc/changelog.docu index 4d890368..3145dc80 100644 --- a/Doc/changelog.docu +++ b/Doc/changelog.docu @@ -20,6 +20,11 @@
  • Add halfedge loop range corresponding to hl_iter()
  • +IO + + Build System IO From 123fe55b727e6c78db4e7d6ba0ce31ec1a64bc46 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 15 Oct 2020 16:54:14 +0200 Subject: [PATCH 13/18] move SmartRangePredicates.hh from Mesh to Predicates.hh in Utils --- .../{Mesh/SmartRangePredicates.hh => Utils/Predicates.hh} | 0 src/Unittests/unittests_smart_ranges.cc | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/OpenMesh/Core/{Mesh/SmartRangePredicates.hh => Utils/Predicates.hh} (100%) diff --git a/src/OpenMesh/Core/Mesh/SmartRangePredicates.hh b/src/OpenMesh/Core/Utils/Predicates.hh similarity index 100% rename from src/OpenMesh/Core/Mesh/SmartRangePredicates.hh rename to src/OpenMesh/Core/Utils/Predicates.hh diff --git a/src/Unittests/unittests_smart_ranges.cc b/src/Unittests/unittests_smart_ranges.cc index a1062e96..ea2d97c9 100644 --- a/src/Unittests/unittests_smart_ranges.cc +++ b/src/Unittests/unittests_smart_ranges.cc @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include @@ -812,7 +812,7 @@ void test_make_predicate(Mesh& _mesh) for (auto el : set2) _mesh.status(el).set_selected(true); - auto set = _mesh.elements().filtered(Selected() || make_predicate(&test_func)).to_vector(); + auto set = _mesh.elements().filtered(Selected() || make_predicate(test_func)).to_vector(); std::vector union_set; std::set_union(set1.begin(), set1.end(), set2.begin(), set2.end(), std::back_inserter(union_set)); From e257be3e0bd71a40574d9ce5ff3d29b1c8f29d71 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 15 Oct 2020 16:54:29 +0200 Subject: [PATCH 14/18] add docu --- Doc/Tutorial/12-predicates/predicates.cc | 58 ++++++++++++++++++++++++ Doc/mainpage.docu | 1 + Doc/tutorial_11.docu | 2 +- Doc/tutorial_12.docu | 38 ++++++++++++++++ Doc/tutorial_main.docu | 1 + 5 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 Doc/Tutorial/12-predicates/predicates.cc create mode 100644 Doc/tutorial_12.docu diff --git a/Doc/Tutorial/12-predicates/predicates.cc b/Doc/Tutorial/12-predicates/predicates.cc new file mode 100644 index 00000000..790a0fbb --- /dev/null +++ b/Doc/Tutorial/12-predicates/predicates.cc @@ -0,0 +1,58 @@ + +#include +#include +#include +#include + +#include +#include + +using MyMesh = OpenMesh::TriMesh; + +bool is_divisible_by_3(OpenMesh::FaceHandle vh) { return vh.idx() % 3 == 0; } + +int main(int argc, char** argv) +{ + using namespace OpenMesh::Predicates; // for easier access to predicates + + // Read command line options + MyMesh mesh; + if (argc != 4) { + std::cerr << "Usage: " << argv[0] << " infile" << std::endl; + return 1; + } + const std::string infile = argv[1]; + + // Read mesh file + if (!OpenMesh::IO::read_mesh(mesh, infile)) { + std::cerr << "Error: Cannot read mesh from " << infile << std::endl; + return 1; + } + + // Count boundary vertices + std::cout << "Mesh contains " << mesh.vertices().count_if(Boundary()) << " boundary vertices"; + + // Selected inner vertices + std::cout << "These are the selected inner vertices: " << std::endl; + for (auto vh : mesh.vertices().filtered(!Boundary() && Selected())) + std::cout << vh.idx() << ", "; + std::cout << std::endl; + + // Faces whose id is divisible by 3 + auto vec = mesh.faces().filtered(is_divisible_by_3).to_vector(); + std::cout << "There are " << vec.size() << " faces whose id is divisible by 3" << std::endl; + + // Faces which are tagged or whose id is not divisible by 3 + auto vec2 = mesh.faces().filtered(Tagged() || !make_predicate(is_divisible_by_3)).to_vector(); + std::cout << "There are " << vec2.size() << " faces which are tagged or whose id is not divisible by 3" << std::endl; + + // Edges that are longer than 10 or shorter than 2 + OpenMesh::EProp longer_than_10(mesh); + for (auto eh : mesh.edges()) + longer_than_10[eh] = mesh.calc_edge_length(eh) > 10; + std::cout << "There are " << + mesh.edges().count_if(make_predicate(longer_than_10) || make_predicate([&](OpenMesh::EdgeHandle eh) { return mesh.calc_edge_length(eh) < 2; })) << + " edges which are shorter than 2 or longer than 10" << std::endl; + +} + diff --git a/Doc/mainpage.docu b/Doc/mainpage.docu index 63bfee8c..d31b189c 100644 --- a/Doc/mainpage.docu +++ b/Doc/mainpage.docu @@ -83,6 +83,7 @@ repeatedly replacing each vertex' position by the center of gravity \li \ref tutorial_03 \li \ref tutorial_04 \li \ref tutorial_11 +\li \ref tutorial_12 \li \ref tutorial_05 \li \ref tutorial_06 \li \ref tutorial_07 diff --git a/Doc/tutorial_11.docu b/Doc/tutorial_11.docu index f99724cf..50b9b787 100644 --- a/Doc/tutorial_11.docu +++ b/Doc/tutorial_11.docu @@ -71,7 +71,7 @@ Similarily we compute the update vector as the laplace of the freshly computed l Finally, we apply the update after damping it by a factor of -0.5. -\skipline udpate points +\skipline update points \until bi_laplace Below is the complete source code: diff --git a/Doc/tutorial_12.docu b/Doc/tutorial_12.docu new file mode 100644 index 00000000..80ad2a8a --- /dev/null +++ b/Doc/tutorial_12.docu @@ -0,0 +1,38 @@ +/** \page tutorial_12 Filtering ranges with predicates + +This examples shows: +- How to use predicates to filter which elements of a mesh you want iterate over + +In the previous tutorial we discussed already that the ranges returned by functions like all_vertices(), voh_range() or outgoing_halfedges() provide a few helpful methods such as avg() or to_vector(). Another interesting method is filtered() which requires as argument something that can be called for an element of the range and returns a bool. The resulting range will then only iterate over elements for which the filter returs true. +The filter can be a lambda, a function pointer, a property manager holding a bool property, or a functor object such as the predicates defined in . The predefined predicates can check the status of a mesh element and test if they are boundary. +With their help you can for example count all boundary vertices: + +\dontinclude 12-predicates/predicates.cc +\skipline Count boundary vertices +\until boundary vertices + +Predicates can be composed using the operators ||, &&, and !. This enables you to specify precisely which elements you want process in your loop, e.g. inner vertices that are selected: + +\skipline Selected inner vertices +\until std::cout << std::endl + +As mentioned above, the filter argument can be anything returning a bool for a given input, e.g. a function pointer: + +\skipline Faces whose id is divisible by 3 +\until faces whose id is divisible by 3 + +However, function pointers, lambdas etc are not composable. Fortunately, you can use make_predicate to turn them into composable predicates: + + +\skipline Faces which are tagged or whose id is not divisible by 3 +\until faces which are tagged or whose id is not divisible by 3 + +Below is the complete source code: + +\include 12-predicates/predicates.cc + +--- + + + +*/ diff --git a/Doc/tutorial_main.docu b/Doc/tutorial_main.docu index 908099dc..3bb81314 100644 --- a/Doc/tutorial_main.docu +++ b/Doc/tutorial_main.docu @@ -34,6 +34,7 @@ repeatedly replacing each vertex' position by the center of gravity
  • \subpage tutorial_03
  • \subpage tutorial_04
  • \subpage tutorial_11 +
  • \subpage tutorial_12
  • \subpage tutorial_05
  • \subpage tutorial_06
  • \subpage tutorial_07 From 4e234a91cdae81a28bd4a05ae72d286f64eb5e20 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 29 Oct 2020 10:34:12 +0100 Subject: [PATCH 15/18] fix/simplify FilteredSmartRange --- src/OpenMesh/Core/Mesh/SmartRange.hh | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/src/OpenMesh/Core/Mesh/SmartRange.hh b/src/OpenMesh/Core/Mesh/SmartRange.hh index c3fba9f0..6524042c 100644 --- a/src/OpenMesh/Core/Mesh/SmartRange.hh +++ b/src/OpenMesh/Core/Mesh/SmartRange.hh @@ -428,27 +428,10 @@ struct SmartRangeT * @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 filtered(Functor&& f) -> FilteredSmartRangeT { auto range = static_cast(this); - auto b = (*range).begin(); - auto e = (*range).end(); - return FilteredSmartRangeT::type>(f, b, e); - } - - /** @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); + return FilteredSmartRangeT(std::forward(f), (*range).begin(), (*range).end()); } }; @@ -488,11 +471,12 @@ struct FilteredSmartRangeT : public SmartRangeT(f)), begin_(std::move(begin)), end_(std::move(end)){} FilteredIterator begin() const { return FilteredIterator(f_, begin_, end_); } FilteredIterator end() const { return FilteredIterator(f_, end_, end_); } From e7148d0dda729284be68f31f86841a8d49bdff17 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 29 Oct 2020 10:52:34 +0100 Subject: [PATCH 16/18] add MemberFunctionWrapper to conveniently use member functions as predicates --- src/OpenMesh/Core/Utils/Predicates.hh | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/OpenMesh/Core/Utils/Predicates.hh b/src/OpenMesh/Core/Utils/Predicates.hh index 332b488a..3bb85a08 100644 --- a/src/OpenMesh/Core/Utils/Predicates.hh +++ b/src/OpenMesh/Core/Utils/Predicates.hh @@ -250,6 +250,39 @@ using RegularQuad = Regular<4,3>; using RegularTri = Regular<6,4>; +/// Wrapper object to hold an object and a member function pointer, +/// and provides operator() to call that member function for that object with one argument +template +struct MemberFunctionWrapper +{ + T t_; // Objects whose member function we want to call + MF mf_; // pointer to member function + + MemberFunctionWrapper(T _t, MF _mf) + : + t_(_t), + mf_(_mf) + {} + + template + auto operator()(const O& _o) -> decltype ((t_.*mf_)(_o)) + { + return (t_.*mf_)(_o); + } +}; + +/// Helper to create a MemberFunctionWrapper without explicitely naming the types +template +MemberFunctionWrapper make_member_function_wrapper(T&& _t, MF _mf) +{ + return MemberFunctionWrapper(std::forward(_t), _mf); +} + +/// Convenience macro to create a MemberFunctionWrapper for *this object +#define OM_MFW(member_function) OpenMesh::Predicates::make_member_function_wrapper(*this, &std::decay::type::member_function) + + + //============================================================================= } // namespace Predicates From ad7f7eccad50ce8d13804e813c2d7b1ecafc436f Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 29 Oct 2020 11:05:18 +0100 Subject: [PATCH 17/18] add missing include --- src/OpenMesh/Core/Utils/Predicates.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/OpenMesh/Core/Utils/Predicates.hh b/src/OpenMesh/Core/Utils/Predicates.hh index 3bb85a08..2880eb5a 100644 --- a/src/OpenMesh/Core/Utils/Predicates.hh +++ b/src/OpenMesh/Core/Utils/Predicates.hh @@ -49,6 +49,7 @@ #include #include #include +#include //== NAMESPACES =============================================================== From 68d408cf411c960b1876022337608aee6fc90dbe Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 29 Oct 2020 11:05:40 +0100 Subject: [PATCH 18/18] add test for member function wrappper --- src/Unittests/unittests_smart_ranges.cc | 54 +++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/Unittests/unittests_smart_ranges.cc b/src/Unittests/unittests_smart_ranges.cc index ea2d97c9..a547686d 100644 --- a/src/Unittests/unittests_smart_ranges.cc +++ b/src/Unittests/unittests_smart_ranges.cc @@ -856,5 +856,59 @@ TEST_F(OpenMeshSmartRanges, Predicate) test_make_predicate(mesh_); } +struct MemberFunctionWrapperTestStruct +{ + MemberFunctionWrapperTestStruct(int _i) + : + i_(_i) + { + } + + int get_i(const OpenMesh::SmartEdgeHandle& /*_eh*/) const + { + return i_; + } + + bool id_divisible_by_2(const OpenMesh::SmartEdgeHandle& _eh) const + { + return _eh.idx() % 2 == 0; + } + + int valence_times_i(const OpenMesh::SmartVertexHandle& vh) + { + return vh.edges().sum(OM_MFW(get_i)); + } + + int i_; +}; + +TEST_F(OpenMeshSmartRanges, MemberFunctionFunctor) +{ + using namespace OpenMesh::Predicates; + + EXPECT_TRUE(mesh_.n_vertices() > 0) << "Mesh has no vertices"; + EXPECT_TRUE(mesh_.n_edges() > 0) << "Mesh has no edges"; + + int factor = 3; + MemberFunctionWrapperTestStruct test_object(factor); + + // Test using a MemberFunctionWrapper as Functor + EXPECT_EQ(mesh_.n_edges() / 2, mesh_.edges().count_if(make_member_function_wrapper(test_object, &MemberFunctionWrapperTestStruct::id_divisible_by_2))); + + + // Test using a MemberFunctionWrapper as Functor that is created using the convenience macro from inside the struct + for (auto vh : mesh_.vertices()) + EXPECT_EQ(test_object.valence_times_i(vh), vh.valence() * factor); + + factor = 4; + test_object.i_ = factor; + for (auto vh : mesh_.vertices()) + { + EXPECT_EQ(test_object.valence_times_i(vh), vh.valence() * factor); + } + + + +} }