2026-07-23 07:19:13 +00:00
|
|
|
|
#pragma once
|
2026-07-24 11:09:45 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @file brep.h
|
|
|
|
|
|
* @brief 边界表示(B-Rep)核心数据结构
|
|
|
|
|
|
*
|
|
|
|
|
|
* B-Rep(Boundary 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 中的高层 API(make_box, extrude 等)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @ingroup brep
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
#include "vde/core/point.h"
|
|
|
|
|
|
#include "vde/core/aabb.h"
|
|
|
|
|
|
#include "vde/curves/nurbs_curve.h"
|
|
|
|
|
|
#include "vde/curves/nurbs_surface.h"
|
|
|
|
|
|
#include "vde/mesh/halfedge_mesh.h"
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
|
|
|
|
namespace vde::brep {
|
2026-07-23 08:19:24 +00:00
|
|
|
|
using core::Point3D;
|
|
|
|
|
|
using core::Vector3D;
|
|
|
|
|
|
using core::AABB3D;
|
2026-07-23 07:19:13 +00:00
|
|
|
|
|
2026-07-24 11:09:45 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 边曲线类型枚举
|
|
|
|
|
|
*
|
|
|
|
|
|
* 指定边所使用的曲线表示类别,
|
|
|
|
|
|
* 影响 tessellation、布尔运算和文件交换时的处理方式。
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
enum class CurveType { Line, Circle, Bezier, BSpline, Nurbs };
|
|
|
|
|
|
|
2026-07-24 11:09:45 +00:00
|
|
|
|
// ═══════════════════════════════════════════════
|
|
|
|
|
|
// 拓扑实体定义
|
|
|
|
|
|
// ═══════════════════════════════════════════════
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @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(隐含直线边)
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
struct TopoEdge {
|
2026-07-24 11:09:45 +00:00
|
|
|
|
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
|
|
|
|
|
|
std::vector<int> loops; ///< 环 ID 列表(第一个为外环,后续为内环)
|
|
|
|
|
|
bool reversed = false; ///< 面法向量是否反转
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 拓扑壳
|
|
|
|
|
|
*
|
|
|
|
|
|
* 一组面的连续集合,构成空间的封闭或开放边界。
|
|
|
|
|
|
* closed = true 表示壳封闭一个体积(水密)。
|
|
|
|
|
|
*/
|
|
|
|
|
|
struct TopoShell {
|
|
|
|
|
|
int id; ///< 唯一壳 ID
|
|
|
|
|
|
std::vector<int> faces; ///< 面 ID 列表
|
|
|
|
|
|
bool closed = true; ///< 是否为封闭壳
|
2026-07-23 07:19:13 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-24 11:09:45 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @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 布尔运算
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
class BrepModel {
|
|
|
|
|
|
public:
|
|
|
|
|
|
BrepModel() = default;
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 添加顶点
|
|
|
|
|
|
* @param p 顶点坐标
|
|
|
|
|
|
* @return 新顶点的 ID
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
int add_vertex(const Point3D& p);
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 添加直线边
|
|
|
|
|
|
* @param v0 起点顶点 ID
|
|
|
|
|
|
* @param v1 终点顶点 ID
|
|
|
|
|
|
* @return 新边的 ID
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
int add_edge(int v0, int v1);
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 添加曲线边
|
|
|
|
|
|
* @param v0 起点顶点 ID
|
|
|
|
|
|
* @param v1 终点顶点 ID
|
|
|
|
|
|
* @param curve 边的几何曲线
|
|
|
|
|
|
* @return 新边的 ID
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
int add_edge(int v0, int v1, const curves::NurbsCurve& curve);
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 添加环
|
|
|
|
|
|
* @param edges 环中边的 ID 序列(顺序连接)
|
|
|
|
|
|
* @param outer true = 外环, false = 内环
|
|
|
|
|
|
* @return 新环的 ID
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
int add_loop(const std::vector<int>& edges, bool outer = true);
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 添加面
|
|
|
|
|
|
* @param surface_id 关联曲面 ID
|
|
|
|
|
|
* @param loops 环 ID 列表
|
|
|
|
|
|
* @return 新面的 ID
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
int add_face(int surface_id, const std::vector<int>& loops);
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 添加壳
|
|
|
|
|
|
* @param faces 面 ID 列表
|
|
|
|
|
|
* @param closed 是否为封闭壳
|
|
|
|
|
|
* @return 新壳的 ID
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
int add_shell(const std::vector<int>& faces, bool closed = true);
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 添加体
|
|
|
|
|
|
* @param shells 壳 ID 列表
|
|
|
|
|
|
* @param name 体名称
|
|
|
|
|
|
* @return 新体的 ID
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
int add_body(const std::vector<int>& shells, const std::string& name = "");
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 添加 NURBS 曲面
|
|
|
|
|
|
* @param surf NURBS 曲面定义
|
|
|
|
|
|
* @return 新曲面的 ID
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
int add_surface(const curves::NurbsSurface& surf);
|
|
|
|
|
|
|
2026-07-24 11:09:45 +00:00
|
|
|
|
// ── 计数查询 ──
|
|
|
|
|
|
|
|
|
|
|
|
/** @brief 顶点数量 */
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] size_t num_vertices() const { return vertices_.size(); }
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/** @brief 边数量 */
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] size_t num_edges() const { return edges_.size(); }
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/** @brief 面数量 */
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] size_t num_faces() const { return faces_.size(); }
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/** @brief 体数量 */
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] size_t num_bodies() const { return bodies_.size(); }
|
|
|
|
|
|
|
2026-07-24 11:09:45 +00:00
|
|
|
|
/** @brief 曲面数量 */
|
|
|
|
|
|
[[nodiscard]] size_t num_surfaces() const { return surfaces_.size(); }
|
|
|
|
|
|
|
|
|
|
|
|
// ── 元素访问(按数组索引 — 更快,适合顺序遍历) ──
|
|
|
|
|
|
|
|
|
|
|
|
/** @brief 按数组索引访问顶点 */
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] const TopoVertex& vertex(int id) const { return vertices_[id]; }
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/** @brief 按唯一 ID 访问顶点(适用于边数据中的 ID 引用) */
|
2026-07-24 05:18:52 +00:00
|
|
|
|
[[nodiscard]] const TopoVertex& vertex_by_id(int id) const;
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/** @brief 按数组索引访问边 */
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] const TopoEdge& edge(int id) const { return edges_[id]; }
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/** @brief 按数组索引访问面 */
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] const TopoFace& face(int id) const { return faces_[id]; }
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/** @brief 按数组索引访问曲面 */
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] const curves::NurbsSurface& surface(int id) const { return surfaces_[id]; }
|
|
|
|
|
|
|
2026-07-24 11:09:45 +00:00
|
|
|
|
/** @brief 按唯一 ID 访问环 */
|
|
|
|
|
|
[[nodiscard]] const TopoLoop& loop_by_id(int id) const;
|
|
|
|
|
|
|
|
|
|
|
|
/** @brief 获取所有环的只读引用 */
|
|
|
|
|
|
[[nodiscard]] const std::vector<TopoLoop>& all_loops() const { return loops_; }
|
|
|
|
|
|
|
|
|
|
|
|
// ── 几何/拓扑查询 ──
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 计算模型的轴对齐包围盒
|
|
|
|
|
|
*
|
|
|
|
|
|
* 遍历所有顶点,取最小/最大坐标。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return 包围盒(AABB)
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] core::AABB3D bounds() const;
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 检查模型有效性(快速版本)
|
|
|
|
|
|
*
|
|
|
|
|
|
* 进行基本检查:是否有顶点/边/面,ID 引用是否有效。
|
|
|
|
|
|
* 完整验证请使用 brep_validate.h 中的 validate()。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return true 如果模型通过基本检查
|
|
|
|
|
|
*
|
|
|
|
|
|
* @see validate() 完整验证(水密性、方向一致性等)
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] bool is_valid() const;
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 将 B-Rep 模型 tessellate 为三角网格
|
|
|
|
|
|
*
|
|
|
|
|
|
* 所有曲面和曲线以给定的弦高误差 tessellate 为三角形。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param deflection 弦高误差(控制 tessellation 精度,默认 0.01)
|
|
|
|
|
|
* @return HalfedgeMesh 三角网格
|
|
|
|
|
|
*
|
|
|
|
|
|
* @note deflection 越小网格越精细,但三角形数量显著增加。
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] mesh::HalfedgeMesh to_mesh(double deflection = 0.01) const;
|
|
|
|
|
|
|
2026-07-24 11:09:45 +00:00
|
|
|
|
// ── 邻接关系查询 ──
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 获取面的所有边
|
|
|
|
|
|
*
|
|
|
|
|
|
* 展开面的所有环,收集其中包含的边 ID。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param face_id 面 ID
|
|
|
|
|
|
* @return 边 ID 列表(去重)
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] std::vector<int> face_edges(int face_id) const;
|
2026-07-24 11:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 获取边的所有相邻面
|
|
|
|
|
|
*
|
|
|
|
|
|
* 反向查找:哪些面包含此边。
|
|
|
|
|
|
* 在流形网格中每条边应有恰好 2 个相邻面。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param edge_id 边 ID
|
|
|
|
|
|
* @return 相邻面 ID 列表
|
|
|
|
|
|
*/
|
2026-07-23 07:19:13 +00:00
|
|
|
|
[[nodiscard]] std::vector<int> edge_faces(int edge_id) const;
|
|
|
|
|
|
|
2026-07-24 11:09:45 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 获取顶点的所有相邻边
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param vertex_id 顶点 ID
|
|
|
|
|
|
* @return 相邻边 ID 列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
[[nodiscard]] std::vector<int> vertex_edges(int vertex_id) const;
|
2026-07-24 05:18:52 +00:00
|
|
|
|
|
2026-07-24 15:15:53 +00:00
|
|
|
|
// ── 友元:拓扑修复需要直接访问内部数据 ──
|
|
|
|
|
|
friend int heal_merge_vertices(BrepModel&, double);
|
|
|
|
|
|
friend int heal_merge_edges(BrepModel&, double);
|
|
|
|
|
|
friend int heal_close_gaps(BrepModel&, double);
|
|
|
|
|
|
friend int heal_orientation(BrepModel&);
|
|
|
|
|
|
|
2026-07-23 07:19:13 +00:00
|
|
|
|
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_;
|
|
|
|
|
|
int next_id_ = 0;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace vde::brep
|