Skip to content

第11章 STL重网格:无CAD模型的表面重构

11.1 学习目标

  • 理解CAE前处理中"只有STL网格、无CAD几何"场景的处理流程
  • 掌握 classifySurfaces() 进行拓扑识别和 createGeometry() 生成参数化几何
  • 学会对离散表面重新划分网格(改变密度/单元类型)
  • 了解ONELAB参数化接口的基本使用

11.2 核心概念说明

11.2.1 为什么需要STL重网格

在CAE前处理中,模型的来源多种多样:

  • 逆向工程:3D扫描 → STL点云 → 三角面片
  • 医学影像:CT/MRI → 分割 → STL表面网格
  • 外部数据:第三方软件导出的STL(可能不含CAD信息)

这些场景的共同特点是:只有离散的三角面片,没有参数化CAD几何。直接使用原始STL做有限元分析通常会遇到网格质量差、单元数量不可控、无法局部细化等问题。

Gmsh提供了从STL重建参数化几何、然后重新划分网格的能力。

11.2.2 处理流程

STL文件 ──merge()──> 离散三角网格
  └── classifySurfaces() ──> 识别尖锐特征,创建离散实体(面/线/点)
  └── createGeometry()    ──> 为每个离散面计算参数化(u,v)
  └── addSurfaceLoop + Volume ──> 构建体
  └── mesh::generate()    ──> 在参数化几何上重新划分网格

如果有STEP等CAD格式可用(参见第18章),优先使用CAD模型而非STL重网格。CAD几何的精度更高、参数化更光滑,网格质量也更好。

11.2.3 ONELAB参数接口

Gmsh提供了ONELAB参数系统,允许在GUI中动态调整参数并触发热更新。本章示例通过 gmsh::onelab::set() 定义三个参数(分类角度、强制参数化、趣味尺寸场),在GUI中修改后自动重新构建几何和网格。

11.3 C++代码逐段讲解

11.3.1 导入STL文件

#include <gmsh.h>

gmsh::initialize();
gmsh::model::add("t13");

try {
    gmsh::merge("../t13_data.stl");   // 加载STL三角面片
} catch(...) {
    gmsh::logger::write("Could not load STL mesh: bye!");
    return;
}

merge() 可以加载STL、MSH、POS等多种格式。try/catch保护是为了在文件缺失时优雅退出。

11.3.2 表面分类(拓扑识别)

double angle = 40;  // 尖锐边判定角度(度)
bool includeBoundary = true;
bool forceParametrizablePatches = false;
double curveAngle = 180;  // 强制分割曲线的角度

gmsh::model::mesh::classifySurfaces(
    angle * M_PI / 180.,      // 弧度=角度*pi/180
    includeBoundary,           // 包含边界边
    forceParametrizablePatches,// 强制生成可参数化的patch
    curveAngle * M_PI / 180.   // 曲线分割角度
);

classifySurfaces() 做四件事: 1. 检测相邻三角面片间的二面角,大于 angle 的边标记为尖锐边 2. 沿尖锐边将原始表面分割为多个离散面(discrete surface) 3. 创建离散曲线(discrete curve,标签从1开始)和离散点 4. 建立面-线-点的拓扑关系

参数说明: - angle:越小越敏感,识别出更多尖锐特征(更多分割) - includeBoundary:开表面是否将边界纳入分类 - forceParametrizablePatches:对复杂patch强制分割为可参数化的小patch - curveAngle:大于此角度的曲线节点会被分割

11.3.3 生成参数化几何

gmsh::model::mesh::createGeometry();

createGeometry() 为每个离散面计算 $R^2 \rightarrow R^3$ 的参数映射(u, v坐标),使得后续可以在参数空间上重新划分网格。这一步是STL重网格的关键。

11.3.4 构建体并设置网格尺寸

// 获取所有离散面,构建SurfaceLoop和Volume
std::vector<std::pair<int, int>> s;
gmsh::model::getEntities(s, 2);   // 获取所有2D实体
std::vector<int> sl;
for(auto surf : s) sl.push_back(surf.second);

int l = gmsh::model::geo::addSurfaceLoop(sl);
gmsh::model::geo::addVolume({l});
gmsh::model::geo::synchronize();

// 用MathEval尺寸场控制重网格密度
int f = gmsh::model::mesh::field::add("MathEval");
gmsh::model::mesh::field::setString(f, "F", "4");  // 全场均匀尺寸=4
gmsh::model::mesh::field::setAsBackgroundMesh(f);

gmsh::model::mesh::generate(3);

尺寸场设为常数 "F"="4" 得到全场均匀网格。你可以用更复杂的表达式来实现局部细化。

11.4 完整可运行代码

// ch11_stl_remesh.cpp — STL重网格示例
#include <set>
#include <cmath>
#include <gmsh.h>

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

    // 1. 导入STL文件
    try {
        gmsh::merge("path/to/your_model.stl");
    } catch(...) {
        gmsh::logger::write("STL file not found!");
        gmsh::finalize();
        return 1;
    }

    // 2. 表面分类(角度=40度,含边界)
    double angle = 40;
    gmsh::model::mesh::classifySurfaces(
        angle * M_PI / 180., true, false, M_PI);

    // 3. 创建参数化几何
    gmsh::model::mesh::createGeometry();

    // 4. 构建体(如果STL是闭合的)
    std::vector<std::pair<int, int>> s;
    gmsh::model::getEntities(s, 2);
    std::vector<int> sl;
    for(auto &e : s) sl.push_back(e.second);
    int l = gmsh::model::geo::addSurfaceLoop(sl);
    gmsh::model::geo::addVolume({l});
    gmsh::model::geo::synchronize();

    // 5. 设置网格尺寸并生成
    int f = gmsh::model::mesh::field::add("MathEval");
    gmsh::model::mesh::field::setString(f, "F", "4");
    gmsh::model::mesh::field::setAsBackgroundMesh(f);
    gmsh::model::mesh::generate(3);

    gmsh::write("remeshed.msh");

    std::set<std::string> args(argv, argv + argc);
    if(!args.count("-nopopup")) gmsh::fltk::run();
    gmsh::finalize();
    return 0;
}

11.5 关键API速查表

API 说明
merge(filename) 加载STL/MSH/POS等文件
mesh::classifySurfaces(angle, boundary, forceParam, curveAngle) 识别尖锐特征,创建离散实体和拓扑
mesh::createGeometry() 为离散面生成参数化(u,v)映射
geo::addSurfaceLoop({surfaceTags}) 由面列表构建面环
geo::addVolume({surfaceLoopTags}) 由面环列表构建体

11.6 注意事项

  1. 优先使用CAD格式:如果有STEP/IGES可用,使用第18章的OpenCASCADE导入方法,几何精度远高于STL重建
  2. 分类角度调节angle 太小会产生过多碎面(难以参数化),太大则丢失几何特征。40°是经验出发点
  3. 闭合性要求:STL必须是水密的(watertight)才能构建Volume;开表面只能做2D重网格
  4. 参数化质量:极细长的patch可能导致参数化失败,可尝试 forceParametrizablePatches=true
  5. 大模型性能:STL面片数超过数十万时,classifySurfaces()createGeometry() 可能耗时较长