CGAL:Surface mesh

User Manual 用户手册

该类Surface_mesh是半边数据结构的实现,可用于表示多面体表面。它是CGAL包Halfedge Data Structure3D Polyhedral Surface的替代方案。主要区别在于它是基于索引的,而不是基于指针的。此外,将信息添加到顶点,半边,边和面的机制要简单得多,并且可以在运行时(runtime)完成,而不必在编译时完成。

因为数据结构使用整数索引作为顶点,半边,边和面的描述符,所以它的内存占用空间比基于64位指针的版本要低。由于索引是连续的,因此它们可以用作存储属性的向量的索引。

当元素被删除时,它们仅被标记为已删除,并且必须调用垃圾回收函数才能真正删除它们。

1 用法(usage)

主类Surface_mesh提供了四个嵌套类,它们表示半边数据结构的基本元素:

这些class是基本图元个体的封装,这样做的目的是为了保证类型安全。它们默认是可构造的,因此可能会产生一些无效的元素。新的元素可以通过一系列不包括连通性的低级函数向surface mesh这个类添加或者删除。一个例外是Surface_mesh::add_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是基于索引的(index based),Vertex_indexHalfedge_indexEdge_indexFace_index没有成员函数来访问连接性或属性。从Surface_mesh中创建的函数必须有获取这些信息的功能。

2 连通性(connectivity)

surface mesh是一种能够保持顶点,边和面的入射信息的,以边为核心的数据结构。
每个边由两个方向相反的半边表示,每个半边均存储对入射面和入射顶点的引用,此外,它还存储对入射面的下一个和上一个入射半边的引用。
其实说白了,surface mesh有一个半边结构的表达方式:对于一个半边h它有以下这样几个导航的表达方式:Surface_mesh::opposite()Surface_mesh::next()Surface_mesh::prev()Surface_mesh::target(),和Surface_mesh::face()和半边数据结构是差不多的。

3 范围和迭代器(range and iterators)

Surface_mesh提供迭代器范围以枚举所有顶点,半边,边和面。它提供的成员函数返回与Boost.Range库兼容的元素范围。

来自官方文档的示例,演示如何从范围中获取迭代器类型,获得begin和end迭代器的替代方法以及基于范围(range for c++11)的循环的替代方法。

#include <vector>

#include <boost/foreach.hpp>

#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));

/* 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

BOOST_FOREACH(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

#ifndef CGAL_CFG_NO_CPP0X_RANGE_BASED_FOR

for(vertex_descriptor vd : m.vertices()){

std::cout << vd << std::endl;

}

#endif

}

return 0;

}

4 循环器(circulators)

CGAL和Boost Graph Library中提供了围绕面和顶点的循环器作为模板类。

围绕面的循环器基本上会进行调用Surface_mesh::next(),以从该面的逆时针方向从半边变为半边,并且取消引用时返回半边或入射顶点或相对面。

围绕目标边的顶点的循环器基本上会进行调用Surface_mesh::opposite(Surface_mesh::next()),以使围绕同一目标点的顺时针方向从半边变为半边。

example:

下面的示例演示如何枚举给定半边目标周围的顶点。第二个循环表明,每种循环器类型都带有一个等效的迭代器和一个用于创建迭代器范围的自由函数

#include <vector>
#include <boost/foreach.hpp>
#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));
  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
  BOOST_FOREACH(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”。

example:

#include <string>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <boost/foreach.hpp>
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();
  BOOST_FOREACH( vertex_descriptor vd, m.vertices()) { 
    std::cout << name[vd] << " @ " << location[vd] << std::endl;
  }
  std::vector<std::string> props = m.properties<vertex_descriptor>();
  BOOST_FOREACH(std::string p, props){
    std::cout << p << std::endl;
  }
  
  // delete the string property again
  m.remove_property_map(name);
  return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容