Files
ViewDesignEngine/tests/brep/test_boolean_fallback.cpp
T

263 lines
12 KiB
C++
Raw Normal View History

/**
* @file test_boolean_fallback.cpp
* @brief 布尔策略回退阶梯 单元测试
*
* 覆盖场景:
* 1. Level 1 正常成功(ssi_boolean 直接返回)
* 2. Level 2 容差回退触发
* 3. Level 3 网格回退触发
* 4. Level 4 SDF 兜底
* 5. 诊断信息验证(levels_tried, diagnostics
* 6. brep_quality 标记
* 7. 空体/退化输入
* 8. summary() 输出
* 9. 三操差异(Union/Intersection/Difference
* 10. 异常安全(不崩溃)
*/
#include <gtest/gtest.h>
#include "vde/brep/brep.h"
#include "vde/brep/modeling.h"
#include "vde/brep/boolean_fallback.h"
#include "vde/brep/ssi_boolean.h"
#include "vde/brep/tolerant_modeling.h"
using namespace vde::brep;
using namespace vde::core;
// ═══════════════════════════════════════════════════════════
// Test 1: Level 1 正常成功 — 两个简单立方体 Union
// ═══════════════════════════════════════════════════════════
TEST(BooleanFallbackTest, Level1_Success_SimpleUnion) {
auto box1 = make_box(2.0, 2.0, 2.0);
auto box2 = make_box(2.0, 2.0, 2.0);
auto result = brep_boolean_robust(box1, box2, FallbackBoolOp::Union);
EXPECT_EQ(result.final_level, 1);
EXPECT_TRUE(result.brep_quality);
EXPECT_GT(result.result_body.num_vertices(), 0u);
EXPECT_GT(result.result_body.num_faces(), 0u);
EXPECT_TRUE(result.brep_body.has_value());
EXPECT_EQ(result.levels_tried.size(), 1u);
EXPECT_EQ(result.levels_tried[0], 1);
}
// ═══════════════════════════════════════════════════════════
// Test 2: Level 1 正常成功 — Intersection
// ═══════════════════════════════════════════════════════════
TEST(BooleanFallbackTest, Level1_Success_Intersection) {
auto box1 = make_box(4.0, 4.0, 4.0);
auto box2 = make_box(4.0, 4.0, 4.0);
auto result = brep_boolean_robust(box1, box2, FallbackBoolOp::Intersection);
EXPECT_EQ(result.final_level, 1);
EXPECT_TRUE(result.brep_quality);
EXPECT_GT(result.result_body.num_vertices(), 0u);
}
// ═══════════════════════════════════════════════════════════
// Test 3: Level 1 正常成功 — Difference
// ═══════════════════════════════════════════════════════════
TEST(BooleanFallbackTest, Level1_Success_Difference) {
auto big = make_box(4.0, 4.0, 4.0);
auto small = make_box(2.0, 2.0, 2.0);
auto result = brep_boolean_robust(big, small, FallbackBoolOp::Difference);
EXPECT_EQ(result.final_level, 1);
EXPECT_TRUE(result.brep_quality);
EXPECT_GT(result.result_body.num_faces(), 0u);
}
// ═══════════════════════════════════════════════════════════
// Test 4: 诊断信息验证 — levels_tried + diagnostics 数量一致
// ═══════════════════════════════════════════════════════════
TEST(BooleanFallbackTest, Diagnostics_Tracking) {
auto box1 = make_box(2.0, 2.0, 2.0);
auto box2 = make_box(2.0, 2.0, 2.0);
auto result = brep_boolean_robust(box1, box2, FallbackBoolOp::Union);
// levels_tried 和 level_diagnostics 数量必须一致
EXPECT_EQ(result.levels_tried.size(), result.level_diagnostics.size());
// Level 1 成功时,只尝试了 1 级
EXPECT_EQ(result.levels_tried.size(), 1u);
EXPECT_EQ(result.final_level, 1);
// final_level 必须在 levels_tried 中
bool found = false;
for (int lv : result.levels_tried) {
if (lv == result.final_level) { found = true; break; }
}
EXPECT_TRUE(found);
}
// ═══════════════════════════════════════════════════════════
// Test 5: brep_quality 标记 — Level 1/2 为 true
// ═══════════════════════════════════════════════════════════
TEST(BooleanFallbackTest, BRepQuality_Flag_Level1) {
auto box1 = make_box(2.0, 2.0, 2.0);
auto box2 = make_box(2.0, 2.0, 2.0);
auto result = brep_boolean_robust(box1, box2, FallbackBoolOp::Union);
if (result.final_level <= 2) {
EXPECT_TRUE(result.brep_quality);
EXPECT_TRUE(result.brep_body.has_value());
} else {
EXPECT_FALSE(result.brep_quality);
}
}
// ═══════════════════════════════════════════════════════════
// Test 6: summary() 输出非空
// ═══════════════════════════════════════════════════════════
TEST(BooleanFallbackTest, Summary_ProducesOutput) {
auto box1 = make_box(2.0, 2.0, 2.0);
auto box2 = make_box(2.0, 2.0, 2.0);
auto result = brep_boolean_robust(box1, box2, FallbackBoolOp::Union);
auto s = result.summary();
EXPECT_FALSE(s.empty());
EXPECT_NE(s.find("BooleanFallbackResult"), std::string::npos);
EXPECT_NE(s.find("final_level"), std::string::npos);
EXPECT_NE(s.find("levels_tried"), std::string::npos);
}
// ═══════════════════════════════════════════════════════════
// Test 7: 空体输入不崩溃
// ═══════════════════════════════════════════════════════════
TEST(BooleanFallbackTest, EmptyBody_DoesNotCrash) {
BrepModel empty_a;
BrepModel empty_b;
auto box = make_box(2.0, 2.0, 2.0);
// 两个空体
auto r1 = brep_boolean_robust(empty_a, empty_b, FallbackBoolOp::Union);
EXPECT_NO_THROW({ auto s = r1.summary(); (void)s; });
// 一个空体 + 正常体
auto r2 = brep_boolean_robust(empty_a, box, FallbackBoolOp::Union);
EXPECT_NO_THROW({ auto s = r2.summary(); (void)s; });
auto r3 = brep_boolean_robust(box, empty_a, FallbackBoolOp::Intersection);
EXPECT_NO_THROW({ auto s = r3.summary(); (void)s; });
}
// ═══════════════════════════════════════════════════════════
// Test 8: 每级内部函数独立可调
// ═══════════════════════════════════════════════════════════
TEST(BooleanFallbackTest, LevelFunctions_IndependentlyCallable) {
auto box1 = make_box(2.0, 2.0, 2.0);
auto box2 = make_box(3.0, 3.0, 3.0);
// Level 1: try_ssi_boolean
{
auto [diag, ok] = try_ssi_boolean(box1, box2, FallbackBoolOp::Union);
EXPECT_TRUE(ok);
EXPECT_TRUE(diag.failed_faces.empty());
}
// Level 2: try_tolerant_boolean
{
auto [diag, ok] = try_tolerant_boolean(box1, box2, FallbackBoolOp::Union);
EXPECT_TRUE(ok);
EXPECT_TRUE(diag.failed_faces.empty());
}
// Level 3: try_mesh_boolean_fallback
{
auto [mesh, ok] = try_mesh_boolean_fallback(box1, box2, FallbackBoolOp::Union);
EXPECT_TRUE(ok);
EXPECT_GT(mesh.num_vertices(), 0u);
}
// Level 4: try_sdf_boolean_fallback (永不失败)
{
auto mesh = try_sdf_boolean_fallback(box1, box2, FallbackBoolOp::Union);
EXPECT_GT(mesh.num_vertices(), 0u);
}
}
// ═══════════════════════════════════════════════════════════
// Test 9: 三操作都支持(Union / Intersection / Difference
// ═══════════════════════════════════════════════════════════
TEST(BooleanFallbackTest, AllThreeOperations_Supported) {
auto box1 = make_box(2.0, 2.0, 2.0);
auto box2 = make_box(3.0, 3.0, 3.0);
auto r_union = brep_boolean_robust(box1, box2, FallbackBoolOp::Union);
EXPECT_GT(r_union.result_body.num_vertices(), 0u);
auto r_inter = brep_boolean_robust(box1, box2, FallbackBoolOp::Intersection);
EXPECT_GT(r_inter.result_body.num_vertices(), 0u);
auto r_diff = brep_boolean_robust(box2, box1, FallbackBoolOp::Difference);
EXPECT_GT(r_diff.result_body.num_vertices(), 0u);
}
// ═══════════════════════════════════════════════════════════
// Test 10: 重叠立方体 — 各级都能产生有效结果
// ═══════════════════════════════════════════════════════════
TEST(BooleanFallbackTest, OverlappingBoxes_AllLevelsProduceValidMesh) {
auto box1 = make_box(2.0, 2.0, 2.0);
auto box2 = make_box(2.0, 2.0, 2.0);
auto result = brep_boolean_robust(box1, box2, FallbackBoolOp::Union);
// 无论哪一级成功,结果网格必须有效
EXPECT_GT(result.result_body.num_vertices(), 0u);
EXPECT_GT(result.result_body.num_faces(), 0u);
// final_level 在 1-4 范围内
EXPECT_GE(result.final_level, 1);
EXPECT_LE(result.final_level, 4);
// 诊断链非空
EXPECT_FALSE(result.level_diagnostics.empty());
}
// ═══════════════════════════════════════════════════════════
// Test 11: SDF Level 4 单独验证(盒+球并集)
// ═══════════════════════════════════════════════════════════
TEST(BooleanFallbackTest, SDF_Level4_BoxWithSphere) {
auto box = make_box(3.0, 3.0, 3.0);
auto sphere = make_sphere(1.5);
auto mesh = try_sdf_boolean_fallback(box, sphere, FallbackBoolOp::Union);
EXPECT_GT(mesh.num_vertices(), 0u);
EXPECT_GT(mesh.num_faces(), 0u);
}
// ═══════════════════════════════════════════════════════════
// Test 12: 异常安全 — 不崩溃
// ═══════════════════════════════════════════════════════════
TEST(BooleanFallbackTest, ExceptionSafety_AllOperations) {
auto box = make_box(2.0, 2.0, 2.0);
auto sphere = make_sphere(1.0);
// 重复相同操作不应该崩溃
for (int i = 0; i < 3; ++i) {
EXPECT_NO_THROW({
auto r = brep_boolean_robust(box, sphere, FallbackBoolOp::Union);
auto s = r.summary(); (void)s;
});
}
}