diff --git a/AGENTS.md b/AGENTS.md index 8851250..6eb1690 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -296,6 +296,16 @@ var rotation = new Rotation3D(qw, qx, qy, qz); // 错误 - 走平面姿态链 - 已禁止偷偷退回旧 `yaw` 方案 - 起点、逐帧、终点、通行空间,必须共享同一套尺寸语义 +- `Ground + 真实物体` 的业务跟踪点固定为**原始包围盒中心** +- 旋转后的实时 `BoundingBox.Center` 只允许用于: + - 诊断旋转后漂移 + - 计算当帧补偿 + - 校验补偿结果 +- 不允许把实时 `BoundingBox.Center` 直接当成: + - 起点目标跟踪点 + - 逐帧业务跟踪点 + - 碰撞记录主语义跟踪点 +- 如果 `Ground` 出现“起点正确但越走越偏”或“起点偏了但拐弯后偏差反而减小”,优先检查是否把实时包围盒中心误当成了业务跟踪点 #### Rail diff --git a/UnitTests/CoordinateSystem/RailPreservedPoseTests.cs b/UnitTests/CoordinateSystem/RailPreservedPoseTests.cs index 1e8326a..f416c13 100644 --- a/UnitTests/CoordinateSystem/RailPreservedPoseTests.cs +++ b/UnitTests/CoordinateSystem/RailPreservedPoseTests.cs @@ -149,6 +149,23 @@ namespace NavisworksTransport.UnitTests.CoordinateSystem Assert.IsFalse(PathAnimationManager.ShouldUseRealObjectOverrideRotation(false)); } + [TestMethod] + public void ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition_ShouldEnableOnlyForGroundRealObjects() + { + Assert.IsTrue(PathAnimationManager.ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition( + PathType.Ground, + isRealObjectMode: true)); + Assert.IsFalse(PathAnimationManager.ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition( + PathType.Ground, + isRealObjectMode: false)); + Assert.IsFalse(PathAnimationManager.ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition( + PathType.Hoisting, + isRealObjectMode: true)); + Assert.IsFalse(PathAnimationManager.ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition( + PathType.Rail, + isRealObjectMode: true)); + } + [TestMethod] public void ResolveHoistingPoseSourceLabel_ShouldDescribeCachedBasePose() { diff --git a/doc/working/current-engineering-state.md b/doc/working/current-engineering-state.md index 7869750..48f90f7 100644 --- a/doc/working/current-engineering-state.md +++ b/doc/working/current-engineering-state.md @@ -111,8 +111,19 @@ - `AnimatedObjectTrackedPosition` 的唯一语义: - 当前动画主链路使用的跟踪点位置。 -- 当前主链路已经切到: - - 几何中心跟踪。 +- `Ground + 真实物体` 当前必须明确区分两类“中心”: + - 业务跟踪点应固定为物体**原始包围盒中心** + - 不是旋转后的实时包围盒中心 +- 实时包围盒中心的用途仅限于: + - 诊断旋转后漂移 + - 计算当帧补偿 + - 验证补偿结果 +- 不允许把实时包围盒中心直接回写成 `Ground` 路径的业务跟踪点定义。 +- 这条规则的直接影响范围包括: + - 起点落位 + - 逐帧补偿 + - 动画日志 + - 碰撞与结果记录语义 - 如果后续动画跟踪点再变,数据库和碰撞结果语义必须同步更新。 ### 4.4 虚拟物体资源 diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs index 61754d8..8cc6b3a 100644 --- a/src/Core/Animation/PathAnimationManager.cs +++ b/src/Core/Animation/PathAnimationManager.cs @@ -186,7 +186,7 @@ namespace NavisworksTransport.Core.Animation private Transform3D _originalTransform; private Point3D _originalCenter; // 存储部件的原始中心位置 - private Point3D _trackedPosition; // 动画管理器内部跟踪的位置状态,不等于宿主即时读回位置 + private Point3D _trackedPosition; // 动画管理器内部业务跟踪点,不等于宿主即时读回的实时包围盒中心 private AnimationState _currentState = AnimationState.Idle; private double _pausedProgress = 0.0; // 暂停时的进度(0-1之间) private double _currentYaw = 0.0; // 当前偏航角(弧度) @@ -484,7 +484,7 @@ namespace NavisworksTransport.Core.Animation } var originalBoundingBox = objectToRestore.BoundingBox(); - _trackedPosition = GetTrackedObjectPosition(objectToRestore); + _trackedPosition = ResolveInitialBusinessTrackedPosition(objectToRestore); string objectName = isVirtual ? "虚拟物体" : objectToRestore.DisplayName; _groundRealObjectStartCompensation = new Vector3D(0, 0, 0); @@ -525,7 +525,7 @@ namespace NavisworksTransport.Core.Animation { _originalTransform = animatedObject.Transform; _originalCenter = animatedObject.BoundingBox().Center; - _trackedPosition = GetTrackedObjectPosition(animatedObject); + _trackedPosition = ResolveInitialBusinessTrackedPosition(animatedObject); SyncTrackedRotationToObjectReference(animatedObject, isVirtualObject: false); } } @@ -689,7 +689,7 @@ namespace NavisworksTransport.Core.Animation ResetRealObjectReferenceRotation(); _originalTransform = animatedObject.Transform; _originalCenter = animatedObject.BoundingBox().Center; - _trackedPosition = GetTrackedObjectPosition(animatedObject); + _trackedPosition = ResolveInitialBusinessTrackedPosition(animatedObject); if (isVirtualObject) { _currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform); @@ -808,12 +808,12 @@ namespace NavisworksTransport.Core.Animation UpdateObjectPosition(startPosition, planarRotation); LogManager.Info("[移动到起点] 地面/吊装路径已应用完整三维姿态"); - var startAppliedPoint = GetTrackedObjectPosition(CurrentControlledObject ?? _animatedObject); + var startAppliedPoint = GetLiveBoundingBoxCenter(CurrentControlledObject ?? _animatedObject); LogManager.Info( - $"[路径起点诊断] 起点应用后实际trackedPoint=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), " + + $"[路径起点诊断] 起点应用后实际包围盒中心=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), " + $"相对目标偏差=({startAppliedPoint.X - startPosition.X:F3},{startAppliedPoint.Y - startPosition.Y:F3},{startAppliedPoint.Z - startPosition.Z:F3})"); - // 🔥 起点不再进行二次补偿,旋转偏移由 MoveItemIncrementallyToTrackedPositionAndRotation 内部处理 + // Ground 起点不再进行二次补偿;业务跟踪点语义由 _trackedPosition 主链负责 if (IsRealObjectMode && _route.PathType == PathType.Ground) { var startOffset = new Vector3D( @@ -875,9 +875,9 @@ namespace NavisworksTransport.Core.Animation // 使用 UpdateObjectPosition 统一处理移动和旋转 UpdateObjectPosition(startPosition, yaw); - var startAppliedPoint = GetTrackedObjectPosition(CurrentControlledObject ?? _animatedObject); + var startAppliedPoint = GetLiveBoundingBoxCenter(CurrentControlledObject ?? _animatedObject); LogManager.Info( - $"[路径起点诊断] 起点应用后实际trackedPoint=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), " + + $"[路径起点诊断] 起点应用后实际包围盒中心=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), " + $"相对目标偏差=({startAppliedPoint.X - startPosition.X:F3},{startAppliedPoint.Y - startPosition.Y:F3},{startAppliedPoint.Z - startPosition.Z:F3})"); } @@ -922,7 +922,7 @@ namespace NavisworksTransport.Core.Animation ResetRealObjectReferenceRotation(); _originalTransform = animatedObject.Transform; _originalCenter = animatedObject.BoundingBox().Center; - _trackedPosition = GetTrackedObjectPosition(animatedObject); + _trackedPosition = ResolveInitialBusinessTrackedPosition(animatedObject); if (isVirtualObject) { _currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform); @@ -989,11 +989,11 @@ namespace NavisworksTransport.Core.Animation _groundRealObjectStartCompensation = new Vector3D(0, 0, 0); _hasGroundRealObjectStartCompensation = false; - var startAppliedPoint = GetTrackedObjectPosition(CurrentControlledObject ?? _animatedObject); + var startAppliedPoint = GetLiveBoundingBoxCenter(CurrentControlledObject ?? _animatedObject); LogManager.Info( $"[平移到起点] 已按终点原始位姿残差搬运到起点: 路径point0=({pathStartPoint.X:F3},{pathStartPoint.Y:F3},{pathStartPoint.Z:F3}), " + $"目标trackedPoint=({startPosition.X:F3},{startPosition.Y:F3},{startPosition.Z:F3}), " + - $"实际trackedPoint=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), 路径类型={_route.PathType.GetDisplayName()}"); + $"实际包围盒中心=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), 路径类型={_route.PathType.GetDisplayName()}"); return true; } catch (Exception ex) @@ -2786,8 +2786,11 @@ namespace NavisworksTransport.Core.Animation try { - var actualHostPosition = GetTrackedObjectPosition(controlledObject); - currentPositionForTransform = actualHostPosition; + var actualHostPosition = GetLiveBoundingBoxCenter(controlledObject); + if (!(IsRealObjectMode && _route?.PathType == PathType.Ground)) + { + currentPositionForTransform = actualHostPosition; + } LogManager.Info( $"[动画姿态入口] {controlledObject.DisplayName} 宿主即时读回点=({actualHostPosition.X:F3},{actualHostPosition.Y:F3},{actualHostPosition.Z:F3})"); @@ -2830,26 +2833,12 @@ namespace NavisworksTransport.Core.Animation $"Y=({targetLinear.Get(0, 1):F4},{targetLinear.Get(1, 1):F4},{targetLinear.Get(2, 1):F4}), " + $"Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})"); - // 🔥 Ground 路径使用带旋转偏移补偿的新方法 - // Rail/Hoisting 保持原行为 - if (_route?.PathType == PathType.Ground && IsRealObjectMode) - { - ModelItemTransformHelper.MoveItemIncrementallyToTrackedPositionAndRotation( - controlledObject, - currentPositionForTransform, - currentRotation, - appliedTargetPosition, - appliedTargetRotation); - } - else - { - ModelItemTransformHelper.MoveItemIncrementallyToPositionAndRotation( - controlledObject, - currentPositionForTransform, - currentRotation, - appliedTargetPosition, - appliedTargetRotation); - } + ModelItemTransformHelper.MoveItemIncrementallyToPositionAndRotation( + controlledObject, + currentPositionForTransform, + currentRotation, + appliedTargetPosition, + appliedTargetRotation); _trackedPosition = appliedTargetPosition; _trackedRotation = appliedTargetRotation; @@ -2858,7 +2847,7 @@ namespace NavisworksTransport.Core.Animation if (isRailRealObject) { - Point3D hostActualAfter = GetTrackedObjectPosition(controlledObject); + Point3D hostActualAfter = GetLiveBoundingBoxCenter(controlledObject); LogManager.Debug( $"[Rail姿态应用] 宿主最终跟踪中心=({hostActualAfter.X:F3},{hostActualAfter.Y:F3},{hostActualAfter.Z:F3}), " + $"目标跟踪中心=({newPosition.X:F3},{newPosition.Y:F3},{newPosition.Z:F3}), " + @@ -3797,7 +3786,7 @@ namespace NavisworksTransport.Core.Animation { _originalTransform = animatedObject.Transform; _originalCenter = animatedObject.BoundingBox().Center; - _trackedPosition = GetTrackedObjectPosition(animatedObject); + _trackedPosition = ResolveInitialBusinessTrackedPosition(animatedObject); // 保持当前的 _currentYaw(因为物体可能已经被 MoveObjectToPathStart 旋转) // 不要从 Transform 中提取,因为 Transform 返回的是原始值,不是当前值 @@ -3814,7 +3803,7 @@ namespace NavisworksTransport.Core.Animation LogManager.Info($"[CreateAnimation] 动画已创建,状态设置为Ready"); } - private Point3D GetTrackedObjectPosition(ModelItem item) + private Point3D GetLiveBoundingBoxCenter(ModelItem item) { if (item == null) { @@ -3825,6 +3814,24 @@ namespace NavisworksTransport.Core.Animation return bounds?.Center ?? new Point3D(0, 0, 0); } + private Point3D ResolveInitialBusinessTrackedPosition(ModelItem item) + { + if (item == null) + { + return new Point3D(0, 0, 0); + } + + if (ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition( + _route?.PathType ?? PathType.Ground, + IsRealObjectMode) && + _originalCenter != null) + { + return _originalCenter; + } + + return GetLiveBoundingBoxCenter(item); + } + private Point3D ResolveGroundTrackedCenter(Point3D groundContactPoint, double objectHeight) { var adapter = CoordinateSystemManager.Instance.CreateHostAdapter(); @@ -3892,7 +3899,7 @@ namespace NavisworksTransport.Core.Animation return; } - Point3D currentTrackedCenter = GetTrackedObjectPosition(CurrentControlledObject ?? _animatedObject); + Point3D currentTrackedCenter = GetLiveBoundingBoxCenter(CurrentControlledObject ?? _animatedObject); Point3D semanticTrackedCenter = RailPathPoseHelper.ResolveObjectSpaceCenterPosition( _route, referencePoint, @@ -3979,6 +3986,13 @@ namespace NavisworksTransport.Core.Animation return isRealObjectMode; } + internal static bool ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition( + PathType pathType, + bool isRealObjectMode) + { + return isRealObjectMode && pathType == PathType.Ground; + } + private void SyncTrackedRotationToDisplayedPose(ModelItem sourceObject) { if (sourceObject == null) @@ -5584,14 +5598,14 @@ namespace NavisworksTransport.Core.Animation } /// - /// 专门用于Ground路径动画帧播放。使用 _trackedPosition 作为当前位置,不重新读取包围盒。 + /// 专门用于Ground路径动画帧播放。使用业务跟踪点 _trackedPosition 作为当前位置,不读取实时包围盒中心回写主链。 /// private void ApplyGroundAnimationFrame(Point3D targetPosition, Rotation3D targetRotation) { var controlledObject = CurrentControlledObject; if (controlledObject == null) return; - // 使用 _trackedPosition 作为当前位置(几何中心),不重新读取包围盒 + // 使用业务跟踪点 _trackedPosition 作为当前位置,不把实时包围盒中心混入主链 Point3D currentPosition = _trackedPosition; Rotation3D currentRotation = _hasTrackedRotation ? _trackedRotation : Rotation3D.Identity; diff --git a/src/Utils/ModelItemTransformHelper.cs b/src/Utils/ModelItemTransformHelper.cs index dd7f7b6..52f858c 100644 --- a/src/Utils/ModelItemTransformHelper.cs +++ b/src/Utils/ModelItemTransformHelper.cs @@ -591,122 +591,6 @@ namespace NavisworksTransport.Utils LogIncrementalTransformActual(item, targetPosition, targetRotation); } - public static void MoveItemIncrementallyToTrackedPositionAndRotation( - ModelItem item, - Point3D currentTrackedPosition, - Rotation3D currentRotation, - Point3D targetTrackedPosition, - Rotation3D targetRotation) - { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } - - var doc = Application.ActiveDocument; - var modelItems = new ModelItemCollection { item }; - - Rotation3D deltaRotation = BuildDeltaRotation(currentRotation, targetRotation); - var currentLinear = new Transform3D(currentRotation).Linear; - var targetLinear = new Transform3D(targetRotation).Linear; - - LogManager.Info( - $"[模型增量补偿] {item.DisplayName} 当前trackedPoint=({currentTrackedPosition.X:F3},{currentTrackedPosition.Y:F3},{currentTrackedPosition.Z:F3}), " + - $"目标trackedPoint=({targetTrackedPosition.X:F3},{targetTrackedPosition.Y:F3},{targetTrackedPosition.Z:F3})"); - // LogManager.Debug( - // $"[模型增量补偿] {item.DisplayName} 当前旋转: " + - // $"X=({currentLinear.Get(0, 0):F4},{currentLinear.Get(1, 0):F4},{currentLinear.Get(2, 0):F4}), " + - // $"Y=({currentLinear.Get(0, 1):F4},{currentLinear.Get(1, 1):F4},{currentLinear.Get(2, 1):F4}), " + - // $"Z=({currentLinear.Get(0, 2):F4},{currentLinear.Get(1, 2):F4},{currentLinear.Get(2, 2):F4})"); - // LogManager.Debug( - // $"[模型增量补偿] {item.DisplayName} 目标旋转: " + - // $"X=({targetLinear.Get(0, 0):F4},{targetLinear.Get(1, 0):F4},{targetLinear.Get(2, 0):F4}), " + - // $"Y=({targetLinear.Get(0, 1):F4},{targetLinear.Get(1, 1):F4},{targetLinear.Get(2, 1):F4}), " + - // $"Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})"); - - // LogGeometryLevelTransforms(item, "[模型增量补偿应用前][GeometryAPI]"); - - // 第1步:将物体中心移到原点(此时几何中心在原点) - var toOrigin = Transform3D.CreateTranslation(new Vector3D( - -currentTrackedPosition.X, - -currentTrackedPosition.Y, - -currentTrackedPosition.Z)); - doc.Models.OverridePermanentTransform(modelItems, toOrigin, false); - - // 第2步:在原点旋转 - var rotationOnlyComponents = Transform3D.CreateTranslation(new Vector3D(0, 0, 0)).Factor(); - rotationOnlyComponents.Rotation = deltaRotation; - var rotationOnly = rotationOnlyComponents.Combine(); - doc.Models.OverridePermanentTransform(modelItems, rotationOnly, false); - - // 🔥 关键:读取旋转后、平移前的包围盒中心(相对于几何中心的偏移) - Point3D centerAfterRotation = item.BoundingBox()?.Center ?? new Point3D(0, 0, 0); - - // 这就是目标旋转下,BoundingBox中心相对于几何中心的偏移 - var rotationOffset = new Vector3D( - centerAfterRotation.X, - centerAfterRotation.Y, - centerAfterRotation.Z); - LogManager.Info( - $"[模型增量补偿] {item.DisplayName} 旋转后包围盒中心=({centerAfterRotation.X:F3},{centerAfterRotation.Y:F3},{centerAfterRotation.Z:F3}), " + - $"旋转偏移量=({rotationOffset.X:F3},{rotationOffset.Y:F3},{rotationOffset.Z:F3})"); - - // 🔥 关键修正:我们要几何中心对齐目标,而不是 BoundingBox 中心对齐目标 - // rotationOffset = 旋转后 BoundingBox 中心相对于原点的位置 - // 几何中心在原点,相对于 BoundingBox 中心的偏移 = -rotationOffset - // 所以 BoundingBox 中心应该位于 target + rotationOffset,才能让几何中心对齐 target - var adjustedTargetPosition = new Point3D( - targetTrackedPosition.X + rotationOffset.X, - targetTrackedPosition.Y + rotationOffset.Y, - targetTrackedPosition.Z + rotationOffset.Z); - LogManager.Info( - $"[模型增量补偿] {item.DisplayName} 调整后目标位置=({adjustedTargetPosition.X:F3},{adjustedTargetPosition.Y:F3},{adjustedTargetPosition.Z:F3}), " + - $"补偿量=(+{rotationOffset.X:F3},+{rotationOffset.Y:F3},+{rotationOffset.Z:F3})"); - - // 第3步:预落位到调整后的目标位置 - var provisionalToTarget = Transform3D.CreateTranslation(new Vector3D( - adjustedTargetPosition.X, - adjustedTargetPosition.Y, - adjustedTargetPosition.Z)); - doc.Models.OverridePermanentTransform(modelItems, provisionalToTarget, false); - - Point3D actualTrackedPosition = item.BoundingBox()?.Center ?? new Point3D(0, 0, 0); - var correctionTranslation = new Vector3D( - adjustedTargetPosition.X - actualTrackedPosition.X, - adjustedTargetPosition.Y - actualTrackedPosition.Y, - adjustedTargetPosition.Z - actualTrackedPosition.Z); - - LogManager.Info( - $"[模型增量补偿] {item.DisplayName} 预落位后实际trackedPoint=({actualTrackedPosition.X:F3},{actualTrackedPosition.Y:F3},{actualTrackedPosition.Z:F3}), " + - $"纠偏平移=({correctionTranslation.X:F3},{correctionTranslation.Y:F3},{correctionTranslation.Z:F3})"); - - if (Math.Abs(correctionTranslation.X) > 1e-6 || - Math.Abs(correctionTranslation.Y) > 1e-6 || - Math.Abs(correctionTranslation.Z) > 1e-6) - { - var correction = Transform3D.CreateTranslation(correctionTranslation); - doc.Models.OverridePermanentTransform(modelItems, correction, false); - LogManager.Info($"[模型增量补偿] {item.DisplayName} 已应用纠偏平移"); - } - else - { - LogManager.Info($"[模型增量补偿] {item.DisplayName} 纠偏平移太小,跳过应用"); - } - - // 🔥 验证纠偏后的实际位置(包围盒中心应该偏离目标,但几何中心对齐目标) - Point3D finalTrackedPosition = item.BoundingBox()?.Center ?? new Point3D(0, 0, 0); - // 几何中心 = BoundingBox中心 - rotationOffset(因为rotationOffset是BoundingBox中心相对于几何中心的偏移) - var geometricCenterDeviation = new Vector3D( - targetTrackedPosition.X - (finalTrackedPosition.X - rotationOffset.X), - targetTrackedPosition.Y - (finalTrackedPosition.Y - rotationOffset.Y), - targetTrackedPosition.Z - (finalTrackedPosition.Z - rotationOffset.Z)); - LogManager.Info( - $"[模型增量补偿] {item.DisplayName} 最终包围盒中心=({finalTrackedPosition.X:F3},{finalTrackedPosition.Y:F3},{finalTrackedPosition.Z:F3}), " + - $"几何中心偏差=({geometricCenterDeviation.X:F3},{geometricCenterDeviation.Y:F3},{geometricCenterDeviation.Z:F3})"); - - // LogIncrementalTransformActual(item, targetTrackedPosition, targetRotation); - } - /// /// 读取物体当前实际几何姿态。 /// 优先使用 ModelGeometry.ActiveTransform,因为 ModelItem.Transform 只反映原始设计变换。