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); + } + } /// /// 初始化参数更新防抖定时器