CGAL-Surface Mesh Manual
Author: Mario Botsch, Daniel Sieger, Philipp Moeller and Andreas Fabri
Surface_mesh类是一个半凸数据结构的实现,可以用来表示一个多面体曲面。它是CGAL包Halfedge数据结构和3D多面体表面的替代方案。主要区别在于它是基于索引的,而不是基于指针的。此外,向顶点、半边、边和面添加信息的机制要简单得多,并且是在运行时而不是在编译时完成的。
由于该数据结构使用整数索引作为顶点、半边、边和面的描述符,因此它比基于64位指针的版本占用更少的内存。由于索引是连续的,它们可以用作存储属性的向量的索引。
当删除元素时,它们仅被标记为已删除,并且必须调用垃圾收集函数来真正删除它们。
Surface_mesh类可以通过它的类成员函数使用,也可以通过包CGAL和Boost Graph Library中描述的BGL API使用,因为它是MutableFaceGraph和FaceListGraph概念的模型。因此,可以在一个表面网格上应用包三角曲面网格简化、三角曲面网格分割和三角曲面网格变形算法。
1 Usage
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.1 Example
下面的例子展示了如何通过添加2个面来创建一个非常简单的Surface_mesh,以及如何检查一个面是否正确地添加到网格中。
File Surface_mesh/check_orientation.cpp
#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 vertices
vertex_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 Connectivity
曲面网格是一种以边为中心的数据结构,能够保持顶点、边和面的关联信息。每条边由两个方向相反的半边表示。每个半边存储一个对附属面和附属顶点的引用。此外,它存储下一个和前一个半边结构及附带面的引用。对于每个面和每个顶点,存储一个附属的半边。半边结构中不存储相对的半边结构的索引,因为Surface_mesh在内存中连续存储相对的半边结构。
下图演示了允许在曲面网格中导航的函数:Surface_mesh::opposite() , Surface_mesh::next() ,Surface_mesh::prev() , Surface_mesh::target()和Surface_mesh::face()。此外,Surface_mesh::halfedge() 函数允许获取与顶点和面相关的半元。另外,也可以使用包CGAL和Boost Graph Library中定义的同名免费函数。
半边结构附带一个面组成一个循环,根据我们从表面的哪一边看,半边的序列似乎是顺时针或逆时针方向的。在本手册中,当我们谈到遍历的方向时,我们会观察表面,使围绕一个面的半凸面逆时针方向,如图1所示.
3 Ranges and Iterators
Surface_mesh提供范围迭代器来枚举所有顶点、半边、边和面。它提供了返回与 Boost.Range 库兼容的成员函数。
File Surface_mesh/sm_iterators.cpp
#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 vertices
vertex_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));
m.add_face(u,v,w,x);/* face_descriptor f = */
{
std::cout << "all vertices " << std::endl;
// The vertex iterator type is a nested type of the Vertex_range
Mesh::Vertex_range::iterator vb, ve;
Mesh::Vertex_range r = m.vertices();
// The iterators can be accessed through the C++ range API
vb = r.begin();
ve = r.end();
// or the boost Range API
vb = boost::begin(r);
ve = boost::end(r);
// or with boost::tie, as the CGAL range derives from std::pair
for(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 range
for(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_FOREACH
for(vertex_descriptor vd : m.vertices()){
std::cout << vd << std::endl;
}
}
return 0;
}
4 Circulators
在包CGAL和Boost Graph Library中,围绕面和顶点的Circulators作为类模板被提供。
围绕面的Circulators基本上调用Surface_mesh::next(),以便围绕面逆时针地从一个半边移动到另一个半边,当解引用时,可返回半边或半边附带的顶点或相反的面。
CGAL::Halfedge_around_face_circulator<Mesh>
CGAL::Vertex_around_face_circulator<Mesh>
CGAL::Face_around_face_circulator<Mesh>
围绕一条边的目标顶点的Circulators基本上调用Surface_mesh::opposite(Surface_mesh::next()),以便围绕同一目标顶点顺时针从一个半边到另一个半边。
CGAL::Halfedge_around_target_circulator<Mesh>
CGAL::Vertex_around_target_circulator<Mesh>
CGAL::Face_around_target_circulator<Mesh>
所有的Circulators模型BidirectionalCirculator。除此之外,它们还支持转换为bool值,以便更方便地检查空值。
4.1 Example
下面的示例演示如何枚举给围绕定半边目标的顶点。第二个循环表明,每种Circulator类型都有一个等效的迭代器和一个创建范围迭代器的自由函数。
File Surface_mesh/sm_circulators.cpp
#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 vertices
vertex_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 loop
for(vertex_descriptor vd : vertices_around_face(m.halfedge(f), m)){
std::cout << vd << std::endl;
}
return 0;
}
5 Properties
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()),也可以是全部删除他们(Surface_mesh::remove_all_property_maps())。
要清除一个网格,你可以删除一个网格中所有添加的属性映射(Surface_mesh::clear())或保留它们(Surface_mesh::clear_without_removing_property_maps())。注意,在这两种情况下,“v:point”属性映射都将被保留,并且保持对它的引用是安全的。
5.1 Example
这个例子展示了如何使用属性系统中的最常见特性。
File Surface_mesh/sm_properties.cpp
#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 empty
Mesh::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 vertices
name[v0] = "hello";
name[v2] = "world";
{
// You get an existing property, and created will be false
Mesh::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 exist
Mesh::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 function
Mesh::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 again
m.remove_property_map(name);
return 0;
}
6 Borders
半边存储一个面引用,即它的附属面。如果一个半边h没有附属面,即sm.face(h) == Surface_mesh::null_face()成立,则说明这个半边在边界上。同时,一个边的任意一个半边都在边界上,则说明这个边在边界上。同理,如果一个顶点它所属的任意一个半边都在边界上,则说明这个顶点在边界上。
一个顶点只有一个相关联的半边。如果用户注意到那个相关连的半边是一个边界半边的话,这种情况则说明顶点也在边界上。这是就没有必要再针对于顶点使用is_border()函数来查看他的所有所属半边了。为了只检查相关联的halfedges是否在边界上,函数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) 和Surface_mesh::set_vertex_halfedge_to_border_halfedge() 可以分别为单个顶点v,面向h的边界上的所有顶点,以及面向曲面网格的所有顶点设置边界半边。
7 Surface Mesh and the BGL API
Surface_mesh类是一个在Boost Graph Library中被定义的IncidenceGraph概念模型。这使得可以直接在表面网格上应用Dijkstra最短路径或Kruskal最小生成树等算法。
例如,BGL API的类型和自由函数都有相似的类型或成员函数
如果返回的顶点数不考虑被删除的顶点,效果会更好,但这会与底层顶点/边索引映射产生严重的交互。索引映射将不再位于许多算法中假设的[0,num_vertices(g))范围内。
Surface_mesh类也是包CGAL和Boost Graph Library中定义的MutableFaceGraph概念的模型。与之相类似的如HalfedgeGraph等概念,通过引入半边和面,以及围绕面和围绕顶点的半边循环等概念,改进了BGL的图概念。同样,有类似的类型和函数,例如:
包CGAL和Boost Graph Library中描述的BGL API使我们能够编写在曲面网格上运行的几何算法,适用于任何模型的FaceGraph或MutableFaceGraph。也就是说,曲面网格简化、变形或分割算法适用于Surface_mesh和Polyhedron_3。
BGL算法使用属性映射来将信息关联到顶点和边。一个重要的属性是索引,一个0到num_vertices(g)之间的整数,用于图g的顶点。这允许算法创建一个适当大小的向量,以存储每个顶点的信息。例如,如果一个顶点在图遍历过程中已经被访问,则存储一个布尔值。
获取图g的顶点索引属性映射的BGL方法是使用vipm = get(boost::vertex_index, g)和get(vipm, vd)来获取顶点描述符vd的索引,或是用get(vertex_index, g, vd)直接获取顶点索引。
7.1 Example
第一个例子表明,我们可以直接在曲面网格上应用Kruskal的最小生成树算法。
File Surface_mesh/sm_kruskal.cpp
#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 edge
std::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类,这可以归结为一个恒等函数,因为顶点是索引。
File Surface_mesh/sm_bgl.cpp
#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 Surface Mesh I/O
作为FaceGraph的一个模型(参见章节Surface Mesh和BGL API), CGAL::Surface_mesh可以使用多种不同的文件格式进行读写。更多信息请参考CGAL和Boost Graph Library包的I/O函数,以及Polygon Mesh Processing包的I/O函数。
此外,这个包提供了来自CGAL和Boost Graph Library包的特定于surface_mesh的I/O函数重载。这允许直接从/写入内部属性映射,有关更多信息,请参阅I/O函数。
9 Memory Management
内存管理是半自动的。当向结构中添加更多的元素时,内存会增加,但当元素被删除时,内存不会缩小。
当您添加元素并且底层向量的容量耗尽时,该向量将重新分配内存。由于描述符基本上是索引,它们在重新分配后引用相同的元素。
当您删除一个元素时,它只被标记为已删除。在内部,它被放在一个空闲列表中,当你向曲面网格添加元素时,它们从空闲列表中取出,以防它不是空的。
对于所有元素,我们提供了一个函数来获取已使用元素的数量,以及已使用和已删除元素的数量。对于顶点,这些函数分别是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()。垃圾收集还压缩与表面网格相关的属性。
但是请注意,通过垃圾收集,元素将获得新的索引。如果你保留顶点描述符,它们很可能不再指向正确的顶点。
9.1 Example
File Surface_mesh/sm_memory.cpp
#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> Mesh;
typedef Mesh::Vertex_index vertex_descriptor;
int main()
{
Mesh m;
Mesh::Vertex_index u;
for(unsigned int i=0; i < 5; ++i){
Mesh::Vertex_index v = m.add_vertex(K::Point_3(0,0,i+1));
if(i==2) u=v;
}
m.remove_vertex(u);
std::cout << "After insertion of 5 vertices and removal of the 3. vertex\n"
<< "# vertices / # vertices + # removed vertices = "
<< m.number_of_vertices()
<< " / " << m.number_of_vertices() + m.number_of_removed_vertices() << std::endl;
std::cout << "Iterate over vertices\n";
{
for(vertex_descriptor vd : m.vertices()){
std::cout << m.point(vd) << std::endl;
}
}
// The status of being used or removed is stored in a property map
Mesh::Property_map<Mesh::Vertex_index,bool> removed
= m.property_map<Mesh::Vertex_index,bool>("v:removed").first;
std::cout << "\nIterate over vertices and deleted vertices\n"
<< "# vertices / # vertices + # removed vertices = "
<< 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();
for( ; i < end; ++i) {
vertex_descriptor 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 << "\nAfter garbage collection\n"
<< "# vertices / # vertices + # removed vertices = "
<< 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();
for( ; i < end; ++i) {
vertex_descriptor vh(i);
std::cout << m.point(vh) << ((m.is_removed(vh)) ? " R\n" : "\n");
}
}
return 0;
}
10 Draw a Surface Mesh
一个表面网格可以通过调用CGAL::draw()来可视化,如下面的例子所示。这个函数打开一个新窗口,显示给定的表面网格。对这个函数的调用是阻塞的,也就是说,只要用户关闭窗口,程序就会继续。
File Surface_mesh/draw_surface_mesh.cpp
#include <CGAL/Simple_cartesian.h>
#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。