diff --git a/doc/design/2026/NavisworksAPI使用方法.md b/doc/design/2026/NavisworksAPI使用方法.md
index 8433cf5..6dd0b65 100644
--- a/doc/design/2026/NavisworksAPI使用方法.md
+++ b/doc/design/2026/NavisworksAPI使用方法.md
@@ -822,6 +822,117 @@ Autodesk官方论坛已确认的限制(Issue NW-53280):
| 动画初始化 | `_currentYaw = firstFrame.YawRadians` | 避免第一帧产生旋转增量 |
| 调试验证 | 测试物体远离原点的情况 | 原点附近可能掩盖问题 |
+#### 11.7.7 关键原则:移动物体前必须先重置到CAD位置 ⚠️ 重要
+
+**问题场景**:
+当物体已经被移动过(如动画结束在终点位置),再次移动时如果直接从当前位置计算增量,会导致错误的结果。
+
+**原因**:
+`OverridePermanentTransform` 的增量是相对于**CAD原始位置**的,不是相对于当前位置。
+
+**❌ 错误做法**:
+```csharp
+// 物体当前在终点位置,但我们要移动到另一个位置
+var currentPos = item.BoundingBox().Center; // 终点位置
+var deltaPos = new Vector3D(
+ targetPos.X - currentPos.X, // 从终点计算增量 - 错误!
+ targetPos.Y - currentPos.Y,
+ targetPos.Z - currentPos.Z
+);
+var transform = Transform3D.CreateTranslation(deltaPos);
+doc.Models.OverridePermanentTransform(modelItems, transform, false);
+// 结果:物体会移动到错误位置(因为增量是相对于CAD位置的)
+```
+
+**✅ 正确做法**:
+```csharp
+// 1. 先重置到CAD原始位置
+doc.Models.ResetPermanentTransform(modelItems);
+
+// 2. 从CAD原始位置计算到目标位置的增量
+var originalBounds = item.BoundingBox();
+var originalPos = new Point3D(
+ originalBounds.Center.X,
+ originalBounds.Center.Y,
+ originalBounds.Min.Z
+);
+var deltaPos = new Vector3D(
+ targetPos.X - originalPos.X, // 从CAD位置计算增量 - 正确!
+ targetPos.Y - originalPos.Y,
+ targetPos.Z - originalPos.Z
+);
+
+// 3. 应用变换
+var transform = Transform3D.CreateTranslation(deltaPos);
+doc.Models.OverridePermanentTransform(modelItems, transform, false);
+```
+
+**使用场景**:
+- 碰撞报告还原物体到碰撞位置
+- 手动指定物体位置
+- 任何需要精确控制物体最终位置的操作
+
+**最佳实践**:
+```csharp
+///
+/// 将物体移动到指定位置和朝向(先回到CAD原始位置)
+///
+public static void MoveItemToPositionAndYaw(ModelItem item, Point3D targetPosition, double targetYaw)
+{
+ var doc = Application.ActiveDocument;
+ var modelItems = new ModelItemCollection { item };
+
+ // 🔥 关键:先回到CAD原始位置
+ doc.Models.ResetPermanentTransform(modelItems);
+
+ // 获取CAD原始状态
+ var originalBounds = item.BoundingBox();
+ var originalGroundPos = new Point3D(
+ originalBounds.Center.X,
+ originalBounds.Center.Y,
+ originalBounds.Min.Z
+ );
+ var originalYaw = GetYawFromTransform(item.Transform);
+
+ // 计算从CAD位置到目标位置的增量
+ var deltaPos = new Vector3D(
+ targetPosition.X - originalGroundPos.X,
+ targetPosition.Y - originalGroundPos.Y,
+ targetPosition.Z - originalGroundPos.Z
+ );
+ double deltaYaw = targetYaw - originalYaw;
+
+ // 应用增量变换(包含旋转补偿)
+ Transform3D transform;
+ if (Math.Abs(deltaYaw) > 0.001)
+ {
+ // 计算旋转补偿
+ double cos = Math.Cos(deltaYaw);
+ double sin = Math.Sin(deltaYaw);
+ double rotatedX = originalGroundPos.X * cos - originalGroundPos.Y * sin;
+ double rotatedY = originalGroundPos.X * sin + originalGroundPos.Y * cos;
+
+ var compensatedTranslation = new Vector3D(
+ targetPosition.X - rotatedX,
+ targetPosition.Y - rotatedY,
+ deltaPos.Z
+ );
+
+ var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
+ var components = identity.Factor();
+ components.Rotation = new Rotation3D(new UnitVector3D(0, 0, 1), deltaYaw);
+ components.Translation = compensatedTranslation;
+ transform = components.Combine();
+ }
+ else
+ {
+ transform = Transform3D.CreateTranslation(deltaPos);
+ }
+
+ doc.Models.OverridePermanentTransform(modelItems, transform, false);
+}
+```
+
**调试技巧**:
```csharp
diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs
index 07be8ca..a4383e7 100644
--- a/src/Core/Animation/PathAnimationManager.cs
+++ b/src/Core/Animation/PathAnimationManager.cs
@@ -2230,6 +2230,7 @@ namespace NavisworksTransport.Core.Animation
///
/// 获取物体当前位置和朝向
+ /// 🔥 关键:返回实际当前朝向(_currentYaw),而不是 Transform 的 CAD 原始朝向
///
public (Point3D Position, double Yaw) GetObjectCurrentPosition(ModelItem obj)
{
@@ -2245,8 +2246,9 @@ namespace NavisworksTransport.Core.Animation
bbox.Center.Y,
bbox.Min.Z
);
- var yaw = ModelItemTransformHelper.GetYawFromTransform(obj.Transform);
- return (position, yaw);
+ // 🔥 关键:使用 _currentYaw(实际当前朝向),而不是 Transform 的 CAD 原始朝向
+ // Transform 返回的是 CAD 设计时的原始朝向,不是动画后的实际朝向
+ return (position, _currentYaw);
}
///
@@ -2749,6 +2751,12 @@ namespace NavisworksTransport.Core.Animation
///
public List AllCollisionResults => _allCollisionResults;
+ ///
+ /// 获取当前物体的朝向(弧度)
+ /// 用于保存物体当前状态(因为Transform返回的是CAD原始值)
+ ///
+ public double CurrentYaw => _currentYaw;
+
///
/// 获取当前碰撞检测精度
///
diff --git a/src/Core/Collision/ClashDetectiveIntegration.cs b/src/Core/Collision/ClashDetectiveIntegration.cs
index b25d017..0796812 100644
--- a/src/Core/Collision/ClashDetectiveIntegration.cs
+++ b/src/Core/Collision/ClashDetectiveIntegration.cs
@@ -815,21 +815,56 @@ namespace NavisworksTransport
var testAnimatedObject = candidate.Item1;
var modelItems = new ModelItemCollection { testAnimatedObject };
var targetPosition = candidate.Item1Position;
+ var targetYaw = candidate.Item1YawRadians;
- var currentBounds = testAnimatedObject.BoundingBox();
- var currentPos = new Point3D(
- (currentBounds.Min.X + currentBounds.Max.X) / 2,
- (currentBounds.Min.Y + currentBounds.Max.Y) / 2,
- (currentBounds.Min.Z + currentBounds.Max.Z) / 2
+ // 🔥 关键:为了最高精度,先回到CAD原始位置,再移动到目标位置
+ // 避免累积误差影响碰撞检测精度
+ doc.Models.ResetPermanentTransform(modelItems);
+
+ // 获取CAD原始状态
+ var originalBounds = testAnimatedObject.BoundingBox();
+ var originalPos = new Point3D(
+ (originalBounds.Min.X + originalBounds.Max.X) / 2,
+ (originalBounds.Min.Y + originalBounds.Max.Y) / 2,
+ (originalBounds.Min.Z + originalBounds.Max.Z) / 2
);
-
- var offset = new Vector3D(
- targetPosition.X - currentPos.X,
- targetPosition.Y - currentPos.Y,
- targetPosition.Z - currentPos.Z
+ var originalYaw = ModelItemTransformHelper.GetYawFromTransform(testAnimatedObject.Transform);
+
+ // 计算从CAD位置到目标位置的偏移和旋转
+ var deltaPos = new Vector3D(
+ targetPosition.X - originalPos.X,
+ targetPosition.Y - originalPos.Y,
+ targetPosition.Z - originalPos.Z
);
-
- var transform = Transform3D.CreateTranslation(offset);
+ double deltaYaw = targetYaw - originalYaw;
+
+ // 应用变换(包含旋转补偿)
+ Transform3D transform;
+ if (Math.Abs(deltaYaw) > 0.001)
+ {
+ // 计算绕原点旋转的补偿
+ double cos = Math.Cos(deltaYaw);
+ double sin = Math.Sin(deltaYaw);
+ double rotatedX = originalPos.X * cos - originalPos.Y * sin;
+ double rotatedY = originalPos.X * sin + originalPos.Y * cos;
+
+ var compensatedTranslation = new Vector3D(
+ targetPosition.X - rotatedX,
+ targetPosition.Y - rotatedY,
+ deltaPos.Z
+ );
+
+ var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
+ var components = identity.Factor();
+ components.Rotation = new Rotation3D(new UnitVector3D(0, 0, 1), deltaYaw);
+ components.Translation = compensatedTranslation;
+ transform = components.Combine();
+ }
+ else
+ {
+ transform = Transform3D.CreateTranslation(deltaPos);
+ }
+
doc.Models.OverridePermanentTransform(modelItems, transform, false);
var tempTestName = $"临时验证_{confirmedCount + 1}_{i}_{DateTime.Now:HHmmss_fff}";
diff --git a/src/UI/WPF/ViewModels/CollisionReportViewModel.cs b/src/UI/WPF/ViewModels/CollisionReportViewModel.cs
index 7ba40e9..531c23c 100644
--- a/src/UI/WPF/ViewModels/CollisionReportViewModel.cs
+++ b/src/UI/WPF/ViewModels/CollisionReportViewModel.cs
@@ -6,6 +6,7 @@ using System.Windows.Input;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Utils;
using NavisworksTransport.Commands;
+using NavisworksTransport.Core.Animation;
using System.IO;
using System.Text;
using Microsoft.Win32;
@@ -1273,8 +1274,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (_savedAnimatedObjectState == null || _currentAnimatedObject != collisionData.Item1)
{
_currentAnimatedObject = collisionData.Item1;
- _savedAnimatedObjectState = ModelItemTransformHelper.SaveObjectState(collisionData.Item1);
- LogManager.Info($"[碰撞查看] 已保存运动物体原始状态,供后续恢复使用");
+ // 🔥 关键:从 PathAnimationManager 获取当前实际朝向
+ // 因为 Transform 返回的是CAD原始朝向,不是当前实际朝向
+ var pam = PathAnimationManager.GetInstance();
+ var currentYaw = pam.CurrentYaw;
+ var transformYaw = ModelItemTransformHelper.GetYawFromTransform(collisionData.Item1.Transform);
+ LogManager.Debug($"[碰撞查看调试] PathAnimationManager.CurrentYaw={currentYaw * 180 / Math.PI:F2}°, Transform.Yaw={transformYaw * 180 / Math.PI:F2}°");
+ _savedAnimatedObjectState = ModelItemTransformHelper.SaveObjectState(collisionData.Item1, currentYaw);
+ LogManager.Info($"[碰撞查看] 已保存运动物体原始状态: pos=({_savedAnimatedObjectState.Position.X:F2},{_savedAnimatedObjectState.Position.Y:F2},{_savedAnimatedObjectState.Position.Z:F2}), yaw={currentYaw * 180 / Math.PI:F2}°");
}
RestoreAnimatedObjectToCollisionPosition(collisionData);
diff --git a/src/Utils/ModelItemTransformHelper.cs b/src/Utils/ModelItemTransformHelper.cs
index a7c7181..8ec156a 100644
--- a/src/Utils/ModelItemTransformHelper.cs
+++ b/src/Utils/ModelItemTransformHelper.cs
@@ -177,16 +177,32 @@ namespace NavisworksTransport.Utils
///
/// 保存物体当前状态
+ /// 注意:YawRadians 通过当前位置计算,因为 Transform 返回的是CAD原始值
///
- public static ObjectStateSnapshot SaveObjectState(ModelItem item)
+ public static ObjectStateSnapshot SaveObjectState(ModelItem item, double? currentYaw = null)
{
if (item == null) return null;
var bounds = item.BoundingBox();
+
+ // 🔥 关键:如果提供了当前朝向(如 PathAnimationManager._currentYaw),使用它
+ // 否则从 Transform 获取(但这可能不是实际朝向)
+ double yaw;
+ if (currentYaw.HasValue)
+ {
+ yaw = currentYaw.Value;
+ }
+ else
+ {
+ // 尝试通过包围盒方向计算(如果物体有几何特征)
+ yaw = GetYawFromTransform(item.Transform);
+ LogManager.Debug($"[SaveObjectState] 未提供当前朝向,使用Transform: {yaw * 180 / Math.PI:F2}°");
+ }
+
return new ObjectStateSnapshot
{
Position = new Point3D(bounds.Center.X, bounds.Center.Y, bounds.Min.Z),
- YawRadians = GetYawFromTransform(item.Transform),
+ YawRadians = yaw,
Transform = item.Transform
};
}