第4章 高级几何构建:内置函数、孔洞、注释与颜色
4.1 学习目标
- 掌握 Gmsh 内置曲线函数(Circle Arc、Spline、B-Spline、Bezier、Ellipse)的 C++ API 用法
- 理解
addPlaneSurface中多曲线环(Curve Loop)的机制,正确构建带孔洞的面 - 学会设置实体颜色、名称与可见性,增强几何模型的可读性
- 掌握注释(Annotation)与视图(View)的基本使用方法,用于前处理中标记边界条件位置
4.2 核心概念说明
4.2.1 内置曲线函数概览
在前两章中我们只使用了 addLine 来创建直线段。Gmsh 提供了丰富的内置曲线函数,使得复杂轮廓的定义无需依赖外部 CAD 内核即可完成:
| 曲线类型 | C++ API | 参数说明 |
|---|---|---|
| 圆弧 | addCircleArc(start, center, end, tag) |
三点定义,弧度必须 < Pi(OpenCASCADE 内核无此限制) |
| 椭圆弧 | addEllipseArc(start, center, majorAxisPoint, end, tag) |
通过起点、中心、长轴点和终点定义 |
| 完整椭圆 | addEllipse(start, center, majorAxisPoint, end, tag) |
与椭圆弧参数类似,生成闭合椭圆曲线 |
| Catmull-Rom 样条 | addSpline({pointTags}, tag) |
通过所有给定点,自动计算切线 |
| B 样条 | addBSpline({pointTags}, tag) |
控制点不一定在曲线上,提供更平滑的曲线 |
| 贝塞尔曲线 | addBezier({pointTags}, tag) |
首尾点为端点,中间点为控制点 |
| 复合 Catmull-Rom 样条 | addCompoundSpline({pointTags}, ...) |
分段样条,每段使用各自的控制点集 |
| 复合 B 样条 | addCompoundBSpline({pointTags}, ...) |
分段 B 样条 |
这些函数均位于 gmsh::model::geo 命名空间下。为简化代码,通常使用命名空间别名:
namespace factory = gmsh::model::geo;
之后即可通过 factory::addCircleArc(...) 等形式调用。
4.2.2 带孔洞的面(Surface with Holes)
在 Gmsh 中,一个平面面(Plane Surface)由一个或多个曲线环(Curve Loop)定义:
- 第一条曲线环是该面的外边界(exterior boundary)
- 后续曲线环是该面的内孔(hole boundaries)
一般规律:如果一个面有 N 个孔,就需要 N+1 条曲线环。
// 外边界曲线环 tag=1
factory::addCurveLoop({...}, 1);
// 内孔曲线环 tag=2
factory::addCurveLoop({...}, 2);
// 定义带孔面:第一个参数是外环,后续是内孔环
factory::addPlaneSurface({1, 2}, 100);
在定义内孔曲线环时,曲线的方向符号约定同样适用:正值表示沿曲线定义方向,负值表示反向。
4.2.3 实体属性与注释
Gmsh 允许为几何实体设置颜色、名称等属性,这对前处理中标记边界条件位置极为有用:
- 实体颜色:
gmsh::model::setColor({{dim, tag}}, r, g, b),其中r, g, b取值范围 0-255 - 实体命名:
gmsh::model::setEntityName(dim, tag, "name"),在 GUI 中显示可读标签 - 实体可见性:
gmsh::model::setVisibility({{dim, tag}}, visible),控制实体显示/隐藏
注释(Annotation)通过后处理视图(View)实现。视图可以包含文本字符串、图片等,支持模型坐标和窗口坐标两种定位方式。
4.3 C++ 代码逐段讲解
示例一:带圆孔的方板
这是一个入门级示例,展示如何使用 addCircleArc 构建圆孔,并定义带孔的面。
4.3.1 初始化与参数定义
#include <gmsh.h>
int main(int argc, char **argv) {
gmsh::initialize(argc, argv);
gmsh::model::add("plate_with_hole");
namespace factory = gmsh::model::geo;
double L = 1.0; // 方板边长
double R = 0.2; // 圆孔半径
double lc = 0.05; // 网格特征长度
namespace factory = gmsh::model::geo;创建命名空间别名,显著缩短后续 API 调用长度- 定义几何参数时尽量使用变量,方便后续修改
4.3.2 创建方板外边界
// 方板四个角点
factory::addPoint(0, 0, 0, lc, 1); // 左下
factory::addPoint(L, 0, 0, lc, 2); // 右下
factory::addPoint(L, L, 0, lc, 3); // 右上
factory::addPoint(0, L, 0, lc, 4); // 左上
// 四条直线边界
factory::addLine(1, 2, 1);
factory::addLine(2, 3, 2);
factory::addLine(3, 4, 3);
factory::addLine(4, 1, 4);
4.3.3 使用 Circle Arc 定义圆孔
三点圆弧由 addCircleArc(startPoint, centerPoint, endPoint, tag) 定义。这里我们用一个完整的圆作为孔,需要 4 段圆弧(每段 90 度)。
double cx = L / 2, cy = L / 2; // 圆心在方板中心
// 孔周边 4 个等分点
factory::addPoint(cx + R, cy, 0, lc, 5); // 0 deg
factory::addPoint(cx, cy + R, 0, lc, 6); // 90 deg
factory::addPoint(cx - R, cy, 0, lc, 7); // 180 deg
factory::addPoint(cx, cy - R, 0, lc, 8); // 270 deg
// 圆心点(用作圆弧中心)
factory::addPoint(cx, cy, 0, lc, 9);
// 4 段圆弧构成完整圆
factory::addCircleArc(5, 9, 6, 5); // 0 -> 90 deg
factory::addCircleArc(6, 9, 7, 6); // 90 -> 180 deg
factory::addCircleArc(7, 9, 8, 7); // 180 -> 270 deg
factory::addCircleArc(8, 9, 5, 8); // 270 -> 360 deg
每条圆弧的角度为 90 度,小于 Pi(180 度),符合 Gmsh 内置几何内核的限制。圆心点(tag=9)是 4 段圆弧的共同中心。
4.3.4 定义带孔面
// 外边界曲线环(4 条直线,全部正向)
factory::addCurveLoop({1, 2, 3, 4}, 1);
// 内孔曲线环(4 段圆弧)
factory::addCurveLoop({5, 6, 7, 8}, 2);
// 定义带孔面:外环=1,内孔环=2
factory::addPlaneSurface({1, 2}, 1);
addPlaneSurface 的参数 {1, 2} 中,1 是外边界,2 是内孔。如果方向错误导致法向量翻转,可以加负号纠正。
4.3.5 设置颜色与命名
// 为内外边界设置不同颜色
gmsh::model::setColor({{1, 1}, {1, 2}, {1, 3}, {1, 4}}, 0, 0, 255); // 外边界:蓝色
gmsh::model::setColor({{1, 5}, {1, 6}, {1, 7}, {1, 8}}, 255, 0, 0); // 孔边界:红色
gmsh::model::setColor({{2, 1}}, 200, 200, 200); // 面:浅灰
// 为实体设置可读名称
gmsh::model::setEntityName(2, 1, "Plate");
gmsh::model::setEntityName(1, 5, "Hole_Edge_1");
setColor 的第一个参数是 vector<pair<int,int>>,每对 {dim, tag} 标识一个实体。这里尺寸 dim: 0=点, 1=曲线, 2=面。
4.3.6 同步、网格生成与运行
factory::synchronize();
gmsh::model::mesh::generate(2);
gmsh::write("plate_with_hole.msh");
// 启动 GUI
std::set<std::string> args(argv, argv + argc);
if (!args.count("-nopopup")) gmsh::fltk::run();
gmsh::finalize();
return 0;
}
示例二:使用多种曲线的复杂面(基于官方 t4)
本示例示范如何使用 B 样条、贝塞尔曲线、椭圆弧以及注释功能,构建一个复杂轮廓。此处提取官方 t4.cpp 中的关键知识点进行讲解。
4.4.1 添加注释(Annotation)
注释通过视图系统实现,适用于标记边界条件位置、材料区域或载荷点:
// 创建一个视图用于存放注释
int v = gmsh::view::add("comments");
// 窗口坐标注释:距左边界 10px,距下边界 -10px(从底部向上)
gmsh::view::addListDataString(v, {10, -10}, {"Created with Gmsh"});
// 模型坐标注释:在 (0, 0.11, 0) 处显示文字"Hole"
gmsh::view::addListDataString(v, {0, 0.11, 0}, {"Hole"},
{"Align", "Center", "Font", "Helvetica"});
// 嵌入图片(以 file:// 前缀开头)
// @ 符号后指定宽x高,自然缩放设 0,像素尺寸用两个 0
gmsh::view::addListDataString(v, {0, 0.09, 0},
{"file://../image.png@0.01x0"}, {"Align", "Center"});
// Billboard 模式(# 符号):图片始终朝向相机
gmsh::view::addListDataString(v, {0, 0.12, 0},
{"file://../image.png@0.01x0#"}, {"Align", "Center"});
addListDataString 的关键参数:
- 第一个参数:视图句柄
- 第二个参数:位置坐标 {x, y, z} 或窗口坐标 {pixelX, pixelY} —— z 坐标由参数个数自动判定
- 第三个参数:字符串内容数组
- 后续参数:可选样式属性(键值对)
4.4.2 设置实体颜色
颜色设置支持批量操作——将同一颜色的实体放入一个 vector<pair<int,int>> 中:
// 面颜色
gmsh::model::setColor({{2, 22}}, 127, 127, 127); // Gray50
gmsh::model::setColor({{2, 24}}, 160, 32, 240); // Purple
// 曲线颜色(范围设置)
for (int i = 1; i <= 14; i++)
gmsh::model::setColor({{1, i}}, 255, 0, 0); // 曲线 1~14:红色
for (int i = 15; i <= 20; i++)
gmsh::model::setColor({{1, i}}, 255, 255, 0); // 曲线 15~20:黄色
setColor 的 r/g/b 参数为 0-255 的整数值。
4.4.3 双点击事件响应
为视图和几何实体设置双击回调,便于交互式调试:
gmsh::view::option::setString(v, "DoubleClickedCommand",
"Printf('View[0] has been double-clicked!');");
gmsh::option::setString("Geometry.DoubleClickedLineCommand",
"Printf('Curve %g has been double-clicked!', "
"Geometry.DoubleClickedEntityTag);");
DoubleClickedEntityTag 是 Gmsh 在双击事件中自动注入的特殊变量。
4.5 完整可运行代码
完整示例一:带圆孔的方板
#include <set>
#include <gmsh.h>
int main(int argc, char **argv) {
gmsh::initialize(argc, argv);
gmsh::model::add("plate_with_hole");
namespace factory = gmsh::model::geo;
double L = 1.0;
double R = 0.2;
double cx = L / 2, cy = L / 2;
double lc = 0.05;
// ===== 外边界(方板) =====
factory::addPoint(0, 0, 0, lc, 1);
factory::addPoint(L, 0, 0, lc, 2);
factory::addPoint(L, L, 0, lc, 3);
factory::addPoint(0, L, 0, lc, 4);
factory::addLine(1, 2, 1);
factory::addLine(2, 3, 2);
factory::addLine(3, 4, 3);
factory::addLine(4, 1, 4);
// ===== 内孔(圆) =====
factory::addPoint(cx + R, cy, 0, lc, 5);
factory::addPoint(cx, cy + R, 0, lc, 6);
factory::addPoint(cx - R, cy, 0, lc, 7);
factory::addPoint(cx, cy - R, 0, lc, 8);
factory::addPoint(cx, cy, 0, lc, 9); // 圆心
factory::addCircleArc(5, 9, 6, 5);
factory::addCircleArc(6, 9, 7, 6);
factory::addCircleArc(7, 9, 8, 7);
factory::addCircleArc(8, 9, 5, 8);
// ===== 定义带孔面 =====
factory::addCurveLoop({1, 2, 3, 4}, 1); // 外环
factory::addCurveLoop({5, 6, 7, 8}, 2); // 内孔环
factory::addPlaneSurface({1, 2}, 1); // 面 = 外环 + 内孔
// ===== 颜色与命名 =====
gmsh::model::setColor({{1, 1}, {1, 2}, {1, 3}, {1, 4}}, 0, 0, 255);
gmsh::model::setColor({{1, 5}, {1, 6}, {1, 7}, {1, 8}}, 255, 0, 0);
gmsh::model::setColor({{2, 1}}, 200, 200, 200);
gmsh::model::setEntityName(2, 1, "Plate");
gmsh::model::setEntityName(1, 5, "Hole_Edge");
// ===== 注释 =====
int v = gmsh::view::add("annotations");
gmsh::view::addListDataString(v, {cx, cy, 0}, {"Hole Center"},
{"Align", "Center", "Font", "Helvetica"});
// ===== 网格 =====
factory::synchronize();
gmsh::model::mesh::generate(2);
gmsh::write("plate_with_hole.msh");
std::set<std::string> args(argv, argv + argc);
if (!args.count("-nopopup")) gmsh::fltk::run();
gmsh::finalize();
return 0;
}
完整示例二:使用多种曲线的复杂面
本示例构造一个由直线、圆弧、Spline 和 B-Spline 共同围成的多边形面,并在其中间开一个椭圆孔(使用 addEllipse),同时演示实体颜色设置。
#include <set>
#include <gmsh.h>
int main(int argc, char **argv) {
gmsh::initialize(argc, argv);
gmsh::model::add("complex_surface");
namespace factory = gmsh::model::geo;
double lc = 0.1;
// ===== 外边界:直线 + 圆弧 + Spline + B-Spline =====
factory::addPoint( 0, 0, 0, lc, 1);
factory::addPoint( 1, 0, 0, lc, 2);
factory::addPoint( 2, 1, 0, lc, 3);
factory::addPoint( 2, 2, 0, lc, 4);
factory::addPoint( 1, 3, 0, lc, 5);
factory::addPoint( 0, 3, 0, lc, 6);
factory::addPoint(-1, 2, 0, lc, 7);
factory::addPoint(-1, 1, 0, lc, 8);
// 直线
factory::addLine(1, 2, 1);
// 圆弧 (start=2, center=3, end=4) — 用点 3 作中心
factory::addPoint(1.5, 0.5, 0, lc, 31); // 圆弧辅助中心
factory::addCircleArc(2, 31, 4, 2);
// Spline 通过点 4, 5, 6
factory::addSpline({4, 5, 6}, 3);
// B-Spline 通过点 6, 7, 8
factory::addSpline({6, 7, 8}, 4);
// 直线闭合
factory::addLine(8, 1, 5);
// ===== 内孔:完整椭圆 =====
double cx = 0.5, cy = 1.5;
factory::addPoint(cx, cy, 0, lc, 10); // 中心
factory::addPoint(cx + 0.3, cy, 0, lc, 11); // 长轴端点
factory::addPoint(cx, cy + 0.15, 0, lc, 12); // 椭圆上一点(定义短轴)
// addEllipse(start, center, majorAxisPoint, tag)
factory::addEllipse(11, 10, 12, 6);
// ===== 曲线环与面 =====
factory::addCurveLoop({1, 2, 3, 4, 5}, 1); // 外环
factory::addCurveLoop({6}, 2); // 内孔环(椭圆)
factory::addPlaneSurface({1, 2}, 1);
// ===== 实体命名与颜色 =====
gmsh::model::setEntityName(2, 1, "ComplexPlate");
gmsh::model::setEntityName(1, 1, "BottomEdge");
gmsh::model::setEntityName(1, 2, "CircleArc");
gmsh::model::setEntityName(1, 3, "Spline");
gmsh::model::setEntityName(1, 4, "BSpline");
gmsh::model::setEntityName(1, 6, "EllipseHole");
gmsh::model::setColor({{1, 1}}, 0, 0, 255); // 直线:蓝
gmsh::model::setColor({{1, 2}}, 0, 200, 200); // 圆弧:青
gmsh::model::setColor({{1, 3}}, 200, 100, 0); // Spline:橙
gmsh::model::setColor({{1, 4}}, 150, 0, 200); // B-Spline:紫
gmsh::model::setColor({{1, 6}}, 255, 0, 0); // 椭圆孔:红
gmsh::model::setColor({{2, 1}}, 220, 220, 220); // 面:浅灰
// ===== 注释:标记边界名称 =====
int v = gmsh::view::add("bcs");
gmsh::view::addListDataString(v, {0.25, -0.1, 0}, {"BottomEdge (fixed)"},
{"Align", "Center"});
gmsh::view::addListDataString(v, {1.5, 1.5, 0}, {"EllipseHole (free)"},
{"Align", "Center"});
// ===== 网格 =====
factory::synchronize();
gmsh::model::mesh::generate(2);
gmsh::write("complex_surface.msh");
std::set<std::string> args(argv, argv + argc);
if (!args.count("-nopopup")) gmsh::fltk::run();
gmsh::finalize();
return 0;
}
4.6 关键 API 速查表
曲线创建(gmsh::model::geo)
| 函数 | 签名 | 说明 |
|---|---|---|
addLine |
addLine(startPoint, endPoint, tag) |
创建直线 |
addCircleArc |
addCircleArc(start, center, end, tag) |
三点圆弧(弧度 < Pi) |
addEllipseArc |
addEllipseArc(start, center, majorPt, end, tag) |
椭圆弧 |
addEllipse |
addEllipse(start, center, majorPt, tag) |
完整闭合椭圆 |
addSpline |
addSpline({ptTags}, tag) |
Catmull-Rom 样条(通过所有点) |
addBSpline |
addBSpline({ptTags}, tag) |
B 样条(控制点不在曲线上) |
addBezier |
addBezier({ptTags}, tag) |
贝塞尔曲线 |
addCompoundSpline |
addCompoundSpline({ptTagsVec}, ...) |
分段复合样条 |
addCompoundBSpline |
addCompoundBSpline({ptTagsVec}, ...) |
分段复合 B 样条 |
曲线环与面
| 函数 | 说明 |
|---|---|
addCurveLoop({curveTags}, tag) |
由曲线列表构成闭合环,负号表示反向 |
addPlaneSurface({loopTags}, tag) |
第一个环 = 外边界,后续 = 内孔 |
实体属性
| 函数 | 说明 |
|---|---|
gmsh::model::setColor({{dim,tag},...}, r, g, b) |
设置实体颜色(RGB 0-255) |
gmsh::model::setEntityName(dim, tag, "name") |
为实体设置可读名称 |
gmsh::model::setVisibility({{dim,tag},...}, visible) |
控制实体可见性(true/false) |
注释与视图
| 函数 | 说明 |
|---|---|
gmsh::view::add("name") |
创建新视图,返回视图句柄 |
gmsh::view::addListDataString(v, coords, {strings}, ...) |
添加文本/图片注释 |
gmsh::view::option::setString(v, key, val) |
设置视图选项(如双击回调) |
gmsh::option::setString(key, val) |
设置全局选项 |
注意事项
- 圆弧限制:
addCircleArc要求弧角小于 Pi(180 度)。完整圆需要 3 段以上圆弧。使用 OpenCASCADE 内核时无此限制。 - 曲线方向:在
addCurveLoop的曲线标签列表中,正值表示沿定义方向,负值表示反向。内部算法会确保所有曲线沿同一方向(顺时针或逆时针)构成闭合环。 - 孔洞定义顺序:
addPlaneSurface的曲线环列表中,第一个必须是外边界,其余为内孔。外边界与内孔的方向可以互相独立。 - 同步:在调用
gmsh::model::mesh::generate()之前,务必调用factory::synchronize()将几何数据从geo模块同步到 Gmsh 核心模型。 - 命名空间:推荐使用
namespace factory = gmsh::model::geo;简化代码,但对gmsh::model::setColor等顶层 API 仍需使用全限定名。