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 -
- - -
- - - - - - - - - - - -
- - - - - - - - - - - - - -