添加物体角度调整功能,新增角度修正窗口及相关逻辑

This commit is contained in:
tian 2026-01-19 22:48:11 +08:00
parent b6dd1ca61b
commit ca503c0448
6 changed files with 487 additions and 48 deletions

View File

@ -235,6 +235,9 @@
<Compile Include="src\UI\WPF\Views\EditCoordinatesWindow.xaml.cs">
<DependentUpon>EditCoordinatesWindow.xaml</DependentUpon>
</Compile>
<Compile Include="src\UI\WPF\Views\EditRotationWindow.xaml.cs">
<DependentUpon>EditRotationWindow.xaml</DependentUpon>
</Compile>
<Compile Include="src\UI\WPF\Views\HoistingHeightDialog.xaml.cs">
<DependentUpon>HoistingHeightDialog.xaml</DependentUpon>
</Compile>
@ -295,6 +298,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="src\UI\WPF\Views\EditRotationWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="src\UI\WPF\Views\ModelSettingsView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View File

@ -97,6 +97,9 @@ namespace NavisworksTransport.Core.Animation
private List<ModelItem> _manualCollisionTargets = new List<ModelItem>();
private bool _manualCollisionOverrideEnabled = false;
// === 角度修正 ===
private double _objectRotationCorrection = 0.0; // 物体角度修正值(度,顺时针)
// === 碰撞排除列表缓存管理从LogisticsAnimationManager迁移===
private ModelItem _currentCachedAnimationObject;
private List<ModelItem> _cachedExclusionList;
@ -385,16 +388,9 @@ namespace NavisworksTransport.Core.Animation
_originalCenter = animatedObject.BoundingBox().Center;
_currentPosition = new Point3D(_originalCenter.X, _originalCenter.Y, animatedObject.BoundingBox().Min.Z);
// 对于虚拟车辆,重置 _currentYaw 为 0因为虚拟车辆刚创建时已经被重置为单位变换
// 对于普通物体,保持原始朝向
if (_isVirtualVehicle)
{
_currentYaw = 0.0;
}
else
{
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform);
}
// 统一逻辑:从当前 Transform 中提取实际朝向(无论虚拟车辆还是普通物体)
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform);
LogManager.Debug($"[MoveVehicleToPathStart初始化] 当前实际yaw={_currentYaw * 180 / Math.PI:F2}度, _isVirtualVehicle={_isVirtualVehicle}");
}
if (pathPoints != null)
@ -412,6 +408,8 @@ namespace NavisworksTransport.Core.Animation
// 计算朝向(使用前两个路径点的方向)
double pathDirectionYaw = Math.Atan2(_pathPoints[1].Y - _pathPoints[0].Y, _pathPoints[1].X - _pathPoints[0].X);
double yaw;
LogManager.Info($"[移动到起点] 路径方向yaw: {pathDirectionYaw * 180 / Math.PI:F2}度, 角度修正: {_objectRotationCorrection:F1}度");
// 根据路径类型调整朝向
if (_route?.PathType == PathType.Hoisting)
@ -427,6 +425,14 @@ namespace NavisworksTransport.Core.Animation
LogManager.Debug($"[移动到起点] 地面/空轨路径使用路径方向: {yaw * 180 / Math.PI:F2}度");
}
// 应用角度修正值(顺时针,转换为弧度)
if (_objectRotationCorrection != 0.0)
{
double correctionRad = _objectRotationCorrection * Math.PI / 180.0;
yaw += correctionRad;
LogManager.Debug($"[移动到起点] 应用角度修正: {_objectRotationCorrection:F1}°, 修正后yaw: {yaw * 180 / Math.PI:F2}度");
}
// 根据路径类型调整起点位置
Point3D startPosition = _pathPoints[0];
if (_route?.PathType == PathType.Hoisting)
@ -730,6 +736,12 @@ namespace NavisworksTransport.Core.Animation
Collisions = new List<CollisionResult>()
};
// 记录第一帧的角度(用于调试)
if (i == 0)
{
LogManager.Debug($"[预计算] 第0帧: pos=({framePosition.X:F2},{framePosition.Y:F2}), yaw={yawRadians * 180 / Math.PI:F2}度");
}
// 虚拟碰撞检测
// 计算车辆包围盒相对于framePosition的偏移
@ -1116,9 +1128,11 @@ namespace NavisworksTransport.Core.Animation
{
var firstFrame = _animationFrames[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($"[动画开始] _currentYaw之前={_currentYaw * 180 / Math.PI:F2}度, _isVirtualVehicle={_isVirtualVehicle}");
// 物体已经在 MoveVehicleToPathStart 中被正确旋转到起点(包含角度修正)
// 不需要再次旋转,否则会破坏角度修正
LogManager.Debug($"[动画开始] 跳过初始位置设置,物体已在起点: pos=({firstFrame.Position.X:F2},{firstFrame.Position.Y:F2}), yaw={firstFrame.YawRadians:F3}rad");
}
else
{
@ -1796,6 +1810,7 @@ namespace NavisworksTransport.Core.Animation
// 我们需要手动计算旋转导致的位置偏移,并补偿
double deltaYaw = newYaw - _currentYaw;
LogManager.Debug($"[UpdateObjectPosition] 当前yaw={_currentYaw * 180 / Math.PI:F2}度, 目标yaw={newYaw * 180 / Math.PI:F2}度, 旋转增量deltaYaw={deltaYaw * 180 / Math.PI:F2}度");
// 计算绕当前位置旋转的等效变换:
// 1. 如果绕原点旋转deltaYaw当前位置_currentPosition会移动到哪里
@ -1823,17 +1838,17 @@ namespace NavisworksTransport.Core.Animation
_currentYaw = newYaw;
}
else
{
// 纯平移
incrementalTransform = Transform3D.CreateTranslation(deltaPos);
}
// 应用增量变换false = 增量模式)
doc.Models.OverridePermanentTransform(modelItems, incrementalTransform, false);
// 更新当前位置
_currentPosition = newPosition;
}
{
// 纯平移
incrementalTransform = Transform3D.CreateTranslation(deltaPos);
LogManager.Debug($"[UpdateObjectPosition] 纯平移: ({deltaPos.X:F2},{deltaPos.Y:F2},{deltaPos.Z:F2})");
}
// 应用增量变换false = 增量模式)
doc.Models.OverridePermanentTransform(modelItems, incrementalTransform, false);
// 更新当前位置
_currentPosition = newPosition; }
catch (Exception ex)
{
LogManager.Error($"更新部件位置失败: {ex.Message}");
@ -2477,9 +2492,53 @@ namespace NavisworksTransport.Core.Animation
_virtualVehicleLength = virtualVehicleLength;
_virtualVehicleWidth = virtualVehicleWidth;
_virtualVehicleHeight = virtualVehicleHeight;
_pathPoints = pathPoints;
_animatedObject = animatedObject;
SetupAnimation(animatedObject, durationSeconds, _route);
// 初始化物体位置和朝向
if (animatedObject != null)
{
_originalTransform = animatedObject.Transform;
_originalCenter = animatedObject.BoundingBox().Center;
_currentPosition = new Point3D(_originalCenter.X, _originalCenter.Y, animatedObject.BoundingBox().Min.Z);
// 保持当前的 _currentYaw因为物体可能已经被 MoveVehicleToPathStart 旋转)
// 不要从 Transform 中提取,因为 Transform 返回的是原始值,不是当前值
LogManager.Debug($"[CreateAnimation] 保持_currentYaw={_currentYaw * 180 / Math.PI:F2}度不变, _isVirtualVehicle={_isVirtualVehicle}");
}
// 设置动画参数并预计算动画帧
// 注意物体应该在调用CreateAnimation之前已经通过MoveVehicleToPathStart旋转到起点
LogManager.Debug($"[CreateAnimation开始] _currentYaw之前={_currentYaw * 180 / Math.PI:F2}度, _isVirtualVehicle={_isVirtualVehicle}");
SetupAnimation(animatedObject, durationSeconds, _route);
LogManager.Debug($"[CreateAnimation结束] _currentYaw之后={_currentYaw * 180 / Math.PI:F2}度");
// 设置动画状态为Ready动画已生成可以播放
SetState(AnimationState.Ready);
LogManager.Info($"[CreateAnimation] 动画已创建状态设置为Ready");
}
/// <summary>
/// 设置物体角度修正值(度,顺时针)
/// </summary>
/// <param name="rotationCorrection">角度修正值(度)</param>
public void SetObjectRotationCorrection(double rotationCorrection)
{
_objectRotationCorrection = rotationCorrection;
// 如果动画已创建,更新物体到起点的朝向
if (_animatedObject != null && _pathPoints != null && _pathPoints.Count > 0)
{
try
{
// 重新计算并应用朝向
MoveVehicleToPathStart();
LogManager.Info($"[角度修正] 物体角度已更新: {_objectRotationCorrection:F1}°");
}
catch (Exception ex)
{
LogManager.Error($"[角度修正] 更新物体角度失败: {ex.Message}");
}
}
}
#region
@ -2616,7 +2675,16 @@ namespace NavisworksTransport.Core.Animation
if (_currentFrameIndex < _animationFrames.Count)
{
var frameData = _animationFrames[_currentFrameIndex];
UpdateObjectPosition(frameData.Position, frameData.YawRadians);
// 计算实际朝向 = 路径方向 + 角度修正
double actualYaw = frameData.YawRadians;
if (_objectRotationCorrection != 0.0)
{
double correctionRad = _objectRotationCorrection * Math.PI / 180.0;
actualYaw += correctionRad;
}
UpdateObjectPosition(frameData.Position, actualYaw);
// 更新碰撞高亮(基于预计算结果)
UpdateCollisionHighlightFromFrame();

View File

@ -233,6 +233,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private double _virtualVehicleHeight; // 虚拟车辆高度(米)
private double _safetyMargin; // 检测间隙(米),从路径编辑同步
// 角度修正相关字段
private double _objectRotationCorrection = 0.0; // 物体角度修正值(度,顺时针)
// 移动物体原始尺寸(米,在选择物体时保存)
private double _objectOriginalLength = 0.0; // 物体原始长度X方向
private double _objectOriginalWidth = 0.0; // 物体原始宽度Y方向
private double _objectOriginalHeight = 0.0; // 物体原始高度Z方向
// 手工碰撞对象相关字段
private bool _isManualCollisionTargetEnabled = false;
private ObservableCollection<ManualCollisionTargetViewModel> _manualCollisionTargets;
@ -548,8 +556,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
_pathAnimationManager?.MoveVehicleToPathStart(vModel, points);
}
// 重置角度修正值(虚拟车辆重新选择时重置)
ObjectRotationCorrection = 0.0;
LogManager.Debug($"[切换虚拟车辆] 已重置角度修正值为0度");
// 当切换到虚拟车辆模式时,根据路径类型打开通行空间可视化
UpdatePassageSpaceVisualization();
// 设置已选择物体状态(虚拟车辆也算已选择)
HasSelectedAnimatedObject = true;
}
catch (Exception ex)
{
@ -612,6 +627,22 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private set => SetProperty(ref _safetyMargin, value);
}
/// <summary>
/// 物体角度修正值(度,顺时针)
/// </summary>
public double ObjectRotationCorrection
{
get => _objectRotationCorrection;
set
{
if (SetProperty(ref _objectRotationCorrection, value))
{
// 角度修正值改变时,更新物体朝向和通行空间
UpdateObjectRotation();
}
}
}
/// <summary>
/// 设置虚拟车辆参数由主ViewModel调用
/// </summary>
@ -769,6 +800,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public ICommand ViewCollisionReportCommand { get; private set; }
public ICommand SelectAnimatedObjectCommand { get; private set; }
public ICommand ClearAnimatedObjectCommand { get; private set; }
public ICommand EditObjectRotationCommand { get; private set; }
public ICommand GenerateAnimationCommand { get; private set; }
public ICommand ApplyManualTargetsFromSelectionCommand { get; private set; }
public ICommand ClearManualTargetsCommand { get; private set; }
@ -910,8 +942,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
PauseAnimationCommand = new RelayCommand(async () => await ExecutePauseAnimationAsync(), () => CanPauseAnimation);
StopAnimationCommand = new RelayCommand(async () => await ExecuteStopAnimationAsync(), () => CanStopAnimation);
ViewCollisionReportCommand = new RelayCommand(async () => await ExecuteViewCollisionReport(), () => HasClashDetectiveResults);
SelectAnimatedObjectCommand = new RelayCommand(ExecuteSelectAnimatedObject);
SelectAnimatedObjectCommand = new RelayCommand(() =>
{
// 重置角度修正值(每个物体的旋转独立)
ObjectRotationCorrection = 0.0;
LogManager.Debug($"[选择物体] 已重置角度修正值为0度");
ExecuteSelectAnimatedObject();
});
ClearAnimatedObjectCommand = new RelayCommand(ExecuteClearAnimatedObject, () => HasSelectedAnimatedObject);
EditObjectRotationCommand = new RelayCommand(ExecuteEditObjectRotation, () => HasSelectedAnimatedObject);
GenerateAnimationCommand = new RelayCommand(ExecuteGenerateAnimation, () => CanGenerateAnimation);
ApplyManualTargetsFromSelectionCommand = new RelayCommand(ExecuteApplyManualTargetsFromSelection);
ClearManualTargetsCommand = new RelayCommand(ExecuteClearManualTargets, () => HasManualCollisionTargets);
@ -1285,17 +1324,32 @@ namespace NavisworksTransport.UI.WPF.ViewModels
var newObject = doc.CurrentSelection.SelectedItems.First;
if (newObject == null) return;
// 1. 如果有旧物体,先归位并清理旧动画
if (_selectedAnimatedObject != null && !ReferenceEquals(_selectedAnimatedObject, newObject))
// 1. 保存旧物体引用,在设置新物体之前
var oldObject = _selectedAnimatedObject;
// 2. 如果有旧物体且与新物体不同,先归位
if (oldObject != null && !ReferenceEquals(oldObject, newObject))
{
_pathAnimationManager?.RestoreObjectToCADPosition();
LogManager.Info($"[选择物体] 已归位旧物体: {oldObject.DisplayName}");
}
_pathAnimationManager?.ClearAnimationResults();
// 2. 设置新物体
// 2. 先保存物体原始尺寸在设置SelectedAnimatedObject之前
var bbox = newObject.BoundingBox();
double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor();
_objectOriginalLength = (bbox.Max.X - bbox.Min.X) / metersToModelUnits;
_objectOriginalWidth = (bbox.Max.Y - bbox.Min.Y) / metersToModelUnits;
_objectOriginalHeight = (bbox.Max.Z - bbox.Min.Z) / metersToModelUnits;
LogManager.Debug($"[选择物体] 保存原始尺寸: 长度={_objectOriginalLength:F2}m, 宽度={_objectOriginalWidth:F2}m, 高度={_objectOriginalHeight:F2}m");
// 3. 设置新物体会触发UpdatePassageSpaceVisualization
SelectedAnimatedObject = newObject;
LogManager.Info($"已选择移动物体: {SelectedAnimatedObject.DisplayName}");
// 重置PathAnimationManager的角度修正值每个物体的旋转独立
_pathAnimationManager?.SetObjectRotationCorrection(0.0);
// 3. 立即移动到起点(如果路径存在)
if (CurrentPathRoute != null && CurrentPathRoute.Points != null && _pathAnimationManager != null)
{
@ -1653,6 +1707,54 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
/// <summary>
/// 执行编辑物体角度修正
/// </summary>
private void ExecuteEditObjectRotation()
{
try
{
var dialog = new Views.EditRotationWindow(_objectRotationCorrection);
if (dialog.ShowDialog() == true)
{
ObjectRotationCorrection = dialog.RotationAngle;
LogManager.Info($"物体角度修正已更新: {_objectRotationCorrection:F1}°");
UpdateMainStatus($"物体角度修正: {_objectRotationCorrection:F1}°");
// 应用角度修正到物体
UpdateObjectRotation();
// 更新通行空间可视化(考虑旋转后的尺寸)
UpdatePassageSpaceVisualization();
}
}
catch (Exception ex)
{
LogManager.Error($"编辑物体角度失败: {ex.Message}");
UpdateMainStatus($"编辑物体角度失败: {ex.Message}");
}
}
/// <summary>
/// 更新物体旋转(应用角度修正)
/// </summary>
private void UpdateObjectRotation()
{
try
{
if (_pathAnimationManager != null && HasSelectedAnimatedObject)
{
// 将角度修正传递给动画管理器
_pathAnimationManager.SetObjectRotationCorrection(_objectRotationCorrection);
LogManager.Debug($"[角度修正] 已更新物体旋转: {_objectRotationCorrection:F1}°");
}
}
catch (Exception ex)
{
LogManager.Error($"更新物体旋转失败: {ex.Message}");
}
}
#region
private void ExecuteApplyManualTargetsFromSelection()
@ -2408,6 +2510,51 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
/// <summary>
/// 计算旋转后的有效宽度和长度
/// </summary>
/// <returns>(有效宽度, 有效长度)</returns>
private (double effectiveWidth, double effectiveLength) CalculateRotatedDimensions()
{
double baseLength, baseWidth;
if (UseVirtualVehicle)
{
baseLength = VirtualVehicleLength;
baseWidth = VirtualVehicleWidth;
}
else if (SelectedAnimatedObject != null)
{
// 使用保存的原始尺寸,避免物体旋转后包围盒尺寸变化
baseLength = _objectOriginalLength;
baseWidth = _objectOriginalWidth;
}
else
{
return (0, 0);
}
// 如果没有角度修正,直接返回原始尺寸
if (_objectRotationCorrection == 0.0)
{
return (baseWidth, baseLength);
}
// 计算旋转后的有效尺寸(投影)
double rotationRad = _objectRotationCorrection * Math.PI / 180.0;
double cos = Math.Abs(Math.Cos(rotationRad));
double sin = Math.Abs(Math.Sin(rotationRad));
// 旋转后的宽度 = |宽度*cos| + |长度*sin|
double effectiveWidth = baseWidth * cos + baseLength * sin;
// 旋转后的长度 = |长度*cos| + |宽度*sin|
double effectiveLength = baseLength * cos + baseWidth * sin;
LogManager.Debug($"[角度修正] 旋转后尺寸: 原始({baseLength:F2}×{baseWidth:F2}) -> 有效({effectiveLength:F2}×{effectiveWidth:F2}), 角度={_objectRotationCorrection:F1}°");
return (effectiveWidth, effectiveLength);
}
/// <summary>
/// 根据路径类型更新通行空间可视化
/// 空轨和吊装路径默认打开通行空间(车辆通行空间模式),地面路径不打开
@ -2423,6 +2570,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
return;
}
// 计算旋转后的有效尺寸(考虑角度修正)
(double effectiveWidth, double effectiveLength) = CalculateRotatedDimensions();
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
@ -2441,28 +2591,28 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (CurrentPathRoute.PathType == NavisworksTransport.PathType.Hoisting)
{
// 吊装路径:通行空间四面都加检测间隙
passageAcrossPath = VirtualVehicleLength + 2 * safetyMargin; // X方向长度+ 2*间隙(垂直于路径方向)
passageNormalToPath = VirtualVehicleWidth + 2 * safetyMargin; // Y方向宽度+ 2*间隙(法线方向)
passageAcrossPath = effectiveLength + 2 * safetyMargin; // 旋转后长度 + 2*间隙(垂直于路径方向)
passageNormalToPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(法线方向)
passageAlongPath = VirtualVehicleHeight + 2 * safetyMargin; // Z方向高度+ 2*间隙(沿路径方向)
LogManager.Info($"[通行空间可视化] 吊装路径使用虚拟车辆尺寸: X方向(长度)={VirtualVehicleLength:F2}m, Y方向(宽度)={VirtualVehicleWidth:F2}m, Z方向(高度)={VirtualVehicleHeight:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
LogManager.Info($"[通行空间可视化] 吊装路径使用虚拟车辆尺寸: 有效长度={effectiveLength:F2}m, 有效宽度={effectiveWidth:F2}m, 高度={VirtualVehicleHeight:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
}
else if (CurrentPathRoute.PathType == NavisworksTransport.PathType.Ground)
{
// 地面路径物体的长度方向X轴前进方向朝向路径方向
// 通行空间沿路径方向 = X方向尺寸长度垂直于路径方向 = Y方向尺寸宽度高度上方加间隙下方不需要因为有地面
passageAcrossPath = VirtualVehicleWidth + 2 * safetyMargin; // Y方向宽度+ 2*间隙(垂直于路径方向)
passageAcrossPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(垂直于路径方向)
passageNormalToPath = VirtualVehicleHeight + safetyMargin; // Z方向高度+ 间隙(法线方向,只有上方)
passageAlongPath = VirtualVehicleLength; // X方向长度沿路径方向)
LogManager.Info($"[通行空间可视化] 地面路径使用虚拟车辆尺寸: X方向(长度)={VirtualVehicleLength:F2}m, Y方向(宽度)={VirtualVehicleWidth:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
passageAlongPath = effectiveLength; // 旋转后长度(沿路径方向)
LogManager.Info($"[通行空间可视化] 地面路径使用虚拟车辆尺寸: 有效长度={effectiveLength:F2}m, 有效宽度={effectiveWidth:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
}
else // Rail
{
// 空轨路径物体的长度方向X轴前进方向朝向路径方向
// 通行空间沿路径方向 = X方向尺寸长度垂直于路径方向 = Y方向尺寸宽度高度上下都加间隙在空中
passageAcrossPath = VirtualVehicleWidth + 2 * safetyMargin; // Y方向宽度+ 2*间隙(垂直于路径方向)
passageAcrossPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(垂直于路径方向)
passageNormalToPath = VirtualVehicleHeight + 2 * safetyMargin; // Z方向高度+ 2*间隙(法线方向,上下都加)
passageAlongPath = VirtualVehicleLength; // X方向长度沿路径方向)
LogManager.Info($"[通行空间可视化] 空轨路径使用虚拟车辆尺寸: X方向(长度)={VirtualVehicleLength:F2}m, Y方向(宽度)={VirtualVehicleWidth:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
passageAlongPath = effectiveLength; // 旋转后长度(沿路径方向)
LogManager.Info($"[通行空间可视化] 空轨路径使用虚拟车辆尺寸: 有效长度={effectiveLength:F2}m, 有效宽度={effectiveWidth:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
}
}
else if (SelectedAnimatedObject != null)
@ -2481,30 +2631,30 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
// 吊装路径(严格垂直路径):
// 通行空间四面都加检测间隙
passageAcrossPath = sizeX + 2 * safetyMargin; // X方向长度+ 2*间隙(垂直于路径方向)
passageNormalToPath = sizeY + 2 * safetyMargin; // Y方向宽度+ 2*间隙(法线方向)
passageAcrossPath = effectiveLength + 2 * safetyMargin; // 旋转后长度 + 2*间隙(垂直于路径方向)
passageNormalToPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(法线方向)
passageAlongPath = sizeZ + 2 * safetyMargin; // Z方向高度+ 2*间隙(沿路径方向)
LogManager.Info($"[通行空间可视化] 吊装路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): X方向(长度)={sizeX:F2}m, Y方向(宽度)={sizeY:F2}m, Z方向(高度)={sizeZ:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
LogManager.Info($"[通行空间可视化] 吊装路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): 有效长度={effectiveLength:F2}m, 有效宽度={effectiveWidth:F2}m, 高度={sizeZ:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
}
else if (CurrentPathRoute.PathType == NavisworksTransport.PathType.Ground)
{
// 地面路径(可能有坡度):
// 物体的长度方向X轴前进方向朝向路径方向
// 通行空间沿路径方向 = X方向尺寸长度垂直于路径方向 = Y方向尺寸宽度高度上方加间隙下方不需要因为有地面
passageAcrossPath = sizeY + 2 * safetyMargin; // Y方向宽度+ 2*间隙(垂直于路径方向)
passageAcrossPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(垂直于路径方向)
passageNormalToPath = sizeZ + safetyMargin; // Z方向高度+ 间隙(法线方向,只有上方)
passageAlongPath = sizeX; // X方向长度沿路径方向)
LogManager.Info($"[通行空间可视化] 地面路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): X方向(长度)={sizeX:F2}m, Y方向(宽度)={sizeY:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
passageAlongPath = effectiveLength; // 旋转后长度(沿路径方向)
LogManager.Info($"[通行空间可视化] 地面路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): 有效长度={effectiveLength:F2}m, 有效宽度={effectiveWidth:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
}
else // Rail
{
// 空轨路径(可能有坡度):
// 物体的长度方向X轴前进方向朝向路径方向
// 通行空间沿路径方向 = X方向尺寸长度垂直于路径方向 = Y方向尺寸宽度高度上下都加间隙在空中
passageAcrossPath = sizeY + 2 * safetyMargin; // Y方向宽度+ 2*间隙(垂直于路径方向)
passageAcrossPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(垂直于路径方向)
passageNormalToPath = sizeZ + 2 * safetyMargin; // Z方向高度+ 2*间隙(法线方向,上下都加)
passageAlongPath = sizeX; // X方向长度沿路径方向)
LogManager.Info($"[通行空间可视化] 空轨路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): X方向(长度)={sizeX:F2}m, Y方向(宽度)={sizeY:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
passageAlongPath = effectiveLength; // 旋转后长度(沿路径方向)
LogManager.Info($"[通行空间可视化] 空轨路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): 有效长度={effectiveLength:F2}m, 有效宽度={effectiveWidth:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
}
}
else

View File

@ -258,6 +258,10 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
<Button Content="选择物体"
Command="{Binding SelectAnimatedObjectCommand}"
Style="{StaticResource SecondaryButtonStyle}"/>
<Button Content="调整角度"
Command="{Binding EditObjectRotationCommand}"
IsEnabled="{Binding HasSelectedAnimatedObject}"
Style="{StaticResource SecondaryButtonStyle}"/>
<Button Content="清除"
Command="{Binding ClearAnimatedObjectCommand}"
IsEnabled="{Binding HasSelectedAnimatedObject}"
@ -273,6 +277,7 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="虚拟车辆:" Style="{StaticResource ParameterLabelStyle}"/>
@ -289,6 +294,11 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
<Run Text="{Binding VirtualVehicleHeight, StringFormat=F1, Mode=OneWay}"/>
<Run Text="m"/>
</TextBlock>
<Button Grid.Column="2"
Content="调整角度"
Command="{Binding EditObjectRotationCommand}"
Style="{StaticResource SecondaryButtonStyle}"
Margin="10,0,0,0"/>
</Grid>
<!-- 虚拟车辆提示 -->

View File

@ -0,0 +1,112 @@
<Window x:Class="NavisworksTransport.UI.WPF.Views.EditRotationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="调整物体角度" Height="320" Width="400"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
ShowInTaskbar="False"
Topmost="True"
Background="White">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/NavisworksTransportPlugin;component/src/UI/WPF/Resources/NavisworksStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 标题栏 -->
<Border Grid.Row="0" Background="#FFF8FBFF" BorderBrush="#FFD4E7FF" BorderThickness="0,0,0,1" Padding="20,15">
<StackPanel>
<TextBlock Text="调整物体角度" FontWeight="SemiBold" FontSize="14" Foreground="#FF2B579A"/>
<TextBlock Text="调整物体在路径起点时的水平旋转角度(顺时针)" FontSize="10" Foreground="#FF666666" Margin="0,3,0,0"/>
</StackPanel>
</Border>
<!-- 输入区域 -->
<Border Grid.Row="1" Padding="20,15">
<StackPanel>
<!-- 角度输入 -->
<Grid Margin="0,0,0,12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<TextBlock Text="旋转角度:" VerticalAlignment="Center" FontSize="11" Foreground="#FF333333"/>
<TextBox Grid.Column="1"
Text="{Binding RotationAngle, StringFormat=0.0, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Height="28"
VerticalContentAlignment="Center"
Padding="8,0"
FontSize="11"
BorderBrush="#FFCCCCCC"
BorderThickness="1"/>
<TextBlock Grid.Column="2" Text="°" VerticalAlignment="Center" FontSize="11" Foreground="#FF666666" Margin="8,0,0,0"/>
</Grid>
<!-- 快捷按钮 -->
<TextBlock Text="快捷角度:" FontSize="10" Foreground="#FF666666" Margin="0,0,0,8"/>
<StackPanel Orientation="Horizontal">
<Button Content="0°"
Click="OnQuickAngleClick"
Tag="0"
Style="{StaticResource SecondaryButtonStyle}"
Width="70"
Height="28"
Margin="0,0,8,0"/>
<Button Content="90°"
Click="OnQuickAngleClick"
Tag="90"
Style="{StaticResource SecondaryButtonStyle}"
Width="70"
Height="28"
Margin="0,0,8,0"/>
<Button Content="180°"
Click="OnQuickAngleClick"
Tag="180"
Style="{StaticResource SecondaryButtonStyle}"
Width="70"
Height="28"
Margin="0,0,8,0"/>
<Button Content="270°"
Click="OnQuickAngleClick"
Tag="270"
Style="{StaticResource SecondaryButtonStyle}"
Width="70"
Height="28"/>
</StackPanel>
</StackPanel>
</Border>
<!-- 按钮栏 -->
<Border Grid.Row="2" Background="#FFF8FBFF" BorderBrush="#FFD4E7FF" BorderThickness="0,1,0,0" Padding="20,12">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="恢复初始值"
Click="OnResetClick"
Style="{StaticResource SecondaryButtonStyle}"
Width="90"
Height="32"
Margin="0,0,10,0"/>
<Button Content="确认"
Click="OnConfirmClick"
Style="{StaticResource ActionButtonStyle}"
Width="90"
Height="32"
Margin="0,0,10,0"/>
<Button Content="取消"
Click="OnCancelClick"
Style="{StaticResource SecondaryButtonStyle}"
Width="90"
Height="32"/>
</StackPanel>
</Border>
</Grid>
</Window>

View File

@ -0,0 +1,92 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using NavisworksTransport.Utils;
namespace NavisworksTransport.UI.WPF.Views
{
public partial class EditRotationWindow : Window, INotifyPropertyChanged
{
private double _rotationAngle;
public double RotationAngle
{
get => _rotationAngle;
set
{
if (_rotationAngle != value)
{
_rotationAngle = value;
OnPropertyChanged();
}
}
}
public EditRotationWindow(double currentAngle)
{
try
{
InitializeComponent();
RotationAngle = currentAngle;
DataContext = this;
}
catch (Exception ex)
{
LogManager.Error($"角度修正对话框初始化失败: {ex.Message}", ex);
MessageBox.Show($"角度修正对话框初始化失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void OnQuickAngleClick(object sender, RoutedEventArgs e)
{
if (sender is System.Windows.Controls.Button button)
{
LogManager.Debug($"[快捷角度] 点击按钮: {button.Content}, Tag类型: {button.Tag?.GetType()}, Tag值: {button.Tag}");
if (button.Tag != null)
{
if (button.Tag is int intTag)
{
RotationAngle = intTag;
LogManager.Debug($"[快捷角度] 设置角度为: {intTag}°");
}
else if (button.Tag is string stringTag && double.TryParse(stringTag, out double angle))
{
RotationAngle = angle;
LogManager.Debug($"[快捷角度] 设置角度为: {angle}°");
}
else if (button.Tag is double doubleTag)
{
RotationAngle = doubleTag;
LogManager.Debug($"[快捷角度] 设置角度为: {doubleTag}°");
}
}
}
}
private void OnResetClick(object sender, RoutedEventArgs e)
{
RotationAngle = 0.0;
}
private void OnConfirmClick(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
private void OnCancelClick(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}