修改移动物体初始角度不对的问题

This commit is contained in:
tian 2025-12-25 17:48:49 +08:00
parent dab8dc34c3
commit 736e6e8448
4 changed files with 278 additions and 216 deletions

View File

@ -11,6 +11,7 @@
5. [ ] 测试路径规划文件能导入DELMIA
6. [ ] (优化)优化路径规划分析和分析报告
7. [ ] (功能)增加物流属性自定义
8. [x] (BUG) 动画时物流车在起点时应该朝向路径方向,切换虚拟车辆和指定物体时,原有的要归位
### [2025/12/18]

View File

@ -144,6 +144,11 @@ namespace NavisworksTransport.Core.Animation
private TimeLinerIntegrationManager _timeLinerManager;
private string _currentTaskId;
/// <summary>
/// 当前正在进行动画的对象
/// </summary>
public ModelItem AnimatedObject => _animatedObject;
// --- 新增事件 ---
/// <summary>
/// 当动画状态发生改变时触发
@ -206,6 +211,89 @@ namespace NavisworksTransport.Core.Animation
return _instance;
}
/// <summary>
/// 清理当前所有的动画生成结果(帧列表、碰撞结果、状态)
/// </summary>
public void ClearAnimationResults()
{
_animationFrames = null;
_allCollisionResults = null;
SetState(AnimationState.Idle);
LogManager.Info("[PathAnimationManager] 动画数据和状态已重置为 Idle");
}
/// <summary>
/// 仅仅将物理模型同步到路径起点(不清理数据,不重置状态)
/// </summary>
public void SyncToPathStart(ModelItem item, List<Point3D> points)
{
try
{
if (item == null || points == null || points.Count == 0) return;
// 更新引用,确保后续 MoveVehicleToPathStart 使用正确物体
_animatedObject = item;
_pathPoints = new List<Point3D>(points);
// 记录原始位置(用于归位)
_originalTransform = item.Transform;
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform);
_originalCenter = item.BoundingBox().Center;
_currentPosition = new Point3D(_originalCenter.X, _originalCenter.Y, item.BoundingBox().Min.Z);
if (points.Count >= 2)
{
double yaw = Math.Atan2(points[1].Y - points[0].Y, points[1].X - points[0].X);
UpdateObjectPosition(points[0], yaw);
}
else
{
UpdateObjectPosition(points[0]);
}
LogManager.Debug($"[Sync] 物体 {item.DisplayName} 已移动到起点");
}
catch (Exception ex)
{
LogManager.Error($"[Sync] 同步到起点失败: {ex.Message}");
}
}
/// <summary>
/// 将动画对象彻底恢复到 CAD 原始位置(清除所有覆盖变换)
/// 常用于模式切换或彻底清除动画干扰
/// </summary>
public void RestoreObjectToCADPosition()
{
try
{
var doc = NavisApplication.ActiveDocument;
if (doc == null || _animatedObject == null) return;
var modelItems = new ModelItemCollection { _animatedObject };
// 1. 彻底清除 Navisworks 中的所有覆盖变换,强制恢复到设计文件定义的原始状态
doc.Models.ResetPermanentTransform(modelItems);
// 2. 重置内部跟踪变量(同步到 CAD 原始状态)
// 注意ResetPermanentTransform 后物体的 Transform 属性会自动变回 CAD 原始值
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(_animatedObject.Transform);
var originalBoundingBox = _animatedObject.BoundingBox();
_currentPosition = new Point3D(
originalBoundingBox.Center.X,
originalBoundingBox.Center.Y,
originalBoundingBox.Min.Z
);
LogManager.Info($"[归位] 物体 {_animatedObject.DisplayName} 已彻底恢复到 CAD 原始位置, yaw={_currentYaw:F3}");
}
catch (Exception ex)
{
LogManager.Error($"[归位] 恢复物体到 CAD 位置失败: {ex.Message}");
}
}
/// <summary>
/// 设置动画参数
/// </summary>
@ -264,10 +352,21 @@ namespace NavisworksTransport.Core.Animation
// 保存原始变换以便重置
_originalTransform = _animatedObject.Transform; // 使用真正的Transform不是自造的
// 获取并初始化当前偏航角
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform);
LogManager.Info($"[动画设置] 初始偏航角提取: {_currentYaw:F3}rad ({(_currentYaw * 180 / Math.PI):F1}度)");
// 保存车辆的原始中心位置
var originalBoundingBox = animatedObject.BoundingBox();
_originalCenter = originalBoundingBox.Center;
// 初始化当前位置为物体底面中心,用于后续增量变换计算
_currentPosition = new Point3D(
_originalCenter.X,
_originalCenter.Y,
originalBoundingBox.Min.Z
);
// 调试查看原始Transform的组成
var origComponents = _originalTransform.Factor();
LogManager.Info($"[原始Transform] Translation=({origComponents.Translation.X:F2},{origComponents.Translation.Y:F2},{origComponents.Translation.Z:F2})");
@ -315,45 +414,19 @@ namespace NavisworksTransport.Core.Animation
{
try
{
if (_pathPoints.Count == 0) return;
if (_pathPoints.Count == 0 || _animationFrames == null || _animationFrames.Count == 0) return;
var doc = NavisApplication.ActiveDocument;
var modelItems = new ModelItemCollection { _animatedObject };
var firstFrame = _animationFrames[0];
// 获取物体的包围盒
var boundingBox = _animatedObject.BoundingBox();
// 使用 UpdateObjectPosition 统一处理移动和旋转
// 这将物体从当前位置(底面中心)移动到第一帧的位置,并旋转到第一帧的朝向
UpdateObjectPosition(firstFrame.Position, firstFrame.YawRadians);
// 计算物体底面中心点(而不是整体中心点)
var objectBottomCenter = new Point3D(
_originalCenter.X,
_originalCenter.Y,
boundingBox.Min.Z // 使用包围盒的最小Z值作为底面
);
// 计算从物体底面中心到路径起点的偏移
var startOffset = new Vector3D(
_pathPoints[0].X - objectBottomCenter.X,
_pathPoints[0].Y - objectBottomCenter.Y,
_pathPoints[0].Z - objectBottomCenter.Z
);
// 创建变换并应用
var startTransform = Transform3D.CreateTranslation(startOffset);
doc.Models.OverridePermanentTransform(modelItems, startTransform, false);
// 更新当前位置为路径起点
_currentPosition = _pathPoints[0];
LogManager.Info($"物体已移动到路径起点,底面对齐地面");
LogManager.Info($"物体包围盒: Min=({boundingBox.Min.X:F2},{boundingBox.Min.Y:F2},{boundingBox.Min.Z:F2}), Max=({boundingBox.Max.X:F2},{boundingBox.Max.Y:F2},{boundingBox.Max.Z:F2})");
LogManager.Info($"物体原始中心: ({_originalCenter.X:F2},{_originalCenter.Y:F2},{_originalCenter.Z:F2})");
LogManager.Info($"物体底面中心: ({objectBottomCenter.X:F2},{objectBottomCenter.Y:F2},{objectBottomCenter.Z:F2})");
LogManager.Info($"路径起点坐标: ({_pathPoints[0].X:F2},{_pathPoints[0].Y:F2},{_pathPoints[0].Z:F2})");
LogManager.Info($"应用的偏移量: ({startOffset.X:F2},{startOffset.Y:F2},{startOffset.Z:F2})");
LogManager.Info($"物体已初始化到路径起点并对齐朝向: pos=({firstFrame.Position.X:F2},{firstFrame.Position.Y:F2},{firstFrame.Position.Z:F2}), yaw={firstFrame.YawRadians:F3}rad");
}
catch (Exception ex)
{
LogManager.Error($"移动部件到路径起点失败: {ex.Message}");
LogManager.Error($"初始化物体到路径起点失败: {ex.Message}");
}
}
@ -447,7 +520,7 @@ namespace NavisworksTransport.Core.Animation
boundingBoxSize.Z * boundingBoxSize.Z
);
//空间查询半径,不能设置太小,否则会漏掉周围的对象
searchRadiusInModelUnits = objectDiagonal + _detectionGap;
searchRadiusInModelUnits = objectDiagonal + _detectionGap;
LogManager.Info($"空间查询半径: {searchRadiusInModelUnits:F2} 模型单位");
}
@ -623,7 +696,7 @@ namespace NavisworksTransport.Core.Animation
// 1. 定义物体包围盒的4个底面角点相对于中心
double halfX = size.X / 2;
double halfY = size.Y / 2;
var corners = new[]
{
new { x = -halfX, y = -halfY },
@ -635,7 +708,7 @@ namespace NavisworksTransport.Core.Animation
// 2. 旋转这些角点
double cos = Math.Cos(yawRadians);
double sin = Math.Sin(yawRadians);
double minX = double.MaxValue;
double maxX = double.MinValue;
double minY = double.MaxValue;
@ -646,7 +719,7 @@ namespace NavisworksTransport.Core.Animation
// 旋转公式x' = x*cos - y*sin, y' = x*sin + y*cos
double rotatedX = corner.x * cos - corner.y * sin;
double rotatedY = corner.x * sin + corner.y * cos;
minX = Math.Min(minX, rotatedX);
maxX = Math.Max(maxX, rotatedX);
minY = Math.Min(minY, rotatedY);
@ -771,16 +844,18 @@ namespace NavisworksTransport.Core.Animation
_animationFrameCount = 0; // 重置帧计数
_currentFrameIndex = 0; // 重置当前帧索引,确保从第一帧开始
_pausedProgress = 0.0; // 重置暂停进度
// 初始化yaw为第一帧的yaw避免第一帧就产生旋转增量
// 初始化动画状态时不再强行覆盖 _currentYaw 为第一帧的 yaw
// 这样在 UpdateObjectPosition(firstFrame.Position, firstFrame.YawRadians) 时
// deltaYaw = firstFrame.YawRadians - 物体初始Yaw从而产生旋转增量让物体转向
if (_animationFrames != null && _animationFrames.Count > 0)
{
var firstFrame = _animationFrames[0];
_currentYaw = firstFrame.YawRadians; // 关键设置为第一帧的yaw使deltaYaw=0
// 立即设置到第一帧的位置和朝向此时deltaYaw=0只有平移
// 立即设置到第一帧的位置和朝向由于MoveVehicleToPathStart已经设置过了这里主要是确保状态同步
UpdateObjectPosition(firstFrame.Position, firstFrame.YawRadians);
LogManager.Debug($"[动画开始] 设置初始位置和朝向: pos=({firstFrame.Position.X:F2},{firstFrame.Position.Y:F2}), yaw={firstFrame.YawRadians:F3}rad");
LogManager.Debug($"[动画开始] 确认初始位置和朝向: pos=({firstFrame.Position.X:F2},{firstFrame.Position.Y:F2}), yaw={firstFrame.YawRadians:F3}rad");
}
else
{
@ -924,11 +999,11 @@ namespace NavisworksTransport.Core.Animation
_pausedProgress = 0.0; // 重置暂停进度
_currentFrameIndex = 0; // 重置帧索引
// 将物体移回起点位置
// 将物体移回起点位置并恢复初始朝向
if (_pathPoints != null && _pathPoints.Count > 0 && _animatedObject != null)
{
UpdateObjectPosition(_pathPoints[0]);
LogManager.Info($"物体已移回起点位置: ({_pathPoints[0].X:F2},{_pathPoints[0].Y:F2},{_pathPoints[0].Z:F2})");
MoveVehicleToPathStart();
LogManager.Info($"物体已移回起点位置并恢复初始朝向");
}
LogManager.Info("动画已停止");
@ -1018,11 +1093,12 @@ namespace NavisworksTransport.Core.Animation
LogManager.Info("动画完成,路径上无碰撞对象");
}
// 将物体移回起点位置
// 将物体移回起点位置并恢复初始朝向
if (_pathPoints != null && _pathPoints.Count > 0 && _animatedObject != null)
{
UpdateObjectPosition(_pathPoints[0]);
LogManager.Info($"动画完成,物体已移回起点位置: ({_pathPoints[0].X:F2},{_pathPoints[0].Y:F2},{_pathPoints[0].Z:F2})");
RestoreObjectToCADPosition();
MoveVehicleToPathStart();
LogManager.Info($"动画播放自然结束,物体已移回起点位置并恢复初始朝向");
}
// 直接设置为完成状态,避免中间状态切换
@ -1156,21 +1232,24 @@ namespace NavisworksTransport.Core.Animation
var activeDoc = NavisApplication.ActiveDocument;
if (activeDoc != null && activeDoc.Models != null)
{
// 先安全获取对象名称,避免访问已释放对象的属性
string objectName = GetSafeObjectName(_animatedObject);
// 第一步:仅仅彻底归位到 CAD 原始位置(不涉及路径起点),这是“清除”按钮的逻辑
RestoreObjectToCADPosition();
var modelItems = new ModelItemCollection { _animatedObject };
activeDoc.Models.OverridePermanentTransform(modelItems, _originalTransform, false);
LogManager.Info($"部件 {objectName} 已重置到原始位置");
// 第二步:如果已经生成了动画路径,则重新对齐到起点(带旋转)
if (_animationFrames != null && _animationFrames.Count > 0)
{
MoveVehicleToPathStart();
LogManager.Info("已重新对齐到路径起点");
}
}
else
{
LogManager.Info("Navisworks文档已不可用过对象重置操作");
LogManager.Info("Navisworks文档已不可用回空闲状态");
}
}
catch (Exception resetEx)
{
LogManager.Warning($"重置对象位置时出现警告可能因为Navisworks对象已释放: {resetEx.Message}");
LogManager.Warning($"重置对象位置时出现警告: {resetEx.Message}");
// 不再抛出异常,因为在资源清理阶段这是正常的
}
}
@ -1382,16 +1461,16 @@ namespace NavisworksTransport.Core.Animation
// 有旋转:需要实现"绕物体当前位置自转"
// 由于Transform3DComponents.Rotation总是绕世界原点旋转
// 我们需要手动计算旋转导致的位置偏移,并补偿
double deltaYaw = newYaw - _currentYaw;
// 计算绕当前位置旋转的等效变换:
// 1. 如果绕原点旋转deltaYaw当前位置_currentPosition会移动到哪里
double cos = Math.Cos(deltaYaw);
double sin = Math.Sin(deltaYaw);
double rotatedX = _currentPosition.X * cos - _currentPosition.Y * sin;
double rotatedY = _currentPosition.X * sin + _currentPosition.Y * cos;
// 2. 但我们希望物体绕自己旋转位置移动到newPosition
// 所以需要的平移 = newPosition - (旋转后的位置)
var compensatedTranslation = new Vector3D(
@ -1399,15 +1478,15 @@ namespace NavisworksTransport.Core.Animation
newPosition.Y - rotatedY,
newPosition.Z - _currentPosition.Z // Z保持deltaPos
);
// 3. 组合:先旋转(绕原点),再平移(补偿+目标位置)
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;
incrementalTransform = components.Combine();
_currentYaw = newYaw;
}
else

