# 虚拟物体立方体碰撞检测功能设计方案
## 需求背景
当前项目中,检测动画用的移动物体是手工选择的。需要增加一种方式,按照路径编辑中物体的长宽高,动态生成一个立方体,用它来进行碰撞检测。
**需求来源**:`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. 测试完整流程