【CGAL_网格】Surface_mesh
官方文檔鏈接:CGAL 5.4.2 - Surface Mesh: User Manual
0 概述
Surface_mesh 類是半邊數據結構的實現,可用于表示多面體表面。
相較于 Halfedge Data Structures 和 3D 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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: R语言可视化散点图、ggrepel包ge
- 下一篇: 【STM32】HAL库-电源控制(低功耗