diff --git a/BoundarySnappingT.cc b/BoundarySnappingT.cc new file mode 100644 index 0000000000000000000000000000000000000000..e4df0195958f891f91bf1c9eb1af11b1df28edfe --- /dev/null +++ b/BoundarySnappingT.cc @@ -0,0 +1,223 @@ + +/*===========================================================================*\ + * * + * OpenMesh * + * Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen * + * www.openmesh.org * + * * + *---------------------------------------------------------------------------* + * This file is part of OpenMesh. * + * * + * OpenMesh is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of * + * the License, or (at your option) any later version with the * + * following exceptions: * + * * + * If other files instantiate templates or use macros * + * or inline functions from this file, or you compile this file and * + * link it with other files to produce an executable, this file does * + * not by itself cause the resulting executable to be covered by the * + * GNU Lesser General Public License. This exception does not however * + * invalidate any other reasons why the executable file might be * + * covered by the GNU Lesser General Public License. * + * * + * OpenMesh is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU LesserGeneral Public * + * License along with OpenMesh. If not, * + * see . * + * * + \*===========================================================================*/ + +/*===========================================================================*\ + * * + * $Revision: 685 $ * + * $Date: 2012-09-19 18:15:39 +0200 (Mi, 19 Sep 2012) $ * + * * + \*===========================================================================*/ + +/** \file BoundarySnappingT.cc + */ + +//============================================================================= +// +// CLASS MeshFixing - IMPLEMENTATION +// +//============================================================================= + +#define BOUNDARYSNAPPING_CC + +//== INCLUDES ================================================================= + +#include "BoundarySnappingT.hh" + +//== NAMESPACE =============================================================== + + +//== IMPLEMENTATION ========================================================== + + +template +BoundarySnappingT::BoundarySnappingT(MeshT& _mesh ) : +mesh_(_mesh) +{ +} + +template +bool sort_less_pair_second(const std::pair &lhs,const std::pair &rhs) +{ + return lhs.second < rhs.second; +} + +template +void BoundarySnappingT::snap(double _epsilon) +{ + std::vector v_boundary; + + // collect all boundary vertices + for (typename MeshT::VertexIter v_iter = mesh_.vertices_begin(); v_iter != mesh_.vertices_end(); ++v_iter) + if (mesh_.is_boundary(v_iter) && mesh_.status(v_iter).selected()) + v_boundary.push_back(v_iter); + + //two maps + // vertexDistMap saves the vertex and his distances as pairs + // vertexVertexMap saves the vertices of a vertex in range and the distances + std::vector< std::pair > vertexDistMap; + std::map > > vertexVertexMap; + + // get all boundary vertices in range and save them into the maps + for (typename std::vector< typename MeshT::VertexHandle >::iterator oIter = v_boundary.begin(); oIter != v_boundary.end(); ++oIter) + { + typename MeshT::Point pos = mesh_.point(*oIter); + if (!mesh_.status(*oIter).deleted()) + { + std::vector< std::pair > verticesInRange; + + //collect all vertices in range + for (typename std::vector::iterator cIter = v_boundary.begin(); cIter != v_boundary.end(); ++cIter) + { + if ( !mesh_.status(*cIter).deleted() && cIter != oIter) + { + double dist = (pos - mesh_.point(*cIter)).length(); + + if ( dist <= _epsilon ) + verticesInRange.push_back(std::make_pair(*cIter,dist)); + } + } + + // sort them, so nearest vertex is on position 0 (if exist) + if (!verticesInRange.empty()) + { + std::sort( verticesInRange.begin(),verticesInRange.end(),&sort_less_pair_second ); + vertexDistMap.push_back(std::make_pair(*oIter,verticesInRange[0].second)); + vertexVertexMap[*oIter] = verticesInRange; + } + } + } + + bool finished = false; + while(!finished) + { + finished = true; + + double min = _epsilon; + typename MeshT::VertexHandle v_old;//will be replaced by v_new + typename MeshT::VertexHandle v_new; + typename std::vector >::iterator v_oldIter = vertexDistMap.end(); + typename std::vector >::iterator v_newIter; + + // find next min pair + for (typename std::vector >::iterator vd_iter = vertexDistMap.begin(); vd_iter != vertexDistMap.end(); ++vd_iter) + { + typename MeshT::VertexHandle v_1 = vd_iter->first; + if (v_1.is_valid() && !mesh_.status(v_1).deleted() && vertexVertexMap.find(v_1) != vertexVertexMap.end()) + { + typename MeshT::VertexHandle v_2; + std::vector >& verticesInRange = vertexVertexMap[v_1]; + + bool validPair = false; + + for (typename std::vector >::iterator iter = verticesInRange.begin(); iter != verticesInRange.end(); ++iter) + { + //check if v_2 shares a face with v_1 + //if so, it is not usable + v_2 = iter->first; + + for(typename MeshT::VertexFaceIter vf_iter = mesh_.vf_begin(v_1); vf_iter && v_2.is_valid(); ++vf_iter) + for (typename MeshT::FaceVertexIter fv_iter = mesh_.fv_begin(vf_iter.handle()); fv_iter && v_2.is_valid(); ++fv_iter) + if (fv_iter.handle() == v_2) + v_2 = typename MeshT::VertexHandle(); + + validPair = v_2.is_valid() && !mesh_.status(v_2).deleted() && mesh_.is_boundary(v_2); + + //if v_2 is valid, save it, or erase it if not, because v_2 will not be valid in the future + if (validPair && iter->second <= min) + { + //new min pair found + min = iter->second; + v_old = v_1; + v_new = v_2; + finished = false; + v_oldIter = vd_iter; + v_newIter = iter; + } + } + } + + } + // merge, if not finished (pair found) + if (!finished) + { + //remove the vertex since we will proceed with it + vertexVertexMap[v_old].erase(v_newIter); + //save all faces, because faces will be added/deleted + std::vector faces; + for (typename MeshT::VertexFaceIter vf_iter = mesh_.vf_begin(v_old); vf_iter; ++vf_iter) + if (!mesh_.status(vf_iter).deleted()) + faces.push_back(vf_iter); + + //replace v_old with v_new by creating new faces with v_new instead of v_old if possible + for (typename std::vector::iterator f_iter = faces.begin(); f_iter !=faces.end(); ++f_iter) + { + typename MeshT::FaceHandle fHandle = *f_iter; + if (!fHandle.is_valid() || mesh_.status(fHandle).deleted()) + continue; + + //get face vertices + std::vector f_vertices; + for(typename MeshT::FaceVertexIter fv_iter = mesh_.fv_begin(fHandle); fv_iter; ++fv_iter) + f_vertices.push_back( fv_iter.handle() ); + + mesh_.delete_face(fHandle,false); + + //try to add new face + std::vector newFace_vertices(f_vertices); + std::replace(newFace_vertices.begin(),newFace_vertices.end(),v_old,v_new); + typename MeshT::FaceHandle faceH = mesh_.add_face(newFace_vertices); + + if (!faceH.is_valid()) + { + //failed, try reverse direction + std::reverse(newFace_vertices.begin(),newFace_vertices.end()); + faceH = mesh_.add_face(newFace_vertices); + if (!faceH.is_valid()) + { + //failed, so add the old one + mesh_.add_face(f_vertices); + } + } + } + } + vertexDistMap.erase(v_oldIter); + + //todo: delete vertex before proceed. Now, they will be deleted at the end resulting worse snap + } + mesh_.delete_isolated_vertices(); + + mesh_.garbage_collection(); + +} diff --git a/BoundarySnappingT.hh b/BoundarySnappingT.hh new file mode 100644 index 0000000000000000000000000000000000000000..2fd9de1a10f2c6a0c97d4331ef9970c7bc65ec8d --- /dev/null +++ b/BoundarySnappingT.hh @@ -0,0 +1,94 @@ +/*===========================================================================*\ + * * + * OpenMesh * + * Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen * + * www.openmesh.org * + * * + *---------------------------------------------------------------------------* + * This file is part of OpenMesh. * + * * + * OpenMesh is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of * + * the License, or (at your option) any later version with the * + * following exceptions: * + * * + * If other files instantiate templates or use macros * + * or inline functions from this file, or you compile this file and * + * link it with other files to produce an executable, this file does * + * not by itself cause the resulting executable to be covered by the * + * GNU Lesser General Public License. This exception does not however * + * invalidate any other reasons why the executable file might be * + * covered by the GNU Lesser General Public License. * + * * + * OpenMesh is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU LesserGeneral Public * + * License along with OpenMesh. If not, * + * see . * + * * +\*===========================================================================*/ + +/*===========================================================================*\ + * * + * $Revision: 653 $ * + * $Date: 2012-08-22 10:49:55 +0200 (Mi, 22 Aug 2012) $ * + * * +\*===========================================================================*/ + + +#ifndef BOUNDARYSNAPPING_HH +#define BOUNDARYSNAPPING_HH + +//============================================================================= +// +// CLASS BoundarySnappingT +// +//============================================================================= + +//== INCLUDES ================================================================= + +//== NAMESPACE ================================================================ + +//== CLASS DEFINITION ========================================================= + +/** \brief Snaps selected vertices at boundaries + * + * Snaps selected boundary vertices together if they are closer than the given + * distance. No new vertices will be introduced on either edge, so they are just + * snapped to existing ones. + * + */ +template +class BoundarySnappingT { + +public: + + BoundarySnappingT(MeshT& _mesh ); + + /** \brief snaps boundary vertices + * + * snaps selected boundary vertices where the vertices + * distance is not greater than the given distance + * + * @param _epsilon max Distance between 2 boundary vertices + * + */ + void snap(double _epsilon); + +private: + + MeshT& mesh_; + +}; + +#if defined(INCLUDE_TEMPLATES) && !defined(BOUNDARYSNAPPING_CC) +#define BOUNDARYSNAPPING_TEMPLATES +#include "BoundarySnappingT.cc" +#endif + +#endif + diff --git a/GeneralMeshFixing.cc b/GeneralMeshFixing.cc index 16766d75a00d9b145641468434062b499f986289..cd085313cc879250134fca7c2d58551c348ff18a 100644 --- a/GeneralMeshFixing.cc +++ b/GeneralMeshFixing.cc @@ -47,7 +47,34 @@ #include "MeshRepairPlugin.hh" #include "MeshFixingT.hh" #include "NonManifoldVertexFixingT.hh" +#include "BoundarySnappingT.hh" +//----------------------------------------------------------------------------- + +void MeshRepairPlugin::snapBoundary(int _objectId, double _eps) +{ + TriMesh* triMesh = 0; + PolyMesh* polyMesh = 0; + + PluginFunctions::getMesh(_objectId, triMesh); + PluginFunctions::getMesh(_objectId, polyMesh); + if (triMesh) { + BoundarySnappingT snapper(*triMesh); + snapper.snap(_eps); + } + else if (polyMesh) { + BoundarySnappingT snapper(*polyMesh); + snapper.snap(_eps); + } else + { + emit log(LOGERR, tr("Unsupported Object Type.")); + return; + } + + emit updatedObject(_objectId, UPDATE_ALL); + emit createBackup(_objectId, "snapBoundary", UPDATE_ALL); + emit scriptInfo("snapBoundary(" + QString::number(_objectId) + ", " + QString::number(_eps) +")"); +} //----------------------------------------------------------------------------- diff --git a/MeshRepairControls.ui b/MeshRepairControls.ui index e232a303fb68b9254de4e70b04b6e91c812b98b6..dd82891beb430ae2fc9c93a3aaaed6a2c1790456 100644 --- a/MeshRepairControls.ui +++ b/MeshRepairControls.ui @@ -93,46 +93,6 @@ These vertices can usually be removed without destroying the features of the mes - - - - - - Snaps selected boundary vertices together. - - - Snap Boundary - - - - - - - max. Distance - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 6 - - - 0.500000000000000 - - - 0.100000000000000 - - - 0.500000000000000 - - - - - @@ -682,6 +642,46 @@ There is no automatic algorithm to fix these foldovers here. So you will have to General Operations + + + + + + <html><head/><body><p>Snaps selected boundary vertices together if they are closer than the given distance. No new vertices will be introduced on either edge, so they are just snapped to existing ones.</p></body></html> + + + Snap Boundary + + + + + + + max. Distance + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 6 + + + 0.500000000000000 + + + 0.100000000000000 + + + 0.500000000000000 + + + + + diff --git a/MeshRepairPlugin.cc b/MeshRepairPlugin.cc index 0aa8819d09bbc80597e371c4a9f372e22d7423d3..a5b4fe9a3ca4f8553c414faf68667a93fad45dd6 100644 --- a/MeshRepairPlugin.cc +++ b/MeshRepairPlugin.cc @@ -44,8 +44,6 @@ #include "MeshRepairPlugin.hh" -#include - #include #include "OpenFlipper/INIFile/INIFile.hh" @@ -389,29 +387,6 @@ void MeshRepairPlugin::pluginsInitialized() { // Scriptable functions //=========================================================================== -void MeshRepairPlugin::snapBoundary(int _objectId, double _eps) -{ - TriMesh* triMesh = 0; - PolyMesh* polyMesh = 0; - - PluginFunctions::getMesh(_objectId, triMesh); - PluginFunctions::getMesh(_objectId, polyMesh); - if (triMesh) - snapBoundary(triMesh,_eps); - else if (polyMesh) - snapBoundary(polyMesh,_eps); - else - { - emit log(LOGERR, tr("Unsupported Object Type.")); - return; - } - - emit updatedObject(_objectId, UPDATE_ALL); - emit createBackup(_objectId, "snapBoundary", UPDATE_ALL); - emit scriptInfo("snapBoundary(" + QString::number(_objectId) + ", " + QString::number(_eps) +")"); -} - - void MeshRepairPlugin::removeSelectedVal3Vertices(int _objectId) { diff --git a/MeshRepairPlugin.hh b/MeshRepairPlugin.hh index 82a15711f46b268335cd0238ef29319da193d0be..8c611f244795bf40e1d0a09ef3203dea33c44c63 100644 --- a/MeshRepairPlugin.hh +++ b/MeshRepairPlugin.hh @@ -194,13 +194,9 @@ public slots: /// Detect triangle with aspect of _aspect and select candidates void detectTriangleAspect(int _objectId, float _aspect); - - /// Flips the normals of all faces by changing the vertex order void flipOrientation(int _objectId); - - /// Selects all edges of an object which are shorter than the given length void selectEdgesShorterThan(int _objectId,double _length); @@ -210,7 +206,8 @@ public slots: /// Detect valence 3 vertices with faces that lie in the plane of their adjacent triangles void detectFlatValence3Vertices(int _objectId, double _angle); - void snapBoundary(int _objectId, double _eps); + + @@ -222,16 +219,28 @@ public slots: // Normal recomputations // ================================================== - /// Recomputes the face normals of an object + /** \brief Recomputes the face normals of an object + * + * @param _objectId Id of the object + */ void updateFaceNormals(int _objectId); - /// Recomputes the halfedge normals of an object + /** \brief Recomputes the halfedge normals of an object + * + * @param _objectId Id of the object + */ void updateHalfedgeNormals(int _objectId); - /// Recomputes the vertex normals of an object + /** \brief Recomputes the vertex normals of an object + * + * @param _objectId Id of the object + */ void updateVertexNormals(int _objectId); - /// Recomputes the face and vertex normals of an object + /** \brief Recomputes the face and vertex normals of an object + * + * @param _objectId Id of the object + */ void updateNormals(int _objectId); @@ -239,6 +248,9 @@ public slots: // General // ================================================== + + void snapBoundary(int _objectId, double _eps); + /** \brief remove non-manifold vertices by duplicating them * * @param _objectId Id of the mesh to fix @@ -283,20 +295,6 @@ private: template inline unsigned n_verticesPerFace(); - /** \brief snaps boundary vertices - * - * snaps selected boundary vertices where the vertices - * distance is not greater than a given distance - * @param _mesh target mesh - * @param _eps max Distance between 2 boundary vertices - * - */ - template - void snapBoundary(MeshT *_mesh, double _eps); - - template - static bool sort_less_pair_second(const std::pair &lhs,const std::pair &rhs); - public slots: QString version() { return QString("1.2"); diff --git a/MeshRepairPluginT.cc b/MeshRepairPluginT.cc index e388075fc69fd155b9c02e2acc0611ed7c233c53..e3d35dc8a68de02925111ef99364b567e09cde5a 100644 --- a/MeshRepairPluginT.cc +++ b/MeshRepairPluginT.cc @@ -95,161 +95,3 @@ void MeshRepairPlugin::flipOrientationSelected(MeshT *_mesh) //----------------------------------------------------------------------------- -template -bool MeshRepairPlugin::sort_less_pair_second(const std::pair &lhs,const std::pair &rhs) -{ - return lhs.second < rhs.second; -} - -//----------------------------------------------------------------------------- - -template -void MeshRepairPlugin::snapBoundary(MeshT *_mesh, double _eps) -{ - std::vector v_boundary; - - //collect all boundary vertices - for (typename MeshT::VertexIter v_iter = _mesh->vertices_begin(); v_iter != _mesh->vertices_end(); ++v_iter) - if (_mesh->is_boundary(v_iter) && _mesh->status(v_iter).selected()) - v_boundary.push_back(v_iter); - - //two maps - //vertexDistMap saves the vertex and his distanceas a pair - //vertexVertexMap saves the vertices of a vertex in range and the distances - std::vector< std::pair > vertexDistMap; - std::map > > vertexVertexMap; - - //get all boundary vertices in range and save them into the maps - for (typename std::vector< typename MeshT::VertexHandle >::iterator oIter = v_boundary.begin(); oIter != v_boundary.end(); ++oIter) - { - typename MeshT::Point pos = _mesh->point(*oIter); - if (!_mesh->status(*oIter).deleted()) - { - std::vector< std::pair > verticesInRange; - - //collect all vertices in range - for (typename std::vector::iterator cIter = v_boundary.begin(); cIter != v_boundary.end(); ++cIter) - { - if ( !_mesh->status(*cIter).deleted() && cIter != oIter) - { - double dist = (pos - _mesh->point(*cIter)).length(); - - if ( dist <= _eps ) - verticesInRange.push_back(std::make_pair(*cIter,dist)); - } - } - - //sort them, so nearest vertex is on position 0 (if exist) - if (!verticesInRange.empty()) - { - std::sort(verticesInRange.begin(),verticesInRange.end(),sort_less_pair_second); - vertexDistMap.push_back(std::make_pair(*oIter,verticesInRange[0].second)); - vertexVertexMap[*oIter] = verticesInRange; - } - } - } - - bool finished = false; - while(!finished) - { - finished = true; - - double min = _eps; - typename MeshT::VertexHandle v_old;//will be replaced by v_new - typename MeshT::VertexHandle v_new; - typename std::vector >::iterator v_oldIter = vertexDistMap.end(); - typename std::vector >::iterator v_newIter; - - //find next min pair - for (typename std::vector >::iterator vd_iter = vertexDistMap.begin(); vd_iter != vertexDistMap.end(); ++vd_iter) - { - typename MeshT::VertexHandle v_1 = vd_iter->first; - if (v_1.is_valid() && !_mesh->status(v_1).deleted() && vertexVertexMap.find(v_1) != vertexVertexMap.end()) - { - typename MeshT::VertexHandle v_2; - std::vector >& verticesInRange = vertexVertexMap[v_1]; - - bool validPair = false; - - for (typename std::vector >::iterator iter = verticesInRange.begin(); iter != verticesInRange.end(); ++iter) - { - //check if v_2 shares a face with v_1 - //if so, it is not usable - v_2 = iter->first; - - for(typename MeshT::VertexFaceIter vf_iter = _mesh->vf_begin(v_1); vf_iter && v_2.is_valid(); ++vf_iter) - for (typename MeshT::FaceVertexIter fv_iter = _mesh->fv_begin(vf_iter.handle()); fv_iter && v_2.is_valid(); ++fv_iter) - if (fv_iter.handle() == v_2) - v_2 = typename MeshT::VertexHandle(); - - validPair = v_2.is_valid() && !_mesh->status(v_2).deleted() && _mesh->is_boundary(v_2); - - //if v_2 is valid, save it, or erase it if not, because v_2 will not be valid in the future - if (validPair && iter->second <= min) - { - //new min pair found - min = iter->second; - v_old = v_1; - v_new = v_2; - finished = false; - v_oldIter = vd_iter; - v_newIter = iter; - } - } - } - - } - //merge, if not finished (pair found) - if (!finished) - { - //remove the vertex since we will proceed with it - vertexVertexMap[v_old].erase(v_newIter); - //save all faces, because faces will be added/deleted - std::vector faces; - for (typename MeshT::VertexFaceIter vf_iter = _mesh->vf_begin(v_old); vf_iter; ++vf_iter) - if (!_mesh->status(vf_iter).deleted()) - faces.push_back(vf_iter); - - //replace v_old with v_new by creating new faces with v_new instead of v_old if possible - for (typename std::vector::iterator f_iter = faces.begin(); f_iter !=faces.end(); ++f_iter) - { - typename MeshT::FaceHandle fHandle = *f_iter; - if (!fHandle.is_valid() || _mesh->status(fHandle).deleted()) - continue; - - //get face vertices - std::vector f_vertices; - for(typename MeshT::FaceVertexIter fv_iter = _mesh->fv_begin(fHandle); fv_iter; ++fv_iter) - f_vertices.push_back( fv_iter.handle() ); - - _mesh->delete_face(fHandle,false); - - //try to add new face - std::vector newFace_vertices(f_vertices); - std::replace(newFace_vertices.begin(),newFace_vertices.end(),v_old,v_new); - typename MeshT::FaceHandle faceH = _mesh->add_face(newFace_vertices); - - if (!faceH.is_valid()) - { - //failed, try reverse direction - std::reverse(newFace_vertices.begin(),newFace_vertices.end()); - faceH = _mesh->add_face(newFace_vertices); - if (!faceH.is_valid()) - { - //failed, so add the old one - _mesh->add_face(f_vertices); - } - } - } - } - vertexDistMap.erase(v_oldIter); - - //todo: delete vertex before proceed. Now, they will be deleted at the end resulting worse snap - } - _mesh->delete_isolated_vertices(); - - _mesh->garbage_collection(); - -} - -//----------------------------------------------------------------------------- diff --git a/NonManifoldVertexFixingT.hh b/NonManifoldVertexFixingT.hh index be7f0993226d208fdb729b8d9fd42efb089d5f75..22f7669ce6eac7e2a6d5fc4f2939765fd2ecebd6 100644 --- a/NonManifoldVertexFixingT.hh +++ b/NonManifoldVertexFixingT.hh @@ -45,7 +45,7 @@ //============================================================================= // -// CLASS MeshFixing +// CLASS NonManifoldVertexFixingT // //=============================================================================