10.3 Alpha Wrap 3D

本章介绍 Alpha Shapes 的扩展——Alpha Wrap 3D,它保证输出封闭、流形、无自交的三角网格表面。

1. 理论基础

1.1 包装问题背景

Alpha Wrap 3D 是 CGAL 中用于计算输入几何数据”包装”(wrap)的算法。与 Alpha Shapes 不同,Alpha Wrap 保证输出是一个封闭、流形、无自交的三角网格表面。

核心思想

  • 从输入数据(点云、三角网格、三角汤)出发
  • 使用 Delaunay 细化算法构造一个”紧密”包围输入的网格
  • 通过 alpha 参数控制网格的精细程度
  • 通过 offset 参数控制与输入数据的距离

1.2 算法概述

Alpha Wrap 算法基于以下核心概念:

  1. Delaunay 三角剖分:维护输入空间的四面体化
  2. 内外标记:将四面体标记为”内部”或”外部”
  3. 门(Gate):内外边界上的三角面片
  4. Steiner 点插入:细化 Delaunay 网格以逼近目标表面

1.3 参数说明

参数含义影响
alpha特征尺寸控制保留的细节程度,较小的 alpha 保留更多特征
offset偏移距离控制输出与输入的距离,必须为正

参数关系

  • offset 默认值为 alpha / 30
  • alpha 越大,网格越粗糙
  • offset 越大,输出离输入越远

2. CGAL Alpha Wrap 3D 架构

2.1 模块组织

Alpha_wrap_3/
  include/CGAL/
    alpha_wrap_3.h                    # 主接口函数
    Alpha_wrap_3/internal/
      Alpha_wrap_3.h                  # 核心算法类
      Alpha_wrap_triangulation_cell_base_3.h    # 单元基类
      Alpha_wrap_triangulation_vertex_base_3.h  # 顶点基类
      gate_priority_queue.h             # 优先队列
      oracles.h                         # Oracle 接口
      Oracle_base.h                     # Oracle 基类
      Point_set_oracle.h                # 点云 Oracle
      Triangle_soup_oracle.h            # 三角汤 Oracle
      Triangle_mesh_oracle.h            # 三角网格 Oracle
      Segment_soup_oracle.h             # 线段汤 Oracle
      geometry_utils.h                  # 几何工具
      offset_intersection.h             # 偏移交点计算

2.2 核心类层次

Alpha_wrapper_3<Oracle>
    ├── Oracle (模板参数)
    │       ├── Point_set_oracle
    │       ├── Triangle_soup_oracle
    │       ├── Triangle_mesh_oracle
    │       └── Segment_soup_oracle
    ├── Triangulation (Delaunay_triangulation_3)
    └── Alpha_PQ (优先队列)

3. 核心实现详解

3.1 Alpha_wrapper_3 类

// Alpha_wrap_3/internal/Alpha_wrap_3.h
template <typename Oracle_, typename Triangulation_ = CGAL::Default>
class Alpha_wrapper_3 {
    using Oracle = Oracle_;
    using Base_GT = typename Oracle::Geom_traits;
    using Default_Gt = CGAL::Robust_circumcenter_filtered_traits_3<Base_GT>;
    
    // 顶点基类包含类型信息
    using Default_Vb = Alpha_wrap_triangulation_vertex_base_3<Default_Gt>;
    // 单元基类包含内外标记
    using Default_Cb = Alpha_wrap_triangulation_cell_base_3<Default_Gt>;
    using Default_Cbt = Cell_base_with_timestamp<Default_Cb>;
    using Default_Tds = CGAL::Triangulation_data_structure_3<Default_Vb, Default_Cbt>;
    using Default_Triangulation = CGAL::Delaunay_triangulation_3<
        Default_Gt, Default_Tds, Fast_location>;
    
public:
    using Triangulation = typename Default::Get<Triangulation_, Default_Triangulation>::type;
    using Geom_traits = typename Triangulation::Geom_traits;
    
private:
    using Cell_handle = typename Triangulation::Cell_handle;
    using Facet = typename Triangulation::Facet;
    using Vertex_handle = typename Triangulation::Vertex_handle;
    using Gate = internal::Gate<Triangulation>;
    using Alpha_PQ = std::stack<Gate>;  // 或使用 Modifiable_priority_queue
    
    // 核心成员
    Oracle m_oracle;                    // 输入数据访问接口
    Triangulation m_tr;                 // Delaunay 三角剖分
    Alpha_PQ m_queue;                   // 待处理的面片队列
    
