From ac67648317bc3f4de0a0d84a32b828c68f4abefd Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Mon, 24 Aug 2020 14:54:45 +0200 Subject: [PATCH 01/25] add weighted average to smart ranges --- src/OpenMesh/Core/Mesh/SmartRange.hh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/OpenMesh/Core/Mesh/SmartRange.hh b/src/OpenMesh/Core/Mesh/SmartRange.hh index 0277bd59..c3fba9f0 100644 --- a/src/OpenMesh/Core/Mesh/SmartRange.hh +++ b/src/OpenMesh/Core/Mesh/SmartRange.hh @@ -124,6 +124,34 @@ struct SmartRangeT return (1.0 / n_elements) * result; } + /** @brief Computes the weighted average of elements. + * + * Computes the weighted average of all elements in the range after applying the functor \p f. + * + * @param f Functor that is applied to all elements before computing the average. + * @param w Functor returning element weight. + */ + template + auto avg(Functor&& f, WeightFunctor&& w) -> typename std::decay())+w(std::declval())))*f(std::declval()))>::type + { + auto range = static_cast(this); + auto begin = range->begin(); + auto end = range->end(); + assert(begin != end); + typename std::decay::type weight = w(*begin); + typename std::decay::type result = weight * f(*begin); + typename std::decay::type weight_sum = weight; + auto it = begin; + ++it; + for (; it != end; ++it) + { + weight = w(*it); + result += weight*f(*it); + weight_sum += weight; + } + return (1.0 / weight_sum) * result; + } + /** @brief Check if any element fulfils condition. * * Checks if functor \p f returns true for any of the elements in the range. From ff609b890946c476d465bdd50acb4f88454d6b30 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Wed, 26 Aug 2020 10:20:58 +0200 Subject: [PATCH 02/25] add tests for smart range avg and weighted avg --- src/Unittests/unittests_smart_ranges.cc | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/Unittests/unittests_smart_ranges.cc b/src/Unittests/unittests_smart_ranges.cc index 353e528c..93eaa15d 100644 --- a/src/Unittests/unittests_smart_ranges.cc +++ b/src/Unittests/unittests_smart_ranges.cc @@ -386,6 +386,39 @@ TEST_F(OpenMeshSmartRanges, Filtered) } +/* Test avg + */ +TEST_F(OpenMeshSmartRanges, Avg) +{ + + Mesh::Point cog(0,0,0); + for (auto vh : mesh_.vertices()) + cog += mesh_.point(vh); + cog /= mesh_.n_vertices(); + + auto points = OpenMesh::getPointsProperty(mesh_); + auto cog2 = mesh_.vertices().avg(points); + + EXPECT_LT(norm(cog - cog2), 0.00001) << "Computed center of gravities are significantly different."; +} + +/* Test weighted avg + */ +TEST_F(OpenMeshSmartRanges, WeightedAvg) +{ + Mesh::Point cog(0,0,0); + for (auto fh : mesh_.faces()) + cog += mesh_.calc_face_centroid(fh); + cog /= mesh_.n_faces(); + + OpenMesh::FProp area(mesh_); + for (auto fh : mesh_.faces()) + area[fh] = mesh_.calc_face_area(fh); + + auto cog2 = mesh_.faces().avg([&](OpenMesh::FaceHandle fh) { return mesh_.calc_face_centroid(fh); }, area); + + EXPECT_LT(norm(cog - cog2), 0.00001) << "Computed area weighted center of gravities are significantly different."; +} } From 1c71cf568d7357f1accccd46a9749bac5e467a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20M=C3=B6bius?= Date: Thu, 24 Sep 2020 11:54:03 +0200 Subject: [PATCH 03/25] Fixed some qt deprecation warnings --- src/OpenMesh/Apps/Decimating/decimaterviewer.cc | 1 - src/OpenMesh/Apps/ProgViewer/progviewer.cc | 3 +-- src/OpenMesh/Apps/QtViewer/meshviewer.cc | 3 +-- src/OpenMesh/Apps/Subdivider/qtsubdivider.cc | 3 +-- src/OpenMesh/Apps/VDProgMesh/Synthesizer/vdpmsynthesizer.cc | 3 +-- 5 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/OpenMesh/Apps/Decimating/decimaterviewer.cc b/src/OpenMesh/Apps/Decimating/decimaterviewer.cc index 5edaf767..da5a22db 100644 --- a/src/OpenMesh/Apps/Decimating/decimaterviewer.cc +++ b/src/OpenMesh/Apps/Decimating/decimaterviewer.cc @@ -63,7 +63,6 @@ int main(int argc, char **argv) #endif // OpenGL check - QApplication::setColorSpec( QApplication::CustomColor ); QApplication app(argc,argv); if ( !QGLFormat::hasOpenGL() ) { diff --git a/src/OpenMesh/Apps/ProgViewer/progviewer.cc b/src/OpenMesh/Apps/ProgViewer/progviewer.cc index 6f8eebc5..4ce5de6b 100644 --- a/src/OpenMesh/Apps/ProgViewer/progviewer.cc +++ b/src/OpenMesh/Apps/ProgViewer/progviewer.cc @@ -57,7 +57,6 @@ int main(int argc, char **argv) { // OpenGL check - QApplication::setColorSpec( QApplication::CustomColor ); QApplication app(argc,argv); if ( !QGLFormat::hasOpenGL() ) { @@ -67,7 +66,7 @@ int main(int argc, char **argv) // create widget - ProgViewerWidget w(0); + ProgViewerWidget w(nullptr); w.resize(400, 400); w.show(); diff --git a/src/OpenMesh/Apps/QtViewer/meshviewer.cc b/src/OpenMesh/Apps/QtViewer/meshviewer.cc index a3649154..fdb5c144 100644 --- a/src/OpenMesh/Apps/QtViewer/meshviewer.cc +++ b/src/OpenMesh/Apps/QtViewer/meshviewer.cc @@ -60,12 +60,11 @@ void usage_and_exit(int xcode); int main(int argc, char **argv) { // OpenGL check - QApplication::setColorSpec( QApplication::CustomColor ); QApplication app(argc,argv); if ( !QGLFormat::hasOpenGL() ) { QString msg = "System has no OpenGL support!"; - QMessageBox::critical( 0, QString("OpenGL"), msg + QString(argv[1]) ); + QMessageBox::critical( nullptr, QString("OpenGL"), msg + QString(argv[1]) ); return -1; } diff --git a/src/OpenMesh/Apps/Subdivider/qtsubdivider.cc b/src/OpenMesh/Apps/Subdivider/qtsubdivider.cc index 665274dc..9421653b 100644 --- a/src/OpenMesh/Apps/Subdivider/qtsubdivider.cc +++ b/src/OpenMesh/Apps/Subdivider/qtsubdivider.cc @@ -55,7 +55,6 @@ int main(int argc, char **argv) { // OpenGL check - QApplication::setColorSpec( QApplication::CustomColor ); QApplication app(argc,argv); if ( !QGLFormat::hasOpenGL() ) { @@ -66,7 +65,7 @@ int main(int argc, char **argv) // create widget - SubdivideWidget* w = new SubdivideWidget(0, "Subdivider"); + SubdivideWidget* w = new SubdivideWidget(nullptr, "Subdivider"); w->resize(400, 400); w->show(); diff --git a/src/OpenMesh/Apps/VDProgMesh/Synthesizer/vdpmsynthesizer.cc b/src/OpenMesh/Apps/VDProgMesh/Synthesizer/vdpmsynthesizer.cc index d4c11fcd..a46e8a19 100644 --- a/src/OpenMesh/Apps/VDProgMesh/Synthesizer/vdpmsynthesizer.cc +++ b/src/OpenMesh/Apps/VDProgMesh/Synthesizer/vdpmsynthesizer.cc @@ -55,7 +55,6 @@ int main(int argc, char **argv) { // OpenGL check - QApplication::setColorSpec( QApplication::CustomColor ); QApplication app(argc,argv); @@ -66,7 +65,7 @@ int main(int argc, char **argv) // create widget VDPMSynthesizerViewerWidget* - w = new VDPMSynthesizerViewerWidget(0, "VDPMSynthesizerViewer"); + w = new VDPMSynthesizerViewerWidget(nullptr, "VDPMSynthesizerViewer"); w->resize(400, 400); w->show(); 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 04/25] 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 05/25] 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 +
      +
    • STL Reader: Change stl reader behaviour on extension .stl , Don't check for the solid keyword explicitly
    • +
    + Build System
    • Dropped 32-bit Windows continuous integration and artifact builds.
    • diff --git a/src/OpenMesh/Core/IO/reader/STLReader.cc b/src/OpenMesh/Core/IO/reader/STLReader.cc index e8d5dd85..1964d194 100644 --- a/src/OpenMesh/Core/IO/reader/STLReader.cc +++ b/src/OpenMesh/Core/IO/reader/STLReader.cc @@ -468,7 +468,6 @@ check_stl_type(const std::string& _filename) const fread(dummy, 1, 80, in); size_t nT = read_int(in, swapFlag); - // compute file size from nT size_t binary_size = 84 + nT*50; From a8d275123aa2cc2d13e6b9fe047db6756ad5cfc1 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Tue, 13 Oct 2020 16:46:51 +0200 Subject: [PATCH 06/25] add unittest to check if mesh property initialization works --- src/Unittests/unittests_propertymanager.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Unittests/unittests_propertymanager.cc b/src/Unittests/unittests_propertymanager.cc index 87c92b95..d620f3ec 100644 --- a/src/Unittests/unittests_propertymanager.cc +++ b/src/Unittests/unittests_propertymanager.cc @@ -870,5 +870,12 @@ TEST_F(OpenMeshPropertyManager, return_property_from_function) { } +TEST_F(OpenMeshPropertyManager, mesh_property_initialization) { + + OpenMesh::MProp mesh_id(13, mesh_); + ASSERT_EQ(mesh_id(), 13); +} + + } From 657dc2c8912db5f0ba242e874e31ba11b334b93d Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Tue, 13 Oct 2020 16:50:40 +0200 Subject: [PATCH 07/25] add support for initializing property managers of mesh properties --- src/OpenMesh/Core/Utils/PropertyManager.hh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/OpenMesh/Core/Utils/PropertyManager.hh b/src/OpenMesh/Core/Utils/PropertyManager.hh index b5d07764..62d87b76 100644 --- a/src/OpenMesh/Core/Utils/PropertyManager.hh +++ b/src/OpenMesh/Core/Utils/PropertyManager.hh @@ -85,14 +85,17 @@ class PropertyManager { private: // Mesh properties (MPropHandleT<...>) are stored differently than the other properties. - // This class implements different behavior when copying or swapping data from one - // property manager to a another one. + // This class implements different behavior when initializing a property or when + // copying or swapping data from one property manager to a another one. template struct StorageT; // specialization for Mesh Properties template struct StorageT> { + static void initialize(PropertyManager& pm, const Value& initial_value ) { + pm() = initial_value; + } static void copy(const PropertyManager& from, PropertyManager2& to) { *to = *from; } @@ -110,6 +113,9 @@ class PropertyManager { // definition for other Mesh Properties template struct StorageT { + static void initialize(PropertyManager& pm, const Value& initial_value ) { + pm.set_range(pm.mesh_.template all_elements(), initial_value); + } static void copy(const PropertyManager& from, PropertyManager2& to) { from.copy_to(from.mesh_.template all_elements(), to, to.mesh_.template all_elements()); } @@ -190,7 +196,7 @@ class PropertyManager { PropertyManager(const Value& initial_value, PolyConnectivity& mesh, const char *propname) : mesh_(mesh), retain_(true), name_(propname) { if (!mesh_.get_property_handle(prop_, propname)) { PropertyManager::mesh().add_property(prop_, propname); - set_range(mesh_.all_elements(), initial_value); + Storage::initialize(*this, initial_value); } } @@ -215,7 +221,7 @@ class PropertyManager { */ PropertyManager(const Value& initial_value, const PolyConnectivity& mesh) : mesh_(mesh), retain_(false), name_("") { PropertyManager::mesh().add_property(prop_, name_); - set_range(mesh_.all_elements(), initial_value); + Storage::initialize(*this, initial_value); } /** From 7d281c14376a7150d1a2d0f9d70510106d81d682 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Tue, 13 Oct 2020 21:49:41 +0200 Subject: [PATCH 08/25] add methods to SmartHandles that give access to the status --- src/OpenMesh/Core/Mesh/SmartHandles.hh | 87 ++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/src/OpenMesh/Core/Mesh/SmartHandles.hh b/src/OpenMesh/Core/Mesh/SmartHandles.hh index 2fd0af68..4d46bd49 100644 --- a/src/OpenMesh/Core/Mesh/SmartHandles.hh +++ b/src/OpenMesh/Core/Mesh/SmartHandles.hh @@ -43,6 +43,8 @@ #error Do not include this directly, include instead PolyConnectivity.hh #endif//OPENMESH_POLYCONNECTIVITY_INTERFACE_INCLUDE +#include + //== NAMESPACES =============================================================== namespace OpenMesh { @@ -73,8 +75,29 @@ private: }; +/// Base class for all smart handle types that contains status related methods +template +class SmartBaseHandleStatus +{ +public: + /// Returns true iff the handle is marked as feature + bool feature() const; + /// Returns true iff the handle is marked as selected + bool selected() const; + /// Returns true iff the handle is marked as tagged + bool tagged() const; + /// Returns true iff the handle is marked as tagged2 + bool tagged2() const; + /// Returns true iff the handle is marked as locked + bool locked() const; + /// Returns true iff the handle is marked as hidden + bool hidden() const; + /// Returns true iff the handle is marked as deleted + bool deleted() const; +}; + /// Smart version of VertexHandle contains a pointer to the corresponding mesh and allows easier access to navigation methods -struct OPENMESHDLLEXPORT SmartVertexHandle : public SmartBaseHandle, VertexHandle +struct OPENMESHDLLEXPORT SmartVertexHandle : public SmartBaseHandle, VertexHandle, SmartBaseHandleStatus { explicit SmartVertexHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), VertexHandle(_idx) {} @@ -104,7 +127,7 @@ struct OPENMESHDLLEXPORT SmartVertexHandle : public SmartBaseHandle, VertexHandl bool is_manifold() const; }; -struct OPENMESHDLLEXPORT SmartHalfedgeHandle : public SmartBaseHandle, HalfedgeHandle +struct OPENMESHDLLEXPORT SmartHalfedgeHandle : public SmartBaseHandle, HalfedgeHandle, SmartBaseHandleStatus { explicit SmartHalfedgeHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), HalfedgeHandle(_idx) {} @@ -130,7 +153,7 @@ struct OPENMESHDLLEXPORT SmartHalfedgeHandle : public SmartBaseHandle, HalfedgeH bool is_boundary() const; }; -struct OPENMESHDLLEXPORT SmartEdgeHandle : public SmartBaseHandle, EdgeHandle +struct OPENMESHDLLEXPORT SmartEdgeHandle : public SmartBaseHandle, EdgeHandle, SmartBaseHandleStatus { explicit SmartEdgeHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), EdgeHandle(_idx) {} @@ -155,7 +178,7 @@ struct OPENMESHDLLEXPORT SmartEdgeHandle : public SmartBaseHandle, EdgeHandle bool is_boundary() const; }; -struct OPENMESHDLLEXPORT SmartFaceHandle : public SmartBaseHandle, FaceHandle +struct OPENMESHDLLEXPORT SmartFaceHandle : public SmartBaseHandle, FaceHandle, SmartBaseHandleStatus { explicit SmartFaceHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), FaceHandle(_idx) {} @@ -207,6 +230,62 @@ template <> struct SmartHandle { using type = SmartEdgeHandle; template <> struct SmartHandle { using type = SmartFaceHandle; }; +template +inline bool SmartBaseHandleStatus::feature() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).feature(); +} + +template +inline bool SmartBaseHandleStatus::selected() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).selected(); +} + +template +inline bool SmartBaseHandleStatus::tagged() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).tagged(); +} + +template +inline bool SmartBaseHandleStatus::tagged2() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).tagged2(); +} + +template +inline bool SmartBaseHandleStatus::locked() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).locked(); +} + +template +inline bool SmartBaseHandleStatus::hidden() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).hidden(); +} + +template +inline bool SmartBaseHandleStatus::deleted() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->status(handle).deleted(); +} + inline SmartHalfedgeHandle SmartVertexHandle::out() const { assert(mesh() != nullptr); From f6781e250683282b004f288954869d6f398aafd7 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Tue, 13 Oct 2020 21:50:08 +0200 Subject: [PATCH 09/25] add test for smart handle status access --- src/Unittests/unittests_smart_handles.cc | 54 ++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/Unittests/unittests_smart_handles.cc b/src/Unittests/unittests_smart_handles.cc index 6b42b45a..729d8b3c 100644 --- a/src/Unittests/unittests_smart_handles.cc +++ b/src/Unittests/unittests_smart_handles.cc @@ -560,6 +560,60 @@ TEST(OpenMeshSmartHandlesNoFixture, SplitTriMesh) } +template +void test_status_fields(MeshT& _mesh, const RangeT& _range) +{ + for (auto el : _range) + _mesh.status(el).set_selected(el.idx() % 3 == 0); + for (auto el : _range) + EXPECT_EQ(_mesh.status(el).selected(), el.selected()); + + for (auto el : _range) + _mesh.status(el).set_feature(el.idx() % 3 == 0); + for (auto el : _range) + EXPECT_EQ(_mesh.status(el).feature(), el.feature()); + + for (auto el : _range) + _mesh.status(el).set_tagged(el.idx() % 3 == 0); + for (auto el : _range) + EXPECT_EQ(_mesh.status(el).tagged(), el.tagged()); + + for (auto el : _range) + _mesh.status(el).set_tagged2(el.idx() % 3 == 0); + for (auto el : _range) + EXPECT_EQ(_mesh.status(el).tagged2(), el.tagged2()); + + for (auto el : _range) + _mesh.status(el).set_hidden(el.idx() % 3 == 0); + for (auto el : _range) + EXPECT_EQ(_mesh.status(el).hidden(), el.hidden()); + + for (auto el : _range) + _mesh.status(el).set_locked(el.idx() % 3 == 0); + for (auto el : _range) + EXPECT_EQ(_mesh.status(el).locked(), el.locked()); + + for (auto el : _range) + _mesh.status(el).set_deleted(el.idx() % 3 == 0); + for (auto el : _range) + EXPECT_EQ(_mesh.status(el).deleted(), el.deleted()); + } + +TEST_F(OpenMeshSmartHandles, StatusAccess) +{ + ASSERT_TRUE(mesh_.n_vertices() > 0) << "Mesh is empty"; + + mesh_.request_vertex_status(); + mesh_.request_halfedge_status(); + mesh_.request_edge_status(); + mesh_.request_face_status(); + + test_status_fields(mesh_, mesh_.vertices()); + test_status_fields(mesh_, mesh_.edges()); + test_status_fields(mesh_, mesh_.halfedges()); + test_status_fields(mesh_, mesh_.faces()); +} + From d0aef334c0db271becd0782147c853160c212ae1 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 15 Oct 2020 13:49:07 +0200 Subject: [PATCH 10/25] improve name of helper class containing status access functions and implement is_boundary in the same way --- src/OpenMesh/Core/Mesh/SmartHandles.hh | 74 ++++++++++---------------- 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/src/OpenMesh/Core/Mesh/SmartHandles.hh b/src/OpenMesh/Core/Mesh/SmartHandles.hh index 4d46bd49..fbe4a94e 100644 --- a/src/OpenMesh/Core/Mesh/SmartHandles.hh +++ b/src/OpenMesh/Core/Mesh/SmartHandles.hh @@ -77,7 +77,7 @@ private: /// Base class for all smart handle types that contains status related methods template -class SmartBaseHandleStatus +class SmartHandleStatusPredicates { public: /// Returns true iff the handle is marked as feature @@ -96,8 +96,17 @@ public: bool deleted() const; }; +/// Base class for all smart handle types that contains status related methods +template +class SmartHandleBoundaryPredicate +{ +public: + /// Returns true iff the handle is boundary + bool is_boundary() const; +}; + /// Smart version of VertexHandle contains a pointer to the corresponding mesh and allows easier access to navigation methods -struct OPENMESHDLLEXPORT SmartVertexHandle : public SmartBaseHandle, VertexHandle, SmartBaseHandleStatus +struct OPENMESHDLLEXPORT SmartVertexHandle : public SmartBaseHandle, VertexHandle, SmartHandleStatusPredicates, SmartHandleBoundaryPredicate { explicit SmartVertexHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), VertexHandle(_idx) {} @@ -121,13 +130,11 @@ struct OPENMESHDLLEXPORT SmartVertexHandle : public SmartBaseHandle, VertexHandl /// Returns valence of the vertex uint valence() const; - /// Returns true iff the vertex is incident to a boundary halfedge - bool is_boundary() const; /// Returns true iff (the mesh at) the vertex is two-manifold ? bool is_manifold() const; }; -struct OPENMESHDLLEXPORT SmartHalfedgeHandle : public SmartBaseHandle, HalfedgeHandle, SmartBaseHandleStatus +struct OPENMESHDLLEXPORT SmartHalfedgeHandle : public SmartBaseHandle, HalfedgeHandle, SmartHandleStatusPredicates, SmartHandleBoundaryPredicate { explicit SmartHalfedgeHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), HalfedgeHandle(_idx) {} @@ -148,12 +155,9 @@ struct OPENMESHDLLEXPORT SmartHalfedgeHandle : public SmartBaseHandle, HalfedgeH /// Returns a range of halfedges in the face of the halfedge (or along the boundary) (PolyConnectivity::hl_range()) PolyConnectivity::ConstHalfedgeLoopRange loop() const; - - /// Returns true iff the halfedge is on the boundary (i.e. it has no corresponding face) - bool is_boundary() const; }; -struct OPENMESHDLLEXPORT SmartEdgeHandle : public SmartBaseHandle, EdgeHandle, SmartBaseHandleStatus +struct OPENMESHDLLEXPORT SmartEdgeHandle : public SmartBaseHandle, EdgeHandle, SmartHandleStatusPredicates, SmartHandleBoundaryPredicate { explicit SmartEdgeHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), EdgeHandle(_idx) {} @@ -173,12 +177,9 @@ struct OPENMESHDLLEXPORT SmartEdgeHandle : public SmartBaseHandle, EdgeHandle, S SmartVertexHandle v0() const; /// Shorthand for vertex(1) SmartVertexHandle v1() const; - - /// Returns true iff the edge lies on the boundary (i.e. one of the halfedges is boundary) - bool is_boundary() const; }; -struct OPENMESHDLLEXPORT SmartFaceHandle : public SmartBaseHandle, FaceHandle, SmartBaseHandleStatus +struct OPENMESHDLLEXPORT SmartFaceHandle : public SmartBaseHandle, FaceHandle, SmartHandleStatusPredicates, SmartHandleBoundaryPredicate { explicit SmartFaceHandle(int _idx=-1, const PolyConnectivity* _mesh = nullptr) : SmartBaseHandle(_mesh), FaceHandle(_idx) {} @@ -196,8 +197,6 @@ struct OPENMESHDLLEXPORT SmartFaceHandle : public SmartBaseHandle, FaceHandle, S /// Returns the valence of the face uint valence() const; - /// Returns true iff the face lies at the boundary (i.e. one of the edges is boundary) - bool is_boundary() const; }; @@ -231,7 +230,7 @@ template <> struct SmartHandle { using type = SmartFaceHandle; template -inline bool SmartBaseHandleStatus::feature() const +inline bool SmartHandleStatusPredicates::feature() const { const auto& handle = static_cast(*this); assert(handle.mesh() != nullptr); @@ -239,7 +238,7 @@ inline bool SmartBaseHandleStatus::feature() const } template -inline bool SmartBaseHandleStatus::selected() const +inline bool SmartHandleStatusPredicates::selected() const { const auto& handle = static_cast(*this); assert(handle.mesh() != nullptr); @@ -247,7 +246,7 @@ inline bool SmartBaseHandleStatus::selected() const } template -inline bool SmartBaseHandleStatus::tagged() const +inline bool SmartHandleStatusPredicates::tagged() const { const auto& handle = static_cast(*this); assert(handle.mesh() != nullptr); @@ -255,7 +254,7 @@ inline bool SmartBaseHandleStatus::tagged() const } template -inline bool SmartBaseHandleStatus::tagged2() const +inline bool SmartHandleStatusPredicates::tagged2() const { const auto& handle = static_cast(*this); assert(handle.mesh() != nullptr); @@ -263,7 +262,7 @@ inline bool SmartBaseHandleStatus::tagged2() const } template -inline bool SmartBaseHandleStatus::locked() const +inline bool SmartHandleStatusPredicates::locked() const { const auto& handle = static_cast(*this); assert(handle.mesh() != nullptr); @@ -271,7 +270,7 @@ inline bool SmartBaseHandleStatus::locked() const } template -inline bool SmartBaseHandleStatus::hidden() const +inline bool SmartHandleStatusPredicates::hidden() const { const auto& handle = static_cast(*this); assert(handle.mesh() != nullptr); @@ -279,13 +278,21 @@ inline bool SmartBaseHandleStatus::hidden() const } template -inline bool SmartBaseHandleStatus::deleted() const +inline bool SmartHandleStatusPredicates::deleted() const { const auto& handle = static_cast(*this); assert(handle.mesh() != nullptr); return handle.mesh()->status(handle).deleted(); } +template +inline bool SmartHandleBoundaryPredicate::is_boundary() const +{ + const auto& handle = static_cast(*this); + assert(handle.mesh() != nullptr); + return handle.mesh()->is_boundary(handle); +} + inline SmartHalfedgeHandle SmartVertexHandle::out() const { assert(mesh() != nullptr); @@ -308,12 +315,6 @@ inline uint SmartVertexHandle::valence() const return mesh()->valence(*this); } -inline bool SmartVertexHandle::is_boundary() const -{ - assert(mesh() != nullptr); - return mesh()->is_boundary(*this); -} - inline bool SmartVertexHandle::is_manifold() const { assert(mesh() != nullptr); @@ -362,12 +363,6 @@ inline SmartFaceHandle SmartHalfedgeHandle::face() const return make_smart(mesh()->face_handle(*this), mesh()); } -inline bool SmartHalfedgeHandle::is_boundary() const -{ - assert(mesh() != nullptr); - return mesh()->is_boundary(*this); -} - inline SmartHalfedgeHandle SmartEdgeHandle::halfedge(unsigned int _i) const { assert(mesh() != nullptr); @@ -409,12 +404,6 @@ inline SmartVertexHandle SmartEdgeHandle::v1() const return v(1); } -inline bool SmartEdgeHandle::is_boundary() const -{ - assert(mesh() != nullptr); - return mesh()->is_boundary(*this); -} - inline SmartHalfedgeHandle SmartFaceHandle::halfedge() const { assert(mesh() != nullptr); @@ -427,11 +416,6 @@ inline uint SmartFaceHandle::valence() const return mesh()->valence(*this); } -inline bool SmartFaceHandle::is_boundary() const -{ - assert(mesh() != nullptr); - return mesh()->is_boundary(*this); -} //============================================================================= } // namespace OpenMesh //============================================================================= From 6396a04c25995a254fb8685d12d7bdcfdb238157 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 15 Oct 2020 13:52:32 +0200 Subject: [PATCH 11/25] iterate over all elements in smart handle unittest --- src/Unittests/unittests_smart_handles.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Unittests/unittests_smart_handles.cc b/src/Unittests/unittests_smart_handles.cc index 729d8b3c..eec50a1a 100644 --- a/src/Unittests/unittests_smart_handles.cc +++ b/src/Unittests/unittests_smart_handles.cc @@ -608,10 +608,10 @@ TEST_F(OpenMeshSmartHandles, StatusAccess) mesh_.request_edge_status(); mesh_.request_face_status(); - test_status_fields(mesh_, mesh_.vertices()); - test_status_fields(mesh_, mesh_.edges()); - test_status_fields(mesh_, mesh_.halfedges()); - test_status_fields(mesh_, mesh_.faces()); + test_status_fields(mesh_, mesh_.all_vertices()); + test_status_fields(mesh_, mesh_.all_edges()); + test_status_fields(mesh_, mesh_.all_halfedges()); + test_status_fields(mesh_, mesh_.all_faces()); } From 62f30db35b04478cb84c7bfc6fdd2e294d20ce64 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 15 Oct 2020 14:01:32 +0200 Subject: [PATCH 12/25] update changelog --- Doc/changelog.docu | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/changelog.docu b/Doc/changelog.docu index 3145dc80..de3b5d66 100644 --- a/Doc/changelog.docu +++ b/Doc/changelog.docu @@ -18,6 +18,7 @@
      • Add filtered range that stores reference instead of copy if the filter is not an rvalue reference
      • Add halfedge loop range corresponding to hl_iter()
      • +
      • Smart handles now give read access to status fields (feature(), selected(), deleted(), tagged(), tagged2(), hidden(), locked()
      IO From e5729bcbeb802983e32ec6ff2a5d01a45f2036e7 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 15 Oct 2020 14:03:39 +0200 Subject: [PATCH 13/25] add smart handle predicates --- .../Core/Mesh/SmartRangePredicates.hh | 259 ++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 src/OpenMesh/Core/Mesh/SmartRangePredicates.hh diff --git a/src/OpenMesh/Core/Mesh/SmartRangePredicates.hh b/src/OpenMesh/Core/Mesh/SmartRangePredicates.hh new file mode 100644 index 00000000..332b488a --- /dev/null +++ b/src/OpenMesh/Core/Mesh/SmartRangePredicates.hh @@ -0,0 +1,259 @@ +/* ========================================================================= * + * * + * OpenMesh * + * Copyright (c) 2001-2020, RWTH-Aachen University * + * Department of Computer Graphics and Multimedia * + * All rights reserved. * + * www.openmesh.org * + * * + *---------------------------------------------------------------------------* + * This file is part of OpenMesh. * + *---------------------------------------------------------------------------* + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright notice, * + * this list of conditions and the following disclaimer. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the following disclaimer in the * + * documentation and/or other materials provided with the distribution. * + * * + * 3. Neither the name of the copyright holder nor the names of its * + * contributors may be used to endorse or promote products derived from * + * this software without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + * * + * ========================================================================= */ + + +#pragma once + +#include +#include + +#include +#include +#include +#include + +//== NAMESPACES =============================================================== + +namespace OpenMesh { + +namespace Predicates { + +//== FORWARD DECLARATION ====================================================== + +//== CLASS DEFINITION ========================================================= + +template +struct PredicateBase +{ +}; + +template +struct Predicate : public PredicateBase> +{ + Predicate(PredicateT _p) + : + p_(_p) + {} + + template + bool operator()(const T& _t) const { return p_(_t); } + + PredicateT p_; +}; + +template +Predicate make_predicate(PredicateT& _p) { return { _p }; } + +template +Predicate make_predicate(PredicateT&& _p) { return { _p }; } + +template +struct Disjunction : public PredicateBase> +{ + Disjunction(Predicate1T _p1, Predicate2T _p2) + : + p1_(_p1), + p2_(_p2) + {} + + template + bool operator()(const T& _t) const { return p1_( _t) || p2_( _t); } + + Predicate1T p1_; + Predicate2T p2_; +}; + +template +struct Conjunction : public PredicateBase> +{ + Conjunction(Predicate1T _p1, Predicate2T _p2) + : + p1_(_p1), + p2_(_p2) + {} + + template + bool operator()(const T& _t) const { return p1_( _t) && p2_( _t); } + + Predicate1T p1_; + Predicate2T p2_; +}; + + +template +struct Negation : public PredicateBase> +{ + Negation(const PredicateT& _p1) + : + p1_(_p1) + {} + + template + bool operator()(const T& _t) const { return !p1_( _t); } + + PredicateT p1_; +}; + +template +Disjunction operator||(PredicateBase& p1, PredicateBase& p2) +{ + return Disjunction(static_cast(p1), static_cast(p2)); +} + +template +Disjunction operator||(PredicateBase& p1, PredicateBase&& p2) +{ + return Disjunction(static_cast(p1), static_cast(p2)); +} + +template +Disjunction operator||(PredicateBase&& p1, PredicateBase& p2) +{ + return Disjunction(static_cast(p1), static_cast(p2)); +} + +template +Disjunction operator||(PredicateBase&& p1, PredicateBase&& p2) +{ + return Disjunction(static_cast(p1), static_cast(p2)); +} + +template +Conjunction operator&&(PredicateBase& p1, PredicateBase& p2) +{ + return Conjunction(static_cast(p1), static_cast(p2)); +} + +template +Conjunction operator&&(PredicateBase& p1, PredicateBase&& p2) +{ + return Conjunction(static_cast(p1), static_cast(p2)); +} + +template +Conjunction operator&&(PredicateBase&& p1, PredicateBase& p2) +{ + return Conjunction(static_cast(p1), static_cast(p2)); +} + +template +Conjunction operator&&(PredicateBase&& p1, PredicateBase&& p2) +{ + return Conjunction(static_cast(p1), static_cast(p2)); +} + +template +Negation operator!(PredicateBase

      & p) +{ + return Negation(static_cast(p)); +} + +template +Negation

      operator!(PredicateBase

      && p) +{ + return Negation

      (static_cast(p)); +} + +struct Feature : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.feature(); } +}; + +struct Selected : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.selected(); } +}; + +struct Tagged : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.tagged(); } +}; + +struct Tagged2 : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.tagged2(); } +}; + +struct Locked : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.locked(); } +}; + +struct Hidden : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.hidden(); } +}; + +struct Deleted : public PredicateBase +{ + template + bool operator()(const SmartHandleStatusPredicates& _h) const { return _h.deleted(); } +}; + +struct Boundary : public PredicateBase +{ + template + bool operator()(const SmartHandleBoundaryPredicate& _h) const { return _h.is_boundary(); } +}; + +template +struct Regular: public PredicateBase> +{ + bool operator()(const SmartVertexHandle& _vh) const { return _vh.valence() == (_vh.is_boundary() ? boundary_reg : inner_reg); } +}; + +using RegularQuad = Regular<4,3>; +using RegularTri = Regular<6,4>; + + +//============================================================================= +} // namespace Predicates + +} // namespace OpenMesh +//============================================================================= + +//============================================================================= From 563149a8c5424bf66975addae664d3e92b32848a Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 15 Oct 2020 14:34:17 +0200 Subject: [PATCH 14/25] add test for range predicates --- src/Unittests/unittests_smart_ranges.cc | 438 +++++++++++++++++++++++- 1 file changed, 437 insertions(+), 1 deletion(-) diff --git a/src/Unittests/unittests_smart_ranges.cc b/src/Unittests/unittests_smart_ranges.cc index 93eaa15d..a1062e96 100644 --- a/src/Unittests/unittests_smart_ranges.cc +++ b/src/Unittests/unittests_smart_ranges.cc @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -258,7 +259,7 @@ TEST_F(OpenMeshSmartRanges, BoundingBox) // Thus we convert here. OpenMesh::VProp myPos(mesh_); for (auto vh : mesh_.vertices()) - for (size_t i = 0; i < 3; ++i) + for (int i = 0; i < 3; ++i) myPos(vh)[i] = mesh_.point(vh)[i]; auto bb_min = mesh_.vertices().min(myPos); @@ -421,4 +422,439 @@ TEST_F(OpenMeshSmartRanges, WeightedAvg) } +template +void test_range_predicates(Mesh& _mesh) +{ + using namespace OpenMesh::Predicates; + + auto get_random_set = [&](int n) + { + auto max = _mesh.n_elements(); + std::vector set; + set.push_back(HandleT(0)); + for (int i = 0; i < n; ++i) + set.push_back(HandleT(rand() % max)); + std::sort(set.begin(), set.end()); + set.erase(std::unique(set.begin(), set.end()), set.end()); + return set; + }; + + // Feature + { + for (auto el : _mesh.elements()) + _mesh.status(el).set_feature(false); + auto set = get_random_set(4); + for (auto el : set) + _mesh.status(el).set_feature(true); + + auto set2 = _mesh.elements().filtered(Feature()).to_vector(); + + EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ"; + for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i) + EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.elements()) + _mesh.status(el).set_feature(false); + } + + // Selected + { + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + auto set = get_random_set(4); + for (auto el : set) + _mesh.status(el).set_selected(true); + + auto set2 = _mesh.elements().filtered(Selected()).to_vector(); + + EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ"; + for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i) + EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + } + + // Tagged + { + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + auto set = get_random_set(4); + for (auto el : set) + _mesh.status(el).set_tagged(true); + + auto set2 = _mesh.elements().filtered(Tagged()).to_vector(); + + EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ"; + for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i) + EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + } + + // Tagged2 + { + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged2(false); + auto set = get_random_set(4); + for (auto el : set) + _mesh.status(el).set_tagged2(true); + + auto set2 = _mesh.elements().filtered(Tagged2()).to_vector(); + + EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ"; + for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i) + EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged2(false); + } + + // Locked + { + for (auto el : _mesh.elements()) + _mesh.status(el).set_locked(false); + auto set = get_random_set(4); + for (auto el : set) + _mesh.status(el).set_locked(true); + + auto set2 = _mesh.elements().filtered(Locked()).to_vector(); + + EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ"; + for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i) + EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.elements()) + _mesh.status(el).set_locked(false); + } + + // Hidden + { + for (auto el : _mesh.all_elements()) + _mesh.status(el).set_hidden(false); + auto set = get_random_set(4); + for (auto el : set) + _mesh.status(el).set_hidden(true); + + auto set2 = _mesh.all_elements().filtered(Hidden()).to_vector(); + + EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ"; + for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i) + EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.all_elements()) + _mesh.status(el).set_hidden(false); + } + + // Deleted + { + for (auto el : _mesh.all_elements()) + _mesh.status(el).set_deleted(false); + auto set = get_random_set(4); + for (auto el : set) + _mesh.status(el).set_deleted(true); + + auto set2 = _mesh.all_elements().filtered(Deleted()).to_vector(); + + EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ"; + for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i) + EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.all_elements()) + _mesh.status(el).set_deleted(false); + } + + // Custom property + { + OpenMesh::PropertyManager::template type> prop(false, _mesh); + auto set = get_random_set(4); + for (auto el : set) + prop[el] = true; + + auto set2 = _mesh.elements().filtered(prop).to_vector(); + + EXPECT_EQ(set.size(), set2.size()) << "Set sizes differ"; + for (size_t i = 0; i < std::min(set.size(), set2.size()); ++i) + EXPECT_EQ(set[i], set2[i]) << "Sets differ at position " << i; + } + + // boundary + { + for (auto el : _mesh.elements().filtered(Boundary())) + EXPECT_TRUE(el.is_boundary()); + int n_boundary1 = 0.0; + for (auto el : _mesh.elements()) + if (el.is_boundary()) + n_boundary1 += 1; + int n_boundary2 = _mesh.elements().count_if(Boundary()); + EXPECT_EQ(n_boundary1, n_boundary2); + } +} + +template +void test_range_predicate_combinations(Mesh& _mesh) +{ + using namespace OpenMesh::Predicates; + + auto n_elements = _mesh.n_elements(); + auto get_random_set = [&](int n) + { + std::vector set; + for (int i = 0; i < n; ++i) + set.push_back(HandleT(rand() % n_elements)); + std::sort(set.begin(), set.end()); + set.erase(std::unique(set.begin(), set.end()), set.end()); + return set; + }; + + // negation + { + auto set = get_random_set(4); + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : set) + _mesh.status(el).set_selected(true); + + auto true_set = _mesh.elements().filtered(Selected()).to_vector(); + auto false_set = _mesh.elements().filtered(!Selected()).to_vector(); + + std::vector intersection; + std::set_intersection(true_set.begin(), true_set.end(), false_set.begin(), false_set.end(), std::back_inserter(intersection)); + + EXPECT_TRUE(intersection.empty()); + EXPECT_EQ(true_set.size() + false_set.size(), n_elements); + + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + } + + // conjunction + { + auto set1 = get_random_set(4); + auto set2 = get_random_set(4); + // make sure there is some overlap + { + auto set3 = get_random_set(3); + set1.insert(set1.end(), set3.begin(), set3.end()); + set2.insert(set2.end(), set3.begin(), set3.end()); + std::sort(set1.begin(), set1.end()); + std::sort(set2.begin(), set2.end()); + set1.erase(std::unique(set1.begin(), set1.end()), set1.end()); + set2.erase(std::unique(set2.begin(), set2.end()), set2.end()); + } + + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + for (auto el : set1) + _mesh.status(el).set_selected(true); + for (auto el : set2) + _mesh.status(el).set_tagged(true); + + auto set = _mesh.elements().filtered(Selected() && Tagged()).to_vector(); + + std::vector intersection; + std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), std::back_inserter(intersection)); + + EXPECT_EQ(intersection.size(), set.size()); + for (size_t i = 0; i < std::min(intersection.size(), set.size()); ++i) + EXPECT_EQ(intersection[i], set[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + } + + // Disjunction + { + auto set1 = get_random_set(4); + auto set2 = get_random_set(4); + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + for (auto el : set1) + _mesh.status(el).set_selected(true); + for (auto el : set2) + _mesh.status(el).set_tagged(true); + + auto set = _mesh.elements().filtered(Selected() || Tagged()).to_vector(); + + std::vector union_set; + std::set_union(set1.begin(), set1.end(), set2.begin(), set2.end(), std::back_inserter(union_set)); + + EXPECT_EQ(union_set.size(), set.size()); + for (size_t i = 0; i < std::min(union_set.size(), set.size()); ++i) + EXPECT_EQ(union_set[i], set[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + } + +} + +template +bool test_func(HandleT _h) +{ + return _h.idx() % 3 == 0; +} + +template +void test_make_predicate(Mesh& _mesh) +{ + using namespace OpenMesh::Predicates; + + auto n_elements = _mesh.n_elements(); + auto get_random_set = [&](int n) + { + std::vector set; + for (int i = 0; i < n; ++i) + set.push_back(HandleT(rand() % n_elements)); + std::sort(set.begin(), set.end()); + set.erase(std::unique(set.begin(), set.end()), set.end()); + return set; + }; + + // custom property + { + OpenMesh::PropertyManager::template type> prop(false, _mesh); + auto set1 = get_random_set(4); + auto set2 = get_random_set(4); + for (auto el : set1) + prop[el] = true; + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : set2) + _mesh.status(el).set_selected(true); + + auto set = _mesh.elements().filtered(Selected() || make_predicate(prop)).to_vector(); + + std::vector union_set; + std::set_union(set1.begin(), set1.end(), set2.begin(), set2.end(), std::back_inserter(union_set)); + + EXPECT_EQ(union_set.size(), set.size()); + for (size_t i = 0; i < std::min(union_set.size(), set.size()); ++i) + EXPECT_EQ(union_set[i], set[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + } + + // lambda + { + OpenMesh::PropertyManager::template type> prop(false, _mesh); + auto set1 = get_random_set(4); + auto set2 = get_random_set(4); + for (auto el : set1) + prop[el] = true; + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : set2) + _mesh.status(el).set_selected(true); + + auto test = [&](HandleT h) { return prop(h); }; + + auto set = _mesh.elements().filtered(Selected() || make_predicate(test)).to_vector(); + + std::vector union_set; + std::set_union(set1.begin(), set1.end(), set2.begin(), set2.end(), std::back_inserter(union_set)); + + EXPECT_EQ(union_set.size(), set.size()); + for (size_t i = 0; i < std::min(union_set.size(), set.size()); ++i) + EXPECT_EQ(union_set[i], set[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + } + + // r value lambda + { + OpenMesh::PropertyManager::template type> prop(false, _mesh); + auto set1 = get_random_set(4); + auto set2 = get_random_set(4); + for (auto el : set1) + prop[el] = true; + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : set2) + _mesh.status(el).set_selected(true); + + auto set = _mesh.elements().filtered(Selected() || make_predicate([&](HandleT h) { return prop(h); })).to_vector(); + + std::vector union_set; + std::set_union(set1.begin(), set1.end(), set2.begin(), set2.end(), std::back_inserter(union_set)); + + EXPECT_EQ(union_set.size(), set.size()); + for (size_t i = 0; i < std::min(union_set.size(), set.size()); ++i) + EXPECT_EQ(union_set[i], set[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + } + + // function pointer + { + auto set1 = _mesh.elements().filtered([&](const HandleT& h) { return test_func(h); }).to_vector(); + auto set2 = get_random_set(4); + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : set2) + _mesh.status(el).set_selected(true); + + 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)); + + EXPECT_EQ(union_set.size(), set.size()); + for (size_t i = 0; i < std::min(union_set.size(), set.size()); ++i) + EXPECT_EQ(union_set[i], set[i]) << "Sets differ at position " << i; + + for (auto el : _mesh.elements()) + _mesh.status(el).set_selected(false); + for (auto el : _mesh.elements()) + _mesh.status(el).set_tagged(false); + } +} + +TEST_F(OpenMeshSmartRanges, Predicate) +{ + using namespace OpenMesh; + + mesh_.request_vertex_status(); + mesh_.request_halfedge_status(); + mesh_.request_edge_status(); + mesh_.request_face_status(); + + mesh_.delete_face(FaceHandle(0)); // ensure there is a boundary + mesh_.garbage_collection(); + + test_range_predicates(mesh_); + test_range_predicates(mesh_); + test_range_predicates(mesh_); + test_range_predicates(mesh_); + + test_range_predicate_combinations(mesh_); + test_range_predicate_combinations(mesh_); + test_range_predicate_combinations(mesh_); + test_range_predicate_combinations(mesh_); + + test_make_predicate(mesh_); + test_make_predicate(mesh_); + test_make_predicate(mesh_); + test_make_predicate(mesh_); +} + + } From 4ac19ab52eee320a939e984af3caa63339998bd7 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 15 Oct 2020 14:35:30 +0200 Subject: [PATCH 15/25] update changelog --- Doc/changelog.docu | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/changelog.docu b/Doc/changelog.docu index de3b5d66..24f358f1 100644 --- a/Doc/changelog.docu +++ b/Doc/changelog.docu @@ -19,6 +19,8 @@

    • Add filtered range that stores reference instead of copy if the filter is not an rvalue reference
    • Add halfedge loop range corresponding to hl_iter()
    • Smart handles now give read access to status fields (feature(), selected(), deleted(), tagged(), tagged2(), hidden(), locked()
    • +
    • Add predicates that can be used as smart range filters. There are default predicates to test the status fields and whether an elements is boundary. Predicates can be composed via operators ||, && and !
    • +
    • Add make_predicate to create a predicate from anything containing a bool operator(T), e.g. a PropertyManager, a lambda, or a function pointer.
    IO From 123fe55b727e6c78db4e7d6ba0ce31ec1a64bc46 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Thu, 15 Oct 2020 16:54:14 +0200 Subject: [PATCH 16/25] 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 17/25] 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 18/25] 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 19/25] 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 20/25] 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 21/25] 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); + } + + + +} } From 4436f3299dd59da6399712ce773c1663815331a8 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Mon, 9 Nov 2020 15:28:01 +0100 Subject: [PATCH 22/25] cast to pointers instead of references --- src/OpenMesh/Core/IO/writer/OMWriter.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/OpenMesh/Core/IO/writer/OMWriter.cc b/src/OpenMesh/Core/IO/writer/OMWriter.cc index 528b2cfa..9e88b969 100644 --- a/src/OpenMesh/Core/IO/writer/OMWriter.cc +++ b/src/OpenMesh/Core/IO/writer/OMWriter.cc @@ -623,17 +623,17 @@ size_t _OMWriter_::store_binary_custom_chunk(std::ostream& _os, // 3. data type OMFormat::Chunk::PropertyType type = OMFormat::Chunk::PropertyType::UnknownType; - OpenMesh::PropertyT& bp_bool = dynamic_cast&>(_bp); - OpenMesh::PropertyT& bp_char = dynamic_cast&>(_bp); - OpenMesh::PropertyT& bp_double = dynamic_cast&>(_bp); - OpenMesh::PropertyT& bp_float = dynamic_cast&>(_bp); - OpenMesh::PropertyT& bp_int = dynamic_cast&>(_bp); - OpenMesh::PropertyT& bp_long = dynamic_cast&>(_bp); - OpenMesh::PropertyT& bp_short = dynamic_cast&>(_bp); - OpenMesh::PropertyT& bp_uchar = dynamic_cast&>(_bp); - OpenMesh::PropertyT& bp_uint = dynamic_cast&>(_bp); - OpenMesh::PropertyT& bp_ulong = dynamic_cast&>(_bp); - OpenMesh::PropertyT& bp_vecdouble = dynamic_cast&>(_bp); + OpenMesh::PropertyT* bp_bool = dynamic_cast*>(&_bp); + OpenMesh::PropertyT* bp_char = dynamic_cast*>(&_bp); + OpenMesh::PropertyT* bp_double = dynamic_cast*>(&_bp); + OpenMesh::PropertyT* bp_float = dynamic_cast*>(&_bp); + OpenMesh::PropertyT* bp_int = dynamic_cast*>(&_bp); + OpenMesh::PropertyT* bp_long = dynamic_cast*>(&_bp); + OpenMesh::PropertyT* bp_short = dynamic_cast*>(&_bp); + OpenMesh::PropertyT* bp_uchar = dynamic_cast*>(&_bp); + OpenMesh::PropertyT* bp_uint = dynamic_cast*>(&_bp); + OpenMesh::PropertyT* bp_ulong = dynamic_cast*>(&_bp); + OpenMesh::PropertyT* bp_vecdouble = dynamic_cast*>(&_bp); if(_bp.internal_type_name() == get_type_name()) type = OMFormat::Chunk::PropertyType::BoolType; From 7a559a2bce1be5832b9e77d440a7e80bc9943a5d Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Mon, 9 Nov 2020 16:53:12 +0100 Subject: [PATCH 23/25] add unittest checking loading of properties of om version 2.1 --- .../TestFiles/cube_tri_with_properties_2_1.om | Bin 0 -> 24340 bytes src/Unittests/unittests_read_write_OM.cc | 190 ++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 src/Unittests/TestFiles/cube_tri_with_properties_2_1.om diff --git a/src/Unittests/TestFiles/cube_tri_with_properties_2_1.om b/src/Unittests/TestFiles/cube_tri_with_properties_2_1.om new file mode 100644 index 0000000000000000000000000000000000000000..26c4e1715db6d530afb27e91992ff60de2bf1232 GIT binary patch literal 24340 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 literal 0 HcmV?d00001 diff --git a/src/Unittests/unittests_read_write_OM.cc b/src/Unittests/unittests_read_write_OM.cc index 35e4522d..4766b629 100644 --- a/src/Unittests/unittests_read_write_OM.cc +++ b/src/Unittests/unittests_read_write_OM.cc @@ -1,6 +1,7 @@ #include #include +#include namespace { @@ -1458,6 +1459,195 @@ TEST_F(OpenMeshReadWriteOM, LoadPolyMeshVersion_2_0) { } + +std::string get_type_string(OpenMesh::FaceHandle) { return "Face"; } +std::string get_type_string(OpenMesh::EdgeHandle) { return "Edge"; } +std::string get_type_string(OpenMesh::HalfedgeHandle) { return "Halfedge"; } +std::string get_type_string(OpenMesh::VertexHandle) { return "Vertex"; } + +std::string get_type_string(char) { return "char"; } +std::string get_type_string(double) { return "double"; } +std::string get_type_string(float) { return "float"; } +std::string get_type_string(int) { return "int"; } +std::string get_type_string(short) { return "short"; } +std::string get_type_string(unsigned char) { return "unsigned char"; } +std::string get_type_string(unsigned int) { return "unsigned int"; } +std::string get_type_string(unsigned short) { return "unsigned short"; } +std::string get_type_string(bool) { return "bool"; } + +template +std::string get_type_string(std::vector) { return "std::vector of " + get_type_string(T()); } + +template +std::string get_type_string(OpenMesh::VectorT) { return "OM vector of dimension " + std::to_string(Dim) + " of type " + get_type_string(T()); } + + +template +T get_value(int seed, T, int seed2 = 0) +{ + return (seed * 3 + seed2) % 20; +} + +template +std::vector get_value(int seed, const std::vector&) +{ + int size = get_value(seed, 3); + std::vector res(size); + for (int i = 0; i < size; ++i) + res[i] = get_value(seed, T(), i); + return res; +} + +template +OpenMesh::VectorT get_value(int seed, const OpenMesh::VectorT&) +{ + OpenMesh::VectorT res; + for (int i = 0; i < Dim; ++i) + res[i] = get_value(seed, T(), i); + return res; +} + +template +OpenMesh::Prop add_property(MeshT& _mesh) +{ + std::string name = get_type_string(HandleT()) + ": " + get_type_string(T()); + OpenMesh::Prop prop(_mesh, name.c_str()); + _mesh.property(prop.getRawProperty()).set_persistent(true); + for (auto e : _mesh.template elements()) + prop[e] = get_value(e.idx(), T()); + + return prop; +} + +template +void check_property(MeshT& _mesh) +{ + std::string name = get_type_string(HandleT()) + ": " + get_type_string(T()); + bool has_prop = OpenMesh::hasProperty(_mesh, name.c_str()); + EXPECT_TRUE(has_prop) << "Property " << name << " is not available"; + if (!has_prop) + return; + OpenMesh::Prop prop(_mesh, name.c_str()); + for (auto e : _mesh.template elements()) + EXPECT_EQ(prop[e], get_value(e.idx(), T())) << "For property " << name; +} + +template +void request_property(MeshT& _mesh) +{ + std::string name = get_type_string(HandleT()) + ": " + get_type_string(T()); + OpenMesh::Prop prop(_mesh, name.c_str()); +} + + +enum class PropertyAction +{ + Add, Check, Request +}; + + +template +void do_property(MeshT& _mesh, PropertyAction action) +{ + switch (action) + { + case PropertyAction::Add: + add_property(_mesh); + break; + case PropertyAction::Check: + check_property(_mesh); + break; + case PropertyAction::Request: + request_property(_mesh); + break; + } +} + + + +template +void do_all_property_types(MeshT& _mesh, PropertyAction action) +{ + // TODO: add support for commented out types + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); +// do_property (_mesh, action); +// do_property> (_mesh, action); +// do_property>(_mesh, action); +// do_property> (_mesh, action); +// do_property> (_mesh, action); +// do_property> (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); +// do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); +// do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); +// do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); +// do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); +} + +template +void do_all_properties(MeshT& _mesh, PropertyAction action) +{ + do_all_property_types (_mesh, action); + do_all_property_types (_mesh, action); + do_all_property_types(_mesh, action); + do_all_property_types (_mesh, action); +} + +template void add_all_properties(MeshT& _mesh) { do_all_properties(_mesh, PropertyAction::Add ); } +template void check_all_properties(MeshT& _mesh) { do_all_properties(_mesh, PropertyAction::Check ); } +template void request_all_properties(MeshT& _mesh) { do_all_properties(_mesh, PropertyAction::Request); } + +/* + * Load a triangle mesh from an om file of version 2.1 with properties + */ +TEST_F(OpenMeshReadWriteOM, LoadTriangleMeshWithPropertiesVersion_2_1) { + + mesh_.clear(); + + std::string file_name = "cube_tri_with_properties_2_1.om"; + + request_all_properties(mesh_); + bool ok = OpenMesh::IO::read_mesh(mesh_, file_name); + + ASSERT_TRUE(ok) << file_name; + + ASSERT_EQ(8u , mesh_.n_vertices()) << "The number of loaded vertices is not correct!"; + ASSERT_EQ(18u , mesh_.n_edges()) << "The number of loaded edges is not correct!"; + ASSERT_EQ(12u , mesh_.n_faces()) << "The number of loaded faces is not correct!"; + ASSERT_EQ(36u , mesh_.n_halfedges()) << "The number of loaded halfedges is not correct!"; + + check_all_properties(mesh_); +} + /* * Try to load mesh from om file with a version that is not yet supported */ From a5e204a6b42bd7712d85a4e376b30680dcdb6ab6 Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Mon, 9 Nov 2020 17:04:06 +0100 Subject: [PATCH 24/25] add unittest to check if loading properties works in om file format version 2.2 --- src/Unittests/unittests_read_write_OM.cc | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/Unittests/unittests_read_write_OM.cc b/src/Unittests/unittests_read_write_OM.cc index 4766b629..60788df6 100644 --- a/src/Unittests/unittests_read_write_OM.cc +++ b/src/Unittests/unittests_read_write_OM.cc @@ -1648,6 +1648,39 @@ TEST_F(OpenMeshReadWriteOM, LoadTriangleMeshWithPropertiesVersion_2_1) { check_all_properties(mesh_); } +/* + * store and load a triangle mesh from an om file of the current version with properties + */ +TEST_F(OpenMeshReadWriteOM, LoadTriangleMeshWithPropertiesCurrentVersion) { + + // TODO: create a LoadTriangleMeshWithPropertiesVersion_2_2 unittest from the file resulting from this test + // so we will keep testing version 2.2 in the future. + + mesh_.clear(); + + std::string file_name = "cube_tri_version_2_0.om"; + + bool ok = OpenMesh::IO::read_mesh(mesh_, file_name); + + ASSERT_TRUE(ok) << file_name; + ASSERT_EQ(8u , mesh_.n_vertices()) << "The number of loaded vertices is not correct!"; + ASSERT_EQ(18u , mesh_.n_edges()) << "The number of loaded edges is not correct!"; + ASSERT_EQ(12u , mesh_.n_faces()) << "The number of loaded faces is not correct!"; + ASSERT_EQ(36u , mesh_.n_halfedges()) << "The number of loaded halfedges is not correct!"; + + add_all_properties(mesh_); + + std::string file_name_2_2 = "cube_tri_with_properties_2_2.om"; + OpenMesh::IO::Options ops(OpenMesh::IO::Options::Custom); + OpenMesh::IO::write_mesh(mesh_, file_name_2_2, ops); + + Mesh new_mesh; + + OpenMesh::IO::read_mesh(new_mesh, file_name_2_2, ops); + + check_all_properties(new_mesh); +} + /* * Try to load mesh from om file with a version that is not yet supported */ From 0cce038f104aa987e46d058c47a5335cb7ba4d3b Mon Sep 17 00:00:00 2001 From: Max Lyon Date: Mon, 9 Nov 2020 17:24:36 +0100 Subject: [PATCH 25/25] small unit test refactoring --- src/Unittests/unittests_read_write_OM.cc | 73 ++++++++++-------------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/src/Unittests/unittests_read_write_OM.cc b/src/Unittests/unittests_read_write_OM.cc index 60788df6..fe244cfc 100644 --- a/src/Unittests/unittests_read_write_OM.cc +++ b/src/Unittests/unittests_read_write_OM.cc @@ -1563,54 +1563,43 @@ void do_property(MeshT& _mesh, PropertyAction action) } } +template +void do_all_property_types_vec(MeshT& _mesh, PropertyAction action) +{ + do_property>(_mesh, action); + do_property>(_mesh, action); + do_property>(_mesh, action); +// do_property>(_mesh, action); + do_property>(_mesh, action); + do_property>(_mesh, action); + do_property>(_mesh, action); + do_property>(_mesh, action); +} +template +void do_all_property_types_vec_all_dim(MeshT& _mesh, PropertyAction action) +{ + do_all_property_types_vec(_mesh, action); + do_all_property_types_vec(_mesh, action); + do_all_property_types_vec(_mesh, action); + do_all_property_types_vec(_mesh, action); +} template void do_all_property_types(MeshT& _mesh, PropertyAction action) { // TODO: add support for commented out types - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); -// do_property (_mesh, action); -// do_property> (_mesh, action); -// do_property>(_mesh, action); -// do_property> (_mesh, action); -// do_property> (_mesh, action); -// do_property> (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); -// do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); -// do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); -// do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); -// do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); - do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); + do_property (_mesh, action); +// do_property (_mesh, action); +// do_property> (_mesh, action); +// do_property> (_mesh, action); +// do_property> (_mesh, action); +// do_property> (_mesh, action); +// do_property> (_mesh, action); + do_all_property_types_vec_all_dim(_mesh, action); } template