From cdd6ee931992dc28a3a7b0d15b5a09afe51b1e8e Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Fri, 30 Jan 2026 12:41:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96ClashDetective=E9=9B=86?= =?UTF-8?q?=E6=88=90=EF=BC=8C=E5=87=8F=E5=B0=91=E4=B8=B4=E6=97=B6=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=88=9B=E5=BB=BA=E5=92=8C=E5=88=A0=E9=99=A4=E7=9A=84?= =?UTF-8?q?=E6=80=A7=E8=83=BD=E5=BC=80=E9=94=80=EF=BC=8C=E7=AE=80=E5=8C=96?= =?UTF-8?q?=E7=A2=B0=E6=92=9E=E7=BB=93=E6=9E=9C=E7=9A=84DisplayName?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/Animation/PathAnimationManager.cs | 99 ++++++++++--------- .../Collision/ClashDetectiveIntegration.cs | 60 ++++++++--- 2 files changed, 96 insertions(+), 63 deletions(-) diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs index cca4973..492e4a1 100644 --- a/src/Core/Animation/PathAnimationManager.cs +++ b/src/Core/Animation/PathAnimationManager.cs @@ -483,7 +483,7 @@ namespace NavisworksTransport.Core.Animation _currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform); LogManager.Debug($"[MoveVehicleToPathStart初始化] 当前实际yaw={_currentYaw * 180 / Math.PI:F2}度, _isVirtualVehicle={_isVirtualVehicle}"); } - + if (pathPoints != null) { _pathPoints = pathPoints; @@ -499,7 +499,7 @@ namespace NavisworksTransport.Core.Animation // 计算朝向(使用前两个路径点的方向) double pathDirectionYaw = Math.Atan2(_pathPoints[1].Y - _pathPoints[0].Y, _pathPoints[1].X - _pathPoints[0].X); double yaw; - + LogManager.Info($"[移动到起点] 路径方向yaw: {pathDirectionYaw * 180 / Math.PI:F2}度, 角度修正: {_objectRotationCorrection:F1}度"); // 根据路径类型调整朝向 @@ -564,7 +564,7 @@ namespace NavisworksTransport.Core.Animation } LogManager.Info($"物体已初始化到路径起点并对齐朝向: pos=({startPosition.X:F2},{startPosition.Y:F2},{startPosition.Z:F2}), yaw={yaw:F3}rad, 路径类型={pathTypeName}"); - + // 打印实际物体的位置和方向 if (_animatedObject != null) { @@ -719,10 +719,10 @@ namespace NavisworksTransport.Core.Animation if (!manualOverrideActive) { LogManager.Info("=== 构建全局空间索引 ==="); - + // 🔥 设置移动物体到 ClashDetectiveIntegration,以便从空间索引中排除 ClashDetectiveIntegration.SetAnimatedObject(_animatedObject); - + spatialIndexManager = SpatialIndexManager.Instance; // 使用动画专用的空间索引格子大小配置 - 使用模型单位接口 @@ -731,9 +731,9 @@ namespace NavisworksTransport.Core.Animation LogManager.Info($"[空间索引] 格子大小: {cellSizeInModelUnits:F2}模型单位 (动画配置)"); // 检查空间索引是否需要重新构建(格子大小变化或未初始化) - bool needRebuild = !spatialIndexManager.IsInitialized || + bool needRebuild = !spatialIndexManager.IsInitialized || Math.Abs(spatialIndexManager.CellSize - cellSizeInModelUnits) > 0.0001; - + if (needRebuild) { LogManager.Info("[空间索引] 空间索引未初始化或格子大小已改变,开始重新构建..."); @@ -767,15 +767,15 @@ namespace NavisworksTransport.Core.Animation double yawRadians; if (isAerialPath) - { - // === 空中路径:在路径点之间进行线性插值 === - int segmentIndex = FindSegmentForDistance(targetDistance, segmentLengths); - if (segmentIndex < 0 || segmentIndex >= segmentLengths.Count) - { - string subTypeName = _route.PathType == PathType.Rail ? "空轨" : "吊装"; - LogManager.Warning($"[{subTypeName}路径] 无法找到线段,目标距离:{targetDistance / metersToModelUnits:F2}米"); - continue; - } + { + // === 空中路径:在路径点之间进行线性插值 === + int segmentIndex = FindSegmentForDistance(targetDistance, segmentLengths); + if (segmentIndex < 0 || segmentIndex >= segmentLengths.Count) + { + string subTypeName = _route.PathType == PathType.Rail ? "空轨" : "吊装"; + LogManager.Warning($"[{subTypeName}路径] 无法找到线段,目标距离:{targetDistance / metersToModelUnits:F2}米"); + continue; + } double accumulatedLength = segmentLengths.Take(segmentIndex).Sum(); double segmentProgress = (targetDistance - accumulatedLength) / segmentLengths[segmentIndex]; @@ -846,14 +846,14 @@ namespace NavisworksTransport.Core.Animation } } else - { - // === 地面路径:在边内插值 === - int edgeIndex = FindEdgeForDistance(targetDistance, segmentLengths); - if (edgeIndex < 0 || edgeIndex >= _route.Edges.Count) - { - LogManager.Warning($"无法找到边,目标距离:{targetDistance / metersToModelUnits:F2}米"); - continue; - } + { + // === 地面路径:在边内插值 === + int edgeIndex = FindEdgeForDistance(targetDistance, segmentLengths); + if (edgeIndex < 0 || edgeIndex >= _route.Edges.Count) + { + LogManager.Warning($"无法找到边,目标距离:{targetDistance / metersToModelUnits:F2}米"); + continue; + } var edge = _route.Edges[edgeIndex]; double accumulatedLength = segmentLengths.Take(edgeIndex).Sum(); double edgeProgress = (targetDistance - accumulatedLength) / edge.PhysicalLength; @@ -941,9 +941,9 @@ namespace NavisworksTransport.Core.Animation if (intersects) { - LogManager.Debug($"帧 {i} 检测到碰撞: {_animatedObject.DisplayName} <-> {collider.DisplayName}, 距离: {distance:F4},阈值: {safetyMarginInModelUnits:F4}"); - LogManager.Debug($"移动物体位置: {framePosition.X:F2},{framePosition.Y:F2},{framePosition.Z:F2}"); - LogManager.Debug($"被撞物体位置:{GetObjectPosition(collider).X:F2},{GetObjectPosition(collider).Y:F2},{GetObjectPosition(collider).Z:F2}"); + // LogManager.Debug($"帧 {i} 检测到碰撞: {_animatedObject.DisplayName} <-> {collider.DisplayName}, 距离: {distance:F4},阈值: {safetyMarginInModelUnits:F4}"); + // LogManager.Debug($"移动物体位置: {framePosition.X:F2},{framePosition.Y:F2},{framePosition.Z:F2}"); + // LogManager.Debug($"被撞物体位置:{GetObjectPosition(collider).X:F2},{GetObjectPosition(collider).Y:F2},{GetObjectPosition(collider).Z:F2}"); var collisionResult = new CollisionResult { @@ -990,7 +990,7 @@ namespace NavisworksTransport.Core.Animation LogManager.Info($"包含碰撞的帧: {framesWithCollision}"); LogManager.Info($"总碰撞次数: {totalCollisions}"); LogManager.Info($"记录的碰撞结果总数: {_allCollisionResults.Count} 个"); - + // 🔥 清除移动物体集合 ClashDetectiveIntegration.ClearAnimatedObject(); } @@ -1525,11 +1525,11 @@ namespace NavisworksTransport.Core.Animation if (!_completedCollisionTests.Contains(_currentAnimationHash)) { LogManager.Info($"此动画配置首次完成,开始创建碰撞测试汇总(基于 {_allCollisionResults.Count} 个预计算碰撞记录)..."); - + // 🔥 设置等待光标,避免进度条关闭后的无响应 var oldCursor = System.Windows.Input.Mouse.OverrideCursor; System.Windows.Input.Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; - + try { // 🔥 使用用户设置的检测容差(米) @@ -1552,7 +1552,7 @@ namespace NavisworksTransport.Core.Animation // 恢复光标 System.Windows.Input.Mouse.OverrideCursor = oldCursor; } - + _completedCollisionTests.Add(_currentAnimationHash); // 记录此配置已完成碰撞检测 LogManager.Info($"碰撞测试汇总已创建并记录,此配置后续播放将跳过碰撞检测"); } @@ -1954,7 +1954,7 @@ namespace NavisworksTransport.Core.Animation // 我们需要手动计算旋转导致的位置偏移,并补偿 double deltaYaw = newYaw - _currentYaw; - LogManager.Debug($"[UpdateObjectPosition] 当前yaw={_currentYaw * 180 / Math.PI:F2}度, 目标yaw={newYaw * 180 / Math.PI:F2}度, 旋转增量deltaYaw={deltaYaw * 180 / Math.PI:F2}度"); + //LogManager.Debug($"[UpdateObjectPosition] 当前yaw={_currentYaw * 180 / Math.PI:F2}度, 目标yaw={newYaw * 180 / Math.PI:F2}度, 旋转增量deltaYaw={deltaYaw * 180 / Math.PI:F2}度"); // 计算绕当前位置旋转的等效变换: // 1. 如果绕原点旋转deltaYaw,当前位置_currentPosition会移动到哪里? @@ -1982,17 +1982,18 @@ namespace NavisworksTransport.Core.Animation _currentYaw = newYaw; } else - { - // 纯平移 - incrementalTransform = Transform3D.CreateTranslation(deltaPos); - LogManager.Debug($"[UpdateObjectPosition] 纯平移: ({deltaPos.X:F2},{deltaPos.Y:F2},{deltaPos.Z:F2})"); - } - - // 应用增量变换(false = 增量模式) - doc.Models.OverridePermanentTransform(modelItems, incrementalTransform, false); - - // 更新当前位置 - _currentPosition = newPosition; } + { + // 纯平移 + incrementalTransform = Transform3D.CreateTranslation(deltaPos); + LogManager.Debug($"[UpdateObjectPosition] 纯平移: ({deltaPos.X:F2},{deltaPos.Y:F2},{deltaPos.Z:F2})"); + } + + // 应用增量变换(false = 增量模式) + doc.Models.OverridePermanentTransform(modelItems, incrementalTransform, false); + + // 更新当前位置 + _currentPosition = newPosition; + } catch (Exception ex) { LogManager.Error($"更新部件位置失败: {ex.Message}"); @@ -2601,10 +2602,10 @@ namespace NavisworksTransport.Core.Animation } // 设置动画参数并预计算动画帧 - // 注意:物体应该在调用CreateAnimation之前已经通过MoveVehicleToPathStart旋转到起点 - LogManager.Debug($"[CreateAnimation开始] _currentYaw之前={_currentYaw * 180 / Math.PI:F2}度, _isVirtualVehicle={_isVirtualVehicle}"); - SetupAnimation(animatedObject, durationSeconds, _route); - LogManager.Debug($"[CreateAnimation结束] _currentYaw之后={_currentYaw * 180 / Math.PI:F2}度"); + // 注意:物体应该在调用CreateAnimation之前已经通过MoveVehicleToPathStart旋转到起点 + LogManager.Debug($"[CreateAnimation开始] _currentYaw之前={_currentYaw * 180 / Math.PI:F2}度, _isVirtualVehicle={_isVirtualVehicle}"); + SetupAnimation(animatedObject, durationSeconds, _route); + LogManager.Debug($"[CreateAnimation结束] _currentYaw之后={_currentYaw * 180 / Math.PI:F2}度"); // 设置动画状态为Ready(动画已生成,可以播放) SetState(AnimationState.Ready); LogManager.Info($"[CreateAnimation] 动画已创建,状态设置为Ready"); @@ -2771,7 +2772,7 @@ namespace NavisworksTransport.Core.Animation if (_currentFrameIndex < _animationFrames.Count) { var frameData = _animationFrames[_currentFrameIndex]; - + // 计算实际朝向 = 路径方向 + 角度修正 double actualYaw = frameData.YawRadians; if (_objectRotationCorrection != 0.0) @@ -2779,7 +2780,7 @@ namespace NavisworksTransport.Core.Animation double correctionRad = _objectRotationCorrection * Math.PI / 180.0; actualYaw += correctionRad; } - + UpdateObjectPosition(frameData.Position, actualYaw); // 更新碰撞高亮(基于预计算结果) diff --git a/src/Core/Collision/ClashDetectiveIntegration.cs b/src/Core/Collision/ClashDetectiveIntegration.cs index 459113b..84eb77c 100644 --- a/src/Core/Collision/ClashDetectiveIntegration.cs +++ b/src/Core/Collision/ClashDetectiveIntegration.cs @@ -608,6 +608,9 @@ namespace NavisworksTransport int skippedCount = 0; var doc = Application.ActiveDocument; + // 收集所有临时测试,用于批量删除 + var tempTestsToRemove = new List(); + // 2. 遍历每一组 for (int groupIndex = 0; groupIndex < groupedCollisions.Count; groupIndex++) { @@ -655,6 +658,8 @@ namespace NavisworksTransport doc.Models.OverridePermanentTransform(modelItems, transform, false); var tempTestName = $"临时验证_{confirmedCount + 1}_{i}_{DateTime.Now:HHmmss_fff}"; + + // 🔥 优化3:预配置 ClashTest,避免后续 CreateCopy 和 TestsEditTestFromCopy 调用 var tempTest = new ClashTest { DisplayName = tempTestName, @@ -668,17 +673,19 @@ namespace NavisworksTransport var selectionB = new ModelItemCollection { candidate.Item2 }; tempTest.SelectionA.Selection.CopyFrom(selectionA); tempTest.SelectionB.Selection.CopyFrom(selectionB); + + // 直接设置 PrimitiveTypes,避免后续的 CreateCopy 和 TestsEditTestFromCopy + tempTest.SelectionA.PrimitiveTypes = PrimitiveTypes.Triangles; + tempTest.SelectionB.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points; _documentClash.TestsData.TestsAddCopy(tempTest); var addedTempTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == tempTestName) as ClashTest; if (addedTempTest != null) { - var copyTest = addedTempTest.CreateCopy() as ClashTest; - copyTest.SelectionA.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points; - copyTest.SelectionB.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points; - _documentClash.TestsData.TestsEditTestFromCopy(addedTempTest, copyTest); - + // 🔥 优化3:跳过了 addedTempTest.CreateCopy() 和 TestsEditTestFromCopy 调用 + // 因为 PrimitiveTypes 已在创建 tempTest 时设置好 + _documentClash.TestsData.TestsRunTest(addedTempTest); var refreshedTempTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == tempTestName) as ClashTest; @@ -697,15 +704,10 @@ namespace NavisworksTransport var copiedResult = result.CreateCopy() as ClashResult; copiedResult.Guid = Guid.NewGuid(); // 生成新的GUID - // 设置唯一且有意义的碰撞名称 - // 先找到有意义的容器对象,再获取其名称 - var container1 = ModelItemAnalysisHelper.FindNamedParentContainer(result.Item1); - var container2 = ModelItemAnalysisHelper.FindNamedParentContainer(result.Item2); - var object1Name = ModelItemAnalysisHelper.GetSafeDisplayName(container1); - var object2Name = ModelItemAnalysisHelper.GetSafeDisplayName(container2); - + // 🔥 优化:简化 DisplayName,避免复杂的父容器查找 + // 详细的名称将在去重后统一设置 var timeStamp = DateTime.Now.ToString("HHmmss"); - copiedResult.DisplayName = $"物流碰撞#{confirmedCount:00}-{subResultIndex:00}_{timeStamp}: {object1Name} ↔ {object2Name}"; + copiedResult.DisplayName = $"碰撞_{confirmedCount:00}_{subResultIndex:00}_{timeStamp}"; collisionGroup.Children.Add(copiedResult); subResultIndex++; @@ -713,7 +715,8 @@ namespace NavisworksTransport } } - _documentClash.TestsData.TestsRemove(refreshedTempTest ?? addedTempTest); + // 🔥 优化4:收集测试用于批量删除,而不是立即删除 + tempTestsToRemove.Add(refreshedTempTest ?? addedTempTest); if (pairConfirmed) { @@ -728,6 +731,23 @@ namespace NavisworksTransport } } + // 🔥 优化4:批量删除所有临时测试 + if (tempTestsToRemove.Count > 0) + { + LogManager.Debug($"[分组测试] 批量删除 {tempTestsToRemove.Count} 个临时测试"); + foreach (var test in tempTestsToRemove) + { + try + { + _documentClash.TestsData.TestsRemove(test); + } + catch (Exception ex) + { + LogManager.Debug($"[分组测试] 删除临时测试失败: {ex.Message}"); + } + } + } + LogManager.Info($"[分组测试] ClashDetective检测完成: 确认碰撞 {confirmedCount} 组, 跳过 {skippedCount} 个冗余检测点"); // 第三步:处理碰撞结果 @@ -766,6 +786,18 @@ namespace NavisworksTransport LogManager.Info($"[最终去重] ClashDetective结果去重: {clashResults.Count} 个碰撞 -> {finalClashResults.Count} 个唯一碰撞对"); + // 🔥 优化:在去重后为结果设置详细的 DisplayName + // 这样只对保留的结果进行名称计算,避免在子碰撞结果上浪费性能 + foreach (var result in finalClashResults) + { + if (result.Item1 != null && result.Item2 != null) + { + var object1Name = ModelItemAnalysisHelper.GetSafeDisplayName(result.Item1); + var object2Name = ModelItemAnalysisHelper.GetSafeDisplayName(result.Item2); + result.DisplayName = $"物流碰撞: {object1Name} ↔ {object2Name}"; + } + } + // 缓存最终结果 lock (_clashResultsCacheLock) {