Unify planar collision pose replay

This commit is contained in:
tian 2026-04-12 15:01:33 +08:00
parent 970d3465f4
commit 315fa46043
3 changed files with 86 additions and 52 deletions

View File

@ -132,6 +132,24 @@ namespace NavisworksTransport.UnitTests.CoordinateSystem
Assert.IsFalse(PathAnimationManager.ShouldUsePlanarRealObjectPureIncrementFrames(PathType.Rail, true)); Assert.IsFalse(PathAnimationManager.ShouldUsePlanarRealObjectPureIncrementFrames(PathType.Rail, true));
} }
[TestMethod]
public void ResolveAnimationFramePlaybackYawRadians_ShouldPreferPlanarHostYawWithHostCorrection()
{
var frame = new AnimationFrame
{
YawRadians = 0.25,
PlanarHostYawRadians = 0.75,
HasCustomRotation = true
};
double resolved = PathAnimationManager.ResolveAnimationFramePlaybackYawRadians(
frame,
new LocalEulerRotationCorrection(10.0, 20.0, 30.0),
CoordinateSystemType.YUp);
Assert.AreEqual(0.75 + 20.0 * System.Math.PI / 180.0, resolved, 1e-9);
}
[TestMethod] [TestMethod]
public void ApplyRecordedPlanarRealObjectFramePose_ShouldPreserveFullRotation_ForGroundCollisionLogCase() public void ApplyRecordedPlanarRealObjectFramePose_ShouldPreserveFullRotation_ForGroundCollisionLogCase()
{ {

View File

@ -759,7 +759,7 @@ namespace NavisworksTransport.Core.Animation
$"Y=({railLinearTransform.Get(0, 1):F4},{railLinearTransform.Get(1, 1):F4},{railLinearTransform.Get(2, 1):F4}), " + $"Y=({railLinearTransform.Get(0, 1):F4},{railLinearTransform.Get(1, 1):F4},{railLinearTransform.Get(2, 1):F4}), " +
$"Z=({railLinearTransform.Get(0, 2):F4},{railLinearTransform.Get(1, 2):F4},{railLinearTransform.Get(2, 2):F4})"); $"Z=({railLinearTransform.Get(0, 2):F4},{railLinearTransform.Get(1, 2):F4},{railLinearTransform.Get(2, 2):F4})");
UpdateObjectPosition(startPosition, railRotation); ApplyFullPoseUpdate(startPosition, railRotation);
LogManager.Info("[移动到起点] Rail路径已应用完整三维旋转姿态"); LogManager.Info("[移动到起点] Rail路径已应用完整三维旋转姿态");
return true; return true;
} }
@ -821,7 +821,7 @@ namespace NavisworksTransport.Core.Animation
else if (_route.PathType != PathType.Rail && else if (_route.PathType != PathType.Rail &&
TryCreatePlanarPathRotationAtStart(out var planarRotation)) TryCreatePlanarPathRotationAtStart(out var planarRotation))
{ {
UpdateObjectPosition(startPosition, planarRotation); ApplyFullPoseUpdate(startPosition, planarRotation);
LogManager.Info("[移动到起点] 地面/吊装路径已应用完整三维姿态"); LogManager.Info("[移动到起点] 地面/吊装路径已应用完整三维姿态");
var startAppliedPoint = GetLiveBoundingBoxCenter(CurrentControlledObject ?? _animatedObject); var startAppliedPoint = GetLiveBoundingBoxCenter(CurrentControlledObject ?? _animatedObject);
@ -975,7 +975,7 @@ namespace NavisworksTransport.Core.Animation
startPosition = ApplyGroundPathObjectLiftOffset(startPosition); startPosition = ApplyGroundPathObjectLiftOffset(startPosition);
} }
UpdateObjectPosition(startPosition); ApplyLegacyYawPoseUpdate(startPosition);
SyncTrackedRotationToDisplayedPose(CurrentControlledObject ?? _animatedObject); SyncTrackedRotationToDisplayedPose(CurrentControlledObject ?? _animatedObject);
_hasGroundRealObjectBasePose = false; _hasGroundRealObjectBasePose = false;
@ -1879,7 +1879,7 @@ namespace NavisworksTransport.Core.Animation
_pausedProgress = 0.0; // 重置暂停进度 _pausedProgress = 0.0; // 重置暂停进度
// 初始化动画状态时不再强行覆盖 _currentYaw 为第一帧的 yaw // 初始化动画状态时不再强行覆盖 _currentYaw 为第一帧的 yaw
// 这样在 UpdateObjectPosition(firstFrame.Position, firstFrame.YawRadians) 时 // 这样在 ApplyLegacyYawPoseUpdate(firstFrame.Position, firstFrame.YawRadians) 时
// deltaYaw = firstFrame.YawRadians - 物体初始Yaw从而产生旋转增量让物体转向 // deltaYaw = firstFrame.YawRadians - 物体初始Yaw从而产生旋转增量让物体转向
if (_animationFrames != null && _animationFrames.Count > 0) if (_animationFrames != null && _animationFrames.Count > 0)
@ -2685,7 +2685,7 @@ namespace NavisworksTransport.Core.Animation
/// <summary> /// <summary>
/// 更新对象位置和朝向(支持绕物体中心旋转) /// 更新对象位置和朝向(支持绕物体中心旋转)
/// </summary> /// </summary>
private void UpdateObjectPosition(Point3D newPosition, double newYaw = double.NaN) private void ApplyLegacyYawPoseUpdate(Point3D newPosition, double newYaw = double.NaN)
{ {
try try
{ {
@ -2741,7 +2741,7 @@ namespace NavisworksTransport.Core.Animation
{ {
// 纯平移 // 纯平移
incrementalTransform = Transform3D.CreateTranslation(deltaPos); incrementalTransform = Transform3D.CreateTranslation(deltaPos);
LogManager.Debug($"[UpdateObjectPosition] 纯平移: ({deltaPos.X:F2},{deltaPos.Y:F2},{deltaPos.Z:F2})"); LogManager.Debug($"[LegacyYawPose] 纯平移: ({deltaPos.X:F2},{deltaPos.Y:F2},{deltaPos.Z:F2})");
} }
// 应用增量变换false = 增量模式) // 应用增量变换false = 增量模式)
@ -2760,7 +2760,7 @@ namespace NavisworksTransport.Core.Animation
/// 使用完整三维姿态更新对象位置。 /// 使用完整三维姿态更新对象位置。
/// 当前先用于 Rail 路径,避免影响现有地面/吊装路径的 yaw 流程。 /// 当前先用于 Rail 路径,避免影响现有地面/吊装路径的 yaw 流程。
/// </summary> /// </summary>
private void UpdateObjectPosition(Point3D newPosition, Rotation3D newRotation) private void ApplyFullPoseUpdate(Point3D newPosition, Rotation3D newRotation)
{ {
try try
{ {
@ -3054,19 +3054,13 @@ namespace NavisworksTransport.Core.Animation
$"Z=({linear.Get(0, 2):F4},{linear.Get(1, 2):F4},{linear.Get(2, 2):F4})"); $"Z=({linear.Get(0, 2):F4},{linear.Get(1, 2):F4},{linear.Get(2, 2):F4})");
} }
if (_savedObjectHasCustomRotation) ApplyResolvedObjectPose(
{ _savedObjectPosition,
UpdateObjectPosition(_savedObjectPosition, _savedObjectRotation); _savedObjectYaw,
} _savedObjectRotation,
else _savedObjectHasCustomRotation,
{ frameIndex: -1,
UpdateObjectPosition(_savedObjectPosition, _savedObjectYaw); planarHostYawRadians: _savedObjectYaw);
}
_trackedPosition = _savedObjectPosition;
_currentYaw = _savedObjectYaw;
_trackedRotation = _savedObjectRotation;
_hasTrackedRotation = _savedObjectHasCustomRotation;
LogHostActualPoseAxes(obj, "[PAM恢复后宿主姿态]", false); LogHostActualPoseAxes(obj, "[PAM恢复后宿主姿态]", false);
@ -3132,14 +3126,13 @@ namespace NavisworksTransport.Core.Animation
LogHostActualPoseAxes(obj, "[动画姿态复用前宿主姿态]", false); LogHostActualPoseAxes(obj, "[动画姿态复用前宿主姿态]", false);
if (hasCustomRotation) ApplyResolvedObjectPose(
{ targetPosition,
UpdateObjectPosition(targetPosition, targetRotation); targetYawRadians,
} targetRotation,
else hasCustomRotation,
{ frameIndex: -1,
UpdateObjectPosition(targetPosition, targetYawRadians); planarHostYawRadians: targetYawRadians);
}
LogHostActualPoseAxes(obj, "[动画姿态复用后宿主姿态]", false); LogHostActualPoseAxes(obj, "[动画姿态复用后宿主姿态]", false);
@ -3251,16 +3244,9 @@ namespace NavisworksTransport.Core.Animation
_currentFrameIndex = frameIndex; _currentFrameIndex = frameIndex;
// 更新对象位置和朝向 // 更新对象位置和朝向,复用动画主播放与恢复使用的同一入口。
var frameData = _animationFrames[_currentFrameIndex]; var frameData = _animationFrames[_currentFrameIndex];
if (frameData.HasCustomRotation) ApplyAnimationFramePose(frameData, _currentFrameIndex);
{
UpdateObjectPosition(frameData.Position, frameData.Rotation);
}
else
{
UpdateObjectPosition(frameData.Position, ResolveAnimationFramePlaybackYawRadians(frameData));
}
// 更新碰撞高亮 // 更新碰撞高亮
UpdateCollisionHighlightFromFrame(); UpdateCollisionHighlightFromFrame();
@ -5740,27 +5726,57 @@ namespace NavisworksTransport.Core.Animation
return; return;
} }
ApplyResolvedObjectPose(
frameData.Position,
ResolveAnimationFramePlaybackYawRadians(frameData),
frameData.Rotation,
frameData.HasCustomRotation,
frameIndex,
frameData.PlanarHostYawRadians);
}
private void ApplyResolvedObjectPose(
Point3D targetPosition,
double resolvedYawRadians,
Rotation3D targetRotation,
bool hasCustomRotation,
int frameIndex,
double? planarHostYawRadians = null)
{
if (ShouldUsePlanarRealObjectPureIncrementFrames(_route?.PathType ?? PathType.Ground, IsRealObjectMode) && if (ShouldUsePlanarRealObjectPureIncrementFrames(_route?.PathType ?? PathType.Ground, IsRealObjectMode) &&
frameData.PlanarHostYawRadians.HasValue) planarHostYawRadians.HasValue)
{ {
ApplyPlanarAnimationFrame(frameData.Position, frameData.PlanarHostYawRadians.Value); ApplyPlanarAnimationFrame(targetPosition, planarHostYawRadians.Value);
return;
} }
else if (frameData.HasCustomRotation)
if (hasCustomRotation)
{ {
UpdateObjectPosition(frameData.Position, frameData.Rotation); ApplyFullPoseUpdate(targetPosition, targetRotation);
return;
} }
else if (_route?.PathType == PathType.Ground || _route?.PathType == PathType.Hoisting)
if (_route?.PathType == PathType.Ground || _route?.PathType == PathType.Hoisting)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
$"平面路径播放帧 {frameIndex} 缺少完整姿态,禁止退回旧 yaw 链路。"); $"平面路径播放帧 {frameIndex} 缺少完整姿态,禁止退回旧 yaw 链路。");
} }
else
{ ApplyLegacyYawPoseUpdate(targetPosition, resolvedYawRadians);
UpdateObjectPosition(frameData.Position, ResolveAnimationFramePlaybackYawRadians(frameData));
}
} }
private double ResolveAnimationFramePlaybackYawRadians(AnimationFrame frameData) private double ResolveAnimationFramePlaybackYawRadians(AnimationFrame frameData)
{
return ResolveAnimationFramePlaybackYawRadians(
frameData,
_objectRotationCorrection,
CoordinateSystemManager.Instance.ResolvedType);
}
internal static double ResolveAnimationFramePlaybackYawRadians(
AnimationFrame frameData,
LocalEulerRotationCorrection rotationCorrection,
CoordinateSystemType hostType)
{ {
if (frameData == null) if (frameData == null)
{ {
@ -5772,14 +5788,14 @@ namespace NavisworksTransport.Core.Animation
return NormalizeRadians( return NormalizeRadians(
frameData.PlanarHostYawRadians.Value + frameData.PlanarHostYawRadians.Value +
ResolvePlanarHostUpCorrectionRadians( ResolvePlanarHostUpCorrectionRadians(
_objectRotationCorrection, rotationCorrection,
CoordinateSystemManager.Instance.ResolvedType)); hostType));
} }
double resolvedYaw = frameData.YawRadians; double resolvedYaw = frameData.YawRadians;
if (!frameData.HasCustomRotation && !_objectRotationCorrection.IsZero) if (!frameData.HasCustomRotation && !rotationCorrection.IsZero)
{ {
resolvedYaw += _objectRotationCorrection.ZDegrees * Math.PI / 180.0; resolvedYaw += rotationCorrection.ZDegrees * Math.PI / 180.0;
} }
return NormalizeRadians(resolvedYaw); return NormalizeRadians(resolvedYaw);

View File

@ -804,7 +804,7 @@ namespace NavisworksTransport
if (candidate.AnimatedObjectHasTrackedRotation && targetRotation != null) if (candidate.AnimatedObjectHasTrackedRotation && targetRotation != null)
{ {
LogManager.Info( LogManager.Info(
$"[ClashDetective验证] 三维候选恢复: " + $"[ClashDetective验证] 已跟踪姿态候选恢复: " +
$"对象={testAnimatedObject.DisplayName}, " + $"对象={testAnimatedObject.DisplayName}, " +
$"目标跟踪点=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3})"); $"目标跟踪点=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3})");
@ -821,7 +821,7 @@ namespace NavisworksTransport
else else
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
$"ClashDetective三维候选恢复失败:对象 {testAnimatedObject.DisplayName} 不受当前动画管理器控制。"); $"ClashDetective已跟踪姿态候选恢复失败:对象 {testAnimatedObject.DisplayName} 不受当前动画管理器控制。");
} }
} }
else else