修复路径预计算范围不对、碰撞检测报告名字匹配错误的BUG

This commit is contained in:
tian 2025-10-13 19:14:46 +08:00
parent 882f8283b5
commit bd7eeb3d46
4 changed files with 167 additions and 51 deletions

View File

@ -294,6 +294,7 @@
<Compile Include="src\Utils\NavisworksToDMesh3Converter.cs" />
<Compile Include="src\Utils\UnitsConverter.cs" />
<Compile Include="src\Utils\VisibilityHelper.cs" />
<Compile Include="src\Utils\ModelItemTransformHelper.cs" />
<!-- Assembly Info -->
<Compile Include="Properties\AssemblyInfo.cs" />

View File

@ -275,27 +275,36 @@ namespace NavisworksTransport.Commands
return null;
}
// 查找最新的物流碰撞检测测试
var latestTest = documentClash.TestsData.Tests.Cast<ClashTest>()
.Where(t => t.DisplayName.StartsWith("物流碰撞检测_"))
.OrderByDescending(t => t.DisplayName)
// 从动画管理器获取当前路径名称
var animationManager = Core.Animation.PathAnimationManager.GetInstance();
var currentPathName = animationManager?.PathName;
if (string.IsNullOrEmpty(currentPathName))
{
throw new InvalidOperationException("无法获取当前路径名称");
}
// 精确匹配当前路径的测试
var matchedTest = documentClash.TestsData.Tests.Cast<ClashTest>()
.Where(t => t.DisplayName.StartsWith("物流碰撞检测_") &&
t.DisplayName.Contains(currentPathName))
.OrderByDescending(t => t.DisplayName) // 同一路径可能有多个测试,取最新的
.FirstOrDefault();
if (latestTest != null)
if (matchedTest != null)
{
LogManager.Debug($"缓存键用测试名称: {latestTest.DisplayName}");
return latestTest.DisplayName;
LogManager.Info($"找到当前路径的测试: {matchedTest.DisplayName}");
return matchedTest.DisplayName;
}
else
{
LogManager.Info("未找到物流碰撞检测测试,无法生成缓存键");
return null;
throw new InvalidOperationException($"未找到路径 '{currentPathName}' 的碰撞测试");
}
}
catch (Exception ex)
{
LogManager.Error($"获取测试名称作为缓存键失败: {ex.Message}");
return null;
throw; // 让错误暴露出来
}
}
@ -337,27 +346,37 @@ namespace NavisworksTransport.Commands
int totalCollisionCount = 0; // Clash Detective总碰撞数
// 🔧 修复:只统计最新的物流碰撞检测测试,避免重复累积
var latestTest = documentClash.TestsData.Tests.Cast<ClashTest>()
.Where(t => t.DisplayName.StartsWith("物流碰撞检测_"))
// 从动画管理器获取当前路径名称
var animationManager = Core.Animation.PathAnimationManager.GetInstance();
var currentPathName = animationManager?.PathName;
if (string.IsNullOrEmpty(currentPathName))
{
throw new InvalidOperationException("无法获取当前路径名称");
}
// 精确匹配当前路径的测试
var matchedTest = documentClash.TestsData.Tests.Cast<ClashTest>()
.Where(t => t.DisplayName.StartsWith("物流碰撞检测_") &&
t.DisplayName.Contains(currentPathName))
.OrderByDescending(t => t.DisplayName)
.FirstOrDefault();
if (latestTest != null)
if (matchedTest != null)
{
LogManager.Info($"找到最新的物流路径测试: {latestTest.DisplayName}");
// 提取最新测试的碰撞数据
var testCollisions = ExtractCollisionsFromTest(latestTest);
LogManager.Info($"找到当前路径的测试: {matchedTest.DisplayName}");
// 提取匹配测试的碰撞数据
var testCollisions = ExtractCollisionsFromTest(matchedTest);
allCollisions.AddRange(testCollisions);
// 统计最新测试的碰撞总数
totalCollisionCount = GetTotalClashResultCountFromTest(latestTest);
LogManager.Debug($"最新测试 '{latestTest.DisplayName}' 包含 {totalCollisionCount} 个碰撞");
// 统计匹配测试的碰撞总数
totalCollisionCount = GetTotalClashResultCountFromTest(matchedTest);
LogManager.Debug($"匹配测试 '{matchedTest.DisplayName}' 包含 {totalCollisionCount} 个碰撞");
}
else
{
LogManager.Info("未找到物流碰撞检测测试");
throw new InvalidOperationException($"未找到路径 '{currentPathName}' 的碰撞测试");
}
// 去重处理

View File

