1限定三角化
1.1 定义
Delaunay三角剖分:
Delaunay三角剖分是 Boris Delaunay 于1934 年提出的。它具有一些优秀的性质,例如说它最大化三角剖分T中所有三角形的最小角,以避免剖分出扁平的三角形。
Delaunay边:e是E中满足一下条件的边:存在一个圆经过其端点a、b,圆内(不是圆上)不含有点集V中的任何其他点,则称e为Delaunay边。
Delaunay三角剖分:如果V的一个三角剖分T只包含Delaunay边,那么该三角剖分称为Delaunay三角剖分。
约束性Delaunay三角剖分:尽可能的进行Delaunay三角剖分称为约束Delaunay三角剖分。
Delaunay边:如何一个边内接于一个空圆,则为一个Delaunay边。
Gabriel边:如果一个边,若以此边为直径的圆是一个空圆(圆内不含其他点)则称为Gabriel边。
另外,一个约束Delaunay三角剖分可以称为限定Delaunay三角剖分当且它的每个约束边是一个Delaunay边。一个限定Delaunay三角剖分事实上就是一个Delaunay三角剖分,唯一的不同的是限定三角剖分的一些边被标识为约束边。一个约束Delaunay三角剖分可以称为限定性Gabriel三角剖分当且它的每条约束边是一个Delaunay边。Gabriel三角剖分的特点强于Delaunay的属性,每个Gabriel边是一个Delaunay边,所以可以看出限定性Gabriel三角剖分也可以称为Delaunay三角剖分。
任何约束性Delaunay三角剖分都可以细化为一个限定性Delaunay三角剖分或者通过添加顶点(Steiner点)来细化一个Gabriel三角剖分。
1.2创建限定三角剖分
约束性Delaunay三角剖分可以用以下两个全局函数细化为限定三角剖分。它们分别是:
template<class CDT> void make_conforming_Delaunay_2 (CDT& t)
template<class CDT> void make_conforming_Gabriel_2 (CDT& t)
在上面两个函数中,参数CDT必须通过约束性Delaynay三角剖分类进行实例化。(关于三角剖分部分可以参考2D三角剖分部分,ps:我会陆续翻译相应的部分O(∩_∩)O)
关于用于实例化约束性Delaunay三角剖分CDT的几何特性参数geometric traits必须是 ConformingDelaunayTriangulationTraits_2
从上面的两个函数可以看出,变量t是通过引用的方式传递参数的。它可以使约束三角剖分网格细分为限定三角剖分网格,或者通过添加顶点细化为Gabriel三角剖分。建议用户备份原始数据,特别当源数据还要进行其他计算的时候。
算法函数make_conforming_Delaunay_2()和 make_conforming_Gabriel_2()
如何被同时用于同一个三角剖分,那么其内部的数据结构将会计算两次,为了避免数据被构造两次,高级的用户会使用类Triangulation_conformer_2<CDT>来细化一个约束Delaunay三角剖分为限定Delaunay三角剖分然后再细化为一个限定Gabriel三角剖分,另外为了控制细分算法的,这个类提供了一个独立的函数使得用户能在某一时间插入一个Steiner点。
1.3把约束Delaunay三角剖分细化为限定Delaunay三角剖分和一个限定Gabriel三角剖分
例子中插入了一些线段到约束Delaunay三角剖分中,使它细化为一个限定Delaunay三角剖分,然后变成Gabriel三角剖分。在每一个步骤中,三角剖分的顶点数都会被打印显示出来。
[cpp] view plaincopy
- #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
- #include <CGAL/Constrained_Delaunay_triangulation_2.h>
- #include <CGAL/Triangulation_conformer_2.h>
- #include <iostream>
- typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
- typedef CGAL::Constrained_Delaunay_triangulation_2<K> CDT;
- typedef CDT::Point Point;
- typedef CDT::Vertex_handle Vertex_handle;
- int main()
- {
- CDT cdt;
- // construct a constrained triangulation
- Vertex_handle
- va = cdt.insert(Point( 5., 5.)),
- vb = cdt.insert(Point(-5., 5.)),
- vc = cdt.insert(Point( 4., 3.)),
- vd = cdt.insert(Point( 5.,-5.)),
- ve = cdt.insert(Point( 6., 6.)),
- vf = cdt.insert(Point(-6., 6.)),
- vg = cdt.insert(Point(-6.,-6.)),
- vh = cdt.insert(Point( 6.,-6.));
- cdt.insert_constraint(va,vb);
- cdt.insert_constraint(vb,vc);
- cdt.insert_constraint(vc,vd);
- cdt.insert_constraint(vd,va);
- cdt.insert_constraint(ve,vf);
- cdt.insert_constraint(vf,vg);
- cdt.insert_constraint(vg,vh);
- cdt.insert_constraint(vh,ve);
- std::cout << "Number of vertices before: "
- << cdt.number_of_vertices() << std::endl;
- // make it conforming Delaunay
- CGAL::make_conforming_Delaunay_2(cdt);
- std::cout << "Number of vertices after make_conforming_Delaunay_2: "
- << cdt.number_of_vertices() << std::endl;
- // then make it conforming Gabriel
- CGAL::make_conforming_Gabriel_2(cdt);
- std::cout << "Number of vertices after make_conforming_Gabriel_2: "
- << cdt.number_of_vertices() << std::endl;
- }
上面三个图分别是最初的三角剖分,中间的是Delaunay三角剖分,右边的是限定Gabriel三角剖分
上图是限定Delaunay三角剖分
2.网格生成
2.1定义:
网格生成:将一个给定区域划分成一个个简单的,形状和大小都满足一定标准的几何形状。
域:表示用户想要进行网格生成的区域,这个区域必须是一个有边界的。另外这个域用一个平面直线图来表示。它其实是一个一个线段集合,每个线段之间要么不邻接要么有个公共端点。这种线段是否为约束的是通过网格中的边的联合来描述的。这种平面直线图(planar straight line graph)也可以包含独立的点,而这些点则是通过网格的顶点表现出来。
PSLG的线段要么是边界要么内部的约束边,另外线段必须覆盖域的边界。PLSG把平面划分成几个小的组成部分,缺省情况下。下图是没有使用种子点的域,右边是它的一种可能得网格生成。
用户可以通过添加一些种子节点来重写缺省的域。可以用种子节点标记子部分让这部分生成网格,也可以标记几部分不进行网格化(所谓的洞)
一下图示另外一个域(都是通过统一的PLSG来定义的)两个种子节点用于定义洞相应的网格中这两个洞没有进行网格生成,而只是进行了相应的三角化。
2.2形状和几何大小
前面说过每个网格的几何形状必须满足一定的标准,那么详细说下这些标准。
三角化的每个小三角形状必须满足:有下界B=r/min({三角形三个边});这样这个角对应三角形的最小角(三角形的性质:大边对大角,小边对小角)arcsin(1/2B)=arcsin(el/2r)(el=min{三角形三条边}).那么此时三角形对应的上界为pi-2*arcsin(1/2B).对应最大角。当然上界和下界不可能同时出现,要不然就满足三角形结构了。所以说到这,当B=根号2的时候算法就可以得到满足。这个值对应的三角形角度为20.7度。
大小标准:一般来说,三角形大小基本都是趋向于比较小的三角形的。大小标准可以用三角形的最长边作为一个上界,或者使用三角形的外接圆作为上界。(因为三角形都比较喜欢趋向于小的三角形,所以一般三角大小标准都只说三角形最大为多大)。域上的三角形的大小各不相同。在CGAL的程序中形状和大小标准是通过一个关于三角形大小和形状的标准的类的对象作为参数传递给网格生成函数的。
2.3网格生成算法
网格问题的输入是一些PSLG和一些种子节点,这些输入用来描述被网格化的域。此外还要输入三角单元的形状和大小标准。在本部分算法的实现过程中,首先用输入的数据进行约束Delaunay三角化,然后使用Delaunay细化方法来生成网格。这个方法是通过不断插入新的点(这个店应该尽量离其他点远),直到设定的三角标准得到满足,算法停止。
如果所有的关联边之间的夹角都大于60度,并且下界B=r/(min)edge大于根号2,那么这个算法就可以保证在满足设定规范的情况终止。如果一些输入角小于60度的话,那么这个算法运算结束后,有些三角形是独立于其他网格的,这种独立的三角形一般在小于60度角附近生成。实际情况下,这样的情况是无法避免的。此外,有些输入的域不能细分为网格,如果输入了一些小角。值得注意的是,如果输入的域是以凸多边形,在没有输入小角的情况下,都可以生成相应的三角网格的。
2.4创建网格
网格是通过利用约束Delaunay三角化的结果来进行计算获得的。通过一下的函数:
template<class CDT, class Criteria> void refine_Delaunay_mesh_2 (CDT &t, const Criteria& criteria)
class CDT是一个constrained Delaunay triangulation 类的实例、为了重写相应的域,函数有多于两个参数用来定义种子节点。CDT的几何特征类必须是DelaunayMeshTraits_2模板的具体。另外模板参数criteria必须是MeshingCriteria_2,这个参数定义生成的网格必须满足的标准。CGAL为这个标准提供了两个模板Delaunay_mesh_criteria_2:定义三角网格单元的最小角。Delaunay_mesh_size_criteria_2用来定义三角网格单元的大小的(即最大边大小)。
如果函数refine_Delaunay_mesh_2()被多次调用,而且每次调用都设置不同的标准。那么每次调用的时候,算法都会重建内部的数据结构,那么为了避免在每次调用的时候内部数据结构的重构,高级用户会使用 Delaunay_mesher_2<CDT>。这个类提供步骤性的操作函数。这些函数一次只插入一个点。
Delaunay_mesher_2<CDT>
对象是通过引用CDT来进行构造的。这个类有些成员函数用来生成网格的域和将CDT进行网格化,一下几个例子给出了具体使用的方法。注意CDT在Delaunay_mesher_2对象的声明周期内,不要在外部对其进行修改。
2.5使用全局函数的例子
下面的例子插入一些线段到约束三角化域中,然后通过使用全局函数 refine_Delaunay_mesh_2()进行网格的生成。生成网格的标准是默认的。即
Delaunay_mesh_criteria_2<K>的默认参数。本例子中没有种子顶点。这意味着三角网格覆盖整个域。除了那些没有边界的部分。
[cpp] view plaincopy
- #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
- #include <CGAL/Constrained_Delaunay_triangulation_2.h>
- #include <CGAL/Delaunay_mesher_2.h>
- #include <CGAL/Delaunay_mesh_face_base_2.h>
- #include <CGAL/Delaunay_mesh_size_criteria_2.h>
- #include <iostream>
- typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
- typedef CGAL::Triangulation_vertex_base_2<K> Vb;
- typedef CGAL::Delaunay_mesh_face_base_2<K> Fb;
- typedef CGAL::Triangulation_data_structure_2<Vb, Fb> Tds;
- typedef CGAL::Constrained_Delaunay_triangulation_2<K, Tds> CDT;
- typedef CGAL::Delaunay_mesh_size_criteria_2<CDT> Criteria;
- typedef CDT::Vertex_handle Vertex_handle;
- typedef CDT::Point Point;
- int main()
- {
- CDT cdt;
- Vertex_handle va = cdt.insert(Point(-4,0));
- Vertex_handle vb = cdt.insert(Point(0,-1));
- Vertex_handle vc = cdt.insert(Point(4,0));
- Vertex_handle vd = cdt.insert(Point(0,1));
- cdt.insert(Point(2, 0.6));
- cdt.insert_constraint(va, vb);
- cdt.insert_constraint(vb, vc);
- cdt.insert_constraint(vc, vd);
- cdt.insert_constraint(vd, va);
- std::cout << "Number of vertices: " << cdt.number_of_vertices() << std::endl;
- std::cout << "Meshing the triangulation..." << std::endl;
- CGAL::refine_Delaunay_mesh_2(cdt, Criteria(0.125, 0.5));
- std::cout << "Number of vertices: " << cdt.number_of_vertices() << std::endl;
- }
2.6使用类
Delaunay_mesher_2<CDT>
下面的例子中通过调用refine_mesh()这个成员好函数两次,在两次的函数调用之间修改了标准参数。如果使用refine_Delaunay_mesh()两次来进行同样的操作的话,显然会变的更低效些,因为这个方法会重构内部数据结构两次。如果你调用这个函数两次的话。
[cpp] view plaincopy
- #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
- #include <CGAL/Constrained_Delaunay_triangulation_2.h>
- #include <CGAL/Delaunay_mesher_2.h>
- #include <CGAL/Delaunay_mesh_face_base_2.h>
- #include <CGAL/Delaunay_mesh_size_criteria_2.h>
- #include <iostream>
- typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
- typedef CGAL::Triangulation_vertex_base_2<K> Vb;
- typedef CGAL::Delaunay_mesh_face_base_2<K> Fb;
- typedef CGAL::Triangulation_data_structure_2<Vb, Fb> Tds;
- typedef CGAL::Constrained_Delaunay_triangulation_2<K, Tds> CDT;
- typedef CGAL::Delaunay_mesh_size_criteria_2<CDT> Criteria;
- typedef CGAL::Delaunay_mesher_2<CDT, Criteria> Mesher;
- typedef CDT::Vertex_handle Vertex_handle;
- typedef CDT::Point Point;
- int main()
- {
- CDT cdt;
- Vertex_handle va = cdt.insert(Point(-4,0));
- Vertex_handle vb = cdt.insert(Point(0,-1));
- Vertex_handle vc = cdt.insert(Point(4,0));
- Vertex_handle vd = cdt.insert(Point(0,1));
- cdt.insert(Point(2, 0.6));
- cdt.insert_constraint(va, vb);
- cdt.insert_constraint(vb, vc);
- cdt.insert_constraint(vc, vd);
- cdt.insert_constraint(vd, va);
- std::cout << "Number of vertices: " << cdt.number_of_vertices() << std::endl;
- std::cout << "Meshing the triangulation with default criterias..."
- << std::endl;
- Mesher mesher(cdt);
- mesher.refine_mesh();
- std::cout << "Number of vertices: " << cdt.number_of_vertices() << std::endl;
- std::cout << "Meshing with new criterias..." << std::endl;
- // 0.125 is the default shape bound. It corresponds to abound 20.6 degree.
- // 0.5 is the upper bound on the length of the longuest edge.
- // See reference manual for Delaunay_mesh_size_traits_2<K>.
- mesher.set_criteria(Criteria(0.125, 0.5));
- mesher.refine_mesh();
- std::cout << "Number of vertices: " << cdt.number_of_vertices() << std::endl;
- }
2.7加入种子节点
本例子中使用全局函数
refine_Delaunay_mesh_2(),但是使用一个种子点来定义一个域。标准使用标准类的默认参数。
[cpp] view plaincopy
- #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
- #include <CGAL/Constrained_Delaunay_triangulation_2.h>
- #include <CGAL/Delaunay_mesher_2.h>
- #include <CGAL/Delaunay_mesh_face_base_2.h>
- #include <CGAL/Delaunay_mesh_size_criteria_2.h>
- #include <iostream>
- typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
- typedef CGAL::Triangulation_vertex_base_2<K> Vb;
- typedef CGAL::Delaunay_mesh_face_base_2<K> Fb;
- typedef CGAL::Triangulation_data_structure_2<Vb, Fb> Tds;
- typedef CGAL::Constrained_Delaunay_triangulation_2<K, Tds> CDT;
- typedef CGAL::Delaunay_mesh_size_criteria_2<CDT> Criteria;
- typedef CDT::Vertex_handle Vertex_handle;
- typedef CDT::Point Point;
- int main()
- {
- CDT cdt;
- Vertex_handle va = cdt.insert(Point(2,0));
- Vertex_handle vb = cdt.insert(Point(0,2));
- Vertex_handle vc = cdt.insert(Point(-2,0));
- Vertex_handle vd = cdt.insert(Point(0,-2));
- cdt.insert_constraint(va, vb);
- cdt.insert_constraint(vb, vc);
- cdt.insert_constraint(vc, vd);
- cdt.insert_constraint(vd, va);
- va = cdt.insert(Point(3,3));
- vb = cdt.insert(Point(-3,3));
- vc = cdt.insert(Point(-3,-3));
- vd = cdt.insert(Point(3,0-3));
- cdt.insert_constraint(va, vb);
- cdt.insert_constraint(vb, vc);
- cdt.insert_constraint(vc, vd);
- cdt.insert_constraint(vd, va);
- std::list<Point> list_of_seeds;
- list_of_seeds.push_back(Point(0, 0));
- std::cout << "Number of vertices: " << cdt.number_of_vertices() << std::endl;
- std::cout << "Meshing the domain..." << std::endl;
- CGAL::refine_Delaunay_mesh_2(cdt, list_of_seeds.begin(), list_of_seeds.end(),
- Criteria());
- std::cout << "Number of vertices: " << cdt.number_of_vertices() << std::endl;
- std::cout << "Number of finite faces: " << cdt.number_of_faces() << std::endl;
- int mesh_faces_counter = 0;
- for(CDT::Finite_faces_iterator fit = cdt.finite_faces_begin();
- fit != cdt.finite_faces_end(); ++fit)
- {
- if(fit->is_in_domain()) ++mesh_faces_counter;
- }
- std::cout << "Number of faces in the mesh domain: " << mesh_faces_counter << std::endl;
- }
PS:mesh_2翻译就这么多了,说实话,国内使用CGAL的人太少了,网上百度的时候几乎没有什么资料。由于本人现在在学习这个,所以边学习边翻译相关的内容,如果有时间的话我会深入研究算法的具体的实现过程,由于CGAL几乎是用模板来写的,所以看的时候估计有些压力。这也是CGAL.cpp文件那么少的原因。模板都是写在.h文件中的。所以不要误认为CGAL不开源哦!所以在此我也倡导有兴趣的朋友和我一起翻译CGAL的各个部分。有想法的可以在评论中留言或者发邮件到[email protected]
Link : http://blog.csdn.net/jacayang/article/details/24142935