第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/Text:setColor() 接受 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");
}
}
}
动画循环结构详解:
- 外层循环(num 1→3):三轮循环,每轮递增一个时间步。
view1.pos有 5 个时间步(0-4),三轮分别显示 t=1,2,3 - 时间步循环递增:
t = (t < nbt - 1) ? t + 1 : 0实现环形递增(0→1→2→3→4→0→1...)。所有 View 同步设置为同一时间步 - 动态 RaiseZ:
0.01 / max * t将时间步映射为抬升高度,产生标量值随时间"生长"的动画效果 - 窗口尺寸调整:
GraphicsWidth = MenuWidth + 640确保图形区域恰好 640 像素宽(菜单栏占 MenuWidth 像素)。480 像素高是标准 4:3 比例 - 内层循环(50 帧):每帧递增 RotationX/Y/Z:
- X 轴每帧旋转 10 度,一轮完整旋转 500 度
- Y 轴按 X 的三分之一比例旋转,产生复杂的复合旋转
- Z 轴每帧旋转 0.1 度(缓慢的面内旋转)
- graphics::draw():触发 Gmsh 的渲染管线,将当前 OpenGL 场景绘制到屏幕
- 帧导出(已注释):
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 注意事项
-
文件路径与工作目录:
merge()使用相对路径时基于程序的工作目录(通常为可执行文件所在目录或 IDE 运行配置中的工作目录)。在跨平台项目中建议使用绝对路径或相对于可执行文件的路径,避免运行时找不到文件。 -
.pos vs .msh 格式选择:.pos(parsed format)适合演示和小规模数据(几百到几千个元素)。对于实际 CAE 结果(百万级以上节点),务必使用 .msh 格式(model-based view),其数据以二进制存储,加载和渲染效率高出数个数量级。
-
视图选项的标签有效性:
view::option::setNumber/setString要求 viewTag 对应的 View 必须存在,否则 Gmsh 会在内部报错。在merge()后应立即使用view::getTags()验证 View 数量。 -
动画性能:
graphics::draw()是同步渲染调用,每次调用都会完整执行 GPU 渲染管线。在高帧率动画中(如 50fps),确保场景复杂度与硬件能力匹配。对于大规模数据集,考虑降低渲染质量选项(如禁用 SmoothNormals、减少 NbIso)以提升帧率。 -
RaiseZ 的缩放:
RaiseZ使用原始标量值乘以该因子。如果标量场数值范围很大(如温度 293-1500 K),直接使用需要适当的缩放因子。本例中0.01/max*t将抬升量归一化到 0-0.01 范围,避免抬升过大导致几何严重扭曲。 -
图像导出尺寸:
GraphicsWidth/GraphicsHeight需要在draw()之前设置。导出图像的分辨率由这两个选项决定,而非屏幕分辨率。对于出版物级插图,建议至少 1920x1080 以上。 -
-nopopup参数的使用:在批量导出动画帧时,使用-nopopup跳过 GUI 可以减少初始化时间和内存占用。但注意此时fltk::run()不会被调用,程序在动画循环结束后直接退出。 -
旋转角度的累积:
RotationX每帧递增 10 度且不重置,在多轮循环中角度会持续累积(第1轮结束后 +500 度,第2轮 +1000 度...)。这不影响视觉效果(角度按 360 度取模),但在需要精确控制视角时应注意。 -
跨平台颜色差异:
setColor()的 RGB 值在不同操作系统和显示器上可能有轻微的颜色差异。对于出版物用途,建议使用 CMYK 规范的标准化颜色。 -
编译与依赖:本章代码需链接 Gmsh 库并确保头文件路径正确。Windows 下使用 MSVC 的编译命令示例:
cl /EHsc /Fe:ch19.exe ch19.cpp /I <gmsh_include_path> /link <gmsh_lib_path>\gmsh.libLinux/macOS 下使用:g++ -o ch19 ch19.cpp -lgmsh -std=c++11