Skip to content

第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:黄色

setColorr/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) 设置全局选项

注意事项

  1. 圆弧限制addCircleArc 要求弧角小于 Pi(180 度)。完整圆需要 3 段以上圆弧。使用 OpenCASCADE 内核时无此限制。
  2. 曲线方向:在 addCurveLoop 的曲线标签列表中,正值表示沿定义方向,负值表示反向。内部算法会确保所有曲线沿同一方向(顺时针或逆时针)构成闭合环。
  3. 孔洞定义顺序addPlaneSurface 的曲线环列表中,第一个必须是外边界,其余为内孔。外边界与内孔的方向可以互相独立。
  4. 同步:在调用 gmsh::model::mesh::generate() 之前,务必调用 factory::synchronize() 将几何数据从 geo 模块同步到 Gmsh 核心模型。
  5. 命名空间:推荐使用 namespace factory = gmsh::model::geo; 简化代码,但对 gmsh::model::setColor 等顶层 API 仍需使用全限定名。