CGAL的表面网格

        类 Surface_mesh 是半边数据结构的实现,可用于表示多面体表面。它是CGAL包 Halfedge Data Structures 和 3D Polyhedral Surface 的替代品。主要区别在于它是基于索引而非基于指针的。此外,向顶点、半边、边和面添加信息的机制要简单得多,并且在运行时而不是在编译时完成。

        由于数据结构使用整数索引作为顶点、半边、边和面的描述符,因此它的内存占用比基于64位指针的版本要低。由于索引是连续的,因此它们可以用作存储属性的向量的索引。

        当元素被移除时,它们只被标记为已移除,必须调用垃圾收集函数才能真正移除它们。

        类Surface_mesh可以通过其类成员函数以及通过包CGAL和Boost Graph Library中描述的BGL API使用,因为它是MutableFaceGraph和FaceListGraph概念的模型。因此,可以在表面网格上应用三角表面网格简化、三角表面网格分割和三角表面网格变形等算法。 

        MutableFaceGraph:MutableFaceGraph是一个可变图模型,它允许修改图的结构和属性。MutableFaceGraph概念模型定义了添加、删除和修改图中的顶点、半边、面和边的方法。它还定义了检查两个顶点之间是否存在边、检查两个面之间是否存在共享边等操作。MutableFaceGraph概念模型适用于需要动态修改图的形状和结构的算法。

        FaceListGraph:FaceListGraph是一个只读图模型,它提供了只读访问图中的顶点、半边、面和边的方法。FaceListGraph概念模型适用于需要遍历图中的元素但不修改它们的情况。它提供了获取图中所有顶点、所有面、所有半边等的方法,但不支持添加、删除或修改这些元素。

1、使用

        主类 Surface_mesh 提供了四个嵌套类,表示半边数据结构的基本元素:Surface_mesh::Vertex_index、Surface_mesh::Halfedge_index、Surface_mesh::Face_index、 Surface_mesh::Edge_index。

        这些类型只是整型的包装器,其主要目的是保证类型安全。它们是默认可构造的,会产生无效元素。可以通过一组不保持连通性的低级函数向 Surface_mesh 添加和删除新元素。一个例外是 Surface_mesh::add_face(),它试图向网格(由一系列顶点定义)添加新面,如果操作在拓扑上无效,则失败。在这种情况下,返回的 Face_index 是 Surface_mesh::null_face()。

        由于 Surface_mesh 是基于索引的 Vertex_index,Halfedge_index,Edge_index 和 Face_index 没有成员函数来访问连接或属性。必须使用创建它们的 Surface_mesh 实例的函数来获取此信息。

2、连通性

        表面网格是一种以边为中心的数据结构,能够维护顶点、边和面的关联信息。每条边由两个方向相反的半边表示。每个半边存储一个关联面和一个关联顶点的引用。此外,它还存储一个与其关联面关联的下一个和上一个半边的引用。对于每个面和每个顶点,都存储一个关联半边。半边不存储相反半边的索引,因为 Surface_mesh 在内存中连续存储相反半边。

        下图说明了允许在表面网格中导航的功能:Surface_mesh::opposite()、Surface_mesh::next()、Surface_mesh::prev()、Surface_mesh::target()和Surface_mesh::face()。此外,函数Surface_mesh::halfedge()允许获取与顶点和面相关联的半边。或者,可以使用在 Boost Graph Library 和igraph包中定义的同名自由函数。

 

        面上的半边形成一个循环。根据我们从哪一侧看表面,半边的顺序似乎是顺时针或逆时针方向。在本手册中,当我们谈到遍历的方向时,我们看表面时,面周围的半边是逆时针方向的,如图27.1所示。

        连接不允许表示有孔的面。 

3、范围和迭代

        Surface_mesh 提供迭代器范围来枚举所有顶点、半边、边和面。它提供成员函数来返回与 Boost.Range 库兼容的元素范围。