    // 参数
    FT m_alpha = FT(-1), m_sq_alpha = FT(-1);
    FT m_offset = FT(-1), m_sq_offset = FT(-1);
    
    // 种子点(用于创建初始腔体)
    Seeds m_seeds;
};

3.2 单元标签系统

// Alpha_wrap_triangulation_cell_base_3.h
enum class Cell_label {
    INSIDE = 0,     // 内部单元
    OUTSIDE = 1,    // 外部单元
    MANIFOLD = 2    // 用于保证流形性的临时标记
};
 
template <class GT>
class Alpha_wrap_triangulation_cell_base_3 {
    Cell_label m_label = Cell_label::INSIDE;
    
public:
    bool is_inside() const { return m_label == Cell_label::INSIDE; }
    bool is_outside() const { return m_label == Cell_label::OUTSIDE; }
    bool is_manifold() const { return m_label == Cell_label::MANIFOLD; }
    void set_label(Cell_label label) { m_label = label; }
};

3.3 顶点类型

// Alpha_wrap_triangulation_vertex_base_3.h
enum class Vertex_type {
    DEFAULT = 0,      // 普通顶点(Steiner 点)
    BBOX_VERTEX = 1,  // 包围盒顶点
    SEED_VERTEX = 2   // 种子顶点(用于创建初始腔体)
};
 
template <class GT>
class Alpha_wrap_triangulation_vertex_base_3 {
    Vertex_type m_type = Vertex_type::DEFAULT;
public:
    Vertex_type& type() { return m_type; }
};

3.4 核心算法流程

// 主算法流程
template <typename Visitor>
void operator()(const double alpha, const double offset, 
                OutputMesh& output_mesh, ...) {
    // 1. 初始化
    if (\!initialize(alpha, offset, refining))
        return;
    
    // 2. Alpha 洪水填充(核心细化循环)
    alpha_flood_fill(visitor);
    
    // 3. 保证流形性
    if (do_enforce_manifoldness)
        make_manifold();
    
    // 4. 清除内部连通分量
    if (\!keep_inner_ccs)
        purge_inner_connected_components();
    
    // 5. 提取表面
    extract_surface(output_mesh, ovpm, \!do_enforce_manifoldness);
}

3.5 初始化过程

// 初始化 Delaunay 三角剖分
bool initialize(const double alpha, const double offset, const bool refining) {
    m_alpha = FT(alpha);
    m_sq_alpha = square(m_alpha);
    m_offset = FT(offset);
    m_sq_offset = square(m_offset);
    
    if (refining) {
        // 从现有三角剖分继续
        return initialize_from_existing_triangulation();
    }
    
    // 清空并重新初始化
    m_tr.clear();
    insert_bbox_corners();  // 插入包围盒角点
    
    if (m_seeds.empty())
        return initialize_from_infinity();  // 从无限远开始
    else
        return initialize_with_cavities();  // 从种子点开始
}
 
// 从无限远初始化(标准模式)
bool initialize_from_infinity() {
    for (Cell_handle ch : m_tr.all_cell_handles()) {
        if (m_tr.is_infinite(ch)) {
            ch->set_label(Cell_label::OUTSIDE);
            const int inf_index = ch->index(m_tr.infinite_vertex());
            push_facet(std::make_pair(ch, inf_index));
        } else {
            ch->set_label(Cell_label::INSIDE);
        }
    }
    return true;
}
 
// 使用种子点创建初始腔体
bool initialize_with_cavities() {
    // 在每个种子点周围插入正二十面体顶点
    // 标记这些单元为内部,其余为外部
    // 将边界 facet 加入队列
    for (const Point_3& seed_p : m_seeds) {
        Vertex_handle seed_v = m_tr.insert(seed_p);
        seed_v->type() = AW3i::Vertex_type::SEED_VERTEX;
        
        // 插入正二十面体顶点
        std::array<Point_3, 12> ico_ps = construct_icosahedron(seed_p, 1.65 * m_alpha);
        for (const Point_3& ico_p : ico_ps) {
            Vertex_handle ico_v = m_tr.insert(ico_p, seed_v);
            ico_v->type() = AW3i::Vertex_type::SEED_VERTEX;
        }
    }
    
    // 标记 incident 单元并填充队列
    // ...
}

3.6 Alpha 洪水填充算法

