Skip to content

第19章 后处理视图与动画


19.1 学习目标

  • 理解 Gmsh 后处理数据架构:View(视图)作为数据容器的角色,以及 merge() 加载 .pos/.msh 后处理文件的完整流程
  • 掌握 view::getTags()view::option::setNumber/setString/getNumber 的三层 API 结构,学会区分全局选项与视图级选项的设置方式
  • 学会配置视图显示效果:IntervalsType(等值线/填充/两者)、RaiseZ/OffsetZ(3D 效果)、Light/SmoothNormals(视觉增强)、NbIso(等值线数量)、ShowScale(颜色标尺)
  • 掌握视图窗口布局控制:AutoPosition 自动布局 vs PositionX/Y/Width/Height 手动定位,以及 Type 选项切换 2D/3D 显示模式
  • 掌握动画编程的核心循环:通过 NbTimeStep + TimeStep 实现时间步动画,结合 RotationX/Y/Z 实现旋转动画,理解 graphics::draw() 作为帧渲染触发器的角色
  • 学会通过 write() 方法导出单帧图像(.gif/.ppm/.jpg),为生成离线动画/影片打下基础
  • 了解全局可视化选项(背景/前景颜色、正交/透视投影、坐标轴显示)在后处理场景下的配置方式

19.2 核心概念说明

19.2.1 View(视图)是什么?

在 Gmsh 的后处理体系中,View(视图)是后处理数据集的核心容器。每个 View 可以承载多种类型的数据:

数据类型 英文名称 说明 数据结构
标量场 Scalar field 每个点一个浮点值(温度、压力、位移幅值等) ST(Scalar Triangle)、SP(Scalar Point)等
矢量场 Vector field 每个点一个三维向量(速度、位移、力等) VT(Vector Triangle)、VP(Vector Point)等
张量场 Tensor field 每个点一个对称张量(应力、应变等) TT(Tensor Triangle)、TP(Tensor Point)等
文本标注 Text annotation 2D/3D 空间中的文字标注 T2(2D 文字)、T3(3D 文字)

View 的来源:View 可以通过两种方式获得: 1. 从文件加载:通过 merge() 加载 .pos(Gmsh parsed format)或 .msh 文件 2. 通过 API 创建:通过 view::add() 创建空 View,再用 addListData()addModelData() 填充数据

一个文件可含多个 View:例如 view4.pos 包含 2 个 View(一个包含 2D 图表数据,另一个包含图表数据+文本标注)。merge() 会自动解析并创建对应数量的 View。

19.2.2 全局选项 vs 视图选项

Gmsh 的选项系统分两个层级,必须理解其区别:

全局选项:gmsh::option::setNumber("General.RotationX", 30)
          影响整体图形窗口(投影方式、背景颜色、旋转角度、图形窗口大小等)

视图选项:gmsh::view::option::setNumber(viewTag, "IntervalsType", 2)
          影响特定 View 的显示方式(等值线/填充、Z偏移、光照、颜色标尺等)

API 对比

层级 设置数值 设置字符串 获取数值 设置颜色
全局 option::setNumber(name, val) option::setString(name, val) option::getNumber(name, val) option::setColor(name, r, g, b)
视图 view::option::setNumber(tag, name, val) view::option::setString(tag, name, val) view::option::getNumber(tag, name, val) 通过 setNumber 设置颜色相关选项

关键区别:视图级函数需要 viewTag 作为第一个参数,指定操作哪个 View。

19.2.3 View 标签的获取方式

// 方式A:view::getTags() —— 获取当前模型中的所有 View 标签(推荐)
std::vector<int> v;
gmsh::view::getTags(v);
// v = {0, 1, 2, 3}  例如merge了4个View

// 方式B:通过选项反查(不推荐,仅保留兼容性)
// 旧式的 GEO 脚本写法 v0 = PostProcessing.NbViews - 4
// 应避免在 C++ API 中使用这种方式

view::getTags() 返回的标签向量允许程序动态遍历所有 View,无需硬编码数量。

19.2.4 等值线(Iso-lines)vs 填充(Continuous)vs 两者(Both)

IntervalsType 控制标量场在视图中的显示方式:

IntervalsType 含义 视觉效果 适用场景
0 等值线(Iso-lines) 绘制等值线(等高线),每条线上值相等 传统工程图、打印友好的结果展示
1 填充(Continuous / Filled) 面片按值着色,颜色连续渐变 温度分布图、应力场云图
2 两者(Both) 同时显示等值线和填充色 兼顾数值精度(等值线标注)和直观感知(颜色填充)
// 设置视图 v[0] 同时显示填充和等值线
gmsh::view::option::setNumber(v[0], "IntervalsType", 2);
// 设置视图 v[1] 仅填充
gmsh::view::option::setNumber(v[1], "IntervalsType", 1);

