Files
ViewDesignEngine/tests/brep/test_brep_drawing.cpp
T

594 lines
13 KiB
C++
Raw Normal View History

#include <gtest/gtest.h>
#include "vde/brep/brep_drawing.h"
#include "vde/brep/dxf_import.h"
#include "vde/brep/modeling.h"
#include <cmath>
#include <fstream>
#include <cstdio>
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<char>(in)),
std::istreambuf_iterator<char>());
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<char>(in)),
std::istreambuf_iterator<char>());
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<ProjectionView> 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<char>(in)),
std::istreambuf_iterator<char>());
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);
}