官方文档链接:CGAL 5.4.2 - Surface Mesh: User Manual
Surface_mesh 类是半边数据结构的实现,可用于表示多面体表面。
相较于 Halfedge Data Structures
和 3D Polyhedral Surface
,其具有以下特点:
Surface_mesh类提供了如下四个类来表示网格的基本元素:
Surface_mesh::Vertex_index
Surface_mesh::Halfedge_index
Surface_mesh::Face_index
Surface_mesh::Edge_index
由于Surface_mesh是基于索引值构造的,所以没有访问连接性或属性的成员函数。
#include
#include
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;
}
在这个例子中,通过添加两个面创建一个简单的表面网格。可以看到,在使用add_face()
添加face的时候,若其返回的Face_index值为Surface_mesh::null_face()
则说明添加面的操作在拓扑上无效,添加失败。
在一个surface_mesh中可以使用如下函数进行基于连接关系的查询:
Surface_mesh::opposite()
, Surface_mesh::next()
, Surface_mesh::prev()
, Surface_mesh::target()
, and Surface_mesh::face()
。 此外,函数Surface_mesh::halfedge()
能够获取与顶点和面相关联的半边。
surface_mesh提供了迭代器用以枚举所有的顶点、半边、边和面。
#include
#include
#include
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 << "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;
}
这是一个遍历表面网格中所有顶点索引的例子。其中关键迭代器类型Mesh::Vertex_range::iterator
,关键范围类型Mesh::Vertex_range
。通过后者得到前者,进而对范围中元素进行迭代遍历。
围绕面的循环器:
CGAL::Halfedge_around_face_circulator
CGAL::Vertex_around_face_circulator
CGAL::Face_around_face_circulator
围绕目标顶点的循环器:
CGAL::Halfedge_around_target_circulator
CGAL::Vertex_around_target_circulator
CGAL::Face_around_target_circulator
所有循环器围绕方向为逆时针。
#include
#include
#include
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;
}
这个例子中展示了Vertex_around_target_circulator
的使用方法,其中也展示了使用迭代器达到相同的效果。
surface_mesh提供了可以在运行时指定顶点、半边、边和面的属性的机制。每个属性通过一个字符串和它的键类型进行标识。
默认情况下,只有一个属性"v:point"。当通过 Surface_mesh::add_vertex()
向数据结构添加新点时,必须提供此属性的值。 可以使用 Surface_mesh::points()
或 Surface_mesh::point(Surface_mesh::Vertex_index v)
直接访问该属性。
当一个元素被移除,它并不会立刻被真正意义上的删除,而是被标记为“已移除”,只有调用Surface_mesh::collect_garbage()
才能真正地移除该元素的所有相关属性。
#include
#include
#include
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;
}
上面例子中依次实现了property的创建、查询、删除等操作。
一个半边存储了其入射面的引用,如果它没有入射面,则表示该半边在边界上,即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() //将单表面网格上所有顶点的关联半边设置为边界半边
#include
#include
#include
#include
#include
#include
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;
}