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
This commit is contained in:
@@ -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 <cmath>
|
||||
#include <functional>
|
||||
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#include <gtest/gtest.h>
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user