Files
ViewDesignEngine/include/vde/brep/tolerance.h
T
茂之钳 05b62e8238
CI / Build & Test (push) Failing after 48s
CI / Release Build (push) Failing after 40s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled
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
2026-07-26 20:35:24 +08:00

310 lines
10 KiB
C++
Raw 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 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 <cmath>
#include <functional>
#include <string>
#include <vector>
#include <map>
#include <utility>
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<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
// ═══════════════════════════════════════════════════════════
/**
* @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<std::pair<std::string, double>>& steps() const {
return steps_;
}
/** @brief 清空链 */
void clear() { steps_.clear(); }
private:
std::vector<std::pair<std::string, double>> steps_;
};
} // namespace vde::brep