diff --git a/doc/design/2026/NavisworksAPI使用方法.md b/doc/design/2026/NavisworksAPI使用方法.md
index a7c354d..6f64ff5 100644
--- a/doc/design/2026/NavisworksAPI使用方法.md
+++ b/doc/design/2026/NavisworksAPI使用方法.md
@@ -1491,7 +1491,7 @@ public async Task SafeCacheRefreshAsync()
- `DatabaseDockPane/Models.cs` - 数据库操作示例
- `ClashDetective` 相关示例 - 高级功能示例
-碰撞检测模式说明
+### 碰撞检测模式说明
Hard(硬碰撞)
@@ -1559,3 +1559,89 @@ public async Task SafeCacheRefreshAsync()
collisionTest.SelectionA.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
collisionTest.SelectionB.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
```
+
+### ModelGeometry 属性
+
+ ModelGeometry类公开了以下成员:
+
+ 属性
+
+ | 名称 | 说明 |
+ |----------------------------|----------------------------------------|
+ | ActiveColor | 此几何体的当前(可见)颜色 |
+ | ActiveTransform | 返回几何体当前活动的变换矩阵 |
+ | ActiveTransparency | 此几何体的当前(可见)透明度 |
+ | BoundingBox | 此几何体在世界坐标系中的包围盒 |
+ | FragmentCount | 此几何体被分割成的片段数量 |
+ | IsDisposed | 获取一个值,指示对象是否已被释放且不再可用(继承自NativeHandle) |
+ | IsReadOnly | 是否只读(重写NativeHandle的IsReadOnly属性) |
+ | IsSolid | 此几何体是否为实体?(包括所有形成封闭、流形外壳的三角形图元) |
+ | Item | 模型层次结构中对应此几何体的项目 |
+ | OriginalColor | 此几何体的原始颜色(设计文件中指定的) |
+ | OriginalTransform | 返回几何体加载时的原始变换矩阵 |
+ | OriginalTransparency | 此几何体的原始透明度(设计文件中指定的) |
+ | PermanentColor | 几何体的永久颜色。可能是原始颜色或用户明确覆盖的颜色 |
+ | PermanentOverrideTransform | 应用于模型几何体原始变换的覆盖变换 |
+ | PermanentTransform | 模型几何体的永久变换。由原始变换与覆盖变换组合形成的变换 |
+ | PermanentTransparency | 几何体的永久透明度。可能是原始透明度或用户明确覆盖的透明度 |
+ | PrimitiveCount | 定义此几何体的图元(三角形、线、点)数量 |
+ | PrimitiveTypes | 用于定义此几何体的图元类型 |
+
+ 这些属性提供了访问和查询Navisworks模型几何体各种状态信息的接口,包括颜色、变换、透明度、包围盒和几何体结构等重要属性。
+
+```csharp
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+
+using System.Text;
+
+using Autodesk.Navisworks.Api.Controls;
+
+ static public void OutputFirstGeometry()
+ {
+ try
+ {
+ if (Autodesk.Navisworks.Api.Application.ActiveDocument != null &&
+ !Autodesk.Navisworks.Api.Application.ActiveDocument.IsClear)
+ {
+ ModelGeometry first =
+ Autodesk.Navisworks.Api.Application.ActiveDocument.
+ Models[0].RootItem.FindFirstGeometry();
+
+ if (first != null)
+ {
+ string text = string.Empty;
+ text = string.Format("ActiveColor = {0}" +
+ "\nActiveTransparency = {1}" +
+ "\nBoundingBox = {2}" +
+ "\nFragmentCount {3}" +
+ "\nIsSolid {4}" +
+ "\nItem {5}" +
+ "\nOriginalColor {6}" +
+ "\nOriginalTransparency {7}" +
+ "\nPermanentColor {8}" +
+ "\nPermanentTransparency {9}",
+ first.ActiveColor.ToString(),
+ first.ActiveTransparency,
+ first.BoundingBox.ToString(),
+ first.FragmentCount,
+ first.IsSolid.ToString(),
+ first.Item.ToString(),
+ first.OriginalColor.ToString(),
+ first.OriginalTransparency,
+ first.PermanentColor.ToString(),
+ first.PermanentTransparency);
+
+ MessageBox.Show(text);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(e.Message + "\n\n" + e.ToString());
+ }
+ }
+```
diff --git a/src/Core/Collision/ClashDetectiveIntegration.cs b/src/Core/Collision/ClashDetectiveIntegration.cs
index bac41c4..4e76112 100644
--- a/src/Core/Collision/ClashDetectiveIntegration.cs
+++ b/src/Core/Collision/ClashDetectiveIntegration.cs
@@ -6,7 +6,6 @@ using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Clash;
using ComApi = Autodesk.Navisworks.Api.Interop.ComApi;
using ComApiBridge = Autodesk.Navisworks.Api.ComApi;
-using NavisworksTransport.UI.WPF;
using NavisworksTransport.Utils;
namespace NavisworksTransport
diff --git a/src/Core/Collision/ClashDetectiveIntegration_copy.cs b/src/Core/Collision/ClashDetectiveIntegration_copy.cs
deleted file mode 100644
index c81494f..0000000
--- a/src/Core/Collision/ClashDetectiveIntegration_copy.cs
+++ /dev/null
@@ -1,2788 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Autodesk.Navisworks.Api;
-using Autodesk.Navisworks.Api.Clash;
-using ComApi = Autodesk.Navisworks.Api.Interop.ComApi;
-using ComApiBridge = Autodesk.Navisworks.Api.ComApi;
-using NavisworksTransport.UI.WPF;
-using NavisworksTransport.Utils;
-
-namespace NavisworksTransport
-{
- ///
- /// Clash Detective 集成管理器
- /// 实现动态碰撞检测与Clash Detective窗口的联动
- ///
- public class ClashDetectiveIntegration
- {
- private static ClashDetectiveIntegration _instance;
- private ComApi.InwOpClashElement _clashElement;
- private ComApi.InwOpState10 _state;
- private DocumentClash _documentClash;
- private ClashTest _dynamicClashTest;
- private List _currentCollisions;
- private bool _isInitialized = false;
- // 通道对象缓存,用于优化碰撞检测性能
- private static HashSet _channelObjectsCache = null;
- private static readonly object _cacheLock = new object();
-
- // 几何对象列表缓存,用于避免重复获取对象列表
- private static List _allGeometryItemsCache = null;
-
- // 碰撞检测计数器
- private int _animationCollisionCount = 0; // 动画过程中简单包围盒检测的碰撞数量
- private int _clashDetectiveCollisionCount = 0; // Clash Detective最终检测的碰撞数量
-
- ///
- /// 动画过程中检测到的碰撞数量(仅供参考统计)
- ///
- public int AnimationCollisionCount
- {
- get { return _animationCollisionCount; }
- }
-
- ///
- /// Clash Detective检测到的权威碰撞数量
- ///
- public int ClashDetectiveCollisionCount
- {
- get { return _clashDetectiveCollisionCount; }
- }
-
- ///
- /// 单例实例
- ///
- public static ClashDetectiveIntegration Instance
- {
- get
- {
- if (_instance == null)
- {
- _instance = new ClashDetectiveIntegration();
- }
- return _instance;
- }
- }
-
- ///
- /// 当前检测到的碰撞结果
- ///
- public List CurrentCollisions
- {
- get { return _currentCollisions ?? new List(); }
- }
-
- ///
- /// 碰撞检测结果变化事件
- ///
- public event EventHandler CollisionDetected;
-
- private ClashDetectiveIntegration()
- {
- _currentCollisions = new List();
- _cachedResults = new List();
-
- // 延迟初始化,避免在清理时无意中触发
- // Initialize()方法现在需要手动调用
- }
-
- ///
- /// 初始化Clash Detective集成
- ///
- public void Initialize()
- {
- try
- {
- LogManager.Info("初始化Clash Detective集成...");
-
- // 获取COM API状态
- _state = ComApiBridge.ComApiBridge.State;
-
- // 查找Clash Detective插件
- _clashElement = FindClashDetectivePlugin();
-
- // 尝试获取.NET API的Clash文档(无论是否找到COM插件)
- try
- {
- _documentClash = Application.ActiveDocument.GetClash();
- if (_documentClash != null)
- {
- LogManager.Info("成功获取.NET API Clash文档");
-
- // 创建动态碰撞检测测试
- SetupDynamicClashTest();
-
- _isInitialized = true;
-
- if (_clashElement != null)
- {
- LogManager.Info("Clash Detective集成初始化成功(完整功能)");
- }
- else
- {
- LogManager.Info("Clash Detective集成初始化成功(.NET API模式)");
- }
- }
- else
- {
- LogManager.Warning("无法获取Clash文档,Clash Detective可能未安装");
- _isInitialized = false;
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"获取.NET API Clash文档失败: {ex.Message}");
- _isInitialized = false;
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"初始化Clash Detective集成失败: {ex.Message}");
- _isInitialized = false;
- }
- }
-
- ///
- /// 查找Clash Detective插件(简化版本 - 跳过无效的COM API枚举)
- ///
- private ComApi.InwOpClashElement FindClashDetectivePlugin()
- {
- try
- {
- LogManager.Info("跳过COM API插件枚举,直接使用.NET API访问Clash Detective...");
-
- // 直接尝试通过.NET API访问,避免枚举大量无效COM插件
- try
- {
- var documentClash = Application.ActiveDocument.GetClash();
- if (documentClash != null)
- {
- LogManager.Info("通过.NET API成功访问Clash Detective功能");
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"通过.NET API访问Clash Detective失败: {ex.Message}");
- }
-
- LogManager.Info("将使用.NET API替代方案,跳过COM插件查找");
- return null;
- }
- catch (Exception ex)
- {
- LogManager.Error($"查找Clash Detective插件失败: {ex.Message}");
- return null;
- }
- }
-
- ///
- /// 设置动态碰撞检测测试
- ///
- private void SetupDynamicClashTest()
- {
- try
- {
- // 清理所有动画相关的测试(包括之前创建的"动画路径碰撞_X"测试)
- var existingAnimationTests = _documentClash.TestsData.Tests
- .Where(t => t.DisplayName.StartsWith("动态运输路径碰撞检测") ||
- t.DisplayName.StartsWith("动画路径碰撞"))
- .ToList();
-
- if (existingAnimationTests.Any())
- {
- LogManager.Info($"发现 {existingAnimationTests.Count} 个需要清理的动画测试");
- int removedCount = 0;
-
- foreach (var test in existingAnimationTests)
- {
- if (test is ClashTest testToRemove)
- {
- try
- {
- _documentClash.TestsData.TestsRemove(testToRemove);
- removedCount++;
- LogManager.Debug($"已删除测试: {test.DisplayName}");
- }
- catch (Exception ex)
- {
- LogManager.Warning($"删除测试失败 {test.DisplayName}: {ex.Message}");
- }
- }
- }
-
- LogManager.Info($"成功清理了 {removedCount} 个旧的动画碰撞测试");
- }
- else
- {
- LogManager.Info("未发现需要清理的旧动画测试");
- }
-
- // 创建新的碰撞测试
- _dynamicClashTest = new ClashTest();
- _dynamicClashTest.DisplayName = "动态运输路径碰撞检测";
- _dynamicClashTest.TestType = ClashTestType.Hard;
- _dynamicClashTest.Tolerance = 0.01; // 1cm容差
-
- // 设置几何类型:包含面、线和点以获得最全面的碰撞检测
- _dynamicClashTest.SelectionA.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
- _dynamicClashTest.SelectionB.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
-
- LogManager.Debug("动态碰撞测试几何类型设置: 包含面、线、点");
-
- // 设置测试规则 - 移除在2017版本中不可用的规则设置
- // _dynamicClashTest.Rules.Add(ClashRule.IgnoreWithinSameFile);
- // _dynamicClashTest.Rules.Add(ClashRule.IgnoreWithinSameLayer);
-
- // 添加到文档,并获取返回的测试对象
- _documentClash.TestsData.TestsAddCopy(_dynamicClashTest);
-
- // 重新获取添加后的测试对象
- var addedTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == "动态运输路径碰撞检测");
- if (addedTest is ClashTest clashTest)
- {
- _dynamicClashTest = clashTest;
- LogManager.Info("动态碰撞检测测试创建成功,使用返回的测试对象");
- }
- else
- {
- LogManager.Warning("无法获取添加后的测试对象,继续使用原始对象");
- }
-
- LogManager.Info("动态碰撞检测测试创建成功");
- }
- catch (Exception ex)
- {
- LogManager.Error($"设置动态碰撞检测测试失败: {ex.Message}");
- }
- }
-
- ///
- /// 执行动态碰撞检测
- ///
- /// 动画对象
- /// 排除对象
- /// 检测间隙(米)
- /// 碰撞结果
- public List DetectCollisions(ModelItem animatedObject,
- ModelItemCollection excludeObjects = null, double detectionGap = 0.2)
- {
- try
- {
- // 使用简化检测模式
- LogManager.Debug($"使用简化检测模式进行碰撞检测,检测间隙: {detectionGap}米");
- var results = DetectCollisionsSimple(animatedObject, excludeObjects, detectionGap);
-
- // 在动画过程中不创建Clash Detective测试,避免影响性能
- // 只有在动画结束后手动测试时才创建完整的测试
-
- // 触发事件
- OnCollisionDetected(new CollisionDetectedEventArgs(results));
-
- LogManager.Debug($"碰撞检测完成,发现 {results.Count} 个碰撞");
- return results;
- }
- catch (Exception ex)
- {
- LogManager.Error($"碰撞检测失败: {ex.Message}");
- // 回退到简化检测
- return DetectCollisionsSimple(animatedObject, excludeObjects, detectionGap);
- }
- }
-
-///
- /// 创建碰撞快照(动画结束后一次性更新结果)
- ///
- public void CreateCollisionSnapshot(List results)
- {
- try
- {
- if (_documentClash == null)
- return;
-
- // 缓存结果,等动画结束后统一处理
- CacheCollisionResults(results);
- }
- catch (Exception ex)
- {
- LogManager.Error($"创建实时碰撞快照失败: {ex.Message}");
- }
- }
-
- private List _cachedResults = new List();
-
- // 高亮管理字段
- private Dictionary _activeHighlights = new Dictionary();
- private readonly object _highlightLock = new object();
-
- ///
- /// 缓存碰撞结果
- ///
- private void CacheCollisionResults(List results)
- {
- if (results != null && results.Count > 0)
- {
- _cachedResults.AddRange(results);
- }
- }
-
- private DateTime _lastCollisionTestTime = DateTime.MinValue;
- private readonly TimeSpan _minTestInterval = TimeSpan.FromMilliseconds(500); // 最小间隔500ms
-
- ///
- /// 缓存碰撞结果(动画过程中使用)- 现在包含位置信息用于恢复测试
- /// 使用精确的碰撞检测算法替代简化检测
- ///
- public void CacheCollisionDuringAnimation(ModelItem animatedObject, Point3D animatedObjectPosition, ModelItem collisionObject, Point3D collisionObjectPosition = null)
- {
- try
- {
- // 🔍 添加碰撞缓存开始的详细日志
- LogManager.Debug($"=== [碰撞缓存-开始] ===");
- LogManager.Debug($"[碰撞缓存-开始] 动画对象: {animatedObject?.DisplayName ?? "NULL"}");
- LogManager.Debug($"[碰撞缓存-开始] 碰撞对象: {collisionObject?.DisplayName ?? "NULL"}");
-
- // 🔍 对象验证详情
- if (animatedObject == null)
- {
- LogManager.Error($"[碰撞缓存-错误] 动画对象为NULL!");
- }
-
- if (collisionObject == null)
- {
- LogManager.Error($"[碰撞缓存-错误] 碰撞对象为NULL!");
- }
-
- // 🔍 位置信息详情
- LogManager.Debug($"[碰撞缓存-位置] 动画对象位置: ({animatedObjectPosition.X:F3},{animatedObjectPosition.Y:F3},{animatedObjectPosition.Z:F3})");
-
- if (collisionObjectPosition != null)
- {
- LogManager.Debug($"[碰撞缓存-位置] 碰撞对象位置: ({collisionObjectPosition.X:F3},{collisionObjectPosition.Y:F3},{collisionObjectPosition.Z:F3})");
- }
- else
- {
- LogManager.Debug($"[碰撞缓存-位置] 碰撞对象位置: NULL (将自动计算)");
- }
-
- if (!IsModelItemValid(animatedObject) || !IsModelItemValid(collisionObject))
- {
- LogManager.Warning($"[诊断-无效对象] 对象验证失败,退出缓存过程");
- return;
- }
-
- // 🔧 智能容器映射:将几何体子对象映射到有名称的容器对象
- var mappedAnimatedObject = GetCollisionObjectWithValidIdentity(animatedObject);
- var mappedCollisionObject = GetCollisionObjectWithValidIdentity(collisionObject);
-
- // 检查是否进行了容器映射
- bool hasMapping1 = !mappedAnimatedObject.Equals(animatedObject);
- bool hasMapping2 = !mappedCollisionObject.Equals(collisionObject);
-
- LogManager.Info($"[容器映射] 动画对象: '{animatedObject.DisplayName}' -> '{mappedAnimatedObject.DisplayName}' (映射: {hasMapping1})");
- LogManager.Info($"[容器映射] 碰撞对象: '{collisionObject.DisplayName}' -> '{mappedCollisionObject.DisplayName}' (映射: {hasMapping2})");
-
- // 使用包围盒碰撞检测算法
- var animatedBoundingBox = animatedObject.BoundingBox();
- var collisionBoundingBox = collisionObject.BoundingBox();
-
- LogManager.Debug($"[碰撞缓存-包围盒] 动画对象包围盒: Min({animatedBoundingBox.Min.X:F3},{animatedBoundingBox.Min.Y:F3},{animatedBoundingBox.Min.Z:F3}) Max({animatedBoundingBox.Max.X:F3},{animatedBoundingBox.Max.Y:F3},{animatedBoundingBox.Max.Z:F3})");
- LogManager.Debug($"[碰撞缓存-包围盒] 碰撞对象包围盒: Min({collisionBoundingBox.Min.X:F3},{collisionBoundingBox.Min.Y:F3},{collisionBoundingBox.Min.Z:F3}) Max({collisionBoundingBox.Max.X:F3},{collisionBoundingBox.Max.Y:F3},{collisionBoundingBox.Max.Z:F3})");
-
- if (BoundingBoxGeometryUtils.BoundingBoxesIntersect(animatedBoundingBox, collisionBoundingBox))
- {
- // 🔍 计算并验证位置信息
- var finalCollisionPosition = collisionObjectPosition ?? GetObjectPosition(collisionObject);
-
- LogManager.Debug($"[碰撞缓存-位置计算] 最终使用的碰撞对象位置: ({finalCollisionPosition.X:F3},{finalCollisionPosition.Y:F3},{finalCollisionPosition.Z:F3})");
-
- // 🔍 检查位置是否相同(可能的自碰撞标志)
- var positionDistance = Math.Sqrt(
- Math.Pow(animatedObjectPosition.X - finalCollisionPosition.X, 2) +
- Math.Pow(animatedObjectPosition.Y - finalCollisionPosition.Y, 2) +
- Math.Pow(animatedObjectPosition.Z - finalCollisionPosition.Z, 2));
-
- LogManager.Debug($"[碰撞缓存-距离计算] 动画对象与碰撞对象的位置距离: {positionDistance:F4}");
-
- if (positionDistance < 0.001) // 位置基本相同
- {
- LogManager.Warning($"[诊断-位置警告] 两个对象位置几乎相同 (距离: {positionDistance:F6}),这可能是自碰撞的标志!");
- }
-
- // 创建碰撞结果
- var collisionDistance = BoundingBoxGeometryUtils.CalculateDistance(animatedBoundingBox, collisionBoundingBox);
- var collisionCenter = BoundingBoxGeometryUtils.CalculateCenter(animatedBoundingBox, collisionBoundingBox);
-
- LogManager.Debug($"[碰撞缓存-距离] 包围盒碰撞距离: {collisionDistance:F4}");
- LogManager.Debug($"[碰撞缓存-中心点] 碰撞中心: ({collisionCenter.X:F3},{collisionCenter.Y:F3},{collisionCenter.Z:F3})");
-
- var collision = new CollisionResult
- {
- ClashGuid = Guid.NewGuid(),
- DisplayName = $"精确碰撞: {mappedAnimatedObject.DisplayName} <-> {mappedCollisionObject.DisplayName}",
- Status = ClashResultStatus.New,
- Item1 = mappedAnimatedObject, // 记录容器对象
- Item2 = mappedCollisionObject, // 记录容器对象
- OriginalItem1 = animatedObject, // 保存原始几何体对象
- OriginalItem2 = collisionObject, // 保存原始几何体对象
- HasContainerMapping = hasMapping1 || hasMapping2, // 标记是否进行了映射
- CreatedTime = DateTime.Now,
- Distance = collisionDistance,
- Center = collisionCenter,
- Item1Position = animatedObjectPosition,
- Item2Position = finalCollisionPosition,
- HasPositionInfo = true
- };
-
- // 去重处理:避免重复缓存相同的碰撞对(基于容器对象)
- var existing = _cachedResults.FirstOrDefault(r =>
- r.Item1.Equals(mappedAnimatedObject) && r.Item2.Equals(mappedCollisionObject));
-
- if (existing == null)
- {
- _cachedResults.Add(collision);
- }
- }
- else
- {
- LogManager.Debug($"[碰撞缓存-包围盒] 包围盒不相交,跳过缓存");
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"缓存碰撞失败: {ex.Message}");
- LogManager.Error($"[诊断-异常堆栈] {ex.StackTrace}");
- }
- }
-
- ///
- /// 获取对象当前位置
- ///
- 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);
- }
- }
-
- ///
- /// 动画结束后统一创建和运行ClashDetective碰撞测试
- ///
- public void CreateAllAnimationCollisionTests(double detectionGap = 0.05)
- {
- try
- {
- LogManager.Info($"=== 动画结束,开始创建ClashDetective碰撞测试(容差: {detectionGap}米) ===");
-
- if (_documentClash == null || _cachedResults.Count == 0)
- {
- LogManager.Info($"没有缓存的碰撞结果需要处理 - _documentClash: {_documentClash != null}, _cachedResults.Count: {_cachedResults.Count}");
- return;
- }
-
- LogManager.Info($"[缓存分析-统计] 共有 {_cachedResults.Count} 个原始缓存结果");
-
- // 🔍 详细分析每个缓存项
- LogManager.Info($"[缓存分析-原始] 开始分析所有原始缓存结果:");
- for (int i = 0; i < _cachedResults.Count; i++)
- {
- var item = _cachedResults[i];
- LogManager.Info($"[缓存分析-{i+1:00}] 对象: {item.Item1?.DisplayName ?? "NULL"} <-> {item.Item2?.DisplayName ?? "NULL"}");
- LogManager.Info($"[缓存分析-{i+1:00}] 时间: {item.CreatedTime:HH:mm:ss.fff}");
- LogManager.Info($"[缓存分析-{i+1:00}] 动画位置: ({item.Item1Position.X:F3},{item.Item1Position.Y:F3},{item.Item1Position.Z:F3})");
- LogManager.Info($"[缓存分析-{i+1:00}] 碰撞位置: ({item.Item2Position.X:F3},{item.Item2Position.Y:F3},{item.Item2Position.Z:F3})");
- LogManager.Info($"[缓存分析-{i+1:00}] 距离: {item.Distance:F4}");
- LogManager.Info($"[缓存分析-{i+1:00}] GUID: {item.ClashGuid}");
- LogManager.Info($"[缓存分析-{i+1:00}] HasPositionInfo: {item.HasPositionInfo}");
-
- // 验证对象有效性
- var item1Valid = IsModelItemValid(item.Item1);
- var item2Valid = IsModelItemValid(item.Item2);
- LogManager.Info($"[缓存分析-{i+1:00}] 对象有效性: Item1={item1Valid}, Item2={item2Valid}");
-
- if (!item1Valid)
- {
- LogManager.Warning($"[缓存分析-{i+1:00}] Item1无效: {item.Item1?.DisplayName ?? "NULL"}");
- }
- if (!item2Valid)
- {
- LogManager.Warning($"[缓存分析-{i+1:00}] Item2无效: {item.Item2?.DisplayName ?? "NULL"}");
- }
- }
-
- // 获取动画对象和当前动画终点位置(用于后续恢复)
- ModelItem animatedObject = null;
- Point3D animationEndPosition = new Point3D(0, 0, 0);
-
- if (_cachedResults.Count > 0)
- {
- animatedObject = _cachedResults[0].Item1;
-
- // 记录当前位置(应该是动画终点)
- if (animatedObject != null && IsModelItemValid(animatedObject))
- {
- var bounds = animatedObject.BoundingBox();
- animationEndPosition = new Point3D(
- (bounds.Min.X + bounds.Max.X) / 2,
- (bounds.Min.Y + bounds.Max.Y) / 2,
- (bounds.Min.Z + bounds.Max.Z) / 2
- );
- LogManager.Info($"记录动画终点位置: ({animationEndPosition.X:F2},{animationEndPosition.Y:F2},{animationEndPosition.Z:F2})");
- }
- }
-
- // 🔍 去重处理:按对象对分组,并详细记录过程
- LogManager.Info($"[去重分析-开始] 开始对 {_cachedResults.Count} 个缓存结果进行去重");
-
- var uniqueCollisions = _cachedResults
- .GroupBy(r => new { r.Item1, r.Item2 })
- .Select(g => new
- {
- Collision = g.First(),
- Count = g.Count(),
- FirstTime = g.Min(r => r.CreatedTime),
- LastTime = g.Max(r => r.CreatedTime),
- AllItems = g.ToList() // 保留所有项用于分析
- })
- .ToList();
-
- LogManager.Info($"[去重分析-结果] 去重后得到 {uniqueCollisions.Count} 个唯一碰撞对");
- LogManager.Info($"[去重分析-效率] 去重前: {_cachedResults.Count}, 去重后: {uniqueCollisions.Count}, 压缩率: {(1.0 - (double)uniqueCollisions.Count / _cachedResults.Count) * 100:F1}%");
-
- // 🔍 分析每个唯一碰撞对的详情
- for (int i = 0; i < uniqueCollisions.Count; i++)
- {
- var group = uniqueCollisions[i];
- LogManager.Info($"[去重分析-唯一{i+1:00}] 碰撞对: {group.Collision.Item1?.DisplayName ?? "NULL"} <-> {group.Collision.Item2?.DisplayName ?? "NULL"}");
- LogManager.Info($"[去重分析-唯一{i+1:00}] 重复次数: {group.Count}");
- LogManager.Info($"[去重分析-唯一{i+1:00}] 时间范围: {group.FirstTime:HH:mm:ss.fff} ~ {group.LastTime:HH:mm:ss.fff}");
- LogManager.Info($"[去重分析-唯一{i+1:00}] 选择记录: 时间={group.Collision.CreatedTime:HH:mm:ss.fff}, 距离={group.Collision.Distance:F4}");
-
- // 如果有重复,分析所有重复项
- if (group.Count > 1)
- {
- LogManager.Info($"[去重分析-唯一{i+1:00}] 重复项详情:");
- for (int j = 0; j < group.AllItems.Count; j++)
- {
- var dupItem = group.AllItems[j];
- LogManager.Info($" [{j+1}] 时间: {dupItem.CreatedTime:HH:mm:ss.fff}, 距离: {dupItem.Distance:F4}, GUID: {dupItem.ClashGuid}");
- }
- }
-
- // 验证去重键的具体值
- var key1Name = group.Collision.Item1?.DisplayName ?? "NULL";
- var key2Name = group.Collision.Item2?.DisplayName ?? "NULL";
- var key1Guid = group.Collision.Item1?.InstanceGuid.ToString() ?? "NULL";
- var key2Guid = group.Collision.Item2?.InstanceGuid.ToString() ?? "NULL";
-
- LogManager.Debug($"[去重分析-唯一{i+1:00}] 去重键: Item1Name='{key1Name}', Item2Name='{key2Name}'");
- LogManager.Debug($"[去重分析-唯一{i+1:00}] 去重键: Item1GUID='{key1Guid}', Item2GUID='{key2Guid}'");
- }
-
- int createdCount = 1; // 从1开始编号
- var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
-
- // 使用位置恢复方案:为每个碰撞单独创建测试并恢复位置
- LogManager.Info("=== 开始位置恢复方案:为每个碰撞恢复对象位置 ===");
-
- foreach (var collisionGroup in uniqueCollisions)
- {
- try
- {
- var collision = collisionGroup.Collision;
-
- // 检查是否有位置信息
- if (!collision.HasPositionInfo)
- {
- LogManager.Warning($"跳过无位置信息的碰撞: {collision.Item1?.DisplayName} <-> {collision.Item2?.DisplayName}");
- continue;
- }
-
- // 确保对象仍然有效
- if (!IsModelItemValid(collision.Item1) || !IsModelItemValid(collision.Item2))
- {
- LogManager.Warning($"跳过无效对象: {collision.Item1?.DisplayName} <-> {collision.Item2?.DisplayName}");
- continue;
- }
-
- var testName = $"动画路径碰撞_{createdCount:0}_{DateTime.Now:HHmmss}";
-
- LogManager.Info($"=== [测试创建-{createdCount:00}] 开始创建测试: {testName} ===");
- LogManager.Info($"[测试创建-{createdCount:00}] 动画对象: {collision.Item1?.DisplayName ?? "NULL"}");
- LogManager.Info($"[测试创建-{createdCount:00}] 碰撞对象: {collision.Item2?.DisplayName ?? "NULL"}");
- LogManager.Info($"[测试创建-{createdCount:00}] 动画对象位置: ({collision.Item1Position.X:F3},{collision.Item1Position.Y:F3},{collision.Item1Position.Z:F3})");
- LogManager.Info($"[测试创建-{createdCount:00}] 碰撞对象位置: ({collision.Item2Position.X:F3},{collision.Item2Position.Y:F3},{collision.Item2Position.Z:F3})");
- LogManager.Info($"[测试创建-{createdCount:00}] 原始距离: {collision.Distance:F4}");
- LogManager.Info($"[测试创建-{createdCount:00}] 创建时间: {collision.CreatedTime:HH:mm:ss.fff}");
- LogManager.Info($"[测试创建-{createdCount:00}] 使用容差: {detectionGap}米");
-
- // 将对象移动到碰撞位置
- var testAnimatedObject = collision.Item1;
- var modelItems = new ModelItemCollection { testAnimatedObject };
- var targetPosition = collision.Item1Position;
-
- // 计算当前位置到目标位置的偏移
- var currentBounds = testAnimatedObject.BoundingBox();
- var currentPos = new Point3D(
- (currentBounds.Min.X + currentBounds.Max.X) / 2,
- (currentBounds.Min.Y + currentBounds.Max.Y) / 2,
- (currentBounds.Min.Z + currentBounds.Max.Z) / 2
- );
-
- var offset = new Vector3D(
- targetPosition.X - currentPos.X,
- targetPosition.Y - currentPos.Y,
- targetPosition.Z - currentPos.Z
- );
-
- var transform = Transform3D.CreateTranslation(offset);
- doc.Models.OverridePermanentTransform(modelItems, transform, false);
-
- LogManager.Info($"[测试创建-{createdCount:00}] 已将 {testAnimatedObject.DisplayName} 移动到碰撞位置: ({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3})");
- LogManager.Debug($"[测试创建-{createdCount:00}] 移动偏移量: ({offset.X:F3},{offset.Y:F3},{offset.Z:F3})");
-
- try
- {
- // 创建新的碰撞测试
- var collisionTest = new ClashTest
- {
- DisplayName = testName,
- TestType = ClashTestType.HardConservative, // 保守硬碰撞
- Tolerance = detectionGap, // 使用传入的检测间隙参数(保守硬碰撞,建议为0,结果准确)
- Guid = Guid.Empty
- };
-
-
- LogManager.Debug($"[测试创建-{createdCount:00}] 几何类型设置: 包含面、线、点");
-
- // 设置选择集A
- var selectionA = new ModelItemCollection
- {
- collision.Item1
- };
-
- // 设置几何类型:包含面、线和点以获得最全面的碰撞检测
- collisionTest.SelectionA.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
- collisionTest.SelectionA.Selection.CopyFrom(selectionA);
- LogManager.Debug($"[测试创建-{createdCount:00}] 选择集A设置: {collision.Item1?.DisplayName ?? "NULL"} (数量: {selectionA.Count})");
-
- // 设置选择集B
- var selectionB = new ModelItemCollection
- {
- collision.Item2
- };
-
- collisionTest.SelectionB.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
- collisionTest.SelectionB.Selection.CopyFrom(selectionB);
- LogManager.Debug($"[测试创建-{createdCount:00}] 选择集B设置: {collision.Item2?.DisplayName ?? "NULL"} (数量: {selectionB.Count})");
-
- try
- {
- _documentClash.TestsData.TestsAddCopy(collisionTest);
- }
- catch (Exception addEx)
- {
- LogManager.Error($"添加测试失败: {addEx.GetType().Name}: {addEx.Message}");
- throw;
- }
-
- // 获取添加后的测试并运行
- var addedTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == testName) as ClashTest;
-
- if (addedTest != null)
- {
- try
- {
- LogManager.Info($"[测试创建-{createdCount:00}] 开始运行测试...");
- _documentClash.TestsData.TestsRunTest(addedTest);
-
- // 重新获取测试对象,避免访问已释放的对象
- var refreshedTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == testName) as ClashTest;
- if (refreshedTest != null)
- {
- LogManager.Info($"[测试创建-{createdCount:00}] 测试运行完成 - 碰撞数量: {refreshedTest.Children.Count}");
-
- // 🔍 详细分析测试结果
- if (refreshedTest.Children.Count > 0)
- {
- LogManager.Info($"[测试创建-{createdCount:00}] 检测到碰撞,分析结果:");
- foreach (var child in refreshedTest.Children.Take(3)) // 只显示前3个结果
- {
- if (child is ClashResult result)
- {
- LogManager.Info($" 碰撞对象: {result.Item1?.DisplayName ?? "空"} <-> {result.Item2?.DisplayName ?? "空"}");
- LogManager.Info($" 状态: {result.Status}, 距离: {result.Distance:F4}");
- LogManager.Info($" 中心点: ({result.Center.X:F3},{result.Center.Y:F3},{result.Center.Z:F3})");
- }
- }
- if (refreshedTest.Children.Count > 3)
- {
- LogManager.Info($" ... 还有 {refreshedTest.Children.Count - 3} 个碰撞结果");
- }
- }
- else
- {
- LogManager.Warning($"[测试创建-{createdCount:00}] 测试未检测到碰撞!这可能表示:");
- LogManager.Warning($" 1. 对象移动后不再碰撞");
- LogManager.Warning($" 2. 对象在测试时无效");
- LogManager.Warning($" 3. Clash Detective测试设置问题");
- }
- }
- else
- {
- LogManager.Error($"[测试创建-{createdCount:00}] 运行后无法获取刷新的测试对象");
- }
- }
- catch (Exception runEx)
- {
- LogManager.Error($"运行测试失败: {runEx.Message}");
- }
- }
- }
- catch (Exception createEx)
- {
- LogManager.Error($"创建测试失败 - 异常类型: {createEx.GetType().Name}: {createEx.Message}");
- }
-
- createdCount++;
-
- // 小延迟确保测试完成
- System.Threading.Thread.Sleep(100);
-
- }
- catch (Exception createEx)
- {
- LogManager.Error($"创建单个测试失败: {createEx.Message}");
- }
- }
-
- // 关键修复:碰撞测试完成后,将物体恢复到动画终点位置
- if (animatedObject != null && IsModelItemValid(animatedObject))
- {
- try
- {
- LogManager.Info("=== 开始恢复物体到动画终点位置 ===");
-
- var modelItems = new ModelItemCollection { animatedObject };
-
- // 计算当前位置到动画终点的偏移
- var currentBounds = animatedObject.BoundingBox();
- var currentPos = new Point3D(
- (currentBounds.Min.X + currentBounds.Max.X) / 2,
- (currentBounds.Min.Y + currentBounds.Max.Y) / 2,
- (currentBounds.Min.Z + currentBounds.Max.Z) / 2
- );
-
- var restoreOffset = new Vector3D(
- animationEndPosition.X - currentPos.X,
- animationEndPosition.Y - currentPos.Y,
- animationEndPosition.Z - currentPos.Z
- );
-
- var restoreTransform = Transform3D.CreateTranslation(restoreOffset);
- doc.Models.OverridePermanentTransform(modelItems, restoreTransform, false);
-
- LogManager.Info($"已将 {animatedObject.DisplayName} 恢复到动画终点位置: ({animationEndPosition.X:F2},{animationEndPosition.Y:F2},{animationEndPosition.Z:F2})");
- LogManager.Info("=== 物体位置恢复完成 ===");
- }
- catch (Exception restoreEx)
- {
- LogManager.Error($"恢复物体到动画终点失败: {restoreEx.Message}");
- }
- }
-
- LogManager.Info($"=== 位置恢复方案完成:成功创建并运行 {createdCount - 1} 个碰撞测试(容差: {detectionGap}米) ===");
-
- if (createdCount > 1)
- {
- // 刷新Clash Detective窗口
- RefreshClashDetectiveUI();
-
- // 自动高亮所有碰撞结果(使用类别管理)
- try
- {
- // 使用过滤后的缓存结果进行高亮
- var validResults = _cachedResults.Where(r =>
- IsModelItemValid(r.Item1) && IsModelItemValid(r.Item2)).ToList();
-
- if (validResults.Count > 0)
- {
- // 使用类别管理的高亮方式
- var darkRed = Color.Red; // 红色(暗红色在Navisworks API中不可用)
- ManageHighlightsByCategory("animation", validResults, darkRed, true);
- LogManager.Info($"自动高亮显示 {validResults.Count} 个动画结束后的碰撞结果(暗红色)");
- }
- }
- catch (Exception highlightEx)
- {
- LogManager.Error($"动画结束后自动高亮失败: {highlightEx.Message}");
- }
-
- // 更新Clash Detective碰撞计数器 - 统计实际创建的测试数量
- var finalClashDetectiveCount = 0;
- if (_documentClash != null)
- {
- // 计算所有动画相关测试的碰撞总数
- var animationTests = _documentClash.TestsData.Tests.Cast()
- .Where(t => t.DisplayName.Contains("动画路径碰撞") || t.DisplayName.Contains("动画碰撞"))
- .ToList();
-
- finalClashDetectiveCount = animationTests.Sum(t => t.Children.Count);
- LogManager.Info($"统计Clash Detective最终碰撞数量: {animationTests.Count}个测试,总共{finalClashDetectiveCount}个碰撞");
- }
-
- _clashDetectiveCollisionCount = finalClashDetectiveCount;
- LogManager.Info($"Clash Detective碰撞计数器已更新: {_clashDetectiveCollisionCount}");
-
- // 清空缓存
- _cachedResults.Clear();
- LogManager.Info("=== 动画碰撞测试(位置恢复方案)完成 ===");
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"动画结束后创建测试失败: {ex.Message}");
- }
- }
-
- /*
- ///
- /// 动画结束后创建最终碰撞结果汇总 - 使用官方示例方法(已注释,改为实时添加)
- ///
- public void CreateFinalCollisionSummary()
- {
- try
- {
- LogManager.Info("=== 开始创建最终碰撞结果汇总(官方方法) ===");
- LogManager.Info($"步骤1: 检查前置条件 - _documentClash是否存在: {_documentClash != null}");
- LogManager.Info($"步骤1: 检查前置条件 - 缓存结果数量: {_cachedResults.Count}");
-
- if (_documentClash == null || _cachedResults.Count == 0)
- {
- 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;
- }
-
- // 使用事务处理,避免只读错误
- using (var transaction = Autodesk.Navisworks.Api.Application.ActiveDocument.BeginTransaction("创建碰撞汇总"))
- {
- try
- {
- var testName = $"物流路径碰撞汇总_{DateTime.Now:yyyyMMdd_HHmmss}";
- LogManager.Info($"步骤4: 创建测试对象 - 测试名称: {testName}");
-
- // 创建新的测试(使用CreateCopy避免只读问题)
- var templateTest = new ClashTest();
- templateTest.DisplayName = testName;
- templateTest.TestType = ClashTestType.Hard;
- templateTest.Tolerance = 0.01;
- templateTest.Guid = Guid.Empty; // 让系统生成新GUID
-
- // 为每个唯一碰撞创建测试
- int resultCount = 0;
-
- foreach (var collision in uniqueCollisions)
- {
- try
- {
- if (!IsModelItemValid(collision.Item1) || !IsModelItemValid(collision.Item2))
- {
- LogManager.Warning("跳过无效的碰撞对象");
- continue;
- }
-
- // 为每个碰撞创建单独的测试(官方推荐方法)
- var collisionTest = (ClashTest)templateTest.CreateCopyWithoutChildren();
- collisionTest.DisplayName = $"碰撞_{resultCount + 1}: {collision.Item1.DisplayName} 与 {collision.Item2.DisplayName}";
-
- // 设置选择集A:第一个对象
- var selectionA = new ModelItemCollection();
- selectionA.Add(collision.Item1);
- collisionTest.SelectionA.Selection.CopyFrom(selectionA);
-
- // 设置选择集B:第二个对象
- var selectionB = new ModelItemCollection();
- selectionB.Add(collision.Item2);
- collisionTest.SelectionB.Selection.CopyFrom(selectionB);
-
- // 使用官方方法添加测试
- _documentClash.TestsData.TestsAddCopy(collisionTest);
- resultCount++;
-
- LogManager.Debug($"已创建碰撞测试 {resultCount}: {collision.Item1.DisplayName} <-> {collision.Item2.DisplayName}");
- }
- catch (Exception ex)
- {
- LogManager.Error($"创建碰撞测试失败: {ex.Message}");
- }
- }
-
- LogManager.Info($"步骤7: 碰撞测试创建完成,共 {resultCount} 个测试");
-
- // 提交事务
- transaction.Commit();
-
- // 运行所有新创建的测试
- RefreshClashDetectiveUI();
-
- // 清空缓存
- _cachedResults.Clear();
- LogManager.Info("=== 最终碰撞结果汇总创建完成 ===");
- }
- catch (Exception)
- {
- // Navisworks 2017没有RollBack方法,不提交事务即可回滚
- throw;
- }
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"创建最终碰撞汇总失败 - {ex.GetType().Name}: {ex.Message}");
- LogManager.Error($"创建最终碰撞汇总失败 - 堆栈跟踪: {ex.StackTrace}");
- }
- }
- */
-
-
-
-
- ///
- /// 刷新Clash Detective UI - 安全版本
- ///
- private void RefreshClashDetectiveUI()
- {
- try
- {
- // 检查文档是否有效
- var doc = Application.ActiveDocument;
- if (doc == null || doc.IsClear)
- {
- LogManager.Warning("文档无效,跳过UI刷新");
- return;
- }
-
- // 检查视图是否有效
- if (doc.ActiveView != null)
- {
- try
- {
- doc.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All);
- LogManager.Info("Clash Detective UI已刷新");
- }
- catch (ObjectDisposedException)
- {
- LogManager.Warning("视图对象已释放,跳过重绘");
- }
- catch (Exception ex)
- {
- LogManager.Error($"重绘视图失败: {ex.Message}");
- }
- }
- else
- {
- LogManager.Warning("活动视图无效,跳过UI刷新");
- }
- }
- catch (ObjectDisposedException)
- {
- LogManager.Warning("文档或视图对象已释放,跳过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}");
- }
- }
-
-
- ///
- /// 更新主测试记录(避免频繁创建新测试)
- ///
- private void UpdateMainClashTest(List results)
- {
- try
- {
- if (_documentClash == null || results.Count == 0)
- return;
-
- // 限制创建测试的频率 - 只在有实际碰撞且距离最近的测试超过一定时间时创建
- if (ShouldCreateNewTest())
- {
- var firstCollision = results[0];
- if (firstCollision.Item1 != null && firstCollision.Item2 != null)
- {
- // 检查对象是否仍然有效
- if (IsModelItemValid(firstCollision.Item1) && IsModelItemValid(firstCollision.Item2))
- {
- // 创建一个快照测试来展示实际碰撞
- var timestamp = DateTime.Now.ToString("HH:mm:ss");
- var testName = $"动画碰撞快照_{timestamp}";
-
- var snapshotTest = new ClashTest();
- snapshotTest.DisplayName = testName;
- snapshotTest.TestType = ClashTestType.Hard;
- snapshotTest.Tolerance = 0.01;
-
- 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);
-
- // 重新获取并运行测试,使用异步方式避免阻塞UI
- var addedTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == testName);
- if (addedTest is ClashTest runTest)
- {
- // 使用后台任务运行测试,避免阻塞UI
- System.Threading.Tasks.Task.Run(() =>
- {
- try
- {
- _documentClash.TestsData.TestsRunTest(runTest);
- LogManager.Info($"已创建并运行碰撞快照: {testName},展示 {firstCollision.Item1.DisplayName} <-> {firstCollision.Item2.DisplayName},结果数量: {runTest.Children.Count}");
- }
- catch (Exception ex)
- {
- LogManager.Error($"异步运行测试失败: {ex.Message}");
- }
- });
- }
-
- // 更新最后创建测试的时间
- _lastTestCreationTime = DateTime.Now;
- }
- else
- {
- LogManager.Warning("碰撞对象已被释放,跳过测试创建");
- }
- }
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"更新主测试记录失败: {ex.Message}");
- }
- }
-
- ///
- /// 检查ModelItem是否仍然有效
- ///
- private bool IsModelItemValid(ModelItem item)
- {
- try
- {
- if (item == null)
- return false;
-
- // 尝试访问对象的属性来检查是否有效
- var displayName = item.DisplayName;
- var hasGeometry = item.HasGeometry;
-
- // 额外检查:确保对象没有被释放
- var boundingBox = item.BoundingBox();
- return true;
- }
- catch (Exception ex)
- {
- LogManager.Debug($"ModelItem无效: {ex.Message}");
- return false;
- }
- }
-
- ///
- /// 增强的自碰撞检测逻辑 - 多重验证确保不会检测对象与自身的碰撞
- ///
- /// 动画对象
- /// 检测对象
- /// 如果是同一对象返回true
- private bool IsSameObjectAdvanced(ModelItem obj1, ModelItem obj2)
- {
- try
- {
- // 1. 首要检查:对象引用相等
- if (ReferenceEquals(obj1, obj2))
- {
- LogManager.Debug($"[自碰撞检测] 引用相等检测: 同一对象 {obj1.DisplayName}");
- return true;
- }
-
- // 2. Equals方法检查(Navisworks标准检查)
- if (obj1.Equals(obj2))
- {
- LogManager.Debug($"[自碰撞检测] Equals方法检测: 同一对象 {obj1.DisplayName}");
- return true;
- }
-
- // 3. InstanceGuid检查 - 但要考虑无效GUID的情况
- var guid1 = obj1.InstanceGuid;
- var guid2 = obj2.InstanceGuid;
- var isValidGuid1 = guid1 != Guid.Empty;
- var isValidGuid2 = guid2 != Guid.Empty;
-
- // 只有当两个GUID都有效且相同时,才认为是同一对象
- if (isValidGuid1 && isValidGuid2 && guid1 == guid2)
- {
- LogManager.Debug($"[自碰撞检测] 有效GUID检测: 同一对象 {obj1.DisplayName} (GUID: {guid1})");
- return true;
- }
-
- // 4. 如果GUID无效,使用DisplayName + 包围盒位置组合检查
- if (!isValidGuid1 || !isValidGuid2)
- {
- var name1 = obj1.DisplayName ?? "";
- var name2 = obj2.DisplayName ?? "";
-
- // 名称相同且都有几何体,进一步检查位置
- if (name1 == name2 && !string.IsNullOrEmpty(name1) && obj1.HasGeometry && obj2.HasGeometry)
- {
- try
- {
- var bbox1 = obj1.BoundingBox();
- var bbox2 = obj2.BoundingBox();
-
- // 检查包围盒是否完全相同(精确到小数点后3位)
- var centerDistance = BoundingBoxGeometryUtils.CalculatePointDistance(
- new Point3D((bbox1.Min.X + bbox1.Max.X) / 2, (bbox1.Min.Y + bbox1.Max.Y) / 2, (bbox1.Min.Z + bbox1.Max.Z) / 2),
- new Point3D((bbox2.Min.X + bbox2.Max.X) / 2, (bbox2.Min.Y + bbox2.Max.Y) / 2, (bbox2.Min.Z + bbox2.Max.Z) / 2)
- );
-
- // 如果中心点距离非常小(小于1mm),认为是同一对象
- if (centerDistance < 0.001) // 1mm以内认为是同一位置
- {
- LogManager.Debug($"[自碰撞检测] 位置+名称检测: 同一对象 {name1} (中心距离: {centerDistance:F6})");
- return true;
- }
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[自碰撞检测] 包围盒比较失败: {ex.Message}");
- }
- }
- }
-
- // 5. 最后兜底:检查空DisplayName的情况
- var displayName1 = obj1.DisplayName ?? "";
- var displayName2 = obj2.DisplayName ?? "";
-
- // 如果一个对象有名称,另一个没有,但GUID相同且无效,可能是同一对象的不同表示
- if ((string.IsNullOrEmpty(displayName1) && !string.IsNullOrEmpty(displayName2)) ||
- (!string.IsNullOrEmpty(displayName1) && string.IsNullOrEmpty(displayName2)))
- {
- if (guid1 == guid2 && guid1 == Guid.Empty)
- {
- LogManager.Debug($"[自碰撞检测] 空名称+无效GUID检测: 疑似同一对象 '{displayName1}' vs '{displayName2}'");
- return true;
- }
- }
-
- return false;
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[自碰撞检测] 检测过程出错: {ex.Message}");
- return false; // 出错时保守处理,不跳过
- }
- }
-
- ///
- /// 计算两个3D点之间的距离
- ///
- // 此方法已迁移到 BoundingBoxGeometryUtils.CalculatePointDistance
- [System.Obsolete("请使用 BoundingBoxGeometryUtils.CalculatePointDistance 替代", true)]
- private double CalculatePointDistance(Point3D point1, Point3D point2)
- {
- throw new NotImplementedException("方法已迁移到工具类");
- }
-
- #region 智能排除策略逻辑
-
- // 注意:智能排除策略相关代码已移至LogisticsAnimationManager
- // ClashDetectiveIntegration现在专注于纯碰撞检测功能
-
- #endregion
-
- private DateTime _lastTestCreationTime = DateTime.MinValue;
-
- ///
- /// 判断是否应该创建新测试(限制创建频率)
- ///
- private bool ShouldCreateNewTest()
- {
- // 每5秒最多创建一个测试
- return DateTime.Now - _lastTestCreationTime > TimeSpan.FromSeconds(5);
- }
-
-
- ///
- /// 确保动态测试存在
- ///
- private void EnsureDynamicTestExists()
- {
- try
- {
- if (_dynamicClashTest == null)
- {
- // 查找现有的动态测试
- var existingTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == "动态运输路径碰撞检测");
- if (existingTest is ClashTest clashTest)
- {
- _dynamicClashTest = clashTest;
- LogManager.Debug("找到现有动态测试");
- }
- else
- {
- LogManager.Info("动态测试不存在,创建新测试");
- SetupDynamicClashTest();
- }
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"确保动态测试存在失败: {ex.Message}");
- }
- }
-
- ///
- /// 安全地更新选择集
- ///
- private bool SafeUpdateSelections(ModelItem animatedObject, ModelItemCollection excludeObjects, bool isRetry = false)
- {
- try
- {
- // 创建新的选择集
- var selectionA = new ModelItemCollection();
- selectionA.Add(animatedObject);
-
- var allItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf
- .Where(item => item.HasGeometry && !item.Equals(animatedObject));
-
- if (excludeObjects != null)
- {
- allItems = allItems.Where(item => !excludeObjects.Contains(item));
- }
-
- var selectionB = new ModelItemCollection();
- selectionB.AddRange(allItems);
-
- // 尝试更新选择集
- _dynamicClashTest.SelectionA.Selection.Clear();
- _dynamicClashTest.SelectionA.Selection.CopyFrom(selectionA);
-
- _dynamicClashTest.SelectionB.Selection.Clear();
- _dynamicClashTest.SelectionB.Selection.CopyFrom(selectionB);
-
- LogManager.Debug($"选择集更新成功: A={selectionA.Count}, B={selectionB.Count}");
- return true;
- }
- catch (Exception ex)
- {
- LogManager.Error($"更新选择集失败: {ex.Message}");
-
- // 只在第一次失败时尝试重新创建测试,避免无限递归
- if (!isRetry && ex.Message.Contains("Read-Only"))
- {
- try
- {
- LogManager.Info("尝试重新创建动态测试(仅重试一次)");
- SetupDynamicClashTest();
- return SafeUpdateSelections(animatedObject, excludeObjects, true); // 重试一次,标记为重试
- }
- catch (Exception retryEx)
- {
- LogManager.Error($"重新创建测试也失败: {retryEx.Message}");
- return false;
- }
- }
- else
- {
- LogManager.Error($"选择集更新失败,{(isRetry ? "重试后仍然失败" : "跳过重试")}");
- return false;
- }
- }
- }
-
- ///
- /// 创建临时碰撞测试
- ///
- /// 动画对象
- /// 排除对象
- /// 临时测试对象
- private ClashTest CreateTemporaryClashTest(ModelItem animatedObject, ModelItemCollection excludeObjects)
- {
- try
- {
- // 创建临时测试
- var tempTest = new ClashTest();
- tempTest.DisplayName = $"临时碰撞检测_{DateTime.Now:HHmmss}";
- tempTest.TestType = ClashTestType.Hard;
- tempTest.Tolerance = 0.01; // 1cm容差
-
- // 设置选择集A:动画对象
- var selectionA = new ModelItemCollection();
- selectionA.Add(animatedObject);
- tempTest.SelectionA.Selection.CopyFrom(selectionA);
-
- // 设置选择集B:所有其他对象(排除指定对象)
- var allItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf
- .Where(item => item.HasGeometry && !item.Equals(animatedObject));
-
- if (excludeObjects != null)
- {
- allItems = allItems.Where(item => !excludeObjects.Contains(item));
- }
-
- var selectionB = new ModelItemCollection();
- selectionB.AddRange(allItems);
- tempTest.SelectionB.Selection.CopyFrom(selectionB);
-
- // 添加到文档
- _documentClash.TestsData.TestsAddCopy(tempTest);
-
- // 重新获取添加后的测试对象
- var addedTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == tempTest.DisplayName);
- if (addedTest is ClashTest clashTest)
- {
- LogManager.Debug($"创建临时测试成功: {clashTest.DisplayName}, 选择集A={selectionA.Count}, B={selectionB.Count}");
- return clashTest;
- }
- else
- {
- LogManager.Error("无法获取添加后的临时测试对象");
- return null;
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"创建临时碰撞测试失败: {ex.Message}");
- return null;
- }
- }
-
- ///
- /// 确保测试仍然存在并且有效
- ///
- private void EnsureTestExists()
- {
- try
- {
- // 检查测试是否仍然在文档中
- var existingTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == "动态运输路径碰撞检测");
-
- if (existingTest == null)
- {
- LogManager.Info("动态测试不存在,重新创建...");
- SetupDynamicClashTest();
- }
- else if (existingTest is ClashTest clashTest)
- {
- // 检查测试是否可写
- try
- {
- // 尝试访问选择集来测试是否可写
- var testSelection = clashTest.SelectionA.Selection;
- _dynamicClashTest = clashTest;
- LogManager.Debug("动态测试已确认存在并可写");
- }
- catch (Exception readOnlyEx)
- {
- if (readOnlyEx.Message.Contains("Read-Only"))
- {
- LogManager.Warning("现有测试为只读,重新创建新测试");
- SetupDynamicClashTest();
- }
- else
- {
- throw;
- }
- }
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"确保测试存在失败: {ex.Message}");
- // 重新创建测试
- SetupDynamicClashTest();
- }
- }
-
- ///
- /// 安全地运行碰撞检测
- ///
- private void RunCollisionDetection()
- {
- try
- {
- // 验证测试是否可以运行
- if (_dynamicClashTest == null)
- {
- LogManager.Error("动态测试为空,无法运行");
- return;
- }
-
- // 检查测试是否在文档的测试列表中
- var testExists = _documentClash.TestsData.Tests.Any(t => t.DisplayName == _dynamicClashTest.DisplayName);
- if (!testExists)
- {
- LogManager.Info("测试不在文档中,重新添加...");
- _documentClash.TestsData.TestsAddCopy(_dynamicClashTest);
- }
-
- // 运行碰撞检测
- _documentClash.TestsData.TestsRunTest(_dynamicClashTest);
- LogManager.Debug($"碰撞检测运行完成,结果数量: {_dynamicClashTest.Children.Count}");
- }
- catch (Exception ex)
- {
- LogManager.Error($"运行碰撞检测失败: {ex.Message}");
- // 如果运行失败,尝试重新创建测试
- LogManager.Info("尝试重新创建测试...");
- SetupDynamicClashTest();
-
- // 再次尝试运行
- try
- {
- _documentClash.TestsData.TestsRunTest(_dynamicClashTest);
- LogManager.Info("重新创建测试后运行成功");
- }
- catch (Exception retryEx)
- {
- LogManager.Error($"重试运行测试也失败: {retryEx.Message}");
- throw; // 重新抛出异常,让上层处理
- }
- }
- }
-
- ///
- /// 更新碰撞测试的选择集
- ///
- private void UpdateClashTestSelections(ModelItem animatedObject,
- ModelItemCollection excludeObjects)
- {
- try
- {
- // 选择集A:动画对象
- var selectionA = new ModelItemCollection();
- selectionA.Add(animatedObject);
- _dynamicClashTest.SelectionA.Selection.CopyFrom(selectionA);
-
- // 选择集B:所有其他对象(排除指定对象)
- var allItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf
- .Where(item => item.HasGeometry && !item.Equals(animatedObject));
-
- if (excludeObjects != null)
- {
- allItems = allItems.Where(item => !excludeObjects.Contains(item));
- }
-
- var selectionB = new ModelItemCollection();
- selectionB.AddRange(allItems);
- _dynamicClashTest.SelectionB.Selection.CopyFrom(selectionB);
-
- LogManager.Debug($"更新碰撞测试选择集: A={selectionA.Count}, B={selectionB.Count}");
- }
- catch (Exception ex)
- {
- LogManager.Error($"更新碰撞测试选择集失败: {ex.Message}");
- }
- }
-
- ///
- /// 处理碰撞检测结果
- ///
- private List ProcessClashResults(ClashTest clashTest)
- {
- var results = new List();
-
- try
- {
- foreach (var child in clashTest.Children)
- {
- if (child is ClashResult clashResult)
- {
- var result = new CollisionResult
- {
- ClashGuid = clashResult.Guid,
- DisplayName = clashResult.DisplayName,
- Status = clashResult.Status,
- GridLocation = clashResult.DisplayName, // 使用DisplayName代替GridLocation
- Item1 = clashResult.Item1,
- Item2 = clashResult.Item2,
- Center = clashResult.Center,
- Distance = clashResult.Distance,
- CreatedTime = DateTime.Now
- };
-
- results.Add(result);
- }
- else if (child is ClashResultGroup resultGroup)
- {
- // 处理分组结果
- foreach (var groupChild in resultGroup.Children)
- {
- if (groupChild is ClashResult groupResult)
- {
- var result = new CollisionResult
- {
- ClashGuid = groupResult.Guid,
- DisplayName = groupResult.DisplayName,
- Status = groupResult.Status,
- GridLocation = groupResult.DisplayName, // 使用DisplayName代替GridLocation
- Item1 = groupResult.Item1,
- Item2 = groupResult.Item2,
- Center = groupResult.Center,
- Distance = groupResult.Distance,
- CreatedTime = DateTime.Now
- };
-
- results.Add(result);
- }
- }
- }
- }
-
- LogManager.Debug($"处理碰撞结果: {results.Count} 个碰撞");
- }
- catch (Exception ex)
- {
- LogManager.Error($"处理碰撞结果失败: {ex.Message}");
- }
-
- return results;
- }
-
- ///
- /// 同步结果到Clash Detective窗口
- ///
- private void SyncToClashDetectiveWindow()
- {
- try
- {
- LogManager.Info("开始同步结果到Clash Detective窗口...");
-
- // 强制刷新Clash Detective窗口显示
- RefreshClashDetectiveWindow();
-
- // 确保测试结果在界面中可见
- EnsureTestVisibility();
-
- LogManager.Info("同步到Clash Detective窗口完成");
- }
- catch (Exception ex)
- {
- LogManager.Error($"同步到Clash Detective窗口失败: {ex.Message}");
- }
- }
-
- ///
- /// 刷新Clash Detective窗口(修复版 - 避免UI阻塞)
- ///
- private void RefreshClashDetectiveWindow()
- {
- try
- {
- LogManager.Info("开始刷新Clash Detective窗口...");
-
- // 检查文档是否有效
- if (Application.ActiveDocument == null)
- {
- LogManager.Warning("文档已失效,跳过刷新");
- return;
- }
-
- // 使用后台任务执行,避免UI阻塞
- System.Threading.Tasks.Task.Run(() =>
- {
- try
- {
- // 方法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.Error($"后台刷新失败: {ex.Message}");
- }
- });
-
- }
- catch (Exception ex)
- {
- LogManager.Error($"刷新Clash Detective窗口启动失败: {ex.Message}");
- }
- }
-
- ///
- /// 确保测试在界面中可见
- ///
- private void EnsureTestVisibility()
- {
- try
- {
- // 检查测试是否已添加到文档
- var existingTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == "动态运输路径碰撞检测");
-
- if (existingTest == null)
- {
- LogManager.Warning("动态碰撞检测测试未找到,重新创建...");
- SetupDynamicClashTest();
- }
- else if (existingTest is ClashTest clashTest)
- {
- LogManager.Info($"找到测试: {clashTest.DisplayName}, 结果数量: {clashTest.Children.Count}");
-
- // 确保测试有结果
- if (clashTest.Children.Count > 0)
- {
- LogManager.Info("测试已有结果,应该在Clash Detective窗口中可见");
- }
- else
- {
- LogManager.Info("测试无结果,可能需要重新运行");
- }
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"确保测试可见性失败: {ex.Message}");
- }
- }
-
- ///
- /// 高亮显示碰撞对象
- ///
- /// 碰撞结果
- /// 是否清除之前的高亮
- public void HighlightCollisions(List results, bool clearPrevious = true)
- {
- HighlightCollisions(results, Color.Red, clearPrevious);
- }
-
- ///
- /// 高亮显示碰撞对象(支持自定义颜色)
- ///
- /// 碰撞结果
- /// 高亮颜色
- /// 是否清除之前的高亮
- public void HighlightCollisions(List results, Color highlightColor, bool clearPrevious = true)
- {
- try
- {
- var doc = Application.ActiveDocument;
-
- // 根据参数决定是否清除之前的高亮
- if (clearPrevious)
- {
- doc.Models.ResetAllTemporaryMaterials();
- }
-
- if (results != null && results.Count > 0)
- {
- var collidingItems = new ModelItemCollection();
-
- foreach (var result in results)
- {
- if (result.Item1 != null && !collidingItems.Contains(result.Item1))
- collidingItems.Add(result.Item1);
- if (result.Item2 != null && !collidingItems.Contains(result.Item2))
- collidingItems.Add(result.Item2);
- }
-
- // 高亮碰撞对象(使用指定颜色)
- doc.Models.OverrideTemporaryColor(collidingItems, highlightColor);
-
- LogManager.Info($"高亮显示 {collidingItems.Count} 个碰撞对象,颜色: {highlightColor}");
- }
- else
- {
- LogManager.Debug("没有碰撞结果需要高亮");
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"高亮显示碰撞对象失败: {ex.Message}");
- }
- }
-
- ///
- /// 清除所有高亮
- ///
- public void ClearHighlights()
- {
- try
- {
- lock (_highlightLock)
- {
- var doc = Application.ActiveDocument;
- doc.Models.ResetAllTemporaryMaterials();
- _activeHighlights.Clear();
- LogManager.Info("已清除所有碰撞高亮");
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"清除高亮失败: {ex.Message}");
- }
- }
-
- ///
- /// 按类别管理高亮显示
- ///
- /// 高亮类别(如"animation", "independent", "report"等)
- /// 碰撞结果
- /// 高亮颜色
- /// 是否清除其他类别的高亮
- public void ManageHighlightsByCategory(string category, List results,
- Color highlightColor, bool clearOtherCategories = false)
- {
- try
- {
- lock (_highlightLock)
- {
- var doc = Application.ActiveDocument;
-
- // 清除其他类别的高亮(如果需要)
- if (clearOtherCategories)
- {
- doc.Models.ResetAllTemporaryMaterials();
- _activeHighlights.Clear();
- }
- else if (_activeHighlights.ContainsKey(category))
- {
- // 只清除当前类别的高亮
- var existingItems = _activeHighlights[category];
- // Navisworks不支持按对象清除高亮,需要全部重置后重新应用其他类别
- doc.Models.ResetAllTemporaryMaterials();
- _activeHighlights.Remove(category);
-
- // 重新应用其他类别的高亮
- foreach (var kvp in _activeHighlights)
- {
- // 这里需要根据类别使用不同颜色重新高亮
- var categoryColor = GetCategoryColor(kvp.Key);
- doc.Models.OverrideTemporaryColor(kvp.Value, categoryColor);
- }
- }
-
- if (results != null && results.Count > 0)
- {
- var collidingItems = new ModelItemCollection();
-
- foreach (var result in results)
- {
- if (result.Item1 != null && !collidingItems.Contains(result.Item1))
- collidingItems.Add(result.Item1);
- if (result.Item2 != null && !collidingItems.Contains(result.Item2))
- collidingItems.Add(result.Item2);
- }
-
- // 应用高亮
- doc.Models.OverrideTemporaryColor(collidingItems, highlightColor);
-
- // 记录活跃高亮
- _activeHighlights[category] = collidingItems;
-
- LogManager.Info($"类别 '{category}' 高亮显示 {collidingItems.Count} 个碰撞对象,颜色: {highlightColor}");
- }
- else
- {
- LogManager.Debug($"类别 '{category}' 没有碰撞结果需要高亮");
- }
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"按类别管理高亮失败 [{category}]: {ex.Message}");
- }
- }
-
- ///
- /// 根据类别获取默认颜色
- ///
- private Color GetCategoryColor(string category)
- {
- switch (category.ToLower())
- {
- case "animation":
- case "runtime":
- return Color.Red; // 动画过程:红色
- case "independent":
- case "static":
- return Color.Red; // 独立检测:红色 (Navisworks API中没有DarkRed)
- case "report":
- return Color.Green; // 报告查看:绿色 (Navisworks API中没有Orange)
- case "preview":
- return Color.Blue; // 预览:蓝色 (Navisworks API中没有Yellow)
- default:
- return Color.White; // 未知类别:白色 (Navisworks API中没有Magenta)
- }
- }
-
- ///
- /// 获取当前活跃高亮信息
- ///
- public string GetActiveHighlightInfo()
- {
- try
- {
- lock (_highlightLock)
- {
- if (_activeHighlights.Count == 0)
- {
- return "当前无活跃高亮";
- }
-
- var info = new StringBuilder();
- info.AppendLine("=== 当前活跃高亮 ===");
-
- foreach (var kvp in _activeHighlights)
- {
- var category = kvp.Key;
- var items = kvp.Value;
- var color = GetCategoryColor(category);
-
- info.AppendLine($"类别: {category}");
- info.AppendLine($" 对象数量: {items.Count}");
- info.AppendLine($" 颜色: {color}");
- info.AppendLine();
- }
-
- return info.ToString();
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"获取高亮信息失败: {ex.Message}");
- return "获取高亮信息失败";
- }
- }
-
- ///
- /// 基于包围盒的快速碰撞检测(不用Clash Detective)
- ///
- private List DetectCollisionsSimple(ModelItem animatedObject,
- ModelItemCollection excludeObjects, double detectionGap)
- {
- var results = new List();
-
- try
- {
- LogManager.Debug($"开始快速碰撞检测,动画对象: {animatedObject.DisplayName}");
-
- var animatedBoundingBox = animatedObject.BoundingBox();
- LogManager.Debug($"动画对象包围盒: Min({animatedBoundingBox.Min.X:F2}, {animatedBoundingBox.Min.Y:F2}, {animatedBoundingBox.Min.Z:F2}) Max({animatedBoundingBox.Max.X:F2}, {animatedBoundingBox.Max.Y:F2}, {animatedBoundingBox.Max.Z:F2})");
-
- // 性能分析计时器
- var exclusionStopwatch = new System.Diagnostics.Stopwatch();
- var getAllItemsStopwatch = new System.Diagnostics.Stopwatch();
- var excludeLogicStopwatch = new System.Diagnostics.Stopwatch();
- var boundingBoxStopwatch = new System.Diagnostics.Stopwatch();
- var intersectionStopwatch = new System.Diagnostics.Stopwatch();
- var distanceStopwatch = new System.Diagnostics.Stopwatch();
-
- // 计时:构建排除列表
- exclusionStopwatch.Start();
- var exclusionList = new List();
-
- // 1. 排除动画对象本身及其所有子对象
- exclusionList.AddRange(animatedObject.DescendantsAndSelf.Where(d => d.HasGeometry));
-
- // 2. 合并用户指定的排除对象
- if (excludeObjects != null)
- {
- foreach (var item in excludeObjects)
- {
- if (!exclusionList.Contains(item))
- {
- exclusionList.Add(item);
- }
- }
- }
- exclusionStopwatch.Stop();
- LogManager.Debug($"构建排除列表完成,耗时: {exclusionStopwatch.ElapsedMilliseconds}ms,排除对象: {exclusionList.Count} 个");
-
- // 🔥 使用预构建的缓存获取对象列表
- getAllItemsStopwatch.Start();
- List itemList;
-
- lock (_cacheLock)
- {
- if (_allGeometryItemsCache != null)
- {
- // 从缓存中过滤掉动画对象本身
- itemList = _allGeometryItemsCache.Where(item => !item.Equals(animatedObject)).ToList();
- LogManager.Debug($"使用缓存获取对象列表,缓存对象总数: {_allGeometryItemsCache.Count},过滤后: {itemList.Count}");
- }
- else
- {
- // 缓存不存在,回退到实时获取(不应该发生,但保险起见)
- LogManager.Warning("几何对象缓存不存在,回退到实时获取(这可能影响性能)");
- var allItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf
- .Where(item => item.HasGeometry && !item.Equals(animatedObject));
- itemList = allItems.ToList();
- }
- }
-
- getAllItemsStopwatch.Stop();
- LogManager.Debug($"获取对象列表完成,耗时: {getAllItemsStopwatch.ElapsedMilliseconds}ms,对象总数: {itemList.Count}");
-
- int checkedCount = 0;
- int excludedCount = 0;
- int boundingBoxCallCount = 0;
- int intersectionCallCount = 0;
- int distanceCallCount = 0;
-
- foreach (var item in itemList)
- {
- try
- {
- // 计时:排除逻辑检查(现在使用缓存,应该很快)
- excludeLogicStopwatch.Start();
- bool shouldExclude = ShouldExcludeFromCollisionDetectionSimple(item, exclusionList);
- excludeLogicStopwatch.Stop();
-
- if (shouldExclude)
- {
- excludedCount++;
- continue;
- }
-
- // 计时:包围盒获取
- boundingBoxStopwatch.Start();
- var itemBoundingBox = item.BoundingBox();
- boundingBoxStopwatch.Stop();
- boundingBoxCallCount++;
-
- // 使用UI设置的检测间隙作为碰撞容差
- var tolerance = detectionGap;
-
- // 计时:相交检测
- intersectionStopwatch.Start();
- bool intersects = BoundingBoxGeometryUtils.BoundingBoxesIntersectWithTolerance(animatedBoundingBox, itemBoundingBox, tolerance);
- intersectionStopwatch.Stop();
- intersectionCallCount++;
-
- // 检查是否在容差范围内接近
- if (intersects)
- {
- // 计时:距离计算
- distanceStopwatch.Start();
- var distance = BoundingBoxGeometryUtils.CalculateDistance(animatedBoundingBox, itemBoundingBox);
- distanceStopwatch.Stop();
- distanceCallCount++;
-
- // 只有真正相交(距离为0)或在检测间隙内的才算碰撞
- if (distance <= tolerance)
- {
- // 🔧 智能容器映射:使用有名称的容器对象而非几何体子对象
- var mappedAnimatedObject = GetCollisionObjectWithValidIdentity(animatedObject);
- var mappedCollisionObject = GetCollisionObjectWithValidIdentity(item);
-
- // 检查是否进行了容器映射
- bool hasMapping1 = !mappedAnimatedObject.Equals(animatedObject);
- bool hasMapping2 = !mappedCollisionObject.Equals(item);
-
- LogManager.Info($"[容器映射] 原始对象: {animatedObject.DisplayName} -> 容器对象: {mappedAnimatedObject.DisplayName} (映射: {hasMapping1})");
- LogManager.Info($"[容器映射] 原始对象: {item.DisplayName} -> 容器对象: {mappedCollisionObject.DisplayName} (映射: {hasMapping2})");
-
- var result = new CollisionResult
- {
- ClashGuid = Guid.NewGuid(),
- DisplayName = $"纯碰撞检测: {mappedAnimatedObject.DisplayName} <-> {mappedCollisionObject.DisplayName}",
- Status = ClashResultStatus.New,
- Item1 = mappedAnimatedObject, // 使用映射后的容器对象
- Item2 = mappedCollisionObject, // 使用映射后的容器对象
- OriginalItem1 = animatedObject, // 保存原始几何体对象
- OriginalItem2 = item, // 保存原始几何体对象
- HasContainerMapping = hasMapping1 || hasMapping2, // 标记是否进行了映射
- CreatedTime = DateTime.Now,
- Distance = distance,
- Center = BoundingBoxGeometryUtils.CalculateCenter(animatedBoundingBox, itemBoundingBox)
- };
-
- results.Add(result);
- LogManager.Info($"检测到碰撞: {mappedAnimatedObject.DisplayName} <-> {mappedCollisionObject.DisplayName},距离: {result.Distance:F2}");
- }
- }
-
- checkedCount++;
- }
- catch (Exception itemEx)
- {
- LogManager.Warning($"检查单个对象碰撞时出错 {item.DisplayName}: {itemEx.Message}");
- }
- }
-
- if (results.Count == 0)
- {
- LogManager.Info($"未发现碰撞");
- }
-
- // 更新动画碰撞计数器
- _animationCollisionCount = results.Count;
- LogManager.Info($"动画碰撞计数器已更新: {_animationCollisionCount}");
- }
- catch (Exception ex)
- {
- LogManager.Error($"纯碰撞检测失败: {ex.Message}", ex);
- }
-
- return results;
- }
-
- ///
- /// 简化的排除判断逻辑(不依赖LogisticsAnimationManager)
- ///
- /// 要测试的对象
- /// 排除列表
- /// 如果应该排除返回true
- private bool ShouldExcludeFromCollisionDetectionSimple(ModelItem testObject, List exclusionList)
- {
- try
- {
- // 1. 基本排除列表检查
- if (exclusionList != null && exclusionList.Contains(testObject))
- {
- return true;
- }
-
- // 2. 通道对象检查(保留原有逻辑)
- if (IsChannelObject(testObject))
- {
- return true;
- }
-
- return false;
- }
- catch (Exception ex)
- {
- LogManager.Warning($"简化排除判断异常: {ex.Message}");
- return false; // 出错时保守处理,不排除
- }
- }
-
- ///
- /// 检查物体是否为通道物体(通道物体在碰撞检测时不应该变红)
- ///
- private bool IsChannelObject(ModelItem item)
- {
- try
- {
- // 使用缓存的通道对象集合
- if (_channelObjectsCache == null)
- {
- BuildChannelObjectsCache();
- }
-
- // 直接查询缓存,避免递归和重复属性查询
- return _channelObjectsCache.Contains(item);
- }
- catch (Exception ex)
- {
- LogManager.Warning($"检查通道物体时出错 {item.DisplayName}: {ex.Message}");
- return false;
- }
- }
-
- ///
- /// 构建通道对象缓存,一次性扫描所有对象,避免重复的属性查询
- ///
- ///
- /// 构建通道对象缓存,一次性扫描所有对象,避免重复的属性查询
- ///
- public void BuildChannelObjectsCache()
- {
- lock (_cacheLock)
- {
- if (_channelObjectsCache != null) return; // 双重检查锁定
-
- var cacheStopwatch = new System.Diagnostics.Stopwatch();
- cacheStopwatch.Start();
-
- _channelObjectsCache = new HashSet();
-
- try
- {
- var document = Application.ActiveDocument;
- LogManager.Debug("[通道缓存] 开始使用SearchAPI构建通道对象缓存");
-
- // 🔥 一行调用:直接获取所有可通行的物流模型项(使用优化后的SearchAPI)
- var allChannelItems = CategoryAttributeManager.GetAllTraversableLogisticsItems(document);
- LogManager.Info($"[通道缓存] 使用SearchAPI直接获取到 {allChannelItems.Count} 个可通行物流元素");
-
- if (allChannelItems.Count == 0)
- {
- LogManager.Warning("[通道缓存] ⚠️ 未找到任何可通行的物流元素,请检查模型中的物流属性设置");
- cacheStopwatch.Stop();
- return;
- }
-
- // 智能收集通道相关节点(包括父节点、同级节点、子节点等)
- foreach (var channelItem in allChannelItems)
- {
- try
- {
- LogManager.Debug($"[通道收集] 开始处理通道节点: '{channelItem.DisplayName}'");
-
- // 🔥 直接使用 ModelItemAnalysisHelper 的方法收集相关节点
- var itemRelatedNodes = ModelItemAnalysisHelper.CollectRelatedNodes(channelItem);
-
- // 将结果添加到缓存中
- foreach (var node in itemRelatedNodes)
- {
- _channelObjectsCache.Add(node);
- }
-
- LogManager.Debug($"[通道收集] 从 '{channelItem.DisplayName}' 收集到 {itemRelatedNodes.Count} 个相关节点");
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[通道收集] 处理通道节点 '{channelItem?.DisplayName ?? "NULL"}' 时出错: {ex.Message}");
- // 出错时至少保证通道本身被添加
- _channelObjectsCache.Add(channelItem);
- }
- }
-
- cacheStopwatch.Stop();
- LogManager.Info($"通道对象缓存构建完成,耗时: {cacheStopwatch.ElapsedMilliseconds}ms");
- LogManager.Info($" - 可通行物流根对象: {allChannelItems.Count} 个");
- LogManager.Info($" - 缓存总对象数: {_channelObjectsCache.Count} 个");
-
- // 列出找到的通道根对象
- if (allChannelItems.Count > 0)
- {
- LogManager.Info("[通道缓存] 找到的可通行物流对象列表:");
- for (int i = 0; i < Math.Min(allChannelItems.Count, 10); i++) // 最多显示前10个
- {
- var item = allChannelItems[i];
- LogManager.Info($" {i + 1}. {item.DisplayName} (HasGeometry: {item.HasGeometry})");
- }
- if (allChannelItems.Count > 10)
- {
- LogManager.Info($" ... 还有 {allChannelItems.Count - 10} 个对象(省略显示)");
- }
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"构建通道对象缓存时发生错误: {ex.Message}", ex);
- _channelObjectsCache = new HashSet(); // 创建空缓存,避免重复构建
- }
- }
- }
-
- ///
- /// 清除通道对象缓存,在模型变化时调用
- ///
- public static void ClearChannelObjectsCache()
- {
- lock (_cacheLock)
- {
- _channelObjectsCache = null;
- LogManager.Debug("通道对象缓存已清除");
- }
- }
-
-
- ///
- /// 构建几何对象列表缓存,一次性获取所有几何对象
- ///
- ///
- /// 构建几何对象列表缓存,一次性获取所有几何对象
- ///
- public static void BuildAllGeometryItemsCache()
- {
- lock (_cacheLock)
- {
- if (_allGeometryItemsCache != null) return; // 双重检查锁定
-
- var cacheStopwatch = new System.Diagnostics.Stopwatch();
- cacheStopwatch.Start();
-
- try
- {
- var allItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf
- .Where(item => item.HasGeometry);
-
- _allGeometryItemsCache = allItems.ToList();
-
- cacheStopwatch.Stop();
- LogManager.Info($"几何对象列表缓存构建完成,耗时: {cacheStopwatch.ElapsedMilliseconds}ms");
- LogManager.Info($" - 缓存对象总数: {_allGeometryItemsCache.Count} 个");
- }
- catch (Exception ex)
- {
- LogManager.Error($"构建几何对象列表缓存时发生错误: {ex.Message}", ex);
- _allGeometryItemsCache = new List(); // 创建空缓存,避免重复构建
- }
- }
- }
-
- ///
- /// 清除所有缓存,在模型变化时调用
- ///
- public static void ClearAllCaches()
- {
- lock (_cacheLock)
- {
- _channelObjectsCache = null;
- _allGeometryItemsCache = null;
- LogManager.Debug("所有对象缓存已清除");
- }
- }
-
- ///
- /// 增强的碰撞对象信息,包含容器映射
- ///
- /// 原始碰撞对象(可能是几何体子对象)
- /// 包含完整标识信息的对象
- private static ModelItem GetCollisionObjectWithValidIdentity(ModelItem originalItem)
- {
- if (originalItem == null) return null;
-
- // 如果原对象已经有有效名称,直接返回
- if (!string.IsNullOrEmpty(originalItem.DisplayName) && originalItem.DisplayName.Trim().Length > 0)
- {
- return originalItem;
- }
-
- // 否则查找有名称的父级容器
- var containerObject = ModelItemAnalysisHelper.FindNamedParentContainer(originalItem);
-
- LogManager.Info($"[碰撞对象映射] 原对象: '{originalItem.DisplayName}' -> 容器对象: '{containerObject?.DisplayName}'");
-
- return containerObject ?? originalItem;
- }
-
- ///
- /// 计算两个包围盒之间的最短距离(真实碰撞距离)
- ///
- // 此方法已迁移到 BoundingBoxGeometryUtils.CalculateDistance
- [System.Obsolete("请使用 BoundingBoxGeometryUtils.CalculateDistance 替代", true)]
- private double CalculateDistance(BoundingBox3D box1, BoundingBox3D box2)
- {
- throw new NotImplementedException("方法已迁移到工具类");
- }
-
-
- ///
- /// 计算两个包围盒之间的中心点
- ///
- // 此方法已迁移到 BoundingBoxGeometryUtils.CalculateCenter
- [System.Obsolete("请使用 BoundingBoxGeometryUtils.CalculateCenter 替代", true)]
- private Point3D CalculateCenter(BoundingBox3D box1, BoundingBox3D box2)
- {
- throw new NotImplementedException("方法已迁移到工具类");
- }
-
- ///
- /// 检查两个包围盒是否相交(带容差)
- ///
- // 此方法已迁移到 BoundingBoxGeometryUtils.BoundingBoxesIntersectWithTolerance
- [System.Obsolete("请使用 BoundingBoxGeometryUtils.BoundingBoxesIntersectWithTolerance 替代", true)]
- private bool BoundingBoxesIntersectWithTolerance(BoundingBox3D box1, BoundingBox3D box2, double tolerance)
- {
- throw new NotImplementedException("方法已迁移到工具类");
- }
-
- ///
- /// 检查两个包围盒是否相交
- ///
- // 此方法已迁移到 BoundingBoxGeometryUtils.BoundingBoxesIntersect
- [System.Obsolete("请使用 BoundingBoxGeometryUtils.BoundingBoxesIntersect 替代", true)]
- private bool BoundingBoxesIntersect(BoundingBox3D box1, BoundingBox3D box2)
- {
- throw new NotImplementedException("方法已迁移到工具类");
- }
-
- ///
- /// 获取当前测试状态信息(用于调试和验证)
- ///
- /// 测试状态信息
- public string GetTestStatusInfo()
- {
- try
- {
- if (!_isInitialized)
- {
- return "Clash Detective 集成未初始化";
- }
-
- if (_documentClash == null)
- {
- return "无法访问 Clash Detective 文档";
- }
-
- var testInfo = new StringBuilder();
- testInfo.AppendLine("=== Clash Detective 集成状态 ===");
- testInfo.AppendLine($"初始化状态: {(_isInitialized ? "已初始化" : "未初始化")}");
- testInfo.AppendLine($"COM API 连接: {(_clashElement != null ? "已连接" : "未连接")}");
- testInfo.AppendLine($"文档 Clash 对象: {(_documentClash != null ? "可用" : "不可用")}");
-
- // 检查测试列表
- var tests = _documentClash.TestsData.Tests;
- testInfo.AppendLine($"总测试数量: {tests.Count}");
-
- var dynamicTest = tests.FirstOrDefault(t => t.DisplayName == "动态运输路径碰撞检测");
- if (dynamicTest != null && dynamicTest is ClashTest clashTest)
- {
- testInfo.AppendLine($"动态测试: 已找到");
- testInfo.AppendLine($"测试类型: {clashTest.TestType}");
- testInfo.AppendLine($"容差: {clashTest.Tolerance}");
- testInfo.AppendLine($"结果数量: {clashTest.Children.Count}");
-
- if (clashTest.Children.Count > 0)
- {
- testInfo.AppendLine("最近结果:");
- int count = 0;
- foreach (var child in clashTest.Children)
- {
- if (child is ClashResult result && count < 3)
- {
- testInfo.AppendLine($" - {result.DisplayName}: {result.Status}");
- count++;
- }
- }
- }
- }
- else
- {
- testInfo.AppendLine($"动态测试: 未找到");
- }
-
- testInfo.AppendLine("=== 状态信息结束 ===");
- return testInfo.ToString();
- }
- catch (Exception ex)
- {
- return $"获取测试状态失败: {ex.Message}";
- }
- }
-
- ///
- /// 测试容器映射功能是否正常工作
- ///
- public void TestContainerMapping()
- {
- try
- {
- LogManager.Info("=== [容器映射测试] 开始测试容器映射功能 ===");
-
- var testResults = new List();
-
- // 获取一些测试对象
- var allItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf
- .Where(item => item.HasGeometry).Take(10).ToList();
-
- LogManager.Info($"[容器映射测试] 找到 {allItems.Count} 个几何体对象进行测试");
-
- foreach (var item in allItems)
- {
- try
- {
- var originalName = item.DisplayName ?? "无名称";
- var mappedObject = GetCollisionObjectWithValidIdentity(item);
- var mappedName = mappedObject?.DisplayName ?? "无名称";
-
- bool hasMapping = !mappedObject.Equals(item);
-
- testResults.Add($"原始: '{originalName}' -> 映射: '{mappedName}' (映射: {hasMapping})");
-
- LogManager.Info($"[容器映射测试] {testResults.Last()}");
-
- // 验证映射对象的有效性
- if (hasMapping)
- {
- var isValidOriginal = IsModelItemValid(item);
- var isValidMapped = IsModelItemValid(mappedObject);
- LogManager.Info($" - 原始对象有效性: {isValidOriginal}");
- LogManager.Info($" - 映射对象有效性: {isValidMapped}");
- LogManager.Info($" - 原始对象GUID: {item.InstanceGuid}");
- LogManager.Info($" - 映射对象GUID: {mappedObject.InstanceGuid}");
- }
- }
- catch (Exception itemEx)
- {
- LogManager.Warning($"[容器映射测试] 测试单个对象失败: {itemEx.Message}");
- }
- }
-
- LogManager.Info("=== [容器映射测试] 测试结果汇总 ===");
- int mappingCount = testResults.Count(r => r.Contains("映射: True"));
- LogManager.Info($"测试对象总数: {testResults.Count}");
- LogManager.Info($"发生映射的对象: {mappingCount}");
- LogManager.Info($"映射成功率: {(mappingCount * 100.0 / Math.Max(testResults.Count, 1)):F1}%");
-
- // 测试碰撞结果数据结构
- var testCollision = new CollisionResult
- {
- ClashGuid = Guid.NewGuid(),
- DisplayName = "测试碰撞",
- Item1 = allItems.FirstOrDefault(),
- Item2 = allItems.Skip(1).FirstOrDefault(),
- OriginalItem1 = allItems.FirstOrDefault(),
- OriginalItem2 = allItems.Skip(1).FirstOrDefault(),
- HasContainerMapping = true,
- HasPositionInfo = true
- };
-
- LogManager.Info($"[容器映射测试] CollisionResult数据结构测试:");
- LogManager.Info($" - GetValidItem1: {testCollision.GetValidItem1()?.DisplayName ?? "NULL"}");
- LogManager.Info($" - GetValidItem2: {testCollision.GetValidItem2()?.DisplayName ?? "NULL"}");
- LogManager.Info($" - GetOriginalItem1: {testCollision.GetOriginalItem1()?.DisplayName ?? "NULL"}");
- LogManager.Info($" - GetOriginalItem2: {testCollision.GetOriginalItem2()?.DisplayName ?? "NULL"}");
- LogManager.Info($" - HasContainerMapping: {testCollision.HasContainerMapping}");
-
- LogManager.Info("=== [容器映射测试] 测试完成 ===");
- }
- catch (Exception ex)
- {
- LogManager.Error($"[容器映射测试] 测试失败: {ex.Message}");
- LogManager.Error($"[容器映射测试] 堆栈跟踪: {ex.StackTrace}");
- }
- }
-
- // 已删除:ForceShowTestResults 和 CreateDemoTest - 不再需要这些已废弃的方法
-
- ///
- /// 清理资源
- ///
- public void Cleanup()
- {
- try
- {
- LogManager.Info("开始Clash Detective集成资源清理...");
-
- // 检查文档是否仍然有效,如果无效则跳过操作
- var document = Application.ActiveDocument;
- if (document == null || document.IsClear)
- {
- LogManager.Info("文档已无效,跳过资源清理操作");
- return;
- }
-
- // 清除临时高亮
- try
- {
- document.Models.ResetAllTemporaryMaterials();
- LogManager.Info("已清除临时材质高亮");
- }
- catch (ObjectDisposedException)
- {
- LogManager.Info("文档对象已释放,跳过材质清理");
- }
- catch (Exception ex)
- {
- LogManager.Warning($"清除临时材质失败: {ex.Message}");
- }
-
- // 清空内存中的结果
- _currentCollisions?.Clear();
- _cachedResults?.Clear();
-
- // 清理高亮管理器
- lock (_highlightLock)
- {
- _activeHighlights?.Clear();
- }
-
- // 清理引用,防止内存泄漏
- _clashElement = null;
- _dynamicClashTest = null;
- _documentClash = null;
- _state = null;
-
- _isInitialized = false;
-
- LogManager.Info("Clash Detective集成资源清理完成");
- }
- catch (Exception ex)
- {
- LogManager.Error($"清理Clash Detective资源时发生异常: {ex.Message}");
- }
- }
-
- // 已删除:ForceCloseClashDetectiveDialogs - 不再需要强制关闭对话框
-
- // 已删除:CleanupSnapshotTests - 不再需要,使用动画结束后汇总
-
- ///
- /// 触发碰撞检测事件
- ///
- private void OnCollisionDetected(CollisionDetectedEventArgs e)
- {
- CollisionDetected?.Invoke(this, e);
- }
- }
-
- ///
- /// 碰撞结果数据结构
- ///
- public class CollisionResult
- {
- public Guid ClashGuid { get; set; }
- public string DisplayName { get; set; }
- public ClashResultStatus Status { get; set; }
- public string GridLocation { get; set; }
- public ModelItem Item1 { get; set; }
- public ModelItem Item2 { get; set; }
- public Point3D Center { get; set; }
- public double Distance { get; set; }
- public DateTime CreatedTime { get; set; }
-
- // 位置信息用于恢复测试
- public Point3D Item1Position { get; set; }
- public Point3D Item2Position { get; set; }
- public bool HasPositionInfo { get; set; }
-
- // 📦 容器映射信息:记录原始几何体对象引用
- public ModelItem OriginalItem1 { get; set; } // 原始几何体对象
- public ModelItem OriginalItem2 { get; set; } // 原始几何体对象
- public bool HasContainerMapping { get; set; } // 标识是否进行了容器映射
-
- // 📍 获取最终用于ClashDetective的有效对象
- public ModelItem GetValidItem1() => Item1; // 容器对象,用于ClashDetective选择集
- public ModelItem GetValidItem2() => Item2; // 容器对象,用于ClashDetective选择集
-
- // 📍 获取原始几何体对象(用于精确位置信息)
- public ModelItem GetOriginalItem1() => OriginalItem1 ?? Item1;
- public ModelItem GetOriginalItem2() => OriginalItem2 ?? Item2;
- }
-
- ///
- /// 碰撞检测事件参数
- ///
- public class CollisionDetectedEventArgs : EventArgs
- {
- public List Results { get; private set; }
- public int CollisionCount { get; private set; }
-
- public CollisionDetectedEventArgs(List results)
- {
- Results = results;
- CollisionCount = results.Count;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Core/MainPlugin.cs b/src/Core/MainPlugin.cs
index d5b89f6..9ef9c9b 100644
--- a/src/Core/MainPlugin.cs
+++ b/src/Core/MainPlugin.cs
@@ -48,19 +48,8 @@ namespace NavisworksTransport
// 捕获任务调度器未观察到的异常
System.Threading.Tasks.TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
- // 尝试设置线程异常处理,但如果失败就跳过(可能控件已创建)
- try
- {
- System.Windows.Forms.Application.ThreadException += OnThreadException;
- System.Windows.Forms.Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
- LogManager.Info("[全局异常] WinForms异常处理已设置");
- }
- catch (InvalidOperationException ex)
- {
- LogManager.Warning($"[全局异常] 无法设置WinForms异常处理模式(控件已创建): {ex.Message}");
- // 仍然可以添加事件处理器
- System.Windows.Forms.Application.ThreadException += OnThreadException;
- }
+ // 直接注册WinForms线程异常处理器(不调用SetUnhandledExceptionMode,避免插件环境下的警告)
+ System.Windows.Forms.Application.ThreadException += OnThreadException;
_isInitialized = true;
@@ -319,6 +308,15 @@ namespace NavisworksTransport
[DockPanePlugin(420, 700, FixedSize = false, AutoScroll = true)]
public class Main : DockPanePlugin
{
+ ///
+ /// 构造函数,提前初始化全局异常处理器
+ ///
+ public Main()
+ {
+ // 在任何控件创建之前初始化全局异常处理器
+ GlobalExceptionHandler.Initialize();
+ }
+
public override Control CreateControlPane()
{
try
@@ -374,4 +372,5 @@ namespace NavisworksTransport
}, "销毁控制面板");
}
}
+
}
\ No newline at end of file
diff --git a/src/Core/UIStateManager.cs b/src/Core/UIStateManager.cs
index 1ecf377..8c9832f 100644
--- a/src/Core/UIStateManager.cs
+++ b/src/Core/UIStateManager.cs
@@ -743,11 +743,15 @@ namespace NavisworksTransport.Core
{
try
{
- if (_flushTimer != null)
+ var timer = _flushTimer;
+ if (timer != null)
{
+ _flushTimer = null; // 先置空,防止回调继续执行
LogManager.Info("停止保底定时器");
- _flushTimer.Dispose();
- _flushTimer = null;
+
+ // 使用Change方法停止定时器,然后安全释放
+ timer.Change(Timeout.Infinite, Timeout.Infinite);
+ timer.Dispose();
}
}
catch (Exception ex)
diff --git a/src/PathPlanning/GridMapGenerator.cs b/src/PathPlanning/GridMapGenerator.cs
index b4fae5b..0aba662 100644
--- a/src/PathPlanning/GridMapGenerator.cs
+++ b/src/PathPlanning/GridMapGenerator.cs
@@ -1133,9 +1133,9 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info($"[包围盒障碍物处理] 输入统计 - 总模型项: {totalItems}, 将排除通道元素: {channelItemsSet.Count}");
- // 使用并行处理提高性能,使用75%的CPU内核以平衡性能和稳定性
+ // 使用并行处理提高性能,使用50%的CPU内核以平衡性能和稳定性
var lockObject = new object();
- Parallel.ForEach(allItems, new ParallelOptions { MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount * 3 / 4) }, item =>
+ Parallel.ForEach(allItems, new ParallelOptions { MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount / 2) }, item =>
{
try
{
diff --git a/src/UI/WPF/LogisticsControlPanel.xaml.cs b/src/UI/WPF/LogisticsControlPanel.xaml.cs
index bcab159..3c2bd56 100644
--- a/src/UI/WPF/LogisticsControlPanel.xaml.cs
+++ b/src/UI/WPF/LogisticsControlPanel.xaml.cs
@@ -96,8 +96,7 @@ namespace NavisworksTransport.UI.WPF
{
LogManager.Info("=== 物流路径规划插件初始化开始 ===");
- // 初始化全局异常处理
- GlobalExceptionHandler.Initialize();
+ // 全局异常处理器已在Main构造函数中初始化,此处无需重复初始化
// 初始化路径规划管理器
InitializePathPlanningManager();
diff --git a/src/UI/WPF/Models/PathRouteViewModel.cs b/src/UI/WPF/Models/PathRouteViewModel.cs
index b7361c2..c1a5073 100644
--- a/src/UI/WPF/Models/PathRouteViewModel.cs
+++ b/src/UI/WPF/Models/PathRouteViewModel.cs
@@ -572,10 +572,29 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
try
{
+ // 先在UI线程上创建快照,确保线程安全
+ var pointsSnapshot = await _uiStateManager.ExecuteUIUpdateAsync(() =>
+ {
+ return Points?.ToList() ?? new List();
+ });
+
+ // 然后在后台线程计算
return await Task.Run(() =>
{
double totalLength = 0.0;
- var pointsSnapshot = Points.ToList();
+
+ if (pointsSnapshot.Count < 2)
+ {
+ // 在UI线程上更新总长度
+ _uiStateManager.QueueUIUpdate(() =>
+ {
+ _totalLength = 0.0;
+ OnPropertyChanged(nameof(TotalLength));
+ OnPropertyChanged(nameof(SummaryInfo));
+ });
+
+ return 0.0;
+ }
for (int i = 0; i < pointsSnapshot.Count - 1; i++)
{
@@ -617,27 +636,36 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
try
{
+ // 先在UI线程上创建快照,确保线程安全
+ var pointsSnapshot = await _uiStateManager.ExecuteUIUpdateAsync(() =>
+ {
+ return Points?.ToList() ?? new List();
+ });
+
+ var nameSnapshot = await _uiStateManager.ExecuteUIUpdateAsync(() => _name);
+
+ // 然后在后台线程验证
return await Task.Run(() =>
{
bool isValid = true;
var validationMessages = new List();
// 检查路径点数量
- if (Points.Count < 2)
+ if (pointsSnapshot.Count < 2)
{
isValid = false;
validationMessages.Add("路径至少需要2个点");
}
// 检查路径名称
- if (string.IsNullOrWhiteSpace(_name))
+ if (string.IsNullOrWhiteSpace(nameSnapshot))
{
isValid = false;
validationMessages.Add("路径名称不能为空");
}
// 检查重复点
- var duplicatePoints = Points.ToList()
+ var duplicatePoints = pointsSnapshot
.GroupBy(p => new { p.X, p.Y, p.Z })
.Where(g => g.Count() > 1)
.ToList();
@@ -657,7 +685,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
OnPropertyChanged(nameof(ValidationStatus));
});
- LogManager.Debug($"路径验证完成:{_name},结果:{(isValid ? "通过" : "失败")}");
+ LogManager.Debug($"路径验证完成:{nameSnapshot},结果:{(isValid ? "通过" : "失败")}");
return isValid;
});
}
diff --git a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
index 60ea622..6295daa 100644
--- a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
+++ b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
@@ -456,12 +456,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
AnimationDuration = 10.0;
CurrentAnimationTime = 0.0;
- // 设置初始状态
- CanStartAnimation = true;
+ // 设置初始状态 - 修改: 默认状态应该是未激活,等待有效动画
CanPauseAnimation = false;
CanStopAnimation = false;
StartAnimationButtonText = "开始动画";
- AnimationStatus = "动画状态: 就绪";
AnimationProgress = 0;
// 初始化碰撞检测状态
@@ -479,6 +477,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 初始化动画管理器设置
UpdatePathAnimationManagerSettings();
+ // 修改: 使用新的按钮状态更新方法来设置正确的初始状态
+ UpdateAnimationButtonStates();
+
LogManager.Info("动画设置初始化完成");
}
@@ -719,24 +720,20 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
CurrentPathRoute = pathRoute;
- // 更新按钮状态
- var hasValidPath = pathRoute != null && pathRoute.Points.Count >= 2;
- CanStartAnimation = hasValidPath;
+ // 使用新的统一按钮状态更新方法
+ UpdateAnimationButtonStates();
- // 更新状态文本
+ // 更新碰撞状态
if (pathRoute == null)
{
- AnimationStatus = "动画状态: 请选择路径";
CollisionStatus = "请选择路径";
}
else if (pathRoute.Points.Count < 2)
{
- AnimationStatus = "动画状态: 路径点数不足(需要至少2个点)";
CollisionStatus = "路径点数不足";
}
else
{
- AnimationStatus = "动画状态: 就绪";
CollisionStatus = "就绪";
}
@@ -878,15 +875,27 @@ namespace NavisworksTransport.UI.WPF.ViewModels
///
/// 执行清除移动物体命令
- ///
- ///
- /// 执行清除移动物体命令
+ /// 清除移动物体选择、停止当前动画并更新按钮状态
///
private void ExecuteClearAnimatedObject()
{
try
{
- LogManager.Info("开始清除移动物体选择并恢复原始位置");
+ LogManager.Info("开始清除移动物体选择、当前动画并恢复原始位置");
+
+ // 首先停止当前动画(如果正在播放)
+ if (_pathAnimationManager != null && _pathAnimationManager.IsAnimating)
+ {
+ try
+ {
+ _pathAnimationManager.StopAnimation();
+ LogManager.Info("已停止当前播放的动画");
+ }
+ catch (Exception stopEx)
+ {
+ LogManager.Warning($"停止当前动画时出现警告: {stopEx.Message}");
+ }
+ }
// 如果有选中的物体,则恢复到原始位置
if (SelectedAnimatedObject != null)
@@ -918,7 +927,22 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 清理选择状态
SelectedAnimatedObject = null;
- LogManager.Info("移动物体选择已完全清除");
+ // 重置动画进度和状态
+ AnimationProgress = 0;
+ CurrentAnimationTime = 0.0;
+
+ // 清理碰撞检测结果
+ HasCollisionResults = false;
+ CollisionStatus = "就绪";
+ CollisionSummary = "尚未运行碰撞检测";
+
+ // 更新按钮状态 - 清除动画后应该重新评估按钮状态
+ UpdateAnimationButtonStates();
+
+ // 更新生成动画的能力状态
+ UpdateCanGenerateAnimation();
+
+ LogManager.Info("移动物体选择和当前动画已完全清除,按钮状态已更新");
}
catch (Exception ex)
{
@@ -1047,9 +1071,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 使用PathAnimationManager设置物体动画(真正的物体移动动画)
_pathAnimationManager.SetupAnimation(SelectedAnimatedObject, pathPoints, AnimationDuration);
- // 更新状态
- CanStartAnimation = true;
- AnimationStatus = "动画已生成,可以开始播放";
+ // 更新状态 - 使用新的按钮状态更新方法
+ UpdateAnimationButtonStates();
GenerationStatus = "动画生成成功";
var totalElapsed = (DateTime.Now - cacheStartTime).TotalMilliseconds;
@@ -1129,6 +1152,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
GenerationStatus = "可以生成动画";
}
+
+ // 同时更新动画按钮状态,因为对象或路径的变化会影响"开始动画"按钮的可用性
+ UpdateAnimationButtonStates();
}
///
@@ -1146,6 +1172,78 @@ namespace NavisworksTransport.UI.WPF.ViewModels
CollisionDetectionFrequency = 10.0; // 默认值
}
}
+
+ ///
+ /// 统一更新动画按钮状态逻辑
+ /// 根据当前的路径、动画对象和动画管理器状态来决定按钮的可用性
+ ///
+ private void UpdateAnimationButtonStates()
+ {
+ try
+ {
+ // 检查是否有有效的动画可以播放
+ var hasValidPath = CurrentPathRoute != null && CurrentPathRoute.Points.Count >= 2;
+ var hasValidAnimatedObject = SelectedAnimatedObject != null;
+ var hasValidAnimation = hasValidPath && hasValidAnimatedObject;
+
+ // 检查动画管理器当前状态
+ var animationState = _pathAnimationManager?.CurrentState ?? NavisworksTransport.Core.Animation.AnimationState.Stopped;
+ var isAnimating = _pathAnimationManager?.IsAnimating ?? false;
+
+ // 根据动画状态和条件更新按钮状态
+ switch (animationState)
+ {
+ case NavisworksTransport.Core.Animation.AnimationState.Playing:
+ // 播放中:开始按钮禁用,暂停和停止按钮可用
+ CanStartAnimation = false;
+ CanPauseAnimation = true;
+ CanStopAnimation = true;
+ AnimationStatus = "动画状态: 播放中";
+ break;
+
+ case NavisworksTransport.Core.Animation.AnimationState.Paused:
+ // 暂停中:开始按钮可用(显示为继续),暂停按钮禁用,停止按钮可用
+ CanStartAnimation = hasValidAnimation;
+ CanPauseAnimation = false;
+ CanStopAnimation = true;
+ StartAnimationButtonText = "继续播放";
+ AnimationStatus = "动画状态: 已暂停";
+ break;
+
+ default:
+ // 停止或其他状态:根据是否有有效动画来决定开始按钮状态
+ CanStartAnimation = hasValidAnimation;
+ CanPauseAnimation = false;
+ CanStopAnimation = false;
+ StartAnimationButtonText = "开始动画";
+
+ // 更新状态文本
+ if (!hasValidPath && !hasValidAnimatedObject)
+ {
+ AnimationStatus = "动画状态: 请选择路径和移动物体";
+ }
+ else if (!hasValidPath)
+ {
+ AnimationStatus = "动画状态: 请选择有效路径(至少2个点)";
+ }
+ else if (!hasValidAnimatedObject)
+ {
+ AnimationStatus = "动画状态: 请选择移动物体";
+ }
+ else
+ {
+ AnimationStatus = "动画状态: 就绪";
+ }
+ break;
+ }
+
+ LogManager.Debug($"按钮状态已更新: CanStart={CanStartAnimation}, CanPause={CanPauseAnimation}, CanStop={CanStopAnimation}, Status={AnimationStatus}");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"更新动画按钮状态时发生错误: {ex.Message}", ex);
+ }
+ }
///
/// 初始化参数更新防抖定时器