openmesh - impl - Remove Duplicated Vertices
關於openmesh元素刪除實現的介紹參見:openmesh - src - trimesh delete and add elements - grassofsky - 部落格園 (cnblogs.com)
重複點刪除的主要步驟如下:
- 找到所有的重複頂點,並設定每組重複頂點中需要保留的頂點;
- 記錄這些重複頂點對應的三角形;(因為下一步在頂點刪除的時候,會將頂點周圍關聯的三角形都刪除,並將頂點的狀態設定為deleted);
- 刪除所有的重複的頂點;(此時會刪除關聯的三角形和半邊);
- 可能出現孤立點,刪除孤立點;(此步驟可選);
- 回收,邊和麵的資源;(此步驟為了後續新增關聯面做準備);
- 遍歷所有的重複頂點關聯的三角形,如果這個三角形中僅含有一個重複點,那麼新增該三角形,並將重複點替換為對應的保留的點;
- 恢復重新新增的保留的頂點的刪除狀態;
- 資源回收;
具體實現程式碼如下:
#include <iostream>
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
#include <OpenMesh/Tools/Utils/Timer.hh>
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
using namespace OpenMesh;
struct MyTraits : public OpenMesh::DefaultTraits
{
#if 1
typedef OpenMesh::Vec3f Point;
typedef OpenMesh::Vec3f Normal;
#else
typedef OpenMesh::Vec3d Point;
typedef OpenMesh::Vec3d Normal;
#endif
};
typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> TriMesh;
//-----------------------------------------------------------------------------
bool HasDuplicatedVertex(const TriMesh& mesh)
{
std::map<TriMesh::Point, int> mapPoints;
for (auto iter = mesh.vertices_begin(); iter != mesh.vertices_end(); ++iter)
{
auto iterPt = mapPoints.find(mesh.point(*iter));
if (iterPt == mapPoints.end())
{
mapPoints.insert_or_assign(mesh.point(*iter), 1);
}
else
{
return true;
}
}
return false;
}
int RemoveDuplicatedVertex(TriMesh& mesh)
{
// Find all duplicated vertices
int iOriginalNumVertices = static_cast<int>(mesh.n_vertices());
std::map<TriMesh::Point, TriMesh::VertexHandle> mapPoints;
std::map<TriMesh::VertexHandle, std::set<TriMesh::VertexHandle>> mapDuplicatedVertices;
for (auto iter = mesh.vertices_begin(); iter != mesh.vertices_end(); ++iter)
{
auto& pt = mesh.point(*iter);
if (mapPoints.find(pt) != mapPoints.end())
{
auto& vh = mapPoints[pt];
auto iterDuplicatedVertices = mapDuplicatedVertices.find(vh);
if (iterDuplicatedVertices == mapDuplicatedVertices.end())
{
mapDuplicatedVertices.insert(std::make_pair(vh, std::set<TriMesh::VertexHandle>{ vh, *iter }));
}
else
{
iterDuplicatedVertices->second.insert(*iter);
}
}
else
{
mapPoints.insert(std::make_pair(pt, *iter));
}
}
// Record the duplicated vertices related faces
std::map<TriMesh::VertexHandle, std::set<std::array<TriMesh::VertexHandle, 3>>> mapVertexFaces;
for (auto iter = mapDuplicatedVertices.begin(); iter != mapDuplicatedVertices.end(); ++iter)
{
auto pair = mapVertexFaces.insert(std::make_pair(iter->first, std::set<std::array<TriMesh::VertexHandle, 3>>{}));
auto& vecFaces = pair.first->second;
for (auto v_it = iter->second.begin(); v_it != iter->second.end(); ++v_it)
{
for (auto vf_it = mesh.vf_iter(*v_it); vf_it.is_valid(); ++vf_it)
{
std::array<TriMesh::VertexHandle, 3> face;
int i = 0;
auto fv_it = mesh.fv_begin(*vf_it);
face[i++] = *(fv_it++);
face[i++] = *(fv_it++);
face[i++] = *(fv_it++);
vecFaces.insert(face);
}
}
}
if (!mesh.has_vertex_status()) mesh.request_vertex_status();
if (!mesh.has_face_status()) mesh.request_face_status();
if (!mesh.has_edge_status()) mesh.request_edge_status();
// remove all duplicated vertices
for (auto iter = mapDuplicatedVertices.begin(); iter != mapDuplicatedVertices.end(); ++iter)
{
for (auto v_it = iter->second.begin(); v_it != iter->second.end(); ++v_it)
{
mesh.delete_vertex(*v_it);
}
}
mesh.delete_isolated_vertices();
// garbage_collection edge and face
mesh.garbage_collection(false, true, true);
// add not degenereated faces
std::set<TriMesh::VertexHandle> setRemainVertices;
for (auto iter = mapVertexFaces.begin(); iter != mapVertexFaces.end(); ++iter)
{
for (auto f_it = iter->second.begin(); f_it != iter->second.end(); ++f_it)
{
std::array<TriMesh::VertexHandle, 3> face = *f_it;
unsigned short result = 0;
for (auto iter = mapDuplicatedVertices.begin(); iter != mapDuplicatedVertices.end(); ++iter)
{
// bit pos records if the vertex is duplicated vertex or not
result = 0;
result |= (iter->second.count(face[0]) == 0 ? 0 : 1);
result |= (iter->second.count(face[1]) == 0 ? 0 : 2);
result |= (iter->second.count(face[2]) == 0 ? 0 : 4);
if (result == 1 || result == 2 || result == 4)
{
// replace the duplicated vertex as remaining vertex
face[result / 2] = iter->first;
mesh.add_face(face[0], face[1], face[2]);
setRemainVertices.insert(face[0]);
setRemainVertices.insert(face[1]);
setRemainVertices.insert(face[2]);
break;
}
}
}
}
// recover status of remain vertex
for (auto v_it = setRemainVertices.begin(); v_it != setRemainVertices.end(); ++v_it)
{
mesh.status(*v_it).set_deleted(false);
}
// garbage_collection vertex
mesh.garbage_collection();
if (!mesh.has_vertex_status()) mesh.release_vertex_status();
if (!mesh.has_face_status()) mesh.release_vertex_status();
if (!mesh.has_edge_status()) mesh.release_vertex_status();
return iOriginalNumVertices - static_cast<int>(mesh.n_vertices());
}
//-----------------------------------------------------------------------------
TEST_CASE("testing delete duplicated vertex")
{
TriMesh mesh;
// Add some vertices
TriMesh::VertexHandle vhandle[7];
vhandle[0] = mesh.add_vertex(TriMesh::Point(0, 0, 0));
vhandle[1] = mesh.add_vertex(TriMesh::Point(0, 2, 0));
vhandle[2] = mesh.add_vertex(TriMesh::Point(2, 2, 0));
vhandle[3] = mesh.add_vertex(TriMesh::Point(2, 0, 0));
vhandle[4] = mesh.add_vertex(TriMesh::Point(1, 1, 0));
vhandle[5] = mesh.add_vertex(TriMesh::Point(1, 1, 0));
vhandle[6] = mesh.add_vertex(TriMesh::Point(1, 1, 0));
// Add faces
mesh.add_face(vhandle[0], vhandle[1], vhandle[4]);
mesh.add_face(vhandle[0], vhandle[4], vhandle[6]);
mesh.add_face(vhandle[0], vhandle[6], vhandle[3]);
mesh.add_face(vhandle[1], vhandle[5], vhandle[4]);
mesh.add_face(vhandle[1], vhandle[2], vhandle[5]);
mesh.add_face(vhandle[2], vhandle[6], vhandle[5]);
mesh.add_face(vhandle[2], vhandle[3], vhandle[6]);
mesh.add_face(vhandle[4], vhandle[5], vhandle[6]);
auto iNDeleted = RemoveDuplicatedVertex(mesh);
CHECK(iNDeleted == 2);
CHECK(mesh.n_faces() == 4);
CHECK(mesh.n_vertices() == 5);
CHECK_FALSE(HasDuplicatedVertex(mesh));
}