Commit 44bed7b2 authored by Matthias Möller's avatar Matthias Möller

- add function: snaps boundary

- fix template types in flipOrientation

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@15423 383ad7c9-94d9-4d36-a494-682f7c89f535
parent 62697203
......@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>456</width>
<height>710</height>
<height>795</height>
</rect>
</property>
<property name="windowTitle">
......@@ -592,6 +592,55 @@ There is no automatic algorithm to fix these foldovers here. So you will have to
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Snap Boundary</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_13">
<item>
<widget class="QPushButton" name="snapBoundaryButton">
<property name="toolTip">
<string>Snaps selected boundary vertices together.</string>
</property>
<property name="text">
<string>Snap</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>max. Distance</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="snapBoundarySpinBox">
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>0.500000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.500000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
......
......@@ -75,6 +75,7 @@ initializePlugin()
// Vertex Selection/Removal
connect(tool_->valenceThreeButton, SIGNAL(clicked()), this, SLOT(slotDetectFlatValence3Vertices()) );
connect(tool_->repairRemoveVButton, SIGNAL(clicked()), this, SLOT(slotRemoveSelectedVal3Vertices()) );
connect(tool_->snapBoundaryButton, SIGNAL(clicked()), this, SLOT(slotSnapBoundary()) );
// Edge Selection/Repairing
connect(tool_->detectEShorterButton, SIGNAL(clicked()), this, SLOT(slotDetectEdgesShorter()) );
......@@ -244,6 +245,16 @@ void MeshRepairPlugin::slotDetectFlatValence3Vertices() {
emit updateView();
}
//-----------------------------------------------------------------------------
void MeshRepairPlugin::slotSnapBoundary()
{
double eps = tool_->snapBoundarySpinBox->value();
for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) ); o_it != PluginFunctions::objectsEnd(); ++o_it)
snapBoundary(o_it->id(), eps);
emit updateView();
}
//-----------------------------------------------------------------------------
......@@ -303,6 +314,9 @@ void MeshRepairPlugin::pluginsInitialized() {
emit setSlotDescription("detectSkinnyTriangleByAngle(int,double,bool)",tr("Select or remove skinny triangles. Whether a triangle is skinny is determined by a minimum angle threshold."),
QString(tr("objectId,angle,remove")).split(","),
QString(tr("ID of an object;Minimum angle threshold")).split(";"));
emit setSlotDescription("snapBoundary(int,double)",tr("Snaps selected and boundary vertices if the distance is less than the given max. distance."),
QString(tr("objectId,epsilon")).split(","),
QString(tr("ID of an object;Max Distance")).split(";"));
}
......@@ -312,6 +326,28 @@ 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) {
unsigned int count = 0;
......
......@@ -162,6 +162,9 @@ private slots:
/// Button slot
void slotDetectFlatValence3Vertices();
/// Button slot
void slotSnapBoundary();
//Scripting functions:
public slots:
......@@ -204,6 +207,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);
private:
/** \brief select edges based on length
*
......@@ -215,12 +220,34 @@ private:
void selectionEdgeLength(int _objectId, double _length, bool _larger);
/** \brief flip orientation of selected faces.
*
* flips the orientation of selected faces.
* for correct topology, some vertices are added
* @param _mesh target mesh
*
*/
template<typename MeshT>
void flipOrientationSelected(MeshT *_mesh);
template<typename MeshT>
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<typename MeshT>
void snapBoundary(MeshT *_mesh, double _eps);
template<typename MeshT>
static bool sort_less_pair_second(const std::pair<typename MeshT::VertexHandle,double> &lhs,const std::pair<typename MeshT::VertexHandle,double> &rhs);
public slots:
QString version() {
return QString("1.2");
......
......@@ -22,7 +22,7 @@ void MeshRepairPlugin::flipOrientationSelected(MeshT *_mesh)
{
typename MeshT::FaceIter f_it, f_end = _mesh->faces_end();
std::vector< TriMesh::VertexHandle > handles;
std::vector< typename MeshT::VertexHandle > handles;
std::vector< uint > valence;
handles.reserve( _mesh->n_faces() * n_verticesPerFace<MeshT>() );
......@@ -34,7 +34,7 @@ void MeshRepairPlugin::flipOrientationSelected(MeshT *_mesh)
if (_mesh->status(f_it).selected())
{
// Collect vertex handles
TriMesh::FaceVertexIter fv_it = _mesh->fv_iter(f_it);
typename MeshT::FaceVertexIter fv_it = _mesh->fv_iter(f_it);
valence.push_back( _mesh->valence(f_it) );
......@@ -55,14 +55,14 @@ void MeshRepairPlugin::flipOrientationSelected(MeshT *_mesh)
uint pos = 0;
for (uint i=0; i < valence.size(); i++) {
std::vector< TriMesh::VertexHandle > faceVertices;
std::vector< typename MeshT::VertexHandle > faceVertices;
pos += valence[i];
// add valence vertices in the inverse order
for (uint j = 1 ; j <= valence[i] ; ++j )
{
TriMesh::VertexHandle handle = handles[pos - j];
typename MeshT::VertexHandle handle = handles[pos - j];
//if vertex is not isolated, it has a face and we have to add a new vertex to prevent holes
//between the old flipped and not flipped faces
......@@ -77,3 +77,165 @@ void MeshRepairPlugin::flipOrientationSelected(MeshT *_mesh)
_mesh->update_normals();
}
template<typename MeshT>
bool MeshRepairPlugin::sort_less_pair_second(const std::pair<typename MeshT::VertexHandle,double> &lhs,const std::pair<typename MeshT::VertexHandle,double> &rhs)
{
return lhs.second < rhs.second;
}
template<typename MeshT>
void MeshRepairPlugin::snapBoundary(MeshT *_mesh, double _eps)
{
std::vector<typename MeshT::VertexHandle> 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<typename MeshT::VertexHandle,double> > vertexDistMap;
std::map<typename MeshT::VertexHandle,std::vector<std::pair<typename MeshT::VertexHandle,double> > > 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<typename MeshT::VertexHandle,double> > verticesInRange;
//collect all vertices in range
for (typename std::vector<typename MeshT::VertexHandle>::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<MeshT>);
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<std::pair<typename MeshT::VertexHandle,double> >::iterator used_vertex = vertexDistMap.end();
//find next min pair
for (typename std::vector<std::pair<typename MeshT::VertexHandle,double> >::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() && _mesh->is_boundary(v_1))
{
typename MeshT::VertexHandle v_2;
std::vector<std::pair<typename MeshT::VertexHandle,double> >& verticesInRange = vertexVertexMap[v_1];
bool validPair = false;
unsigned i = 0;
while(i < verticesInRange.size() && !validPair)
{
//check if v_2 shares a face with v_1
//if so, it is not usable
v_2 = verticesInRange[i].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)
{
if(verticesInRange[i].second <= min)
{
//new min pair found
min = verticesInRange[i].second;
v_old = v_1;
v_new = v_2;
finished = false;
used_vertex = vd_iter;
}
++i;
}
else
verticesInRange.erase(verticesInRange.begin()+i);
}
}
}
//erase the entry, because the vertex will be erased when it is merged
if (used_vertex != vertexDistMap.end())
vertexDistMap.erase(used_vertex);
//merge, if not finished (pair found)
if (!finished)
{
//save all faces, because faces will be added/deleted
std::vector<typename MeshT::FaceHandle> 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<typename MeshT::FaceHandle>::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<typename MeshT::VertexHandle> 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<typename MeshT::VertexHandle> 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);
}
}
//erase the vertex
if (_mesh->is_isolated(v_old))
_mesh->delete_vertex(v_old);
}
}
_mesh->garbage_collection();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment