openmesh - impl - Remove Duplicated Vertices
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));
}
openmesh - impl - Remove Duplicated Vertices的更多相关文章
- leetcode 283 Move Zeros; 27 Remove Elements; 26 Remove Duplicated from Sorted Array;
,,,,}; //把数组的值赋给vector vector<int> vec(arr, arr+sizeof(arr)/sizeof(int)); 解法一: 时间复杂度O(n) 空间复杂度 ...
- remove duplicated gene pair using awk
cat input.txt TRINITY_DN106621_c0_g1_i1 TRINITY_DN129833_c0_g1_i2 TRINITY_DN106621_c0_g1_i1 TRINITY_ ...
- CG&CAD resource
Computational Geometry The Geometry Center (UIUC) Computational Geometry Pages (UIUC) Geometry in Ac ...
- 大数据技术之_19_Spark学习_05_Spark GraphX 应用解析 + Spark GraphX 概述、解析 + 计算模式 + Pregel API + 图算法参考代码 + PageRank 实例
第1章 Spark GraphX 概述1.1 什么是 Spark GraphX1.2 弹性分布式属性图1.3 运行图计算程序第2章 Spark GraphX 解析2.1 存储模式2.1.1 图存储模式 ...
- 07-THREE.JS 各种形状的几何图形
<!DOCTYPE html> <html> <head> <title>Example 02.04 - Geometries</title> ...
- 2. Spark GraphX解析
2.1 存储模式 2.1.1 图存储模式 巨型图的存储总体上有边分割和点分割两种存储方式 1)边分割(Edge-Cut):每个顶点都存储一次,但有的边会被打断分到两台机器上.这样做的好处是节省存储空间 ...
- Spark GraphX从入门到实战
第1章 Spark GraphX 概述 1.1 什么是 Spark GraphX Spark GraphX 是一个分布式图处理框架,它是基于 Spark 平台提供对图计算和图挖掘简洁易用的而丰 ...
- arcmap Command
The information in this document is useful if you are trying to programmatically find a built-in com ...
- python查找并删除相同文件-UNIQ File-wxPython-v6
相比第一版,新增:菜单,对话框,文件过滤器,操作结果保存,配置功能(自己写了一个读写配置文件的功能),提示语优化,模块分化更合理. 截图: 源代码: UniqFile-wxPython-v6.py: ...
随机推荐
- iOS UIWebview 长按图片,保存到本地相册
我们所要解决的问题如题目所示:ios中,长按Webview中的图片,将图片保存到本地相册.解决方案:对load的html网页,执行js注入,通过在webview中执行js代码,来响应点击事件,通过js ...
- 【编程思想】【设计模式】【行为模式Behavioral】观察者模式Observer
Python转载版 https://github.com/faif/python-patterns/blob/master/behavioral/observer.py #!/usr/bin/env ...
- Python @函数装饰器及用法(超级详细)
函数装饰器的工作原理是怎样的呢?假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示: #funA 作为装饰器函数 def funA(fn): #... fn() # 执行传入的fn参 ...
- 【Word】自动化参考文献-交叉引用
第一步:设置参考文献标号 开始-定义新编号格式中,定义参考文献式的方框编号: 这里注意不要把他原来的数字去掉 第二步:选择交叉引用 插入-交叉引用: 第三步:更新标号 如果更新标号,使用右键-更新域. ...
- 【JavaWeb】【学习】【过滤器】Filter 的简单应用
实现效果 在编辑框中输入暗号:如果暗号正确,则跳转到正确页面:如果暗号错误,则跳转到错误界面. [入口界面] [错误界面] [成功界面] [项目结构] JSP文件 本练习有两个jsp页面 页面1:in ...
- 调整markdown 图片大小和对齐方式
[博客园]调整markdown 图片大小和对齐方式 图片大小 例 <img src="https://img2020.cnblogs.com/blog/2199257/202101/2 ...
- springboot学习(一)
最近想学习springboot所以在网上找了很多文章参考怎么构建springboot项目以及集成mybatis 集成mybatis的部分参考了这两篇文章 https://blog.csdn.net/t ...
- SpringCloud (一) Eureka
Eureka Eureka 是一个服务治理组件,它主要包括服务注册和服务发现,主要用来搭建服务注册中心. Eureka 是一个基于 REST 的服务,用来定位服务,进行中间层服务器的负载均衡和故障转移 ...
- JAVA获取某个月(当月)第一天的开始时刻和某个月(当月)最后一天的最后时刻
package com.date; import java.text.SimpleDateFormat; import java.util.Calendar; public class Test { ...
- nim_duilib之virtualListbox用法(22)
概述 本文将介绍virtualListbox的用法. 更多请参考源码. 一个样式 样式丑了点,勿喷. 重写函数 使用virtualListbox, 需要一个派生类(继承自基类VirtualListIn ...