官方文档链接:CGAL 5.4.2 - Surface Mesh: User Manual

0 概述

Surface_mesh 类是半边数据结构的实现,可用于表示多面体表面。

相较于 Halfedge Data Structures3D Polyhedral Surface ,其具有以下特点:

  • 不同于基于指针,Surface_mesh基于索引(数据结构使用整数索引作为顶点、半边、边和面的描述符);
  • 向顶点、半边、边和面添加信息的机制要简单得多;
  • 具有更低的内存占用。
  • 元素被移除后,会被标记为已移除,需要调用垃圾回收函数才能实现真正地移除。

1 用法

Surface_mesh类提供了如下四个类来表示网格的基本元素:

  • Surface_mesh::Vertex_index
  • Surface_mesh::Halfedge_index
  • Surface_mesh::Face_index
  • Surface_mesh::Edge_index

由于Surface_mesh是基于索引值构造的,所以没有访问连接性或属性的成员函数。

1.1 栗子

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
typedef CGAL::Simple_cartesian<double> K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef Mesh::Vertex_index vertex_descriptor;   //基于索引的顶点描述
typedef Mesh::Face_index face_descriptor;       //基于索引的面描述
int main()
{Mesh m;// Add the points as verticesvertex_descriptor u = m.add_vertex(K::Point_3(0, 1, 0));vertex_descriptor v = m.add_vertex(K::Point_3(0, 0, 0));vertex_descriptor w = m.add_vertex(K::Point_3(1, 1, 0));vertex_descriptor x = m.add_vertex(K::Point_3(1, 0, 0));m.add_face(u, v, w);face_descriptor f = m.add_face(u, v, x);if (f == Mesh::null_face()){std::cerr << "The face could not be added because of an orientation error." << std::endl;f = m.add_face(u, x, v);assert(f != Mesh::null_face());}return 0;
}

在这个例子中,通过添加两个面创建一个简单的表面网格。可以看到,在使用add_face()添加face的时候,若其返回的Face_index值为Surface_mesh::null_face()则说明添加面的操作在拓扑上无效,添加失败。

2 连接关系

在一个surface_mesh中可以使用如下函数进行基于连接关系的查询:
Surface_mesh::opposite(), Surface_mesh::next(), Surface_mesh::prev(), Surface_mesh::target(), and Surface_mesh::face()。 此外,函数Surface_mesh::halfedge() 能够获取与顶点和面相关联的半边。

范围和迭代器

surface_mesh提供了迭代器用以枚举所有的顶点、半边、边和面。

#include <vector>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
typedef CGAL::Simple_cartesian<double> K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef Mesh::Vertex_index vertex_descriptor;
typedef Mesh::Face_index face_descriptor;
int main()
{Mesh m;// u            x// +------------+// |            |// |            |// |      f     |// |            |// |            |// +------------+// v            w// Add the points as verticesvertex_descriptor u = m.add_vertex(K::Point_3(0,1,0));vertex_descriptor v = m.add_vertex(K::Point_3(0,0,0));vertex_descriptor w = m.add_vertex(K::Point_3(1,0,0));vertex_descriptor x = m.add_vertex(K::Point_3(1,1,0));/* face_descriptor f = */ m.add_face(u,v,w,x);{std::cout << "all vertices " << std::endl;// The vertex iterator type is a nested type of the Vertex_rangeMesh::Vertex_range::iterator  vb, ve;Mesh::Vertex_range r = m.vertices();// The iterators can be accessed through the C++ range APIvb = r.begin();ve = r.end();// or the boost Range APIvb = boost::begin(r);ve = boost::end(r);// or with boost::tie, as the CGAL range derives from std::pairfor(boost::tie(vb, ve) = m.vertices(); vb != ve; ++vb){std::cout << *vb << std::endl;}// Instead of the classical for loop one can use// the boost macro for a rangefor(vertex_descriptor vd : m.vertices()){std::cout << vd << std::endl;}// or the C++11 for loop. Note that there is a ':' and not a ',' as in BOOST_FOREACHfor(vertex_descriptor vd : m.vertices()){std::cout << vd << std::endl;}}return 0;
}

