优化物体移动和朝向处理,确保在移动前重置到CAD原始位置,修复获取和保存当前朝向的逻辑

This commit is contained in:
tian 2026-02-11 03:18:06 +08:00
parent bdb3ed23db
commit fdb11d2119
5 changed files with 195 additions and 18 deletions

View File

@ -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
/// <summary>
/// 将物体移动到指定位置和朝向先回到CAD原始位置
/// </summary>
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

View File

@ -2230,6 +2230,7 @@ namespace NavisworksTransport.Core.Animation
/// <summary>
/// 获取物体当前位置和朝向
/// 🔥 关键返回实际当前朝向_currentYaw而不是 Transform 的 CAD 原始朝向
/// </summary>
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);
}
/// <summary>
@ -2749,6 +2751,12 @@ namespace NavisworksTransport.Core.Animation
/// </summary>
public List<CollisionResult> AllCollisionResults => _allCollisionResults;
/// <summary>
/// 获取当前物体的朝向(弧度)
/// 用于保存物体当前状态因为Transform返回的是CAD原始值
/// </summary>
public double CurrentYaw => _currentYaw;
/// <summary>
/// 获取当前碰撞检测精度
/// </summary>

View File

@ -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}";

View File

@ -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);

View File

@ -177,16 +177,32 @@ namespace NavisworksTransport.Utils
/// <summary>
/// 保存物体当前状态
/// 注意YawRadians 通过当前位置计算,因为 Transform 返回的是CAD原始值
/// </summary>
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
};
}