From c7f6586fa9fa50a0a861edde7f1aa1f7e917dba9 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Tue, 14 Oct 2025 11:56:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=9B=86=E6=88=90=E7=A9=BA=E9=97=B4?= =?UTF-8?q?=E7=B4=A2=E5=BC=95=E5=88=B0=E7=A2=B0=E6=92=9E=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=EF=BC=88=E5=8D=95=E5=B1=82=E6=9E=B6=E6=9E=84?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构说明: - 移除两层架构的 GetPotentialColliders() 方法 - 移除不再使用的 CalculatePathBounds() 方法 - 在 PrecomputeAnimationFrames() 中构建全局空间索引 - 每帧使用空间索引直接查询附近对象(O(1) 复杂度) 技术细节: - 网格大小:基于动画对象包围盒尺寸 - 搜索半径:对象对角线的一半 + 检测间隙 - 自动排除动画对象本身 性能优势: - 旧架构:290 对象 → 108 对象(预过滤)→ 每帧遍历 108 对象 - 新架构:290 对象 → 每帧空间查询 ~5-15 对象 - 预期性能提升:10-15x --- src/Core/Animation/PathAnimationManager.cs | 162 +++++---------------- 1 file changed, 36 insertions(+), 126 deletions(-) diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs index a19f614..b18849e 100644 --- a/src/Core/Animation/PathAnimationManager.cs +++ b/src/Core/Animation/PathAnimationManager.cs @@ -377,17 +377,36 @@ namespace NavisworksTransport.Core.Animation originalBoundingBox.Max.Y - originalBoundingBox.Min.Y, originalBoundingBox.Max.Z - originalBoundingBox.Min.Z ); - - // 获取场景中所有可能碰撞的对象 - var potentialColliders = GetPotentialColliders(); - LogManager.Info($"潜在碰撞对象数: {potentialColliders.Count}"); - + + // 【新架构】构建全局空间索引(单层架构) + LogManager.Info("=== 构建全局空间索引 ==="); + var spatialIndexManager = SpatialIndexManager.Instance; + + // 使用包围盒对角线作为网格大小的参考(确保合理的网格粒度) + double cellSizeInModelUnits = Math.Max( + boundingBoxSize.X, + Math.Max(boundingBoxSize.Y, boundingBoxSize.Z) + ); + + spatialIndexManager.BuildGlobalIndex(cellSizeInModelUnits); + LogManager.Info(spatialIndexManager.GetStatistics()); + + // 计算搜索半径:动画对象包围盒的对角线 + 检测间隙 + double objectDiagonal = Math.Sqrt( + boundingBoxSize.X * boundingBoxSize.X + + boundingBoxSize.Y * boundingBoxSize.Y + + boundingBoxSize.Z * boundingBoxSize.Z + ); + double searchRadiusInModelUnits = objectDiagonal / 2 + _detectionGap; + + LogManager.Info($"空间查询半径: {searchRadiusInModelUnits:F2} 模型单位"); + // 预计算每一帧 for (int i = 0; i < totalFrames; i++) { double progress = (double)i / totalFrames; var framePosition = InterpolatePosition(progress); - + // 创建帧数据 var frame = new AnimationFrame { @@ -396,12 +415,19 @@ namespace NavisworksTransport.Core.Animation Position = framePosition, Collisions = new List() }; - + // 虚拟碰撞检测(不移动实际物体) var virtualBoundingBox = CreateVirtualBoundingBox(framePosition, boundingBoxSize); - - // 检测与所有潜在对象的碰撞 - foreach (var collider in potentialColliders) + + // 【新架构】使用空间索引查询附近对象 + var nearbyObjects = spatialIndexManager.FindNearbyObjects( + framePosition, + searchRadiusInModelUnits, + excludeObject: _animatedObject + ); + + // 检测与附近对象的碰撞 + foreach (var collider in nearbyObjects) { var colliderBox = collider.BoundingBox(); @@ -455,122 +481,6 @@ namespace NavisworksTransport.Core.Animation } } - /// - /// 获取潜在碰撞对象列表 - /// - private List GetPotentialColliders() - { - try - { - LogManager.Info("开始获取潜在碰撞对象"); - - // 构建通道缓存 - ClashDetectiveIntegration.Instance.BuildChannelObjectsCache(); - ClashDetectiveIntegration.BuildAllGeometryItemsCache(); - - // 获取所有有几何体的对象 - var allItems = NavisApplication.ActiveDocument.Models.RootItemDescendantsAndSelf - .Where(item => item.HasGeometry) - .ToList(); - - // 构建排除列表:动画对象本身及其子对象 - var excludeList = new ModelItemCollection(); - excludeList.AddRange(_animatedObject.DescendantsAndSelf); - - // 计算路径包围盒 - 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 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 - { - LogManager.Info($"检测半径: {detectionGap:F2} 模型单位(对角线的一半)"); - - // 调用ClashDetectiveIntegration的DetectCollisions方法 - // 现在物体在路径中心,检测路径长度半径内的对象 - // 该方法内部会自动排除通道对象(通过IsChannelObject方法) - var collisionResults = ClashDetectiveIntegration.Instance.DetectCollisions( - _animatedObject, - excludeList, - detectionGap - ); - - // 从碰撞结果中提取唯一的碰撞对象 - var potentialColliders = new HashSet(); - foreach (var result in collisionResults) - { - 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("物体已恢复到原始位置"); - } - } - catch (Exception ex) - { - LogManager.Error($"获取潜在碰撞对象失败: {ex.Message}"); - return new List(); - } - } - - /// - /// 计算路径的包围盒 - /// - private BoundingBox3D CalculatePathBounds() - { - if (_pathPoints == null || _pathPoints.Count == 0) - return new BoundingBox3D(); - - var minX = _pathPoints.Min(p => p.X); - var minY = _pathPoints.Min(p => p.Y); - var minZ = _pathPoints.Min(p => p.Z); - var maxX = _pathPoints.Max(p => p.X); - var maxY = _pathPoints.Max(p => p.Y); - var maxZ = _pathPoints.Max(p => p.Z); - - return new BoundingBox3D( - new Point3D(minX, minY, minZ), - new Point3D(maxX, maxY, maxZ) - ); - } - /// /// 创建虚拟包围盒(用于碰撞检测) ///