9.3 四面体重网格化 (Tetrahedral_remeshing)

上一节: 9.2 曲面网格生成

9.3.1 数学理论基础

重网格化问题定义

四面体重网格化(Tetrahedral Remeshing)是改善现有四面体网格质量的过程。给定一个四面体网格 ,重网格化的目标是:

  1. 改善质量:提高四面体的形状质量
  2. 调整尺寸:根据尺寸场调整网格密度
  3. 保持特征:保持几何特征(边、角)
  4. 保持拓扑:保持原始网格的拓扑结构

质量度量

1. 半径-边比(Radius-Edge Ratio)

其中 是外接球半径, 是最短边长。

  • 等边四面体:
  • 退化四面体:

2. 二面角(Dihedral Angle)

四面体两个面之间的夹角,范围

  • 等边四面体:
  • 薄片(Sliver):二面角接近

3. 体积-边比(Volume-Edge Ratio)

其中 是体积, 是边长的均方根。

重网格化操作

CGAL的Tetrahedral_remeshing基于局部网格修改操作

1. 边分裂(Edge Split)

将一条边在中点处分裂,连接相邻顶点形成新四面体。

    v3                    v3
   /|\                   /|\
  / | \                 / | \
 /  |  \               /  |  \
v1--*--v2   -->    v1--c--v2
 \  |  /               \  |  /
  \ | /                 \ | /
   \|/                   \|/
    v4                    v4

2. 边折叠(Edge Collapse)

将一条边折叠为一个顶点,删除相邻四面体。

3. 边翻转(Edge Flip)

改变边的连接方式,改变局部拓扑。

2-3翻转:两个四面体 -> 三个四面体
3-2翻转:三个四面体 -> 两个四面体(逆操作)
4-4翻转:四个四面体重新排列

4. 顶点重定位(Vertex Relocation)

移动顶点位置以改善局部质量。

Laplacian平滑

优化平滑:求解局部优化问题

尺寸场自适应

重网格化支持自适应尺寸场

  1. 分裂过大边:当边长 时分裂
  2. 折叠过小边:当边长 时折叠
  3. 保持目标尺寸:边长维持在 范围内

9.3.2 架构分析

整体架构

Tetrahedral_remeshing
├── Remeshing_triangulation(重网格化三角剖分)
│   ├── Tetrahedral_remeshing_triangulation
│   └── Remeshing_vertex_base
│
├── Remeshing_algorithm(重网格化算法)
│   ├── Split_edges(边分裂)
│   ├── Collapse_edges(边折叠)
│   ├── Flip_edges(边翻转)
│   └── Move_vertices(顶点移动)
│
├── Sizing_field(尺寸场)
│   ├── Constant_sizing_field
│   ├── Adaptive_sizing_field
│   └── Custom_sizing_field
│
└── Criteria(质量标准)
    ├── Dihedral_angle_bound
    └── Cell_quality_threshold

核心类

1. 重网格化三角剖分

template <class Gt, class Vb = Default, class Cb = Default>
class Tetrahedral_remeshing_triangulation
    : public Delaunay_triangulation_3<Gt, Tds> {
public:
    // 额外的重网格化数据
    bool is_in_domain(Cell_handle c) const;
    void set_subdomain(Cell_handle c, Subdomain_index idx);
    
    // 边访问
    Edge_iterator edges_in_domain_begin() const;
    Edge_iterator edges_in_domain_end() const;
};

2. 重网格化算法

template <class Tr>
class Tetrahedral_remeshing {
public:
    // 构造函数
    Tetrahedral_remeshing(Tr& tr, 
                          const Sizing_field& sizing,
                          const Remeshing_options& options);
    
    // 执行重网格化
    void remesh();
    
    // 分步执行
    void split_edges();
    void collapse_edges();
    void flip_edges();
    void smooth_vertices();
};

3. 尺寸场概念

class Sizing_field {
public:
    // 必需的操作
    FT operator()(const Point_3& p) const;  // 在点p处的目标尺寸
    FT operator()(const Edge& e) const;      // 在边e上的目标尺寸
};

算法流程

输入:四面体网格 M,尺寸场 ψ,迭代次数 N
输出:改善后的网格 M'

for iter = 1 to N:
    // 1. 边分裂
    for each edge e in M:
        if length(e) > ψ(e):
            split_edge(e)
    
    // 2. 边折叠
    for each edge e in M:
        if length(e) < ψ(e)/√2:
            if collapse_preserves_topology(e):
                collapse_edge(e)
    
    // 3. 边翻转
    for each edge e in M:
        if flip_improves_quality(e):
            flip_edge(e)
    
    // 4. 顶点平滑
    for each vertex v in M:
        if v is not on feature:
            v_new = optimize_position(v)
            if quality_improves(v_new):
                move_vertex(v, v_new)