这是一个遍历表面网格中所有顶点索引的例子。其中关键迭代器类型Mesh::Vertex_range::iterator,关键范围类型Mesh::Vertex_range。通过后者得到前者,进而对范围中元素进行迭代遍历。

4 循环器

围绕面的循环器:

  • CGAL::Halfedge_around_face_circulator<Mesh>
  • CGAL::Vertex_around_face_circulator<Mesh>
  • CGAL::Face_around_face_circulator<Mesh>

围绕目标顶点的循环器:

  • CGAL::Halfedge_around_target_circulator<Mesh>
  • CGAL::Vertex_around_target_circulator<Mesh>
  • CGAL::Face_around_target_circulator<Mesh>

所有循环器围绕方向为逆时针。

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <vector>
typedef CGAL::Simple_cartesian<double> K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef Mesh::Vertex_index vertex_descriptor;
typedef Mesh::Face_index face_descriptor;
int main()
{Mesh m;// u            x// +------------+// |            |// |            |// |      f     |// |            |// |            |// +------------+// v            w// Add the points as verticesvertex_descriptor u = m.add_vertex(K::Point_3(0,1,0));vertex_descriptor v = m.add_vertex(K::Point_3(0,0,0));vertex_descriptor w = m.add_vertex(K::Point_3(1,0,0));vertex_descriptor x = m.add_vertex(K::Point_3(1,1,0));face_descriptor f = m.add_face(u,v,w,x);{std::cout << "vertices around vertex " << v << std::endl;CGAL::Vertex_around_target_circulator<Mesh> vbegin(m.halfedge(v),m), done(vbegin);do {std::cout << *vbegin++ << std::endl;} while(vbegin != done);}{std::cout << "vertices around face " << f << std::endl;CGAL::Vertex_around_face_iterator<Mesh> vbegin, vend;for(boost::tie(vbegin, vend) = vertices_around_face(m.halfedge(f), m);vbegin != vend;++vbegin){std::cout << *vbegin << std::endl;}}// or the same again, but directly with a range based loopfor(vertex_descriptor vd : vertices_around_face(m.halfedge(f), m)){std::cout << vd << std::endl;}return 0;
}

这个例子中展示了Vertex_around_target_circulator的使用方法,其中也展示了使用迭代器达到相同的效果。

5 属性

surface_mesh提供了可以在运行时指定顶点、半边、边和面的属性的机制。每个属性通过一个字符串和它的键类型进行标识。

默认情况下,只有一个属性"v:point"。当通过 Surface_mesh::add_vertex() 向数据结构添加新点时,必须提供此属性的值。 可以使用 Surface_mesh::points()Surface_mesh::point(Surface_mesh::Vertex_index v) 直接访问该属性。

当一个元素被移除,它并不会立刻被真正意义上的删除,而是被标记为“已移除”,只有调用Surface_mesh::collect_garbage()才能真正地移除该元素的所有相关属性。

#include <string>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
typedef CGAL::Simple_cartesian<double> K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef Mesh::Vertex_index vertex_descriptor;
typedef Mesh::Face_index face_descriptor;
int main()
{Mesh m;vertex_descriptor v0 = m.add_vertex(K::Point_3(0,2,0));vertex_descriptor v1 = m.add_vertex(K::Point_3(2,2,0));vertex_descriptor v2 = m.add_vertex(K::Point_3(0,0,0));vertex_descriptor v3 = m.add_vertex(K::Point_3(2,0,0));vertex_descriptor v4 = m.add_vertex(K::Point_3(1,1,0));m.add_face(v3, v1, v4);m.add_face(v0, v4, v1);m.add_face(v0, v2, v4);m.add_face(v2, v3, v4);// give each vertex a name, the default is emptyMesh::Property_map<vertex_descriptor,std::string> name;bool created;boost::tie(name, created) = m.add_property_map<vertex_descriptor,std::string>("v:name","");assert(created);// add some names to the verticesname[v0] = "hello";name[v2] = "world";{// You get an existing property, and created will be falseMesh::Property_map<vertex_descriptor,std::string> name;bool created;boost::tie(name, created) = m.add_property_map<vertex_descriptor,std::string>("v:name", "");assert(! created);}//  You can't get a property that does not existMesh::Property_map<face_descriptor,std::string> gnus;bool found;boost::tie(gnus, found) = m.property_map<face_descriptor,std::string>("v:gnus");assert(! found);// retrieve the point property for which exists a convenience functionMesh::Property_map<vertex_descriptor, K::Point_3> location = m.points();for(vertex_descriptor vd : m.vertices()) {std::cout << name[vd] << " @ " << location[vd] << std::endl;}std::vector<std::string> props = m.properties<vertex_descriptor>();for(std::string p : props){std::cout << p << std::endl;}// delete the string property againm.remove_property_map(name);return 0;
}

