• 【CGAL_网格】Surface_mesh


    官方文档链接:CGAL 5.4.2 - Surface Mesh: User Manual

    0 概述

    Surface_mesh 类是半边数据结构的实现,可用于表示多面体表面。

    相较于 Halfedge Data Structures3D 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 
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这个例子中,通过添加两个面创建一个简单的表面网格。可以看到,在使用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() 能够获取与顶点和面相关联的半边。

    image.png

    范围和迭代器

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    这是一个遍历表面网格中所有顶点索引的例子。其中关键迭代器类型Mesh::Vertex_range::iterator,关键范围类型Mesh::Vertex_range。通过后者得到前者,进而对范围中元素进行迭代遍历。

    4 循环器

    围绕面的循环器:

    • 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    这个例子中展示了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 
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    上面例子中依次实现了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() //将单表面网格上所有顶点的关联半边设置为边界半边
      
      • 1
      • 2
      • 3

    7 相关API

    image.png

    image.png

    7.1 栗子

    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
  • 相关阅读:
    IDEA 集成 GitHub
    FL Studio All Plugins Edition2024中文完整版Win/Mac
    设计模式---抽象工厂模式
    C#的基本知识(1)
    Httpd(一)
    使用selenium调用firefox提示Profile Missing的问题解决
    qt学习之旅--MinGW编译FFmpeg(32bit)
    MySQL数据库期末考试试题及参考答案(01)
    三分钟学会Sqoop安装与部署
    【Web前端二级导航栏】
  • 原文地址:https://blog.csdn.net/qq_39784672/article/details/126472273