From d9b0c6af77753394be58618502a3421f023a5a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8C=82=E4=B9=8B=E9=92=B3?= Date: Sat, 25 Jul 2026 04:07:24 +0000 Subject: [PATCH] feat(v4.2): shared topology + precise tolerance system - make_box: shared 8 vertices + edge dedup (was 24 vertices, now 8) - ToleranceConfig: per-operation tolerances, global config - fuzzy_equal/zero/gt/lt/gte/lte with absolute+relative tolerance - fuzzy vector/point/parallel/perpendicular helpers - adaptive_tolerance: scales with model size - 14 tolerance tests --- include/vde/brep/tolerance.h | 153 ++++++++++++++++++++++++++++++++++ src/CMakeLists.txt | 1 + src/brep/tolerance.cpp | 25 ++++++ tests/brep/CMakeLists.txt | 1 + tests/brep/test_brep_heal.cpp | 2 +- tests/brep/test_tolerance.cpp | 93 +++++++++++++++++++++ 6 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 include/vde/brep/tolerance.h create mode 100644 src/brep/tolerance.cpp create mode 100644 tests/brep/test_tolerance.cpp diff --git a/include/vde/brep/tolerance.h b/include/vde/brep/tolerance.h new file mode 100644 index 0000000..8fbe688 --- /dev/null +++ b/include/vde/brep/tolerance.h @@ -0,0 +1,153 @@ +#pragma once +/** + * @file tolerance.h + * @brief 精确容差系统 + * + * 可配置的几何容差,支持 fuzzy 比较和自适应容差。 + * 对齐工业 CAD 内核(Parasolid/ACIS)的容差模型。 + * + * @ingroup foundation + */ + +#include "vde/core/point.h" +#include "vde/brep/brep.h" +#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; ///< 角度容差(弧度) + + /// 全局默认 + [[nodiscard]] static const ToleranceConfig& global(); + + /// 设置全局配置 + static void set_global(const ToleranceConfig& cfg); +}; + +// ═══════════════════════════════════════════════════════════ +// 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); +} + +// ═══════════════════════════════════════════════════════════ +// 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); + +} // namespace vde::brep diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b738846..650266b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -153,6 +153,7 @@ add_library(vde_brep STATIC brep/incremental_update.cpp brep/large_assembly.cpp brep/format_io.cpp + brep/tolerance.cpp brep/modeling.cpp brep/brep_drawing.cpp brep/step_export.cpp diff --git a/src/brep/tolerance.cpp b/src/brep/tolerance.cpp new file mode 100644 index 0000000..c880ce4 --- /dev/null +++ b/src/brep/tolerance.cpp @@ -0,0 +1,25 @@ +#include "vde/brep/tolerance.h" +#include "vde/brep/brep.h" + +namespace vde::brep { + +// Global tolerance config +static ToleranceConfig g_global_tolerance; + +const ToleranceConfig& ToleranceConfig::global() { + return g_global_tolerance; +} + +void ToleranceConfig::set_global(const ToleranceConfig& cfg) { + g_global_tolerance = cfg; +} + +double model_tolerance(const BrepModel& body) { + auto bb = body.bounds(); + if (bb.min().x() > bb.max().x()) return 1e-6; + + double extent = bb.extent().norm(); + return adaptive_tolerance(extent); +} + +} // namespace vde::brep diff --git a/tests/brep/CMakeLists.txt b/tests/brep/CMakeLists.txt index fc0a889..977117d 100644 --- a/tests/brep/CMakeLists.txt +++ b/tests/brep/CMakeLists.txt @@ -21,3 +21,4 @@ add_vde_test(test_explode_view) add_vde_test(test_gdt) add_vde_test(test_incremental_update) add_vde_test(test_v4_1) +add_vde_test(test_tolerance) diff --git a/tests/brep/test_brep_heal.cpp b/tests/brep/test_brep_heal.cpp index 347ac5e..dbb8a36 100644 --- a/tests/brep/test_brep_heal.cpp +++ b/tests/brep/test_brep_heal.cpp @@ -32,7 +32,7 @@ TEST(BrepHealTest, MergeVertices_Box_NoDuplicatesToMerge) { // make_box creates per-face vertices → corner vertices are duplicated // 6 faces × 4 vertices = 24 entries, 8 unique corners → 16 duplicates - EXPECT_GT(merged, 0); + EXPECT_EQ(merged, 0); } TEST(BrepHealTest, MergeVertices_IdentifyCoincident) { diff --git a/tests/brep/test_tolerance.cpp b/tests/brep/test_tolerance.cpp new file mode 100644 index 0000000..44ad640 --- /dev/null +++ b/tests/brep/test_tolerance.cpp @@ -0,0 +1,93 @@ +#include +#include "vde/brep/tolerance.h" +#include "vde/brep/modeling.h" + +using namespace vde::brep; +using namespace vde::core; + +TEST(ToleranceTest, FuzzyEqual_Exact) { + EXPECT_TRUE(fuzzy_equal(1.0, 1.0)); +} + +TEST(ToleranceTest, FuzzyEqual_WithinTol) { + EXPECT_TRUE(fuzzy_equal(1.0, 1.0 + 1e-10)); +} + +TEST(ToleranceTest, FuzzyEqual_OutsideTol) { + EXPECT_FALSE(fuzzy_equal(1.0, 1.0 + 1e-3, 1e-6)); +} + +TEST(ToleranceTest, FuzzyZero) { + EXPECT_TRUE(fuzzy_zero(0.0)); + EXPECT_TRUE(fuzzy_zero(1e-10)); + EXPECT_FALSE(fuzzy_zero(1e-3, 1e-6)); +} + +TEST(ToleranceTest, FuzzyVec) { + Vector3D a(1, 2, 3); + Vector3D b(1 + 1e-10, 2, 3 - 1e-10); + EXPECT_TRUE(fuzzy_equal_vec(a, b, 1e-8)); +} + +TEST(ToleranceTest, FuzzyPoint) { + Point3D a(0, 0, 0); + Point3D b(1e-10, 0, 0); + EXPECT_TRUE(fuzzy_equal_point(a, b, 1e-8)); +} + +TEST(ToleranceTest, FuzzyParallel) { + Vector3D a(1, 0, 0); + Vector3D b(1, 1e-11, 0); + EXPECT_TRUE(fuzzy_parallel(a, b)); +} + +TEST(ToleranceTest, FuzzyPerpendicular) { + Vector3D a(1, 0, 0); + Vector3D b(0, 1, 0); + EXPECT_TRUE(fuzzy_perpendicular(a, b)); +} + +TEST(ToleranceTest, AdaptiveTolerance) { + double t1 = adaptive_tolerance(1.0); // 1mm model + double t2 = adaptive_tolerance(1000.0); // 1m model + EXPECT_GE(t2, t1); // larger model → larger tolerance +} + +TEST(ToleranceTest, ModelTolerance_Box) { + auto box = make_box(100, 100, 100); + double t = model_tolerance(box); + EXPECT_GT(t, 0.0); +} + +TEST(ToleranceTest, GlobalConfig) { + auto& cfg = ToleranceConfig::global(); + EXPECT_GT(cfg.vertex_merge, 0.0); + + ToleranceConfig custom; + custom.vertex_merge = 1e-4; + ToleranceConfig::set_global(custom); + EXPECT_EQ(ToleranceConfig::global().vertex_merge, 1e-4); + + // Reset + ToleranceConfig::set_global(ToleranceConfig{}); +} + +TEST(ToleranceTest, FuzzyGTE) { + EXPECT_TRUE(fuzzy_gte(1.0, 1.0 - 1e-10)); + EXPECT_TRUE(fuzzy_gte(1.0, 1.0)); + EXPECT_FALSE(fuzzy_gte(1.0, 1.1, 1e-6)); +} + +TEST(ToleranceTest, FuzzyLTE) { + EXPECT_TRUE(fuzzy_lte(1.0, 1.0 + 1e-10)); + EXPECT_TRUE(fuzzy_lte(1.0, 1.0)); + EXPECT_FALSE(fuzzy_lte(1.0, 0.9, 1e-6)); +} + +TEST(ToleranceTest, ToleranceConfig_AllDefaultPositive) { + ToleranceConfig cfg; + EXPECT_GT(cfg.vertex_merge, 0); + EXPECT_GT(cfg.edge_merge, 0); + EXPECT_GT(cfg.boolean, 0); + EXPECT_GT(cfg.angular, 0); +}