// 核心细化循环
template <typename Visitor>
bool alpha_flood_fill(Visitor& visitor) {
    while (\!m_queue.empty()) {
        if (\!visitor.go_further(*this))
            return false;  // 用户中断
        
        const Gate& gate = m_queue.top();
        const Facet& f = gate.facet();
        Cell_handle ch = f.first;      // 外部单元
        int s = f.second;
        Cell_handle nh = ch->neighbor(s);  // 内部单元
        
        m_queue.pop();
        
        // 计算 Steiner 点
        Point_3 steiner_point;
        if (compute_steiner_point(ch, nh, steiner_point)) {
            // 需要细化:插入 Steiner 点
            insert_steiner_point(steiner_point, nh, visitor);
        } else {
            // 不需要细化:直接标记为外部
            nh->set_label(Cell_label::OUTSIDE);
            // 将新边界 facet 加入队列
            const int mi = m_tr.mirror_index(ch, s);
            for (int i = 1; i < 4; ++i) {
                push_facet(std::make_pair(nh, (mi + i) & 3));
            }
        }
    }
    return true;
}
 
// 计算 Steiner 点
bool compute_steiner_point(const Cell_handle ch, const Cell_handle neighbor,
                          Point_3& steiner_point) const {
    const Point_3& neighbor_cc = circumcenter(neighbor);
    
    // 检查邻居的外心是否在偏移体积内
    Ball_3 neighbor_cc_offset_ball(neighbor_cc, m_sq_offset);
    bool is_neighbor_cc_in_offset = m_oracle.do_intersect(neighbor_cc_offset_ball);
    
    if (is_neighbor_cc_in_offset) {
        const Point_3& ch_cc = circumcenter(ch);
        // Voronoi 边与偏移表面相交
        if (m_oracle.first_intersection(ch_cc, neighbor_cc, steiner_point, m_offset)) {
            return true;
        }
    }
    
    // 检查邻居四面体是否与输入相交
    Tetrahedron_with_outside_info<Geom_traits> tet(neighbor, geom_traits());
    if (m_oracle.do_intersect(tet)) {
        // Steiner 点是输入上最近点沿法向偏移
        const Point_3 closest_pt = m_oracle.closest_point(neighbor_cc);
        Vector_3 unit = closest_pt - neighbor_cc;
        unit = unit * (m_offset / sqrt(unit.squared_length()));
        steiner_point = closest_pt + unit;
        return true;
    }
    
    return false;  // 不需要细化
}
 
// 插入 Steiner 点
void insert_steiner_point(const Point_3& steiner_point, Cell_handle neighbor,
                         Visitor& visitor) {
    // 定位冲突区域
    Locate_type lt;
    int li, lj;
    Cell_handle conflict_cell = m_tr.locate(steiner_point, lt, li, lj, neighbor);
    
    // 查找冲突区域边界
    std::vector<Facet> boundary_facets;
    std::vector<Cell_handle> conflict_zone;
    m_tr.find_conflicts(steiner_point, conflict_cell,
                        std::back_inserter(boundary_facets),
                        std::back_inserter(conflict_zone));
    
    visitor.before_Steiner_point_insertion(*this, steiner_point);
    
    // 插入点
    Vertex_handle vh = m_tr.insert(steiner_point, lt, conflict_cell, li, lj);
    vh->type() = AW3i::Vertex_type::DEFAULT;
    
    visitor.after_Steiner_point_insertion(*this, vh);
    
    // 标记新单元并更新队列
    std::vector<Cell_handle> new_cells;
    m_tr.incident_cells(vh, std::back_inserter(new_cells));
    for (const Cell_handle& new_ch : new_cells) {
        new_ch->set_label(m_tr.is_infinite(new_ch) ? 
                          Cell_label::OUTSIDE : Cell_label::INSIDE);
    }
    
    // 将新边界 facet 加入队列
    for (Cell_handle new_ch : new_cells) {
        for (int i = 0; i < 4; ++i) {
            if (m_tr.is_infinite(new_ch, i)) continue;
            Cell_handle new_nh = new_ch->neighbor(i);
            if (new_nh->label() == new_ch->label()) continue;
            
            Facet boundary_f = std::make_pair(new_ch, i);
            if (new_ch->is_outside())
                push_facet(boundary_f);
            else
                push_facet(m_tr.mirror_facet(boundary_f));
        }
    }
}

3.7 Facet 状态判断

