# 虚拟物体立方体碰撞检测功能设计方案 ## 需求背景 当前项目中,检测动画用的移动物体是手工选择的。需要增加一种方式,按照路径编辑中物体的长宽高,动态生成一个立方体,用它来进行碰撞检测。 **需求来源**:`doc/requirement/todo_features.md` - 2025/12/08 功能点3 > (功能)动画检测,增加使用模拟物流构件立方体选项 ## 方案选择 ### 方案A(已弃用):虚拟包围盒 - 仅使用虚拟包围盒进行碰撞检测 - **问题**:Clash Detective需要两个真实的ModelItem进行精确几何体验证,虚拟包围盒无法满足 ### 方案B(采用):创建真实几何体 - 在Navisworks中动态创建一个真实的立方体几何体 - 将其作为动画对象,复用现有的全部碰撞检测逻辑 - **优势**:完全复用现有代码,支持Clash Detective精确验证 ## 核心思路 **关键洞察**:虚拟物体本质上就是一个动态创建的立方体ModelItem,一旦创建完成,后续流程与手动选择物体**完全一致**。 ``` 用户选择"虚拟物体模式" ↓ 根据物体尺寸创建立方体几何体(NWD/NWC文件或内存几何体) ↓ 将创建的立方体作为 _animatedObject ↓ 【完全复用现有流程】 - SetupAnimation() - PrecomputeAnimationFrames() - StartAnimation() - CreateClashTestAfterAnimation() ``` ## 技术实现 ### 1. 创建立方体几何体的方法 Navisworks API有以下几种方式创建几何体: #### 方法1:使用临时NWD文件(推荐) ```csharp // 1. 创建临时NWD文件包含立方体 // 2. 追加到当前文档 // 3. 获取追加后的ModelItem引用 // 4. 动画结束后移除临时模型 ``` #### 方法2:使用Presenter API(如果可用) ```csharp // 使用 Autodesk.Navisworks.Api.Presenter 命名空间 // 创建临时几何体用于可视化 ``` #### 方法3:使用外部工具生成 ```csharp // 预先创建一个单位立方体NWC文件 // 运行时追加并缩放到目标尺寸 ``` ### 2. 实现方案详细设计 #### 2.1 创建 VirtualObjectManager 类 ```csharp /// /// 虚拟物体管理器 - 负责创建和管理虚拟物体几何体 /// public class VirtualObjectManager { private static VirtualObjectManager _instance; public static VirtualObjectManager Instance => _instance ?? (_instance = new VirtualObjectManager()); private ModelItem _virtualObjectModelItem; private string _tempFilePath; /// /// 创建虚拟物体立方体 /// /// 长度(米) /// 宽度(米) /// 高度(米) /// 初始位置 /// 创建的ModelItem public ModelItem CreateVirtualObject(double lengthMeters, double widthMeters, double heightMeters, Point3D position) { try { // 1. 清理之前的虚拟物体 RemoveVirtualObject(); // 2. 创建临时NWD文件包含立方体 _tempFilePath = CreateCubeNwdFile(lengthMeters, widthMeters, heightMeters); // 3. 追加到当前文档 var doc = Application.ActiveDocument; doc.AppendFile(_tempFilePath); // 4. 获取追加后的ModelItem(最后一个模型的根节点) _virtualObjectModelItem = GetLastAppendedModel(); // 5. 移动到指定位置 MoveToPosition(_virtualObjectModelItem, position); LogManager.Info($"虚拟物体创建成功: {lengthMeters:F1}m × {widthMeters:F1}m × {heightMeters:F1}m"); return _virtualObjectModelItem; } catch (Exception ex) { LogManager.Error($"创建虚拟物体失败: {ex.Message}"); throw; } } /// /// 移除虚拟物体 /// public void RemoveVirtualObject() { if (_virtualObjectModelItem != null) { // 从文档中移除模型 // ... _virtualObjectModelItem = null; } // 清理临时文件 if (!string.IsNullOrEmpty(_tempFilePath) && File.Exists(_tempFilePath)) { File.Delete(_tempFilePath); _tempFilePath = null; } } /// /// 获取当前虚拟物体 /// public ModelItem CurrentVirtualObject => _virtualObjectModelItem; } ``` #### 2.2 修改 AnimationControlViewModel ```csharp private void ExecuteGenerateAnimation() { try { ModelItem animatedObject; if (UseVirtualObject) { // 创建虚拟物体几何体 var startPosition = CurrentPathRoute.Points.First(); animatedObject = VirtualObjectManager.Instance.CreateVirtualObject( VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight, new Point3D(startPosition.X, startPosition.Y, startPosition.Z) ); LogManager.Info($"使用虚拟物体: {VirtualObjectLength:F1}m × {VirtualObjectWidth:F1}m × {VirtualObjectHeight:F1}m"); } else { animatedObject = SelectedAnimatedObject; } // 【以下完全复用现有逻辑】 var pathPoints = CurrentPathRoute.Points.Select(p => new Point3D(p.X, p.Y, p.Z)).ToList(); _pathAnimationManager.CreateAnimation( animatedObject, // 无论是手动选择的还是虚拟物体,都是真实的ModelItem pathPoints, AnimationDuration, CurrentPathRoute.Name, CurrentPathRoute.Id ); // ... 其余现有逻辑不变 ... } catch (Exception ex) { // ... } } ``` #### 2.3 动画结束后清理 ```csharp // 在动画完成后的处理中 private void OnAnimationCompleted() { // ... 现有的报告生成和Clash测试创建 ... // 如果使用了虚拟物体,询问用户是否保留 if (UseVirtualObject && VirtualObjectManager.Instance.CurrentVirtualObject != null) { // 可选:提示用户是否保留虚拟物体 // 或者自动移除 // VirtualObjectManager.Instance.RemoveVirtualObject(); } } ``` ### 3. 创建立方体NWD文件的技术细节 由于Navisworks API不直接支持创建几何体,可以采用以下方案: #### 方案A:预制单位立方体 + 缩放 1. 预先准备一个1m×1m×1m的立方体NWC文件(放在插件资源目录) 2. 运行时追加这个文件 3. 使用`Transform3D.CreateScale`缩放到目标尺寸 ```csharp private ModelItem CreateScaledCube(double length, double width, double height) { // 追加预制的单位立方体 var unitCubePath = Path.Combine(PluginResourcesPath, "unit_cube.nwc"); doc.AppendFile(unitCubePath); var cubeItem = GetLastAppendedModel(); // 缩放到目标尺寸 var scaleTransform = Transform3D.CreateScale(length, width, height); doc.Models.OverridePermanentTransform(new ModelItemCollection { cubeItem }, scaleTransform, true); return cubeItem; } ``` #### 方案B:使用FBX/OBJ中间格式 1. 运行时生成OBJ格式的立方体文件 2. 使用Navisworks的文件转换功能导入 ### 4. 关于预制立方体文件 需要准备一个`unit_cube.nwc`文件: - 尺寸:1m × 1m × 1m - 原点:底面中心在原点 - 材质:半透明(便于观察碰撞) 可以使用以下工具创建: 1. AutoCAD/Revit 导出 2. 3ds Max 导出 3. 简单的OBJ文件手动编写后转换 ## 修改文件清单 | 文件 | 修改类型 | 主要改动 | |------|----------|----------| | `VirtualObjectManager.cs` | **新增** | 虚拟物体几何体创建和管理 | | `AnimationControlView.xaml` | 修改 | 添加RadioButton选择组和虚拟物体尺寸显示 | | `AnimationControlViewModel.cs` | 修改 | 调用VirtualObjectManager创建虚拟物体 | | `LogisticsControlPanel.xaml.cs` | 修改 | 添加物体参数同步逻辑 | | `resources/unit_cube.nwc` | **新增** | 预制的单位立方体模型文件 | ## 关键优势 1. **完全复用现有代码**:创建真实ModelItem后,所有现有逻辑(动画、碰撞检测、Clash Detective测试、报告生成)无需任何修改 2. **真实的碰撞检测结果**:Clash Detective可以进行精确几何体验证 3. **最小改动原则**:只需添加创建几何体的逻辑,其他代码不变 ## 测试要点 1. **几何体创建** - 虚拟物体立方体正确创建 - 尺寸和位置正确 2. **碰撞检测** - 与手动选择物体的检测结果一致 - Clash Detective测试正常生成 3. **清理** - 动画结束后正确清理临时文件 - 用户可选择是否保留虚拟物体 ## 实现步骤 1. 准备预制的单位立方体NWC文件 2. 创建VirtualObjectManager类 3. 修改AnimationControlViewModel调用VirtualObjectManager 4. 测试完整流程