上面例子中依次实现了property的创建、查询、删除等操作。

6 边界

一个半边存储了其入射面的引用,如果它没有入射面,则表示该半边在边界上,即sm.face(h) == Surface_mesh::null_face()。若一个半边在边界上,那么包含它的边和以它为入射半边的顶点都位于边界上。

  • Surface_mesh::is_border(Vertex_index v, bool check_all_incident_halfedges = false):检查一个顶点的关联半边是否在边界上。

  • Surface_mesh::set_vertex_halfedge_to_border_halfedge(Vertex_index v) //将单个顶点的关联半边设置为边界半边
    Surface_mesh::set_vertex_halfedge_to_border_halfedge(Halfedge_index h) //将面h的边界顶点的关联半边设置为边界半边
    Surface_mesh::set_vertex_halfedge_to_border_halfedge() //将单表面网格上所有顶点的关联半边设置为边界半边
    

7 相关API

7.1 栗子

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <iostream>
#include <fstream>
#include <list>
typedef CGAL::Simple_cartesian<double>                       Kernel;
typedef Kernel::Point_3                                      Point;
typedef CGAL::Surface_mesh<Point>                            Mesh;
typedef boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Mesh>::vertex_iterator   vertex_iterator;
typedef boost::graph_traits<Mesh>::edge_descriptor   edge_descriptor;
void kruskal(const Mesh& sm)
{// We use the default edge weight which is the squared length of the edgestd::list<edge_descriptor> mst;boost::kruskal_minimum_spanning_tree(sm,std::back_inserter(mst));std::cout << "#VRML V2.0 utf8\n""Shape {\n""  appearance Appearance {\n""    material Material { emissiveColor 1 0 0}}\n""    geometry\n""    IndexedLineSet {\n""      coord Coordinate {\n""        point [ \n";vertex_iterator vb,ve;for(boost::tie(vb, ve) = vertices(sm); vb!=ve; ++vb){std::cout <<  "        " << sm.point(*vb) << "\n";}std::cout << "        ]\n""     }\n""      coordIndex [\n";for(std::list<edge_descriptor>::iterator it = mst.begin(); it != mst.end(); ++it){edge_descriptor e = *it ;vertex_descriptor s = source(e,sm);vertex_descriptor t = target(e,sm);std::cout << "      " << s << ", " << t <<  ", -1\n";}std::cout << "]\n""  }#IndexedLineSet\n""}# Shape\n";
}
int main(int argc, char** argv)
{Mesh sm;std::string fname = argc==1?CGAL::data_file_path("meshes/knot1.off"):argv[1];if(!CGAL::IO::read_polygon_mesh(fname, sm)){std::cerr << "Invalid input file." << std::endl;return EXIT_FAILURE;}kruskal(sm);return 0;
}