// 5. 投影到边界
project_boundary_vertices_to_surface()

9.3.3 实现细节

1. 初始化三角剖分

#include <CGAL/Tetrahedral_remeshing/Remeshing_triangulation.h>
 
// 类型定义
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Tetrahedral_remeshing::Remeshing_triangulation<K> Remeshing_triangulation;
 
typedef Remeshing_triangulation::Point Point;
typedef Remeshing_triangulation::Vertex_handle Vertex_handle;
typedef Remeshing_triangulation::Cell_handle Cell_handle;
 
// 创建三角剖分
Remeshing_triangulation tr;
 
// 从现有网格初始化
// 方法1:从顶点-单元列表
std::vector<Point> points;
std::vector<std::array<int, 4>> cells;
// ... 填充点和单元 ...
 
for (const auto& p : points) {
    tr.insert(p);
}
 
// 标记域
for (auto cit = tr.finite_cells_begin(); cit != tr.finite_cells_end(); ++cit) {
    tr.set_subdomain(cit, 1);  // 标记为域内
}

2. 定义尺寸场

#include <CGAL/Tetrahedral_remeshing/Sizing_field.h>
 
// 常数尺寸场
struct Constant_sizing {
    double target_size;
    
    double operator()(const Point& p) const {
        return target_size;
    }
    
    double operator()(const Remeshing_triangulation::Edge& e) const {
        return target_size;
    }
};
 
// 自适应尺寸场
struct Adaptive_sizing {
    double operator()(const Point& p) const {
        double r = std::sqrt(p.x()*p.x() + p.y()*p.y() + p.z()*p.z());
        return 0.1 / (1.0 + r);  // 远离原点处更粗
    }
};
 
// 从背景网格插值
typedef CGAL::Tetrahedral_remeshing::Interpolated_sizing_field<K> Sizing_field;
Sizing_field sizing(background_mesh);

3. 执行重网格化

#include <CGAL/tetrahedral_remeshing.h>
 
// 设置选项
CGAL::Tetrahedral_remeshing::Remeshing_options options;
options.number_of_iterations = 3;
options.preserve_features = true;
options.smooth_constrained_edges = false;
 
// 执行重网格化
Constant_sizing sizing(0.1);  // 目标尺寸0.1
CGAL::tetrahedral_remeshing(tr, sizing, options);

4. 特征保护

// 标记特征边
void mark_features(Remeshing_triangulation& tr) {
    // 基于二面角检测特征边
    for (auto eit = tr.finite_edges_begin(); eit != tr.finite_edges_end(); ++eit) {
        double dihedral_angle = compute_dihedral_angle(tr, *eit);
        
        if (dihedral_angle < CGAL::to_radians(120.0)) {
            // 标记为特征边
            tr.set_feature(*eit, true);
        }
    }
}
 
// 标记边界边
void mark_boundary(Remeshing_triangulation& tr) {
    for (auto eit = tr.finite_edges_begin(); eit != tr.finite_edges_end(); ++eit) {
        if (tr.is_boundary(*eit)) {
            tr.set_feature(*eit, true);
        }
    }
}

5. 质量评估

#include <CGAL/Tetrahedral_remeshing/Quality.h>
 
// 计算四面体质量
template <class Cell_handle>
double compute_radius_edge_ratio(Cell_handle c) {
    // 外接球半径
    auto circumsphere = circumcenter(c);
    double R = std::sqrt(CGAL::squared_distance(
        circumsphere.first, c->vertex(0)->point()));
    
    // 最短边
    double L_min = std::numeric_limits<double>::max();
    for (int i = 0; i < 4; i++) {
        for (int j = i+1; j < 4; j++) {
            double L = std::sqrt(CGAL::squared_distance(
                c->vertex(i)->point(), c->vertex(j)->point()));
            L_min = std::min(L_min, L);
        }
    }
    
    return R / L_min;
}
 
// 统计网格质量
void analyze_quality(const Remeshing_triangulation& tr) {
    double min_ratio = std::numeric_limits<double>::max();
    double max_ratio = 0;
    double sum_ratio = 0;
    int count = 0;
    
    for (auto cit = tr.finite_cells_begin(); cit != tr.finite_cells_end(); ++cit) {
        if (tr.is_in_domain(cit)) {
            double ratio = compute_radius_edge_ratio(cit);
            min_ratio = std::min(min_ratio, ratio);
            max_ratio = std::max(max_ratio, ratio);
            sum_ratio += ratio;
            count++;
        }
    }
    
    std::cout << "质量统计:" << std::endl;
    std::cout << "  最小半径-边比: " << min_ratio << std::endl;
    std::cout << "  最大半径-边比: " << max_ratio << std::endl;
    std::cout << "  平均半径-边比: " << sum_ratio / count << std::endl;
}