// 判断 facet 是否可以遍历
enum class Facet_status {
    IRRELEVANT = 0,           // 不相关(内部 facet)
    HAS_INFINITE_NEIGHBOR = 1,  // 邻接无限单元(宽松)
    SCAFFOLDING = 2,          // 邻接 BBOX 或 SEED 顶点(宽松)
    TRAVERSABLE = 3           // 可遍历(满足 alpha 条件)
};
 
Facet_status facet_status(const Facet& f) const {
    CGAL_precondition(\!m_tr.is_infinite(f));
    
    Cell_handle ch = f.first;
    int id = f.second;
    CGAL_precondition(ch->is_outside());
    
    Cell_handle nh = ch->neighbor(id);
    
    if (m_tr.is_infinite(nh))
        return Facet_status::HAS_INFINITE_NEIGHBOR;
    
    if (nh->is_outside())
        return Facet_status::IRRELEVANT;
    
    // 检查是否邻接脚手架顶点
    for (int i = 0; i < 3; ++i) {
        Vertex_handle vh = ch->vertex(Triangulation::vertex_triple_index(id, i));
        if (vh->type() == AW3i::Vertex_type::BBOX_VERTEX ||
            vh->type() == AW3i::Vertex_type::SEED_VERTEX) {
            return Facet_status::SCAFFOLDING;
        }
    }
    
    // 检查 alpha 条件
    if (is_traversable(f))
        return Facet_status::TRAVERSABLE;
    
    return Facet_status::IRRELEVANT;
}
 
// 判断 facet 是否满足 alpha 条件
bool is_traversable(const Facet& f) const {
    return less_squared_radius_of_min_empty_sphere(m_sq_alpha, f, m_tr);
}

3.8 流形性保证

// 检测并修复非流形顶点
void make_manifold() {
    std::stack<Vertex_handle> non_manifold_vertices;
    
    // 收集所有非流形顶点
    for (Vertex_handle v : m_tr.finite_vertex_handles()) {
        if (is_non_manifold(v))
            non_manifold_vertices.push(v);
    }
    
    // 逐个修复
    while (\!non_manifold_vertices.empty()) {
        Vertex_handle v = non_manifold_vertices.top();
        non_manifold_vertices.pop();
        
        if (\!is_non_manifold(v))
            continue;
        
        // 找到与 v 关联的外部单元,按大小排序
        std::vector<Cell_handle> inc_cells;
        m_tr.finite_incident_cells(v, std::back_inserter(inc_cells));
        
        std::vector<Cell_handle> outside_cells;
        for (Cell_handle c : inc_cells) {
            if (c->is_outside())
                outside_cells.push_back(c);
        }
        
        // 按最长边排序(优先添加小单元)
        std::stable_sort(outside_cells.begin(), outside_cells.end(),
            [&](Cell_handle l, Cell_handle r) {
                return sq_longest_edge(l) < sq_longest_edge(r);
            });
        
        // 逐个标记为 MANIFOLD 直到修复
        for (Cell_handle c : outside_cells) {
            c->set_label(Cell_label::MANIFOLD);
            if (\!is_non_manifold(v))
                break;
        }
        
        // 检查邻接顶点是否变为非流形
        std::vector<Vertex_handle> adj_vertices;
        m_tr.finite_adjacent_vertices(v, std::back_inserter(adj_vertices));
        for (Vertex_handle nv : adj_vertices) {
            if (is_non_manifold(nv))
                non_manifold_vertices.push(nv);
        }
    }
}
 
// 检测顶点是否非流形
bool is_non_manifold(Vertex_handle v) const {
    // 收集所有与 v 关联的单元
    std::vector<Cell_handle> inc_cells;
    m_tr.incident_cells(v, std::back_inserter(inc_cells));
    
    // 在关联单元中 flood fill 内外连通分量
    // 如果有多个未访问的连通分量,则为非流形
    // ...
}

3.9 Oracle 接口

Oracle 是 Alpha Wrap 与输入数据交互的抽象接口:

// Oracle_base.h
class Oracle_base {
public:
    // 距离查询
    virtual FT squared_distance(const Point_3& p) const = 0;
    virtual Point_3 closest_point(const Point_3& p) const = 0;
    
    // 相交测试
    virtual bool do_intersect(const Tetrahedron_3& tet) const = 0;
    virtual bool do_intersect(const Ball_3& ball) const = 0;
    
