#pragma once /** * @file tolerance.h * @brief 精确容差系统 * * 可配置的几何容差,支持 fuzzy 比较、自适应容差、 * PerFaceTolerance 局部覆盖和容差传播链追踪。 * 对齐工业 CAD 内核(Parasolid/ACIS)的容差模型。 * * @ingroup foundation */ #include "vde/core/point.h" #include "vde/core/aabb.h" #include "vde/brep/brep.h" #include #include #include #include #include #include namespace vde::brep { // ═══════════════════════════════════════════════════════════ // Tolerance configuration // ═══════════════════════════════════════════════════════════ /** * @brief 容差配置 * * 集中管理所有几何比较的容差值。 * 可全局配置或按操作类型分别设置。 */ 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 sliver_area = 1e-12; ///< 退化面(sliver)面积阈值 /// 全局默认 [[nodiscard]] static const ToleranceConfig& global(); /// 设置全局配置 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& overrides() const { return overrides_; } private: std::map 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 // ═══════════════════════════════════════════════════════════ /** * @brief Fuzzy 相等(带相对容差) * * 使用绝对 + 相对容差组合: * |a - b| <= max(abs_tol, rel_tol * max(|a|, |b|)) */ [[nodiscard]] inline bool fuzzy_equal( double a, double b, double abs_tol = 1e-9, double rel_tol = 1e-12) { double diff = std::abs(a - b); if (diff <= abs_tol) return true; double scale = std::max(std::abs(a), std::abs(b)); return diff <= rel_tol * scale; } /// Fuzzy 零检查 [[nodiscard]] inline bool fuzzy_zero(double x, double tol = 1e-9) { return std::abs(x) <= tol; } /// Fuzzy 大于 [[nodiscard]] inline bool fuzzy_gt(double a, double b, double tol = 1e-9) { return a > b + tol; } /// Fuzzy 小于 [[nodiscard]] inline bool fuzzy_lt(double a, double b, double tol = 1e-9) { return a < b - tol; } /// Fuzzy 大于等于 [[nodiscard]] inline bool fuzzy_gte(double a, double b, double tol = 1e-9) { return a >= b - tol; } /// Fuzzy 小于等于 [[nodiscard]] inline bool fuzzy_lte(double a, double b, double tol = 1e-9) { return a <= b + tol; } // ═══════════════════════════════════════════════════════════ // Vector fuzzy operations // ═══════════════════════════════════════════════════════════ /// 两向量在容差内相等 [[nodiscard]] inline bool fuzzy_equal_vec( const core::Vector3D& a, const core::Vector3D& b, double tol = 1e-9) { return fuzzy_equal(a.x(), b.x(), tol) && fuzzy_equal(a.y(), b.y(), tol) && fuzzy_equal(a.z(), b.z(), tol); } /// 两点在容差内相等 [[nodiscard]] inline bool fuzzy_equal_point( const core::Point3D& a, const core::Point3D& b, double tol = 1e-9) { return (a - b).norm() <= tol; } /// 两向量平行(共线) [[nodiscard]] inline bool fuzzy_parallel( const core::Vector3D& a, const core::Vector3D& b, double angle_tol = 1e-10) { double dot = std::abs(a.normalized().dot(b.normalized())); return fuzzy_equal(dot, 1.0, 1e-9); } /// 两向量垂直 [[nodiscard]] inline bool fuzzy_perpendicular( const core::Vector3D& a, const core::Vector3D& b, double angle_tol = 1e-10) { double dot = std::abs(a.normalized().dot(b.normalized())); return fuzzy_equal(dot, 0.0, angle_tol); } // ═══════════════════════════════════════════════════════════ // Tolerance chain — 容差传播追踪 // ═══════════════════════════════════════════════════════════ /** * @brief 容差传播链 * * 追踪操作链中的容差累积。每个操作注入自身的容差贡献, * 末端可查询累积容差上界。 * * 使用场景: * - 布尔运算链:求交 → 分割 → 缝合,累积容差逐级放大 * - 特征链:拉伸 → 倒圆 → 抽壳,容差传播路径 * * @code * ToleranceChain chain; * chain.push("intersect", 1e-6); * chain.push("split", 1e-6); * chain.push("sew", 1e-5); * double worst = chain.cumulative(); // 1.2e-5 (root-sum-square) * @endcode */ class ToleranceChain { public: /** * @brief 记录一个操作及其容差贡献 * @param op_name 操作名称(用于调试/日志) * @param tol 该操作注入的容差 */ void push(const std::string& op_name, double tol); /** * @brief 累积容差 * * 使用均方根 (RSS) 合成:sqrt(Σ tol²) * 比简单求和更保守,但比对数叠加更实用。 * * @return 累积容差 */ [[nodiscard]] double cumulative() const; /** * @brief 最大单步容差 * @return 链中最大的单步容差 */ [[nodiscard]] double max_step() const; /** @brief 链深度 */ [[nodiscard]] size_t depth() const { return steps_.size(); } /** @brief 所有步骤(只读) */ [[nodiscard]] const std::vector>& steps() const { return steps_; } /** @brief 清空链 */ void clear() { steps_.clear(); } private: std::vector> steps_; }; } // namespace vde::brep