#include #include "vde/brep/brep.h" #include "vde/brep/modeling.h" #include "vde/brep/brep_validate.h" #include "vde/brep/tolerance.h" #include "vde/brep/advanced_healing.h" #include "vde/curves/nurbs_surface.h" #include "vde/curves/nurbs_curve.h" using namespace vde::brep; using namespace vde::core; using namespace vde::curves; namespace { /// 创建测试用简单平面曲面 NurbsSurface make_test_surface() { std::vector> grid = { {Point3D(-1, -1, 0), Point3D(-1, 1, 0)}, {Point3D( 1, -1, 0), Point3D( 1, 1, 0)} }; return NurbsSurface(grid, {0,0,1,1}, {0,0,1,1}, std::vector>{}, 1, 1); } } // namespace // ═══════════════════════════════════════════════════════════ // auto_heal_pipeline 测试 // ═══════════════════════════════════════════════════════════ TEST(AdvancedHealingTest, AutoHealPipeline_ValidBox_NoChanges) { auto box = make_box(2, 2, 2); auto pre_val = validate(box); EXPECT_TRUE(pre_val.valid); auto report = auto_heal_pipeline(box); EXPECT_TRUE(report.success); EXPECT_GT(report.steps_executed.size(), 0u); EXPECT_TRUE(report.after_validation.valid); } TEST(AdvancedHealingTest, AutoHealPipeline_DuplicateVertices_Healed) { auto box = make_box(2, 2, 2); // Add duplicate near one vertex const auto& v0 = box.vertex(0); box.add_vertex(v0.point + Vector3D(1e-7, 1e-7, 1e-7)); auto report = auto_heal_pipeline(box); EXPECT_TRUE(report.success); EXPECT_GT(report.total_fixes, 0); } TEST(AdvancedHealingTest, AutoHealPipeline_StagesExecuted) { auto box = make_box(2, 2, 2); auto report = auto_heal_pipeline(box); // All 7 stages should execute EXPECT_GE(report.steps_executed.size(), 5u); EXPECT_EQ(report.details.size(), report.steps_executed.size()); } TEST(AdvancedHealingTest, AutoHealPipeline_ReportContainsValidations) { auto box = make_box(2, 2, 2); auto report = auto_heal_pipeline(box); EXPECT_TRUE(report.before_validation.valid); EXPECT_TRUE(report.after_validation.valid); } // ═══════════════════════════════════════════════════════════ // face_splitting 测试 // ═══════════════════════════════════════════════════════════ TEST(AdvancedHealingTest, FaceSplitting_InvalidFaceId_ReturnsEmpty) { auto box = make_box(2, 2, 2); // Create a simple p-curve (line in UV space) std::vector cpts = {Point3D(0.2, 0.5, 0), Point3D(0.8, 0.5, 0)}; NurbsCurve pcurve(cpts, {0,0,1,1}, {}, 1); auto result = face_splitting(box, 999, pcurve); EXPECT_TRUE(result.empty()); } TEST(AdvancedHealingTest, FaceSplitting_ValidFace_NoCrash) { auto box = make_box(2, 2, 2); // p-curve: horizontal line at v=0.5 std::vector cpts = {Point3D(0.1, 0.5, 0), Point3D(0.9, 0.5, 0)}; NurbsCurve pcurve(cpts, {0,0,1,1}, {}, 1); auto result = face_splitting(box, 0, pcurve); // Should not crash; may or may not split depending on intersection EXPECT_FALSE(result.empty()); } // ═══════════════════════════════════════════════════════════ // face_merging 测试 // ═══════════════════════════════════════════════════════════ TEST(AdvancedHealingTest, FaceMerging_SingleFace_NoMerge) { auto box = make_box(2, 2, 2); int merged = face_merging(box, {0}); EXPECT_EQ(merged, 0); } TEST(AdvancedHealingTest, FaceMerging_EmptyList_NoMerge) { auto box = make_box(2, 2, 2); int merged = face_merging(box, {}); EXPECT_EQ(merged, 0); } TEST(AdvancedHealingTest, FaceMerging_AllFaces_NoCrash) { auto box = make_box(2, 2, 2); std::vector all_faces; for (size_t fi = 0; fi < box.num_faces(); ++fi) all_faces.push_back(static_cast(fi)); int merged = face_merging(box, all_faces); // On a box, adjacent faces are orthogonal — no coplanar merges expected EXPECT_EQ(merged, 0); } // ═══════════════════════════════════════════════════════════ // topology_optimization 测试 // ═══════════════════════════════════════════════════════════ TEST(AdvancedHealingTest, TopologyOptimization_ValidBox_Optimized) { auto box = make_box(2, 2, 2); int optimized = topology_optimization(box); // A clean box may have zero or some optimization EXPECT_GE(optimized, 0); EXPECT_TRUE(box.is_valid()); } TEST(AdvancedHealingTest, TopologyOptimization_AfterHealing_RemainsValid) { auto box = make_box(2, 2, 2); heal_topology(box); int optimized = topology_optimization(box); EXPECT_GE(optimized, 0); EXPECT_TRUE(validate(box).valid); } // ═══════════════════════════════════════════════════════════ // tolerance_analysis 测试 // ═══════════════════════════════════════════════════════════ TEST(AdvancedHealingTest, ToleranceAnalysis_Box_GeneratesReport) { auto box = make_box(2, 2, 2); auto report = tolerance_analysis(box); EXPECT_EQ(report.total_vertices, box.num_vertices()); EXPECT_EQ(report.total_edges, box.num_edges()); EXPECT_GT(report.model_size, 0.0); EXPECT_GT(report.items.size(), 0u); EXPECT_TRUE(report.overall_pass); } TEST(AdvancedHealingTest, ToleranceAnalysis_ReportHasRecommendations) { auto box = make_box(2, 2, 2); auto report = tolerance_analysis(box); EXPECT_FALSE(report.recommendations.empty()); } TEST(AdvancedHealingTest, ToleranceAnalysis_CustomConfig_Applied) { auto box = make_box(2, 2, 2); ToleranceConfig cfg; cfg.vertex_merge = 1e-3; cfg.sliver_area = 1e-8; auto report = tolerance_analysis(box, cfg); EXPECT_DOUBLE_EQ(report.config_used.vertex_merge, cfg.vertex_merge); EXPECT_DOUBLE_EQ(report.config_used.sliver_area, cfg.sliver_area); } // ═══════════════════════════════════════════════════════════ // watertight_verification 测试 // ═══════════════════════════════════════════════════════════ TEST(AdvancedHealingTest, WatertightVerification_Box_IsWatertight) { auto box = make_box(2, 2, 2); auto result = watertight_verification(box); EXPECT_TRUE(result.is_watertight); EXPECT_GE(result.watertight_score, 0.999); EXPECT_EQ(result.boundary_edges, 0u); EXPECT_EQ(result.dangling_edges, 0u); EXPECT_EQ(result.non_manifold_edges, 0u); } TEST(AdvancedHealingTest, WatertightVerification_Box_NoGaps) { auto box = make_box(2, 2, 2); auto result = watertight_verification(box); EXPECT_TRUE(result.gaps.empty()); } TEST(AdvancedHealingTest, WatertightVerification_ResultStructure) { auto box = make_box(2, 2, 2); auto result = watertight_verification(box); EXPECT_GT(result.total_edges, 0u); EXPECT_GE(result.watertight_score, 0.0); EXPECT_LE(result.watertight_score, 1.0); }