    // 射线求交
    virtual bool first_intersection(const Ray_3& ray, Point_3& out) const = 0;
    
    // 包围盒
    virtual Bbox_3 bbox() const = 0;
};
 
// Triangle_mesh_oracle.h
class Triangle_mesh_oracle : public Oracle_base {
    // 使用 AABB 树加速查询
    AABB_tree tree;
    
public:
    template <typename TriangleMesh, typename NamedParameters>
    void add_triangle_mesh(const TriangleMesh& mesh, const NamedParameters& np) {
        // 构建 AABB 树
        tree.insert(faces(mesh).first, faces(mesh).second, mesh, ...);
        tree.build();
    }
    
    Point_3 closest_point(const Point_3& p) const override {
        return tree.closest_point(p);
    }
    
    bool do_intersect(const Tetrahedron_3& tet) const override {
        return tree.do_intersect(tet);
    }
    // ...
};

4. 使用示例

4.1 包装三角网格

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/alpha_wrap_3.h>
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/Real_timer.h>
 
namespace PMP = CGAL::Polygon_mesh_processing;
 
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Point_3 = K::Point_3;
using Mesh = CGAL::Surface_mesh<Point_3>;
 
int main(int argc, char** argv) {
    // 读取输入
    const std::string filename = argv[1];
    Mesh mesh;
    if (\!PMP::IO::read_polygon_mesh(filename, mesh) || 
        is_empty(mesh) || \!is_triangle_mesh(mesh)) {
        std::cerr <> "Invalid input" <> std::endl;
        return EXIT_FAILURE;
    }
    
    std::cout <> "Input: " <> num_vertices(mesh) <> " vertices, "
              <> num_faces(mesh) <> " faces" <> std::endl;
    
    // 计算 alpha 和 offset
    CGAL::Bbox_3 bbox = PMP::bbox(mesh);
    const double diag_length = std::sqrt(
        CGAL::square(bbox.xmax() - bbox.xmin()) +
        CGAL::square(bbox.ymax() - bbox.ymin()) +
        CGAL::square(bbox.zmax() - bbox.zmin()));
    
    const double relative_alpha = 20.0;   // 相对 alpha 值
    const double relative_offset = 600.0; // 相对 offset 值
    
    const double alpha = diag_length / relative_alpha;
    const double offset = diag_length / relative_offset;
    
    std::cout <> "alpha: " <> alpha <> ", offset: " <> offset <> std::endl;
    
    // 构造包装
    CGAL::Real_timer t;
    t.start();
    
    Mesh wrap;
    CGAL::alpha_wrap_3(mesh, alpha, offset, wrap);
    
    t.stop();
    std::cout <> "Result: " <> num_vertices(wrap) <> " vertices, "
              <> num_faces(wrap) <> " faces" <> std::endl;
    std::cout <> "Took " <> t.time() <> " s." <> std::endl;
    
    // 保存结果
    CGAL::IO::write_polygon_mesh("wrap.off", wrap, 
        CGAL::parameters::stream_precision(17));
    
    return EXIT_SUCCESS;
}

4.2 包装点云

#include <CGAL/alpha_wrap_3.h>
 
using Point_3 = K::Point_3;
using Mesh = CGAL::Surface_mesh<Point_3>;
 
int main() {
    std::vector<Point_3> points;
    // ... 填充点云数据 ...
    
    const double alpha = 0.5;
    const double offset = alpha / 30.0;
    
    Mesh wrap;
    CGAL::alpha_wrap_3(points, alpha, offset, wrap);
    
    std::cout <> "Wrapped point set with " <> points.size() <> " points" <> std::endl;
    std::cout <> "Result: " <> num_vertices(wrap) <> " vertices" <> std::endl;
    
    return 0;
}

4.3 使用种子点

#include <CGAL/alpha_wrap_3.h>
 
int main() {
    Mesh mesh;
    // ... 加载网格 ...
    
    // 定义种子点(用于创建初始腔体)
    std::vector<Point_3> seeds = {
        Point_3(0, 0, 0),
        Point_3(10, 0, 0),
        Point_3(5, 10, 0)
    };
    
    Mesh wrap;
    CGAL::alpha_wrap_3(mesh, alpha, offset, wrap,
        CGAL::parameters::seed_points(seeds));
    
    return 0;
}

4.4 渐进式包装(多分辨率)

#include <CGAL/alpha_wrap_3.h>
 
