Commit 48289493 authored by Isaak Lim's avatar Isaak Lim
Browse files

- added the set_error_tolerance_factor function to ModBaseT and implemented it...

- added the set_error_tolerance_factor function to ModBaseT and implemented it in inherited classes as necessary
- added the set_error_tolerance_factor function to BaseDecimaterT, which calls set_error_tolerance_factor for all loaded Mods
- implemented a decimate_constraints_only function for the McDecimater (and adjusted the MixedDecimater accordingly)
- implemented stop criterions for the McDecimater
- added some OpenMP loops for the sample generation to the McDecimater

git-svn-id: http://www.openmesh.org/svnrepo/OpenMesh/trunk@685 fdac6126-5c0c-442c-9429-916003d36597
parent 33b72fb6
...@@ -207,6 +207,19 @@ void BaseDecimaterT<Mesh>::preprocess_collapse(CollapseInfo& _ci) { ...@@ -207,6 +207,19 @@ void BaseDecimaterT<Mesh>::preprocess_collapse(CollapseInfo& _ci) {
cmodule_->preprocess_collapse(_ci); cmodule_->preprocess_collapse(_ci);
} }
//-----------------------------------------------------------------------------
template<class Mesh>
void BaseDecimaterT<Mesh>::set_error_tolerance_factor(double _factor) {
if (_factor >= 0.0 && _factor <= 1.0) {
typename ModuleList::iterator m_it, m_end = bmodules_.end();
for (m_it = bmodules_.begin(); m_it != m_end; ++m_it)
(*m_it)->set_error_tolerance_factor(_factor);
cmodule_->set_error_tolerance_factor(_factor);
}
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
......
...@@ -199,6 +199,16 @@ protected: //---------------------------------------------------- private method ...@@ -199,6 +199,16 @@ protected: //---------------------------------------------------- private method
/// Post-process a collapse /// Post-process a collapse
void postprocess_collapse(CollapseInfo& _ci); void postprocess_collapse(CollapseInfo& _ci);
/**
* This provides a function that allows the setting of a percentage
* of the original contraint of the modules
*
* Note that some modules might re-initialize in their
* set_error_tolerance_factor function as necessary
* @param factor_ has to be in the closed interval between 0.0 and 1.0
*/
void set_error_tolerance_factor(double _factor);
private: //------------------------------------------------------- private data private: //------------------------------------------------------- private data
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
* Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen * * Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen *
* www.openmesh.org * * www.openmesh.org *
* * * *
*---------------------------------------------------------------------------* *---------------------------------------------------------------------------*
* This file is part of OpenMesh. * * This file is part of OpenMesh. *
* * * *
* OpenMesh is free software: you can redistribute it and/or modify * * OpenMesh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as * * it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of * * published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version with the * * the License, or (at your option) any later version with the *
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
\*===========================================================================*/ \*===========================================================================*/
/*===========================================================================*\ /*===========================================================================*\
* * * *
* $Revision$ * * $Revision$ *
* $Date$ * * $Date$ *
* * * *
...@@ -214,6 +214,7 @@ size_t DecimaterT<Mesh>::decimate(size_t _n_collapses) { ...@@ -214,6 +214,7 @@ size_t DecimaterT<Mesh>::decimate(size_t _n_collapses) {
// delete heap // delete heap
heap_.reset(); heap_.reset();
// DON'T do garbage collection here! It's up to the application. // DON'T do garbage collection here! It's up to the application.
return n_collapses; return n_collapses;
} }
...@@ -307,6 +308,7 @@ size_t DecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) { ...@@ -307,6 +308,7 @@ size_t DecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) {
// delete heap // delete heap
heap_.reset(); heap_.reset();
// DON'T do garbage collection here! It's up to the application. // DON'T do garbage collection here! It's up to the application.
return n_collapses; return n_collapses;
} }
......
...@@ -59,6 +59,9 @@ ...@@ -59,6 +59,9 @@
#else #else
# include <cfloat> # include <cfloat>
#endif #endif
#ifdef USE_OPENMP
#include <omp.h>
#endif
//== NAMESPACE =============================================================== //== NAMESPACE ===============================================================
...@@ -103,17 +106,33 @@ size_t McDecimaterT<Mesh>::decimate(size_t _n_collapses) { ...@@ -103,17 +106,33 @@ size_t McDecimaterT<Mesh>::decimate(size_t _n_collapses) {
unsigned int n_collapses(0); unsigned int n_collapses(0);
bool collapsesUnchanged = false;
// old n_collapses in order to check for convergence
unsigned int oldCollapses = 0;
// number of iterations where no new collapses where
// performed in a row
unsigned int noCollapses = 0;
while ( n_collapses < _n_collapses) { while ( n_collapses < _n_collapses) {
if (noCollapses > 20) {
omlog() << "[McDecimater] : no collapses performed in over 20 iterations in a row\n";
break;
}
// Optimal id and value will be collected during the random sampling // Optimal id and value will be collected during the random sampling
typename Mesh::HalfedgeHandle bestHandle(-1); typename Mesh::HalfedgeHandle bestHandle(-1);
typename Mesh::HalfedgeHandle tmpHandle(-1);
double bestEnergy = FLT_MAX; double bestEnergy = FLT_MAX;
double energy = FLT_MAX;
// Generate random samples for collapses // Generate random samples for collapses
#ifdef USE_OPENMP
#pragma omp parallel for private(energy,tmpHandle) shared(bestEnergy,bestHandle)
#endif
for ( int i = 0; i < (int)randomSamples_; ++i) { for ( int i = 0; i < (int)randomSamples_; ++i) {
// Random halfedge handle // Random halfedge handle
typename Mesh::HalfedgeHandle tmpHandle = typename Mesh::HalfedgeHandle((static_cast<double>(rand()) / RAND_MAX) * (mesh_.n_halfedges()-1) ); tmpHandle = typename Mesh::HalfedgeHandle((static_cast<double>(rand()) / RAND_MAX) * (mesh_.n_halfedges()-1) );
// if it is not deleted, we analyse it // if it is not deleted, we analyse it
if ( ! mesh_.status(tmpHandle).deleted() ) { if ( ! mesh_.status(tmpHandle).deleted() ) {
...@@ -122,13 +141,22 @@ size_t McDecimaterT<Mesh>::decimate(size_t _n_collapses) { ...@@ -122,13 +141,22 @@ size_t McDecimaterT<Mesh>::decimate(size_t _n_collapses) {
// Check if legal we analyze the priority of this collapse operation // Check if legal we analyze the priority of this collapse operation
if (this->is_collapse_legal(ci)) { if (this->is_collapse_legal(ci)) {
double energy = this->collapse_priority(ci); #ifdef USE_OPENMP
#pragma omp critical(energyUpdate)
// Check if the current samples energy is better than any energy before {
if ( energy < bestEnergy ) { #endif
bestEnergy = energy; energy = this->collapse_priority(ci);
bestHandle = tmpHandle;
if (energy != ModBaseT<Mesh>::ILLEGAL_COLLAPSE) {
// Check if the current samples energy is better than any energy before
if ( energy < bestEnergy ) {
bestEnergy = energy;
bestHandle = tmpHandle;
}
} }
#ifdef USE_OPENMP
}
#endif
} else { } else {
continue; continue;
} }
...@@ -153,6 +181,11 @@ size_t McDecimaterT<Mesh>::decimate(size_t _n_collapses) { ...@@ -153,6 +181,11 @@ size_t McDecimaterT<Mesh>::decimate(size_t _n_collapses) {
mesh_.collapse(bestHandle); mesh_.collapse(bestHandle);
++n_collapses; ++n_collapses;
// store current collapses state
oldCollapses = n_collapses;
noCollapses = 0;
collapsesUnchanged = false;
// update triangle normals // update triangle normals
typename Mesh::VertexFaceIter vf_it = mesh_.vf_iter(ci.v1); typename Mesh::VertexFaceIter vf_it = mesh_.vf_iter(ci.v1);
for (; vf_it; ++vf_it) for (; vf_it; ++vf_it)
...@@ -162,6 +195,15 @@ size_t McDecimaterT<Mesh>::decimate(size_t _n_collapses) { ...@@ -162,6 +195,15 @@ size_t McDecimaterT<Mesh>::decimate(size_t _n_collapses) {
// post-process collapse // post-process collapse
this->postprocess_collapse(ci); this->postprocess_collapse(ci);
} else {
if (oldCollapses == n_collapses) {
if (collapsesUnchanged == false) {
noCollapses = 1;
collapsesUnchanged = true;
} else {
noCollapses++;
}
}
} }
} }
...@@ -177,34 +219,43 @@ size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) { ...@@ -177,34 +219,43 @@ size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) {
if (!this->is_initialized()) if (!this->is_initialized())
return 0; return 0;
// check if no vertex or face contraints were set
if ( (_nv == 0) && (_nf == 1) )
return decimate_constraints_only(1.0);
unsigned int nv = mesh_.n_vertices(); unsigned int nv = mesh_.n_vertices();
unsigned int nf = mesh_.n_faces(); unsigned int nf = mesh_.n_faces();
unsigned int n_collapses(0); unsigned int n_collapses(0);
// check if no vertex or face contraints were set bool collapsesUnchanged = false;
bool contraintsOnly = (_nv == 0) && (_nf == 1); // old n_collapses in order to check for convergence
unsigned int oldCollapses = 0;
// check if no legal collapses were found three times in a row // number of iterations where no new collapses where
// for the sampled halfedges // performed in a row
bool foundNoLegalCollapsesThrice = false; unsigned int noCollapses = 0;
// store the last two amount of legal collapses found while ( (_nv < nv) && (_nf < nf) ) {
int lastLegalCollapses = -1; if (noCollapses > 20) {
int beforeLastLegalCollapses = -1; omlog() << "[McDecimater] : no collapses performed in over 20 iterations in a row\n";
break;
while ( !foundNoLegalCollapsesThrice && (_nv < nv) && (_nf < nf) ) { }
// Optimal id and value will be collected during the random sampling // Optimal id and value will be collected during the random sampling
typename Mesh::HalfedgeHandle bestHandle(-1); typename Mesh::HalfedgeHandle bestHandle(-1);
typename Mesh::HalfedgeHandle tmpHandle(-1);
double bestEnergy = FLT_MAX; double bestEnergy = FLT_MAX;
double energy = FLT_MAX;
// Generate random samples for collapses // Generate random samples for collapses
unsigned int legalCollapses = 0; #ifdef USE_OPENMP
#pragma omp parallel for private(energy,tmpHandle) shared(bestEnergy,bestHandle)
#endif
for ( int i = 0; i < (int)randomSamples_; ++i) { for ( int i = 0; i < (int)randomSamples_; ++i) {
// Random halfedge handle // Random halfedge handle
typename Mesh::HalfedgeHandle tmpHandle = typename Mesh::HalfedgeHandle((static_cast<double>(rand()) / RAND_MAX) * (mesh_.n_halfedges()-1) ); tmpHandle = typename Mesh::HalfedgeHandle((static_cast<double>(rand()) / RAND_MAX) * (mesh_.n_halfedges()-1) );
// if it is not deleted, we analyse it // if it is not deleted, we analyse it
if ( ! mesh_.status(tmpHandle).deleted() ) { if ( ! mesh_.status(tmpHandle).deleted() ) {
...@@ -213,14 +264,22 @@ size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) { ...@@ -213,14 +264,22 @@ size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) {
// Check if legal we analyze the priority of this collapse operation // Check if legal we analyze the priority of this collapse operation
if (this->is_collapse_legal(ci)) { if (this->is_collapse_legal(ci)) {
++legalCollapses; #ifdef USE_OPENMP
double energy = this->collapse_priority(ci); #pragma omp critical(energyUpdate)
{
// Check if the current samples energy is better than any energy before #endif
if ( energy < bestEnergy ) { energy = this->collapse_priority(ci);
bestEnergy = energy;
bestHandle = tmpHandle; if (energy != ModBaseT<Mesh>::ILLEGAL_COLLAPSE) {
// Check if the current samples energy is better than any energy before
if ( energy < bestEnergy ) {
bestEnergy = energy;
bestHandle = tmpHandle;
}
}
#ifdef USE_OPENMP
} }
#endif
} else { } else {
continue; continue;
} }
...@@ -228,13 +287,149 @@ size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) { ...@@ -228,13 +287,149 @@ size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) {
} }
if (contraintsOnly) { // Found the best energy?
// check if no legal collapses were found three times in a row if ( bestEnergy != FLT_MAX ) {
foundNoLegalCollapsesThrice = (beforeLastLegalCollapses == 0) && (lastLegalCollapses == 0) && (legalCollapses == 0);
// setup collapse info
CollapseInfo ci(mesh_, bestHandle);
// check topological correctness AGAIN !
if (!this->is_collapse_legal(ci))
continue;
// adjust complexity in advance (need boundary status)
// One vertex is killed by the collapse
--nv;
// If we are at a boundary, one face is lost,
// otherwise two
if (mesh_.is_boundary(ci.v0v1) || mesh_.is_boundary(ci.v1v0))
--nf;
else
nf -= 2;
// pre-processing
this->preprocess_collapse(ci);
// perform collapse
mesh_.collapse(bestHandle);
++n_collapses;
// store current collapses state
oldCollapses = n_collapses;
noCollapses = 0;
collapsesUnchanged = false;
// update triangle normals
typename Mesh::VertexFaceIter vf_it = mesh_.vf_iter(ci.v1);
for (; vf_it; ++vf_it)
if (!mesh_.status(vf_it).deleted())
mesh_.set_normal(vf_it, mesh_.calc_face_normal(vf_it.handle()));
// post-process collapse
this->postprocess_collapse(ci);
} else {
if (oldCollapses == n_collapses) {
if (collapsesUnchanged == false) {
noCollapses = 1;
collapsesUnchanged = true;
} else {
noCollapses++;
}
}
}
}
// DON'T do garbage collection here! It's up to the application.
return n_collapses;
}
//-----------------------------------------------------------------------------
template<class Mesh>
size_t McDecimaterT<Mesh>::decimate_constraints_only(float _factor) {
if (!this->is_initialized())
return 0;
if (_factor < 1.0)
this->set_error_tolerance_factor(_factor);
unsigned int n_collapses(0);
unsigned int nv = mesh_.n_vertices();
unsigned int nf = mesh_.n_faces();
bool lastCollapseIllegal = false;
// number of illegal collapses that occured in a row
unsigned int illegalCollapses = 0;
bool collapsesUnchanged = false;
// old n_collapses in order to check for convergence
unsigned int oldCollapses = 0;
// number of iterations where no new collapses where
// performed in a row
unsigned int noCollapses = 0;
while ( (noCollapses <= 20) && (illegalCollapses <= 10) && (nv > 0) && (nf > 1) ) {
// Optimal id and value will be collected during the random sampling
typename Mesh::HalfedgeHandle bestHandle(-1);
typename Mesh::HalfedgeHandle tmpHandle(-1);
double bestEnergy = FLT_MAX;
double energy = FLT_MAX;
// Generate random samples for collapses
#ifdef USE_OPENMP
#pragma omp parallel for private(energy,tmpHandle) shared(bestEnergy,bestHandle)
#endif
for ( int i = 0; i < (int)randomSamples_; ++i) {
// Random halfedge handle
tmpHandle = typename Mesh::HalfedgeHandle((static_cast<double>(rand()) / RAND_MAX) * (mesh_.n_halfedges()-1) );
// if it is not deleted, we analyse it
if ( ! mesh_.status(tmpHandle).deleted() ) {
CollapseInfo ci(mesh_, tmpHandle);
// Check if legal we analyze the priority of this collapse operation
if (this->is_collapse_legal(ci)) {
#ifdef USE_OPENMP
#pragma omp critical(energyUpdate)
{
#endif
energy = this->collapse_priority(ci);
if (energy == ModBaseT<Mesh>::ILLEGAL_COLLAPSE){
if (lastCollapseIllegal) {
illegalCollapses++;
} else {
illegalCollapses = 1;
lastCollapseIllegal = true;
}
} else {
illegalCollapses = 0;
lastCollapseIllegal = false;
// Check if the current samples energy is better than any energy before
if ( energy < bestEnergy ) {
bestEnergy = energy;
bestHandle = tmpHandle;
}
}
#ifdef USE_OPENMP
}
#endif
} else {
continue;
}
}
// store amount of last legal collapses found
beforeLastLegalCollapses = lastLegalCollapses;
lastLegalCollapses = legalCollapses;
} }
// Found the best energy? // Found the best energy?
...@@ -259,7 +454,6 @@ size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) { ...@@ -259,7 +454,6 @@ size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) {
else else
nf -= 2; nf -= 2;
// pre-processing // pre-processing
this->preprocess_collapse(ci); this->preprocess_collapse(ci);
...@@ -267,6 +461,12 @@ size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) { ...@@ -267,6 +461,12 @@ size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) {
mesh_.collapse(bestHandle); mesh_.collapse(bestHandle);
++n_collapses; ++n_collapses;
// store current collapses state
oldCollapses = n_collapses;
noCollapses = 0;
collapsesUnchanged = false;
// update triangle normals // update triangle normals
typename Mesh::VertexFaceIter vf_it = mesh_.vf_iter(ci.v1); typename Mesh::VertexFaceIter vf_it = mesh_.vf_iter(ci.v1);
for (; vf_it; ++vf_it) for (; vf_it; ++vf_it)
...@@ -276,12 +476,25 @@ size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) { ...@@ -276,12 +476,25 @@ size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) {
// post-process collapse // post-process collapse
this->postprocess_collapse(ci); this->postprocess_collapse(ci);
} else {
if (oldCollapses == n_collapses) {
if (collapsesUnchanged == false) {
noCollapses = 1;
collapsesUnchanged = true;
} else {
noCollapses++;
}
}
} }
} }
if (_factor < 1.0)
this->set_error_tolerance_factor(1.0);
// DON'T do garbage collection here! It's up to the application. // DON'T do garbage collection here! It's up to the application.
return n_collapses; return n_collapses;
} }
//============================================================================= //=============================================================================
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
* Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen * * Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen *
* www.openmesh.org * * www.openmesh.org *
* * * *
*---------------------------------------------------------------------------* *---------------------------------------------------------------------------*
* This file is part of OpenMesh. * * This file is part of OpenMesh. *
* * * *
* OpenMesh is free software: you can redistribute it and/or modify * * OpenMesh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as * * it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of * * published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version with the * * the License, or (at your option) any later version with the *
...@@ -30,10 +30,10 @@ ...@@ -30,10 +30,10 @@
* License along with OpenMesh. If not, * * License along with OpenMesh. If not, *
* see <http://www.gnu.org/licenses/>. * * see <http://www.gnu.org/licenses/>. *
* * * *
\*===========================================================================*/ \*===========================================================================*/
/*===========================================================================*\ /*===========================================================================*\
* * * *
* $Revision: 448 $ * * $Revision: 448 $ *
* $Date: 2011-11-04 13:59:37 +0100 (Fr, 04 Nov 2011) $ * * $Date: 2011-11-04 13:59:37 +0100 (Fr, 04 Nov 2011) $ *
* * * *
...@@ -111,6 +111,12 @@ public: ...@@ -111,6 +111,12 @@ public:
*/