CGAL笔记之单元格复合体和多面体篇—曲面网格
CGAL笔记之单元格复合体和多面体篇—曲面网格
- 0、前言
- 1、用法
- 1、示例
- 2、连通性
- 3、范围和迭代器
- 3.1 示例
- 4、循环器
- 1、示例
- 5、属性
- 1、示例
- 6、边界
- 7、Surface Mesh和 BGL API
- 1、示例
- 8、表面网格 I/O
- 9、内存管理
- 1、 示例
- 10、绘制Surface Mesh
- 11 实现细节
0、前言
类Surface_mesh是半边数据结构的实现,可用于表示多面体表面。它是 CGAL 包Halfedge Data Structures和3D Polyhedral Surface的替代品。主要区别在于它是基于索引的,而不是基于指针的。此外,向顶点、半边、边和面添加信息的机制要简单得多,并且是在运行时完成的,而不是在编译时完成的。
因为数据结构使用整数索引作为顶点、半边、边和面的描述符,所以它比基于 64 位指针的版本具有更低的内存占用。由于索引是连续的,因此它们可以用作存储属性的向量的索引。
当元素被移除时,它们只是被标记为已移除,必须调用垃圾回收函数才能真正移除它们。
该类Surface_mesh
可以通过其类成员函数以及CGAL 包和 Boost Graph Library中描述的 BGL API 来使用,因为它是concept
的 MutableFaceGraph
和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()
。
typedef Surface_mesh<Point> Mesh;
Mesh m;
Mesh::Vertex_index u = m.add_vertex(Point(0,1,0));
Mesh::Vertex_index v = m.add_vertex(Point(0,0,0));
Mesh::Vertex_index w = m.add_vertex(Point(1,0,0));
m.add_face(u, v, w);
Surface_mesh
与基于索引的Vertex_index、Halfedge_index、Edge_index
和Face_index
一样,没有成员函数来访问连通性或属性。Surface_mesh
必须使用创建它们的实例的函数来获取此信息。
1、示例
下面的示例展示了如何Surface_mesh
通过添加 2 个面来创建一个非常简单的网格,以及如何检查面是否已正确添加到网格中。
#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;
}
2、连通性
表面网格是一种以边为中心的数据结构,能够维护顶点、边和面的关联信息。每条边由两个方向相反的半边表示。每个半边存储对入射面和入射顶点的引用。此外,它还会将下一个和上一个半边事件的引用存储到其事件面。对于每个面和每个顶点,都会存储一个关联的半边。半边不存储相对半边的索引,因为Surface_mesh
在内存中连续存储相对半边。
下图说明了允许在表面网格中导航的函数:Surface_mesh::opposite()
、Surface_mesh::next()
、Surface_mesh::prev()
、Surface_mesh::target()
和Surface_mesh::face()
。此外,这些函数Surface_mesh::halfedge()
允许获得与顶点和面相关联的半边。或者,可以使用包CGAL 和 Boost Graph Library中定义的具有相同名称的函数。
入射到面的半边形成一个循环。根据我们从表面的哪一侧看,半边的序列似乎是顺时针或逆时针方向。当在本手册中我们谈到遍历的方向时,我们会看到表面周围的半边是逆时针方向的。
连通性不允许表示有孔的面。
3、范围和迭代器
Surface_mesh
提供迭代器范围以枚举所有顶点、半边、边和面。它提供返回与Boost.Range库兼容的元素范围的成员函数。
3.1 示例
以下示例显示如何从范围中获取迭代器类型、获取开始和结束迭代器的备选方案以及基于范围的循环的备选方案。
#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;
}
4、循环器
在 CGAL 包和 Boost Graph Library中,围绕面和顶点的循环器作为类模板提供。
围绕面的循环器基本上是Surface_mesh::next()
为了从 halfedge 到 halfedge 逆时针绕着面进行调用,当取消引用时返回 halfedge 或入射顶点或相反的面。
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>
所有循环器模型BidirectionalCirculator
。除此之外,它们还支持转换为bool
以更方便地检查是否为空。
1、示例
以下示例显示如何枚举给定半边的目标周围的顶点。第二个循环显示这些循环器类型中的每一个都带有一个等效的迭代器和一个创建迭代器范围的自由函数。
#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;
}
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”属性映射将被保留并且保持对它的引用是安全的。
1、示例
此示例说明如何使用属性系统的最常见功能。
#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;
}
6、边界
半边存储对面的引用,即它的入射面。一条半边h
在边界上,如果它没有入射面,即如果。如果一条边的任何半边在边界上,则该边在边界上。如果顶点的任何关联半边在边界上,则该顶点在边界上。sm.face(h) == Surface_mesh::null_face()
一个顶点只有一个关联的半边。如果用户注意关联的半边是边界半边,如果顶点在边界上,则无需在函数中查看所有入射半边以查找顶点is_border()
。为了只检查关联的半边是否在边界上,Surface_mesh::is_border(Vertex_index v, bool check_all_incident_halfedges = true)
必须使用调用函数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)
和分别为单个顶点v、面边界上的所有顶点和表面网格的所有顶点hSurface_mesh::set_vertex_halfedge_to_border_halfedge()
设置边界半边。
7、Surface Mesh和 BGL API
该类是Boost Graph Library 中定义的Surface_mesh
概念模型。这使得可以直接在表面网格上IncidenceGraph
应用诸如Dijkstra 最短路径或Kruskal 最小生成树等算法。
BGL API 的类型和自由函数都有相似的类型或成员函数,例如
BGL | Surface_mesh | 评论 |
---|---|---|
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()
|
计算未删除的顶点并且没有 BGL 等价物 |
n = num_vertices(g)
|
n = sm.number_of_vertices() + sm.number_of_removed_vertices()
|
计算已使用和已删除的顶点,以便在使用的最大顶点索引上有一个上限 |
返回顶点数而不考虑删除的顶点会更好,但这会与底层顶点/边索引映射交互不良。[0,num_vertices(g))
索引映射将不再落在许多算法中假定的范围内。
该类也是包CGAL 和 Boost Graph Library 中定义的concept的Surface_mesh
的模型。这个和concept类型的 HalfedgeGraph
类似,通过引入半边和面的概念以及围绕面和顶点的半边循环来改进 BGL 的图形概念。同样,有类似的类型和,例如:MutableFaceGraph
和HalfedgeGraph
BGL | 表面网格 |
---|---|
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)
|
ETC。 |
CGAL包和 Boost Graph Library中描述的 BGL API使我们能够编写在表面网格上运行的几何算法,适用于FaceGraph
, 或的任何模型MutableFaceGraph
。即表面网格简化、变形或分割算法适用于Surface_mesh
和Polyhedron_3
。
BGL 算法使用属性映射将信息关联到顶点和边。一个重要的属性是索引,一个图的顶点0
之间的整数。这允许算法创建适当大小的向量以存储每个顶点信息。例如,用于存储在图形遍历期间是否已访问顶点的布尔值。num_vertices(g)``g
检索图的顶点索引属性映射的 BGL 方法g
是vipm = get(boost::vertex_index, g)
,然后get(vipm, vd)
为了检索顶点描述符的索引vd
,它是get(vertex_index, g, vd)
直接获取顶点索引。
1、示例
第一个示例表明我们可以直接在表面网格上应用 Kruskal 的最小生成树算法。
#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;
}
第二个示例展示了我们如何将属性映射用于诸如 Prim 的最小生成树之类的算法。该算法在内部也使用顶点索引属性映射调用get(boost::vertex_index_t,sm)
。对于这个类,Surface_mesh
这归结为一个身份函数,因为顶点是索引。
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <iostream>
#include <fstream>
// workaround a bug in Boost-1.54
#include <CGAL/boost/graph/dijkstra_shortest_paths.h>
#include <boost/graph/prim_minimum_spanning_tree.hpp>
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;
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;}Mesh::Property_map<vertex_descriptor,vertex_descriptor> predecessor;predecessor = sm.add_property_map<vertex_descriptor,vertex_descriptor>("v:predecessor").first;boost::prim_minimum_spanning_tree(sm, predecessor, boost::root_vertex(*vertices(sm).first));std::cout << "#VRML V2.0 utf8\n""DirectionalLight {\n""direction 0 -1 0\n""}\n""Shape {\n"" appearance Appearance {\n"" material Material { emissiveColor 1 0 0}}\n"" geometry\n"" IndexedLineSet {\n"" coord Coordinate {\n"" point [ \n";for(vertex_descriptor vd : vertices(sm)){std::cout << " " << sm.point(vd) << "\n";}std::cout << " ]\n"" }\n"" coordIndex [\n";for(vertex_descriptor vd : vertices(sm)){if(predecessor[vd]!=vd){std::cout << " " << std::size_t(vd) << ", " << std::size_t(predecessor[vd]) << ", -1\n";}}std::cout << "]\n"" }#IndexedLineSet\n""}# Shape\n";sm.remove_property_map(predecessor);return 0;
}
8、表面网格 I/O
作为模型FaceGraph
(请参阅部分曲面网格和 BGL API),CGAL::Surface_mesh
可以使用多种不同的文件格式进行读取和写入。有关详细信息,请参阅CGAL 和 Boost Graph Library包的I/O 函数以及多边形网格处理包的I/O 函数。
此外,该包还提供来自CGAL 和 Boost Graph Library 中Surface_mesh
包的 I/O 函数的特定重载。这允许直接从内部属性映射读取/写入,有关更多信息,请参阅I/O 函数。
9、内存管理
内存管理是半自动的。内存随着更多元素被添加到结构中而增长,但当元素被移除时内存不会减少。
当您添加元素并且基础向量的容量耗尽时,向量会重新分配内存。由于描述符基本上是索引,因此它们在重新分配后引用相同的元素。
当您删除一个元素时,它只会被标记为已删除。在内部,它被放在一个空闲列表中,当您将元素添加到表面网格时,它们会从空闲列表中取出,以防它不为空。
对于所有元素,我们提供了一个函数来获取已使用元素的数量,以及已使用和删除元素的数量。对于顶点,函数分别是Surface_mesh::number_of_vertices()
和Surface_mesh::number_of_removed_vertices()
。num_vertices(const G&)
第一个函数与BGL 包的免费函数略有不同。由于 BGL 样式算法使用元素的索引来访问临时大小向量中的数据,因此num_vertices()
此函数必须返回一个大于元素的最大索引的数字。
诸如Surface_mesh::Vertex_iterator
仅枚举未标记为已删除的元素的迭代器。
要真正缩小使用的内存,Surface_mesh::collect_garbage()
必须调用。垃圾收集还会压缩与表面网格相关的属性。
但是请注意,通过垃圾收集元素可以获得新的索引。如果您保留顶点描述符,它们很可能不再引用正确的顶点。
1、 示例
#include <iostream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
typedef CGAL::Simple_cartesian<double> K;
typedef CGAL::Surface_mesh<K::Point_3>网格;
typedef Mesh::Vertex_index vertex_descriptor;
主函数()
{网格米;Mesh::Vertex_index u;对于(无符号 整数i=0;i < 5;++i){网格::顶点索引 v = m。add_vertex (K::Point_3(0,0,i+1));如果(i==2)你=v;}m.remove_vertex(u);std::cout << "插入 5 个顶点并移除 3 个顶点后\n"<< “#个顶点/#个顶点+#个删除的顶点=”<< m.number_of_vertices()<< " / " << m.number_of_vertices() + m.number_of_removed_vertices() << std::endl;std::cout << "遍历顶点\n" ;{for (vertex_descriptor vd : m.vertices()){std::cout << m.point(vd) << std::endl;}}// 被使用或被移除的状态存储在属性映射中Mesh::Property_map<Mesh::Vertex_index,bool> 移除= m.property_map<Mesh::Vertex_index, bool >( "v:removed" ).first;std::cout << "\n遍历顶点并删除顶点\n"<< “#个顶点/#个顶点+#个删除的顶点=”<< m.number_of_vertices()<< " / " << m.number_of_vertices() + m.number_of_removed_vertices() << std::endl;{unsigned int i = 0, end = m.number_of_vertices() + m.number_of_removed_vertices();对于( ; i < 结束; ++i) {顶点描述符 vh(i);assert(m.is_removed(vh) == removed[vh]);std::cout << m.point(vh) << ((m.is_removed(vh)) ? " R\n" : "\n" );}}m.collect_garbage();std::cout << "\n垃圾回收后\n"<< “#个顶点/#个顶点+#个删除的顶点=”<< m.number_of_vertices()<< " / " << m.number_of_vertices() + m.number_of_removed_vertices() << std::endl;
{unsigned int i = 0, end = m.number_of_vertices() + m.number_of_removed_vertices();对于( ; i < 结束; ++i) {顶点描述符 vh(i);std::cout << m.point(vh) << ((m.is_removed(vh)) ? " R\n" : "\n" );}}返回0;
}
10、绘制Surface Mesh
可以通过调用CGAL::draw()来可视化表面网格,如以下示例所示。此函数打开一个新窗口,显示给定的表面网格。对该函数的调用是阻塞的,也就是说,只要用户关闭窗口,程序就会继续。
#include <CGAL/Surface_mesh.h>
#include <CGAL/draw_surface_mesh.h>
#include <fstream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Surface_mesh<Point> Mesh;
int main(int argc, char* argv[])
{const std::string filename = (argc>1) ? argv[1] : CGAL::data_file_path("meshes/elephant.off");Mesh sm;if(!CGAL::IO::read_polygon_mesh(filename, sm)){std::cerr << "Invalid input file." << std::endl;return EXIT_FAILURE;}CGAL::draw(sm);return EXIT_SUCCESS;
}
此函数需要, 且仅在定义CGAL_Qt5
宏时可用。CGAL_USE_BASIC_VIEWER
与 cmake 目标链接CGAL::CGAL_Basic_viewer
将链接CGAL_Qt5
并添加定义CGAL_USE_BASIC_VIEWER
。
11 实现细节
作为我们选择的索引的整数类型boost::uint32_t
。在 64 位操作系统上,它们只占指针大小的一半。他们仍然允许拥有 20 亿个元素的网格。
我们用于std::vector
存储属性。因此,通过访问属性映射的第 0 个元素的地址,您可以访问底层原始数组。这可能很有用,例如将点数组传递给 OpenGL。
我们对删除的元素使用*空闲列表。*这意味着当一个顶点被移除并稍后add_vertex
被调用时,被移除元素的内存将被重用。这尤其意味着第 n 个插入的元素不一定具有索引n-1
,并且在遍历元素时它们不会按插入顺序枚举。
CGAL笔记之单元格复合体和多面体篇—曲面网格相关推荐
- CGAL笔记之单元格复合体和多面体篇—三维多面体曲面
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 CGAL笔记之单元格复合体和多面体篇-三维多面体曲面 1 介绍 2 定义 3 示例程序 3.1 第一个使用默认值的例子 3.2 顶点中 ...
- CGAL笔记之单元格复合体和多面体篇——多面体凸分解
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 CGAL笔记之单元格复合体和多面体篇--多面体凸分解 1 介绍 2 接口使用 1 介绍 对于非凸多面体的许多应用,有一个有效的解决方案 ...
- CGAL笔记之单元格复合体和多面体篇——多面体的 3D Minkowski 和
CGAL笔记之单元格复合体和多面体篇--多面体的 3D 闵可夫斯基和 介绍 分解法 特性和约束 用法 介绍 如下图,勺子和星星的 Minkowski 和. 分解法 计算非凸多面体闵可夫斯基和的分解方法 ...
- PHPSpreadsheet学习笔记——访问单元格
文章目录 一.实例化对象 二.访问单元格 2.1获取单元格 2.2单元格赋值 2.3excel数据类型 2.3.1在单元格设置公式 2.3.2在单元格设置日期和时间值 2.3.3 设置带前导零的数字 ...
- 王佩丰excel教程笔记(单元格设置)
设置单元格格式对话框 合并后居中 跨越合并可以一次合并多行 改字体 改字体颜色 填充单元格背景色 在设置单元格选项卡下可以给一个单元格绘制线条 例如斜线 Alt+回车 实现一个单元格换行. 框线设置 ...
- vba excel学习笔记-从单元格获取数据,修改数据
从单元格获取数据 获取sheet名字为 "sheetname"的表格中的第一行第五列的值 赋值给变量a a = Sheets("sheetname").Cell ...
- vb 变量赋值为当前选定单元格_第7篇:根据Excel选定行对Word模板填充数据(补充修改)...
题外:由于漏掉了勾选Microsoft Word 16.0 Object Library这个关键步骤(不勾选是无法导出的),故修改后再次发出.本期我是从知乎上的郭大牛老师那里学的,非常建议大家去阅读, ...
- 设置Table单元格颜色(Table篇三)
组件:Y_XIAO_03 视图:V_MAIN 窗口:W_MAIN Application:Y_XIAO_03 创建组件. 创建节点. 参考类型(重要字段:CELL_COLOR TYPE WDUI_TA ...
- list Control实现单元格编辑与插入Combo Box
之前写过一篇(list Control实现单元格编辑)文章,那篇文章不是很完善执行的时候有时会出错,这篇文章经过完善后还加入了Combo Box功能! 这里我就只是晒一下我的代码; 头文件: [c-s ...
最新文章
- jQuery中的.height()、.innerHeight()和.outerHeight()
- ajax不支持post,AJAX不能正确发送POST变量
- mysql scws_php利用scws实现mysql全文搜索功能的方法
- byte二维数组表示
- mysql-笔记-命名、索引规范
- 图像处理总结:Canny边缘检测(二)
- 阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第4节 等待唤醒机制_7_等待唤醒机制需求分析...
- 计算机网络中处理通信控制功能的是,在计算机网络中处理通信控制功能的计算机是()。A.通信线路B.终端C.主计算机D.通信控制处理机...
- html 页面没有鼠标,网页上鼠标箭头不见了 电脑上不显示鼠标箭头怎么办?
- Creator打字机效果
- 电脑怎么连接隐藏的无线WiFi信号呢
- CenterNet代码解析-ctdet目标检测
- FX110网:鳄鱼线(Alligator)指标的操作应用
- “3D游戏之父”考古“诺基亚时代”手游,网友:求支持智能手机!
- ByRef和ByVal
- Snipaste 截图贴图
- 2022-02-16 工作记录--Swiper-实现跑马灯效果:鼠标经过时停止滑动,鼠标离开后继续滑动
- 超大文件上传和断点续传的实现
- 核雕图案的寓意(上)
- python requests的作用_requests session的作用
热门文章
- u盘启动计算机的几种方式,进入U盘启动模式的启动模式是什么?第一种方法是输入BIO...
- InDesign 教程:如何在 InDesign 中使用的不同类型的框架?
- 人脸识别最新数据库(表情)汇总
- DE2-115 FPGA开发板的VGA显示
- 股票配资交易系统【实盘】
- Arduino自动点击鼠标
- 联系生活实际计算机的应用领域都有哪些,浅谈数学在我们日常生活的实际应用...
- 什么是机器视觉检测?视觉检测技术的应用与优势?
- 2022年T电梯修理题库及模拟考试
- html在线音频播放器实训总结,HTML5 播放器心得与小结