2026-07-25 04:07:24 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @file tolerance.h
|
|
|
|
|
|
* @brief 精确容差系统
|
|
|
|
|
|
*
|
2026-07-26 20:35:24 +08:00
|
|
|
|
* 可配置的几何容差,支持 fuzzy 比较、自适应容差、
|
|
|
|
|
|
* PerFaceTolerance 局部覆盖和容差传播链追踪。
|
2026-07-25 04:07:24 +00:00
|
|
|
|
* 对齐工业 CAD 内核(Parasolid/ACIS)的容差模型。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @ingroup foundation
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "vde/core/point.h"
|
2026-07-26 20:35:24 +08:00
|
|
|
|
#include "vde/core/aabb.h"
|
2026-07-25 04:07:24 +00:00
|
|
|
|
#include "vde/brep/brep.h"
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
#include <functional>
|
2026-07-26 16:42:55 +08:00
|
|
|
|
#include <string>
|
|
|
|
|
|
#include <vector>
|
2026-07-26 20:35:24 +08:00
|
|
|
|
#include <map>
|
2026-07-26 16:42:55 +08:00
|
|
|
|
#include <utility>
|
2026-07-25 04:07:24 +00:00
|
|
|
|
|
|
|
|
|
|
namespace vde::brep {
|
|
|
|
|
|
|
|
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
|
|
// Tolerance configuration
|
|
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 容差配置
|
|
|
|
|
|
*
|
|
|
|
|
|
* 集中管理所有几何比较的容差值。
|
|
|
|
|
|
* 可全局配置或按操作类型分别设置。
|
|
|
|
|
|
*/
|
|
|
|
|
|
struct ToleranceConfig {
|
2026-07-26 20:35:24 +08:00
|
|
|
|
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)面积阈值
|
2026-07-25 04:07:24 +00:00
|
|
|
|
|
|
|
|
|
|
/// 全局默认
|
|
|
|
|
|
[[nodiscard]] static const ToleranceConfig& global();
|
|
|
|
|
|
|
|
|
|
|
|
/// 设置全局配置
|
|
|
|
|
|
static void set_global(const ToleranceConfig& cfg);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-26 20:35:24 +08:00
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
2026-07-25 04:07:24 +00:00
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-26 16:42:55 +08:00
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
|
|
// 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<std::pair<std::string, double>>& steps() const {
|
|
|
|
|
|
return steps_;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** @brief 清空链 */
|
|
|
|
|
|
void clear() { steps_.clear(); }
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
std::vector<std::pair<std::string, double>> steps_;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-25 04:07:24 +00:00
|
|
|
|
} // namespace vde::brep
|