4、循环迭代器

        CGAL 和 Boost Graph Library 中提供了围绕面和顶点的循环器作为类模板。

        围绕面的循环器基本上调用 Surface_mesh::next(),以便按逆时针方向从半边到半边围绕面,并且在被解引用时返回半边或关联的顶点或对面的面。

        CGAL::Halfedge_around_face_circulator<Mesh>

        CGAL::Vertex_around_face_circulator<Mesh>

        CGAL::Face_around_face_circulator<Mesh>

        围绕目标顶点的循环器基本上调用 Surface_mesh::opposite(Surface_mesh::next()),以便按顺时针方向围绕相同的目标顶点从半边到半边。 

        CGAL::Halfedge_around_target_circulator<Mesh>

        CGAL::Vertex_around_target_circulator<Mesh>

        CGAL::Face_around_target_circulator<Mesh>
        所有循环器都实现了 双向循环器模型。除此之外,它们还支持转换为 bool,以便更方便地检查是否为空。

5、特性

        Surface_mesh 提供了一种机制来在运行时为顶点、半边、边和面指定新属性。每个属性都由一个字符串和其键类型标识。给定属性的所有值都作为连续的内存块存储。每当向数据结构中添加具有键类型的新元素或调用函数 Surface_mesh::collect_garbage() 时,对属性的引用将被无效化。删除元素后,元素的属性将继续存在。通过无效的元素访问属性将导致未定义的行为。

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

        当元素被删除时,它只是被标记为已删除,并在调用 Surface_mesh::collect_garbage() 时被真正删除。垃圾回收也会真正删除这些元素的属性。

        连接性也存储在属性中,即名为 "v:connectivity"、"h:connectivity" 和 "f:connectivity" 的属性。删除元素的标记与此非常相似,我们有 "v:removed"、"e:removed" 和 "f:removed"。

        提供方便的函数来删除用户添加的属性映射,要么通过索引类型 (Surface_mesh::remove_property_maps<I>()),要么全部删除 (Surface_mesh::remove_all_property_maps())。

        要清除网格,您可以选择获取所有已添加的属性映射都被移除的网格 (Surface_mesh::clear()),或者保留它们 (Surface_mesh::clear_without_removing_property_maps())。请注意,在这两种情况下,"v:point" 属性映射都将被保留,保留对其的引用是安全的。

6、边界

        半边存储了对面的引用,即它的关联面。如果半边h没有关联面,即sm.face(h) == Surface_mesh::null_face(),则它在边界上。如果半边的任何一半在边界上,则边在边界上。如果顶点的任何一半在边界上,则顶点在边界上。

        一个顶点只有一个关联的半边。如果用户注意到了关联的半边是边界半边,则如果顶点在边界上,则不需要在is_border()函数中查看所有关联的半边。为了只检查关联的半边是否在边界上,必须使用check_all_incident_halfedges = false调用函数Surface_mesh::is_border(Vertex_index v, bool check_all_incident_halfedges = true)。

        用户负责在应用可能使此属性无效的操作后,正确设置与顶点关联的半边。函数 Surface_mesh::set_vertex_halfedge_to_border_halfedge(Vertex_index v)、Surface_mesh::set_vertex_halfedge_to_border_halfedge(Halfedge_index h) 和 Surface_mesh::set_vertex_halfedge_to_border_halfedge() 分别用于为单个顶点 v、h 表面边界上的所有顶点以及曲面网格的所有顶点设置边界半边。

7、表面网格和BGL的 API(和boost库的联合使用,不了解也可以)

        类 Surface_mesh 是 Boost Graph Library 中定义的概念 IncidenceGraph 的模型。这使得可以直接在表面网格上应用 Dijkstra 最短路径或 Kruskal 最小生成树等算法。

        例如,BGL API的类型和自由函数都具有类似的类型或成员函数

BGL

Surface_mesh

Remark

boost::graph_traits<G>::vertex_descriptor

Surface_mesh::Vertex_index

boost::graph_traits<G>::edge_descriptor

Surface_mesh::Edge_index

vertices(const G& g)

sm.vertices()

edges(const G& g)

sm.edges()

vd = source(ed,g)

vd = sm.source(ed)

na

n = sm.number_of_vertices()

counts non-deleted vertices and has no BGL equivalent

n = num_vertices(g)

n = sm.number_of_vertices() + sm.number_of_removed_vertices()

