feat(v2.0.0): Sprint 10 — B-Rep完善 + STEP导入/导出 + 布尔运算 + 验证
CI / Build & Test (push) Failing after 40s
CI / Release Build (push) Failing after 1m31s

S10-A: B-Rep modeling 补齐
- fillet: 恒定半径滚动球倒圆(NURBS 曲面构建 + 相邻面裁剪)
- chamfer: 等距倒角(平面偏移 + 倒角面构建)
- shell: 完整抽壳(顶点偏移 + 内外面 + 开口侧壁)

S10-B: STEP AP203/AP214 导入
- ISO 10303-21 解析器(1,424 行)
- 支持 14 种几何实体 → NurbsCurve/NurbsSurface 转换
- 完整拓扑链:VERTEX_POINT → EDGE_CURVE → EDGE_LOOP → ADVANCED_FACE → CLOSED_SHELL → MANIFOLD_SOLID_BREP
- 装配支持:NEXT_ASSEMBLY_USAGE_OCCURRENCE + CONTEXT_DEPENDENT_SHAPE_REPRESENTATION

S10-C.1: STEP AP214 导出
- export_step / export_step_file
- 曲线/曲面类型自动检测简化输出

S10-C.2: B-Rep 级布尔运算
- brep_union / brep_intersection / brep_difference
- 面-面求交 + 内/外分类 + 面缝合

S10-C.3: B-Rep 工程验证
- ValidationResult: 水密性/悬边/自相交/方向一致性/欧拉示性数

测试: 5 个新测试文件,47 项测试(modeling/STEP导入/导出/布尔/验证)