19.2.5 3D 效果:RaiseZ 与 OffsetZ

这两个选项将标量值映射到视觉空间的 Z 轴方向,产生 3D 立体效果:

  • RaiseZ:将标量值按比例映射为 Z 方向的位移(数据值越大,面片抬升越高)。值是缩放因子,乘以标量场的局部值得到 Z 偏移量
  • OffsetZ:为整个视图施加一个固定的 Z 方向偏移(平面平移),与数据值无关
// 将 View v[0] 的标量场按比例抬升,产生3D地形图效果
double max;
gmsh::view::option::getNumber(v[0], "Max", max);    // 获取数据最大值
gmsh::view::option::setNumber(v[0], "RaiseZ", 0.01 / max * t);  // 随时间步动态抬升

// 对整个视图施加固定Z偏移,用于分离多个重叠视图
gmsh::view::option::setNumber(v[0], "OffsetZ", 0.05);

应用场景:RaiseZ 常用于将仿真结果(如变形位移)可视化映射为几何变形,OffsetZ 用于在同一个3D场景中分离显示多个标量场(避免重叠)。

19.2.6 光照与法向量平滑

这两个选项显著提升 3D 渲染的视觉质量:

选项 取值 效果
Light 0/1 启用/禁用光照。启用后面片根据法向量和光源方向计算漫反射明暗,呈现立体感
SmoothNormals 0/1 启用/禁用法向量平滑。启用后在顶点处对相邻面片的法向量做加权平均,消除面片间的棱角感
gmsh::view::option::setNumber(v[0], "Light", 1);           // 开启光照
gmsh::view::option::setNumber(v[0], "SmoothNormals", 1);   // 开启法向量平滑

19.2.7 视图窗口布局控制

Gmsh 允许每个 View 在图形窗口中占据独立的矩形子窗口(类似画中画效果):

选项 类型 说明
AutoPosition Number 0=手动定位, 1=自动排版(默认平铺)
PositionX Number 子窗口左上角 X 坐标(像素),窗口顶部为 0
PositionY Number 子窗口左上角 Y 坐标(像素),窗口顶部为 0
Width Number 子窗口宽度(像素)
Height Number 子窗口高度(像素)
Type Number 1=3D 视图, 2=2D 视图(XY 平面图)
// 将 View v[2] 设置为 2D 显示、手动定位的子窗口
gmsh::view::option::setString(v[2], "Name", "Test...");   // 设置视图名称
gmsh::view::option::setNumber(v[2], "Type", 2);           // 2D 模式
gmsh::view::option::setNumber(v[2], "AutoPosition", 0);   // 关闭自动布局
gmsh::view::option::setNumber(v[2], "PositionX", 85);     // 左上角 X=85px
gmsh::view::option::setNumber(v[2], "PositionY", 50);     // 左上角 Y=50px
gmsh::view::option::setNumber(v[2], "Width", 200);        // 宽 200px
gmsh::view::option::setNumber(v[2], "Height", 130);       // 高 130px

19.2.8 动画原理:时间步与帧循环

Gmsh 的动画机制基于时间步(Time Step)概念:

┌──────────────────────────────────────────────────┐
│  View 包含 N 个时间步(NbTimeStep)                │
│  Step 0 → Step 1 → Step 2 → ... → Step N-1       │
│  每个时间步存储一组完整的场数据                      │
└──────────────────────────────────────────────────┘

动画循环结构:
  for(每一帧) {
    设置所有 View 的 TimeStep 为当前帧号
    更新旋转角度 / RaiseZ 等视觉效果
    graphics::draw()           // 渲染当前帧到屏幕
    write("frame_N.gif")       // (可选)保存帧到文件
  }

关键点: - NbTimeStep:View 中存储的时间步总数(只读) - TimeStep:当前显示的时间步索引(0 到 NbTimeStep-1,可读写) - 每个 View 可独立设置其 TimeStep,允许不同 View 显示不同时间步 - graphics::draw() 是帧渲染的触发器,调用后场景重新渲染到屏幕上 - write("filename.ext") 将当前渲染结果导出为图像文件(.gif / .ppm / .jpg 等)

19.2.9 全局可视化选项大全

