From 5cfdcb2aad9b8390d99e10ae2823a680797482d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8C=82=E4=B9=8B=E9=92=B3?= Date: Mon, 27 Jul 2026 00:05:24 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20strip=20non-kernel=20code=20?= =?UTF-8?q?=E2=80=94=20keep=20pure=20geometry=20engine=20only?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed (application layer, not kernel): - blender_addon/ — Blender plugin - dotnet/ — .NET/C# bindings - viewer/ — Web 3D viewer - include/vde/ai/ — AI/ML (GNN, generative design) - include/vde/cloud/ — Cloud-native (OT, sessions) - include/vde/distributed/ — Cluster computing - include/vde/kbe/ — Knowledge-based engineering - include/vde/wasm/ — WASM compilation - include/vde/digital_twin/ — Digital twin - include/vde/plugin/ — Plugin system - include/vde/gpu/ — GPU acceleration - src/ai/, src/cloud/, src/distributed/, src/kbe/, src/wasm/, src/digital_twin/, src/plugin/, src/gpu/ - tests/ai/, tests/cloud/, tests/distributed/, tests/kbe/, tests/gpu/ Kept (pure geometry kernel): - brep/ curves/ mesh/ core/ foundation/ spatial/ boolean/ collision/ sdf/ sketch/ capi/ - python/ bindings (kept as thin binding layer) Rationale: Parasolid/ACIS/CGM are pure kernels. AI, cloud, WASM, digital twin, Blender plugins, .NET bindings, and Web viewers belong in the application layer, not in the geometry engine. VDE should focus on being the best geometry kernel. --- README.md | 395 ++++---- blender_addon/__init__.py | 54 - blender_addon/operators.py | 570 ----------- blender_addon/panels.py | 420 -------- blender_addon/preferences.py | 187 ---- blender_addon/vde_bridge.py | 501 --------- docs/dev-guide/plugin-system.md | 501 --------- dotnet/VdeSharp.csproj | 16 - dotnet/VdeSharp/Assembly.cs | 46 - dotnet/VdeSharp/BrepModel.cs | 191 ---- dotnet/VdeSharp/MeshData.cs | 104 -- dotnet/VdeSharp/NativeMethods.cs | 190 ---- dotnet/VdeSharp/SdfEngine.cs | 82 -- dotnet/VdeSharp/VdeSharp.csproj | 11 - dotnet/examples/Program.cs | 63 -- include/vde/ai/feature_learning.h | 366 ------- include/vde/ai/generative_design.h | 297 ------ include/vde/ai/inference_engine.h | 467 --------- include/vde/cloud/cloud_native.h | 348 ------- include/vde/digital_twin/dt_engine.h | 376 ------- include/vde/distributed/cluster_engine.h | 543 ---------- include/vde/distributed/grpc_service.h | 430 -------- include/vde/gpu/gpu_acceleration.h | 133 --- include/vde/kbe/knowledge_engine.h | 344 ------- include/vde/plugin/plugin_system.h | 396 -------- include/vde/wasm/vde_wasm.h | 327 ------ src/CMakeLists.txt | 879 ++++++++-------- src/ai/feature_learning.cpp | 721 ------------- src/ai/generative_design.cpp | 772 -------------- src/ai/inference_engine.cpp | 588 ----------- src/cloud/cloud_native.cpp | 438 -------- src/digital_twin/dt_engine.cpp | 469 --------- src/distributed/cluster_engine.cpp | 955 ------------------ src/distributed/grpc_service.cpp | 487 --------- src/gpu/gpu_acceleration.cpp | 670 ------------ src/gpu/gpu_acceleration.cu | 393 -------- src/kbe/knowledge_engine.cpp | 818 --------------- src/plugin/plugin_system.cpp | 654 ------------ src/wasm/vde_wasm.cpp | 341 ------- tests/ai/CMakeLists.txt | 2 - tests/ai/test_feature_learning.cpp | 234 ----- tests/ai/test_generative_design.cpp | 240 ----- tests/cloud/CMakeLists.txt | 4 - tests/cloud/test_cloud.cpp | 177 ---- tests/distributed/CMakeLists.txt | 1 - tests/distributed/test_cluster.cpp | 418 -------- tests/gpu/CMakeLists.txt | 1 - tests/gpu/test_gpu_acceleration.cpp | 254 ----- tests/kbe/CMakeLists.txt | 4 - tests/kbe/test_knowledge.cpp | 312 ------ viewer/app.js | 1175 ---------------------- viewer/index.html | 430 -------- 52 files changed, 634 insertions(+), 18161 deletions(-) delete mode 100644 blender_addon/__init__.py delete mode 100644 blender_addon/operators.py delete mode 100644 blender_addon/panels.py delete mode 100644 blender_addon/preferences.py delete mode 100644 blender_addon/vde_bridge.py delete mode 100644 docs/dev-guide/plugin-system.md delete mode 100644 dotnet/VdeSharp.csproj delete mode 100644 dotnet/VdeSharp/Assembly.cs delete mode 100644 dotnet/VdeSharp/BrepModel.cs delete mode 100644 dotnet/VdeSharp/MeshData.cs delete mode 100644 dotnet/VdeSharp/NativeMethods.cs delete mode 100644 dotnet/VdeSharp/SdfEngine.cs delete mode 100644 dotnet/VdeSharp/VdeSharp.csproj delete mode 100644 dotnet/examples/Program.cs delete mode 100644 include/vde/ai/feature_learning.h delete mode 100644 include/vde/ai/generative_design.h delete mode 100644 include/vde/ai/inference_engine.h delete mode 100644 include/vde/cloud/cloud_native.h delete mode 100644 include/vde/digital_twin/dt_engine.h delete mode 100644 include/vde/distributed/cluster_engine.h delete mode 100644 include/vde/distributed/grpc_service.h delete mode 100644 include/vde/gpu/gpu_acceleration.h delete mode 100644 include/vde/kbe/knowledge_engine.h delete mode 100644 include/vde/plugin/plugin_system.h delete mode 100644 include/vde/wasm/vde_wasm.h delete mode 100644 src/ai/feature_learning.cpp delete mode 100644 src/ai/generative_design.cpp delete mode 100644 src/ai/inference_engine.cpp delete mode 100644 src/cloud/cloud_native.cpp delete mode 100644 src/digital_twin/dt_engine.cpp delete mode 100644 src/distributed/cluster_engine.cpp delete mode 100644 src/distributed/grpc_service.cpp delete mode 100644 src/gpu/gpu_acceleration.cpp delete mode 100644 src/gpu/gpu_acceleration.cu delete mode 100644 src/kbe/knowledge_engine.cpp delete mode 100644 src/plugin/plugin_system.cpp delete mode 100644 src/wasm/vde_wasm.cpp delete mode 100644 tests/ai/CMakeLists.txt delete mode 100644 tests/ai/test_feature_learning.cpp delete mode 100644 tests/ai/test_generative_design.cpp delete mode 100644 tests/cloud/CMakeLists.txt delete mode 100644 tests/cloud/test_cloud.cpp delete mode 100644 tests/distributed/CMakeLists.txt delete mode 100644 tests/distributed/test_cluster.cpp delete mode 100644 tests/gpu/CMakeLists.txt delete mode 100644 tests/gpu/test_gpu_acceleration.cpp delete mode 100644 tests/kbe/CMakeLists.txt delete mode 100644 tests/kbe/test_knowledge.cpp delete mode 100644 viewer/app.js delete mode 100644 viewer/index.html diff --git a/README.md b/README.md index 0009fce..63b8c4e 100644 --- a/README.md +++ b/README.md @@ -1,199 +1,196 @@ -# ViewDesignEngine — 高性能 CAD 计算几何引擎 - -C++17 全栈计算几何引擎。从底层数学工具到 B-Rep 拓扑建模、SDF 隐式建模、可微分几何、工业格式互操作,一条龙覆盖。 - -[![CI](https://img.shields.io/badge/CI-passing-brightgreen)](https://git.hdtime.space/ViewDesignEngine/ViewDesignEngine/actions) -[![Tests](https://img.shields.io/badge/tests-476%2B%2F476-brightgreen)](tests/) -[![Version](https://img.shields.io/badge/version-3.2.0-blue)](CHANGELOG.md) -[![C++](https://img.shields.io/badge/C%2B%2B-17-blue)](CMakeLists.txt) -[![Python](https://img.shields.io/badge/python-3.8%2B-blue)](pyproject.toml) - -## 快速开始 - -### C++ 构建 - -```bash -# Docker 构建环境(推荐) -docker exec vde-builder bash -c "cd /ws/ViewDesignEngine && cmake -B build && cmake --build build -j\$(nproc)" - -# 本地构建 -cmake -B build -DCMAKE_BUILD_TYPE=Release -cmake --build build -j$(nproc) - -# 运行测试 -cd build && ctest --output-on-failure - -# 运行示例 -./build/examples/07_pipeline/pipeline_demo # SDF → GLB 全流程 -``` - -**依赖**: CMake ≥ 3.16, C++17, Eigen 3, GoogleTest(自动下载) - -### Python 安装 - -```bash -# 从源码安装(推荐) -pip install . - -# 开发模式(可编辑安装) -pip install -e . - -# 仅 CMake 构建(不安装到 site-packages) -cmake -B build_py -DVDE_BUILD_PYTHON=ON -DCMAKE_BUILD_TYPE=Release -cmake --build build_py -j$(nproc) - -# 测试导入 -PYTHONPATH=build_py/python python3 -c "import vde; print(vde.__version__)" -``` - -**Python 依赖**: CMake ≥ 3.16, C++17, Eigen 3(自动下载), pybind11 ≥ 2.10 - -## 模块总览 - -| 模块 | 命名空间 | 关键能力 | -|------|---------|----------| -| `foundation` | `vde::foundation` | 数学类型、公差、精确谓词、STL/OBJ/PLY/GLTF(F/GLB) 读写 | -| `core` | `vde::core` | 多边形、Voronoi、凸包、ICP、距离计算 | -| `curves` | `vde::curves` | Bézier / B-Spline / NURBS 曲线曲面、自适应细分 | -| `mesh` | `vde::mesh` | 半边网格、Delaunay 2D/3D、QEM 简化、平滑、Marching Cubes、测地线、网格布尔 | -| `spatial` | `vde::spatial` | BVH (SAH)、Octree、KD-Tree、R-Tree (STR) | -| `boolean` | `vde::boolean` | 2D Sutherland-Hodgman、3D 网格布尔、多边形偏移 | -| `collision` | `vde::collision` | GJK/EPA、SAT、射线-三角形、三角交线 | -| `brep` | `vde::brep` | 边界表示 (B-Rep) 拓扑建模、布尔运算、验证 | -| `sdf` | `vde::sdf` | 15+ 隐式图元、CSG 树、自动微分、梯度优化、SDF → Mesh | -| `sketch` | `vde::sketch` | 2D 草图约束求解器 | -| `capi` | `vde::capi` | C API,跨语言互操作 | -| `plugin` | `vde::plugin` | 插件系统:动态加载、第三方扩展(v6.0+) | - -## 格式支持 - -| 格式 | 导入 | 导出 | 说明 | -|------|------|------|------| -| STEP (AP203/214) | ✅ | ✅ | 工业 CAD 交换标准 | -| IGES (5.3) | ✅ | ✅ | 传统制造业格式 | -| glTF 2.0 / GLB | — | ✅ | 实时 3D / Web 查看 | -| STL | — | ✅ | 3D 打印 | -| OBJ | — | ✅ | 通用网格 | -| PLY | — | ✅ | 点云/网格 | - -## 特色功能 - -### 🔮 SDF 隐式建模 -```cpp -#include -#include -#include - -// 光滑并集 -auto shape = SdfNode::smooth_union( - SdfNode::sphere(1.5), - SdfNode::box(Point3D(1, 1, 1)), - 0.3 -); -double d = evaluate(shape, Point3D(0, 0, 0)); // SDF 值 -``` - -### ⚡ 可微分几何 + torch 集成 -```cpp -#include -#include - -// 拟合 SDF 到点云 -auto sphere = SdfNode::sphere(1.0); -auto result = fit_to_point_cloud(sphere, point_cloud, 0.01, 100); -// result.optimized_shape → 优化后的形状 -``` - -### 🔧 B-Rep 工业建模 -```cpp -#include -#include - -auto box = make_box(2, 2, 2); -auto shelled = shell(box, -1, 0.15); // 抽壳 -auto filleted = fillet(box, 0, 0.3); // 倒圆 -auto result = brep_union(a, b); // 布尔并 -``` - -## 代码示例 - -完整示例见 [`examples/`](examples/): -- `01_hello_triangle` — 基础几何 -- `02_bezier` — 曲线求值 -- `03_mesh` — 网格操作 -- `04_delaunay` — 三角剖分 -- `05_boolean` — 布尔运算 -- `06_collision` — 碰撞检测 -- `07_pipeline` — **SDF → Mesh → GLB 完整管线** - -## 测试 - -| 类别 | 测试数 | 状态 | -|------|--------|------| -| 核心 + 曲线 + 网格 + 空间 + 碰撞 | ~120 | ✅ | -| B-Rep 建模 + 验证 | ~70 | ✅ | -| STEP 导入/导出 | 31 | ✅ | -| IGES 导入/导出 | 39 | ✅ | -| SDF 隐式建模 | 189 | ✅ | -| 可微分几何 | 76 | ✅ | -| GLTF/GLB 导出 | 6 | ✅ | -| 草图约束 | — | ✅ | -| **合计** | **~476** | **100%** | - -## 工程指标 - -- **语言**: C++17,零外部运行时依赖(仅 header-only Eigen + GTest) -- **测试**: 500+ 用例,100% 通过 -- **编译**: GCC 11+ / Clang 16+,零错误零警告 -- **格式支持**: STEP、IGES、glTF/GLB、STL、OBJ、PLY - -## 文档 - -- [开发者指南](docs/dev-guide/) — 架构/API/构建/测试/贡献/插件系统(v6.0+) -- [开发计划](docs/00-开发计划.md) — 版本规划 + Sprint 划分 -- [v3.1 计划](docs/10-v3.1-开发计划.md) — 下一阶段路线图 -- [CHANGELOG](CHANGELOG.md) — 版本历史 -- [构建指南](docs/06-部署维护/01-构建指南.md) - -## v6.0 新功能(计划中) 🔌 - -### 插件系统 (`vde::plugin`) - -```cpp -#include - -// 插件管理器:动态加载第三方扩展 -vde::plugin::PluginManager mgr; -mgr.load_plugin_dir("/usr/lib/vde/plugins"); - -// 查找并使用插件 -auto* exporter = mgr.get_plugin("io.export.gltf"); -exporter->execute("export", {{"path", "output.glb"}}, &mesh, nullptr); -``` - -**核心能力**: -- **动态加载**: 运行时 dlopen/LoadLibrary,无需重新编译 VDE -- **版本兼容**: PluginManifest 声明约束,自动检查兼容性 -- **插件隔离**: 崩溃不影响主程序,独立 .so/.dylib/.dll -- **注册宏**: `VDE_REGISTER_PLUGIN(MyPlugin)` 一行注册 -- **清单文件**: `plugin.json` 加速发现和验证 - -**插件类型**: -| 类型 | 说明 | 示例 | -|------|------|------| -| IO_IMPORT | 格式导入器 | USDZ, FBX, 3DS | -| IO_EXPORT | 格式导出器 | USDZ, X3D, AMF | -| GEOMETRY_FILTER | 几何过滤器 | 平滑、简化、重网格 | -| CUSTOM_TOOL | 自定义工具 | 应力分析、拓扑优化 | - -详见: [插件系统设计](docs/dev-guide/plugin-system.md) | [开发者指南](docs/dev-guide/) - -### 开发者生态 - -- 📘 **[开发者指南](docs/dev-guide/)** — 7 篇完整文档覆盖架构/API/构建/测试/贡献/插件 -- 🏗️ **插件 SDK** — 头文件 + CMake 模板 + 注册宏,5 分钟上手 -- 🔒 **安全分级** — L0 内置 → L1 签名 → L2 社区 → L3 沙箱 - -## 许可证 - -Apache License 2.0 — 详见 [LICENSE](LICENSE) +1|# ViewDesignEngine — 高性能 CAD 计算几何引擎 +2| +3|C++17 全栈计算几何引擎。从底层数学工具到 B-Rep 拓扑建模、SDF 隐式建模、可微分几何、工业格式互操作,一条龙覆盖。 +4| +5|[![CI](https://img.shields.io/badge/CI-passing-brightgreen)](https://git.hdtime.space/ViewDesignEngine/ViewDesignEngine/actions) +6|[![Tests](https://img.shields.io/badge/tests-476%2B%2F476-brightgreen)](tests/) +7|[![Version](https://img.shields.io/badge/version-3.2.0-blue)](CHANGELOG.md) +8|[![C++](https://img.shields.io/badge/C%2B%2B-17-blue)](CMakeLists.txt) +9|[![Python](https://img.shields.io/badge/python-3.8%2B-blue)](pyproject.toml) +10| +11|## 快速开始 +12| +13|### C++ 构建 +14| +15|```bash +16|# Docker 构建环境(推荐) +17|docker exec vde-builder bash -c "cd /ws/ViewDesignEngine && cmake -B build && cmake --build build -j\$(nproc)" +18| +19|# 本地构建 +20|cmake -B build -DCMAKE_BUILD_TYPE=Release +21|cmake --build build -j$(nproc) +22| +23|# 运行测试 +24|cd build && ctest --output-on-failure +25| +26|# 运行示例 +27|./build/examples/07_pipeline/pipeline_demo # SDF → GLB 全流程 +28|``` +29| +30|**依赖**: CMake ≥ 3.16, C++17, Eigen 3, GoogleTest(自动下载) +31| +32|### Python 安装 +33| +34|```bash +35|# 从源码安装(推荐) +36|pip install . +37| +38|# 开发模式(可编辑安装) +39|pip install -e . +40| +41|# 仅 CMake 构建(不安装到 site-packages) +42|cmake -B build_py -DVDE_BUILD_PYTHON=ON -DCMAKE_BUILD_TYPE=Release +43|cmake --build build_py -j$(nproc) +44| +45|# 测试导入 +46|PYTHONPATH=build_py/python python3 -c "import vde; print(vde.__version__)" +47|``` +48| +49|**Python 依赖**: CMake ≥ 3.16, C++17, Eigen 3(自动下载), pybind11 ≥ 2.10 +50| +51|## 模块总览 +52| +53|| 模块 | 命名空间 | 关键能力 | +54||------|---------|----------| +55|| `foundation` | `vde::foundation` | 数学类型、公差、精确谓词、STL/OBJ/PLY/GLTF(F/GLB) 读写 | +56|| `core` | `vde::core` | 多边形、Voronoi、凸包、ICP、距离计算 | +57|| `curves` | `vde::curves` | Bézier / B-Spline / NURBS 曲线曲面、自适应细分 | +58|| `mesh` | `vde::mesh` | 半边网格、Delaunay 2D/3D、QEM 简化、平滑、Marching Cubes、测地线、网格布尔 | +59|| `spatial` | `vde::spatial` | BVH (SAH)、Octree、KD-Tree、R-Tree (STR) | +60|| `boolean` | `vde::boolean` | 2D Sutherland-Hodgman、3D 网格布尔、多边形偏移 | +61|| `collision` | `vde::collision` | GJK/EPA、SAT、射线-三角形、三角交线 | +62|| `brep` | `vde::brep` | 边界表示 (B-Rep) 拓扑建模、布尔运算、验证 | +63|| `sdf` | `vde::sdf` | 15+ 隐式图元、CSG 树、自动微分、梯度优化、SDF → Mesh | +64|| `sketch` | `vde::sketch` | 2D 草图约束求解器 | +65|| `capi` | `vde::capi` | C API,跨语言互操作 | +67| +68|## 格式支持 +69| +70|| 格式 | 导入 | 导出 | 说明 | +71||------|------|------|------| +72|| STEP (AP203/214) | ✅ | ✅ | 工业 CAD 交换标准 | +73|| IGES (5.3) | ✅ | ✅ | 传统制造业格式 | +74|| glTF 2.0 / GLB | — | ✅ | 实时 3D / Web 查看 | +75|| STL | — | ✅ | 3D 打印 | +76|| OBJ | — | ✅ | 通用网格 | +77|| PLY | — | ✅ | 点云/网格 | +78| +79|## 特色功能 +80| +81|### 🔮 SDF 隐式建模 +82|```cpp +83|#include +84|#include +85|#include +86| +87|// 光滑并集 +88|auto shape = SdfNode::smooth_union( +89| SdfNode::sphere(1.5), +90| SdfNode::box(Point3D(1, 1, 1)), +91| 0.3 +92|); +93|double d = evaluate(shape, Point3D(0, 0, 0)); // SDF 值 +94|``` +95| +96|### ⚡ 可微分几何 + torch 集成 +97|```cpp +98|#include +99|#include +100| +101|// 拟合 SDF 到点云 +102|auto sphere = SdfNode::sphere(1.0); +103|auto result = fit_to_point_cloud(sphere, point_cloud, 0.01, 100); +104|// result.optimized_shape → 优化后的形状 +105|``` +106| +107|### 🔧 B-Rep 工业建模 +108|```cpp +109|#include +110|#include +111| +112|auto box = make_box(2, 2, 2); +113|auto shelled = shell(box, -1, 0.15); // 抽壳 +114|auto filleted = fillet(box, 0, 0.3); // 倒圆 +115|auto result = brep_union(a, b); // 布尔并 +116|``` +117| +118|## 代码示例 +119| +120|完整示例见 [`examples/`](examples/): +121|- `01_hello_triangle` — 基础几何 +122|- `02_bezier` — 曲线求值 +123|- `03_mesh` — 网格操作 +124|- `04_delaunay` — 三角剖分 +125|- `05_boolean` — 布尔运算 +126|- `06_collision` — 碰撞检测 +127|- `07_pipeline` — **SDF → Mesh → GLB 完整管线** +128| +129|## 测试 +130| +131|| 类别 | 测试数 | 状态 | +132||------|--------|------| +133|| 核心 + 曲线 + 网格 + 空间 + 碰撞 | ~120 | ✅ | +134|| B-Rep 建模 + 验证 | ~70 | ✅ | +135|| STEP 导入/导出 | 31 | ✅ | +136|| IGES 导入/导出 | 39 | ✅ | +137|| SDF 隐式建模 | 189 | ✅ | +138|| 可微分几何 | 76 | ✅ | +139|| GLTF/GLB 导出 | 6 | ✅ | +140|| 草图约束 | — | ✅ | +141|| **合计** | **~476** | **100%** | +142| +143|## 工程指标 +144| +145|- **语言**: C++17,零外部运行时依赖(仅 header-only Eigen + GTest) +146|- **测试**: 500+ 用例,100% 通过 +147|- **编译**: GCC 11+ / Clang 16+,零错误零警告 +148|- **格式支持**: STEP、IGES、glTF/GLB、STL、OBJ、PLY +149| +150|## 文档 +151| +153|- [开发计划](docs/00-开发计划.md) — 版本规划 + Sprint 划分 +154|- [v3.1 计划](docs/10-v3.1-开发计划.md) — 下一阶段路线图 +155|- [CHANGELOG](CHANGELOG.md) — 版本历史 +156|- [构建指南](docs/06-部署维护/01-构建指南.md) +157| +158|## v6.0 新功能(计划中) 🔌 +159| +161| +162|```cpp +163|#include +164| +165|// 插件管理器:动态加载第三方扩展 +166|vde::plugin::PluginManager mgr; +167|mgr.load_plugin_dir("/usr/lib/vde/plugins"); +168| +169|// 查找并使用插件 +170|auto* exporter = mgr.get_plugin("io.export.gltf"); +171|exporter->execute("export", {{"path", "output.glb"}}, &mesh, nullptr); +172|``` +173| +174|**核心能力**: +175|- **动态加载**: 运行时 dlopen/LoadLibrary,无需重新编译 VDE +176|- **版本兼容**: PluginManifest 声明约束,自动检查兼容性 +177|- **插件隔离**: 崩溃不影响主程序,独立 .so/.dylib/.dll +178|- **注册宏**: `VDE_REGISTER_PLUGIN(MyPlugin)` 一行注册 +179|- **清单文件**: `plugin.json` 加速发现和验证 +180| +181|**插件类型**: +182|| 类型 | 说明 | 示例 | +183||------|------|------| +184|| IO_IMPORT | 格式导入器 | USDZ, FBX, 3DS | +185|| IO_EXPORT | 格式导出器 | USDZ, X3D, AMF | +186|| GEOMETRY_FILTER | 几何过滤器 | 平滑、简化、重网格 | +187|| CUSTOM_TOOL | 自定义工具 | 应力分析、拓扑优化 | +188| +190| +191|### 开发者生态 +192| +193|- 📘 **[开发者指南](docs/dev-guide/)** — 7 篇完整文档覆盖架构/API/构建/测试/贡献/插件 +194|- 🏗️ **插件 SDK** — 头文件 + CMake 模板 + 注册宏,5 分钟上手 +195|- 🔒 **安全分级** — L0 内置 → L1 签名 → L2 社区 → L3 沙箱 +196| +197|## 许可证 +198| +199|Apache License 2.0 — 详见 [LICENSE](LICENSE) +200| \ No newline at end of file diff --git a/blender_addon/__init__.py b/blender_addon/__init__.py deleted file mode 100644 index 3a5ffd6..0000000 --- a/blender_addon/__init__.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -ViewDesignEngine (VDE) Blender Integration Addon -================================================ -将 VDE 高性能计算几何引擎集成到 Blender 中,提供: - - STEP/IGES 文件导入/导出 - - B-Rep 图元创建 (Box, Cylinder, Sphere) - - 布尔运算 (并集, 交集, 差集) - - SDF 隐式建模与网格转换 - -Author: VDE Team -Version: 1.0.0 -Blender: (3, 0, 0) 及以上 -""" - -bl_info = { - "name": "ViewDesignEngine (VDE)", - "description": "高性能 CAD 几何引擎集成 — B-Rep/SDF 建模、STEP 导入导出、布尔运算", - "author": "VDE Team", - "version": (1, 0, 0), - "blender": (3, 0, 0), - "location": "3D Viewport > Sidebar (N) > VDE", - "category": "Import-Export", - "support": "COMMUNITY", - "doc_url": "https://github.com/ViewDesignEngine/ViewDesignEngine", - "tracker_url": "https://github.com/ViewDesignEngine/ViewDesignEngine/issues", -} - -import bpy - -# 延迟导入 — Blender 注册时需要模块可用 -from . import operators -from . import panels -from . import preferences -from . import vde_bridge - - -def register(): - """注册所有操作符、面板和偏好设置。""" - preferences.register() - operators.register() - panels.register() - print("[VDE] ViewDesignEngine addon registered.") - - -def unregister(): - """注销所有操作符、面板和偏好设置。""" - panels.unregister() - operators.unregister() - preferences.unregister() - print("[VDE] ViewDesignEngine addon unregistered.") - - -if __name__ == "__main__": - register() diff --git a/blender_addon/operators.py b/blender_addon/operators.py deleted file mode 100644 index e39efdd..0000000 --- a/blender_addon/operators.py +++ /dev/null @@ -1,570 +0,0 @@ -""" -operators.py — VDE Blender 操作符 -================================= -所有 VDE 操作符定义,可在 Blender 中搜索和调用。 - -操作符列表: - - VDE_OT_import_step — 导入 STEP 文件 - - VDE_OT_export_step — 导出选中对象为 STEP - - VDE_OT_create_box — 创建 Box 图元 - - VDE_OT_create_cylinder — 创建 Cylinder 图元 - - VDE_OT_create_sphere — 创建 Sphere 图元 - - VDE_OT_boolean_union — 布尔并集 - - VDE_OT_boolean_intersect— 布尔交集 - - VDE_OT_boolean_diff — 布尔差集 - - VDE_OT_sdf_to_mesh — SDF→Mesh 转换 -""" - -import os - -import bpy -from bpy.props import ( - StringProperty, FloatProperty, IntProperty, - BoolProperty, EnumProperty, PointerProperty, -) -from bpy_extras.io_utils import ImportHelper, ExportHelper - -from . import vde_bridge - - -# ═══════════════════════════════════════════════════════════════ -# 辅助函数 -# ═══════════════════════════════════════════════════════════════ - -def get_prefs(): - """获取插件偏好设置。""" - return bpy.context.preferences.addons[__package__].preferences - - -def get_bridge(): - """获取 VDE 桥接实例。""" - prefs = get_prefs() - return vde_bridge.VDEBridge( - vde_lib_path=prefs.vde_lib_path, - python_executable=prefs.python_path or None, - ) - - -def report_and_raise(self, error_msg): - """报告错误并设置操作符状态。""" - self.report({'ERROR'}, error_msg) - return {'CANCELLED'} - - -# ═══════════════════════════════════════════════════════════════ -# 导入/导出操作符 -# ═══════════════════════════════════════════════════════════════ - -class VDE_OT_import_step(bpy.types.Operator, ImportHelper): - """从 STEP 文件导入几何体为 Blender Mesh。""" - bl_idname = "vde.import_step" - bl_label = "导入 STEP" - bl_description = "使用 VDE 引擎从 STEP/STP 文件导入 B-Rep 几何体" - bl_options = {'REGISTER', 'UNDO'} - - filename_ext = ".stp;.step" - filter_glob: StringProperty( - default="*.stp;*.step", - options={'HIDDEN'}, - ) - - heal_import: BoolProperty( - name="自动修复", - description="导入后自动修复拓扑缺陷(合并顶点、闭合缝隙)", - default=True, - ) - - deflection: FloatProperty( - name="网格精度", - description="B-Rep 离散化为网格的弦高偏差 (越小越精细)", - default=0.01, - min=0.001, - max=1.0, - precision=4, - subtype='FACTOR', - ) - - def execute(self, context): - bridge = get_bridge() - self.report({'INFO'}, f"正在导入: {self.filepath}") - - try: - result = bridge.execute("import_step", path=self.filepath) - except RuntimeError as e: - return report_and_raise(self, str(e)) - - name = os.path.basename(self.filepath).rsplit(".", 1)[0] - try: - obj = vde_bridge.bridge_result_to_blender(result, name=name) - except RuntimeError as e: - return report_and_raise(self, str(e)) - - self.report({'INFO'}, f"导入成功: {name} " - f"({result.get('num_vertices', 0)} 顶点, " - f"{result.get('num_faces', 0)} 面)") - return {'FINISHED'} - - -class VDE_OT_export_step(bpy.types.Operator, ExportHelper): - """将选中对象导出为 STEP 文件。""" - bl_idname = "vde.export_step" - bl_label = "导出 STEP" - bl_description = "使用 VDE 引擎将选中网格导出为 STEP AP242 格式" - bl_options = {'REGISTER', 'UNDO'} - - filename_ext = ".stp" - filter_glob: StringProperty( - default="*.stp;*.step", - options={'HIDDEN'}, - ) - - include_pmi: BoolProperty( - name="包含 PMI", - description="在 STEP 文件中包含产品和制造信息", - default=False, - ) - - def execute(self, context): - bridge = get_bridge() - - selected = [o for o in context.selected_objects if o.type == 'MESH'] - if not selected: - return report_and_raise(self, "请先选中至少一个 Mesh 对象。") - - meshes = [] - for obj in selected: - try: - mesh_data = vde_bridge.blender_mesh_to_bridge_data(obj) - meshes.append(mesh_data) - except RuntimeError as e: - return report_and_raise(self, f"对象 '{obj.name}': {e}") - - self.report({'INFO'}, f"正在导出 {len(meshes)} 个对象到: {self.filepath}") - - try: - result = bridge.execute( - "export_step", - path=self.filepath, - meshes=meshes, - ) - except RuntimeError as e: - return report_and_raise(self, str(e)) - - self.report({'INFO'}, f"导出成功: {self.filepath}") - return {'FINISHED'} - - -# ═══════════════════════════════════════════════════════════════ -# 图元创建操作符 -# ═══════════════════════════════════════════════════════════════ - -class VDE_OT_create_box(bpy.types.Operator): - """创建 Box B-Rep 图元。""" - bl_idname = "vde.create_box" - bl_label = "Box" - bl_description = "使用 VDE 创建 Box 图元" - bl_options = {'REGISTER', 'UNDO'} - - width: FloatProperty( - name="宽度 (X)", - default=2.0, - min=0.01, - max=100.0, - ) - height: FloatProperty( - name="高度 (Y)", - default=2.0, - min=0.01, - max=100.0, - ) - depth: FloatProperty( - name="深度 (Z)", - default=2.0, - min=0.01, - max=100.0, - ) - - def execute(self, context): - bridge = get_bridge() - try: - result = bridge.execute("create_primitive", type="box", params={ - "width": self.width, - "height": self.height, - "depth": self.depth, - }) - except RuntimeError as e: - return report_and_raise(self, str(e)) - - try: - vde_bridge.bridge_result_to_blender( - result, - name=f"VDE_Box_{self.width:.1f}x{self.height:.1f}x{self.depth:.1f}" - ) - except RuntimeError as e: - return report_and_raise(self, str(e)) - - self.report({'INFO'}, f"创建 Box ({self.width}×{self.height}×{self.depth})") - return {'FINISHED'} - - -class VDE_OT_create_cylinder(bpy.types.Operator): - """创建 Cylinder B-Rep 图元。""" - bl_idname = "vde.create_cylinder" - bl_label = "Cylinder" - bl_description = "使用 VDE 创建圆柱体图元" - bl_options = {'REGISTER', 'UNDO'} - - radius: FloatProperty( - name="半径", - default=1.0, - min=0.01, - max=50.0, - ) - height: FloatProperty( - name="高度", - default=2.0, - min=0.01, - max=100.0, - ) - - def execute(self, context): - bridge = get_bridge() - try: - result = bridge.execute("create_primitive", type="cylinder", params={ - "radius": self.radius, - "height": self.height, - }) - except RuntimeError as e: - return report_and_raise(self, str(e)) - - try: - vde_bridge.bridge_result_to_blender( - result, - name=f"VDE_Cylinder_r{self.radius:.1f}_h{self.height:.1f}" - ) - except RuntimeError as e: - return report_and_raise(self, str(e)) - - self.report({'INFO'}, f"创建 Cylinder (r={self.radius}, h={self.height})") - return {'FINISHED'} - - -class VDE_OT_create_sphere(bpy.types.Operator): - """创建 Sphere B-Rep 图元。""" - bl_idname = "vde.create_sphere" - bl_label = "Sphere" - bl_description = "使用 VDE 创建球体图元" - bl_options = {'REGISTER', 'UNDO'} - - radius: FloatProperty( - name="半径", - default=1.0, - min=0.01, - max=50.0, - ) - - def execute(self, context): - bridge = get_bridge() - try: - result = bridge.execute("create_primitive", type="sphere", params={ - "radius": self.radius, - }) - except RuntimeError as e: - return report_and_raise(self, str(e)) - - try: - vde_bridge.bridge_result_to_blender( - result, - name=f"VDE_Sphere_r{self.radius:.1f}" - ) - except RuntimeError as e: - return report_and_raise(self, str(e)) - - self.report({'INFO'}, f"创建 Sphere (r={self.radius})") - return {'FINISHED'} - - -# ═══════════════════════════════════════════════════════════════ -# 布尔运算操作符 -# ═══════════════════════════════════════════════════════════════ - -class VDE_OT_boolean_union(bpy.types.Operator): - """布尔并集 — 合并两个选中对象。""" - bl_idname = "vde.boolean_union" - bl_label = "布尔并集 (Union)" - bl_description = "使用 VDE 引擎计算两个网格的布尔并集" - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - return _execute_boolean(self, context, "union", "Union") - - -class VDE_OT_boolean_intersect(bpy.types.Operator): - """布尔交集 — 保留两对象重叠部分。""" - bl_idname = "vde.boolean_intersect" - bl_label = "布尔交集 (Intersect)" - bl_description = "使用 VDE 引擎计算两个网格的布尔交集" - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - return _execute_boolean(self, context, "intersect", "Intersect") - - -class VDE_OT_boolean_diff(bpy.types.Operator): - """布尔差集 — 从第一个对象减去第二个对象。""" - bl_idname = "vde.boolean_diff" - bl_label = "布尔差集 (Difference)" - bl_description = "使用 VDE 引擎计算两个网格的布尔差集 (A - B)" - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - return _execute_boolean(self, context, "diff", "Diff") - - -def _execute_boolean(operator, context, op_type, label): - """内部布尔运算执行函数。""" - selected = [o for o in context.selected_objects if o.type == 'MESH'] - if len(selected) < 2: - return report_and_raise(operator, "请选中恰好两个 Mesh 对象。") - - obj_a = context.active_object - obj_b = selected[0] if selected[0] != obj_a else selected[1] - - if obj_a is None or obj_b is None: - return report_and_raise(operator, "请设置活动对象并选中另一个对象。") - - operator.report( - {'INFO'}, - f"正在计算布尔{label}: {obj_a.name} vs {obj_b.name}" - ) - - bridge = get_bridge() - try: - mesh_a = vde_bridge.blender_mesh_to_bridge_data(obj_a) - mesh_b = vde_bridge.blender_mesh_to_bridge_data(obj_b) - except RuntimeError as e: - return report_and_raise(operator, str(e)) - - try: - result = bridge.execute( - "boolean", - boolean_type=op_type, - mesh_a=mesh_a, - mesh_b=mesh_b, - ) - except RuntimeError as e: - return report_and_raise(operator, str(e)) - - try: - vde_bridge.bridge_result_to_blender( - result, - name=f"VDE_Boolean_{label}_{obj_a.name}" - ) - except RuntimeError as e: - return report_and_raise(operator, str(e)) - - operator.report( - {'INFO'}, - f"布尔{label}完成: " - f"{result.get('num_vertices', 0)} 顶点, " - f"{result.get('num_faces', 0)} 面" - ) - return {'FINISHED'} - - -# ═══════════════════════════════════════════════════════════════ -# SDF → Mesh 操作符 -# ═══════════════════════════════════════════════════════════════ - -class VDE_OT_sdf_to_mesh(bpy.types.Operator): - """将 SDF 隐式曲面转换为三角网格。""" - bl_idname = "vde.sdf_to_mesh" - bl_label = "SDF → Mesh" - bl_description = "使用 VDE 引擎将 SDF 隐式几何体转为三角网格" - bl_options = {'REGISTER', 'UNDO'} - - sdf_type: EnumProperty( - name="SDF 类型", - description="要生成的隐式曲面类型", - items=[ - ('sphere', "球体", "球体 SDF"), - ('box', "Box", "Box SDF"), - ('cylinder', "圆柱体", "圆柱体 SDF"), - ('torus', "环面", "环面 SDF"), - ('custom', "自定义 CSG", "自定义 SDF CSG 组合树"), - ], - default='sphere', - ) - - resolution: IntProperty( - name="分辨率", - description="Marching Cubes 网格分辨率 (越高越精细)", - default=64, - min=16, - max=512, - ) - - # ══ 球体参数 ══ - sphere_radius: FloatProperty( - name="半径", - default=1.0, - min=0.01, - max=50.0, - ) - - # ══ Box 参数 ══ - box_hx: FloatProperty( - name="半长 (X)", - default=1.0, - min=0.01, - max=50.0, - ) - box_hy: FloatProperty( - name="半长 (Y)", - default=1.0, - min=0.01, - max=50.0, - ) - box_hz: FloatProperty( - name="半长 (Z)", - default=1.0, - min=0.01, - max=50.0, - ) - - # ══ 圆柱体参数 ══ - cyl_radius: FloatProperty( - name="半径", - default=1.0, - min=0.01, - max=50.0, - ) - cyl_height: FloatProperty( - name="高度", - default=2.0, - min=0.01, - max=100.0, - ) - - # ══ 环面参数 ══ - torus_major: FloatProperty( - name="主半径", - default=1.0, - min=0.1, - max=50.0, - ) - torus_minor: FloatProperty( - name="管半径", - default=0.3, - min=0.01, - max=25.0, - ) - - def draw(self, context): - layout = self.layout - layout.prop(self, "sdf_type") - layout.prop(self, "resolution") - - box = layout.box() - if self.sdf_type == 'sphere': - box.prop(self, "sphere_radius") - elif self.sdf_type == 'box': - box.prop(self, "box_hx") - box.prop(self, "box_hy") - box.prop(self, "box_hz") - elif self.sdf_type == 'cylinder': - box.prop(self, "cyl_radius") - box.prop(self, "cyl_height") - elif self.sdf_type == 'torus': - box.prop(self, "torus_major") - box.prop(self, "torus_minor") - elif self.sdf_type == 'custom': - box.label(text="自定义 CSG 通过面板配置") - - def execute(self, context): - params = {} - node_name = "SDF" - - if self.sdf_type == 'sphere': - params = {"radius": self.sphere_radius} - node_name = f"VDE_SDF_Sphere_r{self.sphere_radius:.1f}" - elif self.sdf_type == 'box': - params = {"half_extents": [self.box_hx, self.box_hy, self.box_hz]} - node_name = (f"VDE_SDF_Box_" - f"{self.box_hx:.1f}x{self.box_hy:.1f}x{self.box_hz:.1f}") - elif self.sdf_type == 'cylinder': - params = {"radius": self.cyl_radius, "height": self.cyl_height} - node_name = f"VDE_SDF_Cyl_r{self.cyl_radius:.1f}_h{self.cyl_height:.1f}" - elif self.sdf_type == 'torus': - params = {"major_radius": self.torus_major, - "minor_radius": self.torus_minor} - node_name = (f"VDE_SDF_Torus_" - f"R{self.torus_major:.1f}_r{self.torus_minor:.1f}") - elif self.sdf_type == 'custom': - # 从场景属性读取自定义 CSG 配置 - scene = context.scene - params = { - "children": list(scene.vde_sdf_children), - "operations": list(scene.vde_sdf_operations), - } - if scene.vde_sdf_blend_radius > 0: - params["blend_radius"] = scene.vde_sdf_blend_radius - if scene.vde_sdf_round > 0: - params["round"] = scene.vde_sdf_round - node_name = "VDE_SDF_CSG" - - bridge = get_bridge() - self.report( - {'INFO'}, - f"SDF→Mesh: {self.sdf_type}, res={self.resolution}" - ) - - try: - result = bridge.execute( - "sdf_to_mesh", - sdf_type=self.sdf_type, - params=params, - resolution=self.resolution, - ) - except RuntimeError as e: - return report_and_raise(self, str(e)) - - try: - vde_bridge.bridge_result_to_blender(result, name=node_name) - except RuntimeError as e: - return report_and_raise(self, str(e)) - - self.report( - {'INFO'}, - f"SDF→Mesh 完成: " - f"{result.get('num_vertices', 0)} 顶点, " - f"{result.get('num_faces', 0)} 面" - ) - return {'FINISHED'} - - -# ═══════════════════════════════════════════════════════════════ -# 注册/注销 -# ═══════════════════════════════════════════════════════════════ - -CLASSES = [ - VDE_OT_import_step, - VDE_OT_export_step, - VDE_OT_create_box, - VDE_OT_create_cylinder, - VDE_OT_create_sphere, - VDE_OT_boolean_union, - VDE_OT_boolean_intersect, - VDE_OT_boolean_diff, - VDE_OT_sdf_to_mesh, -] - - -def register(): - for cls in CLASSES: - bpy.utils.register_class(cls) - - -def unregister(): - for cls in reversed(CLASSES): - bpy.utils.unregister_class(cls) diff --git a/blender_addon/panels.py b/blender_addon/panels.py deleted file mode 100644 index 33e4675..0000000 --- a/blender_addon/panels.py +++ /dev/null @@ -1,420 +0,0 @@ -""" -panels.py — VDE Blender 侧边栏面板 -=================================== -在 3D Viewport 的 N 面板(右侧工具栏)添加 VDE 选项卡, -包含 Create、Modify、SDF 三个子面板。 -""" - -import bpy -from bpy.props import ( - FloatProperty, IntProperty, EnumProperty, - StringProperty, CollectionProperty, BoolProperty, -) - - -# ═══════════════════════════════════════════════════════════════ -# SDF CSG 配置属性(存储在场景级别) -# ═══════════════════════════════════════════════════════════════ - -SDF_PRIMITIVE_ITEMS = [ - ('sphere', "球体", "球体 SDF"), - ('box', "Box", "Box SDF"), - ('cylinder', "圆柱体", "圆柱体 SDF"), - ('torus', "环面", "环面 SDF"), -] - -SDF_OPERATION_ITEMS = [ - ('union', "Union (并集)", ""), - ('intersection', "Intersection (交集)", ""), - ('difference', "Difference (差集)", ""), - ('smooth_union', "Smooth Union (光滑并集)", ""), - ('smooth_intersection', "Smooth Intersection (光滑交集)", ""), - ('smooth_difference', "Smooth Difference (光滑差集)", ""), -] - - -class VDE_SdfChildItem(bpy.types.PropertyGroup): - """SDF CSG 子节点定义。""" - sdf_type: EnumProperty( - name="类型", - items=SDF_PRIMITIVE_ITEMS, - default='sphere', - ) - radius: FloatProperty(name="半径", default=1.0, min=0.01, max=50.0) - half_x: FloatProperty(name="半长 X", default=1.0, min=0.01, max=50.0) - half_y: FloatProperty(name="半长 Y", default=1.0, min=0.01, max=50.0) - half_z: FloatProperty(name="半长 Z", default=1.0, min=0.01, max=50.0) - cyl_radius: FloatProperty(name="半径", default=1.0, min=0.01, max=50.0) - cyl_height: FloatProperty(name="高度", default=2.0, min=0.01, max=100.0) - torus_major: FloatProperty(name="主半径", default=1.0, min=0.1, max=50.0) - torus_minor: FloatProperty(name="管半径", default=0.3, min=0.01, max=25.0) - - -class VDE_SdfOpItem(bpy.types.PropertyGroup): - """SDF CSG 操作定义。""" - operation: EnumProperty( - name="操作", - items=SDF_OPERATION_ITEMS, - default='union', - ) - - -# ═══════════════════════════════════════════════════════════════ -# 面板: VDE 主面板 -# ═══════════════════════════════════════════════════════════════ - -class VIEW3D_PT_vde_main(bpy.types.Panel): - """VDE 主面板容器。""" - bl_label = "VDE" - bl_idname = "VIEW3D_PT_vde_main" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "VDE" - bl_description = "ViewDesignEngine 计算几何引擎" - - def draw(self, context): - layout = self.layout - prefs = context.preferences.addons[__package__].preferences - - # 状态指示器 - row = layout.row(align=True) - available, msg = self._check_available(prefs) - if available: - row.label(text="● VDE 就绪", icon='CHECKMARK') - else: - row.label(text="○ VDE 未连接", icon='ERROR') - row = layout.row() - row.label(text=msg, icon='INFO') - - # 快捷设置 - box = layout.box() - box.label(text="快捷操作:", icon='TOOL_SETTINGS') - col = box.column(align=True) - col.operator("vde.import_step", text="导入 STEP...", icon='IMPORT') - col.operator("vde.export_step", text="导出 STEP...", icon='EXPORT') - col.operator("vde.sdf_to_mesh", text="SDF → Mesh", icon='MESH_DATA') - - def _check_available(self, prefs): - """缓存检查以避免每次重绘都调用子进程。""" - try: - return (prefs.vde_available, prefs.vde_status_message) - except Exception: - return (False, "检查失败") - - -# ═══════════════════════════════════════════════════════════════ -# 子面板: Create — 图元创建 -# ═══════════════════════════════════════════════════════════════ - -class VIEW3D_PT_vde_create(bpy.types.Panel): - """创建 B-Rep 图元子面板。""" - bl_label = "创建" - bl_parent_id = "VIEW3D_PT_vde_main" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "VDE" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - - # Box - box = layout.box() - box.label(text="Box", icon='MESH_CUBE') - col = box.column(align=True) - op = col.operator("vde.create_box", text="创建 Box") - col.prop( - bpy.types.VDE_OT_create_box.bl_rna.properties['width'], - "default", text="宽" - ) - col.prop( - bpy.types.VDE_OT_create_box.bl_rna.properties['height'], - "default", text="高" - ) - col.prop( - bpy.types.VDE_OT_create_box.bl_rna.properties['depth'], - "default", text="深" - ) - - # Cylinder - box = layout.box() - box.label(text="圆柱体", icon='MESH_CYLINDER') - col = box.column(align=True) - op = col.operator("vde.create_cylinder", text="创建圆柱体") - col.prop( - bpy.types.VDE_OT_create_cylinder.bl_rna.properties['radius'], - "default", text="半径" - ) - col.prop( - bpy.types.VDE_OT_create_cylinder.bl_rna.properties['height'], - "default", text="高" - ) - - # Sphere - box = layout.box() - box.label(text="球体", icon='MESH_UVSPHERE') - col = box.column(align=True) - op = col.operator("vde.create_sphere", text="创建球体") - col.prop( - bpy.types.VDE_OT_create_sphere.bl_rna.properties['radius'], - "default", text="半径" - ) - - -# ═══════════════════════════════════════════════════════════════ -# 子面板: Modify — 布尔/变形 -# ═══════════════════════════════════════════════════════════════ - -class VIEW3D_PT_vde_modify(bpy.types.Panel): - """布尔运算和修改子面板。""" - bl_label = "修改" - bl_parent_id = "VIEW3D_PT_vde_main" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "VDE" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - - # 布尔运算 - box = layout.box() - box.label(text="布尔运算", icon='MOD_BOOLEAN') - box.label(text="选中两个 Mesh 对象后执行:", icon='INFO') - - col = box.column(align=True) - col.operator("vde.boolean_union", text="并集 (Union)", icon='ADD') - col.operator("vde.boolean_intersect", text="交集 (Intersect)", - icon='SELECT_INTERSECT') - col.operator("vde.boolean_diff", text="差集 (A − B)", icon='REMOVE') - - # 使用提示 - box = layout.box() - box.label(text="使用说明", icon='HELP') - col = box.column(align=True) - col.label(text="1. Shift+点击选中两个对象") - col.label(text="2. 最后选中的为「活动对象」") - col.label(text="3. 点击布尔运算按钮") - - -# ═══════════════════════════════════════════════════════════════ -# 子面板: SDF — 隐式建模 -# ═══════════════════════════════════════════════════════════════ - -class VIEW3D_PT_vde_sdf(bpy.types.Panel): - """SDF 隐式建模子面板。""" - bl_label = "SDF 建模" - bl_parent_id = "VIEW3D_PT_vde_main" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "VDE" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - scene = context.scene - - # SDF 类型选择 - box = layout.box() - box.label(text="基础 SDF", icon='MESH_DATA') - col = box.column(align=True) - - # 直接用操作符属性绘制 - op_props = bpy.types.VDE_OT_sdf_to_mesh.bl_rna.properties - col.prop(op_props['sdf_type'], "default", text="类型") - - # 根据类型显示参数 - sdf_type = op_props['sdf_type'].default - if sdf_type == 'sphere': - col.prop(op_props['sphere_radius'], "default", text="半径") - elif sdf_type == 'box': - col.prop(op_props['box_hx'], "default", text="半长 X") - col.prop(op_props['box_hy'], "default", text="半长 Y") - col.prop(op_props['box_hz'], "default", text="半长 Z") - elif sdf_type == 'cylinder': - col.prop(op_props['cyl_radius'], "default", text="半径") - col.prop(op_props['cyl_height'], "default", text="高度") - elif sdf_type == 'torus': - col.prop(op_props['torus_major'], "default", text="主半径") - col.prop(op_props['torus_minor'], "default", text="管半径") - - col.prop(op_props['resolution'], "default", text="分辨率") - - row = col.row() - row.operator("vde.sdf_to_mesh", text="生成网格", icon='PLAY') - - # 自定义 CSG - box = layout.box() - box.label(text="自定义 CSG 组合", icon='NODETREE') - - # 子节点列表 - row = box.row() - row.label(text="子节点:") - row.operator("vde.sdf_add_child", text="", icon='ADD') - row.operator("vde.sdf_remove_child", text="", icon='REMOVE') - - for i, child in enumerate(scene.vde_sdf_children): - sub_box = box.box() - sub_box.label(text=f"节点 {i + 1}:") - sub_box.prop(child, "sdf_type", text="类型") - if child.sdf_type == 'sphere': - sub_box.prop(child, "radius", text="半径") - elif child.sdf_type == 'box': - sub_box.prop(child, "half_x", text="半长 X") - sub_box.prop(child, "half_y", text="半长 Y") - sub_box.prop(child, "half_z", text="半长 Z") - elif child.sdf_type == 'cylinder': - sub_box.prop(child, "cyl_radius", text="半径") - sub_box.prop(child, "cyl_height", text="高度") - elif child.sdf_type == 'torus': - sub_box.prop(child, "torus_major", text="主半径") - sub_box.prop(child, "torus_minor", text="管半径") - - # CSG 操作列表 - if len(scene.vde_sdf_children) > 1: - row = box.row() - row.label(text="操作:") - row.operator("vde.sdf_add_operation", text="", icon='ADD') - row.operator("vde.sdf_remove_operation", text="", icon='REMOVE') - - for i, op_item in enumerate(scene.vde_sdf_operations): - sub_box = box.box() - sub_box.prop(op_item, "operation", - text=f"节点{i}→节点{i + 1}") - - # 额外变换 - col = box.column(align=True) - col.prop(scene, "vde_sdf_blend_radius", text="光滑混合半径") - col.prop(scene, "vde_sdf_round", text="圆角半径") - - # 生成按钮 - row = box.row() - row.scale_y = 1.5 - row.operator("vde.sdf_to_mesh", text="▶ CSG → Mesh", - icon='OUTLINER_OB_MESH').sdf_type = 'custom' - - -# ═══════════════════════════════════════════════════════════════ -# SDF CSG 管理操作符 -# ═══════════════════════════════════════════════════════════════ - -class VDE_OT_sdf_add_child(bpy.types.Operator): - """添加 SDF CSG 子节点。""" - bl_idname = "vde.sdf_add_child" - bl_label = "添加节点" - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - item = context.scene.vde_sdf_children.add() - item.name = f"Node_{len(context.scene.vde_sdf_children)}" - return {'FINISHED'} - - -class VDE_OT_sdf_remove_child(bpy.types.Operator): - """移除最后一个 SDF CSG 子节点。""" - bl_idname = "vde.sdf_remove_child" - bl_label = "移除节点" - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - children = context.scene.vde_sdf_children - ops = context.scene.vde_sdf_operations - if len(children) > 0: - children.remove(len(children) - 1) - # 保持操作列表与节点数一致 - while len(ops) >= len(children): - ops.remove(len(ops) - 1) - return {'FINISHED'} - - -class VDE_OT_sdf_add_operation(bpy.types.Operator): - """添加 SDF CSG 操作。""" - bl_idname = "vde.sdf_add_operation" - bl_label = "添加操作" - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - children = context.scene.vde_sdf_children - ops = context.scene.vde_sdf_operations - if len(ops) < len(children) - 1: - ops.add() - return {'FINISHED'} - - -class VDE_OT_sdf_remove_operation(bpy.types.Operator): - """移除最后一个 SDF CSG 操作。""" - bl_idname = "vde.sdf_remove_operation" - bl_label = "移除操作" - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - ops = context.scene.vde_sdf_operations - if len(ops) > 0: - ops.remove(len(ops) - 1) - return {'FINISHED'} - - -# ═══════════════════════════════════════════════════════════════ -# 注册/注销 -# ═══════════════════════════════════════════════════════════════ - -PANEL_CLASSES = [ - VIEW3D_PT_vde_main, - VIEW3D_PT_vde_create, - VIEW3D_PT_vde_modify, - VIEW3D_PT_vde_sdf, -] - -SDF_CLASSES = [ - VDE_SdfChildItem, - VDE_SdfOpItem, - VDE_OT_sdf_add_child, - VDE_OT_sdf_remove_child, - VDE_OT_sdf_add_operation, - VDE_OT_sdf_remove_operation, -] - -SCENE_PROPS = [ - # SDF CSG 配置 - ("vde_sdf_children", CollectionProperty(type=VDE_SdfChildItem)), - ("vde_sdf_operations", CollectionProperty(type=VDE_SdfOpItem)), - ("vde_sdf_blend_radius", FloatProperty( - name="光滑混合半径", - default=0.1, - min=0.0, - max=10.0, - precision=3, - )), - ("vde_sdf_round", FloatProperty( - name="圆角半径", - default=0.0, - min=0.0, - max=10.0, - precision=3, - )), -] - - -def register(): - for cls in SDF_CLASSES: - bpy.utils.register_class(cls) - - for cls in PANEL_CLASSES: - bpy.utils.register_class(cls) - - # 注册场景属性 - for name, prop in SCENE_PROPS: - setattr(bpy.types.Scene, name, prop) - - -def unregister(): - for cls in reversed(PANEL_CLASSES): - bpy.utils.unregister_class(cls) - - for cls in reversed(SDF_CLASSES): - bpy.utils.unregister_class(cls) - - # 清理场景属性 - for name, _ in SCENE_PROPS: - if hasattr(bpy.types.Scene, name): - delattr(bpy.types.Scene, name) diff --git a/blender_addon/preferences.py b/blender_addon/preferences.py deleted file mode 100644 index 7a38962..0000000 --- a/blender_addon/preferences.py +++ /dev/null @@ -1,187 +0,0 @@ -""" -preferences.py — VDE 插件偏好设置 -================================== -在 Blender 偏好设置中添加 VDE 配置面板: - - VDE Python 库路径 - - Python 解释器路径 - - 状态检测与缓存 -""" - -import bpy -from bpy.props import StringProperty, BoolProperty, FloatProperty - -from . import vde_bridge - - -class VDEAddonPreferences(bpy.types.AddonPreferences): - """VDE 插件偏好设置。""" - bl_idname = __package__ - - # ══ 路径设置 ══ - vde_lib_path: StringProperty( - name="VDE 库路径", - description="VDE Python 包的安装目录(包含 vde/__init__.py 的父目录)", - default="", - subtype='DIR_PATH', - ) - - python_path: StringProperty( - name="Python 解释器", - description="用于运行 VDE 子进程的 Python 解释器路径(留空使用当前 Blender Python)", - default="", - subtype='FILE_PATH', - ) - - # ══ 网格化参数 ══ - default_deflection: FloatProperty( - name="默认网格精度", - description="B-Rep → Mesh 转换的默认弦高偏差", - default=0.01, - min=0.001, - max=1.0, - precision=4, - ) - - default_sdf_resolution: bpy.props.IntProperty( - name="默认 SDF 分辨率", - description="Marching Cubes 的默认网格分辨率", - default=64, - min=16, - max=512, - ) - - # ══ 缓存状态(不持久化) ══ - vde_available: BoolProperty( - name="VDE 可用", - default=False, - options={'SKIP_SAVE'}, - ) - vde_status_message: StringProperty( - name="状态消息", - default="未检测", - options={'SKIP_SAVE'}, - ) - - def draw(self, context): - layout = self.layout - - # 标题 - box = layout.box() - box.label(text="ViewDesignEngine (VDE) 设置", icon='SETTINGS') - - # 路径设置 - col = box.column(align=True) - col.prop(self, "vde_lib_path") - col.prop(self, "python_path") - - # 检测按钮 - row = box.row(align=True) - row.operator("vde.check_installation", - text="检测 VDE 安装", icon='CHECKMARK') - row.operator("vde.open_docs", - text="在线文档", icon='URL') - - # 状态 - if self.vde_available: - box.label(text=f"✓ {self.vde_status_message}", icon='CHECKMARK') - else: - row = box.row() - row.label(text=f"✗ {self.vde_status_message}", icon='ERROR') - row = box.row() - row.label( - text="VDE 库未连接。请确保 VDE Python 包已安装,", - icon='INFO' - ) - row = box.row() - row.label( - text="并将「VDE 库路径」设为包含 vde/ 的目录。", - icon='BLANK1' - ) - - # 默认参数 - box = layout.box() - box.label(text="默认参数", icon='PREFERENCES') - box.prop(self, "default_deflection") - box.prop(self, "default_sdf_resolution") - - # 关于 - box = layout.box() - box.label(text="关于", icon='INFO') - col = box.column() - col.label(text="ViewDesignEngine v1.0.0") - col.label(text="高性能 C++ CAD 计算几何引擎") - col.label(text="B-Rep | SDF | 布尔 | 网格") - col.label( - text="文档: github.com/ViewDesignEngine/ViewDesignEngine" - ) - - -# ═══════════════════════════════════════════════════════════════ -# 辅助操作符 -# ═══════════════════════════════════════════════════════════════ - -class VDE_OT_check_installation(bpy.types.Operator): - """检测 VDE Python 库是否可用。""" - bl_idname = "vde.check_installation" - bl_label = "检测 VDE 安装" - bl_description = "检测 VDE Python 库是否配置正确" - - def execute(self, context): - prefs = context.preferences.addons[__package__].preferences - - self.report({'INFO'}, "正在检测 VDE...") - - try: - available, message = vde_bridge.check_vde_available(prefs) - except Exception as e: - available, message = False, str(e) - - prefs.vde_available = available - prefs.vde_status_message = message - - if available: - self.report({'INFO'}, f"VDE 检测成功: {message}") - else: - self.report({'ERROR'}, f"VDE 检测失败: {message}") - - # 刷新 UI - for area in context.screen.areas: - if area.type == 'PREFERENCES': - area.tag_redraw() - return {'FINISHED'} - - -class VDE_OT_open_docs(bpy.types.Operator): - """在浏览器中打开 VDE 文档。""" - bl_idname = "vde.open_docs" - bl_label = "打开文档" - bl_description = "在浏览器中打开 ViewDesignEngine 在线文档" - - def execute(self, context): - import webbrowser - webbrowser.open( - "https://github.com/ViewDesignEngine/ViewDesignEngine" - ) - self.report({'INFO'}, "已在浏览器中打开文档页面。") - return {'FINISHED'} - - -# ═══════════════════════════════════════════════════════════════ -# 注册/注销 -# ═══════════════════════════════════════════════════════════════ - -CLASSES = [ - VDEAddonPreferences, - VDE_OT_check_installation, - VDE_OT_open_docs, -] - - -def register(): - for cls in CLASSES: - bpy.utils.register_class(cls) - - -def unregister(): - for cls in reversed(CLASSES): - bpy.utils.unregister_class(cls) diff --git a/blender_addon/vde_bridge.py b/blender_addon/vde_bridge.py deleted file mode 100644 index 3d0e939..0000000 --- a/blender_addon/vde_bridge.py +++ /dev/null @@ -1,501 +0,0 @@ -""" -vde_bridge.py — VDE 子进程桥接模块 -=================================== -通过独立的 Python 子进程调用 VDE Python 绑定,避免 Blender 与 VDE 的环境冲突。 - -工作原理: - 1. 将操作参数序列化为 JSON - 2. 启动 Python 子进程,传入操作类型和参数 - 3. 子进程导入 VDE,执行操作,输出 JSON 结果 - 4. 解析结果,将几何数据转换为 Blender Mesh - -对于不支持的操作(VDE 库未安装时),返回错误信息并提示安装。 -""" - -import os -import sys -import json -import subprocess - - -# ═══════════════════════════════════════════════════════════════ -# VDE 内联执行脚本(使用 %s 占位符避免与 JSON 大括号冲突) -# ═══════════════════════════════════════════════════════════════ - -_VDE_SCRIPT = r'''"""VDE bridge worker — 由 blender_addon 子进程调用。""" -import sys -import json - -def error(msg): - print(json.dumps({"error": str(msg)})) - sys.exit(1) - -# ── 定位 VDE 库 ── -vde_path = %s - -if vde_path: - sys.path.insert(0, vde_path) - -try: - from vde import brep, mesh, sdf as vde_sdf -except ImportError as e: - error("VDE 库未安装或路径不正确: " + str(e) + - "\n请检查 Blender 插件偏好设置中的 VDE 库路径。") - -# ── 执行操作 ── -operation = %s -args = %s - -try: - if operation == "import_step": - result = run_import_step(args) - elif operation == "export_step": - result = run_export_step(args) - elif operation == "create_primitive": - result = run_create_primitive(args) - elif operation == "boolean": - result = run_boolean(args) - elif operation == "sdf_to_mesh": - result = run_sdf_to_mesh(args) - else: - error("未知操作: " + operation) - print(json.dumps(result)) -except Exception as e: - import traceback - print(json.dumps({ - "error": str(e), - "traceback": traceback.format_exc() - })) - -# ── 操作实现 ── - -def brep_to_mesh_data(body): - """将 BrepModel 转换为网格数据字典。""" - try: - he_mesh = body.to_mesh(0.01) - except Exception as e: - raise RuntimeError("B-Rep 转网格失败: " + str(e)) - - verts = [] - for i in range(he_mesh.num_vertices()): - v = he_mesh.vertex(i) - verts.append([v.x(), v.y(), v.z()]) - - faces = [] - for i in range(he_mesh.num_faces()): - fv = he_mesh.face_vertices(i) - faces.append(list(fv)) - - return { - "vertices": verts, - "faces": faces, - "num_vertices": len(verts), - "num_faces": len(faces), - } - - -def run_import_step(args): - """导入 STEP 文件。""" - path = args.get("path", "") - if not path: - error("STEP 文件路径未指定") - - # 使用 industrial_formats 或 brep 的 import_step - try: - body = brep.import_step(path) - except AttributeError: - try: - from vde._vde.foundation import import_step as vde_import_step - body = vde_import_step(path) - except (ImportError, AttributeError): - error("VDE 未编译 STEP 导入支持。请确保 industrial_formats 模块已编译。") - - return brep_to_mesh_data(body) - - -def run_export_step(args): - """导出选中对象为 STEP 文件。""" - path = args.get("path", "") - if not path: - error("导出路径未指定") - - meshes = args.get("meshes", []) - bodies = [] - - for m in meshes: - verts = m.get("vertices", []) - faces = m.get("faces", []) - - if not verts or not faces: - continue - - # 从网格数据创建 HalfedgeMesh → BrepModel - hm = mesh.HalfedgeMesh() - hm.build_from_triangles(verts, faces) - body = brep.BrepModel.from_mesh(hm) - bodies.append(body) - - if not bodies: - error("没有有效的网格数据可导出") - - try: - brep.export_step(path, bodies) - except AttributeError: - try: - from vde._vde import io - io.export_step_ap242_file(path, bodies) - except (ImportError, AttributeError) as e: - error("VDE 未编译 STEP 导出支持: " + str(e)) - - return {"status": "ok", "path": path, "num_bodies": len(bodies)} - - -def run_create_primitive(args): - """创建 B-Rep 图元。""" - prim_type = args.get("type", "box") - params = args.get("params", {}) - - if prim_type == "box": - w = params.get("width", 2.0) - h = params.get("height", 2.0) - d = params.get("depth", 2.0) - body = brep.make_box(w, h, d) - elif prim_type == "cylinder": - r = params.get("radius", 1.0) - h = params.get("height", 2.0) - body = brep.make_cylinder(r, h) - elif prim_type == "sphere": - r = params.get("radius", 1.0) - body = brep.make_sphere(r) - else: - error("未知图元类型: " + prim_type) - - return brep_to_mesh_data(body) - - -def run_boolean(args): - """布尔运算。""" - op_type = args.get("boolean_type", "union") - mesh_a = args.get("mesh_a", {}) - mesh_b = args.get("mesh_b", {}) - - def mesh_to_brep(m): - hm = mesh.HalfedgeMesh() - hm.build_from_triangles(m.get("vertices", []), m.get("faces", [])) - return brep.BrepModel.from_mesh(hm) - - body_a = mesh_to_brep(mesh_a) - body_b = mesh_to_brep(mesh_b) - - if op_type == "union": - result_body = brep.boolean_union(body_a, body_b) - elif op_type == "intersect": - result_body = brep.boolean_intersection(body_a, body_b) - elif op_type == "diff": - result_body = brep.boolean_difference(body_a, body_b) - else: - error("未知布尔操作: " + op_type) - - return brep_to_mesh_data(result_body) - - -def run_sdf_to_mesh(args): - """SDF → Mesh 转换。""" - sdf_type = args.get("sdf_type", "sphere") - params = args.get("params", {}) - resolution = args.get("resolution", 64) - - if sdf_type == "sphere": - sdf_node = vde_sdf.SdfNode.sphere(params.get("radius", 1.0)) - elif sdf_type == "box": - sdf_node = vde_sdf.SdfNode.box(params.get("half_extents", [1.0, 1.0, 1.0])) - elif sdf_type == "cylinder": - sdf_node = vde_sdf.SdfNode.cylinder( - params.get("radius", 1.0), params.get("height", 2.0) - ) - elif sdf_type == "torus": - sdf_node = vde_sdf.SdfNode.torus( - params.get("major_radius", 1.0), params.get("minor_radius", 0.3) - ) - elif sdf_type == "custom": - sdf_node = build_custom_sdf(params) - else: - error("未知 SDF 类型: " + sdf_type) - - mc_mesh = vde_sdf.sdf_to_mesh(sdf_node, resolution) - - verts = [[v[0], v[1], v[2]] for v in mc_mesh.vertices] - faces = [[t[0], t[1], t[2]] for t in mc_mesh.triangles] - - return { - "vertices": verts, - "faces": faces, - "num_vertices": len(verts), - "num_faces": len(faces), - } - - -def build_custom_sdf(params): - """构建自定义 SDF CSG 树。""" - children = params.get("children", []) - operations = params.get("operations", []) - - if not children: - return vde_sdf.SdfNode.sphere(1.0) - - nodes = [] - for child in children: - ct = child.get("type", "sphere") - cp = child.get("params", {}) - if ct == "sphere": - nodes.append(vde_sdf.SdfNode.sphere(cp.get("radius", 1.0))) - elif ct == "box": - nodes.append(vde_sdf.SdfNode.box( - cp.get("half_extents", [1.0, 1.0, 1.0])) - ) - elif ct == "cylinder": - nodes.append(vde_sdf.SdfNode.cylinder( - cp.get("radius", 1.0), cp.get("height", 2.0)) - ) - elif ct == "torus": - nodes.append(vde_sdf.SdfNode.torus( - cp.get("major_radius", 1.0), cp.get("minor_radius", 0.3)) - ) - - result = nodes[0] - for i, op in enumerate(operations): - next_node = nodes[i + 1] - if op == "union": - result = vde_sdf.SdfNode.op_union(result, next_node) - elif op == "intersection": - result = vde_sdf.SdfNode.op_intersection(result, next_node) - elif op == "difference": - result = vde_sdf.SdfNode.op_difference(result, next_node) - elif op == "smooth_union": - result = vde_sdf.SdfNode.smooth_union( - result, next_node, params.get("blend_radius", 0.1)) - elif op == "smooth_intersection": - result = vde_sdf.SdfNode.smooth_intersection( - result, next_node, params.get("blend_radius", 0.1)) - elif op == "smooth_difference": - result = vde_sdf.SdfNode.smooth_difference( - result, next_node, params.get("blend_radius", 0.1)) - - # 应用变换 - if params.get("translate"): - t = params["translate"] - result = vde_sdf.SdfNode.translate(result, t) - if params.get("scale"): - s = params["scale"] - result = vde_sdf.SdfNode.scale(result, s) - if params.get("round"): - result = vde_sdf.SdfNode.round(result, params["round"]) - if params.get("onion"): - result = vde_sdf.SdfNode.onion(result, params["onion"]) - - return result -''' - - -# ═══════════════════════════════════════════════════════════════ -# 桥接客户端 -# ═══════════════════════════════════════════════════════════════ - -class VDEBridge: - """VDE 桥接客户端 — 通过子进程调用 VDE Python 绑定。""" - - def __init__(self, vde_lib_path=None, python_executable=None): - self.vde_lib_path = vde_lib_path or "" - self.python_executable = python_executable or sys.executable - - def execute(self, operation, **kwargs): - """ - 通过子进程执行 VDE 操作。 - - Args: - operation: 操作类型 ("import_step", "export_step", - "create_primitive", "boolean", "sdf_to_mesh") - **kwargs: 操作参数 - - Returns: - dict: 操作结果,可能包含 "vertices" 和 "faces" 字段 - - Raises: - RuntimeError: VDE 操作失败 - """ - vde_path_repr = repr(self.vde_lib_path) - op_repr = repr(operation) - args_json = json.dumps(kwargs, indent=2) - - script = _VDE_SCRIPT % (vde_path_repr, op_repr, args_json) - - try: - proc = subprocess.run( - [self.python_executable, "-c", script], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - timeout=120, - env={**os.environ, "PYTHONUNBUFFERED": "1"}, - ) - except subprocess.TimeoutExpired: - raise RuntimeError("VDE 操作超时 (120s)。请减小分辨率或检查模型复杂度。") - except FileNotFoundError: - raise RuntimeError( - f"Python 解释器未找到: {self.python_executable}。" - "请检查 VDE 库路径设置。" - ) - - stdout = proc.stdout.strip() - stderr = proc.stderr.strip() - - if proc.returncode != 0: - error_msg = stdout or stderr or f"进程退出码: {proc.returncode}" - raise RuntimeError(f"VDE 子进程错误:\n{error_msg}") - - try: - result = json.loads(stdout) - except json.JSONDecodeError: - raise RuntimeError( - f"VDE 子进程返回了无效 JSON。\n" - f"stdout: {stdout[:500]}\n" - f"stderr: {stderr[:500]}" - ) - - if "error" in result: - raise RuntimeError(f"VDE 操作错误: {result['error']}") - - return result - - -# ═══════════════════════════════════════════════════════════════ -# Blender Mesh 构建工具 -# ═══════════════════════════════════════════════════════════════ - -def bridge_result_to_blender(result, name="VDE_Mesh", collection=None): - """ - 将 VDE 桥接结果转换为 Blender Mesh 对象。 - - Args: - result: VDE 操作返回的 dict,含 "vertices" 和 "faces" - name: 对象名称 - collection: 目标集合(None 则使用活动集合) - - Returns: - bpy.types.Object: 新创建的 Blender 对象 - """ - import bpy - - vertices = result.get("vertices", []) - faces = result.get("faces", []) - - if not vertices or not faces: - raise RuntimeError("VDE 返回了空的网格数据。") - - # 创建 Blender Mesh - mesh = bpy.data.meshes.new(name=name) - mesh.from_pydata(vertices, [], faces) - mesh.update(calc_edges=True) - mesh.validate() - - # 创建 Object 并链接到场景 - obj = bpy.data.objects.new(name=name, object_data=mesh) - if collection is None: - collection = bpy.context.collection - collection.objects.link(obj) - - # 设为活动选中对象 - bpy.context.view_layer.objects.active = obj - obj.select_set(True) - - print(f"[VDE] 创建了 '{name}': " - f"{len(vertices)} 顶点, {len(faces)} 面") - - return obj - - -def blender_mesh_to_bridge_data(obj): - """ - 将 Blender 对象网格数据转换为桥接 JSON 格式。 - - Args: - obj: Blender Object (须为 MESH 类型) - - Returns: - dict: {"vertices": [...], "faces": [...]} - """ - import bpy - - if obj.type != 'MESH': - raise RuntimeError(f"对象 '{obj.name}' 不是 MESH 类型 (是 {obj.type})") - - # 获取世界空间坐标 - depsgraph = bpy.context.evaluated_depsgraph_get() - eval_obj = obj.evaluated_get(depsgraph) - mesh = eval_obj.to_mesh() - - vertices = [] - for v in mesh.vertices: - co = v.co - vertices.append([co.x, co.y, co.z]) - - # 三角剖分(布尔运算需要三角形网格) - faces = [] - for poly in mesh.polygons: - if len(poly.vertices) == 3: - faces.append(list(poly.vertices)) - elif len(poly.vertices) == 4: - faces.append([poly.vertices[0], poly.vertices[1], poly.vertices[2]]) - faces.append([poly.vertices[0], poly.vertices[2], poly.vertices[3]]) - else: - for i in range(1, len(poly.vertices) - 1): - faces.append([ - poly.vertices[0], - poly.vertices[i], - poly.vertices[i + 1] - ]) - - eval_obj.to_mesh_clear() - - return {"vertices": vertices, "faces": faces} - - -# ═══════════════════════════════════════════════════════════════ -# VDE 可用性检测 -# ═══════════════════════════════════════════════════════════════ - -def check_vde_available(prefs): - """ - 检测 VDE Python 库是否可用。 - - Args: - prefs: 插件偏好设置(含 vde_lib_path) - - Returns: - tuple: (available: bool, message: str) - """ - vde_path = prefs.vde_lib_path if prefs else "" - test_script = """import sys -vde_path = %r -if vde_path: - sys.path.insert(0, vde_path) -try: - from vde import brep, mesh, sdf - print("OK") -except ImportError as e: - print("ERROR: " + str(e)) -""" - try: - proc = subprocess.run( - [sys.executable, "-c", test_script % vde_path], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True, timeout=10, - env={**os.environ, "PYTHONUNBUFFERED": "1"}, - ) - output = proc.stdout.strip() - if output == "OK": - return True, "VDE 库可用" - else: - return False, output - except Exception as e: - return False, str(e) diff --git a/docs/dev-guide/plugin-system.md b/docs/dev-guide/plugin-system.md deleted file mode 100644 index c13c95a..0000000 --- a/docs/dev-guide/plugin-system.md +++ /dev/null @@ -1,501 +0,0 @@ -# 插件系统设计 - -v6.0 引入插件系统,允许第三方开发者扩展 ViewDesignEngine 的功能,无需修改核心代码。 - -## 目录 - -1. [设计目标](#1-设计目标) -2. [架构概览](#2-架构概览) -3. [核心组件](#3-核心组件) -4. [插件开发](#4-插件开发) -5. [插件加载流程](#5-插件加载流程) -6. [生命周期管理](#6-生命周期管理) -7. [清单文件](#7-清单文件) -8. [安全考虑](#8-安全考虑) -9. [最佳实践](#9-最佳实践) - ---- - -## 1. 设计目标 - -| 目标 | 说明 | -|------|------| -| **零侵入** | 插件不修改核心代码,独立编译 | -| **动态加载** | 运行时 dlopen 加载,无需重新编译 VDE | -| **版本兼容** | Manifest 声明版本约束,自动检查兼容性 | -| **隔离性** | 插件崩溃不影响主程序 | -| **易开发** | 继承 `PluginInterface` 即可,最少样板代码 | - -## 2. 架构概览 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Application │ -│ │ │ -│ PluginManager │ -│ ┌──────┴──────┐ │ -│ │ Registry │ │ -│ │ name→Plugin* │ │ -│ └──────┬──────┘ │ -│ ┌───────────────┼───────────────┐ │ -│ │ │ │ │ -│ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │ -│ │Plugin A │ │Plugin B │ │Plugin C │ │ -│ │(.so) │ │(.so) │ │(.so) │ │ -│ │IO Format│ │Geometry │ │ Custom │ │ -│ │Exporter │ │Filter │ │ Tool │ │ -│ └─────────┘ └─────────┘ └─────────┘ │ -│ │ -│ Plugin Directory: /usr/lib/vde/plugins/ │ -│ ./vde_plugins/ │ -│ $VDE_PLUGIN_PATH │ -└─────────────────────────────────────────────────────────────┘ -``` - -## 3. 核心组件 - -### 3.1 PluginInterface(抽象基类) - -所有插件必须实现的接口: - -```cpp -namespace vde::plugin { - -class PluginInterface { -public: - virtual ~PluginInterface() = default; - - /// 插件初始化(加载后调用一次) - virtual bool initialize() = 0; - - /// 插件清理(卸载前调用一次) - virtual void shutdown() = 0; - - /// 返回插件清单 - virtual const PluginManifest& manifest() const = 0; - - /// 插件唯一名称(如 "io.export.usdz") - virtual const char* name() const = 0; - - /// 插件版本(语义化版本) - virtual const char* version() const = 0; - - /// 执行插件功能 - virtual bool execute(const std::string& action, - const std::map& params, - void* input, void* output) = 0; -}; - -} // namespace vde::plugin -``` - -### 3.2 PluginManifest(插件元数据) - -```cpp -namespace vde::plugin { - -struct PluginManifest { - std::string name; ///< 唯一标识符,如 "io.export.usdz" - std::string version; ///< 语义化版本 "1.0.0" - std::string author; ///< 作者信息 - std::string description; ///< 功能描述 - std::string license; ///< 许可证 "MIT" / "Apache-2.0" - std::string vde_version_min;///< 最低 VDE 版本 "6.0.0" - std::string vde_version_max;///< 最高兼容 VDE 版本 (空=无上限) - - /// 插件类型 - enum class Type { - IO_IMPORT, ///< 格式导入器 - IO_EXPORT, ///< 格式导出器 - GEOMETRY_FILTER,///< 几何过滤器 - CUSTOM_TOOL, ///< 自定义工具 - OTHER - }; - Type type = Type::OTHER; - - /// 依赖的其他插件名称(可选) - std::vector dependencies; - - /// 从 JSON 文件加载 - static std::optional from_file(const std::string& path); -}; - -} // namespace vde::plugin -``` - -### 3.3 PluginManager(插件管理器) - -```cpp -namespace vde::plugin { - -class PluginManager { -public: - PluginManager(); - ~PluginManager(); - - // 禁止拷贝 - PluginManager(const PluginManager&) = delete; - PluginManager& operator=(const PluginManager&) = delete; - - /// 从目录加载所有插件 - int load_plugin_dir(const std::string& dir_path); - - /// 加载单个插件(路径到 .so/.dylib/.dll) - bool load_plugin(const std::string& plugin_path); - - /// 卸载指定插件 - bool unload_plugin(const std::string& name); - - /// 卸载所有插件 - void unload_all(); - - /// 获取已加载的插件 - PluginInterface* get_plugin(const std::string& name); - - /// 列出所有已加载插件 - std::vector list_plugins() const; - - /// 检查插件是否已加载 - bool is_loaded(const std::string& name) const; - - /// 获取加载错误信息 - std::string last_error() const; - -private: - struct Impl; - std::unique_ptr impl_; -}; - -} // namespace vde::plugin -``` - -### 3.4 注册/加载函数 - -每个插件动态库必须导出以下 C 链接符号: - -```cpp -// 创建插件实例(必须导出) -extern "C" VDE_PLUGIN_EXPORT vde::plugin::PluginInterface* create_plugin(); - -// 销毁插件实例(必须导出) -extern "C" VDE_PLUGIN_EXPORT void destroy_plugin(vde::plugin::PluginInterface* plugin); - -// 获取插件清单(可选,优化加载速度) -extern "C" VDE_PLUGIN_EXPORT const vde::plugin::PluginManifest* get_manifest(); -``` - -## 4. 插件开发 - -### 4.1 最小插件示例 - -```cpp -// my_exporter.cpp -#include -#include - -using namespace vde::plugin; - -class MyExporter : public PluginInterface { -public: - bool initialize() override { - manifest_.name = "io.export.myformat"; - manifest_.version = "1.0.0"; - manifest_.author = "Your Name"; - manifest_.description = "Export to MyFormat"; - manifest_.type = PluginManifest::Type::IO_EXPORT; - manifest_.vde_version_min = "6.0.0"; - return true; - } - - void shutdown() override { - // 清理资源 - } - - const PluginManifest& manifest() const override { - return manifest_; - } - - const char* name() const override { - return manifest_.name.c_str(); - } - - const char* version() const override { - return manifest_.version.c_str(); - } - - bool execute(const std::string& action, - const std::map& params, - void* input, void* output) override { - if (action == "export") { - // 执行导出逻辑 - return true; - } - return false; - } - -private: - PluginManifest manifest_; -}; - -// 必需的导出符号 -extern "C" VDE_PLUGIN_EXPORT PluginInterface* create_plugin() { - return new MyExporter(); -} - -extern "C" VDE_PLUGIN_EXPORT void destroy_plugin(PluginInterface* plugin) { - delete plugin; -} -``` - -### 4.2 CMake 构建 - -```cmake -# CMakeLists.txt for plugin -cmake_minimum_required(VERSION 3.16) -project(my_exporter VERSION 1.0.0) - -find_package(ViewDesignEngine REQUIRED) - -add_library(my_exporter SHARED my_exporter.cpp) -target_link_libraries(my_exporter PRIVATE vde::engine) -target_include_directories(my_exporter PRIVATE ${VDE_INCLUDE_DIRS}) - -# 设置输出到插件目录 -set_target_properties(my_exporter PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins" -) -``` - -### 4.3 插件类型 - -| 类型 | 枚举值 | 说明 | 示例 | -|------|--------|------|------| -| 格式导入器 | `IO_IMPORT` | 新增文件格式导入 | USDZ, 3DS, AMF | -| 格式导出器 | `IO_EXPORT` | 新增文件格式导出 | USDZ, FBX, X3D | -| 几何过滤器 | `GEOMETRY_FILTER` | 几何后处理 | 平滑、简化、重网格 | -| 自定义工具 | `CUSTOM_TOOL` | 领域特定工具 | 应力分析、拓扑优化 | - -## 5. 插件加载流程 - -``` -Application::init() - │ - ▼ -PluginManager::load_plugin_dir("/usr/lib/vde/plugins") - │ - ├── 扫描目录: for each *.so / *.dylib / *.dll - │ - ├── dlopen(plugin_path, RTLD_NOW) - │ │ - │ ├── 成功 → 继续 - │ └── 失败 → 记录错误,跳过 - │ - ├── dlsym(handle, "get_manifest") - │ │ - │ ├── 找到 → 读取 manifest,检查版本兼容性 - │ └── 未找到 → 继续加载(稍后从 initialize 获取) - │ - ├── dlsym(handle, "create_plugin") - │ │ - │ ├── 找到 → PluginInterface* plugin = create_plugin() - │ └── 未找到 → dlclose,跳过 - │ - ├── plugin->initialize() - │ │ - │ ├── 返回 true → 注册到 Registry - │ └── 返回 false → destroy_plugin + dlclose - │ - └── 返回加载成功数量 -``` - -### 版本兼容性检查 - -```cpp -// 伪代码 -bool is_compatible(const PluginManifest& m, const std::string& vde_version) { - // 检查最小版本 - if (compare_versions(vde_version, m.vde_version_min) < 0) - return false; - - // 检查最大版本(如果指定) - if (!m.vde_version_max.empty() && - compare_versions(vde_version, m.vde_version_max) > 0) - return false; - - return true; -} -``` - -## 6. 生命周期管理 - -``` -┌──────────────────────────────────────────────────────────────┐ -│ 插件生命周期 │ -│ │ -│ [编译] → [发现] → [加载] → [初始化] → [运行] → [卸载] │ -│ │ -│ 编译: 开发者编写代码,构建为 .so/.dylib/.dll │ -│ 发现: PluginManager 扫描插件目录 │ -│ 加载: dlopen 加载动态库 │ -│ 初始化: initialize() 注册功能 │ -│ 运行: execute() 被调用 │ -│ 卸载: shutdown() + dlclose │ -│ │ -│ 状态机: │ -│ UNLOADED → LOADING → INITIALIZED → RUNNING │ -│ ↑ ↓ ↓ │ -│ └── ERROR SHUTTING_DOWN → UNLOADED│ -└──────────────────────────────────────────────────────────────┘ -``` - -### 状态说明 - -| 状态 | 说明 | -|------|------| -| `UNLOADED` | 插件未加载或已卸载 | -| `LOADING` | dlopen 成功,正在初始化 | -| `INITIALIZED` | initialize() 完成,等待 execute | -| `RUNNING` | execute() 正在执行中 | -| `SHUTTING_DOWN` | shutdown() 执行中 | -| `ERROR` | 加载或初始化失败 | - -## 7. 清单文件 - -插件可以在同目录放置 `plugin.json` 描述文件,加快发现速度: - -```json -{ - "name": "io.export.usdz", - "version": "1.0.0", - "author": "Your Name ", - "description": "Export geometry to USDZ format for Apple AR", - "license": "MIT", - "type": "IO_EXPORT", - "vde_version_min": "6.0.0", - "vde_version_max": "", - "dependencies": [], - "library": "libvde_usdz_export.so", - "entry_points": { - "create": "create_plugin", - "destroy": "destroy_plugin" - } -} -``` - -### PluginManager 加载优先级 - -1. 如果存在 `plugin.json` → 先读取 JSON 验证兼容性 -2. 兼容 → `dlopen` + `dlsym(create_plugin)` -3. 不兼容 → 跳过,记录日志 -4. 无 `plugin.json` → 直接 `dlopen` + 尝试 `get_manifest` 符号 - -## 8. 安全考虑 - -### 8.1 隔离性 - -| 措施 | 说明 | -|------|------| -| 独立 .so | 每个插件独立动态库 | -| 崩溃隔离 | 插件 SIGSEGV 不传播到主程序(信号处理器) | -| 符号可见性 | 插件使用 `-fvisibility=hidden` 隐藏内部符号 | -| 内存隔离 | 插件通过 PluginInterface 与主程序交互,不直接访问内部数据 | - -### 8.2 信任模型 - -``` -┌───────────────────────────────────────┐ -│ 插件信任级别 │ -│ │ -│ L0 内置: VDE 官方维护,完全信任 │ -│ L1 签名: 第三方已签名,受信任 │ -│ L2 社区: 社区插件,有限沙箱 │ -│ L3 未知: 未签名/未知来源,严格沙箱 │ -│ │ -│ L3 限制: │ -│ - 不能访问文件系统(除了输出目录) │ -│ - 不能调用网络 │ -│ - 不能 fork │ -│ - CPU/内存限制 │ -└───────────────────────────────────────┘ -``` - -### 8.3 最佳安全实践 - -1. **始终验证 manifest 版本兼容性**后再 initialize -2. **dlopen 使用 `RTLD_NOW | RTLD_LOCAL`** 而非 `RTLD_LAZY | RTLD_GLOBAL` -3. **设置超时**:execute() 调用应有超时机制 -4. **资源限制**:通过 cgroups/Docker 限制插件资源 -5. **日志审计**:记录所有插件加载/卸载/执行事件 - -## 9. 最佳实践 - -### 9.1 插件开发建议 - -- ✅ 插件尽量无状态,每次 execute 独立 -- ✅ 用 manifest 声明清晰的功能范围 -- ✅ 支持版本协商(`vde_version_min` / `vde_version_max`) -- ✅ 提供详细的错误信息(通过返回值 + 日志) -- ✅ 清理所有资源在 shutdown() 中 -- ❌ 不要在 initialize() 中做耗时操作 -- ❌ 不要依赖全局可变状态 -- ❌ 不要修改 VDE 核心数据结构 - -### 9.2 插件命名规范 - -``` -格式: .. - -示例: -- io.import.fbx # FBX 格式导入 -- io.export.usdz # USDZ 格式导出 -- geometry.smooth.laplacian # Laplacian 平滑 -- tool.analysis.stress # 应力分析工具 -``` - -### 9.3 使用示例 - -```cpp -#include - -int main() { - vde::plugin::PluginManager mgr; - - // 加载所有插件 - int count = mgr.load_plugin_dir("/usr/lib/vde/plugins"); - std::cout << "Loaded " << count << " plugins\n"; - - // 列出已加载插件 - for (auto& name : mgr.list_plugins()) { - std::cout << " - " << name << "\n"; - } - - // 使用特定插件 - auto* exporter = mgr.get_plugin("io.export.gltf"); - if (exporter) { - std::map params; - params["path"] = "output.glb"; - params["binary"] = "true"; - - void* mesh_data = /* ... */; - bool ok = exporter->execute("export", params, mesh_data, nullptr); - } - - // 卸载 - mgr.unload_all(); - return 0; -} -``` - ---- - -## 附录: 平台差异 - -| 操作 | Linux | macOS | Windows | -|------|-------|-------|---------| -| 动态库后缀 | `.so` | `.dylib` | `.dll` | -| 加载函数 | `dlopen` | `dlopen` | `LoadLibraryA` | -| 符号解析 | `dlsym` | `dlsym` | `GetProcAddress` | -| 卸载函数 | `dlclose` | `dlclose` | `FreeLibrary` | -| 错误信息 | `dlerror()` | `dlerror()` | `GetLastError()` | -| 链接库 | `-ldl` | (内置) | `kernel32.lib` | -| 可见性宏 | `__attribute__((visibility("default")))` | 同 Linux | `__declspec(dllexport)` | diff --git a/dotnet/VdeSharp.csproj b/dotnet/VdeSharp.csproj deleted file mode 100644 index 1a2d5cf..0000000 --- a/dotnet/VdeSharp.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - net8.0 - VdeSharp - enable - enable - true - - - - - - - diff --git a/dotnet/VdeSharp/Assembly.cs b/dotnet/VdeSharp/Assembly.cs deleted file mode 100644 index 3e51828..0000000 --- a/dotnet/VdeSharp/Assembly.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace VdeSharp; - -/// -/// Managed wrapper for VDE Assembly. -/// Represents a hierarchical assembly of BrepModel parts. -/// -public class Assembly : IDisposable -{ - private readonly nint _ctx; - internal nint _handle; - private bool _disposed; - - /// - /// Creates a new assembly with the given name. - /// - public Assembly(nint ctx, string name) - { - _ctx = ctx; - _handle = NativeMethods.vde_assembly_create(ctx, name); - } - - /// - /// Adds a part (BrepModel) to the root of this assembly. - /// The part is deep-copied into the assembly. - /// - /// 1 on success - public int AddPart(string name, BrepModel body) - { - return NativeMethods.vde_assembly_add_part(_handle, name, body._handle); - } - - /// - /// Returns the total number of part nodes in this assembly. - /// - public int PartCount => NativeMethods.vde_assembly_part_count(_handle); - - public void Dispose() - { - if (!_disposed && _handle != nint.Zero) - { - NativeMethods.vde_assembly_free(_handle); - _handle = nint.Zero; - _disposed = true; - } - } -} diff --git a/dotnet/VdeSharp/BrepModel.cs b/dotnet/VdeSharp/BrepModel.cs deleted file mode 100644 index 77b2094..0000000 --- a/dotnet/VdeSharp/BrepModel.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System.Runtime.InteropServices; - -namespace VdeSharp; - -/// -/// Managed wrapper around a native VDE B-Rep model (VdeBodyHandle). -/// Provides STEP/IGES import/export, Boolean operations, and mesh tessellation. -/// -public class BrepModel : IDisposable -{ - /// Native engine context handle - private readonly nint _ctx; - - /// Native B-Rep body handle - internal nint _handle; - - private bool _disposed; - - /// Returns the native handle (for internal use). - internal nint Handle => _handle; - - /// - /// Wraps an existing native body handle. Used internally by factory methods. - /// - internal BrepModel(nint ctx, nint handle) - { - _ctx = ctx; - _handle = handle; - } - - /// - /// Creates a box primitive. - /// - public static BrepModel CreateBox(nint ctx, double w, double h, double d) - { - nint hdl = NativeMethods.vde_body_create_box(ctx, w, h, d); - return new BrepModel(ctx, hdl); - } - - /// - /// Creates a cylinder primitive. - /// - public static BrepModel CreateCylinder(nint ctx, double radius, double height, int segments = 32) - { - nint hdl = NativeMethods.vde_body_create_cylinder(ctx, radius, height, segments); - return new BrepModel(ctx, hdl); - } - - /// - /// Creates a sphere primitive. - /// - public static BrepModel CreateSphere(nint ctx, double radius, int su = 32, int sv = 32) - { - nint hdl = NativeMethods.vde_body_create_sphere(ctx, radius, su, sv); - return new BrepModel(ctx, hdl); - } - - /// - /// Imports B-Rep models from a STEP file. - /// Returns an array of BrepModel instances. - /// - public static unsafe BrepModel[] LoadStep(nint ctx, string path) - { - nint* bodiesPtr; - int count = NativeMethods.vde_body_load_step(ctx, path, &bodiesPtr); - if (count <= 0) return Array.Empty(); - - var result = new BrepModel[count]; - for (int i = 0; i < count; i++) - result[i] = new BrepModel(ctx, bodiesPtr[i]); - - // Free the pointer array (but NOT the individual handles) - Marshal.FreeHGlobal((nint)bodiesPtr); - return result; - } - - /// - /// Imports B-Rep models from an IGES file. - /// - public static unsafe BrepModel[] LoadIges(nint ctx, string path) - { - nint* bodiesPtr; - int count = NativeMethods.vde_body_load_iges(ctx, path, &bodiesPtr); - if (count <= 0) return Array.Empty(); - - var result = new BrepModel[count]; - for (int i = 0; i < count; i++) - result[i] = new BrepModel(ctx, bodiesPtr[i]); - - Marshal.FreeHGlobal((nint)bodiesPtr); - return result; - } - - /// - /// Exports a list of BrepModel instances to a STEP file. - /// - public static void SaveStep(nint ctx, BrepModel[] bodies, string path) - { - var handles = new nint[bodies.Length]; - for (int i = 0; i < bodies.Length; i++) - handles[i] = bodies[i]._handle; - NativeMethods.vde_body_save_step(ctx, handles, handles.Length, path); - } - - /// - /// Exports a list of BrepModel instances to an IGES file. - /// - public static void SaveIges(nint ctx, BrepModel[] bodies, string path) - { - var handles = new nint[bodies.Length]; - for (int i = 0; i < bodies.Length; i++) - handles[i] = bodies[i]._handle; - NativeMethods.vde_body_save_iges(ctx, handles, handles.Length, path); - } - - /// - /// Saves this single body to a STEP file. - /// - public void SaveStep(string path) - => SaveStep(_ctx, new[] { this }, path); - - /// - /// Saves this single body to an IGES file. - /// - public void SaveIges(string path) - => SaveIges(_ctx, new[] { this }, path); - - // ═══════════════════════════════════════════════════ - // Boolean operations - // ═══════════════════════════════════════════════════ - - /// Boolean union: this ∪ other - public BrepModel Union(BrepModel other) - { - nint hdl = NativeMethods.vde_body_boolean(_ctx, _handle, other._handle, 0); - return new BrepModel(_ctx, hdl); - } - - /// Boolean intersection: this ∩ other - public BrepModel Intersect(BrepModel other) - { - nint hdl = NativeMethods.vde_body_boolean(_ctx, _handle, other._handle, 1); - return new BrepModel(_ctx, hdl); - } - - /// Boolean difference: this \ other - public BrepModel Diff(BrepModel other) - { - nint hdl = NativeMethods.vde_body_boolean(_ctx, _handle, other._handle, 2); - return new BrepModel(_ctx, hdl); - } - - // ═══════════════════════════════════════════════════ - // Tessellation / Mesh - // ═══════════════════════════════════════════════════ - - /// - /// Tessellates the B-Rep model into a triangle mesh. - /// - /// Chord height error for tessellation (default 0.01) - public MeshData ToMesh(double deflection = 0.01) - { - nint mh = NativeMethods.vde_body_to_mesh(_ctx, _handle, deflection); - var mesh = new MeshData(mh, ownsHandle: true); - mesh.ExtractFromNative(); - return mesh; - } - - /// - /// Deep clone of this B-Rep model. - /// - public BrepModel Clone() - { - nint hdl = NativeMethods.vde_body_clone(_ctx, _handle); - return new BrepModel(_ctx, hdl); - } - - // ═══════════════════════════════════════════════════ - // IDisposable - // ═══════════════════════════════════════════════════ - - public void Dispose() - { - if (!_disposed && _handle != nint.Zero) - { - NativeMethods.vde_body_free(_handle); - _handle = nint.Zero; - _disposed = true; - } - } -} diff --git a/dotnet/VdeSharp/MeshData.cs b/dotnet/VdeSharp/MeshData.cs deleted file mode 100644 index 7dfdb86..0000000 --- a/dotnet/VdeSharp/MeshData.cs +++ /dev/null @@ -1,104 +0,0 @@ -namespace VdeSharp; - -/// -/// Mesh data structure representing a triangulated surface. -/// Contains vertices and triangle indices. -/// -public class MeshData : IDisposable -{ - /// 3D vertex positions (x,y,z triplets) - public double[] Vertices { get; private set; } - - /// Triangle indices, 3 per face - public int[] Indices { get; private set; } - - /// Axis-aligned bounding box min (x,y,z) - public (double X, double Y, double Z) BoundsMin { get; private set; } - - /// Axis-aligned bounding box max (x,y,z) - public (double X, double Y, double Z) BoundsMax { get; private set; } - - /// Native mesh handle (opaque). - internal nint _handle; - private bool _ownsHandle; - - /// - /// Wraps an existing native mesh handle. Caller sets ownership. - /// - internal MeshData(nint handle, bool ownsHandle = true) - { - _handle = handle; - _ownsHandle = ownsHandle; - Vertices = Array.Empty(); - Indices = Array.Empty(); - } - - /// - /// Creates a MeshData from raw vertex and index arrays. - /// - public MeshData(double[] vertices, int[] indices) - { - Vertices = vertices; - Indices = indices; - _handle = nint.Zero; - _ownsHandle = false; - } - - /// - /// Extracts mesh data from the native handle into managed arrays. - /// Call this after receiving a mesh from native code. - /// - public unsafe void ExtractFromNative() - { - if (_handle == nint.Zero) return; - - nuint numVerts = NativeMethods.vde_mesh_num_vertices(_handle); - nuint numFaces = NativeMethods.vde_mesh_num_faces(_handle); - - Vertices = new double[(int)numVerts * 3]; - Indices = new int[(int)numFaces * 3]; - - for (nuint i = 0; i < numVerts; i++) - { - NativeMethods.vde_mesh_get_vertex(_handle, i, - out Vertices[(int)i * 3], - out Vertices[(int)i * 3 + 1], - out Vertices[(int)i * 3 + 2]); - } - - for (nuint i = 0; i < numFaces; i++) - { - int* facePtr; - int count = NativeMethods.vde_mesh_get_face(_handle, i, &facePtr); - if (count >= 3) - { - Indices[(int)i * 3] = facePtr[0]; - Indices[(int)i * 3 + 1] = facePtr[1]; - Indices[(int)i * 3 + 2] = facePtr[2]; - } - NativeMethods.vde_free_buffer(facePtr); - } - - // Bounds - NativeMethods.vde_mesh_get_bounds(_handle, - out double mx, out double my, out double mz, - out double Mx, out double My, out double Mz); - BoundsMin = (mx, my, mz); - BoundsMax = (Mx, My, Mz); - } - - /// Number of vertices - public int VertexCount => Vertices.Length / 3; - - /// Number of triangles - public int TriangleCount => Indices.Length / 3; - - public void Dispose() - { - if (_ownsHandle && _handle != nint.Zero) - { - NativeMethods.vde_mesh_free(_handle); - _handle = nint.Zero; - } - } -} diff --git a/dotnet/VdeSharp/NativeMethods.cs b/dotnet/VdeSharp/NativeMethods.cs deleted file mode 100644 index 666b5f3..0000000 --- a/dotnet/VdeSharp/NativeMethods.cs +++ /dev/null @@ -1,190 +0,0 @@ -using System.Runtime.InteropServices; - -namespace VdeSharp; - -/// -/// P/Invoke declarations for the VDE C API (libvde_capi.so / vde_capi.dll). -/// All native handles are represented as nint (IntPtr). -/// -internal static partial class NativeMethods -{ - // ── Library name ── - private const string LibName = "vde_capi"; - - // ═══════════════════════════════════════════════════ - // Engine lifecycle - // ═══════════════════════════════════════════════════ - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_create(); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern void vde_destroy(nint ctx); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr vde_version(); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr vde_last_error(nint ctx); - - // ═══════════════════════════════════════════════════ - // Mesh I/O - // ═══════════════════════════════════════════════════ - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_load_mesh(nint ctx, string path); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern int vde_save_mesh(nint ctx, nint mesh, string path, string format); - - // ═══════════════════════════════════════════════════ - // Mesh query - // ═══════════════════════════════════════════════════ - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nuint vde_mesh_num_vertices(nint mesh); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nuint vde_mesh_num_faces(nint mesh); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern void vde_mesh_get_vertex(nint mesh, nuint idx, - out double x, out double y, out double z); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern void vde_mesh_get_bounds(nint mesh, - out double minX, out double minY, out double minZ, - out double maxX, out double maxY, out double maxZ); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern void vde_mesh_free(nint mesh); - - // ═══════════════════════════════════════════════════ - // Mesh processing - // ═══════════════════════════════════════════════════ - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_mesh_simplify(nint ctx, nint mesh, double targetRatio); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_mesh_smooth(nint ctx, nint mesh, int iterations); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_mesh_boolean(nint ctx, nint a, nint b, int op); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern unsafe int vde_mesh_get_face(nint mesh, nuint faceIdx, int** outIndices); - - // ═══════════════════════════════════════════════════ - // B-Rep modeling - // ═══════════════════════════════════════════════════ - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_body_create_box(nint ctx, double w, double h, double d); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_body_create_cylinder(nint ctx, double r, double h, int segs); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_body_create_sphere(nint ctx, double r, int su, int sv); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_body_to_mesh(nint ctx, nint body, double deflection); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern void vde_body_free(nint body); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_body_clone(nint ctx, nint body); - - // ═══════════════════════════════════════════════════ - // B-Rep STEP/IGES I/O - // ═══════════════════════════════════════════════════ - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern unsafe int vde_body_load_step(nint ctx, string path, nint** outBodies); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern unsafe int vde_body_load_iges(nint ctx, string path, nint** outBodies); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern int vde_body_save_step(nint ctx, nint[] bodies, int count, string path); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern int vde_body_save_iges(nint ctx, nint[] bodies, int count, string path); - - // ═══════════════════════════════════════════════════ - // B-Rep Boolean - // ═══════════════════════════════════════════════════ - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_body_boolean(nint ctx, nint a, nint b, int op); - - // ═══════════════════════════════════════════════════ - // Body array lifetime - // ═══════════════════════════════════════════════════ - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern unsafe void vde_body_free_array(nint* bodies, int count); - - // ═══════════════════════════════════════════════════ - // Buffer lifetime - // ═══════════════════════════════════════════════════ - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern unsafe void vde_free_buffer(void* ptr); - - // ═══════════════════════════════════════════════════ - // SDF - // ═══════════════════════════════════════════════════ - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_sdf_sphere(nint ctx, double r); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_sdf_box(nint ctx, double hx, double hy, double hz); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_sdf_cylinder(nint ctx, double r, double h); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_sdf_union(nint ctx, nint a, nint b); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_sdf_intersection(nint ctx, nint a, nint b); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_sdf_difference(nint ctx, nint a, nint b); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_sdf_to_mesh(nint ctx, nint sdf, int resolution); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern void vde_sdf_free(nint sdf); - - // ═══════════════════════════════════════════════════ - // Assembly - // ═══════════════════════════════════════════════════ - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern nint vde_assembly_create(nint ctx, string name); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern int vde_assembly_add_part(nint assembly, string name, nint body); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern int vde_assembly_part_count(nint assembly); - - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - internal static extern void vde_assembly_free(nint assembly); - - // ═══════════════════════════════════════════════════ - // Helpers - // ═══════════════════════════════════════════════════ - - internal static string PtrToStringUTF8(IntPtr ptr) - { - if (ptr == IntPtr.Zero) return string.Empty; - return Marshal.PtrToStringUTF8(ptr) ?? string.Empty; - } -} diff --git a/dotnet/VdeSharp/SdfEngine.cs b/dotnet/VdeSharp/SdfEngine.cs deleted file mode 100644 index a3d9c1b..0000000 --- a/dotnet/VdeSharp/SdfEngine.cs +++ /dev/null @@ -1,82 +0,0 @@ -namespace VdeSharp; - -/// -/// Managed wrapper for VDE SDF (Signed Distance Function) modeling. -/// Provides an expression-tree based CSG modeling approach. -/// -public class SdfEngine : IDisposable -{ - private readonly nint _ctx; - internal nint _handle; - private bool _disposed; - - internal SdfEngine(nint ctx, nint handle) - { - _ctx = ctx; - _handle = handle; - } - - /// Create a sphere SDF node. - public static SdfEngine Sphere(nint ctx, double radius) - { - nint hdl = NativeMethods.vde_sdf_sphere(ctx, radius); - return new SdfEngine(ctx, hdl); - } - - /// Create a box SDF node with half-extents (hx, hy, hz). - public static SdfEngine Box(nint ctx, double hx, double hy, double hz) - { - nint hdl = NativeMethods.vde_sdf_box(ctx, hx, hy, hz); - return new SdfEngine(ctx, hdl); - } - - /// Create a cylinder SDF node. - public static SdfEngine Cylinder(nint ctx, double radius, double height) - { - nint hdl = NativeMethods.vde_sdf_cylinder(ctx, radius, height); - return new SdfEngine(ctx, hdl); - } - - /// CSG union: this ∪ other - public SdfEngine Union(SdfEngine other) - { - nint hdl = NativeMethods.vde_sdf_union(_ctx, _handle, other._handle); - return new SdfEngine(_ctx, hdl); - } - - /// CSG intersection: this ∩ other - public SdfEngine Intersection(SdfEngine other) - { - nint hdl = NativeMethods.vde_sdf_intersection(_ctx, _handle, other._handle); - return new SdfEngine(_ctx, hdl); - } - - /// CSG difference: this \ other - public SdfEngine Difference(SdfEngine other) - { - nint hdl = NativeMethods.vde_sdf_difference(_ctx, _handle, other._handle); - return new SdfEngine(_ctx, hdl); - } - - /// - /// Convert the SDF tree to a triangle mesh using Marching Cubes. - /// - /// Grid resolution per axis (e.g., 64 for 64³ voxels) - public MeshData ToMesh(int resolution = 64) - { - nint mh = NativeMethods.vde_sdf_to_mesh(_ctx, _handle, resolution); - var mesh = new MeshData(mh, ownsHandle: true); - mesh.ExtractFromNative(); - return mesh; - } - - public void Dispose() - { - if (!_disposed && _handle != nint.Zero) - { - NativeMethods.vde_sdf_free(_handle); - _handle = nint.Zero; - _disposed = true; - } - } -} diff --git a/dotnet/VdeSharp/VdeSharp.csproj b/dotnet/VdeSharp/VdeSharp.csproj deleted file mode 100644 index df7235c..0000000 --- a/dotnet/VdeSharp/VdeSharp.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - net8.0 - VdeSharp - enable - enable - true - - - diff --git a/dotnet/examples/Program.cs b/dotnet/examples/Program.cs deleted file mode 100644 index 7ed7111..0000000 --- a/dotnet/examples/Program.cs +++ /dev/null @@ -1,63 +0,0 @@ -using VdeSharp; - -// ═══════════════════════════════════════════════════════ -// VdeSharp Example: STEP Import → Boolean → Export -// ═══════════════════════════════════════════════════════ - -Console.WriteLine("=== VdeSharp .NET Bindings Example ===\n"); - -// 1. Create engine context -nint ctx = NativeMethods.vde_create(); -string version = NativeMethods.PtrToStringUTF8(NativeMethods.vde_version()); -Console.WriteLine($"VDE version: {version}"); - -try -{ - // 2. Create two primitives via BrepModel factory - using var box = BrepModel.CreateBox(ctx, 4.0, 2.0, 3.0); - using var sphere = BrepModel.CreateSphere(ctx, 2.0, su: 40, sv: 40); - - Console.WriteLine("Created box (4x2x3) and sphere (r=2)."); - - // 3. Perform Boolean operations - using var unionResult = box.Union(sphere); - using var diffResult = box.Diff(sphere); - - Console.WriteLine("Performed Boolean union and difference."); - - // 4. Export results to STEP - unionResult.SaveStep("/tmp/vde_union.step"); - Console.WriteLine("Exported union to /tmp/vde_union.step"); - - diffResult.SaveStep("/tmp/vde_diff.step"); - Console.WriteLine("Exported difference to /tmp/vde_diff.step"); - - // 5. Tessellate to mesh and print stats - using var mesh = diffResult.ToMesh(deflection: 0.05); - Console.WriteLine($"\nDifference mesh: {mesh.VertexCount} vertices, {mesh.TriangleCount} triangles"); - Console.WriteLine($"Bounds: ({mesh.BoundsMin.X:F2},{mesh.BoundsMin.Y:F2},{mesh.BoundsMin.Z:F2}) -> ({mesh.BoundsMax.X:F2},{mesh.BoundsMax.Y:F2},{mesh.BoundsMax.Z:F2})"); - - // 6. SDF Modeling example - using var sdfSphere = SdfEngine.Sphere(ctx, 1.5); - using var sdfBox = SdfEngine.Box(ctx, 2.0, 1.0, 1.0); - using var sdfUnion = sdfSphere.Union(sdfBox); - using var sdfMesh = sdfUnion.ToMesh(resolution: 64); - Console.WriteLine($"\nSDF union mesh: {sdfMesh.VertexCount} vertices, {sdfMesh.TriangleCount} triangles"); - - // 7. Assembly example - using var assembly = new Assembly(ctx, "MyAssembly"); - assembly.AddPart("base", box); - assembly.AddPart("sphere", sphere); - Console.WriteLine($"\nAssembly '{assembly}' contains {assembly.PartCount} parts."); - - Console.WriteLine("\n=== All examples completed successfully! ==="); -} -catch (Exception ex) -{ - Console.Error.WriteLine($"Error: {ex.Message}"); - Console.Error.WriteLine(ex.StackTrace); -} -finally -{ - NativeMethods.vde_destroy(ctx); -} diff --git a/include/vde/ai/feature_learning.h b/include/vde/ai/feature_learning.h deleted file mode 100644 index 0ea57ee..0000000 --- a/include/vde/ai/feature_learning.h +++ /dev/null @@ -1,366 +0,0 @@ -#pragma once -/** - * @file feature_learning.h - * @brief 基于 AI/ML 的特征学习:图神经网络特征分类 + 3D 形状相似度搜索 - * - * ## 主要功能 - * - * | 功能 | 说明 | - * |-----------------------|----------------------------------------------| - * | FeatureClassifier | 基于 GNN 的 B-Rep 面邻接图特征分类 | - * | SimilaritySearch | 3D 形状描述子(ESF/FPFH) + 历史模型相似度检索 | - * - * ## 使用模式 - * - * @code{.cpp} - * // 特征分类 - * vde::ai::FeatureClassifier classifier; - * classifier.load_model("models/feature_gnn.onnx"); - * auto result = classifier.classify_features(body); - * for (auto& f : result.features) { ... } - * - * // 相似度搜索 - * vde::ai::SimilaritySearch search; - * search.build_index(model_database); - * auto matches = search.query(target_body, 5); // top-5 - * @endcode - * - * @ingroup ai - */ -#include "vde/brep/brep.h" -#include "vde/core/point.h" -#include "vde/core/aabb.h" -#include "vde/mesh/halfedge_mesh.h" -#include -#include -#include -#include -#include -#include -#include - -namespace vde::ai { - -using core::Point3D; -using core::Vector3D; -using core::AABB3D; -using mesh::HalfedgeMesh; - -// ═══════════════════════════════════════════════════════════ -// 通用类型定义 -// ═══════════════════════════════════════════════════════════ - -/** @brief 特征类型枚举 */ -enum class FeatureType { - Unknown, Hole, Slot, Pocket, Boss, Fillet, Chamfer, Rib, Step, Groove -}; - -/** @brief 单个已分类特征 */ -struct ClassifiedFeature { - FeatureType type = FeatureType::Unknown; - std::vector face_ids; ///< 构成该特征的面 ID - std::string label; ///< 特征标签 - double confidence = 0.0; ///< 分类置信度 [0,1] - Point3D centroid{0,0,0}; ///< 特征形心 - Vector3D orientation{0,0,1}; ///< 特征方向 - std::unordered_map params; ///< 几何参数(半径、深度等) -}; - -/** @brief 特征分类结果 */ -struct FeatureClassificationResult { - std::vector features; - int hole_count = 0, slot_count = 0, pocket_count = 0; - int boss_count = 0, fillet_count = 0, chamfer_count = 0; - int rib_count = 0, step_count = 0, groove_count = 0; - double inference_time_ms = 0.0; - bool success = false; - std::string error_message; -}; - -// ═══════════════════════════════════════════════════════════ -// 面邻接图 (Face Adjacency Graph) — GNN 输入 -// ═══════════════════════════════════════════════════════════ - -/** @brief 面邻接图的节点(对应一个 B-Rep 面) */ -struct FAGNode { - int face_id; ///< B-Rep 面 ID - std::vector node_features; ///< 节点特征向量 - int surface_type; ///< 曲面类型(0=平面,1=圆柱,2=圆锥,3=球面,...) - double area; ///< 面面积 - Point3D centroid; ///< 面形心 - Vector3D normal; ///< 面平均法向 -}; - -/** @brief 面邻接图 */ -struct FaceAdjacencyGraph { - std::vector nodes; ///< 节点列表 - std::vector> edges;///< 边列表(face_id 对) - std::vector edge_features; ///< 边特征(凹/凸角、边长比等), 每个 edge 一个标量 -}; - -// ═══════════════════════════════════════════════════════════ -// 3D 形状描述子 -// ═══════════════════════════════════════════════════════════ - -/** @brief Ensemble of Shape Functions (ESF) 描述子 — 640维直方图 */ -struct ESFDescriptor { - static constexpr int kDim = 640; - std::array histogram{}; -}; - -/** @brief FPFH (Fast Point Feature Histograms) 描述子 — 33维 */ -struct FPFHDescriptor { - static constexpr int kDim = 33; - std::array histogram{}; -}; - -/** @brief 联合 3D 形状描述子(ESF + FPFH) */ -struct ShapeDescriptor { - ESFDescriptor esf; - FPFHDescriptor fpfh; - std::string model_id; ///< 关联的模型 ID - std::string model_path; ///< 模型文件路径 -}; - -// ═══════════════════════════════════════════════════════════ -// FeatureClassifier — 基于 GNN 的特征分类器 -// ═══════════════════════════════════════════════════════════ - -/** - * @brief 基于图神经网络的特征分类器 - * - * 从 B-Rep 模型的面邻接图出发,通过 GNN(加载预训练 ONNX 模型) - * 对每个面进行分类,再将相邻同类面聚合为高层特征。 - * - * ## 算法流水线 - * - * 1. B-Rep 模型 → FaceAdjacencyGraph(面邻接图) - * 2. 节点特征编码:曲面类型、面积、曲率、法向 - * 3. 边特征编码:二面角(凹/凸)、公共边长比 - * 4. GNN 前向传播(ONNX Runtime)→ 每个面的分类概率 - * 5. 相邻同类面聚合 → 特征实例(Hole/Slot/Pocket/...) - * 6. 几何参数估计(半径、深度、方向等) - */ -class FeatureClassifier { -public: - FeatureClassifier(); - ~FeatureClassifier(); - - // ── 模型管理 ────────────────────────────────── - - /** @brief 从文件加载预训练 ONNX 模型 */ - bool load_model(const std::string& model_path); - - /** @brief 检查模型是否已加载 */ - [[nodiscard]] bool is_model_loaded() const noexcept { return model_loaded_; } - - /** @brief 获取模型版本信息 */ - [[nodiscard]] const std::string& model_version() const noexcept { return model_version_; } - - // ── 图构建 ──────────────────────────────────── - - /** - * @brief 从 B-Rep 模型构建面邻接图 - * @param body 输入的 B-Rep 模型 - * @return 面邻接图(节点=面,边=拓扑邻接关系) - */ - [[nodiscard]] FaceAdjacencyGraph build_face_graph(const brep::BrepModel& body) const; - - // ── 特征分类 ────────────────────────────────── - - /** - * @brief 对 B-Rep 模型进行特征分类 - * @param body 输入的 B-Rep 模型 - * @return 分类结果(含特征列表和统计信息) - */ - [[nodiscard]] FeatureClassificationResult classify_features(const brep::BrepModel& body); - - // ── 特征编码(可独立使用) ───────────────────── - - /** - * @brief 计算面的节点特征向量 - * @param body B-Rep 模型 - * @param face_id 面索引 - * @return 节点特征向量(曲面类型、面积、曲率等) - */ - [[nodiscard]] std::vector encode_face_node( - const brep::BrepModel& body, int face_id) const; - - /** - * @brief 计算面邻接边的特征 - * @param body B-Rep 模型 - * @param face_id_a 面 A - * @param face_id_b 面 B - * @return 边特征标量(二面角类型:凹/凸) - */ - [[nodiscard]] double encode_edge_feature( - const brep::BrepModel& body, int face_id_a, int face_id_b) const; - - /** - * @brief 聚合相邻同类面为高层特征 - * @param graph 面邻接图 - * @param face_labels 每个面的分类标签 - * @param body B-Rep 模型 - * @return 聚合后的特征列表 - */ - [[nodiscard]] std::vector aggregate_features( - const FaceAdjacencyGraph& graph, - const std::vector>& face_labels, - const brep::BrepModel& body) const; - -private: - struct Impl; - std::unique_ptr impl_; - bool model_loaded_ = false; - std::string model_version_; -}; - -// ═══════════════════════════════════════════════════════════ -// SimilaritySearch — 3D 形状相似度搜索 -// ═══════════════════════════════════════════════════════════ - -/** @brief 单个相似度搜索结果 */ -struct SimilarityMatch { - std::string model_id; ///< 匹配的模型 ID - std::string model_path; ///< 模型文件路径 - double similarity = 0.0; ///< 相似度 [0,1],越高越相似 - double esf_distance = 0.0; ///< ESF 描述子距离 - double fpfh_distance = 0.0; ///< FPFH 描述子距离 -}; - -/** - * @brief 基于 3D 形状描述子(ESF + FPFH)的相似度搜索引擎 - * - * 构建模型数据库的索引,支持查询与目标模型最相似的历史模型。 - * - * ## 使用流程 - * - * 1. 对每个历史模型计算 ShapeDescriptor(ESF + FPFH) - * 2. build_index(descriptors) 建立搜索索引 - * 3. query(target_body, k) 找到 top-k 最相似模型 - */ -class SimilaritySearch { -public: - SimilaritySearch(); - ~SimilaritySearch(); - - // ── 描述子计算 ──────────────────────────────── - - /** - * @brief 从 B-Rep 模型计算 ESF 描述子 - * - * Ensemble of Shape Functions 使用三种形状函数 - * (A3: 三点夹角, D2: 两点距离, D3: 三点面积) 的直方图。 - * - * @param body B-Rep 模型 - * @param samples 表面采样点数 - * @return 640 维 ESF 直方图 - */ - [[nodiscard]] ESFDescriptor compute_esf(const brep::BrepModel& body, int samples = 10000) const; - - /** - * @brief 从 B-Rep 模型计算 FPFH 描述子 - * - * Fast Point Feature Histograms 使用点对之间的角度关系。 - * - * @param body B-Rep 模型 - * @param samples 表面采样点数 - * @return 33 维 FPFH 直方图 - */ - [[nodiscard]] FPFHDescriptor compute_fpfh(const brep::BrepModel& body, int samples = 5000) const; - - /** - * @brief 计算完整联合描述子 (ESF + FPFH) - * @param body B-Rep 模型 - * @param model_id 模型标识符 - * @param model_path 模型文件路径 - * @param esf_samples ESF 采样数 - * @param fpfh_samples FPFH 采样数 - * @return 联合形状描述子 - */ - [[nodiscard]] ShapeDescriptor compute_shape_descriptor( - const brep::BrepModel& body, - const std::string& model_id = "", - const std::string& model_path = "", - int esf_samples = 10000, - int fpfh_samples = 5000) const; - - // ── 索引管理 ────────────────────────────────── - - /** - * @brief 构建相似度搜索索引 - * @param descriptors 历史模型的描述子集合 - */ - void build_index(const std::vector& descriptors); - - /** - * @brief 向索引追加一个模型描述子 - * @param desc 描述子 - */ - void add_to_index(const ShapeDescriptor& desc); - - /** @brief 索引中的模型数量 */ - [[nodiscard]] size_t index_size() const noexcept; - - /** @brief 清空索引 */ - void clear_index(); - - // ── 搜索 ────────────────────────────────────── - - /** - * @brief 搜索与目标模型最相似的历史模型 - * @param body 目标 B-Rep 模型 - * @param k 返回的 top-k 数量 - * @return 按相似度降序排列的匹配结果 - */ - [[nodiscard]] std::vector query( - const brep::BrepModel& body, int k = 5) const; - - /** - * @brief 使用已有描述子搜索 - * @param desc 目标描述子 - * @param k 返回的 top-k 数量 - * @return 按相似度降序排列的匹配结果 - */ - [[nodiscard]] std::vector query_by_descriptor( - const ShapeDescriptor& desc, int k = 5) const; - - // ── 距离度量 ────────────────────────────────── - - /** @brief 计算两个 ESF 描述子之间的 L1 距离 */ - [[nodiscard]] static double esf_distance(const ESFDescriptor& a, const ESFDescriptor& b); - - /** @brief 计算两个 FPFH 描述子之间的 L2 距离 */ - [[nodiscard]] static double fpfh_distance(const FPFHDescriptor& a, const FPFHDescriptor& b); - - /** @brief 计算融合相似度 (ESF + FPFH 加权平均),返回 [0,1] */ - [[nodiscard]] static double combined_similarity( - const ShapeDescriptor& a, const ShapeDescriptor& b, - double esf_weight = 0.5); - -private: - struct Impl; - std::unique_ptr impl_; -}; - -// ═══════════════════════════════════════════════════════════ -// 辅助函数 -// ═══════════════════════════════════════════════════════════ - -/** - * @brief 在 B-Rep 模型表面上随机采样点 - * @param body B-Rep 模型 - * @param num_samples 采样点数 - * @return 表面上的随机点集合 - */ -[[nodiscard]] std::vector sample_surface_points( - const brep::BrepModel& body, int num_samples); - -/** - * @brief 从 B-Rep 模型中提取所有面的形心 - * @param body B-Rep 模型 - * @return 面的形心列表(按面 ID 索引) - */ -[[nodiscard]] std::vector face_centroids(const brep::BrepModel& body); - -} // namespace vde::ai diff --git a/include/vde/ai/generative_design.h b/include/vde/ai/generative_design.h deleted file mode 100644 index 1e3e28d..0000000 --- a/include/vde/ai/generative_design.h +++ /dev/null @@ -1,297 +0,0 @@ -#pragma once -/** - * @file generative_design.h - * @brief 生成式设计:拓扑优化 + 晶格结构生成 + GAN 3D 生成 - * - * ## 主要功能 - * - * | 功能 | 说明 | - * |-------------------------|------------------------------------------------| - * | topology_optimization | SIMP 方法密度场拓扑优化 → 等值面 → B-Rep | - * | lattice_generation | Gyroid/Diamond/BCC/FCC 晶格 + 变密度晶格 | - * | generative_adversarial_3d | 条件 GAN 3D 形状生成(载荷/约束 → 形状) | - * - * @ingroup ai - */ -#include "vde/brep/brep.h" -#include "vde/core/point.h" -#include "vde/core/aabb.h" -#include "vde/mesh/halfedge_mesh.h" -#include -#include -#include -#include - -namespace vde::ai { - -using core::Point3D; -using core::Vector3D; -using core::AABB3D; -using mesh::HalfedgeMesh; - -// ═══════════════════════════════════════════════════════════ -// 拓扑优化 (Topology Optimization) -// ═══════════════════════════════════════════════════════════ - -/** @brief 载荷条件 */ -struct LoadCondition { - Point3D position; ///< 载荷施加点 - Vector3D force; ///< 力向量 (N) - double magnitude = 0.0; ///< 力的大小 -}; - -/** @brief 固定约束 */ -struct FixedConstraint { - Point3D position; ///< 约束点/面 - bool fix_x = false; ///< 固定 X 方向 - bool fix_y = false; ///< 固定 Y 方向 - bool fix_z = false; ///< 固定 Z 方向 -}; - -/** @brief 优化约束参数 */ -struct OptimizationConstraints { - double volume_fraction = 0.3; ///< 目标体积分数 (0, 1],默认保留 30% - double penalization_power = 3.0; ///< SIMP 惩罚因子 p,典型值 3.0 - double filter_radius = 1.5; ///< 密度过滤半径(单元数) - int max_iterations = 100; ///< 最大迭代次数 - double convergence_tolerance = 0.01;///< 收敛容差 - int grid_resolution = 64; ///< 密度场网格分辨率(每轴) -}; - -/** @brief 拓扑优化结果 */ -struct TopologyOptimizationResult { - std::vector density_field; ///< 体素密度值 [0,1],线性排列 - HalfedgeMesh optimized_mesh; ///< 等值面提取的三角网格 - brep::BrepModel reconstructed_body; ///< B-Rep 重建结果(简化) - int grid_resolution = 0; ///< 实际使用的网格分辨率 - double final_volume_fraction = 0.0; ///< 最终体积分数 - int iterations = 0; ///< 实际迭代次数 - bool converged = false; ///< 是否收敛 - std::string status_message; ///< 状态信息 -}; - -/** - * @brief SIMP 方法拓扑优化 - * - * Solid Isotropic Material with Penalization (SIMP) 是最常用的 - * 连续体拓扑优化方法。对设计空间的每个体素赋予密度变量 ρ∈[0,1], - * 通过有限元分析计算柔度,迭代更新密度场。 - * - * ## 算法概要 - * - * 1. 初始化密度场为均匀 volume_fraction - * 2. 循环: - * a. 滤波密度场(密度过滤:加权平均邻域密度) - * b. 有限元分析(刚度矩阵 K(ρ) = ρ^p * K_e) - * c. 计算目标函数和灵敏度(∂c/∂ρ = -p·ρ^(p-1)·u^T·K_e·u) - * d. OC (Optimality Criteria) 更新密度 - * e. 检查收敛 - * 3. 等值面提取(Marching Cubes)→ 三角网格 - * 4. B-Rep 曲面重建 - * - * @param design_space 设计空间包围盒 - * @param loads 载荷条件列表 - * @param constraints 固定约束列表 - * @param opt_constraints 优化约束参数 - * @return 拓扑优化结果 - */ -[[nodiscard]] TopologyOptimizationResult topology_optimization( - const AABB3D& design_space, - const std::vector& loads, - const std::vector& constraints, - const OptimizationConstraints& opt_constraints = OptimizationConstraints{}); - -/** - * @brief 密度场 → 等值面提取 - * - * 使用 Marching Cubes 算法从 3D 密度场中提取等值面。 - * - * @param density_field 体素密度值 [0,1] - * @param resolution 网格分辨率 - * @param bounds 包围盒 - * @param iso_level 等值面阈值(默认 0.5) - * @return 三角网格 - */ -[[nodiscard]] HalfedgeMesh extract_isosurface( - const std::vector& density_field, - int resolution, - const AABB3D& bounds, - double iso_level = 0.5); - -/** - * @brief 三角网格 → B-Rep 重建(简化版) - * - * 将密度场等值面网格简化为 B-Rep 实体。 - * 使用面片合并 + 边界识别策略。 - * - * @param mesh 输入三角网格 - * @param simplification_angle 面片合并的角度阈值(度) - * @return B-Rep 模型 - */ -[[nodiscard]] brep::BrepModel mesh_to_brep_reconstruct( - const HalfedgeMesh& mesh, double simplification_angle = 15.0); - -// ═══════════════════════════════════════════════════════════ -// 晶格结构生成 (Lattice Generation) -// ═══════════════════════════════════════════════════════════ - -/** @brief 晶格单元类型 */ -enum class LatticeCellType { - Gyroid, ///< 三周期极小曲面 Gyroid: sin(x)cos(y)+sin(y)cos(z)+sin(z)cos(x)=0 - Diamond, ///< Diamond: sin(x)sin(y)sin(z)+sin(x)cos(y)cos(z)+cos(x)sin(y)cos(z)+cos(x)cos(y)sin(z)=0 - BCC, ///< Body-Centered Cubic: 体心立方晶格 - FCC, ///< Face-Centered Cubic: 面心立方晶格 - Octet, ///< Octet Truss: 八面体桁架晶格 - Cubic ///< 简单立方晶格 -}; - -/** @brief 晶格参数 */ -struct LatticeParams { - double cell_size = 1.0; ///< 晶格单元尺寸 - double strut_thickness = 0.15; ///< 支柱厚度(相对于 cell_size 的比例) - double min_density = 0.1; ///< 最小密度(不可见区域的填充率) - bool variable_density = false; ///< 是否启用变密度晶格 - std::vector density_map; ///< 变密度映射(驱动密度场的体素值,用于应力驱动) - int density_map_resolution = 0; ///< 密度映射网格分辨率 - bool smooth_transition = true; ///< 变密度区域间是否平滑过渡 - double surface_thickness = 0.5; ///< 表面壳层厚度(晶格仅在内部生成) -}; - -/** @brief 晶格生成结果 */ -struct LatticeGenerationResult { - HalfedgeMesh lattice_mesh; ///< 晶格三角网格 - brep::BrepModel lattice_body; ///< 晶格 B-Rep 体 - int cell_count = 0; ///< 晶格单元总数 - double porosity = 0.0; ///< 孔隙率 - bool success = false; - std::string error_message; -}; - -/** - * @brief 在 B-Rep 体内部生成晶格结构 - * - * 将输入实体作为外部轮廓,在其内部空间填充晶格结构。 - * 晶格由三周期极小曲面(TPMS)或桁架晶格定义。 - * - * @param body 外部轮廓 B-Rep 体 - * @param cell_type 晶格类型 - * @param params 晶格参数 - * @return 晶格生成结果 - */ -[[nodiscard]] LatticeGenerationResult lattice_generation( - const brep::BrepModel& body, - LatticeCellType cell_type, - const LatticeParams& params = LatticeParams{}); - -/** - * @brief 评估特定晶格在点 (x,y,z) 处的 SDF 值 - * - * @param x X 坐标 - * @param y Y 坐标 - * @param z Z 坐标 - * @param cell_type 晶格类型 - * @param cell_size 单元尺寸 - * @param strut_thickness 支柱厚度 - * @return SDF 值(内部为负,表面为 0,外部为正) - */ -[[nodiscard]] double lattice_sdf( - double x, double y, double z, - LatticeCellType cell_type, - double cell_size, - double strut_thickness); - -/** - * @brief 生成变密度晶格的密度映射 - * - * 基于应力场或用户指定的密度场,为每个晶格单元计算局部密度。 - * - * @param body B-Rep 体 - * @param loads 载荷条件(用于应力分析) - * @param cell_size 晶格单元尺寸 - * @param stress_threshold 应力阈值 - * @return (density_map, resolution) pair - */ -[[nodiscard]] std::pair, int> compute_variable_density_map( - const brep::BrepModel& body, - const std::vector& loads, - double cell_size, - double stress_threshold = 1.0); - -// ═══════════════════════════════════════════════════════════ -// GAN 3D 生成 (Generative Adversarial 3D) -// ═══════════════════════════════════════════════════════════ - -/** @brief GAN 生成条件 */ -struct GANCondition { - std::vector loads; ///< 载荷条件 - std::vector constraints; ///< 固定约束 - double target_volume = 0.0; ///< 目标体积 (0=自动) - double target_stiffness = 0.0; ///< 目标刚度 (0=自动) - std::string style_tag; ///< 风格标签(如 "aerospace", "automotive") -}; - -/** @brief GAN 生成结果 */ -struct GANGenerationResult { - HalfedgeMesh generated_mesh; ///< 生成的三角网格 - brep::BrepModel generated_body; ///< 生成的 B-Rep 体 - double generation_time_ms = 0.0; ///< 生成耗时 - double latent_z_score = 0.0; ///< 潜在空间得分 - bool success = false; - std::string error_message; - std::vector candidate_tags; ///< 可选的风格标签 -}; - -/** - * @brief 基于条件 GAN 的 3D 形状生成 - * - * 使用预训练的 cGAN 模型,根据工程条件(载荷、约束、目标体积等) - * 生成符合要求的 3D 形状。 - * - * ## 算法流程 - * - * 1. 编码条件(载荷/约束 → 条件向量 c) - * 2. 采样潜在向量 z ~ N(0, 1) - * 3. Generator(z, c) → 3D SDF 体素网格 - * 4. Marching Cubes → 三角网格 - * 5. B-Rep 重建 - * - * @param conditions 工程条件 - * @param model_path 预训练 GAN 模型路径(ONNX 格式) - * @param resolution 输出分辨率 - * @return 生成结果 - */ -[[nodiscard]] GANGenerationResult generative_adversarial_3d( - const GANCondition& conditions, - const std::string& model_path = "models/gan3d_generator.onnx", - int resolution = 64); - -/** - * @brief 编码为条件向量 - * - * 将工程条件(载荷、约束等)编码为网络可用的固定长度向量。 - * - * @param conditions 工程条件 - * @return 条件向量 - */ -[[nodiscard]] std::vector encode_conditions(const GANCondition& conditions); - -/** - * @brief 从潜在空间插值生成形状序列 - * - * 在两个潜在向量之间线性插值,生成形状过渡序列。 - * - * @param z0 起始潜在向量 - * @param z1 终止潜在向量 - * @param steps 插值步数 - * @param conditions 共享条件 - * @param model_path GAN 模型路径 - * @return 形状序列 - */ -[[nodiscard]] std::vector latent_interpolation( - const std::vector& z0, - const std::vector& z1, - int steps, - const GANCondition& conditions, - const std::string& model_path = "models/gan3d_generator.onnx"); - -} // namespace vde::ai diff --git a/include/vde/ai/inference_engine.h b/include/vde/ai/inference_engine.h deleted file mode 100644 index cd2a22a..0000000 --- a/include/vde/ai/inference_engine.h +++ /dev/null @@ -1,467 +0,0 @@ -#pragma once -/** - * @file inference_engine.h - * @brief AI 推理引擎:ONNX Runtime 集成 + TensorRT 加速 + 模型版本管理 - * - * ## 主要功能 - * - * | 功能 | 说明 | - * |-----------------------|----------------------------------------------| - * | InferenceSession | ONNX Runtime 推理会话封装 | - * | TensorRTAccelerator | TensorRT 构建/优化/推理加速 | - * | ModelRegistry | 模型版本管理与自动选择 | - * - * ## 使用模式 - * - * @code{.cpp} - * vde::ai::InferenceSession session; - * session.load_model("model.onnx"); - * auto output = session.run(input_tensor); - * - * // TensorRT 加速(需 NVIDIA GPU + CUDA) - * vde::ai::TensorRTAccelerator trt; - * trt.build_engine("model.onnx"); - * auto fast_output = trt.run(input_tensor); - * - * // 模型版本管理 - * vde::ai::ModelRegistry registry; - * registry.register_model("classifier", "v2.0", "model_v2.onnx"); - * auto best = registry.get_best_model("classifier"); // 自动选择最新兼容版本 - * @endcode - * - * @ingroup ai - */ -#include -#include -#include -#include -#include -#include -#include - -namespace vde::ai { - -// ═══════════════════════════════════════════════════════════ -// 通用张量类型 -// ═══════════════════════════════════════════════════════════ - -/** @brief 数据类型枚举 */ -enum class TensorDataType : int { - Float32 = 0, - Float64 = 1, - Int32 = 3, - Int64 = 7, -}; - -/** @brief 张量容器 */ -struct Tensor { - std::string name; ///< 张量名称(对应 ONNX 节点名) - std::vector shape; ///< 形状 (N, C, H, W, ...) - TensorDataType dtype = TensorDataType::Float32; - std::vector data; ///< 原始二进制数据 - size_t element_count = 0; ///< 元素总数 - - /** @brief 获取数据的 float* 视图 */ - [[nodiscard]] const float* data_f32() const; - /** @brief 获取数据的 float* 可写视图 */ - [[nodiscard]] float* mutable_data_f32(); - /** @brief 获取数据的 int64_t* 视图 */ - [[nodiscard]] const int64_t* data_i64() const; - /** @brief 获取字节大小 */ - [[nodiscard]] size_t byte_size() const; - - /** @brief 创建给定形状的 float32 张量 */ - static Tensor make_f32(const std::vector& shape); - /** @brief 创建给定形状的 int64 张量 */ - static Tensor make_i64(const std::vector& shape); -}; - -// ═══════════════════════════════════════════════════════════ -// Provider 类型 -// ═══════════════════════════════════════════════════════════ - -/** @brief ONNX Runtime 执行提供者 */ -enum class ExecutionProvider { - CPU, ///< CPU 执行(默认,总是可用) - CUDA, ///< CUDA GPU 加速 - TensorRT, ///< NVIDIA TensorRT - OpenVINO, ///< Intel OpenVINO - DirectML ///< Microsoft DirectML (Windows) -}; - -/** @brief 设备信息 */ -struct DeviceInfo { - ExecutionProvider provider = ExecutionProvider::CPU; - int device_id = 0; ///< 设备编号 - std::string device_name; ///< 设备名称(如 "NVIDIA GeForce RTX 4090") - int64_t memory_mb = 0; ///< 可用显存/内存 (MB) - bool available = false; ///< 是否可用 -}; - -// ═══════════════════════════════════════════════════════════ -// InferenceSession — ONNX Runtime 推理会话 -// ═══════════════════════════════════════════════════════════ - -/** @brief 会话配置 */ -struct InferenceConfig { - ExecutionProvider preferred_provider = ExecutionProvider::CPU; - int intra_op_threads = 4; ///< 算子内并行线程数 - int inter_op_threads = 2; ///< 算子间并行线程数 - bool enable_profiling = false; ///< 是否开启性能分析 - int graph_optimization_level = 2; ///< 图优化级别 (0=无, 1=基础, 2=扩展, 99=ALL) - bool enable_memory_pattern = true; ///< 是否启用内存重用模式 - int log_severity_level = 2; ///< 日志级别 (0=VERBOSE, 1=INFO, 2=WARNING, 3=ERROR, 4=FATAL) -}; - -/** @brief 推理统计信息 */ -struct InferenceStats { - double inference_time_ms = 0.0; ///< 本轮推理耗时 - double avg_inference_time_ms = 0.0; ///< 平均推理耗时 - int64_t total_inferences = 0; ///< 总推理次数 - size_t input_size_bytes = 0; ///< 输入大小 - size_t output_size_bytes = 0; ///< 输出大小 -}; - -/** - * @brief ONNX Runtime 推理会话封装 - * - * 管理 ONNX 模型加载、输入/输出绑定和推理执行。 - */ -class InferenceSession { -public: - InferenceSession(); - explicit InferenceSession(const InferenceConfig& config); - ~InferenceSession(); - - // ── 模型加载 ────────────────────────────────── - - /** - * @brief 加载 ONNX 模型 - * @param model_path 模型文件路径 (.onnx) - * @return 是否成功 - */ - bool load_model(const std::string& model_path); - - /** - * @brief 从内存加载 ONNX 模型 - * @param model_data 模型字节数据 - * @param data_size 数据大小 - * @return 是否成功 - */ - bool load_model_from_memory(const uint8_t* model_data, size_t data_size); - - /** @brief 模型是否已加载 */ - [[nodiscard]] bool is_loaded() const noexcept; - - // ── 模型信息 ────────────────────────────────── - - /** @brief 获取输入张量名称和形状 */ - [[nodiscard]] std::unordered_map> input_info() const; - - /** @brief 获取输出张量名称和形状 */ - [[nodiscard]] std::unordered_map> output_info() const; - - /** @brief 获取模型元数据(作者、版本等) */ - [[nodiscard]] std::unordered_map model_metadata() const; - - // ── 推理 ────────────────────────────────────── - - /** - * @brief 执行推理 - * @param inputs 输入张量列表 - * @return 输出张量列表 - */ - [[nodiscard]] std::vector run(const std::vector& inputs); - - /** - * @brief 执行推理(单输入单输出便捷接口) - * @param input 输入张量 - * @return 输出张量 - */ - [[nodiscard]] Tensor run_single(const Tensor& input); - - // ── 性能 ────────────────────────────────────── - - /** @brief 获取最新推理统计 */ - [[nodiscard]] const InferenceStats& stats() const noexcept { return stats_; } - - /** @brief 重置统计 */ - void reset_stats(); - - /** @brief 获取可用执行提供者列表 */ - [[nodiscard]] std::vector available_providers() const; - - // ── 配置 ────────────────────────────────────── - - /** @brief 获取当前配置 */ - [[nodiscard]] const InferenceConfig& config() const noexcept { return config_; } - -private: - struct Impl; - std::unique_ptr impl_; - InferenceConfig config_; - InferenceStats stats_; -}; - -// ═══════════════════════════════════════════════════════════ -// TensorRTAccelerator — TensorRT 加速 -// ═══════════════════════════════════════════════════════════ - -/** @brief TensorRT 引擎构建配置 */ -struct TensorRTConfig { - std::string onnx_model_path; ///< 源 ONNX 模型路径 - std::string engine_cache_path; ///< TensorRT 引擎缓存路径 - int max_batch_size = 1; ///< 最大批大小 - int max_workspace_size_mb = 4096; ///< 最大工作空间 (MB) - bool use_fp16 = false; ///< 是否使用 FP16 半精度 - bool use_int8 = false; ///< 是否使用 INT8 量化 - bool strict_type_constraints = true;///< 严格类型约束 - int device_id = 0; ///< CUDA 设备编号 -}; - -/** @brief TensorRT 加速器状态 */ -struct TensorRTStatus { - bool engine_built = false; ///< 引擎是否已构建 - bool engine_loaded = false; ///< 引擎是否已加载 - double build_time_ms = 0.0; ///< 构建耗时 - double inference_time_ms = 0.0; ///< 最近一次推理耗时 - int64_t total_inferences = 0; ///< 总推理次数 - int64_t engine_size_bytes = 0; ///< 序列化引擎大小 -}; - -/** - * @brief TensorRT 推理加速器 - * - * 将 ONNX 模型编译为 TensorRT 引擎以实现高性能推理。 - * 需要 NVIDIA GPU + CUDA + TensorRT 库。 - */ -class TensorRTAccelerator { -public: - TensorRTAccelerator(); - explicit TensorRTAccelerator(const TensorRTConfig& config); - ~TensorRTAccelerator(); - - // ── 引擎构建 ────────────────────────────────── - - /** - * @brief 从 ONNX 模型构建 TensorRT 引擎 - * @param onnx_model_path ONNX 模型路径 - * @param engine_cache_path 引擎缓存保存路径(空字符串=不缓存) - * @return 是否成功 - */ - bool build_engine(const std::string& onnx_model_path, - const std::string& engine_cache_path = ""); - - /** - * @brief 从缓存加载已编译的 TensorRT 引擎 - * @param engine_path 引擎文件路径 - * @return 是否成功 - */ - bool load_engine(const std::string& engine_path); - - // ── 推理 ────────────────────────────────────── - - /** - * @brief 执行 TensorRT 推理 - * @param inputs 输入张量 - * @return 输出张量 - */ - [[nodiscard]] std::vector run(const std::vector& inputs); - - // ── 状态 ────────────────────────────────────── - - /** @brief 检查是否可用(CUDA + TensorRT) */ - [[nodiscard]] static bool is_available() noexcept; - - /** @brief 获取状态 */ - [[nodiscard]] const TensorRTStatus& status() const noexcept { return status_; } - - /** @brief 获取配置 */ - [[nodiscard]] const TensorRTConfig& config() const noexcept { return config_; } - - // ── 性能 ────────────────────────────────────── - - /** - * @brief 预热引擎(执行若干次空推理以稳定 GPU 频率) - * @param warmup_runs 预热运行次数 - */ - void warmup(int warmup_runs = 10); - - /** - * @brief 性能基准测试 - * @param input_shape 输入张量形状 - * @param iterations 迭代次数 - * @return 平均推理耗时 (ms) - */ - [[nodiscard]] double benchmark(const std::vector& input_shape, int iterations = 100); - -private: - struct Impl; - std::unique_ptr impl_; - TensorRTConfig config_; - TensorRTStatus status_; -}; - -// ═══════════════════════════════════════════════════════════ -// ModelRegistry — 模型版本管理 -// ═══════════════════════════════════════════════════════════ - -/** @brief 模型条目 */ -struct ModelEntry { - std::string name; ///< 模型名称(如 "feature_classifier") - std::string version; ///< 语义版本号(如 "1.0.0") - std::string path; ///< 模型文件路径 - std::string checksum; ///< 文件 SHA256 校验和 - std::string framework; ///< 框架来源("onnx", "tensorrt", "custom") - std::unordered_map metadata; ///< 附加元数据 - int64_t file_size = 0; ///< 文件大小(字节) - int64_t registered_at = 0; ///< 注册时间戳 - bool is_active = true; ///< 是否活跃 -}; - -/** @brief 模型注册表配置 */ -struct ModelRegistryConfig { - std::string registry_root; ///< 模型存储根目录 - std::string default_provider; ///< 默认执行提供者 - int max_versions_per_model = 5; ///< 每个模型保留的最大版本数 - bool auto_cleanup = true; ///< 是否自动清理旧版本 -}; - -/** - * @brief AI 模型版本管理器 - * - * 管理多个模型的版本注册、查找和生命周期。 - * - * ## 功能 - * - * - 注册模型版本(名称 + 语义版本号 + 文件路径) - * - 获取最新版本、特定版本或满足约束的最佳版本 - * - 自动清理过时版本 - * - 校验和验证 - */ -class ModelRegistry { -public: - explicit ModelRegistry(const ModelRegistryConfig& config = ModelRegistryConfig{}); - ~ModelRegistry(); - - // ── 注册 ────────────────────────────────────── - - /** - * @brief 注册一个模型版本 - * @param entry 模型条目 - * @return 是否成功 - */ - bool register_model(const ModelEntry& entry); - - /** - * @brief 便捷注册 - * @param name 模型名称 - * @param version 版本号 - * @param path 文件路径 - * @param framework 框架类型 - * @param metadata 附加元数据 - * @return 是否成功 - */ - bool register_model(const std::string& name, - const std::string& version, - const std::string& path, - const std::string& framework = "onnx", - const std::unordered_map& metadata = {}); - - // ── 查询 ────────────────────────────────────── - - /** - * @brief 获取指定模型的最新活跃版本 - * @param name 模型名称 - * @return 模型条目(如不存在返回 nullopt) - */ - [[nodiscard]] std::optional get_latest(const std::string& name) const; - - /** - * @brief 获取特定版本 - * @param name 模型名称 - * @param version 版本号 - * @return 模型条目(如不存在返回 nullopt) - */ - [[nodiscard]] std::optional get_version( - const std::string& name, const std::string& version) const; - - /** - * @brief 获取满足要求的最佳版本(选择最新兼容版本) - * @param name 模型名称 - * @param min_version 最低版本要求(如 "1.2.0") - * @return 最佳兼容版本 - */ - [[nodiscard]] std::optional get_best_model( - const std::string& name, const std::string& min_version = "0.0.0") const; - - /** - * @brief 列出某模型的所有已注册版本 - * @param name 模型名称 - * @return 版本列表(按版本号降序) - */ - [[nodiscard]] std::vector list_versions(const std::string& name) const; - - /** - * @brief 列出所有已注册模型名称 - * @return 模型名称列表 - */ - [[nodiscard]] std::vector list_models() const; - - // ── 管理 ────────────────────────────────────── - - /** - * @brief 停用某个版本 - * @param name 模型名称 - * @param version 版本号 - * @return 是否成功 - */ - bool deactivate(const std::string& name, const std::string& version); - - /** - * @brief 删除某个版本 - * @param name 模型名称 - * @param version 版本号 - * @return 是否成功 - */ - bool remove(const std::string& name, const std::string& version); - - /** - * @brief 清理过时版本(保留最新的 max_versions_per_model 个) - */ - void cleanup(); - - /** - * @brief 验证所有模型文件的校验和 - * @return (valid_count, invalid_count) pair - */ - [[nodiscard]] std::pair verify_checksums() const; - - /** @brief 获取已注册模型总数 */ - [[nodiscard]] size_t total_entries() const noexcept; - - /** @brief 获取配置 */ - [[nodiscard]] const ModelRegistryConfig& config() const noexcept { return config_; } - - // ── 静态工具 ────────────────────────────────── - - /** - * @brief 比较两个语义版本号 - * @return -1 (ab) - */ - [[nodiscard]] static int compare_versions(const std::string& a, const std::string& b); - - /** - * @brief 计算文件的 SHA256 校验和 - * @param filepath 文件路径 - * @return 十六进制校验和字符串 - */ - [[nodiscard]] static std::string compute_sha256(const std::string& filepath); - -private: - struct Impl; - std::unique_ptr impl_; - ModelRegistryConfig config_; -}; - -} // namespace vde::ai diff --git a/include/vde/cloud/cloud_native.h b/include/vde/cloud/cloud_native.h deleted file mode 100644 index a35d474..0000000 --- a/include/vde/cloud/cloud_native.h +++ /dev/null @@ -1,348 +0,0 @@ -#pragma once -/// @file cloud_native.h 云原生协同设计基础设施 -/// @ingroup cloud - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace vde::cloud { - -// ═══════════════════════════════════════════════════════ -// 基础数据结构 -// ═══════════════════════════════════════════════════════ - -/// 操作类型枚举 -enum class OpType : uint8_t { - INSERT, - DELETE, - MODIFY, - MOVE, - ROTATE, - SCALE -}; - -/// 增量操作单位(DeltaSync 同步单元) -struct DeltaOp { - OpType type; - uint64_t entity_id; // 面/边/体 ID - uint64_t timestamp; // UTC 微秒时间戳 - std::vector data; // 序列化增量数据 -}; - -/// 模型变更集(一次同步的增量集合) -struct ChangeSet { - uint64_t base_version; // 基于哪个版本 - uint64_t new_version; // 新的版本号 - std::string user_id; - std::vector ops; - std::chrono::system_clock::time_point t; -}; - -// ═══════════════════════════════════════════════════════ -// OT (Operation Transform) -// ═══════════════════════════════════════════════════════ - -/** - * @brief 操作变换 —— 实时协作冲突解决引擎 - * - * 实现 OT 算法核心:对并发的操作对进行变换, - * 确保多用户编辑最终一致。 - * - * @ingroup cloud - */ -class OperationalTransform { -public: - OperationalTransform() = default; - - /// 变换 op1 使其与 op2 兼容(op1' = OT(op1, op2)) - DeltaOp transform(const DeltaOp& op1, const DeltaOp& op2) const; - - /// 组合变换:将整个 changeset 变换到另一个 changeset 之后 - ChangeSet transform_set(const ChangeSet& cs1, - const ChangeSet& cs2) const; - - /// 检查两个操作是否冲突 - [[nodiscard]] bool conflicts(const DeltaOp& a, const DeltaOp& b) const; - - /// 合并两个并发变更集 - ChangeSet merge(const ChangeSet& cs1, const ChangeSet& cs2) const; - -private: - bool same_entity(const DeltaOp& a, const DeltaOp& b) const; - DeltaOp rebase_insert(const DeltaOp& op, const DeltaOp& against) const; - DeltaOp rebase_delete(const DeltaOp& op, const DeltaOp& against) const; - DeltaOp rebase_modify(const DeltaOp& op, const DeltaOp& against) const; -}; - -// ═══════════════════════════════════════════════════════ -// DeltaSync -// ═══════════════════════════════════════════════════════ - -/** - * @brief 增量同步引擎 - * - * 仅同步被修改的面/边/体,而非完整模型。 - * 基于版本链的变更日志实现高效增量传输。 - * - * @ingroup cloud - */ -class DeltaSync { -public: - DeltaSync() = default; - - /// 生成相对于 base_version 的增量 - ChangeSet diff(uint64_t base_version) const; - - /// 应用远程增量 - bool apply_remote(const ChangeSet& cs); - - /// 提交本地变更 - void commit_local(const std::vector& ops, - const std::string& user_id); - - /// 获取当前版本号 - [[nodiscard]] uint64_t version() const { return current_version_; } - - /// 获取所有已追踪的 entity_id - [[nodiscard]] std::vector dirty_entities() const; - - /// 检查指定实体是否已修改 - [[nodiscard]] bool is_dirty(uint64_t entity_id) const; - - /// 回滚到指定版本 - bool rollback(uint64_t target_version); - -private: - uint64_t current_version_ = 0; - std::vector change_log_; - std::set dirty_set_; - std::map entity_version_; // entity_id → last changed version - mutable std::shared_mutex mutex_; -}; - -// ═══════════════════════════════════════════════════════ -// CloudSession -// ═══════════════════════════════════════════════════════ - -/// 用户信息 -struct UserInfo { - std::string user_id; - std::string display_name; - uint32_t color = 0xFF0000; // 协同光标颜色 - bool online = false; -}; - -/** - * @brief 多用户协作会话 - * - * 管理多个用户的实时协同编辑会话。 - * 集成 OT 冲突解决与 DeltaSync 增量同步。 - * - * @ingroup cloud - */ -class CloudSession { -public: - explicit CloudSession(std::string session_id); - ~CloudSession(); - - /// 加入会话 - bool join(const UserInfo& user); - - /// 离开会话 - bool leave(const std::string& user_id); - - /// 提交操作(经 OT 变换后合并) - bool submit_operation(const std::string& user_id, - const std::vector& ops); - - /// 拉取当前用户未接收的变更 - ChangeSet pull_changes(const std::string& user_id); - - /// 获取会话基本信息 - [[nodiscard]] const std::string& session_id() const { return session_id_; } - - /// 在线用户列表 - [[nodiscard]] std::vector online_users() const; - - /// 当前模型版本 - [[nodiscard]] uint64_t version() const; - - /// 获取用户的版本游标(该用户已拉到哪个版本) - [[nodiscard]] uint64_t user_cursor(const std::string& user_id) const; - -private: - std::string session_id_; - OperationalTransform ot_; - DeltaSync sync_; - std::map users_; // user_id → UserInfo - std::map user_cursor_; // user_id → 已拉取版本 - std::map pending_seq_; // user_id → 待合并 seq - mutable std::shared_mutex mutex_; -}; - -// ═══════════════════════════════════════════════════════ -// Serverless Function (AWS Lambda / Cloud Functions) -// ═══════════════════════════════════════════════════════ - -/// 函数调用请求 -struct FunctionRequest { - std::string function_name; - std::vector payload; - std::map headers; - std::chrono::milliseconds timeout{30000}; -}; - -/// 函数调用响应 -struct FunctionResponse { - int status_code = 200; - std::vector body; - std::map headers; - std::chrono::milliseconds duration{0}; - bool ok = false; -}; - -/** - * @brief Serverless 函数部署与调用接口 - * - * 支持 AWS Lambda / Google Cloud Functions 等无服务器平台。 - * 用于部署设计验证规则、模型转换管道等无状态函数。 - * - * @ingroup cloud - */ -class ServerlessFunction { -public: - ServerlessFunction() = default; - virtual ~ServerlessFunction() = default; - - /// 调用远程函数 - virtual FunctionResponse invoke(const FunctionRequest& req); - - /// 异步调用(fire-and-forget) - void invoke_async(const FunctionRequest& req); - - /// 部署函数到云端 - virtual bool deploy(const std::string& function_name, - const std::string& code_path, - const std::string& runtime = "provided.al2023"); - - /// 更新函数配置 - virtual bool update_config(const std::string& function_name, - int memory_mb = 256, - std::chrono::seconds timeout = std::chrono::seconds(30)); - - /// 列出已部署的函数 - virtual std::vector list_functions() const; - - /// 删除函数 - virtual bool remove_function(const std::string& function_name); - - /// 设置云端端点 - void set_endpoint(const std::string& url) { endpoint_ = url; } - -protected: - std::string endpoint_; - std::string region_ = "us-east-1"; - std::string credentials_path_; -}; - -// ═══════════════════════════════════════════════════════ -// ObjectStorage (S3 / MinIO) -// ═══════════════════════════════════════════════════════ - -/// 对象元信息 -struct ObjectMeta { - std::string key; - uint64_t size_bytes = 0; - std::string content_type; - std::string etag; - std::chrono::system_clock::time_point last_modified; -}; - -/** - * @brief 对象存储 —— S3/MinIO 模型仓库 - * - * 管理 3D 模型文件(STEP/IGES/glTF/OBJ 等)的云端存储。 - * 支持分片上传、断点续传、版本管理和预签名 URL。 - * - * @ingroup cloud - */ -class ObjectStorage { -public: - ObjectStorage() = default; - virtual ~ObjectStorage() = default; - - /// 配置存储后端 - void configure(const std::string& endpoint, - const std::string& bucket, - const std::string& access_key, - const std::string& secret_key); - - /// 上传对象 - virtual bool put_object(const std::string& key, - const std::vector& data, - const std::string& content_type = "application/octet-stream"); - - /// 分片上传(大文件自动分片) - bool multipart_upload(const std::string& key, - const std::vector& data, - size_t part_size_mb = 5); - - /// 下载对象 - virtual std::optional> get_object(const std::string& key); - - /// 检查对象是否存在 - virtual bool object_exists(const std::string& key) const; - - /// 列出前缀下的对象 - virtual std::vector list_objects(const std::string& prefix = "", - int max_keys = 1000) const; - - /// 删除对象 - virtual bool delete_object(const std::string& key); - - /// 生成预签名下载 URL - virtual std::string presigned_get_url(const std::string& key, - std::chrono::seconds expires); - - /// 生成预签名上传 URL - virtual std::string presigned_put_url(const std::string& key, - std::chrono::seconds expires); - - /// 复制对象 - bool copy_object(const std::string& src_key, const std::string& dst_key); - - /// 获取对象元信息 - virtual std::optional head_object(const std::string& key); - - /// 模型文件快捷存储(.stp / .igs / .gltf 等) - bool store_model(const std::string& model_name, - const std::vector& model_data, - const std::string& format); - - /// 模型文件快捷下载 - std::optional> load_model(const std::string& model_name, - const std::string& format); - - /// 删除模型 - bool delete_model(const std::string& model_name, - const std::string& format); - -protected: - std::string endpoint_; - std::string bucket_; - std::string access_key_; - std::string secret_key_; - std::string region_ = "us-east-1"; - bool use_ssl_ = true; -}; - -} // namespace vde::cloud diff --git a/include/vde/digital_twin/dt_engine.h b/include/vde/digital_twin/dt_engine.h deleted file mode 100644 index b229fdb..0000000 --- a/include/vde/digital_twin/dt_engine.h +++ /dev/null @@ -1,376 +0,0 @@ -#pragma once -/// @file dt_engine.h 数字孪生引擎 -/// @ingroup digital_twin - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace vde::dt { - -// ═══════════════════════════════════════════════════════ -// 基础类型 -// ═══════════════════════════════════════════════════════ - -/// 传感器读数 -struct SensorReading { - std::string sensor_id; - std::string sensor_type; // "temperature", "vibration", "pressure", "position" ... - double value = 0.0; - std::string unit; - std::chrono::system_clock::time_point timestamp; - double quality = 1.0; // 0.0 ~ 1.0 数据质量 -}; - -/// 传感器定义 -struct SensorDef { - std::string id; - std::string type; - std::string unit; - std::string location; // 在物理资产上的位置描述 - double range_min = 0.0; - double range_max = 100.0; - double accuracy = 0.01; // 精度 - int sample_rate_hz = 1; -}; - -/// 资产状态快照 -struct AssetState { - std::string asset_id; - std::map sensor_values; // sensor_id → value - std::chrono::system_clock::time_point timestamp; - uint64_t version = 0; -}; - -// ═══════════════════════════════════════════════════════ -// DigitalTwin —— 物理资产数字映射 -// ═══════════════════════════════════════════════════════ - -/** - * @brief 数字孪生 —— 物理资产数字映射 - * - * 将物理设备的几何模型、材料属性、运行状态 - * 同步到数字空间,实现虚实映射。 - * - * @ingroup digital_twin - */ -class DigitalTwin { -public: - explicit DigitalTwin(std::string asset_id); - ~DigitalTwin(); - - /// 配置数字孪生 - void configure(const std::string& model_path, // 3D 模型路径 - const std::string& material_db = ""); - - /// 注册传感器 - void register_sensor(const SensorDef& sensor); - - /// 获取传感器列表 - [[nodiscard]] std::vector sensors() const; - - /// 更新传感器读数(来自 IoT) - void update_reading(const SensorReading& reading); - - /// 批量更新传感器读数 - void update_readings(const std::vector& readings); - - /// 获取最新读数 - [[nodiscard]] std::optional latest_reading(const std::string& sensor_id) const; - - /// 获取当前资产状态 - [[nodiscard]] AssetState current_state() const; - - /// 获取历史读数(时间窗口内) - [[nodiscard]] std::vector history(const std::string& sensor_id, - std::chrono::minutes window) const; - - /// 资产 ID - [[nodiscard]] const std::string& asset_id() const { return asset_id_; } - - /// 状态版本号 - [[nodiscard]] uint64_t version() const { return version_; } - - /// 设置可视化状态(颜色/透明度等) - void set_visual_state(const std::string& key, double value); - - /// 获取可视化状态 - [[nodiscard]] std::optional visual_state(const std::string& key) const; - -private: - std::string asset_id_; - std::string model_path_; - std::map sensor_defs_; - std::map latest_readings_; - std::map> history_; - std::map visual_states_; - uint64_t version_ = 0; - size_t max_history_per_sensor_ = 10000; - mutable std::shared_mutex mutex_; -}; - -// ═══════════════════════════════════════════════════════ -// IoT Sensor 数据接入 (MQTT / OPC-UA) -// ═══════════════════════════════════════════════════════ - -/// IoT 协议类型 -enum class IoTProtocol { - MQTT, - OPC_UA, - MODBUS, - HTTP_REST, - COAP -}; - -/// IoT 连接配置 -struct IoTConfig { - IoTProtocol protocol = IoTProtocol::MQTT; - std::string broker_url; // MQTT broker: tcp://192.168.1.100:1883 - std::string opcua_endpoint; // OPC-UA: opc.tcp://192.168.1.100:4840 - std::string client_id; - std::string username; - std::string password; - int qos = 1; - int keepalive_sec = 60; - bool use_tls = false; -}; - -/** - * @brief IoT 传感器数据接入层 - * - * 支持 MQTT / OPC-UA / Modbus 等工业物联网协议。 - * 从传感器网关采集实时数据并推送到 DigitalTwin。 - * - * @ingroup digital_twin - */ -class IoTConnector { -public: - IoTConnector() = default; - ~IoTConnector(); - - /// 配置连接 - bool configure(const IoTConfig& config); - - /// 连接到 IoT broker / server - bool connect(); - - /// 断开连接 - void disconnect(); - - /// 是否已连接 - [[nodiscard]] bool is_connected() const { return connected_; } - - /// 订阅主题(MQTT)/ 节点(OPC-UA) - bool subscribe(const std::string& topic_or_node); - - /// 取消订阅 - bool unsubscribe(const std::string& topic_or_node); - - /// 发布消息(MQTT) - bool publish(const std::string& topic, const std::vector& payload); - - /// 读取 OPC-UA 节点值 - std::optional read_opcua_node(const std::string& node_id); - - /// 写入 OPC-UA 节点值 - bool write_opcua_node(const std::string& node_id, double value); - - /// 设置数据回调(推送模式) - using DataCallback = std::function; - void on_data(DataCallback callback); - - /// 轮询拉取最新读数 - std::vector poll(); - -private: - IoTConfig config_; - bool connected_ = false; - DataCallback data_callback_; - - // Buffer for incoming readings - std::vector buffer_; - std::mutex buffer_mutex_; -}; - -// ═══════════════════════════════════════════════════════ -// RealTimeSync —— 实时状态同步 -// ═══════════════════════════════════════════════════════ - -/// 同步方向 -enum class SyncDirection { - PHYSICAL_TO_DIGITAL, // 物理→数字(传感器→模型) - DIGITAL_TO_PHYSICAL, // 数字→物理(控制指令) - BIDIRECTIONAL // 双向 -}; - -/// 同步策略 -enum class SyncStrategy { - TIME_BASED, // 按时间间隔 - EVENT_BASED, // 事件驱动(值变化超阈值) - HYBRID // 混合 -}; - -/// 同步配置 -struct SyncConfig { - SyncDirection direction = SyncDirection::PHYSICAL_TO_DIGITAL; - SyncStrategy strategy = SyncStrategy::TIME_BASED; - std::chrono::milliseconds interval{100}; // 时间间隔(TIME_BASED) - double change_threshold = 0.01; // 变化阈值(EVENT_BASED) - bool compress = true; // 是否压缩传输 - bool batch = true; // 是否批量同步 - size_t batch_size = 50; // 批量大小 -}; - -/** - * @brief 实时同步引擎 - * - * 维护物理资产与数字孪生之间的实时状态同步。 - * 支持时间驱动和事件驱动两种同步策略。 - * - * @ingroup digital_twin - */ -class RealTimeSync { -public: - RealTimeSync() = default; - - /// 绑定数字孪生和 IoT 连接器 - void bind(DigitalTwin* dt, IoTConnector* iot); - - /// 启动同步 - bool start(const SyncConfig& config); - - /// 停止同步 - void stop(); - - /// 是否在运行 - [[nodiscard]] bool is_running() const { return running_; } - - /// 手动触发一次同步 - void sync_once(); - - /// 获取同步统计 - struct Stats { - uint64_t sync_count = 0; - uint64_t bytes_synced = 0; - uint64_t errors = 0; - std::chrono::milliseconds last_sync_duration{0}; - }; - [[nodiscard]] Stats stats() const; - - /// 获取最近同步的数据点 - [[nodiscard]] std::vector last_sync_data() const; - -private: - DigitalTwin* dt_ = nullptr; - IoTConnector* iot_ = nullptr; - SyncConfig config_; - bool running_ = false; - Stats stats_; - - std::vector last_sync_data_; - mutable std::mutex mutex_; - - void time_based_loop(); - void event_based_check(); -}; - -// ═══════════════════════════════════════════════════════ -// PredictiveMaintenance —— 预测性维护 -// ═══════════════════════════════════════════════════════ - -/// 预测结果 -struct MaintenancePrediction { - std::string component_id; - double failure_probability; // 0.0 ~ 1.0 - std::chrono::system_clock::time_point estimated_failure_time; - std::chrono::hours remaining_useful_life; - std::string recommendation; - int severity; // 1-5 -}; - -/// 时序窗口配置 -struct TimeWindow { - std::chrono::hours lookback{720}; // 回溯窗口(默认 30 天) - std::chrono::hours forecast_horizon{168}; // 预测窗口(默认 7 天) - int min_data_points = 100; - double anomaly_threshold = 3.0; // 异常检测 Z-score 阈值 -}; - -/** - * @brief 预测性维护引擎 - * - * 基于时序数据(振动、温度、压力等)进行故障预测。 - * 提供 RUL(剩余使用寿命)估算和维护建议。 - * - * @ingroup digital_twin - */ -class PredictiveMaintenance { -public: - PredictiveMaintenance() = default; - - /// 添加历史传感器数据 - void add_readings(const std::string& sensor_id, - const std::vector& readings); - - /// 训练模型 - bool train(const std::string& sensor_id, - const TimeWindow& window = TimeWindow{}); - - /// 预测故障概率和 RUL - MaintenancePrediction predict(const std::string& component_id, - const std::string& sensor_id); - - /// 批量预测所有组件 - std::vector predict_all(); - - /// 设置退化阈值 - void set_degradation_threshold(const std::string& sensor_id, - double threshold); - - /// 计算趋势(线性回归斜率) - [[nodiscard]] double compute_trend(const std::string& sensor_id, - std::chrono::hours lookback) const; - - /// 计算移动平均 - [[nodiscard]] std::vector moving_average(const std::string& sensor_id, - size_t window_size) const; - - /// 异常检测 - [[nodiscard]] std::vector - detect_anomalies(const std::string& sensor_id, - const TimeWindow& window) const; - - /// 获取传感器数据统计 - struct SensorStats { - double mean = 0.0; - double stddev = 0.0; - double min_val = 0.0; - double max_val = 0.0; - size_t count = 0; - }; - [[nodiscard]] SensorStats sensor_statistics(const std::string& sensor_id) const; - -private: - std::map> data_; - std::map degradation_thresholds_; - std::map trained_; - mutable std::shared_mutex mutex_; - - // 时序预测内部方法 - double compute_slope(const std::vector& values) const; - double compute_rul(double current_value, double degradation_rate, - double failure_threshold) const; - std::vector extract_values(const std::string& sensor_id, - std::chrono::hours lookback) const; - bool check_degradation(const std::string& sensor_id) const; -}; - -} // namespace vde::dt diff --git a/include/vde/distributed/cluster_engine.h b/include/vde/distributed/cluster_engine.h deleted file mode 100644 index d6af42e..0000000 --- a/include/vde/distributed/cluster_engine.h +++ /dev/null @@ -1,543 +0,0 @@ -#pragma once -/** - * @file cluster_engine.h - * @brief 分布式计算引擎 — 集群管理、任务调度、分布式几何运算 - * - * ## 架构概览 - * - * ``` - * ClusterManager — 节点发现 / 心跳 / 负载均衡 - * ├─ ClusterNode — 节点信息 (host:port, cores, memory, gpu) - * ├─ register_node() — 注册节点到集群 - * ├─ heartbeat() — 节点心跳维护 - * └─ select_node() — 基于负载的节点选择 - * - * TaskScheduler — DAG 任务依赖 + 跨节点调度 - * ├─ Task (id, deps, fn, affinity) - * ├─ schedule() — 拓扑排序调度 - * └─ execute() — 并行执行 - * - * Distributed Operations — 分布式几何计算 - * ├─ distributed_boolean() — 分布式布尔运算(面片分发 + 归约合并) - * ├─ distributed_marching_cubes()— 分布式 MC(体素分区) - * └─ distributed_ray_tracing() — 分布式光线追踪(分块渲染) - * - * Message Serialization — 网络消息编解码(protobuf 风格二进制) - * ``` - * - * @ingroup distributed - */ - -#include "vde/core/point.h" -#include "vde/core/aabb.h" -#include "vde/mesh/halfedge_mesh.h" -#include "vde/mesh/marching_cubes.h" -#include "vde/brep/brep.h" -#include "vde/collision/ray_intersect.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace vde::distributed { - -using core::AABB3D; -using core::Point3D; -using core::Vector3D; -using mesh::HalfedgeMesh; -using mesh::MCMesh; -using brep::BrepModel; -using collision::Ray3Dd; - -// ══════════════════════════════════════════════════════════════════ -// ClusterNode — 集群节点信息 -// ══════════════════════════════════════════════════════════════════ - -/** - * @brief GPU 信息描述 - */ -struct GpuInfo { - std::string name; ///< GPU 名称(如 "NVIDIA A100") - int64_t memory_mb = 0; ///< 显存大小 (MB) - int compute_capability = 0; ///< 计算能力(如 80 表示 SM 8.0) -}; - -/** - * @brief 集群节点 — 描述参与分布式计算的单个节点 - * - * 包含节点的网络地址、硬件资源、当前负载和存活状态。 - */ -struct ClusterNode { - std::string node_id; ///< 全局唯一节点 ID(UUID) - std::string host; ///< 主机名或 IP 地址 - int port = 0; ///< 服务端口 - int cores = 0; ///< CPU 核心数 - int64_t memory_mb = 0; ///< 总内存 (MB) - int64_t memory_used_mb = 0; ///< 当前已用内存 (MB) - std::vector gpus; ///< GPU 设备列表 - double cpu_load = 0.0; ///< 当前 CPU 负载 (0.0–1.0) - int active_tasks = 0; ///< 当前活跃任务数 - bool alive = true; ///< 节点是否存活 - int64_t last_heartbeat = 0; ///< 上次心跳时间戳 (ms) - - /** 计算可用容量评分(越高越空闲,用于负载均衡) */ - [[nodiscard]] double capacity_score() const noexcept { - if (!alive) return -1.0; - double cpu_score = (1.0 - cpu_load) * cores; - double mem_score = static_cast(memory_mb - memory_used_mb) / (1024.0 * 1024.0); - double gpu_score = 0.0; - for (const auto& g : gpus) gpu_score += g.memory_mb / 1024.0; - return cpu_score + mem_score * 0.5 + gpu_score * 2.0; - } -}; - -// ══════════════════════════════════════════════════════════════════ -// Message Serialization(Protobuf 风格二进制序列化) -// ══════════════════════════════════════════════════════════════════ - -/** - * @brief 消息类型枚举 - */ -enum class MessageType : uint8_t { - HEARTBEAT = 0x01, - REGISTER_NODE = 0x02, - TASK_ASSIGN = 0x03, - TASK_RESULT = 0x04, - MESH_TRANSFER = 0x05, - SDF_TRANSFER = 0x06, - HEALTH_CHECK = 0x07, - ACK = 0x08, - ERROR = 0xFF -}; - -/** - * @brief 序列化缓冲区 — 可变长二进制缓冲区 - * - * 提供类似 protobuf 的 varint 编解码和结构化写入/读取。 - */ -class SerializedMessage { -public: - SerializedMessage() = default; - - /** 获取原始字节缓冲区 */ - [[nodiscard]] const uint8_t* data() const noexcept { return buf_.data(); } - [[nodiscard]] size_t size() const noexcept { return buf_.size(); } - - // ── 写入 ── - void write_byte(uint8_t v); - void write_int32(int32_t v); - void write_int64(int64_t v); - void write_float64(double v); - void write_string(const std::string& s); - void write_bytes(const uint8_t* data, size_t len); - void write_message_type(MessageType t); - - /** 写入变长整数 (varint) */ - void write_varint(uint64_t v); - - // ── 读取 ── - uint8_t read_byte(); - int32_t read_int32(); - int64_t read_int64(); - double read_float64(); - std::string read_string(); - std::vector read_bytes(size_t len); - MessageType read_message_type(); - uint64_t read_varint(); - - /** 重置读取位置 */ - void reset_read() noexcept { read_pos_ = 0; } - /** 清空缓冲区 */ - void clear() noexcept { buf_.clear(); read_pos_ = 0; } - [[nodiscard]] bool eof() const noexcept { return read_pos_ >= buf_.size(); } - -private: - std::vector buf_; - size_t read_pos_ = 0; -}; - -/** - * @brief 序列化 Mesh 为二进制消息 - */ -void serialize_mesh(const HalfedgeMesh& mesh, SerializedMessage& msg); - -/** - * @brief 从二进制消息反序列化 Mesh - */ -HalfedgeMesh deserialize_mesh(SerializedMessage& msg); - -/** - * @brief 序列化 BrepModel 为二进制消息 - */ -void serialize_brep(const BrepModel& brep, SerializedMessage& msg); - -/** - * @brief 从二进制消息反序列化 BrepModel - */ -BrepModel deserialize_brep(SerializedMessage& msg); - -// ══════════════════════════════════════════════════════════════════ -// ClusterManager — 节点发现 / 心跳 / 负载均衡 -// ══════════════════════════════════════════════════════════════════ - -/** - * @brief 集群配置 - */ -struct ClusterConfig { - int heartbeat_interval_ms = 2000; ///< 心跳间隔 (ms) - int heartbeat_timeout_ms = 10000; ///< 心跳超时 (ms),超过则标记为离线 - int max_retry_connect = 3; ///< 最大重连次数 - int retry_backoff_ms = 1000; ///< 重连退避 (ms) - bool enable_auto_discovery = false; ///< 是否启用自动发现 (mDNS) - std::string discovery_service = "_vde._tcp"; ///< mDNS 服务名 -}; - -/** - * @brief 集群管理器 — 管理节点生命周期与负载均衡 - * - * 职责: - * - 维护集群节点注册表 - * - 定期心跳检测,标记离线节点 - * - 基于容量评分选择最优节点 - * - 支持多种负载均衡策略 - */ -class ClusterManager { -public: - enum class Strategy { ROUND_ROBIN, LEAST_LOADED, CAPACITY_WEIGHTED, AFFINITY }; - - explicit ClusterManager(const ClusterConfig& cfg = ClusterConfig{}); - ~ClusterManager(); - - // ── 节点管理 ── - /** 注册一个新节点,返回生成的 node_id */ - std::string register_node(const std::string& host, int port, - int cores, int64_t memory_mb, - const std::vector& gpus = {}); - - /** 注销节点 */ - void unregister_node(const std::string& node_id); - - /** 接收心跳,更新节点负载 */ - void heartbeat(const std::string& node_id, double cpu_load, - int64_t memory_used_mb, int active_tasks); - - /** 获取所有存活节点 */ - [[nodiscard]] std::vector alive_nodes() const; - - /** 根据 node_id 查找节点 */ - [[nodiscard]] std::optional find_node(const std::string& node_id) const; - - // ── 负载均衡 ── - /** 设置负载均衡策略 */ - void set_strategy(Strategy s) noexcept { strategy_ = s; } - [[nodiscard]] Strategy strategy() const noexcept { return strategy_; } - - /** - * @brief 选择最优节点执行任务 - * @param required_memory_mb 任务所需内存 (MB),0 表示不限制 - * @param prefer_gpu 是否偏好 GPU 节点 - * @param affinity_tag 亲和标签(空字符串表示无偏好) - * @return 选中的节点,若没有可用节点返回 nullopt - */ - [[nodiscard]] std::optional select_node( - int64_t required_memory_mb = 0, - bool prefer_gpu = false, - const std::string& affinity_tag = "") const; - - /** 获取集群节点总数(含离线) */ - [[nodiscard]] size_t node_count() const; - - /** 启动心跳检测后台线程 */ - void start_heartbeat_monitor(); - /** 停止心跳检测 */ - void stop_heartbeat_monitor(); - -private: - void check_timeouts(); - [[nodiscard]] std::optional select_round_robin() const; - [[nodiscard]] std::optional select_least_loaded() const; - [[nodiscard]] std::optional select_capacity_weighted() const; - - mutable std::mutex mutex_; - std::unordered_map nodes_; - Strategy strategy_ = Strategy::LEAST_LOADED; - ClusterConfig config_; - std::atomic running_{false}; - std::thread heartbeat_thread_; - mutable size_t rr_counter_ = 0; -}; - -// ══════════════════════════════════════════════════════════════════ -// TaskScheduler — DAG 任务依赖 + 跨节点调度 -// ══════════════════════════════════════════════════════════════════ - -/** - * @brief 任务状态 - */ -enum class TaskStatus : uint8_t { - PENDING, ///< 等待依赖完成 - READY, ///< 依赖已满足,等待调度 - RUNNING, ///< 正在执行 - COMPLETED, ///< 执行成功 - FAILED ///< 执行失败 -}; - -/** - * @brief 调度任务 — DAG 中的节点 - */ -struct Task { - int64_t task_id; ///< 唯一任务 ID - std::string name; ///< 任务名称(用于日志) - std::vector depends_on; ///< 依赖的任务 ID 列表 - std::string target_node_id; ///< 目标节点(空 = 自动分配) - int64_t estimated_memory_mb = 0; ///< 预估内存占用 (MB) - bool requires_gpu = false; ///< 是否需要 GPU - std::string affinity_tag; ///< 亲和标签 - TaskStatus status = TaskStatus::PENDING; ///< 当前状态 - std::function work; ///< 任务执行体 - std::string error_message; ///< 失败时的错误信息 - - /** 辅助:任务开始时间 (epoch ms) */ - int64_t started_at = 0; - /** 辅助:任务完成时间 (epoch ms) */ - int64_t finished_at = 0; -}; - -/** - * @brief DAG 任务调度器 - * - * 管理有向无环图(DAG)中的任务依赖关系,按拓扑序调度执行。 - * 支持跨节点分配任务到远程节点。 - * - * 使用方式: - * @code{.cpp} - * TaskScheduler scheduler(cluster); - * scheduler.add_task({.task_id=1, .name="step1", .work=[]{ ... }}); - * scheduler.add_task({.task_id=2, .name="step2", .depends_on={1}, .work=[]{ ... }}); - * scheduler.execute_all(); - * @endcode - */ -class TaskScheduler { -public: - explicit TaskScheduler(std::shared_ptr cluster); - ~TaskScheduler(); - - // ── 任务管理 ── - /** - * @brief 添加任务到 DAG - * @param task 任务定义(内部会拷贝) - * @return task_id - */ - int64_t add_task(const Task& task); - - /** - * @brief 批量添加任务 - */ - void add_tasks(const std::vector& tasks); - - /** 获取任务当前状态 */ - [[nodiscard]] TaskStatus get_status(int64_t task_id) const; - - /** 获取所有任务 */ - [[nodiscard]] std::vector all_tasks() const; - - // ── 执行 ── - /** - * @brief 执行 DAG 中所有任务(阻塞直到全部完成或失败) - * - * 1. 拓扑排序,确定执行顺序 - * 2. 对每层 Ready 任务并行提交 - * 3. 等待该层全部完成,再推进下一层 - * - * @param max_parallel 每层最大并行数(0 = 不限) - * @return true 全部成功, false 有任务失败 - */ - bool execute_all(int max_parallel = 0); - - /** - * @brief 提交单个任务到远程节点(异步) - * @return true 提交成功 - */ - bool dispatch_to_node(int64_t task_id, const std::string& node_id); - - /** 取消所有未开始的任务 */ - void cancel_all(); - - /** 获取执行统计 */ - struct Stats { int completed = 0; int failed = 0; int64_t elapsed_ms = 0; }; - [[nodiscard]] Stats stats() const noexcept { return stats_; } - -private: - /** 拓扑排序:返回按层级分组的任务 ID */ - [[nodiscard]] std::vector> topological_sort() const; - - /** 标记任务为 ready(依赖全部完成) */ - void mark_ready(); - - /** 检查 task 的所有依赖是否已完成 */ - [[nodiscard]] bool deps_completed(const Task& task) const; - - mutable std::mutex mutex_; - std::map tasks_; - std::shared_ptr cluster_; - int64_t next_task_id_ = 1; - Stats stats_; -}; - -// ══════════════════════════════════════════════════════════════════ -// Distributed Operations — 分布式几何计算 -// ══════════════════════════════════════════════════════════════════ - -/** - * @brief 分布式布尔运算结果 - */ -struct DistributedBooleanResult { - HalfedgeMesh mesh; ///< 合并后的结果网格 - int64_t elapsed_ms = 0; ///< 总耗时 (ms) - int nodes_used = 0; ///< 参与计算的节点数 - std::vector errors; ///< 各节点错误信息 -}; - -/** - * @brief 分布式布尔运算 — 将面片分发到不同节点计算后归约合并 - * - * 策略: - * 1. 将两个操作网格的面片按空间哈希分桶 - * 2. 每对面片桶分配到不同节点进行局部布尔运算 - * 3. 各节点返回局部结果 - * 4. 主节点合并所有局部结果 - * - * @param a 网格 A - * @param b 网格 B - * @param cluster 集群管理器 - * @param op_type 操作类型: 0=并集, 1=交集, 2=差集(A-B), 3=对称差 - * @return 分布式布尔运算结果 - * - * @note 无可用远程节点时退化为本地单机计算 - */ -DistributedBooleanResult distributed_boolean( - const HalfedgeMesh& a, - const HalfedgeMesh& b, - std::shared_ptr cluster, - int op_type = 0); - -/** - * @brief 分布式 Marching Cubes 配置 - */ -struct DistributedMCConfig { - int resolution = 64; ///< 每轴分辨率 - int subdiv_axis = 0; ///< 沿哪个轴分区(0=X, 1=Y, 2=Z) - int subdivisions = 1; ///< 分区数(每个分区由一个节点处理) - double iso_level = 0.0; ///< 等值面值 - int stitch_overlap = 1; ///< 分区边界重叠体素数(用于接缝缝合) -}; - -/** - * @brief 分布式 Marching Cubes 结果 - */ -struct DistributedMCResult { - MCMesh mesh; ///< 合并后的三角网格 - int64_t elapsed_ms = 0; ///< 总耗时 (ms) - int nodes_used = 0; ///< 参与计算节点数 - std::vector errors; -}; - -/** - * @brief 分布式 Marching Cubes — 将体素空间分区到多个节点 - * - * 策略: - * 1. 沿指定轴将包围盒划分为 N 个子区间(subdivisions) - * 2. 每个子区间分配给一个节点执行 marching_cubes - * 3. 各节点返回局部三角网格 - * 4. 主节点合并网格并缝合边界接缝 - * - * @param sdf 有符号距离函数 f(x,y,z) → double - * @param bounds 包围盒 - * @param config 分布式 MC 配置 - * @param cluster 集群管理器 - * @return 分布式 MC 结果 - */ -DistributedMCResult distributed_marching_cubes( - const std::function& sdf, - const AABB3D& bounds, - const DistributedMCConfig& config, - std::shared_ptr cluster); - -/** - * @brief 分布式光线追踪场景描述 - */ -struct DistributedRayTraceScene { - HalfedgeMesh mesh; ///< 场景网格 - std::vector rays; ///< 待追踪的光线列表 - int image_width = 0; ///< 图像宽度(分块用) - int image_height = 0; ///< 图像高度(分块用) - int samples_per_pixel = 1; ///< 每像素采样数 - int max_depth = 5; ///< 最大递归深度 -}; - -/** - * @brief 光线命中结果 - */ -struct RayHit { - int ray_index = -1; ///< 光线索引 - double t = 0.0; ///< 命中参数 - Point3D point; ///< 命中点坐标 - Vector3D normal; ///< 命中点法向 - int triangle_index = -1; ///< 命中的三角形索引 -}; - -/** - * @brief 分布式光线追踪结果 - */ -struct DistributedRayTraceResult { - std::vector hits; ///< 所有命中结果 - int64_t elapsed_ms = 0; ///< 总耗时 - int nodes_used = 0; ///< 参与节点数 - int total_rays = 0; ///< 总光线数 - std::vector errors; -}; - -/** - * @brief 分布式光线追踪 — 将光线分块分发到多个节点并行追踪 - * - * 策略: - * 1. 将光线列表均匀分块 - * 2. 每块分配给一个节点 - * 3. 场景网格广播到所有参与节点 - * 4. 各节点独立追踪本块光线 - * 5. 主节点收集所有命中结果 - * - * @param scene 场景描述 - * @param cluster 集群管理器 - * @return 分布式光线追踪结果 - */ -DistributedRayTraceResult distributed_ray_tracing( - const DistributedRayTraceScene& scene, - std::shared_ptr cluster); - -// ══════════════════════════════════════════════════════════════════ -// 工具函数 -// ══════════════════════════════════════════════════════════════════ - -/** - * @brief 获取当前时间戳 (ms) - */ -[[nodiscard]] int64_t now_ms() noexcept; - -/** - * @brief 生成 UUID v4 字符串 - */ -[[nodiscard]] std::string generate_uuid(); - -} // namespace vde::distributed diff --git a/include/vde/distributed/grpc_service.h b/include/vde/distributed/grpc_service.h deleted file mode 100644 index b0864f3..0000000 --- a/include/vde/distributed/grpc_service.h +++ /dev/null @@ -1,430 +0,0 @@ -#pragma once -/** - * @file grpc_service.h - * @brief gRPC 分布式服务接口 — VdeService (BrepOps, MeshOps, SdfOps) - * - * 定义基于 gRPC 的远程过程调用服务,支持: - * - BrepOps: 远程 B-Rep 操作(布尔、特征识别、修复) - * - MeshOps: 远程网格操作(简化、平滑、质量分析) - * - SdfOps: 远程 SDF 操作(求值、梯度、网格化) - * - 流式传输: 大模型分块传输(流式请求/响应) - * - 健康检查 + 自动重连 - * - TLS 加密通信 - * - * ## 服务架构 - * - * ``` - * VdeService (gRPC) - * ├─ BrepOps - * │ ├─ Boolean (请求/响应) - * │ ├─ Validate (请求/响应) - * │ └─ Heal (请求/响应) - * ├─ MeshOps - * │ ├─ Simplify (请求/响应) - * │ ├─ Smooth (请求/响应) - * │ ├─ MarchingCubes (流式响应: 大网格) - * │ └─ QualityReport (请求/响应) - * └─ SdfOps - * ├─ Evaluate (请求/响应) - * ├─ Gradient (请求/响应) - * └─ ToMesh (流式响应: 大网格) - * ``` - * - * @ingroup distributed - */ - -#include "vde/distributed/cluster_engine.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace vde::distributed { - -// ══════════════════════════════════════════════════════════════════ -// gRPC 服务定义(接口层,可与真实 gRPC 生成代码互换) -// ══════════════════════════════════════════════════════════════════ - -// ── 请求/响应类型 ───────────────────────────────────────────────── - -/** Boolean 操作请求 */ -struct BrepBooleanRequest { - std::string brep_a; ///< 序列化的 BrepModel A - std::string brep_b; ///< 序列化的 BrepModel B - int op_type = 0; ///< 0=union, 1=intersection, 2=difference(A-B), 3=sym_diff - double tolerance = 1e-6; ///< 布尔运算容差 -}; - -/** Boolean 操作响应 */ -struct BrepBooleanResponse { - bool success = false; - std::string result_brep; ///< 序列化的 BrepModel 结果 - std::string error_message; - int64_t elapsed_ms = 0; -}; - -/** B-Rep 验证请求 */ -struct BrepValidateRequest { - std::string brep_data; ///< 序列化的 BrepModel - bool check_topology = true; - bool check_geometry = true; - double tolerance = 1e-6; -}; - -/** B-Rep 验证响应 */ -struct BrepValidateResponse { - bool is_valid = false; - std::vector issues; - int num_faces = 0; - int num_edges = 0; - int num_vertices = 0; -}; - -/** B-Rep 修复请求 */ -struct BrepHealRequest { - std::string brep_data; - bool close_gaps = true; - bool fix_orientation = true; - double tolerance = 1e-5; -}; - -/** B-Rep 修复响应 */ -struct BrepHealResponse { - bool success = false; - std::string healed_brep; - int issues_fixed = 0; - std::string error_message; -}; - -/** 网格简化请求 */ -struct MeshSimplifyRequest { - std::string mesh_data; ///< 序列化的 HalfedgeMesh - int target_faces = 0; ///< 目标面数(0=自动) - double reduction_ratio = 0.5; ///< 减面比例 - bool preserve_boundaries = true; -}; - -/** 网格简化响应 */ -struct MeshSimplifyResponse { - bool success = false; - std::string simplified_mesh; - int original_faces = 0; - int result_faces = 0; - std::string error_message; -}; - -/** 网格平滑请求 */ -struct MeshSmoothRequest { - std::string mesh_data; - int iterations = 10; - double lambda = 0.5; - bool preserve_volume = true; -}; - -/** 网格平滑响应 */ -struct MeshSmoothResponse { - bool success = false; - std::string smoothed_mesh; - std::string error_message; -}; - -/** Marching Cubes 请求 */ -struct MarchingCubesRequest { - int resolution = 64; - double iso_level = 0.0; - std::array bmin = {-1, -1, -1}; - std::array bmax = {1, 1, 1}; - int chunk_index = 0; ///< 分块索引(流式传输用) - int total_chunks = 1; ///< 总分块数 -}; - -/** Marching Cubes 块响应(流式) */ -struct MarchingCubesChunk { - int chunk_index = 0; - int total_chunks = 1; - bool is_last = false; - std::vector vertex_data; ///< 顶点数据(序列化字节) - std::vector index_data; ///< 索引数据(序列化字节) - int vertex_count = 0; - int triangle_count = 0; -}; - -/** SDF 求值请求 */ -struct SdfEvaluateRequest { - std::string sdf_tree_data; ///< 序列化的 SDF 树 - std::vector points; ///< 待求值点 (x1,y1,z1, x2,y2,z2, ...) -}; - -/** SDF 求值响应 */ -struct SdfEvaluateResponse { - std::vector values; ///< 每个点的 SDF 值 - bool success = false; - std::string error_message; -}; - -/** SDF 梯度请求 */ -struct SdfGradientRequest { - std::string sdf_tree_data; - std::vector points; - double epsilon = 1e-5; ///< 有限差分步长 -}; - -/** SDF 梯度响应 */ -struct SdfGradientResponse { - std::vector gradients; ///< (gx1,gy1,gz1, gx2,gy2,gz2, ...) - bool success = false; - std::string error_message; -}; - -/** 健康检查请求 */ -struct HealthCheckRequest { - std::string node_id; - int64_t timestamp_ms = 0; -}; - -/** 健康检查响应 */ -struct HealthCheckResponse { - bool healthy = false; - std::string node_id; - int64_t server_time_ms = 0; - int active_connections = 0; - int64_t uptime_ms = 0; - double cpu_load = 0.0; - int64_t memory_used_mb = 0; -}; - -// ══════════════════════════════════════════════════════════════════ -// VdeService — gRPC 服务接口 -// ══════════════════════════════════════════════════════════════════ - -/** - * @brief TLS 配置 - */ -struct TlsConfig { - bool enabled = false; ///< 是否启用 TLS - std::string cert_chain_path; ///< 证书链文件路径 (PEM) - std::string private_key_path; ///< 私钥文件路径 (PEM) - std::string ca_cert_path; ///< CA 证书路径(双向 TLS) - bool verify_client = false; ///< 是否验证客户端证书 - std::string ciphers; ///< 自定义密码套件 -}; - -/** - * @brief gRPC 服务配置 - */ -struct GrpcServiceConfig { - std::string host = "0.0.0.0"; - int port = 50051; - int max_message_size_mb = 256; ///< 最大消息大小 (MB) - int keepalive_time_ms = 30000; ///< keepalive 间隔 - int keepalive_timeout_ms = 10000; ///< keepalive 超时 - bool enable_reflection = true; ///< 启用 gRPC 反射 - TlsConfig tls; -}; - -/** - * @brief gRPC 客户端配置 - */ -struct GrpcClientConfig { - std::string target; ///< "host:port" - int connect_timeout_ms = 5000; - int max_reconnect_attempts = 5; - int reconnect_backoff_ms = 1000; - int max_message_size_mb = 256; - TlsConfig tls; -}; - -/** - * @brief VdeService — 分布式几何计算 gRPC 服务 - * - * 提供 BrepOps、MeshOps、SdfOps 三大类远程过程调用。 - * 支持大模型流式分块传输和健康检查/自动重连。 - * - * 使用模式: - * @code{.cpp} - * // 服务端 - * VdeService server(cfg); - * server.start(); - * - * // 客户端 - * VdeServiceClient client({"localhost:50051"}); - * auto resp = client.brep_boolean(req); - * @endcode - */ -class VdeService { -public: - explicit VdeService(const GrpcServiceConfig& cfg = GrpcServiceConfig{}); - ~VdeService(); - - // ── 生命周期 ── - /** 启动 gRPC 服务(阻塞当前线程) */ - bool start(); - - /** 在后台线程启动 */ - bool start_async(); - - /** 优雅关闭 */ - void shutdown(); - - /** 服务是否正在运行 */ - [[nodiscard]] bool is_running() const noexcept; - - // ── BrepOps 处理器注册 ── - using BrepBooleanHandler = std::function; - using BrepValidateHandler = std::function; - using BrepHealHandler = std::function; - - void set_brep_boolean_handler(BrepBooleanHandler h); - void set_brep_validate_handler(BrepValidateHandler h); - void set_brep_heal_handler(BrepHealHandler h); - - // ── MeshOps 处理器注册 ── - using MeshSimplifyHandler = std::function; - using MeshSmoothHandler = std::function; - using MeshMarchingCubesHandler = std::function; - - void set_mesh_simplify_handler(MeshSimplifyHandler h); - void set_mesh_smooth_handler(MeshSmoothHandler h); - void set_mesh_marching_cubes_handler(MeshMarchingCubesHandler h); - - // ── SdfOps 处理器注册 ── - using SdfEvaluateHandler = std::function; - using SdfGradientHandler = std::function; - - void set_sdf_evaluate_handler(SdfEvaluateHandler h); - void set_sdf_gradient_handler(SdfGradientHandler h); - - // ── 健康检查 ── - [[nodiscard]] HealthCheckResponse health_check() const; - - // ── 服务端统计 ── - struct ServerStats { - int64_t total_requests = 0; - int64_t total_errors = 0; - int active_connections = 0; - int64_t uptime_ms = 0; - }; - [[nodiscard]] ServerStats server_stats() const; - -private: - struct Impl; - std::unique_ptr impl_; -}; - -// ══════════════════════════════════════════════════════════════════ -// VdeServiceClient — gRPC 客户端(自动重连) -// ══════════════════════════════════════════════════════════════════ - -/** - * @brief gRPC 客户端 — 自动重连 + 健康检查 - * - * 封装 gRPC stub,提供自动重连和超时重试机制。 - */ -class VdeServiceClient { -public: - explicit VdeServiceClient(const GrpcClientConfig& cfg); - ~VdeServiceClient(); - - // ── 连接管理 ── - /** 建立连接 */ - bool connect(); - - /** 断开连接 */ - void disconnect(); - - /** 是否已连接 */ - [[nodiscard]] bool is_connected() const noexcept; - - /** 健康检查 */ - [[nodiscard]] HealthCheckResponse health_check(); - - // ── BrepOps ── - BrepBooleanResponse brep_boolean(const BrepBooleanRequest& req); - BrepValidateResponse brep_validate(const BrepValidateRequest& req); - BrepHealResponse brep_heal(const BrepHealRequest& req); - - // ── MeshOps ── - MeshSimplifyResponse mesh_simplify(const MeshSimplifyRequest& req); - MeshSmoothResponse mesh_smooth(const MeshSmoothRequest& req); - std::vector mesh_marching_cubes(const MarchingCubesRequest& req); - - // ── SdfOps ── - SdfEvaluateResponse sdf_evaluate(const SdfEvaluateRequest& req); - SdfGradientResponse sdf_gradient(const SdfGradientRequest& req); - - // ── 流式传输:大模型分块 ── - /** - * @brief 分块上传大网格 - * - * 将大网格切分为多个块,通过多次 RPC 调用发送。 - * 客户端负责分块,服务端负责重组。 - * - * @param mesh 待上传网格 - * @param chunk_size_mb 每块大小 (MB) - * @return true 上传成功 - */ - bool upload_large_mesh(const HalfedgeMesh& mesh, size_t chunk_size_mb = 8); - - /** - * @brief 分块下载大网格 - * - * 通过多次 MarchingCubesChunk 响应重组完整网格。 - */ - HalfedgeMesh download_large_mesh(int resolution, double iso_level, - const AABB3D& bounds); - -private: - struct Impl; - std::unique_ptr impl_; -}; - -// ══════════════════════════════════════════════════════════════════ -// 连接池 — 多节点连接管理 -// ══════════════════════════════════════════════════════════════════ - -/** - * @brief gRPC 连接池 — 管理到多个节点的连接 - * - * 维护一个连接池,按需创建和回收连接。 - * 支持连接的惰性初始化和空闲回收。 - */ -class GrpcConnectionPool { -public: - struct PoolConfig { - int max_connections = 32; - int idle_timeout_ms = 60000; ///< 空闲超时 (ms),超时后回收连接 - int max_retries_per_node = 3; - }; - - explicit GrpcConnectionPool(const PoolConfig& cfg = PoolConfig{}); - ~GrpcConnectionPool(); - - /** - * @brief 获取到指定节点的客户端(若不存在则创建) - * @param host 节点地址 "host:port" - * @return 连接成功返回客户端,失败返回 nullptr - */ - std::shared_ptr get_client(const std::string& host); - - /** 释放指定节点的连接 */ - void release_client(const std::string& host); - - /** 回收所有空闲连接 */ - void reap_idle(); - - /** 获取当前活跃连接数 */ - [[nodiscard]] size_t active_count() const; - - /** 关闭所有连接 */ - void close_all(); - -private: - struct Impl; - std::unique_ptr impl_; -}; - -} // namespace vde::distributed diff --git a/include/vde/gpu/gpu_acceleration.h b/include/vde/gpu/gpu_acceleration.h deleted file mode 100644 index 457c175..0000000 --- a/include/vde/gpu/gpu_acceleration.h +++ /dev/null @@ -1,133 +0,0 @@ -#pragma once -/// @defgroup gpu GPU 加速模块 -/// CUDA 加速的 SDF 网格化、QEM 简化与布尔运算。 -/// 无 CUDA 环境自动退化为 CPU 实现,API 行为一致。 -/// @{ - -#include "vde/mesh/halfedge_mesh.h" -#include "vde/core/aabb.h" -#include -#include - -// ── CUDA 可用性宏(由 CMake 传入) ──────────────────────────── -#ifndef VDE_USE_CUDA -# define VDE_USE_CUDA 0 -#endif - -namespace vde::gpu { - -using core::AABB3D; -using core::Point3D; -using core::Vector3D; -using mesh::HalfedgeMesh; - -// ====================================================================== -// API -// ====================================================================== - -/** - * @brief 检测 GPU(CUDA)运行时是否可用 - * - * 运行时检测 CUDA 驱动 + 至少 1 个设备。 - * 编译时通过 VDE_USE_CUDA 宏控制是否链接 CUDA 库。 - * - * @return true 若 CUDA 环境就绪且编译时已开启支持 - * @return false 否则 - */ -[[nodiscard]] bool gpu_available() noexcept; - -// ── Marching Cubes ───────────────────────────────────────────────── - -/** - * @brief GPU 加速 Marching Cubes(SDF → 三角网格) - * - * 将包围盒划分为 res³ 个体素。CUDA 路径:每线程一个体素, - * 共享内存缓存相邻 SDF 值,原子计数器收集三角形。 - * 无 CUDA 时自动回退到 vde::mesh::marching_cubes() 等效实现。 - * - * @param sdf 有符号距离函数 f(x,y,z) → double - * @param bounds 包围盒(轴对齐) - * @param res 每轴分辨率,总 voxel 数 = res³(默认 64) - * @return HalfedgeMesh 三角网格,内部使用 HalfedgeMesh::build_from_triangles - */ -[[nodiscard]] HalfedgeMesh gpu_marching_cubes( - const std::function& sdf, - const AABB3D& bounds, - int res = 64); - -// ── Mesh Simplify (QEM) ──────────────────────────────────────────── - -/** - * @brief GPU 加速 QEM 网格简化 - * - * CUDA 路径: - * - 并行计算每条边的塌缩代价(quadric error) - * - 多 pass 逐步塌缩直到达到 target_ratio - * - 每 pass 内独立边集并行塌缩 - * 无 CUDA 时自动回退到 vde::mesh::simplify_mesh()。 - * - * @param mesh 输入三角网格 - * @param target_ratio 目标面数比例 (0, 1] - * @return 简化后的网格 - */ -[[nodiscard]] HalfedgeMesh gpu_mesh_simplify( - const HalfedgeMesh& mesh, - float target_ratio = 0.5f); - -// ── Boolean Intersect ────────────────────────────────────────────── - -/** - * @brief GPU 加速布尔交集运算 - * - * CUDA 路径: - * - 空间哈希网格对三角形分组 - * - GPU 并行三角形-三角形求交 - * - 输出交集区域的重构网格 - * 无 CUDA 时自动回退到 vde::mesh 布尔运算。 - * - * @param mesh_a 网格 A - * @param mesh_b 网格 B - * @return 交集网格(A ∩ B) - */ -[[nodiscard]] HalfedgeMesh gpu_boolean_intersect( - const HalfedgeMesh& mesh_a, - const HalfedgeMesh& mesh_b); - -// ====================================================================== -// CUDA 内核声明(仅在 CUDA 编译单元可见) -// ====================================================================== -#if VDE_USE_CUDA - -/// CUDA 内核:Marching Cubes 体素处理 -void launch_mc_kernel(const double* sdf_grid, int res, - const Point3D& origin, double cell_size, - int* tri_count, int* tri_indices, - double* tri_verts); - -/// CUDA 内核:QEM 边代价计算 -void launch_qem_cost_kernel(const double* vertices, int num_verts, - const int* tri_indices, int num_tris, - double* edge_costs, int* collapse_targets); - -/// CUDA 内核:三角形-三角形求交 -void launch_tri_intersect_kernel(const double* verts_a, const int* tris_a, int ntri_a, - const double* verts_b, const int* tris_b, int ntri_b, - const int* hash_bins, const int* hash_offsets, - int* intersect_flags); - -/// CUDA 错误检查宏 -#define CUDA_CHECK(call) \ - do { \ - cudaError_t err_ = call; \ - if (err_ != cudaSuccess) { \ - fprintf(stderr, "CUDA error %s:%d: %s\n", \ - __FILE__, __LINE__, cudaGetErrorString(err_)); \ - std::abort(); \ - } \ - } while (0) - -#endif // VDE_USE_CUDA - -} // namespace vde::gpu - -/** @} */ // end of gpu group diff --git a/include/vde/kbe/knowledge_engine.h b/include/vde/kbe/knowledge_engine.h deleted file mode 100644 index b90e759..0000000 --- a/include/vde/kbe/knowledge_engine.h +++ /dev/null @@ -1,344 +0,0 @@ -#pragma once -/// @file knowledge_engine.h 知识工程 —— 设计规则 & 优化 -/// @ingroup kbe - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace vde::kbe { - -// ═══════════════════════════════════════════════════════ -// 公用类型 -// ═══════════════════════════════════════════════════════ - -/// 设计参数值类型 -using ParamValue = std::variant; - -/// 参数定义 -struct Parameter { - std::string name; - ParamValue value; - ParamValue min; - ParamValue max; - std::string unit; // "mm", "deg", "kg" ... - std::string description; - bool locked = false; // 锁定参数不可优化 -}; - -// ═══════════════════════════════════════════════════════ -// CheckMate —— 设计验证规则(对标 CATIA Knowledgeware) -// ═══════════════════════════════════════════════════════ - -/// 验证结果严重级别 -enum class CheckSeverity { - INFO, - WARNING, - ERROR, - FATAL -}; - -/// 单条验证结果 -struct CheckResult { - CheckSeverity severity = CheckSeverity::INFO; - std::string rule_name; - std::string message; - std::string entity_ref; // 关联实体 ID - bool passed = true; -}; - -/// 验证规则 -struct CheckRule { - std::string name; - std::string category; // "geometry", "topology", "material", "assembly" - std::string description; - CheckSeverity default_severity = CheckSeverity::WARNING; - bool enabled = true; -}; - -/** - * @brief CheckMate —— CATIA Knowledgeware 对标设计验证引擎 - * - * 提供一套预定义的设计规则,可扩展。 - * 覆盖几何合法性、拓扑正确性、材料合规、装配约束等。 - * - * @ingroup kbe - */ -class CheckMate { -public: - CheckMate(); - - /// 注册一条新规则 - void register_rule(const CheckRule& rule); - - /// 添加自定义验证函数 - void add_check(const std::string& rule_name, - std::function&)> fn); - - /// 运行所有已启用的规则 - std::vector run_all(const std::vector& params) const; - - /// 运行指定分类的规则 - std::vector run_category(const std::string& category, - const std::vector& params) const; - - /// 运行指定规则 - CheckResult run_rule(const std::string& rule_name, - const std::vector& params) const; - - /// 启用/禁用某条规则 - void set_rule_enabled(const std::string& name, bool enabled); - - /// 获取所有规则 - [[nodiscard]] std::vector rules() const; - - /// 获取失败的检查数量(按 severity 统计) - [[nodiscard]] std::map summary(const std::vector& results) const; - -private: - std::vector rules_; - std::map&)>> checks_; - - void register_default_rules(); -}; - -// ═══════════════════════════════════════════════════════ -// RuleEngine —— IF-THEN 设计规则引擎 -// ═══════════════════════════════════════════════════════ - -/// IF-THEN 规则 -struct Rule { - std::string name; - std::string condition; // IF 条件表达式(字符串形式,如 "diameter > 10 AND length < 100") - std::string action; // THEN 动作(如 "SET thickness = 2.0") - int priority = 0; - bool enabled = true; -}; - -/// 规则触发记录 -struct RuleTrace { - std::string rule_name; - bool fired = false; - ParamValue old_value; - ParamValue new_value; -}; - -/** - * @brief 设计规则引擎 - * - * 基于 IF-THEN 模式的产品配置与设计自动化。 - * 支持规则优先级、正向链推理、循环检测。 - * - * @ingroup kbe - */ -class RuleEngine { -public: - RuleEngine() = default; - - /// 添加规则 - void add_rule(const Rule& rule); - - /// 删除规则 - bool remove_rule(const std::string& name); - - /// 设置/更新参数 - void set_param(const std::string& name, const ParamValue& value); - - /// 获取参数 - [[nodiscard]] std::optional get_param(const std::string& name) const; - - /// 触发推理:执行所有满足条件的规则,迭代直至不动点 - std::vector infer(); - - /// 检查循环依赖 - [[nodiscard]] bool has_cycles() const; - - /// 获取所有参数键名 - [[nodiscard]] std::vector param_names() const; - - /// 获取所有规则 - [[nodiscard]] std::vector rules() const; - - /// 重置所有参数 - void reset(); - -private: - std::vector rules_; - std::map params_; - std::set fired_set_; // 本轮已触发规则 - - bool evaluate_condition(const std::string& condition) const; - ParamValue evaluate_action(const std::string& action) const; - bool parse_expression(const std::string& expr, double& out) const; -}; - -// ═══════════════════════════════════════════════════════ -// DesignTable —— Excel 式设计表 -// ═══════════════════════════════════════════════════════ - -/// 设计表行(一行 = 一组参数值) -using DesignRow = std::map; - -/** - * @brief 设计表(对标 CATIA DesignTable) - * - * Excel 式参数表格:列=参数,行=配置组合。 - * 支持从 CSV/Excel 导入导出。 - * - * @ingroup kbe - */ -class DesignTable { -public: - DesignTable() = default; - - /// 定义列 - void define_columns(const std::vector& col_names, - const std::vector& units = {}); - - /// 添加一行 - void add_row(const DesignRow& row); - - /// 获取指定行 - [[nodiscard]] std::optional get_row(size_t index) const; - - /// 获取所有行 - [[nodiscard]] std::vector rows() const; - - /// 行数 - [[nodiscard]] size_t row_count() const { return rows_.size(); } - - /// 列数 - [[nodiscard]] size_t col_count() const { return columns_.size(); } - - /// 列名列表 - [[nodiscard]] std::vector column_names() const { return columns_; } - - /// 删除行 - bool remove_row(size_t index); - - /// 清空 - void clear(); - - // ── 导入导出 ── - - /// 从 CSV 字符串导入 - bool import_csv(const std::string& csv_content); - - /// 导出为 CSV 字符串 - [[nodiscard]] std::string export_csv() const; - - /// 从 xlsx 路径导入(需要调用外部解析器) - bool import_excel(const std::string& filepath); - - /// 导出为 xlsx(需要调用外部写入器) - bool export_excel(const std::string& filepath) const; - -private: - std::vector columns_; - std::vector units_; - std::vector rows_; - - ParamValue parse_value(const std::string& s) const; -}; - -// ═══════════════════════════════════════════════════════ -// ParametricOptimization —— 参数优化 -// ═══════════════════════════════════════════════════════ - -/// 优化结果 -struct OptimizationResult { - std::vector optimal_params; - double optimal_fitness = 0.0; - int iterations = 0; - bool converged = false; - std::chrono::milliseconds duration{0}; -}; - -/// 遗传算法配置 -struct GAConfig { - size_t population_size = 100; - size_t generations = 200; - double crossover_rate = 0.8; - double mutation_rate = 0.05; - size_t elitism_count = 2; - bool parallel_eval = false; -}; - -/// 梯度优化配置 -struct GradientConfig { - double learning_rate = 0.01; - double tolerance = 1e-6; - int max_iterations = 1000; - double momentum = 0.9; - bool use_adam = true; -}; - -/** - * @brief 参数优化引擎 - * - * 支持遗传算法(GA)和梯度下降两种优化策略。 - * 用于结构参数、拓扑参数、工艺参数的多目标优化。 - * - * @ingroup kbe - */ -class ParametricOptimization { -public: - ParametricOptimization() = default; - - /// 设置适应度函数 - using FitnessFn = std::function&)>; - void set_fitness(FitnessFn fn); - - /// 设置约束函数(返回 true = 满足约束) - using ConstraintFn = std::function&)>; - void add_constraint(ConstraintFn fn); - - /// 遗传算法优化 - OptimizationResult optimize_ga(const std::vector& initial, - const GAConfig& config = GAConfig{}); - - /// 梯度下降优化(连续参数) - OptimizationResult optimize_gradient(const std::vector& initial, - const GradientConfig& config = GradientConfig{}); - - /// 设置参数边界(按名称) - void set_bounds(const std::string& param_name, - const ParamValue& min_val, - const ParamValue& max_val); - - /// 获取优化历史 - [[nodiscard]] std::vector fitness_history() const { return fitness_history_; } - -private: - FitnessFn fitness_fn_; - std::vector constraints_; - std::map> bounds_; - std::vector fitness_history_; - - // GA 内部 - struct Individual { - std::vector params; - double fitness = 0.0; - }; - - Individual create_random_individual(const std::vector& template_params) const; - Individual crossover(const Individual& a, const Individual& b) const; - void mutate(Individual& ind) const; - bool satisfies_all(const std::vector& params) const; - double evaluate(const std::vector& params); - - // 梯度内部 - double numerical_gradient(const std::vector& params, - size_t param_idx, double epsilon = 1e-5) const; -}; - -} // namespace vde::kbe diff --git a/include/vde/plugin/plugin_system.h b/include/vde/plugin/plugin_system.h deleted file mode 100644 index 4cfebe7..0000000 --- a/include/vde/plugin/plugin_system.h +++ /dev/null @@ -1,396 +0,0 @@ -#pragma once -/** - * @file plugin_system.h - * @brief 插件系统 — 动态加载、生命周期管理、元数据定义 - * - * ## 设计目标 - * - 零侵入:插件独立编译,不修改核心代码 - * - 动态加载:运行时 dlopen/LoadLibrary 加载 - * - 版本兼容:Manifest 声明版本约束,自动检查 - * - 隔离性:插件崩溃不影响主程序 - * - * ## 核心组件 - * - PluginInterface: 所有插件必须实现的抽象基类 - * - PluginManifest: 插件元数据(name/version/author/type) - * - PluginManager: 加载/卸载/查询插件的管理器 - * - * ## 使用示例 - * @code - * vde::plugin::PluginManager mgr; - * mgr.load_plugin_dir("/usr/lib/vde/plugins"); - * auto* p = mgr.get_plugin("io.export.gltf"); - * p->execute("export", params, &mesh, &result); - * mgr.unload_all(); - * @endcode - * - * @ingroup plugin - * @since v6.0 - */ - -#include -#include -#include -#include -#include -#include - -// ═══════════════════════════════════════════════════════════ -// 平台宏 — 跨平台动态库导入/导出 -// ═══════════════════════════════════════════════════════════ - -#if defined(_WIN32) || defined(_WIN64) - #ifdef VDE_PLUGIN_BUILD - #define VDE_PLUGIN_EXPORT __declspec(dllexport) - #else - #define VDE_PLUGIN_EXPORT __declspec(dllimport) - #endif - #define VDE_PLUGIN_LOCAL -#else - #if __GNUC__ >= 4 - #define VDE_PLUGIN_EXPORT __attribute__((visibility("default"))) - #define VDE_PLUGIN_LOCAL __attribute__((visibility("hidden"))) - #else - #define VDE_PLUGIN_EXPORT - #define VDE_PLUGIN_LOCAL - #endif -#endif - -namespace vde::plugin { - -// ═══════════════════════════════════════════════════════════ -// PluginManifest — 插件元数据 -// ═══════════════════════════════════════════════════════════ - -/// 插件元数据,提供插件的身份信息和兼容性约束。 -/// -/// 每个插件在 initialize() 中填充此结构。PluginManager -/// 在加载时读取 manifest 检查版本兼容性。 -/// -/// @see plugin.json 清单文件格式 -struct PluginManifest { - /// 唯一标识符,遵循反向域名格式 - /// 示例: "io.export.usdz", "geometry.filter.laplacian" - std::string name; - - /// 语义化版本号 "major.minor.patch" - /// 示例: "1.0.0", "2.1.3-beta" - std::string version; - - /// 作者信息,格式 "Name " 或 "Organization" - std::string author; - - /// 插件功能描述(单行简介) - std::string description; - - /// 许可证标识符 (SPDX) - /// 示例: "MIT", "Apache-2.0", "GPL-3.0-only" - std::string license; - - /// 最低兼容的 VDE 版本 "6.0.0" - std::string vde_version_min; - - /// 最高兼容的 VDE 版本(空字符串 = 无上限) - std::string vde_version_max; - - // ══════════════════════════════════════════════ - // 插件类型枚举 - // ══════════════════════════════════════════════ - - enum class Type : uint8_t { - IO_IMPORT = 0, ///< 格式导入器 (STEP额外格式, USDZ, FBX...) - IO_EXPORT = 1, ///< 格式导出器 (USDZ, FBX, X3D...) - GEOMETRY_FILTER = 2, ///< 几何过滤器 (平滑, 简化, 特征提取) - CUSTOM_TOOL = 3, ///< 自定义工具 (应力分析, 拓扑优化...) - OTHER = 255 ///< 未分类 - }; - Type type = Type::OTHER; - - /// 依赖的其他插件名称列表 - /// 加载此插件前必须先加载依赖项 - std::vector dependencies; - - /// 动态库文件名(用于清单文件加载模式) - /// 示例: "libvde_io_usdz.so" - std::string library; - - /// 入口点符号名称 - struct EntryPoints { - std::string create = "create_plugin"; ///< 工厂函数符号 - std::string destroy = "destroy_plugin"; ///< 销毁函数符号 - std::string get_manifest = "get_manifest"; ///< 清单查询(可选) - }; - EntryPoints entry_points; - - // ── 工厂方法 ────────────────────────────────── - - /// 从 plugin.json 文件加载清单 - /// @param path JSON 文件路径 - /// @return 清单对象,解析失败返回 nullopt - static std::optional from_file(const std::string& path); - - /// 从 JSON 字符串解析清单 - /// @param json JSON 文本内容 - /// @return 清单对象,解析失败返回 nullopt - static std::optional from_json(const std::string& json); - - /// 获取类型对应的可读字符串 - static const char* type_name(Type t); - - /// 验证清单必要字段是否完整 - /// @return true 如果 name/version/vde_version_min 均已设置 - bool valid() const; -}; - -// ═══════════════════════════════════════════════════════════ -// PluginInterface — 插件抽象基类 -// ═══════════════════════════════════════════════════════════ - -/// 所有 VDE 插件必须实现的抽象基类。 -/// -/// 最小实现要求: -/// - initialize() — 设置 manifest 并初始化资源 -/// - shutdown() — 清理所有资源 -/// - manifest() — 返回 const 引用 -/// - name() — 返回 manifest().name.c_str() -/// - version() — 返回 manifest().version.c_str() -/// - execute() — 实现插件核心功能 -/// -/// ## 生命周期 -/// ``` -/// create_plugin() → initialize() → execute(...)* → shutdown() → destroy_plugin() -/// ``` -class PluginInterface { -public: - PluginInterface() = default; - virtual ~PluginInterface() = default; - - // 禁止拷贝,允许移动 - PluginInterface(const PluginInterface&) = delete; - PluginInterface& operator=(const PluginInterface&) = delete; - PluginInterface(PluginInterface&&) noexcept = default; - PluginInterface& operator=(PluginInterface&&) noexcept = default; - - /// 初始化插件。在 dlopen + create_plugin 之后调用一次。 - /// 应在此设置 manifest 并分配资源。 - /// @return true 表示初始化成功 - virtual bool initialize() = 0; - - /// 关闭插件。在 dlclose 之前调用一次。 - /// 应在此释放所有资源。 - virtual void shutdown() = 0; - - /// 获取插件清单的只读引用 - virtual const PluginManifest& manifest() const = 0; - - /// 插件唯一名称(与 manifest().name 一致) - virtual const char* name() const = 0; - - /// 插件版本(与 manifest().version 一致) - virtual const char* version() const = 0; - - /// 执行插件核心功能 - /// - /// @param action 操作名称,如 "export", "filter", "analyze" - /// @param params 参数键值对,如 {"path":"/tmp/out.glb", "binary":"true"} - /// @param input 输入数据指针(类型由 action 定义) - /// @param output 输出数据指针(类型由 action 定义,调用方负责生命周期) - /// @return true 表示执行成功 - virtual bool execute(const std::string& action, - const std::map& params, - void* input, - void* output) = 0; - - /// 查询插件是否处于就绪状态 - /// @return true 表示 initialize() 已完成且未 shutdown - virtual bool ready() const { return true; } - - /// 返回最近一次错误描述 - virtual std::string error() const { return {}; } -}; - -// ═══════════════════════════════════════════════════════════ -// PluginManager — 插件加载/卸载/生命周期管理 -// ═══════════════════════════════════════════════════════════ - -/// 插件管理器:扫描目录、加载动态库、维护注册表。 -/// -/// 线程安全:所有公开方法持有内部读写锁。 -/// -/// ## 搜索路径优先级 -/// 1. load_plugin_dir() 指定的目录 -/// 2. 环境变量 $VDE_PLUGIN_PATH(冒号分隔) -/// 3. 默认路径: /usr/lib/vde/plugins, ./vde_plugins -/// -/// @code -/// PluginManager mgr; -/// mgr.load_plugin_dir("/usr/lib/vde/plugins"); -/// mgr.load_plugin("/opt/custom/my_plugin.so"); -/// -/// if (auto* p = mgr.get_plugin("io.export.gltf")) { -/// p->execute("export", {}, &mesh, &buf); -/// } -/// @endcode -class PluginManager { -public: - PluginManager(); - ~PluginManager(); - - // 禁止拷贝 - PluginManager(const PluginManager&) = delete; - PluginManager& operator=(const PluginManager&) = delete; - - // ── 目录/批量加载 ─────────────────────────────── - - /// 扫描并加载指定目录下的所有插件 - /// - /// 识别 .so / .dylib / .dll 文件。 - /// 如果同目录存在 plugin.json 清单,优先读取以验证兼容性。 - /// - /// @param dir_path 插件目录路径 - /// @return 成功加载的插件数量 - /// @note 单个插件加载失败不影响其他插件 - int load_plugin_dir(const std::string& dir_path); - - /// 通过环境变量和默认路径加载插件 - /// 等价于 load_plugin_dir() 遍历 $VDE_PLUGIN_PATH - /// @return 成功加载的插件总数 - int load_default_paths(); - - // ── 单个加载 ───────────────────────────────────── - - /// 加载单个插件动态库 - /// - /// 流程: dlopen → dlsym(create_plugin) → initialize() - /// - /// @param plugin_path 动态库完整路径(.so/.dylib/.dll) - /// @return true 表示加载成功 - bool load_plugin(const std::string& plugin_path); - - /// 注册已创建的插件实例(通常由静态注册宏使用) - /// - /// @param plugin 插件实例(所有权转移给 PluginManager) - /// @return true 表示注册成功(名称不冲突) - bool register_plugin(std::unique_ptr plugin); - - // ── 卸载 ───────────────────────────────────────── - - /// 按名称卸载指定插件 - /// - /// @param name 插件名称(manifest().name) - /// @return true 表示找到并卸载成功 - bool unload_plugin(const std::string& name); - - /// 卸载所有已加载插件 - /// 按加载倒序依次 shutdown + destroy - void unload_all(); - - // ── 查询 ───────────────────────────────────────── - - /// 按名称查找已加载插件 - /// - /// @param name 插件名称 - /// @return 插件指针,未找到返回 nullptr - PluginInterface* get_plugin(const std::string& name); - - /// 按名称查找(const 重载) - const PluginInterface* get_plugin(const std::string& name) const; - - /// 列出所有已加载插件的名称 - std::vector list_plugins() const; - - /// 检查指定插件是否已加载 - bool is_loaded(const std::string& name) const; - - /// 已加载插件数量 - size_t plugin_count() const; - - /// 最近一次错误的描述 - std::string last_error() const; - - /// 清空错误状态 - void clear_error(); - -private: - struct Impl; - std::unique_ptr impl_; -}; - -// ═══════════════════════════════════════════════════════════ -// 插件导出符号 — 每个插件动态库必须导出 -// ═══════════════════════════════════════════════════════════ - -/// @brief 创建插件实例(必须由每个 .so/.dll 导出) -/// -/// 示例实现: -/// @code -/// extern "C" VDE_PLUGIN_EXPORT PluginInterface* create_plugin() { -/// return new MyPlugin(); -/// } -/// @endcode -using CreatePluginFunc = PluginInterface* (*)(); - -/// @brief 销毁插件实例(必须由每个 .so/.dll 导出) -/// -/// 示例实现: -/// @code -/// extern "C" VDE_PLUGIN_EXPORT void destroy_plugin(PluginInterface* p) { -/// delete p; -/// } -/// @endcode -using DestroyPluginFunc = void (*)(PluginInterface*); - -/// @brief 获取插件清单(可选导出,优化加载速度) -/// -/// 示例实现: -/// @code -/// extern "C" VDE_PLUGIN_EXPORT const PluginManifest* get_manifest() { -/// static PluginManifest m = { .name = "...", .version = "1.0.0", ... }; -/// return &m; -/// } -/// @endcode -using GetManifestFunc = const PluginManifest* (*)(); - -// ═══════════════════════════════════════════════════════════ -// 便捷注册宏 -// ═══════════════════════════════════════════════════════════ - -/// 在插件 .cpp 文件中注册插件类 -/// -/// 自动生成 create_plugin / destroy_plugin 导出符号。 -/// -/// 用法: -/// @code -/// VDE_REGISTER_PLUGIN(MyExporter) -/// @endcode -/// -/// 等价于手写: -/// @code -/// extern "C" VDE_PLUGIN_EXPORT void* create_plugin() { -/// return new MyExporter(); -/// } -/// extern "C" VDE_PLUGIN_EXPORT void destroy_plugin(void* p) { -/// delete static_cast(p); -/// } -/// @endcode -#define VDE_REGISTER_PLUGIN(PluginClass) \ - extern "C" VDE_PLUGIN_EXPORT vde::plugin::PluginInterface* \ - create_plugin() { \ - return new PluginClass(); \ - } \ - extern "C" VDE_PLUGIN_EXPORT void \ - destroy_plugin(vde::plugin::PluginInterface* p) { \ - delete p; \ - } - -/// 注册插件并同时导出 manifest 符号 -/// -/// @param PluginClass 插件类名 -/// @param ManifestVar 静态 PluginManifest 变量名 -#define VDE_REGISTER_PLUGIN_WITH_MANIFEST(PluginClass, ManifestVar) \ - extern "C" VDE_PLUGIN_EXPORT const vde::plugin::PluginManifest* \ - get_manifest() { \ - return &ManifestVar; \ - } \ - VDE_REGISTER_PLUGIN(PluginClass) - -} // namespace vde::plugin diff --git a/include/vde/wasm/vde_wasm.h b/include/vde/wasm/vde_wasm.h deleted file mode 100644 index 791b5ff..0000000 --- a/include/vde/wasm/vde_wasm.h +++ /dev/null @@ -1,327 +0,0 @@ -#pragma once -/// @file vde_wasm.h WebAssembly / Emscripten 绑定 -/// @ingroup wasm - -#include -#include -#include -#include -#include -#include - -// ── Emscripten 条件编译 ────────────────────────────── -#ifdef __EMSCRIPTEN__ -#include -#include -#include -#include -#endif - -namespace vde::wasm { - -// ═══════════════════════════════════════════════════════ -// Emscripten 绑定 —— C++ → WASM -// ═══════════════════════════════════════════════════════ - -/// WASM 导出标记(跨平台兼容) -#ifndef EMSCRIPTEN_KEEPALIVE -# ifdef __EMSCRIPTEN__ -# define EMSCRIPTEN_KEEPALIVE __attribute__((used)) -# else -# define EMSCRIPTEN_KEEPALIVE -# endif -#endif - -/** - * @brief C++ 到 WASM 的桥接层 - * - * 封装 Emscripten 绑定,将 VDE 核心功能 - * 导出为 JavaScript 可调用的 WASM 函数。 - * - * @ingroup wasm - */ -class WasmBridge { -public: - WasmBridge() = default; - - /// 初始化 WASM 运行时 - bool initialize(); - - /// 注册 Emscripten 绑定(C++ 类 → JS) - void register_bindings(); - - /// 获取 WASM 内存使用情况 - [[nodiscard]] size_t memory_used() const; - - /// 获取 WASM 堆基址 - [[nodiscard]] uintptr_t heap_base() const; - - // ── 模型加载 ── - - /// 从 ArrayBuffer 加载模型 - EMSCRIPTEN_KEEPALIVE - bool load_model_from_buffer(const uint8_t* data, size_t size, - const std::string& format); - - /// 导出模型为 ArrayBuffer - EMSCRIPTEN_KEEPALIVE - std::vector export_model_to_buffer(const std::string& format); - - // ── 几何操作 ── - - /// 布尔运算 - EMSCRIPTEN_KEEPALIVE - bool boolean_union(); - - EMSCRIPTEN_KEEPALIVE - bool boolean_intersect(); - - EMSCRIPTEN_KEEPALIVE - bool boolean_subtract(); - - // ── 渲染数据 ── - - /// 生成三角面片数据(用于 Three.js / WebGL) - EMSCRIPTEN_KEEPALIVE - std::vector tessellate_for_webgl(float tolerance = 0.01f); - - /// 生成线框数据 - EMSCRIPTEN_KEEPALIVE - std::vector wireframe_data(); - -private: - bool initialized_ = false; -}; - -// ═══════════════════════════════════════════════════════ -// WebWorker 多线程 -// ═══════════════════════════════════════════════════════ - -/// 任务描述 -struct WorkerTask { - uint64_t task_id; - std::string name; - std::vector payload; - int priority = 0; -}; - -/// 任务结果 -struct WorkerResult { - uint64_t task_id; - bool success = false; - std::vector data; - std::string error; -}; - -/// Worker 线程函数类型 -using WorkerFn = std::function; - -/** - * @brief WebWorker 多线程管理器 - * - * 利用 WebWorker 实现浏览器端多线程: - * - 主线程提交任务 - * - Worker 线程异步执行 - * - SharedArrayBuffer 传输数据 - * - * @ingroup wasm - */ -class WebWorkerPool { -public: - explicit WebWorkerPool(int num_workers = 0); - ~WebWorkerPool(); - - /// 初始化 Worker 池 - bool start(); - - /// 停止所有 Worker - void stop(); - - /// 提交任务 - uint64_t submit_task(const WorkerTask& task); - - /// 轮询已完成的任务 - std::vector poll_results(); - - /// 等待指定任务完成 - WorkerResult wait_for(uint64_t task_id, int timeout_ms = 30000); - - /// 当前活跃任务数 - [[nodiscard]] int active_tasks() const; - - /// Worker 数量 - [[nodiscard]] int worker_count() const { return num_workers_; } - - /// 注册任务处理函数 - void register_handler(const std::string& task_name, WorkerFn handler); - -private: - int num_workers_ = 4; - bool running_ = false; - - struct TaskState { - WorkerTask task; - bool completed = false; - WorkerResult result; - }; - - std::map handlers_; - std::map tasks_; - std::vector completed_; - uint64_t next_task_id_ = 1; - - void worker_loop(int worker_id); -}; - -// ═══════════════════════════════════════════════════════ -// SharedArrayBuffer 共享内存 -// ═══════════════════════════════════════════════════════ - -/** - * @brief SharedArrayBuffer 管理器 - * - * 利用 SharedArrayBuffer 实现主线程与 Worker 之间的 - * 零拷贝数据共享。适用于大模型数据的多线程渲染/计算。 - * - * @ingroup wasm - */ -class SharedMemory { -public: - SharedMemory() = default; - ~SharedMemory(); - - /// 分配共享内存 - bool allocate(size_t size_bytes); - - /// 释放 - void free(); - - /// 获取可写指针 - [[nodiscard]] void* data() { return buffer_; } - - /// 获取只读指针 - [[nodiscard]] const void* data() const { return buffer_; } - - /// 内存大小 - [[nodiscard]] size_t size() const { return size_; } - - /// 获取 SharedArrayBuffer 的 JS handle -#ifdef __EMSCRIPTEN__ - [[nodiscard]] emscripten::val js_handle() const; -#endif - - /// 使用 Atomics 进行同步 - void atomic_store32(size_t offset, int32_t value); - int32_t atomic_load32(size_t offset) const; - int32_t atomic_add32(size_t offset, int32_t delta); - int32_t atomic_cas32(size_t offset, int32_t expected, int32_t desired); - -private: - uint8_t* buffer_ = nullptr; - size_t size_ = 0; -}; - -// ═══════════════════════════════════════════════════════ -// IndexedDB 本地存储 -// ═══════════════════════════════════════════════════════ - -/** - * @brief IndexedDB 本地存储适配器 - * - * 利用浏览器 IndexedDB 实现本地模型持久化存储。 - * 支持大文件存储、查询和版本管理。 - * - * @ingroup wasm - */ -class IndexedDBStorage { -public: - IndexedDBStorage() = default; - - /// 打开/创建数据库 - bool open(const std::string& db_name, int version = 1); - - /// 关闭数据库 - void close(); - - /// 存储对象 - bool put(const std::string& store_name, - const std::string& key, - const std::vector& data); - - /// 读取对象 - std::optional> get(const std::string& store_name, - const std::string& key); - - /// 删除对象 - bool remove(const std::string& store_name, const std::string& key); - - /// 检查 key 是否存在 - bool exists(const std::string& store_name, const std::string& key); - - /// 获取 store 中所有 key - std::vector keys(const std::string& store_name); - - /// 清空 store - bool clear_store(const std::string& store_name); - - /// 存储模型文件 - bool save_model(const std::string& model_name, - const std::vector& model_data, - const std::string& format); - - /// 加载模型文件 - std::optional> load_model(const std::string& model_name, - const std::string& format); - - /// 列出所有已存储模型 - std::vector list_models() const; - - /// 数据库是否已打开 - [[nodiscard]] bool is_open() const { return opened_; } - -private: - std::string db_name_ = "vde_models"; - bool opened_ = false; - -#ifdef __EMSCRIPTEN__ - // Emscripten 版本直接调用 JS IndexedDB API - emscripten::val js_db_ = emscripten::val::undefined(); -#endif -}; - -// ═══════════════════════════════════════════════════════ -// Emscripten 绑定注册(顶层函数) -// ═══════════════════════════════════════════════════════ - -#ifdef __EMSCRIPTEN__ -/// 注册所有 WASM 绑定到 JavaScript -EMSCRIPTEN_BINDINGS(vde_module) { - emscripten::class_("WasmBridge") - .constructor<>() - .function("initialize", &WasmBridge::initialize) - .function("memoryUsed", &WasmBridge::memory_used) - .function("loadModelFromBuffer", &WasmBridge::load_model_from_buffer) - .function("exportModelToBuffer", &WasmBridge::export_model_to_buffer) - .function("booleanUnion", &WasmBridge::boolean_union) - .function("booleanIntersect", &WasmBridge::boolean_intersect) - .function("booleanSubtract", &WasmBridge::boolean_subtract) - .function("tessellateForWebGL", &WasmBridge::tessellate_for_webgl) - .function("wireframeData", &WasmBridge::wireframe_data); - - emscripten::class_("SharedMemory") - .constructor<>() - .function("allocate", &SharedMemory::allocate) - .function("free", &SharedMemory::free) - .function("size", &SharedMemory::size); - - emscripten::class_("IndexedDBStorage") - .constructor<>() - .function("open", &IndexedDBStorage::open) - .function("close", &IndexedDBStorage::close) - .function("saveModel", &IndexedDBStorage::save_model) - .function("loadModel", &IndexedDBStorage::load_model) - .function("listModels", &IndexedDBStorage::list_models); -} -#endif - -} // namespace vde::wasm diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c5df32c..2ffa1c1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,441 +1,438 @@ -# ── 编译选项接口目标 ────────────────────────────── - -# ── foundation ───────────────────────────────────── -add_library(vde_foundation STATIC - foundation/tolerance.cpp - foundation/exact_predicates.cpp - foundation/io_obj.cpp - foundation/io_stl.cpp - foundation/serializer.cpp - foundation/io_ply.cpp - foundation/io_gltf.cpp - foundation/io_3mf.cpp - foundation/industrial_formats.cpp -) -target_include_directories(vde_foundation - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_foundation - PUBLIC vde_compile_options Eigen3::Eigen -) - -# ── core ─────────────────────────────────────────── -add_library(vde_core STATIC - core/polygon.cpp - core/voronoi.cpp - core/distance.cpp - core/convex_hull.cpp - core/icp.cpp - core/cam_toolpath.cpp - core/cam_strategies.cpp - core/cam_5axis.cpp - core/cam_advanced.cpp - core/cam_optimization.cpp - core/performance_tuning.cpp - core/exact_predicates.cpp - core/concurrent_data.cpp - core/transaction.cpp - core/topology_compression.cpp -) -target_include_directories(vde_core - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_core - PUBLIC vde_foundation vde_compile_options -) - -# ── curves ───────────────────────────────────────── -add_library(vde_curves STATIC - curves/bezier_curve.cpp - curves/bspline_curve.cpp - curves/nurbs_curve.cpp - curves/bezier_surface.cpp - curves/bspline_surface.cpp - curves/nurbs_surface.cpp - curves/nurbs_operations.cpp - curves/surface_intersection.cpp - curves/tessellation.cpp - curves/parallel_intersection.cpp - curves/surface_fairing.cpp - curves/exact_offset.cpp - curves/surface_extension.cpp - curves/surface_continuity.cpp - curves/surface_analysis.cpp - curves/class_a_surfacing.cpp - curves/advanced_intersection.cpp - curves/iga_prep.cpp -) -target_include_directories(vde_curves - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_curves - PUBLIC vde_core vde_compile_options -) - -# ── mesh ──────────────────────────────────────────── -add_library(vde_mesh STATIC - mesh/halfedge_mesh.cpp - mesh/delaunay_2d.cpp - mesh/cdt_2d.cpp - mesh/delaunay_3d.cpp - mesh/mesh_simplify.cpp - mesh/mesh_smooth.cpp - mesh/mesh_boolean.cpp - mesh/mesh_quality.cpp - mesh/mesh_repair.cpp - mesh/alpha_shapes.cpp - mesh/mesh_parameterize.cpp - mesh/geodesic.cpp - mesh/mesh_curvature.cpp - mesh/marching_cubes.cpp - mesh/mesh_lod.cpp - mesh/parallel_mc.cpp - mesh/reverse_engineering.cpp - mesh/point_cloud.cpp - mesh/fea_mesh.cpp - mesh/visualization_quality.cpp -) -target_include_directories(vde_mesh - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_mesh - PUBLIC vde_core vde_brep vde_compile_options -) - -# ── spatial ───────────────────────────────────────── -add_library(vde_spatial STATIC - spatial/bvh.cpp - spatial/octree.cpp - spatial/kd_tree.cpp - spatial/r_tree.cpp - spatial/incremental_bvh.cpp -) -target_include_directories(vde_spatial - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_spatial - PUBLIC vde_core vde_compile_options -) - -# ── boolean ───────────────────────────────────────── -add_library(vde_boolean STATIC - boolean/boolean_2d.cpp - boolean/boolean_mesh.cpp - boolean/polygon_offset.cpp -) -target_include_directories(vde_boolean - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_boolean - PUBLIC vde_mesh vde_spatial vde_compile_options -) - -# ── collision ─────────────────────────────────────── -add_library(vde_collision STATIC - collision/gjk.cpp - collision/sat.cpp - collision/ray_intersect.cpp - collision/tri_intersect.cpp -) -target_include_directories(vde_collision - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_collision - PUBLIC vde_core vde_spatial vde_compile_options -) - -# ── 聚合库 ────────────────────────────────────────── -add_library(vde INTERFACE) -target_link_libraries(vde - INTERFACE vde_foundation - INTERFACE vde_core - INTERFACE vde_curves - INTERFACE vde_mesh - INTERFACE vde_spatial - INTERFACE vde_boolean - INTERFACE vde_collision - INTERFACE vde_sdf - INTERFACE vde_brep - INTERFACE vde_sketch - INTERFACE vde_cloud - INTERFACE vde_kbe - INTERFACE vde_wasm - INTERFACE vde_digital_twin - INTERFACE vde_ai -) -add_library(vde::engine ALIAS vde) - -# ── brep ─────────────────────────────────────────── -add_library(vde_brep STATIC - brep/brep.cpp - brep/interference_check.cpp - brep/motion_simulation.cpp - brep/explode_view.cpp - brep/gdt.cpp - brep/incremental_update.cpp - brep/large_assembly.cpp - brep/format_io.cpp - brep/tolerance.cpp - brep/modeling.cpp - brep/brep_drawing.cpp - brep/auto_dimensioning.cpp - brep/pmi_mbd.cpp - brep/step_export.cpp - brep/step_import.cpp - brep/iges_import.cpp - brep/iges_export.cpp - brep/brep_boolean.cpp - brep/ssi_boolean.cpp - brep/brep_validate.cpp - brep/brep_heal.cpp - brep/brep_face_split.cpp - brep/feature_tree.cpp - brep/assembly_constraints.cpp - brep/constraint_solver.cpp - brep/measure.cpp - brep/trimmed_surface.cpp - brep/dxf_import.cpp - brep/assembly_instance.cpp - brep/euler_op.cpp - brep/draft_analysis.cpp - brep/incremental_mesh.cpp - brep/kinematic_chain.cpp - brep/parallel_boolean.cpp - brep/feature_recognition.cpp - brep/defeature.cpp - brep/advanced_blend.cpp - brep/assembly_feature.cpp - brep/assembly_patterns.cpp - brep/fastener_library.cpp - brep/flexible_assembly.cpp - brep/assembly_lod.cpp - brep/ffd_deformation.cpp - brep/advanced_healing.cpp - brep/sheet_metal.cpp - brep/direct_modeling.cpp - brep/quality_feedback.cpp -) -target_include_directories(vde_brep - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_brep - PUBLIC vde_curves vde_mesh vde_collision vde_compile_options -) - -# ── sketch ───────────────────────────────────────── -add_library(vde_sketch STATIC - sketch/constraint_solver.cpp -) -target_include_directories(vde_sketch - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_sketch - PUBLIC vde_core vde_compile_options -) - -# ── sdf ──────────────────────────────────────────── -add_library(vde_sdf STATIC - sdf/sdf_primitives.cpp - sdf/sdf_tree.cpp - sdf/sdf_to_mesh.cpp - sdf/sdf_torch.cpp - sdf/sdf_optimize.cpp -) -target_include_directories(vde_sdf - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_sdf - PUBLIC vde_core vde_mesh vde_compile_options -) - -# ── cloud ────────────────────────────────────────── -add_library(vde_cloud STATIC - cloud/cloud_native.cpp -) -target_include_directories(vde_cloud - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_cloud - PUBLIC vde_core vde_compile_options -) - -# ── kbe ──────────────────────────────────────────── -add_library(vde_kbe STATIC - kbe/knowledge_engine.cpp -) -target_include_directories(vde_kbe - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_kbe - PUBLIC vde_core vde_compile_options -) - -# ── wasm ─────────────────────────────────────────── -add_library(vde_wasm STATIC - wasm/vde_wasm.cpp -) -target_include_directories(vde_wasm - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_wasm - PUBLIC vde_core vde_compile_options -) - -# ── digital_twin ─────────────────────────────────── -add_library(vde_digital_twin STATIC - digital_twin/dt_engine.cpp -) -target_include_directories(vde_digital_twin - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_digital_twin - PUBLIC vde_core vde_compile_options -) - -# ── AI / ML ───────────────────────────────────────── -add_library(vde_ai STATIC - ai/feature_learning.cpp - ai/generative_design.cpp - ai/inference_engine.cpp -) -target_include_directories(vde_ai - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_ai - PUBLIC vde_brep vde_mesh vde_sdf vde_compile_options -) - -# ── ONNX Runtime (optional) ── -if(VDE_USE_ONNX) - find_package(onnxruntime QUIET) - if(onnxruntime_FOUND) - target_compile_definitions(vde_ai PUBLIC VDE_USE_ONNX=1) - target_link_libraries(vde_ai PUBLIC onnxruntime::onnxruntime) - message(STATUS "ONNX Runtime enabled") - else() - message(STATUS "ONNX Runtime not found — AI module in heuristic fallback mode") - endif() -endif() - -# ── TensorRT (optional) ── -if(VDE_USE_TENSORRT) - find_package(TensorRT QUIET) - if(TensorRT_FOUND) - target_compile_definitions(vde_ai PUBLIC VDE_USE_TENSORRT=1) - target_link_libraries(vde_ai PUBLIC nvinfer nvonnxparser) - message(STATUS "TensorRT enabled") - else() - message(STATUS "TensorRT not found — AI module in CPU-only mode") - endif() -endif() - -# ── C API ────────────────────────────────────────── -add_library(vde_capi SHARED - capi/vde_capi.cpp -) -target_include_directories(vde_capi - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_capi - PUBLIC vde_brep vde_sketch vde_collision vde_boolean vde_spatial - PUBLIC vde_mesh vde_curves vde_core vde_foundation vde_sdf vde_compile_options -) - -# ── GMP exact predicates (optional) ── -if(VDE_USE_GMP) - list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") - find_package(GMP REQUIRED) - target_compile_definitions(vde_foundation PUBLIC VDE_USE_GMP) - target_include_directories(vde_foundation PUBLIC ${GMP_INCLUDE_DIRS}) - target_link_libraries(vde_foundation PUBLIC ${GMP_LIBRARIES}) - message(STATUS "GMP exact arithmetic: ${GMP_INCLUDE_DIRS}") -else() - message(STATUS "GMP disabled (-DVDE_USE_GMP=ON to enable)") -endif() - -# ── OpenMP parallelization ── -if(VDE_USE_OPENMP) - find_package(OpenMP) - if(OpenMP_CXX_FOUND) - target_link_libraries(vde_brep PUBLIC OpenMP::OpenMP_CXX) - target_compile_definitions(vde_brep PUBLIC VDE_USE_OPENMP) - message(STATUS "OpenMP enabled") - else() - message(STATUS "OpenMP not found (parallelism disabled)") - endif() -endif() - -# ── distributed ──────────────────────────────────── -add_library(vde_distributed STATIC - distributed/cluster_engine.cpp - distributed/grpc_service.cpp -) -target_include_directories(vde_distributed - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} -) -target_link_libraries(vde_distributed - PUBLIC vde_brep vde_mesh vde_collision vde_boolean vde_sdf vde_compile_options -) -target_link_libraries(vde INTERFACE vde_distributed) - -# ── GPU (CUDA) acceleration ──────────────────────── -if(VDE_USE_CUDA) - # 支持 CUDA 10.0+,兼容 CMake 3.16+ 的 FindCUDAToolkit - find_package(CUDAToolkit QUIET) - if(CUDAToolkit_FOUND) - enable_language(CUDA) - set(CMAKE_CUDA_STANDARD 14) - set(CMAKE_CUDA_STANDARD_REQUIRED ON) - - # GPU 加速库(混合 C++ / CUDA) - add_library(vde_gpu STATIC - gpu/gpu_acceleration.cpp - gpu/gpu_acceleration.cu - ) - target_include_directories(vde_gpu - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - ) - target_compile_definitions(vde_gpu PUBLIC VDE_USE_CUDA=1) - target_link_libraries(vde_gpu - PUBLIC vde_mesh vde_compile_options CUDA::cudart - ) - # 将 vde_gpu 加入聚合接口 - target_link_libraries(vde INTERFACE vde_gpu) - message(STATUS "CUDA GPU acceleration enabled (${CUDAToolkit_VERSION})") - else() - message(STATUS "CUDAToolkit not found — GPU acceleration disabled") - set(VDE_USE_CUDA OFF) - endif() -else() - # 无 CUDA 时仍编译 CPU-only 库 - add_library(vde_gpu STATIC - gpu/gpu_acceleration.cpp - ) - target_include_directories(vde_gpu - PUBLIC ${CMAKE_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - ) - target_link_libraries(vde_gpu - PUBLIC vde_mesh vde_compile_options - ) - target_link_libraries(vde INTERFACE vde_gpu) - message(STATUS "GPU library (CPU-only, use -DVDE_USE_CUDA=ON for CUDA)") -endif() +1|# ── 编译选项接口目标 ────────────────────────────── +2| +3|# ── foundation ───────────────────────────────────── +4|add_library(vde_foundation STATIC +5| foundation/tolerance.cpp +6| foundation/exact_predicates.cpp +7| foundation/io_obj.cpp +8| foundation/io_stl.cpp +9| foundation/serializer.cpp +10| foundation/io_ply.cpp +11| foundation/io_gltf.cpp +12| foundation/io_3mf.cpp +13| foundation/industrial_formats.cpp +14|) +15|target_include_directories(vde_foundation +16| PUBLIC ${CMAKE_SOURCE_DIR}/include +17| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +18|) +19|target_link_libraries(vde_foundation +20| PUBLIC vde_compile_options Eigen3::Eigen +21|) +22| +23|# ── core ─────────────────────────────────────────── +24|add_library(vde_core STATIC +25| core/polygon.cpp +26| core/voronoi.cpp +27| core/distance.cpp +28| core/convex_hull.cpp +29| core/icp.cpp +30| core/cam_toolpath.cpp +31| core/cam_strategies.cpp +32| core/cam_5axis.cpp +33| core/cam_advanced.cpp +34| core/cam_optimization.cpp +35| core/performance_tuning.cpp +36| core/exact_predicates.cpp +37| core/concurrent_data.cpp +38| core/transaction.cpp +39| core/topology_compression.cpp +40|) +41|target_include_directories(vde_core +42| PUBLIC ${CMAKE_SOURCE_DIR}/include +43| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +44|) +45|target_link_libraries(vde_core +46| PUBLIC vde_foundation vde_compile_options +47|) +48| +49|# ── curves ───────────────────────────────────────── +50|add_library(vde_curves STATIC +51| curves/bezier_curve.cpp +52| curves/bspline_curve.cpp +53| curves/nurbs_curve.cpp +54| curves/bezier_surface.cpp +55| curves/bspline_surface.cpp +56| curves/nurbs_surface.cpp +57| curves/nurbs_operations.cpp +58| curves/surface_intersection.cpp +59| curves/tessellation.cpp +60| curves/parallel_intersection.cpp +61| curves/surface_fairing.cpp +62| curves/exact_offset.cpp +63| curves/surface_extension.cpp +64| curves/surface_continuity.cpp +65| curves/surface_analysis.cpp +66| curves/class_a_surfacing.cpp +67| curves/advanced_intersection.cpp +68| curves/iga_prep.cpp +69|) +70|target_include_directories(vde_curves +71| PUBLIC ${CMAKE_SOURCE_DIR}/include +72| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +73|) +74|target_link_libraries(vde_curves +75| PUBLIC vde_core vde_compile_options +76|) +77| +78|# ── mesh ──────────────────────────────────────────── +79|add_library(vde_mesh STATIC +80| mesh/halfedge_mesh.cpp +81| mesh/delaunay_2d.cpp +82| mesh/cdt_2d.cpp +83| mesh/delaunay_3d.cpp +84| mesh/mesh_simplify.cpp +85| mesh/mesh_smooth.cpp +86| mesh/mesh_boolean.cpp +87| mesh/mesh_quality.cpp +88| mesh/mesh_repair.cpp +89| mesh/alpha_shapes.cpp +90| mesh/mesh_parameterize.cpp +91| mesh/geodesic.cpp +92| mesh/mesh_curvature.cpp +93| mesh/marching_cubes.cpp +94| mesh/mesh_lod.cpp +95| mesh/parallel_mc.cpp +96| mesh/reverse_engineering.cpp +97| mesh/point_cloud.cpp +98| mesh/fea_mesh.cpp +99| mesh/visualization_quality.cpp +100|) +101|target_include_directories(vde_mesh +102| PUBLIC ${CMAKE_SOURCE_DIR}/include +103| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +104|) +105|target_link_libraries(vde_mesh +106| PUBLIC vde_core vde_brep vde_compile_options +107|) +108| +109|# ── spatial ───────────────────────────────────────── +110|add_library(vde_spatial STATIC +111| spatial/bvh.cpp +112| spatial/octree.cpp +113| spatial/kd_tree.cpp +114| spatial/r_tree.cpp +115| spatial/incremental_bvh.cpp +116|) +117|target_include_directories(vde_spatial +118| PUBLIC ${CMAKE_SOURCE_DIR}/include +119| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +120|) +121|target_link_libraries(vde_spatial +122| PUBLIC vde_core vde_compile_options +123|) +124| +125|# ── boolean ───────────────────────────────────────── +126|add_library(vde_boolean STATIC +127| boolean/boolean_2d.cpp +128| boolean/boolean_mesh.cpp +129| boolean/polygon_offset.cpp +130|) +131|target_include_directories(vde_boolean +132| PUBLIC ${CMAKE_SOURCE_DIR}/include +133| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +134|) +135|target_link_libraries(vde_boolean +136| PUBLIC vde_mesh vde_spatial vde_compile_options +137|) +138| +139|# ── collision ─────────────────────────────────────── +140|add_library(vde_collision STATIC +141| collision/gjk.cpp +142| collision/sat.cpp +143| collision/ray_intersect.cpp +144| collision/tri_intersect.cpp +145|) +146|target_include_directories(vde_collision +147| PUBLIC ${CMAKE_SOURCE_DIR}/include +148| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +149|) +150|target_link_libraries(vde_collision +151| PUBLIC vde_core vde_spatial vde_compile_options +152|) +153| +154|# ── 聚合库 ────────────────────────────────────────── +155|add_library(vde INTERFACE) +156|target_link_libraries(vde +157| INTERFACE vde_foundation +158| INTERFACE vde_core +159| INTERFACE vde_curves +160| INTERFACE vde_mesh +161| INTERFACE vde_spatial +162| INTERFACE vde_boolean +163| INTERFACE vde_collision +164| INTERFACE vde_sdf +165| INTERFACE vde_brep +166| INTERFACE vde_sketch +167|168|169|170|171| INTERFACE vde_ai +172|) +173|add_library(vde::engine ALIAS vde) +174| +175|# ── brep ─────────────────────────────────────────── +176|add_library(vde_brep STATIC +177| brep/brep.cpp +178| brep/interference_check.cpp +179| brep/motion_simulation.cpp +180| brep/explode_view.cpp +181| brep/gdt.cpp +182| brep/incremental_update.cpp +183| brep/large_assembly.cpp +184| brep/format_io.cpp +185| brep/tolerance.cpp +186| brep/modeling.cpp +187| brep/brep_drawing.cpp +188| brep/auto_dimensioning.cpp +189| brep/pmi_mbd.cpp +190| brep/step_export.cpp +191| brep/step_import.cpp +192| brep/iges_import.cpp +193| brep/iges_export.cpp +194| brep/brep_boolean.cpp +195| brep/ssi_boolean.cpp +196| brep/brep_validate.cpp +197| brep/brep_heal.cpp +198| brep/brep_face_split.cpp +199| brep/feature_tree.cpp +200| brep/assembly_constraints.cpp +201| brep/constraint_solver.cpp +202| brep/measure.cpp +203| brep/trimmed_surface.cpp +204| brep/dxf_import.cpp +205| brep/assembly_instance.cpp +206| brep/euler_op.cpp +207| brep/draft_analysis.cpp +208| brep/incremental_mesh.cpp +209| brep/kinematic_chain.cpp +210| brep/parallel_boolean.cpp +211| brep/feature_recognition.cpp +212| brep/defeature.cpp +213| brep/advanced_blend.cpp +214| brep/assembly_feature.cpp +215| brep/assembly_patterns.cpp +216| brep/fastener_library.cpp +217| brep/flexible_assembly.cpp +218| brep/assembly_lod.cpp +219| brep/ffd_deformation.cpp +220| brep/advanced_healing.cpp +221| brep/sheet_metal.cpp +222| brep/direct_modeling.cpp +223| brep/quality_feedback.cpp +224|) +225|target_include_directories(vde_brep +226| PUBLIC ${CMAKE_SOURCE_DIR}/include +227| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +228|) +229|target_link_libraries(vde_brep +230| PUBLIC vde_curves vde_mesh vde_collision vde_compile_options +231|) +232| +233|# ── sketch ───────────────────────────────────────── +234|add_library(vde_sketch STATIC +235| sketch/constraint_solver.cpp +236|) +237|target_include_directories(vde_sketch +238| PUBLIC ${CMAKE_SOURCE_DIR}/include +239| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +240|) +241|target_link_libraries(vde_sketch +242| PUBLIC vde_core vde_compile_options +243|) +244| +245|# ── sdf ──────────────────────────────────────────── +246|add_library(vde_sdf STATIC +247| sdf/sdf_primitives.cpp +248| sdf/sdf_tree.cpp +249| sdf/sdf_to_mesh.cpp +250| sdf/sdf_torch.cpp +251| sdf/sdf_optimize.cpp +252|) +253|target_include_directories(vde_sdf +254| PUBLIC ${CMAKE_SOURCE_DIR}/include +255| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +256|) +257|target_link_libraries(vde_sdf +258| PUBLIC vde_core vde_mesh vde_compile_options +259|) +260| +261|# ── cloud ────────────────────────────────────────── +262|add_library(vde_cloud STATIC +263| cloud/cloud_native.cpp +264|) +265|target_include_directories(vde_cloud +266| PUBLIC ${CMAKE_SOURCE_DIR}/include +267| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +268|) +269|target_link_libraries(vde_cloud +270| PUBLIC vde_core vde_compile_options +271|) +272| +273|# ── kbe ──────────────────────────────────────────── +274|add_library(vde_kbe STATIC +275| kbe/knowledge_engine.cpp +276|) +277|target_include_directories(vde_kbe +278| PUBLIC ${CMAKE_SOURCE_DIR}/include +279| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +280|) +281|target_link_libraries(vde_kbe +282| PUBLIC vde_core vde_compile_options +283|) +284| +285|# ── wasm ─────────────────────────────────────────── +286|add_library(vde_wasm STATIC +287| wasm/vde_wasm.cpp +288|) +289|target_include_directories(vde_wasm +290| PUBLIC ${CMAKE_SOURCE_DIR}/include +291| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +292|) +293|target_link_libraries(vde_wasm +294| PUBLIC vde_core vde_compile_options +295|) +296| +297|# ── digital_twin ─────────────────────────────────── +298|add_library(vde_digital_twin STATIC +299| digital_twin/dt_engine.cpp +300|) +301|target_include_directories(vde_digital_twin +302| PUBLIC ${CMAKE_SOURCE_DIR}/include +303| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +304|) +305|target_link_libraries(vde_digital_twin +306| PUBLIC vde_core vde_compile_options +307|) +308| +309|# ── AI / ML ───────────────────────────────────────── +310|add_library(vde_ai STATIC +311| ai/feature_learning.cpp +312| ai/generative_design.cpp +313| ai/inference_engine.cpp +314|) +315|target_include_directories(vde_ai +316| PUBLIC ${CMAKE_SOURCE_DIR}/include +317| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +318|) +319|target_link_libraries(vde_ai +320| PUBLIC vde_brep vde_mesh vde_sdf vde_compile_options +321|) +322| +323|# ── ONNX Runtime (optional) ── +324|if(VDE_USE_ONNX) +325| find_package(onnxruntime QUIET) +326| if(onnxruntime_FOUND) +327| target_compile_definitions(vde_ai PUBLIC VDE_USE_ONNX=1) +328| target_link_libraries(vde_ai PUBLIC onnxruntime::onnxruntime) +329| message(STATUS "ONNX Runtime enabled") +330| else() +331| message(STATUS "ONNX Runtime not found — AI module in heuristic fallback mode") +332| endif() +333|endif() +334| +335|# ── TensorRT (optional) ── +336|if(VDE_USE_TENSORRT) +337| find_package(TensorRT QUIET) +338| if(TensorRT_FOUND) +339| target_compile_definitions(vde_ai PUBLIC VDE_USE_TENSORRT=1) +340| target_link_libraries(vde_ai PUBLIC nvinfer nvonnxparser) +341| message(STATUS "TensorRT enabled") +342| else() +343| message(STATUS "TensorRT not found — AI module in CPU-only mode") +344| endif() +345|endif() +346| +347|# ── C API ────────────────────────────────────────── +348|add_library(vde_capi SHARED +349| capi/vde_capi.cpp +350|) +351|target_include_directories(vde_capi +352| PUBLIC ${CMAKE_SOURCE_DIR}/include +353| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +354|) +355|target_link_libraries(vde_capi +356| PUBLIC vde_brep vde_sketch vde_collision vde_boolean vde_spatial +357| PUBLIC vde_mesh vde_curves vde_core vde_foundation vde_sdf vde_compile_options +358|) +359| +360|# ── GMP exact predicates (optional) ── +361|if(VDE_USE_GMP) +362| list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +363| find_package(GMP REQUIRED) +364| target_compile_definitions(vde_foundation PUBLIC VDE_USE_GMP) +365| target_include_directories(vde_foundation PUBLIC ${GMP_INCLUDE_DIRS}) +366| target_link_libraries(vde_foundation PUBLIC ${GMP_LIBRARIES}) +367| message(STATUS "GMP exact arithmetic: ${GMP_INCLUDE_DIRS}") +368|else() +369| message(STATUS "GMP disabled (-DVDE_USE_GMP=ON to enable)") +370|endif() +371| +372|# ── OpenMP parallelization ── +373|if(VDE_USE_OPENMP) +374| find_package(OpenMP) +375| if(OpenMP_CXX_FOUND) +376| target_link_libraries(vde_brep PUBLIC OpenMP::OpenMP_CXX) +377| target_compile_definitions(vde_brep PUBLIC VDE_USE_OPENMP) +378| message(STATUS "OpenMP enabled") +379| else() +380| message(STATUS "OpenMP not found (parallelism disabled)") +381| endif() +382|endif() +383| +384|# ── distributed ──────────────────────────────────── +385|add_library(vde_distributed STATIC +386| distributed/cluster_engine.cpp +387| distributed/grpc_service.cpp +388|) +389|target_include_directories(vde_distributed +390| PUBLIC ${CMAKE_SOURCE_DIR}/include +391| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +392|) +393|target_link_libraries(vde_distributed +394| PUBLIC vde_brep vde_mesh vde_collision vde_boolean vde_sdf vde_compile_options +395|) +396|target_link_libraries(vde INTERFACE vde_distributed) +397| +398|# ── GPU (CUDA) acceleration ──────────────────────── +399|if(VDE_USE_CUDA) +400| # 支持 CUDA 10.0+,兼容 CMake 3.16+ 的 FindCUDAToolkit +401| find_package(CUDAToolkit QUIET) +402| if(CUDAToolkit_FOUND) +403| enable_language(CUDA) +404| set(CMAKE_CUDA_STANDARD 14) +405| set(CMAKE_CUDA_STANDARD_REQUIRED ON) +406| +407| # GPU 加速库(混合 C++ / CUDA) +408| add_library(vde_gpu STATIC +409| gpu/gpu_acceleration.cpp +410| gpu/gpu_acceleration.cu +411| ) +412| target_include_directories(vde_gpu +413| PUBLIC ${CMAKE_SOURCE_DIR}/include +414| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +415| ) +416| target_compile_definitions(vde_gpu PUBLIC VDE_USE_CUDA=1) +417| target_link_libraries(vde_gpu +418| PUBLIC vde_mesh vde_compile_options CUDA::cudart +419| ) +420| # 将 vde_gpu 加入聚合接口 +421| target_link_libraries(vde INTERFACE vde_gpu) +422| message(STATUS "CUDA GPU acceleration enabled (${CUDAToolkit_VERSION})") +423| else() +424| message(STATUS "CUDAToolkit not found — GPU acceleration disabled") +425| set(VDE_USE_CUDA OFF) +426| endif() +427|else() +428| # 无 CUDA 时仍编译 CPU-only 库 +429| add_library(vde_gpu STATIC +430| gpu/gpu_acceleration.cpp +431| ) +432| target_include_directories(vde_gpu +433| PUBLIC ${CMAKE_SOURCE_DIR}/include +434| PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} +435| ) +436| target_link_libraries(vde_gpu +437| PUBLIC vde_mesh vde_compile_options +438| ) +439| target_link_libraries(vde INTERFACE vde_gpu) +440| message(STATUS "GPU library (CPU-only, use -DVDE_USE_CUDA=ON for CUDA)") +441|endif() +442| \ No newline at end of file diff --git a/src/ai/feature_learning.cpp b/src/ai/feature_learning.cpp deleted file mode 100644 index 01176d5..0000000 --- a/src/ai/feature_learning.cpp +++ /dev/null @@ -1,721 +0,0 @@ -#include "vde/ai/feature_learning.h" -#include -#include -#include -#include -#include - -namespace vde::ai { - -// ═══════════════════════════════════════════════════════════ -// FeatureClassifier 实现 -// ═══════════════════════════════════════════════════════════ - -struct FeatureClassifier::Impl { - // 特征维度配置 - static constexpr int kNodeFeatureDim = 8; // 每面节点特征维度 - static constexpr int kEdgeFeatureDim = 1; // 每边特征维度 - static constexpr int kNumClasses = 10; // 分类数(含 Unknown) - - // GNN 层参数(当无 ONNX 模型时用于回退启发式分类) - // 节点特征: [曲面类型 one-hot(4), ln(area+1), 高斯曲率, 平均曲率, 球度] - std::vector layer1_weights; - std::vector layer2_weights; - - Impl() { - // 初始化默认权重(小型 GNN,带 ONNX 时被覆盖) - layer1_weights.resize(kNodeFeatureDim * 16, 0.0); - layer2_weights.resize(16 * kNumClasses, 0.0); - } -}; - -FeatureClassifier::FeatureClassifier() - : impl_(std::make_unique()) -{ -} - -FeatureClassifier::~FeatureClassifier() = default; - -bool FeatureClassifier::load_model(const std::string& model_path) { - (void)model_path; - // 当集成 ONNX Runtime 时,通过 InferenceSession 加载 GNN 模型 - // 当前无 ONNX 运行时回退到启发式规则 - model_loaded_ = true; - model_version_ = "1.0.0-heuristic"; - return true; -} - -FaceAdjacencyGraph FeatureClassifier::build_face_graph(const brep::BrepModel& body) const { - FaceAdjacencyGraph graph; - - const auto nf = body.num_faces(); - if (nf == 0) return graph; - - graph.nodes.reserve(nf); - - // 构建节点 - for (size_t fi = 0; fi < nf; ++fi) { - const auto& face = body.face(static_cast(fi)); - FAGNode node; - node.face_id = static_cast(fi); - - // 曲面类型 - const auto& surf = body.surface(face.surface_id); - // 通过 NURBS 阶次推断曲面类型: - // deg=(1,1) → 平面, deg=(2,1)-圆柱, deg=(2,2)-自由曲面 - int du = surf.degree_u(), dv = surf.degree_v(); - if (du <= 1 && dv <= 1) { - node.surface_type = 0; // 平面 - } else if (du <= 2 && dv <= 1) { - node.surface_type = 1; // 近似圆柱/锥 - } else { - node.surface_type = 2; // 自由曲面 - } - - // 计算特征向量 - node.node_features = encode_face_node(body, static_cast(fi)); - node.area = 0.0; // 简化:从 tessellation 估算面积 - node.centroid = Point3D{0, 0, 0}; - node.normal = Vector3D{0, 0, 1}; - - // 近似面积和形心(采样 tessellated 网格) - try { - auto mesh = body.to_mesh(0.05); - for (size_t mi = 0; mi < mesh.num_faces(); ++mi) { - auto fv = mesh.face_vertices(static_cast(mi)); - if (fv.size() == 3) { - auto p0 = Point3D{0, 0, 0}, p1 = Point3D{0, 0, 0}, p2 = Point3D{0, 0, 0}; - (void)p0; (void)p1; (void)p2; - } - } - } catch (...) { - // tessellation 可能失败,使用零值 - } - - graph.nodes.push_back(std::move(node)); - } - - // 构建边(共享边的面 → 邻接) - std::set> edge_set; - for (size_t fi = 0; fi < nf; ++fi) { - auto edge_ids = body.face_edges(static_cast(fi)); - for (int eid : edge_ids) { - // 查找共享该边的其他面 - for (size_t fj = fi + 1; fj < nf; ++fj) { - auto edge_ids_j = body.face_edges(static_cast(fj)); - if (std::find(edge_ids_j.begin(), edge_ids_j.end(), eid) != edge_ids_j.end()) { - auto pair = std::make_pair(static_cast(fi), static_cast(fj)); - if (edge_set.find(pair) == edge_set.end()) { - edge_set.insert(pair); - graph.edges.push_back(pair); - graph.edge_features.push_back( - encode_edge_feature(body, static_cast(fi), static_cast(fj))); - } - break; - } - } - } - } - - return graph; -} - -std::vector FeatureClassifier::encode_face_node( - const brep::BrepModel& body, int face_id) const -{ - (void)body; - (void)face_id; - - // 节点特征: 8 维 - // [曲面类型 one-hot(3), ln(area+1), 高斯曲率估计, 平均曲率估计, 球度, 斜率] - std::vector feats(Impl::kNodeFeatureDim, 0.0); - - const auto& face = body.face(face_id); - const auto& surf = body.surface(face.surface_id); - int du = surf.degree_u(), dv = surf.degree_v(); - - // 曲面类型 one-hot (features[0..2]) - if (du <= 1 && dv <= 1) { - feats[0] = 1.0; // 平面 - } else if (du <= 2 && dv <= 1) { - feats[1] = 1.0; // 圆柱/锥 - } else { - feats[2] = 1.0; // 自由曲面 - } - - // 面积对数 (features[3]) - double area_approx = 1.0; // 简化:默认 1.0 - feats[3] = std::log(area_approx + 1.0); - - // 高斯曲率估计 (features[4]) - feats[4] = (du > 1) ? 0.1 : 0.0; - - // 平均曲率估计 (features[5]) - feats[5] = (du > 1) ? 0.05 : 0.0; - - // 球度估计 (features[6]) - feats[6] = 0.0; - - // 面法向倾角 (features[7]) - feats[7] = 0.5; // 中性值 - - return feats; -} - -double FeatureClassifier::encode_edge_feature( - const brep::BrepModel& body, int face_id_a, int face_id_b) const -{ - (void)body; - (void)face_id_a; - (void)face_id_b; - - // 边特征: 二面角类型 - // 正值 = 凸边 (convex), 负值 = 凹边 (concave) - // 简化实现:根据面法向推断 - const auto& face_a = body.face(face_id_a); - const auto& face_b = body.face(face_id_b); - - // 取面中点近似法向 - Vector3D normal_a{0, 0, 1}; - Vector3D normal_b{0, 0, 1}; - - try { - // 采样面中点作为近似形心 - const auto& surf_a = body.surface(face_a.surface_id); - const auto& surf_b = body.surface(face_b.surface_id); - - double mid_u = 0.5, mid_v = 0.5; - auto pt_a = surf_a.evaluate(mid_u, mid_v); - auto pt_b = surf_b.evaluate(mid_u, mid_v); - - // 法向 - normal_a = surf_a.normal(mid_u, mid_v); - normal_b = surf_b.normal(mid_u, mid_v); - - // 计算二面角 - double dot = normal_a.dot(normal_b); - dot = std::max(-1.0, std::min(1.0, dot)); - double angle = std::acos(dot); - - // 判断凹凸: 使用 (pt_b - pt_a) 在 normal_a 上的投影 - auto dir = pt_b - pt_a; - double proj = dir.dot(normal_a); - if (proj > 0) { - // pt_b 在 normal_a 方向 → 凸边 - return angle / M_PI; // 归一化 [0, 1] - } else { - return -angle / M_PI; // 凹边 [-1, 0] - } - } catch (...) { - return 0.0; // 评估失败 → 中性边 - } -} - -FeatureClassificationResult FeatureClassifier::classify_features( - const brep::BrepModel& body) -{ - FeatureClassificationResult result; - - if (!model_loaded_) { - result.error_message = "Model not loaded"; - return result; - } - - // 1. 构建面邻接图 - auto graph = build_face_graph(body); - if (graph.nodes.empty()) { - result.success = true; - return result; - } - - // 2. 面级分类(启发式:基于曲面类型和邻域关系的规则) - std::vector> face_labels; - face_labels.reserve(graph.nodes.size()); - - for (const auto& node : graph.nodes) { - const auto& feats = node.node_features; - if (feats.size() < 4) { - face_labels.emplace_back(FeatureType::Unknown, 0.0); - continue; - } - - FeatureType ft = FeatureType::Unknown; - double confidence = 0.5; - - // 启发式规则 - bool is_planar = feats[0] > 0.5; - bool is_cylindrical = feats[1] > 0.5; - - if (is_planar) { - // 平面面 → 可能是 Rib, Step, 或 Pocket 的底面 - // 需要邻域信息进一步判断 - ft = FeatureType::Unknown; - } else if (is_cylindrical) { - // 圆柱面 → Hole 或 Boss 或 Fillet - double curvature_est = feats[4]; - if (curvature_est > 0.05) { - ft = FeatureType::Hole; - confidence = 0.85; - } else { - ft = FeatureType::Boss; - confidence = 0.7; - } - } else { - // 自由曲面 → 可能是 Fillet 或 Chamfer - ft = FeatureType::Fillet; - confidence = 0.6; - } - - face_labels.emplace_back(ft, confidence); - } - - // 3. 聚合相邻同类面 → 特征实例 - result.features = aggregate_features(graph, face_labels, body); - - // 4. 统计 - for (const auto& f : result.features) { - switch (f.type) { - case FeatureType::Hole: result.hole_count++; break; - case FeatureType::Slot: result.slot_count++; break; - case FeatureType::Pocket: result.pocket_count++; break; - case FeatureType::Boss: result.boss_count++; break; - case FeatureType::Fillet: result.fillet_count++; break; - case FeatureType::Chamfer: result.chamfer_count++; break; - case FeatureType::Rib: result.rib_count++; break; - case FeatureType::Step: result.step_count++; break; - case FeatureType::Groove: result.groove_count++; break; - default: break; - } - } - - result.success = true; - return result; -} - -std::vector FeatureClassifier::aggregate_features( - const FaceAdjacencyGraph& graph, - const std::vector>& face_labels, - const brep::BrepModel& body) const -{ - (void)body; - std::vector features; - - if (graph.nodes.empty() || face_labels.empty()) return features; - - // 构建邻接表 - std::unordered_map> adj; - for (const auto& [a, b] : graph.edges) { - adj[a].push_back(b); - adj[b].push_back(a); - } - - // BFS 聚合同类型相邻面 - std::vector visited(graph.nodes.size(), false); - - for (size_t i = 0; i < graph.nodes.size(); ++i) { - if (visited[i]) continue; - if (face_labels[i].first == FeatureType::Unknown) { - visited[i] = true; - continue; - } - - // BFS 收集同类型连通子图 - FeatureType cur_type = face_labels[i].first; - std::vector cluster_faces; - std::vector queue = {i}; - visited[i] = true; - - while (!queue.empty()) { - size_t cur = queue.back(); - queue.pop_back(); - cluster_faces.push_back(graph.nodes[cur].face_id); - - auto it = adj.find(graph.nodes[cur].face_id); - if (it == adj.end()) continue; - - for (int neighbor_face_id : it->second) { - // 找到 neighbor 在 nodes 中的索引 - for (size_t ni = 0; ni < graph.nodes.size(); ++ni) { - if (graph.nodes[ni].face_id == neighbor_face_id && !visited[ni]) { - if (face_labels[ni].first == cur_type) { - visited[ni] = true; - queue.push_back(ni); - } - } - } - } - } - - if (!cluster_faces.empty()) { - ClassifiedFeature cf; - cf.type = cur_type; - cf.face_ids = std::move(cluster_faces); - cf.confidence = face_labels[i].second; - cf.centroid = Point3D{0, 0, 0}; - cf.orientation = Vector3D{0, 0, 1}; - - // 特征标签 - switch (cur_type) { - case FeatureType::Hole: cf.label = "Hole"; break; - case FeatureType::Slot: cf.label = "Slot"; break; - case FeatureType::Pocket: cf.label = "Pocket"; break; - case FeatureType::Boss: cf.label = "Boss"; break; - case FeatureType::Fillet: cf.label = "Fillet"; break; - case FeatureType::Chamfer: cf.label = "Chamfer"; break; - case FeatureType::Rib: cf.label = "Rib"; break; - case FeatureType::Step: cf.label = "Step"; break; - case FeatureType::Groove: cf.label = "Groove"; break; - default: cf.label = "Unknown"; break; - } - - features.push_back(std::move(cf)); - } - } - - return features; -} - -// ═══════════════════════════════════════════════════════════ -// SimilaritySearch 实现 -// ═══════════════════════════════════════════════════════════ - -struct SimilaritySearch::Impl { - std::vector index; - - // 内部随机数生成器(用于点采样) - std::mt19937 rng{42}; -}; - -SimilaritySearch::SimilaritySearch() - : impl_(std::make_unique()) -{ -} - -SimilaritySearch::~SimilaritySearch() = default; - -ESFDescriptor SimilaritySearch::compute_esf(const brep::BrepModel& body, int samples) const { - ESFDescriptor desc; - - // 在表面上采样点 - auto points = sample_surface_points(body, samples); - if (points.size() < 3) return desc; - - const size_t n = points.size(); - const int bin_count = 640; - std::vector d2_bins(bin_count, 0.0); - std::vector d3_bins(bin_count, 0.0); - std::vector a3_bins(bin_count, 0.0); - - // 每函数约 200 个采样点用于加速 - size_t d2_samples = std::min(n, static_cast(200)); - size_t d3_samples = std::min(n, static_cast(200)); - size_t a3_samples = std::min(n, static_cast(200)); - - // D2: 点对距离直方图 - double max_dist = 0.0; - std::vector d2_vals; - for (size_t i = 0; i < d2_samples; ++i) { - size_t j = (i + 1) % n; - double dx = points[i].x() - points[j].x(); - double dy = points[i].y() - points[j].y(); - double dz = points[i].z() - points[j].z(); - double d = std::sqrt(dx*dx + dy*dy + dz*dz); - d2_vals.push_back(d); - if (d > max_dist) max_dist = d; - } - - if (max_dist < 1e-9) max_dist = 1.0; - - int d2_bin_count = bin_count / 3 + 1; - for (double d : d2_vals) { - int bin = static_cast((d / max_dist) * (d2_bin_count - 1)); - if (bin >= 0 && bin < d2_bin_count) d2_bins[bin]++; - } - - // D3: 三点面积直方图(简化:周长) - double max_area = 0.0; - std::vector d3_vals; - for (size_t i = 0; i < d3_samples; ++i) { - size_t j = (i + 1) % n; - size_t k = (i + 2) % n; - double dx1 = points[i].x() - points[j].x(); - double dy1 = points[i].y() - points[j].y(); - double dz1 = points[i].z() - points[j].z(); - double dx2 = points[k].x() - points[j].x(); - double dy2 = points[k].y() - points[j].y(); - double dz2 = points[k].z() - points[j].z(); - // 叉积大小 = 2*三角形面积 - double cx = dy1*dz2 - dz1*dy2; - double cy = dz1*dx2 - dx1*dz2; - double cz = dx1*dy2 - dy1*dx2; - double area = 0.5 * std::sqrt(cx*cx + cy*cy + cz*cz); - d3_vals.push_back(area); - if (area > max_area) max_area = area; - } - - if (max_area < 1e-9) max_area = 1.0; - int d3_bin_count = bin_count / 3 + 1; - for (double a : d3_vals) { - int bin = static_cast((a / max_area) * (d3_bin_count - 1)); - if (bin >= 0 && bin < d3_bin_count) d3_bins[bin]++; - } - - // A3: 三点夹角直方图 - double max_angle = M_PI; - std::vector a3_vals; - for (size_t i = 0; i < a3_samples; ++i) { - size_t j = (i + 1) % n; - size_t k = (i + 2) % n; - double dx1 = points[i].x() - points[j].x(); - double dy1 = points[i].y() - points[j].y(); - double dz1 = points[i].z() - points[j].z(); - double dx2 = points[k].x() - points[j].x(); - double dy2 = points[k].y() - points[j].y(); - double dz2 = points[k].z() - points[j].z(); - double d1 = std::sqrt(dx1*dx1 + dy1*dy1 + dz1*dz1); - double d2 = std::sqrt(dx2*dx2 + dy2*dy2 + dz2*dz2); - if (d1 < 1e-12 || d2 < 1e-12) continue; - double cos_a = (dx1*dx2 + dy1*dy2 + dz1*dz2) / (d1 * d2); - cos_a = std::max(-1.0, std::min(1.0, cos_a)); - double angle = std::acos(cos_a); - a3_vals.push_back(angle); - } - - int a3_bin_count = bin_count / 3; - for (double a : a3_vals) { - int bin = static_cast((a / max_angle) * (a3_bin_count - 1)); - if (bin >= 0 && bin < a3_bin_count) a3_bins[bin]++; - } - - // 合并三个直方图 → 640 维 - std::vector merged(bin_count, 0.0); - // 归一化各部分 - double d2_sum = std::accumulate(d2_bins.begin(), d2_bins.end(), 0.0); - double d3_sum = std::accumulate(d3_bins.begin(), d3_bins.end(), 0.0); - double a3_sum = std::accumulate(a3_bins.begin(), a3_bins.end(), 0.0); - - for (int i = 0; i < d2_bin_count; ++i) { - merged[i] = (d2_sum > 0) ? d2_bins[i] / d2_sum : 0.0; - } - for (int i = 0; i < d3_bin_count; ++i) { - merged[d2_bin_count + i] = (d3_sum > 0) ? d3_bins[i] / d3_sum : 0.0; - } - for (int i = 0; i < a3_bin_count; ++i) { - merged[d2_bin_count + d3_bin_count + i] = (a3_sum > 0) ? a3_bins[i] / a3_sum : 0.0; - } - - for (int i = 0; i < bin_count; ++i) { - desc.histogram[static_cast(i)] = merged[static_cast(i)]; - } - - return desc; -} - -FPFHDescriptor SimilaritySearch::compute_fpfh(const brep::BrepModel& body, int samples) const { - FPFHDescriptor desc; - - auto points = sample_surface_points(body, samples); - if (points.size() < 2) return desc; - - const size_t n = points.size(); - const int bin_count = 33; // 3×11 bins for α, φ, θ - std::vector alpha_bins(11, 0.0); - std::vector phi_bins(11, 0.0); - std::vector theta_bins(11, 0.0); - - // 对每对邻近点计算 SPFH - for (size_t i = 0; i < std::min(n, static_cast(500)); ++i) { - for (size_t j = i + 1; j < std::min(n, static_cast(501)); ++j) { - double dx = points[j].x() - points[i].x(); - double dy = points[j].y() - points[i].y(); - double dz = points[j].z() - points[i].z(); - double d = std::sqrt(dx*dx + dy*dy + dz*dz); - if (d < 1e-12) continue; - - double nx = dx / d; - double ny = dy / d; - double nz = dz / d; - - // α: angle between normals projected - double alpha = std::acos(std::max(-1.0, std::min(1.0, nz))); - // φ: azimuthal angle - double phi = std::atan2(ny, nx); - // θ: elevation angle - double theta = std::asin(std::max(-1.0, std::min(1.0, nz))); - - int a_bin = static_cast((alpha / M_PI) * 10); - int p_bin = static_cast(((phi + M_PI) / (2 * M_PI)) * 10); - int t_bin = static_cast(((theta + M_PI_2) / M_PI) * 10); - - if (a_bin >= 0 && a_bin < 11) alpha_bins[a_bin]++; - if (p_bin >= 0 && p_bin < 11) phi_bins[p_bin]++; - if (t_bin >= 0 && t_bin < 11) theta_bins[t_bin]++; - } - } - - // 归一化并填充 33 维 - double a_sum = std::accumulate(alpha_bins.begin(), alpha_bins.end(), 0.0); - double p_sum = std::accumulate(phi_bins.begin(), phi_bins.end(), 0.0); - double t_sum = std::accumulate(theta_bins.begin(), theta_bins.end(), 0.0); - - for (int i = 0; i < 11; ++i) { - desc.histogram[static_cast(i)] = (a_sum > 0) ? alpha_bins[i] / a_sum : 0.0; - desc.histogram[static_cast(i + 11)] = (p_sum > 0) ? phi_bins[i] / p_sum : 0.0; - desc.histogram[static_cast(i + 22)] = (t_sum > 0) ? theta_bins[i] / t_sum : 0.0; - } - - return desc; -} - -ShapeDescriptor SimilaritySearch::compute_shape_descriptor( - const brep::BrepModel& body, - const std::string& model_id, - const std::string& model_path, - int esf_samples, - int fpfh_samples) const -{ - ShapeDescriptor sd; - sd.esf = compute_esf(body, esf_samples); - sd.fpfh = compute_fpfh(body, fpfh_samples); - sd.model_id = model_id; - sd.model_path = model_path; - return sd; -} - -void SimilaritySearch::build_index(const std::vector& descriptors) { - impl_->index = descriptors; -} - -void SimilaritySearch::add_to_index(const ShapeDescriptor& desc) { - impl_->index.push_back(desc); -} - -size_t SimilaritySearch::index_size() const noexcept { - return impl_->index.size(); -} - -void SimilaritySearch::clear_index() { - impl_->index.clear(); -} - -double SimilaritySearch::esf_distance(const ESFDescriptor& a, const ESFDescriptor& b) { - double dist = 0.0; - for (size_t i = 0; i < ESFDescriptor::kDim; ++i) { - dist += std::abs(a.histogram[i] - b.histogram[i]); - } - return dist; -} - -double SimilaritySearch::fpfh_distance(const FPFHDescriptor& a, const FPFHDescriptor& b) { - double dist = 0.0; - for (size_t i = 0; i < FPFHDescriptor::kDim; ++i) { - double d = a.histogram[i] - b.histogram[i]; - dist += d * d; - } - return std::sqrt(dist); -} - -double SimilaritySearch::combined_similarity( - const ShapeDescriptor& a, const ShapeDescriptor& b, - double esf_weight) -{ - double e_dist = esf_distance(a.esf, b.esf); - double f_dist = fpfh_distance(a.fpfh, b.fpfh); - - // 转换距离为相似度:相似度 = exp(-distance) - double e_sim = std::exp(-e_dist * 0.5); - double f_sim = std::exp(-f_dist * 2.0); - - return esf_weight * e_sim + (1.0 - esf_weight) * f_sim; -} - -std::vector SimilaritySearch::query( - const brep::BrepModel& body, int k) const -{ - auto desc = compute_shape_descriptor(body); - return query_by_descriptor(desc, k); -} - -std::vector SimilaritySearch::query_by_descriptor( - const ShapeDescriptor& desc, int k) const -{ - std::vector results; - - for (const auto& indexed : impl_->index) { - SimilarityMatch match; - match.model_id = indexed.model_id; - match.model_path = indexed.model_path; - match.esf_distance = esf_distance(desc.esf, indexed.esf); - match.fpfh_distance = fpfh_distance(desc.fpfh, indexed.fpfh); - match.similarity = combined_similarity(desc, indexed); - results.push_back(match); - } - - // 按相似度降序排序 - std::sort(results.begin(), results.end(), - [](const SimilarityMatch& a, const SimilarityMatch& b) { - return a.similarity > b.similarity; - }); - - if (k > 0 && static_cast(k) < results.size()) { - results.resize(static_cast(k)); - } - - return results; -} - -// ═══════════════════════════════════════════════════════════ -// 辅助函数 -// ═══════════════════════════════════════════════════════════ - -std::vector sample_surface_points(const brep::BrepModel& body, int num_samples) { - std::vector points; - - const auto nf = body.num_faces(); - if (nf == 0 || num_samples <= 0) return points; - - // 每个面分配的采样点数(均匀分配) - int samples_per_face = std::max(1, num_samples / static_cast(nf)); - - std::mt19937 rng(42); - std::uniform_real_distribution dist(0.0, 1.0); - - for (size_t fi = 0; fi < nf; ++fi) { - const auto& face = body.face(static_cast(fi)); - const auto& surf = body.surface(face.surface_id); - - for (int s = 0; s < samples_per_face; ++s) { - double u = dist(rng); - double v = dist(rng); - try { - auto pt = surf.evaluate(u, v); - points.push_back(pt); - } catch (...) { - // 某些 (u,v) 可能超出有效域,跳过 - } - } - } - - return points; -} - -std::vector face_centroids(const brep::BrepModel& body) { - std::vector centroids; - const auto nf = body.num_faces(); - centroids.reserve(nf); - - for (size_t fi = 0; fi < nf; ++fi) { - const auto& face = body.face(static_cast(fi)); - const auto& surf = body.surface(face.surface_id); - try { - auto pt = surf.evaluate(0.5, 0.5); - centroids.push_back(pt); - } catch (...) { - centroids.push_back(Point3D{0, 0, 0}); - } - } - - return centroids; -} - -} // namespace vde::ai diff --git a/src/ai/generative_design.cpp b/src/ai/generative_design.cpp deleted file mode 100644 index bf3854f..0000000 --- a/src/ai/generative_design.cpp +++ /dev/null @@ -1,772 +0,0 @@ -#include "vde/ai/generative_design.h" -#include "vde/core/aabb.h" -#include -#include -#include -#include -#include - -namespace vde::ai { - -// ═══════════════════════════════════════════════════════════ -// 拓扑优化 (SIMP 方法) -// ═══════════════════════════════════════════════════════════ - -namespace { - -// 3D 索引 → 1D 索引 -inline int idx3d(int x, int y, int z, int res) { - return (z * res + y) * res + x; -} - -// 密度过滤:对每个体素进行邻域加权平均 -void density_filter(std::vector& rho, int res, double r_min) { - std::vector rho_old = rho; - int ir_min = static_cast(std::ceil(r_min)); - - // 预计算滤波权重 - std::vector weights; - std::vector offsets; - for (int dz = -ir_min; dz <= ir_min; ++dz) { - for (int dy = -ir_min; dy <= ir_min; ++dy) { - for (int dx = -ir_min; dx <= ir_min; ++dx) { - double dist = std::sqrt(static_cast(dx*dx + dy*dy + dz*dz)); - if (dist <= r_min) { - double w = r_min - dist; - weights.push_back(w); - offsets.push_back(idx3d(dx, dy, dz, res)); - } - } - } - } - - const int n = res * res * res; - for (int z = 0; z < res; ++z) { - for (int y = 0; y < res; ++y) { - for (int x = 0; x < res; ++x) { - int center = idx3d(x, y, z, res); - double sum_w = 0.0; - double sum_wr = 0.0; - - for (size_t k = 0; k < offsets.size(); ++k) { - int nx = x + offsets[k] % res; - int ny = y + (offsets[k] / res) % res; - int nz = z + offsets[k] / (res * res); - - // 边界检查 - if (nx < 0) nx = -nx - 1; - if (ny < 0) ny = -ny - 1; - if (nz < 0) nz = -nz - 1; - if (nx >= res) nx = 2 * res - nx - 1; - if (ny >= res) ny = 2 * res - ny - 1; - if (nz >= res) nz = 2 * res - nz - 1; - - if (nx >= 0 && nx < res && ny >= 0 && ny < res && nz >= 0 && nz < res) { - int ni = idx3d(nx, ny, nz, res); - sum_w += weights[k]; - sum_wr += weights[k] * rho_old[static_cast(ni)]; - } - } - - if (sum_w > 0) { - rho[static_cast(center)] = sum_wr / sum_w; - } - } - } - } -} - -// SIMP 灵敏度计算(简化:基于包围盒内的载荷投影) -void compute_sensitivity(const std::vector& rho, int res, - const AABB3D& bounds, - const std::vector& loads, - std::vector& sensitivity) { - const int n = res * res * res; - sensitivity.assign(static_cast(n), 0.0); - - auto extent = bounds.extent(); - double cell_size = extent.x() / res; - - for (const auto& load : loads) { - // 将载荷位置映射到体素索引 - double rx = (load.position.x() - bounds.min().x()) / extent.x(); - double ry = (load.position.y() - bounds.min().y()) / extent.y(); - double rz = (load.position.z() - bounds.min().z()) / extent.z(); - - int lx = static_cast(rx * res); - int ly = static_cast(ry * res); - int lz = static_cast(rz * res); - - // 载荷影响范围(高斯扩散) - int influence_radius = std::max(1, res / 8); - for (int dz = -influence_radius; dz <= influence_radius; ++dz) { - for (int dy = -influence_radius; dy <= influence_radius; ++dy) { - for (int dx = -influence_radius; dx <= influence_radius; ++dx) { - int nx = lx + dx; - int ny = ly + dy; - int nz = lz + dz; - if (nx < 0 || nx >= res || ny < 0 || ny >= res || nz < 0 || nz >= res) continue; - - double dist = std::sqrt(static_cast(dx*dx + dy*dy + dz*dz)); - double factor = std::exp(-dist / (influence_radius * 0.5)); - int idx = idx3d(nx, ny, nz, res); - sensitivity[static_cast(idx)] += factor * load.magnitude; - } - } - } - } -} - -// Optimality Criteria (OC) 更新 -void oc_update(std::vector& rho, const std::vector& sensitivity, - int res, double vol_frac, double penal, double move = 0.2) { - const int n = res * res * res; - double l1 = 0.0, l2 = 1e9; - - // 二分搜索拉格朗日乘子 - for (int iter = 0; iter < 50; ++iter) { - double lmid = 0.5 * (l1 + l2); - double vol = 0.0; - - for (int i = 0; i < n; ++i) { - double si = std::max(sensitivity[static_cast(i)], 1e-12); - double be = si / lmid; - double rho_new = rho[static_cast(i)] * std::pow(be, penal); - rho_new = std::max(0.001, std::min(1.0, rho_new)); - - // 移动限制 - rho_new = std::max(rho[static_cast(i)] - move, - std::min(rho[static_cast(i)] + move, rho_new)); - - rho[static_cast(i)] = rho_new; - vol += rho_new; - } - - if (vol / n - vol_frac > 0) { - l1 = lmid; - } else { - l2 = lmid; - } - - if (std::abs(l2 - l1) < 1e-6) break; - } -} - -// Marching Cubes 边表简化版(8 体素 × 12 边) -struct EdgeVertex { - int edge_index; - double t; // 插值参数 -}; - -// 等值面提取:简化版 Marching Cubes -HalfedgeMesh simple_marching_cubes(const std::vector& field, int res, - const AABB3D& bounds, double iso) { - HalfedgeMesh mesh; - - auto extent = bounds.extent(); - double dx = extent.x() / (res - 1); - double dy = extent.y() / (res - 1); - double dz = extent.z() / (res - 1); - - // 遍历每个体素 - std::vector verts; - std::vector> faces; - - for (int z = 0; z < res - 1; ++z) { - for (int y = 0; y < res - 1; ++y) { - for (int x = 0; x < res - 1; ++x) { - // 8 个角点的值 - double v[8]; - v[0] = field[static_cast(idx3d(x, y, z, res))]; - v[1] = field[static_cast(idx3d(x+1, y, z, res))]; - v[2] = field[static_cast(idx3d(x+1, y+1, z, res))]; - v[3] = field[static_cast(idx3d(x, y+1, z, res))]; - v[4] = field[static_cast(idx3d(x, y, z+1, res))]; - v[5] = field[static_cast(idx3d(x+1, y, z+1, res))]; - v[6] = field[static_cast(idx3d(x+1, y+1, z+1, res))]; - v[7] = field[static_cast(idx3d(x, y+1, z+1, res))]; - - // 立方体索引 - int cube_idx = 0; - for (int i = 0; i < 8; ++i) { - if (v[i] > iso) cube_idx |= (1 << i); - } - - if (cube_idx == 0 || cube_idx == 255) continue; - - // 12 条边上的插值点 - Point3D edge_pts[12]; - bool edge_valid[12] = {false}; - - // 边定义(体素局部坐标) - auto interpolate = [&](int e, double v0, double v1, - double x0, double y0, double z0, - double x1, double y1, double z1) { - if ((v0 > iso) == (v1 > iso)) return; - double t = (iso - v0) / (v1 - v0); - t = std::max(0.0, std::min(1.0, t)); - double px = bounds.min().x() + (x + x0 + t * (x1 - x0)) * dx; - double py = bounds.min().y() + (y + y0 + t * (y1 - y0)) * dy; - double pz = bounds.min().z() + (z + z0 + t * (z1 - z0)) * dz; - edge_pts[e] = Point3D{px, py, pz}; - edge_valid[e] = true; - }; - - interpolate(0, v[0], v[1], 0,0,0, 1,0,0); - interpolate(1, v[1], v[2], 1,0,0, 1,1,0); - interpolate(2, v[2], v[3], 1,1,0, 0,1,0); - interpolate(3, v[3], v[0], 0,1,0, 0,0,0); - interpolate(4, v[4], v[5], 0,0,1, 1,0,1); - interpolate(5, v[5], v[6], 1,0,1, 1,1,1); - interpolate(6, v[6], v[7], 1,1,1, 0,1,1); - interpolate(7, v[7], v[4], 0,1,1, 0,0,1); - interpolate(8, v[0], v[4], 0,0,0, 0,0,1); - interpolate(9, v[1], v[5], 1,0,0, 1,0,1); - interpolate(10, v[2], v[6], 1,1,0, 1,1,1); - interpolate(11, v[3], v[7], 0,1,0, 0,1,1); - - // 简化三角剖分:对每个活跃体素生成 2 个三角形 - // 收集活跃边 - std::vector active_edges; - for (int e = 0; e < 12; ++e) - if (edge_valid[e]) active_edges.push_back(e); - - if (active_edges.size() >= 3) { - // 贪心三角形扇 - for (size_t e = 1; e + 1 < active_edges.size(); ++e) { - int base = static_cast(verts.size()); - verts.push_back(edge_pts[active_edges[0]]); - verts.push_back(edge_pts[active_edges[e]]); - verts.push_back(edge_pts[active_edges[e + 1]]); - faces.push_back({base, base + 1, base + 2}); - } - } - } - } - } - - if (!verts.empty()) { - mesh.build_from_triangles(verts, faces); - } - - return mesh; -} - -} // anonymous namespace - -TopologyOptimizationResult topology_optimization( - const AABB3D& design_space, - const std::vector& loads, - const std::vector& constraints, - const OptimizationConstraints& opt_constraints) -{ - TopologyOptimizationResult result; - const int res = opt_constraints.grid_resolution; - const int n = res * res * res; - - // 1. 初始化均匀密度场 - std::vector rho(static_cast(n), opt_constraints.volume_fraction); - - // 对约束点附近强制设 1.0(固定区域必须保留材料) - auto extent = design_space.extent(); - double cell_size = extent.x() / res; - int solid_radius = std::max(1, res / 16); - - for (const auto& fc : constraints) { - double rx = (fc.position.x() - design_space.min().x()) / extent.x(); - double ry = (fc.position.y() - design_space.min().y()) / extent.y(); - double rz = (fc.position.z() - design_space.min().z()) / extent.z(); - int cx = static_cast(rx * res); - int cy = static_cast(ry * res); - int cz = static_cast(rz * res); - - for (int dz = -solid_radius; dz <= solid_radius; ++dz) - for (int dy = -solid_radius; dy <= solid_radius; ++dy) - for (int dx = -solid_radius; dx <= solid_radius; ++dx) { - int nx = cx + dx, ny = cy + dy, nz = cz + dz; - if (nx >= 0 && nx < res && ny >= 0 && ny < res && nz >= 0 && nz < res) { - rho[static_cast(idx3d(nx, ny, nz, res))] = 1.0; - } - } - } - - // 2. SIMP 迭代循环 - std::vector sensitivity; - double prev_change = 1e9; - - for (int iter = 0; iter < opt_constraints.max_iterations; ++iter) { - // a. 密度过滤 - density_filter(rho, res, opt_constraints.filter_radius); - - // b. 灵敏度分析 - compute_sensitivity(rho, res, design_space, loads, sensitivity); - - // c. OC 更新 - std::vector rho_old = rho; - oc_update(rho, sensitivity, res, opt_constraints.volume_fraction, - opt_constraints.penalization_power); - - // d. 确保约束区域保持固体 - for (const auto& fc : constraints) { - double rx = (fc.position.x() - design_space.min().x()) / extent.x(); - double ry = (fc.position.y() - design_space.min().y()) / extent.y(); - double rz = (fc.position.z() - design_space.min().z()) / extent.z(); - int cx = static_cast(rx * res); - int cy = static_cast(ry * res); - int cz = static_cast(rz * res); - for (int dz = -solid_radius; dz <= solid_radius; ++dz) - for (int dy = -solid_radius; dy <= solid_radius; ++dy) - for (int dx = -solid_radius; dx <= solid_radius; ++dx) { - int nx = cx + dx, ny = cy + dy, nz = cz + dz; - if (nx >= 0 && nx < res && ny >= 0 && ny < res && nz >= 0 && nz < res) { - rho[static_cast(idx3d(nx, ny, nz, res))] = 1.0; - } - } - } - - // e. 收敛检查 - double change = 0.0; - for (int i = 0; i < n; ++i) { - change += std::abs(rho[static_cast(i)] - rho_old[static_cast(i)]); - } - change /= n; - - if (change < opt_constraints.convergence_tolerance) { - result.converged = true; - result.iterations = iter + 1; - break; - } - prev_change = change; - result.iterations = iter + 1; - } - - // 3. 计算最终体积分数 - double total_vol = 0.0; - for (int i = 0; i < n; ++i) total_vol += rho[static_cast(i)]; - result.final_volume_fraction = total_vol / n; - - // 4. 等值面提取 - result.density_field = rho; - result.grid_resolution = res; - - result.optimized_mesh = extract_isosurface(rho, res, design_space, 0.5); - - // 5. B-Rep 重建(如果网格非空) - if (result.optimized_mesh.num_vertices() > 0 && result.optimized_mesh.num_faces() > 0) { - result.reconstructed_body = mesh_to_brep_reconstruct(result.optimized_mesh); - } - - result.status_message = result.converged ? "Converged" : "Max iterations reached"; - return result; -} - -HalfedgeMesh extract_isosurface( - const std::vector& density_field, - int resolution, - const AABB3D& bounds, - double iso_level) -{ - return simple_marching_cubes(density_field, resolution, bounds, iso_level); -} - -brep::BrepModel mesh_to_brep_reconstruct( - const HalfedgeMesh& mesh, double simplification_angle) -{ - (void)simplification_angle; - brep::BrepModel body; - - // 简化 B-Rep 重建:将网格的每个三角形映射为一个 B-Rep 面 - // 在实际系统中会使用面片合并和 NURBS 曲面拟合 - const auto nf = mesh.num_faces(); - if (nf == 0) return body; - - // 收集所有顶点作为 B-Rep 顶点 - const auto nv = mesh.num_vertices(); - if (nv == 0) return body; - - // 注意: 此处为简化实现,真实系统会做平面识别和 NURBS 拟合 - // 返回一个空体表示重建尚未完全实现 - // 用户应使用 optimized_mesh 字段获取三角网格 - - return body; -} - -// ═══════════════════════════════════════════════════════════ -// 晶格结构生成 -// ═══════════════════════════════════════════════════════════ - -double lattice_sdf(double x, double y, double z, - LatticeCellType cell_type, - double cell_size, double strut_thickness) -{ - const double pi = M_PI; - const double f = 2.0 * pi / cell_size; - double fx = f * x, fy = f * y, fz = f * z; - - switch (cell_type) { - case LatticeCellType::Gyroid: { - // TPMS Gyroid: sin(x)cos(y) + sin(y)cos(z) + sin(z)cos(x) = 0 - double sdf_val = std::sin(fx) * std::cos(fy) - + std::sin(fy) * std::cos(fz) - + std::sin(fz) * std::cos(fx); - // 厚度偏移: 绝对值 < strut_thickness ⇒ 实体材料 - return std::abs(sdf_val) - strut_thickness * 2.0; - } - case LatticeCellType::Diamond: { - // TPMS Diamond - double sdf_val = std::sin(fx) * std::sin(fy) * std::sin(fz) - + std::sin(fx) * std::cos(fy) * std::cos(fz) - + std::cos(fx) * std::sin(fy) * std::cos(fz) - + std::cos(fx) * std::cos(fy) * std::sin(fz); - return std::abs(sdf_val) - strut_thickness * 1.5; - } - case LatticeCellType::BCC: { - // BCC: 8 个角点 + 1 个体心 → 支柱连接 - // 距离到最近的支柱骨架 - double cx = std::fmod(x, cell_size); - double cy = std::fmod(y, cell_size); - double cz = std::fmod(z, cell_size); - double h = cell_size * 0.5; - - // 到体心 (h,h,h) 的距离和到最近角点的距离取 min - double d_center = std::sqrt((cx-h)*(cx-h) + (cy-h)*(cy-h) + (cz-h)*(cz-h)); - double d_corner = std::sqrt(cx*cx + cy*cy + cz*cz); - d_corner = std::min(d_corner, std::sqrt((cx-cell_size)*(cx-cell_size) + cy*cy + cz*cz)); - d_corner = std::min(d_corner, std::sqrt(cx*cx + (cy-cell_size)*(cy-cell_size) + cz*cz)); - d_corner = std::min(d_corner, std::sqrt(cx*cx + cy*cy + (cz-cell_size)*(cz-cell_size))); - - return std::min(d_center, d_corner) - strut_thickness * cell_size; - } - case LatticeCellType::FCC: { - // FCC: 8 角点 + 6 面心 → 支柱连接 - double cx = std::fmod(x, cell_size); - double cy = std::fmod(y, cell_size); - double cz = std::fmod(z, cell_size); - double h = cell_size * 0.5; - - double d_corner = std::sqrt(cx*cx + cy*cy + cz*cz); - double d_face_x = std::sqrt((cx-h)*(cx-h) + cy*cy + cz*cz); - double d_face_y = std::sqrt(cx*cx + (cy-h)*(cy-h) + cz*cz); - double d_face_z = std::sqrt(cx*cx + cy*cy + (cz-h)*(cz-h)); - - return std::min({d_corner, d_face_x, d_face_y, d_face_z}) - - strut_thickness * cell_size; - } - case LatticeCellType::Octet: { - // Octet Truss: 八面体桁架 - double cx = std::fmod(x, cell_size); - double cy = std::fmod(y, cell_size); - double cz = std::fmod(z, cell_size); - double h = cell_size * 0.5; - - // 到体心 (h,h,h) 的距离:在 XY/YZ/XZ 对角平面上的支柱 - double d1 = std::abs(cx - h) + std::abs(cy - h) - h; - double d2 = std::abs(cy - h) + std::abs(cz - h) - h; - double d3 = std::abs(cx - h) + std::abs(cz - h) - h; - double d_diag = std::max({-d1, -d2, -d3}); - - double d_corner = std::sqrt(cx*cx + cy*cy + cz*cz); - - return std::min(d_corner, d_diag) - strut_thickness * cell_size; - } - case LatticeCellType::Cubic: { - // 简单立方:轴对齐支架 - double cx = std::fmod(x, cell_size); - double cy = std::fmod(y, cell_size); - double cz = std::fmod(z, cell_size); - double h = cell_size * 0.5; - double thick = strut_thickness * cell_size; - - double dx = std::min(std::abs(cx), std::abs(cx - cell_size)); - double dy = std::min(std::abs(cy), std::abs(cy - cell_size)); - double dz_val = std::min(std::abs(cz), std::abs(cz - cell_size)); - - // 支柱沿 X/Y/Z 轴 - double d_x_axis = std::sqrt(dy*dy + dz_val*dz_val); - double d_y_axis = std::sqrt(dx*dx + dz_val*dz_val); - double d_z_axis = std::sqrt(dx*dx + dy*dy); - - return std::min({d_x_axis, d_y_axis, d_z_axis}) - thick; - } - } - - return 1.0; // unreachable -} - -std::pair, int> compute_variable_density_map( - const brep::BrepModel& body, - const std::vector& loads, - double cell_size, - double stress_threshold) -{ - // 简单实现:基于载荷距离的密度映射 - auto bbox_min = Point3D{0,0,0}; - auto bbox_max = Point3D{10,10,10}; // 默认 10x10x10 包围盒 - - int res = 20; - std::vector density_map(static_cast(res*res*res), 0.3); - - if (loads.empty()) return {density_map, res}; - - auto extent = Vector3D{ - bbox_max.x() - bbox_min.x(), - bbox_max.y() - bbox_min.y(), - bbox_max.z() - bbox_min.z() - }; - - double dx = extent.x() / res; - double dy = extent.y() / res; - double dz = extent.z() / res; - - for (int zi = 0; zi < res; ++zi) { - for (int yi = 0; yi < res; ++yi) { - for (int xi = 0; xi < res; ++xi) { - Point3D p{ - bbox_min.x() + (xi + 0.5) * dx, - bbox_min.y() + (yi + 0.5) * dy, - bbox_min.z() + (zi + 0.5) * dz - }; - - double max_influence = 0.0; - for (const auto& load : loads) { - double d = std::sqrt( - (p.x()-load.position.x())*(p.x()-load.position.x()) + - (p.y()-load.position.y())*(p.y()-load.position.y()) + - (p.z()-load.position.z())*(p.z()-load.position.z()) - ); - double influence = load.magnitude / (1.0 + d * d); - max_influence = std::max(max_influence, influence); - } - - double density = std::min(1.0, 0.2 + max_influence * 5.0); - density_map[static_cast(idx3d(xi, yi, zi, res))] = density; - } - } - } - - (void)body; - (void)cell_size; - (void)stress_threshold; - - return {density_map, res}; -} - -LatticeGenerationResult lattice_generation( - const brep::BrepModel& body, - LatticeCellType cell_type, - const LatticeParams& params) -{ - LatticeGenerationResult result; - - // 简化实现:使用 lattice_sdf 构建一个规则晶格 - auto bbox_min = Point3D{0, 0, 0}; - auto bbox_max = Point3D{10, 10, 10}; - - int grid_res = 64; - double cell_size = params.cell_size; - - // 在包围盒内填充厚度密度因子 - double surface_thick = params.surface_thickness; - double strut = params.strut_thickness; - - // 使用简单 Marching Cubes 从 SDF 采样生成晶格 - auto extent = Vector3D{ - bbox_max.x() - bbox_min.x(), - bbox_max.y() - bbox_min.y(), - bbox_max.z() - bbox_min.z() - }; - - AABB3D bounds{bbox_min, bbox_max}; - std::vector field(static_cast(grid_res * grid_res * grid_res), 1.0); - - double dx = extent.x() / grid_res; - double dy = extent.y() / grid_res; - double dz_val = extent.z() / grid_res; - - for (int zi = 0; zi < grid_res; ++zi) { - for (int yi = 0; yi < grid_res; ++yi) { - for (int xi = 0; xi < grid_res; ++xi) { - double x = bbox_min.x() + (xi + 0.5) * dx; - double y = bbox_min.y() + (yi + 0.5) * dy; - double z = bbox_min.z() + (zi + 0.5) * dz_val; - - double sdf = lattice_sdf(x, y, z, cell_type, cell_size, strut); - - if (params.variable_density && !params.density_map.empty()) { - // 变密度: 根据密度映射调整厚度 - int dix = static_cast(xi * params.density_map_resolution / grid_res); - int diy = static_cast(yi * params.density_map_resolution / grid_res); - int diz = static_cast(zi * params.density_map_resolution / grid_res); - int d_res = params.density_map_resolution; - double local_rho = 0.5; - if (dix >= 0 && dix < d_res && diy >= 0 && diy < d_res && diz >= 0 && diz < d_res) { - local_rho = params.density_map[static_cast(idx3d(dix, diy, diz, d_res))]; - } - double adjusted_strut = strut * (0.2 + 0.8 * local_rho); - sdf = lattice_sdf(x, y, z, cell_type, cell_size, adjusted_strut); - } - - field[static_cast(idx3d(xi, yi, zi, grid_res))] = sdf; - } - } - } - - result.lattice_mesh = simple_marching_cubes(field, grid_res, bounds, 0.0); - - if (result.lattice_mesh.num_vertices() > 0 && result.lattice_mesh.num_faces() > 0) { - result.lattice_body = mesh_to_brep_reconstruct(result.lattice_mesh); - } - - // 估算单元数 - auto bbox_vol = extent.x() * extent.y() * extent.z(); - double cell_vol = cell_size * cell_size * cell_size; - result.cell_count = static_cast(bbox_vol / cell_vol); - - // 估算孔隙率 - int inside_count = 0; - for (auto v : field) { if (v < 0) inside_count++; } - result.porosity = 1.0 - static_cast(inside_count) / static_cast(field.size()); - - result.success = true; - return result; -} - -// ═══════════════════════════════════════════════════════════ -// GAN 3D 生成 -// ═══════════════════════════════════════════════════════════ - -std::vector encode_conditions(const GANCondition& conditions) { - // 编码为 32 维条件向量 - std::vector cond(32, 0.0); - - // 载荷编码 (features 0..8, 3 个载荷) - size_t load_count = std::min(conditions.loads.size(), static_cast(3)); - for (size_t i = 0; i < load_count; ++i) { - size_t base = i * 3; - cond[base + 0] = conditions.loads[i].force.x() * 0.1; - cond[base + 1] = conditions.loads[i].force.y() * 0.1; - cond[base + 2] = conditions.loads[i].magnitude * 0.01; - } - - // 约束编码 (features 9..14, 2 个约束) - size_t constraint_count = std::min(conditions.constraints.size(), static_cast(2)); - for (size_t i = 0; i < constraint_count; ++i) { - size_t base = 9 + i * 3; - cond[base + 0] = conditions.constraints[i].fix_x ? 1.0 : 0.0; - cond[base + 1] = conditions.constraints[i].fix_y ? 1.0 : 0.0; - cond[base + 2] = conditions.constraints[i].fix_z ? 1.0 : 0.0; - } - - // 目标体积/刚度 (features 15..16) - cond[15] = conditions.target_volume * 0.1; - cond[16] = conditions.target_stiffness * 0.01; - - // 风格标签编码 (features 17..20, 简单哈希) - if (!conditions.style_tag.empty()) { - size_t hash = std::hash{}(conditions.style_tag) % 4; - cond[17 + hash] = 1.0; - } - - // 其余维度保持为 0(可扩展) - return cond; -} - -GANGenerationResult generative_adversarial_3d( - const GANCondition& conditions, - const std::string& model_path, - int resolution) -{ - GANGenerationResult result; - - (void)model_path; // ONNX 模型路径(当集成推理引擎时使用) - - // 条件编码 - auto cond_vec = encode_conditions(conditions); - - // 模拟 Generator 输出: 生成 3D SDF 体素网格 - // 在实际系统中会加载 ONNX Generator 模型进行前向传播 - int n = resolution * resolution * resolution; - std::vector sdf_grid(static_cast(n), 0.0); - - // 基于条件的简单形状生成(回退到规则形状直到 ONNX 模型可用) - std::mt19937 rng(42); - std::normal_distribution noise(0.0, 0.1); - - for (int zi = 0; zi < resolution; ++zi) { - for (int yi = 0; yi < resolution; ++yi) { - for (int xi = 0; xi < resolution; ++xi) { - double cx = (xi - resolution * 0.5) / resolution; - double cy = (yi - resolution * 0.5) / resolution; - double cz = (zi - resolution * 0.5) / resolution; - - // 基础球体 SDF (半径 0.35),加入条件调制的变形 - double r = std::sqrt(cx*cx + cy*cy + cz*cz); - double sdf = r - 0.35; - - // 条件调制的各向异性缩放 - double sx = 1.0 + cond_vec[0] * 0.2; - double sy = 1.0 + cond_vec[1] * 0.2; - double sz = 1.0 + cond_vec[2] * 0.2; - - // 包围盒约束(减去约束区域的 SDF) - for (const auto& fc : conditions.constraints) { - // 在约束位置附近开放空间 - double dx_val = cx - 0.3; - double dy_val = cy; - double dz_v = cz; - double d = std::sqrt(dx_val*dx_val + dy_val*dy_val + dz_v*dz_v); - sdf = std::max(sdf, -(d - 0.1)); // 在约束点附近挖空 - } - - sdf += noise(rng) * 0.02; - sdf_grid[static_cast(idx3d(xi, yi, zi, resolution))] = sdf; - } - } - } - - // Marching Cubes → 网格 - AABB3D bounds{Point3D{-1,-1,-1}, Point3D{1,1,1}}; - result.generated_mesh = simple_marching_cubes(sdf_grid, resolution, bounds, 0.0); - - if (result.generated_mesh.num_vertices() > 0 && result.generated_mesh.num_faces() > 0) { - result.generated_body = mesh_to_brep_reconstruct(result.generated_mesh); - } - - result.success = true; - result.generation_time_ms = 50.0; // 模拟耗时 - result.latent_z_score = 0.75; - result.candidate_tags = {"aerospace", "automotive", "biomedical", "consumer"}; - - return result; -} - -std::vector latent_interpolation( - const std::vector& z0, - const std::vector& z1, - int steps, - const GANCondition& conditions, - const std::string& model_path) -{ - std::vector results; - if (steps < 2) return results; - - for (int i = 0; i < steps; ++i) { - double t = static_cast(i) / static_cast(steps - 1); - - // 线性插值潜在向量 - std::vector z(z0.size()); - for (size_t j = 0; j < z.size(); ++j) { - z[j] = z0[j] + t * (z1[j] - z0[j]); - } - - // 插值向量嵌入 conditions(通过 style_tag) - GANCondition cond = conditions; - cond.style_tag = "interpolated_t" + std::to_string(i); - - results.push_back(generative_adversarial_3d(cond, model_path)); - } - - return results; -} - -} // namespace vde::ai diff --git a/src/ai/inference_engine.cpp b/src/ai/inference_engine.cpp deleted file mode 100644 index 79f9941..0000000 --- a/src/ai/inference_engine.cpp +++ /dev/null @@ -1,588 +0,0 @@ -#include "vde/ai/inference_engine.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace vde::ai { - -// ═══════════════════════════════════════════════════════════ -// Tensor 实现 -// ═══════════════════════════════════════════════════════════ - -const float* Tensor::data_f32() const { - return reinterpret_cast(data.data()); -} - -float* Tensor::mutable_data_f32() { - return reinterpret_cast(data.data()); -} - -const int64_t* Tensor::data_i64() const { - return reinterpret_cast(data.data()); -} - -size_t Tensor::byte_size() const { - return data.size(); -} - -Tensor Tensor::make_f32(const std::vector& shape) { - Tensor t; - t.dtype = TensorDataType::Float32; - t.shape = shape; - t.element_count = 1; - for (auto dim : shape) t.element_count *= static_cast(dim); - t.data.resize(t.element_count * sizeof(float), 0); - return t; -} - -Tensor Tensor::make_i64(const std::vector& shape) { - Tensor t; - t.dtype = TensorDataType::Int64; - t.shape = shape; - t.element_count = 1; - for (auto dim : shape) t.element_count *= static_cast(dim); - t.data.resize(t.element_count * sizeof(int64_t), 0); - return t; -} - -// ═══════════════════════════════════════════════════════════ -// InferenceSession 实现 -// ═══════════════════════════════════════════════════════════ - -struct InferenceSession::Impl { - std::vector model_data; - bool loaded = false; - std::unordered_map> input_info_map; - std::unordered_map> output_info_map; - std::unordered_map metadata; -}; - -InferenceSession::InferenceSession() - : impl_(std::make_unique()) -{ -} - -InferenceSession::InferenceSession(const InferenceConfig& config) - : impl_(std::make_unique()), config_(config) -{ -} - -InferenceSession::~InferenceSession() = default; - -bool InferenceSession::load_model(const std::string& model_path) { - // 尝试从文件读取 ONNX 模型 - std::ifstream file(model_path, std::ios::binary | std::ios::ate); - if (!file.is_open()) { - // 模型文件不存在时返回 false,使用回退模式 - impl_->loaded = false; - return false; - } - - auto size = file.tellg(); - file.seekg(0, std::ios::beg); - - impl_->model_data.resize(static_cast(size)); - file.read(reinterpret_cast(impl_->model_data.data()), size); - file.close(); - - impl_->loaded = true; - - // 模拟输入/输出信息(实际系统从 ONNX 元数据读取) - impl_->input_info_map["input"] = {1, 8}; - impl_->output_info_map["output"] = {1, 10}; - impl_->metadata["framework"] = "onnx"; - impl_->metadata["producer"] = "ViewDesignEngine"; - - return true; -} - -bool InferenceSession::load_model_from_memory(const uint8_t* model_data, size_t data_size) { - if (!model_data || data_size == 0) return false; - - impl_->model_data.assign(model_data, model_data + data_size); - impl_->loaded = true; - impl_->input_info_map["input"] = {1, 8}; - impl_->output_info_map["output"] = {1, 10}; - impl_->metadata["framework"] = "onnx"; - impl_->metadata["source"] = "memory"; - - return true; -} - -bool InferenceSession::is_loaded() const noexcept { - return impl_->loaded; -} - -std::unordered_map> InferenceSession::input_info() const { - return impl_->input_info_map; -} - -std::unordered_map> InferenceSession::output_info() const { - return impl_->output_info_map; -} - -std::unordered_map InferenceSession::model_metadata() const { - return impl_->metadata; -} - -std::vector InferenceSession::run(const std::vector& inputs) { - auto t0 = std::chrono::steady_clock::now(); - - std::vector outputs; - - if (!impl_->loaded) { - // 回退:生成零输出 - for (const auto& [name, shape] : impl_->output_info_map) { - Tensor out; - out.name = name; - out.shape = shape; - out.dtype = TensorDataType::Float32; - out.element_count = 1; - for (auto dim : shape) out.element_count *= static_cast(dim); - out.data.resize(out.element_count * sizeof(float), 0); - outputs.push_back(std::move(out)); - } - } else { - // 模拟推理: 对每个输入做恒等映射(回退到无操作) - for (const auto& [name, shape] : impl_->output_info_map) { - Tensor out = Tensor::make_f32(shape); - outputs.push_back(std::move(out)); - } - } - - auto t1 = std::chrono::steady_clock::now(); - double ms = std::chrono::duration(t1 - t0).count(); - - stats_.inference_time_ms = ms; - stats_.total_inferences++; - stats_.avg_inference_time_ms = - (stats_.avg_inference_time_ms * (stats_.total_inferences - 1) + ms) - / stats_.total_inferences; - - stats_.input_size_bytes = 0; - for (const auto& inp : inputs) stats_.input_size_bytes += inp.byte_size(); - stats_.output_size_bytes = 0; - for (const auto& out : outputs) stats_.output_size_bytes += out.byte_size(); - - return outputs; -} - -Tensor InferenceSession::run_single(const Tensor& input) { - auto outputs = run({input}); - if (outputs.empty()) return Tensor{}; - return outputs[0]; -} - -void InferenceSession::reset_stats() { - stats_ = InferenceStats{}; -} - -std::vector InferenceSession::available_providers() const { - std::vector devices; - - // CPU 总是可用 - DeviceInfo cpu; - cpu.provider = ExecutionProvider::CPU; - cpu.device_id = 0; - cpu.device_name = "CPU"; - cpu.available = true; - devices.push_back(cpu); - - // CUDA 检测(延迟到运行时) - DeviceInfo cuda; - cuda.provider = ExecutionProvider::CUDA; - cuda.device_id = 0; - cuda.device_name = "CUDA (not initialized)"; - cuda.available = false; - devices.push_back(cuda); - - return devices; -} - -// ═══════════════════════════════════════════════════════════ -// TensorRTAccelerator 实现 -// ═══════════════════════════════════════════════════════════ - -struct TensorRTAccelerator::Impl { - std::vector engine_data; - bool engine_ready = false; -}; - -TensorRTAccelerator::TensorRTAccelerator() - : impl_(std::make_unique()) -{ -} - -TensorRTAccelerator::TensorRTAccelerator(const TensorRTConfig& config) - : impl_(std::make_unique()), config_(config) -{ -} - -TensorRTAccelerator::~TensorRTAccelerator() = default; - -bool TensorRTAccelerator::is_available() noexcept { - // 检查 CUDA 和 TensorRT 运行时是否可用 - // 实际系统:尝试 dlopen libnvinfer.so - return false; // 默认不可用(无 GPU 环境) -} - -bool TensorRTAccelerator::build_engine( - const std::string& onnx_model_path, - const std::string& engine_cache_path) -{ - if (!is_available()) return false; - - config_.onnx_model_path = onnx_model_path; - config_.engine_cache_path = engine_cache_path; - - // 尝试从 ONNX 构建 TensorRT 引擎 - // 实际系统会调用 nvonnxparser 和 nvinfer API - impl_->engine_data.clear(); - impl_->engine_ready = false; - - std::ifstream onnx_file(onnx_model_path, std::ios::binary); - if (!onnx_file.is_open()) return false; - - // 模拟引擎构建(实现在真实 CUDA 环境中) - // 读取 ONNX 数据然后进行图优化和层融合 - impl_->engine_ready = true; - status_.engine_built = true; - status_.engine_loaded = true; - - // 如果指定了缓存路径,序列化引擎 - if (!engine_cache_path.empty()) { - std::ofstream cache(engine_cache_path, std::ios::binary); - if (cache.is_open()) { - // 写入序列化引擎 - cache.write("TRT_ENGINE_PLACEHOLDER", 21); - cache.close(); - } - } - - return true; -} - -bool TensorRTAccelerator::load_engine(const std::string& engine_path) { - if (!is_available()) return false; - - std::ifstream file(engine_path, std::ios::binary | std::ios::ate); - if (!file.is_open()) return false; - - auto size = file.tellg(); - file.seekg(0, std::ios::beg); - - impl_->engine_data.resize(static_cast(size)); - file.read(reinterpret_cast(impl_->engine_data.data()), size); - file.close(); - - impl_->engine_ready = true; - status_.engine_built = true; - status_.engine_loaded = true; - status_.engine_size_bytes = static_cast(size); - - return true; -} - -std::vector TensorRTAccelerator::run(const std::vector& inputs) { - auto t0 = std::chrono::steady_clock::now(); - - std::vector outputs; - - if (!impl_->engine_ready) return outputs; - - // TensorRT 推理(回退到 dummy 输出) - // 实际系统会调用 context->executeV2() - for (size_t i = 0; i < inputs.size(); ++i) { - Tensor out = Tensor::make_f32({1, 10}); - outputs.push_back(std::move(out)); - } - - auto t1 = std::chrono::steady_clock::now(); - status_.inference_time_ms = std::chrono::duration(t1 - t0).count(); - status_.total_inferences++; - - return outputs; -} - -void TensorRTAccelerator::warmup(int warmup_runs) { - if (!impl_->engine_ready) return; - - // 创建 dummy 输入并执行 warmup 推理 - Tensor dummy = Tensor::make_f32({1, 8}); - for (int i = 0; i < warmup_runs; ++i) { - run({dummy}); - } -} - -double TensorRTAccelerator::benchmark( - const std::vector& input_shape, int iterations) { - if (!impl_->engine_ready) return 0.0; - - Tensor dummy = Tensor::make_f32(input_shape); - - auto t0 = std::chrono::steady_clock::now(); - for (int i = 0; i < iterations; ++i) { - run({dummy}); - } - auto t1 = std::chrono::steady_clock::now(); - - return std::chrono::duration(t1 - t0).count() / iterations; -} - -// ═══════════════════════════════════════════════════════════ -// ModelRegistry 实现 -// ═══════════════════════════════════════════════════════════ - -struct ModelRegistry::Impl { - // 模型版本存储: model_name → vector - std::unordered_map> models; -}; - -ModelRegistry::ModelRegistry(const ModelRegistryConfig& config) - : impl_(std::make_unique()), config_(config) -{ -} - -ModelRegistry::~ModelRegistry() = default; - -bool ModelRegistry::register_model(const ModelEntry& entry) { - if (entry.name.empty() || entry.version.empty()) return false; - - // 检查是否已存在相同版本 - auto& versions = impl_->models[entry.name]; - for (const auto& v : versions) { - if (v.version == entry.version) return false; // 版本已存在 - } - - versions.push_back(entry); - - // 按版本号排序(降序) - std::sort(versions.begin(), versions.end(), - [](const ModelEntry& a, const ModelEntry& b) { - return compare_versions(a.version, b.version) > 0; - }); - - // 自动清理过时版本 - if (config_.auto_cleanup && versions.size() > static_cast(config_.max_versions_per_model)) { - versions.resize(static_cast(config_.max_versions_per_model)); - } - - return true; -} - -bool ModelRegistry::register_model( - const std::string& name, - const std::string& version, - const std::string& path, - const std::string& framework, - const std::unordered_map& metadata) -{ - ModelEntry entry; - entry.name = name; - entry.version = version; - entry.path = path; - entry.framework = framework; - entry.metadata = metadata; - entry.registered_at = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count(); - entry.is_active = true; - - // 计算文件大小和校验和 - std::ifstream file(path, std::ios::binary | std::ios::ate); - if (file.is_open()) { - entry.file_size = file.tellg(); - file.close(); - entry.checksum = compute_sha256(path); - } - - return register_model(entry); -} - -std::optional ModelRegistry::get_latest(const std::string& name) const { - auto it = impl_->models.find(name); - if (it == impl_->models.end() || it->second.empty()) return std::nullopt; - - // 找第一个活跃版本(已按版本降序排列) - for (const auto& entry : it->second) { - if (entry.is_active) return entry; - } - - return std::nullopt; -} - -std::optional ModelRegistry::get_version( - const std::string& name, const std::string& version) const -{ - auto it = impl_->models.find(name); - if (it == impl_->models.end()) return std::nullopt; - - for (const auto& entry : it->second) { - if (entry.version == version && entry.is_active) return entry; - } - - return std::nullopt; -} - -std::optional ModelRegistry::get_best_model( - const std::string& name, const std::string& min_version) const -{ - auto it = impl_->models.find(name); - if (it == impl_->models.end() || it->second.empty()) return std::nullopt; - - // 找 ≥ min_version 的最新活跃版本 - for (const auto& entry : it->second) { - if (entry.is_active && compare_versions(entry.version, min_version) >= 0) { - return entry; - } - } - - return std::nullopt; -} - -std::vector ModelRegistry::list_versions(const std::string& name) const { - auto it = impl_->models.find(name); - if (it == impl_->models.end()) return {}; - return it->second; -} - -std::vector ModelRegistry::list_models() const { - std::vector names; - names.reserve(impl_->models.size()); - for (const auto& [name, versions] : impl_->models) { - (void)versions; - names.push_back(name); - } - std::sort(names.begin(), names.end()); - return names; -} - -bool ModelRegistry::deactivate(const std::string& name, const std::string& version) { - auto it = impl_->models.find(name); - if (it == impl_->models.end()) return false; - - for (auto& entry : it->second) { - if (entry.version == version) { - entry.is_active = false; - return true; - } - } - - return false; -} - -bool ModelRegistry::remove(const std::string& name, const std::string& version) { - auto it = impl_->models.find(name); - if (it == impl_->models.end()) return false; - - auto& versions = it->second; - auto entry_it = std::find_if(versions.begin(), versions.end(), - [&version](const ModelEntry& e) { return e.version == version; }); - - if (entry_it == versions.end()) return false; - - versions.erase(entry_it); - - if (versions.empty()) { - impl_->models.erase(it); - } - - return true; -} - -void ModelRegistry::cleanup() { - for (auto& [name, versions] : impl_->models) { - (void)name; - if (versions.size() > static_cast(config_.max_versions_per_model)) { - versions.resize(static_cast(config_.max_versions_per_model)); - } - } -} - -std::pair ModelRegistry::verify_checksums() const { - int valid = 0, invalid = 0; - - for (const auto& [name, versions] : impl_->models) { - (void)name; - for (const auto& entry : versions) { - if (entry.checksum.empty()) continue; - auto actual = compute_sha256(entry.path); - if (actual == entry.checksum) { - valid++; - } else { - invalid++; - } - } - } - - return {valid, invalid}; -} - -size_t ModelRegistry::total_entries() const noexcept { - size_t total = 0; - for (const auto& [name, versions] : impl_->models) { - (void)name; - total += versions.size(); - } - return total; -} - -int ModelRegistry::compare_versions(const std::string& a, const std::string& b) { - // 解析语义版本号 major.minor.patch - auto parse = [](const std::string& v) -> std::array { - std::array parts = {0, 0, 0}; - size_t pos = 0; - for (int i = 0; i < 3; ++i) { - size_t dot = v.find('.', pos); - std::string part = (dot == std::string::npos) ? v.substr(pos) : v.substr(pos, dot - pos); - try { - parts[static_cast(i)] = std::stoi(part); - } catch (...) { - parts[static_cast(i)] = 0; - } - if (dot == std::string::npos) break; - pos = dot + 1; - } - return parts; - }; - - auto va = parse(a); - auto vb = parse(b); - - for (int i = 0; i < 3; ++i) { - if (va[static_cast(i)] < vb[static_cast(i)]) return -1; - if (va[static_cast(i)] > vb[static_cast(i)]) return 1; - } - - return 0; -} - -std::string ModelRegistry::compute_sha256(const std::string& filepath) { - // 简化 SHA256 实现(使用简单哈希作为回退) - std::ifstream file(filepath, std::ios::binary); - if (!file.is_open()) return ""; - - // 使用 FNV-1a 64 位哈希作为轻量级替代 - uint64_t hash = 14695981039346656037ULL; - const uint64_t prime = 1099511628211ULL; - - char buffer[4096]; - while (file.read(buffer, sizeof(buffer)) || file.gcount() > 0) { - for (std::streamsize i = 0; i < file.gcount(); ++i) { - hash ^= static_cast(buffer[i]); - hash *= prime; - } - } - - // 格式化为 16 进制字符串 - std::ostringstream oss; - oss << std::hex << std::setfill('0') << std::setw(16) << hash; - return oss.str(); -} - -} // namespace vde::ai diff --git a/src/cloud/cloud_native.cpp b/src/cloud/cloud_native.cpp deleted file mode 100644 index b92587f..0000000 --- a/src/cloud/cloud_native.cpp +++ /dev/null @@ -1,438 +0,0 @@ -/// @file cloud_native.cpp 云原生协同设计 —— 实现 -#include "vde/cloud/cloud_native.h" -#include -#include -#include -#include - -namespace vde::cloud { - -// ═══════════════════════════════════════════════════════ -// OperationalTransform -// ═══════════════════════════════════════════════════════ - -bool OperationalTransform::same_entity(const DeltaOp& a, const DeltaOp& b) const { - return a.entity_id == b.entity_id; -} - -bool OperationalTransform::conflicts(const DeltaOp& a, const DeltaOp& b) const { - // 操作同一实体且不是只读操作才冲突 - if (!same_entity(a, b)) return false; - // 任意一个是 DELETE 或 MOVE 时认为冲突 - if (a.type == OpType::DELETE || b.type == OpType::DELETE) return true; - if (a.type == OpType::MOVE || b.type == OpType::MOVE) return true; - // MODIFY vs MODIFY 在同一实体上冲突 - return a.type == OpType::MODIFY && b.type == OpType::MODIFY; -} - -DeltaOp OperationalTransform::rebase_insert(const DeltaOp& op, const DeltaOp& against) const { - // INSERT 的优先级由时间戳决定 - DeltaOp result = op; - if (against.type == OpType::DELETE) { - // 对方删除了实体,INSERT 变为 MODIFY - result.type = OpType::MODIFY; - } - return result; -} - -DeltaOp OperationalTransform::rebase_delete(const DeltaOp& op, const DeltaOp& against) const { - // DELETE 不会被 after-op 改变 - return op; -} - -DeltaOp OperationalTransform::rebase_modify(const DeltaOp& op, const DeltaOp& against) const { - DeltaOp result = op; - if (against.type == OpType::DELETE) { - // 对方已删除,MODIFY 变为 INSERT - result.type = OpType::INSERT; - } - return result; -} - -DeltaOp OperationalTransform::transform(const DeltaOp& op1, const DeltaOp& op2) const { - if (!same_entity(op1, op2)) { - // 不涉及同一实体,无需变换 - return op1; - } - - // op1 的时间戳更晚则占优 - if (op1.timestamp > op2.timestamp) { - return op1; - } - - switch (op1.type) { - case OpType::INSERT: return rebase_insert(op1, op2); - case OpType::DELETE: return rebase_delete(op1, op2); - case OpType::MODIFY: return rebase_modify(op1, op2); - case OpType::MOVE: return op1; - case OpType::ROTATE: return op1; - case OpType::SCALE: return op1; - } - return op1; -} - -ChangeSet OperationalTransform::transform_set(const ChangeSet& cs1, - const ChangeSet& cs2) const { - ChangeSet result = cs1; - result.base_version = cs2.new_version; - result.new_version = cs2.new_version + 1; - - for (auto& op : result.ops) { - for (const auto& against : cs2.ops) { - op = transform(op, against); - } - } - return result; -} - -ChangeSet OperationalTransform::merge(const ChangeSet& cs1, - const ChangeSet& cs2) const { - ChangeSet merged; - merged.base_version = std::min(cs1.base_version, cs2.base_version); - merged.new_version = std::max(cs1.new_version, cs2.new_version) + 1; - merged.user_id = "system"; - merged.t = std::chrono::system_clock::now(); - - // 取 cs1 全部 ops - merged.ops = cs1.ops; - - // cs2 的 ops 经过 transform 后加入 - for (const auto& op2 : cs2.ops) { - DeltaOp transformed = op2; - for (const auto& op1 : cs1.ops) { - transformed = transform(transformed, op1); - } - merged.ops.push_back(transformed); - } - return merged; -} - -// ═══════════════════════════════════════════════════════ -// DeltaSync -// ═══════════════════════════════════════════════════════ - -ChangeSet DeltaSync::diff(uint64_t base_version) const { - std::shared_lock lock(mutex_); - ChangeSet cs; - cs.base_version = base_version; - cs.new_version = current_version_; - cs.t = std::chrono::system_clock::now(); - - for (const auto& entry : change_log_) { - if (entry.new_version > base_version) { - cs.ops.insert(cs.ops.end(), entry.ops.begin(), entry.ops.end()); - } - } - return cs; -} - -bool DeltaSync::apply_remote(const ChangeSet& cs) { - std::unique_lock lock(mutex_); - - // 只接受连续版本 - if (cs.base_version != current_version_ && cs.base_version != 0) { - return false; // 版本跳跃,需要先拉取缺失的历史 - } - - change_log_.push_back(cs); - current_version_ = cs.new_version; - - for (const auto& op : cs.ops) { - if (op.type == OpType::DELETE) { - dirty_set_.erase(op.entity_id); - } else { - dirty_set_.insert(op.entity_id); - } - entity_version_[op.entity_id] = cs.new_version; - } - return true; -} - -void DeltaSync::commit_local(const std::vector& ops, - const std::string& user_id) { - std::unique_lock lock(mutex_); - - ChangeSet cs; - cs.base_version = current_version_; - cs.new_version = current_version_ + 1; - cs.user_id = user_id; - cs.ops = ops; - cs.t = std::chrono::system_clock::now(); - - change_log_.push_back(cs); - current_version_ = cs.new_version; - - for (const auto& op : ops) { - if (op.type == OpType::DELETE) { - dirty_set_.erase(op.entity_id); - } else { - dirty_set_.insert(op.entity_id); - } - entity_version_[op.entity_id] = cs.new_version; - } -} - -std::vector DeltaSync::dirty_entities() const { - std::shared_lock lock(mutex_); - return std::vector(dirty_set_.begin(), dirty_set_.end()); -} - -bool DeltaSync::is_dirty(uint64_t entity_id) const { - std::shared_lock lock(mutex_); - return dirty_set_.find(entity_id) != dirty_set_.end(); -} - -bool DeltaSync::rollback(uint64_t target_version) { - std::unique_lock lock(mutex_); - if (target_version >= current_version_) return false; - - // 移除 target_version 之后的 change log - change_log_.erase( - std::remove_if(change_log_.begin(), change_log_.end(), - [target_version](const ChangeSet& cs) { - return cs.new_version > target_version; - }), - change_log_.end()); - - current_version_ = target_version; - - // 重建 dirty_set - dirty_set_.clear(); - entity_version_.clear(); - for (const auto& cs : change_log_) { - for (const auto& op : cs.ops) { - if (op.type == OpType::DELETE) { - dirty_set_.erase(op.entity_id); - } else { - dirty_set_.insert(op.entity_id); - } - entity_version_[op.entity_id] = cs.new_version; - } - } - return true; -} - -// ═══════════════════════════════════════════════════════ -// CloudSession -// ═══════════════════════════════════════════════════════ - -CloudSession::CloudSession(std::string session_id) - : session_id_(std::move(session_id)) {} - -CloudSession::~CloudSession() = default; - -bool CloudSession::join(const UserInfo& user) { - std::unique_lock lock(mutex_); - if (users_.find(user.user_id) != users_.end()) { - return false; // 已在线 - } - users_[user.user_id] = user; - user_cursor_[user.user_id] = 0; - pending_seq_[user.user_id] = 0; - return true; -} - -bool CloudSession::leave(const std::string& user_id) { - std::unique_lock lock(mutex_); - auto it = users_.find(user_id); - if (it == users_.end()) return false; - users_.erase(it); - user_cursor_.erase(user_id); - pending_seq_.erase(user_id); - return true; -} - -bool CloudSession::submit_operation(const std::string& user_id, - const std::vector& ops) { - std::unique_lock lock(mutex_); - - if (users_.find(user_id) == users_.end()) return false; - - // OT: 将 ops 变换到当前最新版本之后 - auto pending_ops = ops; - // 简化:直接通过 DeltaSync 提交 - sync_.commit_local(pending_ops, user_id); - - // 更新用户的 commit seq - pending_seq_[user_id] = sync_.version(); - - return true; -} - -ChangeSet CloudSession::pull_changes(const std::string& user_id) { - std::shared_lock lock(mutex_); - uint64_t cursor = 0; - auto it = user_cursor_.find(user_id); - if (it != user_cursor_.end()) { - cursor = it->second; - } - return sync_.diff(cursor); -} - -std::vector CloudSession::online_users() const { - std::shared_lock lock(mutex_); - std::vector result; - for (const auto& [id, info] : users_) { - result.push_back(info); - } - return result; -} - -uint64_t CloudSession::version() const { - return sync_.version(); -} - -uint64_t CloudSession::user_cursor(const std::string& user_id) const { - std::shared_lock lock(mutex_); - auto it = user_cursor_.find(user_id); - return (it != user_cursor_.end()) ? it->second : 0; -} - -// ═══════════════════════════════════════════════════════ -// ServerlessFunction -// ═══════════════════════════════════════════════════════ - -FunctionResponse ServerlessFunction::invoke(const FunctionRequest& req) { - FunctionResponse resp; - // Stub: 实际调用通过 AWS SDK / HTTP - resp.status_code = 200; - resp.ok = true; - resp.duration = std::chrono::milliseconds(0); - return resp; -} - -void ServerlessFunction::invoke_async(const FunctionRequest& req) { - (void)req; - // Stub: 异步触发,fire-and-forget -} - -bool ServerlessFunction::deploy(const std::string& function_name, - const std::string& code_path, - const std::string& runtime) { - (void)function_name; - (void)code_path; - (void)runtime; - // Stub: 通过 AWS SDK create_function / gcloud functions deploy - return true; -} - -bool ServerlessFunction::update_config(const std::string& function_name, - int memory_mb, - std::chrono::seconds timeout) { - (void)function_name; - (void)memory_mb; - (void)timeout; - return true; -} - -std::vector ServerlessFunction::list_functions() const { - return {}; -} - -bool ServerlessFunction::remove_function(const std::string& function_name) { - (void)function_name; - return true; -} - -// ═══════════════════════════════════════════════════════ -// ObjectStorage -// ═══════════════════════════════════════════════════════ - -void ObjectStorage::configure(const std::string& endpoint, - const std::string& bucket, - const std::string& access_key, - const std::string& secret_key) { - endpoint_ = endpoint; - bucket_ = bucket; - access_key_ = access_key; - secret_key_ = secret_key; -} - -bool ObjectStorage::put_object(const std::string& key, - const std::vector& data, - const std::string& content_type) { - (void)key; - (void)data; - (void)content_type; - // Stub: 实际通过 S3 SDK PutObject - return true; -} - -bool ObjectStorage::multipart_upload(const std::string& key, - const std::vector& data, - size_t part_size_mb) { - (void)key; - (void)data; - (void)part_size_mb; - // Stub: CreateMultipartUpload → UploadPart → CompleteMultipartUpload - return true; -} - -std::optional> ObjectStorage::get_object(const std::string& key) { - (void)key; - return std::nullopt; // Stub -} - -bool ObjectStorage::object_exists(const std::string& key) const { - (void)key; - return false; // Stub -} - -std::vector ObjectStorage::list_objects(const std::string& prefix, - int max_keys) const { - (void)prefix; - (void)max_keys; - return {}; // Stub -} - -bool ObjectStorage::delete_object(const std::string& key) { - (void)key; - return true; -} - -std::string ObjectStorage::presigned_get_url(const std::string& key, - std::chrono::seconds expires) { - (void)key; - (void)expires; - return ""; // Stub -} - -std::string ObjectStorage::presigned_put_url(const std::string& key, - std::chrono::seconds expires) { - (void)key; - (void)expires; - return ""; // Stub -} - -bool ObjectStorage::copy_object(const std::string& src_key, - const std::string& dst_key) { - (void)src_key; - (void)dst_key; - return true; -} - -std::optional ObjectStorage::head_object(const std::string& key) { - (void)key; - return std::nullopt; // Stub -} - -bool ObjectStorage::store_model(const std::string& model_name, - const std::vector& model_data, - const std::string& format) { - std::string key = "models/" + model_name + "." + format; - return put_object(key, model_data); -} - -std::optional> ObjectStorage::load_model(const std::string& model_name, - const std::string& format) { - std::string key = "models/" + model_name + "." + format; - return get_object(key); -} - -bool ObjectStorage::delete_model(const std::string& model_name, - const std::string& format) { - std::string key = "models/" + model_name + "." + format; - return delete_object(key); -} - -} // namespace vde::cloud diff --git a/src/digital_twin/dt_engine.cpp b/src/digital_twin/dt_engine.cpp deleted file mode 100644 index 7ff5f73..0000000 --- a/src/digital_twin/dt_engine.cpp +++ /dev/null @@ -1,469 +0,0 @@ -/// @file dt_engine.cpp 数字孪生引擎 —— 实现 -#include "vde/digital_twin/dt_engine.h" -#include -#include -#include -#include -#include - -namespace vde::dt { - -// ═══════════════════════════════════════════════════════ -// DigitalTwin -// ═══════════════════════════════════════════════════════ - -DigitalTwin::DigitalTwin(std::string asset_id) - : asset_id_(std::move(asset_id)) {} - -DigitalTwin::~DigitalTwin() = default; - -void DigitalTwin::configure(const std::string& model_path, - const std::string& material_db) { - model_path_ = model_path; - (void)material_db; -} - -void DigitalTwin::register_sensor(const SensorDef& sensor) { - std::unique_lock lock(mutex_); - sensor_defs_[sensor.id] = sensor; -} - -std::vector DigitalTwin::sensors() const { - std::shared_lock lock(mutex_); - std::vector result; - for (const auto& [id, def] : sensor_defs_) { - result.push_back(def); - } - return result; -} - -void DigitalTwin::update_reading(const SensorReading& reading) { - std::unique_lock lock(mutex_); - - latest_readings_[reading.sensor_id] = reading; - - // 追加到历史 - auto& hist = history_[reading.sensor_id]; - hist.push_back(reading); - if (hist.size() > max_history_per_sensor_) { - hist.pop_front(); - } - - version_++; -} - -void DigitalTwin::update_readings(const std::vector& readings) { - for (const auto& r : readings) { - update_reading(r); - } -} - -std::optional DigitalTwin::latest_reading(const std::string& sensor_id) const { - std::shared_lock lock(mutex_); - auto it = latest_readings_.find(sensor_id); - if (it != latest_readings_.end()) { - return it->second.value; - } - return std::nullopt; -} - -AssetState DigitalTwin::current_state() const { - std::shared_lock lock(mutex_); - AssetState state; - state.asset_id = asset_id_; - state.version = version_; - state.timestamp = std::chrono::system_clock::now(); - - for (const auto& [sid, reading] : latest_readings_) { - state.sensor_values[sid] = reading.value; - } - return state; -} - -std::vector DigitalTwin::history(const std::string& sensor_id, - std::chrono::minutes window) const { - std::shared_lock lock(mutex_); - std::vector result; - auto it = history_.find(sensor_id); - if (it == history_.end()) return result; - - auto cutoff = std::chrono::system_clock::now() - window; - for (const auto& r : it->second) { - if (r.timestamp >= cutoff) { - result.push_back(r); - } - } - return result; -} - -void DigitalTwin::set_visual_state(const std::string& key, double value) { - std::unique_lock lock(mutex_); - visual_states_[key] = value; -} - -std::optional DigitalTwin::visual_state(const std::string& key) const { - std::shared_lock lock(mutex_); - auto it = visual_states_.find(key); - if (it != visual_states_.end()) { - return it->second; - } - return std::nullopt; -} - -// ═══════════════════════════════════════════════════════ -// IoTConnector -// ═══════════════════════════════════════════════════════ - -IoTConnector::~IoTConnector() { - disconnect(); -} - -bool IoTConnector::configure(const IoTConfig& config) { - config_ = config; - return true; -} - -bool IoTConnector::connect() { - if (connected_) return true; - // Stub: 实际连接 MQTT broker / OPC-UA server - // MQTT: mosquitto_connect() - // OPC-UA: UA_Client_connect() - connected_ = true; - return true; -} - -void IoTConnector::disconnect() { - connected_ = false; -} - -bool IoTConnector::subscribe(const std::string& topic_or_node) { - if (!connected_) return false; - (void)topic_or_node; - // Stub: MQTT subscribe / OPC-UA CreateMonitoredItems - return true; -} - -bool IoTConnector::unsubscribe(const std::string& topic_or_node) { - (void)topic_or_node; - return true; -} - -bool IoTConnector::publish(const std::string& topic, const std::vector& payload) { - (void)topic; - (void)payload; - // Stub: MQTT publish - return connected_; -} - -std::optional IoTConnector::read_opcua_node(const std::string& node_id) { - (void)node_id; - // Stub: UA_Client_readValueAttribute - return std::nullopt; -} - -bool IoTConnector::write_opcua_node(const std::string& node_id, double value) { - (void)node_id; - (void)value; - return false; -} - -void IoTConnector::on_data(DataCallback callback) { - data_callback_ = std::move(callback); -} - -std::vector IoTConnector::poll() { - std::lock_guard lock(buffer_mutex_); - std::vector result; - result.swap(buffer_); - return result; -} - -// ═══════════════════════════════════════════════════════ -// RealTimeSync -// ═══════════════════════════════════════════════════════ - -void RealTimeSync::bind(DigitalTwin* dt, IoTConnector* iot) { - dt_ = dt; - iot_ = iot; -} - -bool RealTimeSync::start(const SyncConfig& config) { - if (!dt_ || !iot_) return false; - config_ = config; - running_ = true; - return true; -} - -void RealTimeSync::stop() { - running_ = false; -} - -void RealTimeSync::sync_once() { - if (!dt_ || !iot_ || !iot_->is_connected()) return; - - auto t_start = std::chrono::steady_clock::now(); - - auto readings = iot_->poll(); - if (!readings.empty()) { - dt_->update_readings(readings); - last_sync_data_ = readings; - - stats_.sync_count++; - for (const auto& r : readings) { - stats_.bytes_synced += sizeof(SensorReading); - } - } - - auto t_end = std::chrono::steady_clock::now(); - stats_.last_sync_duration = std::chrono::duration_cast(t_end - t_start); -} - -std::vector RealTimeSync::last_sync_data() const { - std::lock_guard lock(mutex_); - return last_sync_data_; -} - -RealTimeSync::Stats RealTimeSync::stats() const { - return stats_; -} - -void RealTimeSync::time_based_loop() { - // Stub: 时间驱动的循环由调用方管理(如 JS setInterval) -} - -void RealTimeSync::event_based_check() { - // Stub -} - -// ═══════════════════════════════════════════════════════ -// PredictiveMaintenance -// ═══════════════════════════════════════════════════════ - -void PredictiveMaintenance::add_readings(const std::string& sensor_id, - const std::vector& readings) { - std::unique_lock lock(mutex_); - auto& vec = data_[sensor_id]; - vec.insert(vec.end(), readings.begin(), readings.end()); - // 按时间排序 - std::sort(vec.begin(), vec.end(), - [](const SensorReading& a, const SensorReading& b) { - return a.timestamp < b.timestamp; - }); -} - -std::vector PredictiveMaintenance::extract_values(const std::string& sensor_id, - std::chrono::hours lookback) const { - std::shared_lock lock(mutex_); - std::vector values; - auto it = data_.find(sensor_id); - if (it == data_.end()) return values; - - auto cutoff = std::chrono::system_clock::now() - lookback; - for (const auto& r : it->second) { - if (r.timestamp >= cutoff) { - values.push_back(r.value); - } - } - return values; -} - -double PredictiveMaintenance::compute_slope(const std::vector& values) const { - if (values.size() < 2) return 0.0; - - size_t n = values.size(); - double sum_x = 0, sum_y = 0, sum_xy = 0, sum_xx = 0; - - for (size_t i = 0; i < n; ++i) { - double x = static_cast(i); - double y = values[i]; - sum_x += x; - sum_y += y; - sum_xy += x * y; - sum_xx += x * x; - } - - double denominator = n * sum_xx - sum_x * sum_x; - if (std::abs(denominator) < 1e-12) return 0.0; - - return (n * sum_xy - sum_x * sum_y) / denominator; -} - -double PredictiveMaintenance::compute_rul(double current_value, double degradation_rate, - double failure_threshold) const { - if (std::abs(degradation_rate) < 1e-12) { - return std::numeric_limits::infinity(); - } - double remaining = (failure_threshold - current_value) / degradation_rate; - return std::max(0.0, remaining); // 返回剩余时间(小时) -} - -double PredictiveMaintenance::compute_trend(const std::string& sensor_id, - std::chrono::hours lookback) const { - auto values = extract_values(sensor_id, lookback); - return compute_slope(values); -} - -std::vector PredictiveMaintenance::moving_average(const std::string& sensor_id, - size_t window_size) const { - std::shared_lock lock(mutex_); - std::vector result; - auto it = data_.find(sensor_id); - if (it == data_.end() || it->second.empty()) return result; - - const auto& vec = it->second; - if (window_size == 0 || window_size > vec.size()) { - for (const auto& r : vec) result.push_back(r.value); - return result; - } - - double sum = 0; - for (size_t i = 0; i < vec.size(); ++i) { - sum += vec[i].value; - if (i >= window_size) { - sum -= vec[i - window_size].value; - } - if (i >= window_size - 1) { - result.push_back(sum / static_cast(window_size)); - } - } - return result; -} - -bool PredictiveMaintenance::check_degradation(const std::string& sensor_id) const { - auto it = degradation_thresholds_.find(sensor_id); - if (it == degradation_thresholds_.end()) return false; - - auto values = extract_values(sensor_id, std::chrono::hours(168)); // 最近1周 - if (values.empty()) return false; - - double avg = std::accumulate(values.begin(), values.end(), 0.0) / values.size(); - return avg > it->second; -} - -bool PredictiveMaintenance::train(const std::string& sensor_id, - const TimeWindow& window) { - (void)window; - std::unique_lock lock(mutex_); - trained_[sensor_id] = true; - - // 计算基准统计 - auto it = data_.find(sensor_id); - if (it == data_.end() || it->second.empty()) return false; - - return true; -} - -MaintenancePrediction PredictiveMaintenance::predict(const std::string& component_id, - const std::string& sensor_id) { - MaintenancePrediction pred; - pred.component_id = component_id; - - auto values = extract_values(sensor_id, std::chrono::hours(720)); // 30天 - if (values.empty()) { - pred.failure_probability = 0.0; - pred.remaining_useful_life = std::chrono::hours(8760); // 1年默认 - pred.recommendation = "Insufficient data for prediction"; - pred.severity = 1; - return pred; - } - - // 计算趋势 - double slope = compute_slope(values); - double current = values.back(); - - // 获取退化阈值 - double threshold = current * 1.5; // 默认 - auto dt = degradation_thresholds_.find(sensor_id); - if (dt != degradation_thresholds_.end()) { - threshold = dt->second; - } - - // RUL 计算 - double rul_hours = compute_rul(current, std::max(slope, 1e-6), threshold); - pred.remaining_useful_life = std::chrono::hours(static_cast(rul_hours)); - - // 故障概率估计(Sigmoid) - double degradation_ratio = current / threshold; - pred.failure_probability = 1.0 / (1.0 + std::exp(-10.0 * (degradation_ratio - 0.8))); - - // 估计故障时间 - pred.estimated_failure_time = std::chrono::system_clock::now() + - std::chrono::hours(static_cast(rul_hours)); - - // 严重级别 - if (pred.failure_probability > 0.7) { - pred.severity = 5; - pred.recommendation = "URGENT: Schedule immediate maintenance. RUL < 30 days."; - } else if (pred.failure_probability > 0.4) { - pred.severity = 3; - pred.recommendation = "Plan maintenance within next maintenance window."; - } else if (pred.failure_probability > 0.2) { - pred.severity = 2; - pred.recommendation = "Monitor closely. Degradation trend detected."; - } else { - pred.severity = 1; - pred.recommendation = "Normal operation. Continue routine monitoring."; - } - - return pred; -} - -std::vector PredictiveMaintenance::predict_all() { - std::vector results; - std::shared_lock lock(mutex_); - for (const auto& [sensor_id, _] : data_) { - // 从 sensor_id 推导 component_id(简化) - results.push_back(predict(sensor_id, sensor_id)); - } - return results; -} - -void PredictiveMaintenance::set_degradation_threshold(const std::string& sensor_id, - double threshold) { - degradation_thresholds_[sensor_id] = threshold; -} - -PredictiveMaintenance::SensorStats -PredictiveMaintenance::sensor_statistics(const std::string& sensor_id) const { - SensorStats stats; - auto values = extract_values(sensor_id, std::chrono::hours(720)); - if (values.empty()) return stats; - - stats.count = values.size(); - stats.mean = std::accumulate(values.begin(), values.end(), 0.0) / values.size(); - - double sq_sum = 0; - stats.min_val = values[0]; - stats.max_val = values[0]; - for (double v : values) { - sq_sum += (v - stats.mean) * (v - stats.mean); - stats.min_val = std::min(stats.min_val, v); - stats.max_val = std::max(stats.max_val, v); - } - stats.stddev = std::sqrt(sq_sum / values.size()); - - return stats; -} - -std::vector -PredictiveMaintenance::detect_anomalies(const std::string& sensor_id, - const TimeWindow& window) const { - auto stats = sensor_statistics(sensor_id); - std::vector anomalies; - - std::shared_lock lock(mutex_); - auto it = data_.find(sensor_id); - if (it == data_.end()) return anomalies; - - auto cutoff = std::chrono::system_clock::now() - window.lookback; - for (const auto& r : it->second) { - if (r.timestamp < cutoff) continue; - double z = std::abs(r.value - stats.mean) / (stats.stddev + 1e-12); - if (z > window.anomaly_threshold) { - anomalies.push_back(r.timestamp); - } - } - return anomalies; -} - -} // namespace vde::dt diff --git a/src/distributed/cluster_engine.cpp b/src/distributed/cluster_engine.cpp deleted file mode 100644 index d990f2c..0000000 --- a/src/distributed/cluster_engine.cpp +++ /dev/null @@ -1,955 +0,0 @@ -/** - * @file cluster_engine.cpp - * @brief 分布式计算引擎实现 — 集群管理、任务调度、分布式几何运算、消息序列化 - * @ingroup distributed - */ - -#include "vde/distributed/cluster_engine.h" -#include "vde/core/aabb.h" -#include "vde/mesh/marching_cubes.h" -#include "vde/collision/ray_intersect.h" - -#include -#include -#include -#include -#include -#include - -namespace vde::distributed { - -// ══════════════════════════════════════════════════════════════════ -// 工具函数 -// ══════════════════════════════════════════════════════════════════ - -int64_t now_ms() noexcept { - auto now = std::chrono::steady_clock::now(); - return std::chrono::duration_cast( - now.time_since_epoch()).count(); -} - -std::string generate_uuid() { - static thread_local std::mt19937_64 rng( - static_cast(now_ms()) ^ - std::hash{}(std::this_thread::get_id())); - static thread_local std::uniform_int_distribution dist; - - uint64_t a = dist(rng); - uint64_t b = dist(rng); - - // UUID v4: set version bits (0100) and variant bits (10) - a = (a & 0xFFFFFFFFFFFF0FFFULL) | 0x0000000000004000ULL; - b = (b & 0x3FFFFFFFFFFFFFFFULL) | 0x8000000000000000ULL; - - char buf[37]; - std::snprintf(buf, sizeof(buf), - "%08x-%04x-%04x-%04x-%012llx", - static_cast(a >> 32), - static_cast((a >> 16) & 0xFFFF), - static_cast(a & 0xFFFF), - static_cast(b >> 48), - static_cast(b & 0xFFFFFFFFFFFFULL)); - return std::string(buf); -} - -// ══════════════════════════════════════════════════════════════════ -// SerializedMessage 实现 -// ══════════════════════════════════════════════════════════════════ - -void SerializedMessage::write_byte(uint8_t v) { - buf_.push_back(v); -} - -void SerializedMessage::write_varint(uint64_t v) { - while (v >= 0x80) { - buf_.push_back(static_cast(v | 0x80)); - v >>= 7; - } - buf_.push_back(static_cast(v)); -} - -void SerializedMessage::write_int32(int32_t v) { - write_varint(static_cast(static_cast(v))); -} - -void SerializedMessage::write_int64(int64_t v) { - uint64_t uv = static_cast(v); - // ZigZag encode - uint64_t encoded = (uv << 1) ^ static_cast(v >> 63); - write_varint(encoded); -} - -void SerializedMessage::write_float64(double v) { - uint64_t bits; - std::memcpy(&bits, &v, sizeof(bits)); - for (int i = 0; i < 8; ++i) { - buf_.push_back(static_cast(bits & 0xFF)); - bits >>= 8; - } -} - -void SerializedMessage::write_string(const std::string& s) { - write_varint(s.size()); - buf_.insert(buf_.end(), s.begin(), s.end()); -} - -void SerializedMessage::write_bytes(const uint8_t* data, size_t len) { - write_varint(len); - buf_.insert(buf_.end(), data, data + len); -} - -void SerializedMessage::write_message_type(MessageType t) { - write_byte(static_cast(t)); -} - -// ── 读取 ── - -uint8_t SerializedMessage::read_byte() { - if (read_pos_ >= buf_.size()) throw std::runtime_error("SerializedMessage: read past end"); - return buf_[read_pos_++]; -} - -uint64_t SerializedMessage::read_varint() { - uint64_t result = 0; - int shift = 0; - while (read_pos_ < buf_.size()) { - uint8_t byte = buf_[read_pos_++]; - result |= static_cast(byte & 0x7F) << shift; - if (!(byte & 0x80)) break; - shift += 7; - if (shift >= 64) throw std::runtime_error("SerializedMessage: varint too long"); - } - return result; -} - -int32_t SerializedMessage::read_int32() { - return static_cast(read_varint()); -} - -int64_t SerializedMessage::read_int64() { - uint64_t encoded = read_varint(); - // ZigZag decode - return static_cast((encoded >> 1) ^ -(encoded & 1)); -} - -double SerializedMessage::read_float64() { - if (read_pos_ + 8 > buf_.size()) throw std::runtime_error("SerializedMessage: read past end"); - uint64_t bits = 0; - for (int i = 0; i < 8; ++i) { - bits |= static_cast(buf_[read_pos_++]) << (i * 8); - } - double v; - std::memcpy(&v, &bits, sizeof(v)); - return v; -} - -std::string SerializedMessage::read_string() { - size_t len = static_cast(read_varint()); - if (read_pos_ + len > buf_.size()) throw std::runtime_error("SerializedMessage: string too long"); - std::string s(reinterpret_cast(buf_.data() + read_pos_), len); - read_pos_ += len; - return s; -} - -std::vector SerializedMessage::read_bytes(size_t len) { - if (read_pos_ + len > buf_.size()) throw std::runtime_error("SerializedMessage: read_bytes past end"); - std::vector result(buf_.begin() + read_pos_, buf_.begin() + read_pos_ + len); - read_pos_ += len; - return result; -} - -MessageType SerializedMessage::read_message_type() { - return static_cast(read_byte()); -} - -// ══════════════════════════════════════════════════════════════════ -// Mesh / Brep 序列化 -// ══════════════════════════════════════════════════════════════════ - -void serialize_mesh(const HalfedgeMesh& mesh, SerializedMessage& msg) { - // Header: vertex count, face count - msg.write_int32(static_cast(mesh.num_vertices())); - msg.write_int32(static_cast(mesh.num_faces())); - - // Vertices - for (size_t i = 0; i < mesh.num_vertices(); ++i) { - auto v = mesh.vertex(i); - msg.write_float64(v.x()); - msg.write_float64(v.y()); - msg.write_float64(v.z()); - } - - // Faces (triangle indices) - for (size_t i = 0; i < mesh.num_faces(); ++i) { - auto f = mesh.face_vertices(i); - msg.write_int32(f[0]); - msg.write_int32(f[1]); - msg.write_int32(f[2]); - } -} - -HalfedgeMesh deserialize_mesh(SerializedMessage& msg) { - int32_t num_vertices = msg.read_int32(); - int32_t num_faces = msg.read_int32(); - - std::vector vertices; - vertices.reserve(static_cast(num_vertices)); - for (int32_t i = 0; i < num_vertices; ++i) { - double x = msg.read_float64(); - double y = msg.read_float64(); - double z = msg.read_float64(); - vertices.push_back(Point3D(x, y, z)); - } - - std::vector> faces; - faces.reserve(static_cast(num_faces)); - for (int32_t i = 0; i < num_faces; ++i) { - int v0 = msg.read_int32(); - int v1 = msg.read_int32(); - int v2 = msg.read_int32(); - faces.push_back({v0, v1, v2}); - } - - HalfedgeMesh mesh; - mesh.build(vertices, faces); - return mesh; -} - -void serialize_brep(const BrepModel& brep, SerializedMessage& msg) { - // Serialize brep: store vertex count + vertex data - // Simplified serialization for transport layer — stores minimal geometry - msg.write_int32(0); // placeholder for body count - msg.write_int32(static_cast(brep.num_vertices())); - for (size_t i = 0; i < brep.num_vertices(); ++i) { - auto v = brep.vertex(static_cast(i)); - msg.write_float64(v.point.x()); - msg.write_float64(v.point.y()); - msg.write_float64(v.point.z()); - } -} - -BrepModel deserialize_brep(SerializedMessage& msg) { - BrepModel model; - int32_t n_bodies = msg.read_int32(); - int32_t n_vertices = msg.read_int32(); - for (int32_t i = 0; i < n_vertices; ++i) { - double x = msg.read_float64(); - double y = msg.read_float64(); - double z = msg.read_float64(); - model.add_vertex(Point3D(x, y, z)); - } - (void)n_bodies; - return model; -} - -// ══════════════════════════════════════════════════════════════════ -// ClusterManager 实现 -// ══════════════════════════════════════════════════════════════════ - -ClusterManager::ClusterManager(const ClusterConfig& cfg) - : config_(cfg) {} - -ClusterManager::~ClusterManager() { - stop_heartbeat_monitor(); -} - -std::string ClusterManager::register_node( - const std::string& host, int port, - int cores, int64_t memory_mb, - const std::vector& gpus) -{ - std::lock_guard lock(mutex_); - std::string node_id = generate_uuid(); - - ClusterNode node; - node.node_id = node_id; - node.host = host; - node.port = port; - node.cores = cores; - node.memory_mb = memory_mb; - node.gpus = gpus; - node.last_heartbeat = now_ms(); - node.alive = true; - - nodes_[node_id] = std::move(node); - return node_id; -} - -void ClusterManager::unregister_node(const std::string& node_id) { - std::lock_guard lock(mutex_); - nodes_.erase(node_id); -} - -void ClusterManager::heartbeat(const std::string& node_id, - double cpu_load, - int64_t memory_used_mb, - int active_tasks) -{ - std::lock_guard lock(mutex_); - auto it = nodes_.find(node_id); - if (it != nodes_.end()) { - it->second.cpu_load = cpu_load; - it->second.memory_used_mb = memory_used_mb; - it->second.active_tasks = active_tasks; - it->second.last_heartbeat = now_ms(); - it->second.alive = true; - } -} - -std::vector ClusterManager::alive_nodes() const { - std::lock_guard lock(mutex_); - std::vector result; - for (const auto& [id, node] : nodes_) { - if (node.alive) result.push_back(node); - } - return result; -} - -std::optional ClusterManager::find_node(const std::string& node_id) const { - std::lock_guard lock(mutex_); - auto it = nodes_.find(node_id); - if (it != nodes_.end() && it->second.alive) { - return it->second; - } - return std::nullopt; -} - -std::optional ClusterManager::select_node( - int64_t required_memory_mb, - bool prefer_gpu, - const std::string& affinity_tag) const -{ - (void)affinity_tag; - - switch (strategy_) { - case Strategy::ROUND_ROBIN: return select_round_robin(); - case Strategy::LEAST_LOADED: return select_least_loaded(); - case Strategy::CAPACITY_WEIGHTED: return select_capacity_weighted(); - case Strategy::AFFINITY: - default: return select_least_loaded(); - } -} - -std::optional ClusterManager::select_round_robin() const { - auto alive = alive_nodes(); - if (alive.empty()) return std::nullopt; - - std::lock_guard lock(mutex_); - size_t idx = (rr_counter_++) % alive.size(); - return alive[idx]; -} - -std::optional ClusterManager::select_least_loaded() const { - auto alive = alive_nodes(); - if (alive.empty()) return std::nullopt; - - const ClusterNode* best = &alive[0]; - for (const auto& node : alive) { - if (node.capacity_score() > best->capacity_score()) { - best = &node; - } - } - return *best; -} - -std::optional ClusterManager::select_capacity_weighted() const { - auto alive = alive_nodes(); - if (alive.empty()) return std::nullopt; - - // Compute total capacity - double total_cap = 0.0; - for (const auto& n : alive) { - double s = n.capacity_score(); - if (s > 0) total_cap += s; - } - if (total_cap <= 0.0) return alive[0]; - - // Weighted random selection - static thread_local std::mt19937_64 rng(std::random_device{}()); - std::uniform_real_distribution dist(0.0, total_cap); - double r = dist(rng); - double acc = 0.0; - for (const auto& n : alive) { - double s = n.capacity_score(); - if (s <= 0) continue; - acc += s; - if (r <= acc) return n; - } - return alive.back(); -} - -size_t ClusterManager::node_count() const { - std::lock_guard lock(mutex_); - return nodes_.size(); -} - -void ClusterManager::check_timeouts() { - std::lock_guard lock(mutex_); - int64_t now = now_ms(); - for (auto& [id, node] : nodes_) { - if (node.alive && (now - node.last_heartbeat) > config_.heartbeat_timeout_ms) { - node.alive = false; - } - } -} - -void ClusterManager::start_heartbeat_monitor() { - if (running_.exchange(true)) return; // Already running - heartbeat_thread_ = std::thread([this]() { - while (running_.load()) { - check_timeouts(); - std::this_thread::sleep_for( - std::chrono::milliseconds(config_.heartbeat_interval_ms)); - } - }); -} - -void ClusterManager::stop_heartbeat_monitor() { - running_.store(false); - if (heartbeat_thread_.joinable()) { - heartbeat_thread_.join(); - } -} - -// ══════════════════════════════════════════════════════════════════ -// TaskScheduler 实现 -// ══════════════════════════════════════════════════════════════════ - -TaskScheduler::TaskScheduler(std::shared_ptr cluster) - : cluster_(std::move(cluster)) {} - -TaskScheduler::~TaskScheduler() { - cancel_all(); -} - -int64_t TaskScheduler::add_task(const Task& task) { - std::lock_guard lock(mutex_); - int64_t id = task.task_id > 0 ? task.task_id : next_task_id_++; - Task t = task; - t.task_id = id; - tasks_[id] = std::move(t); - return id; -} - -void TaskScheduler::add_tasks(const std::vector& tasks) { - for (const auto& t : tasks) { - add_task(t); - } -} - -TaskStatus TaskScheduler::get_status(int64_t task_id) const { - std::lock_guard lock(mutex_); - auto it = tasks_.find(task_id); - if (it != tasks_.end()) return it->second.status; - return TaskStatus::FAILED; -} - -std::vector TaskScheduler::all_tasks() const { - std::lock_guard lock(mutex_); - std::vector result; - for (const auto& [id, t] : tasks_) { - result.push_back(t); - } - return result; -} - -std::vector> TaskScheduler::topological_sort() const { - // Build adjacency and in-degree - std::unordered_map> adj; - std::unordered_map in_degree; - - for (const auto& [id, task] : tasks_) { - in_degree[id] = 0; // Ensure all nodes in map - } - for (const auto& [id, task] : tasks_) { - for (auto dep : task.depends_on) { - adj[dep].push_back(id); - in_degree[id]++; - } - } - - // Kahn's algorithm with level grouping - std::queue q; - for (const auto& [id, deg] : in_degree) { - if (deg == 0) q.push(id); - } - - std::vector> levels; - while (!q.empty()) { - std::vector level; - size_t sz = q.size(); - for (size_t i = 0; i < sz; ++i) { - int64_t id = q.front(); q.pop(); - level.push_back(id); - for (auto next : adj[id]) { - if (--in_degree[next] == 0) { - q.push(next); - } - } - } - levels.push_back(std::move(level)); - } - return levels; -} - -bool TaskScheduler::deps_completed(const Task& task) const { - for (auto dep_id : task.depends_on) { - auto it = tasks_.find(dep_id); - if (it == tasks_.end() || it->second.status != TaskStatus::COMPLETED) { - return false; - } - } - return true; -} - -void TaskScheduler::mark_ready() { - std::lock_guard lock(mutex_); - for (auto& [id, task] : tasks_) { - if (task.status == TaskStatus::PENDING && deps_completed(task)) { - task.status = TaskStatus::READY; - } - } -} - -bool TaskScheduler::execute_all(int max_parallel) { - int64_t start = now_ms(); - stats_ = Stats{}; - - auto levels = topological_sort(); - - for (auto& level : levels) { - mark_ready(); - - // Collect ready tasks in this level - std::vector ready; - { - std::lock_guard lock(mutex_); - for (auto id : level) { - auto it = tasks_.find(id); - if (it != tasks_.end() && it->second.status == TaskStatus::READY) { - ready.push_back(&it->second); - } - } - } - - if (ready.empty()) continue; - - // Execute in parallel with thread pool - std::vector workers; - std::atomic completed{0}; - std::atomic failed{0}; - - for (auto* t : ready) { - if (max_parallel > 0 && workers.size() >= static_cast(max_parallel)) { - // Wait for one to finish - for (auto& w : workers) { if (w.joinable()) w.join(); } - workers.clear(); - } - - workers.emplace_back([t, &completed, &failed]() { - t->status = TaskStatus::RUNNING; - t->started_at = now_ms(); - try { - if (t->work) t->work(); - t->status = TaskStatus::COMPLETED; - completed++; - } catch (const std::exception& e) { - t->status = TaskStatus::FAILED; - t->error_message = e.what(); - failed++; - } catch (...) { - t->status = TaskStatus::FAILED; - t->error_message = "Unknown error"; - failed++; - } - t->finished_at = now_ms(); - }); - } - - // Wait for all workers in this level - for (auto& w : workers) { - if (w.joinable()) w.join(); - } - - stats_.completed += completed.load(); - stats_.failed += failed.load(); - - if (failed.load() > 0) { - stats_.elapsed_ms = now_ms() - start; - return false; - } - } - - stats_.elapsed_ms = now_ms() - start; - return stats_.failed == 0; -} - -bool TaskScheduler::dispatch_to_node(int64_t task_id, const std::string& node_id) { - std::lock_guard lock(mutex_); - auto it = tasks_.find(task_id); - if (it == tasks_.end()) return false; - it->second.target_node_id = node_id; - return true; -} - -void TaskScheduler::cancel_all() { - std::lock_guard lock(mutex_); - for (auto& [id, task] : tasks_) { - if (task.status == TaskStatus::PENDING || - task.status == TaskStatus::READY) { - task.status = TaskStatus::FAILED; - task.error_message = "Cancelled"; - } - } -} - -// ══════════════════════════════════════════════════════════════════ -// Distributed Operations 实现 -// ══════════════════════════════════════════════════════════════════ - -// ── distributed_boolean ──────────────────────────────────────────── - -DistributedBooleanResult distributed_boolean( - const HalfedgeMesh& a, - const HalfedgeMesh& b, - std::shared_ptr cluster, - int op_type) -{ - DistributedBooleanResult result; - int64_t t0 = now_ms(); - - auto nodes = cluster->alive_nodes(); - int total_nodes = static_cast(nodes.size()); - - if (total_nodes <= 1) { - // Fallback to local computation - result.nodes_used = 1; - // Local boolean operation based on op_type - try { - switch (op_type) { - case 0: result.mesh = HalfedgeMesh(a); break; // union placeholder - case 1: result.mesh = HalfedgeMesh(a); break; // intersection placeholder - case 2: result.mesh = HalfedgeMesh(a); break; // difference placeholder - default: result.mesh = HalfedgeMesh(a); break; - } - } catch (const std::exception& e) { - result.errors.push_back(std::string("Local boolean failed: ") + e.what()); - } - } else { - result.nodes_used = total_nodes; - - // Spatial hash bucket partitioning - struct Bucket { - std::vector face_indices_a; - std::vector face_indices_b; - }; - - int buckets_per_axis = static_cast(std::ceil(std::cbrt(total_nodes))); - int buckets_per_node = buckets_per_axis * buckets_per_axis * buckets_per_axis; - - // Compute bounding box of combined meshes - AABB3D bb_a = a.bounds(); - AABB3D bb_b = b.bounds(); - AABB3D bb(bb_a.min(), bb_a.max()); - bb.expand(bb_b); - - double cell_size_x = (bb.max().x() - bb.min().x()) / buckets_per_axis; - double cell_size_y = (bb.max().y() - bb.min().y()) / buckets_per_axis; - double cell_size_z = (bb.max().z() - bb.min().z()) / buckets_per_axis; - - // Parallel execution using TaskScheduler - auto cluster_ptr = cluster; - TaskScheduler scheduler(cluster_ptr); - - std::vector partial_results(total_nodes); - - for (int n = 0; n < total_nodes; ++n) { - int node_idx = n; - - Task t; - t.task_id = n + 1; - t.name = "Boolean_partition_" + std::to_string(n); - t.work = [&partial_results, node_idx, &a, &b, op_type, - &bb, buckets_per_axis, cell_size_x, cell_size_y, cell_size_z]() { - // Collect faces that intersect this node's spatial bucket - // For simplicity in this implementation, partition by face centroid - int bx = node_idx % buckets_per_axis; - int by = (node_idx / buckets_per_axis) % buckets_per_axis; - int bz = node_idx / (buckets_per_axis * buckets_per_axis); - - double min_x = bb.min().x() + bx * cell_size_x; - double max_x = min_x + cell_size_x; - double min_y = bb.min().y() + by * cell_size_y; - double max_y = min_y + cell_size_y; - double min_z = bb.min().z() + bz * cell_size_z; - double max_z = min_z + cell_size_z; - - // Build partial mesh from faces whose centroids fall in this bucket - std::vector verts; - std::vector> faces; - std::map vmap_a, vmap_b; - - // Collect from mesh A - for (size_t fi = 0; fi < a.num_faces(); ++fi) { - auto fv = a.face_vertices(static_cast(fi)); - Point3D center = (a.vertex(fv[0]) + a.vertex(fv[1]) + a.vertex(fv[2])) / 3.0; - if (center.x() >= min_x && center.x() < max_x && - center.y() >= min_y && center.y() < max_y && - center.z() >= min_z && center.z() < max_z) { - // Add face - for (int j = 0; j < 3; ++j) { - if (vmap_a.find(fv[j]) == vmap_a.end()) { - vmap_a[fv[j]] = static_cast(verts.size()); - verts.push_back(a.vertex(fv[j])); - } - } - faces.push_back({vmap_a[fv[0]], vmap_a[fv[1]], vmap_a[fv[2]]}); - } - } - - // Collect from mesh B - for (size_t fi = 0; fi < b.num_faces(); ++fi) { - auto fv = b.face_vertices(static_cast(fi)); - Point3D center = (b.vertex(fv[0]) + b.vertex(fv[1]) + b.vertex(fv[2])) / 3.0; - if (center.x() >= min_x && center.x() < max_x && - center.y() >= min_y && center.y() < max_y && - center.z() >= min_z && center.z() < max_z) { - for (int j = 0; j < 3; ++j) { - if (vmap_b.find(fv[j]) == vmap_b.end()) { - vmap_b[fv[j]] = static_cast(verts.size()); - verts.push_back(b.vertex(fv[j])); - } - } - faces.push_back({vmap_b[fv[0]], vmap_b[fv[1]], vmap_b[fv[2]]}); - } - } - - if (!verts.empty() && !faces.empty()) { - HalfedgeMesh partial; - partial.build_from_triangles(verts, faces); - partial_results[node_idx] = std::move(partial); - } - }; - - scheduler.add_task(t); - } - - scheduler.execute_all(total_nodes); - - // Merge all partial results - std::vector all_verts; - std::vector> all_faces; - for (auto& partial : partial_results) { - int offset = static_cast(all_verts.size()); - // Copy vertices - for (size_t vi = 0; vi < partial.num_vertices(); ++vi) { - all_verts.push_back(partial.vertex(static_cast(vi))); - } - // Copy faces with offset - for (size_t fi = 0; fi < partial.num_faces(); ++fi) { - auto fv = partial.face_vertices(static_cast(fi)); - all_faces.push_back({fv[0] + offset, fv[1] + offset, fv[2] + offset}); - } - } - - if (!all_verts.empty() && !all_faces.empty()) { - result.mesh.build_from_triangles(all_verts, all_faces); - } - } - - result.elapsed_ms = now_ms() - t0; - return result; -} - -// ── distributed_marching_cubes ───────────────────────────────────── - -DistributedMCResult distributed_marching_cubes( - const std::function& sdf, - const AABB3D& bounds, - const DistributedMCConfig& config, - std::shared_ptr cluster) -{ - DistributedMCResult result; - int64_t t0 = now_ms(); - - auto nodes = cluster->alive_nodes(); - int total_nodes = static_cast(nodes.size()); - int subdivisions = std::max(config.subdivisions, 1); - int actual_nodes = std::min(subdivisions, std::max(total_nodes, 1)); - - result.nodes_used = actual_nodes; - int axis = config.subdiv_axis; // 0=X, 1=Y, 2=Z - - double total_len = 0.0; - double start_coord = 0.0; - if (axis == 0) { - total_len = bounds.max().x() - bounds.min().x(); - start_coord = bounds.min().x(); - } else if (axis == 1) { - total_len = bounds.max().y() - bounds.min().y(); - start_coord = bounds.min().y(); - } else { - total_len = bounds.max().z() - bounds.min().z(); - start_coord = bounds.min().z(); - } - - double slab_len = total_len / actual_nodes; - double overlap = slab_len * config.stitch_overlap / config.resolution; - - // Parallel execution - std::vector partial_meshes(actual_nodes); - std::vector workers; - - for (int n = 0; n < actual_nodes; ++n) { - workers.emplace_back([&, n]() { - double slab_min = start_coord + n * slab_len - (n > 0 ? overlap : 0); - double slab_max = start_coord + (n + 1) * slab_len + (n < actual_nodes - 1 ? overlap : 0); - - AABB3D slab_bounds = bounds; - if (axis == 0) { - slab_bounds = AABB3D(Point3D(slab_min, bounds.min().y(), bounds.min().z()), - Point3D(slab_max, bounds.max().y(), bounds.max().z())); - } else if (axis == 1) { - slab_bounds = AABB3D(Point3D(bounds.min().x(), slab_min, bounds.min().z()), - Point3D(bounds.max().x(), slab_max, bounds.max().z())); - } else { - slab_bounds = AABB3D(Point3D(bounds.min().x(), bounds.min().y(), slab_min), - Point3D(bounds.max().x(), bounds.max().y(), slab_max)); - } - - // Compute adaptive resolution based on slab aspect ratio - int slab_res = config.resolution; - if (axis == 0) { - double ratio = (slab_max - slab_min) / total_len; - slab_res = std::max(2, static_cast(config.resolution * ratio + 0.5)); - } - - try { - partial_meshes[n] = mesh::marching_cubes( - sdf, config.iso_level, - slab_bounds.min(), slab_bounds.max(), slab_res); - } catch (const std::exception& e) { - // Skip errors on this slab - } - }); - } - - for (auto& w : workers) { - if (w.joinable()) w.join(); - } - - // Merge partial meshes - std::vector all_verts; - std::vector> all_tris; - - for (auto& pm : partial_meshes) { - int offset = static_cast(all_verts.size()); - for (auto& v : pm.vertices) { - all_verts.push_back(v); - } - for (auto& t : pm.triangles) { - all_tris.push_back({t[0] + offset, t[1] + offset, t[2] + offset}); - } - } - - result.mesh.vertices = std::move(all_verts); - result.mesh.triangles = std::move(all_tris); - result.elapsed_ms = now_ms() - t0; - return result; -} - -// ── distributed_ray_tracing ──────────────────────────────────────── - -DistributedRayTraceResult distributed_ray_tracing( - const DistributedRayTraceScene& scene, - std::shared_ptr cluster) -{ - DistributedRayTraceResult result; - int64_t t0 = now_ms(); - - auto nodes = cluster->alive_nodes(); - int total_nodes = static_cast(std::max(1UL, nodes.size())); - - result.nodes_used = total_nodes; - result.total_rays = static_cast(scene.rays.size()); - - // Partition rays into chunks for each node - size_t total_rays = scene.rays.size(); - size_t rays_per_node = (total_rays + total_nodes - 1) / total_nodes; - - std::vector> node_results(total_nodes); - std::vector workers; - - for (int n = 0; n < total_nodes; ++n) { - size_t start = n * rays_per_node; - size_t end = std::min(start + rays_per_node, total_rays); - if (start >= total_rays) break; - - workers.emplace_back([&, n, start, end]() { - for (size_t ri = start; ri < end; ++ri) { - const Ray3Dd& ray = scene.rays[ri]; - // Trace ray against scene mesh - double closest_t = std::numeric_limits::max(); - int closest_tri = -1; - - for (size_t fi = 0; fi < scene.mesh.num_faces(); ++fi) { - auto fv = scene.mesh.face_vertices(static_cast(fi)); - Point3D v0 = scene.mesh.vertex(fv[0]); - Point3D v1 = scene.mesh.vertex(fv[1]); - Point3D v2 = scene.mesh.vertex(fv[2]); - - // Möller–Trumbore - Vector3D e1 = v1 - v0; - Vector3D e2 = v2 - v0; - Vector3D h = ray.direction().cross(e2); - double a = e1.dot(h); - - if (std::fabs(a) < 1e-10) continue; // Parallel - - double f = 1.0 / a; - Vector3D s = ray.origin() - v0; - double u = f * s.dot(h); - if (u < 0.0 || u > 1.0) continue; - - Vector3D q = s.cross(e1); - double v = f * ray.direction().dot(q); - if (v < 0.0 || u + v > 1.0) continue; - - double t = f * e2.dot(q); - if (t > 1e-8 && t < closest_t) { - closest_t = t; - closest_tri = static_cast(fi); - } - } - - if (closest_tri >= 0) { - RayHit hit; - hit.ray_index = static_cast(ri); - hit.t = closest_t; - hit.point = ray.origin() + ray.direction() * closest_t; - hit.triangle_index = closest_tri; - // Normal approximation - auto fv = scene.mesh.face_vertices(closest_tri); - Vector3D n = (scene.mesh.vertex(fv[1]) - scene.mesh.vertex(fv[0])) - .cross(scene.mesh.vertex(fv[2]) - scene.mesh.vertex(fv[0])); - hit.normal = n.normalized(); - node_results[n].push_back(hit); - } - } - }); - } - - for (auto& w : workers) { - if (w.joinable()) w.join(); - } - - // Collect all results - for (auto& nr : node_results) { - result.hits.insert(result.hits.end(), nr.begin(), nr.end()); - } - - result.elapsed_ms = now_ms() - t0; - return result; -} - -} // namespace vde::distributed diff --git a/src/distributed/grpc_service.cpp b/src/distributed/grpc_service.cpp deleted file mode 100644 index cf05292..0000000 --- a/src/distributed/grpc_service.cpp +++ /dev/null @@ -1,487 +0,0 @@ -/** - * @file grpc_service.cpp - * @brief gRPC 分布式服务实现 — VdeService, VdeServiceClient, GrpcConnectionPool - * @ingroup distributed - */ - -#include "vde/distributed/grpc_service.h" -#include "vde/distributed/cluster_engine.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace vde::distributed { - -// ══════════════════════════════════════════════════════════════════ -// VdeService 实现 -// ══════════════════════════════════════════════════════════════════ - -struct VdeService::Impl { - GrpcServiceConfig config; - std::atomic running{false}; - int64_t start_time_ms = 0; - - // BrepOps handlers - BrepBooleanHandler brep_boolean_handler; - BrepValidateHandler brep_validate_handler; - BrepHealHandler brep_heal_handler; - - // MeshOps handlers - MeshSimplifyHandler mesh_simplify_handler; - MeshSmoothHandler mesh_smooth_handler; - MeshMarchingCubesHandler mesh_marching_cubes_handler; - - // SdfOps handlers - SdfEvaluateHandler sdf_evaluate_handler; - SdfGradientHandler sdf_gradient_handler; - - // Stats - mutable std::mutex stats_mutex; - int64_t total_requests = 0; - int64_t total_errors = 0; - int active_connections = 0; -}; - -VdeService::VdeService(const GrpcServiceConfig& cfg) - : impl_(std::make_unique()) -{ - impl_->config = cfg; -} - -VdeService::~VdeService() { - shutdown(); -} - -bool VdeService::start() { - if (impl_->running.exchange(true)) return false; - - impl_->start_time_ms = now_ms(); - - // In a real gRPC implementation, this would: - // 1. Create a grpc::ServerBuilder - // 2. Register service handlers - // 3. Configure TLS if enabled - // 4. Bind to host:port - // 5. Start the server event loop - - // For this implementation, we simulate the server start - // and register handlers for local invocation. - - return true; -} - -bool VdeService::start_async() { - if (!start()) return false; - - // Start service in background thread - std::thread([this]() { - // Server event loop simulation - while (impl_->running.load()) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - }).detach(); - - return true; -} - -void VdeService::shutdown() { - impl_->running.store(false); -} - -bool VdeService::is_running() const noexcept { - return impl_->running.load(); -} - -// ── BrepOps handlers ─────────────────────────────────────────────── - -void VdeService::set_brep_boolean_handler(BrepBooleanHandler h) { - impl_->brep_boolean_handler = std::move(h); -} - -void VdeService::set_brep_validate_handler(BrepValidateHandler h) { - impl_->brep_validate_handler = std::move(h); -} - -void VdeService::set_brep_heal_handler(BrepHealHandler h) { - impl_->brep_heal_handler = std::move(h); -} - -// ── MeshOps handlers ─────────────────────────────────────────────── - -void VdeService::set_mesh_simplify_handler(MeshSimplifyHandler h) { - impl_->mesh_simplify_handler = std::move(h); -} - -void VdeService::set_mesh_smooth_handler(MeshSmoothHandler h) { - impl_->mesh_smooth_handler = std::move(h); -} - -void VdeService::set_mesh_marching_cubes_handler(MeshMarchingCubesHandler h) { - impl_->mesh_marching_cubes_handler = std::move(h); -} - -// ── SdfOps handlers ──────────────────────────────────────────────── - -void VdeService::set_sdf_evaluate_handler(SdfEvaluateHandler h) { - impl_->sdf_evaluate_handler = std::move(h); -} - -void VdeService::set_sdf_gradient_handler(SdfGradientHandler h) { - impl_->sdf_gradient_handler = std::move(h); -} - -// ── 健康检查 ────────────────────────────────────────────────────── - -HealthCheckResponse VdeService::health_check() const { - HealthCheckResponse resp; - resp.healthy = impl_->running.load(); - resp.server_time_ms = now_ms(); - resp.uptime_ms = resp.server_time_ms - impl_->start_time_ms; - resp.active_connections = impl_->active_connections; - // Simulate CPU and memory stats - resp.cpu_load = 0.0; - resp.memory_used_mb = 0; - return resp; -} - -VdeService::ServerStats VdeService::server_stats() const { - ServerStats s; - s.total_requests = impl_->total_requests; - s.total_errors = impl_->total_errors; - s.active_connections = impl_->active_connections; - s.uptime_ms = now_ms() - impl_->start_time_ms; - return s; -} - -// ══════════════════════════════════════════════════════════════════ -// VdeServiceClient 实现 -// ══════════════════════════════════════════════════════════════════ - -struct VdeServiceClient::Impl { - GrpcClientConfig config; - std::atomic connected{false}; - int64_t last_connect_attempt = 0; - int connect_attempts = 0; - - // Simulated connection stats - int64_t total_rpcs = 0; - int64_t failed_rpcs = 0; -}; - -VdeServiceClient::VdeServiceClient(const GrpcClientConfig& cfg) - : impl_(std::make_unique()) -{ - impl_->config = cfg; -} - -VdeServiceClient::~VdeServiceClient() { - disconnect(); -} - -bool VdeServiceClient::connect() { - if (impl_->connected.load()) return true; - - impl_->last_connect_attempt = now_ms(); - impl_->connect_attempts++; - - // In a real gRPC implementation, this would: - // 1. Create a grpc::Channel to the target - // 2. Configure TLS credentials if enabled - // 3. Configure keepalive - // 4. Create service stubs - - impl_->connected.store(true); - return true; -} - -void VdeServiceClient::disconnect() { - impl_->connected.store(false); -} - -bool VdeServiceClient::is_connected() const noexcept { - return impl_->connected.load(); -} - -HealthCheckResponse VdeServiceClient::health_check() { - if (!is_connected() && !connect()) { - HealthCheckResponse resp; - resp.healthy = false; - return resp; - } - - // In real gRPC: make unary RPC to Health/Check - HealthCheckResponse resp; - resp.healthy = true; - resp.server_time_ms = now_ms(); - return resp; -} - -// ── BrepOps RPCs ─────────────────────────────────────────────────── - -BrepBooleanResponse VdeServiceClient::brep_boolean(const BrepBooleanRequest& req) { - if (!is_connected() && !connect()) { - BrepBooleanResponse resp; - resp.success = false; - resp.error_message = "Connection failed"; - return resp; - } - - impl_->total_rpcs++; - - // In real gRPC: Serialize request, make unary RPC, deserialize response - BrepBooleanResponse resp; - resp.success = true; - return resp; -} - -BrepValidateResponse VdeServiceClient::brep_validate(const BrepValidateRequest& req) { - (void)req; - impl_->total_rpcs++; - BrepValidateResponse resp; - return resp; -} - -BrepHealResponse VdeServiceClient::brep_heal(const BrepHealRequest& req) { - (void)req; - impl_->total_rpcs++; - BrepHealResponse resp; - return resp; -} - -// ── MeshOps RPCs ─────────────────────────────────────────────────── - -MeshSimplifyResponse VdeServiceClient::mesh_simplify(const MeshSimplifyRequest& req) { - (void)req; - impl_->total_rpcs++; - MeshSimplifyResponse resp; - resp.success = true; - return resp; -} - -MeshSmoothResponse VdeServiceClient::mesh_smooth(const MeshSmoothRequest& req) { - (void)req; - impl_->total_rpcs++; - MeshSmoothResponse resp; - resp.success = true; - return resp; -} - -std::vector VdeServiceClient::mesh_marching_cubes(const MarchingCubesRequest& req) { - impl_->total_rpcs++; - std::vector chunks; - - // Streamed response: receive chunks until is_last=true - // In real gRPC: server-side streaming RPC - for (int i = 0; i < req.total_chunks; ++i) { - MarchingCubesChunk chunk; - chunk.chunk_index = i; - chunk.total_chunks = req.total_chunks; - chunk.is_last = (i == req.total_chunks - 1); - chunks.push_back(chunk); - } - return chunks; -} - -// ── SdfOps RPCs ──────────────────────────────────────────────────── - -SdfEvaluateResponse VdeServiceClient::sdf_evaluate(const SdfEvaluateRequest& req) { - (void)req; - impl_->total_rpcs++; - SdfEvaluateResponse resp; - resp.success = true; - return resp; -} - -SdfGradientResponse VdeServiceClient::sdf_gradient(const SdfGradientRequest& req) { - (void)req; - impl_->total_rpcs++; - SdfGradientResponse resp; - resp.success = true; - return resp; -} - -// ── 流式传输:大模型分块 ────────────────────────────────────────── - -bool VdeServiceClient::upload_large_mesh(const HalfedgeMesh& mesh, size_t chunk_size_mb) { - if (!is_connected() && !connect()) return false; - - // Serialize the whole mesh - SerializedMessage full_msg; - serialize_mesh(mesh, full_msg); - - size_t chunk_size = chunk_size_mb * 1024 * 1024; - size_t total_size = full_msg.size(); - size_t offset = 0; - int chunk_index = 0; - int total_chunks = static_cast((total_size + chunk_size - 1) / chunk_size); - - while (offset < total_size) { - size_t this_chunk = std::min(chunk_size, total_size - offset); - // In real gRPC: client-side streaming RPC - // Send chunk with index, total, and data - (void)this_chunk; - offset += this_chunk; - chunk_index++; - } - - impl_->total_rpcs++; - return true; -} - -HalfedgeMesh VdeServiceClient::download_large_mesh( - int resolution, double iso_level, const AABB3D& bounds) -{ - MarchingCubesRequest req; - req.resolution = resolution; - req.iso_level = iso_level; - req.bmin = {bounds.min().x, bounds.min().y, bounds.min().z}; - req.bmax = {bounds.max().x, bounds.max().y, bounds.max().z}; - - auto chunks = mesh_marching_cubes(req); - - // Reassemble mesh from chunks - std::vector all_verts; - std::vector> all_faces; - - for (auto& chunk : chunks) { - // Deserialize chunk data - if (!chunk.vertex_data.empty()) { - SerializedMessage vmsg; - for (auto b : chunk.vertex_data) vmsg.write_byte(b); - vmsg.reset_read(); - // Deserialize vertices from chunk - int vc = chunk.vertex_count; - for (int i = 0; i < vc && !vmsg.eof(); ++i) { - try { - double x = vmsg.read_float64(); - double y = vmsg.read_float64(); - double z = vmsg.read_float64(); - all_verts.push_back(Point3D(x, y, z)); - } catch (...) { break; } - } - } - - // Deserialize indices - if (!chunk.index_data.empty() && chunk.triangle_count > 0) { - SerializedMessage imsg; - for (auto b : chunk.index_data) imsg.write_byte(b); - imsg.reset_read(); - int offset = static_cast(all_verts.size()) - chunk.vertex_count; - for (int i = 0; i < chunk.triangle_count; ++i) { - try { - int v0 = imsg.read_int32() + offset; - int v1 = imsg.read_int32() + offset; - int v2 = imsg.read_int32() + offset; - all_faces.push_back({v0, v1, v2}); - } catch (...) { break; } - } - } - } - - HalfedgeMesh result; - if (!all_verts.empty() && !all_faces.empty()) { - result.build(all_verts, all_faces); - } - return result; -} - -// ══════════════════════════════════════════════════════════════════ -// GrpcConnectionPool 实现 -// ══════════════════════════════════════════════════════════════════ - -struct GrpcConnectionPool::Impl { - PoolConfig config; - mutable std::mutex mutex; - struct Entry { - std::shared_ptr client; - int64_t last_used = 0; - int failures = 0; - }; - std::map clients; -}; - -GrpcConnectionPool::GrpcConnectionPool(const PoolConfig& cfg) - : impl_(std::make_unique()) -{ - impl_->config = cfg; -} - -GrpcConnectionPool::~GrpcConnectionPool() { - close_all(); -} - -std::shared_ptr GrpcConnectionPool::get_client(const std::string& host) { - std::lock_guard lock(impl_->mutex); - - auto it = impl_->clients.find(host); - if (it != impl_->clients.end()) { - it->second.last_used = now_ms(); - return it->second.client; - } - - // Check max connections - if (impl_->clients.size() >= static_cast(impl_->config.max_connections)) { - reap_idle(); - } - - // Create new client - GrpcClientConfig cfg; - cfg.target = host; - cfg.connect_timeout_ms = 5000; - cfg.max_reconnect_attempts = impl_->config.max_retries_per_node; - - auto client = std::make_shared(cfg); - if (!client->connect()) { - return nullptr; - } - - Impl::Entry entry; - entry.client = client; - entry.last_used = now_ms(); - - impl_->clients[host] = std::move(entry); - return client; -} - -void GrpcConnectionPool::release_client(const std::string& host) { - std::lock_guard lock(impl_->mutex); - auto it = impl_->clients.find(host); - if (it != impl_->clients.end()) { - it->second.client->disconnect(); - impl_->clients.erase(it); - } -} - -void GrpcConnectionPool::reap_idle() { - std::lock_guard lock(impl_->mutex); - int64_t now = now_ms(); - for (auto it = impl_->clients.begin(); it != impl_->clients.end(); ) { - if ((now - it->second.last_used) > impl_->config.idle_timeout_ms) { - it->second.client->disconnect(); - it = impl_->clients.erase(it); - } else { - ++it; - } - } -} - -size_t GrpcConnectionPool::active_count() const { - std::lock_guard lock(impl_->mutex); - return impl_->clients.size(); -} - -void GrpcConnectionPool::close_all() { - std::lock_guard lock(impl_->mutex); - for (auto& [host, entry] : impl_->clients) { - entry.client->disconnect(); - } - impl_->clients.clear(); -} - -} // namespace vde::distributed diff --git a/src/gpu/gpu_acceleration.cpp b/src/gpu/gpu_acceleration.cpp deleted file mode 100644 index da2a454..0000000 --- a/src/gpu/gpu_acceleration.cpp +++ /dev/null @@ -1,670 +0,0 @@ -#include "vde/gpu/gpu_acceleration.h" - -// CPU 回退所需的头文件 -#include "vde/mesh/marching_cubes.h" -#include "vde/mesh/mesh_simplify.h" -#include "vde/mesh/halfedge_mesh.h" - -#include -#include -#include -#include - -namespace vde::gpu { - -// ====================================================================== -// gpu_available — 运行时检测 -// ====================================================================== - -bool gpu_available() noexcept { -#if VDE_USE_CUDA - int device_count = 0; - cudaError_t err = cudaGetDeviceCount(&device_count); - return (err == cudaSuccess && device_count > 0); -#else - return false; -#endif -} - -// ====================================================================== -// CPU 回退实现(无 CUDA 或降级时使用) -// ====================================================================== - -namespace cpu { - -// ── MC 边表(与标准 Marching Cubes 一致) ────────────────────────── -namespace { -constexpr int kEdgeTable[256] = { - 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, - 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, - 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, - 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, - 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, - 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, - 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, - 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, - 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, - 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, - 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, - 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, - 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, - 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, - 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , - 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, - 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, - 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, - 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, - 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, - 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, - 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, - 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, - 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, - 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, - 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, - 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, - 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, - 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, - 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, - 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, - 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 -}; - -// 12 条边的端点顶点索引(体素 8 角点) -constexpr int kEdgeVertMap[12][2] = { - {0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4}, - {0,4},{1,5},{2,6},{3,7} -}; - -// 体素 8 角偏移 -constexpr double kVoxelCorners[8][3] = { - {0,0,0},{1,0,0},{1,1,0},{0,1,0}, - {0,0,1},{1,0,1},{1,1,1},{0,1,1} -}; -} // anonymous namespace - -/// 使用标准 MC 三角形表在边上插值 -static inline Point3D interpolate_edge(const Point3D& p0, const Point3D& p1, - double v0, double v1) { - if (std::abs(v0) < 1e-12) return p0; - if (std::abs(v1) < 1e-12) return p1; - if (std::abs(v0 - v1) < 1e-12) { - return {(p0.x() + p1.x()) * 0.5, - (p0.y() + p1.y()) * 0.5, - (p0.z() + p1.z()) * 0.5}; - } - double t = -v0 / (v1 - v0); - return {p0.x() + t * (p1.x() - p0.x()), - p0.y() + t * (p1.y() - p0.y()), - p0.z() + t * (p1.z() - p0.z())}; -} - -/// CPU: 单个体素的面生成(使用经典 triTable) -/// tri_table 按每行 16 个 int 存储,-1 终止 -static int process_cube(const double vals[8], const Point3D corners[8], - int (&tri_table)[256][16], - Point3D* out_verts, int* out_tris, int& tri_offset) { - int cube_index = 0; - for (int i = 0; i < 8; ++i) - if (vals[i] < 0.0) cube_index |= (1 << i); - - int edge_flags = kEdgeTable[cube_index]; - if (edge_flags == 0) return 0; - - Point3D edge_pts[12]; - for (int e = 0; e < 12; ++e) { - if (edge_flags & (1 << e)) { - edge_pts[e] = interpolate_edge( - corners[kEdgeVertMap[e][0]], corners[kEdgeVertMap[e][1]], - vals[kEdgeVertMap[e][0]], vals[kEdgeVertMap[e][1]]); - } - } - - int tri_count = 0; - for (int i = 0; tri_table[cube_index][i] != -1; i += 3, ++tri_count) { - int i0 = tri_table[cube_index][i]; - int i1 = tri_table[cube_index][i + 1]; - int i2 = tri_table[cube_index][i + 2]; - out_verts[tri_offset + i0] = edge_pts[i0]; - out_verts[tri_offset + i1] = edge_pts[i1]; - out_verts[tri_offset + i2] = edge_pts[i2]; - out_tris[tri_offset / 3] = tri_offset; - tri_offset += 3; - } - return tri_count; -} - -/// CPU fallback: marching_cubes -static HalfedgeMesh cpu_marching_cubes( - const std::function& sdf, - const AABB3D& bounds, int res) -{ - (void) sdf; (void) bounds; (void) res; - // 委托给已有的 vde::mesh::marching_cubes - auto mc_result = mesh::marching_cubes(sdf, 0.0, - bounds.min(), bounds.max(), res); - - HalfedgeMesh result; - result.build_from_triangles(mc_result.vertices, mc_result.triangles); - return result; -} - -/// CPU fallback: QEM mesh simplify -static HalfedgeMesh cpu_mesh_simplify(const HalfedgeMesh& mesh, float target_ratio) { - mesh::SimplifyOptions opts; - opts.target_ratio = static_cast(target_ratio); - opts.preserve_boundary = true; - return mesh::simplify_mesh(mesh, opts); -} - -/// CPU fallback: boolean intersect -static HalfedgeMesh cpu_boolean_intersect(const HalfedgeMesh& mesh_a, - const HalfedgeMesh& mesh_b) { - // 使用简单的三角形-三角形求交后重构 - // 收集三角形数据 - std::vector verts_a, verts_b; - std::vector> tris_a, tris_b; - - for (size_t vi = 0; vi < mesh_a.num_vertices(); ++vi) - verts_a.push_back(mesh_a.vertex(vi)); - for (size_t fi = 0; fi < mesh_a.num_faces(); ++fi) { - auto fv = mesh_a.face_vertices(static_cast(fi)); - if (fv.size() == 3) - tris_a.push_back({fv[0], fv[1], fv[2]}); - } - - for (size_t vi = 0; vi < mesh_b.num_vertices(); ++vi) - verts_b.push_back(mesh_b.vertex(vi)); - for (size_t fi = 0; fi < mesh_b.num_faces(); ++fi) { - auto fv = mesh_b.face_vertices(static_cast(fi)); - if (fv.size() == 3) - tris_b.push_back({fv[0], fv[1], fv[2]}); - } - - // 空间哈希网格加速 - const int GRID_RES = 32; - // 计算两个网格的联合包围盒 - AABB3D a_bb = mesh_a.bounds(); - AABB3D b_bb = mesh_b.bounds(); - AABB3D total_bb; - total_bb.expand(a_bb); - total_bb.expand(b_bb); - - Vector3D ext = total_bb.extent(); - double cell_size = std::max({ext.x(), ext.y(), ext.z()}) / GRID_RES; - if (cell_size < 1e-10) cell_size = 1.0; - - // 将 B 的三角形放入空间哈希网格 - auto cell_hash = [&](const Point3D& p) -> int { - int ix = static_cast((p.x() - total_bb.min().x()) / cell_size); - int iy = static_cast((p.y() - total_bb.min().y()) / cell_size); - int iz = static_cast((p.z() - total_bb.min().z()) / cell_size); - ix = std::max(0, std::min(ix, GRID_RES - 1)); - iy = std::max(0, std::min(iy, GRID_RES - 1)); - iz = std::max(0, std::min(iz, GRID_RES - 1)); - return (ix * GRID_RES + iy) * GRID_RES + iz; - }; - - int total_cells = GRID_RES * GRID_RES * GRID_RES; - std::vector> grid(total_cells); - for (size_t ti = 0; ti < tris_b.size(); ++ti) { - Point3D c = (verts_b[tris_b[ti][0]] + verts_b[tris_b[ti][1]] + - verts_b[tris_b[ti][2]]) * (1.0 / 3.0); - grid[cell_hash(c)].push_back(static_cast(ti)); - } - - // 三角形-三角形求交(Möller 算法简化版) - auto tri_tri_intersect = [&](const std::array& ta, - const std::array& tb) -> bool { - // 先用 AABB 快速剔除 - AABB3D ta_bb, tb_bb; - for (int i = 0; i < 3; ++i) { ta_bb.expand(ta[i]); tb_bb.expand(tb[i]); } - if (!ta_bb.intersects(tb_bb)) return false; - - // 使用平面-三角形相交检测 - Vector3D na = (ta[1] - ta[0]).cross(ta[2] - ta[0]); - double na_len = na.norm(); - if (na_len < 1e-12) return false; - na /= na_len; - double da = -na.dot(ta[0]); - - // tb 各点到 ta 平面的有向距离 - double d0 = na.dot(tb[0]) + da; - double d1 = na.dot(tb[1]) + da; - double d2 = na.dot(tb[2]) + da; - - if ((d0 > 1e-9 && d1 > 1e-9 && d2 > 1e-9) || - (d0 < -1e-9 && d1 < -1e-9 && d2 < -1e-9)) - return false; - - Vector3D nb = (tb[1] - tb[0]).cross(tb[2] - tb[0]); - double nb_len = nb.norm(); - if (nb_len < 1e-12) return false; - nb /= nb_len; - double db = -nb.dot(tb[0]); - - double e0 = nb.dot(ta[0]) + db; - double e1 = nb.dot(ta[1]) + db; - double e2 = nb.dot(ta[2]) + db; - - if ((e0 > 1e-9 && e1 > 1e-9 && e2 > 1e-9) || - (e0 < -1e-9 && e1 < -1e-9 && e2 < -1e-9)) - return false; - - // 交线方向 - Vector3D dir = na.cross(nb); - double dir_len = dir.norm(); - if (dir_len < 1e-12) return false; // 平行或共面 - - return true; - }; - - // 在每个 A 的候选网格单元中与 B 的三角形求交 - std::vector out_verts; - std::vector> out_tris; - - for (size_t ti = 0; ti < tris_a.size(); ++ti) { - std::array ta = {verts_a[tris_a[ti][0]], - verts_a[tris_a[ti][1]], - verts_a[tris_a[ti][2]]}; - Point3D center = (ta[0] + ta[1] + ta[2]) * (1.0 / 3.0); - int hc = cell_hash(center); - - // 查本单元及相邻单元 - for (int dx = -1; dx <= 1; ++dx) - for (int dy = -1; dy <= 1; ++dy) - for (int dz = -1; dz <= 1; ++dz) { - int ix = hc / (GRID_RES * GRID_RES); - int iy = (hc / GRID_RES) % GRID_RES; - int iz = hc % GRID_RES; - int nx = ix + dx, ny = iy + dy, nz = iz + dz; - if (nx < 0 || nx >= GRID_RES || ny < 0 || ny >= GRID_RES || - nz < 0 || nz >= GRID_RES) - continue; - int neighbor = (nx * GRID_RES + ny) * GRID_RES + nz; - for (int bti : grid[neighbor]) { - std::array tb = {verts_b[tris_b[bti][0]], - verts_b[tris_b[bti][1]], - verts_b[tris_b[bti][2]]}; - if (tri_tri_intersect(ta, tb)) { - // 两三角都有交集,输出三角形对 - int base = static_cast(out_verts.size()); - for (int j = 0; j < 3; ++j) - out_verts.push_back(ta[j]); - out_tris.push_back({base, base + 1, base + 2}); - base = static_cast(out_verts.size()); - for (int j = 0; j < 3; ++j) - out_verts.push_back(tb[j]); - out_tris.push_back({base, base + 1, base + 2}); - goto next_tri_a; // 已记录交集 - } - } - } - next_tri_a:; - } - - HalfedgeMesh result; - if (!out_verts.empty()) - result.build_from_triangles(out_verts, out_tris); - return result; -} - -} // namespace cpu - -// ====================================================================== -// GPU 路径(CUDA 可用时) -// ====================================================================== -#if VDE_USE_CUDA - -// ── GPU Marching Cubes ──────────────────────────────────────────── -HalfedgeMesh gpu_marching_cubes( - const std::function& sdf, - const AABB3D& bounds, int res) -{ - if (res < 2) res = 2; - int grid_dim = res + 1; // 采样点 = 体素角点数 - - Vector3D ext = bounds.extent(); - double cell_size = std::max({ext.x(), ext.y(), ext.z()}) / res; - - // 在 host 上采样 SDF 网格(避免 device function pointer 复杂性) - std::vector sdf_grid(grid_dim * grid_dim * grid_dim); - for (int iz = 0; iz < grid_dim; ++iz) - for (int iy = 0; iy < grid_dim; ++iy) - for (int ix = 0; ix < grid_dim; ++ix) { - double x = bounds.min().x() + ix * cell_size; - double y = bounds.min().y() + iy * cell_size; - double z = bounds.min().z() + iz * cell_size; - sdf_grid[(iz * grid_dim + iy) * grid_dim + ix] = sdf(x, y, z); - } - - int total_voxels = res * res * res; - - // 分配 GPU 内存 - int *d_tri_count, *d_tri_indices; - double *d_tri_verts, *d_sdf_grid; - - CUDA_CHECK(cudaMalloc(&d_sdf_grid, sdf_grid.size() * sizeof(double))); - CUDA_CHECK(cudaMalloc(&d_tri_count, sizeof(int))); - // 估计最大三角形数:每体素最多 5 个三角形 × 3 顶点 × 3 坐标 - int max_tris = total_voxels * 5; - CUDA_CHECK(cudaMalloc(&d_tri_indices, max_tris * 3 * sizeof(int))); - CUDA_CHECK(cudaMalloc(&d_tri_verts, max_tris * 3 * 3 * sizeof(double))); - - CUDA_CHECK(cudaMemcpy(d_sdf_grid, sdf_grid.data(), - sdf_grid.size() * sizeof(double), - cudaMemcpyHostToDevice)); - CUDA_CHECK(cudaMemset(d_tri_count, 0, sizeof(int))); - - launch_mc_kernel(d_sdf_grid, res, bounds.min(), cell_size, - d_tri_count, d_tri_indices, d_tri_verts); - - // 取回三角形数据 - int h_tri_count = 0; - CUDA_CHECK(cudaMemcpy(&h_tri_count, d_tri_count, sizeof(int), - cudaMemcpyDeviceToHost)); - - HalfedgeMesh result; - if (h_tri_count > 0) { - h_tri_count = std::min(h_tri_count, max_tris); - std::vector h_tri_indices(h_tri_count * 3); - std::vector h_tri_verts(h_tri_count * 3 * 3); - - CUDA_CHECK(cudaMemcpy(h_tri_indices.data(), d_tri_indices, - h_tri_count * 3 * sizeof(int), - cudaMemcpyDeviceToHost)); - CUDA_CHECK(cudaMemcpy(h_tri_verts.data(), d_tri_verts, - h_tri_count * 3 * 3 * sizeof(double), - cudaMemcpyDeviceToHost)); - - std::vector verts; - std::vector> tris; - for (int i = 0; i < h_tri_count; ++i) { - int base = i * 3; - int vbase = base * 3; - int i0 = h_tri_indices[base]; - int i1 = h_tri_indices[base + 1]; - int i2 = h_tri_indices[base + 2]; - - // 用顶点索引直接存储位置;实际项目中应做去重 - int v_idx0 = static_cast(verts.size()); - verts.push_back({h_tri_verts[vbase], h_tri_verts[vbase + 1], h_tri_verts[vbase + 2]}); - int v_idx1 = static_cast(verts.size()); - verts.push_back({h_tri_verts[vbase + 3], h_tri_verts[vbase + 4], h_tri_verts[vbase + 5]}); - int v_idx2 = static_cast(verts.size()); - verts.push_back({h_tri_verts[vbase + 6], h_tri_verts[vbase + 7], h_tri_verts[vbase + 8]}); - tris.push_back({v_idx0, v_idx1, v_idx2}); - } - - result.build_from_triangles(verts, tris); - } - - CUDA_CHECK(cudaFree(d_sdf_grid)); - CUDA_CHECK(cudaFree(d_tri_count)); - CUDA_CHECK(cudaFree(d_tri_indices)); - CUDA_CHECK(cudaFree(d_tri_verts)); - - return result; -} - -// ── GPU Mesh Simplify ──────────────────────────────────────────── -HalfedgeMesh gpu_mesh_simplify(const HalfedgeMesh& mesh, float target_ratio) { - if (mesh.num_vertices() == 0 || mesh.num_faces() == 0) - return mesh; - - int num_verts = static_cast(mesh.num_vertices()); - int num_faces = static_cast(mesh.num_faces()); - size_t target_faces = static_cast(num_faces * target_ratio); - if (target_faces < 1) target_faces = 1; - if (static_cast(num_faces) <= target_faces) - return mesh; - - // 准备 flat arrays - std::vector h_vertices(num_verts * 3); - for (int i = 0; i < num_verts; ++i) { - const Point3D& p = mesh.vertex(i); - h_vertices[i * 3] = p.x(); - h_vertices[i * 3 + 1] = p.y(); - h_vertices[i * 3 + 2] = p.z(); - } - - std::vector h_tri_indices(num_faces * 3); - for (int fi = 0; fi < num_faces; ++fi) { - auto fv = mesh.face_vertices(fi); - h_tri_indices[fi * 3] = fv[0]; - h_tri_indices[fi * 3 + 1] = fv[1]; - h_tri_indices[fi * 3 + 2] = fv[2]; - } - - double *d_vertices, *d_edge_costs; - int *d_tri_indices, *d_collapse_targets; - - CUDA_CHECK(cudaMalloc(&d_vertices, num_verts * 3 * sizeof(double))); - CUDA_CHECK(cudaMalloc(&d_tri_indices, num_faces * 3 * sizeof(int))); - int num_edges_est = num_faces * 3 / 2; - CUDA_CHECK(cudaMalloc(&d_edge_costs, num_edges_est * sizeof(double))); - CUDA_CHECK(cudaMalloc(&d_collapse_targets, num_edges_est * sizeof(int))); - - CUDA_CHECK(cudaMemcpy(d_vertices, h_vertices.data(), - num_verts * 3 * sizeof(double), cudaMemcpyHostToDevice)); - CUDA_CHECK(cudaMemcpy(d_tri_indices, h_tri_indices.data(), - num_faces * 3 * sizeof(int), cudaMemcpyHostToDevice)); - - // 多 pass 简化 - const int MAX_PASSES = 8; - for (int pass = 0; pass < MAX_PASSES; ++pass) { - launch_qem_cost_kernel(d_vertices, num_verts, - d_tri_indices, num_faces, - d_edge_costs, d_collapse_targets); - - // 在 host 侧收集要塌缩的边并更新顶点 - std::vector h_edge_costs(num_edges_est); - std::vector h_collapse(num_edges_est); - CUDA_CHECK(cudaMemcpy(h_edge_costs.data(), d_edge_costs, - num_edges_est * sizeof(double), - cudaMemcpyDeviceToHost)); - CUDA_CHECK(cudaMemcpy(h_collapse.data(), d_collapse_targets, - num_edges_est * sizeof(int), - cudaMemcpyDeviceToHost)); - - // 找最小代价的 N 条边(不共享顶点的独立边集) - // 简化:取代价最小的边,若可减少足够面数就继续 - int current_faces = num_faces; - if (current_faces <= static_cast(target_faces)) - break; - - // 每 pass 减少约 10% 的面 - int collapse_count = std::max(1, current_faces / 10); - current_faces -= collapse_count; - num_faces = current_faces; - } - - // 回读最终顶点 - CUDA_CHECK(cudaMemcpy(h_vertices.data(), d_vertices, - num_verts * 3 * sizeof(double), - cudaMemcpyDeviceToHost)); - - CUDA_CHECK(cudaFree(d_vertices)); - CUDA_CHECK(cudaFree(d_tri_indices)); - CUDA_CHECK(cudaFree(d_edge_costs)); - CUDA_CHECK(cudaFree(d_collapse_targets)); - - // 重建网格 - std::vector verts; - for (int i = 0; i < num_verts; ++i) - verts.push_back({h_vertices[i * 3], h_vertices[i * 3 + 1], h_vertices[i * 3 + 2]}); - - std::vector> tris; - for (int fi = 0; fi < num_faces; ++fi) { - int i0 = h_tri_indices[fi * 3]; - int i1 = h_tri_indices[fi * 3 + 1]; - int i2 = h_tri_indices[fi * 3 + 2]; - if (i0 >= 0 && i1 >= 0 && i2 >= 0) - tris.push_back({i0, i1, i2}); - } - - HalfedgeMesh result; - result.build_from_triangles(verts, tris); - return result; -} - -// ── GPU Boolean Intersect ───────────────────────────────────────── -HalfedgeMesh gpu_boolean_intersect(const HalfedgeMesh& mesh_a, - const HalfedgeMesh& mesh_b) { - int nv_a = static_cast(mesh_a.num_vertices()); - int nv_b = static_cast(mesh_b.num_vertices()); - int ntri_a = static_cast(mesh_a.num_faces()); - int ntri_b = static_cast(mesh_b.num_faces()); - - if (nv_a == 0 || nv_b == 0 || ntri_a == 0 || ntri_b == 0) - return {}; - - // 准备 flat 数据 - std::vector h_verts_a(nv_a * 3), h_verts_b(nv_b * 3); - std::vector h_tris_a(ntri_a * 3), h_tris_b(ntri_b * 3); - - for (int i = 0; i < nv_a; ++i) { - const auto& p = mesh_a.vertex(i); - h_verts_a[i * 3] = p.x(); h_verts_a[i * 3 + 1] = p.y(); h_verts_a[i * 3 + 2] = p.z(); - } - for (int i = 0; i < nv_b; ++i) { - const auto& p = mesh_b.vertex(i); - h_verts_b[i * 3] = p.x(); h_verts_b[i * 3 + 1] = p.y(); h_verts_b[i * 3 + 2] = p.z(); - } - for (int fi = 0; fi < ntri_a; ++fi) { - auto fv = mesh_a.face_vertices(fi); - h_tris_a[fi * 3] = fv[0]; h_tris_a[fi * 3 + 1] = fv[1]; h_tris_a[fi * 3 + 2] = fv[2]; - } - for (int fi = 0; fi < ntri_b; ++fi) { - auto fv = mesh_b.face_vertices(fi); - h_tris_b[fi * 3] = fv[0]; h_tris_b[fi * 3 + 1] = fv[1]; h_tris_b[fi * 3 + 2] = fv[2]; - } - - double *d_verts_a, *d_verts_b; - int *d_tris_a, *d_tris_b, *d_intersect_flags; - int *d_hash_bins, *d_hash_offsets; - - CUDA_CHECK(cudaMalloc(&d_verts_a, nv_a * 3 * sizeof(double))); - CUDA_CHECK(cudaMalloc(&d_verts_b, nv_b * 3 * sizeof(double))); - CUDA_CHECK(cudaMalloc(&d_tris_a, ntri_a * 3 * sizeof(int))); - CUDA_CHECK(cudaMalloc(&d_tris_b, ntri_b * 3 * sizeof(int))); - CUDA_CHECK(cudaMalloc(&d_intersect_flags, ntri_a * sizeof(int))); - - // 空间哈希(在 host 构建,传给 device) - const int HASH_RES = 32; - int total_bins = HASH_RES * HASH_RES * HASH_RES; - CUDA_CHECK(cudaMalloc(&d_hash_bins, total_bins * ntri_b * sizeof(int))); - CUDA_CHECK(cudaMalloc(&d_hash_offsets, (total_bins + 1) * sizeof(int))); - - // 构建哈希(简化:host 端计算) - AABB3D total_bb = mesh_a.bounds(); - total_bb.expand(mesh_b.bounds()); - Vector3D ext = total_bb.extent(); - double cell = std::max({ext.x(), ext.y(), ext.z()}) / HASH_RES; - if (cell < 1e-10) cell = 1.0; - - std::vector h_hash_bins(total_bins * ntri_b, -1); - std::vector h_hash_offsets(total_bins + 1, 0); - std::vector bin_counts(total_bins, 0); - - for (int ti = 0; ti < ntri_b; ++ti) { - Point3D c((h_verts_b[h_tris_b[ti * 3] * 3] + - h_verts_b[h_tris_b[ti * 3 + 1] * 3] + - h_verts_b[h_tris_b[ti * 3 + 2] * 3]) / 3.0, - (h_verts_b[h_tris_b[ti * 3] * 3 + 1] + - h_verts_b[h_tris_b[ti * 3 + 1] * 3 + 1] + - h_verts_b[h_tris_b[ti * 3 + 2] * 3 + 1]) / 3.0, - (h_verts_b[h_tris_b[ti * 3] * 3 + 2] + - h_verts_b[h_tris_b[ti * 3 + 1] * 3 + 2] + - h_verts_b[h_tris_b[ti * 3 + 2] * 3 + 2]) / 3.0); - int ix = std::max(0, std::min(HASH_RES - 1, - static_cast((c.x() - total_bb.min().x()) / cell))); - int iy = std::max(0, std::min(HASH_RES - 1, - static_cast((c.y() - total_bb.min().y()) / cell))); - int iz = std::max(0, std::min(HASH_RES - 1, - static_cast((c.z() - total_bb.min().z()) / cell))); - int bin = (ix * HASH_RES + iy) * HASH_RES + iz; - if (bin_counts[bin] < ntri_b) - h_hash_bins[bin * ntri_b + bin_counts[bin]] = ti; - bin_counts[bin]++; - } - for (int i = 0; i <= total_bins; ++i) - h_hash_offsets[i] = (i == 0) ? 0 : h_hash_offsets[i - 1] + bin_counts[i - 1]; - - CUDA_CHECK(cudaMemcpy(d_verts_a, h_verts_a.data(), - nv_a * 3 * sizeof(double), cudaMemcpyHostToDevice)); - CUDA_CHECK(cudaMemcpy(d_verts_b, h_verts_b.data(), - nv_b * 3 * sizeof(double), cudaMemcpyHostToDevice)); - CUDA_CHECK(cudaMemcpy(d_tris_a, h_tris_a.data(), - ntri_a * 3 * sizeof(int), cudaMemcpyHostToDevice)); - CUDA_CHECK(cudaMemcpy(d_tris_b, h_tris_b.data(), - ntri_b * 3 * sizeof(int), cudaMemcpyHostToDevice)); - CUDA_CHECK(cudaMemcpy(d_hash_bins, h_hash_bins.data(), - total_bins * ntri_b * sizeof(int), cudaMemcpyHostToDevice)); - CUDA_CHECK(cudaMemcpy(d_hash_offsets, h_hash_offsets.data(), - (total_bins + 1) * sizeof(int), cudaMemcpyHostToDevice)); - CUDA_CHECK(cudaMemset(d_intersect_flags, 0, ntri_a * sizeof(int))); - - launch_tri_intersect_kernel(d_verts_a, d_tris_a, ntri_a, - d_verts_b, d_tris_b, ntri_b, - d_hash_bins, d_hash_offsets, - d_intersect_flags); - - // 回读交集标志 - std::vector h_intersect_flags(ntri_a); - CUDA_CHECK(cudaMemcpy(h_intersect_flags.data(), d_intersect_flags, - ntri_a * sizeof(int), cudaMemcpyDeviceToHost)); - - // 收集有交集的三角形 - std::vector out_verts; - std::vector> out_tris; - for (int i = 0; i < ntri_a; ++i) { - if (h_intersect_flags[i]) { - int i0 = h_tris_a[i * 3], i1 = h_tris_a[i * 3 + 1], i2 = h_tris_a[i * 3 + 2]; - int base = static_cast(out_verts.size()); - out_verts.push_back({h_verts_a[i0 * 3], h_verts_a[i0 * 3 + 1], h_verts_a[i0 * 3 + 2]}); - out_verts.push_back({h_verts_a[i1 * 3], h_verts_a[i1 * 3 + 1], h_verts_a[i1 * 3 + 2]}); - out_verts.push_back({h_verts_a[i2 * 3], h_verts_a[i2 * 3 + 1], h_verts_a[i2 * 3 + 2]}); - out_tris.push_back({base, base + 1, base + 2}); - } - } - - HalfedgeMesh result; - if (!out_verts.empty()) - result.build_from_triangles(out_verts, out_tris); - - CUDA_CHECK(cudaFree(d_verts_a)); - CUDA_CHECK(cudaFree(d_verts_b)); - CUDA_CHECK(cudaFree(d_tris_a)); - CUDA_CHECK(cudaFree(d_tris_b)); - CUDA_CHECK(cudaFree(d_intersect_flags)); - CUDA_CHECK(cudaFree(d_hash_bins)); - CUDA_CHECK(cudaFree(d_hash_offsets)); - - return result; -} - -#else // !VDE_USE_CUDA → CPU fallback - -// ====================================================================== -// 无 CUDA:委托给 CPU 实现 -// ====================================================================== - -HalfedgeMesh gpu_marching_cubes( - const std::function& sdf, - const AABB3D& bounds, int res) -{ - return cpu::cpu_marching_cubes(sdf, bounds, res); -} - -HalfedgeMesh gpu_mesh_simplify(const HalfedgeMesh& mesh, float target_ratio) { - return cpu::cpu_mesh_simplify(mesh, target_ratio); -} - -HalfedgeMesh gpu_boolean_intersect(const HalfedgeMesh& mesh_a, - const HalfedgeMesh& mesh_b) { - return cpu::cpu_boolean_intersect(mesh_a, mesh_b); -} - -#endif // VDE_USE_CUDA - -} // namespace vde::gpu diff --git a/src/gpu/gpu_acceleration.cu b/src/gpu/gpu_acceleration.cu deleted file mode 100644 index be1cdc6..0000000 --- a/src/gpu/gpu_acceleration.cu +++ /dev/null @@ -1,393 +0,0 @@ -/** - * @file gpu_acceleration.cu - * @brief CUDA 内核实现:Marching Cubes、QEM 简化、三角形求交 - * - * 本文件仅在 VDE_USE_CUDA 启用时编译。 - * 编译选项:nvcc -arch=sm_60 -std=c++14 - */ - -#include "vde/gpu/gpu_acceleration.h" - -// ── CUDA 常量 ──────────────────────────────────────────────────────────── -#define MC_BLOCK_SIZE 8 // 每个 block 处理 8×8×8 = 512 个体素 -#define QEM_BLOCK_SIZE 256 // QEM 代价计算的 block 大小 -#define TRI_BLOCK_SIZE 128 // 三角形求交的 block 大小 - -// ====================================================================== -// Marching Cubes 边表(device constant memory 友好) -// ====================================================================== -__device__ __constant__ int d_edge_table[256] = { - 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, - 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, - 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, - 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, - 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, - 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, - 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, - 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, - 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, - 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, - 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, - 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, - 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, - 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, - 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , - 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, - 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, - 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, - 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, - 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, - 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, - 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, - 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, - 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, - 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, - 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, - 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, - 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, - 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, - 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, - 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, - 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 -}; - -// 12 条边的端点索引 -__device__ __constant__ int d_edge_vert_map[12][2] = { - {0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4}, - {0,4},{1,5},{2,6},{3,7} -}; - -// 体素 8 角偏移(单位化) -__device__ __constant__ double d_voxel_corners[8][3] = { - {0,0,0},{1,0,0},{1,1,0},{0,1,0}, - {0,0,1},{1,0,1},{1,1,1},{0,1,1} -}; - -// ====================================================================== -// MC:边上线性插值 -// ====================================================================== -__device__ inline void mc_interpolate(double x0, double y0, double z0, double v0, - double x1, double y1, double z1, double v1, - double* out) { - if (fabs(v0) < 1e-10f) { out[0] = x0; out[1] = y0; out[2] = z0; return; } - if (fabs(v1) < 1e-10f) { out[0] = x1; out[1] = y1; out[2] = z1; return; } - if (fabs(v0 - v1) < 1e-10f) { - out[0] = 0.5 * (x0 + x1); out[1] = 0.5 * (y0 + y1); out[2] = 0.5 * (z0 + z1); - return; - } - double t = -v0 / (v1 - v0); - out[0] = x0 + t * (x1 - x0); - out[1] = y0 + t * (y1 - y0); - out[2] = z0 + t * (z1 - z0); -} - -// ====================================================================== -// Marching Cubes 内核 -// 每线程一个体素,共享内存缓存 SDF 值,原子计数器收集三角形 -// ====================================================================== -__global__ void mc_kernel(const double* __restrict__ sdf_grid, - int res, double origin_x, double origin_y, double origin_z, - double cell_size, - int* __restrict__ tri_count, - int* __restrict__ tri_indices, - double* __restrict__ tri_verts) -{ - int ix = blockIdx.x * blockDim.x + threadIdx.x; - int iy = blockIdx.y * blockDim.y + threadIdx.y; - int iz = blockIdx.z * blockDim.z + threadIdx.z; - - if (ix >= res || iy >= res || iz >= res) return; - - int grid_dim = res + 1; - int idx = (iz * grid_dim + iy) * grid_dim + ix; - - // 该体素 8 个角在采样网格中的 SDF 值 - double corner_vals[8]; - for (int c = 0; c < 8; ++c) { - int cx = ix + (int)d_voxel_corners[c][0]; - int cy = iy + (int)d_voxel_corners[c][1]; - int cz = iz + (int)d_voxel_corners[c][2]; - corner_vals[c] = sdf_grid[(cz * grid_dim + cy) * grid_dim + cx]; - } - - // 计算 cube index - int cube_index = 0; - for (int c = 0; c < 8; ++c) - if (corner_vals[c] < 0.0) cube_index |= (1 << c); - - int edge_flags = d_edge_table[cube_index]; - if (edge_flags == 0) return; - - // 体素 8 角世界坐标 - double corner_coords[8][3]; - for (int c = 0; c < 8; ++c) { - corner_coords[c][0] = origin_x + (ix + d_voxel_corners[c][0]) * cell_size; - corner_coords[c][1] = origin_y + (iy + d_voxel_corners[c][1]) * cell_size; - corner_coords[c][2] = origin_z + (iz + d_voxel_corners[c][2]) * cell_size; - } - - // 在边上插值 - double edge_pts[12][3]; - for (int e = 0; e < 12; ++e) { - if (edge_flags & (1 << e)) { - int v0 = d_edge_vert_map[e][0], v1 = d_edge_vert_map[e][1]; - mc_interpolate(corner_coords[v0][0], corner_coords[v0][1], corner_coords[v0][2], - corner_vals[v0], - corner_coords[v1][0], corner_coords[v1][1], corner_coords[v1][2], - corner_vals[v1], - edge_pts[e]); - } - } - - // 三角形表(经典 Lorensen & Cline)的 GPU 精简版 - // 每边 3 条边构成一个三角形,-1 终止 - // 此处使用展开的简化表 — 直接用 edge_flags 驱动的 0~5 三角形 - struct TriEntry { int e[3]; }; - - // 按 cube_index 查表(常数内存中的完整 tri 表省略,使用运行时构建) - // 简化为:按 edge_flags 生成三角形 - // 对于 0 ≤ cube_index ≤ 14,内联前几个常用 case - // 完整实现需要 tri_table,此处展示核心逻辑: - // 使用与 CPU 一致的查表法 - - // 三层嵌套查找(展开为内联表),对每个可能的三角形: - // e0 = triTable[cube_index][i]; e1 = triTable[cube_index][i+1]; e2 = triTable[cube_index][i+2]; - // if(e0 == -1) break; - // write_triangle(edge_pts[e0], edge_pts[e1], edge_pts[e2]); - // - // 由于 CUDA 核函数体限制,此处使用运行时查表方案: - // - // 警告:完整 256 项三角形表会极大增加代码体积。 - // 此内核示意核心逻辑结构。生产代码中需引入 d_tri_table[] constant 内存。 - // - // 替代方案:仅输出 edge_pts 到全局内存,由 host 端后处理。 - // 这里选择简单的方案:直接不生成三角形,仅递增计数(测试用)。 - - // 简化输出到顶点缓冲区 - for (int e = 0; e < 12; ++e) { - if (edge_flags & (1 << e)) { - int slot = atomicAdd(tri_count, 1); - int base = slot * 9; // 3 顶点 × 3 坐标 - tri_verts[base + 0] = edge_pts[e][0]; - tri_verts[base + 1] = edge_pts[e][1]; - tri_verts[base + 2] = edge_pts[e][2]; - tri_indices[slot * 3 + 0] = slot * 3; - tri_indices[slot * 3 + 1] = slot * 3 + 1; - tri_indices[slot * 3 + 2] = slot * 3 + 2; - } - } -} - -// ====================================================================== -// QEM 边代价计算内核 -// ====================================================================== -__global__ void qem_cost_kernel(const double* __restrict__ vertices, - int num_verts, - const int* __restrict__ tri_indices, - int num_tris, - double* __restrict__ edge_costs, - int* __restrict__ collapse_targets) -{ - int tid = blockIdx.x * blockDim.x + threadIdx.x; - if (tid >= num_tris) return; - - int i0 = tri_indices[tid * 3]; - int i1 = tri_indices[tid * 3 + 1]; - int i2 = tri_indices[tid * 3 + 2]; - - // 取顶点 - double v0x = vertices[i0 * 3], v0y = vertices[i0 * 3 + 1], v0z = vertices[i0 * 3 + 2]; - double v1x = vertices[i1 * 3], v1y = vertices[i1 * 3 + 1], v1z = vertices[i1 * 3 + 2]; - double v2x = vertices[i2 * 3], v2y = vertices[i2 * 3 + 1], v2z = vertices[i2 * 3 + 2]; - - // 面法向(未归一化) - double e1x = v1x - v0x, e1y = v1y - v0y, e1z = v1z - v0z; - double e2x = v2x - v0x, e2y = v2y - v0y, e2z = v2z - v0z; - - double nx = e1y * e2z - e1z * e2y; - double ny = e1z * e2x - e1x * e2z; - double nz = e1x * e2y - e1y * e2x; - - double len = sqrt(nx * nx + ny * ny + nz * nz); - if (len < 1e-12) { - edge_costs[tid] = 1e30; // 退化面 → 不要塌缩 - collapse_targets[tid] = -1; - return; - } - - nx /= len; ny /= len; nz /= len; - double d_plane = -(nx * v0x + ny * v0y + nz * v0z); - - // Q = Kp^T Kp 元素 - double a11 = nx * nx, a12 = nx * ny, a13 = nx * nz, a14 = nx * d_plane; - double a22 = ny * ny, a23 = ny * nz, a24 = ny * d_plane; - double a33 = nz * nz, a34 = nz * d_plane; - double a44 = d_plane * d_plane; - - // 边 (i0,i1) 的中点代价 - double mx = 0.5 * (v0x + v1x), my = 0.5 * (v0y + v1y), mz = 0.5 * (v0z + v1z); - double cost = a11 * mx * mx + 2.0 * a12 * mx * my + 2.0 * a13 * mx * mz + - 2.0 * a14 * mx + a22 * my * my + 2.0 * a23 * my * mz + - 2.0 * a24 * my + a33 * mz * mz + 2.0 * a34 * mz + a44; - - edge_costs[tid] = cost; - collapse_targets[tid] = (cost < 0.01) ? i0 : -1; // 低代价 → 候选塌缩 -} - -// ====================================================================== -// 三角形-三角形求交内核 -// ====================================================================== -__device__ inline bool tri_tri_overlap_device( - const double* va, const double* vb, const double* vc, - const double* ua, const double* ub, const double* uc) -{ - // AABB 快速剔除 - double aminx = fmin(fmin(va[0], vb[0]), vc[0]); - double amaxx = fmax(fmax(va[0], vb[0]), vc[0]); - double aminy = fmin(fmin(va[1], vb[1]), vc[1]); - double amaxy = fmax(fmax(va[1], vb[1]), vc[1]); - double aminz = fmin(fmin(va[2], vb[2]), vc[2]); - double amaxz = fmax(fmax(va[2], vb[2]), vc[2]); - - double bminx = fmin(fmin(ua[0], ub[0]), uc[0]); - double bmaxx = fmax(fmax(ua[0], ub[0]), uc[0]); - double bminy = fmin(fmin(ua[1], ub[1]), uc[1]); - double bmaxy = fmax(fmax(ua[1], ub[1]), uc[1]); - double bminz = fmin(fmin(ua[2], ub[2]), uc[2]); - double bmaxz = fmax(fmax(ua[2], ub[2]), uc[2]); - - if (amaxx < bminx || aminx > bmaxx || - amaxy < bminy || aminy > bmaxy || - amaxz < bminz || aminz > bmaxz) - return false; - - // 平面法向 - double e1x = vb[0] - va[0], e1y = vb[1] - va[1], e1z = vb[2] - va[2]; - double e2x = vc[0] - va[0], e2y = vc[1] - va[1], e2z = vc[2] - va[2]; - double nx = e1y * e2z - e1z * e2y; - double ny = e1z * e2x - e1x * e2z; - double nz = e1x * e2y - e1y * e2x; - double nlen = sqrt(nx * nx + ny * ny + nz * nz); - if (nlen < 1e-12) return false; - nx /= nlen; ny /= nlen; nz /= nlen; - double d_plane = -(nx * va[0] + ny * va[1] + nz * va[2]); - - // tb 各点到 ta 平面的有向距离 - double d0 = nx * ua[0] + ny * ua[1] + nz * ua[2] + d_plane; - double d1 = nx * ub[0] + ny * ub[1] + nz * ub[2] + d_plane; - double d2 = nx * uc[0] + ny * uc[1] + nz * uc[2] + d_plane; - - if ((d0 > 1e-5f && d1 > 1e-5f && d2 > 1e-5f) || - (d0 < -1e-5f && d1 < -1e-5f && d2 < -1e-5f)) - return false; - - // 双方平面都穿越 → 交集存在 - return true; -} - -__global__ void tri_intersect_kernel(const double* __restrict__ verts_a, - const int* __restrict__ tris_a, - int ntri_a, - const double* __restrict__ verts_b, - const int* __restrict__ tris_b, - int ntri_b, - const int* __restrict__ hash_bins, - const int* __restrict__ hash_offsets, - int hash_res, - int max_per_bin, - int* __restrict__ intersect_flags) -{ - int tid = blockIdx.x * blockDim.x + threadIdx.x; - if (tid >= ntri_a) return; - - int i0 = tris_a[tid * 3], i1 = tris_a[tid * 3 + 1], i2 = tris_a[tid * 3 + 2]; - const double* va = &verts_a[i0 * 3]; - const double* vb = &verts_a[i1 * 3]; - const double* vc = &verts_a[i2 * 3]; - - // 计算三角形 a 的中心所属哈希格 - double cx = (va[0] + vb[0] + vc[0]) / 3.0; - double cy = (va[1] + vb[1] + vc[1]) / 3.0; - double cz = (va[2] + vb[2] + vc[2]) / 3.0; - - // 简化:直接在全部 B 三角形上遍历,受限于空间哈希分组 - // 迭代本格及 26 邻格 - for (int bin_offset = 0; bin_offset < 27; ++bin_offset) { - int bin = (tid * 27 + bin_offset) % (hash_res * hash_res * hash_res); - int start = hash_offsets[bin]; - int end = hash_offsets[bin + 1]; - - for (int idx = start; idx < end; ++idx) { - int b_tri_idx = hash_bins[idx]; - if (b_tri_idx < 0 || b_tri_idx >= ntri_b) continue; - - int j0 = tris_b[b_tri_idx * 3]; - int j1 = tris_b[b_tri_idx * 3 + 1]; - int j2 = tris_b[b_tri_idx * 3 + 2]; - - if (tri_tri_overlap_device(va, vb, vc, - &verts_b[j0 * 3], - &verts_b[j1 * 3], - &verts_b[j2 * 3])) { - intersect_flags[tid] = 1; - return; // 找到一个即止 - } - } - } -} - -// ====================================================================== -// 启动包装函数 -// ====================================================================== -namespace vde::gpu { - -void launch_mc_kernel(const double* sdf_grid, int res, - const Point3D& origin, double cell_size, - int* tri_count, int* tri_indices, - double* tri_verts) -{ - dim3 block(MC_BLOCK_SIZE, MC_BLOCK_SIZE, MC_BLOCK_SIZE); - dim3 grid((res + block.x - 1) / block.x, - (res + block.y - 1) / block.y, - (res + block.z - 1) / block.z); - - mc_kernel<<>>(sdf_grid, res, - origin.x(), origin.y(), origin.z(), - cell_size, tri_count, tri_indices, tri_verts); - CUDA_CHECK(cudaGetLastError()); - CUDA_CHECK(cudaDeviceSynchronize()); -} - -void launch_qem_cost_kernel(const double* vertices, int num_verts, - const int* tri_indices, int num_tris, - double* edge_costs, int* collapse_targets) -{ - int block = QEM_BLOCK_SIZE; - int grid = (num_tris + block - 1) / block; - qem_cost_kernel<<>>(vertices, num_verts, - tri_indices, num_tris, - edge_costs, collapse_targets); - CUDA_CHECK(cudaGetLastError()); - CUDA_CHECK(cudaDeviceSynchronize()); -} - -void launch_tri_intersect_kernel(const double* verts_a, const int* tris_a, int ntri_a, - const double* verts_b, const int* tris_b, int ntri_b, - const int* hash_bins, const int* hash_offsets, - int* intersect_flags) -{ - const int HASH_RES = 32; - const int MAX_PER_BIN = ntri_b; - - int block = TRI_BLOCK_SIZE; - int grid = (ntri_a + block - 1) / block; - tri_intersect_kernel<<>>(verts_a, tris_a, ntri_a, - verts_b, tris_b, ntri_b, - hash_bins, hash_offsets, - HASH_RES, MAX_PER_BIN, - intersect_flags); - CUDA_CHECK(cudaGetLastError()); - CUDA_CHECK(cudaDeviceSynchronize()); -} - -} // namespace vde::gpu diff --git a/src/kbe/knowledge_engine.cpp b/src/kbe/knowledge_engine.cpp deleted file mode 100644 index c342faf..0000000 --- a/src/kbe/knowledge_engine.cpp +++ /dev/null @@ -1,818 +0,0 @@ -/// @file knowledge_engine.cpp 知识工程 —— 实现 -#include "vde/kbe/knowledge_engine.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace vde::kbe { - -// ═══════════════════════════════════════════════════════ -// CheckMate -// ═══════════════════════════════════════════════════════ - -CheckMate::CheckMate() { - register_default_rules(); -} - -void CheckMate::register_default_rules() { - // 几何规则 - register_rule({"R_GEO_POSITIVE_DIM", "geometry", "尺寸必须为正数", CheckSeverity::ERROR}); - register_rule({"R_GEO_MIN_WALL", "geometry", "壁厚不低于最小值", CheckSeverity::WARNING}); - register_rule({"R_GEO_MAX_ASPECT", "geometry", "长宽比不超标", CheckSeverity::WARNING}); - register_rule({"R_GEO_CLOSED_SHELL", "geometry", "壳体必须封闭", CheckSeverity::ERROR}); - register_rule({"R_GEO_NON_SELF_INTER", "geometry", "模型无自相交", CheckSeverity::FATAL}); - - // 拓扑规则 - register_rule({"R_TOP_MANIFOLD", "topology", "流形拓扑检查", CheckSeverity::ERROR}); - register_rule({"R_TOP_WATER_TIGHT", "topology", "模型须水密", CheckSeverity::ERROR}); - register_rule({"R_TOP_NO_DEGENERATE", "topology", "无退化面/边", CheckSeverity::WARNING}); - - // 材料规则 - register_rule({"R_MAT_DENSITY", "material", "材料密度合法范围", CheckSeverity::WARNING}); - register_rule({"R_MAT_STRENGTH", "material", "材料强度安全系数≥ 1.5", CheckSeverity::ERROR}); - - // 装配规则 - register_rule({"R_ASM_INTERFERENCE", "assembly", "装配体无干涉", CheckSeverity::FATAL}); - register_rule({"R_ASM_CLEARANCE", "assembly", "运动间隙≥ 0.1mm", CheckSeverity::WARNING}); - register_rule({"R_ASM_FASTENER", "assembly", "紧固件数量与孔数一致", CheckSeverity::WARNING}); -} - -void CheckMate::register_rule(const CheckRule& rule) { - auto it = std::find_if(rules_.begin(), rules_.end(), - [&](const CheckRule& r) { return r.name == rule.name; }); - if (it != rules_.end()) { - *it = rule; // 覆盖 - } else { - rules_.push_back(rule); - } -} - -void CheckMate::add_check(const std::string& rule_name, - std::function&)> fn) { - checks_[rule_name] = std::move(fn); -} - -std::vector CheckMate::run_all(const std::vector& params) const { - std::vector results; - for (const auto& rule : rules_) { - if (!rule.enabled) continue; - results.push_back(run_rule(rule.name, params)); - } - return results; -} - -std::vector CheckMate::run_category(const std::string& category, - const std::vector& params) const { - std::vector results; - for (const auto& rule : rules_) { - if (!rule.enabled || rule.category != category) continue; - results.push_back(run_rule(rule.name, params)); - } - return results; -} - -CheckResult CheckMate::run_rule(const std::string& rule_name, - const std::vector& params) const { - CheckResult result; - result.rule_name = rule_name; - - // 查找预定义规则 - auto rule_it = std::find_if(rules_.begin(), rules_.end(), - [&](const CheckRule& r) { return r.name == rule_name; }); - if (rule_it == rules_.end()) { - result.severity = CheckSeverity::ERROR; - result.message = "Rule not found: " + rule_name; - result.passed = false; - return result; - } - - result.severity = rule_it->default_severity; - - // 检查是否有自定义验证函数 - auto check_it = checks_.find(rule_name); - if (check_it != checks_.end()) { - return check_it->second(params); - } - - // 默认实现:查找参数并执行基本检查 - std::string pname; - if (rule_name == "R_GEO_POSITIVE_DIM") pname = "diameter"; - else if (rule_name == "R_GEO_MIN_WALL") pname = "wall_thickness"; - else if (rule_name == "R_GEO_MAX_ASPECT") pname = "aspect_ratio"; - else if (rule_name == "R_MAT_DENSITY") pname = "density"; - else if (rule_name == "R_MAT_STRENGTH") pname = "safety_factor"; - - if (!pname.empty()) { - for (const auto& p : params) { - if (p.name == pname) { - if (auto* v = std::get_if(&p.value)) { - if (rule_name == "R_GEO_POSITIVE_DIM") { - result.passed = (*v > 0.0); - result.message = result.passed ? "OK" : "Dimension must be > 0"; - } else if (rule_name == "R_GEO_MIN_WALL") { - result.passed = (*v >= 0.5); - result.message = result.passed ? "OK" : "Wall thickness too thin"; - } else if (rule_name == "R_GEO_MAX_ASPECT") { - result.passed = (*v <= 20.0); - result.message = result.passed ? "OK" : "Aspect ratio exceeds limit"; - } else if (rule_name == "R_MAT_DENSITY") { - result.passed = (*v > 0.0 && *v < 25000.0); - result.message = result.passed ? "OK" : "Density out of valid range"; - } else if (rule_name == "R_MAT_STRENGTH") { - result.passed = (*v >= 1.5); - result.message = result.passed ? "OK" : "Safety factor insufficient"; - } - return result; - } - } - } - } - - // 规则无默认实现时,标记为通过(需要扩展 add_check) - result.passed = true; - result.message = "No default implementation (use add_check)"; - return result; -} - -void CheckMate::set_rule_enabled(const std::string& name, bool enabled) { - for (auto& r : rules_) { - if (r.name == name) { - r.enabled = enabled; - return; - } - } -} - -std::vector CheckMate::rules() const { - return rules_; -} - -std::map CheckMate::summary(const std::vector& results) const { - std::map s; - for (const auto& r : results) { - if (!r.passed) s[r.severity]++; - } - return s; -} - -// ═══════════════════════════════════════════════════════ -// RuleEngine -// ═══════════════════════════════════════════════════════ - -void RuleEngine::add_rule(const Rule& rule) { - auto it = std::find_if(rules_.begin(), rules_.end(), - [&](const Rule& r) { return r.name == rule.name; }); - if (it != rules_.end()) { - *it = rule; - } else { - rules_.push_back(rule); - } -} - -bool RuleEngine::remove_rule(const std::string& name) { - auto it = std::find_if(rules_.begin(), rules_.end(), - [&](const Rule& r) { return r.name == name; }); - if (it == rules_.end()) return false; - rules_.erase(it); - return true; -} - -void RuleEngine::set_param(const std::string& name, const ParamValue& value) { - params_[name] = value; -} - -std::optional RuleEngine::get_param(const std::string& name) const { - auto it = params_.find(name); - if (it != params_.end()) return it->second; - return std::nullopt; -} - -bool RuleEngine::parse_expression(const std::string& expr, double& out) const { - // 处理简单表达式:数字、参数引用、基本运算 - std::string s = expr; - // 去除空格 - s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end()); - - // 替换参数引用 - for (const auto& [name, value] : params_) { - if (auto* dv = std::get_if(&value)) { - std::string placeholder = name; - size_t pos = 0; - while ((pos = s.find(placeholder, pos)) != std::string::npos) { - // 确保是一个完整的标识符 - bool left_ok = (pos == 0 || !std::isalnum(s[pos-1]) && s[pos-1] != '_'); - bool right_ok = (pos + placeholder.size() == s.size() || - !std::isalnum(s[pos + placeholder.size()]) && s[pos + placeholder.size()] != '_'); - if (left_ok && right_ok) { - s.replace(pos, placeholder.size(), std::to_string(*dv)); - pos += std::to_string(*dv).size(); - } else { - pos++; - } - } - } else if (auto* iv = std::get_if(&value)) { - std::string placeholder = name; - size_t pos = 0; - while ((pos = s.find(placeholder, pos)) != std::string::npos) { - bool left_ok = (pos == 0 || !std::isalnum(s[pos-1]) && s[pos-1] != '_'); - bool right_ok = (pos + placeholder.size() == s.size() || - !std::isalnum(s[pos + placeholder.size()]) && s[pos + placeholder.size()] != '_'); - if (left_ok && right_ok) { - s.replace(pos, placeholder.size(), std::to_string(*iv)); - pos += std::to_string(*iv).size(); - } else { - pos++; - } - } - } - } - - // 尝试解析为数值 - try { - out = std::stod(s); - return true; - } catch (...) { - return false; - } -} - -bool RuleEngine::evaluate_condition(const std::string& condition) const { - // 支持简单的比较表达式:param > value, param < value, param >= value, param <= value, param == value - // 以及 AND / OR 连接 - - if (condition.empty()) return true; - - // 处理 AND - size_t and_pos = condition.find(" AND "); - if (and_pos != std::string::npos) { - return evaluate_condition(condition.substr(0, and_pos)) && - evaluate_condition(condition.substr(and_pos + 5)); - } - - // 处理 OR - size_t or_pos = condition.find(" OR "); - if (or_pos != std::string::npos) { - return evaluate_condition(condition.substr(0, or_pos)) || - evaluate_condition(condition.substr(or_pos + 4)); - } - - // 比较运算 - std::string op; - size_t op_pos = std::string::npos; - - if ((op_pos = condition.find(">=")) != std::string::npos) op = ">="; - else if ((op_pos = condition.find("<=")) != std::string::npos) op = "<="; - else if ((op_pos = condition.find("!=")) != std::string::npos) op = "!="; - else if ((op_pos = condition.find("==")) != std::string::npos) op = "=="; - else if ((op_pos = condition.find(">")) != std::string::npos) op = ">"; - else if ((op_pos = condition.find("<")) != std::string::npos) op = "<"; - - if (op_pos == std::string::npos) { - // 无运算符,尝试当作布尔参数 - std::string trimmed = condition; - trimmed.erase(std::remove_if(trimmed.begin(), trimmed.end(), ::isspace), trimmed.end()); - auto it = params_.find(trimmed); - if (it != params_.end()) { - if (auto* bv = std::get_if(&it->second)) return *bv; - } - return false; - } - - std::string left_str = condition.substr(0, op_pos); - std::string right_str = condition.substr(op_pos + op.size()); - - double left_val = 0, right_val = 0; - if (!parse_expression(left_str, left_val)) return false; - if (!parse_expression(right_str, right_val)) return false; - - if (op == ">") return left_val > right_val; - if (op == "<") return left_val < right_val; - if (op == ">=") return left_val >= right_val; - if (op == "<=") return left_val <= right_val; - if (op == "==") return std::abs(left_val - right_val) < 1e-9; - if (op == "!=") return std::abs(left_val - right_val) >= 1e-9; - - return false; -} - -ParamValue RuleEngine::evaluate_action(const std::string& action) const { - // 解析 SET param = value - std::string s = action; - std::string prefix = "SET "; - if (s.find(prefix) != 0) { - // 返回一个空字符串 - return std::string(""); - } - s = s.substr(prefix.size()); - auto eq_pos = s.find('='); - if (eq_pos == std::string::npos) return std::string(""); - - std::string val_str = s.substr(eq_pos + 1); - double val = 0; - if (parse_expression(val_str, val)) { - return val; - } - // 去除空格,当字符串 - val_str.erase(std::remove_if(val_str.begin(), val_str.end(), ::isspace), val_str.end()); - return std::string(val_str); -} - -std::vector RuleEngine::infer() { - std::vector traces; - fired_set_.clear(); - - bool changed = true; - int max_iterations = 100; // 防止无限循环 - - while (changed && max_iterations-- > 0) { - changed = false; - // 按优先级排序(高优先级先执行) - std::sort(rules_.begin(), rules_.end(), - [](const Rule& a, const Rule& b) { return a.priority > b.priority; }); - - for (const auto& rule : rules_) { - if (!rule.enabled) continue; - if (fired_set_.count(rule.name)) continue; - - if (evaluate_condition(rule.condition)) { - fired_set_.insert(rule.name); - changed = true; - - ParamValue new_val = evaluate_action(rule.action); - std::string param_name; - - // 从 action 提取参数名: "SET name = value" - std::string s = rule.action; - std::string prefix = "SET "; - if (s.find(prefix) == 0) { - s = s.substr(prefix.size()); - auto eq_pos = s.find('='); - if (eq_pos != std::string::npos) { - param_name = s.substr(0, eq_pos); - // trim - param_name.erase(std::remove_if(param_name.begin(), param_name.end(), ::isspace), param_name.end()); - } - } - - RuleTrace trace; - trace.rule_name = rule.name; - trace.fired = true; - auto old_it = params_.find(param_name); - if (old_it != params_.end()) trace.old_value = old_it->second; - trace.new_value = new_val; - - params_[param_name] = new_val; - traces.push_back(trace); - } - } - } - return traces; -} - -bool RuleEngine::has_cycles() const { - // 简化:检查是否有规则自身引用 - for (const auto& rule : rules_) { - std::string s = rule.condition; - for (const auto& [name, _] : params_) { - if (s.find(name) != std::string::npos) { - // 如果 action 也修改同一个参数,可能存在循环 - if (rule.action.find(name) != std::string::npos) { - return true; // 自引用可能有循环风险 - } - } - } - } - return false; -} - -std::vector RuleEngine::param_names() const { - std::vector names; - for (const auto& [k, _] : params_) names.push_back(k); - return names; -} - -std::vector RuleEngine::rules() const { - return rules_; -} - -void RuleEngine::reset() { - fired_set_.clear(); -} - -// ═══════════════════════════════════════════════════════ -// DesignTable -// ═══════════════════════════════════════════════════════ - -void DesignTable::define_columns(const std::vector& col_names, - const std::vector& units) { - columns_ = col_names; - units_ = units; - if (units_.size() < columns_.size()) { - units_.resize(columns_.size()); - } -} - -void DesignTable::add_row(const DesignRow& row) { - rows_.push_back(row); -} - -std::optional DesignTable::get_row(size_t index) const { - if (index >= rows_.size()) return std::nullopt; - return rows_[index]; -} - -std::vector DesignTable::rows() const { - return rows_; -} - -bool DesignTable::remove_row(size_t index) { - if (index >= rows_.size()) return false; - rows_.erase(rows_.begin() + static_cast(index)); - return true; -} - -void DesignTable::clear() { - rows_.clear(); -} - -ParamValue DesignTable::parse_value(const std::string& s) const { - // 尝试解析为 double - try { - size_t pos = 0; - double d = std::stod(s, &pos); - if (pos == s.size()) return d; - } catch (...) {} - - // 尝试解析为 int - try { - size_t pos = 0; - int i = std::stoi(s, &pos); - if (pos == s.size()) return i; - } catch (...) {} - - // 尝试解析为 bool - if (s == "true" || s == "TRUE" || s == "1") return true; - if (s == "false" || s == "FALSE" || s == "0") return false; - - // 默认字符串 - return s; -} - -bool DesignTable::import_csv(const std::string& csv_content) { - std::istringstream stream(csv_content); - std::string line; - bool header_processed = false; - - while (std::getline(stream, line)) { - if (line.empty()) continue; - - // 分割 CSV 行(简单逗号分割) - std::vector fields; - std::string field; - bool in_quotes = false; - for (char c : line) { - if (c == '"') { - in_quotes = !in_quotes; - } else if (c == ',' && !in_quotes) { - fields.push_back(field); - field.clear(); - } else { - field += c; - } - } - fields.push_back(field); - - if (!header_processed) { - // 第一行是列名 - define_columns(fields); - header_processed = true; - } else { - DesignRow row; - for (size_t i = 0; i < fields.size(); ++i) { - if (i < columns_.size()) { - row[columns_[i]] = parse_value(fields[i]); - } - } - rows_.push_back(row); - } - } - return !rows_.empty(); -} - -std::string DesignTable::export_csv() const { - std::ostringstream ss; - - // 表头 - for (size_t i = 0; i < columns_.size(); ++i) { - if (i > 0) ss << ","; - ss << columns_[i]; - } - ss << "\n"; - - // 数据行 - for (const auto& row : rows_) { - for (size_t i = 0; i < columns_.size(); ++i) { - if (i > 0) ss << ","; - auto it = row.find(columns_[i]); - if (it != row.end()) { - std::visit([&](const auto& v) { ss << v; }, it->second); - } - } - ss << "\n"; - } - return ss.str(); -} - -bool DesignTable::import_excel(const std::string& filepath) { - (void)filepath; - // Stub: 需要第三方库(如 libxlsx)或外部工具 - return false; -} - -bool DesignTable::export_excel(const std::string& filepath) const { - (void)filepath; - // Stub - return false; -} - -// ═══════════════════════════════════════════════════════ -// ParametricOptimization -// ═══════════════════════════════════════════════════════ - -void ParametricOptimization::set_fitness(FitnessFn fn) { - fitness_fn_ = std::move(fn); -} - -void ParametricOptimization::add_constraint(ConstraintFn fn) { - constraints_.push_back(std::move(fn)); -} - -bool ParametricOptimization::satisfies_all(const std::vector& params) const { - for (const auto& c : constraints_) { - if (c && !c(params)) return false; - } - return true; -} - -double ParametricOptimization::evaluate(const std::vector& params) { - if (!fitness_fn_) return 0.0; - double f = fitness_fn_(params); - fitness_history_.push_back(f); - return f; -} - -ParametricOptimization::Individual -ParametricOptimization::create_random_individual(const std::vector& template_params) const { - static thread_local std::mt19937 rng(static_cast(std::time(nullptr) + rand())); - Individual ind; - ind.params = template_params; - - for (auto& p : ind.params) { - if (p.locked) continue; - if (auto* dv = std::get_if(&p.value)) { - double lo = *dv * 0.5, hi = *dv * 1.5; - auto bit = bounds_.find(p.name); - if (bit != bounds_.end()) { - if (auto* bv = std::get_if(&bit->second.first)) lo = *bv; - if (auto* bv = std::get_if(&bit->second.second)) hi = *bv; - } - std::uniform_real_distribution dist(lo, hi); - *dv = dist(rng); - } else if (auto* iv = std::get_if(&p.value)) { - int lo = *iv / 2, hi = *iv * 2; - auto bit = bounds_.find(p.name); - if (bit != bounds_.end()) { - if (auto* bv = std::get_if(&bit->second.first)) lo = *bv; - if (auto* bv = std::get_if(&bit->second.second)) hi = *bv; - } - std::uniform_int_distribution dist(lo, hi); - *iv = dist(rng); - } - } - return ind; -} - -ParametricOptimization::Individual -ParametricOptimization::crossover(const Individual& a, const Individual& b) const { - static thread_local std::mt19937 rng(static_cast(std::time(nullptr))); - Individual child; - child.params = a.params; - std::uniform_real_distribution coin(0.0, 1.0); - - for (size_t i = 0; i < child.params.size(); ++i) { - if (child.params[i].locked) continue; - if (coin(rng) < 0.5) { - child.params[i].value = b.params[i].value; - } - } - return child; -} - -void ParametricOptimization::mutate(Individual& ind) const { - static thread_local std::mt19937 rng(static_cast(std::time(nullptr) + rand())); - std::uniform_real_distribution coin(0.0, 1.0); - std::normal_distribution gauss(0.0, 0.1); - - for (auto& p : ind.params) { - if (p.locked) continue; - if (auto* dv = std::get_if(&p.value)) { - if (coin(rng) < 0.05) { - *dv += gauss(rng) * (*dv); - } - // clamp to bounds - auto bit = bounds_.find(p.name); - if (bit != bounds_.end()) { - if (auto* bv = std::get_if(&bit->second.first)) *dv = std::max(*dv, *bv); - if (auto* bv = std::get_if(&bit->second.second)) *dv = std::min(*dv, *bv); - } - } - } -} - -OptimizationResult -ParametricOptimization::optimize_ga(const std::vector& initial, - const GAConfig& config) { - OptimizationResult result; - fitness_history_.clear(); - - auto t_start = std::chrono::steady_clock::now(); - - // 初始化种群 - std::vector population; - population.reserve(config.population_size); - for (size_t i = 0; i < config.population_size; ++i) { - population.push_back(create_random_individual(initial)); - } - - // 评价初始种群 - for (auto& ind : population) { - if (satisfies_all(ind.params)) { - ind.fitness = evaluate(ind.params); - } else { - ind.fitness = -std::numeric_limits::infinity(); - } - } - - // 进化循环 - for (size_t gen = 0; gen < config.generations; ++gen) { - // 按适应度降序排序 - std::sort(population.begin(), population.end(), - [](const Individual& a, const Individual& b) { return a.fitness > b.fitness; }); - - std::vector next_gen; - - // 精英保留 - for (size_t i = 0; i < config.elitism_count && i < population.size(); ++i) { - next_gen.push_back(population[i]); - } - - // 交叉 + 变异 - static thread_local std::mt19937 rng(static_cast(std::time(nullptr))); - std::uniform_int_distribution parent_dist(0, config.population_size / 2); - std::uniform_real_distribution coin(0.0, 1.0); - - while (next_gen.size() < config.population_size) { - auto& p1 = population[parent_dist(rng)]; - auto& p2 = population[parent_dist(rng)]; - Individual child; - if (coin(rng) < config.crossover_rate) { - child = crossover(p1, p2); - } else { - child = (coin(rng) < 0.5) ? p1 : p2; - } - if (coin(rng) < config.mutation_rate) { - mutate(child); - } - if (satisfies_all(child.params)) { - child.fitness = evaluate(child.params); - } else { - child.fitness = -std::numeric_limits::infinity(); - } - next_gen.push_back(std::move(child)); - } - - population = std::move(next_gen); - } - - // 返回最优个体 - std::sort(population.begin(), population.end(), - [](const Individual& a, const Individual& b) { return a.fitness > b.fitness; }); - - result.optimal_params = population[0].params; - result.optimal_fitness = population[0].fitness; - result.iterations = static_cast(config.generations); - result.converged = true; - - auto t_end = std::chrono::steady_clock::now(); - result.duration = std::chrono::duration_cast(t_end - t_start); - - return result; -} - -double ParametricOptimization::numerical_gradient(const std::vector& params, - size_t param_idx, double epsilon) const { - std::vector forward = params; - std::vector backward = params; - - auto& fv = forward[param_idx]; - auto& bv = backward[param_idx]; - - if (auto* dv = std::get_if(&fv.value)) { - *dv += epsilon; - } - if (auto* dv = std::get_if(&bv.value)) { - *dv -= epsilon; - } - - double f_plus = fitness_fn_ ? fitness_fn_(forward) : 0.0; - double f_minus = fitness_fn_ ? fitness_fn_(backward) : 0.0; - return (f_plus - f_minus) / (2.0 * epsilon); -} - -OptimizationResult -ParametricOptimization::optimize_gradient(const std::vector& initial, - const GradientConfig& config) { - OptimizationResult result; - fitness_history_.clear(); - - auto t_start = std::chrono::steady_clock::now(); - - std::vector current = initial; - std::vector velocity; - velocity.resize(current.size(), 0.0); - - double beta1 = 0.9, beta2 = 0.999, eps_adam = 1e-8; - std::vector m(current.size(), 0.0); - std::vector v(current.size(), 0.0); - int t = 0; - - for (int iter = 0; iter < config.max_iterations; ++iter) { - t++; - double prev_fitness = fitness_fn_ ? fitness_fn_(current) : 0.0; - - for (size_t i = 0; i < current.size(); ++i) { - if (current[i].locked) continue; - if (!std::holds_alternative(current[i].value)) continue; - - double grad = numerical_gradient(current, i); - - if (config.use_adam) { - m[i] = beta1 * m[i] + (1.0 - beta1) * grad; - v[i] = beta2 * v[i] + (1.0 - beta2) * grad * grad; - double m_hat = m[i] / (1.0 - std::pow(beta1, t)); - double v_hat = v[i] / (1.0 - std::pow(beta2, t)); - double step = config.learning_rate * m_hat / (std::sqrt(v_hat) + eps_adam); - auto& dv = std::get(current[i].value); - dv -= step; - } else { - velocity[i] = config.momentum * velocity[i] - config.learning_rate * grad; - auto& dv = std::get(current[i].value); - dv += velocity[i]; - } - - // clamp to bounds - auto bit = bounds_.find(current[i].name); - if (bit != bounds_.end()) { - if (auto* bv = std::get_if(&bit->second.first)) { - auto& dv = std::get(current[i].value); - dv = std::max(dv, *bv); - } - if (auto* bv = std::get_if(&bit->second.second)) { - auto& dv = std::get(current[i].value); - dv = std::min(dv, *bv); - } - } - } - - double cur_fitness = fitness_fn_ ? fitness_fn_(current) : 0.0; - fitness_history_.push_back(cur_fitness); - - if (std::abs(cur_fitness - prev_fitness) < config.tolerance) { - result.converged = true; - break; - } - result.iterations = iter + 1; - } - - result.optimal_params = current; - result.optimal_fitness = fitness_fn_ ? fitness_fn_(current) : 0.0; - - auto t_end = std::chrono::steady_clock::now(); - result.duration = std::chrono::duration_cast(t_end - t_start); - - return result; -} - -void ParametricOptimization::set_bounds(const std::string& param_name, - const ParamValue& min_val, - const ParamValue& max_val) { - bounds_[param_name] = {min_val, max_val}; -} - -} // namespace vde::kbe diff --git a/src/plugin/plugin_system.cpp b/src/plugin/plugin_system.cpp deleted file mode 100644 index b069e03..0000000 --- a/src/plugin/plugin_system.cpp +++ /dev/null @@ -1,654 +0,0 @@ -/** - * @file plugin_system.cpp - * @brief 插件系统实现 — 动态库加载、符号解析、生命周期管理 - * - * 跨平台实现: - * - Linux/macOS: dlopen / dlsym / dlclose - * - Windows: LoadLibraryA / GetProcAddress / FreeLibrary - * - * @ingroup plugin - * @since v6.0 - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -// ═══════════════════════════════════════════════════════════ -// 平台头文件 -// ═══════════════════════════════════════════════════════════ - -#if defined(_WIN32) || defined(_WIN64) - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #include - using NativeHandle = HMODULE; - #define VDE_INVALID_HANDLE nullptr -#else - #include - using NativeHandle = void*; - #define VDE_INVALID_HANDLE nullptr -#endif - -#include -#include - -// ═══════════════════════════════════════════════════════════ -// 辅助函数 -// ═══════════════════════════════════════════════════════════ - -namespace { - -/// 获取文件扩展名(小写) -std::string file_extension(const std::string& path) { - auto pos = path.rfind('.'); - if (pos == std::string::npos) return {}; - std::string ext = path.substr(pos); - std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); - return ext; -} - -/// 检查路径是否为动态库文件 -bool is_shared_library(const std::string& path) { - std::string ext = file_extension(path); - return ext == ".so" || ext == ".dylib" || ext == ".dll"; -} - -/// 检查路径是否为普通文件 -bool is_regular_file(const std::string& path) { - struct stat st; - return stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode); -} - -/// 检查路径是否为目录 -bool is_directory(const std::string& path) { - struct stat st; - return stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode); -} - -/// 简单的语义化版本比较 -/// @return <0: a0: a>b -int compare_versions(const std::string& a, const std::string& b) { - auto parse = [](const std::string& v) -> std::vector { - std::vector parts; - std::istringstream ss(v); - std::string token; - while (std::getline(ss, token, '.')) { - // 提取数字部分(忽略 -beta, -rc 等后缀中的非数字) - try { - parts.push_back(std::stoi(token)); - } catch (...) { - parts.push_back(0); - } - } - while (parts.size() < 3) parts.push_back(0); - return parts; - }; - - auto pa = parse(a); - auto pb = parse(b); - for (size_t i = 0; i < std::min(pa.size(), pb.size()); ++i) { - if (pa[i] != pb[i]) return pa[i] - pb[i]; - } - return 0; -} - -/// 解析简单 JSON 字符串值("..." 去引号) -std::string json_extract_string(const std::string& json, const std::string& key) { - std::string search = "\"" + key + "\""; - auto pos = json.find(search); - if (pos == std::string::npos) return {}; - pos = json.find(':', pos + search.size()); - if (pos == std::string::npos) return {}; - // 跳过空白 - while (++pos < json.size() && (json[pos] == ' ' || json[pos] == '\t' || json[pos] == '\n')) {} - if (pos >= json.size() || json[pos] != '"') return {}; - auto end = json.find('"', pos + 1); - if (end == std::string::npos) return {}; - return json.substr(pos + 1, end - pos - 1); -} - -/// 解析简单 JSON 数组 -std::vector json_extract_array(const std::string& json, const std::string& key) { - std::vector result; - std::string search = "\"" + key + "\""; - auto pos = json.find(search); - if (pos == std::string::npos) return result; - pos = json.find('[', pos + search.size()); - if (pos == std::string::npos) return result; - auto end = json.find(']', pos); - if (end == std::string::npos) return result; - std::string arr = json.substr(pos + 1, end - pos - 1); - // 提取每个字符串元素 - size_t i = 0; - while (i < arr.size()) { - auto q1 = arr.find('"', i); - if (q1 == std::string::npos) break; - auto q2 = arr.find('"', q1 + 1); - if (q2 == std::string::npos) break; - result.push_back(arr.substr(q1 + 1, q2 - q1 - 1)); - i = q2 + 1; - } - return result; -} - -} // anonymous namespace - -// ═══════════════════════════════════════════════════════════ -// PluginManifest 实现 -// ═══════════════════════════════════════════════════════════ - -namespace vde::plugin { - -const char* PluginManifest::type_name(Type t) { - switch (t) { - case Type::IO_IMPORT: return "IO_IMPORT"; - case Type::IO_EXPORT: return "IO_EXPORT"; - case Type::GEOMETRY_FILTER: return "GEOMETRY_FILTER"; - case Type::CUSTOM_TOOL: return "CUSTOM_TOOL"; - case Type::OTHER: - default: return "OTHER"; - } -} - -bool PluginManifest::valid() const { - return !name.empty() && !version.empty() && !vde_version_min.empty(); -} - -std::optional PluginManifest::from_json(const std::string& json) { - PluginManifest m; - m.name = json_extract_string(json, "name"); - m.version = json_extract_string(json, "version"); - m.author = json_extract_string(json, "author"); - m.description = json_extract_string(json, "description"); - m.license = json_extract_string(json, "license"); - m.vde_version_min = json_extract_string(json, "vde_version_min"); - m.vde_version_max = json_extract_string(json, "vde_version_max"); - m.library = json_extract_string(json, "library"); - - // 解析类型 - std::string type_str = json_extract_string(json, "type"); - if (type_str == "IO_IMPORT") m.type = Type::IO_IMPORT; - else if (type_str == "IO_EXPORT") m.type = Type::IO_EXPORT; - else if (type_str == "GEOMETRY_FILTER") m.type = Type::GEOMETRY_FILTER; - else if (type_str == "CUSTOM_TOOL") m.type = Type::CUSTOM_TOOL; - else m.type = Type::OTHER; - - // 解析依赖 - m.dependencies = json_extract_array(json, "dependencies"); - - if (!m.valid()) return std::nullopt; - return m; -} - -std::optional PluginManifest::from_file(const std::string& path) { - FILE* f = fopen(path.c_str(), "r"); - if (!f) return std::nullopt; - - std::string content; - char buf[4096]; - size_t n; - while ((n = fread(buf, 1, sizeof(buf), f)) > 0) { - content.append(buf, n); - } - fclose(f); - - return from_json(content); -} - -// ═══════════════════════════════════════════════════════════ -// PluginManager::Impl — 内部实现 -// ═══════════════════════════════════════════════════════════ - -struct PluginManager::Impl { - /// 单个已加载插件的记录 - struct PluginRecord { - std::unique_ptr instance; - NativeHandle handle = VDE_INVALID_HANDLE; - std::string file_path; - std::string load_order; // 用于倒序卸载 - }; - - /// 名称 → 记录映射 - std::unordered_map registry; - - /// 加载顺序列表(保证卸载顺序 = 加载的逆序) - std::vector load_order; - - /// 读写锁(线程安全) - mutable std::shared_mutex mutex; - - /// 错误信息 - std::string error_msg; - - /// 当前 VDE 版本(编译期常量) - static constexpr const char* VDE_VERSION = "6.0.0"; - - // ── 辅助方法 ────────────────────────────────── - - void set_error(const std::string& msg) { - std::unique_lock lock(mutex); - error_msg = msg; - } - - /// 检查版本兼容性 - bool check_compatibility(const PluginManifest& m) { - // 最低版本检查 - if (compare_versions(VDE_VERSION, m.vde_version_min) < 0) { - set_error("Plugin '" + m.name + "' requires VDE >= " + - m.vde_version_min + " (current: " + VDE_VERSION + ")"); - return false; - } - // 最高版本检查 - if (!m.vde_version_max.empty() && - compare_versions(VDE_VERSION, m.vde_version_max) > 0) { - set_error("Plugin '" + m.name + "' is compatible up to VDE " + - m.vde_version_max + " (current: " + VDE_VERSION + ")"); - return false; - } - // 依赖检查 - for (const auto& dep : m.dependencies) { - bool found = false; - { - std::shared_lock lock(mutex); - found = (registry.find(dep) != registry.end()); - } - if (!found) { - set_error("Plugin '" + m.name + "' depends on '" + dep + - "' which is not loaded"); - return false; - } - } - return true; - } - - /// 从路径加载单个插件 - bool load_from_path(const std::string& plugin_path) { - if (!is_shared_library(plugin_path)) { - // 不是动态库文件,跳过 - return false; - } - if (!is_regular_file(plugin_path)) { - return false; - } - - // ── 1. 加载动态库 ────────────────────── - NativeHandle handle = VDE_INVALID_HANDLE; -#if defined(_WIN32) || defined(_WIN64) - handle = LoadLibraryA(plugin_path.c_str()); -#else - handle = dlopen(plugin_path.c_str(), RTLD_NOW | RTLD_LOCAL); -#endif - if (handle == VDE_INVALID_HANDLE) { -#if defined(_WIN32) || defined(_WIN64) - DWORD err = GetLastError(); - LPSTR msgBuf = nullptr; - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, err, 0, (LPSTR)&msgBuf, 0, nullptr); - set_error(std::string("dlopen failed for ") + plugin_path + - ": " + (msgBuf ? msgBuf : "unknown error")); - if (msgBuf) LocalFree(msgBuf); -#else - const char* err = dlerror(); - set_error(std::string("dlopen failed for ") + plugin_path + - ": " + (err ? err : "unknown error")); -#endif - return false; - } - - // ── 2. 尝试读取 manifest(可选符号) ── - PluginManifest manifest; - bool has_manifest = false; -#if defined(_WIN32) || defined(_WIN64) - auto get_manifest_fn = reinterpret_cast( - GetProcAddress(handle, "get_manifest")); -#else - auto get_manifest_fn = reinterpret_cast( - dlsym(handle, "get_manifest")); -#endif - if (get_manifest_fn) { - const PluginManifest* m = get_manifest_fn(); - if (m && m->valid()) { - manifest = *m; - has_manifest = true; - if (!check_compatibility(manifest)) { - // 版本不兼容 -#if defined(_WIN32) || defined(_WIN64) - FreeLibrary(handle); -#else - dlclose(handle); -#endif - return false; - } - } - } - - // ── 3. 查找 create_plugin 符号 ────────── -#if defined(_WIN32) || defined(_WIN64) - auto create_fn = reinterpret_cast( - GetProcAddress(handle, "create_plugin")); -#else - auto create_fn = reinterpret_cast( - dlsym(handle, "create_plugin")); -#endif - if (!create_fn) { - set_error("Symbol 'create_plugin' not found in " + plugin_path); -#if defined(_WIN32) || defined(_WIN64) - FreeLibrary(handle); -#else - dlclose(handle); -#endif - return false; - } - - // ── 4. 查找 destroy_plugin 符号 ───────── -#if defined(_WIN32) || defined(_WIN64) - auto destroy_fn = reinterpret_cast( - GetProcAddress(handle, "destroy_plugin")); -#else - auto destroy_fn = reinterpret_cast( - dlsym(handle, "destroy_plugin")); -#endif - if (!destroy_fn) { - set_error("Symbol 'destroy_plugin' not found in " + plugin_path); -#if defined(_WIN32) || defined(_WIN64) - FreeLibrary(handle); -#else - dlclose(handle); -#endif - return false; - } - - // ── 5. 创建插件实例 ───────────────────── - PluginInterface* raw = create_fn(); - if (!raw) { - set_error("create_plugin() returned null for " + plugin_path); - destroy_fn(raw); -#if defined(_WIN32) || defined(_WIN64) - FreeLibrary(handle); -#else - dlclose(handle); -#endif - return false; - } - auto instance = std::unique_ptr(raw); - - // ── 6. 初始化插件 ─────────────────────── - if (!instance->initialize()) { - set_error("Plugin '" + std::string(instance->name()) + - "' failed to initialize"); - // destroy_fn 由 unique_ptr 删除器调用 - instance.reset(); -#if defined(_WIN32) || defined(_WIN64) - FreeLibrary(handle); -#else - dlclose(handle); -#endif - return false; - } - - // ── 7. 检查版本兼容性(如果前面没检查) ── - if (!has_manifest) { - if (!check_compatibility(instance->manifest())) { - instance->shutdown(); - instance.reset(); -#if defined(_WIN32) || defined(_WIN64) - FreeLibrary(handle); -#else - dlclose(handle); -#endif - return false; - } - } - - // ── 8. 名称冲突检查 ───────────────────── - std::string name = instance->name(); - { - std::shared_lock lock(mutex); - if (registry.find(name) != registry.end()) { - set_error("Plugin '" + name + "' is already loaded"); - instance->shutdown(); - instance.reset(); -#if defined(_WIN32) || defined(_WIN64) - FreeLibrary(handle); -#else - dlclose(handle); -#endif - return false; - } - } - - // ── 9. 注册到 registry ────────────────── - { - std::unique_lock lock(mutex); - PluginRecord rec; - rec.instance = std::move(instance); - rec.handle = handle; - rec.file_path = plugin_path; - registry[name] = std::move(rec); - load_order.push_back(name); - } - - return true; - } -}; - -// ═══════════════════════════════════════════════════════════ -// PluginManager 公开接口 -// ═══════════════════════════════════════════════════════════ - -PluginManager::PluginManager() - : impl_(std::make_unique()) {} - -PluginManager::~PluginManager() { - unload_all(); -} - -int PluginManager::load_plugin_dir(const std::string& dir_path) { - if (!is_directory(dir_path)) { - impl_->set_error("Not a directory: " + dir_path); - return 0; - } - - // 扫描目录下的所有 .so / .dylib / .dll 文件 - DIR* dir = opendir(dir_path.c_str()); - if (!dir) { - impl_->set_error("Cannot open directory: " + dir_path); - return 0; - } - - int loaded = 0; - struct dirent* entry; - while ((entry = readdir(dir)) != nullptr) { - if (entry->d_name[0] == '.') continue; // 跳过隐藏文件 - - std::string full_path = dir_path + "/" + entry->d_name; - - // 如果是 plugin.json,跳过(由 load_plugin 单独处理) - if (std::string(entry->d_name) == "plugin.json") continue; - - if (impl_->load_from_path(full_path)) { - ++loaded; - } - } - closedir(dir); - return loaded; -} - -int PluginManager::load_default_paths() { - int loaded = 0; - - // 1. 环境变量 $VDE_PLUGIN_PATH - const char* env = getenv("VDE_PLUGIN_PATH"); - if (env) { - std::string paths(env); - std::istringstream ss(paths); - std::string dir; - while (std::getline(ss, dir, ':')) { - if (!dir.empty()) { - loaded += load_plugin_dir(dir); - } - } - } - - // 2. 默认系统路径 - loaded += load_plugin_dir("/usr/lib/vde/plugins"); - - // 3. 本地路径 - loaded += load_plugin_dir("./vde_plugins"); - - return loaded; -} - -bool PluginManager::load_plugin(const std::string& plugin_path) { - return impl_->load_from_path(plugin_path); -} - -bool PluginManager::register_plugin(std::unique_ptr plugin) { - if (!plugin) { - impl_->set_error("Cannot register null plugin"); - return false; - } - - std::string name = plugin->name(); - if (name.empty()) { - impl_->set_error("Plugin has empty name"); - return false; - } - - std::unique_lock lock(impl_->mutex); - if (impl_->registry.find(name) != impl_->registry.end()) { - impl_->set_error("Plugin '" + name + "' is already registered"); - return false; - } - - // 检查版本兼容性 - if (!impl_->check_compatibility(plugin->manifest())) { - return false; - } - - Impl::PluginRecord rec; - rec.instance = std::move(plugin); - impl_->registry[name] = std::move(rec); - impl_->load_order.push_back(name); - return true; -} - -bool PluginManager::unload_plugin(const std::string& name) { - std::unique_lock lock(impl_->mutex); - auto it = impl_->registry.find(name); - if (it == impl_->registry.end()) { - impl_->set_error("Plugin '" + name + "' not found"); - return false; - } - - auto& rec = it->second; - - // 1. 调用 shutdown - if (rec.instance) { - rec.instance->shutdown(); - } - - // 2. 销毁实例(unique_ptr 自动 delete) - rec.instance.reset(); - - // 3. 卸载动态库 - if (rec.handle != VDE_INVALID_HANDLE) { -#if defined(_WIN32) || defined(_WIN64) - FreeLibrary(rec.handle); -#else - dlclose(rec.handle); -#endif - rec.handle = VDE_INVALID_HANDLE; - } - - // 4. 从注册表移除 - impl_->registry.erase(it); - auto order_it = std::find(impl_->load_order.begin(), - impl_->load_order.end(), name); - if (order_it != impl_->load_order.end()) { - impl_->load_order.erase(order_it); - } - - return true; -} - -void PluginManager::unload_all() { - // 按加载的逆序卸载(先卸载后加载的插件) - std::unique_lock lock(impl_->mutex); - while (!impl_->load_order.empty()) { - const std::string& name = impl_->load_order.back(); - auto it = impl_->registry.find(name); - if (it != impl_->registry.end()) { - auto& rec = it->second; - if (rec.instance) { - rec.instance->shutdown(); - } - rec.instance.reset(); - if (rec.handle != VDE_INVALID_HANDLE) { -#if defined(_WIN32) || defined(_WIN64) - FreeLibrary(rec.handle); -#else - dlclose(rec.handle); -#endif - rec.handle = VDE_INVALID_HANDLE; - } - impl_->registry.erase(it); - } - impl_->load_order.pop_back(); - } -} - -PluginInterface* PluginManager::get_plugin(const std::string& name) { - std::shared_lock lock(impl_->mutex); - auto it = impl_->registry.find(name); - if (it != impl_->registry.end()) { - return it->second.instance.get(); - } - return nullptr; -} - -const PluginInterface* PluginManager::get_plugin(const std::string& name) const { - std::shared_lock lock(impl_->mutex); - auto it = impl_->registry.find(name); - if (it != impl_->registry.end()) { - return it->second.instance.get(); - } - return nullptr; -} - -std::vector PluginManager::list_plugins() const { - std::shared_lock lock(impl_->mutex); - return impl_->load_order; // 返回加载顺序 -} - -bool PluginManager::is_loaded(const std::string& name) const { - std::shared_lock lock(impl_->mutex); - return impl_->registry.find(name) != impl_->registry.end(); -} - -size_t PluginManager::plugin_count() const { - std::shared_lock lock(impl_->mutex); - return impl_->registry.size(); -} - -std::string PluginManager::last_error() const { - std::shared_lock lock(impl_->mutex); - return impl_->error_msg; -} - -void PluginManager::clear_error() { - std::unique_lock lock(impl_->mutex); - impl_->error_msg.clear(); -} - -} // namespace vde::plugin diff --git a/src/wasm/vde_wasm.cpp b/src/wasm/vde_wasm.cpp deleted file mode 100644 index f13e2c7..0000000 --- a/src/wasm/vde_wasm.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/// @file vde_wasm.cpp WebAssembly 绑定 —— 实现 -#include "vde/wasm/vde_wasm.h" -#include -#include -#include -#include - -namespace vde::wasm { - -// ═══════════════════════════════════════════════════════ -// WasmBridge -// ═══════════════════════════════════════════════════════ - -bool WasmBridge::initialize() { -#ifdef __EMSCRIPTEN__ - // Emscripten 运行时已在 main() 前初始化 - initialized_ = true; -#else - // Native 构建模拟 - initialized_ = true; -#endif - return initialized_; -} - -void WasmBridge::register_bindings() { - // 绑定由 EMSCRIPTEN_BINDINGS 宏在编译时处理 - // 此处为 native build 占位 -} - -size_t WasmBridge::memory_used() const { -#ifdef __EMSCRIPTEN__ - return static_cast(emscripten_get_heap_size()); -#else - return 0; -#endif -} - -uintptr_t WasmBridge::heap_base() const { -#ifdef __EMSCRIPTEN__ - return reinterpret_cast(emscripten_get_sbrk_ptr()); -#else - return 0; -#endif -} - -bool WasmBridge::load_model_from_buffer(const uint8_t* data, size_t size, - const std::string& format) { - (void)data; - (void)size; - (void)format; - // Stub: 解析 ArrayBuffer,创建 VDE 内部模型 - return true; -} - -std::vector WasmBridge::export_model_to_buffer(const std::string& format) { - (void)format; - // Stub: 序列化当前模型 - return {}; -} - -bool WasmBridge::boolean_union() { - // Stub - return true; -} - -bool WasmBridge::boolean_intersect() { - // Stub - return true; -} - -bool WasmBridge::boolean_subtract() { - // Stub - return true; -} - -std::vector WasmBridge::tessellate_for_webgl(float tolerance) { - (void)tolerance; - // Stub: 返回顶点数组 {x,y,z, nx,ny,nz, u,v ...} - // 一个简单的测试立方体 - return {}; -} - -std::vector WasmBridge::wireframe_data() { - // Stub - return {}; -} - -// ═══════════════════════════════════════════════════════ -// WebWorkerPool -// ═══════════════════════════════════════════════════════ - -WebWorkerPool::WebWorkerPool(int num_workers) - : num_workers_(num_workers > 0 ? num_workers : 4) {} - -WebWorkerPool::~WebWorkerPool() { - stop(); -} - -bool WebWorkerPool::start() { - if (running_) return true; - running_ = true; - // 实际 WebWorker 由 JS 侧创建,C++ 侧维护逻辑队列 - return true; -} - -void WebWorkerPool::stop() { - running_ = false; - tasks_.clear(); - completed_.clear(); -} - -uint64_t WebWorkerPool::submit_task(const WorkerTask& task) { - uint64_t id = next_task_id_++; - TaskState state; - state.task = task; - tasks_[id] = state; - - // 检查是否有注册的处理函数 - auto it = handlers_.find(task.name); - if (it != handlers_.end()) { - // 同步执行(简化;实际应提交到 Worker 线程) - WorkerResult result = it->second(task); - result.task_id = id; - completed_.push_back(result); - if (auto ts = tasks_.find(id); ts != tasks_.end()) { - ts->second.completed = true; - ts->second.result = result; - } - } - return id; -} - -std::vector WebWorkerPool::poll_results() { - std::vector results; - results.swap(completed_); - return results; -} - -WorkerResult WebWorkerPool::wait_for(uint64_t task_id, int timeout_ms) { - (void)timeout_ms; - auto it = tasks_.find(task_id); - if (it != tasks_.end() && it->second.completed) { - tasks_.erase(it); - return it->second.result; - } - WorkerResult err; - err.task_id = task_id; - err.success = false; - err.error = "Task timed out or not found"; - return err; -} - -int WebWorkerPool::active_tasks() const { - int count = 0; - for (const auto& [id, state] : tasks_) { - if (!state.completed) count++; - } - return count; -} - -void WebWorkerPool::register_handler(const std::string& task_name, WorkerFn handler) { - handlers_[task_name] = std::move(handler); -} - -void WebWorkerPool::worker_loop(int worker_id) { - (void)worker_id; - // Stub: 实际 Worker 循环由 Web Worker 上下文实现 -} - -// ═══════════════════════════════════════════════════════ -// SharedMemory -// ═══════════════════════════════════════════════════════ - -SharedMemory::~SharedMemory() { - free(); -} - -bool SharedMemory::allocate(size_t size_bytes) { - free(); -#ifdef __EMSCRIPTEN__ - // 使用 Emscripten 的 aligned malloc - buffer_ = static_cast(std::aligned_alloc(64, - (size_bytes + 63) & ~63ULL)); -#else - buffer_ = new uint8_t[size_bytes]; -#endif - if (buffer_) { - size_ = size_bytes; - std::memset(buffer_, 0, size_); - return true; - } - return false; -} - -void SharedMemory::free() { - if (buffer_) { -#ifdef __EMSCRIPTEN__ - std::free(buffer_); -#else - delete[] buffer_; -#endif - buffer_ = nullptr; - size_ = 0; - } -} - -#ifdef __EMSCRIPTEN__ -emscripten::val SharedMemory::js_handle() const { - // 将 C++ buffer 暴露为 SharedArrayBuffer - return emscripten::val(emscripten::typed_memory_view(size_, buffer_)); -} -#endif - -void SharedMemory::atomic_store32(size_t offset, int32_t value) { - if (offset + sizeof(int32_t) <= size_) { -#ifdef __EMSCRIPTEN__ - __atomic_store(reinterpret_cast(buffer_ + offset), - &value, __ATOMIC_SEQ_CST); -#else - reinterpret_cast(buffer_ + offset)[0] = value; -#endif - } -} - -int32_t SharedMemory::atomic_load32(size_t offset) const { - if (offset + sizeof(int32_t) <= size_) { -#ifdef __EMSCRIPTEN__ - int32_t val; - __atomic_load(reinterpret_cast(const_cast(buffer_) + offset), - &val, __ATOMIC_SEQ_CST); - return val; -#else - return reinterpret_cast(buffer_ + offset)[0]; -#endif - } - return 0; -} - -int32_t SharedMemory::atomic_add32(size_t offset, int32_t delta) { - if (offset + sizeof(int32_t) <= size_) { -#ifdef __EMSCRIPTEN__ - return __atomic_fetch_add(reinterpret_cast(buffer_ + offset), - delta, __ATOMIC_SEQ_CST); -#else - int32_t& ref = reinterpret_cast(buffer_ + offset)[0]; - int32_t old = ref; - ref += delta; - return old; -#endif - } - return 0; -} - -int32_t SharedMemory::atomic_cas32(size_t offset, int32_t expected, int32_t desired) { - if (offset + sizeof(int32_t) <= size_) { -#ifdef __EMSCRIPTEN__ - __atomic_compare_exchange(reinterpret_cast(buffer_ + offset), - &expected, &desired, - false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; -#else - int32_t& ref = reinterpret_cast(buffer_ + offset)[0]; - int32_t old = ref; - if (ref == expected) ref = desired; - return old; -#endif - } - return 0; -} - -// ═══════════════════════════════════════════════════════ -// IndexedDBStorage -// ═══════════════════════════════════════════════════════ - -bool IndexedDBStorage::open(const std::string& db_name, int version) { - db_name_ = db_name; - (void)version; - opened_ = true; - return true; -} - -void IndexedDBStorage::close() { - opened_ = false; -} - -bool IndexedDBStorage::put(const std::string& store_name, - const std::string& key, - const std::vector& data) { - (void)store_name; - (void)key; - (void)data; - // Stub: 实际通过 JS IndexedDB API 存储 - return opened_; -} - -std::optional> IndexedDBStorage::get(const std::string& store_name, - const std::string& key) { - (void)store_name; - (void)key; - return std::nullopt; // Stub -} - -bool IndexedDBStorage::remove(const std::string& store_name, const std::string& key) { - (void)store_name; - (void)key; - return opened_; -} - -bool IndexedDBStorage::exists(const std::string& store_name, const std::string& key) { - (void)store_name; - (void)key; - return false; -} - -std::vector IndexedDBStorage::keys(const std::string& store_name) { - (void)store_name; - return {}; -} - -bool IndexedDBStorage::clear_store(const std::string& store_name) { - (void)store_name; - return opened_; -} - -bool IndexedDBStorage::save_model(const std::string& model_name, - const std::vector& model_data, - const std::string& format) { - return put("models", model_name + "." + format, model_data); -} - -std::optional> IndexedDBStorage::load_model(const std::string& model_name, - const std::string& format) { - return get("models", model_name + "." + format); -} - -std::vector IndexedDBStorage::list_models() const { - // Stub - return {}; -} - -} // namespace vde::wasm diff --git a/tests/ai/CMakeLists.txt b/tests/ai/CMakeLists.txt deleted file mode 100644 index 79dab3a..0000000 --- a/tests/ai/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_vde_test(test_feature_learning) -add_vde_test(test_generative_design) diff --git a/tests/ai/test_feature_learning.cpp b/tests/ai/test_feature_learning.cpp deleted file mode 100644 index ec5325f..0000000 --- a/tests/ai/test_feature_learning.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include -#include "vde/ai/feature_learning.h" - -using namespace vde::ai; -using namespace vde::brep; -using namespace vde::core; - -// ═══════════════════════════════════════════════════════════ -// FeatureClassifier 测试 (4 项) -// ═══════════════════════════════════════════════════════════ - -TEST(FeatureClassifierTest, DefaultConstruction) { - FeatureClassifier classifier; - EXPECT_FALSE(classifier.is_model_loaded()); - EXPECT_TRUE(classifier.model_version().empty()); -} - -TEST(FeatureClassifierTest, LoadModelSucceeds) { - FeatureClassifier classifier; - EXPECT_TRUE(classifier.load_model("models/feature_gnn.onnx")); - EXPECT_TRUE(classifier.is_model_loaded()); - EXPECT_FALSE(classifier.model_version().empty()); -} - -TEST(FeatureClassifierTest, ClassifyEmptyBody) { - FeatureClassifier classifier; - classifier.load_model("models/dummy.onnx"); - - BrepModel empty_body; - auto result = classifier.classify_features(empty_body); - - EXPECT_TRUE(result.success); - EXPECT_TRUE(result.features.empty()); - EXPECT_EQ(result.hole_count, 0); -} - -TEST(FeatureClassifierTest, BuildFaceGraphOnBox) { - FeatureClassifier classifier; - auto box = make_box(2, 2, 2); - - auto graph = classifier.build_face_graph(box); - - // 一个盒子有 6 个面(具体取决于 make_box 实现) - EXPECT_GT(graph.nodes.size(), 0u); - // 面之间应有邻接关系 - EXPECT_GT(graph.edges.size(), 0u); - EXPECT_EQ(graph.edges.size(), graph.edge_features.size()); -} - -TEST(FeatureClassifierTest, EncodeFaceNodeReturnsCorrectDim) { - FeatureClassifier classifier; - auto box = make_box(2, 2, 2); - - // 对每个面编码 - const auto nf = box.num_faces(); - if (nf > 0) { - auto feats = classifier.encode_face_node(box, 0); - EXPECT_EQ(feats.size(), 8u); // kNodeFeatureDim = 8 - } -} - -TEST(FeatureClassifierTest, EncodeEdgeFeatureOnBox) { - FeatureClassifier classifier; - auto box = make_box(2, 2, 2); - - auto graph = classifier.build_face_graph(box); - if (!graph.edges.empty()) { - double edge_feat = classifier.encode_edge_feature( - box, graph.edges[0].first, graph.edges[0].second); - // 盒子边应为凸边(正值) - EXPECT_GE(edge_feat, -1.0); - EXPECT_LE(edge_feat, 1.0); - } -} - -TEST(FeatureClassifierTest, ClassifyFeaturesOnBox) { - FeatureClassifier classifier; - classifier.load_model("models/dummy.onnx"); - - auto box = make_box(2, 2, 2); - auto result = classifier.classify_features(box); - - EXPECT_TRUE(result.success); - EXPECT_TRUE(result.error_message.empty()); - // 盒子可能在启发式分类下被归为某些特征 -} - -TEST(FeatureClassifierTest, AggregateFeaturesMergesAdjacent) { - FeatureClassifier classifier; - auto box = make_box(2, 2, 2); - auto graph = classifier.build_face_graph(box); - - // 模拟面标签(全标记为 Fillet) - std::vector> labels( - graph.nodes.size(), {FeatureType::Fillet, 0.8}); - - auto features = classifier.aggregate_features(graph, labels, box); - - // 连通的面会被聚合 - EXPECT_GE(features.size(), 1u); - for (const auto& f : features) { - EXPECT_EQ(f.type, FeatureType::Fillet); - EXPECT_GT(f.confidence, 0.0); - } -} - -// ═══════════════════════════════════════════════════════════ -// SimilaritySearch 测试 (4 项) -// ═══════════════════════════════════════════════════════════ - -TEST(SimilaritySearchTest, ComputeESFOnBox) { - SimilaritySearch search; - auto box = make_box(2, 2, 2); - - auto esf = search.compute_esf(box, 500); - - // 640 维直方图 - EXPECT_EQ(esf.histogram.size(), 640u); -} - -TEST(SimilaritySearchTest, ComputeFPFHOnBox) { - SimilaritySearch search; - auto box = make_box(2, 2, 2); - - auto fpfh = search.compute_fpfh(box, 300); - - // 33 维直方图 - EXPECT_EQ(fpfh.histogram.size(), 33u); -} - -TEST(SimilaritySearchTest, ShapeDescriptorOnBox) { - SimilaritySearch search; - auto box = make_box(2, 2, 2); - - auto desc = search.compute_shape_descriptor(box, "box_001", "/path/to/box.step", 500, 300); - - EXPECT_EQ(desc.model_id, "box_001"); - EXPECT_EQ(desc.model_path, "/path/to/box.step"); - EXPECT_EQ(desc.esf.histogram.size(), 640u); - EXPECT_EQ(desc.fpfh.histogram.size(), 33u); -} - -TEST(SimilaritySearchTest, IndexAndQuery) { - SimilaritySearch search; - - auto box1 = make_box(2, 2, 2); - auto box2 = make_box(3, 3, 3); - - auto desc1 = search.compute_shape_descriptor(box1, "box1", "", 500, 300); - auto desc2 = search.compute_shape_descriptor(box2, "box2", "", 500, 300); - - search.build_index({desc1, desc2}); - EXPECT_EQ(search.index_size(), 2u); - - // 查询 - auto matches = search.query(box1, 5); - EXPECT_GE(matches.size(), 1u); - - // 自身应最相似 - if (matches.size() >= 1) { - EXPECT_EQ(matches[0].model_id, "box1"); - EXPECT_GT(matches[0].similarity, 0.0); - } -} - -TEST(SimilaritySearchTest, ESFDistanceSelfIsZero) { - SimilaritySearch search; - auto box = make_box(2, 2, 2); - auto esf = search.compute_esf(box, 500); - - double dist = SimilaritySearch::esf_distance(esf, esf); - EXPECT_NEAR(dist, 0.0, 1e-10); -} - -TEST(SimilaritySearchTest, FPFHDistanceSelfIsZero) { - SimilaritySearch search; - auto box = make_box(2, 2, 2); - auto fpfh = search.compute_fpfh(box, 300); - - double dist = SimilaritySearch::fpfh_distance(fpfh, fpfh); - EXPECT_NEAR(dist, 0.0, 1e-10); -} - -TEST(SimilaritySearchTest, CombinedSimilarityInRange) { - auto box1 = make_box(2, 2, 2); - auto box2 = make_box(3, 3, 3); - - SimilaritySearch search; - auto desc1 = search.compute_shape_descriptor(box1, "", "", 500, 300); - auto desc2 = search.compute_shape_descriptor(box2, "", "", 500, 300); - - double sim = SimilaritySearch::combined_similarity(desc1, desc2); - EXPECT_GE(sim, 0.0); - EXPECT_LE(sim, 1.0); -} - -TEST(SimilaritySearchTest, IndexClear) { - SimilaritySearch search; - auto box = make_box(2, 2, 2); - auto desc = search.compute_shape_descriptor(box); - search.add_to_index(desc); - EXPECT_EQ(search.index_size(), 1u); - - search.clear_index(); - EXPECT_EQ(search.index_size(), 0u); -} - -// ═══════════════════════════════════════════════════════════ -// 辅助函数测试 (2 项) -// ═══════════════════════════════════════════════════════════ - -TEST(FeatureLearningAuxTest, SampleSurfacePointsOnBox) { - auto box = make_box(2, 2, 2); - auto pts = sample_surface_points(box, 200); - - EXPECT_GE(pts.size(), 0u); - // 采样点应在表面上(近似包围盒内) - for (const auto& p : pts) { - EXPECT_GE(p.x(), -1.5); - EXPECT_LE(p.x(), 1.5); - } -} - -TEST(FeatureLearningAuxTest, FaceCentroidsOnBox) { - auto box = make_box(2, 2, 2); - auto centroids = face_centroids(box); - - EXPECT_EQ(centroids.size(), box.num_faces()); - for (const auto& c : centroids) { - EXPECT_TRUE(std::isfinite(c.x())); - EXPECT_TRUE(std::isfinite(c.y())); - EXPECT_TRUE(std::isfinite(c.z())); - } -} diff --git a/tests/ai/test_generative_design.cpp b/tests/ai/test_generative_design.cpp deleted file mode 100644 index f8552da..0000000 --- a/tests/ai/test_generative_design.cpp +++ /dev/null @@ -1,240 +0,0 @@ -#include -#include "vde/ai/generative_design.h" -#include "vde/brep/modeling.h" - -using namespace vde::ai; -using namespace vde::brep; -using namespace vde::core; - -// ═══════════════════════════════════════════════════════════ -// 拓扑优化测试 (4 项) -// ═══════════════════════════════════════════════════════════ - -TEST(TopologyOptimizationTest, SIMPOptimizationConverges) { - AABB3D design_space{Point3D{0,0,0}, Point3D{10,5,2}}; - - std::vector loads = { - {Point3D{5, 2.5, 2}, Vector3D{0, 0, -1}, 100.0} - }; - - std::vector constraints = { - {Point3D{0, 0, 0}, true, true, true}, - {Point3D{10, 0, 0}, true, true, true} - }; - - OptimizationConstraints opt; - opt.grid_resolution = 32; - opt.max_iterations = 30; - opt.volume_fraction = 0.4; - opt.convergence_tolerance = 0.01; - - auto result = topology_optimization(design_space, loads, constraints, opt); - - EXPECT_EQ(result.grid_resolution, 32); - EXPECT_GT(result.iterations, 0); - EXPECT_LE(result.iterations, 30); - EXPECT_GT(result.final_volume_fraction, 0.0); - EXPECT_LE(result.final_volume_fraction, 1.0); - EXPECT_EQ(result.density_field.size(), 32u * 32 * 32); -} - -TEST(TopologyOptimizationTest, DensityFieldIsBounded) { - AABB3D design_space{Point3D{0,0,0}, Point3D{4,4,4}}; - std::vector loads = {{Point3D{2,2,4}, Vector3D{0,0,-1}, 50.0}}; - std::vector constraints = {{Point3D{1,1,0}, true, true, true}}; - - OptimizationConstraints opt; - opt.grid_resolution = 16; - opt.max_iterations = 20; - - auto result = topology_optimization(design_space, loads, constraints, opt); - - for (auto rho : result.density_field) { - EXPECT_GE(rho, 0.0); - EXPECT_LE(rho, 1.0); - } -} - -TEST(TopologyOptimizationTest, ConstraintRegionIsSolid) { - AABB3D design_space{Point3D{0,0,0}, Point3D{4,4,4}}; - std::vector loads; - std::vector constraints = { - {Point3D{0, 0, 0}, true, true, true} - }; - - OptimizationConstraints opt; - opt.grid_resolution = 16; - opt.max_iterations = 10; - - auto result = topology_optimization(design_space, loads, constraints, opt); - - // 约束区域 (0,0,0) 附近应为实体 - int idx = 0; // idx3d(0,0,0,16) → 0 - EXPECT_NEAR(result.density_field[static_cast(idx)], 1.0, 0.1); -} - -TEST(TopologyOptimizationTest, ExtractIsosurfaceFromDensityField) { - AABB3D bounds{Point3D{0,0,0}, Point3D{2,2,2}}; - int res = 8; - int n = res * res * res; - - // 创建球形密度场 - std::vector field(static_cast(n), 0.0); - for (int z = 0; z < res; ++z) - for (int y = 0; y < res; ++y) - for (int x = 0; x < res; ++x) { - double cx = (x - res*0.5) / (res*0.5); - double cy = (y - res*0.5) / (res*0.5); - double cz = (z - res*0.5) / (res*0.5); - double r = std::sqrt(cx*cx + cy*cy + cz*cz); - field[static_cast((z*res + y)*res + x)] = (r < 0.8) ? 1.0 : 0.0; - } - - auto mesh = extract_isosurface(field, res, bounds, 0.5); - - // 应有输出网格(球体应有非零顶点) - EXPECT_GE(mesh.num_vertices(), 0u); -} - -// ═══════════════════════════════════════════════════════════ -// 晶格结构生成测试 (4 项) -// ═══════════════════════════════════════════════════════════ - -TEST(LatticeGenerationTest, GyroidSDFReturnsFiniteValue) { - double sdf = lattice_sdf(1.0, 1.0, 1.0, LatticeCellType::Gyroid, 2.0, 0.1); - EXPECT_TRUE(std::isfinite(sdf)); -} - -TEST(LatticeGenerationTest, DiamondSDFReturnsFiniteValue) { - double sdf = lattice_sdf(0.5, 0.5, 0.5, LatticeCellType::Diamond, 1.0, 0.15); - EXPECT_TRUE(std::isfinite(sdf)); -} - -TEST(LatticeGenerationTest, BCCSDFReturnsFiniteValue) { - double sdf = lattice_sdf(0.0, 0.0, 0.0, LatticeCellType::BCC, 3.0, 0.1); - EXPECT_TRUE(std::isfinite(sdf)); -} - -TEST(LatticeGenerationTest, FCCSDFReturnsFiniteValue) { - double sdf = lattice_sdf(2.0, 2.0, 2.0, LatticeCellType::FCC, 2.0, 0.12); - EXPECT_TRUE(std::isfinite(sdf)); -} - -TEST(LatticeGenerationTest, CubicSDFReturnsFiniteValue) { - double sdf = lattice_sdf(1.5, 1.5, 1.5, LatticeCellType::Cubic, 1.5, 0.08); - EXPECT_TRUE(std::isfinite(sdf)); -} - -TEST(LatticeGenerationTest, OctetSDFReturnsFiniteValue) { - double sdf = lattice_sdf(0.8, 0.8, 0.8, LatticeCellType::Octet, 1.8, 0.1); - EXPECT_TRUE(std::isfinite(sdf)); -} - -TEST(LatticeGenerationTest, GenerateGyroidLatticeOnBox) { - auto box = make_box(5, 5, 5); - - LatticeParams params; - params.cell_size = 1.5; - params.strut_thickness = 0.12; - - auto result = lattice_generation(box, LatticeCellType::Gyroid, params); - - EXPECT_TRUE(result.success); - EXPECT_TRUE(result.error_message.empty()); - EXPECT_GT(result.cell_count, 0); - EXPECT_GE(result.porosity, 0.0); - EXPECT_LE(result.porosity, 1.0); - // 晶格网格应有输出 - EXPECT_GE(result.lattice_mesh.num_vertices(), 0u); -} - -TEST(LatticeGenerationTest, VariableDensityLattice) { - auto box = make_box(5, 5, 5); - std::vector loads = {{Point3D{2.5, 2.5, 5}, Vector3D{0,0,-1}, 100.0}}; - - auto [density_map, res] = compute_variable_density_map(box, loads, 0.5, 1.0); - - EXPECT_EQ(density_map.size(), static_cast(res * res * res)); - EXPECT_GT(res, 0); - - LatticeParams params; - params.cell_size = 1.0; - params.strut_thickness = 0.1; - params.variable_density = true; - params.density_map = density_map; - params.density_map_resolution = res; - - auto result = lattice_generation(box, LatticeCellType::Gyroid, params); - EXPECT_TRUE(result.success); -} - -TEST(LatticeGenerationTest, AllCellTypesProduceValidSDF) { - // 验证所有晶格类型都在 (0,0,0) 处产生有限的 SDF 值 - std::vector types = { - LatticeCellType::Gyroid, LatticeCellType::Diamond, - LatticeCellType::BCC, LatticeCellType::FCC, - LatticeCellType::Octet, LatticeCellType::Cubic - }; - - for (auto ct : types) { - double sdf = lattice_sdf(0.0, 0.0, 0.0, ct, 2.0, 0.1); - EXPECT_TRUE(std::isfinite(sdf)) << "Cell type " << static_cast(ct) << " produced nan/inf"; - } -} - -// ═══════════════════════════════════════════════════════════ -// GAN 生成测试 (2 项) -// ═══════════════════════════════════════════════════════════ - -TEST(GANGenerationTest, EncodeConditionsReturns32DVector) { - GANCondition conditions; - conditions.loads = {{Point3D{1,1,1}, Vector3D{0,0,-1}, 50.0}}; - conditions.constraints = {{Point3D{0,0,0}, true, true, true}}; - conditions.target_volume = 0.3; - conditions.style_tag = "aerospace"; - - auto cond_vec = encode_conditions(conditions); - EXPECT_EQ(cond_vec.size(), 32u); -} - -TEST(GANGenerationTest, Generative3DProducesValidMesh) { - GANCondition conditions; - conditions.loads = {{Point3D{0, 0, 1}, Vector3D{0, 0, -1}, 100.0}}; - conditions.constraints = {{Point3D{0, 0, -1}, true, true, true}}; - conditions.target_volume = 0.25; - conditions.style_tag = "structural"; - - auto result = generative_adversarial_3d(conditions, "", 32); - - EXPECT_TRUE(result.success); - EXPECT_GT(result.generation_time_ms, 0.0); - EXPECT_GE(result.generated_mesh.num_vertices(), 0u); - EXPECT_FALSE(result.candidate_tags.empty()); -} - -TEST(GANGenerationTest, LatentInterpolationProducesSequence) { - GANCondition conditions; - conditions.style_tag = "smooth_transition"; - - std::vector z0(32, -0.5); - std::vector z1(32, 0.5); - - auto results = latent_interpolation(z0, z1, 5, conditions); - - EXPECT_EQ(results.size(), 5u); - for (const auto& r : results) { - EXPECT_TRUE(r.success); - } -} - -TEST(GANGenerationTest, MultipleStyleConditions) { - std::vector styles = {"aerospace", "automotive", "biomedical", "consumer"}; - - for (const auto& style : styles) { - GANCondition conditions; - conditions.style_tag = style; - - auto result = generative_adversarial_3d(conditions, "", 16); - EXPECT_TRUE(result.success) << "Failed for style: " << style; - } -} diff --git a/tests/cloud/CMakeLists.txt b/tests/cloud/CMakeLists.txt deleted file mode 100644 index bf161cc..0000000 --- a/tests/cloud/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_executable(test_cloud test_cloud.cpp) -target_include_directories(test_cloud PRIVATE ${CMAKE_SOURCE_DIR}/include) -target_link_libraries(test_cloud PRIVATE vde_cloud vde_core vde_foundation GTest::gtest GTest::gtest_main) -gtest_discover_tests(test_cloud) diff --git a/tests/cloud/test_cloud.cpp b/tests/cloud/test_cloud.cpp deleted file mode 100644 index c217030..0000000 --- a/tests/cloud/test_cloud.cpp +++ /dev/null @@ -1,177 +0,0 @@ -#include -#include "vde/cloud/cloud_native.h" -#include -#include - -using namespace vde::cloud; - -// ═══════════════════════════════════════════════════════════ -// 1. DeltaSync 基本操作 -// ═══════════════════════════════════════════════════════════ - -TEST(CloudTest, DeltaSync_InitialVersionIsZero) { - DeltaSync sync; - EXPECT_EQ(sync.version(), 0u); -} - -TEST(CloudTest, DeltaSync_CommitIncrementsVersion) { - DeltaSync sync; - DeltaOp op{OpType::MODIFY, 1001, 1, {}}; - sync.commit_local({op}, "user1"); - EXPECT_EQ(sync.version(), 1u); -} - -TEST(CloudTest, DeltaSync_MultipleCommits) { - DeltaSync sync; - sync.commit_local({{OpType::INSERT, 1, 1, {}}}, "user1"); - sync.commit_local({{OpType::MODIFY, 1, 2, {}}}, "user2"); - sync.commit_local({{OpType::INSERT, 2, 3, {}}}, "user1"); - EXPECT_EQ(sync.version(), 3u); -} - -TEST(CloudTest, DeltaSync_DirtyEntitiesTracking) { - DeltaSync sync; - sync.commit_local({{OpType::INSERT, 100, 1, {}}}, "user1"); - EXPECT_TRUE(sync.is_dirty(100)); - EXPECT_FALSE(sync.is_dirty(200)); -} - -TEST(CloudTest, DeltaSync_DeleteRemovesDirty) { - DeltaSync sync; - sync.commit_local({{OpType::INSERT, 100, 1, {}}}, "user1"); - EXPECT_TRUE(sync.is_dirty(100)); - sync.commit_local({{OpType::DELETE, 100, 2, {}}}, "user1"); - EXPECT_FALSE(sync.is_dirty(100)); -} - -TEST(CloudTest, DeltaSync_Rollback) { - DeltaSync sync; - sync.commit_local({{OpType::INSERT, 1, 1, {}}}, "user1"); - sync.commit_local({{OpType::INSERT, 2, 2, {}}}, "user1"); - EXPECT_EQ(sync.version(), 2u); - EXPECT_TRUE(sync.rollback(1)); - EXPECT_EQ(sync.version(), 1u); -} - -// ═══════════════════════════════════════════════════════════ -// 2. OperationalTransform 冲突检测 -// ═══════════════════════════════════════════════════════════ - -TEST(CloudTest, OT_NoConflictOnDifferentEntities) { - OperationalTransform ot; - DeltaOp op1{OpType::MODIFY, 1, 100, {}}; - DeltaOp op2{OpType::MODIFY, 2, 200, {}}; - EXPECT_FALSE(ot.conflicts(op1, op2)); -} - -TEST(CloudTest, OT_ConflictOnSameEntityModify) { - OperationalTransform ot; - DeltaOp op1{OpType::MODIFY, 1, 100, {}}; - DeltaOp op2{OpType::MODIFY, 1, 200, {}}; - EXPECT_TRUE(ot.conflicts(op1, op2)); -} - -TEST(CloudTest, OT_ConflictOnDelete) { - OperationalTransform ot; - DeltaOp op1{OpType::DELETE, 1, 100, {}}; - DeltaOp op2{OpType::MODIFY, 1, 200, {}}; - EXPECT_TRUE(ot.conflicts(op1, op2)); -} - -TEST(CloudTest, OT_TransformDifferentEntitiesNoOp) { - OperationalTransform ot; - DeltaOp op1{OpType::MODIFY, 1, 100, {1,2,3}}; - DeltaOp op2{OpType::MODIFY, 2, 200, {4,5,6}}; - DeltaOp result = ot.transform(op1, op2); - EXPECT_EQ(result.entity_id, 1u); - EXPECT_EQ(result.type, OpType::MODIFY); - EXPECT_EQ(result.data, (std::vector{1,2,3})); -} - -TEST(CloudTest, OT_MergeTwoChangeSets) { - OperationalTransform ot; - ChangeSet cs1; - cs1.base_version = 0; - cs1.new_version = 1; - cs1.ops = {{OpType::INSERT, 10, 1, {}}}; - - ChangeSet cs2; - cs2.base_version = 0; - cs2.new_version = 1; - cs2.ops = {{OpType::INSERT, 20, 2, {}}}; - - ChangeSet merged = ot.merge(cs1, cs2); - EXPECT_GE(merged.ops.size(), 2u); - EXPECT_EQ(merged.base_version, 0u); -} - -// ═══════════════════════════════════════════════════════════ -// 3. CloudSession 协作会话 -// ═══════════════════════════════════════════════════════════ - -TEST(CloudTest, Session_JoinAndLeave) { - CloudSession session("test-session-1"); - UserInfo user{"user1", "Alice", 0xFF0000, true}; - - EXPECT_TRUE(session.join(user)); - EXPECT_EQ(session.online_users().size(), 1u); - EXPECT_TRUE(session.leave("user1")); - EXPECT_EQ(session.online_users().size(), 0u); -} - -TEST(CloudTest, Session_DuplicateJoinFails) { - CloudSession session("test-session-2"); - UserInfo user{"user1", "Alice", 0xFF0000, true}; - EXPECT_TRUE(session.join(user)); - EXPECT_FALSE(session.join(user)); // 重复加入 -} - -TEST(CloudTest, Session_SubmitOperation) { - CloudSession session("test-session-3"); - UserInfo user{"user1", "Bob", 0x00FF00, true}; - session.join(user); - - std::vector ops = {{OpType::INSERT, 42, 1, {1,2,3}}}; - EXPECT_TRUE(session.submit_operation("user1", ops)); - EXPECT_GT(session.version(), 0u); -} - -// ═══════════════════════════════════════════════════════════ -// 4. ObjectStorage 对象存储 -// ═══════════════════════════════════════════════════════════ - -TEST(CloudTest, ObjectStorage_Configure) { - ObjectStorage storage; - storage.configure("http://localhost:9000", "vde-models", "minioadmin", "minioadmin"); - SUCCEED(); // 配置不抛出异常 -} - -TEST(CloudTest, ObjectStorage_StoreAndLoadModel) { - ObjectStorage storage; - storage.configure("http://localhost:9000", "vde-models", "key", "secret"); - - std::vector model_data = {0x01, 0x02, 0x03, 0x04}; - EXPECT_TRUE(storage.store_model("test_part", model_data, "stp")); - - // Stub 返回 nullopt,但调用接口正常 - auto loaded = storage.load_model("test_part", "stp"); - // In stub mode, this returns nullopt — that's expected - SUCCEED(); -} - -// ═══════════════════════════════════════════════════════════ -// 5. ServerlessFunction -// ═══════════════════════════════════════════════════════════ - -TEST(CloudTest, Serverless_Deploy) { - ServerlessFunction fn; - EXPECT_TRUE(fn.deploy("validate-model", "/code/validate.py", "python3.9")); -} - -TEST(CloudTest, Serverless_Invoke) { - ServerlessFunction fn; - FunctionRequest req{"validate-model", {0x01, 0x02}, {}, std::chrono::milliseconds(5000)}; - FunctionResponse resp = fn.invoke(req); - EXPECT_EQ(resp.status_code, 200); - EXPECT_TRUE(resp.ok); -} diff --git a/tests/distributed/CMakeLists.txt b/tests/distributed/CMakeLists.txt deleted file mode 100644 index fb4c3df..0000000 --- a/tests/distributed/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_vde_test(test_cluster) diff --git a/tests/distributed/test_cluster.cpp b/tests/distributed/test_cluster.cpp deleted file mode 100644 index be6069b..0000000 --- a/tests/distributed/test_cluster.cpp +++ /dev/null @@ -1,418 +0,0 @@ -/** - * @file test_cluster.cpp - * @brief 分布式计算引擎单元测试 — 集群管理、消息序列化、任务调度、分布式运算 - * @ingroup tests - */ - -#include -#include "vde/distributed/cluster_engine.h" -#include "vde/distributed/grpc_service.h" -#include "vde/core/aabb.h" -#include "vde/mesh/marching_cubes.h" -#include -#include - -using namespace vde::distributed; -using vde::core::AABB3D; -using vde::core::Point3D; -using vde::mesh::marching_cubes; -using vde::mesh::MCMesh; - -// ═════════════════════════════════════════════════════════════════ -// Test 1: SerializedMessage — basic types -// ═════════════════════════════════════════════════════════════════ -TEST(SerializedMessageTest, BasicTypes) { - SerializedMessage msg; - msg.write_int32(42); - msg.write_int64(-1234567890123LL); - msg.write_float64(3.141592653589793); - msg.write_string("hello world"); - msg.write_message_type(MessageType::HEARTBEAT); - - msg.reset_read(); - EXPECT_EQ(msg.read_int32(), 42); - EXPECT_EQ(msg.read_int64(), -1234567890123LL); - EXPECT_DOUBLE_EQ(msg.read_float64(), 3.141592653589793); - EXPECT_EQ(msg.read_string(), "hello world"); - EXPECT_EQ(msg.read_message_type(), MessageType::HEARTBEAT); -} - -// ═════════════════════════════════════════════════════════════════ -// Test 2: SerializedMessage — varint encoding -// ═════════════════════════════════════════════════════════════════ -TEST(SerializedMessageTest, VarintEncoding) { - SerializedMessage msg; - std::vector test_values = {0, 1, 127, 128, 255, 300, 16384, 1000000, UINT64_MAX}; - for (auto v : test_values) { - msg.write_varint(v); - } - msg.reset_read(); - for (auto v : test_values) { - EXPECT_EQ(msg.read_varint(), v); - } -} - -// ═════════════════════════════════════════════════════════════════ -// Test 3: SerializedMessage — binary data -// ═════════════════════════════════════════════════════════════════ -TEST(SerializedMessageTest, BinaryData) { - SerializedMessage msg; - uint8_t test_data[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE}; - msg.write_bytes(test_data, sizeof(test_data)); - msg.reset_read(); - size_t len = static_cast(msg.read_varint()); - EXPECT_EQ(len, sizeof(test_data)); - auto result = msg.read_bytes(len); - EXPECT_EQ(result.size(), sizeof(test_data)); - for (size_t i = 0; i < len; ++i) { - EXPECT_EQ(result[i], test_data[i]); - } -} - -// ═════════════════════════════════════════════════════════════════ -// Test 4: ClusterManager — register node -// ═════════════════════════════════════════════════════════════════ -TEST(ClusterManagerTest, RegisterNode) { - ClusterConfig cfg; - cfg.heartbeat_interval_ms = 500; - ClusterManager mgr(cfg); - - std::string id = mgr.register_node("192.168.1.10", 50051, 16, 32768); - EXPECT_FALSE(id.empty()); - EXPECT_EQ(mgr.node_count(), 1); - - auto nodes = mgr.alive_nodes(); - ASSERT_EQ(nodes.size(), 1); - EXPECT_EQ(nodes[0].host, "192.168.1.10"); - EXPECT_EQ(nodes[0].port, 50051); - EXPECT_EQ(nodes[0].cores, 16); - EXPECT_EQ(nodes[0].memory_mb, 32768); -} - -// ═════════════════════════════════════════════════════════════════ -// Test 5: ClusterManager — heartbeat and timeout -// ═════════════════════════════════════════════════════════════════ -TEST(ClusterManagerTest, HeartbeatAndTimeout) { - ClusterConfig cfg; - cfg.heartbeat_interval_ms = 100; - cfg.heartbeat_timeout_ms = 300; - ClusterManager mgr(cfg); - - std::string id = mgr.register_node("10.0.0.1", 50051, 8, 16384); - EXPECT_EQ(mgr.alive_nodes().size(), 1); - - mgr.heartbeat(id, 0.5, 8192, 3); - auto node = mgr.find_node(id); - ASSERT_TRUE(node.has_value()); - EXPECT_DOUBLE_EQ(node->cpu_load, 0.5); - EXPECT_EQ(node->memory_used_mb, 8192); - EXPECT_EQ(node->active_tasks, 3); - - // Start heartbeat monitor and wait for timeout - mgr.start_heartbeat_monitor(); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - auto alive_after_timeout = mgr.alive_nodes(); - // Node should be marked offline after no heartbeat within timeout - EXPECT_EQ(alive_after_timeout.size(), 0); - mgr.stop_heartbeat_monitor(); -} - -// ═════════════════════════════════════════════════════════════════ -// Test 6: ClusterManager — load balancing selection -// ═════════════════════════════════════════════════════════════════ -TEST(ClusterManagerTest, LoadBalancing) { - ClusterManager mgr; - - // Register nodes with different capacities - mgr.register_node("node1", 50051, 4, 8192); // low capacity - mgr.register_node("node2", 50052, 16, 65536); // high capacity - mgr.register_node("node3", 50053, 8, 16384); // medium capacity - - // Simulate loads - auto nodes = mgr.alive_nodes(); - ASSERT_EQ(nodes.size(), 3); - - // Update loads: node1 heavily loaded, node2 lightly loaded - for (auto& n : nodes) { - if (n.host == "node1") mgr.heartbeat(n.node_id, 0.9, 7000, 10); - if (n.host == "node2") mgr.heartbeat(n.node_id, 0.1, 5000, 1); - if (n.host == "node3") mgr.heartbeat(n.node_id, 0.5, 8000, 4); - } - - // LEAST_LOADED strategy should pick node2 - mgr.set_strategy(ClusterManager::Strategy::LEAST_LOADED); - auto selected = mgr.select_node(); - ASSERT_TRUE(selected.has_value()); - EXPECT_EQ(selected->host, "node2"); - - // Test ROUND_ROBIN - mgr.set_strategy(ClusterManager::Strategy::ROUND_ROBIN); - auto rr1 = mgr.select_node(); - auto rr2 = mgr.select_node(); - EXPECT_TRUE(rr1.has_value()); - EXPECT_TRUE(rr2.has_value()); - - // Test with GPU preference - std::vector gpus = {{"A100", 40960, 80}}; - mgr.register_node("gpu_node", 50054, 64, 262144, gpus); - mgr.heartbeat("gpu_node", 0.2, 10000, 0); - - // After registration, re-fetch node by find - auto gpu_node = mgr.find_node("gpu_node"); - // GPU node exists - EXPECT_TRUE(gpu_node.has_value() || mgr.alive_nodes().size() >= 4); -} - -// ═════════════════════════════════════════════════════════════════ -// Test 7: ClusterNode — capacity score -// ═════════════════════════════════════════════════════════════════ -TEST(ClusterNodeTest, CapacityScore) { - ClusterNode n1; - n1.cores = 8; - n1.memory_mb = 16384; - n1.memory_used_mb = 4096; - n1.cpu_load = 0.2; - n1.alive = true; - double s1 = n1.capacity_score(); - - ClusterNode n2; - n2.cores = 16; - n2.memory_mb = 32768; - n2.memory_used_mb = 4096; - n2.cpu_load = 0.2; - n2.alive = true; - double s2 = n2.capacity_score(); - - // Higher capacity node should have higher score - EXPECT_GT(s2, s1); - - ClusterNode dead; - dead.alive = false; - EXPECT_DOUBLE_EQ(dead.capacity_score(), -1.0); -} - -// ═════════════════════════════════════════════════════════════════ -// Test 8: TaskScheduler — DAG execution -// ═════════════════════════════════════════════════════════════════ -TEST(TaskSchedulerTest, DagExecution) { - auto cluster = std::make_shared(); - cluster->register_node("local", 50051, 4, 8192); - - TaskScheduler scheduler(cluster); - - std::vector execution_order; - std::mutex order_mutex; - int shared_value = 0; - - // Task 1: no deps - scheduler.add_task({.task_id=1, .name="init", .work=[&]() { - std::lock_guard lk(order_mutex); - execution_order.push_back(1); - shared_value = 100; - }}); - - // Task 2: depends on 1 - scheduler.add_task({.task_id=2, .name="step2", .depends_on={1}, .work=[&]() { - std::lock_guard lk(order_mutex); - execution_order.push_back(2); - shared_value += 50; - }}); - - // Task 3: depends on 1 - scheduler.add_task({.task_id=3, .name="step3", .depends_on={1}, .work=[&]() { - std::lock_guard lk(order_mutex); - execution_order.push_back(3); - shared_value += 30; - }}); - - // Task 4: depends on 2 and 3 - scheduler.add_task({.task_id=4, .name="final", .depends_on={2, 3}, .work=[&]() { - std::lock_guard lk(order_mutex); - execution_order.push_back(4); - shared_value *= 2; - }}); - - bool success = scheduler.execute_all(); - EXPECT_TRUE(success); - - // Check execution order constraints - EXPECT_EQ(execution_order[0], 1); // init must be first - EXPECT_EQ(execution_order[3], 4); // final must be last - - // Check results - EXPECT_EQ(shared_value, 360); // (100+50+30)*2 = 360 - - auto stats = scheduler.stats(); - EXPECT_EQ(stats.completed, 4); - EXPECT_EQ(stats.failed, 0); -} - -// ═════════════════════════════════════════════════════════════════ -// Test 9: TaskScheduler — task failure propagation -// ═════════════════════════════════════════════════════════════════ -TEST(TaskSchedulerTest, TaskFailure) { - auto cluster = std::make_shared(); - TaskScheduler scheduler(cluster); - - scheduler.add_task({.task_id=1, .name="failing", .work=[&]() { - throw std::runtime_error("Simulated failure"); - }}); - - scheduler.add_task({.task_id=2, .name="should_not_run", .depends_on={1}, .work=[&]() { - // Should not execute because dependency 1 failed - FAIL() << "Task 2 should not execute after dependency failure"; - }}); - - bool success = scheduler.execute_all(); - EXPECT_FALSE(success); - - EXPECT_EQ(scheduler.get_status(1), TaskStatus::FAILED); - EXPECT_EQ(scheduler.get_status(2), TaskStatus::PENDING); - - auto stats = scheduler.stats(); - EXPECT_EQ(stats.failed, 1); -} - -// ═════════════════════════════════════════════════════════════════ -// Test 10: Distributed Marching Cubes — single node fallback -// ═════════════════════════════════════════════════════════════════ -TEST(DistributedMCTest, SingleNodeFallback) { - auto cluster = std::make_shared(); - cluster->register_node("local", 50051, 4, 8192); - - auto sdf = [](double x, double y, double z) -> double { - return std::sqrt(x*x + y*y + z*z) - 1.0; // unit sphere - }; - - AABB3D bounds(Point3D(-2, -2, -2), Point3D(2, 2, 2)); - DistributedMCConfig config; - config.resolution = 32; - config.subdivisions = 1; - config.iso_level = 0.0; - - auto result = distributed_marching_cubes(sdf, bounds, config, cluster); - - EXPECT_GT(result.mesh.vertices.size(), 0); - EXPECT_GT(result.mesh.triangles.size(), 0); - EXPECT_EQ(result.nodes_used, 1); - EXPECT_GT(result.elapsed_ms, 0); -} - -// ═════════════════════════════════════════════════════════════════ -// Test 11: Distributed Ray Tracing — basic trace -// ═════════════════════════════════════════════════════════════════ -TEST(DistributedRayTraceTest, BasicTrace) { - auto cluster = std::make_shared(); - cluster->register_node("local", 50051, 4, 8192); - - // Build a simple triangle mesh - HalfedgeMesh scene_mesh; - std::vector verts = { - Point3D(-1, -1, 0), Point3D(1, -1, 0), Point3D(0, 1, 0) - }; - std::vector> faces = {{0, 1, 2}}; - scene_mesh.build_from_triangles(verts, faces); - - // Create rays - DistributedRayTraceScene scene; - scene.mesh = scene_mesh; - for (int i = 0; i < 100; ++i) { - vde::core::Ray3Dd ray(Point3D(0, 0, 10), vde::core::Vector3D(0, 0, -1)); - scene.rays.push_back(ray); - } - - auto result = distributed_ray_tracing(scene, cluster); - - EXPECT_GT(result.hits.size(), 0); - EXPECT_EQ(result.total_rays, 100); - EXPECT_EQ(result.nodes_used, 1); - EXPECT_GT(result.elapsed_ms, 0); -} - -// ═════════════════════════════════════════════════════════════════ -// Test 12: gRPC Service — lifecycle -// ═════════════════════════════════════════════════════════════════ -TEST(GrpcServiceTest, Lifecycle) { - GrpcServiceConfig cfg; - cfg.port = 50052; - - VdeService server(cfg); - EXPECT_FALSE(server.is_running()); - - bool started = server.start(); - EXPECT_TRUE(started); - EXPECT_TRUE(server.is_running()); - - auto hc = server.health_check(); - EXPECT_TRUE(hc.healthy); - - server.shutdown(); - EXPECT_FALSE(server.is_running()); -} - -// ═════════════════════════════════════════════════════════════════ -// Test 13: gRPC Client — connect and health check -// ═════════════════════════════════════════════════════════════════ -TEST(GrpcClientTest, ConnectHealthCheck) { - GrpcClientConfig cfg; - cfg.target = "localhost:50051"; - - VdeServiceClient client(cfg); - EXPECT_FALSE(client.is_connected()); - - bool connected = client.connect(); - EXPECT_TRUE(connected); - EXPECT_TRUE(client.is_connected()); - - auto hc = client.health_check(); - EXPECT_TRUE(hc.healthy); - - client.disconnect(); - EXPECT_FALSE(client.is_connected()); -} - -// ═════════════════════════════════════════════════════════════════ -// Test 14: GrpcConnectionPool -// ═════════════════════════════════════════════════════════════════ -TEST(ConnectionPoolTest, BasicPool) { - GrpcConnectionPool::PoolConfig cfg; - cfg.max_connections = 4; - GrpcConnectionPool pool(cfg); - - auto c1 = pool.get_client("192.168.1.1:50051"); - EXPECT_NE(c1, nullptr); - EXPECT_EQ(pool.active_count(), 1); - - auto c2 = pool.get_client("192.168.1.2:50051"); - EXPECT_NE(c2, nullptr); - EXPECT_EQ(pool.active_count(), 2); - - // Getting same host twice returns same client - auto c1_again = pool.get_client("192.168.1.1:50051"); - EXPECT_EQ(c1_again, c1); - EXPECT_EQ(pool.active_count(), 2); - - pool.release_client("192.168.1.1:50051"); - EXPECT_EQ(pool.active_count(), 1); - - pool.close_all(); - EXPECT_EQ(pool.active_count(), 0); -} - -// ═════════════════════════════════════════════════════════════════ -// Test 15: Generate UUID -// ═════════════════════════════════════════════════════════════════ -TEST(UtilityTest, GenerateUuid) { - std::string id1 = generate_uuid(); - std::string id2 = generate_uuid(); - - EXPECT_FALSE(id1.empty()); - EXPECT_FALSE(id2.empty()); - EXPECT_NE(id1, id2); - // UUID format: 8-4-4-4-12 hex digits - EXPECT_EQ(id1.length(), 36); - EXPECT_EQ(id1[8], '-'); - EXPECT_EQ(id1[13], '-'); - EXPECT_EQ(id1[18], '-'); - EXPECT_EQ(id1[23], '-'); -} diff --git a/tests/gpu/CMakeLists.txt b/tests/gpu/CMakeLists.txt deleted file mode 100644 index 03e8b77..0000000 --- a/tests/gpu/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_vde_test(test_gpu_acceleration) diff --git a/tests/gpu/test_gpu_acceleration.cpp b/tests/gpu/test_gpu_acceleration.cpp deleted file mode 100644 index 0f31a0b..0000000 --- a/tests/gpu/test_gpu_acceleration.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/** - * @file test_gpu_acceleration.cpp - * @brief GPU 加速模块测试(8 项) - * - * 测试覆盖: - * 1. gpu_available — 检测 CUDA 可用性 - * 2. gpu_marching_cubes — SDF → 网格(球体) - * 3. gpu_marching_cubes — 低分辨率 - * 4. gpu_marching_cubes — 立方体 SDF - * 5. gpu_mesh_simplify — 基本简化 - * 6. gpu_mesh_simplify — 微小 target_ratio - * 7. gpu_boolean_intersect — 两个相交球体 - * 8. gpu_boolean_intersect — 不相交网格 - */ - -#include "vde/gpu/gpu_acceleration.h" -#include "vde/mesh/marching_cubes.h" -#include "vde/mesh/halfedge_mesh.h" -#include -#include -#include - -using namespace vde::gpu; -using vde::core::AABB3D; -using vde::core::Point3D; -using vde::core::Vector3D; -using vde::mesh::HalfedgeMesh; - -// ── 帮助函数 ───────────────────────────────────────────────────── - -/// 创建一个简单球体 SDF 函数 -static auto make_sphere_sdf(double cx, double cy, double cz, double r) { - return [=](double x, double y, double z) -> double { - return std::sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy) + (z - cz) * (z - cz)) - r; - }; -} - -/// 创建立方体 SDF 函数 -static auto make_box_sdf(double hx, double hy, double hz) { - return [=](double x, double y, double z) -> double { - double dx = std::abs(x) - hx, dy = std::abs(y) - hy, dz = std::abs(z) - hz; - return std::sqrt(std::max(dx, 0.0) * std::max(dx, 0.0) + - std::max(dy, 0.0) * std::max(dy, 0.0) + - std::max(dz, 0.0) * std::max(dz, 0.0)) + - std::min(std::max({dx, dy, dz}), 0.0); - }; -} - -/// 快速创建简单三角网格(正四面体) -static HalfedgeMesh make_tetrahedron(double ox, double oy, double oz, double s) { - HalfedgeMesh mesh; - std::vector verts = { - {ox, oy + s, oz}, - {ox - s * 0.866, oy, oz - s * 0.5}, - {ox + s * 0.866, oy, oz - s * 0.5}, - {ox, oy, oz + s} - }; - std::vector> tris = { - {0, 1, 2}, {0, 2, 3}, {0, 3, 1}, {1, 3, 2} - }; - for (auto& v : verts) mesh.add_vertex(v); - for (auto& t : tris) mesh.add_face({t[0], t[1], t[2]}); - return mesh; -} - -/// 验证网格基本有效性 -static void expect_valid_mesh(const HalfedgeMesh& mesh) { - EXPECT_GT(mesh.num_vertices(), 0u); - EXPECT_GT(mesh.num_faces(), 0u); - EXPECT_GT(mesh.num_edges(), 0u); - // 欧拉公式:V - E + F ≈ 2(对于闭曲面) - // 不作严格检查,但确保拓扑合理 - int euler = static_cast(mesh.num_vertices()) - - static_cast(mesh.num_edges()) + - static_cast(mesh.num_faces()); - EXPECT_GE(euler, 0) << "Euler characteristic should be non-negative"; -} - -// ====================================================================== -// 测试 1:gpu_available -// ====================================================================== -TEST(GpuAcceleration, GpuAvailable) { - // 无论 CUDA 是否可用,函数必须不崩溃并返回布尔值 - bool avail = gpu_available(); - EXPECT_TRUE(avail == true || avail == false); -#if VDE_USE_CUDA - // CUDA 编译时开启 → 运行时至少应无错误 - // (CUDA 设备可能为 0,但调用不崩溃) - SUCCEED() << "CUDA compiled in, runtime detection: " << (avail ? "YES" : "NO"); -#else - EXPECT_FALSE(avail) << "Without CUDA build, gpu_available must return false"; -#endif -} - -// ====================================================================== -// 测试 2:gpu_marching_cubes — 球体 SDF -// ====================================================================== -TEST(GpuAcceleration, MarchingCubesSphere) { - auto sdf = make_sphere_sdf(0, 0, 0, 1.0); - AABB3D bounds({-1.5, -1.5, -1.5}, {1.5, 1.5, 1.5}); - - HalfedgeMesh mesh = gpu_marching_cubes(sdf, bounds, 32); - - expect_valid_mesh(mesh); - - // 球体应当构成闭曲面 - EXPECT_GE(mesh.num_faces(), 50u) << "Low-res sphere should have at least 50 faces"; - - // 所有顶点应大致在球体表面上 - for (size_t i = 0; i < mesh.num_vertices(); ++i) { - const auto& p = mesh.vertex(i); - double dist = std::sqrt(p.x() * p.x() + p.y() * p.y() + p.z() * p.z()); - EXPECT_NEAR(dist, 1.0, 0.15) << "Vertex " << i << " distance from origin"; - } -} - -// ====================================================================== -// 测试 3:gpu_marching_cubes — 低分辨率 -// ====================================================================== -TEST(GpuAcceleration, MarchingCubesLowRes) { - auto sdf = make_sphere_sdf(0, 0, 0, 1.0); - AABB3D bounds({-1.2, -1.2, -1.2}, {1.2, 1.2, 1.2}); - - HalfedgeMesh mesh = gpu_marching_cubes(sdf, bounds, 8); - - // 极低分辨率仍有输出 - EXPECT_GT(mesh.num_vertices(), 0u); - EXPECT_GT(mesh.num_faces(), 0u); -} - -// ====================================================================== -// 测试 4:gpu_marching_cubes — 立方体 SDF -// ====================================================================== -TEST(GpuAcceleration, MarchingCubesBox) { - auto sdf = make_box_sdf(1.0, 0.5, 0.3); - AABB3D bounds({-1.5, -1.0, -0.8}, {1.5, 1.0, 0.8}); - - HalfedgeMesh mesh = gpu_marching_cubes(sdf, bounds, 24); - - expect_valid_mesh(mesh); - - // 检查包围盒尺寸与预期一致 - auto bb = mesh.bounds(); - double max_dim = std::max({bb.extent().x(), bb.extent().y(), bb.extent().z()}); - EXPECT_GT(max_dim, 0.5); - EXPECT_LT(max_dim, 4.0); -} - -// ====================================================================== -// 测试 5:gpu_mesh_simplify — 基本简化 -// ====================================================================== -TEST(GpuAcceleration, MeshSimplifyBasic) { - auto sdf = make_sphere_sdf(0, 0, 0, 1.0); - AABB3D bounds({-1.5, -1.5, -1.5}, {1.5, 1.5, 1.5}); - - HalfedgeMesh original = gpu_marching_cubes(sdf, bounds, 32); - size_t orig_faces = original.num_faces(); - EXPECT_GT(orig_faces, 0u); - - // 简化为 50% 面数 - HalfedgeMesh simplified = gpu_mesh_simplify(original, 0.5f); - - expect_valid_mesh(simplified); - - // 简化后面数应减少 - EXPECT_LE(simplified.num_faces(), orig_faces) - << "Simplified mesh should have ≤ original face count"; -} - -// ====================================================================== -// 测试 6:gpu_mesh_simplify — 极低 target_ratio -// ====================================================================== -TEST(GpuAcceleration, MeshSimplifyTinyRatio) { - auto sdf = make_sphere_sdf(0, 0, 0, 1.0); - AABB3D bounds({-1.5, -1.5, -1.5}, {1.5, 1.5, 1.5}); - - HalfedgeMesh original = gpu_marching_cubes(sdf, bounds, 20); - ASSERT_GT(original.num_faces(), 10u); - - // target_ratio = 0.1 (极低比例) - HalfedgeMesh simplified = gpu_mesh_simplify(original, 0.1f); - - // 不应崩溃且至少保留一些面 - EXPECT_GT(simplified.num_vertices(), 3u); - EXPECT_GT(simplified.num_faces(), 1u); - EXPECT_LT(simplified.num_faces(), original.num_faces()); -} - -// ====================================================================== -// 测试 7:gpu_boolean_intersect — 两个相交球体 -// ====================================================================== -TEST(GpuAcceleration, BooleanIntersectIntersecting) { - // 两个相交球体(中心相距 1.0,半径各 1.2) - auto sdf_a = make_sphere_sdf(-0.5, 0, 0, 1.2); - auto sdf_b = make_sphere_sdf(0.5, 0, 0, 1.2); - AABB3D bounds({-2.0, -1.5, -1.5}, {2.0, 1.5, 1.5}); - - HalfedgeMesh mesh_a = gpu_marching_cubes(sdf_a, bounds, 24); - HalfedgeMesh mesh_b = gpu_marching_cubes(sdf_b, bounds, 24); - - ASSERT_GT(mesh_a.num_faces(), 0u); - ASSERT_GT(mesh_b.num_faces(), 0u); - - HalfedgeMesh result = gpu_boolean_intersect(mesh_a, mesh_b); - - // 交集存在 → 非空输出 - EXPECT_GT(result.num_vertices(), 0u) << "Intersection of overlapping spheres should not be empty"; -} - -// ====================================================================== -// 测试 8:gpu_boolean_intersect — 不相交网格 -// ====================================================================== -TEST(GpuAcceleration, BooleanIntersectDisjoint) { - // 两个远距离球体(中心相距 10,半径各 1) - auto sdf_a = make_sphere_sdf(-5, 0, 0, 1.0); - auto sdf_b = make_sphere_sdf(5, 0, 0, 1.0); - AABB3D bounds({-6.5, -1.5, -1.5}, {6.5, 1.5, 1.5}); - - HalfedgeMesh mesh_a = gpu_marching_cubes(sdf_a, bounds, 20); - HalfedgeMesh mesh_b = gpu_marching_cubes(sdf_b, bounds, 20); - - ASSERT_GT(mesh_a.num_faces(), 0u); - ASSERT_GT(mesh_b.num_faces(), 0u); - - HalfedgeMesh result = gpu_boolean_intersect(mesh_a, mesh_b); - - // 不相交 → 空输出(0 顶点或 0 面) - // 不要求严格为 0,但不应有大量三角形 - EXPECT_LE(result.num_faces(), mesh_a.num_faces() / 2u) - << "Disjoint meshes should have few or no intersection faces"; -} - -// ====================================================================== -// 额外测试:空网格安全 -// ====================================================================== -TEST(GpuAcceleration, EmptyMeshSafety) { - HalfedgeMesh empty; - - // simplify 空网格不崩溃 - HalfedgeMesh r1 = gpu_mesh_simplify(empty, 0.5f); - EXPECT_EQ(r1.num_vertices(), 0u); - EXPECT_EQ(r1.num_faces(), 0u); - - // boolean intersect 空网格不崩溃 - auto sdf = make_sphere_sdf(0, 0, 0, 1.0); - AABB3D bounds({-1.5, -1.5, -1.5}, {1.5, 1.5, 1.5}); - HalfedgeMesh sphere = gpu_marching_cubes(sdf, bounds, 16); - - HalfedgeMesh r2 = gpu_boolean_intersect(empty, sphere); - EXPECT_EQ(r2.num_faces(), 0u); - - HalfedgeMesh r3 = gpu_boolean_intersect(sphere, empty); - EXPECT_EQ(r3.num_faces(), 0u); -} diff --git a/tests/kbe/CMakeLists.txt b/tests/kbe/CMakeLists.txt deleted file mode 100644 index a58f4b4..0000000 --- a/tests/kbe/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_executable(test_knowledge test_knowledge.cpp) -target_include_directories(test_knowledge PRIVATE ${CMAKE_SOURCE_DIR}/include) -target_link_libraries(test_knowledge PRIVATE vde_kbe vde_core vde_foundation GTest::gtest GTest::gtest_main) -gtest_discover_tests(test_knowledge) diff --git a/tests/kbe/test_knowledge.cpp b/tests/kbe/test_knowledge.cpp deleted file mode 100644 index cc65774..0000000 --- a/tests/kbe/test_knowledge.cpp +++ /dev/null @@ -1,312 +0,0 @@ -#include -#include "vde/kbe/knowledge_engine.h" -#include -#include -#include - -using namespace vde::kbe; - -// ═══════════════════════════════════════════════════════════ -// 1. CheckMate 设计验证 -// ═══════════════════════════════════════════════════════════ - -TEST(KBETest, CheckMate_DefaultRulesRegistered) { - CheckMate cm; - auto rules = cm.rules(); - // 默认注册至少 10 条规则 - EXPECT_GE(rules.size(), 10u); -} - -TEST(KBETest, CheckMate_RunAllDefaultRules) { - CheckMate cm; - std::vector params = { - {"diameter", 10.0}, - {"wall_thickness", 0.8}, - {"aspect_ratio", 5.0}, - {"density", 2700.0}, - {"safety_factor", 2.0}, - }; - auto results = cm.run_all(params); - EXPECT_EQ(results.size(), cm.rules().size()); - - auto summary = cm.summary(results); - // 默认规则应全部通过(参数值在合法范围内) - int total_failures = 0; - for (const auto& [sev, count] : summary) total_failures += count; - EXPECT_EQ(total_failures, 0); -} - -TEST(KBETest, CheckMate_PositiveDimensionFails) { - CheckMate cm; - std::vector params = { - {"diameter", -5.0}, // 非法:负值 - }; - auto result = cm.run_rule("R_GEO_POSITIVE_DIM", params); - EXPECT_FALSE(result.passed); - EXPECT_EQ(result.severity, CheckSeverity::ERROR); -} - -TEST(KBETest, CheckMate_CustomCheck) { - CheckMate cm; - cm.add_check("R_GEO_POSITIVE_DIM", [](const std::vector&) { - CheckResult r; - r.rule_name = "R_GEO_POSITIVE_DIM"; - r.severity = CheckSeverity::ERROR; - r.passed = false; - r.message = "Custom: negative dimension detected"; - return r; - }); - auto result = cm.run_rule("R_GEO_POSITIVE_DIM", {}); - EXPECT_FALSE(result.passed); - EXPECT_EQ(result.message, "Custom: negative dimension detected"); -} - -TEST(KBETest, CheckMate_CategoryRun) { - CheckMate cm; - std::vector params = { - {"diameter", 10.0}, {"wall_thickness", 2.0}, {"safety_factor", 2.0}, - }; - auto geo_results = cm.run_category("geometry", params); - // 应有几何规则产生结果 - EXPECT_GT(geo_results.size(), 0u); - - auto mat_results = cm.run_category("material", params); - EXPECT_GT(mat_results.size(), 0u); -} - -// ═══════════════════════════════════════════════════════════ -// 2. RuleEngine IF-THEN 规则引擎 -// ═══════════════════════════════════════════════════════════ - -TEST(KBETest, RuleEngine_AddAndGetParam) { - RuleEngine engine; - engine.set_param("length", 10.0); - auto val = engine.get_param("length"); - ASSERT_TRUE(val.has_value()); - EXPECT_DOUBLE_EQ(std::get(*val), 10.0); -} - -TEST(KBETest, RuleEngine_SimpleInfer) { - RuleEngine engine; - engine.set_param("diameter", 5.0); - engine.set_param("thickness", 1.0); - - // IF diameter > 3 THEN SET thickness = 2.0 - Rule r{"R1", "diameter > 3", "SET thickness = 2.0", 1, true}; - engine.add_rule(r); - - auto traces = engine.infer(); - EXPECT_GE(traces.size(), 1u); - - auto new_thickness = engine.get_param("thickness"); - ASSERT_TRUE(new_thickness.has_value()); - EXPECT_DOUBLE_EQ(std::get(*new_thickness), 2.0); -} - -TEST(KBETest, RuleEngine_PriorityOrder) { - RuleEngine engine; - engine.set_param("x", 1.0); - - // 高优先级规则先执行: SET x = 20 - Rule r1{"low", "x >= 0", "SET x = 10.0", 0, true}; // priority 0 - Rule r2{"high", "x >= 0", "SET x = 20.0", 10, true}; // priority 10 - engine.add_rule(r1); - engine.add_rule(r2); - - // 高优先级规则先触发,但同一轮迭代中低优先级规则也会触发 - // 最终结果由最后触发的规则决定(按优先级排序,低优先级后执行) - auto traces = engine.infer(); - EXPECT_GE(traces.size(), 2u); // 两条规则都触发 - - auto val = engine.get_param("x"); - ASSERT_TRUE(val.has_value()); - // 低优先级(priority 0)后执行,最终为 10 - EXPECT_DOUBLE_EQ(std::get(*val), 10.0); -} - -TEST(KBETest, RuleEngine_ANDCondition) { - RuleEngine engine; - engine.set_param("a", 10.0); - engine.set_param("b", 5.0); - - Rule r{"R_and", "a > 5 AND b < 10", "SET a = 100.0", 1, true}; - engine.add_rule(r); - engine.infer(); - - auto val = engine.get_param("a"); - ASSERT_TRUE(val.has_value()); - EXPECT_DOUBLE_EQ(std::get(*val), 100.0); -} - -TEST(KBETest, RuleEngine_NoInfiniteLoop) { - RuleEngine engine; - engine.set_param("counter", 0.0); - - // 自引用规则 — 应被循环检测阻止 - Rule r{"loop", "counter < 1000", "SET counter = counter + 1", 1, true}; - engine.add_rule(r); - - auto traces = engine.infer(); - // 最多执行 100 次迭代(由引擎内部限制) - EXPECT_LE(traces.size(), 100u); -} - -// ═══════════════════════════════════════════════════════════ -// 3. DesignTable 设计表 -// ═══════════════════════════════════════════════════════════ - -TEST(KBETest, DesignTable_DefineColumnsAndAddRow) { - DesignTable table; - table.define_columns({"length", "width", "height"}, {"mm", "mm", "mm"}); - EXPECT_EQ(table.col_count(), 3u); - - DesignRow row; - row["length"] = 100.0; - row["width"] = 50.0; - row["height"] = 25.0; - table.add_row(row); - EXPECT_EQ(table.row_count(), 1u); - - auto r = table.get_row(0); - ASSERT_TRUE(r.has_value()); - EXPECT_DOUBLE_EQ(std::get(r->at("length")), 100.0); -} - -TEST(KBETest, DesignTable_CSVImportExport) { - DesignTable table; - std::string csv = - "length,width,height\n" - "100,50,25\n" - "200,80,40\n" - "150,60,30\n"; - - EXPECT_TRUE(table.import_csv(csv)); - EXPECT_EQ(table.row_count(), 3u); - EXPECT_EQ(table.col_count(), 3u); - - std::string exported = table.export_csv(); - EXPECT_FALSE(exported.empty()); - EXPECT_NE(exported.find("length"), std::string::npos); - EXPECT_NE(exported.find("100"), std::string::npos); -} - -TEST(KBETest, DesignTable_MixedTypes) { - DesignTable table; - std::string csv = - "name,diameter,material,is_standard\n" - "Bolt_M10,10,Steel,true\n" - "Nut_M8,8,Brass,false\n"; - - EXPECT_TRUE(table.import_csv(csv)); - EXPECT_EQ(table.row_count(), 2u); - - auto r0 = table.get_row(0); - ASSERT_TRUE(r0.has_value()); - EXPECT_EQ(std::get(r0->at("name")), "Bolt_M10"); - EXPECT_DOUBLE_EQ(std::get(r0->at("diameter")), 10.0); - EXPECT_EQ(std::get(r0->at("is_standard")), true); -} - -// ═══════════════════════════════════════════════════════════ -// 4. ParametricOptimization 参数优化 -// ═══════════════════════════════════════════════════════════ - -TEST(KBETest, Optimization_GA_Minimization) { - ParametricOptimization opt; - - // 目标:最小化 (x-5)^2 - opt.set_fitness([](const std::vector& params) { - for (const auto& p : params) { - if (p.name == "x" && std::holds_alternative(p.value)) { - double x = std::get(p.value); - return -((x - 5.0) * (x - 5.0)); // 负值因为 GA 最大化适应度 - } - } - return 0.0; - }); - - std::vector initial = {{"x", 10.0, 0.0, 100.0, "", "", false}}; - opt.set_bounds("x", 0.0, 100.0); - - GAConfig ga_config; - ga_config.population_size = 50; - ga_config.generations = 30; - - auto result = opt.optimize_ga(initial, ga_config); - EXPECT_TRUE(result.converged); - - // 最优解应接近 x=5.0 - for (const auto& p : result.optimal_params) { - if (p.name == "x") { - double val = std::get(p.value); - EXPECT_NEAR(val, 5.0, 5.0); // GA 有随机性,容差大些 - } - } - - // 适应度历史应有记录 - EXPECT_GT(opt.fitness_history().size(), 0u); -} - -TEST(KBETest, Optimization_Gradient_Descent) { - ParametricOptimization opt; - - // 目标:最小化 (x-3)^2,梯度下降直接最小化 fitness - opt.set_fitness([](const std::vector& params) { - for (const auto& p : params) { - if (p.name == "x" && std::holds_alternative(p.value)) { - double x = std::get(p.value); - return (x - 3.0) * (x - 3.0); // 正值,梯度下降最小化 - } - } - return 0.0; - }); - - std::vector initial = {{"x", 8.0, 0.0, 100.0, "", "", false}}; - opt.set_bounds("x", -10.0, 100.0); - - GradientConfig grad_config; - grad_config.learning_rate = 0.05; - grad_config.max_iterations = 500; - grad_config.tolerance = 1e-6; - - auto result = opt.optimize_gradient(initial, grad_config); - - for (const auto& p : result.optimal_params) { - if (p.name == "x") { - double val = std::get(p.value); - EXPECT_NEAR(val, 3.0, 3.0); // 梯度下降有收敛性,容差较大 - } - } -} - -TEST(KBETest, Optimization_Constraints) { - ParametricOptimization opt; - - opt.set_fitness([](const std::vector& params) { - for (const auto& p : params) { - if (p.name == "x" && std::holds_alternative(p.value)) { - double x = std::get(p.value); - return -x; // 最小化 x(GA 最大化适应度 = 最小化 x) - } - } - return 0.0; - }); - - std::vector initial = {{"x", 5.0, 0.0, 100.0, "", "", false}}; - // 用 bounds 约束 x 上限为 10 - opt.set_bounds("x", 0.0, 10.0); - - GAConfig ga_config; - ga_config.population_size = 30; - ga_config.generations = 30; - - auto result = opt.optimize_ga(initial, ga_config); - - for (const auto& p : result.optimal_params) { - if (p.name == "x") { - double val = std::get(p.value); - EXPECT_LE(val, 10.0); // 必须满足 bound 约束 - EXPECT_GT(val, -1.0); // 不能为负 - } - } -} diff --git a/viewer/app.js b/viewer/app.js deleted file mode 100644 index c1e8d0f..0000000 --- a/viewer/app.js +++ /dev/null @@ -1,1175 +0,0 @@ -/** - * ViewDesignEngine 3D Viewer — 主应用模块 - * - * 功能: - * - GLB/STL 加载,OrbitControls 交互 - * - B-Rep 面类型着色 - * - 尺寸标注 2D overlay - * - GD&T 公差标注 - * - 测量工具(Raycaster 两点测距) - * - 剖切面(ClipPlane + 滑块) - * - 爆炸视图动画(GSAP) - * - 文件拖放上传 - */ - -import * as THREE from 'three'; -import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; -import { STLLoader } from 'three/addons/loaders/STLLoader.js'; - -// ═══════════════════════════════════════════ -// 面类型 → 颜色映射(B-Rep 着色方案) -// ═══════════════════════════════════════════ -const FACE_COLORS = { - planar: 0x4ecdc4, - cylindrical: 0x45b7d1, - conical: 0x96ceb4, - spherical: 0xffeaa7, - toroidal: 0xdfe6e9, - spline: 0xfd79a8, - revolved: 0xa29bfe, - extruded: 0x55efc4, - offset: 0x74b9ff, - ruled: 0xfdcb6e, - default: 0xb2bec3 -}; - -const FACE_LABELS = { - planar: '平面 Planar', - cylindrical: '圆柱面 Cylindrical', - conical: '圆锥面 Conical', - spherical: '球面 Spherical', - toroidal: '环面 Toroidal', - spline: '样条面 Spline', - revolved: '旋转面 Revolved', - extruded: '拉伸面 Extruded', - offset: '偏置面 Offset', - ruled: '直纹面 Ruled', - default: '其他 Other' -}; - -// ═══════════════════════════════════════════ -// Viewer3D 类 -// ═══════════════════════════════════════════ -export class Viewer3D { - constructor() { - // ── DOM 引用 ── - this.canvas = document.getElementById('viewer-canvas'); - this.overlayCanvas = document.getElementById('dimension-overlay'); - this.overlayCtx = this.overlayCanvas.getContext('2d'); - this.dropZone = document.getElementById('drop-zone'); - this.dragBorder = document.getElementById('drag-border'); - this.infoBar = document.getElementById('info-bar'); - this.brepLegend = document.getElementById('brep-legend'); - this.gdtPanel = document.getElementById('gdt-panel'); - this.measurePanel = document.getElementById('measure-panel'); - this.clipBar = document.getElementById('clip-bar'); - this.clipSlider = document.getElementById('clip-plane-slider'); - this.clipAxisSel = document.getElementById('clip-axis'); - this.clipValSpan = document.getElementById('clip-val'); - - // ── 状态 ── - this.currentModel = null; // 当前加载的根对象 - this.allMeshes = []; // 所有 Mesh 子节点(扁平化) - this.originalPositions = new Map(); // 爆炸视图:原始位置 - this.isExploded = false; - this.isMeasuring = false; - this.measurePts = []; // 暂存的测量点 [{point, marker}] - this.measureMarkers = []; // 测量小球 - this.clipPlane = null; - this.clipEnabled = false; - this.dimensions = []; // 尺寸标注数据 - this.brepFaceStats = {}; // B-Rep 面统计 - - // ── 初始化 ── - this._initScene(); - this._initLights(); - this._initHelpers(); - this._initControls(); - this._initEvents(); - this._animate(); - } - - // ─────────────────────────────────────── - // 场景初始化 - // ─────────────────────────────────────── - _initScene() { - this.scene = new THREE.Scene(); - this.scene.background = new THREE.Color(0x1a1a2e); - // 渐进过渡背景(微妙的渐变效果) - this.scene.fog = new THREE.Fog(0x1a1a2e, 20, 80); - - this.renderer = new THREE.WebGLRenderer({ - canvas: this.canvas, - antialias: true, - alpha: false - }); - this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); - this.renderer.setSize(window.innerWidth, window.innerHeight); - this.renderer.shadowMap.enabled = true; - this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; - this.renderer.localClippingEnabled = true; - this.renderer.toneMapping = THREE.ACESFilmicToneMapping; - this.renderer.toneMappingExposure = 1.2; - - this.camera = new THREE.PerspectiveCamera( - 45, window.innerWidth / window.innerHeight, 0.1, 200 - ); - this.camera.position.set(8, 6, 10); - this.camera.lookAt(0, 0, 0); - } - - // ─────────────────────────────────────── - // 灯光 - // ─────────────────────────────────────── - _initLights() { - // 环境光 - const ambient = new THREE.AmbientLight(0x404060, 1.2); - this.scene.add(ambient); - - // 半球光(天空+地面) - const hemi = new THREE.HemisphereLight(0x8daef2, 0x333344, 0.6); - this.scene.add(hemi); - - // 主方向光 + 阴影 - const key = new THREE.DirectionalLight(0xffffff, 3); - key.position.set(12, 18, 8); - key.castShadow = true; - key.shadow.mapSize.set(2048, 2048); - key.shadow.camera.near = 0.5; - key.shadow.camera.far = 80; - key.shadow.camera.left = -15; - key.shadow.camera.right = 15; - key.shadow.camera.top = 15; - key.shadow.camera.bottom = -15; - key.shadow.bias = -0.0001; - this.scene.add(key); - - // 补光 - const fill = new THREE.DirectionalLight(0x8899cc, 1.2); - fill.position.set(-6, 2, -4); - this.scene.add(fill); - - // 底部反弹光 - const rim = new THREE.DirectionalLight(0x667788, 0.8); - rim.position.set(0, -2, 6); - this.scene.add(rim); - } - - // ─────────────────────────────────────── - // 辅助对象(网格、地面) - // ─────────────────────────────────────── - _initHelpers() { - // 主网格 - this.grid = new THREE.GridHelper(20, 20, 0x444466, 0x2a2a3e); - this.scene.add(this.grid); - - // 半透明地面(接收阴影) - const groundGeo = new THREE.PlaneGeometry(30, 30); - const groundMat = new THREE.MeshStandardMaterial({ - color: 0x222233, - roughness: 0.9, - metalness: 0.1, - transparent: true, - opacity: 0.5 - }); - const ground = new THREE.Mesh(groundGeo, groundMat); - ground.rotation.x = -Math.PI / 2; - ground.position.y = -0.01; - ground.receiveShadow = true; - ground.name = '__ground__'; - this.scene.add(ground); - - // 坐标轴指示器(原点小球) - this.originMarker = this._createOriginMarker(); - this.scene.add(this.originMarker); - } - - _createOriginMarker() { - const group = new THREE.Group(); - // 原点球 - const sphere = new THREE.Mesh( - new THREE.SphereGeometry(0.12, 16, 16), - new THREE.MeshBasicMaterial({ color: 0xffffff }) - ); - group.add(sphere); - - // X 轴(红) - group.add(this._axisLine(new THREE.Vector3(0, 0, 0), new THREE.Vector3(1.5, 0, 0), 0xff4444)); - // Y 轴(绿) - group.add(this._axisLine(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 1.5, 0), 0x44ff44)); - // Z 轴(蓝) - group.add(this._axisLine(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 1.5), 0x4488ff)); - - group.name = '__origin__'; - return group; - } - - _axisLine(from, to, color) { - const dir = new THREE.Vector3().subVectors(to, from); - const len = dir.length(); - const mid = new THREE.Vector3().addVectors(from, to).multiplyScalar(0.5); - const geo = new THREE.CylinderGeometry(0.03, 0.03, len, 8); - const mat = new THREE.MeshBasicMaterial({ color }); - const mesh = new THREE.Mesh(geo, mat); - mesh.position.copy(mid); - // 对齐方向 - const axis = new THREE.Vector3(0, 1, 0); - const quat = new THREE.Quaternion().setFromUnitVectors(axis, dir.normalize()); - mesh.setRotationFromQuaternion(quat); - return mesh; - } - - // ─────────────────────────────────────── - // OrbitControls - // ─────────────────────────────────────── - _initControls() { - this.controls = new OrbitControls(this.camera, this.renderer.domElement); - this.controls.enableDamping = true; - this.controls.dampingFactor = 0.08; - this.controls.minDistance = 0.5; - this.controls.maxDistance = 50; - this.controls.maxPolarAngle = Math.PI * 0.85; - this.controls.target.set(0, 0, 0); - this.controls.update(); - } - - // ─────────────────────────────────────── - // 事件绑定 - // ─────────────────────────────────────── - _initEvents() { - // 窗口 resize - window.addEventListener('resize', () => this._onResize()); - - // 文件输入 - const fileInput = document.getElementById('file-input'); - fileInput.addEventListener('change', (e) => this._onFileSelect(e)); - - // 拖放 - document.addEventListener('dragover', (e) => { - e.preventDefault(); - e.stopPropagation(); - this.dragBorder.classList.add('show'); - this.dropZone.classList.add('highlight'); - }); - document.addEventListener('dragleave', (e) => { - e.preventDefault(); - e.stopPropagation(); - this.dragBorder.classList.remove('show'); - this.dropZone.classList.remove('highlight'); - }); - document.addEventListener('drop', (e) => { - e.preventDefault(); - e.stopPropagation(); - this.dragBorder.classList.remove('show'); - this.dropZone.classList.remove('highlight'); - const file = e.dataTransfer.files[0]; - if (file) this._loadFile(file); - }); - - // 剖切面滑块 - this.clipSlider.addEventListener('input', () => this._onClipSlider()); - this.clipAxisSel.addEventListener('change', () => this._onClipSlider()); - - // 测量模式:overlay canvas 点击 - this.overlayCanvas.addEventListener('click', (e) => this._onMeasureClick(e)); - - // 键盘快捷键 - window.addEventListener('keydown', (e) => this._onKeyDown(e)); - } - - // ─────────────────────────────────────── - // 渲染循环 - // ─────────────────────────────────────── - _animate() { - requestAnimationFrame(() => this._animate()); - this.controls.update(); - this.renderer.render(this.scene, this.camera); - this._drawDimensionOverlay(); - } - - // ═══════════════════════════════════════ - // 文件加载 - // ═══════════════════════════════════════ - - _onFileSelect(e) { - const file = e.target.files[0]; - if (file) this._loadFile(file); - } - - _loadFile(file) { - const url = URL.createObjectURL(file); - const ext = file.name.split('.').pop().toLowerCase(); - - this._showInfo(`正在加载: ${file.name}…`); - - if (ext === 'stl') { - this._loadSTL(url, file.name); - } else { - this._loadGLB(url, file.name); - } - } - - _loadGLB(url, name) { - const loader = new GLTFLoader(); - loader.load( - url, - (gltf) => { - this._onModelLoaded(gltf.scene, name, 'GLB'); - }, - (progress) => { - const pct = Math.round((progress.loaded / progress.total) * 100); - this._showInfo(`加载中: ${name} (${pct}%)`); - }, - (err) => { - this._showInfo(`❌ 加载失败: ${err.message || '未知错误'}`); - console.error('GLB load error:', err); - } - ); - } - - _loadSTL(url, name) { - const loader = new STLLoader(); - loader.load( - url, - (geometry) => { - geometry.computeVertexNormals(); - const material = new THREE.MeshStandardMaterial({ - color: 0x4499cc, - roughness: 0.4, - metalness: 0.3 - }); - const mesh = new THREE.Mesh(geometry, material); - mesh.castShadow = true; - mesh.receiveShadow = true; - - const group = new THREE.Group(); - group.add(mesh); - this._onModelLoaded(group, name, 'STL'); - }, - (progress) => { - const pct = progress.total - ? Math.round((progress.loaded / progress.total) * 100) - : '?'; - this._showInfo(`加载中: ${name} (${pct}%)`); - }, - (err) => { - this._showInfo(`❌ 加载失败: ${err.message || '未知错误'}`); - console.error('STL load error:', err); - } - ); - } - - _onModelLoaded(model, name, format) { - // 清除旧模型 - this.clear(false); - - // 添加新模型 - this.currentModel = model; - this.scene.add(model); - - // 扁平化收集所有 Mesh - this.allMeshes = []; - model.traverse((child) => { - if (child.isMesh) { - this.allMeshes.push(child); - child.castShadow = true; - child.receiveShadow = true; - } - }); - - // 居中 + 适配视野 - this._fitCameraToModel(model); - - // B-Rep 面着色 - this._applyBRepColoring(); - - // 提取 GD&T 元数据 - this._extractGDTMetadata(model, name); - - // 更新 UI - this.dropZone.classList.add('hidden'); - this._showInfo(`✅ ${format}: ${name} — ${this.allMeshes.length} 个面`); - - // 重置状态 - if (this.clipEnabled) this.toggleClipPlane(); - if (this.isExploded) this.toggleExplode(); - } - - // ═══════════════════════════════════════ - // 相机适配 - // ═══════════════════════════════════════ - - _fitCameraToModel(model) { - const box = new THREE.Box3().setFromObject(model); - const center = new THREE.Vector3(); - box.getCenter(center); - const size = new THREE.Vector3(); - box.getSize(size); - const maxDim = Math.max(size.x, size.y, size.z); - - // 移动网格到模型下方 - this.grid.position.y = box.min.y - 0.1; - // 更新网格 - if (maxDim > 10) { - const gridSize = Math.ceil(maxDim * 1.2 / 10) * 10; - this.scene.remove(this.grid); - this.grid = new THREE.GridHelper(gridSize, Math.round(gridSize), 0x444466, 0x2a2a3e); - this.grid.position.y = box.min.y - 0.1; - this.scene.add(this.grid); - } - - // 更新剖切面滑块范围 - const half = maxDim * 0.75; - this.clipSlider.min = Math.round(-half * 100); - this.clipSlider.max = Math.round(half * 100); - this.clipSlider.value = 0; - - // 相机位置 - const dist = maxDim * 2.2; - this.controls.target.copy(center); - this.camera.position.set( - center.x + dist * 0.7, - center.y + dist * 0.55, - center.z + dist * 0.7 - ); - this.controls.update(); - } - - // ═══════════════════════════════════════ - // B-Rep 面类型着色 - // ═══════════════════════════════════════ - - _applyBRepColoring() { - this.brepFaceStats = {}; - - this.allMeshes.forEach((mesh, idx) => { - const faceType = this._detectFaceType(mesh); - const color = FACE_COLORS[faceType] || FACE_COLORS.default; - - // 克隆材质以避免共享材质问题 - if (mesh.material) { - if (Array.isArray(mesh.material)) { - mesh.material = mesh.material.map(m => { - const clone = m.clone(); - clone.color.setHex(color); - return clone; - }); - } else { - mesh.material = mesh.material.clone(); - mesh.material.color.setHex(color); - } - } - - // 统计 - this.brepFaceStats[faceType] = (this.brepFaceStats[faceType] || 0) + 1; - }); - - this._renderBRepLegend(); - } - - _detectFaceType(mesh) { - const name = (mesh.name || '').toLowerCase(); - const parent = mesh.parent ? (mesh.parent.name || '').toLowerCase() : ''; - const combined = `${parent}_${name}`; - - // 按名称关键词检测面类型 - const patterns = [ - ['planar', /planar|plane|flat|face_pl/i], - ['cylindrical', /cylind|cyl_|hole|bore/i], - ['conical', /conic|cone|chamfer/i], - ['spherical', /spher|sphere|ball/i], - ['toroidal', /toroi|torus|fillet|round/i], - ['spline', /spline|nurb|bezier|bspline/i], - ['revolved', /revolv|rev_/i], - ['extruded', /extrud|ext_/i], - ['offset', /offset/i], - ['ruled', /ruled|rule_/i], - ]; - - for (const [type, regex] of patterns) { - if (regex.test(combined)) return type; - } - - // 启发式:检测形状 - // 如果 mesh 名称没有匹配,尝试通过几何特征判断 - if (mesh.geometry) { - const geo = mesh.geometry; - if (geo.type === 'CylinderGeometry' || geo.type === 'CylindricalGeometry') { - return 'cylindrical'; - } - if (geo.type === 'SphereGeometry' || geo.type === 'SphericalGeometry') { - return 'spherical'; - } - } - - return 'default'; - } - - _renderBRepLegend() { - const container = document.getElementById('brep-legend-items'); - const types = Object.keys(this.brepFaceStats).sort((a, b) => { - // default 放最后 - if (a === 'default') return 1; - if (b === 'default') return -1; - return this.brepFaceStats[b] - this.brepFaceStats[a]; - }); - - container.innerHTML = types.map(type => { - const color = '#' + new THREE.Color(FACE_COLORS[type] || FACE_COLORS.default).getHexString(); - const label = FACE_LABELS[type] || type; - const count = this.brepFaceStats[type]; - return ` -
- - ${label} - ×${count} -
`; - }).join(''); - - this.brepLegend.style.display = types.length > 0 ? 'block' : 'none'; - } - - // ═══════════════════════════════════════ - // GD&T 标注 - // ═══════════════════════════════════════ - - _extractGDTMetadata(model, filename) { - const gdtItems = []; - - // 从模型 userData 或自定义属性中提取 GD&T - model.traverse((child) => { - const ud = child.userData || {}; - if (ud.gdt) { - gdtItems.push(ud.gdt); - } - // 也检测名称中的 GD&T 模式 - const name = (child.name || '').toUpperCase(); - if (name.includes('GDT') || name.includes('TOLERANCE') || name.includes('DATUM')) { - gdtItems.push({ - feature: child.name, - type: this._guessGDTType(name), - tolerance: '?', - datums: this._guessDatums(name) - }); - } - }); - - // 生成模拟 GD&T 数据(当真实数据不可用时) - if (gdtItems.length === 0 && this.allMeshes.length > 0) { - gdtItems.push(...this._generateDemoGDT(filename)); - } - - this._renderGDTPanel(gdtItems); - } - - _guessGDTType(name) { - if (/FLATNESS|FLAT_/i.test(name)) return '⏤ 平面度 Flatness'; - if (/STRAIGHT/i.test(name)) return '— 直线度 Straightness'; - if (/CIRCULAR|ROUND/i.test(name)) return '○ 圆度 Circularity'; - if (/CYLINDRICITY/i.test(name)) return '⌭ 圆柱度 Cylindricity'; - if (/PERPEND|PERP_/i.test(name)) return '⟂ 垂直度 Perpendicularity'; - if (/PARALLEL|PARA_/i.test(name)) return '∥ 平行度 Parallelism'; - if (/POSITION|TRUE_POS/i.test(name)) return '⊕ 位置度 Position'; - if (/CONCENTRIC|COAXIAL/i.test(name)) return '◎ 同轴度 Concentricity'; - if (/PROFILE/i.test(name)) return '⌒ 轮廓度 Profile'; - if (/RUNOUT/i.test(name)) return '↗ 跳动 Runout'; - return '⊕ 位置度 Position'; - } - - _guessDatums(name) { - const match = name.match(/DATUM[_\s]*([A-C])/i); - return match ? [match[1]] : ['A']; - } - - _generateDemoGDT(filename) { - return [ - { feature: '底面', type: '⏤ 平面度 Flatness', tolerance: '0.05', datums: [] }, - { feature: '主孔 Ø20', type: '⊕ 位置度 Position', tolerance: 'Ø0.1', datums: ['A', 'B'] }, - { feature: '顶面', type: '∥ 平行度 Parallelism', tolerance: '0.03', datums: ['A'] }, - ]; - } - - _renderGDTPanel(items) { - const container = document.getElementById('gdt-content'); - if (items.length === 0) { - container.innerHTML = '

无 GD&T 数据

'; - return; - } - - container.innerHTML = items.map((item, i) => ` -
-
${item.feature || 'Feature'}
-
${item.type}
-
-
公差${item.tolerance}
- ${item.datums && item.datums.length > 0 ? ` -
基准${item.datums.join(' | ')}
- ` : ''} -
-
- `).join(''); - } - - toggleGDTPanel() { - const el = this.gdtPanel; - const btn = document.getElementById('btn-gdt'); - el.style.display = (el.style.display === 'block') ? 'none' : 'block'; - btn.classList.toggle('active', el.style.display === 'block'); - } - - // ═══════════════════════════════════════ - // 测量工具 - // ═══════════════════════════════════════ - - toggleMeasure() { - this.isMeasuring = !this.isMeasuring; - const btn = document.getElementById('btn-measure'); - btn.classList.toggle('active', this.isMeasuring); - - if (this.isMeasuring) { - this.overlayCanvas.classList.add('active'); - this.measurePanel.style.display = 'block'; - this._clearMeasurePoints(); - document.getElementById('meas-hint').style.display = 'block'; - document.getElementById('meas-result').style.display = 'none'; - document.getElementById('meas-p1').style.display = 'none'; - document.getElementById('meas-p2').style.display = 'none'; - } else { - this.overlayCanvas.classList.remove('active'); - this._clearMeasurePoints(); - // 不隐藏面板,让用户可以看到最后结果 - } - } - - _onMeasureClick(e) { - if (!this.isMeasuring || !this.currentModel) return; - - const mouse = new THREE.Vector2(); - mouse.x = (e.clientX / window.innerWidth) * 2 - 1; - mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; - - const raycaster = new THREE.Raycaster(); - raycaster.setFromCamera(mouse, this.camera); - - const intersects = raycaster.intersectObjects(this.allMeshes, false); - if (intersects.length === 0) return; - - const point = intersects[0].point.clone(); - - // 已有两个点,清除重新开始 - if (this.measurePts.length >= 2) { - this._clearMeasurePoints(); - } - - // 添加测量点 - this.measurePts.push({ point, screenX: e.clientX, screenY: e.clientY }); - this._addMeasureMarker(point, this.measurePts.length); - - // 更新 UI - if (this.measurePts.length === 1) { - document.getElementById('meas-p1').style.display = 'flex'; - document.getElementById('meas-p1-val').textContent = - `(${point.x.toFixed(2)}, ${point.y.toFixed(2)}, ${point.z.toFixed(2)})`; - document.getElementById('meas-hint').textContent = '请点击第二个测量点'; - } - - if (this.measurePts.length === 2) { - document.getElementById('meas-p2').style.display = 'flex'; - document.getElementById('meas-p2-val').textContent = - `(${point.x.toFixed(2)}, ${point.y.toFixed(2)}, ${point.z.toFixed(2)})`; - - const dist = this.measurePts[0].point.distanceTo(this.measurePts[1].point); - document.getElementById('meas-hint').style.display = 'none'; - document.getElementById('meas-result').style.display = 'block'; - document.getElementById('meas-result').textContent = `${dist.toFixed(3)} mm`; - - // 添加尺寸标注线 - this._addMeasureLine(); - } - } - - _addMeasureMarker(point, idx) { - const color = idx === 1 ? 0xffd93d : 0xff6b6b; - const geo = new THREE.SphereGeometry(0.08, 16, 16); - const mat = new THREE.MeshBasicMaterial({ color }); - const marker = new THREE.Mesh(geo, mat); - marker.position.copy(point); - marker.name = `__measure_marker_${idx}`; - this.scene.add(marker); - this.measureMarkers.push(marker); - } - - _addMeasureLine() { - if (this.measurePts.length < 2) return; - - const p1 = this.measurePts[0].point; - const p2 = this.measurePts[1].point; - - // 添加可视化连线 - const dir = new THREE.Vector3().subVectors(p2, p1); - const mid = new THREE.Vector3().addVectors(p1, p2).multiplyScalar(0.5); - const len = dir.length(); - - const geo = new THREE.CylinderGeometry(0.02, 0.02, len, 8); - const mat = new THREE.MeshBasicMaterial({ color: 0xffd93d }); - const line = new THREE.Mesh(geo, mat); - line.position.copy(mid); - - const axis = new THREE.Vector3(0, 1, 0); - const quat = new THREE.Quaternion().setFromUnitVectors(axis, dir.normalize()); - line.setRotationFromQuaternion(quat); - line.name = '__measure_line'; - this.scene.add(line); - this.measureMarkers.push(line); - - // 保存标注数据用于 overlay 绘制 - this.dimensions.push({ - p1: p1.clone(), - p2: p2.clone(), - value: len, - type: 'measure' - }); - } - - _clearMeasurePoints() { - this.measurePts = []; - this.measureMarkers.forEach(m => this.scene.remove(m)); - this.measureMarkers = []; - // 清理旧标注 - this.dimensions = this.dimensions.filter(d => d.type !== 'measure'); - } - - // ═══════════════════════════════════════ - // 尺寸标注 Overlay(CSS + Canvas) - // ═══════════════════════════════════════ - - _drawDimensionOverlay() { - const canvas = this.overlayCanvas; - const ctx = this.overlayCtx; - const w = window.innerWidth; - const h = window.innerHeight; - - // 同步 canvas 尺寸 - if (canvas.width !== w || canvas.height !== h) { - canvas.width = w; - canvas.height = h; - } - - ctx.clearRect(0, 0, w, h); - - // 没有标注数据 - if (this.dimensions.length === 0) return; - - this.dimensions.forEach(dim => { - const p1Screen = this._worldToScreen(dim.p1); - const p2Screen = this._worldToScreen(dim.p2); - - if (!p1Screen || !p2Screen) return; // 在屏幕外 - - const cx = (p1Screen.x + p2Screen.x) / 2; - const cy = (p1Screen.y + p2Screen.y) / 2; - - ctx.save(); - ctx.strokeStyle = '#ffd93d'; - ctx.fillStyle = '#ffd93d'; - ctx.lineWidth = 2; - ctx.setLineDash([4, 4]); - ctx.beginPath(); - - // 从点1到点2 - ctx.moveTo(p1Screen.x, p1Screen.y); - ctx.lineTo(p2Screen.x, p2Screen.y); - - // 延伸线 - const dx = p2Screen.x - p1Screen.x; - const dy = p2Screen.y - p1Screen.y; - const ext = 20; - const len = Math.sqrt(dx * dx + dy * dy); - if (len > 0) { - const ux = dx / len; - const uy = dy / len; - ctx.moveTo(p1Screen.x - ux * ext, p1Screen.y - uy * ext); - ctx.lineTo(p1Screen.x + ux * ext, p1Screen.y + uy * ext); - ctx.moveTo(p2Screen.x - ux * ext, p2Screen.y - uy * ext); - ctx.lineTo(p2Screen.x + ux * ext, p2Screen.y + uy * ext); - } - - ctx.stroke(); - ctx.setLineDash([]); - - // 标注文本 - const text = `${dim.value.toFixed(2)} mm`; - ctx.font = 'bold 13px "Segoe UI","PingFang SC",sans-serif'; - const textWidth = ctx.measureText(text).width; - - // 背景 - const padX = 6, padY = 3; - ctx.fillStyle = 'rgba(0,0,0,0.75)'; - ctx.fillRect(cx - textWidth / 2 - padX, cy - 18, textWidth + padX * 2, 22); - - // 边框 - ctx.strokeStyle = '#ffd93d'; - ctx.lineWidth = 1; - ctx.strokeRect(cx - textWidth / 2 - padX, cy - 18, textWidth + padX * 2, 22); - - // 文本 - ctx.fillStyle = '#ffd93d'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText(text, cx, cy - 7); - - ctx.restore(); - }); - } - - _worldToScreen(worldPos) { - const vec = worldPos.clone().project(this.camera); - if (vec.z > 1) return null; // 在相机后面 - - return { - x: (vec.x * 0.5 + 0.5) * window.innerWidth, - y: (-vec.y * 0.5 + 0.5) * window.innerHeight, - z: vec.z - }; - } - - // ═══════════════════════════════════════ - // 剖切面 - // ═══════════════════════════════════════ - - toggleClipPlane() { - this.clipEnabled = !this.clipEnabled; - const btn = document.getElementById('btn-clip'); - btn.classList.toggle('active', this.clipEnabled); - - if (this.clipEnabled) { - this.clipBar.classList.add('show'); - this.clipPlane = new THREE.Plane(new THREE.Vector3(0, 0, -1), 0); - this._applyClipPlane(); - this._onClipSlider(); - } else { - this.clipBar.classList.remove('show'); - this.clipPlane = null; - this._applyClipPlane(); - } - } - - _applyClipPlane() { - this.allMeshes.forEach(mesh => { - if (mesh.material) { - const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material]; - materials.forEach(mat => { - mat.clipPlanes = this.clipPlane ? [this.clipPlane] : []; - mat.clipShadows = true; - mat.needsUpdate = true; - }); - } - }); - } - - _onClipSlider() { - if (!this.clipPlane) return; - const val = parseFloat(this.clipSlider.value) / 100; - const axis = this.clipAxisSel.value; - - // 计算模型中心 - const center = this.currentModel - ? new THREE.Box3().setFromObject(this.currentModel).getCenter(new THREE.Vector3()) - : new THREE.Vector3(0, 0, 0); - - let normal; - switch (axis) { - case 'x': normal = new THREE.Vector3(-1, 0, 0); break; - case 'y': normal = new THREE.Vector3(0, -1, 0); break; - case 'z': default: normal = new THREE.Vector3(0, 0, -1); break; - } - - const d = axis === 'x' ? center.x + val - : axis === 'y' ? center.y + val - : center.z + val; - const constant = normal.dot(new THREE.Vector3( - axis === 'x' ? d : center.x, - axis === 'y' ? d : center.y, - axis === 'z' ? d : center.z - )); - - this.clipPlane.normal.copy(normal); - this.clipPlane.constant = constant; - this.clipValSpan.textContent = val.toFixed(2); - - // 触发材质更新 - this.allMeshes.forEach(mesh => { - if (mesh.material) { - const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material]; - materials.forEach(mat => { mat.needsUpdate = true; }); - } - }); - - // 剖切面可视化 - this._updateClipPlaneVisual(normal, constant); - } - - _updateClipPlaneVisual(normal, constant) { - // 移除旧的剖切面可视化 - if (this.clipVisual) { - this.scene.remove(this.clipVisual); - this.clipVisual = null; - } - - if (!this.clipEnabled || !this.currentModel) return; - - const box = new THREE.Box3().setFromObject(this.currentModel); - const size = new THREE.Vector3(); - box.getSize(size); - const maxDim = Math.max(size.x, size.y, size.z) * 1.2; - - const planeGeo = new THREE.PlaneGeometry(maxDim, maxDim); - const planeMat = new THREE.MeshBasicMaterial({ - color: 0xff4444, - side: THREE.DoubleSide, - transparent: true, - opacity: 0.15, - depthWrite: false - }); - this.clipVisual = new THREE.Mesh(planeGeo, planeMat); - - // 定位剖切面:找到模型中心在平面上的投影点 - const center = box.getCenter(new THREE.Vector3()); - const signedDist = normal.dot(center) + constant; - this.clipVisual.position.copy(center).addScaledVector(normal, -signedDist); - - // 对齐法线 - const defaultNormal = new THREE.Vector3(0, 0, 1); - const quat = new THREE.Quaternion().setFromUnitVectors(defaultNormal, normal); - this.clipVisual.setRotationFromQuaternion(quat); - - this.clipVisual.name = '__clip_visual__'; - this.scene.add(this.clipVisual); - } - - // ═══════════════════════════════════════ - // 爆炸视图动画(GSAP) - // ═══════════════════════════════════════ - - toggleExplode() { - if (!this.currentModel || this.allMeshes.length === 0) return; - - this.isExploded = !this.isExploded; - const btn = document.getElementById('btn-explode'); - btn.classList.toggle('active', this.isExploded); - - if (this.isExploded) { - this._explodeParts(); - } else { - this._unExplodeParts(); - } - } - - _explodeParts() { - // 保存原始位置 - this.originalPositions.clear(); - this.allMeshes.forEach((mesh, i) => { - this.originalPositions.set(mesh, mesh.position.clone()); - }); - - // 计算模型中心和爆炸方向 - const box = new THREE.Box3().setFromObject(this.currentModel); - const center = new THREE.Vector3(); - box.getCenter(center); - - this.allMeshes.forEach((mesh, i) => { - // 从模型中心向外的方向 - let meshCenter = new THREE.Vector3(); - if (mesh.geometry) { - mesh.geometry.computeBoundingBox(); - if (mesh.geometry.boundingBox) { - meshCenter = mesh.geometry.boundingBox.getCenter(new THREE.Vector3()); - } - } - const worldCenter = meshCenter.clone().applyMatrix4(mesh.matrixWorld); - const dir = new THREE.Vector3().subVectors(worldCenter, center).normalize(); - - // 处理零向量 - if (dir.length() < 0.01) { - dir.set( - (Math.random() - 0.5) * 2, - (Math.random() - 0.5) * 2, - (Math.random() - 0.5) * 2 - ).normalize(); - } - - const distance = 1.5 + i * 0.3; - const target = mesh.position.clone().add(dir.multiplyScalar(distance)); - - // GSAP 动画 - gsap.to(mesh.position, { - x: target.x, - y: target.y, - z: target.z, - duration: 0.7, - delay: i * 0.03, - ease: 'power2.out' - }); - }); - } - - _unExplodeParts() { - this.allMeshes.forEach((mesh, i) => { - const original = this.originalPositions.get(mesh); - if (original) { - gsap.to(mesh.position, { - x: original.x, - y: original.y, - z: original.z, - duration: 0.5, - delay: i * 0.02, - ease: 'power2.in' - }); - } - }); - } - - // ═══════════════════════════════════════ - // 工具方法 - // ═══════════════════════════════════════ - - toggleWireframe() { - if (!this.currentModel) return; - this.currentModel.traverse((child) => { - if (child.isMesh && child.material) { - const mats = Array.isArray(child.material) ? child.material : [child.material]; - mats.forEach(mat => { - mat.wireframe = !mat.wireframe; - }); - } - }); - const btn = document.getElementById('btn-wireframe'); - // 检查第一个 mesh 是否已是线框模式 - const sampleMesh = this.allMeshes[0]; - if (sampleMesh) { - const mat = Array.isArray(sampleMesh.material) ? sampleMesh.material[0] : sampleMesh.material; - btn.classList.toggle('active', mat && mat.wireframe); - } - } - - resetCamera() { - if (this.currentModel) { - this._fitCameraToModel(this.currentModel); - } else { - this.camera.position.set(8, 6, 10); - this.controls.target.set(0, 0, 0); - this.controls.update(); - } - } - - setView(direction) { - const box = this.currentModel - ? new THREE.Box3().setFromObject(this.currentModel) - : new THREE.Box3(new THREE.Vector3(-1, -1, -1), new THREE.Vector3(1, 1, 1)); - const center = new THREE.Vector3(); - box.getCenter(center); - const size = new THREE.Vector3(); - box.getSize(size); - const dist = Math.max(size.x, size.y, size.z) * 1.8; - - this.controls.target.copy(center); - - switch (direction) { - case 'top': - this.camera.position.set(center.x, center.y + dist, center.z); - break; - case 'front': - this.camera.position.set(center.x, center.y, center.z + dist); - break; - case 'right': - this.camera.position.set(center.x + dist, center.y, center.z); - break; - case 'bottom': - this.camera.position.set(center.x, center.y - dist, center.z); - break; - case 'left': - this.camera.position.set(center.x - dist, center.y, center.z); - break; - case 'back': - this.camera.position.set(center.x, center.y, center.z - dist); - break; - default: - break; - } - this.controls.update(); - } - - clear(resetUI = true) { - if (this.currentModel) { - this.scene.remove(this.currentModel); - this.currentModel = null; - } - this.allMeshes = []; - this.originalPositions.clear(); - - // 清理剖切面可视化 - if (this.clipVisual) { - this.scene.remove(this.clipVisual); - this.clipVisual = null; - } - - // 清理测量 - this._clearMeasurePoints(); - - // 清理标注 - this.dimensions = []; - - // 清理爆炸视图 - if (this.isExploded) { - this.isExploded = false; - document.getElementById('btn-explode').classList.remove('active'); - } - - if (resetUI) { - this.brepLegend.style.display = 'none'; - this.dropZone.classList.remove('hidden'); - this._showInfo('等待加载模型'); - } - } - - _showInfo(msg) { - this.infoBar.textContent = `ViewDesignEngine 3D Viewer — ${msg}`; - } - - // ─────────────────────────────────────── - // 事件处理 - // ─────────────────────────────────────── - - _onResize() { - this.camera.aspect = window.innerWidth / window.innerHeight; - this.camera.updateProjectionMatrix(); - this.renderer.setSize(window.innerWidth, window.innerHeight); - } - - _onKeyDown(e) { - // 快捷键 - switch (e.key.toLowerCase()) { - case 'w': - this.toggleWireframe(); - break; - case 'r': - this.resetCamera(); - break; - case 'm': - this.toggleMeasure(); - break; - case 'c': - this.toggleClipPlane(); - break; - case 'e': - this.toggleExplode(); - break; - case 'escape': - if (this.isMeasuring) this.toggleMeasure(); - break; - default: - break; - } - } -} diff --git a/viewer/index.html b/viewer/index.html deleted file mode 100644 index 45a50d7..0000000 --- a/viewer/index.html +++ /dev/null @@ -1,430 +0,0 @@ - - - - - -ViewDesignEngine 3D Viewer - - - - - - - - - - - -
- 📦 - 拖放 .glb / .stl 文件到这里
- 或点击下方「打开文件」按钮 -
-
- - -
ViewDesignEngine 3D Viewer — 等待加载模型
- - -
-

🎨 B-Rep 面类型

-
-
- - -
-

📐 GD&T 几何公差

-
-

加载含 GD&T 元数据的模型后可显示

-
-
- - -
-

📏 测量工具

-
点击模型上两点进行测距
- - - -
- - -
- - - - 0.00 -
- - -
- - - - - - - - - - - -
- - - - - - - - - - - - - -