在计算几何、有限元分析(FEA)以及地形建模中,网格生成(Mesh Generation) 是一个至关重要的步骤。如何将一个任意形状的多边形区域离散化为高质量的三角形网格?
本文将以 CGAL (Computational Geometry Algorithms Library) 为例,通过一段标准代码,深入剖析如何生成受约束 Delaunay 三角网格 (Constrained Delaunay Triangulation, CDT),并重点解读控制网格质量的核心参数。
1. 什么是受约束 Delaunay 三角剖分 (CDT)?
普通的 Delaunay 三角剖分虽然能保证三角形尽可能"圆润"(避免极细长的三角形),但它无法强制保留我们指定的边界线段。
CDT (Constrained Delaunay Triangulation) 解决了这个问题:它允许我们定义"约束边(Constraints)"。这些边必须出现在最终的网格中,哪怕它们破坏了 Delaunay 性质。在此基础上,CGAL 的网格生成算法(Mesher)会通过插入额外的点(Steiner Points)来优化网格,使其既满足边界约束,又满足形状和大小的要求。
2. 核心代码实现
下面是一个完整的 C++ 示例,展示了如何定义一个"风筝形"区域并对其进行网格细化。
cpp
#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>
// --- 1. 类型定义与内核配置 ---
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Triangulation_vertex_base_2<K> Vb;
// 关键点:必须使用 Delaunay_mesh_face_base_2 以支持细化算法
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;
// --- 2. 定义几何边界 ---
// 定义一个菱形/风筝形
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));
// --- 3. 插入约束边 ---
cdt.insert_constraint(va, vb);
cdt.insert_constraint(vb, vc);
cdt.insert_constraint(vc, vd);
cdt.insert_constraint(vd, va);
std::cout << "初始顶点数: " << cdt.number_of_vertices() << std::endl;
// --- 4. 执行网格细化 ---
std::cout << "正在生成网格..." << std::endl;
// 核心函数:设置细化标准
CGAL::refine_Delaunay_mesh_2(cdt, CGAL::parameters::criteria(Criteria(0.125, 0.5)));
std::cout << "细化后顶点数: " << cdt.number_of_vertices() << std::endl;
return 0;
}
3. 避坑指南:类型定义
很多初学者在使用 CGAL 网格生成时会遇到编译错误或运行时崩溃,原因通常出在 Face_base 的选择上。
请注意这行代码:
cpp
typedef CGAL::Delaunay_mesh_face_base_2<K> Fb;
普通的三角剖分使用 Triangulation_face_base_2 即可,但如果你要使用 refine_Delaunay_mesh_2 算法,必须 使用 Delaunay_mesh_face_base_2。因为细化算法需要在每个三角形对象中存储额外的状态信息(如三角形是否在约束区域内、是否满足尺寸要求等)。
4. 深度解析:Criteria 参数的魔法
代码中最关键的一行是:
cpp
CGAL::refine_Delaunay_mesh_2(cdt, CGAL::parameters::criteria(Criteria(0.125, 0.5)));
这里的 Criteria(0.125, 0.5) 控制着最终网格的质量和密度。很多开发者不知道如何调整这两个数字。
参数一:Shape Bound (形状标准) = 0.125
这个参数控制三角形的形状质量 ,具体来说是限制三角形的最小角。
- 含义:它定义了一个常数 ,使得 。
- 为何是 0.125? 这是 CGAL 的推荐值。当 时,算法保证生成的网格中所有三角形的最小角 。
- 建议 :保持 0.125 不变。这是该算法能保证数学上收敛(终止)的最严格限制。如果设得更小(试图追求接近 60° 的完美三角形),算法可能会陷入死循环。如果你不关心形状只关心大小,可以设为 0。
参数二:Size Bound (尺寸标准) = 0.5
这个参数控制三角形的最大大小(网格密度)。
-
含义:网格中任意三角形的任意边长都不能超过这个值。
-
影响:
-
值越小:网格越密,顶点越多,计算量越大。
-
值越大:网格越稀疏。
-
设为 0:不限制大小,只为了满足形状要求或边界约束而加点。
-
示例:在上面的代码中,几何体跨度为 8 (-4 到 4),我们将尺寸限制为 0.5。这意味着原来的大三角形会被切碎,算法会自动插入大量的 Steiner Points 直到所有边长都小于 0.5。
5. 进阶技巧:非均匀网格
如果你希望在某些区域网格密(如应力集中区),某些区域网格稀,Criteria 的第二个参数其实可以接受一个函数对象 (Functor) 而不仅仅是 double。
你可以编写一个类,根据坐标 返回不同的尺寸限制值,从而实现自适应网格生成。
cpp
// 伪代码思路
struct Variable_sizing {
double operator()(const Point& p) const {
if (is_important_region(p)) return 0.1; // 核心区域加密
return 1.0; // 边缘区域稀疏
}
};
6. 总结
使用 CGAL 生成二维网格的流程可以概括为:
- 配置 Kernel 和 Traits (别忘了
Delaunay_mesh_face_base_2)。 - 构建 CDT,插入点和约束边。
- 设置 Criteria :记住
0.125是形状的黄金标准,调节第二个参数来控制网格密度。 - **调用
refine_Delaunay_mesh_2**一键生成。