From cbc63809f09213769ec28586285ac3eac3ac75dc Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Wed, 7 Jan 2026 12:29:19 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E8=BD=A6=E8=BE=86=E6=BC=8F?= =?UTF-8?q?=E6=A3=80=E5=92=8C=E5=A4=9A=E6=A3=80=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/requirement/todo_features.md | 5 + src/Core/Animation/PathAnimationManager.cs | 133 ++++++++------------- src/Core/Spatial/SpatialHashGrid.cs | 8 +- src/Utils/BoundingBoxGeometryUtils.cs | 66 +++++++++- 4 files changed, 119 insertions(+), 93 deletions(-) diff --git a/doc/requirement/todo_features.md b/doc/requirement/todo_features.md index e9a7b9d..1a8822b 100644 --- a/doc/requirement/todo_features.md +++ b/doc/requirement/todo_features.md @@ -2,6 +2,11 @@ ## 功能点 +### [2026/1/6] + +1. [ ] (BUG)虚拟车辆模型每次点击都重建 +2. [ ] (BUG)碰撞结果高亮,应该显示精确检测的结果 + ### [2025/12/25] 1. [x] (功能)对路径上的各点进行坐标编辑 diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs index 8fd6585..04324d5 100644 --- a/src/Core/Animation/PathAnimationManager.cs +++ b/src/Core/Animation/PathAnimationManager.cs @@ -498,14 +498,12 @@ namespace NavisworksTransport.Core.Animation LogManager.Info(spatialIndexManager.GetStatistics()); - double objectDiagonal = Math.Sqrt( - boundingBoxSize.X * boundingBoxSize.X + - boundingBoxSize.Y * boundingBoxSize.Y + - boundingBoxSize.Z * boundingBoxSize.Z - ); + // 🔥 修改:使用车辆的最大水平尺寸而不是对角线 + // 原因:车辆在通道上水平移动,主要检测侧面碰撞 + double maxHorizontalDimension = Math.Max(boundingBoxSize.X, boundingBoxSize.Y); - searchRadiusInModelUnits = objectDiagonal + _detectionGap; - LogManager.Info($"空间查询半径: {searchRadiusInModelUnits:F2} 模型单位"); + searchRadiusInModelUnits = maxHorizontalDimension + _detectionGap; + LogManager.Info($"空间查询半径: {searchRadiusInModelUnits:F2} 模型单位 (基于最大水平尺寸: {maxHorizontalDimension:F2})"); } // 3. 生成每一帧 @@ -541,6 +539,9 @@ namespace NavisworksTransport.Core.Animation double yawRadians = ComputeYawOnPath(i, allSampledPoints, edgeIndex, edgeProgress); + // 🔥 调试日志:输出framePosition + LogManager.Debug($"[调试] 帧{i}: framePosition=({framePosition.X:F2},{framePosition.Y:F2},{framePosition.Z:F2})"); + // 创建帧并检测碰撞 var frame = new AnimationFrame { @@ -552,7 +553,33 @@ namespace NavisworksTransport.Core.Animation }; // 虚拟碰撞检测 - var virtualBoundingBox = CreateVirtualBoundingBox(framePosition, boundingBoxSize, yawRadians); + + // 计算车辆包围盒相对于framePosition的偏移 + var currentVehicleBoundingBox = _animatedObject.BoundingBox(); + var originalCenter = new Point3D( + (currentVehicleBoundingBox.Min.X + currentVehicleBoundingBox.Max.X) / 2, + (currentVehicleBoundingBox.Min.Y + currentVehicleBoundingBox.Max.Y) / 2, + (currentVehicleBoundingBox.Min.Z + currentVehicleBoundingBox.Max.Z) / 2 + ); + + // 计算偏移量(当前包围盒中心到framePosition的偏移) + var offsetX = framePosition.X - originalCenter.X; + var offsetY = framePosition.Y - originalCenter.Y; + var offsetZ = framePosition.Z - currentVehicleBoundingBox.Min.Z; // Z方向:framePosition是底面,originalCenter是中心 + + // 创建新的包围盒(将当前包围盒移动到framePosition) + var virtualBoundingBox = new BoundingBox3D( + new Point3D( + currentVehicleBoundingBox.Min.X + offsetX, + currentVehicleBoundingBox.Min.Y + offsetY, + framePosition.Z // 底面Z坐标 + ), + new Point3D( + currentVehicleBoundingBox.Max.X + offsetX, + currentVehicleBoundingBox.Max.Y + offsetY, + framePosition.Z + (currentVehicleBoundingBox.Max.Z - currentVehicleBoundingBox.Min.Z) // 保持高度 + ) + ); IEnumerable nearbyObjects; if (manualOverrideActive) @@ -572,7 +599,19 @@ namespace NavisworksTransport.Core.Animation { var colliderBox = collider.BoundingBox(); - if (BoundingBoxGeometryUtils.BoundingBoxesIntersectWithTolerance(virtualBoundingBox, colliderBox, _detectionGap)) + // 🔥 调试日志:输出包围盒信息 + bool intersects = BoundingBoxGeometryUtils.BoundingBoxesIntersectWithTolerance(virtualBoundingBox, colliderBox, _detectionGap); + if (intersects) + { + double distanceX = Math.Max(0, Math.Max(virtualBoundingBox.Min.X - colliderBox.Max.X, colliderBox.Min.X - virtualBoundingBox.Max.X)); + double distanceY = Math.Max(0, Math.Max(virtualBoundingBox.Min.Y - colliderBox.Max.Y, colliderBox.Min.Y - virtualBoundingBox.Max.Y)); + double distanceZ = Math.Max(0, Math.Max(virtualBoundingBox.Min.Z - colliderBox.Max.Z, colliderBox.Min.Z - virtualBoundingBox.Max.Z)); + double minDistance = Math.Max(distanceX, Math.Max(distanceY, distanceZ)); + + LogManager.Debug($"[碰撞检测] 帧{i}: 车辆包围盒=[({virtualBoundingBox.Min.X:F2},{virtualBoundingBox.Min.Y:F2},{virtualBoundingBox.Min.Z:F2})-({virtualBoundingBox.Max.X:F2},{virtualBoundingBox.Max.Y:F2},{virtualBoundingBox.Max.Z:F2})], 物体={collider.DisplayName}, 包围盒=[({colliderBox.Min.X:F2},{colliderBox.Min.Y:F2},{colliderBox.Min.Z:F2})-({colliderBox.Max.X:F2},{colliderBox.Max.Y:F2},{colliderBox.Max.Z:F2})], 最小距离={minDistance:F3}m, 检测间隙={_detectionGap:F3}m"); + } + + if (intersects) { var collisionResult = new CollisionResult { @@ -801,81 +840,6 @@ namespace NavisworksTransport.Core.Animation return new Vector3D(cross.X, cross.Y, cross.Z).Normalize(); } - /// - /// 创建虚拟包围盒(用于碰撞检测),考虑旋转 - /// - private BoundingBox3D CreateVirtualBoundingBox(Point3D position, Vector3D size, double yawRadians = 0.0) - { - // 如果没有旋转,返回简单的轴对齐包围盒 - if (Math.Abs(yawRadians) < 1e-6) - { - return new BoundingBox3D( - new Point3D( - position.X - size.X / 2, - position.Y - size.Y / 2, - position.Z - ), - new Point3D( - position.X + size.X / 2, - position.Y + size.Y / 2, - position.Z + size.Z - ) - ); - } - - // 有旋转:计算旋转后的轴对齐包围盒 - // 1. 定义物体包围盒的4个底面角点(相对于中心) - double halfX = size.X / 2; - double halfY = size.Y / 2; - - var corners = new[] - { - new { x = -halfX, y = -halfY }, - new { x = halfX, y = -halfY }, - new { x = halfX, y = halfY }, - new { x = -halfX, y = halfY } - }; - - // 2. 旋转这些角点 - double cos = Math.Cos(yawRadians); - double sin = Math.Sin(yawRadians); - - double minX = double.MaxValue; - double maxX = double.MinValue; - double minY = double.MaxValue; - double maxY = double.MinValue; - - foreach (var corner in corners) - { - // 旋转公式:x' = x*cos - y*sin, y' = x*sin + y*cos - double rotatedX = corner.x * cos - corner.y * sin; - double rotatedY = corner.x * sin + corner.y * cos; - - minX = Math.Min(minX, rotatedX); - maxX = Math.Max(maxX, rotatedX); - minY = Math.Min(minY, rotatedY); - maxY = Math.Max(maxY, rotatedY); - } - - // 3. 创建包含所有旋转后角点的轴对齐包围盒 - return new BoundingBox3D( - new Point3D( - position.X + minX, - position.Y + minY, - position.Z - ), - new Point3D( - position.X + maxX, - position.Y + maxY, - position.Z + size.Z - ) - ); - } - - - - - /// /// 开始播放动画 /// @@ -888,7 +852,6 @@ namespace NavisworksTransport.Core.Animation throw new InvalidOperationException("请先调用SetupAnimation设置动画参数"); } - // 创建 TimeLiner 任务(如果可用且未创建过) if (_timeLinerManager != null && _timeLinerManager.IsTimeLinerAvailable) { diff --git a/src/Core/Spatial/SpatialHashGrid.cs b/src/Core/Spatial/SpatialHashGrid.cs index e3090a7..40c83ba 100644 --- a/src/Core/Spatial/SpatialHashGrid.cs +++ b/src/Core/Spatial/SpatialHashGrid.cs @@ -214,11 +214,9 @@ namespace NavisworksTransport.Core.Spatial if (getPositionFunc == null) throw new ArgumentNullException(nameof(getPositionFunc)); - return FindInRadius(center, radius, obj => - { - var objPos = getPositionFunc(obj); - return center.Distance(objPos); - }); + // 🔥 修改:不做距离过滤,只返回格子范围内的所有对象 + // 原因:使用中心点距离过滤会漏检包围盒很大的对象(如墙) + return FindInRadius(center, radius, distanceFunc: null); } /// diff --git a/src/Utils/BoundingBoxGeometryUtils.cs b/src/Utils/BoundingBoxGeometryUtils.cs index 5584281..5a4bf9d 100644 --- a/src/Utils/BoundingBoxGeometryUtils.cs +++ b/src/Utils/BoundingBoxGeometryUtils.cs @@ -99,9 +99,69 @@ namespace NavisworksTransport.Utils /// 如果包围盒在容差范围内相交则返回true public static 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; + // 🔥 修改:计算每个方向的最小距离 + // 只有当所有方向的最小距离都小于检测间隙时,才判定为碰撞 + // 这样可以避免"一个方向相交,另一个方向很远"的情况 + + // 计算X方向的最小距离 + double distanceX; + if (box1.Max.X < box2.Min.X) + { + // box1在box2左侧 + distanceX = box2.Min.X - box1.Max.X; + } + else if (box2.Max.X < box1.Min.X) + { + // box2在box1左侧 + distanceX = box1.Min.X - box2.Max.X; + } + else + { + // X方向相交 + distanceX = 0; + } + + // 计算Y方向的最小距离 + double distanceY; + if (box1.Max.Y < box2.Min.Y) + { + // box1在box2下方 + distanceY = box2.Min.Y - box1.Max.Y; + } + else if (box2.Max.Y < box1.Min.Y) + { + // box2在box1下方 + distanceY = box1.Min.Y - box2.Max.Y; + } + else + { + // Y方向相交 + distanceY = 0; + } + + // 计算Z方向的最小距离 + double distanceZ; + if (box1.Max.Z < box2.Min.Z) + { + // box1在box2下方 + distanceZ = box2.Min.Z - box1.Max.Z; + } + else if (box2.Max.Z < box1.Min.Z) + { + // box2在box1下方 + distanceZ = box1.Min.Z - box2.Max.Z; + } + else + { + // Z方向相交 + distanceZ = 0; + } + + // 计算最大距离(最接近的方向) + double maxDistance = Math.Max(distanceX, Math.Max(distanceY, distanceZ)); + + // 只有当最大距离小于检测间隙时,才判定为碰撞 + return maxDistance <= tolerance; } ///