#include #include "vde/brep/brep_drawing.h" #include "vde/brep/dxf_import.h" #include "vde/brep/modeling.h" #include #include #include using namespace vde::brep; using namespace vde::core; // ═══════════════════════════════════════════════════════════ // generate_views — 基本测试 // ═══════════════════════════════════════════════════════════ TEST(BrepDrawingTest, GenerateViews_Box_ReturnsFourViews) { auto box = make_box(2, 3, 4); auto views = generate_views(box); EXPECT_EQ(views.size(), 4u); EXPECT_EQ(views[0].name, "front"); EXPECT_EQ(views[1].name, "top"); EXPECT_EQ(views[2].name, "right"); EXPECT_EQ(views[3].name, "iso"); } TEST(BrepDrawingTest, GenerateViews_Box_EachViewHasSegments) { auto box = make_box(2, 3, 4); auto views = generate_views(box); for (const auto& v : views) { EXPECT_GT(v.total_segments(), 0u) << "View " << v.name << " should have segments"; } } TEST(BrepDrawingTest, GenerateViews_Box_FrontViewHasExpectedContent) { auto box = make_box(2, 2, 2); auto views = generate_views(box); const auto& front = views[0]; EXPECT_EQ(front.name, "front"); // Front view: XY plane projection, should show X=[-1,1], Y=[-1,1] EXPECT_GT(front.total_segments(), 0u); } TEST(BrepDrawingTest, GenerateViews_Box_ViewDirections) { auto box = make_box(1, 1, 1); auto views = generate_views(box); // Front: looking down -Z EXPECT_NEAR(views[0].view_direction.z(), -1.0, 1e-6); // Top: looking down -Y EXPECT_NEAR(views[1].view_direction.y(), -1.0, 1e-6); // Right: looking down -X EXPECT_NEAR(views[2].view_direction.x(), -1.0, 1e-6); } TEST(BrepDrawingTest, GenerateViews_EmptyBody_ReturnsEmpty) { BrepModel empty; auto views = generate_views(empty); EXPECT_TRUE(views.empty()); } TEST(BrepDrawingTest, GenerateViews_Cylinder_ReturnsFourViews) { auto cyl = make_cylinder(1.0, 3.0, 16); auto views = generate_views(cyl); EXPECT_EQ(views.size(), 4u); for (const auto& v : views) { EXPECT_GT(v.total_segments(), 0u); } } TEST(BrepDrawingTest, GenerateViews_Sphere_ReturnsFourViews) { auto sphere = make_sphere(2.0, 16, 8); auto views = generate_views(sphere); EXPECT_EQ(views.size(), 4u); // 球所有视图的主要轮廓应类似 for (const auto& v : views) { ASSERT_GT(v.total_segments(), 0u); } } // ═══════════════════════════════════════════════════════════ // section_view — 测试 // ═══════════════════════════════════════════════════════════ TEST(BrepDrawingTest, SectionView_Box_AtCenter) { auto box = make_box(4, 4, 4); // 截平面 z = 0(XY 平面,法向量沿 Z) auto view = section_view(box, Point3D(0, 0, 0), Vector3D(0, 0, 1)); EXPECT_EQ(view.name, "section"); // 截面应为矩形轮廓,至少有 4 个交点形成轮廓线段 // + hatch 线,总数应 > 4 ASSERT_GT(view.total_segments(), 4u); } TEST(BrepDrawingTest, SectionView_Box_HorizontalPlane) { auto box = make_box(4, 4, 4); // z = 1 的水平截面(穿过盒子) auto view = section_view(box, Point3D(0, 0, 1), Vector3D(0, 0, 1)); ASSERT_GT(view.total_segments(), 4u); } TEST(BrepDrawingTest, SectionView_Box_PlaneOutside) { auto box = make_box(2, 2, 2); // 截平面在盒子之外(z = 5) auto view = section_view(box, Point3D(0, 0, 5), Vector3D(0, 0, 1)); // 交点不足 3 个 → 无法形成截面 EXPECT_LT(view.total_segments(), 3u); } TEST(BrepDrawingTest, SectionView_EmptyBody_ReturnsEmpty) { BrepModel empty; auto view = section_view(empty, Point3D(0, 0, 0), Vector3D(0, 0, 1)); EXPECT_EQ(view.name, "section"); EXPECT_EQ(view.total_segments(), 0u); } TEST(BrepDrawingTest, SectionView_Cylinder_Midplane) { auto cyl = make_cylinder(2.0, 6.0, 32); // 垂直圆柱的 z=0 截面 → 圆形截面 auto view = section_view(cyl, Point3D(0, 0, 0), Vector3D(0, 0, 1)); ASSERT_GT(view.segments.size(), 3u); } // ═══════════════════════════════════════════════════════════ // DXF 导出 — 测试 // ═══════════════════════════════════════════════════════════ TEST(BrepDrawingTest, ExportDxf_CreatesFile) { auto box = make_box(2, 2, 2); auto views = generate_views(box); const char* filepath = "test_output.dxf"; std::remove(filepath); // 清理旧文件 bool ok = export_dxf(filepath, views); EXPECT_TRUE(ok); // 验证文件存在 std::ifstream in(filepath); EXPECT_TRUE(in.is_open()); std::string content((std::istreambuf_iterator(in)), std::istreambuf_iterator()); in.close(); // 验证关键结构 EXPECT_NE(content.find("SECTION"), std::string::npos); EXPECT_NE(content.find("ENTITIES"), std::string::npos); EXPECT_NE(content.find("LINE"), std::string::npos); EXPECT_NE(content.find("ENDSEC"), std::string::npos); EXPECT_NE(content.find("EOF"), std::string::npos); // 验证有至少一条 LINE 实体 EXPECT_NE(content.find("LINE"), std::string::npos); std::remove(filepath); } TEST(BrepDrawingTest, ExportDxf_HasLayers) { auto box = make_box(2, 2, 2); auto views = generate_views(box); const char* filepath = "test_layers.dxf"; std::remove(filepath); export_dxf(filepath, views); std::ifstream in(filepath); std::string content((std::istreambuf_iterator(in)), std::istreambuf_iterator()); in.close(); EXPECT_NE(content.find("visible"), std::string::npos); EXPECT_NE(content.find("DASHED"), std::string::npos); std::remove(filepath); } TEST(BrepDrawingTest, ExportDxf_EmptyViews_StillWritesSkeleton) { std::vector empty_views; const char* filepath = "test_empty.dxf"; std::remove(filepath); bool ok = export_dxf(filepath, empty_views); EXPECT_TRUE(ok); std::ifstream in(filepath); std::string content((std::istreambuf_iterator(in)), std::istreambuf_iterator()); in.close(); EXPECT_NE(content.find("SECTION"), std::string::npos); EXPECT_NE(content.find("EOF"), std::string::npos); std::remove(filepath); } TEST(BrepDrawingTest, ExportDxf_InvalidPath_ReturnsFalse) { auto box = make_box(1, 1, 1); auto views = generate_views(box); bool ok = export_dxf("/nonexistent_dir_xyz/test.dxf", views); EXPECT_FALSE(ok); } // ═══════════════════════════════════════════════════════════ // 隐藏线 — 测试 // ═══════════════════════════════════════════════════════════ TEST(BrepDrawingTest, HiddenLines_Box_HasHiddenEdges) { auto box = make_box(2, 2, 2); auto views = generate_views(box); // 每个标准视图均应有隐藏边 for (size_t i = 0; i < std::min(views.size(), size_t(3)); ++i) { // 前/俯/右视图都应该有隐藏线 EXPECT_GE(views[i].hidden_lines.size() + views[i].segments.size(), 1u); } } TEST(BrepDrawingTest, DrawSegment_DefaultNotHidden) { DrawSegment seg{0, 0, 1, 1}; EXPECT_FALSE(seg.hidden); } TEST(BrepDrawingTest, DrawSegment_Hidden) { DrawSegment seg{0, 0, 1, 1, true}; EXPECT_TRUE(seg.hidden); } TEST(BrepDrawingTest, ProjectionView_TotalSegments) { ProjectionView v; v.segments = {{0,0,1,0}, {1,0,1,1}}; v.hidden_lines = {{0,1,0,0, true}}; EXPECT_EQ(v.total_segments(), 3u); } // ═══════════════════════════════════════════════════════════ // Offset (stepped) section views // ═══════════════════════════════════════════════════════════ TEST(OffsetSectionTest, SingleOffset) { auto box = make_box(4, 4, 4); auto view = offset_section_view(box, Vector3D(0, 0, 1), {0.0}); EXPECT_FALSE(view.segments.empty()); EXPECT_EQ(view.name, "offset_section"); } TEST(OffsetSectionTest, MultipleOffsets) { auto box = make_box(4, 4, 4); auto view = offset_section_view(box, Vector3D(0, 0, 1), {-1.0, 0.0, 1.0}); EXPECT_GE(view.segments.size(), 3u); // section + step lines } TEST(OffsetSectionTest, EmptyOffsets) { auto box = make_box(4, 4, 4); auto view = offset_section_view(box, Vector3D(0, 0, 1), {}); EXPECT_TRUE(view.segments.empty()); } TEST(OffsetSectionTest, CylinderSection) { auto cyl = make_cylinder(2, 6, 32); auto view = offset_section_view(cyl, Vector3D(0, 0, 1), {-2.0, 0.0, 2.0}); EXPECT_GE(view.total_segments(), 3u); } TEST(DxfImportTest, EmptyString) { auto result = import_dxf_from_string(""); EXPECT_EQ(result.entities_parsed, 0); EXPECT_EQ(result.contours.size(), 0u); } TEST(DxfImportTest, MinimalDxfWithLine) { std::string dxf = R"(0 SECTION 2 ENTITIES 0 LINE 8 0 10 0.0 20 0.0 30 0.0 11 5.0 21 5.0 31 0.0 0 ENDSEC 0 EOF )"; auto result = import_dxf_from_string(dxf); EXPECT_GE(result.entities_parsed, 1); EXPECT_GE(result.contours.size(), 1u); } TEST(DxfImportTest, MinimalDxfWithCircle) { std::string dxf = R"(0 SECTION 2 ENTITIES 0 CIRCLE 8 0 10 0.0 20 0.0 30 0.0 40 3.0 0 ENDSEC 0 EOF )"; auto result = import_dxf_from_string(dxf); EXPECT_GE(result.entities_parsed, 1); EXPECT_GE(result.contours.size(), 1u); // Circle should be closed EXPECT_TRUE(result.contours[0].closed); } TEST(DxfImportTest, MinimalDxfWithArc) { std::string dxf = R"(0 SECTION 2 ENTITIES 0 ARC 8 0 10 0.0 20 0.0 30 0.0 40 2.0 50 0.0 51 180.0 0 ENDSEC 0 EOF )"; auto result = import_dxf_from_string(dxf); EXPECT_GE(result.entities_parsed, 1); EXPECT_GE(result.contours.size(), 1u); } TEST(DxfImportTest, LwPolylineRectangle) { std::string dxf = R"(0 SECTION 2 ENTITIES 0 LWPOLYLINE 8 walls 90 4 70 1 10 0.0 20 0.0 10 10.0 20 0.0 10 10.0 20 5.0 10 0.0 20 5.0 0 ENDSEC 0 EOF )"; auto result = import_dxf_from_string(dxf); EXPECT_GE(result.entities_parsed, 1); // Find the LWPOLYLINE contour bool found_rect = false; for (const auto& c : result.contours) { if (c.layer == "walls" && c.points.size() >= 4) { found_rect = true; EXPECT_TRUE(c.closed); break; } } EXPECT_TRUE(found_rect); } TEST(DxfImportTest, SplineEntity) { std::string dxf = R"(0 SECTION 2 ENTITIES 0 SPLINE 8 curves 70 8 71 3 10 0.0 20 0.0 30 0.0 10 2.0 20 3.0 30 0.0 10 4.0 20 0.0 30 0.0 10 6.0 20 3.0 30 0.0 40 0.0 40 0.0 40 0.0 40 0.0 40 0.333 40 0.667 40 1.0 40 1.0 0 ENDSEC 0 EOF )"; auto result = import_dxf_from_string(dxf); EXPECT_GE(result.entities_parsed, 1); } TEST(DxfImportTest, UnknownEntityTypeIsSkipped) { std::string dxf = R"(0 SECTION 2 ENTITIES 0 MYSTERY_ENTITY 8 0 0 LINE 8 0 10 0 20 0 30 0 11 1 21 1 31 0 0 ENDSEC 0 EOF )"; auto result = import_dxf_from_string(dxf); EXPECT_GE(result.entities_parsed, 1); EXPECT_GE(result.entities_skipped, 1); } TEST(DxfImportTest, MultipleEntities) { std::string dxf = R"(0 SECTION 2 ENTITIES 0 LINE 8 0 10 0 20 0 30 0 11 1 21 0 31 0 0 CIRCLE 8 holes 10 5 20 5 30 0 40 3.0 0 ARC 8 holes 10 0 20 0 30 0 40 2.5 50 90 51 270 0 ENDSEC 0 EOF )"; auto result = import_dxf_from_string(dxf); EXPECT_GE(result.entities_parsed, 3); EXPECT_GE(result.contours.size(), 3u); } TEST(DxfImportTest, ExtrudeContour_ProducesValidBody) { DxfContour c; c.points = {Point3D(0, 0, 0), Point3D(10, 0, 0), Point3D(10, 5, 0), Point3D(0, 5, 0)}; c.closed = true; c.layer = "profile"; auto body = extrude_dxf_contour(c, 2.0); EXPECT_GT(body.num_faces(), 0u); EXPECT_TRUE(body.is_valid()); } TEST(DxfImportTest, ExtrudeEmptyContour_ReturnsEmpty) { DxfContour c; auto body = extrude_dxf_contour(c, 1.0); EXPECT_EQ(body.num_faces(), 0u); }