修改: 7 files (+576/-47) | 新增: 13 files (4,141 行)
总计: +4,670 行
This commit is contained in:
茂之钳
2026-07-24 05:18:52 +00:00
parent c06d8f4d0a
commit 8a19e265cd
20 changed files with 4721 additions and 51 deletions
+166
View File
@@ -0,0 +1,166 @@
#include <gtest/gtest.h>
#include "vde/brep/brep.h"
#include "vde/brep/modeling.h"
#include "vde/brep/step_export.h"
#include <fstream>
#include <sstream>
#include <algorithm>
using namespace vde::brep;
using namespace vde::core;
// ═══════════════════════════════════════════════════════════
// STEP export basic tests
// ═══════════════════════════════════════════════════════════
TEST(StepExportTest, ExportBox_ProducesValidStructure) {
auto box = make_box(2, 3, 4);
std::string step = export_step({box});
// Must start with ISO-10303-21 header
EXPECT_TRUE(step.find("ISO-10303-21;") != std::string::npos);
// Must contain HEADER section
EXPECT_TRUE(step.find("HEADER;") != std::string::npos);
EXPECT_TRUE(step.find("ENDSEC;") != std::string::npos);
// Must contain DATA section
EXPECT_TRUE(step.find("DATA;") != std::string::npos);
// Must contain CARTESIAN_POINT entities
EXPECT_TRUE(step.find("CARTESIAN_POINT") != std::string::npos);
// Must contain topology entities
EXPECT_TRUE(step.find("CLOSED_SHELL") != std::string::npos ||
step.find("ADVANCED_FACE") != std::string::npos);
// Must end properly
EXPECT_TRUE(step.find("END-ISO-10303-21;") != std::string::npos);
}
TEST(StepExportTest, ExportBox_ContainsManifoldSolidBrep) {
auto box = make_box(1, 1, 1);
std::string step = export_step({box});
EXPECT_TRUE(step.find("MANIFOLD_SOLID_BREP") != std::string::npos);
}
TEST(StepExportTest, ExportBox_ContainsPlanes) {
auto box = make_box(2, 2, 2);
std::string step = export_step({box});
// Box faces are degree 1×1 → should export as PLANE
EXPECT_TRUE(step.find("PLANE") != std::string::npos ||
step.find("B_SPLINE_SURFACE") != std::string::npos);
}
TEST(StepExportTest, ExportBox_ContainsEdgeCurve) {
auto box = make_box(1, 1, 1);
std::string step = export_step({box});
EXPECT_TRUE(step.find("EDGE_CURVE") != std::string::npos);
}
TEST(StepExportTest, ExportBox_ContainsOrientedEdge) {
auto box = make_box(1, 1, 1);
std::string step = export_step({box});
EXPECT_TRUE(step.find("ORIENTED_EDGE") != std::string::npos);
}
TEST(StepExportTest, ExportBox_HasProperEntityNumbering) {
auto box = make_box(1, 1, 1);
std::string step = export_step({box});
// Should have at least entity #1
EXPECT_TRUE(step.find("#1 =") != std::string::npos);
// Count entity occurrences to ensure numbering is sequential
int entity_count = 0;
size_t pos = 0;
while ((pos = step.find("= ", pos)) != std::string::npos) {
entity_count++;
pos += 2;
}
EXPECT_GT(entity_count, 5);
}
// ═══════════════════════════════════════════════════════════
// Sphere STEP export
// ═══════════════════════════════════════════════════════════
TEST(StepExportTest, ExportSphere_ProducesValidOutput) {
auto sphere = make_sphere(2.0);
std::string step = export_step({sphere});
EXPECT_TRUE(step.find("ISO-10303-21;") != std::string::npos);
EXPECT_TRUE(step.find("CARTESIAN_POINT") != std::string::npos);
EXPECT_TRUE(step.find("MANIFOLD_SOLID_BREP") != std::string::npos ||
step.find("CLOSED_SHELL") != std::string::npos);
}
// ═══════════════════════════════════════════════════════════
// Cylinder STEP export
// ═══════════════════════════════════════════════════════════
TEST(StepExportTest, ExportCylinder_ProducesValidOutput) {
auto cyl = make_cylinder(1.0, 4.0);
std::string step = export_step({cyl});
EXPECT_TRUE(step.find("ISO-10303-21;") != std::string::npos);
EXPECT_TRUE(step.find("CARTESIAN_POINT") != std::string::npos);
}
// ═══════════════════════════════════════════════════════════
// Multiple bodies
// ═══════════════════════════════════════════════════════════
TEST(StepExportTest, ExportMultipleBodies_ProducesValidOutput) {
auto box1 = make_box(1, 1, 1);
auto box2 = make_box(2, 2, 2);
std::string step = export_step({box1, box2});
EXPECT_TRUE(step.find("ISO-10303-21;") != std::string::npos);
EXPECT_TRUE(step.find("MANIFOLD_SOLID_BREP") != std::string::npos);
}
// ═══════════════════════════════════════════════════════════
// File export
// ═══════════════════════════════════════════════════════════
TEST(StepExportTest, ExportFile_WritesToDisk) {
auto box = make_box(1, 1, 1);
std::string path = "/tmp/test_vde_export.stp";
export_step_file(path, {box});
std::ifstream in(path);
EXPECT_TRUE(in.good());
std::string content((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());
EXPECT_TRUE(content.find("ISO-10303-21;") != std::string::npos);
}
// ═══════════════════════════════════════════════════════════
// Empty model
// ═══════════════════════════════════════════════════════════
TEST(StepExportTest, ExportEmptyModel_ProducesMinimalOutput) {
BrepModel empty;
std::string step = export_step({empty});
EXPECT_TRUE(step.find("ISO-10303-21;") != std::string::npos);
EXPECT_TRUE(step.find("DATA;") != std::string::npos);
}
// ═══════════════════════════════════════════════════════════
// AP214 schema check
// ═══════════════════════════════════════════════════════════
TEST(StepExportTest, UsesAP214Schema) {
auto box = make_box(1, 1, 1);
std::string step = export_step({box});
EXPECT_TRUE(step.find("AUTOMOTIVE_DESIGN") != std::string::npos ||
step.find("AP214") != std::string::npos ||
step.find("10303 214") != std::string::npos);
}