diff --git a/docs/benchmark-report.md b/docs/benchmark-report.md index 7be7cc7..0047030 100644 --- a/docs/benchmark-report.md +++ b/docs/benchmark-report.md @@ -1,64 +1,50 @@ -# Performance Benchmark Report — M3 里程碑 +# ViewDesignEngine 性能基准报告 -**Date**: 2026-07-24 -**Hardware**: 4× 2500 MHz CPU, 1 GB RAM (Docker container) -**Compiler**: GCC 11.4, C++17, -O2 +> 生成: 2026-07-24 | 环境: ECS 4核 2.5GHz, Ubuntu 22.04, GCC 11.4 ---- +## BVH (包围盒层次) -## Results +| 指标 | 规模 | 耗时 | 吞吐 | +|------|------|------|------| +| BVH 构建 | 500 tris | 73.9 µs | 6.8M tris/s | +| BVH 构建 | 1000 tris | 253 µs | 4.0M tris/s | +| BVH 构建 | 2000 tris | 656 µs | 3.1M tris/s | +| 射线查询 | 500 tris | 1.38 ms | 726k queries/s | +| 射线查询 | 1000 tris | 2.06 ms | 488k queries/s | +| 射线查询 | 2000 tris | 3.21 ms | 313k queries/s | -### BVH (Bounding Volume Hierarchy) +## Delaunay 三角剖分 -| Benchmark | Size | Time | Throughput | -|-----------|------|------|------------| -| Build | 500 tris | 72 µs | 6.9M tris/s | -| Build | 1000 tris | 245 µs | 4.1M tris/s | -| Build | 2000 tris | 650 µs | 3.1M tris/s | -| Ray Query | 500 tris × 1000 rays | 1.39 ms | 721K rays/s | -| Ray Query | 2000 tris × 1000 rays | 3.19 ms | 315K rays/s | +| 指标 | 规模 | 耗时 | 吞吐 | +|------|------|------|------| +| Delaunay 2D | 100 pts | 64.8 µs | 1.55M pts/s | +| Delaunay 2D | 1000 pts | 3.97 ms | 253k pts/s | +| Delaunay 2D | 10000 pts | 659 ms | 15.2k pts/s | -### 2D Delaunay Triangulation +## 曲线求值 -| Benchmark | Size | Time | Throughput | -|-----------|------|------|------------| -| Bowyer-Watson | 100 pts | 67 µs | 1.5M pts/s | -| Bowyer-Watson | 1000 pts | 3.96 ms | 254K pts/s | -| Bowyer-Watson | 10000 pts | 671 ms | 15K pts/s | +| 指标 | 规模 | 耗时 | 吞吐 | +|------|------|------|------| +| Bézier 求值 | 10K evals | 277 µs | 36M evals/s | +| Bézier 求值 | 100K evals | 2.64 ms | 38M evals/s | +| B-Spline 求值 | 10K evals | 720 µs | 14M evals/s | +| B-Spline 求值 | 100K evals | 7.14 ms | 14M evals/s | -### Curve Evaluation +## 空间索引 -| Benchmark | Size | Time | Throughput | -|-----------|------|------|------------| -| Bezier (cubic) | 10000 | 263 µs | 38.5M evals/s | -| Bezier (cubic) | 100000 | 2.66 ms | 38.1M evals/s | -| BSpline (cubic) | 10000 | 744 µs | 13.6M evals/s | -| BSpline (cubic) | 100000 | 7.33 ms | 13.8M evals/s | +| 指标 | 规模 | 耗时 | 吞吐 | +|------|------|------|------| +| R-Tree 构建 | 500 items | 19.2 µs | 26.1M items/s | +| R-Tree 构建 | 1000 items | 35.7 µs | 28.2M items/s | +| R-Tree 构建 | 2000 items | 151 µs | 13.3M items/s | +| R-Tree kNN | 500 items | 23.9 µs | 842k queries/s | +| R-Tree kNN | 1000 items | 27.5 µs | 731k queries/s | -### Spatial Index (KD-Tree) +## 网格布尔运算 -| Benchmark | Size | Time | Throughput | -|-----------|------|------|------------| -| Build | 500 pts | 71 µs | 7.0M pts/s | -| Build | 2000 pts | 537 µs | 3.8M pts/s | -| kNN (k=10) | 500 pts | 64 µs | 316K q/s | -| kNN (k=10) | 2000 pts | 92 µs | 218K q/s | - -### 3D Mesh Boolean - -| Benchmark | Size | Time | Throughput | -|-----------|------|------|------------| -| Union | ~100 faces | 2.05 ms | 495 ops/s | -| Union | ~500 faces | 9.10 ms | 112 ops/s | -| Intersection | ~100 faces | 1.94 ms | 520 ops/s | -| Intersection | ~500 faces | 8.53 ms | 118 ops/s | - ---- - -## Summary - -All 5 benchmark modules operational (143/143 tests pass). Performance scales reasonably for a C++17 geometry engine without SIMD optimization. Key areas for future improvement: - -- BVH ray query: implement packet tracing / wider BVH -- Delaunay 2D: O(n²) in current Bowyer-Watson -- Boolean: clip-by-point-test is brute-force; needs exact mesh clipping +| 指标 | 规模 | 耗时 | 吞吐 | +|------|------|------|------| +| Union | ~100 faces | 2.00 ms | 501 ops/s | +| Union | ~500 faces | 9.69 ms | 104 ops/s | +| Intersection | ~100 faces | 1.85 ms | 544 ops/s | +| Intersection | ~500 faces | 8.32 ms | 121 ops/s | diff --git a/include/vde/brep/assembly.h b/include/vde/brep/assembly.h new file mode 100644 index 0000000..af711d0 --- /dev/null +++ b/include/vde/brep/assembly.h @@ -0,0 +1,134 @@ +#pragma once +#include "vde/brep/brep.h" +#include "vde/core/transform.h" +#include +#include +#include + +namespace vde::brep { + +using core::Point3D; +using core::Vector3D; +using core::Transform3D; + +/** + * @brief 装配体节点 + * + * 表示装配体树中的一个节点,可以是零件(含 B-Rep 模型)或子装配体。 + * 每个节点有局部变换矩阵和名称。 + * + * @ingroup brep + */ +struct AssemblyNode { + /// 节点名称 + std::string name; + + /// 局部(相对父节点)变换矩阵 + Transform3D local_transform = Transform3D::Identity(); + + /// 此节点的 B-Rep 模型(零件),可选 + std::optional model; + + /// 子节点列表 + std::vector> children; + + /// 是否是零件节点(有 model)还是子装配体(有 children) + [[nodiscard]] bool is_part() const { return model.has_value() && children.empty(); } + + /// 添加子节点 + AssemblyNode* add_child(std::unique_ptr child) { + auto* ptr = child.get(); + children.push_back(std::move(child)); + return ptr; + } + + /// 添加 B-Rep 零件 + AssemblyNode* add_part(const std::string& part_name, BrepModel part_model, + const Transform3D& transform = Transform3D::Identity()) { + auto node = std::make_unique(); + node->name = part_name; + node->model = std::move(part_model); + node->local_transform = transform; + return add_child(std::move(node)); + } + + /// 添加子装配体 + AssemblyNode* add_subassembly(const std::string& sub_name, + const Transform3D& transform = Transform3D::Identity()) { + auto node = std::make_unique(); + node->name = sub_name; + node->local_transform = transform; + return add_child(std::move(node)); + } +}; + +/** + * @brief 装配体 + * + * 装配体树的根节点。管理零件和子装配体的层级结构。 + * + * @code{.cpp} + * Assembly assembly("robot_arm"); + * auto* base = assembly.root().add_part("base", make_box(10,10,2)); + * auto* arm = assembly.root().add_subassembly("arm", + * Transform3D::Translation(0,0,2)); + * arm->add_part("segment1", make_cylinder(0.5, 8)); + * @endcode + * + * @ingroup brep + */ +struct Assembly { + /// 装配体名称 + std::string name; + + /// 版本号 + std::string version = "1.0"; + + /// 根节点 + AssemblyNode root; + + explicit Assembly(const std::string& n) : name(n) { root.name = n; } + + /// 遍历所有零件节点 + template + void visit_parts(Visitor&& visitor) const { + visit_parts_impl(&root, Transform3D::Identity(), + std::forward(visitor)); + } + + /// 统计零件总数 + [[nodiscard]] size_t part_count() const { + size_t count = 0; + visit_parts([&count](const std::string&, const BrepModel&, const Transform3D&) { + ++count; + }); + return count; + } + + /// 统计节点总数(含子装配体) + [[nodiscard]] size_t node_count() const { + return node_count_impl(&root); + } + +private: + template + void visit_parts_impl(const AssemblyNode* node, const Transform3D& parent_xform, + Visitor&& visitor) const { + Transform3D world = parent_xform * node->local_transform; + if (node->model.has_value()) { + visitor(node->name, *node->model, world); + } + for (const auto& child : node->children) { + visit_parts_impl(child.get(), world, std::forward(visitor)); + } + } + + size_t node_count_impl(const AssemblyNode* node) const { + size_t n = 1; // count this node + for (const auto& child : node->children) + n += node_count_impl(child.get()); + return n; + } +}; + +} // namespace vde::brep diff --git a/tests/brep/CMakeLists.txt b/tests/brep/CMakeLists.txt index 0d9726d..3c2f112 100644 --- a/tests/brep/CMakeLists.txt +++ b/tests/brep/CMakeLists.txt @@ -6,3 +6,4 @@ add_vde_test(test_brep_boolean) add_vde_test(test_brep_validate) add_vde_test(test_iges_import) add_vde_test(test_iges_export) +add_vde_test(test_assembly) diff --git a/tests/brep/test_assembly.cpp b/tests/brep/test_assembly.cpp new file mode 100644 index 0000000..baa2330 --- /dev/null +++ b/tests/brep/test_assembly.cpp @@ -0,0 +1,45 @@ +#include +#include "vde/brep/assembly.h" +#include "vde/brep/modeling.h" + +using namespace vde::brep; +using namespace vde::core; + +TEST(AssemblyTest, CreateEmpty) { + Assembly assy("test"); + EXPECT_EQ(assy.name, "test"); + EXPECT_EQ(assy.part_count(), 0u); +} + +TEST(AssemblyTest, AddPart) { + Assembly assy("test"); + auto box = make_box(2, 2, 2); + assy.root.add_part("box1", box); + EXPECT_EQ(assy.part_count(), 1u); + EXPECT_EQ(assy.node_count(), 2u); // root + box +} + +TEST(AssemblyTest, NestedHierarchy) { + Assembly assy("robot"); + auto* base = assy.root.add_part("base", make_box(10, 5, 2)); + auto* arm = assy.root.add_subassembly("arm"); + arm->add_part("seg1", make_cylinder(1, 8)); + arm->add_part("seg2", make_cylinder(1, 6)); + + EXPECT_EQ(assy.part_count(), 3u); + EXPECT_EQ(assy.node_count(), 4u); // root + arm + seg1 + seg2 +} + +TEST(AssemblyTest, VisitPartsWithTransform) { + Assembly assy("test"); + assy.root.add_part("p1", make_box(1, 1, 1)); + + size_t visited = 0; + assy.visit_parts([&visited](const std::string& name, const BrepModel&, + const Transform3D& xform) { + EXPECT_EQ(name, "p1"); + EXPECT_TRUE(xform.isApprox(Transform3D::Identity())); + visited++; + }); + EXPECT_EQ(visited, 1u); +}