View File

@ -350,28 +350,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _selectedAnimatedObject;
set
{
SetProperty(ref _selectedAnimatedObject, value);
UpdateAnimatedObjectInfo();
UpdateCanGenerateAnimation();
// 移动物体改变时清除已生成的动画(如果有的话)
if (_pathAnimationManager != null &&
(_pathAnimationManager.CurrentState == NavisworksTransport.Core.Animation.AnimationState.Ready ||
_pathAnimationManager.CurrentState == NavisworksTransport.Core.Animation.AnimationState.Finished))
if (SetProperty(ref _selectedAnimatedObject, value))
{
try
{
_pathAnimationManager.CancelAnimation(); // 这会将状态设置为Stopped需要重新生成动画
LogManager.Info("移动物体更改,已清除之前生成的动画");
}
catch (Exception ex)
{
LogManager.Warning($"清除之前动画时出现警告: {ex.Message}");
}
}
UpdateAnimatedObjectInfo();
UpdateCanGenerateAnimation();
// ✨ 新功能:动画对象选择时预计算排除列表(线程安全版本)
_ = PrecomputeCollisionExclusionsAsync(value);
// 预计算排除列表(异步)
_ = PrecomputeCollisionExclusionsAsync(value);
}
}
}
@ -418,6 +404,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
_useVirtualVehicle = false;
OnPropertyChanged(nameof(UseVirtualVehicle));
// ✨ 模式切换清理:切换到手动选择模式时,移除存在的虚拟车辆并清理数据
try
{
VirtualVehicleManager.Instance.RemoveVirtualVehicle();
_pathAnimationManager?.ClearAnimationResults(); // 清理虚拟车辆留下的动画数据
LogManager.Info("已切换到手动选择模式,自动隐藏虚拟车辆并清理动画数据");
}
catch (Exception ex)
{
LogManager.Warning($"隐藏虚拟车辆失败: {ex.Message}");
}
}
UpdateCanGenerateAnimation();
}
@ -438,6 +436,24 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
_useSelectedObject = false;
OnPropertyChanged(nameof(UseSelectedObject));
// ✨ 模式切换清理切换到虚拟车辆模式时重置之前手动选择物体的动画状态恢复到CAD位置
try
{
// 只有在当前动画对象不是虚拟车辆时才执行重置
if (_pathAnimationManager != null &&
_pathAnimationManager.AnimatedObject != null &&
!VirtualVehicleManager.Instance.IsVirtualVehicleActive)
{
_pathAnimationManager.RestoreObjectToCADPosition();
_pathAnimationManager.ClearAnimationResults(); // 清理手动选择物体留下的动画数据
LogManager.Info("已切换到虚拟车辆模式,将之前的手动选择物体归位并清理动画数据");
}
}
catch (Exception ex)
{
LogManager.Warning($"重置手动选择物体状态失败: {ex.Message}");
}
}
UpdateCanGenerateAnimation();
}
@ -1080,6 +1096,47 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
/// <summary>
/// 执行选择移动物体命令
/// </summary>
private void ExecuteSelectAnimatedObject()
{
try
{
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
if (doc == null || doc.CurrentSelection.IsEmpty)
{
LogManager.Warning("请先在场景中选择一个物体");
return;
}
var newObject = doc.CurrentSelection.SelectedItems.First;
if (newObject == null) return;
// 1. 如果有旧物体,先归位并清理旧动画
if (_selectedAnimatedObject != null && !ReferenceEquals(_selectedAnimatedObject, newObject))
{
_pathAnimationManager?.RestoreObjectToCADPosition();
}
_pathAnimationManager?.ClearAnimationResults();
// 2. 设置新物体
SelectedAnimatedObject = newObject;
LogManager.Info($"已选择移动物体: {SelectedAnimatedObject.DisplayName}");
// 3. 立即移动到起点(如果路径存在)
if (CurrentPathRoute != null && CurrentPathRoute.Points != null && _pathAnimationManager != null)
{
var points = CurrentPathRoute.Points.Select(p => new Point3D(p.X, p.Y, p.Z)).ToList();
_pathAnimationManager.SyncToPathStart(newObject, points);
}
}
catch (Exception ex)
{
LogManager.Error($"选择物体失败: {ex.Message}");
}
}
/// <summary>
/// 更新媒体控制相关属性
/// </summary>
@ -1330,60 +1387,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
/// <summary>
/// 执行选择移动物体命令
/// </summary>
private void ExecuteSelectAnimatedObject()
{
try
{
LogManager.Info("开始选择移动物体");
// 获取当前选中的模型项
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
var selectedItems = doc.CurrentSelection.SelectedItems;
if (selectedItems.Count == 0)
{
UpdateMainStatus("请先在Navisworks中选择一个物体");
LogManager.Warning("用户未选择任何物体");
return;
}
if (selectedItems.Count > 1)
{
UpdateMainStatus("请只选择一个物体作为移动对象");
LogManager.Warning($"用户选择了{selectedItems.Count}个物体,需要只选择一个");
return;
}
var selectedItem = selectedItems.First;
// 检查选中项是否包含几何体(直接包含或子项包含)
bool hasAnyGeometry = HasGeometryRecursive(selectedItem);
if (!hasAnyGeometry)
{
UpdateMainStatus("选中的项目及其子项都不包含几何体,请选择其他物体");
LogManager.Warning("选中的项目及其子项都不包含几何体");
return;
}
LogManager.Info($"选中物体信息: DisplayName={selectedItem.DisplayName}, HasGeometry={selectedItem.HasGeometry}, ChildCount={selectedItem.Children.Count()}");
// 设置选中的移动物体
SelectedAnimatedObject = selectedItem;
UpdateMainStatus("移动物体选择成功");
LogManager.Info($"移动物体选择成功: {SelectedAnimatedObject.DisplayName}");
}
catch (Exception ex)
{
UpdateMainStatus("选择移动物体失败");
LogManager.Error($"选择移动物体时发生错误: {ex.Message}");
}
}
/// <summary>
/// 执行清除移动物体命令
/// 清除移动物体选择、停止当前动画并更新按钮状态
@ -1392,95 +1395,29 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
try
{
LogManager.Info("开始清除移动物体选择、当前动画并恢复原始位置");
// 首先停止当前动画(如果正在播放)
if (_pathAnimationManager != null && _pathAnimationManager.IsAnimating)
{
try
{
_pathAnimationManager.CancelAnimation();
LogManager.Info("已停止当前播放的动画");
}
catch (Exception stopEx)
{
LogManager.Warning($"停止当前动画时出现警告: {stopEx.Message}");
}
}
// 清除PathAnimationManager中的动画数据将状态重置为Idle
if (_pathAnimationManager != null)
{
try
{
// 重置动画对象到原始位置并清除动画数据
_pathAnimationManager.ResetAnimation();
LogManager.Info("已清除PathAnimationManager中的动画数据");
}
catch (Exception resetEx)
{
LogManager.Warning($"重置PathAnimationManager时出现警告: {resetEx.Message}");
}
// 1. 归位并清理
_pathAnimationManager.RestoreObjectToCADPosition();
_pathAnimationManager.ClearAnimationResults();
LogManager.Info("已清除PathAnimationManager中的动画数据并归位物体");
}
// 如果有选中的物体,则恢复到原始位置
if (SelectedAnimatedObject != null)
{
try
{
LogManager.Info($"开始恢复物体 '{SelectedAnimatedObject.DisplayName}' 到原始位置");
// 使用ResetPermanentTransform清除所有增量变换恢复到设计文件中的原始位置
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
var modelItems = new ModelItemCollection { SelectedAnimatedObject };
doc.Models.ResetPermanentTransform(modelItems);
LogManager.Info($"物体 '{SelectedAnimatedObject.DisplayName}' 已成功恢复到原始位置");
UpdateMainStatus("已清除移动物体选择并恢复到原始位置");
}
catch (Exception restoreEx)
{
LogManager.Error($"恢复物体到原始位置时发生错误: {restoreEx.Message}");
UpdateMainStatus($"已清除物体选择,但恢复位置失败: {restoreEx.Message}");
}
}
else
{
LogManager.Info("没有选中的物体需要清除");
UpdateMainStatus("已清除移动物体选择");
}
// 清理选择状态
// 2. 重置属性
SelectedAnimatedObject = null;
// 清理碰撞检测结果
HasCollisionResults = false;
_latestCollisionResults.Clear();
ModelHighlightHelper.ClearCategory(CollisionResultsHighlightCategory);
UpdateMainStatus("碰撞检测重置");
// 清除高亮构件
try
{
_clashIntegration?.ClearHighlights();
LogManager.Info("已清除高亮构件");
}
catch (Exception highlightEx)
{
LogManager.Warning($"清除高亮构件时出现警告: {highlightEx.Message}");
}
// 更新按钮状态 - 清除动画后应该重新评估按钮状态
UpdateAnimationButtonStates();
// 更新生成动画的能力状态
UpdateAnimatedObjectInfo();
UpdateCanGenerateAnimation();
LogManager.Info("移动物体选择和当前动画已完全清除,按钮状态已更新");
// 3. 清理高亮
_clashIntegration?.ClearHighlights();
ModelHighlightHelper.ClearCategory(CollisionResultsHighlightCategory);
LogManager.Info("移动物体已完全清除并归位");
UpdateMainStatus("已清除物体选择并恢复位置");
}
catch (Exception ex)
{
LogManager.Error($"清除移动物体选择时发生错误: {ex.Message}");
LogManager.Error($"清除失败: {ex.Message}");
UpdateMainStatus($"清除失败: {ex.Message}");
}
}

