用DockPanePlugin代替AddInPlugin,控件UI使用WPF,属性管理和路径设置两个功能的迁移

This commit is contained in:
tian 2025-08-14 09:30:13 +08:00
parent 4dc188f857
commit a625a498a1
38 changed files with 6362 additions and 809 deletions

View File

@ -0,0 +1,516 @@
# Design Document
## Overview
本设计文档详细描述了将Navisworks物流路径规划插件从AddInPlugin架构迁移到DockPanePlugin架构的技术实现方案。基于Navisworks 2026 API文档中的DockPanePlugin示例代码我们将重新设计插件架构以提供可停靠的用户界面面板并利用WPF技术栈实现更现代化的用户体验。
## Architecture
### 插件架构变更
#### 当前架构 (AddInPlugin)
```csharp
[PluginAttribute("Basic", "Tian", ToolTip = "Transport Plugin", DisplayName = "Transport Plugin")]
[AddInPlugin(AddInLocation.AddIn)]
public class Main : AddInPlugin
{
public override int Execute(params string[] parameters)
{
// 显示模态对话框
ShowDockPane();
return 0;
}
}
```
#### 目标架构 (DockPanePlugin)
```csharp
[Plugin("NavisworksTransport.Main", "Tian",
DisplayName = "物流路径规划",
ToolTip = "物流路径规划和动画仿真插件")]
[DockPanePlugin(420, 700)] // 宽度420, 高度700
[Strings("NavisworksTransport.Tian.name")]
public class Main : DockPanePlugin
{
public override Control CreateControlPane()
{
// 创建WPF用户控件并用ElementHost托管
var wpfControl = new LogisticsControlPanel();
var elementHost = new ElementHost
{
Dock = DockStyle.Fill,
Child = wpfControl
};
return elementHost;
}
public override void DestroyControlPane(Control pane)
{
// 清理资源和事件订阅
if (pane is ElementHost host && host.Child is LogisticsControlPanel wpfControl)
{
wpfControl.Cleanup();
}
pane.Dispose();
}
}
```
### WPF用户控件架构
#### 主控件结构
```
LogisticsControlPanel (UserControl)
├── MainTabControl (TabControl)
│ ├── ModelSettingsTab (TabItem)
│ │ └── ModelSettingsView (UserControl)
│ ├── PathEditingTab (TabItem)
│ │ └── PathEditingView (UserControl)
│ ├── AnimationControlTab (TabItem)
│ │ └── AnimationControlView (UserControl)
│ └── SystemManagementTab (TabItem)
│ └── SystemManagementView (UserControl)
└── BottomPanel (StackPanel)
├── HelpButton (Button)
├── AboutButton (Button)
└── CloseButton (Button)
```
## Components and Interfaces
### 1. 主插件类 (Main)
**职责:**
- 实现DockPanePlugin接口
- 管理插件生命周期
- 创建和销毁控件面板
**关键方法:**
- `CreateControlPane()`: 创建ElementHost并托管WPF控件
- `DestroyControlPane(Control pane)`: 清理资源和释放控件
### 2. WPF主控件 (LogisticsControlPanel)
**职责:**
- 作为所有功能的容器
- 管理Tab页面切换
- 处理全局事件和状态
**关键属性和方法:**
```csharp
public partial class LogisticsControlPanel : UserControl
{
// 子视图引用
private ModelSettingsView _modelSettingsView;
private PathEditingView _pathEditingView;
private AnimationControlView _animationControlView;
private SystemManagementView _systemManagementView;
// 管理器实例
private PathPlanningManager _pathPlanningManager;
private static bool _isSessionInitialized = false;
public LogisticsControlPanel()
{
InitializeComponent();
InitializeSession();
InitializeViews();
SubscribeToEvents();
}
public void Cleanup()
{
UnsubscribeFromEvents();
CleanupManagers();
}
}
```
### 3. 子视图控件
#### ModelSettingsView
- 类别属性设置
- 物流模型列表
- 可见性控制
- 统计信息显示
#### PathEditingView
- 路径列表管理
- 当前路径编辑
- 路径文件管理
#### AnimationControlView
- 动画参数设置
- 播放控制
- 碰撞检测
#### SystemManagementView
- 模型分层拆分
- 日志管理
- 插件设置
- 系统信息
### 4. MVVM架构支持
#### ViewModel基类
```csharp
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
```
#### 主ViewModel
```csharp
public class LogisticsControlViewModel : ViewModelBase
{
private string _selectedModelsText;
private string _instructionText;
private ObservableCollection<LogisticsModel> _logisticsModels;
public string SelectedModelsText
{
get => _selectedModelsText;
set => SetProperty(ref _selectedModelsText, value);
}
public string InstructionText
{
get => _instructionText;
set => SetProperty(ref _instructionText, value);
}
public ObservableCollection<LogisticsModel> LogisticsModels
{
get => _logisticsModels;
set => SetProperty(ref _logisticsModels, value);
}
// Commands
public ICommand RefreshCommand { get; }
public ICommand ClearSelectionCommand { get; }
public ICommand ShowAllCommand { get; }
public ICommand HideAllCommand { get; }
}
```
## Data Models
### 1. 数据传输对象
#### LogisticsModel
```csharp
public class LogisticsModel : INotifyPropertyChanged
{
private string _name;
private string _category;
private string _attributes;
private bool _isVisible;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
public string Category
{
get => _category;
set => SetProperty(ref _category, value);
}
public string Attributes
{
get => _attributes;
set => SetProperty(ref _attributes, value);
}
public bool IsVisible
{
get => _isVisible;
set => SetProperty(ref _isVisible, value);
}
public ModelItem NavisworksItem { get; set; }
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
```
#### PathRoute
```csharp
public class PathRoute : INotifyPropertyChanged
{
private string _name;
private ObservableCollection<PathPoint> _points;
private bool _isActive;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
public ObservableCollection<PathPoint> Points
{
get => _points;
set => SetProperty(ref _points, value);
}
public bool IsActive
{
get => _isActive;
set => SetProperty(ref _isActive, value);
}
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
```
### 2. 业务逻辑管理器
现有的管理器类保持不变但需要适配WPF的事件模型
- `PathPlanningManager`
- `PathDataManager`
- `VisibilityManager`
- `ModelSplitterManager`
- `LogisticsAnimationManager`
## Error Handling
### 1. WPF异常处理
```csharp
public partial class LogisticsControlPanel : UserControl
{
public LogisticsControlPanel()
{
InitializeComponent();
// 订阅WPF异常事件
Dispatcher.UnhandledException += OnDispatcherUnhandledException;
}
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
LogManager.Error($"[WPF异常] {e.Exception.Message}");
LogManager.Error($"[WPF异常] 堆栈信息: {e.Exception.StackTrace}");
// 标记异常已处理,避免程序崩溃
e.Handled = true;
// 显示用户友好的错误信息
MessageBox.Show($"界面操作发生错误: {e.Exception.Message}",
"错误", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
```
### 2. 线程安全处理
```csharp
public class ThreadSafeHelper
{
public static void InvokeOnUIThread(Action action)
{
if (Application.Current?.Dispatcher?.CheckAccess() == true)
{
action();
}
else
{
Application.Current?.Dispatcher?.Invoke(action);
}
}
public static T InvokeOnUIThread<T>(Func<T> func)
{
if (Application.Current?.Dispatcher?.CheckAccess() == true)
{
return func();
}
else
{
return Application.Current.Dispatcher.Invoke(func);
}
}
}
```
## Testing Strategy
### 1. 单元测试
#### 插件生命周期测试
```csharp
[TestClass]
public class MainPluginTests
{
[TestMethod]
public void CreateControlPane_ShouldReturnElementHost()
{
// Arrange
var plugin = new Main();
// Act
var control = plugin.CreateControlPane();
// Assert
Assert.IsInstanceOfType(control, typeof(ElementHost));
Assert.IsNotNull(((ElementHost)control).Child);
Assert.IsInstanceOfType(((ElementHost)control).Child, typeof(LogisticsControlPanel));
}
[TestMethod]
public void DestroyControlPane_ShouldDisposeControl()
{
// Arrange
var plugin = new Main();
var control = plugin.CreateControlPane();
// Act
plugin.DestroyControlPane(control);
// Assert
Assert.IsTrue(control.IsDisposed);
}
}
```
#### WPF控件测试
```csharp
[TestClass]
public class LogisticsControlPanelTests
{
[TestMethod]
public void Constructor_ShouldInitializeAllViews()
{
// Arrange & Act
var control = new LogisticsControlPanel();
// Assert
Assert.IsNotNull(control.FindName("MainTabControl"));
Assert.IsNotNull(control.FindName("ModelSettingsTab"));
Assert.IsNotNull(control.FindName("PathEditingTab"));
Assert.IsNotNull(control.FindName("AnimationControlTab"));
Assert.IsNotNull(control.FindName("SystemManagementTab"));
}
}
```
### 2. 集成测试
#### Navisworks API集成测试
```csharp
[TestClass]
public class NavisworksIntegrationTests
{
[TestMethod]
public void SelectionChanged_ShouldUpdateViewModel()
{
// 需要在Navisworks环境中运行
// 测试选择变更事件是否正确更新WPF界面
}
[TestMethod]
public void PathPlanning_ShouldCreateVisualPath()
{
// 测试路径规划功能是否在WPF界面中正确显示
}
}
```
### 3. 用户界面测试
#### WPF自动化测试
```csharp
[TestClass]
public class UIAutomationTests
{
[TestMethod]
public void TabSwitching_ShouldWorkCorrectly()
{
// 使用WPF测试框架测试Tab切换功能
}
[TestMethod]
public void DataBinding_ShouldUpdateUI()
{
// 测试数据绑定是否正确更新界面
}
}
```
## Performance Considerations
### 1. WPF渲染优化
- 使用虚拟化ListView和TreeView处理大量数据
- 实现延迟加载避免界面卡顿
- 使用异步操作处理耗时任务
### 2. 内存管理
- 正确实现IDisposable接口
- 及时取消事件订阅避免内存泄漏
- 使用WeakReference处理长期引用
### 3. 线程优化
- 将Navisworks API调用保持在主线程
- 使用BackgroundWorker处理后台任务
- 合理使用Dispatcher.BeginInvoke避免界面阻塞
## Migration Strategy
### 阶段1基础架构迁移
1. 创建DockPanePlugin基础结构
2. 实现ElementHost托管机制
3. 创建基本的WPF用户控件框架
### 阶段2功能模块迁移
1. 迁移模型设置功能到WPF
2. 迁移路径编辑功能到WPF
3. 迁移动画控制功能到WPF
4. 迁移系统管理功能到WPF
### 阶段3优化和测试
1. 实现MVVM模式和数据绑定
2. 优化性能和用户体验
3. 完善错误处理和异常管理
4. 进行全面测试和调试
### 阶段4部署和验证
1. 更新部署配置
2. 验证插件注册和加载
3. 进行用户验收测试
4. 文档更新和培训

View File

@ -0,0 +1,96 @@
# Requirements Document
## Introduction
本需求文档定义了将现有的Navisworks物流路径规划插件从AddInPlugin架构迁移到DockPanePlugin架构的功能需求。根据技术方案文档DockPanePlugin能够提供更好的用户体验允许创建可停靠的面板并支持WPF技术栈以实现更丰富的交互式导航控件。
## Requirements
### Requirement 1
**User Story:** 作为插件开发者我希望将现有的AddInPlugin改为DockPanePlugin以便提供可停靠的用户界面面板。
#### Acceptance Criteria
1. WHEN 插件加载时 THEN 系统应创建一个可停靠的面板而不是模态对话框
2. WHEN 用户关闭面板时 THEN 面板应能够被重新打开而不重启Navisworks
3. WHEN 面板显示时 THEN 用户应能够将面板停靠到Navisworks界面的不同位置
4. WHEN 面板创建时 THEN 系统应正确实现CreateControlPane和DestroyControlPane方法
### Requirement 2
**User Story:** 作为用户我希望插件界面使用WPF技术以便获得更现代化和响应式的用户体验。
#### Acceptance Criteria
1. WHEN 面板显示时 THEN 界面应使用WPF而不是Windows Forms
2. WHEN 用户与界面交互时 THEN 界面应提供流畅的响应和现代化的视觉效果
3. WHEN 面板调整大小时 THEN WPF控件应正确响应布局变化
4. WHEN 面板内容更新时 THEN WPF数据绑定应正确工作
### Requirement 3
**User Story:** 作为开发者,我希望保持现有的所有功能,以便用户在迁移后不会丢失任何特性。
#### Acceptance Criteria
1. WHEN 迁移完成时 THEN 所有现有的类别设置功能应正常工作
2. WHEN 迁移完成时 THEN 所有现有的路径编辑功能应正常工作
3. WHEN 迁移完成时 THEN 所有现有的动画控制功能应正常工作
4. WHEN 迁移完成时 THEN 所有现有的系统管理功能应正常工作
5. WHEN 迁移完成时 THEN 所有事件处理和数据管理逻辑应保持不变
### Requirement 4
**User Story:** 作为开发者我希望正确处理DockPanePlugin的生命周期管理以便避免内存泄漏和资源冲突。
#### Acceptance Criteria
1. WHEN CreateControlPane被调用时 THEN 系统应正确创建并返回WPF用户控件
2. WHEN DestroyControlPane被调用时 THEN 系统应正确释放所有资源和事件订阅
3. WHEN 面板重复创建时 THEN 系统应避免重复订阅事件或创建重复实例
4. WHEN Navisworks关闭时 THEN 所有插件资源应被正确清理
### Requirement 5
**User Story:** 作为开发者我希望利用ElementHost控件来托管WPF内容以便在Navisworks的.NET Framework环境中正确运行。
#### Acceptance Criteria
1. WHEN 创建面板时 THEN 系统应使用ElementHost控件托管WPF用户控件
2. WHEN WPF控件需要与Navisworks API交互时 THEN ElementHost应正确处理线程调用
3. WHEN 面板大小改变时 THEN ElementHost应正确调整WPF内容的大小
4. WHEN 处理用户输入时 THEN ElementHost应正确转发鼠标和键盘事件
### Requirement 6
**User Story:** 作为开发者我希望更新插件属性和注册方式以便正确注册为DockPanePlugin。
#### Acceptance Criteria
1. WHEN 插件编译时 THEN 系统应使用正确的DockPanePlugin属性而不是AddInPlugin属性
2. WHEN 插件加载时 THEN Navisworks应将插件识别为可停靠面板插件
3. WHEN 插件在Navisworks中显示时 THEN 插件应出现在正确的菜单位置
4. WHEN 插件属性设置时 THEN DisplayName、ToolTip等属性应正确显示
### Requirement 7
**User Story:** 作为开发者我希望创建合适的WPF用户控件结构以便替换现有的Windows Forms界面。
#### Acceptance Criteria
1. WHEN 创建WPF控件时 THEN 系统应创建包含所有现有功能的用户控件
2. WHEN WPF控件布局时 THEN 应使用适当的WPF布局容器如Grid、StackPanel等
3. WHEN WPF控件绑定数据时 THEN 应使用MVVM模式或适当的数据绑定机制
4. WHEN WPF控件处理事件时 THEN 应正确处理WPF事件模型
### Requirement 8
**User Story:** 作为开发者我希望确保API调用的兼容性以便所有Navisworks API调用在新架构下正常工作。
#### Acceptance Criteria
1. WHEN 从WPF控件调用Navisworks API时 THEN 所有API调用应在正确的线程上执行
2. WHEN 处理Navisworks事件时 THEN 事件处理应正确更新WPF界面
3. WHEN 访问Document和Selection对象时 THEN 应正确处理跨线程访问
4. WHEN 使用Graphics类绘制时 THEN 绘制操作应与WPF界面正确协调

View File