int main() {
    Mesh mesh;
    // ... 加载网格 ...
    
    Mesh wrap;
    
    // 第一次包装(粗粒度)
    double alpha1 = 1.0;
    CGAL::alpha_wrap_3(mesh, alpha1, alpha1/30.0, wrap);
    CGAL::IO::write_polygon_mesh("wrap_coarse.off", wrap);
    
    // 第二次包装(细粒度,复用三角剖分)
    double alpha2 = 0.5;
    CGAL::alpha_wrap_3(mesh, alpha2, alpha2/30.0, wrap,
        CGAL::parameters::refine_triangulation(true));
    CGAL::IO::write_polygon_mesh("wrap_fine.off", wrap);
    
    return 0;
}

4.5 使用访问者监控进度

#include <CGAL/alpha_wrap_3.h>
 
struct MyVisitor {
    void on_alpha_wrapping_begin(const auto& wrapper) {
        std::cout <> "Alpha wrapping started" <> std::endl;
    }
    
    void on_flood_fill_begin(const auto& wrapper) {
        std::cout <> "Flood fill started" <> std::endl;
    }
    
    bool go_further(const auto& wrapper) {
        // 可以添加超时检查或用户中断
        return true;
    }
    
    void before_facet_treatment(const auto& wrapper, const auto& gate) {
        // 处理每个 facet 前的回调
    }
    
    void before_Steiner_point_insertion(const auto& wrapper, const Point_3& p) {
        std::cout <> "Inserting Steiner point at " <> p <> std::endl;
    }
    
    void on_alpha_wrapping_end(const auto& wrapper) {
        std::cout <> "Alpha wrapping completed" <> std::endl;
    }
};
 
int main() {
    Mesh mesh;
    // ... 加载网格 ...
    
    MyVisitor visitor;
    Mesh wrap;
    CGAL::alpha_wrap_3(mesh, alpha, offset, wrap,
        CGAL::parameters::visitor(visitor));
    
    return 0;
}

5. 复杂度分析

5.1 时间复杂度

阶段复杂度说明
初始化O(n log n)构建初始 Delaunay 三角剖分
Oracle 查询O(log n) ~ O(n)取决于输入类型和 AABB 树效率
洪水填充O(m log m)m 是最终顶点数
Steiner 点插入O(log m) 均摊Delaunay 插入
流形性保证O(m)通常很快
表面提取O(m)遍历所有面
总计O((n+m) log(n+m))n 输入规模,m 输出规模

5.2 空间复杂度

数据结构空间说明
Delaunay 三角剖分O(m)m 是顶点数
Oracle (AABB 树)O(n)n 是输入规模
优先队列O(m^{2/3})边界 facet 数量
输出网格O(m)三角网格
总计O(n + m)线性空间

6. 关键文件位置

文件路径说明
/home/chunibyo/workspace/osc/cgal/Alpha_wrap_3/include/CGAL/alpha_wrap_3.h主接口函数
/home/chunibyo/workspace/osc/cgal/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h核心算法类
/home/chunibyo/workspace/osc/cgal/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_triangulation_cell_base_3.h单元基类
/home/chunibyo/workspace/osc/cgal/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_triangulation_vertex_base_3.h顶点基类
/home/chunibyo/workspace/osc/cgal/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/gate_priority_queue.h优先队列实现
/home/chunibyo/workspace/osc/cgal/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/oracles.hOracle 接口定义
/home/chunibyo/workspace/osc/cgal/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Triangle_mesh_oracle.h三角网格 Oracle
/home/chunibyo/workspace/osc/cgal/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Point_set_oracle.h点云 Oracle

7. 注意事项

  1. 数值类型

    • 必须使用浮点型内核(如 Epick
    • 精确内核不被支持,因为算法涉及二分查找
  2. 参数选择

    • alpha 应该根据输入数据的特征尺寸选择
    • offset 默认 alpha/30 通常效果良好
    • 过大的 alpha 可能导致丢失细小特征
  3. 输入要求

    • 三角网格必须是封闭的(用于 Oracle 查询)
    • 点云应该足够稠密
    • 输入可以有噪声
  4. 输出特性

    • 保证是封闭流形
    • 保证无自交
    • 严格包含输入数据
    • 可能包含额外的”脚手架”几何
  5. 性能优化

    • 使用 refine_triangulation 参数进行渐进式细化
    • 可以使用种子点控制包装的起始位置
    • 可以通过访问者实现进度监控和中断