选项名称 类型 说明
General.Trackball Number 0=标准旋转, 1=轨迹球模式
General.RotationX Number 绕 X 轴旋转角度(度)
General.RotationY Number 绕 Y 轴旋转角度(度)
General.RotationZ Number 绕 Z 轴旋转角度(度)
General.Background Color 图形窗口背景色(RGB,0-255)
General.Foreground Color 图形窗口前景色(网格线、边框等)
General.Text Color 文本颜色
General.Orthographic Number 0=透视投影, 1=正交投影
General.Axes Number 0=隐藏坐标轴, 1=显示大坐标轴
General.SmallAxes Number 0=隐藏, 1=显示视图角落的小坐标轴
General.GraphicsWidth Number 图形区域像素宽度(不含菜单栏)
General.GraphicsHeight Number 图形区域像素高度
General.MenuWidth Number 菜单栏像素宽度(只读)

19.3 C++ 代码逐段讲解

19.3.1 头文件与初始化

#include <set>
#include <gmsh.h>

int main(int argc, char **argv)
{
    gmsh::initialize();
    gmsh::model::add("ch19_post_processing");

与前几章一致,引入 <gmsh.h> 头文件,调用 gmsh::initialize() 初始化 Gmsh 库,model::add() 创建模型实例。后处理操作同样需要一个模型作为上下文环境。

19.3.2 创建基础几何

    // 创建简单的四边形几何体,作为后处理数据的载体
    double lc = 1e-2;
    gmsh::model::geo::addPoint(0, 0, 0, lc, 1);
    gmsh::model::geo::addPoint(.1, 0, 0, lc, 2);
    gmsh::model::geo::addPoint(.1, .3, 0, lc, 3);
    gmsh::model::geo::addPoint(0, .3, 0, lc, 4);
    gmsh::model::geo::addLine(1, 2, 1);
    gmsh::model::geo::addLine(3, 2, 2);
    gmsh::model::geo::addLine(3, 4, 3);
    gmsh::model::geo::addLine(4, 1, 4);
    gmsh::model::geo::addCurveLoop({4, 1, -2, 3}, 1);
    gmsh::model::geo::addPlaneSurface({1}, 1);
    gmsh::model::geo::synchronize();

使用内置 geo 内核创建一个 0.1 x 0.3 的矩形面。synchronize() 将几何数据从 geo 内核同步到 Gmsh 模型数据库。此几何体为后处理数据提供了可视化的"基底"——后处理视图的数据将叠加显示在这个几何上。

说明:在实际 CAE 前处理工作中,几何体通常由网格划分步骤生成网格后,再叠加有限元求解结果进行后处理。这里为简化演示,直接在几何面上叠加后处理数据。

19.3.3 加载后处理视图文件

    // 加载 .pos 格式的后处理视图文件
    try {
        gmsh::merge("../view1.pos");   // 标量场视图:三角形上的标量数据
        gmsh::merge("../view1.pos");   // 重复加载,创建第二个同源视图
        gmsh::merge("../view4.pos");   // 包含 2 个视图:2D 图表 + 文本标注
    } catch(...) {
        gmsh::logger::write("Could not load post-processing views!", "error");
        gmsh::finalize();
        return 0;
    }

gmsh::merge() 是 Gmsh 的通用文件加载函数,可处理 .geo、.msh、.pos 等多种格式。这里加载的后处理文件: - view1.pos:包含一个名为 "A scalar field" 的标量场视图,定义在三角形上,有 5 个时间步。每个时间步包含若干个三角形(ST 实体),每个三角形 3 个顶点各有 1 个标量值 - view4.pos:包含 2 个视图 —— 第一个 "e" 是 2D 点标量数据(SP 实体),第二个 "e" 是 2D 点标量数据加文本标注(T2, T3

两次加载 view1.pos 会在模型中创建两个独立的 View(标签不同但数据相同),这为后续演示不同 View 的不同显示配置提供了基础。

注意:.pos 是 Gmsh parsed format,适用于小数据集。对于大规模数据集(百万级以上节点),应使用 .msh 格式(基于网格的视图),其加载和渲染效率远高于 .pos。

19.3.4 设置全局可视化选项

    // 全局可视化选项设置
    gmsh::option::setNumber("General.Trackball", 0);     // 关闭轨迹球旋转模式
    gmsh::option::setNumber("General.RotationX", 0);     // 初始化X轴旋转角为0
    gmsh::option::setNumber("General.RotationY", 0);     // 初始化Y轴旋转角为0
    gmsh::option::setNumber("General.RotationZ", 0);     // 初始化Z轴旋转角为0

    int white[3] = {255, 255, 255};
    int black[3] = {0, 0, 0};
    gmsh::option::setColor("General.Background", white[0], white[1], white[2]);
    gmsh::option::setColor("General.Foreground", black[0], black[1], black[2]);
    gmsh::option::setColor("General.Text", black[0], black[1], black[2]);

    gmsh::option::setNumber("General.Orthographic", 0);  // 透视投影(0=透视, 1=正交)
    gmsh::option::setNumber("General.Axes", 0);           // 隐藏大坐标轴
    gmsh::option::setNumber("General.SmallAxes", 0);      // 隐藏小坐标轴

逐项解释: - Trackball:Gmsh 默认使用轨迹球旋转模式(值为 1),鼠标拖拽时旋转行为类似球体跟踪球。设为 0 使用标准旋转模式 - RotationX/Y/Z:设置场景绕各轴的初始旋转角度。在后续动画中会动态修改这些值 - Background/Foreground/TextsetColor() 接受 RGB 三通道值(0-255)。设置为白底黑字,适合截图和论文插图 - Orthographic:0 为透视投影(远处物体更小,有深度感),1 为正交投影(平行投影,无近大远小)。后处理可视化通常使用透视投影以获得更好的3D感知 - Axes/SmallAxes:本例选择隐藏坐标轴以保持画面简洁。在分析场景中通常需要显示坐标轴以提供空间参考

19.3.5 初始化 GUI 与获取 View 标签

    // GUI 初始化(可通过 -nopopup 跳过)
    std::set<std::string> args(argv, argv + argc);
    if(!args.count("-nopopup")) gmsh::fltk::initialize();

    // 获取所有 View 标签
    std::vector<int> v;
    gmsh::view::getTags(v);
    if(v.size() != 4) {     // 预期 4 个 View(2个view1 + 2个view4)
        gmsh::logger::write("Wrong number of views!", "error");
        gmsh::finalize();
        return 1;
    }

view::getTags(v) 将当前模型中所有 View 的标签填充到向量 v 中。标签顺序与 merge() 的调用顺序一致: - v[0] = 第一次加载的 view1.pos → "A scalar field" - v[1] = 第二次加载的 view1.pos → "A scalar field"(同上但独立视图) - v[2] = view4.pos 中的第一个 View → "e"(2D 点标量数据) - v[3] = view4.pos 中的第二个 View → "e"(2D 点标量+文本)

防御性编程v.size() != 4 检查确保加载的文件数量和预期一致。在实际项目中,文件可能缺失或格式变化,此类检查至关重要。

fltk::initialize()-nopopup:当命令行参数包含 -nopopup 时跳过 GUI 初始化,仅执行程序化操作(如批量导出图像),适用于无头服务器(headless)环境。

19.3.6 配置各 View 的显示选项

    // View v[0]:标量场 + 3D效果 + 光照
    gmsh::view::option::setNumber(v[0], "IntervalsType", 2);  // 同时显示等值线和填充
    gmsh::view::option::setNumber(v[0], "OffsetZ", 0.05);     // 整体Z偏移0.05
    gmsh::view::option::setNumber(v[0], "RaiseZ", 0);         // 初始不抬升(动画中动态修改)
    gmsh::view::option::setNumber(v[0], "Light", 1);          // 开启光照
    gmsh::view::option::setNumber(v[0], "ShowScale", 0);      // 隐藏颜色标尺
    gmsh::view::option::setNumber(v[0], "SmoothNormals", 1);  // 开启法向量平滑

    // View v[1]:标量场 + 仅填充 + 10条等值线
    gmsh::view::option::setNumber(v[1], "IntervalsType", 1);  // 仅填充(无等值线)
    gmsh::view::option::setNumber(v[1], "NbIso", 10);         // 颜色分10级(10条色带)
    gmsh::view::option::setNumber(v[1], "ShowScale", 0);      // 隐藏颜色标尺

    // View v[2]:2D 图表 + 手动窗口定位
    gmsh::view::option::setString(v[2], "Name", "Test...");    // 设置视图名称
    gmsh::view::option::setNumber(v[2], "Axes", 1);            // 显示坐标轴
    gmsh::view::option::setNumber(v[2], "IntervalsType", 2);   // 等值线+填充
    gmsh::view::option::setNumber(v[2], "Type", 2);            // 2D 显示模式
    gmsh::view::option::setNumber(v[2], "AutoPosition", 0);    // 关闭自动布局
    gmsh::view::option::setNumber(v[2], "PositionX", 85);      // 窗口左上角X
    gmsh::view::option::setNumber(v[2], "PositionY", 50);      // 窗口左上角Y
    gmsh::view::option::setNumber(v[2], "Width", 200);         // 窗口宽度
    gmsh::view::option::setNumber(v[2], "Height", 130);        // 窗口高度

    // View v[3]:隐藏(包含文本标注,在动画中不需要可见)
    gmsh::view::option::setNumber(v[3], "Visible", 0);         // 设为不可见

设计意图分析: - v[0] 作为"主角":同时显示填充和等值线,开启光照和法向量平滑以获得最佳 3D 呈现效果。RaiseZ 初始为 0 但将在动画中动态调整 - v[1] 作为"对照":仅显示填充(无等值线),NbIso=10 控制颜色精度。两个视图展示同一数据的不同呈现方式 - v[2] 作为"2D 插图":通过手动窗口定位在屏幕角落显示一个小型 2D 图表,形似论文中的插图(inset) - v[3] 被隐藏:该 View 主要包含文本标注和 2D 绘图,作为静态信息而非动态可视化对象

19.3.7 动画循环:时间步 + 旋转 + 帧导出

    int t = 0;  // 当前时间步索引

    for(int num = 1; num <= 3; num++) {     // 外层:3 轮动画循环
        // 获取 View v[0] 的时间步总数
        double nbt;
        gmsh::view::option::getNumber(v[0], "NbTimeStep", nbt);
        t = (t < nbt - 1) ? t + 1 : 0;      // 循环递增时间步

        // 将所有 View 设为相同的时间步
        for(auto vv : v)
            gmsh::view::option::setNumber(vv, "TimeStep", t);

        // 根据当前时间步动态调整 v[0] 的 RaiseZ
        double max;
        gmsh::view::option::getNumber(v[0], "Max", max);
        gmsh::view::option::setNumber(v[0], "RaiseZ", 0.01 / max * t);

        if(num == 3) {
            // 第3轮时调整图形窗口为 640x480,准备导出标准尺寸帧
            double mw;
            gmsh::option::getNumber("General.MenuWidth", mw);
            gmsh::option::setNumber("General.GraphicsWidth", mw + 640);
            gmsh::option::setNumber("General.GraphicsHeight", 480);
        }

        int frames = 50;  // 每轮 50 帧
        for(int num2 = 1; num2 <= frames; num2++) {
            // 逐步旋转场景
            double rotx;
            gmsh::option::getNumber("General.RotationX", rotx);
            gmsh::option::setNumber("General.RotationX", rotx + 10);
            gmsh::option::setNumber("General.RotationY", (rotx + 10) / 3.);
            double rotz;
            gmsh::option::getNumber("General.RotationZ", rotz);
            gmsh::option::setNumber("General.RotationZ", rotz + 0.1);

            // 渲染当前帧
            gmsh::graphics::draw();

            if(num == 3) {
                // 第3轮:导出每一帧为图像文件
                // gmsh::write("ch19-" + std::to_string(num2) + ".gif");
                // gmsh::write("ch19-" + std::to_string(num2) + ".ppm");
                // gmsh::write("ch19-" + std::to_string(num2) + ".jpg");
            }
        }
    }

动画循环结构详解

  1. 外层循环(num 1→3):三轮循环,每轮递增一个时间步。view1.pos 有 5 个时间步(0-4),三轮分别显示 t=1,2,3
  2. 时间步循环递增t = (t < nbt - 1) ? t + 1 : 0 实现环形递增(0→1→2→3→4→0→1...)。所有 View 同步设置为同一时间步
  3. 动态 RaiseZ0.01 / max * t 将时间步映射为抬升高度,产生标量值随时间"生长"的动画效果
  4. 窗口尺寸调整GraphicsWidth = MenuWidth + 640 确保图形区域恰好 640 像素宽(菜单栏占 MenuWidth 像素)。480 像素高是标准 4:3 比例
  5. 内层循环(50 帧):每帧递增 RotationX/Y/Z:
  6. X 轴每帧旋转 10 度,一轮完整旋转 500 度
  7. Y 轴按 X 的三分之一比例旋转,产生复杂的复合旋转
  8. Z 轴每帧旋转 0.1 度(缓慢的面内旋转)
  9. graphics::draw():触发 Gmsh 的渲染管线,将当前 OpenGL 场景绘制到屏幕
  10. 帧导出(已注释)write() 支持 .gif / .ppm / .jpg 等格式。取消注释后,第3轮将导出 50 张图像

生成影片的后续步骤(仅在第3轮后,已注释):

// 在 num==3 的循环结束后,可用 ffmpeg 合成影片:
// ffmpeg -i ch19-%d.jpg ch19.mpg

19.3.8 运行 GUI 与清理

    // 启动事件循环(阻塞直到用户关闭窗口)
    if(!args.count("-nopopup")) gmsh::fltk::run();

    gmsh::finalize();
    return 0;
}

fltk::run() 进入 FLTK 事件循环,用户可与 GUI 交互(旋转、缩放、手动播放动画等)。finalize() 清理 Gmsh 库分配的所有资源。


19.4 完整可运行代码

// -----------------------------------------------------------------------------
//  Gmsh 中文教程 第19章:后处理视图与动画
//  编译:g++ -o ch19 ch19.cpp -lgmsh -std=c++11
//  运行:./ch19              (启动GUI,查看动画效果)
//        ./ch19 -nopopup     (无GUI模式,仅输出信息)
// -----------------------------------------------------------------------------

#include <set>
#include <gmsh.h>

int main(int argc, char **argv)
{
    gmsh::initialize();
    gmsh::model::add("ch19_post_processing");

    // ---- 创建基础几何(作为后处理数据的载体)----
    double lc = 1e-2;
    gmsh::model::geo::addPoint(0, 0, 0, lc, 1);
    gmsh::model::geo::addPoint(.1, 0, 0, lc, 2);
    gmsh::model::geo::addPoint(.1, .3, 0, lc, 3);
    gmsh::model::geo::addPoint(0, .3, 0, lc, 4);
    gmsh::model::geo::addLine(1, 2, 1);
    gmsh::model::geo::addLine(3, 2, 2);
    gmsh::model::geo::addLine(3, 4, 3);
    gmsh::model::geo::addLine(4, 1, 4);
    gmsh::model::geo::addCurveLoop({4, 1, -2, 3}, 1);
    gmsh::model::geo::addPlaneSurface({1}, 1);
    gmsh::model::geo::synchronize();

    // ---- 加载后处理视图文件 ----
    // 注意:运行时需要确保 .pos 文件在工作目录中
    try {
        gmsh::merge("../view1.pos");   // 标量场(三角形),5个时间步
        gmsh::merge("../view1.pos");   // 重复加载,创建第二个独立视图
        gmsh::merge("../view4.pos");   // 2D图表+文本标注,含2个视图
    } catch(...) {
        gmsh::logger::write("Could not load post-processing views! "
                            "Make sure view1.pos and view4.pos are accessible.",
                            "error");
        gmsh::finalize();
        return 0;
    }

    // ---- 全局可视化选项 ----
    gmsh::option::setNumber("General.Trackball", 0);
    gmsh::option::setNumber("General.RotationX", 0);
    gmsh::option::setNumber("General.RotationY", 0);
    gmsh::option::setNumber("General.RotationZ", 0);

    int white[3] = {255, 255, 255};
    int black[3] = {0, 0, 0};
    gmsh::option::setColor("General.Background", white[0], white[1], white[2]);
    gmsh::option::setColor("General.Foreground", black[0], black[1], black[2]);
    gmsh::option::setColor("General.Text", black[0], black[1], black[2]);

    gmsh::option::setNumber("General.Orthographic", 0);  // 透视投影
    gmsh::option::setNumber("General.Axes", 0);
    gmsh::option::setNumber("General.SmallAxes", 0);

    // ---- 初始化 GUI ----
    std::set<std::string> args(argv, argv + argc);
    if(!args.count("-nopopup")) gmsh::fltk::initialize();

    // ---- 获取 View 标签 ----
    std::vector<int> v;
    gmsh::view::getTags(v);
    if(v.size() != 4) {
        gmsh::logger::write("Expected 4 views but got " +
                            std::to_string(v.size()), "error");
        gmsh::finalize();
        return 1;
    }

    // ---- 配置各 View 的显示选项 ----
    // View 0:标量场 + 3D效果
    gmsh::view::option::setNumber(v[0], "IntervalsType", 2);
    gmsh::view::option::setNumber(v[0], "OffsetZ", 0.05);
    gmsh::view::option::setNumber(v[0], "RaiseZ", 0);
    gmsh::view::option::setNumber(v[0], "Light", 1);
    gmsh::view::option::setNumber(v[0], "ShowScale", 0);
    gmsh::view::option::setNumber(v[0], "SmoothNormals", 1);

    // View 1:标量场 + 仅填充
    gmsh::view::option::setNumber(v[1], "IntervalsType", 1);
    gmsh::view::option::setNumber(v[1], "NbIso", 10);
    gmsh::view::option::setNumber(v[1], "ShowScale", 0);

    // View 2:2D 图表 + 手动窗口
    gmsh::view::option::setString(v[2], "Name", "Test...");
    gmsh::view::option::setNumber(v[2], "Axes", 1);
    gmsh::view::option::setNumber(v[2], "IntervalsType", 2);
    gmsh::view::option::setNumber(v[2], "Type", 2);
    gmsh::view::option::setNumber(v[2], "AutoPosition", 0);
    gmsh::view::option::setNumber(v[2], "PositionX", 85);
    gmsh::view::option::setNumber(v[2], "PositionY", 50);
    gmsh::view::option::setNumber(v[2], "Width", 200);
    gmsh::view::option::setNumber(v[2], "Height", 130);

    // View 3:隐藏
    gmsh::view::option::setNumber(v[3], "Visible", 0);

    // ---- 动画循环 ----
    int t = 0;

    for(int num = 1; num <= 3; num++) {
        // 循环递增时间步
        double nbt;
        gmsh::view::option::getNumber(v[0], "NbTimeStep", nbt);
        t = (t < nbt - 1) ? t + 1 : 0;

        // 同步所有 View 的时间步
        for(auto vv : v)
            gmsh::view::option::setNumber(vv, "TimeStep", t);

        // 动态调整 3D 抬升效果
        double max;
        gmsh::view::option::getNumber(v[0], "Max", max);
        gmsh::view::option::setNumber(v[0], "RaiseZ", 0.01 / max * t);

        if(num == 3) {
            // 调整窗口为 640x480 标准尺寸
            double mw;
            gmsh::option::getNumber("General.MenuWidth", mw);
            gmsh::option::setNumber("General.GraphicsWidth", mw + 640);
            gmsh::option::setNumber("General.GraphicsHeight", 480);
        }

        int frames = 50;
        for(int num2 = 1; num2 <= frames; num2++) {
            // 旋转场景
            double rotx;
            gmsh::option::getNumber("General.RotationX", rotx);
            gmsh::option::setNumber("General.RotationX", rotx + 10);
            gmsh::option::setNumber("General.RotationY", (rotx + 10) / 3.);
            double rotz;
            gmsh::option::getNumber("General.RotationZ", rotz);
            gmsh::option::setNumber("General.RotationZ", rotz + 0.1);

            // 渲染
            gmsh::graphics::draw();

            if(num == 3) {
                // 取消注释以导出帧图像
                // gmsh::write("ch19-" + std::to_string(num2) + ".gif");
                // gmsh::write("ch19-" + std::to_string(num2) + ".ppm");
                // gmsh::write("ch19-" + std::to_string(num2) + ".jpg");
            }
        }
    }

    // ---- 运行 GUI 事件循环 ----
    if(!args.count("-nopopup")) gmsh::fltk::run();

    gmsh::finalize();
    return 0;
}

19.5 关键 API 速查表

API 函数签名关键部分 说明
view::getTags gmsh::view::getTags(tags) 获取当前模型中所有 View 的整数标签,填充到 vector
view::option::setNumber gmsh::view::option::setNumber(tag, name, value) 设置指定 View 的数值选项(IntervalsType, NbIso, RaiseZ 等)
view::option::getNumber gmsh::view::option::getNumber(tag, name, value) 获取指定 View 的数值选项当前值
view::option::setString gmsh::view::option::setString(tag, name, value) 设置指定 View 的字符串选项(如 Name)
merge gmsh::merge(filename) 加载外部文件(.pos/.msh/.geo 等),自动创建 View 或几何实体
graphics::draw gmsh::graphics::draw() 触发 OpenGL 渲染管线,将当前场景绘制到屏幕
option::setColor gmsh::option::setColor(name, r, g, b) 设置全局颜色选项(背景、前景、文本),RGB 范围 0-255
option::setNumber gmsh::option::setNumber(name, value) 设置全局数值选项(General.RotationX, General.Orthographic 等)
option::getNumber gmsh::option::getNumber(name, value) 获取全局数值选项的当前值
write gmsh::write(filename) 将当前图形窗口渲染结果导出为图像文件(.gif/.ppm/.jpg)
view::add gmsh::view::add(name, tag) 创建一个新的空 View,返回标签
view::addListData gmsh::view::addListData(tag, dataType, numElements, data) 向 View 添加基于列表的数据(SP/VP/TP 等格式)
view::addModelData gmsh::view::addModelData(tag, step, modelName, dataType, tags, data) 向 View 添加基于网格模型的数据
view::write gmsh::view::write(tag, filename) 将指定 View 的数据导出为文件(如 .msh)
fltk::initialize gmsh::fltk::initialize() 初始化 FLTK GUI
fltk::run gmsh::fltk::run() 启动 FLTK 事件循环(阻塞调用)

常用的 View 选项速查

选项名称 类型 取值范围 说明
IntervalsType Number 0/1/2 等值线(0) / 填充(1) / 两者(2)
NbIso Number 1-N 等值线/颜色分区数量
RaiseZ Number 0.0-任意 标量值映射为 Z 偏移的比例因子
OffsetZ Number 任意 视图整体 Z 方向固定偏移
Light Number 0/1 是否启用光照
SmoothNormals Number 0/1 是否启用法向量平滑
ShowScale Number 0/1 是否显示颜色标尺(colorbar)
Visible Number 0/1 View 是否可见
Type Number 1/2 1=3D 视图, 2=2D 视图
Axes Number 0/1 View 子窗口中是否显示坐标轴
AutoPosition Number 0/1 自动布局(1) vs 手动定位(0)
PositionX/PositionY Number 像素 子窗口左上角坐标
Width/Height Number 像素 子窗口宽高
TimeStep Number 0 到 NbTimeStep-1 当前显示的时间步索引
NbTimeStep Number 只读 View 中的总时间步数
Max Number 只读 View 中数据的最大值
Name String 任意文本 View 的显示名称

19.6 注意事项

  1. 文件路径与工作目录merge() 使用相对路径时基于程序的工作目录(通常为可执行文件所在目录或 IDE 运行配置中的工作目录)。在跨平台项目中建议使用绝对路径或相对于可执行文件的路径,避免运行时找不到文件。

  2. .pos vs .msh 格式选择:.pos(parsed format)适合演示和小规模数据(几百到几千个元素)。对于实际 CAE 结果(百万级以上节点),务必使用 .msh 格式(model-based view),其数据以二进制存储,加载和渲染效率高出数个数量级。

  3. 视图选项的标签有效性view::option::setNumber/setString 要求 viewTag 对应的 View 必须存在,否则 Gmsh 会在内部报错。在 merge() 后应立即使用 view::getTags() 验证 View 数量。

  4. 动画性能graphics::draw() 是同步渲染调用,每次调用都会完整执行 GPU 渲染管线。在高帧率动画中(如 50fps),确保场景复杂度与硬件能力匹配。对于大规模数据集,考虑降低渲染质量选项(如禁用 SmoothNormals、减少 NbIso)以提升帧率。

  5. RaiseZ 的缩放RaiseZ 使用原始标量值乘以该因子。如果标量场数值范围很大(如温度 293-1500 K),直接使用需要适当的缩放因子。本例中 0.01/max*t 将抬升量归一化到 0-0.01 范围,避免抬升过大导致几何严重扭曲。

  6. 图像导出尺寸GraphicsWidth/GraphicsHeight 需要在 draw() 之前设置。导出图像的分辨率由这两个选项决定,而非屏幕分辨率。对于出版物级插图,建议至少 1920x1080 以上。

  7. -nopopup 参数的使用:在批量导出动画帧时,使用 -nopopup 跳过 GUI 可以减少初始化时间和内存占用。但注意此时 fltk::run() 不会被调用,程序在动画循环结束后直接退出。

  8. 旋转角度的累积RotationX 每帧递增 10 度且不重置,在多轮循环中角度会持续累积(第1轮结束后 +500 度,第2轮 +1000 度...)。这不影响视觉效果(角度按 360 度取模),但在需要精确控制视角时应注意。

  9. 跨平台颜色差异setColor() 的 RGB 值在不同操作系统和显示器上可能有轻微的颜色差异。对于出版物用途,建议使用 CMYK 规范的标准化颜色。

  10. 编译与依赖:本章代码需链接 Gmsh 库并确保头文件路径正确。Windows 下使用 MSVC 的编译命令示例: cl /EHsc /Fe:ch19.exe ch19.cpp /I <gmsh_include_path> /link <gmsh_lib_path>\gmsh.lib Linux/macOS 下使用: g++ -o ch19 ch19.cpp -lgmsh -std=c++11