@ -0,0 +1,253 @@
# Implementation Plan
- [x] 1. 创建DockPanePlugin基础架构
- 修改Main类继承DockPanePlugin而不是AddInPlugin
- 更新插件属性为[Plugin]和[DockPanePlugin]
- 实现CreateControlPane()和DestroyControlPane()方法
- 创建ElementHost托管机制
- _Requirements: 1.1, 1.2, 1.3, 1.4_
- [ ] 2. 创建WPF用户控件框架
- [x] 2.1 创建主WPF用户控件LogisticsControlPanel
- 创建LogisticsControlPanel.xaml和.xaml.cs文件
- 实现基本的TabControl布局结构
- 添加底部操作按钮面板
- 实现Cleanup()方法用于资源清理
- _Requirements: 2.1, 2.2, 2.3_
- [x] 2.2 创建MVVM基础架构
- 实现ViewModelBase基类支持INotifyPropertyChanged
- 创建LogisticsControlViewModel主视图模型
- 实现ThreadSafeHelper线程安全辅助类
- 创建数据模型类LogisticsModel和PathRoute
- _Requirements: 2.2, 2.4, 5.2_
- [x] 2.3 创建子视图用户控件
- 创建ModelSettingsView.xaml用户控件
- 创建PathEditingView.xaml用户控件
- 创建AnimationControlView.xaml用户控件
- 创建SystemManagementView.xaml用户控件
- _Requirements: 2.1, 2.3, 7.1, 7.2_
- [x] 3. 迁移模型设置功能到WPF
- [x] 3.1 实现类别属性设置界面
- 将类别下拉框转换为WPF ComboBox
- 实现数据绑定连接CategoryAttributeManager
- 添加选择提示和状态显示标签
- 测试类别选择功能
- _Requirements: 3.1, 3.2, 7.3, 8.2_
- [x] 3.2 实现物流模型列表界面
- 将ListView转换为WPF ListView
- 实现ObservableCollection数据绑定
- 添加模型属性编辑功能
- 实现选择同步机制
- _Requirements: 3.1, 3.2, 7.3, 8.1_
- [x] 3.3 实现可见性控制界面
- 转换可见性控制按钮为WPF Button
- 实现Command模式处理按钮点击
- 连接VisibilityManager业务逻辑
- 添加统计信息显示
- _Requirements: 3.1, 3.2, 7.4, 8.2_
- [ ] 4. 迁移路径编辑功能到WPF
- [x] 4.1 实现路径列表管理界面
- 转换路径列表ListView为WPF版本
- 实现路径创建、删除、重命名功能
- 连接PathPlanningManager业务逻辑
- 添加路径状态显示
- _Requirements: 3.3, 7.3, 8.1, 8.2_
- [x] 4.2 实现当前路径编辑界面
- 转换路径点列表为WPF ListView
- 实现路径点添加、删除、编辑功能
- 添加3D交互支持
- 实现路径可视化更新
- _Requirements: 3.3, 7.4, 8.1, 8.3_
- [x] 4.3 实现路径文件管理界面
- 添加路径导入导出按钮
- 实现文件对话框集成
- 连接PathDataManager文件操作
- 添加操作状态反馈
- _Requirements: 3.3, 8.1_
- [ ] 5. 迁移动画控制功能到WPF
- [x] 5.1 实现动画参数设置界面
- 转换动画参数控件为WPF版本
- 实现数据绑定和验证
- 连接LogisticsAnimationManager
- 添加参数预览功能
- _Requirements: 3.4, 7.3, 8.1_
- [x] 5.2 实现播放控制界面
- 转换播放控制按钮为WPF版本
- 实现进度条和状态显示
- 添加播放速度控制
- 连接动画播放逻辑
- _Requirements: 3.4, 7.4, 8.2_
- [x] 5.3 实现碰撞检测界面
- 转换碰撞检测控件为WPF版本
- 连接ClashDetectiveIntegration
- 实现碰撞结果显示
- 添加碰撞报告功能
- _Requirements: 3.4, 8.1, 8.2_
- [ ] 6. 迁移系统管理功能到WPF
- [x] 6.1 实现模型分层拆分界面
- 转换ModelSplitterDialog为WPF版本
- 实现楼层选择和属性筛选
- 连接ModelSplitterManager业务逻辑
- 添加拆分进度显示
- _Requirements: 3.5, 7.1, 8.1_
- [x] 6.2 实现日志管理界面
- 创建日志查看和管理控件
- 实现日志级别筛选
- 添加日志导出功能
- 连接LogManager
- _Requirements: 3.5, 7.3_
- [x] 6.3 实现插件设置界面
- 创建设置选项控件
- 实现设置保存和加载
- 添加设置验证
- 实现设置重置功能
- _Requirements: 3.5, 7.3_
- [ ] 7. 实现生命周期管理和资源清理
- [ ] 7.1 实现插件生命周期管理
- 完善CreateControlPane()方法
- 实现DestroyControlPane()资源清理
- 添加重复创建保护机制
- 测试面板创建和销毁
- _Requirements: 4.1, 4.2, 4.3, 4.4_
- [ ] 7.2 实现事件订阅管理
- 创建事件订阅管理器
- 实现自动取消订阅机制
- 添加事件处理异常保护
- 测试事件生命周期
- _Requirements: 4.3, 4.4, 8.2_
- [ ] 7.3 实现ElementHost集成
- 完善ElementHost配置
- 实现WPF与WinForms的交互
- 添加大小调整处理
- 测试跨技术栈事件传递
- _Requirements: 5.1, 5.2, 5.3, 5.4_
- [ ] 8. 实现错误处理和线程安全
- [ ] 8.1 实现WPF异常处理
- 添加Dispatcher.UnhandledException处理
- 实现用户友好的错误提示
- 连接GlobalExceptionHandler
- 测试异常恢复机制
- _Requirements: 4.4, 8.1, 8.2_
- [ ] 8.2 实现线程安全机制
- 完善ThreadSafeHelper实现
- 确保Navisworks API调用线程安全
- 实现UI更新线程调度
- 测试跨线程操作
- _Requirements: 5.2, 8.1, 8.3, 8.4_
- [ ] 8.3 实现内存管理
- 添加IDisposable实现
- 实现WeakReference长期引用
- 添加内存泄漏检测
- 优化大对象处理
- _Requirements: 4.4, 7.3_
- [ ] 9. 更新插件注册和部署配置
- [ ] 9.1 更新插件属性和注册
- 修改Plugin属性配置
- 更新DisplayName和ToolTip
- 创建本地化字符串文件
- 测试插件识别和加载
- _Requirements: 6.1, 6.2, 6.3, 6.4_
- [ ] 9.2 更新项目配置
- 添加WPF相关引用
- 更新编译配置
- 修改输出路径设置
- 测试编译和部署
- _Requirements: 6.1, 6.4_
- [ ] 9.3 创建本地化支持
- 创建字符串资源文件
- 实现多语言支持
- 添加本地化测试
- 验证字符串显示
- _Requirements: 6.4_
- [ ] 10. 进行全面测试和优化
- [ ] 10.1 创建单元测试
- 编写插件生命周期测试
- 创建WPF控件测试
- 实现ViewModel测试
- 添加业务逻辑测试
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_
- [ ] 10.2 进行集成测试
- 测试Navisworks API集成
- 验证事件处理机制
- 测试数据绑定功能
- 验证线程安全性
- _Requirements: 8.1, 8.2, 8.3, 8.4_
- [ ] 10.3 进行性能优化
- 优化WPF渲染性能
- 实现虚拟化列表
- 添加延迟加载机制
- 优化内存使用
- _Requirements: 2.3, 7.3_
- [ ] 10.4 进行用户验收测试
- 验证所有现有功能正常工作
- 测试新的可停靠面板体验
- 收集用户反馈
- 修复发现的问题
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_

View File

@ -1,5 +1,24 @@
# NavisworksTransport 变更日志 # NavisworksTransport 变更日志
## [0.3.0] - 2025-08-13
### 重大更新
- **建立了Navisworks2026新开发分支**
- 基于.NET Framework 4.8
- 用DockPanePlugin代替了AddInPlugin
- 控件的用户界面使用WPFWindows Presentation Foundation构建
- 实现了属性管理和路径设置两个功能的迁移(还剩下动画仿真和系统管理)
### 功能优化
- **物流属性管理**
- 增加了限宽属性
- 支持设置多个物流元素
- **可见性控制**
- 用一个checkbox代替多个按钮
- 优化了物流元素的可见性显示
## [0.2.0] - 2025-07-21 ## [0.2.0] - 2025-07-21
### 重大功能突破 🎯 ### 重大功能突破 🎯

View File

@ -1,78 +0,0 @@
# NavisworksTransport 项目说明
## 概述
NavisworksTransport 是一个针对 Autodesk Navisworks Manage 2026 开发的插件,旨在简化移动模型沿确定路径进行物理碰撞或干涉检测的流程。通过自动化 Animator 动画创建、Clash Detective 碰撞测试配置与运行,并提供直观的图形化碰撞结果显示,本插件大大提高工作效率,为用户提供一个快速验证施工物流和设备移动可行性的工具。
## 项目结构
```
NavisworksTransportPlugin/
├── src/ # 源代码目录
│ ├── Core/ # 核心业务逻辑
│ │ ├── MainPlugin.cs # 插件主类和用户界面
│ │ ├── PathPlanningManager.cs # 路径规划核心业务逻辑
│ │ ├── CategoryAttributeManager.cs # COM API封装和属性管理
│ │ ├── PathPointRenderPlugin.cs # 3D路径点渲染插件
│ │ ├── PathClickToolPlugin.cs # 3D视图路径点点击工具
│ │ ├── LogisticsAnimationManager.cs # 物流动画管理器
│ │ ├── ClashDetectiveIntegration.cs # 碰撞检测集成管理器
│ │ └── ... # 其他核心模块
│ ├── UI/ # 用户界面
│ │ └── Forms/ # Windows Forms 窗体
│ ├── Utils/ # 工具类
│ └── Legacy/ # 旧版本兼容代码
├── tests/ # 测试代码目录
│ └── ... # 测试文件
├── Properties/ # 项目属性文件
│ └── AssemblyInfo.cs # 程序集信息
└── ...
```
## 核心功能
1. **类别属性分配**: 为模型项目添加物流类别属性(门、电梯、楼梯、通道、障碍物等)
2. **模型分层转换和可见性控制**: 控制模型的显示和隐藏
3. **3D路径规划**: 在3D视图中创建和编辑物流路径
4. **A*路径规划算法**: 计算最优路径
5. **动态碰撞检测**: 集成 Clash Detective 进行动态碰撞检测
6. **动画和时间线集成**: 创建基于路径点的对象动画
7. **导航地图输出**: 将路径规划结果导出为图片和视频
8. **模型切分及导出**: 根据楼层或属性切分模型并导出为独立文件
## 技术架构
- **开发语言**: C#
- **目标平台**: .NET Framework 4.8
- **API**: Autodesk Navisworks API (包括 COM API 和 .NET API)
- **UI框架**: Windows Forms
## 系统要求
- Windows 10 或更高版本
- Navisworks Manage 2026
- .NET Framework 4.8
## API 参考
本项目包含 Navisworks 2026 的 API 文档和示例代码,位于 `doc/api` 目录下:
- **COM API**:
- 文档: `doc/api/COM/documentation`
- 示例: `doc/api/COM/examples`
- **.NET API**:
- 文档: `doc/api/NET/documentation`
- 示例: `doc/api/NET/examples`
这些资源对于理解和使用 Navisworks API 进行开发非常有帮助。
## 设计方案
针对 Navisworks 2026 的详细设计方案文档位于 `doc/design/2026` 目录下:
- [Navisworks 物流路径规划方案](doc/design/2026/Navisworks%20物流路径规划方案_.md)
- [Navisworks自动路径规划实现方案](doc/design/2026/Navisworks自动路径规划实现方案_.md)
## 开发与构建
1. **构建**: 使用 Visual Studio 打开 `NavisworksTransport.sln` 解决方案文件并编译项目。
2. **安装**: 编译后插件会自动安装到 Navisworks 插件目录。
## 使用说明
1. 在 Navisworks 中选择要设置属性的模型项目
2. 点击 "附加模块" 选项卡中的 "Transport Plugin"
3. 在弹出窗口中进行类别设置、路径编辑、动画控制等操作
## 开发文档
- [开发任务文档](doc/working/类别属性功能开发任务.md)
- [需求文档](doc/requirement/user_requiement.md)

View File

@ -19,7 +19,7 @@
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Plugins\NavisworksTransportPlugin\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NAVISWORKS_2026</DefineConstants> <DefineConstants>DEBUG;TRACE;NAVISWORKS_2026</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
@ -83,11 +83,13 @@
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
<Reference Include="System.Xaml" /> <Reference Include="System.Xaml" />
<Reference Include="WindowsFormsIntegration" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<!-- Core - Main Plugin Files --> <!-- Core - Main Plugin Files -->
<Compile Include="src\Core\MainPlugin.cs" /> <Compile Include="src\Core\MainPlugin.cs" />
<Compile Include="src\Core\TestPlugin.cs" />
<Compile Include="src\Core\PathClickToolPlugin.cs" /> <Compile Include="src\Core\PathClickToolPlugin.cs" />
<Compile Include="src\Core\PathPointRenderPlugin.cs" /> <Compile Include="src\Core\PathPointRenderPlugin.cs" />
@ -99,7 +101,6 @@
<Compile Include="src\Core\VisibilityManager.cs" /> <Compile Include="src\Core\VisibilityManager.cs" />
<!-- Core - Animation System --> <!-- Core - Animation System -->
<Compile Include="src\Core\Animation\AnimationController.cs" />
<Compile Include="src\Core\Animation\LogisticsAnimationManager.cs" /> <Compile Include="src\Core\Animation\LogisticsAnimationManager.cs" />
<Compile Include="src\Core\Animation\TimeLinerIntegrationManager.cs" /> <Compile Include="src\Core\Animation\TimeLinerIntegrationManager.cs" />
@ -118,6 +119,31 @@
<Compile Include="src\UI\Forms\ModelSplitterDialog.cs"> <Compile Include="src\UI\Forms\ModelSplitterDialog.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
<Compile Include="src\UI\Forms\SimpleDockPaneControl.cs">
<SubType>UserControl</SubType>
</Compile>
<!-- UI - WPF -->
<Compile Include="src\UI\WPF\LogisticsControlPanel.xaml.cs">
<DependentUpon>LogisticsControlPanel.xaml</DependentUpon>
</Compile>
<Compile Include="src\UI\WPF\Views\ModelSettingsView.xaml.cs">
<DependentUpon>ModelSettingsView.xaml</DependentUpon>
</Compile>
<Compile Include="src\UI\WPF\Views\PathEditingView.xaml.cs">
<DependentUpon>PathEditingView.xaml</DependentUpon>
</Compile>
<Compile Include="src\UI\WPF\Views\AnimationControlView.xaml.cs">
<DependentUpon>AnimationControlView.xaml</DependentUpon>
</Compile>
<Compile Include="src\UI\WPF\Views\SystemManagementView.xaml.cs">
<DependentUpon>SystemManagementView.xaml</DependentUpon>
</Compile>
<Compile Include="src\UI\WPF\ViewModels\ViewModelBase.cs" />
<Compile Include="src\UI\WPF\ViewModels\LogisticsControlViewModel.cs" />
<Compile Include="src\UI\WPF\Commands\RelayCommand.cs" />
<Compile Include="src\UI\WPF\Models\LogisticsModel.cs" />
<Compile Include="src\UI\WPF\Models\PathRouteViewModel.cs" />
<!-- Utils --> <!-- Utils -->
<Compile Include="src\Utils\CoordinateConverter.cs" /> <Compile Include="src\Utils\CoordinateConverter.cs" />
@ -130,11 +156,44 @@
<Compile Include="src\Legacy\PathAnimationManager.cs" /> <Compile Include="src\Legacy\PathAnimationManager.cs" />
<!-- Tests --> <!-- Tests -->
<Compile Include="tests\AnimationSystemTester.cs" />
<!-- Assembly Info --> <!-- Assembly Info -->
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<!-- WPF XAML Files -->
<Page Include="src\UI\WPF\LogisticsControlPanel.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="src\UI\WPF\Views\ModelSettingsView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="src\UI\WPF\Views\PathEditingView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="src\UI\WPF\Views\AnimationControlView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="src\UI\WPF\Views\SystemManagementView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WinFX\3.0\Microsoft.WinFX.targets" Condition="Exists('$(MSBuildExtensionsPath)\Microsoft\WinFX\3.0\Microsoft.WinFX.targets')" />
<ItemGroup>
<!-- Localization Files -->
<None Include="src\Resources\NavisworksTransport.Tian.name.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>NavisworksTransport.Tian.name.txt</Link>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

150
QWEN.md Normal file
View File

