1858 lines
79 KiB
C#
1858 lines
79 KiB
C#
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;
|
||
}
|
||
}
|
||
} |