NavisworksTransport/src/Core/Collision/ClashDetectiveIntegration.cs

1858 lines
79 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
namespace NavisworksTransport
{
/// <summary>
/// Clash Detective 集成管理器
/// 实现动态碰撞检测与Clash Detective窗口的联动
/// </summary>
public class ClashDetectiveIntegration
{
private static ClashDetectiveIntegration _instance;
private ComApi.InwOpClashElement _clashElement;
private ComApi.InwOpState10 _state;
private DocumentClash _documentClash;
private ClashTest _dynamicClashTest;
private List<ClashResult> _currentCollisions;
private bool _isInitialized = false;
/// <summary>
/// 单例实例
/// </summary>
public static ClashDetectiveIntegration Instance
{
get
{
if (_instance == null)
{
_instance = new ClashDetectiveIntegration();
}
return _instance;
}
}
/// <summary>
/// 当前检测到的碰撞结果
/// </summary>
public List<ClashResult> CurrentCollisions
{
get { return _currentCollisions ?? new List<ClashResult>(); }
}
/// <summary>
/// 碰撞检测结果变化事件
/// </summary>
public event EventHandler<CollisionDetectedEventArgs> CollisionDetected;
private ClashDetectiveIntegration()
{
_currentCollisions = new List<ClashResult>();
_cachedResults = new List<CollisionResult>();
// 延迟初始化,避免在清理时无意中触发
// Initialize()方法现在需要手动调用
}
/// <summary>
/// 初始化Clash Detective集成
/// </summary>
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;
}
}
/// <summary>
/// 查找Clash Detective插件简化版本 - 跳过无效的COM API枚举
/// </summary>
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;
}
}
/// <summary>
/// 设置动态碰撞检测测试
/// </summary>
private void SetupDynamicClashTest()
{
try
{
// 首先检查是否已经存在同名测试,如果存在则删除
var existingTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == "动态运输路径碰撞检测");
if (existingTest != null)
{
LogManager.Info("删除已存在的动态测试");
if (existingTest is ClashTest existingClashTest)
{
_documentClash.TestsData.TestsRemove(existingClashTest);
}
else
{
LogManager.Warning("找到的测试不是ClashTest类型无法删除");
}
}
// 创建新的碰撞测试
_dynamicClashTest = new ClashTest();
_dynamicClashTest.DisplayName = "动态运输路径碰撞检测";
_dynamicClashTest.TestType = ClashTestType.Hard;
_dynamicClashTest.Tolerance = 0.01; // 1cm容差
// 设置测试规则 - 移除在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}");
}
}
/// <summary>
/// 执行动态碰撞检测
/// </summary>
/// <param name="animatedObject">动画对象</param>
/// <param name="excludeObjects">排除对象</param>
/// <returns>碰撞结果</returns>
public List<CollisionResult> DetectCollisions(ModelItem animatedObject,
ModelItemCollection excludeObjects = null)
{
try
{
// 使用简化检测模式
LogManager.Debug("使用简化检测模式进行碰撞检测");
var results = DetectCollisionsSimple(animatedObject, excludeObjects);
// 在动画过程中不创建Clash Detective测试避免影响性能
// 只有在动画结束后手动测试时才创建完整的测试
// 触发事件
OnCollisionDetected(new CollisionDetectedEventArgs(results));
LogManager.Debug($"碰撞检测完成,发现 {results.Count} 个碰撞");
return results;
}
catch (Exception ex)
{
LogManager.Error($"碰撞检测失败: {ex.Message}");
// 回退到简化检测
return DetectCollisionsSimple(animatedObject, excludeObjects);
}
}
/// <summary>
/// 创建碰撞快照(动画结束后一次性更新结果)
/// </summary>
public void CreateCollisionSnapshot(List<CollisionResult> results)
{
try
{
if (_documentClash == null)
return;
// 缓存结果,等动画结束后统一处理
CacheCollisionResults(results);
}
catch (Exception ex)
{
LogManager.Error($"创建实时碰撞快照失败: {ex.Message}");
}
}
private List<CollisionResult> _cachedResults = new List<CollisionResult>();
/// <summary>
/// 缓存碰撞结果
/// </summary>
private void CacheCollisionResults(List<CollisionResult> results)
{
if (results != null && results.Count > 0)
{
_cachedResults.AddRange(results);
}
}
private DateTime _lastCollisionTestTime = DateTime.MinValue;
private readonly TimeSpan _minTestInterval = TimeSpan.FromMilliseconds(500); // 最小间隔500ms
/// <summary>
/// 缓存碰撞结果(动画过程中使用)- 现在包含位置信息用于恢复测试
/// 使用精确的碰撞检测算法替代简化检测
/// </summary>
public void CacheCollisionDuringAnimation(ModelItem animatedObject, Point3D animatedObjectPosition, ModelItem collisionObject, Point3D collisionObjectPosition = null)
{
try
{
if (!IsModelItemValid(animatedObject) || !IsModelItemValid(collisionObject))
return;
// 使用精确的碰撞检测算法
var animatedBoundingBox = animatedObject.BoundingBox();
var collisionBoundingBox = collisionObject.BoundingBox();
if (BoundingBoxesIntersect(animatedBoundingBox, collisionBoundingBox))
{
// 创建精确的碰撞结果
var collision = new CollisionResult
{
ClashGuid = Guid.NewGuid(),
DisplayName = $"精确碰撞: {animatedObject.DisplayName} <-> {collisionObject.DisplayName}",
Status = ClashResultStatus.New,
Item1 = animatedObject,
Item2 = collisionObject,
CreatedTime = DateTime.Now,
Distance = CalculateDistance(animatedBoundingBox, collisionBoundingBox),
Center = CalculateCenter(animatedBoundingBox, collisionBoundingBox),
Item1Position = animatedObjectPosition,
Item2Position = collisionObjectPosition ?? GetObjectPosition(collisionObject),
HasPositionInfo = true
};
// 去重处理:避免重复缓存相同的碰撞对
var existing = _cachedResults.FirstOrDefault(r =>
r.Item1.Equals(animatedObject) && r.Item2.Equals(collisionObject));
if (existing == null)
{
_cachedResults.Add(collision);
LogManager.Info($"缓存精确碰撞: {animatedObject.DisplayName} <-> {collisionObject.DisplayName} " +
$"在 {collision.CreatedTime:HH:mm:ss},位置: ({collision.Item1Position.X:F1},{collision.Item1Position.Y:F1},{collision.Item1Position.Z:F1})" +
$"距离: {collision.Distance:F2}");
}
else
{
LogManager.Debug($"跳过重复碰撞: {animatedObject.DisplayName} <-> {collisionObject.DisplayName}");
}
}
}
catch (Exception ex)
{
LogManager.Error($"缓存碰撞失败: {ex.Message}");
}
}
/// <summary>
/// 获取对象当前位置
/// </summary>
private Point3D GetObjectPosition(ModelItem item)
{
try
{
if (item == null) return new Point3D(0, 0, 0);
var bounds = item.BoundingBox();
if (bounds != null)
{
return new Point3D(
(bounds.Min.X + bounds.Max.X) / 2,
(bounds.Min.Y + bounds.Max.Y) / 2,
(bounds.Min.Z + bounds.Max.Z) / 2
);
}
return new Point3D(0, 0, 0);
}
catch (Exception ex)
{
LogManager.Error($"获取对象位置失败: {ex.Message}");
return new Point3D(0, 0, 0);
}
}
/// <summary>
/// 动画结束后统一创建和运行所有碰撞测试 - 基于官方示例
/// </summary>
public void CreateAllAnimationCollisionTests()
{
try
{
LogManager.Info("=== 动画结束,开始创建所有碰撞测试 ===");
if (_documentClash == null || _cachedResults.Count == 0)
{
LogManager.Info("没有缓存的碰撞结果需要处理");
return;
}
LogManager.Info($"开始处理 { _cachedResults.Count} 个缓存的碰撞结果");
// 简单测试:验证对象索引方案
if (_cachedResults.Count > 0)
{
var testCollision = _cachedResults[0];
LogManager.Info($"[简单测试] 动画后对象状态:");
LogManager.Info($"[简单测试] 对象1: {testCollision.Item1?.DisplayName ?? "null"}");
LogManager.Info($"[简单测试] 对象2: {testCollision.Item2?.DisplayName ?? "null"}");
// 测试对象是否仍然有效
LogManager.Info($"[简单测试] 对象1有效: {IsModelItemValid(testCollision.Item1)}");
LogManager.Info($"[简单测试] 对象2有效: {IsModelItemValid(testCollision.Item2)}");
// 记录对象类型信息用于后续方案
if (testCollision.Item1 != null)
{
LogManager.Info($"[简单测试] 对象1类型: {testCollision.Item1.GetType().Name}");
}
if (testCollision.Item2 != null)
{
LogManager.Info($"[简单测试] 对象2类型: {testCollision.Item2.GetType().Name}");
}
}
// 去重处理:按对象对分组
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)
})
.ToList();
LogManager.Info($"去重后得到 {uniqueCollisions.Count} 个唯一碰撞对");
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}: {testName} ===");
LogManager.Info($"对象: {collision.Item1.DisplayName} 在位置: ({collision.Item1Position.X:F1},{collision.Item1Position.Y:F1},{collision.Item1Position.Z:F1})");
LogManager.Info($"碰撞对象: {collision.Item2.DisplayName}");
// 将对象移动到碰撞位置
var animatedObject = collision.Item1;
var modelItems = new ModelItemCollection { animatedObject };
var targetPosition = collision.Item1Position;
// 计算当前位置到目标位置的偏移
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 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($"已将 {animatedObject.DisplayName} 移动到碰撞位置: ({targetPosition.X:F1},{targetPosition.Y:F1},{targetPosition.Z:F1})");
LogManager.Info($"开始创建测试 {createdCount}: {testName}");
LogManager.Info($"碰撞对象1: {collision.Item1.DisplayName}");
LogManager.Info($"碰撞对象2: {collision.Item2.DisplayName}");
try
{
// 创建新的碰撞测试
var collisionTest = new ClashTest();
LogManager.Info("✓ ClashTest对象创建成功");
collisionTest.DisplayName = testName;
collisionTest.TestType = ClashTestType.Hard;
collisionTest.Tolerance = 0.01;
collisionTest.Guid = Guid.Empty;
//LogManager.Info("✓ 测试属性设置完成");
// 设置选择集A
var selectionA = new ModelItemCollection();
//LogManager.Info("✓ 选择集A集合创建成功");
selectionA.Add(collision.Item1);
//LogManager.Info("✓ 添加到选择集A成功");
collisionTest.SelectionA.Selection.CopyFrom(selectionA);
//LogManager.Info("✓ 选择集A复制完成");
// 设置选择集B
var selectionB = new ModelItemCollection();
//LogManager.Info("✓ 选择集B集合创建成功");
selectionB.Add(collision.Item2);
//LogManager.Info("✓ 添加到选择集B成功");
collisionTest.SelectionB.Selection.CopyFrom(selectionB);
//LogManager.Info("✓ 选择集B复制完成");
try
{
LogManager.Info("开始添加测试到文档...");
_documentClash.TestsData.TestsAddCopy(collisionTest);
LogManager.Info("✓ 测试添加到文档成功");
}
catch (Exception addEx)
{
LogManager.Error($"添加测试失败: {addEx.GetType().Name}: {addEx.Message}");
LogManager.Error($"错误堆栈: {addEx.StackTrace}");
throw; // 重新抛出以便上层捕获
}
// 获取添加后的测试
var addedTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == testName) as ClashTest;
LogManager.Info($"✓ 获取添加后的测试: {addedTest != null}");
if (addedTest != null)
{
LogManager.Info("开始运行测试...");
try
{
_documentClash.TestsData.TestsRunTest(addedTest);
LogManager.Info("测试运行完成");
// 重新获取测试对象,避免访问已释放的对象
var refreshedTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == testName) as ClashTest;
if (refreshedTest != null)
{
LogManager.Info($"测试 {createdCount}: {testName} - 碰撞数量: {refreshedTest.Children.Count}");
}
else
{
LogManager.Info($"测试 {createdCount}: {testName} - 无法获取刷新后的测试结果");
}
}
catch (Exception runEx)
{
LogManager.Error($"运行测试失败: {runEx.Message}");
}
}
}
catch (Exception createEx)
{
LogManager.Error($"具体错误位置: {createEx.StackTrace}");
LogManager.Error($"创建测试失败 - 异常类型: {createEx.GetType().Name}: {createEx.Message}");
}
createdCount++;
// 小延迟确保测试完成
System.Threading.Thread.Sleep(100);
}
catch (Exception createEx)
{
LogManager.Error($"创建单个测试失败: {createEx.Message}");
}
}
// 简单测试使用现有PathAnimationManager的移动方法测试对象位置移动
if (_cachedResults.Count > 0)
{
var testCollision = _cachedResults[0];
LogManager.Info("[移动测试] 开始测试对象位置移动...");
try
{
if (testCollision.Item1 != null)
{ var testDoc = Autodesk.Navisworks.Api.Application.ActiveDocument;
var modelItems = new ModelItemCollection { testCollision.Item1 };
// 获取当前位置
var bounds = testCollision.Item1.BoundingBox();
var currentPosition = 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($"[移动测试] 当前位置: ({currentPosition.X:F2}, {currentPosition.Y:F2}, {currentPosition.Z:F2})");
// 创建测试位置(当前位置+偏移)
var testPosition = new Point3D(currentPosition.X + 100, currentPosition.Y, currentPosition.Z);
LogManager.Info($"[移动测试] 测试位置: ({testPosition.X:F2}, {testPosition.Y:F2}, {testPosition.Z:F2})");
// 使用正确的Navisworks API方法移动对象
var offset = new Vector3D(
testPosition.X - currentPosition.X,
testPosition.Y - currentPosition.Y,
testPosition.Z - currentPosition.Z
);
var transform = Transform3D.CreateTranslation(offset);
testDoc.Models.OverridePermanentTransform(modelItems, transform, false);
LogManager.Info("[移动测试] 对象移动完成");
}
}
catch (Exception ex)
{
LogManager.Error($"[移动测试] 移动失败: {ex.Message}");
}
}
LogManager.Info($"=== 位置恢复方案完成:成功创建并运行 {createdCount} 个碰撞测试 ===");
if (createdCount > 0)
{
// 刷新Clash Detective窗口
RefreshClashDetectiveUI();
// 清空缓存
_cachedResults.Clear();
LogManager.Info("=== 动画碰撞测试(位置恢复方案)完成 ===");
}
}
catch (Exception ex)
{
LogManager.Error($"动画结束后创建测试失败: {ex.Message}");
}
}
/*
/// <summary>
/// 动画结束后创建最终碰撞结果汇总 - 使用官方示例方法(已注释,改为实时添加)
/// </summary>
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}");
}
}
*/
/// <summary>
/// 刷新Clash Detective UI - 安全版本
/// </summary>
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}");
}
}
/// <summary>
/// 更新现有测试的选择集
/// </summary>
private void UpdateExistingTest(ClashTest existingTest, List<CollisionResult> 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}");
}
}
/// <summary>
/// 创建新的动画碰撞测试
/// </summary>
private void CreateNewAnimationTest(string testName, List<CollisionResult> 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}");
}
}
/// <summary>
/// 更新主测试记录(避免频繁创建新测试)
/// </summary>
private void UpdateMainClashTest(List<CollisionResult> 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}");
}
}
/// <summary>
/// 检查ModelItem是否仍然有效
/// </summary>
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;
}
}
private DateTime _lastTestCreationTime = DateTime.MinValue;
/// <summary>
/// 判断是否应该创建新测试(限制创建频率)
/// </summary>
private bool ShouldCreateNewTest()
{
// 每5秒最多创建一个测试
return DateTime.Now - _lastTestCreationTime > TimeSpan.FromSeconds(5);
}
/// <summary>
/// 确保动态测试存在
/// </summary>
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}");
}
}
/// <summary>
/// 安全地更新选择集
/// </summary>
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;
}
}
}
/// <summary>
/// 创建临时碰撞测试
/// </summary>
/// <param name="animatedObject">动画对象</param>
/// <param name="excludeObjects">排除对象</param>
/// <returns>临时测试对象</returns>
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;
}
}
/// <summary>
/// 确保测试仍然存在并且有效
/// </summary>
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();
}
}
/// <summary>
/// 安全地运行碰撞检测
/// </summary>
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; // 重新抛出异常,让上层处理
}
}
}
/// <summary>
/// 更新碰撞测试的选择集
/// </summary>
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}");
}
}
/// <summary>
/// 处理碰撞检测结果
/// </summary>
private List<CollisionResult> ProcessClashResults(ClashTest clashTest)
{
var results = new List<CollisionResult>();
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;
}
/// <summary>
/// 同步结果到Clash Detective窗口
/// </summary>
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}");
}
}
/// <summary>
/// 刷新Clash Detective窗口修复版 - 避免UI阻塞
/// </summary>
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}");
}
}
/// <summary>
/// 确保测试在界面中可见
/// </summary>
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}");
}
}
/// <summary>
/// 高亮显示碰撞对象
/// </summary>
/// <param name="results">碰撞结果</param>
public void HighlightCollisions(List<CollisionResult> results)
{
try
{
var doc = Application.ActiveDocument;
// 清除之前的高亮
doc.Models.ResetAllTemporaryMaterials();
if (results.Count > 0)
{
var collidingItems = new ModelItemCollection();
foreach (var result in results)
{
if (result.Item1 != null)
collidingItems.Add(result.Item1);
if (result.Item2 != null)
collidingItems.Add(result.Item2);
}
// 高亮碰撞对象(红色)
doc.Models.OverrideTemporaryColor(collidingItems, Color.Red);
LogManager.Debug($"高亮显示 {collidingItems.Count} 个碰撞对象");
}
}
catch (Exception ex)
{
LogManager.Error($"高亮显示碰撞对象失败: {ex.Message}");
}
}
/// <summary>
/// 简化的碰撞检测当Clash Detective不可用时
/// </summary>
private List<CollisionResult> DetectCollisionsSimple(ModelItem animatedObject,
ModelItemCollection excludeObjects)
{
var results = new List<CollisionResult>();
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 allItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf
.Where(item => item.HasGeometry && !item.Equals(animatedObject));
if (excludeObjects != null)
{
allItems = allItems.Where(item => !excludeObjects.Contains(item));
}
var itemList = allItems.ToList();
LogManager.Debug($"检测对象总数: {itemList.Count}");
int checkedCount = 0;
foreach (var item in itemList)
{
try
{
var itemBoundingBox = item.BoundingBox();
// 恢复原来的容差设置
var tolerance = 100.0; // 100单位的容差
if (BoundingBoxesIntersectWithTolerance(animatedBoundingBox, itemBoundingBox, tolerance))
{
var result = new CollisionResult
{
ClashGuid = Guid.NewGuid(),
DisplayName = $"简化碰撞检测: {animatedObject.DisplayName} <-> {item.DisplayName}",
Status = ClashResultStatus.New,
Item1 = animatedObject,
Item2 = item,
CreatedTime = DateTime.Now,
Distance = CalculateDistance(animatedBoundingBox, itemBoundingBox),
Center = CalculateCenter(animatedBoundingBox, itemBoundingBox)
};
results.Add(result);
LogManager.Info($"检测到碰撞: {animatedObject.DisplayName} <-> {item.DisplayName},距离: {result.Distance:F2}");
}
checkedCount++;
}
catch (Exception itemEx)
{
LogManager.Warning($"检查单个对象碰撞时出错 {item.DisplayName}: {itemEx.Message}");
}
}
LogManager.Debug($"简化碰撞检测完成: 检查了 {checkedCount} 个对象,发现 {results.Count} 个碰撞");
}
catch (Exception ex)
{
LogManager.Error($"简化碰撞检测失败: {ex.Message}");
}
return results;
}
/// <summary>
/// 计算两个包围盒之间的距离
/// </summary>
private double CalculateDistance(BoundingBox3D box1, BoundingBox3D box2)
{
var center1 = new Point3D(
(box1.Min.X + box1.Max.X) / 2,
(box1.Min.Y + box1.Max.Y) / 2,
(box1.Min.Z + box1.Max.Z) / 2
);
var center2 = new Point3D(
(box2.Min.X + box2.Max.X) / 2,
(box2.Min.Y + box2.Max.Y) / 2,
(box2.Min.Z + box2.Max.Z) / 2
);
return Math.Sqrt(
Math.Pow(center2.X - center1.X, 2) +
Math.Pow(center2.Y - center1.Y, 2) +
Math.Pow(center2.Z - center1.Z, 2)
);
}
/// <summary>
/// 计算两个包围盒之间的中心点
/// </summary>
private Point3D CalculateCenter(BoundingBox3D box1, BoundingBox3D box2)
{
var center1 = new Point3D(
(box1.Min.X + box1.Max.X) / 2,
(box1.Min.Y + box1.Max.Y) / 2,
(box1.Min.Z + box1.Max.Z) / 2
);
var center2 = new Point3D(
(box2.Min.X + box2.Max.X) / 2,
(box2.Min.Y + box2.Max.Y) / 2,
(box2.Min.Z + box2.Max.Z) / 2
);
return new Point3D(
(center1.X + center2.X) / 2,
(center1.Y + center2.Y) / 2,
(center1.Z + center2.Z) / 2
);
}
/// <summary>
/// 检查两个包围盒是否相交(带容差)
/// </summary>
private bool BoundingBoxesIntersectWithTolerance(BoundingBox3D box1, BoundingBox3D box2, double tolerance)
{
return box1.Min.X <= box2.Max.X + tolerance && box1.Max.X >= box2.Min.X - tolerance &&
box1.Min.Y <= box2.Max.Y + tolerance && box1.Max.Y >= box2.Min.Y - tolerance &&
box1.Min.Z <= box2.Max.Z + tolerance && box1.Max.Z >= box2.Min.Z - tolerance;
}
/// <summary>
/// 检查两个包围盒是否相交
/// </summary>
private bool BoundingBoxesIntersect(BoundingBox3D box1, BoundingBox3D box2)
{
return box1.Min.X <= box2.Max.X && box1.Max.X >= box2.Min.X &&
box1.Min.Y <= box2.Max.Y && box1.Max.Y >= box2.Min.Y &&
box1.Min.Z <= box2.Max.Z && box1.Max.Z >= box2.Min.Z;
}
/// <summary>
/// 获取当前测试状态信息(用于调试和验证)
/// </summary>
/// <returns>测试状态信息</returns>
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}";
}
}
// 已删除ForceShowTestResults 和 CreateDemoTest - 不再需要这些已废弃的方法
/// <summary>
/// 清理资源
/// </summary>
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();
// 清理引用,防止内存泄漏
_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 - 不再需要,使用动画结束后汇总
/// <summary>
/// 触发碰撞检测事件
/// </summary>
private void OnCollisionDetected(CollisionDetectedEventArgs e)
{
CollisionDetected?.Invoke(this, e);
}
}
/// <summary>
/// 碰撞结果数据结构
/// </summary>
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; }
}
/// <summary>
/// 碰撞检测事件参数
/// </summary>
public class CollisionDetectedEventArgs : EventArgs
{
public List<CollisionResult> Results { get; private set; }
public int CollisionCount { get; private set; }
public CollisionDetectedEventArgs(List<CollisionResult> results)
{
Results = results;
CollisionCount = results.Count;
}
}
}