/// @file regression_degenerate.cpp /// @brief 退化几何回归测试集 /// /// 覆盖场景: /// 1. 零长度边(zero-length edge) /// 2. 零面积面(zero-area face) /// 3. 重合顶点(coincident vertices) /// 4. 自交体(self-intersecting body) /// /// 每个测试:构造退化体 → 布尔/修复/验证 → 断言不崩溃 #include #include "vde/brep/brep.h" #include "vde/brep/modeling.h" #include "vde/brep/brep_boolean.h" #include "vde/brep/brep_validate.h" #include "vde/brep/brep_heal.h" #include "vde/core/point.h" #include #include using namespace vde::brep; using namespace vde::core; // ── 辅助函数 ── /// 断言 BrepModel 至少不会崩溃(尝试布尔、验证、修复都不会抛异常) void assert_no_crash(const BrepModel& body) { // 1. 基本验证:能调用 is_valid() EXPECT_NO_THROW({ body.is_valid(); }); // 2. 验证:能调用 validate() EXPECT_NO_THROW({ auto result = validate(body); (void)result; }); // 3. 布尔自交:A∪A, A∩A, A\\A EXPECT_NO_THROW({ auto u = brep_union(body, body); (void)u; }); EXPECT_NO_THROW({ auto i = brep_intersection(body, body); (void)i; }); EXPECT_NO_THROW({ auto d = brep_difference(body, body); (void)d; }); // 4. 修复:heal_topology 不崩溃 { BrepModel copy = body; EXPECT_NO_THROW({ heal_topology(copy, 1e-6); }); } // 5. 网格导出:to_mesh 不崩溃 EXPECT_NO_THROW({ auto mesh = body.to_mesh(); (void)mesh; }); } // ═══════════════════════════════════════════════ // 零长度边测试 // ═══════════════════════════════════════════════ TEST(RegressionDegenerate, ZeroLengthEdge_Box) { // 通过 make_box(0, 1, 1) 构造含退化边的体 // 宽度为 0 → 侧面退化为零面积面 EXPECT_NO_THROW({ auto body = make_box(0.0, 1.0, 1.0); assert_no_crash(body); }); } TEST(RegressionDegenerate, ZeroLengthEdge_ExtremeSmall) { // 极窄长方体(宽 ≤ 1e-12)→ 近乎零长度边 for (double w : {1e-12, 1e-10, 1e-8}) { EXPECT_NO_THROW({ auto body = make_box(w, 1.0, 1.0); assert_no_crash(body); }); } } // ═══════════════════════════════════════════════ // 零面积面测试 // ═══════════════════════════════════════════════ TEST(RegressionDegenerate, ZeroAreaFace_FlatBox) { // 一个维度为 0 → 退化成一个平面 EXPECT_NO_THROW({ auto box0 = make_box(0.0, 5.0, 5.0); assert_no_crash(box0); // 与正常 box 做布尔 auto box = make_box(3.0, 3.0, 3.0); auto u = brep_union(box0, box); (void)u.is_valid(); auto i = brep_intersection(box0, box); (void)i.is_valid(); auto d = brep_difference(box, box0); (void)d.is_valid(); }); } TEST(RegressionDegenerate, ZeroAreaFace_Sliver) { // 一个极其扁平的体(高度极其小) for (double h : {1e-12, 1e-10, 1e-9}) { EXPECT_NO_THROW({ auto thin = make_box(1.0, h, 1.0); assert_no_crash(thin); // 瘦短板与正常盒子的布尔 auto box = make_box(2.0, 2.0, 2.0); auto u = brep_union(thin, box); (void)u.is_valid(); }); } } // ═══════════════════════════════════════════════ // 重合顶点测试 // ═══════════════════════════════════════════════ TEST(RegressionDegenerate, CoincidentVertices_SelfUnion) { // 两个完全相同的盒子做布尔 → 所有面/边/顶点重合 auto box = make_box(2.0, 2.0, 2.0); EXPECT_NO_THROW({ auto u = brep_union(box, box); EXPECT_TRUE(u.is_valid()); auto result = validate(u); (void)result; }); } TEST(RegressionDegenerate, CoincidentVertices_SelfIntersection) { auto box = make_box(2.0, 2.0, 2.0); EXPECT_NO_THROW({ auto i = brep_intersection(box, box); EXPECT_TRUE(i.is_valid()); }); } TEST(RegressionDegenerate, CoincidentVertices_SelfDifference) { auto box = make_box(2.0, 2.0, 2.0); EXPECT_NO_THROW({ auto d = brep_difference(box, box); EXPECT_TRUE(d.is_valid()); }); } // ═══════════════════════════════════════════════ // 自交体测试(通过治疗修复验证) // ═══════════════════════════════════════════════ TEST(RegressionDegenerate, SelfIntersecting_Heal) { // 使用极小偏移的重复体模拟自交场景 auto box = make_box(2.0, 2.0, 2.0); // 重复布尔运算产生复杂拓扑,至少不应该崩溃 for (int i = 0; i < 5; ++i) { EXPECT_NO_THROW({ auto u = brep_union(box, box); auto result = validate(u); (void)result; BrepModel copy = u; heal_topology(copy, 1e-4); }); } // 使用不同尺寸的盒子布尔链式操作 auto small = make_box(0.5, 0.5, 0.5); EXPECT_NO_THROW({ auto diff = brep_difference(box, small); auto result = validate(diff); (void)result; heal_topology(diff, 1e-4); }); } // ═══════════════════════════════════════════════ // 退化圆柱测试 // ═══════════════════════════════════════════════ TEST(RegressionDegenerate, DegenerateCylinder_ZeroRadius) { // 半径为 0 的圆柱(退化为线) EXPECT_NO_THROW({ auto cyl = make_cylinder(0.0, 1.0, 8); assert_no_crash(cyl); }); } TEST(RegressionDegenerate, DegenerateCylinder_ZeroHeight) { // 高度为 0 的圆柱(退化为圆盘) EXPECT_NO_THROW({ auto cyl = make_cylinder(1.0, 0.0, 16); assert_no_crash(cyl); }); } TEST(RegressionDegenerate, DegenerateSphere_ZeroRadius) { // 半径为 0 的球(退化为点) EXPECT_NO_THROW({ auto sph = make_sphere(0.0, 8, 4); assert_no_crash(sph); }); } // ═══════════════════════════════════════════════ // 退化修复流水线测试 // ═══════════════════════════════════════════════ TEST(RegressionDegenerate, HealDegenerateModel) { // 从退化体开始,通过修复流水线处理 auto thin = make_box(1.0, 1e-12, 1.0); EXPECT_NO_THROW({ int n_gaps = heal_gaps(thin, 1e-6); (void)n_gaps; int n_slivers = heal_slivers(thin); (void)n_slivers; int n_oriented = heal_orientation(thin); (void)n_oriented; bool ok = heal_topology(thin, 1e-4); (void)ok; }); }