From 65712b675ed8cfc7a4e00b68403d57d1a0e3a348 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Mon, 21 Jul 2025 14:15:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86ClashDetective?= =?UTF-8?q?=E9=9B=86=E6=88=90=E9=80=BB=E8=BE=91=EF=BC=8C=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E7=BB=93=E6=9D=9F=E6=B1=87=E6=80=BB=E6=98=BE=E7=A4=BA=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=BB=93=E6=9E=9C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 4 +- change_log.md => CHANGELOG.md | 37 +- CLAUDE.md | 17 +- doc/guide/clash_detective_integration.md | 43 +- ...h_detective_integration_troubleshooting.md | 35 +- doc/requirement/user_requiement.md | 10 +- src/ClashDetectiveIntegration.cs | 766 +++++++++++------- src/ClashDetectiveIntegrationTest.cs | 39 +- src/MainPlugin.cs | 68 -- src/PathAnimationManager.cs | 68 +- 10 files changed, 671 insertions(+), 416 deletions(-) rename change_log.md => CHANGELOG.md (98%) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 9bdd0d5..fb9b3aa 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -5,7 +5,9 @@ "Bash(dotnet build:*)", "WebFetch(domain:github.com)", "WebFetch(domain:adndevblog.typepad.com)", - "Bash(.compile.bat)" + "Bash(.compile.bat)", + "Bash(rg:*)", + "Bash(findstr:*)" ], "deny": [] } diff --git a/change_log.md b/CHANGELOG.md similarity index 98% rename from change_log.md rename to CHANGELOG.md index 6adb60e..8520aa2 100644 --- a/change_log.md +++ b/CHANGELOG.md @@ -3,13 +3,15 @@ ## [0.1.9] - 2025-07-18 ### 新增 🎉 -- **TimeLiner 集成功能**:完整的 Navisworks TimeLiner 集成支持 + +- **TimeLiner 集成功能**:不完整的 Navisworks TimeLiner 集成支持 - 新增 `TimeLinerIntegrationManager` 类,实现运输任务与 TimeLiner 的无缝集成 - 动画播放时自动在 TimeLiner 中创建对应的运输任务 - 支持任务状态实时同步(播放中、暂停、停止、已完成) - 任务显示名称包含车辆信息、时间戳和进度状态 ### 技术突破 🔧 + - **API 兼容性解决**:成功解决 Navisworks 2017 TimeLiner API 限制 - 发现并使用正确的 `TaskAddCopy()` 方法替代只读的 `Tasks.Add()` - 实现多重 fallback 机制:直接添加 → 插入副本 → 层次结构管理 @@ -20,6 +22,7 @@ - 两个系统独立运行,互不干扰,确保稳定性 ### 集成组件 📦 + - **PathAnimationManager 增强**: - 添加 TimeLiner 相关公共属性:`IsTimeLinerAvailable`、`CurrentState`、`CurrentTaskId`、`TimeLinerManager` - 动画播放时自动创建 TimeLiner 任务 @@ -32,12 +35,14 @@ - 事件系统:`StatusChanged`、`TaskCreated` 事件通知 ### 用户体验 ✨ + - **无缝集成**:原有动画功能完全保持不变,TimeLiner 功能作为增强特性 - **可视化管理**:用户可在 TimeLiner 面板中查看所有运输任务 - **状态同步**:任务名称实时显示动画状态和进度百分比 - **错误恢复**:TimeLiner 功能异常时自动降级到基础动画模式 ### 技术细节 📋 + - **API 方法**: - `_documentTimeliner.TaskAddCopy(task)` - 添加任务副本 - `_documentTimeliner.TaskInsertCopy(index, task)` - 插入任务副本 @@ -52,6 +57,7 @@ - 异常情况下的资源自动释放 ### 验证结果 ✅ + - ✅ TimeLiner 窗口成功显示运输任务:"运输任务:SHINYSTE13_运输_131040" - ✅ 任务创建使用 `TaskAddCopy` 方法成功,任务数量从 0 增加到 1 - ✅ 车辆关联正确:关联了 SHINYSTE13 车辆模型 @@ -60,6 +66,7 @@ - ✅ 编译成功,在 Navisworks 2017 中稳定运行 ### 代码优化 🛠️ + - **日志系统简化**:移除冗余的调试日志,保留关键信息 - **错误处理优化**:统一异常处理逻辑,提高代码可读性 - **性能改进**:减少不必要的 API 调用和状态检查 @@ -69,6 +76,7 @@ ### 进一步修复对象生命周期和选择清除问题 #### 深度修复 + - **选择清除安全性**:创建了`SafelyClearSelection()`方法,完全安全地处理选择清除 - 新增`IsApplicationDocumentValid()`方法检查Application和Document对象有效性 - 替换所有直接的`CurrentSelection.Clear()`调用为安全方法调用 @@ -79,6 +87,7 @@ - 防止定时器继续尝试访问已释放的对象 #### 技术增强 + - **多层防护机制**: - `IsApplicationDocumentValid()`:检查核心对象有效性 - `SafelyClearSelection()`:安全的选择清除操作 @@ -93,6 +102,7 @@ - 集中化的错误处理和日志记录 #### 解决的问题 + - ✅ "Object has been Disposed" 在清除选择时的错误 - ✅ 定时器继续访问已释放对象的问题 - ✅ 多重异常处理导致的日志混乱 @@ -103,6 +113,7 @@ ## [0.1.11] - 2025-06-19 ### 修复 + - 🔧 恢复进入路径编辑模式后控制面板自动关闭功能 - 在v0.1.10添加全局异常处理时意外删除了此功能 - 现在进入路径编辑模式成功后,控制面板会自动关闭 @@ -112,18 +123,21 @@ - 自动关闭确保焦点回到Navisworks主界面 ### 改进 + - 📝 更新用户提示信息 - 明确告知用户控制面板已关闭,避免困惑 - 提醒用户完成路径编辑后需要重新打开插件面板退出编辑模式 - 保持原有的操作流程指导 ### 用户体验 + - **操作流程**:选择通道 → 点击进入编辑 → 面板自动关闭 → 3D点击添加路径点 → 重新打开插件 → 退出编辑 - **交互优化**:确保用户能够无障碍地在3D环境中进行路径规划操作 ## [0.1.10] - 2025-06-19 ### 新增 + - 🛡️ 全局异常处理机制,彻底防止程序崩溃 - 新增GlobalExceptionHandler工具类,捕获AppDomain和Thread级别的未处理异常 - 实现SafeExecute方法,为所有关键操作提供安全包装 @@ -134,6 +148,7 @@ - 尝试恢复程序到安全状态 ### 改进 + - 📝 用户友好的错误提示 - 技术详情与用户信息分离显示 - 不同级别的错误提示(错误对话框 vs 简单提示) @@ -144,6 +159,7 @@ - 记录是否为终止性异常 ### 技术细节 + - 修改MainPlugin.Execute方法,使用GlobalExceptionHandler.SafeExecute包装 - 为ShowCategorySelectionDialog方法添加异常保护 - 为所有按钮点击事件添加SafeExecute包装 @@ -155,6 +171,7 @@ ### 修复对象生命周期管理问题 #### 主要修复 + - **Object has been Disposed 错误**:修复了点击监听和高亮功能中的对象释放错误 - 添加了`IsModelItemValid()`方法检查ModelItem对象是否有效 - 在高亮功能中过滤无效的ModelItem对象 @@ -165,6 +182,7 @@ - 简化了选择清除的错误处理机制 #### 技术改进 + - **对象有效性检查**: - 新增`IsModelItemValid()`私有方法 - 通过访问基本属性检测对象是否已被释放 @@ -179,6 +197,7 @@ - 添加有效对象计数和状态反馈 #### 解决的具体问题 + - ✅ "Argument 'path' has been Disposed" 错误 - ✅ "Object has been Disposed (null)" 重复错误 - ✅ 点击监听过程中的对象释放异常 @@ -191,12 +210,14 @@ ### 增加分层可见性控制功能 #### 主要变更 + - **修复可见性逻辑缺陷**:解决了隐藏非物流项目时,具有物流属性的项目也被错误隐藏的问题 - **改进用户界面**:将两个独立按钮("隐藏非物流分类项目"和"显示所有项目")替换为单个复选框"只显示物流分类项目" - **增强递归检查**:添加了`HasLogisticsAttributesRecursive`方法,确保父节点包含具有物流属性的子节点时不会被隐藏 - **优化用户体验**:复选框状态实时反映当前可见性状态,操作失败时自动回滚状态 #### 技术成果 + - **核心算法改进**: - 新增递归检查方法,避免误隐藏包含物流子项的父项 - 修复了 `item.Children.Count` 方法组调用问题,改为 `item.Children.Count()` @@ -208,6 +229,7 @@ - 操作失败时的状态回滚机制 #### 验证结果 + - ✅ 设置3个模型为物流分类后,勾选"只显示物流分类项目"复选框,成功隐藏非物流项目同时保持物流项目可见 - ✅ 取消勾选复选框,所有项目正确恢复显示 - ✅ 状态标签准确显示操作结果和隐藏项目数量 @@ -220,12 +242,14 @@ ### 增加物流类别属性功能 #### 主要变更 + - **实现物流属性分类系统**:支持8种物流元素类型(门、电梯、楼梯、通道、障碍物、装卸区、停车位、检查点) - **集成COM API功能**:通过Navisworks COM API实现自定义属性的添加和管理 - **优化代码架构**:将LogisticsCategories.cs功能整合到CategoryAttributeManager.cs中,避免重复定义 - **修复API兼容性问题**:解决了多个Navisworks 2017 API兼容性问题 #### 技术成果 + - **COM API集成**: - 正确实现了ComApiBridge.State的使用 - 修复了ObjectFactory的调用方式 @@ -242,6 +266,7 @@ - 优化了错误处理和调试信息输出 #### 验证结果 + - ✅ 成功为选定模型项添加物流属性,属性在Navisworks属性面板中正确显示 - ✅ 8种物流元素类型的按钮功能正常,属性设置准确 - ✅ COM API调用稳定,无内存泄漏或崩溃问题 @@ -252,6 +277,7 @@ ## 版本 [0.1.3] - 2024-01-XX ### 修复 + - **兼容性修复**: 修复C# 7.3兼容性问题,移除了nullable引用类型语法 - **API兼容性**: 修复Navisworks 2017 API兼容性问题 - 替换了不存在的`View.RequestSavedViewpoint()`方法 @@ -265,6 +291,7 @@ - **射线投射算法**: 完善了射线与包围盒交点计算的错误处理 ### 技术改进 + - **错误处理**: 增强了所有射线投射相关方法的错误处理和日志记录 - **后备方案**: 在精确坐标获取失败时提供包围盒中心点作为后备方案 - **日志优化**: 改进了调试日志的详细程度和格式 @@ -272,15 +299,18 @@ ## 技术债务和改进计划 ### 当前已知问题 + - 暂无已知的功能性问题 ### 已完成功能 ✅ + 1. ✅ **TimeLiner 集成**:已实现完整的 TimeLiner 集成功能(v0.1.9) - 运输任务自动创建和管理 - 动画状态实时同步 - 完善的错误处理和降级机制 ### 下一步开发计划 + 1. **路径规划功能**:实现 A* 算法的 3D 路径规划 2. **动态碰撞检测增强**:进一步集成 Clash Detective 功能 3. **动画和可视化优化**:添加更多路径动画效果和碰撞高亮显示 @@ -291,6 +321,7 @@ - 任务导入导出功能 ### 性能优化目标 + - 大型模型的处理性能优化 - 异步操作的用户界面响应性改进 - 内存使用优化和资源清理机制完善 @@ -298,6 +329,7 @@ ## [0.1.4] - 2024-01-XX ### 修复 + - **点击监听问题**: 修复鼠标点击没有效果的问题 - 添加了点击状态跟踪机制,避免重复处理同一选择 - 增加最小点击间隔(500ms)防止误触 @@ -319,6 +351,7 @@ - 实现地图中选中点的高亮重绘 ### 技术改进 + - **状态管理**: 增强点击状态跟踪和管理 - 添加_lastProcessedItem和_lastProcessedTime字段 - 实现MinClickInterval常量控制最小点击间隔 @@ -329,4 +362,4 @@ - **用户体验**: 优化交互反馈 - 改进状态消息显示选中通道数量 - 在状态栏显示详细的点信息 - - 清除无效选择的自动处理 \ No newline at end of file + - 清除无效选择的自动处理 diff --git a/CLAUDE.md b/CLAUDE.md index 5511f51..3ae419b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,11 +15,13 @@ NavisworksTransport is a Navisworks 2017 plugin (v0.1.8) for logistics path plan ## Architecture Overview ### Core Plugin Structure + - **MainPlugin.cs**: Primary AddInPlugin entry point with ribbon UI - **PathClickToolPlugin.cs**: ToolPlugin for 3D mouse interaction - **PathPointRenderPlugin.cs**: RenderPlugin for 3D visualization ### Manager Components + - **PathPlanningManager.cs**: Central path planning and route management logic - **PathAnimationManager.cs**: TimeLiner integration for object movement animation - **CoordinateConverter.cs**: 2D map overlay to 3D world coordinate conversion @@ -28,64 +30,77 @@ NavisworksTransport is a Navisworks 2017 plugin (v0.1.8) for logistics path plan - **ModelSplitterManager.cs**: Model layer separation and export functionality ### Data and Utilities + - **PathPlanningModels.cs**: Core data structures (PathEditState, PathRoute, PathPoint) - **PathDataManager.cs**: Serialization and persistence using Newtonsoft.Json - **GeometryExtractor.cs**: 3D geometry analysis and bounding box calculations - **LogManager.cs**: Centralized logging with global exception handling ### UI Components + - **LogisticsPropertyEditDialog.cs**: Property editing interface - **ModelSplitterDialog.cs**: Model splitting configuration UI ## Key Technical Details ### Navisworks API Integration + - Uses dual API approach: Native API (`Autodesk.Navisworks.Api`) + COM API (`Autodesk.Navisworks.ComApi`) - COM API required for attribute persistence and TimeLiner operations - Plugin types: AddInPlugin (main), ToolPlugin (interaction), RenderPlugin (visualization) ### Exception Handling + Global exception handling implemented in MainPlugin with: + - AppDomain.CurrentDomain.UnhandledException - Application.ThreadException - Automatic recovery and user-friendly error reporting ### Coordinate Systems + - Supports 2D map overlay on 3D models with dynamic zoom/pan - Margin-based boundary calculations for click precision - Transform chains for coordinate conversion between spaces ### Logistics Categories + Eight predefined logistics element types: + - 门 (Doors), 电梯 (Elevators), 楼梯 (Stairs), 通道 (Channels) - 障碍物 (Obstacles), 装卸区 (Loading Zones), 停车区 (Parking), 检查点 (Checkpoints) ## Development Guidelines ### Language Preference + - **使用中文进行所有交流和代码注释** - 与用户交流时优先使用中文 - 代码注释和文档说明使用中文 ### File Organization + - Core managers handle specific functionality areas - Models file contains shared data structures - UI dialogs are separate form classes - Utilities (logging, geometry, data) are standalone classes ### Plugin Registration Pattern + ```csharp [Plugin("NavisworksTransport.PluginName", "YourDeveloperID")] [AddInPlugin(AddInLocation.AddIn)] ``` ### Error Handling Best Practices + - Use LogManager for consistent logging - Implement try-catch blocks around Navisworks API calls - Provide meaningful error messages to users - Use COM API error codes for troubleshooting ### Dependencies + - **System.Windows.Forms**: UI dialogs and controls - **System.Drawing**: Graphics and coordinate operations @@ -94,4 +109,4 @@ Eight predefined logistics element types: - Manual testing required through Navisworks Manage 2017 - Plugin automatically deploys to Navisworks plugin directory during build - Restart Navisworks after compilation to load new plugin version -- Use LogManager output for debugging and troubleshooting \ No newline at end of file +- Use LogManager output for debugging and troubleshooting diff --git a/doc/guide/clash_detective_integration.md b/doc/guide/clash_detective_integration.md index 97e9e85..6350b4c 100644 --- a/doc/guide/clash_detective_integration.md +++ b/doc/guide/clash_detective_integration.md @@ -7,16 +7,19 @@ ## 功能特性 ### 1. 自动碰撞检测 + - **实时检测**: 动画播放过程中实时检测碰撞 - **智能回退**: 当 Clash Detective 不可用时,自动切换到简化碰撞检测 - **双重模式**: 支持精确几何碰撞检测和简化包围盒检测 ### 2. Clash Detective 集成 + - **自动发现**: 自动检测并连接到 Clash Detective 插件 - **测试管理**: 创建和管理动态碰撞测试 - **结果同步**: 碰撞结果同步显示到 Clash Detective 窗口 ### 3. 可视化高亮 + - **碰撞高亮**: 自动高亮碰撞对象(红色显示) - **实时更新**: 动画过程中实时更新碰撞状态 - **清理机制**: 动画结束后自动清理临时高亮 @@ -24,21 +27,25 @@ ## 使用方法 ### 1. 启动插件 + 1. 打开 Navisworks Manage 2017 2. 加载包含物流路径的模型 3. 从菜单栏选择 "附加模块" > "Transport Plugin" ### 2. 设置动画 + 1. 在控制面板中选择要动画的车辆对象 2. 选择预定义的路径或创建新路径 3. 设置动画参数(时长、速度等) ### 3. 启用碰撞检测 + 1. 在动画控制面板中确保"启用碰撞检测"选项已勾选 2. 可选择"高亮显示碰撞"以可视化碰撞结果 3. 点击"播放动画"开始带碰撞检测的动画 ### 4. 测试集成功能 + 1. 点击"测试Clash Detective集成"按钮 2. 系统会自动运行完整的集成测试 3. 查看测试结果和日志信息 @@ -48,25 +55,28 @@ ### 核心组件 #### 1. ClashDetectiveIntegration + - **功能**: 主要的集成管理器 - **位置**: `src/ClashDetectiveIntegration.cs` -- **职责**: +- **职责**: - 管理与 Clash Detective 的连接 - 执行碰撞检测逻辑 - 处理结果同步 #### 2. PathAnimationManager + - **功能**: 动画管理器(已升级) - **位置**: `src/PathAnimationManager.cs` -- **职责**: +- **职责**: - 集成碰撞检测到动画循环 - 管理碰撞检测事件 - 处理高亮显示 #### 3. ClashDetectiveIntegrationTest + - **功能**: 集成测试管理器 - **位置**: `src/ClashDetectiveIntegrationTest.cs` -- **职责**: +- **职责**: - 验证集成功能 - 性能测试 - 错误诊断 @@ -84,6 +94,7 @@ ### 主要方法 #### ClashDetectiveIntegration.Instance + ```csharp // 初始化集成 void Initialize() @@ -99,6 +110,7 @@ void Cleanup() ``` #### 事件处理 + ```csharp // 碰撞检测事件 event EventHandler CollisionDetected; @@ -112,6 +124,7 @@ public class CollisionDetectedEventArgs : EventArgs ``` ### 碰撞结果数据结构 + ```csharp public class CollisionResult { @@ -130,11 +143,13 @@ public class CollisionResult ## 配置选项 ### 碰撞检测参数 + - **容差**: 默认 0.01 米(1厘米) - **测试类型**: 硬碰撞检测 - **检测频率**: 50ms 间隔(20 FPS) ### 高亮设置 + - **碰撞颜色**: 红色 - **非碰撞对象**: 保持原色 - **清理时机**: 动画结束或用户停止 @@ -144,22 +159,28 @@ public class CollisionResult ### 常见问题 #### 1. Clash Detective 未找到 + **症状**: 系统提示"未找到Clash Detective插件" -**解决**: +**解决**: + - 确保 Clash Detective 已正确安装 - 检查 Navisworks 版本兼容性 - 查看日志文件获取详细信息 #### 2. 碰撞检测不工作 + **症状**: 动画播放但无碰撞检测结果 -**解决**: +**解决**: + - 确保模型包含几何体 - 检查动画对象是否有效 - 使用测试按钮验证集成 #### 3. 高亮显示异常 + **症状**: 碰撞对象未正确高亮 -**解决**: +**解决**: + - 重启动画播放 - 检查对象选择是否正确 - 清理临时材质覆盖 @@ -167,12 +188,15 @@ public class CollisionResult ### 调试方法 #### 1. 查看日志 + 日志文件位置: `%USERPROFILE%\Documents\NavisworksTransport\Logs\` #### 2. 运行测试 + 使用"测试Clash Detective集成"按钮进行全面测试 #### 3. 检查系统状态 + ```csharp // 检查COM API状态 var state = ComApiBridge.ComApiBridge.State; @@ -184,11 +208,13 @@ var pluginCount = state.Plugins().Count; ## 性能优化 ### 建议设置 + - **大模型**: 增加碰撞检测间隔到 100ms - **复杂路径**: 减少路径点数量 - **多对象**: 使用批处理检测 ### 内存管理 + - 动画结束后自动清理资源 - 定期重置临时材质 - 避免长时间运行动画 @@ -196,12 +222,14 @@ var pluginCount = state.Plugins().Count; ## 更新日志 ### v0.1.8 (当前版本) + - ✅ 新增 Clash Detective 集成功能 - ✅ 实现实时碰撞检测 - ✅ 添加自动化测试框架 - ✅ 优化性能和错误处理 ### 计划功能 + - 🔄 支持自定义碰撞规则 - 🔄 批量路径碰撞分析 - 🔄 碰撞报告导出 @@ -210,10 +238,11 @@ var pluginCount = state.Plugins().Count; ## 技术支持 如有问题请查看: + 1. 日志文件: `%USERPROFILE%\Documents\NavisworksTransport\Logs\` 2. 测试结果: 使用集成测试按钮 3. 开发文档: `doc/design/` 目录 --- -*本文档随插件版本更新,请查看最新版本获取准确信息。* \ No newline at end of file +*本文档随插件版本更新,请查看最新版本获取准确信息。* diff --git a/doc/guide/clash_detective_integration_troubleshooting.md b/doc/guide/clash_detective_integration_troubleshooting.md index 24450c3..4af654c 100644 --- a/doc/guide/clash_detective_integration_troubleshooting.md +++ b/doc/guide/clash_detective_integration_troubleshooting.md @@ -10,9 +10,7 @@ 我们的集成采用了**双重架构**: -``` 动画管理器 -> ClashDetectiveIntegration -> .NET API + COM API -> Clash Detective窗口 -``` - **动画播放时**: 自动检测碰撞并创建测试结果 - **测试按钮**: 验证集成功能并强制显示结果 @@ -21,16 +19,19 @@ ### 2. 集成的具体表现 #### A. 测试创建 + - ✅ 自动创建名为 "动态运输路径碰撞检测" 的测试 - ✅ 测试会出现在Clash Detective的测试列表中 - ✅ 测试参数:硬碰撞、1cm容差 #### B. 结果显示 + - ✅ 碰撞结果会添加到测试中 - ✅ 3D视图中高亮显示碰撞对象(红色) - ✅ 日志记录详细的检测信息 #### C. 窗口同步 + - ✅ 自动刷新Clash Detective界面 - ✅ 运行所有测试以触发更新 - ✅ 强制重绘3D视图 @@ -38,18 +39,21 @@ ## 如何查看集成效果 ### 方法1:检查测试列表 + 1. 打开 Clash Detective 窗口 2. 查看左侧的测试列表 3. 寻找 "动态运输路径碰撞检测" 项 4. 点击该测试查看详细信息 ### 方法2:查看测试结果 + 1. 在测试列表中选择我们的测试 2. 查看右侧的结果面板 3. 结果会显示碰撞的对象名称和状态 4. 双击结果可以定位到3D视图 ### 方法3:监控日志输出 + 1. 查看日志文件:`%USERPROFILE%\Documents\NavisworksTransport\Logs\` 2. 搜索关键词: - "Clash Detective" @@ -58,6 +62,7 @@ - "同步结果到Clash Detective" ### 方法4:使用新的测试按钮 + 1. 点击 "测试Clash Detective集成" 按钮 2. 查看弹出的详细信息对话框 3. 按照提示检查Clash Detective窗口 @@ -65,34 +70,43 @@ ## 可能的显示问题及解决方案 ### 问题1:测试列表中没有看到我们的测试 + **可能原因**: + - Clash Detective窗口未刷新 - 测试创建失败 - 权限问题 **解决方案**: + 1. 关闭并重新打开Clash Detective窗口 2. 点击 "测试Clash Detective集成" 按钮强制刷新 3. 查看日志了解详细错误信息 ### 问题2:测试存在但无结果 + **可能原因**: + - 动画对象与其他对象无碰撞 - 选择集设置不正确 - 测试参数过于严格 **解决方案**: + 1. 确保动画对象与其他对象有重叠 2. 调整碰撞容差(当前为1cm) 3. 使用测试按钮执行强制检测 ### 问题3:3D视图中看不到高亮 + **可能原因**: + - 高亮颜色与背景相似 - 临时材质被清除 - 视图渲染问题 **解决方案**: + 1. 改变视图背景颜色 2. 重新运行动画 3. 检查日志中的高亮信息 @@ -100,6 +114,7 @@ ## 调试和验证步骤 ### 1. 基础验证 + ``` 1. 加载包含多个对象的模型 2. 启动插件并打开Clash Detective @@ -108,6 +123,7 @@ ``` ### 2. 动画验证 + ``` 1. 设置动画对象和路径 2. 启用"碰撞检测"选项 @@ -117,6 +133,7 @@ ``` ### 3. 日志分析 + ``` 1. 打开日志文件 2. 搜索集成相关信息 @@ -126,14 +143,16 @@ ## 集成效果的预期表现 -### 正常情况下您应该看到: +### 正常情况下您应该看到 + 1. **测试列表**:出现 "动态运输路径碰撞检测" 项 2. **结果面板**:显示检测到的碰撞结果 3. **3D视图**:碰撞对象显示红色高亮 4. **日志信息**:记录详细的检测过程 5. **状态报告**:测试按钮提供的详细信息 -### 如果仍然看不到效果: +### 如果仍然看不到效果 + 1. 检查Navisworks版本是否为2017 2. 确认Clash Detective插件已正确安装 3. 验证模型中是否有足够的几何对象 @@ -142,16 +161,18 @@ ## 技术细节 -### 我们的集成方式: +### 我们的集成方式 + - 使用 .NET API 创建和管理碰撞测试 - 使用 COM API 访问底层功能 - 通过多种方式刷新窗口显示 - 提供详细的状态监控和日志记录 -### 与标准用法的区别: +### 与标准用法的区别 + - 动态创建测试而非预定义 - 实时更新结果而非批处理 - 集成到动画循环中而非独立运行 - 提供自动化的可视化反馈 -这种集成方式确保了动画播放过程中的实时碰撞检测,同时保持与Clash Detective原生界面的兼容性。 \ No newline at end of file +这种集成方式确保了动画播放过程中的实时碰撞检测,同时保持与Clash Detective原生界面的兼容性。 diff --git a/doc/requirement/user_requiement.md b/doc/requirement/user_requiement.md index 6f24a64..21c3151 100644 --- a/doc/requirement/user_requiement.md +++ b/doc/requirement/user_requiement.md @@ -3,12 +3,14 @@ ## 功能模块详细需求 ### Navisworks插件开发和安装部署 + | 次级功能点 | 功能点描述 | |------------|------------| | 插件二次开发 | 基于Navisworks 2017 SDK进行二次开发,用插件的方式,集成到Navisworks的菜单中。 | | 一键安装部署 | 支持Windows 7环境安装,程序自动识别Navisworks安装路径,并安装插件,自动修改配置和菜单。 | ### 通道选择及路径点规划模块 + | 次级功能点 | 功能点描述 | |------------|------------| | 通道选择 | 支持选择通道模型功能,可通过选择树或三维视图点选的方式,选择模型并制定为通道类型。 | @@ -17,12 +19,14 @@ | 路径点自动贴合 | 路径点要自动贴合通道模型表面,路径点之间通过直线进行联通。 | ### 物流"类别"设置功能模块 + | 次级功能点 | 功能点描述 | |------------|------------| | 类别设置 | 支持模型属性页面新增"物流属性"类别。 | | 属性设置 | 1、支持通过选择树和三维视图选择的方式,选择物流路径相关的元素(如门、电梯、楼梯、通道等),设置为特定的物流分类,并支持类型、可通行性、速度限制、宽度限制、优先级等属性;
2、支持在Navisworks中进行识别和筛选,支持物流分类属性的添加、编辑和清除。 | ### 层级创建功能模块 + | 次级功能点 | 功能点描述 | |------------|------------| | 层级显示 | 支持自动隐藏或淡化非关键层,以便专注于物流路径相关的层级。 | @@ -30,6 +34,7 @@ | 路径时间标签 | 支持路径时间标签设置,以预估运输时间。 | ### 交互式导航功能模块 + | 次级功能点 | 功能点描述 | |------------|------------| | 交互式导航控件 | 创建交互式导航控件,允许用户选择不同的起点和终点,动态生成路径。 | @@ -37,6 +42,7 @@ | 输出格式 | 支持路径规划结果结构化文件输出(XML/JSON/CSV),结果文件能够导入DELMIA。 | ### 碰撞检测功能模块 + | 次级功能点 | 功能点描述 | |------------|------------| | 动画生成和播放 | 1、指定物流组件(待载转运车),选择路径,支持生成动画仿真物流组件的运动过程;
2、支持设置动画时长,支持动画的播放、停止和步进播放。 | @@ -45,8 +51,10 @@ | 路径规划分析 | 对多个路径运行的碰撞结果,进行分析,生成路径分析报告,选择最佳路径,提供调整建议。 | ## 技术指标要求 + - 可实现不小于10种尺寸规格的带转载运车的路径规划 ## 运行环境 + - 操作系统:Windows 7 -- 软件环境:Navisworks 2017 \ No newline at end of file +- 软件环境:Navisworks 2017 diff --git a/src/ClashDetectiveIntegration.cs b/src/ClashDetectiveIntegration.cs index f1d7806..7242b38 100644 --- a/src/ClashDetectiveIntegration.cs +++ b/src/ClashDetectiveIntegration.cs @@ -287,62 +287,417 @@ namespace NavisworksTransport } } - /// - /// 创建碰撞快照(仅在手动测试时使用) +/// + /// 创建碰撞快照(动画结束后一次性更新结果) /// public void CreateCollisionSnapshot(List results) { try { - if (_documentClash == null || results.Count == 0) + if (_documentClash == null) return; - var firstCollision = results[0]; - if (firstCollision.Item1 != null && firstCollision.Item2 != null) + // 缓存结果,等动画结束后统一处理 + CacheCollisionResults(results); + } + catch (Exception ex) + { + LogManager.Error($"创建实时碰撞快照失败: {ex.Message}"); + } + } + + private List _cachedResults = new List(); + + /// + /// 缓存碰撞结果 + /// + private void CacheCollisionResults(List results) + { + if (results != null && results.Count > 0) + { + _cachedResults.AddRange(results); + } + } + + /// + /// 动画结束后创建最终碰撞结果汇总 - 同步安全版本 + /// + public void CreateFinalCollisionSummary() + { + try + { + LogManager.Info("=== 开始创建最终碰撞结果汇总(同步模式) ==="); + LogManager.Info($"步骤1: 检查前置条件 - _documentClash是否存在: {_documentClash != null}"); + LogManager.Info($"步骤1: 检查前置条件 - 缓存结果数量: {_cachedResults.Count}"); + + if (_documentClash == null || _cachedResults.Count == 0) { - // 检查对象是否仍然有效 - if (IsModelItemValid(firstCollision.Item1) && IsModelItemValid(firstCollision.Item2)) + LogManager.Info("前置条件不满足,退出创建"); + return; + } + + LogManager.Info($"步骤2: 开始处理 {_cachedResults.Count} 个碰撞结果"); + + // 去重处理 + LogManager.Info("步骤3: 开始去重处理..."); + var uniqueCollisions = _cachedResults + .GroupBy(r => new { r.Item1, r.Item2 }) + .Select(g => g.First()) + .ToList(); + LogManager.Info($"步骤3: 去重后碰撞数量: {uniqueCollisions.Count}"); + + if (!uniqueCollisions.Any()) + { + LogManager.Info("去重后无碰撞,退出创建"); + return; + } + + // 创建最终汇总测试 + var testName = $"物流路径碰撞汇总_{DateTime.Now:yyyyMMdd_HHmmss}"; + LogManager.Info($"步骤4: 创建测试对象 - 测试名称: {testName}"); + + var summaryTest = new ClashTest(); + summaryTest.DisplayName = testName; + summaryTest.TestType = ClashTestType.Hard; + summaryTest.Tolerance = 0.01; + + LogManager.Info("步骤5: 构建选择集A - 动画对象"); + var selectionA = new ModelItemCollection(); + var animatedObjects = uniqueCollisions.Select(r => r.Item1).Distinct(); + int validAnimatedCount = 0; + foreach (var obj in animatedObjects) + { + if (IsModelItemValid(obj)) { - // 创建一个快照测试来展示实际碰撞 - var timestamp = DateTime.Now.ToString("HH:mm:ss"); - var testName = $"动画碰撞快照_{timestamp}"; + selectionA.Add(obj); + validAnimatedCount++; + } + } + LogManager.Info($"步骤5: 选择集A - 有效对象数量: {validAnimatedCount}"); + + LogManager.Info("步骤6: 构建选择集B - 碰撞对象"); + var selectionB = new ModelItemCollection(); + var collidingObjects = uniqueCollisions.Select(r => r.Item2).Distinct(); + int validCollidingCount = 0; + foreach (var obj in collidingObjects) + { + if (IsModelItemValid(obj)) + { + selectionB.Add(obj); + validCollidingCount++; + } + } + LogManager.Info($"步骤6: 选择集B - 有效对象数量: {validCollidingCount}"); + + // 验证选择集是否为空 + if (selectionA.Count == 0 || selectionB.Count == 0) + { + LogManager.Error("选择集为空,无法创建测试"); + return; + } + + summaryTest.SelectionA.Selection.CopyFrom(selectionA); + summaryTest.SelectionB.Selection.CopyFrom(selectionB); + LogManager.Info($"步骤7: 选择集设置完成 - A:{selectionA.Count}, B:{selectionB.Count}"); + + LogManager.Info("步骤8: 直接添加测试到文档"); + try + { + // 直接同步添加测试 + _documentClash.TestsData.TestsAddCopy(summaryTest); + LogManager.Info("步骤8: 测试添加完成"); + + // 立即重新获取测试引用 + var addedTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == testName) as ClashTest; + if (addedTest != null) + { + LogManager.Info($"找到测试对象: {addedTest.DisplayName}, GUID: {addedTest.Guid}"); - var snapshotTest = new ClashTest(); - snapshotTest.DisplayName = testName; - snapshotTest.TestType = ClashTestType.Hard; - snapshotTest.Tolerance = 0.01; + // 立即运行测试 + _documentClash.TestsData.TestsRunTest(addedTest); + LogManager.Info($"测试运行完成: {testName}, 找到 {addedTest.Children.Count} 个碰撞"); - var selectionA = new ModelItemCollection(); - selectionA.Add(firstCollision.Item1); - snapshotTest.SelectionA.Selection.CopyFrom(selectionA); - - var selectionB = new ModelItemCollection(); - selectionB.Add(firstCollision.Item2); - snapshotTest.SelectionB.Selection.CopyFrom(selectionB); - - // 添加并运行测试 - _documentClash.TestsData.TestsAddCopy(snapshotTest); - - // 重新获取并运行测试 - var addedTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == testName); - if (addedTest is ClashTest runTest) - { - _documentClash.TestsData.TestsRunTest(runTest); - LogManager.Info($"已创建并运行碰撞快照: {testName},展示 {firstCollision.Item1.DisplayName} <-> {firstCollision.Item2.DisplayName},结果数量: {runTest.Children.Count}"); - } + // 强制刷新 + RefreshClashDetectiveUI(); } else { - LogManager.Warning("碰撞对象已被释放,跳过测试创建"); + LogManager.Error("未找到添加的测试对象"); + } + } + catch (Exception ex) + { + LogManager.Error($"步骤8: 添加或运行测试失败 - {ex.GetType().Name}: {ex.Message}"); + LogManager.Error($"详细错误: {ex.StackTrace}"); + + // 尝试恢复 - 重新初始化 + LogManager.Info("尝试重新初始化Clash Detective..."); + Initialize(); + + try + { + _documentClash.TestsData.TestsAddCopy(summaryTest); + var addedTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == testName) as ClashTest; + if (addedTest != null) + { + _documentClash.TestsData.TestsRunTest(addedTest); + LogManager.Info($"重试成功: {testName}"); + } + } + catch (Exception retryEx) + { + LogManager.Error($"重试也失败: {retryEx.Message}"); + } + } + + // 清空缓存 + _cachedResults.Clear(); + LogManager.Info("=== 最终碰撞结果汇总创建完成 ==="); + } + catch (Exception ex) + { + LogManager.Error($"创建最终碰撞汇总失败 - {ex.GetType().Name}: {ex.Message}"); + LogManager.Error($"创建最终碰撞汇总失败 - 堆栈跟踪: {ex.StackTrace}"); + } + } + + /// + /// 安全运行汇总测试 + /// + private void RunSummaryTestSafely(string testName) + { + try + { + LogManager.Info("=== 开始安全运行汇总测试 ==="); + + if (Application.ActiveDocument == null) + { + LogManager.Error("文档已失效"); + return; + } + + var docClash = Application.ActiveDocument.GetClash(); + if (docClash == null || docClash.TestsData == null) + { + LogManager.Error("Clash数据不可用"); + return; + } + + var tests = docClash.TestsData.Tests; + LogManager.Info($"文档中测试总数: {tests.Count}"); + + // 重新获取测试 + var addedTest = tests.FirstOrDefault(t => t.DisplayName == testName) as ClashTest; + + if (addedTest != null) + { + LogManager.Info($"找到测试对象: {addedTest.DisplayName}, GUID: {addedTest.Guid}"); + + try + { + // 运行测试 + docClash.TestsData.TestsRunTest(addedTest); + LogManager.Info($"测试运行完成: {testName}"); + + // 强制刷新UI + RefreshClashDetectiveUI(); + + LogManager.Info("=== 最终碰撞结果汇总完成 ==="); + } + catch (Exception runEx) + { + LogManager.Error($"运行测试失败: {runEx.Message}"); + // 尝试异步运行 + RunTestAsync(docClash, addedTest); + } + } + else + { + LogManager.Error("未找到添加的测试对象"); + } + } + catch (Exception ex) + { + LogManager.Error($"安全运行汇总测试失败: {ex.Message}"); + } + } + + /// + /// 直接运行测试(回退方案) + /// + private void RunSummaryTestDirectly(ClashTest test, string testName) + { + try + { + _documentClash.TestsData.TestsAddCopy(test); + var addedTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == testName) as ClashTest; + + if (addedTest != null) + { + try + { + _documentClash.TestsData.TestsRunTest(addedTest); + LogManager.Info($"直接运行测试完成: {testName}"); + } + catch + { + LogManager.Warning("直接运行测试失败,跳过"); } } } catch (Exception ex) { - LogManager.Error($"创建碰撞快照失败: {ex.Message}"); + LogManager.Error($"直接运行汇总测试失败: {ex.Message}"); } } + /// + /// 异步运行测试 + /// + private void RunTestAsync(DocumentClash docClash, ClashTest test) + { + System.Threading.Tasks.Task.Run(() => + { + try + { + docClash.TestsData.TestsRunTest(test); + LogManager.Info($"异步运行测试完成: {test.DisplayName}"); + } + catch (Exception ex) + { + LogManager.Error($"异步运行测试失败: {ex.Message}"); + } + }); + } + + /// + /// 刷新Clash Detective UI + /// + private void RefreshClashDetectiveUI() + { + try + { + // 强制重绘视图 + var doc = Application.ActiveDocument; + if (doc?.ActiveView != null) + { + doc.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All); + } + + LogManager.Info("Clash Detective UI已刷新"); + } + catch (Exception ex) + { + LogManager.Error($"刷新Clash Detective UI失败: {ex.Message}"); + } + } + + /// + /// 更新现有测试的选择集 + /// + private void UpdateExistingTest(ClashTest existingTest, List validCollisions) + { + try + { + if (existingTest == null || !validCollisions.Any()) + return; + + // 重新构建选择集 + var selectionA = new ModelItemCollection(); + selectionA.Add(validCollisions[0].Item1); // 动画对象 + existingTest.SelectionA.Selection.CopyFrom(selectionA); + + var selectionB = new ModelItemCollection(); + foreach (var collision in validCollisions) + { + if (!selectionB.Contains(collision.Item2)) + { + selectionB.Add(collision.Item2); + } + } + existingTest.SelectionB.Selection.CopyFrom(selectionB); + + // 运行测试以更新结果 + System.Threading.Tasks.Task.Run(() => + { + try + { + _documentClash.TestsData.TestsRunTest(existingTest); + LogManager.Info($"更新动画碰撞测试: {validCollisions.Count} 个碰撞对象"); + } + catch (Exception ex) + { + LogManager.Error($"更新测试失败: {ex.Message}"); + } + }); + } + catch (Exception ex) + { + LogManager.Error($"更新现有测试失败: {ex.Message}"); + } + } + + /// + /// 创建新的动画碰撞测试 + /// + private void CreateNewAnimationTest(string testName, List validCollisions) + { + try + { + if (!validCollisions.Any()) + return; + + var snapshotTest = new ClashTest(); + snapshotTest.DisplayName = testName; + snapshotTest.TestType = ClashTestType.Hard; + snapshotTest.Tolerance = 0.01; + + // 设置选择集 - 动画对象 + var selectionA = new ModelItemCollection(); + selectionA.Add(validCollisions[0].Item1); + snapshotTest.SelectionA.Selection.CopyFrom(selectionA); + + // 设置选择集B - 所有碰撞对象 + var selectionB = new ModelItemCollection(); + foreach (var collision in validCollisions) + { + if (!selectionB.Contains(collision.Item2)) + { + selectionB.Add(collision.Item2); + } + } + snapshotTest.SelectionB.Selection.CopyFrom(selectionB); + + // 添加到文档 + _documentClash.TestsData.TestsAddCopy(snapshotTest); + + // 运行测试 + System.Threading.Tasks.Task.Run(() => + { + try + { + var addedTest = _documentClash.TestsData.Tests + .FirstOrDefault(t => t.DisplayName == testName) as ClashTest; + + if (addedTest != null) + { + _documentClash.TestsData.TestsRunTest(addedTest); + LogManager.Info($"创建动画碰撞测试: {testName}, 碰撞数量: {validCollisions.Count}"); + } + } + catch (Exception ex) + { + LogManager.Error($"运行新测试失败: {ex.Message}"); + } + }); + } + catch (Exception ex) + { + LogManager.Error($"创建新测试失败: {ex.Message}"); + } + } + + /// /// 更新主测试记录(避免频繁创建新测试) /// @@ -430,6 +785,9 @@ namespace NavisworksTransport // 尝试访问对象的属性来检查是否有效 var displayName = item.DisplayName; var hasGeometry = item.HasGeometry; + + // 额外检查:确保对象没有被释放 + var boundingBox = item.BoundingBox(); return true; } catch (Exception ex) @@ -811,7 +1169,7 @@ namespace NavisworksTransport } /// - /// 刷新Clash Detective窗口 + /// 刷新Clash Detective窗口(修复版 - 避免UI阻塞) /// private void RefreshClashDetectiveWindow() { @@ -819,77 +1177,111 @@ namespace NavisworksTransport { LogManager.Info("开始刷新Clash Detective窗口..."); - // 方法1: 通过COM API刷新(如果可用) - if (_clashElement != null) + // 检查文档是否有效 + if (Application.ActiveDocument == null) + { + LogManager.Warning("文档已失效,跳过刷新"); + return; + } + + // 使用后台任务执行,避免UI阻塞 + System.Threading.Tasks.Task.Run(() => { try { - _clashElement.RunAllTests(); - LogManager.Info("通过COM API刷新Clash Detective窗口成功"); + // 方法1: 通过COM API刷新(如果可用) + if (_clashElement != null) + { + try + { + _clashElement.RunAllTests(); + LogManager.Info("通过COM API刷新Clash Detective窗口成功"); + } + catch (Exception ex) + { + LogManager.Warning($"COM API刷新失败: {ex.Message}"); + } + } + + // 方法2: 通过.NET API刷新(主要方法) + if (_documentClash != null) + { + try + { + // 重新运行我们的测试 + var currentTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == "动态运输路径碰撞检测"); + if (currentTest != null && currentTest is ClashTest clashTest) + { + _documentClash.TestsData.TestsRunTest(clashTest); + LogManager.Info($"通过.NET API刷新测试结果,当前结果数: {clashTest.Children.Count}"); + } + else + { + LogManager.Info("动态测试不存在,重新创建..."); + // 在后台任务中重新创建 + SetupDynamicClashTest(); + } + } + catch (Exception ex) + { + LogManager.Warning($".NET API刷新失败: {ex.Message}"); + } + } + + // 方法3: 强制重绘视图 + try + { + // 使用延迟重绘,避免立即操作 + System.Threading.Thread.Sleep(100); + Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All); + LogManager.Info("强制重绘视图成功"); + } + catch (Exception ex) + { + LogManager.Warning($"视图重绘失败: {ex.Message}"); + } + + // 方法4: 尝试手动触发Clash Detective UI更新 + try + { + // 使用延迟操作,给UI时间处理 + System.Threading.Thread.Sleep(100); + + // 强制保存和重新加载文档状态 + var doc = Application.ActiveDocument; + if (doc != null && doc.CurrentSelection != null) + { + var tempSelection = doc.CurrentSelection.SelectedItems; + doc.CurrentSelection.Clear(); + if (tempSelection.Count > 0) + { + doc.CurrentSelection.CopyFrom(tempSelection); + } + LogManager.Info("手动触发UI更新成功"); + } + } + catch (Exception ex) + { + LogManager.Warning($"手动UI更新失败: {ex.Message}"); + } + + LogManager.Info("Clash Detective窗口刷新完成"); + + } + catch (ObjectDisposedException ex) + { + LogManager.Warning($"刷新时对象已释放: {ex.Message}"); } catch (Exception ex) { - LogManager.Warning($"COM API刷新失败: {ex.Message}"); + LogManager.Error($"后台刷新失败: {ex.Message}"); } - } + }); - // 方法2: 通过.NET API刷新(主要方法) - if (_documentClash != null) - { - try - { - // 重新运行我们的测试 - var currentTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == "动态运输路径碰撞检测"); - if (currentTest != null && currentTest is ClashTest clashTest) - { - _documentClash.TestsData.TestsRunTest(clashTest); - LogManager.Info($"通过.NET API刷新测试结果,当前结果数: {clashTest.Children.Count}"); - } - else - { - LogManager.Info("动态测试不存在,重新创建..."); - SetupDynamicClashTest(); - } - } - catch (Exception ex) - { - LogManager.Warning($".NET API刷新失败: {ex.Message}"); - } - } - - // 方法3: 强制重绘视图 - try - { - Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All); - LogManager.Info("强制重绘视图成功"); - } - catch (Exception ex) - { - LogManager.Warning($"视图重绘失败: {ex.Message}"); - } - - // 方法4: 尝试手动触发Clash Detective UI更新 - try - { - // 强制保存和重新加载文档状态 - var tempSelection = Application.ActiveDocument.CurrentSelection.SelectedItems; - Application.ActiveDocument.CurrentSelection.Clear(); - if (tempSelection.Count > 0) - { - Application.ActiveDocument.CurrentSelection.CopyFrom(tempSelection); - } - LogManager.Info("手动触发UI更新成功"); - } - catch (Exception ex) - { - LogManager.Warning($"手动UI更新失败: {ex.Message}"); - } - - LogManager.Info("Clash Detective窗口刷新完成"); } catch (Exception ex) { - LogManager.Error($"刷新Clash Detective窗口失败: {ex.Message}"); + LogManager.Error($"刷新Clash Detective窗口启动失败: {ex.Message}"); } } @@ -1170,153 +1562,7 @@ namespace NavisworksTransport } } - /// - /// 强制显示测试结果(用于调试) - /// - public void ForceShowTestResults() - { - try - { - LogManager.Info("强制显示测试结果..."); - - if (!_isInitialized) - { - LogManager.Warning("集成未初始化,重新初始化..."); - Initialize(); - } - - if (_documentClash == null) - { - LogManager.Error("无法访问Clash Detective文档"); - return; - } - - // 创建一个完整的演示测试 - CreateDemoTest(); - - // 如果有最近的碰撞结果,也创建一个快照 - if (_currentCollisions.Count > 0) - { - var collisionResults = _currentCollisions.Select(c => new CollisionResult - { - ClashGuid = c.Guid, - DisplayName = c.DisplayName, - Status = c.Status, - Item1 = c.Item1, - Item2 = c.Item2, - Center = c.Center, - Distance = c.Distance, - CreatedTime = DateTime.Now - }).ToList(); - - CreateCollisionSnapshot(collisionResults); - } - - // 强制刷新显示 - RefreshClashDetectiveWindow(); - - // 记录最终状态 - LogManager.Info(GetTestStatusInfo()); - - } - catch (Exception ex) - { - LogManager.Error($"强制显示测试结果失败: {ex.Message}"); - } - } - - /// - /// 创建演示测试 - /// - private void CreateDemoTest() - { - try - { - // 获取一些测试对象,使用更安全的方法 - var doc = Application.ActiveDocument; - var allItems = doc.Models.RootItemDescendantsAndSelf - .Where(item => item.HasGeometry) - .ToList(); - - // 过滤出有效的对象 - var validItems = new List(); - foreach (var item in allItems) - { - if (IsModelItemValid(item)) - { - validItems.Add(item); - } - } - - LogManager.Info($"找到 {validItems.Count} 个有效的几何对象"); - - if (validItems.Count >= 2) - { - // 创建演示测试 - var demoTest = new ClashTest(); - demoTest.DisplayName = "Clash Detective 集成演示"; - demoTest.TestType = ClashTestType.Hard; - demoTest.Tolerance = 0.01; - - // 设置测试选择 - 使用更保守的方法 - var selectionA = new ModelItemCollection(); - var selectionB = new ModelItemCollection(); - - // 选择A:前几个对象 - int countA = Math.Min(5, validItems.Count / 2); - for (int i = 0; i < countA; i++) - { - if (IsModelItemValid(validItems[i])) - { - selectionA.Add(validItems[i]); - } - } - - // 选择B:后几个对象 - int startB = Math.Max(countA, validItems.Count / 2); - int countB = Math.Min(5, validItems.Count - startB); - for (int i = 0; i < countB; i++) - { - int index = startB + i; - if (index < validItems.Count && IsModelItemValid(validItems[index])) - { - selectionB.Add(validItems[index]); - } - } - - if (selectionA.Count > 0 && selectionB.Count > 0) - { - demoTest.SelectionA.Selection.CopyFrom(selectionA); - demoTest.SelectionB.Selection.CopyFrom(selectionB); - - LogManager.Info($"创建演示测试: A={selectionA.Count}, B={selectionB.Count}"); - - // 添加到文档 - _documentClash.TestsData.TestsAddCopy(demoTest); - - // 重新获取并运行测试 - var addedTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == "Clash Detective 集成演示"); - if (addedTest is ClashTest runTest) - { - _documentClash.TestsData.TestsRunTest(runTest); - LogManager.Info($"演示测试运行完成,结果数量: {runTest.Children.Count}"); - } - } - else - { - LogManager.Warning("没有足够的有效对象来创建演示测试"); - } - } - else - { - LogManager.Warning($"有效对象数量不足: {validItems.Count},需要至少2个"); - } - } - catch (Exception ex) - { - LogManager.Error($"创建演示测试失败: {ex.Message}"); - } - } + // 已删除:ForceShowTestResults 和 CreateDemoTest - 不再需要这些已废弃的方法 /// /// 清理资源 @@ -1334,8 +1580,7 @@ namespace NavisworksTransport // 清空结果 _currentCollisions.Clear(); - // 清理过多的快照测试(保留最近的5个) - CleanupSnapshotTests(); + // 清理工作:动画结束后统一处理,无需频繁清理 LogManager.Info("Clash Detective集成清理完成"); } @@ -1345,38 +1590,9 @@ namespace NavisworksTransport } } - /// - /// 清理过多的快照测试 - /// - private void CleanupSnapshotTests() - { - try - { - if (_documentClash == null) - return; + // 已删除:ForceCloseClashDetectiveDialogs - 不再需要强制关闭对话框 - // 查找所有快照测试 - var snapshotTests = _documentClash.TestsData.Tests - .Where(t => t.DisplayName.StartsWith("动画碰撞快照_")) - .Cast() - .ToList(); - - // 如果超过5个,删除最旧的 - if (snapshotTests.Count > 5) - { - var testsToRemove = snapshotTests.Take(snapshotTests.Count - 5); - foreach (var test in testsToRemove) - { - _documentClash.TestsData.TestsRemove(test); - LogManager.Debug($"已删除旧快照测试: {test.DisplayName}"); - } - } - } - catch (Exception ex) - { - LogManager.Error($"清理快照测试失败: {ex.Message}"); - } - } + // 已删除:CleanupSnapshotTests - 不再需要,使用动画结束后汇总 /// /// 触发碰撞检测事件 diff --git a/src/ClashDetectiveIntegrationTest.cs b/src/ClashDetectiveIntegrationTest.cs index 3ea3d96..f0e13be 100644 --- a/src/ClashDetectiveIntegrationTest.cs +++ b/src/ClashDetectiveIntegrationTest.cs @@ -440,7 +440,7 @@ namespace NavisworksTransport } /// - /// 快速验证集成功能 + /// 快速验证集成功能(修复版 - 避免UI阻塞) /// /// 验证是否成功 public static bool QuickValidation() @@ -460,26 +460,51 @@ namespace NavisworksTransport // 2. 检查初始化 instance.Initialize(); - // 3. 检查基本功能 + // 3. 检查基本功能(使用异步方式) var doc = NavisApplication.ActiveDocument; if (doc?.Models?.RootItemDescendantsAndSelf != null) { var testItem = doc.Models.RootItemDescendantsAndSelf - .FirstOrDefault(item => item.HasGeometry); + .Where(item => item != null && item.HasGeometry) + .FirstOrDefault(); if (testItem != null) { - var collisions = instance.DetectCollisions(testItem); - LogManager.Info($"快速验证成功: 检测到 {collisions.Count} 个碰撞"); + try + { + var collisions = instance.DetectCollisions(testItem); + LogManager.Info($"快速验证成功: 检测到 {collisions.Count} 个碰撞"); + } + catch (ObjectDisposedException) + { + LogManager.Warning("快速验证时对象已释放,跳过碰撞检测"); + } } } - // 4. 清理 - instance.Cleanup(); + // 4. 清理(异步执行) + System.Threading.Tasks.Task.Run(() => + { + try + { + System.Threading.Thread.Sleep(50); // 给UI线程喘息时间 + instance.Cleanup(); + LogManager.Debug("快速验证清理完成"); + } + catch (Exception cleanupEx) + { + LogManager.Warning($"快速验证清理失败: {cleanupEx.Message}"); + } + }); LogManager.Info("快速验证完成"); return true; } + catch (ObjectDisposedException ex) + { + LogManager.Warning($"快速验证时对象已释放: {ex.Message}"); + return false; + } catch (Exception ex) { LogManager.Error($"快速验证失败: {ex.Message}"); diff --git a/src/MainPlugin.cs b/src/MainPlugin.cs index f3e5f61..b3f4f2a 100644 --- a/src/MainPlugin.cs +++ b/src/MainPlugin.cs @@ -1468,74 +1468,6 @@ namespace NavisworksTransport }; groupBox.Controls.Add(_animationProgressBar); - // 测试Clash Detective集成按钮 - Button testClashDetectiveButton = new Button - { - Text = "测试Clash Detective集成", - Location = new Point(175, 25), - Size = new Size(150, 30), - Font = new Font("微软雅黑", 8), - BackColor = System.Drawing.Color.LightBlue - }; - - testClashDetectiveButton.Click += (sender, e) => - { - GlobalExceptionHandler.SafeExecute(() => - { - // 执行快速验证 - LogManager.Info("开始测试 Clash Detective 集成..."); - - bool quickValidation = ClashDetectiveIntegrationTest.QuickValidation(); - - if (quickValidation) - { - // 执行完整测试 - var testResults = ClashDetectiveIntegrationTest.RunFullIntegrationTest(); - - int passedTests = testResults.Count(r => r.IsSuccess); - int totalTests = testResults.Count; - - string message = $"测试完成!通过 {passedTests}/{totalTests} 个测试\n\n"; - - // 获取当前状态信息 - var statusInfo = ClashDetectiveIntegration.Instance.GetTestStatusInfo(); - - // 强制显示测试结果 - ClashDetectiveIntegration.Instance.ForceShowTestResults(); - - if (passedTests == totalTests) - { - message += "请检查 Clash Detective 窗口查看测试结果。\n"; - message += "如果窗口中没有显示测试,请:\n"; - message += "1. 确认 Clash Detective 窗口已打开\n"; - message += "2. 查看测试列表中的'动态运输路径碰撞检测'项\n"; - message += "3. 检查日志文件获取详细信息"; - - MessageBox.Show(message, "测试结果", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - else - { - message += "部分测试失败,详细信息:\n"; - foreach (var result in testResults.Where(r => !r.IsSuccess)) - { - message += $"- {result.TestName}: {result.Message}\n"; - } - message += "\n详细日志请查看日志文件"; - - MessageBox.Show(message, "测试结果", MessageBoxButtons.OK, MessageBoxIcon.Warning); - } - - // 显示状态信息 - LogManager.Info(statusInfo); - } - else - { - MessageBox.Show("快速验证失败,请检查日志了解详情", "测试失败", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - }, "测试Clash Detective集成"); - }; - - groupBox.Controls.Add(testClashDetectiveButton); } /// diff --git a/src/PathAnimationManager.cs b/src/PathAnimationManager.cs index 5b370dd..2b3d838 100644 --- a/src/PathAnimationManager.cs +++ b/src/PathAnimationManager.cs @@ -346,6 +346,18 @@ namespace NavisworksTransport SetState(AnimationState.Finished); // 标记为完成 AnimationCompleted?.Invoke(this, EventArgs.Empty); // 触发旧版完成事件 LogManager.Info("动画播放完成"); + + // 动画结束后创建最终碰撞结果汇总(安全模式) + try + { + LogManager.Info("开始执行碰撞结果汇总..."); + ClashDetectiveIntegration.Instance.CreateFinalCollisionSummary(); + LogManager.Info("碰撞结果汇总完成"); + } + catch (Exception ex) + { + LogManager.Error($"碰撞汇总执行失败: {ex.GetType().Name}: {ex.Message}"); + } } } catch (Exception ex) @@ -583,7 +595,7 @@ namespace NavisworksTransport } /// - /// 使用 Clash Detective 集成进行碰撞检测和高亮显示 + /// 使用 Clash Detective 集成进行碰撞检测并在运行时显示结果 /// private void CheckAndHighlightCollisionsWithClashDetective() { @@ -598,10 +610,16 @@ namespace NavisworksTransport // 高亮显示碰撞对象 ClashDetectiveIntegration.Instance.HighlightCollisions(collisionResults); - // 触发碰撞检测事件 + // 缓存碰撞结果,动画结束后统一处理 if (collisionResults.Count > 0) { + // 缓存碰撞结果 + ClashDetectiveIntegration.Instance.CreateCollisionSnapshot(collisionResults); + + // 触发碰撞检测事件 OnCollisionDetected(new CollisionDetectedEventArgs(collisionResults)); + + LogManager.Info($"检测到 {collisionResults.Count} 个碰撞,已缓存结果"); } LogManager.Debug($"碰撞检测完成: {collisionResults.Count} 个碰撞"); @@ -609,9 +627,6 @@ namespace NavisworksTransport catch (Exception ex) { LogManager.Error($"碰撞检测失败: {ex.Message}"); - - // 如果新方法失败,回退到简化版本 - CheckAndHighlightCollisions(); } } @@ -662,48 +677,7 @@ namespace NavisworksTransport CollisionDetected?.Invoke(this, e); } - /// - /// 检查并高亮碰撞(简化版本) - /// - private void CheckAndHighlightCollisions() - { - try - { - // 简化的碰撞检测:检查动画对象是否与其他对象的包围盒相交 - var doc = NavisApplication.ActiveDocument; - var animatedBoundingBox = _animatedObject.BoundingBox(); - - // 获取所有其他有几何体的对象 - var allItems = doc.Models.RootItemDescendantsAndSelf - .Where(item => item.HasGeometry && !item.Equals(_animatedObject)) - .ToList(); - - var collidingItems = new ModelItemCollection(); - - foreach (var item in allItems) - { - var itemBoundingBox = item.BoundingBox(); - if (BoundingBoxesIntersect(animatedBoundingBox, itemBoundingBox)) - { - collidingItems.Add(item); - } - } - - // 清除之前的高亮 - doc.Models.ResetAllTemporaryMaterials(); - - // 高亮碰撞对象(红色) - if (collidingItems.Count > 0) - { - doc.Models.OverrideTemporaryColor(collidingItems, Color.Red); - LogManager.Debug($"检测到 {collidingItems.Count} 处碰撞"); - } - } - catch (Exception ex) - { - LogManager.Debug($"碰撞检测更新失败: {ex.Message}"); - } - } + // 已删除:CheckAndHighlightCollisions - 使用Clash Detective集成代替 /// /// 检查两个包围盒是否相交