feat(v5-M1): TrimmedSurface integration + SSI boolean + topology healing + tolerance system
M1.1 — TrimmedSurface 深度集成 (Agent #0): - trimmed_surface.h: winding number, closest_boundary_point, to_mesh() with CDT - factory methods: from_rect_with_hole, from_cylinder_patch, from_sphere_patch - brep.h: TopoFace +trimmed_surface_id, add_trimmed_surface() - brep.cpp: to_mesh() prefers TrimmedSurface path - modeling.cpp: make_box uses TrimmedSurface, added make_sphere_patch() - 23 tests, compilation passes M1.2 — SSI 基布尔运算 (Agent #1): - ssi_boolean.h/.cpp: full SSI pipeline (Step1-4) - Step1: parallel face-face SSI, Step2: face splitting via TrimmedSurface - Step3: ray-cast classification + exact predicates, Step4: face sewing - GMP exact predicates integrated (16 references to exact_orient3d/in_sphere) - OpenMP parallel (4 #pragma omp sections) - 33 tests, syntax-check passes M1.3 — 拓扑修复 + 容差系统 (Agent #2): - brep_heal.h/.cpp: heal_gaps (BFS), heal_slivers (Newell area), heal_orientation (Euler) - heal_topology: one-shot pipeline, heal_step_import: STEP auto-heal - tolerance.h: PerFaceTolerance, sliver_area, auto_tolerance(), face_tolerance() - brep_validate.cpp: all hardcoded 1e-6/1e-9 → ToleranceConfig - Tests: tolerance 34/34, heal 24/24, validate 16/16 (all passing) - Fixed: face_area_approx Newell formula bug, heal_step_import reporting 19 files, ~3200 lines net new code, 90+ new tests
This commit is contained in:
@@ -46,6 +46,7 @@
|
||||
#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>
|
||||
@@ -119,6 +120,7 @@ struct TopoLoop {
|
||||
struct TopoFace {
|
||||
int id; ///< 唯一面 ID
|
||||
int surface_id; ///< 关联的曲面 ID
|
||||
int trimmed_surface_id = -1; ///< 关联的裁剪曲面 ID(-1 表示不使用)
|
||||
std::vector<int> loops; ///< 环 ID 列表(第一个为外环,后续为内环)
|
||||
bool reversed = false; ///< 面法向量是否反转
|
||||
};
|
||||
@@ -221,6 +223,13 @@ public:
|
||||
*/
|
||||
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 列表
|
||||
@@ -244,6 +253,13 @@ public:
|
||||
*/
|
||||
int add_surface(const curves::NurbsSurface& surf);
|
||||
|
||||
/**
|
||||
* @brief 添加裁剪曲面
|
||||
* @param ts 裁剪曲面定义
|
||||
* @return 新裁剪曲面的 ID
|
||||
*/
|
||||
int add_trimmed_surface(const TrimmedSurface& ts);
|
||||
|
||||
// ── 计数查询 ──
|
||||
|
||||
/** @brief 顶点数量 */
|
||||
@@ -261,6 +277,9 @@ public:
|
||||
/** @brief 曲面数量 */
|
||||
[[nodiscard]] size_t num_surfaces() const { return surfaces_.size(); }
|
||||
|
||||
/** @brief 裁剪曲面数量 */
|
||||
[[nodiscard]] size_t num_trimmed_surfaces() const { return trimmed_surfaces_.size(); }
|
||||
|
||||
// ── 元素访问(按数组索引 — 更快,适合顺序遍历) ──
|
||||
|
||||
/** @brief 按数组索引访问顶点 */
|
||||
@@ -278,6 +297,9 @@ public:
|
||||
/** @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;
|
||||
|
||||
@@ -354,7 +376,11 @@ public:
|
||||
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 class EulerOp;
|
||||
|
||||
private:
|
||||
@@ -365,6 +391,7 @@ private:
|
||||
std::vector<TopoShell> shells_;
|
||||
std::vector<TopoBody> bodies_;
|
||||
std::vector<curves::NurbsSurface> surfaces_;
|
||||
std::vector<TrimmedSurface> trimmed_surfaces_;
|
||||
int next_id_ = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -4,16 +4,21 @@
|
||||
* @brief B-Rep 拓扑修复
|
||||
*
|
||||
* 对 BrepModel 执行拓扑修复操作,处理导入 STEP/IGES 文件时常见的
|
||||
* 几何缺陷:重复顶点、冗余边、间隙和方向不一致。
|
||||
* 几何缺陷:重复顶点、冗余边、间隙、退化面和方向不一致。
|
||||
*
|
||||
* ## 修复流水线
|
||||
* ## 修复流水线(新版)
|
||||
*
|
||||
* 1. heal_merge_vertices — 合并重合顶点
|
||||
* 2. heal_merge_edges — 合并重合边
|
||||
* 3. heal_close_gaps — 闭合小间隙
|
||||
* 4. heal_orientation — 统一面方向
|
||||
* 1. heal_gaps — BFS 合并距离<tolerance 的顶点(替代旧版 merge + close_gaps)
|
||||
* 2. heal_slivers — 检测并移除面积<sliver_area 的退化面
|
||||
* 3. heal_orientation — 统一面方向(外法线朝外),用欧拉示性数验证
|
||||
* 4. heal_topology — 一站式调用 1→2→3
|
||||
* 5. heal_step_import — STEP 导入后自动修复
|
||||
*
|
||||
* 建议通过 heal_topology() 一次性执行完整流水线。
|
||||
* ## 旧版兼容函数
|
||||
*
|
||||
* - heal_merge_vertices — 保留以兼容旧代码,内部委托给 heal_gaps
|
||||
* - heal_merge_edges — 保留以兼容旧代码
|
||||
* - heal_close_gaps — 保留以兼容旧代码,内部委托给 heal_gaps
|
||||
*
|
||||
* @ingroup brep
|
||||
*/
|
||||
@@ -22,50 +27,42 @@
|
||||
|
||||
namespace vde::brep {
|
||||
|
||||
/**
|
||||
* @brief 合并重合顶点
|
||||
*
|
||||
* 扫描所有顶点对,将距离小于 tolerance 的顶点合并为一个。
|
||||
* 更新所有引用被合并顶点的边,使其指向保留的顶点。
|
||||
*
|
||||
* @param body 待修复的 B-Rep 模型(原地修改)
|
||||
* @param tolerance 距离容差,默认 1e-6
|
||||
* @return 合并的顶点对数
|
||||
*
|
||||
* @note 合并后 body.vertices_ 的大小会减小。
|
||||
* @note 顶点 ID 引用的重定向使用 Edges → Loops 层次。
|
||||
*/
|
||||
int heal_merge_vertices(BrepModel& body, double tolerance = 1e-6);
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 新版修复 API
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 合并重合边
|
||||
* @brief 使用 BFS 合并距离小于 tolerance 的顶点
|
||||
*
|
||||
* 查找共享端点且曲线几何几乎相同的边对,将其中一条边合并到另一条。
|
||||
* 更新所有引用被合并边的环,使其指向保留的边。
|
||||
* 构建顶点邻接图:两点距离 < tolerance 则视为连通。
|
||||
* 对每个连通分量,以第一个顶点为代表合并该分量中所有顶点。
|
||||
* 更新所有边的顶点引用。
|
||||
*
|
||||
* @param body 待修复的 B-Rep 模型(原地修改)
|
||||
* @param tolerance 曲线几何比较容差,默认 1e-6
|
||||
* @return 合并的边对数
|
||||
* @param tolerance 顶点合并距离容差,默认 1e-6
|
||||
* @return 合并的顶点对数(即减少的顶点数)
|
||||
*/
|
||||
int heal_merge_edges(BrepModel& body, double tolerance = 1e-6);
|
||||
int heal_gaps(BrepModel& body, double tolerance = 1e-6);
|
||||
|
||||
/**
|
||||
* @brief 闭合面之间的微小间隙
|
||||
* @brief 检测并移除退化面(sliver faces)
|
||||
*
|
||||
* 检查每条边的端点是否在 max_gap 范围内接近另一条边的端点。
|
||||
* 将接近的顶点吸附到一起,消除拓扑间隙。
|
||||
* 遍历所有面,计算面的近似面积。面积小于 sliver_area
|
||||
* (默认取自 ToleranceConfig::global().sliver_area = 1e-12)的面被移除。
|
||||
* 更新壳的面引用并压缩面数组。
|
||||
*
|
||||
* @param body 待修复的 B-Rep 模型(原地修改)
|
||||
* @param max_gap 最大间隙尺寸,默认 1e-4
|
||||
* @return 闭合的间隙数
|
||||
* @return 移除的退化面数量
|
||||
*/
|
||||
int heal_close_gaps(BrepModel& body, double max_gap = 1e-4);
|
||||
int heal_slivers(BrepModel& body);
|
||||
|
||||
/**
|
||||
* @brief 统一面方向(全部向外)
|
||||
* @brief 统一面方向(全部向外),用欧拉示性数验证
|
||||
*
|
||||
* 通过符号体积计算确保所有面法向量朝外。
|
||||
* 对于方向朝内的面,翻转其 reversed 标志。
|
||||
* 修复后计算欧拉示性数 V - E + F(预期 ≈ 2 对封闭流形),
|
||||
* 偏差较大时记录但不回滚。
|
||||
*
|
||||
* @param body 待修复的 B-Rep 模型(原地修改)
|
||||
* @return 翻转的面数
|
||||
@@ -73,25 +70,54 @@ int heal_close_gaps(BrepModel& body, double max_gap = 1e-4);
|
||||
int heal_orientation(BrepModel& body);
|
||||
|
||||
/**
|
||||
* @brief 执行完整拓扑修复流水线
|
||||
* @brief 一站式拓扑修复流水线
|
||||
*
|
||||
* 按推荐顺序执行:
|
||||
* 1. heal_gaps (BFS 顶点合并 + 间隙闭合)
|
||||
* 2. heal_slivers (移除退化面)
|
||||
* 3. heal_orientation (统一面方向,欧拉验证)
|
||||
*
|
||||
* 按推荐顺序执行所有修复步骤:
|
||||
* 顶点合并 → 边合并 → 间隙闭合 → 方向统一
|
||||
* 最后调用 validate() 检查修复结果。
|
||||
*
|
||||
* @param body 待修复的 B-Rep 模型(原地修改)
|
||||
* @param tolerance 用于顶点/边合并的距离容差,默认 1e-6
|
||||
* @param tolerance 顶点合并距离容差,默认 1e-6
|
||||
* @return true 如果修复后模型通过验证
|
||||
*
|
||||
* @code{.cpp}
|
||||
* BrepModel body = import_step("part.step");
|
||||
* if (heal_topology(body)) {
|
||||
* // 模型已修复,可以安全执行布尔运算等操作
|
||||
* auto result = validate(body);
|
||||
* assert(result.valid);
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
bool heal_topology(BrepModel& body, double tolerance = 1e-6);
|
||||
|
||||
/**
|
||||
* @brief STEP 导入后自动修复
|
||||
*
|
||||
* 使用 auto_tolerance() 根据模型实际尺寸计算自适应容差,
|
||||
* 然后执行 heal_topology()。
|
||||
*
|
||||
* @param body 从 STEP 导入的 B-Rep 模型(原地修改)
|
||||
* @return 修复结果
|
||||
* - >0: 修复成功,返回修复项总数
|
||||
* - 0: 无需修复
|
||||
* - <0: 修复失败
|
||||
*/
|
||||
int heal_step_import(BrepModel& body);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 旧版兼容函数(委托给新版实现)
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 合并重合顶点(旧版兼容)
|
||||
* @deprecated 请使用 heal_gaps()
|
||||
*/
|
||||
int heal_merge_vertices(BrepModel& body, double tolerance = 1e-6);
|
||||
|
||||
/**
|
||||
* @brief 合并重合边(旧版兼容)
|
||||
*/
|
||||
int heal_merge_edges(BrepModel& body, double tolerance = 1e-6);
|
||||
|
||||
/**
|
||||
* @brief 闭合面之间的微小间隙(旧版兼容)
|
||||
* @deprecated 请使用 heal_gaps()
|
||||
*/
|
||||
int heal_close_gaps(BrepModel& body, double max_gap = 1e-4);
|
||||
|
||||
} // namespace vde::brep
|
||||
|
||||
@@ -73,6 +73,12 @@ struct ValidationResult {
|
||||
|
||||
/** @brief 总面数 */
|
||||
size_t total_faces = 0;
|
||||
|
||||
/** @brief 所用容差级别(供外部审查) */
|
||||
double tolerance_used = 0.0;
|
||||
|
||||
/** @brief 欧拉示性数 V - E + F */
|
||||
int euler_characteristic = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file ssi_boolean.h
|
||||
* @brief 基于曲面-曲面求交 (SSI) 的 B-Rep 布尔运算
|
||||
*
|
||||
* 这是 B-Rep 布尔运算的完整 SSI 管线实现,与 brep_boolean.h
|
||||
* 中基于平面切割的旧实现并存。SSI 方法使用精确的曲面交线来
|
||||
* 分割和分类面,从而获得更高质量的布尔结果。
|
||||
*
|
||||
* ## SSI 管线 (4 步)
|
||||
*
|
||||
* 1. **SSI 计算** — 对所有面对并行计算曲面交线
|
||||
* 2. **面分割** — 用交线分割面,生成 TrimmedSurface 片段
|
||||
* 3. **分类** — 射线投射分类每个面片段为 IN/OUT/ON
|
||||
* 4. **缝合** — 缝合保留的面片段,调用 heal_orientation
|
||||
*
|
||||
* ## 精确谓词
|
||||
*
|
||||
* 分类阶段集成 GMP 精确谓词 (exact_orient3d, exact_point_in_polyhedron),
|
||||
* 使用自适应精度:先 double,不确定时自动升级到 GMP。
|
||||
*
|
||||
* @ingroup brep
|
||||
*/
|
||||
#include "vde/brep/brep.h"
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace vde::brep {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// SSI 布尔运算结果
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief SSI 布尔运算的诊断结果
|
||||
*
|
||||
* 包含运算过程中的统计信息和诊断数据。
|
||||
*/
|
||||
struct SSIBooleanResult {
|
||||
BrepModel result; ///< 运算结果模型
|
||||
|
||||
// ── 诊断信息 ──
|
||||
int num_ssi_curves = 0; ///< 计算的曲面交线总数
|
||||
int num_fragments = 0; ///< 生成的面片段总数
|
||||
int num_classified_in = 0; ///< 分类为 IN 的片段数
|
||||
int num_classified_out = 0; ///< 分类为 OUT 的片段数
|
||||
int num_classified_on = 0; ///< 分类为 ON 的片段数
|
||||
int num_kept = 0; ///< 最终保留的片段数
|
||||
int num_exact_upgrades = 0; ///< 升级到 GMP 精确谓词的次数
|
||||
double ssi_time_ms = 0.0; ///< SSI 计算耗时 (ms)
|
||||
double split_time_ms = 0.0; ///< 面分割耗时 (ms)
|
||||
double classify_time_ms = 0.0; ///< 分类耗时 (ms)
|
||||
double sew_time_ms = 0.0; ///< 缝合耗时 (ms)
|
||||
std::string error_message; ///< 错误信息(空表示成功)
|
||||
|
||||
/// 总耗时 (ms)
|
||||
[[nodiscard]] double total_time_ms() const {
|
||||
return ssi_time_ms + split_time_ms + classify_time_ms + sew_time_ms;
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// SSI 布尔运算 API
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief SSI 布尔并集: A ∪ B
|
||||
*
|
||||
* 合并两个 B-Rep 实体为一个。完整的 SSI 管线:
|
||||
* 1. 并行计算所有面对之间的曲面交线
|
||||
* 2. 用交线分割面生成面片段
|
||||
* 3. 射线投射 + 精确谓词分类每个面片段
|
||||
* 4. 缝合保留的面片段并统一方向
|
||||
*
|
||||
* @param a 第一个 B-Rep 实体
|
||||
* @param b 第二个 B-Rep 实体
|
||||
* @return SSIBooleanResult 含结果模型和诊断信息
|
||||
*
|
||||
* @code{.cpp}
|
||||
* auto box = make_box(2, 2, 2);
|
||||
* auto sphere = make_sphere(1.5);
|
||||
* auto r = ssi_boolean_union(box, sphere);
|
||||
* if (r.error_message.empty()) {
|
||||
* // r.result 是有效的 B-Rep 模型
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] SSIBooleanResult ssi_boolean_union(
|
||||
const BrepModel& a, const BrepModel& b);
|
||||
|
||||
/**
|
||||
* @brief SSI 布尔交集: A ∩ B
|
||||
*
|
||||
* 保留两个实体共有的区域。
|
||||
*
|
||||
* @param a 第一个 B-Rep 实体
|
||||
* @param b 第二个 B-Rep 实体
|
||||
* @return SSIBooleanResult 含结果模型和诊断信息
|
||||
*/
|
||||
[[nodiscard]] SSIBooleanResult ssi_boolean_intersection(
|
||||
const BrepModel& a, const BrepModel& b);
|
||||
|
||||
/**
|
||||
* @brief SSI 布尔差集: A \ B
|
||||
*
|
||||
* 从 A 中减去 B 所占据的区域。
|
||||
*
|
||||
* @param a 被减实体 A
|
||||
* @param b 减去实体 B
|
||||
* @return SSIBooleanResult 含结果模型和诊断信息
|
||||
*/
|
||||
[[nodiscard]] SSIBooleanResult ssi_boolean_difference(
|
||||
const BrepModel& a, const BrepModel& b);
|
||||
|
||||
} // namespace vde::brep
|
||||
+126
-35
@@ -3,18 +3,21 @@
|
||||
* @file tolerance.h
|
||||
* @brief 精确容差系统
|
||||
*
|
||||
* 可配置的几何容差,支持 fuzzy 比较和自适应容差。
|
||||
* 可配置的几何容差,支持 fuzzy 比较、自适应容差、
|
||||
* PerFaceTolerance 局部覆盖和容差传播链追踪。
|
||||
* 对齐工业 CAD 内核(Parasolid/ACIS)的容差模型。
|
||||
*
|
||||
* @ingroup foundation
|
||||
*/
|
||||
|
||||
#include "vde/core/point.h"
|
||||
#include "vde/core/aabb.h"
|
||||
#include "vde/brep/brep.h"
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
namespace vde::brep {
|
||||
@@ -30,15 +33,16 @@ namespace vde::brep {
|
||||
* 可全局配置或按操作类型分别设置。
|
||||
*/
|
||||
struct ToleranceConfig {
|
||||
double vertex_merge = 1e-6; ///< 顶点合并容差
|
||||
double edge_merge = 1e-6; ///< 边合并容差
|
||||
double face_plane = 1e-9; ///< 面平面判断容差
|
||||
double boolean = 1e-6; ///< 布尔运算容差
|
||||
double intersection = 1e-6; ///< 求交容差
|
||||
double validation = 1e-6; ///< 验证容差
|
||||
double point_on_curve = 1e-8; ///< 点在曲线上的容差
|
||||
double point_on_surface = 1e-8; ///< 点在曲面上的容差
|
||||
double angular = 1e-10; ///< 角度容差(弧度)
|
||||
double vertex_merge = 1e-6; ///< 顶点合并容差
|
||||
double edge_merge = 1e-6; ///< 边合并容差
|
||||
double face_plane = 1e-9; ///< 面平面判断容差
|
||||
double boolean = 1e-6; ///< 布尔运算容差
|
||||
double intersection = 1e-6; ///< 求交容差
|
||||
double validation = 1e-6; ///< 验证容差
|
||||
double point_on_curve = 1e-8; ///< 点在曲线上的容差
|
||||
double point_on_surface = 1e-8; ///< 点在曲面上的容差
|
||||
double angular = 1e-10; ///< 角度容差(弧度)
|
||||
double sliver_area = 1e-12; ///< 退化面(sliver)面积阈值
|
||||
|
||||
/// 全局默认
|
||||
[[nodiscard]] static const ToleranceConfig& global();
|
||||
@@ -47,6 +51,118 @@ struct ToleranceConfig {
|
||||
static void set_global(const ToleranceConfig& cfg);
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// PerFaceTolerance — 每面独立容差
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 每面独立容差管理器
|
||||
*
|
||||
* 允许为特定面设置不同于全局配置的容差值。
|
||||
* 未显式设置的面使用全局默认值。
|
||||
*
|
||||
* @code
|
||||
* PerFaceTolerance pft;
|
||||
* pft.set(face_id, ToleranceConfig{...}); // 为面 3 设置局部容差
|
||||
* auto cfg = pft.resolve(face_id); // 获取实际容差(局部覆盖优先)
|
||||
* @endcode
|
||||
*/
|
||||
class PerFaceTolerance {
|
||||
public:
|
||||
/// 为指定面设置局部容差
|
||||
void set(int face_id, const ToleranceConfig& cfg) {
|
||||
overrides_[face_id] = cfg;
|
||||
}
|
||||
|
||||
/// 移除指定面的局部容差设置
|
||||
void remove(int face_id) {
|
||||
overrides_.erase(face_id);
|
||||
}
|
||||
|
||||
/// 查询指定面的实际容差:局部覆盖 → 全局回退
|
||||
[[nodiscard]] ToleranceConfig resolve(int face_id) const {
|
||||
auto it = overrides_.find(face_id);
|
||||
if (it != overrides_.end()) return it->second;
|
||||
return ToleranceConfig::global();
|
||||
}
|
||||
|
||||
/// 是否存在局部覆盖
|
||||
[[nodiscard]] bool has_override(int face_id) const {
|
||||
return overrides_.count(face_id) > 0;
|
||||
}
|
||||
|
||||
/// 清除所有局部覆盖
|
||||
void clear() { overrides_.clear(); }
|
||||
|
||||
/// 获取所有覆盖
|
||||
[[nodiscard]] const std::map<int, ToleranceConfig>& overrides() const {
|
||||
return overrides_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<int, ToleranceConfig> overrides_;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 自适应容差
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 根据模型包围盒自动计算容差配置
|
||||
*
|
||||
* 大模型使用宽松容差,小模型使用精密容差。
|
||||
* 所有容差字段按模型尺寸比例缩放。
|
||||
*
|
||||
* @param model_bounds 模型的轴对齐包围盒
|
||||
* @param base_scale 基准尺寸(mm),默认 100mm
|
||||
* @return 自适应 ToleranceConfig
|
||||
*/
|
||||
[[nodiscard]] ToleranceConfig auto_tolerance(
|
||||
const core::AABB3D& model_bounds,
|
||||
double base_scale = 100.0);
|
||||
|
||||
/**
|
||||
* @brief 根据模型自动计算容差(便捷重载)
|
||||
*
|
||||
* @param body B-Rep 模型
|
||||
* @param base_scale 基准尺寸(mm)
|
||||
* @return 自适应 ToleranceConfig
|
||||
*/
|
||||
[[nodiscard]] ToleranceConfig auto_tolerance(
|
||||
const BrepModel& body,
|
||||
double base_scale = 100.0);
|
||||
|
||||
/**
|
||||
* @brief 根据模型尺寸计算自适应容差(单值,保留兼容)
|
||||
*
|
||||
* @param model_size 模型特征尺寸
|
||||
* @param base_tol 基础容差
|
||||
* @return 自适应容差
|
||||
*/
|
||||
[[nodiscard]] inline double adaptive_tolerance(
|
||||
double model_size, double base_tol = 1e-6)
|
||||
{
|
||||
// 1mm 模型 → 0.1μm, 1m 模型 → 100μm
|
||||
return std::max(base_tol, model_size * 1e-7);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据 B-Rep 模型计算容差(保留兼容)
|
||||
*/
|
||||
[[nodiscard]] double model_tolerance(const BrepModel& body);
|
||||
|
||||
/**
|
||||
* @brief 获取指定面的有效容差(全局 + 局部覆盖)
|
||||
*
|
||||
* @param body B-Rep 模型
|
||||
* @param face_id 面 ID(数组索引)
|
||||
* @param pft PerFaceTolerance 覆盖(可为空)
|
||||
* @return 该面的实际 ToleranceConfig
|
||||
*/
|
||||
[[nodiscard]] ToleranceConfig face_tolerance(
|
||||
const BrepModel& body, int face_id,
|
||||
const PerFaceTolerance* pft = nullptr);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Fuzzy comparison utilities
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
@@ -128,31 +244,6 @@ struct ToleranceConfig {
|
||||
return fuzzy_equal(dot, 0.0, angle_tol);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Adaptive tolerance
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 根据模型尺寸计算自适应容差
|
||||
*
|
||||
* 大模型用宽松容差,小模型用精密容差。
|
||||
*
|
||||
* @param model_size 模型特征尺寸
|
||||
* @param base_tol 基础容差
|
||||
* @return 自适应容差
|
||||
*/
|
||||
[[nodiscard]] inline double adaptive_tolerance(
|
||||
double model_size, double base_tol = 1e-6)
|
||||
{
|
||||
// 1mm 模型 → 0.1μm, 1m 模型 → 100μm
|
||||
return std::max(base_tol, model_size * 1e-7);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据 B-Rep 模型计算容差
|
||||
*/
|
||||
[[nodiscard]] double model_tolerance(const BrepModel& body);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Tolerance chain — 容差传播追踪
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
#include "vde/curves/nurbs_curve.h"
|
||||
#include "vde/core/point.h"
|
||||
#include "vde/core/aabb.h"
|
||||
#include "vde/mesh/halfedge_mesh.h"
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace vde::brep {
|
||||
|
||||
@@ -40,15 +42,43 @@ struct TrimmedSurface {
|
||||
/// Check if a parameter point is inside the trimmed region
|
||||
[[nodiscard]] bool is_inside(double u, double v) const;
|
||||
|
||||
/// Compute winding number of point (u,v) relative to a loop
|
||||
/// Positive = inside (for counter-clockwise outer loop), 0 = outside
|
||||
[[nodiscard]] int winding_number(double u, double v, const TrimLoop& loop) const;
|
||||
|
||||
/// Find the closest point on the boundary to (u,v) — for points outside trimmed region
|
||||
[[nodiscard]] core::Point3D closest_boundary_point(double u, double v) const;
|
||||
|
||||
/// Get the axis-aligned bounding box of the trimmed surface
|
||||
[[nodiscard]] core::AABB3D bounds() const;
|
||||
|
||||
/// Tessellate the trimmed surface to a triangle mesh
|
||||
/// Uses adaptive grid sampling with inside/outside testing
|
||||
/// @param deflection chord-height tolerance for tessellation
|
||||
/// @return HalfedgeMesh containing the triangulated trimmed surface
|
||||
[[nodiscard]] mesh::HalfedgeMesh to_mesh(double deflection = 0.01) const;
|
||||
|
||||
/// Create an untrimmed surface (full parameter domain)
|
||||
static TrimmedSurface from_surface(const curves::NurbsSurface& surf);
|
||||
|
||||
/// Create a rectangular trimmed surface
|
||||
static TrimmedSurface from_rect(const curves::NurbsSurface& surf,
|
||||
double u0, double u1, double v0, double v1);
|
||||
|
||||
/// Create a trimmed surface with a circular hole
|
||||
static TrimmedSurface from_rect_with_hole(const curves::NurbsSurface& surf,
|
||||
double u0, double u1, double v0, double v1,
|
||||
double hole_uc, double hole_vc, double hole_r,
|
||||
int hole_segments = 32);
|
||||
|
||||
/// Create a trimmed surface for a cylinder side patch
|
||||
/// u ranges [u0, u1] along circumference, v ranges [v0, v1] along height
|
||||
static TrimmedSurface from_cylinder_patch(const curves::NurbsSurface& cyl_surf,
|
||||
double u0, double u1, double v0, double v1);
|
||||
|
||||
/// Create a trimmed surface for a sphere patch
|
||||
static TrimmedSurface from_sphere_patch(const curves::NurbsSurface& sphere_surf,
|
||||
double u0, double u1, double v0, double v1);
|
||||
};
|
||||
|
||||
} // namespace vde::brep
|
||||
|
||||
Reference in New Issue
Block a user