【CGAL_网格】Surface_mesh相关推荐

  1. 【CGAL_网格处理】坐标变换

    官方手册阅读 文档函数链接:CGAL 5.5 - Polygon Mesh Processing: Polygon Mesh Processing Reference transform() temp ...

  2. 【CGAL_网格处理】Isotropic Remeshing均匀化网格

    原理 算法伪代码如下: remesh(target_edge_length)low = 4/5 * target_edge_lengthhigh = 4/3 * target_edge_lengthf ...

  3. CGAL笔记之单元格复合体和多面体篇—曲面网格

    CGAL笔记之单元格复合体和多面体篇-曲面网格 0.前言 1.用法 1.示例 2.连通性 3.范围和迭代器 3.1 示例 4.循环器 1.示例 5.属性 1.示例 6.边界 7.Surface Mes ...

  4. CGAL表面网格降采样

    CGAL表面网格降采样 1. CGAL Surface_mesh_simplification模块 2. 源为stl格式数据 1. CGAL Surface_mesh_simplification模块 ...

  5. CGAL学习记录——网格孔洞填充

    CGAL--孔洞填充 效果展示 代码 效果展示 Easy3D显示 代码 #include <CGAL/Exact_predicates_inexact_constructions_kernel. ...

  6. Isotropic Remeshing:各向同性网格重建介绍与实现

    最近开始做网格重建与优化方面的工作,这里就不得不提到Isotropic Remeshing. 部分内容参考自:https://zhuanlan.zhihu.com/p/104107745#ref_3 ...

  7. Turing渲染着色器网格技术分析

    Turing渲染着色器网格技术分析 图灵体系结构通过使用 网格着色器 引入了一种新的可编程几何着色管道.新的着色器将计算编程模型引入到图形管道中,因为协同使用线程在芯片上直接生成紧凑网格( meshl ...

  8. 2022-2028年中国塑料网格板行业市场行情动态及发展趋向分析报告

    [报告类型]产业研究 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了塑料网格板行业相关概述.中国塑料网格板行业运行环境.分析了中国塑料网 ...

  9. 用Apache Ignite实现可扩展的数据网格

    在本文中,我们将先介绍数据网格(Data Grid)的基本概念.属性.以及能够提供的服务,然后讨论如何设计可扩展的数据网格,以满足实际场景的业务需求. 什么是数据网格? 数据网格是一组能够提供共享数据 ...

最新文章

  1. python列表元素修改_python – 如何修改列表中列表中的元素
  2. JQuery------获取input type=file中的文件内容
  3. 3、常用数据库访问接口简介
  4. 蓝桥杯 入门训练 试题集汇总 【A+B问题、序列求和、圆的面积、Fibonacci数列】
  5. (二)Mac系统Nginx的安装
  6. 甲骨文中国疯狂裁员 招聘网站上线“甲骨文人才专场”
  7. iterator 怎么使用甀_Iterator的使用
  8. pythonapp自动化_移动App Appium自动化测试教程Appium+Python 【2018年新】
  9. Linux学习笔记---记一次rootfs根文件系统下载时掉进的大坑
  10. Android学习笔记(八)——在Manifest中设置ActionBar
  11. [myeclipse] 官方中文网站
  12. Node.js:POST请求、文件上传
  13. mysql必要的监控项目--转自土豆大神的博客
  14. 用Python操作PPT的办公自动化教程
  15. kali linux如何更新软件源
  16. wifi已连接不可上网服务器无响应,为什么手机连上wifi却上不了网
  17. 自建微信公众号文章搜索舆情系统
  18. 升鲜宝V2.0_生鲜配送行业,对生鲜配送行业的思考及对系统流程开发的反思_升鲜宝生鲜配送系统_15382353715_余东升...
  19. 吴恩达:回顾2021,这些大事件影响了AI这一年
  20. 视频怎么压缩小于100兆?压缩视频这么做就可以了

热门文章

  1. PHP解决数学的游戏问题,幼儿园数学区域材料的投放问题及对策
  2. MySql GRANT IDENTIFIED BY 语句出错
  3. 一个可离线使用的OCR图片转文字工具
  4. 2022年度回顾之显卡篇 (国产显卡迎来春天,RTX 4090大杀四方)
  5. 每天一个Linux命令(22):文件属性详解
  6. ubuntu部署deepsort目标跟踪算法,无人车/无人机应用
  7. 电信禁止路由上网的破解方法
  8. ts有什么用、好处、与js区别
  9. 候选公示!高工智能汽车金球奖第五批入围年度产品/方案亮相
  10. 实验一 CISCO路由器的基本配置