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