9.3.4 代码示例

完整示例:网格优化

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Tetrahedral_remeshing/Remeshing_triangulation.h>
#include <CGAL/tetrahedral_remeshing.h>
#include <CGAL/IO/File_medit.h>
#include <iostream>
#include <fstream>
 
// 类型定义
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Tetrahedral_remeshing::Remeshing_triangulation<K> Tr;
 
typedef Tr::Point Point;
typedef Tr::Vertex_handle Vertex_handle;
typedef Tr::Cell_handle Cell_handle;
 
// 尺寸场
struct Sizing_field {
    double target_size;
    
    Sizing_field(double size) : target_size(size) {}
    
    double operator()(const Point& p) const {
        return target_size;
    }
    
    double operator()(const Tr::Edge& e) const {
        return target_size;
    }
};
 
int main(int argc, char* argv[]) {
    // 读取输入网格
    Tr tr;
    std::ifstream input(argv[1]);
    CGAL::IO::read_MEDIT(input, tr);
    input.close();
    
    std::cout << "输入网格:" << std::endl;
    std::cout << "  顶点数: " << tr.number_of_vertices() << std::endl;
    std::cout << "  单元数: " << tr.number_of_finite_cells() << std::endl;
    
    // 分析初始质量
    analyze_quality(tr);
    
    // 设置尺寸场
    Sizing_field sizing(0.1);  // 目标尺寸0.1
    
    // 设置选项
    CGAL::Tetrahedral_remeshing::Remeshing_options options;
    options.number_of_iterations = 3;
    options.preserve_features = true;
    
    // 执行重网格化
    CGAL::tetrahedral_remeshing(tr, sizing, options);
    
    std::cout << "\n输出网格:" << std::endl;
    std::cout << "  顶点数: " << tr.number_of_vertices() << std::endl;
    std::cout << "  单元数: " << tr.number_of_finite_cells() << std::endl;
    
    // 分析最终质量
    analyze_quality(tr);
    
    // 保存结果
    std::ofstream output("remeshed.mesh");
    CGAL::IO::write_MEDIT(output, tr);
    output.close();
    
    return 0;
}

自适应重网格化

// 基于曲率的自适应尺寸场
struct Curvature_based_sizing {
    Tr& tr;
    double min_size, max_size;
    
    double operator()(const Point& p) const {
        // 估计局部曲率(简化示例)
        double curvature = estimate_curvature(tr, p);
        
        // 高曲率区域使用更细的网格
        double size = max_size / (1.0 + curvature);
        return std::clamp(size, min_size, max_size);
    }
    
    double estimate_curvature(const Tr& tr, const Point& p) const {
        // 查找最近顶点
        Vertex_handle v = tr.nearest_vertex(p);
        
        // 基于邻域法向变化估计曲率
        std::vector<Vector> normals;
        auto vc = tr.incident_cells(v);
        auto done = vc;
        
        do {
            if (!tr.is_infinite(vc)) {
                normals.push_back(compute_cell_normal(vc));
            }
            ++vc;
        } while (vc != done);
        
        // 计算法向变化
        double curvature = 0;
        for (size_t i = 0; i < normals.size(); i++) {
            for (size_t j = i+1; j < normals.size(); j++) {
                curvature = std::max(curvature, 
                    1.0 - std::abs(normals[i] * normals[j]));
            }
        }
        
        return curvature;
    }
};

多域重网格化

// 多域尺寸场
struct Multi_domain_sizing {
    std::map<int, double> subdomain_sizes;
    
    double operator()(const Tr::Cell_handle c) const {
        int subdomain = c->subdomain_index();
        auto it = subdomain_sizes.find(subdomain);
        if (it != subdomain_sizes.end()) {
            return it->second;
        }
        return 0.1;  // 默认尺寸
    }
};
 