counts used and deleted vertices in order to have an upper bound on the largest vertex index used

        在不考虑删除的顶点的情况下返回顶点数量会更好,但这会与底层的顶点/边索引映射产生严重冲突。索引映射将不再落在许多算法中假设的[0,num_vertices(g)]范围内。

        类 Surface_mesh 也是CGAL和 Boost Graph Library 中定义的概念 MutableFaceGraph 的模型。这个概念和类似的概念,如 HalfedgeGraph,通过引入半边和面的概念,以及围绕面和顶点的半边循环,细化了 BGL 的图概念。同样,也有类似的类型和函数,例如

BGL

Surface_mesh

boost::graph_traits<G>::halfedge_descriptor

Surface_mesh::Halfedge_index

boost::graph_traits<G>::face_descriptor

Surface_mesh::Face_index

halfedges(const G& g)

sm.halfedges()

faces(const G& g)

sm.faces()

hd = next(hd, g)

hd = sm.next(hd)

hd = prev(hd, g)

hd = sm.prev(hd)

hd = opposite(hd,g)

hd = sm.opposite(hd)

hd = halfedge(vd,g)

hd = sm.halfedge(vd)

        包CGAL和Boost Graph Library中描述的BGL API使我们能够编写在曲面网格上运行的几何算法,这些算法适用于任何FaceGraph或MutableFaceGraph模型。这是表面网格简化、变形或分割算法对 Surface_mesh 和 Polyhedron_3 有效。

        BGL算法使用属性映射来将信息与顶点和边相关联。一个重要的属性是索引,对于图g的顶点,索引是一个介于0和num_vertices(g)之间的整数。这允许算法创建一个适当大小的向量,以存储每个顶点信息。例如,一个布尔值,用于存储在图遍历过程中是否已经访问过某个顶点。

        BGL检索图g的顶点索引属性映射的方法是vipm = get(boost::vertex_index, g),然后依次使用get(vipm, vd)检索顶点描述符vd的索引,直接获取顶点索引的方法是get(vertex_index, g, vd)。

8、I/O

        作为FaceGraph的模型(请参见剖面曲面网格和BGL API),可以使用多种不同的文件格式读取和写入CGAL::Surface_Mesh。有关更多信息,请参阅C++图形库和Boost Graph Library包的I/O函数,以及多边形网格处理包的I/O函数。

        此外,此软件包还提供了来自C++标准库和Boost Graph Library软件包的I/O函数针对Surface_mesh的特定重载。这使得可以直接从内部属性映射中读取/写入,有关更多信息,请参阅I/O函数。

9、内存管理

        内存管理是半自动的。随着向结构中添加更多元素,内存会增长,但当删除元素时,内存不会缩小。

        当您添加元素并且底层向量的容量耗尽时,向量会重新分配内存。由于描述符基本上是索引,因此在重新分配后它们指向相同的元素。

        当你删除一个元素时,它只会被标记为已删除。在内部,它会被放在一个空闲列表中,当你向曲面网格添加元素时,如果空闲列表不为空,它们将从空闲列表中取出。

        对于所有元素,我们提供了一个函数来获取已使用的元素数量,以及已使用和已删除的元素数量。对于顶点,函数分别是 Surface_mesh::number_of_vertices() 和 Surface_mesh::number_of_removed_vertices()。第一个函数与 BGL 包的自由函数 num_vertices(const G++) 略有不同。由于 BGL 风格的算法使用元素的索引来访问大小为 num_vertices() 的临时向量中的数据,因此该函数必须返回一个大于元素最大索引的数字。

        诸如 Surface_mesh::Vertex_iterator 之类的迭代器仅枚举未标记为已删除的元素。

        要真正减少已使用的内存,必须调用 Surface_mesh::collect_garbage()。垃圾回收也会压缩与曲面网格关联的属性。

        但是请注意,通过垃圾回收,元素会获得新的索引。如果你保留了顶点描述符,它们很可能不再指向正确的顶点。

10、显示表面网格

        通过调用getRoot::draw<SM>(),可以显示表面网格,此函数打开一个新窗口,显示给定的表面网格。调用此函数是阻塞的,即用户关闭窗口后程序会立即继续。