View File

@ -1,3 +1,4 @@
using System;
using Autodesk.Navisworks.Api;
namespace NavisworksTransport.Utils
@ -41,6 +42,50 @@ namespace NavisworksTransport.Utils
return offset;
}
/// <summary>
/// 从Transform3D中提取Yaw角度绕Z轴旋转单位弧度
/// </summary>
/// <param name="transform">变换矩阵</param>
/// <returns>Yaw角度弧度</returns>
public static double GetYawFromTransform(Transform3D transform)
{
if (transform == null) return 0.0;
try
{
// 获取变换的组件
var components = transform.Factor();
var rotation = components.Rotation;
// 使用ToAxisAndAngle()提取轴和角度
var rotationResult = rotation.ToAxisAndAngle();
double angle = rotationResult.Angle;
double ux = rotationResult.Axis.X;
double uy = rotationResult.Axis.Y;
double uz = rotationResult.Axis.Z;
// 旋转公式罗德里格旋转公式简化版提取局部X轴(1,0,0)旋转后的方向)
// 旋转后的向量 v' = v*cosθ + (u×v)*sinθ + u*(u·v)*(1-cosθ)
// 当 v = (1,0,0) 时:
// v'.x = cosθ + ux*ux*(1-cosθ)
// v'.y = uz*sinθ + ux*uy*(1-cosθ)
double cos = Math.Cos(angle);
double sin = Math.Sin(angle);
// 计算旋转后的X轴在世界坐标系XY平面上的分量
double vx = cos + ux * ux * (1 - cos);
double vy = uz * sin + ux * uy * (1 - cos);
// 在XY平面上通过Atan2计算偏航角
return Math.Atan2(vy, vx);
}
catch (Exception)
{
return 0.0;
}
}
/// <summary>
/// 恢复物体到原位置
/// </summary>