@ -0,0 +1,150 @@
# NavisworksTransport 项目上下文 (QWEN.md)
## 项目概述
NavisworksTransport 是一个针对 Autodesk Navisworks Manage 2026 开发的插件。其核心目标是简化在 Navisworks 环境中进行物流路径规划、动画仿真和碰撞检测的工作流程。
该插件旨在通过自动化 Animator 动画创建、Clash Detective 碰撞测试配置与运行,并提供直观的图形化碰撞结果显示,大大提高工作效率,为用户提供一个快速验证施工物流和设备移动可行性的工具。
### 核心功能
1. **类别属性分配**
* 为模型项目添加预定义的物流类别属性(门、电梯、楼梯、通道、障碍物、装卸区、停车位、检查点)。
* 支持批量处理和用户友好的界面(按钮式对话框)。
* 通过 Navisworks COM API 确保属性正确添加。
2. **路径规划与编辑**
* 允许用户在3D视图中交互式地定义移动对象的路径点。
* 管理多条路径,支持路径的创建、编辑、删除、导入、导出和历史记录。
* 提供路径可视化3D标记和基础验证如点数、长度
3. **动画仿真**
* 集成 Navisworks Timeliner API根据规划的路径自动创建对象动画。
* 提供动画控制界面,允许用户设置播放速度、持续时间、帧率、循环等参数。
4. **碰撞检测**
* 集成 Navisworks Clash API自动配置并运行基于路径动画的动态碰撞测试。
* 提供碰撞检测状态和摘要信息。
5. **可见性控制**
* 提供模型分层拆分和仅显示物流相关元素的功能,以优化视图和性能。
6. **用户界面**
* 通过 Navisworks 的 Ribbon 界面添加自定义按钮启动插件。
* 提供一个停靠窗格 (DockPane) 作为主控制面板,使用 WPF 构建。
* 控制面板包含多个 Tab 页:类别设置、路径编辑、检测动画、系统管理。
## 技术架构
### 编程语言与框架
* **语言**: C#
* **框架**: .NET Framework 4.8
* **UI框架**: Windows Forms (部分遗留) 和 Windows Presentation Foundation (WPF)
* **API**: Autodesk Navisworks API (包括 COM API, Timeliner API, Clash API)
### 项目结构
```
NavisworksTransportPlugin/
├── src/
│ ├── Core/ # 核心业务逻辑
│ │ ├── Animation/ # 动画管理 (LogisticsAnimationManager, TimeLinerIntegrationManager)
│ │ ├── Collision/ # 碰撞检测 (ClashDetectiveIntegration)
│ │ ├── Properties/ # 属性管理 (CategoryAttributeManager)
│ │ ├── MainPlugin.cs # 插件主入口和旧版UI
│ │ ├── PathPlanningManager.cs # 路径规划核心逻辑
│ │ ├── PathPlanningModels.cs # 路径数据模型
│ │ ├── VisibilityManager.cs # 可见性控制
│ │ └── ... # 其他核心组件
│ ├── UI/
│ │ ├── Forms/ # 旧版WinForms UI (逐渐被WPF替代)
│ │ └── WPF/ # 新版WPF UI
│ │ ├── ViewModels/ # MVVM ViewModels
│ │ ├── Views/ # MVVM Views (UserControls)
│ │ ├── LogisticsControlPanel.xaml # 主停靠面板
│ │ └── ...
│ ├── Utils/ # 工具类 (日志、几何、坐标转换等)
│ └── Legacy/ # 为兼容性保留的旧代码
├── Properties/
│ └── AssemblyInfo.cs
├── NavisworksTransportPlugin.csproj # 项目文件
└── ...
```
### 核心组件
1. **`MainPlugin` (`MainPlugin.cs`)**:
* 插件的入口点,继承自 `Autodesk.Navisworks.Api.Plugins.DockPanePlugin`
* 负责初始化插件、创建和管理主停靠窗格。
* 包含旧版的 Windows Forms UI 逻辑(已部分迁移到 WPF
2. **`LogisticsControlPanel` (`LogisticsControlPanel.xaml`, `LogisticsControlPanel.xaml.cs`)**:
* WPF 用户控件,作为主停靠窗格的内容。
* 使用 MVVM 模式,通过 `DataContext` 绑定到 `LogisticsControlViewModel`
3. **`LogisticsControlViewModel` (`LogisticsControlViewModel.cs`)**:
* WPF UI 的核心逻辑处理中心。
* 管理 UI 状态、数据绑定、用户命令ICommand
* 与 Core 层(如 `PathPlanningManager`)交互,驱动业务逻辑。
4. **`PathPlanningManager` (`PathPlanningManager.cs`)**:
* 路径规划的核心业务逻辑。
* 管理路径 (`PathRoute`) 和路径点 (`PathPoint`) 的创建、编辑、验证。
* 管理路径编辑状态(查看、创建、编辑)。
* 处理与 Navisworks 模型的交互(选择、边界计算)。
* 提供事件供 UI 层订阅。
5. **`CategoryAttributeManager` (`CategoryAttributeManager.cs`)**:
* 负责通过 Navisworks COM API 为模型项添加、更新、删除自定义物流属性。
* 定义了物流类别 (`LogisticsCategories`) 和属性 (`LogisticsProperties`)。
* 提供了筛选模型项的方法。
6. **`LogisticsAnimationManager`, `TimeLinerIntegrationManager`**:
* 负责与 Navisworks Timeliner 集成,根据路径数据创建动画。
7. **`ClashDetectiveIntegration`**:
* 负责与 Navisworks Clash Detective 集成,配置和运行碰撞测试。
8. **`VisibilityManager`**:
* 管理模型元素的可见性,实现"仅显示物流元素"等功能。
9. **`LogManager`**:
* 统一日志管理工具。
## 开发与构建
### 系统要求
* Windows 10 或更高版本
* Navisworks Manage 2026
* .NET Framework 4.8
* Visual Studio (推荐)
### 构建过程
1. 使用 Visual Studio 打开 `NavisworksTransport.sln` 解决方案文件。
2. 确保项目引用指向正确的 Navisworks 2026 API DLL 文件(路径在 `.csproj` 文件中定义)。
3. 选择 "Debug" 或 "Release" 配置。
4. 构建项目 (`Ctrl+Shift+B`)。
5. 输出文件为 `NavisworksTransportPlugin.dll`,位于 `bin\Debug\``bin\Release\` 目录下。
### 部署与安装
1. 编译生成 `NavisworksTransportPlugin.dll`
2. 插件会自动安装到 Navisworks 插件目录:
`[Navisworks安装路径]\Plugins\NavisworksTransportPlugin\`
3. 重启 Navisworks 即可在 "附加模块" 选项卡中找到插件。
### 运行与调试
* 在 Visual Studio 中可以直接调试插件,方法是设置 Navisworks 可执行文件(如 `NwAddinDev.exe``Navisworks.exe`)为启动外部程序。
* 插件运行时会在 `%AppData%\Autodesk Navisworks Manage 2026\PluginsData\NavisworksTransportPlugin` 目录下生成日志文件。
## 开发约定与实践
* **MVVM 模式**: 新的 UI 开发强烈推荐使用 WPF 和 MVVM 模式,将 UI 逻辑 (`ViewModel`) 与 UI 呈现 (`View`) 分离。
* **全局异常处理**: 使用 `GlobalExceptionHandler` 类来捕获和处理未处理的异常,提供用户友好的错误提示。
* **日志记录**: 使用 `LogManager` 进行统一的日志记录,便于调试和问题追踪。
* **安全执行**: 关键操作包装在 `GlobalExceptionHandler.SafeExecute` 方法中,以提高稳定性。
* **COM API 使用**: 在需要与 Navisworks 属性系统深度交互时,谨慎使用 COM API并注意处理缓存和刷新问题。

View File

@ -1 +1 @@
0.2.0 0.3.0

109
deploy-from-anywhere.bat Normal file
View File

@ -0,0 +1,109 @@
@echo off
echo === Navisworks Transport Plugin Deployment (Auto-locate) ===
echo.
REM Try to find the project directory
set "PROJECT_DIR="
REM Check current directory first
if exist "NavisworksTransportPlugin.csproj" (
set "PROJECT_DIR=%CD%"
goto :found
)
REM Check if we're in a subdirectory, go up one level
cd ..
if exist "NavisworksTransportPlugin.csproj" (
set "PROJECT_DIR=%CD%"
goto :found
)
REM Check common locations
if exist "C:\Users\Tellme\apps\NavisworksTransport\NavisworksTransportPlugin.csproj" (
set "PROJECT_DIR=C:\Users\Tellme\apps\NavisworksTransport"
goto :found
)
REM If not found, ask user
echo ERROR: Could not locate NavisworksTransportPlugin.csproj
echo Please navigate to the project directory and run deploy-plugin.bat
echo.
echo Expected project structure:
echo NavisworksTransportPlugin.csproj
echo bin\Debug\NavisworksTransportPlugin.dll
echo.
pause
exit /b 1
:found
echo Found project at: %PROJECT_DIR%
cd /d "%PROJECT_DIR%"
echo.
REM Check if compiled files exist
if not exist "bin\Debug\NavisworksTransportPlugin.dll" (
echo ERROR: Plugin file not found, please compile the project first
echo Run command: dotnet build NavisworksTransportPlugin.csproj
echo.
echo Current directory contents:
dir /b
echo.
if exist "bin" (
echo bin directory contents:
dir bin /b
if exist "bin\Debug" (
echo bin\Debug directory contents:
dir "bin\Debug" /b
)
)
pause
exit /b 1
)
REM Set target directory (Navisworks installation Plugins folder)
set "TARGET_DIR=C:\Program Files\Autodesk\Navisworks Manage 2026\Plugins\NavisworksTransportPlugin"
echo Target directory: %TARGET_DIR%
echo NOTE: Administrator privileges required to write to this directory
echo.
REM Create directories
if not exist "%TARGET_DIR%" (
mkdir "%TARGET_DIR%" 2>nul
if errorlevel 1 (
echo ERROR: Failed to create directory. Please run as Administrator.
pause
exit /b 1
)
echo [OK] Created plugin directory
)
REM Copy files
copy "bin\Debug\NavisworksTransportPlugin.dll" "%TARGET_DIR%\" >nul 2>&1
if errorlevel 1 (
echo ERROR: Failed to copy plugin file. Please run as Administrator.
pause
exit /b 1
)
echo [OK] Copied plugin file: NavisworksTransportPlugin.dll
if exist "bin\Debug\NavisworksTransport.Tian.name.txt" (
copy "bin\Debug\NavisworksTransport.Tian.name.txt" "%TARGET_DIR%\" >nul 2>&1
if errorlevel 1 (
echo WARNING: Failed to copy localization file
) else (
echo [OK] Copied localization file: NavisworksTransport.Tian.name.txt
)
)
echo.
echo SUCCESS: Plugin deployed successfully!
echo.
echo Next steps:
echo 1. Start Navisworks Manage 2026
echo 2. Look for "Logistics Path Planning" dockable panel
echo 3. If not visible, check Plugin Manager
echo.
echo Plugin location: %TARGET_DIR%
echo.
pause

69
deploy-plugin.bat Normal file
View File

@ -0,0 +1,69 @@
@echo off
echo === Navisworks Transport Plugin Deployment ===
echo.
echo Current directory: %CD%
echo.
REM Check if we're in the right directory
if not exist "NavisworksTransportPlugin.csproj" (
echo ERROR: NavisworksTransportPlugin.csproj not found in current directory
echo Please run this script from the project root directory
pause
exit /b 1
)
REM Check if compiled files exist
if not exist "bin\Debug\NavisworksTransportPlugin.dll" (
echo ERROR: Plugin file not found, please compile the project first
echo Run command: dotnet build NavisworksTransportPlugin.csproj
echo.
echo Checking bin directory...
if exist "bin" (
echo bin directory exists
dir bin /b
if exist "bin\Debug" (
echo bin\Debug directory exists
dir "bin\Debug" /b
) else (
echo bin\Debug directory does not exist
)
) else (
echo bin directory does not exist
)
pause
exit /b 1
)
REM Set target directory (Navisworks installation Plugins folder)
set "TARGET_DIR=C:\Program Files\Autodesk\Navisworks Manage 2026\Plugins\NavisworksTransportPlugin"
echo Target directory: %TARGET_DIR%
echo NOTE: Administrator privileges required to write to this directory
echo.
REM Create directories
if not exist "%TARGET_DIR%" (
mkdir "%TARGET_DIR%"
echo [OK] Created plugin directory
)
REM Copy files
copy "bin\Debug\NavisworksTransportPlugin.dll" "%TARGET_DIR%\" >nul
echo [OK] Copied plugin file: NavisworksTransportPlugin.dll
if exist "bin\Debug\NavisworksTransport.Tian.name.txt" (
copy "bin\Debug\NavisworksTransport.Tian.name.txt" "%TARGET_DIR%\" >nul
echo [OK] Copied localization file: NavisworksTransport.Tian.name.txt
)
echo.
echo SUCCESS: Plugin deployed successfully!
echo.
echo Next steps:
echo 1. Start Navisworks Manage 2026
echo 2. Look for "Logistics Path Planning" dockable panel
echo 3. If not visible, check Plugin Manager
echo.
echo Plugin location: %TARGET_DIR%
echo.
pause

90
deploy-plugin.ps1 Normal file
View File

@ -0,0 +1,90 @@
# Navisworks Transport Plugin 部署脚本
# 用于将编译后的插件文件复制到Navisworks 2026插件目录
param(
[switch]$Debug = $false # 是否包含调试文件
)
# 定义路径
$sourceDir = ".\bin\Debug"
$pluginDll = "$sourceDir\NavisworksTransportPlugin.dll"
$pluginPdb = "$sourceDir\NavisworksTransportPlugin.pdb"
$localizationFile = "$sourceDir\src\Resources\NavisworksTransport.Tian.name.txt"
# 目标目录
$systemPluginDir = "${env:ProgramFiles}\Autodesk\Navisworks Manage 2026\Plugins\NavisworksTransportPlugin"
Write-Host "=== Navisworks Transport Plugin 部署工具 ===" -ForegroundColor Green
Write-Host ""
# 检查源文件是否存在
if (-not (Test-Path $pluginDll)) {
Write-Host "错误: 找不到插件文件 $pluginDll" -ForegroundColor Red
Write-Host "请先编译项目: dotnet build NavisworksTransportPlugin.csproj" -ForegroundColor Yellow
exit 1
}
# 使用标准插件目录
$targetDir = $systemPluginDir
Write-Host "部署到Navisworks插件目录: $targetDir" -ForegroundColor Yellow
Write-Host "注意: 需要管理员权限" -ForegroundColor Yellow
# 创建目标目录
try {
if (-not (Test-Path $targetDir)) {
New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
Write-Host "✓ 创建目录: $targetDir" -ForegroundColor Green
}
# 创建资源子目录
$resourcesTargetDir = "$targetDir\src\Resources"
if (-not (Test-Path $resourcesTargetDir)) {
New-Item -ItemType Directory -Path $resourcesTargetDir -Force | Out-Null
Write-Host "✓ 创建资源目录: $resourcesTargetDir" -ForegroundColor Green
}
} catch {
Write-Host "错误: 无法创建目录 $targetDir" -ForegroundColor Red
Write-Host "错误详情: $($_.Exception.Message)" -ForegroundColor Red
exit 1
}
# 复制文件
try {
# 复制主插件文件
Copy-Item $pluginDll $targetDir -Force
Write-Host "✓ 复制插件文件: NavisworksTransportPlugin.dll" -ForegroundColor Green
# 复制调试文件(如果需要)
if ($Debug -and (Test-Path $pluginPdb)) {
Copy-Item $pluginPdb $targetDir -Force
Write-Host "✓ 复制调试文件: NavisworksTransportPlugin.pdb" -ForegroundColor Green
}
# 复制本地化文件
if (Test-Path $localizationFile) {
Copy-Item $localizationFile $resourcesTargetDir -Force
Write-Host "✓ 复制本地化文件: NavisworksTransport.Tian.name.txt" -ForegroundColor Green
}
} catch {
Write-Host "错误: 复制文件失败" -ForegroundColor Red
Write-Host "错误详情: $($_.Exception.Message)" -ForegroundColor Red
exit 1
}
Write-Host ""
Write-Host "🎉 插件部署成功!" -ForegroundColor Green
Write-Host ""
Write-Host "下一步操作:" -ForegroundColor Yellow
Write-Host "1. 启动 Navisworks Manage 2026" -ForegroundColor White
Write-Host "2. 在菜单中查找 '物流路径规划' 面板" -ForegroundColor White
Write-Host "3. 如果没有出现,请检查插件管理器中是否已加载" -ForegroundColor White
Write-Host ""
Write-Host "插件位置: $targetDir" -ForegroundColor Cyan
Write-Host ""
# 显示Navisworks插件管理器信息
Write-Host "💡 提示:" -ForegroundColor Yellow
Write-Host "- 如果插件没有自动加载请在Navisworks中打开 '主页' > '插件管理器'" -ForegroundColor White
Write-Host "- 确保 'NavisworksTransportPlugin' 已启用" -ForegroundColor White
Write-Host "- 插件将显示为可停靠面板,而不是在附加模块选项卡中" -ForegroundColor White

View File

@ -0,0 +1,38 @@
### Navisworks帮助文档
- 将 Animator 用于软碰撞
软碰撞结合使用了 Animator 和 Clash Detective 的主要功能。
项目模型可能包含临时项目(如工作软件包、船、起重机、安装等)的动态表示。
可以使用“Animator”窗口创建包含这些对象的动画场景以使它们围绕项目现场移动或更改其尺寸等。某些正在移动的对象可能会发生碰撞。
设置软碰撞可以对该碰撞进行自动检查。运行软碰撞会话时在场景序列的每个步骤都会使用“Clash Detective”检查是否发生了碰撞。如果发生碰撞将记录碰撞发生的时间以及导致碰撞的事件。
例如,您可以查看软碰撞结果并重新安排对象的移动以消除此类碰撞。
为软碰撞准备动态工作软件包
需要在覆盖所需面积或体积的项目模型中对每个要为其创建动画的对象建模;例如,可以使用半透明块。
必须使用 Autodesk Navisworks 中的 Animator 窗口来创建具有所需对象的动画场景。
链接到对象动画以进行软碰撞
在 Navisworks 中,打开包含对象动画场景的项目模型文件。
如果尚未打开 Animator 窗口,请单击“常用”选项卡 >“工具”面板 > Animator 。
播放动画。检查动画对象是否在正确的位置、以正确的尺寸显示等等。
如果尚未打开“Clash Detective”窗口请单击“常用”选项卡 >“工具”面板 >“Clash Detective”。
单击“选择”选项卡。
在“选择 A”和“选择 B”窗格中选择要测试的对象。
在“链接”下拉框中选择要链接到的动画场景如“Scene1”。
在“步长”框中,输入要在查找动画中的碰撞时使用的“时间间隔大小”。
单击“运行检测”按钮。“Clash Detective”将在每个时间间隔检查动画中是否存在碰撞。找到的碰撞数将显示在“Clash Detective”窗口的顶部。
注意:如果动画场景很大,则此类碰撞检测将始终比普通碰撞检测需要更长的时间才能完成运行。
现在可以查看找到的碰撞。

44
doc/design/2026/Idea.md Normal file
View File

@ -0,0 +1,44 @@
# 项目开发思路
## **1\. 使用场景**
1. 在Navisworks中打开物流相关的楼层nwd文件前提将建筑按单独的楼层保存
2. 在物流控件中新建任务,任务包含一个物流场景的所有信息,支持保存和打开
2. 选择物流运动模型(在物流模型文件列表中选择,可以是车辆、人员等,也可以选择自定义模型,指定长宽高),设置速度限制、安全距离等信息。
3. 可以对运动模型经过的物流组件,指定物流类型(门、通道、电梯等),可以设置限高、限宽、限速等参数。
4. 用物流导航控件指定物流起点和终点点自动吸附在通道表面用颜色球表示。移动过程可以使用Navisworks的漫游/飞行工具)
5. 在物流导航控件中选择【生成路径】,自动生成从起点到终点的物流路径,包含一系列路径点和之间的连线,用颜色球和圆柱线表示。用标签显示路径的名称、长度、路径点数量等。
6. 在物流导航控件中选择【仿真动画】,设置动画时长、速度、检测距离间隔、碰撞检测间隙后,点击运行,物流运动模型开始从起点沿着指定路径运动,如果发生碰撞,高亮显示物流模型和冲突模型,用标签显示速度、位置、碰撞数量等。
7. 仿真动画结束,提交结果页面,显示各初始参数和统计信息。可以导出路径文件、导航地图和碰撞报告。
8. 对生成的路径,可以进行手工编辑,修改各点位置和限制参数。
9. 可以对修改后的路径,重新运行仿真动画。
## **2\. 设计思路**
### **2.1\. UI**
1. 用浮动控件做物流导航控件
2. 用停靠窗口做物流控件
### **2.2\. 自动路径生成**
1. 用A*算法进行路径规划
### **2.3\. 仿真动画**
1. 用变换实现物流物体的移动,同时进行碰撞检测
2. 动画按固定帧率如30帧进行碰撞检测按检测距离间隔进行
3. 【可选】碰撞检测结果同步到ClashDetective
### **2.4\. 导航地图**
1. 导航地图可以用俯视图加路径生成图片
2. 导航视频可以用视点动画生成
### **2.5\. 分层保存**
1. 可以按楼层搜索,保存选择项
2. 按物流属性搜索,保存选择项
3. 保存当前选择项
### **2.5\. 物流属性**
1. 子节点继承父节点的物流属性,不直接设置
## 编译命令
```
& "C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" NavisworksTransportPlugin.csproj /p:Configuration=Debug /p:Platform=AnyCPU /verbosity:minimal
```

View File

@ -1,53 +0,0 @@
# 源代码文件重组计划
## 📁 目标目录结构
```
src/
├── Core/ # 核心功能模块
│ ├── Animation/ # 动画系统
│ ├── Collision/ # 碰撞检测
│ └── Properties/ # 属性管理
├── Legacy/ # 2017兼容代码
├── UI/ # 用户界面
│ ├── Forms/ # Windows Forms界面
│ └── WPF/ # WPF界面
└── Utils/ # 工具类
```
## 📋 文件移动计划
### Core/Animation/ (动画系统)
- `PathAnimationManager.cs``Legacy/PathAnimationManager.cs` (标记为遗留代码)
- `TimeLinerIntegrationManager.cs``Core/Animation/TimeLinerIntegrationManager.cs`
### Core/Collision/ (碰撞检测)
- `ClashDetectiveIntegration.cs``Core/Collision/ClashDetectiveIntegration.cs`
- `ClashDetectiveIntegrationTest.cs``Core/Collision/ClashDetectiveIntegrationTest.cs`
### Core/Properties/ (属性管理)
- `CategoryAttributeManager.cs``Core/Properties/CategoryAttributeManager.cs`
- `AttributeGrouper.cs``Core/Properties/AttributeGrouper.cs`
### UI/Forms/ (Windows Forms界面)
- `LogisticsPropertyEditDialog.cs``UI/Forms/LogisticsPropertyEditDialog.cs`
- `ModelSplitterDialog.cs``UI/Forms/ModelSplitterDialog.cs`
### Utils/ (工具类)
- `LogManager.cs``Utils/LogManager.cs`
- `CoordinateConverter.cs``Utils/CoordinateConverter.cs`
- `GeometryExtractor.cs``Utils/GeometryExtractor.cs`
- `FloorDetector.cs``Utils/FloorDetector.cs`
- `NavisworksFileExporter.cs``Utils/NavisworksFileExporter.cs`
### 根目录保留 (主要插件文件)
- `MainPlugin.cs` (保持在src根目录)
- `PathClickToolPlugin.cs` (保持在src根目录)
- `PathPointRenderPlugin.cs` (保持在src根目录)
### 需要重新分类的文件
- `ModelSplitterManager.cs``Core/ModelSplitterManager.cs`
- `PathDataManager.cs``Core/PathDataManager.cs`
- `PathPlanningManager.cs``Core/PathPlanningManager.cs`
- `PathPlanningModels.cs``Core/PathPlanningModels.cs`
- `VisibilityManager.cs``Core/VisibilityManager.cs`

View File

@ -1,279 +0,0 @@
# Navisworks 2026 项目配置检查报告
## 📋 当前配置状态
### ✅ 正确配置项
1. **目标框架版本**`.NET Framework 4.8` ✅
- 符合Navisworks 2026要求
- 支持最新的C#语言特性
2. **平台目标**`x64` ✅
- 正确设置为64位平台
- 与Navisworks 2026架构匹配
3. **输出路径**直接输出到Navisworks插件目录 ✅
- Debug模式直接部署到插件目录
- 便于开发和测试
4. **核心API引用**已正确引用主要DLL ✅
- `Autodesk.Navisworks.Api.dll`
- `Autodesk.Navisworks.ComApi.dll`
- `Autodesk.Navisworks.Interop.ComApi.dll`
- `Autodesk.Navisworks.Timeliner.dll`
- `Autodesk.Navisworks.Clash.dll`
### ⚠️ 需要优化的配置项
#### 1. 缺少2026新功能相关的DLL引用
根据迁移分析,需要添加以下引用以支持新功能:
```xml
<!-- 属性集功能支持 -->
<Reference Include="Autodesk.Navisworks.Api.Controls">
<HintPath>..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Api.Controls.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- 动画功能增强支持 -->
<Reference Include="Autodesk.Navisworks.Api.Plugins">
<HintPath>..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Api.Plugins.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- WPF界面支持 -->
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="WindowsBase" />
<Reference Include="System.Xaml" />
```
#### 2. 动画系统问题确认 🔥
**重大发现**当前动画实现确实使用了手动Transform变换
从`PathAnimationManager.cs`代码中可以看到:
```csharp
// ❌ 问题代码:手动变换实现
doc.Models.OverridePermanentTransform(modelItems, incrementalTransform, false);
// ❌ 问题代码Timer + Thread.Sleep模式
_animationTimer = new Timer();
_animationTimer.Interval = 50; // 20 FPS
_animationTimer.Tick += AnimationTimer_Tick;
```
**性能问题**
- 使用`Timer.Tick`事件只能达到20fps
- 手动计算插值位置CPU占用高
- 缺乏专业动画控制功能
#### 3. 项目结构需要重组
当前所有代码都在`src/`目录下,建议按功能模块重新组织:
```
src/
├── Core/ # 核心API封装
│ ├── Animation/ # 动画系统
│ ├── Properties/ # 属性管理
│ └── Collision/ # 碰撞检测
├── Legacy/ # 2017兼容代码
├── UI/ # 用户界面
│ ├── WPF/ # WPF控件
│ └── Forms/ # Windows Forms
└── Utils/ # 工具类
```
## 🚀 推荐的配置更新
### 1. 更新项目文件
```xml
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1A0124F6-3DEB-4153-8760-F568AD9393EE}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>NavisworksTransport</RootNamespace>
<AssemblyName>NavisworksTransportPlugin</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<!-- 新增支持WPF -->
<UseWPF>true</UseWPF>
</PropertyGroup>
<!-- 现有配置保持不变 -->
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Plugins\NavisworksTransportPlugin\</OutputPath>
<DefineConstants>DEBUG;TRACE;NAVISWORKS_2026</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<!-- 现有引用保持不变 -->
<Reference Include="Autodesk.Navisworks.Api">
<HintPath>..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Api.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Autodesk.Navisworks.ComApi">
<HintPath>..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.ComApi.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Autodesk.Navisworks.Interop.ComApi">
<HintPath>..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Interop.ComApi.dll</HintPath>
<EmbedInteropTypes>False</EmbedInteropTypes>
<Private>False</Private>
</Reference>
<Reference Include="Autodesk.Navisworks.Interop.ComApiAutomation">
<HintPath>..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Interop.ComApiAutomation.dll</HintPath>
<EmbedInteropTypes>False</EmbedInteropTypes>
<Private>False</Private>
</Reference>
<Reference Include="Autodesk.Navisworks.Timeliner">
<HintPath>..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Timeliner.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Autodesk.Navisworks.Clash">
<HintPath>..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Clash.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- 新增2026新功能支持 -->
<Reference Include="Autodesk.Navisworks.Controls">
<HintPath>..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Controls.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Autodesk.Navisworks.Interop.Timeliner">
<HintPath>..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Interop.Timeliner.dll</HintPath>
<EmbedInteropTypes>False</EmbedInteropTypes>
<Private>False</Private>
</Reference>
<!-- WPF支持 -->
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="WindowsBase" />
<Reference Include="System.Xaml" />
<!-- 现有系统引用 -->
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<!-- 新增JSON支持用于DELMIA集成 -->
<Reference Include="System.Text.Json" />
</ItemGroup>
</Project>
```
### 2. 验证DLL文件存在
**已验证**以下DLL文件在Navisworks 2026安装目录中存在
```
C:\Program Files\Autodesk\Navisworks Manage 2026\
├── Autodesk.Navisworks.Api.dll ✅ (4.39MB)
├── Autodesk.Navisworks.ComApi.dll ✅ (415KB)
├── Autodesk.Navisworks.Interop.ComApi.dll ✅ (313KB)
├── Autodesk.Navisworks.Interop.ComApiAutomation.dll ✅ (19KB)
├── Autodesk.Navisworks.Timeliner.dll ✅ (526KB)
├── Autodesk.Navisworks.Interop.Timeliner.dll ✅ (19KB)
├── Autodesk.Navisworks.Clash.dll ✅ (551KB)
└── Autodesk.Navisworks.Controls.dll ✅ (147KB) 🆕发现
```
**重要发现**
- ✅ 找到了 `Autodesk.Navisworks.Controls.dll` - 支持WPF控件集成
- ❌ 没有找到 `Autodesk.Navisworks.Api.Plugins.dll` - 可能不存在或名称不同
- ✅ TimeLiner相关DLL完整支持4D动画集成
- ✅ Clash相关DLL完整支持增强碰撞检测
### 3. 添加条件编译符号
在项目中添加`NAVISWORKS_2026`条件编译符号用于区分2017和2026版本的代码
```csharp
#if NAVISWORKS_2026
// 使用2026新功能
var animationSet = new AnimationSet(document, "物流动画");
#else
// 使用2017兼容代码
// 手动变换实现
#endif
```
## 🎯 下一步行动
### 立即执行:
1. **验证DLL引用**检查所有必需的DLL文件是否存在
2. **更新项目文件**添加新的引用和WPF支持
3. **测试编译**:确保项目能够正常编译
### 准备迁移:
1. **创建动画系统分支**:为动画系统重构创建独立分支
2. **备份现有代码**:保留当前手动变换实现作为备用
3. **建立测试环境**:准备动画性能对比测试
## 📊 配置检查清单
- [x] .NET Framework 4.8 ✅
- [x] x64平台目标 ✅
- [x] 基础API引用 ✅
- [x] DLL文件验证 ✅ **所有必需DLL都存在**
- [ ] 新功能API引用 ⚠️ **需要添加Controls和Timeliner.Interop**
- [ ] WPF支持配置 ⚠️ **需要添加WPF引用**
- [ ] 条件编译符号 ⚠️ **需要添加NAVISWORKS_2026**
- [ ] 项目结构重组 ⚠️ **建议重新组织代码结构**
## 🎯 立即行动建议
### 1. 更新项目文件(高优先级)
我已经创建了更新后的项目文件:`NavisworksTransportPlugin_2026_Updated.csproj`
**主要改进**
- ✅ 添加了所有必需的2026 API引用
- ✅ 配置了WPF支持
- ✅ 添加了条件编译符号`NAVISWORKS_2026`
- ✅ 为新的动画系统预留了代码结构
- ✅ 添加了JSON支持用于DELMIA集成
### 2. 验证配置(立即执行)
```powershell
# 1. 备份当前项目文件
Copy-Item "NavisworksTransportPlugin.csproj" "NavisworksTransportPlugin_backup.csproj"
# 2. 替换为新的项目文件
Copy-Item "NavisworksTransportPlugin_2026_Updated.csproj" "NavisworksTransportPlugin.csproj"
# 3. 在Visual Studio中重新加载项目
# 4. 尝试编译,验证所有引用正确
```
## 🚨 重要发现
**动画系统确实需要重构**!当前实现存在以下问题:
1. 手动Transform变换性能差
2. 20fps限制不够流畅
3. 缺乏专业动画控制
4. 维护成本高
这证实了我们的迁移分析是正确的,动画系统重构应该是最高优先级的任务。

View File

@ -0,0 +1,917 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Autodesk.Navisworks.Api;
using NavisApplication = Autodesk.Navisworks.Api.Application;
namespace NavisworksTransport
{
/// <summary>
/// 定义动画播放的状态
/// </summary>
public enum AnimationState
{
Idle, // 空闲,未生成动画
Ready, // 已就绪,动画已生成但未播放
Playing, // 播放中
Paused, // 暂停
Stopped, // 已停止
Finished // 已完成
}
/// <summary>
/// 路径动画管理器 - 基于TimeLiner和动态变换实现沿路径的动画效果
/// 注意由于Navisworks API限制无法直接使用Animator API因此使用OverridePermanentTransform实现动画
/// 已集成 TimeLiner 功能,支持在 TimeLiner 中显示和管理动画任务
/// </summary>
public class PathAnimationManager
{
private ModelItem _animatedObject;
private List<Point3D> _pathPoints;
private Timer _animationTimer;
private double _animationDuration = 10.0; // 动画总时长(秒)
private DateTime _animationStartTime;
private Transform3D _originalTransform;
private Point3D _originalCenter; // 存储部件的原始中心位置
private Point3D _currentPosition; // 存储部件的当前位置
private AnimationState _currentState = AnimationState.Idle;
// TimeLiner 集成
private TimeLinerIntegrationManager _timeLinerManager;
private string _currentTaskId;
// --- 新增事件 ---
/// <summary>
/// 当动画状态发生改变时触发
/// </summary>
public event EventHandler<AnimationState> StateChanged;
/// <summary>
/// 当动画进度更新时触发 (0-100)
/// </summary>
public event EventHandler<int> ProgressChanged;
// 动画完成事件 (旧版,保留兼容性)
public event EventHandler AnimationCompleted;
/// <summary>
/// 当检测到碰撞时触发
/// </summary>
public event EventHandler<CollisionDetectedEventArgs> CollisionDetected;
public PathAnimationManager()
{
_pathPoints = new List<Point3D>();
// 初始化 TimeLiner 集成
try
{
_timeLinerManager = new TimeLinerIntegrationManager();
LogManager.Info("PathAnimationManager 已集成 TimeLiner 功能");
}
catch (Exception ex)
{
LogManager.Warning($"TimeLiner 集成初始化失败,将使用基础动画功能: {ex.Message}");
_timeLinerManager = null;
}
}
/// <summary>
/// 设置动画参数
/// </summary>
/// <param name="animatedObject">要动画化的模型对象</param>
/// <param name="pathPoints">路径点列表</param>
/// <param name="durationSeconds">动画持续时间(秒)</param>
public void SetupAnimation(ModelItem animatedObject, List<Point3D> pathPoints, double durationSeconds = 10.0)
{
try
{
if (animatedObject == null)
throw new ArgumentNullException(nameof(animatedObject));
if (pathPoints == null || pathPoints.Count < 2)
throw new ArgumentException("路径点数量必须至少为2个", nameof(pathPoints));
// 添加路径点坐标有效性验证
LogManager.Info("=== 动画管理器坐标验证 ===");
bool hasInvalidCoordinates = false;
for (int i = 0; i < pathPoints.Count; i++)
{
var point = pathPoints[i];
bool isValid = !double.IsNaN(point.X) && !double.IsNaN(point.Y) && !double.IsNaN(point.Z) &&
!double.IsInfinity(point.X) && !double.IsInfinity(point.Y) && !double.IsInfinity(point.Z);
LogManager.Info($"路径点[{i}]坐标验证: ({point.X:F6},{point.Y:F6},{point.Z:F6}) - {(isValid ? "" : "")}");
if (!isValid)
{
hasInvalidCoordinates = true;
LogManager.Error($"检测到无效坐标: 路径点[{i}] = ({point.X},{point.Y},{point.Z})");
}
// 检查是否为零坐标(可能表示数据丢失)
if (isValid && point.X == 0.0 && point.Y == 0.0 && point.Z == 0.0)
{
LogManager.Warning($"路径点[{i}]坐标为零,可能存在数据丢失问题");
}
}
if (hasInvalidCoordinates)
{
throw new ArgumentException("路径点包含无效坐标NaN或无穷大无法创建动画");
}
LogManager.Info("=== 坐标验证完成 ===");
_animatedObject = animatedObject;
_pathPoints = new List<Point3D>(pathPoints);
_animationDuration = durationSeconds;
// 保存原始变换以便重置
_originalTransform = GetCurrentTransform(_animatedObject);
// 保存车辆的原始中心位置
var originalBoundingBox = animatedObject.BoundingBox();
_originalCenter = originalBoundingBox.Center;
// 关键修复:将车辆立即移动到路径起点
// 这样动画就从路径起点开始,而不是从车辆当前位置开始
MoveVehicleToPathStart();
// 记录文档单位信息
var documentUnits = GetDocumentUnitsInfo();
// 添加详细的调试信息
LogManager.Info($"动画设置完成:对象={_animatedObject.DisplayName}, 路径点数={_pathPoints.Count}, 时长={_animationDuration}秒");
LogManager.Info($"文档单位: {documentUnits}");
LogManager.Info($"路径起点: ({_pathPoints[0].X:F2},{_pathPoints[0].Y:F2},{_pathPoints[0].Z:F2})");
LogManager.Info($"路径终点: ({_pathPoints[_pathPoints.Count-1].X:F2},{_pathPoints[_pathPoints.Count-1].Y:F2},{_pathPoints[_pathPoints.Count-1].Z:F2})");
var totalDist = CalculateTotalPathDistance();
LogManager.Info($"路径总长度: {totalDist:F2}");
LogManager.Info($"车辆已移动到路径起点,动画将从起点开始");
LogManager.Info($"=== 调试信息结束 ===");
}
catch (Exception ex)
{
LogManager.Error($"设置动画失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 将车辆移动到路径起点
/// </summary>
private void MoveVehicleToPathStart()
{
try
{
if (_pathPoints.Count == 0) return;
var doc = NavisApplication.ActiveDocument;
var modelItems = new ModelItemCollection { _animatedObject };
// 计算从部件原始中心到路径起点的偏移
var startOffset = new Vector3D(
_pathPoints[0].X - _originalCenter.X,
_pathPoints[0].Y - _originalCenter.Y,
_pathPoints[0].Z - _originalCenter.Z
);
// 创建变换并应用
var startTransform = Transform3D.CreateTranslation(startOffset);
doc.Models.OverridePermanentTransform(modelItems, startTransform, false);
// 更新当前位置为路径起点
_currentPosition = _pathPoints[0];
LogManager.Info($"部件已移动到路径起点,偏移: ({startOffset.X:F2},{startOffset.Y:F2},{startOffset.Z:F2})");
}
catch (Exception ex)
{
LogManager.Error($"移动部件到路径起点失败: {ex.Message}");
}
}
/// <summary>
/// 开始播放动画
/// </summary>
public void StartAnimation()
{
try
{
if (_animatedObject == null || _pathPoints.Count < 2)
{
throw new InvalidOperationException("请先调用SetupAnimation设置动画参数");
}
// 停止之前的动画
StopAnimation();
// 创建 TimeLiner 任务(如果可用)
if (_timeLinerManager != null && _timeLinerManager.IsTimeLinerAvailable)
{
var taskName = $"{_animatedObject.DisplayName}_运输_{DateTime.Now:HHmmss}";
var duration = TimeSpan.FromSeconds(_animationDuration);
LogManager.Info($"创建 TimeLiner 任务: {taskName}");
_currentTaskId = _timeLinerManager.CreateTransportTask(
taskName,
_pathPoints,
duration,
_animatedObject);
if (!string.IsNullOrEmpty(_currentTaskId))
{
LogManager.Info($"✓ TimeLiner 任务创建成功: {taskName} (ID: {_currentTaskId})");
}
else
{
LogManager.Warning("TimeLiner 任务创建失败,继续使用基础动画功能");
}
}
else
{
LogManager.Info("TimeLiner 不可用,使用基础动画功能");
}
// 设置动态碰撞检测(简化版本)
SetupDynamicClashDetection();
// 初始化动画状态
_animationStartTime = DateTime.Now;
// 创建并启动定时器每50ms更新一次实现流畅动画
_animationTimer = new Timer();
_animationTimer.Interval = 50; // 20 FPS
_animationTimer.Tick += AnimationTimer_Tick;
_animationTimer.Start();
SetState(AnimationState.Playing);
LogManager.Info("动画开始播放");
}
catch (Exception ex)
{
LogManager.Error($"启动动画失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 停止动画
/// </summary>
public void StopAnimation()
{
try
{
if (_animationTimer != null)
{
_animationTimer.Stop();
_animationTimer.Dispose();
_animationTimer = null;
SetState(AnimationState.Stopped);
LogManager.Info("动画已停止");
}
// 动画停止时不创建碰撞测试汇总,由动画完成事件统一处理
LogManager.Info("动画停止,等待动画完成事件统一处理碰撞测试...");
// 更新 TimeLiner 任务状态
if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId))
{
_timeLinerManager.UpdateTaskProgress(_currentTaskId, 0.0, AnimationState.Stopped);
}
}
catch (Exception ex)
{
LogManager.Error($"停止动画失败: {ex.Message}");
}
}
/// <summary>
/// 重置动画对象到原始位置
/// </summary>
public void ResetAnimation()
{
try
{
StopAnimation(); // 停止当前动画
// 恢复对象的原始变换
if (_animatedObject != null)
{
var modelItems = new ModelItemCollection { _animatedObject };
NavisApplication.ActiveDocument.Models.OverridePermanentTransform(modelItems, _originalTransform, false);
LogManager.Info($"部件 {_animatedObject.DisplayName} 已重置到原始位置");
}
ProgressChanged?.Invoke(this, 0); // 重置进度条
SetState(AnimationState.Ready); // 重置后回到就绪状态
}
catch (Exception ex)
{
LogManager.Error($"重置动画失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 动画定时器事件处理
/// </summary>
private void AnimationTimer_Tick(object sender, EventArgs e)
{
try
{
double elapsedSeconds = (DateTime.Now - _animationStartTime).TotalSeconds;
double progress = Math.Min(elapsedSeconds / _animationDuration, 1.0);
// 更新UI进度条
ProgressChanged?.Invoke(this, (int)(progress * 100));
// 同步进度到 TimeLiner如果可用
if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId))
{
_timeLinerManager.UpdateTaskProgress(_currentTaskId, progress, _currentState);
}
Point3D newPosition = InterpolatePosition(progress);
UpdateObjectPosition(newPosition);
// 使用 Clash Detective 集成进行碰撞检测
CheckAndHighlightCollisionsWithClashDetective();
if (progress >= 1.0)
{
StopAnimation();
SetState(AnimationState.Finished); // 标记为完成
AnimationCompleted?.Invoke(this, EventArgs.Empty); // 触发旧版完成事件
LogManager.Info("动画播放完成");
// 动画结束后统一创建所有碰撞测试(基于官方示例的批量处理)
LogManager.Info("动画播放完成,开始创建最终的碰撞测试汇总...");
ClashDetectiveIntegration.Instance.CreateAllAnimationCollisionTests();
}
}
catch (Exception ex)
{
LogManager.Error($"动画帧更新失败: {ex.Message}");
StopAnimation(); // 发生错误时停止动画
}
}
/// <summary>
/// 根据进度插值计算当前位置
/// </summary>
private Point3D InterpolatePosition(double progress)
{
if (_pathPoints.Count < 2)
return _pathPoints[0];
// 确保进度在0-1范围内
progress = Math.Max(0.0, Math.Min(1.0, progress));
// 如果进度达到100%,直接返回终点
if (progress >= 1.0)
{
return _pathPoints[_pathPoints.Count - 1];
}
// 计算总路径长度
var totalDistance = CalculateTotalPathDistance();
// 检查总距离是否有效
if (totalDistance <= 0.0 || double.IsNaN(totalDistance) || double.IsInfinity(totalDistance))
{
LogManager.Error($"路径总长度无效: {totalDistance},返回起点坐标");
return _pathPoints[0];
}
var targetDistance = totalDistance * progress;
// 找到当前应该在哪两个点之间
var accumulatedDistance = 0.0;
for (int i = 0; i < _pathPoints.Count - 1; i++)
{
var segmentDistance = CalculateDistance(_pathPoints[i], _pathPoints[i + 1]);
// 检查段距离是否有效
if (double.IsNaN(segmentDistance) || double.IsInfinity(segmentDistance))
{
LogManager.Error($"路径段[{i}-{i+1}]距离无效: {segmentDistance},跳过此段");
continue;
}
if (accumulatedDistance + segmentDistance >= targetDistance)
{
// 在这个线段内
var segmentProgress = segmentDistance > 0 ? (targetDistance - accumulatedDistance) / segmentDistance : 0.0;
// 确保段内进度也在0-1范围内
segmentProgress = Math.Max(0.0, Math.Min(1.0, segmentProgress));
var interpolatedPoint = InterpolatePoints(_pathPoints[i], _pathPoints[i + 1], segmentProgress);
// 验证插值结果
if (double.IsNaN(interpolatedPoint.X) || double.IsNaN(interpolatedPoint.Y) || double.IsNaN(interpolatedPoint.Z))
{
LogManager.Error($"插值计算结果无效: ({interpolatedPoint.X},{interpolatedPoint.Y},{interpolatedPoint.Z}),返回起点");
return _pathPoints[0];
}
return interpolatedPoint;
}
accumulatedDistance += segmentDistance;
}
// 如果到达这里,返回最后一个点
return _pathPoints[_pathPoints.Count - 1];
}
/// <summary>
/// 在两点间插值
/// </summary>
private Point3D InterpolatePoints(Point3D point1, Point3D point2, double t)
{
return new Point3D(
point1.X + (point2.X - point1.X) * t,
point1.Y + (point2.Y - point1.Y) * t,
point1.Z + (point2.Z - point1.Z) * t
);
}
/// <summary>
/// 计算路径总长度
/// </summary>
private double CalculateTotalPathDistance()
{
var totalDistance = 0.0;
for (int i = 0; i < _pathPoints.Count - 1; i++)
{
totalDistance += CalculateDistance(_pathPoints[i], _pathPoints[i + 1]);
}
return totalDistance;
}
/// <summary>
/// 获取对象当前位置
/// </summary>
private Point3D GetObjectPosition(ModelItem item)
{
try
{
if (item == null) return new Point3D(0, 0, 0);
var bounds = item.BoundingBox();
if (bounds != null)
{
return new Point3D(
(bounds.Min.X + bounds.Max.X) / 2,
(bounds.Min.Y + bounds.Max.Y) / 2,
(bounds.Min.Z + bounds.Max.Z) / 2
);
}
return new Point3D(0, 0, 0);
}
catch (Exception ex)
{
LogManager.Error($"获取对象位置失败: {ex.Message}");
return new Point3D(0, 0, 0);
}
}
/// <summary>
/// 计算两点间距离
/// </summary>
private double CalculateDistance(Point3D point1, Point3D point2)
{
var dx = point2.X - point1.X;
var dy = point2.Y - point1.Y;
var dz = point2.Z - point1.Z;
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}
/// <summary>
/// 更新对象位置
/// </summary>
private void UpdateObjectPosition(Point3D newPosition)
{
try
{
var doc = NavisApplication.ActiveDocument;
var modelItems = new ModelItemCollection { _animatedObject };
// 正确的增量变换:计算从当前位置到新位置的偏移
var incrementalOffset = new Vector3D(
newPosition.X - _currentPosition.X,
newPosition.Y - _currentPosition.Y,
newPosition.Z - _currentPosition.Z
);
// 创建增量变换
var incrementalTransform = Transform3D.CreateTranslation(incrementalOffset);
// 应用增量变换(不重置之前的变换)
doc.Models.OverridePermanentTransform(modelItems, incrementalTransform, false);
LogManager.Debug($"部件位置更新:从({_currentPosition.X:F2},{_currentPosition.Y:F2},{_currentPosition.Z:F2})到({newPosition.X:F2},{newPosition.Y:F2},{newPosition.Z:F2}),增量偏移({incrementalOffset.X:F2},{incrementalOffset.Y:F2},{incrementalOffset.Z:F2})");
// 更新当前位置
_currentPosition = newPosition;
}
catch (Exception ex)
{
LogManager.Error($"更新部件位置失败: {ex.Message}");
}
}
/// <summary>
/// 从车辆变换矩阵中获取真实的缩放系数
/// 注意:此方法已废弃但保留,用于车辆模型的缩放处理。
/// 现在使用场馆部件进行动画,通常不需要复杂的缩放计算。
/// </summary>
private double GetVehicleScaleFactor()
{
try
{
var transform = _animatedObject.Transform;
var linear = transform.Linear;
// 获取变换矩阵的第一行的长度作为缩放系数
// 变换矩阵: (a, b, c)
// (d, e, f)
// (g, h, i)
// 缩放系数 ≈ sqrt(a² + b² + c²)
var scaleX = Math.Sqrt(
linear.Get(0, 0) * linear.Get(0, 0) +
linear.Get(0, 1) * linear.Get(0, 1) +
linear.Get(0, 2) * linear.Get(0, 2)
);
LogManager.Debug($"车辆缩放系数计算: {scaleX:F2} (变换矩阵第一行模长)");
// 如果缩放系数接近155039.37²),说明是米到英寸的平方缩放
if (Math.Abs(scaleX - 1550.0) < 10.0)
{
var linearScale = Math.Sqrt(scaleX);
LogManager.Info($"检测到车辆使用平方缩放系数: {scaleX:F2},已修正为线性缩放系数: {linearScale:F2}");
return linearScale;
}
// 如果缩放系数接近39.37,说明是简单的米到英寸缩放
else if (Math.Abs(scaleX - 39.37) < 1.0)
{
LogManager.Info($"检测到车辆使用线性缩放系数: {scaleX:F2}");
return scaleX;
}
// 如果接近1说明没有缩放
else if (Math.Abs(scaleX - 1.0) < 0.1)
{
LogManager.Info($"车辆无缩放: {scaleX:F2}");
return 1.0;
}
else
{
LogManager.Warning($"未知的车辆缩放系数: {scaleX:F2},使用原值");
return scaleX;
}
}
catch (Exception ex)
{
LogManager.Error($"获取车辆缩放系数失败: {ex.Message}使用默认值1.0");
return 1.0;
}
}
/// <summary>
/// 获取文档单位信息(用于日志记录)
/// </summary>
private string GetDocumentUnitsInfo()
{
try
{
var doc = NavisApplication.ActiveDocument;
var units = doc.Units;
LogManager.Debug($"[单位检测] 文档单位: {units}");
return units.ToString();
}
catch (Exception ex)
{
LogManager.Error($"获取文档单位信息失败: {ex.Message}");
return "Unknown";
}
}
/// <summary>
/// 获取模型当前变换
/// </summary>
private Transform3D GetCurrentTransform(ModelItem item)
{
// 获取包围盒中心作为参考点
var boundingBox = item.BoundingBox();
var center = boundingBox.Center;
// 创建基于中心点的单位变换
return Transform3D.CreateTranslation(new Vector3D(center.X, center.Y, center.Z));
}
/// <summary>
/// 使用 Clash Detective 集成进行碰撞检测并使用缓存的碰撞结果
/// </summary>
private void CheckAndHighlightCollisionsWithClashDetective()
{
try
{
if (_animatedObject == null)
return;
// 使用 Clash Detective 集成进行碰撞检测
var collisionResults = ClashDetectiveIntegration.Instance.DetectCollisions(_animatedObject);
// 高亮显示碰撞对象
ClashDetectiveIntegration.Instance.HighlightCollisions(collisionResults);
// 缓存碰撞结果,动画结束后统一处理
if (collisionResults.Count > 0)
{
LogManager.Info($"=== [动画运行中] 检测到 {collisionResults.Count} 个碰撞,开始记录详细位置 ===");
// 缓存所有碰撞结果,包含位置信息
var animatedObjectPosition = GetObjectPosition(_animatedObject);
LogManager.Info($"[动画位置] 动画对象 {_animatedObject.DisplayName}: ({animatedObjectPosition.X:F2},{animatedObjectPosition.Y:F2},{animatedObjectPosition.Z:F2})");
int collisionIndex = 0;
foreach (var collision in collisionResults)
{
collisionIndex++;
var collisionObjectPosition = GetObjectPosition(collision.Item2);
LogManager.Info($"[碰撞位置{collisionIndex}] 动画对象 vs {collision.Item2.DisplayName}:");
LogManager.Info($" 动画物体位置: ({animatedObjectPosition.X:F2},{animatedObjectPosition.Y:F2},{animatedObjectPosition.Z:F2})");
LogManager.Info($" 碰撞物体位置: ({collisionObjectPosition.X:F2},{collisionObjectPosition.Y:F2},{collisionObjectPosition.Z:F2})");
LogManager.Info($" 两物体距离: {CalculateDistance(animatedObjectPosition, collisionObjectPosition):F2}");
LogManager.Info($" 碰撞状态: 已检测到碰撞");
ClashDetectiveIntegration.Instance.CacheCollisionDuringAnimation(_animatedObject, animatedObjectPosition, collision.Item2, collisionObjectPosition);
}
LogManager.Info($"=== [动画运行中] 位置记录完成 ===");
}
LogManager.Debug($"碰撞检测完成: {collisionResults.Count} 个碰撞 (已缓存)");
}
catch (Exception ex)
{
LogManager.Error($"碰撞检测失败: {ex.Message}");
}
}
/// <summary>
/// 设置动态碰撞检测(实时模式,集成 Clash Detective
/// </summary>
private void SetupDynamicClashDetection()
{
try
{
// 初始化 Clash Detective 集成
ClashDetectiveIntegration.Instance.Initialize();
// 订阅碰撞检测事件
ClashDetectiveIntegration.Instance.CollisionDetected += OnClashDetectiveCollisionDetected;
LogManager.Info("动态碰撞检测设置完成(实时模式)");
}
catch (Exception ex)
{
LogManager.Error($"设置动态碰撞检测失败: {ex.Message}");
}
}
/// <summary>
/// 处理 Clash Detective 碰撞检测事件
/// </summary>
private void OnClashDetectiveCollisionDetected(object sender, CollisionDetectedEventArgs e)
{
try
{
// 将碰撞结果转发到动画管理器的事件
OnCollisionDetected(e);
LogManager.Debug($"Clash Detective 检测到 {e.CollisionCount} 个碰撞");
}
catch (Exception ex)
{
LogManager.Error($"处理 Clash Detective 碰撞事件失败: {ex.Message}");
}
}
/// <summary>
/// 触发碰撞检测事件
/// </summary>
protected virtual void OnCollisionDetected(CollisionDetectedEventArgs e)
{
CollisionDetected?.Invoke(this, e);
}
// 已删除CheckAndHighlightCollisions - 使用Clash Detective集成代替
/// <summary>
/// 检查两个包围盒是否相交
/// </summary>
private bool BoundingBoxesIntersect(BoundingBox3D box1, BoundingBox3D box2)
{
return !(box1.Max.X < box2.Min.X || box2.Max.X < box1.Min.X ||
box1.Max.Y < box2.Min.Y || box2.Max.Y < box1.Min.Y ||
box1.Max.Z < box2.Min.Z || box2.Max.Z < box1.Min.Z);
}
/// <summary>
/// 设置动画持续时间
/// </summary>
public void SetAnimationDuration(double durationSeconds)
{
_animationDuration = Math.Max(1.0, durationSeconds);
}
/// <summary>
/// 获取动画状态
/// </summary>
public bool IsAnimating => _animationTimer != null && _animationTimer.Enabled;
/// <summary>
/// 获取 TimeLiner 是否可用
/// </summary>
public bool IsTimeLinerAvailable => _timeLinerManager?.IsTimeLinerAvailable ?? false;
/// <summary>
/// 获取当前动画状态
/// </summary>
public AnimationState CurrentState => _currentState;
/// <summary>
/// 获取当前 TimeLiner 任务ID
/// </summary>
public string CurrentTaskId => _currentTaskId;
/// <summary>
/// 获取 TimeLiner 集成管理器
/// </summary>
public TimeLinerIntegrationManager TimeLinerManager => _timeLinerManager;
/// <summary>
/// 资源清理
/// </summary>
public void Dispose()
{
StopAnimation();
ResetAnimation();
// 清理 TimeLiner 资源
if (_timeLinerManager != null)
{
try
{
_timeLinerManager.Dispose();
_timeLinerManager = null;
LogManager.Info("TimeLiner 集成资源已清理");
}
catch (Exception ex)
{
LogManager.Warning($"清理 TimeLiner 资源时出现警告: {ex.Message}");
}
}
}
/// <summary>
/// 快速测试TimeLiner API的可用性
/// </summary>
public bool TestTimeLinerAPI()
{
try
{
var doc = NavisApplication.ActiveDocument;
// 尝试访问TimeLiner
var timeliner = doc.Timeliner;
if (timeliner != null)
{
LogManager.Info("TimeLiner API基本可用");
return true;
}
else
{
LogManager.Info("TimeLiner API不可用");
return false;
}
}
catch (Exception ex)
{
LogManager.Info($"TimeLiner API测试失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 简化的动画设置方法,支持直接传入部件和路径
/// </summary>
/// <param name="component">部件模型</param>
/// <param name="pathPoints">路径点列表</param>
/// <param name="durationSeconds">动画持续时间(秒)</param>
public bool SetupSimpleAnimation(ModelItem component, List<Point3D> pathPoints, double durationSeconds = 10.0)
{
try
{
if (component == null)
{
LogManager.Error("部件模型不能为空");
return false;
}
if (pathPoints == null || pathPoints.Count < 2)
{
LogManager.Error("路径点数量必须至少为2个");
return false;
}
// 使用现有的SetupAnimation方法
SetupAnimation(component, pathPoints, durationSeconds);
LogManager.Info($"简化动画设置成功:部件={component.DisplayName}, 路径点数={pathPoints.Count}, 时长={durationSeconds}秒");
return true;
}
catch (Exception ex)
{
LogManager.Error($"简化动画设置失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 获取当前选中的部件模型(简化版本)
/// </summary>
/// <returns>选中的部件模型如果没有选中或选中多个则返回null</returns>
public static ModelItem GetSelectedComponent()
{
try
{
var doc = NavisApplication.ActiveDocument;
var selectedItems = doc.CurrentSelection.SelectedItems;
if (selectedItems.Count == 1)
{
var item = selectedItems.First;
if (item.HasGeometry)
{
LogManager.Info($"已获取选中部件: {item.DisplayName}");
return item;
}
else
{
LogManager.Warning("选中的项目没有几何体,可能不适合用于动画");
return null;
}
}
else if (selectedItems.Count == 0)
{
LogManager.Info("没有选中任何项目");
return null;
}
else
{
LogManager.Info($"选中了{selectedItems.Count}个项目,请只选择一个部件");
return null;
}
}
catch (Exception ex)
{
LogManager.Error($"获取选中部件失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 设置并触发状态变更事件
/// </summary>
/// <param name="newState">新的动画状态</param>
private void SetState(AnimationState newState)
{
if (_currentState != newState)
{
_currentState = newState;
StateChanged?.Invoke(this, _currentState);
}
}
/// <summary>
/// 创建动画
/// </summary>
/// <param name="animatedObject">要动画化的模型对象</param>
/// <param name="pathPoints">路径点列表</param>
/// <param name="durationSeconds">动画持续时间(秒)</param>
public void CreateAnimation(ModelItem animatedObject, List<Point3D> pathPoints, double durationSeconds = 10.0)
{
SetupAnimation(animatedObject, pathPoints, durationSeconds);
SetState(AnimationState.Ready);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -121,6 +121,8 @@ namespace NavisworksTransport
if (lastPoint.Type != PathPointType.StartPoint) // 起点不能是终点 if (lastPoint.Type != PathPointType.StartPoint) // 起点不能是终点
{ {
lastPoint.Type = PathPointType.EndPoint; lastPoint.Type = PathPointType.EndPoint;
// 更新名称以匹配新的类型
lastPoint.Name = GeneratePointName(PathPointType.EndPoint);
LogManager.Info($"已自动设置最后一个点为终点: {lastPoint.Name}"); LogManager.Info($"已自动设置最后一个点为终点: {lastPoint.Name}");
// 更新3D标记的外观 // 更新3D标记的外观
@ -3631,18 +3633,18 @@ namespace NavisworksTransport
LogManager.WriteLog($"[ToolPlugin事件] 精确坐标: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})"); LogManager.WriteLog($"[ToolPlugin事件] 精确坐标: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})");
LogManager.WriteLog($"[ToolPlugin事件] 选中对象: {pickResult.ModelItem?.DisplayName ?? "NULL"}"); LogManager.WriteLog($"[ToolPlugin事件] 选中对象: {pickResult.ModelItem?.DisplayName ?? "NULL"}");
// 检查是否在选定的通道 // 检查是否在可通行的物流模型
if (_selectedChannels != null && _selectedChannels.Any()) if (_selectedChannels != null && _selectedChannels.Any())
{ {
bool isInChannel = IsItemInSelectedChannels(pickResult.ModelItem) || bool isInTraversableLogisticsModel = IsItemInSelectedChannels(pickResult.ModelItem) ||
IsItemChildOfSelectedChannels(pickResult.ModelItem); IsItemChildOfSelectedChannels(pickResult.ModelItem);
LogManager.WriteLog($"[ToolPlugin事件] 在选定通道内: {isInChannel}"); LogManager.WriteLog($"[ToolPlugin事件] 在可通行的物流模型内: {isInTraversableLogisticsModel}");
if (isInChannel) if (isInTraversableLogisticsModel)
{ {
// 不再传递点类型让AddPathPointIn3D方法内部自动判断 // 不再传递点类型让AddPathPointIn3D方法内部自动判断
LogManager.WriteLog($"[ToolPlugin事件] 调用AddPathPointIn3D添加路径点"); LogManager.WriteLog("[ToolPlugin事件] 调用AddPathPointIn3D添加路径点");
var pathPoint = AddPathPointIn3D(pickResult.Point); var pathPoint = AddPathPointIn3D(pickResult.Point);
if (pathPoint != null) if (pathPoint != null)
@ -3656,12 +3658,15 @@ namespace NavisworksTransport
} }
else else
{ {
LogManager.WriteLog("[ToolPlugin事件] ✗ 点击位置不在选定通道内"); LogManager.WriteLog("[ToolPlugin事件] ✗ 点击位置不在可通行的物流模型内");
// 可以考虑给用户一个提示
OnStatusChanged("点击位置不是可通行的物流模型,请在可通行的物流模型上点击");
} }
} }
else else
{ {
LogManager.WriteLog("[ToolPlugin事件] ✗ 未选择通道"); LogManager.WriteLog("[ToolPlugin事件] ✗ 没有可通行的物流模型");
OnStatusChanged("没有找到任何可通行的物流模型,请先为模型设置可通行的物流属性");
} }
} }
catch (Exception ex) catch (Exception ex)
@ -3704,29 +3709,41 @@ namespace NavisworksTransport
_editingRoute = newRoute; _editingRoute = newRoute;
CurrentRoute = newRoute; CurrentRoute = newRoute;
// 自动选择所有物流通道 // 自动选择所有可通行的物流模型
AutoSelectLogisticsChannels(); AutoSelectLogisticsChannels();
// 检查是否有可通行的物流模型
if (_selectedChannels == null || _selectedChannels.Count == 0)
{
LogManager.Warning("没有找到任何可通行的物流模型");
OnErrorOccurred("没有找到任何可通行的物流模型,请先为模型设置可通行的物流属性");
// 重置状态
SwitchToViewingState();
return null;
}
// 智能管理ToolPlugin状态 // 智能管理ToolPlugin状态
ManageToolPluginForEditState(); ManageToolPluginForEditState();
// 高亮通道以便用户点击 // 高亮可通行的物流模型以便用户点击
HighlightLogisticsChannels(); HighlightLogisticsChannels();
LogManager.Info($"开始新建路径: {newRoute.Name}"); LogManager.Info($"开始新建路径: {newRoute.Name}");
OnStatusChanged($"正在新建路径: {newRoute.Name} - 请在3D视图中点击设置路径点"); OnStatusChanged($"正在新建路径: {newRoute.Name} - 请在3D视图中可通行的物流模型上点击设置路径点");
return newRoute; return newRoute;
} }
catch (Exception ex) catch (Exception ex)
{ {
OnErrorOccurred($"开始新建路径失败: {ex.Message}"); OnErrorOccurred($"开始新建路径失败: {ex.Message}");
// 确保在出错时也重置状态
SwitchToViewingState();
return null; return null;
} }
} }
/// <summary> /// <summary>
/// 自动选择所有物流通道 /// 自动选择所有可通行的物流模型
/// </summary> /// </summary>
private void AutoSelectLogisticsChannels() private void AutoSelectLogisticsChannels()
{ {
@ -3738,17 +3755,27 @@ namespace NavisworksTransport
_selectedChannels.Clear(); _selectedChannels.Clear();
// 收集所有物流标记的模型 // 收集所有物流标记的模型
var allLogisticsItems = new ModelItemCollection();
foreach (ModelItem rootItem in document.Models.RootItems) foreach (ModelItem rootItem in document.Models.RootItems)
{ {
foreach (ModelItem item in rootItem.DescendantsAndSelf) foreach (ModelItem item in rootItem.DescendantsAndSelf)
{ {
if (CategoryAttributeManager.HasLogisticsAttributes(item)) if (CategoryAttributeManager.HasLogisticsAttributes(item))
{ {
_selectedChannels.Add(item); allLogisticsItems.Add(item);
} }
} }
} }
// 筛选出可通行的物流模型
var traversableItems = CategoryAttributeManager.FilterTraversableItems(allLogisticsItems);
// 将可通行的物流模型添加到_selectedChannels
foreach (ModelItem item in traversableItems)
{
_selectedChannels.Add(item);
}
if (_selectedChannels.Count > 0) if (_selectedChannels.Count > 0)
{ {
// 计算组合边界 // 计算组合边界
@ -3757,54 +3784,50 @@ namespace NavisworksTransport
// 触发事件 // 触发事件
ChannelsSelected?.Invoke(this, _selectedChannels); ChannelsSelected?.Invoke(this, _selectedChannels);
LogManager.Info($"自动选择了 {_selectedChannels.Count} 个物流通道"); LogManager.Info($"自动选择了 {_selectedChannels.Count} 个可通行的物流模型");
} }
else else
{ {
LogManager.Warning("未找到任何物流通道"); LogManager.Warning("未找到任何可通行的物流模型");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
LogManager.WriteLog($"自动选择物流通道失败: {ex.Message}"); LogManager.WriteLog($"自动选择可通行物流模型失败: {ex.Message}");
} }
} }
/// <summary> /// <summary>
/// 高亮所有物流通道 /// 高亮所有可通行的物流模型
/// </summary> /// </summary>
private void HighlightLogisticsChannels() private void HighlightLogisticsChannels()
{ {
try try
{ {
var document = Application.ActiveDocument; // 直接使用已选择的可通行物流模型(_selectedChannels)
if (document?.Models == null) return; // _selectedChannels在AutoSelectLogisticsChannels()中已经被填充
var logisticsChannels = new ModelItemCollection(); if (_selectedChannels != null && _selectedChannels.Count > 0)
// 收集所有物流标记的模型
foreach (ModelItem rootItem in document.Models.RootItems)
{ {
foreach (ModelItem item in rootItem.DescendantsAndSelf) var logisticsChannels = new ModelItemCollection();
foreach (var item in _selectedChannels)
{ {
if (CategoryAttributeManager.HasLogisticsAttributes(item)) logisticsChannels.Add(item);
{
logisticsChannels.Add(item);
}
} }
}
// 高亮显示
// 高亮显示
if (logisticsChannels.Count > 0)
{
// 改为淡青色,避免与黄色连线冲突 // 改为淡青色,避免与黄色连线冲突
HighlightChannels(logisticsChannels, System.Drawing.Color.Cyan); HighlightChannels(logisticsChannels, System.Drawing.Color.Cyan);
LogManager.Info($"高亮显示 {logisticsChannels.Count} 个物流通道为青色"); LogManager.Info($"高亮显示 {_selectedChannels.Count} 个可通行的物流模型为青色");
}
else
{
LogManager.Info("没有可通行的物流模型需要高亮显示");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
LogManager.WriteLog($"高亮物流通道失败: {ex.Message}"); LogManager.WriteLog($"高亮可通行物流模型失败: {ex.Message}");
} }
} }

View File

@ -31,6 +31,7 @@ namespace NavisworksTransport
public const string PRIORITY = "优先级"; public const string PRIORITY = "优先级";
public const string VEHICLE_SIZE = "适用车辆尺寸"; public const string VEHICLE_SIZE = "适用车辆尺寸";
public const string SPEED_LIMIT = "速度限制"; public const string SPEED_LIMIT = "速度限制";
public const string WIDTH_LIMIT = "宽度限制";
} }
/// <summary> /// <summary>
@ -57,6 +58,7 @@ namespace NavisworksTransport
/// <param name="priority">优先级1-10</param> /// <param name="priority">优先级1-10</param>
/// <param name="vehicleSize">适用车辆尺寸</param> /// <param name="vehicleSize">适用车辆尺寸</param>
/// <param name="speedLimit">速度限制km/h</param> /// <param name="speedLimit">速度限制km/h</param>
/// <param name="widthLimit">宽度限制(米)</param>
/// <returns>成功处理的项目数量</returns> /// <returns>成功处理的项目数量</returns>
public static int AddLogisticsAttributes( public static int AddLogisticsAttributes(
ModelItemCollection items, ModelItemCollection items,
@ -64,7 +66,8 @@ namespace NavisworksTransport
bool isTraversable = true, bool isTraversable = true,
int priority = 5, int priority = 5,
string vehicleSize = "标准", string vehicleSize = "标准",
double speedLimit = 10.0) double speedLimit = 10.0,
double widthLimit = 3.0)
{ {
if (items == null || items.Count == 0) if (items == null || items.Count == 0)
{ {
@ -118,6 +121,10 @@ namespace NavisworksTransport
AddProperty(state, propertyCategory, LogisticsProperties.SPEED_LIMIT, AddProperty(state, propertyCategory, LogisticsProperties.SPEED_LIMIT,
speedLimit.ToString("F1") + " km/h", "SpeedLimit_Internal"); speedLimit.ToString("F1") + " km/h", "SpeedLimit_Internal");
// 创建并添加宽度限制属性
AddProperty(state, propertyCategory, LogisticsProperties.WIDTH_LIMIT,
widthLimit.ToString("F1") + " m", "WidthLimit_Internal");
if (existingIndex >= 0) if (existingIndex >= 0)
{ {
// 如果存在现有属性,使用覆盖模式 (参数1) // 如果存在现有属性,使用覆盖模式 (参数1)

30
src/Core/TestPlugin.cs Normal file
View File

@ -0,0 +1,30 @@
using System.Windows.Forms;
using Autodesk.Navisworks.Api.Plugins;
namespace NavisworksTransport
{
[Plugin("NavisworksTransport.Test", "Tian",
DisplayName = "Test Plugin",
ToolTip = "Simple test plugin")]
[DockPanePlugin(300, 200)]
public class TestPlugin : DockPanePlugin
{
public override Control CreateControlPane()
{
var label = new Label
{
Text = "Test Plugin Loaded Successfully!",
Dock = DockStyle.Fill,
TextAlign = System.Drawing.ContentAlignment.MiddleCenter,
BackColor = System.Drawing.Color.LightGreen
};
return label;
}
public override void DestroyControlPane(Control pane)
{
pane?.Dispose();
}
}
}

View File

@ -0,0 +1,29 @@
# Navisworks Transport Plugin Localization File
# This file contains localized strings for the plugin
# Plugin Display Names
HelloWorldText=物流路径规划插件已加载
PluginDisplayName=物流路径规划
PluginToolTip=物流路径规划和动画仿真插件
# Tab Names
ModelSettingsTab=类别设置
PathEditingTab=路径编辑
AnimationControlTab=检测动画
SystemManagementTab=系统管理
# Button Labels
ShowAllButton=显示全部
HideAllButton=隐藏全部
RefreshButton=刷新
NewPathButton=新建路径
StartEditButton=开始编辑
StartAnimationButton=开始动画
HelpButton=帮助
AboutButton=关于
# Status Messages
InitializingPlugin=正在初始化插件...
PluginReady=插件就绪
SelectModelsInstruction=请在主界面中点击选择模型
OpenModelInstruction=请先打开一个Navisworks模型文件

View File

@ -0,0 +1,205 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using Autodesk.Navisworks.Api;
using NavisApplication = Autodesk.Navisworks.Api.Application;
namespace NavisworksTransport.UI.Forms
{
/// <summary>
/// 简单的DockPane控件用于验证DockPanePlugin基础功能
/// </summary>
public partial class SimpleDockPaneControl : UserControl
{
private Label _statusLabel;
private Button _testButton;
private ListView _infoListView;
public SimpleDockPaneControl()
{
InitializeComponent();
InitializeSession();
}
private void InitializeComponent()
{
this.SuspendLayout();
// 设置控件基本属性
this.Size = new Size(400, 600);
this.BackColor = SystemColors.Control;
this.Padding = new Padding(10);
// 创建状态标签
_statusLabel = new Label
{
Text = "物流路径规划插件 - DockPane模式",
Font = new Font("微软雅黑", 10, FontStyle.Bold),
ForeColor = System.Drawing.Color.DarkBlue,
AutoSize = true,
Location = new Point(10, 10)
};
this.Controls.Add(_statusLabel);
// 创建测试按钮
_testButton = new Button
{
Text = "测试功能",
Size = new Size(100, 30),
Location = new Point(10, 40),
Font = new Font("微软雅黑", 9)
};
_testButton.Click += OnTestButtonClick;
this.Controls.Add(_testButton);
// 创建信息列表
_infoListView = new ListView
{
View = System.Windows.Forms.View.Details,
FullRowSelect = true,
GridLines = true,
Location = new Point(10, 80),
Size = new Size(380, 500),
Font = new Font("微软雅黑", 8)
};
// 添加列
_infoListView.Columns.Add("时间", 120);
_infoListView.Columns.Add("事件", 100);
_infoListView.Columns.Add("描述", 160);
this.Controls.Add(_infoListView);
this.ResumeLayout(false);
}
private void InitializeSession()
{
GlobalExceptionHandler.SafeExecute(() =>
{
LogManager.Info("=== SimpleDockPaneControl 初始化开始 ===");
// 初始化全局异常处理
GlobalExceptionHandler.Initialize();
// 订阅选择变化事件
if (NavisApplication.ActiveDocument != null)
{
NavisApplication.ActiveDocument.CurrentSelection.Changed += OnSelectionChanged;
}
// 添加初始化信息
AddInfoItem("初始化", "插件已加载");
LogManager.Info("SimpleDockPaneControl 初始化完成");
}, "初始化SimpleDockPaneControl");
}
private void OnTestButtonClick(object sender, EventArgs e)
{
GlobalExceptionHandler.SafeExecute(() =>
{
LogManager.Info("测试按钮被点击");
// 获取当前文档信息
var doc = NavisApplication.ActiveDocument;
if (doc != null)
{
var modelCount = doc.Models?.Count ?? 0;
var selectedCount = doc.CurrentSelection?.SelectedItems?.Count ?? 0;
AddInfoItem("测试", $"模型数量: {modelCount}, 选中: {selectedCount}");
MessageBox.Show($"当前文档信息:\n模型数量: {modelCount}\n选中项目: {selectedCount}",
"测试结果", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
AddInfoItem("测试", "没有打开的文档");
MessageBox.Show("请先打开一个Navisworks文档",
"提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}, "测试按钮点击");
}
private void OnSelectionChanged(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new Action(() => OnSelectionChanged(sender, e)));
return;
}
try
{
var selectedCount = NavisApplication.ActiveDocument?.CurrentSelection?.SelectedItems?.Count ?? 0;
AddInfoItem("选择变更", $"选中 {selectedCount} 个项目");
}
catch (Exception ex)
{
LogManager.Error($"处理选择变更事件失败: {ex.Message}");
}
}
private void AddInfoItem(string eventType, string description)
{
try
{
if (this.InvokeRequired)
{
this.BeginInvoke(new Action(() => AddInfoItem(eventType, description)));
return;
}
var item = new ListViewItem(DateTime.Now.ToString("HH:mm:ss"));
item.SubItems.Add(eventType);
item.SubItems.Add(description);
_infoListView.Items.Insert(0, item);
// 保持最多50条记录
while (_infoListView.Items.Count > 50)
{
_infoListView.Items.RemoveAt(_infoListView.Items.Count - 1);
}
}
catch (Exception ex)
{
LogManager.Error($"添加信息项失败: {ex.Message}");
}
}
public void Cleanup()
{
try
{
LogManager.Info("开始清理SimpleDockPaneControl资源");
// 取消事件订阅
if (NavisApplication.ActiveDocument != null)
{
NavisApplication.ActiveDocument.CurrentSelection.Changed -= OnSelectionChanged;
}
AddInfoItem("清理", "资源已清理");
LogManager.Info("SimpleDockPaneControl资源清理完成");
}
catch (Exception ex)
{
LogManager.Error($"清理SimpleDockPaneControl资源失败: {ex.Message}");
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
Cleanup();
}
base.Dispose(disposing);
}
}
}

View File

@ -0,0 +1,102 @@
using System;
using System.Windows.Input;
namespace NavisworksTransport.UI.WPF.ViewModels
{
/// <summary>
/// 通用命令实现用于MVVM模式
/// </summary>
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="execute">执行的操作</param>
/// <param name="canExecute">是否可以执行的判断</param>
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
/// <summary>
/// 判断命令是否可以执行
/// </summary>
/// <param name="parameter">命令参数</param>
/// <returns>是否可以执行</returns>
public bool CanExecute(object parameter)
{
return _canExecute?.Invoke() ?? true;
}
/// <summary>
/// 执行命令
/// </summary>
/// <param name="parameter">命令参数</param>
public void Execute(object parameter)
{
_execute();
}
/// <summary>
/// 可执行状态变更事件
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
/// <summary>
/// 带参数的通用命令实现
/// </summary>
/// <typeparam name="T">参数类型</typeparam>
public class RelayCommand<T> : ICommand
{
private readonly Action<T> _execute;
private readonly Func<T, bool> _canExecute;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="execute">执行的操作</param>
/// <param name="canExecute">是否可以执行的判断</param>
public RelayCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
/// <summary>
/// 判断命令是否可以执行
/// </summary>
/// <param name="parameter">命令参数</param>
/// <returns>是否可以执行</returns>
public bool CanExecute(object parameter)
{
return _canExecute?.Invoke((T)parameter) ?? true;
}
/// <summary>
/// 执行命令
/// </summary>
/// <param name="parameter">命令参数</param>
public void Execute(object parameter)
{
_execute((T)parameter);
}
/// <summary>
/// 可执行状态变更事件
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}

View File

@ -0,0 +1,217 @@
<UserControl x:Class="NavisworksTransport.UI.WPF.LogisticsControlPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="700" d:DesignWidth="420">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 主内容区域 -->
<TabControl Grid.Row="0" Name="MainTabControl" Margin="5">
<TabItem Header="类别设置" Name="ModelSettingsTab">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="10">
<Label Content="{Binding InstructionText}"
FontWeight="Bold"
Foreground="Orange"/>
<Label Content="{Binding SelectedModelsText}"
Foreground="DarkBlue"/>
<GroupBox Header="类别属性设置" Margin="0,10">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Label Content="物流类别:" Width="80"/>
<ComboBox ItemsSource="{Binding AvailableCategories}"
SelectedItem="{Binding SelectedCategory}"
Width="120" Margin="0,0,10,0"/>
<Label Content="宽度限制:" Width="60"/>
<TextBox Text="{Binding WidthLimit}"
Width="60" Margin="0,0,5,0"/>
<Label Content="米" Width="20"/>
<Button Content="设置属性" Command="{Binding SetLogisticsAttributeCommand}"/>
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox Header="物流模型列表" Margin="0,10" Height="200">
<ListView ItemsSource="{Binding LogisticsModels}"
SelectedItem="{Binding SelectedLogisticsModel}"
Margin="10">
<ListView.View>
<GridView>
<GridViewColumn Header="名称" DisplayMemberBinding="{Binding Name}" Width="120"/>
<GridViewColumn Header="类别" DisplayMemberBinding="{Binding Category}" Width="80"/>
<GridViewColumn Header="属性" DisplayMemberBinding="{Binding Attributes}" Width="100"/>
<GridViewColumn Header="可见" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsVisible}" HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</GroupBox>
<Label Content="{Binding StatusText}"
Foreground="DarkGreen"/>
<GroupBox Header="可见性控制" Margin="0,10">
<StackPanel Margin="10">
<CheckBox Content="仅显示物流元素"
IsChecked="{Binding IsLogisticsOnlyMode}"
ToolTip="开启后只显示物流相关元素,关闭后显示所有元素"
FontWeight="Bold"
Margin="0,5"/>
<Label Content="提示: 开启此选项可提高大模型性能,物流模型列表会自动更新"
FontSize="10" Foreground="Gray"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="路径编辑" Name="PathEditingTab">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="10">
<GroupBox Header="路径列表管理" Margin="0,10" Height="160">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="新建" Command="{Binding NewPathCommand}" Margin="0,0,10,0"/>
<Button Content="删除" Command="{Binding DeletePathCommand}" Margin="0,0,10,0"/>
</StackPanel>
<ListView ItemsSource="{Binding PathRoutes}"
SelectedItem="{Binding SelectedPathRoute}"
Height="100">
<ListView.View>
<GridView>
<GridViewColumn Header="路径名称" DisplayMemberBinding="{Binding Name}" Width="120"/>
<GridViewColumn Header="点数" DisplayMemberBinding="{Binding PointCount}" Width="50"/>
<GridViewColumn Header="状态" DisplayMemberBinding="{Binding StatusString}" Width="60"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</GroupBox>
<GroupBox Header="当前路径编辑" Margin="0,10" Height="250">
<StackPanel Margin="10">
<Label Content="{Binding SelectedPathRoute.Name, StringFormat='当前路径: {0}'}"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="开始编辑" Command="{Binding StartEditCommand}" Margin="0,0,10,0"/>
<Button Content="结束编辑" Command="{Binding EndEditCommand}" Margin="0,0,10,0"/>
</StackPanel>
<ListView ItemsSource="{Binding SelectedPathRoute.Points}" Height="150">
<ListView.View>
<GridView>
<GridViewColumn Header="点名称" DisplayMemberBinding="{Binding Name}" Width="80"/>
<GridViewColumn Header="坐标" DisplayMemberBinding="{Binding CoordinateString}" Width="150"/>
<GridViewColumn Header="类型" DisplayMemberBinding="{Binding TypeDisplayString}" Width="80"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</GroupBox>
<GroupBox Header="路径文件管理" Margin="0,10">
<StackPanel Orientation="Horizontal" Margin="10">
<Button Content="导入路径" Name="ImportPathButton" Margin="0,0,10,0"/>
<Button Content="导出路径" Name="ExportPathButton"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="检测动画" Name="AnimationControlTab">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="10">
<GroupBox Header="动画参数设置" Margin="0,10" Height="160">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Label Content="动画速度:" Width="80"/>
<Slider Name="SpeedSlider" Width="200" Minimum="0.1" Maximum="5.0" Value="1.0"/>
<Label Content="1.0x" Name="SpeedLabel" Width="40"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Label Content="帧率:" Width="80"/>
<ComboBox Name="FrameRateComboBox" Width="100"/>
</StackPanel>
<CheckBox Content="循环播放" Name="LoopCheckBox" Margin="0,5"/>
</StackPanel>
</GroupBox>
<GroupBox Header="播放控制" Margin="0,10" Height="130">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="开始动画" Command="{Binding StartAnimationCommand}" Margin="0,0,10,0"/>
<Button Content="停止动画" Command="{Binding StopAnimationCommand}" Margin="0,0,10,0"/>
</StackPanel>
<Label Content="{Binding AnimationStatus}"/>
<ProgressBar Value="{Binding AnimationProgress}" Height="20" Margin="0,5" Maximum="100"/>
</StackPanel>
</GroupBox>
<GroupBox Header="碰撞检测" Margin="0,10">
<StackPanel Orientation="Horizontal" Margin="10">
<Button Content="运行碰撞检测" Name="RunCollisionButton" Margin="0,0,10,0"/>
<Button Content="查看报告" Name="ViewReportButton"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="系统管理" Name="SystemManagementTab">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="10">
<GroupBox Header="模型分层拆分" Margin="0,10" Height="100">
<StackPanel Margin="10">
<Button Content="模型分层拆分" Command="{Binding ModelSplitterCommand}" Margin="0,0,0,10"/>
<Label Content="将大型模型按楼层或属性拆分为多个文件"
Foreground="Gray" FontSize="10"/>
</StackPanel>
</GroupBox>
<GroupBox Header="日志管理" Margin="0,10" Height="100">
<StackPanel Orientation="Horizontal" Margin="10">
<Button Content="查看日志" Name="ViewLogButton" Margin="0,0,10,0"/>
<Button Content="清空日志" Name="ClearLogButton" Margin="0,0,10,0"/>
<Button Content="导出日志" Name="ExportLogButton"/>
</StackPanel>
</GroupBox>
<GroupBox Header="插件设置" Margin="0,10" Height="100">
<StackPanel Orientation="Horizontal" Margin="10">
<Button Content="设置选项" Name="SettingsButton" Margin="0,0,10,0"/>
<Button Content="重置设置" Name="ResetSettingsButton"/>
</StackPanel>
</GroupBox>
<GroupBox Header="系统信息" Margin="0,10" Height="160">
<StackPanel Margin="10">
<Label Content="插件版本: v1.0" Name="VersionLabel"/>
<Label Content="Navisworks版本: 2026" Name="NavisVersionLabel"/>
<Label Content="系统状态: 正常" Name="SystemStatusLabel"/>
<Button Content="检查更新" Name="CheckUpdateButton" Margin="0,10,0,0"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</TabItem>
</TabControl>
<!-- 底部操作栏 -->
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="10">
<Button Content="帮助" Click="HelpButton_Click" Margin="0,0,10,0"/>
<Button Content="关于" Click="AboutButton_Click" Margin="0,0,10,0"/>
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,194 @@
using System;
using System.Windows;
using System.Windows.Controls;
using Autodesk.Navisworks.Api;
using NavisworksTransport.UI.WPF.ViewModels;
using NavisApplication = Autodesk.Navisworks.Api.Application;
namespace NavisworksTransport.UI.WPF
{
/// <summary>
/// LogisticsControlPanel.xaml 的交互逻辑
/// </summary>
public partial class LogisticsControlPanel : UserControl
{
// ViewModel实例
public LogisticsControlViewModel ViewModel { get; private set; }
// 管理器实例
private PathPlanningManager _pathPlanningManager;
private static bool _isSessionInitialized = false;
public LogisticsControlPanel()
{
InitializeComponent();
// 创建并设置ViewModel
ViewModel = new LogisticsControlViewModel();
DataContext = ViewModel;
InitializeSession();
InitializeEventHandlers();
}
// 注意现在使用XAML定义的界面不再需要手动创建
/// <summary>
/// 初始化插件会话
/// </summary>
private void InitializeSession()
{
GlobalExceptionHandler.SafeExecute(() =>
{
if (!_isSessionInitialized)
{
LogManager.Info("=== 物流路径规划插件初始化开始 ===");
// 初始化全局异常处理
GlobalExceptionHandler.Initialize();
// 初始化路径规划管理器
InitializePathPlanningManager();
LogManager.Info("插件会话初始化完成");
_isSessionInitialized = true;
}
}, "初始化会话");
}
/// <summary>
/// 初始化路径规划管理器并设置事件订阅
/// </summary>
private void InitializePathPlanningManager()
{
try
{
LogManager.Info("开始初始化PathPlanningManager");
// 创建或获取PathPlanningManager实例
_pathPlanningManager = PathPlanningManager.GetActivePathManager() ?? new PathPlanningManager();
// TODO: 后续任务中将重新实现事件订阅
// 暂时注释掉事件订阅,避免编译错误
/*
_pathPlanningManager.PathEditStateChanged += OnPathEditStateChanged;
_pathPlanningManager.PathPointAddedIn3D += OnPathPointAddedIn3D;
_pathPlanningManager.PathPointRemovedFrom3D += OnPathPointRemovedFrom3D;
_pathPlanningManager.PathPointsListUpdated += OnPathPointsListUpdated;
_pathPlanningManager.CurrentRouteChanged += OnCurrentRouteChanged;
_pathPlanningManager.StatusChanged += OnPathManagerStatusChanged;
_pathPlanningManager.ErrorOccurred += OnPathManagerErrorOccurred;
*/
LogManager.Info("PathPlanningManager初始化完成");
}
catch (Exception ex)
{
LogManager.Error($"初始化PathPlanningManager失败: {ex.Message}");
}
}
/// <summary>
/// 初始化事件处理器
/// </summary>
private void InitializeEventHandlers()
{
try
{
// 订阅Navisworks选择变化事件
if (NavisApplication.ActiveDocument != null)
{
NavisApplication.ActiveDocument.CurrentSelection.Changed += OnSelectionChanged;
}
LogManager.Info("事件处理器初始化完成");
}
catch (Exception ex)
{
LogManager.Error($"初始化事件处理器失败: {ex.Message}");
}
}
/// <summary>
/// 帮助按钮点击事件
/// </summary>
private void HelpButton_Click(object sender, RoutedEventArgs e)
{
GlobalExceptionHandler.SafeExecute(() =>
{
MessageBox.Show("物流路径规划插件使用说明:\n\n1. 类别设置:设置对象类别和可见性\n2. 路径编辑创建和管理3D路径\n3. 动画控制:创建路径动画\n4. 系统管理:插件设置和日志",
"帮助", MessageBoxButton.OK, MessageBoxImage.Information);
}, "显示帮助");
}
/// <summary>
/// 关于按钮点击事件
/// </summary>
private void AboutButton_Click(object sender, RoutedEventArgs e)
{
GlobalExceptionHandler.SafeExecute(() =>
{
MessageBox.Show("物流路径规划插件 v1.0\n\n适用于 Navisworks 2026\n\n功能3D路径规划、动画创建、碰撞检测",
"关于插件", MessageBoxButton.OK, MessageBoxImage.Information);
}, "显示关于");
}
/// <summary>
/// 清理资源和事件订阅
/// </summary>
public void Cleanup()
{
try
{
LogManager.Info("开始清理LogisticsControlPanel资源");
// 取消Navisworks事件订阅
if (NavisApplication.ActiveDocument != null)
{
NavisApplication.ActiveDocument.CurrentSelection.Changed -= OnSelectionChanged;
}
// TODO: 清理路径规划管理器事件订阅
// 暂时注释掉,避免编译错误
/*
if (_pathPlanningManager != null)
{
_pathPlanningManager.PathEditStateChanged -= OnPathEditStateChanged;
_pathPlanningManager.PathPointAddedIn3D -= OnPathPointAddedIn3D;
_pathPlanningManager.PathPointRemovedFrom3D -= OnPathPointRemovedFrom3D;
_pathPlanningManager.PathPointsListUpdated -= OnPathPointsListUpdated;
_pathPlanningManager.CurrentRouteChanged -= OnCurrentRouteChanged;
_pathPlanningManager.StatusChanged -= OnPathManagerStatusChanged;
_pathPlanningManager.ErrorOccurred -= OnPathManagerErrorOccurred;
}
*/
// 清理 Clash Detective 集成
GlobalExceptionHandler.CleanupClashDetectiveIntegration();
LogManager.Info("LogisticsControlPanel资源清理完成");
}
catch (Exception ex)
{
LogManager.Error($"清理LogisticsControlPanel资源失败: {ex.Message}");
}
}
#region
private void OnSelectionChanged(object sender, EventArgs e)
{
// 使用Dispatcher确保在UI线程上更新ViewModel
Dispatcher.BeginInvoke(new Action(() =>
{
ViewModel?.UpdateSelectionDisplay();
ViewModel?.UpdateInstructionText();
}));
}
// TODO: 其他事件处理器将在后续任务中实现
// 暂时注释掉避免编译错误
#endregion
}
}

View File

@ -0,0 +1,72 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using Autodesk.Navisworks.Api;
namespace NavisworksTransport.UI.WPF.ViewModels
{
/// <summary>
/// 物流模型数据类
/// </summary>
public class LogisticsModel : INotifyPropertyChanged
{
private string _name;
private string _category;
private string _attributes;
private bool _isVisible;
/// <summary>
/// 模型名称
/// </summary>
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
/// <summary>
/// 模型类别
/// </summary>
public string Category
{
get => _category;
set => SetProperty(ref _category, value);
}
/// <summary>
/// 模型属性
/// </summary>
public string Attributes
{
get => _attributes;
set => SetProperty(ref _attributes, value);
}
/// <summary>
/// 是否可见
/// </summary>
public bool IsVisible
{
get => _isVisible;
set => SetProperty(ref _isVisible, value);
}
/// <summary>
/// 关联的Navisworks模型项
/// </summary>
public ModelItem NavisworksItem { get; set; }
#region INotifyPropertyChanged实现
public event PropertyChangedEventHandler PropertyChanged;
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}

View File

@ -0,0 +1,197 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace NavisworksTransport.UI.WPF.ViewModels
{
/// <summary>
/// 路径点数据类 (UI ViewModel)
/// </summary>
public class PathPointViewModel : INotifyPropertyChanged
{
private double _x;
private double _y;
private double _z;
private string _name;
private NavisworksTransport.PathPointType _type; // 使用完全限定名
/// <summary>
/// X坐标
/// </summary>
public double X
{
get => _x;
set => SetProperty(ref _x, value);
}
/// <summary>
/// Y坐标
/// </summary>
public double Y
{
get => _y;
set => SetProperty(ref _y, value);
}
/// <summary>
/// Z坐标
/// </summary>
public double Z
{
get => _z;
set => SetProperty(ref _z, value);
}
/// <summary>
/// 点名称
/// </summary>
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
/// <summary>
/// 点类型
/// </summary>
public NavisworksTransport.PathPointType Type // 使用完全限定名
{
get => _type;
set => SetProperty(ref _type, value);
}
/// <summary>
/// 点类型的显示字符串
/// </summary>
public string TypeDisplayString
{
get
{
switch (Type)
{
case NavisworksTransport.PathPointType.StartPoint:
return "起点";
case NavisworksTransport.PathPointType.EndPoint:
return "终点";
case NavisworksTransport.PathPointType.WayPoint:
return "路径点";
default:
return Type.ToString();
}
}
}
/// <summary>
/// 坐标字符串表示
/// </summary>
public string CoordinateString => $"({X:F2}, {Y:F2}, {Z:F2})";
#region INotifyPropertyChanged实现
public event PropertyChangedEventHandler PropertyChanged;
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// 当 Type 属性变更时,也需要通知 TypeDisplayString 变更
if (propertyName == nameof(Type))
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TypeDisplayString)));
}
}
#endregion
}
/// <summary>
/// 路径视图模型类 - 用于WPF数据绑定
/// </summary>
public class PathRouteViewModel : INotifyPropertyChanged
{
private string _name;
private ObservableCollection<PathPointViewModel> _points;
private bool _isActive;
private string _description;
/// <summary>
/// 路径名称
/// </summary>
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
/// <summary>
/// 路径点集合
/// </summary>
public ObservableCollection<PathPointViewModel> Points
{
get => _points;
set => SetProperty(ref _points, value);
}
/// <summary>
/// 是否为活动路径
/// </summary>
public bool IsActive
{
get => _isActive;
set => SetProperty(ref _isActive, value);
}
/// <summary>
/// 路径描述
/// </summary>
public string Description
{
get => _description;
set => SetProperty(ref _description, value);
}
/// <summary>
/// 路径点数量
/// </summary>
public int PointCount => Points?.Count ?? 0;
/// <summary>
/// 路径状态字符串
/// </summary>
public string StatusString => IsActive ? "活动" : "非活动";
#region INotifyPropertyChanged实现
public event PropertyChangedEventHandler PropertyChanged;
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// 特殊处理当Points变更时通知PointCount变更
if (propertyName == nameof(Points))
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PointCount)));
}
}
#endregion
/// <summary>
/// 构造函数
/// </summary>
public PathRouteViewModel()
{
Points = new ObservableCollection<PathPointViewModel>();
Points.CollectionChanged += (s, e) =>
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PointCount)));
};
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,82 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
namespace NavisworksTransport.UI.WPF.ViewModels
{
/// <summary>
/// ViewModel基类实现INotifyPropertyChanged接口
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 触发属性变更通知
/// </summary>
/// <param name="propertyName">属性名称,自动获取调用者名称</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// 设置属性值并触发变更通知
/// </summary>
/// <typeparam name="T">属性类型</typeparam>
/// <param name="field">字段引用</param>
/// <param name="value">新值</param>
/// <param name="propertyName">属性名称,自动获取调用者名称</param>
/// <returns>如果值发生变化返回true</returns>
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// 安全执行操作,捕获异常
/// </summary>
/// <param name="action">要执行的操作</param>
/// <param name="operationName">操作名称</param>
protected void SafeExecute(Action action, string operationName = "操作")
{
try
{
action();
}
catch (Exception ex)
{
LogManager.Error($"[ViewModel] {operationName}失败: {ex.Message}");
LogManager.Error($"[ViewModel] 堆栈信息: {ex.StackTrace}");
}
}
/// <summary>
/// 安全执行操作,捕获异常并返回结果
/// </summary>
/// <typeparam name="T">返回类型</typeparam>
/// <param name="func">要执行的函数</param>
/// <param name="defaultValue">默认返回值</param>
/// <param name="operationName">操作名称</param>
/// <returns>执行结果或默认值</returns>
protected T SafeExecute<T>(Func<T> func, T defaultValue = default(T), string operationName = "操作")
{
try
{
return func();
}
catch (Exception ex)
{
LogManager.Error($"[ViewModel] {operationName}失败: {ex.Message}");
LogManager.Error($"[ViewModel] 堆栈信息: {ex.StackTrace}");
return defaultValue;
}
}
}
}

View File

@ -0,0 +1,76 @@
<UserControl x:Class="NavisworksTransport.UI.WPF.Views.AnimationControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="400">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="10">
<GroupBox Header="动画参数设置" Margin="0,10" Height="180">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Label Content="动画速度:" Width="80"/>
<Slider Value="{Binding AnimationSpeed}" Width="150" Minimum="0.1" Maximum="5.0"
TickFrequency="0.1" IsSnapToTickEnabled="True" Margin="0,0,10,0"/>
<Label Content="{Binding AnimationSpeed, StringFormat={}{0:F1}x}" Width="40"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Label Content="帧率:" Width="80"/>
<ComboBox ItemsSource="{Binding AvailableFrameRates}"
SelectedItem="{Binding SelectedFrameRate}" Width="100"/>
<Label Content="fps" Margin="5,0,0,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Label Content="持续时间:" Width="80"/>
<TextBox Text="{Binding AnimationDuration}" Width="60" Margin="0,0,5,0"/>
<Label Content="秒" Width="20"/>
</StackPanel>
<CheckBox Content="循环播放" IsChecked="{Binding IsLoopEnabled}" Margin="0,5"/>
<CheckBox Content="平滑过渡" IsChecked="{Binding IsSmoothTransition}" Margin="0,5"/>
</StackPanel>
</GroupBox>
<GroupBox Header="播放控制" Margin="0,10" Height="150">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="开始动画" Command="{Binding StartAnimationCommand}"
IsEnabled="{Binding CanStartAnimation}" Margin="0,0,10,0"/>
<Button Content="暂停动画" Command="{Binding PauseAnimationCommand}"
IsEnabled="{Binding CanPauseAnimation}" Margin="0,0,10,0"/>
<Button Content="停止动画" Command="{Binding StopAnimationCommand}"
IsEnabled="{Binding CanStopAnimation}"/>
</StackPanel>
<Label Content="{Binding AnimationStatus}" Margin="0,5"/>
<StackPanel Orientation="Horizontal" Margin="0,5">
<Label Content="进度:" Width="50"/>
<ProgressBar Value="{Binding AnimationProgress}" Height="20" Width="200" Maximum="100"/>
<Label Content="{Binding AnimationProgress, StringFormat={}{0:F0}%}" Margin="10,0,0,0"/>
</StackPanel>
<Label Content="{Binding CurrentAnimationTime, StringFormat='当前时间: {0:F1}s'}" FontSize="10"/>
</StackPanel>
</GroupBox>
<GroupBox Header="碰撞检测" Margin="0,10" Height="120">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="运行碰撞检测" Command="{Binding RunCollisionDetectionCommand}"
IsEnabled="{Binding CanRunCollisionDetection}" Margin="0,0,10,0"/>
<Button Content="查看报告" Command="{Binding ViewCollisionReportCommand}"
IsEnabled="{Binding HasCollisionResults}"/>
</StackPanel>
<Label Content="{Binding CollisionStatus}" Foreground="DarkRed"/>
<Label Content="{Binding CollisionSummary}" FontSize="10" Foreground="Gray"/>
</StackPanel>
</GroupBox>
<GroupBox Header="动画预览" Margin="0,10">
<StackPanel Margin="10">
<Label Content="• 确保已选择有效的路径进行动画" FontSize="10"/>
<Label Content="• 动画将沿着当前选中的路径播放" FontSize="10"/>
<Label Content="• 碰撞检测会在动画播放时自动运行" FontSize="10"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace NavisworksTransport.UI.WPF.Views
{
/// <summary>
/// AnimationControlView.xaml 的交互逻辑
/// </summary>
public partial class AnimationControlView : UserControl
{
public AnimationControlView()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,75 @@
<UserControl x:Class="NavisworksTransport.UI.WPF.Views.ModelSettingsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="400">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="10">
<Label Content="{Binding InstructionText}"
FontWeight="Bold"
Foreground="Orange"/>
<Label Content="{Binding SelectedModelsText}"
Foreground="DarkBlue"/>
<GroupBox Header="类别属性设置" Margin="0,10">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Label Content="物流类别:" Width="80"/>
<ComboBox ItemsSource="{Binding AvailableCategories}"
SelectedItem="{Binding SelectedCategory}"
Width="120" Margin="0,0,10,0"/>
<Label Content="宽度限制:" Width="60"/>
<TextBox Text="{Binding WidthLimit}"
Width="60" Margin="0,0,5,0"/>
<Label Content="米" Width="20"/>
<Button Content="设置属性" Command="{Binding SetLogisticsAttributeCommand}"/>
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox Header="物流模型列表" Margin="0,10" Height="200">
<ListView ItemsSource="{Binding LogisticsModels}"
SelectedItem="{Binding SelectedLogisticsModel}"
Margin="10">
<ListView.View>
<GridView>
<GridViewColumn Header="名称" DisplayMemberBinding="{Binding Name}" Width="120"/>
<GridViewColumn Header="类别" DisplayMemberBinding="{Binding Category}" Width="80"/>
<GridViewColumn Header="属性" DisplayMemberBinding="{Binding Attributes}" Width="100"/>
<GridViewColumn Header="可见" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsVisible}" HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</GroupBox>
<Label Content="{Binding StatusText}"
Foreground="DarkGreen"/>
<GroupBox Header="可见性控制" Margin="0,10">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Button Content="仅显示物流" Command="{Binding ShowLogisticsOnlyCommand}"
ToolTip="隐藏非物流元素,突出显示物流相关模型"
Margin="0,0,10,0"/>
<Button Content="显示全部" Command="{Binding ShowAllCommand}"
ToolTip="显示所有模型元素"
Margin="0,0,10,0"/>
<Button Content="刷新物流" Command="{Binding RefreshCommand}"
ToolTip="重新扫描和更新物流模型列表"/>
</StackPanel>
<Label Content="提示: 使用'仅显示物流'可提高大模型性能"
FontSize="10" Foreground="Gray"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace NavisworksTransport.UI.WPF.Views
{
/// <summary>
/// ModelSettingsView.xaml 的交互逻辑
/// </summary>
public partial class ModelSettingsView : UserControl
{
public ModelSettingsView()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,78 @@
<UserControl x:Class="NavisworksTransport.UI.WPF.Views.PathEditingView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="400">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="10">
<GroupBox Header="路径列表管理" Margin="0,10" Height="160">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="新建" Command="{Binding NewPathCommand}" Margin="0,0,10,0"/>
<Button Content="删除" Command="{Binding DeletePathCommand}" Margin="0,0,10,0"/>
<Button Content="重命名" Command="{Binding RenamePathCommand}"/>
</StackPanel>
<ListView ItemsSource="{Binding PathRoutes}"
SelectedItem="{Binding SelectedPathRoute}"
Height="100">
<ListView.View>
<GridView>
<GridViewColumn Header="路径名称" DisplayMemberBinding="{Binding Name}" Width="120"/>
<GridViewColumn Header="点数" DisplayMemberBinding="{Binding PointCount}" Width="50"/>
<GridViewColumn Header="状态" DisplayMemberBinding="{Binding StatusString}" Width="60"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</GroupBox>
<GroupBox Header="当前路径编辑" Margin="0,10" Height="250">
<StackPanel Margin="10">
<Label Content="{Binding SelectedPathRoute.Name, StringFormat='当前路径: {0}'}"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="开始编辑" Command="{Binding StartEditCommand}" Margin="0,0,10,0"/>
<Button Content="结束编辑" Command="{Binding EndEditCommand}" Margin="0,0,10,0"/>
<Button Content="清空路径" Command="{Binding ClearPathCommand}"/>
</StackPanel>
<ListView ItemsSource="{Binding SelectedPathRoute.Points}" Height="150">
<ListView.View>
<GridView>
<GridViewColumn Header="点名称" DisplayMemberBinding="{Binding Name}" Width="80"/>
<GridViewColumn Header="坐标" DisplayMemberBinding="{Binding CoordinateString}" Width="150"/>
<GridViewColumn Header="操作" Width="60">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="删除" Command="{Binding DataContext.DeletePointCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding}" FontSize="10" Padding="2"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</GroupBox>
<GroupBox Header="路径文件管理" Margin="0,10">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="导入路径" Command="{Binding ImportPathCommand}" Margin="0,0,10,0"/>
<Button Content="导出路径" Command="{Binding ExportPathCommand}" Margin="0,0,10,0"/>
<Button Content="另存为" Command="{Binding SaveAsPathCommand}"/>
</StackPanel>
<Label Content="{Binding PathFileStatus}" Foreground="DarkBlue" FontSize="10"/>
</StackPanel>
</GroupBox>
<GroupBox Header="3D交互提示" Margin="0,10">
<StackPanel Margin="10">
<Label Content="• 开始编辑后在3D视图中点击添加路径点" FontSize="10"/>
<Label Content="• 右键点击路径点可删除" FontSize="10"/>
<Label Content="• 拖拽路径点可调整位置" FontSize="10"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace NavisworksTransport.UI.WPF.Views
{
/// <summary>
/// PathEditingView.xaml 的交互逻辑
/// </summary>
public partial class PathEditingView : UserControl
{
public PathEditingView()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,96 @@
<UserControl x:Class="NavisworksTransport.UI.WPF.Views.SystemManagementView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="400">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="10">
<GroupBox Header="模型分层拆分" Margin="0,10" Height="120">
<StackPanel Margin="10">
<Button Content="模型分层拆分" Command="{Binding ModelSplitterCommand}"
Margin="0,0,0,10" HorizontalAlignment="Left"/>
<Label Content="将大型模型按楼层或属性拆分为多个文件"
Foreground="Gray" FontSize="10"/>
<Label Content="{Binding ModelSplitterStatus}" FontSize="10" Foreground="DarkBlue"/>
</StackPanel>
</GroupBox>
<GroupBox Header="日志管理" Margin="0,10" Height="140">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="查看日志" Command="{Binding ViewLogCommand}" Margin="0,0,10,0"/>
<Button Content="清空日志" Command="{Binding ClearLogCommand}" Margin="0,0,10,0"/>
<Button Content="导出日志" Command="{Binding ExportLogCommand}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Label Content="日志级别:" Width="60"/>
<ComboBox ItemsSource="{Binding LogLevels}"
SelectedItem="{Binding SelectedLogLevel}" Width="100"/>
</StackPanel>
<Label Content="{Binding LogStatus}" FontSize="10" Foreground="DarkGreen"/>
</StackPanel>
</GroupBox>
<GroupBox Header="插件设置" Margin="0,10" Height="140">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="设置选项" Command="{Binding OpenSettingsCommand}" Margin="0,0,10,0"/>
<Button Content="重置设置" Command="{Binding ResetSettingsCommand}" Margin="0,0,10,0"/>
<Button Content="导入配置" Command="{Binding ImportConfigCommand}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<CheckBox Content="启用自动保存" IsChecked="{Binding IsAutoSaveEnabled}" Margin="0,0,15,0"/>
<CheckBox Content="启用调试模式" IsChecked="{Binding IsDebugModeEnabled}"/>
</StackPanel>
<Label Content="{Binding SettingsStatus}" FontSize="10" Foreground="DarkBlue"/>
</StackPanel>
</GroupBox>
<GroupBox Header="系统信息" Margin="0,10" Height="180">
<StackPanel Margin="10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="插件版本:" FontSize="10"/>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding PluginVersion}" FontSize="10"/>
<Label Grid.Row="1" Grid.Column="0" Content="Navisworks版本:" FontSize="10"/>
<Label Grid.Row="1" Grid.Column="1" Content="{Binding NavisworksVersion}" FontSize="10"/>
<Label Grid.Row="2" Grid.Column="0" Content="系统状态:" FontSize="10"/>
<Label Grid.Row="2" Grid.Column="1" Content="{Binding SystemStatus}" FontSize="10"
Foreground="{Binding SystemStatusColor}"/>
<Label Grid.Row="3" Grid.Column="0" Content="内存使用:" FontSize="10"/>
<Label Grid.Row="3" Grid.Column="1" Content="{Binding MemoryUsage}" FontSize="10"/>
<Label Grid.Row="4" Grid.Column="0" Content="运行时间:" FontSize="10"/>
<Label Grid.Row="4" Grid.Column="1" Content="{Binding RunningTime}" FontSize="10"/>
</Grid>
<Button Content="检查更新" Command="{Binding CheckUpdateCommand}"
Margin="0,10,0,0" HorizontalAlignment="Left"/>
</StackPanel>
</GroupBox>
<GroupBox Header="性能监控" Margin="0,10">
<StackPanel Margin="10">
<Label Content="{Binding PerformanceInfo}" FontSize="10" Foreground="Gray"/>
<Button Content="生成性能报告" Command="{Binding GeneratePerformanceReportCommand}"
HorizontalAlignment="Left" Margin="0,5,0,0"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace NavisworksTransport.UI.WPF.Views
{
/// <summary>
/// SystemManagementView.xaml 的交互逻辑
/// </summary>
public partial class SystemManagementView : UserControl
{
public SystemManagementView()
{
InitializeComponent();
}
}
}