@ -466,45 +466,81 @@ namespace NavisworksTransport.Core.Animation
// 构建通道缓存
ClashDetectiveIntegration.Instance.BuildChannelObjectsCache();
ClashDetectiveIntegration.BuildAllGeometryItemsCache();
// 获取所有有几何体的对象
var allItems = NavisApplication.ActiveDocument.Models.RootItemDescendantsAndSelf
.Where(item => item.HasGeometry)
.ToList();
// 构建排除列表:动画对象本身及其子对象
var excludeList = new ModelItemCollection();
excludeList.AddRange(_animatedObject.DescendantsAndSelf);
// 使用ClashDetectiveIntegration的碰撞检测方法它会自动排除通道对象
// 创建一个较大的检测间隙来获取路径范围内所有潜在碰撞对象
var detectionGap = 100.0; // 100米范围内的对象都视为潜在碰撞对象
// 调用ClashDetectiveIntegration的DetectCollisions方法
// 该方法内部会自动排除通道对象通过IsChannelObject方法
var collisionResults = ClashDetectiveIntegration.Instance.DetectCollisions(
_animatedObject,
excludeList,
detectionGap
// 计算路径包围盒
var pathBounds = CalculatePathBounds();
// 计算路径包围盒中心点(模型单位)
var pathCenter = new Point3D(
(pathBounds.Min.X + pathBounds.Max.X) / 2,
(pathBounds.Min.Y + pathBounds.Max.Y) / 2,
(pathBounds.Min.Z + pathBounds.Max.Z) / 2
);
// 从碰撞结果中提取唯一的碰撞对象
var potentialColliders = new HashSet<ModelItem>();
foreach (var result in collisionResults)
// 计算包围盒对角线长度
var diagonal = Math.Sqrt(
Math.Pow(pathBounds.Max.X - pathBounds.Min.X, 2) +
Math.Pow(pathBounds.Max.Y - pathBounds.Min.Y, 2) +
Math.Pow(pathBounds.Max.Z - pathBounds.Min.Z, 2)
);
// 检测半径 = 包围盒对角线的一半(精确覆盖整条路径)
var detectionGap = diagonal / 2;
LogManager.Info($"路径包围盒: Min=({pathBounds.Min.X:F2}, {pathBounds.Min.Y:F2}, {pathBounds.Min.Z:F2}), " +
$"Max=({pathBounds.Max.X:F2}, {pathBounds.Max.Y:F2}, {pathBounds.Max.Z:F2})");
LogManager.Info($"路径中心: ({pathCenter.X:F2}, {pathCenter.Y:F2}, {pathCenter.Z:F2})");
LogManager.Info($"包围盒对角线: {diagonal:F2} 模型单位");
// 临时移动物体到路径中心,以便在路径范围内检测潜在碰撞对象
var offset = ModelItemTransformHelper.MoveItemToPosition(_animatedObject, pathCenter);
try
{
if (result.Item2 != null && !potentialColliders.Contains(result.Item2))
LogManager.Info($"检测半径: {detectionGap:F2} 模型单位(对角线的一半)");
// 调用ClashDetectiveIntegration的DetectCollisions方法
// 现在物体在路径中心,检测路径长度半径内的对象
// 该方法内部会自动排除通道对象通过IsChannelObject方法
var collisionResults = ClashDetectiveIntegration.Instance.DetectCollisions(
_animatedObject,
excludeList,
detectionGap
);
// 从碰撞结果中提取唯一的碰撞对象
var potentialColliders = new HashSet<ModelItem>();
foreach (var result in collisionResults)
{
potentialColliders.Add(result.Item2);
if (result.Item2 != null && !potentialColliders.Contains(result.Item2))
{
potentialColliders.Add(result.Item2);
}
}
var finalList = potentialColliders.ToList();
LogManager.Info($"获取潜在碰撞对象完成: 总对象={allItems.Count}, " +
$"排除自身及子对象={excludeList.Count}, " +
$"路径沿途潜在碰撞对象={finalList.Count}(已自动排除通道)");
return finalList;
}
finally
{
// 恢复物体到原位置
ModelItemTransformHelper.RestoreItemPosition(_animatedObject, offset);
LogManager.Info("物体已恢复到原始位置");
}
var finalList = potentialColliders.ToList();
LogManager.Info($"获取潜在碰撞对象完成: 总对象={allItems.Count}, " +
$"排除自身及子对象={excludeList.Count}, " +
$"检测范围内潜在碰撞对象={finalList.Count}(已自动排除通道)");
return finalList;
}
catch (Exception ex)
{

View File

@ -0,0 +1,60 @@
using Autodesk.Navisworks.Api;
namespace NavisworksTransport.Utils
{
/// <summary>
/// ModelItem变换辅助工具
/// 提供临时移动和恢复物体位置的通用方法
/// </summary>
public static class ModelItemTransformHelper
{
/// <summary>
/// 临时移动物体到指定位置
/// </summary>
/// <param name="item">要移动的物体</param>
/// <param name="targetPosition">目标位置(物体中心)</param>
/// <returns>用于恢复的偏移向量</returns>
public static Vector3D MoveItemToPosition(ModelItem item, Point3D targetPosition)
{
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { item };
// 计算当前中心位置
var currentBounds = item.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);
return offset;
}
/// <summary>
/// 恢复物体到原位置
/// </summary>
/// <param name="item">要恢复的物体</param>
/// <param name="originalOffset">原始移动偏移向量</param>
public static void RestoreItemPosition(ModelItem item, Vector3D originalOffset)
{
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { item };
// 反向偏移
var restoreOffset = new Vector3D(-originalOffset.X, -originalOffset.Y, -originalOffset.Z);
var restoreTransform = Transform3D.CreateTranslation(restoreOffset);
doc.Models.OverridePermanentTransform(modelItems, restoreTransform, false);
}
}
}