void multi_domain_remeshing(Tr& tr) {
    Multi_domain_sizing sizing;
    sizing.subdomain_sizes[1] = 0.05;  // 域1使用细网格
    sizing.subdomain_sizes[2] = 0.2;   // 域2使用粗网格
    
    // 标记域间界面为特征
    for (auto fit = tr.finite_facets_begin(); fit != tr.finite_facets_end(); ++fit) {
        Cell_handle c1 = fit->first;
        Cell_handle c2 = c1->neighbor(fit->second);
        
        if (!tr.is_infinite(c2) && c1->subdomain_index() != c2->subdomain_index()) {
            // 标记界面边为特征
            for (int i = 0; i < 3; i++) {
                int vi = (fit->second + i + 1) % 4;
                int vj = (fit->second + (i+1)%3 + 1) % 4;
                Tr::Edge e(c1, vi, vj);
                tr.set_feature(e, true);
            }
        }
    }
    
    CGAL::tetrahedral_remeshing(tr, sizing);
}

迭代优化

void iterative_optimization(Tr& tr, int max_iterations) {
    double target_quality = 2.0;  // 目标半径-边比
    
    for (int iter = 0; iter < max_iterations; iter++) {
        std::cout << "迭代 " << iter + 1 << std::endl;
        
        // 计算当前质量
        double worst_quality = compute_worst_quality(tr);
        std::cout << "  最差质量: " << worst_quality << std::endl;
        
        if (worst_quality < target_quality) {
            std::cout << "  达到目标质量,停止" << std::endl;
            break;
        }
        
        // 自适应尺寸场
        auto sizing = create_adaptive_sizing(tr, target_quality);
        
        // 执行重网格化
        CGAL::Tetrahedral_remeshing::Remeshing_options options;
        options.number_of_iterations = 1;
        CGAL::tetrahedral_remeshing(tr, sizing, options);
    }
}

9.3.5 复杂度分析

时间复杂度

操作复杂度说明
边分裂局部操作
边折叠d为边度数
边翻转局部操作
顶点平滑d为顶点度数
单次迭代n为网格规模
完整重网格化k为迭代次数

空间复杂度

  • 三角剖分存储
  • 特征标记
  • 临时数据结构

总空间复杂度:

收敛性

经验观察

  • 通常3-5次迭代即可达到稳定状态
  • 每次迭代显著改善最差质量单元
  • 平均质量单调改善

9.3.6 理论保证

拓扑保持

重网格化算法保证:

  1. 同胚保持:输出网格与输入网格同胚
  2. 边界保持:边界拓扑不变
  3. 特征保持:标记的特征边/角保持

质量改善

经验保证

  • 最差半径-边比通常改善50%以上
  • 平均质量显著改善
  • 消除大部分薄片单元

限制

  • 无法保证达到任意目标质量
  • 受限于输入网格拓扑
  • 特征约束可能限制优化空间

9.3.7 与其他组件的关系

Tetrahedral_remeshing
├── 依赖
│   ├── Delaunay_triangulation_3(基础三角剖分)
│   ├── Mesh_3(网格生成)
│   └── Triangulation_3(三角剖分操作)
│
├── 用于
│   ├── Mesh_3输出优化
│   ├── 有限元预处理
│   └── 多尺度模拟
│
└── 相关工具
    ├── CGAL::Polygon_mesh_processing(表面重网格化)
    └── CGAL::Mesh_3(初始网格生成)

9.3.8 应用

1. 有限元网格优化

// 优化用于FEM的网格
void optimize_fem_mesh(Tr& tr) {
    // 基于应力场的自适应尺寸
    Stress_based_sizing sizing(stress_field);
    
    // 保持几何特征
    mark_geometric_features(tr);
    
    // 重网格化
    CGAL::tetrahedral_remeshing(tr, sizing);
}

2. 多尺度网格

// 创建多尺度网格
void create_multiscale_mesh(Tr& tr) {
    // 近场细网格,远场粗网格
    Distance_from_center_sizing sizing;
    sizing.center = Point(0, 0, 0);
    sizing.near_size = 0.01;
    sizing.far_size = 0.5;
    sizing.transition_distance = 1.0;
    
    CGAL::tetrahedral_remeshing(tr, sizing);
}

3. 网格粗化

// 粗化过细的网格
void coarsen_mesh(Tr& tr, double target_size) {
    Constant_sizing sizing(target_size);
    
    CGAL::Tetrahedral_remeshing::Remeshing_options options;
    options.number_of_iterations = 2;
    
    CGAL::tetrahedral_remeshing(tr, sizing, options);
}

4. 网格细化

// 在特定区域细化网格
void refine_region(Tr& tr, const std::function<bool(Point)>& region) {
    Region_sizing sizing;
    sizing.region = region;
    sizing.in_region_size = 0.01;
    sizing.out_region_size = 0.1;
    
    CGAL::tetrahedral_remeshing(tr, sizing);
}