11、实施细则

        我们选择boost::uint32_t作为索引的整数类型。在64位操作系统上,它们的大小只有指针的一半。它们仍然允许具有20亿个元素的网格。

        我们使用std::vector来存储属性。因此,通过访问属性映射的第0个元素的地址,您可以访问底层原始数组。这可能很有用,例如,可以将点数组传递给OpenGL。

        我们使用一个自由列表来存储被删除的元素。这意味着当一个顶点被删除,并且稍后调用 add_vertex 时,被删除元素的内存会被重用。这尤其意味着第 n 个插入的元素不一定是索引 n-1,并且在遍历元素时,它们将不会按照插入顺序被枚举。

12、实施历史

        该软件包参考Openmesh.

        BGL是Boost Graph Library的缩写,它是一个C++库,提供了许多图算法和数据结构。BGL是Boost库的一部分,是一个经过广泛测试的可靠库,适用于许多不同的应用程序和领域。

CGAL 5.6 - Surface Mesh: User Manual

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CGAL(Computational Geometry Algorithms Library)是一个用于计算几何的C++库,提供了许多算法和数据结构来解决各种计算几何问题,包括网格填洞。 实现网格填洞的一种常见方法是使用Delaunay三角剖分。Delaunay三角剖分是一种将点集分成三角形的方法,这些三角形满足一些性质,例如没有任何点在其外接圆内部。如果我们将网格表现为一个点集,我们可以使用Delaunay三角剖分来创建一组三角形,并填补任何缺口。 以下是使用CGAL进行网格填洞的基本步骤: 1. 加载网格:使用CGAL加载网格,这可以通过读取支持的网格格式文件来实现。例如,可以使用CGAL提供的OFF格式读取器来加载OFF格式网格。 2. 创建Delaunay三角剖分:使用CGAL中的Delaunay三角剖分算法创建一个三角剖分。 3. 检测缺口:使用CGAL中的检测缺口算法检测网格中的缺口。 4. 填充缺口:使用CGAL中的填充缺口算法填充网格中的缺口。 5. 保存网格:使用CGAL中的保存网格算法将填充后的网格保存到文件中。 以下是使用CGAL进行网格填洞的示例代码: ```cpp #include <CGAL/Simple_cartesian.h> #include <CGAL/Polyhedron_3.h> #include <CGAL/Polyhedron_incremental_builder_3.h> #include <CGAL/Polyhedron_traits_with_normals_3.h> #include <CGAL/Delaunay_triangulation_3.h> #include <CGAL/make_mesh_3.h> #include <CGAL/IO/Complex_3_in_triangulation_vertex_base.h> #include <CGAL/IO/Complex_3_in_triangulation_cell_base.h> #include <CGAL/IO/Complex_3_in_triangulation_geom_traits.h> #include <CGAL/IO/Polyhedron_iostream.h> typedef CGAL::Exact_predicates_inexact_constructions_kernel K; typedef CGAL::Polyhedron_traits_with_normals_3<K> Traits; typedef CGAL::Complex_3_in_triangulation_vertex_base<K,Traits> Vb; typedef CGAL::Complex_3_in_triangulation_cell_base<K> Cb; typedef CGAL::Triangulation_data_structure_3<Vb,Cb> Tds; typedef CGAL::Delaunay_triangulation_3<K,Tds> Delaunay; typedef K::Point_3 Point_3; typedef CGAL::Polyhedron_3<Traits,Tds> Polyhedron; int main() { // Load mesh Polyhedron mesh; std::ifstream input("input.off"); input >> mesh; // Create Delaunay triangulation Delaunay dt; dt.insert(mesh.points_begin(), mesh.points_end()); // Fill holes CGAL::Polygon_mesh_processing::triangulate_hole_polygons(dt); // Save mesh std::ofstream output("output.off"); output << mesh; return 0; } ``` 在这个示例代码中,我们首先加载了一个网格文件(假设是OFF格式)。然后,我们创建了一个Delaunay三角剖分,并将网格的顶点插入到三角剖分中。接下来,我们使用CGAL中的`triangulate_hole_polygons`函数来填充任何缺口。最后,我们将填充后的网格保存到文件中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值