Files

215 lines
7.4 KiB
C++
Raw Permalink Normal View History

/// @file regression_degenerate.cpp
/// @brief 退化几何回归测试集
///
/// 覆盖场景:
/// 1. 零长度边(zero-length edge
/// 2. 零面积面(zero-area face
/// 3. 重合顶点(coincident vertices
/// 4. 自交体(self-intersecting body
///
/// 每个测试:构造退化体 → 布尔/修复/验证 → 断言不崩溃
#include <gtest/gtest.h>
#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 <cmath>
#include <limits>
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. 布尔自交:AA, 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;
});
}