Files
茂之钳 4b90438315
CI / Build & Test (push) Failing after 24s
CI / Release Build (push) Failing after 31s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled
feat(v1.0.1): P2 improvements — TolerantEdge + .vde format + boolean fallback
P2 — TolerantEdge (ACIS):
- TolerantEdge: tube_radius, path_tolerance, is_within_tube, intersects
- detect_tolerance_conflicts: VertexInSphere/EdgeInTube/FaceGap
- resolve_tolerance_conflicts: auto merge vertices/edges/gaps

P2 — .vde Native Format (ACIS):
- Binary format: VdeHeader(32B), 6 SerializationSections
- save_vde/load_vde: full B-Rep topology + geometry + tolerance + attributes
- save_vde_json: debuggable JSON export

P2 — Boolean Fallback Ladder (Parasolid):
- 4-level cascade: SSI → tolerant → mesh → SDF
- Level 4 never fails (SDF + MC mathematical guarantee)
- BooleanFallbackResult with per-level diagnostics

Total: +~2650 lines across 8 files
2026-07-27 21:01:33 +08:00

427 lines
14 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
/**
* @file brep.h
* @brief 边界表示(B-Rep)核心数据结构
*
* B-RepBoundary Representation)是 CAD 系统中最常用的实体表示方法,
* 通过拓扑层次结构描述三维几何体的边界。
*
* ## 拓扑层次结构
*
* ```
* Body (体) — 一个或多个封闭壳(含名称)
* └─ Shell (壳) — 一组面的连续集合
* └─ Face (面) — 曲面上由环界定的区域
* └─ Loop (环) — 封闭的边界曲线(外环 + 内环/孔)
* └─ Edge (边) — 曲面的边界段
* └─ Vertex (顶点) — 边的端点
* ```
*
* ## 实体引用关系
*
* 每个实体通过整数 ID 唯一标识。父实体通过 ID 向量引用子实体。
* 曲面(NURBS Surface)独立存储,由面通过 surface_id 引用。
* 曲线(NURBS Curve)作为边的几何定义存储。
*
* ## 使用模式
*
* @code{.cpp}
* BrepModel model;
* // 从顶点层开始构建
* int v0 = model.add_vertex(Point3D(0, 0, 0));
* int v1 = model.add_vertex(Point3D(1, 0, 0));
* int e0 = model.add_edge(v0, v1);
* int loop0 = model.add_loop({e0});
* int s0 = model.add_surface(plane_surface);
* int f0 = model.add_face(s0, {loop0});
* int shell0 = model.add_shell({f0});
* int body0 = model.add_body({shell0}, "MyPart");
* @endcode
*
* 更常见的方式是使用 modeling.h 中的高层 APImake_box, extrude 等)。
*
* @ingroup brep
*/
#include "vde/core/point.h"
#include "vde/core/aabb.h"
#include "vde/curves/nurbs_curve.h"
#include "vde/curves/nurbs_surface.h"
#include "vde/brep/trimmed_surface.h"
#include "vde/mesh/halfedge_mesh.h"
#include <vector>
#include <memory>
#include <string>
#include <map>
namespace vde::brep {
using core::Point3D;
using core::Vector3D;
using core::AABB3D;
// 前向声明(定义在 brep_heal.h
struct AutoHealReport;
struct StepResult;
/**
* @brief 边曲线类型枚举
*
* 指定边所使用的曲线表示类别,
* 影响 tessellation、布尔运算和文件交换时的处理方式。
*/
enum class CurveType { Line, Circle, Bezier, BSpline, Nurbs };
// ═══════════════════════════════════════════════
// 拓扑实体定义
// ═══════════════════════════════════════════════
/**
* @brief 拓扑顶点
*
* 三维空间中的一个点,带有容差信息。
* 容差用于判断顶点的"等同性"——在容差范围内的点被视为同一顶点。
*/
struct TopoVertex {
int id; ///< 唯一定点 ID
Point3D point; ///< 三维坐标
double tolerance = 1e-6; ///< 几何容差
};
/**
* @brief 拓扑边
*
* 连接两个顶点的有向曲线段。
* - `reversed = false`: 方向从 v_start 到 v_end
* - `reversed = true`: 方向从 v_end 到 v_start
* - `curve` 可以是 nullptr(隐含直线边)
*/
struct TopoEdge {
int id; ///< 唯一边 ID
int v_start, v_end; ///< 起点和终点顶点 ID
std::shared_ptr<curves::NurbsCurve> curve; ///< 边的几何曲线(可为空,表示直线)
bool reversed = false; ///< 是否相对于曲线参数方向反转
};
/**
* @brief 拓扑环(Loop
*
* 一组首尾相连的边构成的封闭边界。
* - 外环 (is_outer = true): 面的外部边界,逆时针为正
* - 内环 (is_outer = false): 面上的孔,顺时针为正
*/
struct TopoLoop {
int id; ///< 唯一环 ID
std::vector<int> edges; ///< 边 ID 序列(按环的遍历顺序)
bool is_outer = true; ///< true = 外环, false = 内环(孔)
};
/**
* @brief 拓扑面
*
* 曲面上由一个或多个环界定的区域。
* 面的正方向与曲面的法向量一致。
* reversed = true 表示法向量取反。
*/
struct TopoFace {
int id; ///< 唯一面 ID
int surface_id; ///< 关联的曲面 ID
int trimmed_surface_id = -1; ///< 关联的裁剪曲面 ID(-1 表示不使用)
std::vector<int> loops; ///< 环 ID 列表(第一个为外环,后续为内环)
bool reversed = false; ///< 面法向量是否反转
};
/**
* @brief 拓扑壳
*
* 一组面的连续集合,构成空间的封闭或开放边界。
* closed = true 表示壳封闭一个体积(水密)。
*/
struct TopoShell {
int id; ///< 唯一壳 ID
std::vector<int> faces; ///< 面 ID 列表
bool closed = true; ///< 是否为封闭壳
};
/**
* @brief 拓扑体
*
* 一个或多个壳的集合,构成完整的几何实体。
* name 用于在 STEP/IGES 文件中标识零件。
*/
struct TopoBody {
int id; ///< 唯一体 ID
std::vector<int> shells; ///< 壳 ID 列表
std::string name; ///< 体名称(对应 STEP 中的 PRODUCT
};
// ═══════════════════════════════════════════════
// B-Rep 模型类
// ═══════════════════════════════════════════════
/**
* @brief B-Rep 模型
*
* 完整的三维边界表示模型,包含几何和拓扑数据。
*
* ## 主要职责
*
* - **构建**: 通过 add_* 方法从底层构建拓扑结构
* - **查询**: 获取顶点、面、边及它们的拓扑关系
* - **导出**: 转换为三角网格(用于渲染)或导出到文件格式
* - **验证**: 检查水密性和拓扑一致性
*
* ## 容差约定
*
* | 容差 | 典型值 | 用途 |
* |-------------|---------|--------------------------|
* | 顶点容差 | 1e-6 | 判断两点是否视为同一点 |
* | 边曲线容差 | 1e-5 | 曲线拟合精度 |
* | tessellation | 0.01 | 曲面转网格的弦高误差 |
*
* @see brep_validate.h 验证
* @see modeling.h 高层建模 API
* @see brep_boolean.h 布尔运算
*/
class BrepModel {
public:
BrepModel() = default;
/**
* @brief 添加顶点
* @param p 顶点坐标
* @return 新顶点的 ID
*/
int add_vertex(const Point3D& p);
/** @brief 添加顶点(自动去重) * 若已有顶点在 tolerance 内,返回已有顶点索引。 */ int add_vertex_unique(const Point3D& p, double tolerance = 1e-9);
/**
* @brief 添加直线边
* @param v0 起点顶点 ID
* @param v1 终点顶点 ID
* @return 新边的 ID
*/
int add_edge(int v0, int v1);
/**
* @brief 添加曲线边
* @param v0 起点顶点 ID
* @param v1 终点顶点 ID
* @param curve 边的几何曲线
* @return 新边的 ID
*/
int add_edge(int v0, int v1, const curves::NurbsCurve& curve);
/** @brief 添加边(自动去重) * 查找已有边连接相同的两个端点。 */ int add_edge_unique(int v0, int v1, double tolerance = 1e-9);
/**
* @brief 添加环
* @param edges 环中边的 ID 序列(顺序连接)
* @param outer true = 外环, false = 内环
* @return 新环的 ID
*/
int add_loop(const std::vector<int>& edges, bool outer = true);
/**
* @brief 添加面
* @param surface_id 关联曲面 ID
* @param loops 环 ID 列表
* @return 新面的 ID
*/
int add_face(int surface_id, const std::vector<int>& loops);
/**
* @brief 设置面的裁剪曲面关联
* @param face_idx 面索引
* @param trimmed_surface_id 裁剪曲面 ID-1 表示取消关联)
*/
void set_face_trimmed_surface(int face_idx, int trimmed_surface_id);
/**
* @brief 添加壳
* @param faces 面 ID 列表
* @param closed 是否为封闭壳
* @return 新壳的 ID
*/
int add_shell(const std::vector<int>& faces, bool closed = true);
/**
* @brief 添加体
* @param shells 壳 ID 列表
* @param name 体名称
* @return 新体的 ID
*/
int add_body(const std::vector<int>& shells, const std::string& name = "");
/**
* @brief 添加 NURBS 曲面
* @param surf NURBS 曲面定义
* @return 新曲面的 ID
*/
int add_surface(const curves::NurbsSurface& surf);
/**
* @brief 添加裁剪曲面
* @param ts 裁剪曲面定义
* @return 新裁剪曲面的 ID
*/
int add_trimmed_surface(const TrimmedSurface& ts);
// ── 计数查询 ──
/** @brief 顶点数量 */
[[nodiscard]] size_t num_vertices() const { return vertices_.size(); }
/** @brief 边数量 */
[[nodiscard]] size_t num_edges() const { return edges_.size(); }
/** @brief 面数量 */
[[nodiscard]] size_t num_faces() const { return faces_.size(); }
/** @brief 体数量 */
[[nodiscard]] size_t num_bodies() const { return bodies_.size(); }
/** @brief 曲面数量 */
[[nodiscard]] size_t num_surfaces() const { return surfaces_.size(); }
/** @brief 裁剪曲面数量 */
[[nodiscard]] size_t num_trimmed_surfaces() const { return trimmed_surfaces_.size(); }
// ── 元素访问(按数组索引 — 更快,适合顺序遍历) ──
/** @brief 按数组索引访问顶点 */
[[nodiscard]] const TopoVertex& vertex(int id) const { return vertices_[id]; }
/** @brief 更新顶点位置(按数组索引) */
void set_vertex_point(int idx, const Point3D& p) { vertices_[idx].point = p; }
/** @brief 按唯一 ID 访问顶点(适用于边数据中的 ID 引用) */
[[nodiscard]] const TopoVertex& vertex_by_id(int id) const;
/** @brief 按数组索引访问边 */
[[nodiscard]] const TopoEdge& edge(int id) const { return edges_[id]; }
/** @brief 按数组索引访问面 */
[[nodiscard]] const TopoFace& face(int id) const { return faces_[id]; }
/** @brief 按数组索引访问曲面 */
[[nodiscard]] const curves::NurbsSurface& surface(int id) const { return surfaces_[id]; }
/** @brief 按数组索引访问裁剪曲面 */
[[nodiscard]] const TrimmedSurface& trimmed_surface(int id) const { return trimmed_surfaces_[id]; }
/** @brief 按唯一 ID 访问环 */
[[nodiscard]] const TopoLoop& loop_by_id(int id) const;
/** @brief 获取所有环的只读引用 */
[[nodiscard]] const std::vector<TopoLoop>& all_loops() const { return loops_; }
/** @brief 获取所有壳的只读引用 */
[[nodiscard]] const std::vector<TopoShell>& all_shells() const { return shells_; }
/** @brief 获取所有体的只读引用 */
[[nodiscard]] const std::vector<TopoBody>& all_bodies() const { return bodies_; }
/** @brief 获取所有顶点的只读引用 */
[[nodiscard]] const std::vector<TopoVertex>& all_vertices() const { return vertices_; }
/** @brief 获取所有边的只读引用 */
[[nodiscard]] const std::vector<TopoEdge>& all_edges() const { return edges_; }
/** @brief 获取所有面的只读引用 */
[[nodiscard]] const std::vector<TopoFace>& all_faces() const { return faces_; }
// ── 几何/拓扑查询 ──
/**
* @brief 计算模型的轴对齐包围盒
*
* 遍历所有顶点,取最小/最大坐标。
*
* @return 包围盒(AABB
*/
[[nodiscard]] core::AABB3D bounds() const;
/**
* @brief 检查模型有效性(快速版本)
*
* 进行基本检查:是否有顶点/边/面,ID 引用是否有效。
* 完整验证请使用 brep_validate.h 中的 validate()。
*
* @return true 如果模型通过基本检查
*
* @see validate() 完整验证(水密性、方向一致性等)
*/
[[nodiscard]] bool is_valid() const;
/**
* @brief 将 B-Rep 模型 tessellate 为三角网格
*
* 所有曲面和曲线以给定的弦高误差 tessellate 为三角形。
*
* @param deflection 弦高误差(控制 tessellation 精度,默认 0.01
* @return HalfedgeMesh 三角网格
*
* @note deflection 越小网格越精细,但三角形数量显著增加。
*/
[[nodiscard]] mesh::HalfedgeMesh to_mesh(double deflection = 0.01) const;
// ── 邻接关系查询 ──
/**
* @brief 获取面的所有边
*
* 展开面的所有环,收集其中包含的边 ID。
*
* @param face_id 面 ID
* @return 边 ID 列表(去重)
*/
[[nodiscard]] std::vector<int> face_edges(int face_id) const;
/**
* @brief 获取边的所有相邻面
*
* 反向查找:哪些面包含此边。
* 在流形网格中每条边应有恰好 2 个相邻面。
*
* @param edge_id 边 ID
* @return 相邻面 ID 列表
*/
[[nodiscard]] std::vector<int> edge_faces(int edge_id) const;
/**
* @brief 获取顶点的所有相邻边
*
* @param vertex_id 顶点 ID
* @return 相邻边 ID 列表
*/
[[nodiscard]] std::vector<int> vertex_edges(int vertex_id) const;
// ── 友元:拓扑修复 / 欧拉操作需要直接访问内部数据 ──
friend int heal_merge_vertices(BrepModel&, double);
friend int heal_merge_edges(BrepModel&, double);
friend int heal_close_gaps(BrepModel&, double);
friend int heal_gaps(BrepModel&, double);
friend int heal_slivers(BrepModel&);
friend int heal_orientation(BrepModel&);
friend bool heal_topology(BrepModel&, double);
friend int heal_step_import(BrepModel&);
friend AutoHealReport auto_heal_pipeline(BrepModel&, double);
friend StepResult step_stitch(BrepModel&, double);
friend StepResult step_simplify(BrepModel&, double);
friend StepResult step_regularize(BrepModel&, double);
friend StepResult step_orient(BrepModel&);
friend StepResult step_check(const BrepModel&);
friend class EulerOp;
private:
std::vector<TopoVertex> vertices_;
std::vector<TopoEdge> edges_;
std::vector<TopoLoop> loops_;
std::vector<TopoFace> faces_;
std::vector<TopoShell> shells_;
std::vector<TopoBody> bodies_;
std::vector<curves::NurbsSurface> surfaces_;
std::vector<TrimmedSurface> trimmed_surfaces_;
int next_id_ = 0;
};
} // namespace vde::brep