diff --git a/src/OpenMesh/Tools/Decimater/BaseDecimaterT.cc b/src/OpenMesh/Tools/Decimater/BaseDecimaterT.cc index 2b01732a..31a22054 100644 --- a/src/OpenMesh/Tools/Decimater/BaseDecimaterT.cc +++ b/src/OpenMesh/Tools/Decimater/BaseDecimaterT.cc @@ -67,7 +67,7 @@ namespace Decimater { template BaseDecimaterT::BaseDecimaterT(Mesh& _mesh) : - mesh_(_mesh), cmodule_(NULL), initialized_(false) { + mesh_(_mesh), cmodule_(NULL), initialized_(false), observer_(NULL) { // default properties mesh_.request_vertex_status(); mesh_.request_edge_status(); diff --git a/src/OpenMesh/Tools/Decimater/BaseDecimaterT.hh b/src/OpenMesh/Tools/Decimater/BaseDecimaterT.hh index 26ce61c6..a7ac644b 100644 --- a/src/OpenMesh/Tools/Decimater/BaseDecimaterT.hh +++ b/src/OpenMesh/Tools/Decimater/BaseDecimaterT.hh @@ -71,6 +71,30 @@ namespace Decimater { //== CLASS DEFINITION ========================================================= +class Observer +{ +public: + Observer(size_t _step) : + step_(_step) { + } + + virtual ~Observer() { + } + + size_t get_step() const { return step_; } + // give the notification step size + void set_step(size_t _step) { step_ = _step; } + + //notifies about a finished decimater step with the given step number + virtual void notify(size_t _step) = 0; + //after notification, ask for abort or not, return true, if the decimater should abort + virtual bool abort() const = 0; + +private: + size_t step_; +}; + + /** base class decimater framework \see BaseDecimaterT, \ref decimater_docu */ @@ -113,6 +137,17 @@ public: //------------------------------------------------------ public methods public: //--------------------------------------------------- module management + // Observer interface + void set_observer(Observer* _o) + { + observer_ = _o; + } + + Observer* observer() + { + return observer_; + } + /// access mesh. used in modules. Mesh& mesh() { return mesh_; } @@ -166,6 +201,17 @@ public: //--------------------------------------------------- module management protected: + //returns false, if abort requested by observer + bool notify_observer(size_t _n_collapses) + { + if (observer() && _n_collapses % observer()->get_step() == 0) + { + observer()->notify(_n_collapses); + return !observer()->abort(); + } + return true; + } + // Reset the initialized flag, and clear the bmodules_ and cmodule_ void set_uninitialized() { initialized_ = false; @@ -234,6 +280,8 @@ private: //------------------------------------------------------- private data // Flag if all modules were initialized bool initialized_; + // observer + Observer* observer_; }; diff --git a/src/OpenMesh/Tools/Decimater/DecimaterT.cc b/src/OpenMesh/Tools/Decimater/DecimaterT.cc index 404a3d8b..d42b9f10 100644 --- a/src/OpenMesh/Tools/Decimater/DecimaterT.cc +++ b/src/OpenMesh/Tools/Decimater/DecimaterT.cc @@ -136,13 +136,13 @@ void DecimaterT::heap_vertex(VertexHandle _vh) { mesh_.property(priority_, _vh) = -1; } } - -//----------------------------------------------------------------------------- -template -size_t DecimaterT::decimate(size_t _n_collapses) { - - if (!this->is_initialized()) - return 0; + +//----------------------------------------------------------------------------- +template +size_t DecimaterT::decimate(size_t _n_collapses) { + + if (!this->is_initialized()) + return 0; typename Mesh::VertexIter v_it, v_end(mesh_.vertices_end()); typename Mesh::VertexHandle vp; @@ -212,12 +212,16 @@ size_t DecimaterT::decimate(size_t _n_collapses) { // update heap (former one ring of decimated vertex) for (s_it = support.begin(), s_end = support.end(); s_it != s_end; ++s_it) { - assert(!mesh_.status(*s_it).deleted()); - heap_vertex(*s_it); - } - } - - // delete heap + assert(!mesh_.status(*s_it).deleted()); + heap_vertex(*s_it); + } + + // notify observer and stop if the observer requests it + if (!this->notify_observer(n_collapses)) + return n_collapses; + } + + // delete heap heap_.reset(); @@ -226,13 +230,13 @@ size_t DecimaterT::decimate(size_t _n_collapses) { return n_collapses; } -//----------------------------------------------------------------------------- - -template -size_t DecimaterT::decimate_to_faces(size_t _nv, size_t _nf) { - - if (!this->is_initialized()) - return 0; +//----------------------------------------------------------------------------- + +template +size_t DecimaterT::decimate_to_faces(size_t _nv, size_t _nf) { + + if (!this->is_initialized()) + return 0; if (_nv >= mesh_.n_vertices() || _nf >= mesh_.n_faces()) return 0; @@ -313,12 +317,16 @@ size_t DecimaterT::decimate_to_faces(size_t _nv, size_t _nf) { // update heap (former one ring of decimated vertex) for (s_it = support.begin(), s_end = support.end(); s_it != s_end; ++s_it) { - assert(!mesh_.status(*s_it).deleted()); - heap_vertex(*s_it); - } - } - - // delete heap + assert(!mesh_.status(*s_it).deleted()); + heap_vertex(*s_it); + } + + // notify observer and stop if the observer requests it + if (!this->notify_observer(n_collapses)) + return n_collapses; + } + + // delete heap heap_.reset(); diff --git a/src/OpenMesh/Tools/Decimater/McDecimaterT.cc b/src/OpenMesh/Tools/Decimater/McDecimaterT.cc index 8c5b18f4..395d8178 100644 --- a/src/OpenMesh/Tools/Decimater/McDecimaterT.cc +++ b/src/OpenMesh/Tools/Decimater/McDecimaterT.cc @@ -198,6 +198,10 @@ size_t McDecimaterT::decimate(size_t _n_collapses) { // post-process collapse this->postprocess_collapse(ci); + // notify observer and stop if the observer requests it + if (!this->notify_observer(n_collapses)) + return n_collapses; + } else { if (oldCollapses == n_collapses) { if (collapsesUnchanged == false) { @@ -337,6 +341,10 @@ size_t McDecimaterT::decimate_to_faces(size_t _nv, size_t _nf) { // post-process collapse this->postprocess_collapse(ci); + // notify observer and stop if the observer requests it + if (!this->notify_observer(n_collapses)) + return n_collapses; + } else { if (oldCollapses == n_collapses) { if (collapsesUnchanged == false) { @@ -491,6 +499,10 @@ size_t McDecimaterT::decimate_constraints_only(float _factor) { // post-process collapse this->postprocess_collapse(ci); + // notify observer and stop if the observer requests it + if (!this->notify_observer(n_collapses)) + return n_collapses; + } else { if (oldCollapses == n_collapses) { if (collapsesUnchanged == false) { diff --git a/src/OpenMesh/Tools/Decimater/MixedDecimaterT.cc b/src/OpenMesh/Tools/Decimater/MixedDecimaterT.cc index 245eff22..48e0b09d 100644 --- a/src/OpenMesh/Tools/Decimater/MixedDecimaterT.cc +++ b/src/OpenMesh/Tools/Decimater/MixedDecimaterT.cc @@ -92,6 +92,11 @@ size_t MixedDecimaterT::decimate(const size_t _n_collapses, const float _m size_t r_collapses = 0; if (_mc_factor > 0.0) r_collapses = McDecimaterT::decimate(n_collapses_mc); + + // returns, if the previous steps were aborted by the observer + if (this->observer() && this->observer()->abort()) + return r_collapses; + if (_mc_factor < 1.0) r_collapses += DecimaterT::decimate(n_collapses_inc); @@ -147,6 +152,10 @@ size_t MixedDecimaterT::decimate_to_faces(const size_t _n_vertices,const //Update the mesh::n_vertices function, otherwise the next Decimater function will delete too much this->mesh().garbage_collection(); + // returns, if the previous steps were aborted by the observer + if (this->observer() && this->observer()->abort()) + return r_collapses; + //reduce the rest of the mesh if (_mc_factor < 1.0) { r_collapses += DecimaterT::decimate_to_faces(_n_vertices,_n_faces); diff --git a/src/Unittests/unittests_decimater.cc b/src/Unittests/unittests_decimater.cc index c85357c2..d24439ca 100644 --- a/src/Unittests/unittests_decimater.cc +++ b/src/Unittests/unittests_decimater.cc @@ -129,5 +129,59 @@ TEST_F(OpenMeshDecimater, DecimateMeshExampleFromDoc) { EXPECT_EQ(9996u, mesh_.n_faces()) << "The number of faces after decimation is not correct!"; } +class UnittestObserver : public OpenMesh::Decimater::Observer +{ + size_t notifies_; + size_t all_steps_; +public: + UnittestObserver(size_t _steps) :Observer(_steps), notifies_(0), all_steps_(0) {} + + void notify(size_t _step) + { + ++notifies_; + all_steps_ = _step; + } + bool abort() const + { + return all_steps_ >= 2526u; + } + + size_t countedNotifies() + { + return notifies_; + } +}; + +TEST_F(OpenMeshDecimater, DecimateMeshStoppedByObserver) { + + bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1.off"); + + ASSERT_TRUE(ok); + + typedef OpenMesh::Decimater::DecimaterT< Mesh > Decimater; + typedef OpenMesh::Decimater::ModQuadricT< Mesh >::Handle HModQuadric; + + Decimater decimaterDBG(mesh_); + HModQuadric hModQuadricDBG; + decimaterDBG.add(hModQuadricDBG); + + decimaterDBG.module(hModQuadricDBG).unset_max_err(); + + decimaterDBG.initialize(); + UnittestObserver obs(2); + decimaterDBG.set_observer(&obs); + size_t removedVertices = 0; + removedVertices = decimaterDBG.decimate_to_faces(0, 0); + decimaterDBG.mesh().garbage_collection(); + + EXPECT_TRUE(obs.abort()) << "Observer did not abort the decimater!"; + EXPECT_EQ(obs.countedNotifies(), 2526u / 2u) << "Observer did not get the right amount of notifications!"; + + EXPECT_EQ(2526u, removedVertices) << "The number of remove vertices is not correct!"; + EXPECT_EQ(5000u, mesh_.n_vertices()) << "The number of vertices after decimation is not correct!"; + EXPECT_EQ(14994u, mesh_.n_edges()) << "The number of edges after decimation is not correct!"; + EXPECT_EQ(9996u, mesh_.n_faces()) << "The number of faces after decimation is not correct!"; +} + } diff --git a/src/Unittests/unittests_mc_decimater.cc b/src/Unittests/unittests_mc_decimater.cc index 70273360..3a206c24 100644 --- a/src/Unittests/unittests_mc_decimater.cc +++ b/src/Unittests/unittests_mc_decimater.cc @@ -106,4 +106,58 @@ TEST_F(OpenMeshMultipleChoiceDecimater, DecimateMeshToFaceFaceLimit) { EXPECT_EQ(14994u, mesh_.n_edges()) << "The number of edges after decimation is not correct!"; EXPECT_EQ(9996u, mesh_.n_faces()) << "The number of faces after decimation is not correct!"; } + +class UnittestObserver : public OpenMesh::Decimater::Observer +{ + size_t notifies_; + size_t all_steps_; +public: + UnittestObserver(size_t _steps) :Observer(_steps), notifies_(0), all_steps_(0) {} + + void notify(size_t _step) + { + ++notifies_; + all_steps_ = _step; + } + bool abort() const + { + return all_steps_ >= 2526u; + } + + size_t countedNotifies() + { + return notifies_; + } +}; + +TEST_F(OpenMeshMultipleChoiceDecimater, DecimateMeshStoppedByObserver) { + + bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1.off"); + + ASSERT_TRUE(ok); + + typedef OpenMesh::Decimater::McDecimaterT< Mesh > Decimater; + typedef OpenMesh::Decimater::ModQuadricT< Mesh >::Handle HModQuadric; + + Decimater decimaterDBG(mesh_); + HModQuadric hModQuadricDBG; + decimaterDBG.add(hModQuadricDBG); + + decimaterDBG.module(hModQuadricDBG).unset_max_err(); + + decimaterDBG.initialize(); + UnittestObserver obs(2); + decimaterDBG.set_observer(&obs); + size_t removedVertices = 0; + removedVertices = decimaterDBG.decimate_to_faces(0, 0); + decimaterDBG.mesh().garbage_collection(); + + EXPECT_TRUE(obs.abort()) << "Observer did not abort the decimater!"; + EXPECT_EQ(obs.countedNotifies(), 2526u / 2u) << "Observer did not get the right amount of notifications!"; + + EXPECT_EQ(2526u, removedVertices) << "The number of remove vertices is not correct!"; + EXPECT_EQ(5000u, mesh_.n_vertices()) << "The number of vertices after decimation is not correct!"; + EXPECT_EQ(14994u, mesh_.n_edges()) << "The number of edges after decimation is not correct!"; + EXPECT_EQ(9996u, mesh_.n_faces()) << "The number of faces after decimation is not correct!"; +} } diff --git a/src/Unittests/unittests_mixed_decimater.cc b/src/Unittests/unittests_mixed_decimater.cc index 76f9200e..cd892e44 100644 --- a/src/Unittests/unittests_mixed_decimater.cc +++ b/src/Unittests/unittests_mixed_decimater.cc @@ -106,4 +106,58 @@ TEST_F(OpenMeshMixedDecimater, DecimateMeshToFaceFaceLimit) { EXPECT_EQ(14994u, mesh_.n_edges()) << "The number of edges after decimation is not correct!"; EXPECT_EQ(9996u, mesh_.n_faces()) << "The number of faces after decimation is not correct!"; } + +class UnittestObserver : public OpenMesh::Decimater::Observer +{ + size_t notifies_; + size_t all_steps_; +public: + UnittestObserver(size_t _steps) :Observer(_steps), notifies_(0), all_steps_(0) {} + + void notify(size_t _step) + { + ++notifies_; + all_steps_ = _step; + } + bool abort() const + { + return all_steps_ >= 2526u; + } + + size_t countedNotifies() + { + return notifies_; + } +}; + +TEST_F(OpenMeshMixedDecimater, DecimateMeshStoppedByObserver) { + + bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1.off"); + + ASSERT_TRUE(ok); + + typedef OpenMesh::Decimater::MixedDecimaterT< Mesh > Decimater; + typedef OpenMesh::Decimater::ModQuadricT< Mesh >::Handle HModQuadric; + + Decimater decimaterDBG(mesh_); + HModQuadric hModQuadricDBG; + decimaterDBG.add(hModQuadricDBG); + + decimaterDBG.module(hModQuadricDBG).unset_max_err(); + + decimaterDBG.initialize(); + UnittestObserver obs(2); + decimaterDBG.set_observer(&obs); + size_t removedVertices = 0; + removedVertices = decimaterDBG.decimate_to_faces(0, 0); + decimaterDBG.mesh().garbage_collection(); + + EXPECT_TRUE(obs.abort()) << "Observer did not abort the decimater!"; + EXPECT_EQ(obs.countedNotifies(), 2526u / 2u) << "Observer did not get the right amount of notifications!"; + + EXPECT_EQ(2526u, removedVertices) << "The number of remove vertices is not correct!"; + EXPECT_EQ(5000u, mesh_.n_vertices()) << "The number of vertices after decimation is not correct!"; + EXPECT_EQ(14994u, mesh_.n_edges()) << "The number of edges after decimation is not correct!"; + EXPECT_EQ(9996u, mesh_.n_faces()) << "The number of faces after decimation is not correct!"; +} }