0.15.2 WIP: 自动调整 — 门型偏好、物流车高、上下偏移自动计算、性能优化、日志清理

This commit is contained in:
tian 2026-05-28 23:58:27 +08:00
parent e224e332a3
commit 649c7b50f1
6 changed files with 165 additions and 37 deletions

View File

@ -484,7 +484,7 @@ namespace NavisworksTransport.Core.Animation
string objectName = isVirtual ? "虚拟物体" : objectToRestore.DisplayName;
_railPreservedPoseTrackedCenterOffset = new Vector3D(0, 0, 0);
_hasRailPreservedPoseTrackedCenterOffset = false;
LogManager.Info($"[归位] {objectName} 已彻底恢复到原始位置, yaw={_currentYaw:F3}");
LogManager.Debug($"[归位] {objectName} 已彻底恢复到原始位置, yaw={_currentYaw:F3}");
}
catch (Exception ex)
{
@ -666,7 +666,6 @@ namespace NavisworksTransport.Core.Animation
if (CurrentControlledObject != null || IsVirtualObjectMode)
{
RestoreObjectToCADPosition();
LogManager.Info($"[移动到起点] 已恢复物体到原始状态, _currentYaw={_currentYaw * 180 / Math.PI:F2}°");
}
// 如果提供了参数,更新内部状态
@ -714,11 +713,6 @@ namespace NavisworksTransport.Core.Animation
IsRealObjectMode &&
(_route.PathType == PathType.Ground || _route.PathType == PathType.Hoisting);
if (shouldUsePlanarPureIncrementStart)
{
LogManager.Info($"[移动到起点] {_route.PathType.GetDisplayName()} 真实物体起点改用纯增量姿态主链");
}
// 根据路径类型调整起点位置
Point3D pathStartPoint = _pathPoints[0];
Point3D startPosition = pathStartPoint;
@ -772,17 +766,17 @@ namespace NavisworksTransport.Core.Animation
LogManager.Debug($"[移动到起点] 地面路径中心点=({startPosition.X:F2},{startPosition.Y:F2},{startPosition.Z:F2})");
}
LogManager.Info(
LogManager.Debug(
$"[路径起点诊断] 路径point0=({pathStartPoint.X:F3},{pathStartPoint.Y:F3},{pathStartPoint.Z:F3}), " +
$"起点目标trackedPoint=({startPosition.X:F3},{startPosition.Y:F3},{startPosition.Z:F3}), 路径类型={_route.PathType.GetDisplayName()}");
if (shouldUsePlanarPureIncrementStart &&
TryApplyPlanarRealObjectStartIncrementalTransform(_route.PathType, startPosition, out double planarTargetYawRadians))
{
LogManager.Info($"[移动到起点] {_route.PathType.GetDisplayName()} 真实物体已应用纯增量旋转+平移");
LogManager.Debug($"[移动到起点] {_route.PathType.GetDisplayName()} 真实物体已应用纯增量旋转+平移");
var startAppliedPoint = GetLiveBoundingBoxCenter(CurrentControlledObject ?? _animatedObject);
LogManager.Info(
LogManager.Debug(
$"[路径起点诊断] 起点应用后实际包围盒中心=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), " +
$"相对目标偏差=({startAppliedPoint.X - startPosition.X:F3},{startAppliedPoint.Y - startPosition.Y:F3},{startAppliedPoint.Z - startPosition.Z:F3})");
@ -795,17 +789,14 @@ namespace NavisworksTransport.Core.Animation
startOffset.Y * startOffset.Y +
startOffset.Z * startOffset.Z);
LogManager.Info(
$"[路径起点诊断] 落位偏差=({startOffset.X:F3},{startOffset.Y:F3},{startOffset.Z:F3}), 长度={startOffsetLength:F3} (仅记录,不二次补偿)");
if (_route.PathType == PathType.Ground)
{
_groundRealObjectBaseRotation = _trackedRotation;
_groundRealObjectBaseYaw = planarTargetYawRadians;
_hasGroundRealObjectBasePose = true;
LogManager.Info(
$"[Ground真实物体基姿态] {animatedObject.DisplayName} BaseYaw={_groundRealObjectBaseYaw * 180.0 / Math.PI:F2}°, " +
LogManager.Debug(
$"[Ground真实物体基姿态] {(CurrentControlledObject ?? _animatedObject)?.DisplayName ?? "()"} BaseYaw={_groundRealObjectBaseYaw * 180.0 / Math.PI:F2}°, " +
$"已记录基姿态={_hasGroundRealObjectBasePose} (纯增量链)");
}
else
@ -822,10 +813,10 @@ namespace NavisworksTransport.Core.Animation
TryCreatePlanarPathRotationAtStart(out var planarRotation))
{
ApplyFullPoseUpdate(startPosition, planarRotation);
LogManager.Info("[移动到起点] 地面/吊装路径已应用完整三维姿态");
LogManager.Debug("[移动到起点] 地面/吊装路径已应用完整三维姿态");
var startAppliedPoint = GetLiveBoundingBoxCenter(CurrentControlledObject ?? _animatedObject);
LogManager.Info(
LogManager.Debug(
$"[路径起点诊断] 起点应用后实际包围盒中心=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), " +
$"相对目标偏差=({startAppliedPoint.X - startPosition.X:F3},{startAppliedPoint.Y - startPosition.Y:F3},{startAppliedPoint.Z - startPosition.Z:F3})");
@ -841,17 +832,14 @@ namespace NavisworksTransport.Core.Animation
startOffset.Y * startOffset.Y +
startOffset.Z * startOffset.Z);
LogManager.Info(
$"[路径起点诊断] 落位偏差=({startOffset.X:F3},{startOffset.Y:F3},{startOffset.Z:F3}), 长度={startOffsetLength:F3} (仅记录,不二次补偿)");
}
if (IsRealObjectMode && _route.PathType == PathType.Ground)
{
_groundRealObjectBaseRotation = planarRotation;
_hasGroundRealObjectBasePose = TryResolveGroundRealObjectBaseYaw(out _groundRealObjectBaseYaw);
LogManager.Info(
$"[Ground真实物体基姿态] {animatedObject.DisplayName} BaseYaw={_groundRealObjectBaseYaw * 180.0 / Math.PI:F2}°, " +
LogManager.Debug(
$"[Ground真实物体基姿态] {(CurrentControlledObject ?? _animatedObject)?.DisplayName ?? "()"} BaseYaw={_groundRealObjectBaseYaw * 180.0 / Math.PI:F2}°, " +
$"已记录基姿态={_hasGroundRealObjectBasePose} (旋转偏移由逐帧方法内部处理)");
}
else if (IsRealObjectMode && _route.PathType == PathType.Hoisting)
@ -873,8 +861,6 @@ namespace NavisworksTransport.Core.Animation
return false;
}
string pathTypeName = _route.PathType.GetDisplayName();
LogManager.Info($"物体已初始化到路径起点并对齐朝向: pos=({startPosition.X:F2},{startPosition.Y:F2},{startPosition.Z:F2}), 路径类型={pathTypeName}");
return true;
}
catch (Exception ex)
@ -4389,7 +4375,7 @@ namespace NavisworksTransport.Core.Animation
if (Math.Abs(angleXRadians) > 1e-9 || Math.Abs(angleNonUpRadians) > 1e-9 || Math.Abs(deltaYawRadians) > 1e-9)
{
LogManager.Info(
LogManager.Debug(
$"[{pathType.GetDisplayName()}纯增量起点] 当前Yaw={currentYawRadians * 180.0 / Math.PI:F2}°, " +
$"目标Yaw={targetYawRadians * 180.0 / Math.PI:F2}°, 增量Yaw={deltaYawRadians * 180.0 / Math.PI:F2}°, " +
$"当前跟踪点=({_trackedPosition.X:F3},{_trackedPosition.Y:F3},{_trackedPosition.Z:F3}), " +
@ -5334,7 +5320,7 @@ namespace NavisworksTransport.Core.Animation
_currentYaw = ModelItemTransformHelper.GetYawFromRotation(_trackedRotation);
var linear = new Transform3D(_trackedRotation).Linear;
LogManager.Info(
LogManager.Debug(
$"[真实物体当前姿态] {sourceObject.DisplayName} Ground 路径已改用实际几何姿态同步,不再读取 fragment 参考姿态: " +
$"X=({linear.Get(0, 0):F4},{linear.Get(1, 0):F4},{linear.Get(2, 0):F4}), " +
$"Y=({linear.Get(0, 1):F4},{linear.Get(1, 1):F4},{linear.Get(2, 1):F4}), " +
@ -5437,7 +5423,7 @@ namespace NavisworksTransport.Core.Animation
_objectRotationCorrection = request.RotationCorrection;
_groundPathObjectLiftHeight = UnitsConverter.ConvertFromMeters(request.VerticalLiftInMeters);
LogManager.Info(
LogManager.Debug(
$"[起点摆放] 已应用物体调整请求: 模式={_objectStartPlacementMode}, 角度={_objectRotationCorrection}, " +
$"上下偏移={request.VerticalLiftInMeters:F3}m");

View File

@ -2246,7 +2246,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
// 从 correction 扣除路径 yaw动画链路会在起点再次加上
// 这样 targetYaw = pathYaw + (correctionY - pathYaw) = correctionY
// correction 成为「从 CAD 出发的总 yaw 角度」
CoordinateSystemType hst = CoordinateSystemManager.Instance.ResolvedType;
double pyAdj = 0;
if (PathTargetFrameResolver.TryResolvePlanarHostYaw(request.HostPathForward, hst, out double pyRad))
@ -2263,8 +2262,61 @@ namespace NavisworksTransport.UI.WPF.ViewModels
originalLiftInMeters);
_pathAnimationManager.ApplyObjectStartPlacementRequest(placementRequest);
BoundingBox3D bounds = animatedObject.BoundingBox();
return CalculateMeasuredAabbProjectionScore(bounds, hostSide, hostUp);
var baseScore = CalculateMeasuredAabbProjectionScore(bounds, hostSide, hostUp);
var score = new ObjectPassageProjectionScore(
baseScore.WidthAcrossPath,
baseScore.HeightAlongHostUp + request.VehicleHeightModelUnits);
// 按门型约束过滤:宽门要求 Width >= Height高门要求 Height >= Width
bool meetsConstraint = request.RequireWidthLarger
? score.WidthAcrossPath >= score.HeightAlongHostUp
: score.HeightAlongHostUp >= score.WidthAcrossPath;
if (!meetsConstraint)
{
return ObjectPassageProjectionScore.Invalid;
}
return score;
});
// 计算上下偏移:应用最优旋转后,使物体底面贴合物流车高度
if (result.Success && CurrentPathRoute?.PathType == PathType.Ground)
{
CoordinateSystemType hst = CoordinateSystemManager.Instance.ResolvedType;
double pyAdj = 0;
if (PathTargetFrameResolver.TryResolvePlanarHostYaw(request.HostPathForward, hst, out double pyRad))
{
pyAdj = pyRad * 180.0 / Math.PI;
}
var optimalBaseAdjusted = new LocalEulerRotationCorrection(
result.Correction.XDegrees,
hst == CoordinateSystemType.YUp ? result.Correction.YDegrees - pyAdj : result.Correction.YDegrees,
hst == CoordinateSystemType.ZUp ? result.Correction.ZDegrees - pyAdj : result.Correction.ZDegrees);
var measureRequest = ObjectStartPlacementRequest.CreateRotationCorrection(optimalBaseAdjusted, 0);
_pathAnimationManager.ApplyObjectStartPlacementRequest(measureRequest);
BoundingBox3D measureBounds = animatedObject.BoundingBox();
// 物体底面和地面在世界坐标系中的差值
double objectBottomWorld = hst == CoordinateSystemType.YUp
? measureBounds.Min.Y : measureBounds.Min.Z;
Point3D pathStartPoint = new Point3D(
CurrentPathRoute.Points[0].X,
CurrentPathRoute.Points[0].Y,
CurrentPathRoute.Points[0].Z);
double groundLevel = hst == CoordinateSystemType.YUp
? pathStartPoint.Y : pathStartPoint.Z;
// 需要的上下偏移 = (地面 + 车高) - 物体当前底面
double neededLiftModelUnits = groundLevel + request.VehicleHeightModelUnits - objectBottomWorld;
double neededLiftInMeters = UnitsConverter.ConvertToMeters(neededLiftModelUnits);
result = ObjectPassageProjectionOptimizationResult.CreateSuccess(
result.Correction, result.Score, neededLiftInMeters);
LogManager.Info(
$"[自动调整] 物体底面={UnitsConverter.ConvertToMeters(objectBottomWorld):F3}m, 地面={UnitsConverter.ConvertToMeters(groundLevel):F3}m, " +
$"车高={UnitsConverter.ConvertToMeters(request.VehicleHeightModelUnits):F3}m, 计算偏移={neededLiftInMeters:F3}m");
}
}
finally
{

View File

@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:NavisworksTransport.UI.WPF.Converters"
Title="调整物体" Height="380" Width="470"
Title="调整物体" Height="420" Width="470"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
ShowInTaskbar="False"
@ -127,6 +127,34 @@
<TextBlock Grid.Column="2" Text="m" VerticalAlignment="Center" FontSize="11" Foreground="#FF666666" Margin="8,0,0,0"/>
</Grid>
<Grid Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<TextBlock Text="物流车高:" VerticalAlignment="Center" FontSize="11" Foreground="#FF333333"/>
<TextBox Grid.Column="1"
x:Name="VehicleHeightTextBox"
Text="{Binding VehicleHeightInMeters, Mode=TwoWay, UpdateSourceTrigger=Explicit, Converter={StaticResource EditableNumberConverter}, ConverterParameter=0.###, ValidatesOnExceptions=True, NotifyOnValidationError=True}"
Height="28"
VerticalContentAlignment="Center"
Padding="8,0"
FontSize="11"
BorderBrush="#FFCCCCCC"
BorderThickness="1"/>
<TextBlock Grid.Column="2" Text="m" VerticalAlignment="Center" FontSize="11" Foreground="#FF666666" Margin="8,0,0,0"/>
</Grid>
<!-- 门型偏好 -->
<CheckBox x:Name="WidthLargerCheckBox"
IsChecked="{Binding RequireWidthLarger}"
Content="宽 > 高(否则高 > 宽)"
ToolTip="根据物流通道的门型选择:默认宽扁门,物体放平穿过;取消则为高窄门,物体立起穿过。"
FontSize="10"
Foreground="#FF666666"
Margin="0,0,0,8"/>
<!-- 快捷按钮 -->
<TextBlock Text="快捷角度(作用于当前焦点输入框):" FontSize="10" Foreground="#FF666666" Margin="0,0,0,8"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">

View File

@ -5,6 +5,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using NavisworksTransport.UI.WPF.Converters;
using NavisworksTransport.Utils;
using NavisworksTransport.Utils.CoordinateSystem;
namespace NavisworksTransport.UI.WPF.Views
@ -27,6 +28,26 @@ namespace NavisworksTransport.UI.WPF.Views
private readonly ObjectPassageProjectionOptimizationRequest _autoAdjustmentRequest;
private readonly Func<ObjectPassageProjectionOptimizationResult> _autoAdjustmentProvider;
private readonly bool _isGroundLiftEnabled;
private bool _requireWidthLarger = false;
private double _vehicleHeightInMeters = 0.15;
/// <summary>
/// 门型偏好true=宽>高false=高>宽(默认)
/// </summary>
public bool RequireWidthLarger
{
get => _requireWidthLarger;
set => _requireWidthLarger = value;
}
/// <summary>
/// 物流车辆高度(米),默认 0.15m
/// </summary>
public double VehicleHeightInMeters
{
get => _vehicleHeightInMeters;
set => _vehicleHeightInMeters = value;
}
public double RotationXDegrees
{
@ -139,6 +160,14 @@ namespace NavisworksTransport.UI.WPF.Views
return;
}
// 同步门型偏好和车辆高度到请求
if (_autoAdjustmentRequest != null)
{
_autoAdjustmentRequest.RequireWidthLarger = _requireWidthLarger;
_autoAdjustmentRequest.VehicleHeightModelUnits =
UnitsConverter.ConvertFromMeters(_vehicleHeightInMeters);
}
ObjectPassageProjectionOptimizationResult result = _autoAdjustmentProvider != null
? _autoAdjustmentProvider()
: ObjectPassageProjectionOptimizer.Optimize(_autoAdjustmentRequest);
@ -151,6 +180,10 @@ namespace NavisworksTransport.UI.WPF.Views
RotationXDegrees = NormalizeDisplayDegrees(result.Correction.XDegrees);
RotationYDegrees = NormalizeDisplayDegrees(result.Correction.YDegrees);
RotationZDegrees = NormalizeDisplayDegrees(result.Correction.ZDegrees);
if (result.ComputedLiftOffsetMeters.HasValue)
{
GroundPathLiftHeightInMeters = Math.Round(result.ComputedLiftOffsetMeters.Value, 3);
}
LogManager.Info(
$"[自动调整] 已写入角度: {RotationCorrection}, " +
$"截面宽度={result.Score.WidthAcrossPath:F3}, 高度={result.Score.HeightAlongHostUp:F3}, 面积={result.Score.Area:F3}");
@ -287,6 +320,17 @@ namespace NavisworksTransport.UI.WPF.Views
return false;
}
if (!TryUpdateNumberTextBox(VehicleHeightTextBox, "请输入有效的物流车高度。"))
{
return false;
}
if (VehicleHeightInMeters < 0.0)
{
ShowInputError("物流车高度必须大于或等于 0。", VehicleHeightTextBox);
return false;
}
return true;
}

View File

@ -13,7 +13,8 @@ namespace NavisworksTransport.Utils.CoordinateSystem
Vector3 hostPathForward,
Vector3 hostUp,
double areaRelativeTieTolerance = 0.01,
Quaternion? baselineHostRotation = null)
Quaternion? baselineHostRotation = null,
bool requireWidthLarger = true)
{
SizeX = sizeX;
SizeY = sizeY;
@ -22,6 +23,7 @@ namespace NavisworksTransport.Utils.CoordinateSystem
HostUp = hostUp;
AreaRelativeTieTolerance = areaRelativeTieTolerance;
BaselineHostRotation = Quaternion.Normalize(baselineHostRotation ?? Quaternion.Identity);
RequireWidthLarger = requireWidthLarger;
}
public double SizeX { get; }
@ -37,6 +39,16 @@ namespace NavisworksTransport.Utils.CoordinateSystem
public double AreaRelativeTieTolerance { get; }
public Quaternion BaselineHostRotation { get; }
/// <summary>
/// 是否要求通行截面宽度 >= 高度宽扁门。false 表示高窄门(高度 >= 宽度)。
/// </summary>
public bool RequireWidthLarger { get; set; }
/// <summary>
/// 物体下方物流车辆高度(模型单位),自动调整时加到通行截面高度上。
/// </summary>
public double VehicleHeightModelUnits { get; set; }
}
public sealed class ObjectPassageProjectionOptimizationResult
@ -45,12 +57,14 @@ namespace NavisworksTransport.Utils.CoordinateSystem
bool success,
LocalEulerRotationCorrection correction,
ObjectPassageProjectionScore score,
string errorMessage)
string errorMessage,
double? computedLiftOffsetMeters = null)
{
Success = success;
Correction = correction;
Score = score;
ErrorMessage = errorMessage;
ComputedLiftOffsetMeters = computedLiftOffsetMeters;
}
public bool Success { get; }
@ -61,11 +75,17 @@ namespace NavisworksTransport.Utils.CoordinateSystem
public string ErrorMessage { get; }
/// <summary>
/// 自动调整后计算出的上下偏移(米),使物体底面贴合物流车高度。仅地面路径有效。
/// </summary>
public double? ComputedLiftOffsetMeters { get; }
public static ObjectPassageProjectionOptimizationResult CreateSuccess(
LocalEulerRotationCorrection correction,
ObjectPassageProjectionScore score)
ObjectPassageProjectionScore score,
double? computedLiftOffsetMeters = null)
{
return new ObjectPassageProjectionOptimizationResult(true, correction, score, null);
return new ObjectPassageProjectionOptimizationResult(true, correction, score, null, computedLiftOffsetMeters);
}
public static ObjectPassageProjectionOptimizationResult CreateFailure(string errorMessage)

View File

@ -617,12 +617,10 @@ namespace NavisworksTransport.Utils
var tp = new Vector3((float)trackedPosition.X, (float)trackedPosition.Y, (float)trackedPosition.Z);
var rotatedTp = Vector3.Transform(tp, totalQ);
// 计算组合变换
// 计算组合变换(调用方已在 RestoreObjectToCADPosition 中执行 ResetPermanentTransform此处不重复
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { item };
doc.Models.ResetPermanentTransform(modelItems);
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
var components = identity.Factor();
components.Rotation = new Rotation3D(totalQ.X, totalQ.Y, totalQ.Z, totalQ.W);