#include #include "vde/brep/brep.h" #include "vde/brep/modeling.h" #include "vde/brep/brep_validate.h" #include "vde/brep/brep_heal.h" #include "vde/brep/tolerance.h" #include "vde/curves/nurbs_surface.h" using namespace vde::brep; using namespace vde::core; using namespace vde::curves; namespace { /// Create a simple planar NURBS surface on the XY plane NurbsSurface make_plane_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 // ═══════════════════════════════════════════════════════════ // heal_gaps — BFS 顶点合并(新版) // ═══════════════════════════════════════════════════════════ TEST(BrepHealTest, HealGaps_ExactDuplicates_Merged) { BrepModel body; int v0 = body.add_vertex(Point3D(0, 0, 0)); int v1 = body.add_vertex(Point3D(1, 0, 0)); int v2 = body.add_vertex(Point3D(0, 1, 0)); int v0_dup = body.add_vertex(Point3D(0, 0, 0)); // exact duplicate int e0 = body.add_edge(v0, v1); int e1 = body.add_edge(v1, v2); int e2 = body.add_edge(v2, v0_dup); int loop_id = body.add_loop({e0, e1, e2}); auto surf = make_plane_surface(); int sid = body.add_surface(surf); body.add_face(sid, {loop_id}); size_t before = body.num_vertices(); int merged = heal_gaps(body, 1e-6); EXPECT_EQ(merged, 1); EXPECT_EQ(body.num_vertices(), before - 1); } TEST(BrepHealTest, HealGaps_NearCoincident_Merged) { BrepModel body; int v0 = body.add_vertex(Point3D(0, 0, 0)); int v1 = body.add_vertex(Point3D(1, 0, 0)); int v2 = body.add_vertex(Point3D(0, 1, 0)); int v_near = body.add_vertex(Point3D(1e-7, 1e-7, 1e-7)); int e0 = body.add_edge(v0, v1); int e1 = body.add_edge(v1, v2); int e2 = body.add_edge(v2, v_near); int loop_id = body.add_loop({e0, e1, e2}); auto surf = make_plane_surface(); int sid = body.add_surface(surf); body.add_face(sid, {loop_id}); size_t before = body.num_vertices(); // 小容差不合并 int m1 = heal_gaps(body, 1e-9); EXPECT_EQ(m1, 0); EXPECT_EQ(body.num_vertices(), before); // 大容差合并 int m2 = heal_gaps(body, 1e-6); EXPECT_EQ(m2, 1); EXPECT_EQ(body.num_vertices(), before - 1); } TEST(BrepHealTest, HealGaps_MultiComponent_BFS) { // 创建两组独立的接近顶点对,验证 BFS 分别合并 BrepModel body; // 组1: (0,0,0) 附近 int v0 = body.add_vertex(Point3D(0, 0, 0)); int v1 = body.add_vertex(Point3D(1e-8, 0, 0)); // 组2: (10,10,10) 附近 int v2 = body.add_vertex(Point3D(10, 10, 10)); int v3 = body.add_vertex(Point3D(10, 10 + 1e-8, 10)); int e0 = body.add_edge(v0, v2); int e1 = body.add_edge(v2, v1); int e2 = body.add_edge(v1, v3); int e3 = body.add_edge(v3, v0); int loop_id = body.add_loop({e0, e1, e2, e3}); auto surf = make_plane_surface(); int sid = body.add_surface(surf); body.add_face(sid, {loop_id}); size_t before = body.num_vertices(); // 4 int merged = heal_gaps(body, 1e-6); EXPECT_EQ(merged, 2); EXPECT_EQ(body.num_vertices(), before - 2); // 4 -> 2 } TEST(BrepHealTest, HealGaps_EdgeReferencesUpdated) { BrepModel body; int v0 = body.add_vertex(Point3D(0, 0, 0)); int v1 = body.add_vertex(Point3D(1, 0, 0)); int v0_dup = body.add_vertex(Point3D(0, 0, 0)); int e = body.add_edge(v0_dup, v1); int merged = heal_gaps(body, 1e-6); EXPECT_EQ(merged, 1); const auto& edge = body.edge(e); EXPECT_EQ(edge.v_start, v0); // v0_dup 应被重定向到 v0 } TEST(BrepHealTest, HealGaps_EmptyModel_ReturnsZero) { BrepModel empty; EXPECT_EQ(heal_gaps(empty, 1e-6), 0); } TEST(BrepHealTest, HealGaps_SingleVertex_ReturnsZero) { BrepModel body; body.add_vertex(Point3D(1, 2, 3)); EXPECT_EQ(heal_gaps(body, 1e-6), 0); EXPECT_EQ(body.num_vertices(), 1u); } // ═══════════════════════════════════════════════════════════ // heal_slivers — 退化面移除(新版) // ═══════════════════════════════════════════════════════════ TEST(BrepHealTest, HealSlivers_NoSlivers_ZeroRemoved) { auto box = make_box(2, 2, 2); int removed = heal_slivers(box); // 正常盒子不含退化面 EXPECT_EQ(removed, 0); } TEST(BrepHealTest, HealSlivers_EmptyModel_ReturnsZero) { BrepModel empty; EXPECT_EQ(heal_slivers(empty), 0); } TEST(BrepHealTest, HealSlivers_PreservesValidFaces) { auto box = make_box(2, 2, 2); size_t before = box.num_faces(); heal_slivers(box); // 没有退化面时面数不变 EXPECT_EQ(box.num_faces(), before); } // ═══════════════════════════════════════════════════════════ // heal_orientation — 统一面方向 + 欧拉验证(新版) // ═══════════════════════════════════════════════════════════ TEST(BrepHealTest, HealOrientation_Box_ProducesResult) { auto box = make_box(2, 2, 2); int flipped = heal_orientation(box); EXPECT_GE(flipped, 0); } TEST(BrepHealTest, HealOrientation_EmptyModel_NoFlipped) { BrepModel empty; int flipped = heal_orientation(empty); EXPECT_EQ(flipped, 0); } TEST(BrepHealTest, HealOrientation_FollowedByValidate) { auto box = make_box(2, 2, 2); heal_orientation(box); auto result = validate(box); // 修复后验证结果应包含欧拉示性数 EXPECT_GE(result.watertightness, 0.0); EXPECT_NE(result.euler_characteristic, 0); // 非空模型应有欧拉值 } // ═══════════════════════════════════════════════════════════ // heal_topology — 一站式修复新版 // ═══════════════════════════════════════════════════════════ TEST(BrepHealTest, HealTopology_Box_Completes) { auto box = make_box(2, 3, 4); bool valid = heal_topology(box); EXPECT_TRUE(valid || !valid); // 不崩溃即通过 } TEST(BrepHealTest, HealTopology_Cylinder_Completes) { auto cyl = make_cylinder(1.0, 3.0); bool valid = heal_topology(cyl); auto result = validate(cyl); EXPECT_TRUE(result.valid || !result.valid); } TEST(BrepHealTest, HealTopology_DuplicateVertexModel_ReducesVertices) { BrepModel body; int v0 = body.add_vertex(Point3D(0, 0, 0)); int v1 = body.add_vertex(Point3D(1, 0, 0)); int v2 = body.add_vertex(Point3D(0, 1, 0)); int v0_dup = body.add_vertex(Point3D(0, 0, 0)); int e0 = body.add_edge(v0, v1); int e1 = body.add_edge(v1, v2); int e2 = body.add_edge(v2, v0_dup); int loop_id = body.add_loop({e0, e1, e2}); auto surf = make_plane_surface(); int sid = body.add_surface(surf); body.add_face(sid, {loop_id}); size_t before = body.num_vertices(); heal_topology(body, 1e-6); EXPECT_LE(body.num_vertices(), before); } // ═══════════════════════════════════════════════════════════ // heal_step_import — STEP 导入后自动修复(新版) // ═══════════════════════════════════════════════════════════ TEST(BrepHealTest, HealStepImport_Box_ReturnsNonNegative) { auto box = make_box(2, 2, 2); int result = heal_step_import(box); // 正常模型应该返回 >= 0 EXPECT_GE(result, 0); } TEST(BrepHealTest, HealStepImport_EmptyModel_ReturnsZero) { BrepModel empty; int result = heal_step_import(empty); EXPECT_EQ(result, 0); } TEST(BrepHealTest, HealStepImport_DuplicateVertexModel_Repairs) { BrepModel body; int v0 = body.add_vertex(Point3D(0, 0, 0)); int v1 = body.add_vertex(Point3D(1, 0, 0)); int v2 = body.add_vertex(Point3D(0, 1, 0)); int v_dup = body.add_vertex(Point3D(0, 0, 1e-8)); int e0 = body.add_edge(v0, v1); int e1 = body.add_edge(v1, v2); int e2 = body.add_edge(v2, v_dup); int loop_id = body.add_loop({e0, e1, e2}); auto surf = make_plane_surface(); int sid = body.add_surface(surf); body.add_face(sid, {loop_id}); int result = heal_step_import(body); // 应该有合并操作(自动容差可能更大) EXPECT_GE(result, 0); } // ═══════════════════════════════════════════════════════════ // 旧版兼容测试(委托给新版实现) // ═══════════════════════════════════════════════════════════ TEST(BrepHealTest, Legacy_MergeVertices_StillWorks) { BrepModel body; int v0 = body.add_vertex(Point3D(0, 0, 0)); int v1 = body.add_vertex(Point3D(1, 0, 0)); int v2 = body.add_vertex(Point3D(0, 1, 0)); int v0_dup = body.add_vertex(Point3D(0, 0, 0)); int e0 = body.add_edge(v0, v1); int e1 = body.add_edge(v1, v2); int e2 = body.add_edge(v2, v0_dup); int loop_id = body.add_loop({e0, e1, e2}); auto surf = make_plane_surface(); int sid = body.add_surface(surf); body.add_face(sid, {loop_id}); size_t before = body.num_vertices(); int merged = heal_merge_vertices(body, 1e-6); EXPECT_EQ(merged, 1); EXPECT_EQ(body.num_vertices(), before - 1); } TEST(BrepHealTest, Legacy_CloseGaps_StillWorks) { BrepModel body; int v0 = body.add_vertex(Point3D(0, 0, 0)); int v1 = body.add_vertex(Point3D(1, 0, 0)); int v2 = body.add_vertex(Point3D(0, 1, 0)); int v_near = body.add_vertex(Point3D(5e-5, 0, 0)); int e0 = body.add_edge(v0, v1); int e1 = body.add_edge(v1, v2); int e2 = body.add_edge(v2, v_near); int loop_id = body.add_loop({e0, e1, e2}); auto surf = make_plane_surface(); int sid = body.add_surface(surf); body.add_face(sid, {loop_id}); int closed = heal_close_gaps(body, 1e-4); EXPECT_GE(closed, 0); // 可能合并了顶点 } TEST(BrepHealTest, Legacy_MergeEdges_Box_NoDuplicates) { auto box = make_box(2, 2, 2); int merged = heal_merge_edges(box, 1e-6); EXPECT_GE(merged, 0); // 盒子边不重复 } TEST(BrepHealTest, Legacy_MergeEdges_IdenticalStraightEdges) { BrepModel body; int v0 = body.add_vertex(Point3D(0, 0, 0)); int v1 = body.add_vertex(Point3D(1, 0, 0)); int v2 = body.add_vertex(Point3D(1, 1, 0)); int v3 = body.add_vertex(Point3D(0, 1, 0)); int e0 = body.add_edge(v0, v1); int e1 = body.add_edge(v1, v2); int e2 = body.add_edge(v2, v0); int loop1 = body.add_loop({e0, e1, e2}); int e2_dup = body.add_edge(v2, v0); int e3 = body.add_edge(v0, v3); int e4 = body.add_edge(v3, v2); int loop2 = body.add_loop({e2_dup, e3, e4}); auto surf = make_plane_surface(); int sid = body.add_surface(surf); body.add_face(sid, {loop1}); body.add_face(sid, {loop2}); size_t before = body.num_edges(); int merged = heal_merge_edges(body, 1e-6); EXPECT_EQ(merged, 1); EXPECT_EQ(body.num_edges(), before - 1); } TEST(BrepHealTest, Legacy_MergeEdges_EmptyModel_ReturnsZero) { BrepModel empty; EXPECT_EQ(heal_merge_edges(empty), 0); } // ═══════════════════════════════════════════════════════════ // 验证修复结果包含容差信息 // ═══════════════════════════════════════════════════════════ TEST(BrepHealTest, ValidateAfterHeal_ReportsTolerance) { auto box = make_box(2, 2, 2); heal_topology(box, 1e-6); auto result = validate(box); EXPECT_GT(result.tolerance_used, 0.0); }