Fix rail assembly anchor alignment

This commit is contained in:
tian 2026-03-26 13:37:33 +08:00
parent 3a5693a453
commit 2b6f54898a
6 changed files with 185 additions and 20 deletions

View File

@ -73,6 +73,7 @@
<Compile Include="UnitTests\CoordinateSystem\PathTargetFrameResolverTests.cs" />
<Compile Include="UnitTests\CoordinateSystem\AssemblyEndFaceAnalyzerTests.cs" />
<Compile Include="UnitTests\CoordinateSystem\AssemblyInstallationReferenceBuilderTests.cs" />
<Compile Include="UnitTests\CoordinateSystem\PathPointVisualizationTests.cs" />
<Compile Include="UnitTests\CoordinateSystem\FragmentDefaultUpContextTests.cs" />
<Compile Include="UnitTests\CoordinateSystem\VirtualGroundPoseCharacterizationTests.cs" />
<Compile Include="UnitTests\Properties\AssemblyInfo.cs" />

View File

@ -0,0 +1,32 @@
using Autodesk.Navisworks.Api;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace NavisworksTransport.UnitTests.CoordinateSystem
{
[TestClass]
public class PathPointVisualizationTests
{
[TestMethod]
public void ResolveVisualizationRadius_WhenVisualizationDiameterIsUnset_ShouldUseDefaultRadius()
{
var point = new PathPoint(new Point3D(1.0, 2.0, 3.0), "测试点", PathPointType.WayPoint);
double radius = point.ResolveVisualizationRadius(0.25);
Assert.AreEqual(0.25, radius, 1e-9);
}
[TestMethod]
public void ResolveVisualizationRadius_WhenVisualizationDiameterIsSpecified_ShouldUseHalfDiameter()
{
var point = new PathPoint(new Point3D(1.0, 2.0, 3.0), "测试点", PathPointType.WayPoint)
{
VisualizationDiameter = 0.10
};
double radius = point.ResolveVisualizationRadius(0.25);
Assert.AreEqual(0.05, radius, 1e-9);
}
}
}

View File

@ -191,6 +191,8 @@ namespace NavisworksTransport.Core.Animation
private Vector3D _groundRealObjectStartCompensation = new Vector3D(0, 0, 0);
private bool _hasGroundRealObjectStartCompensation = false;
private bool _suppressGroundRealObjectCompensation = false;
private Vector3D _railPreservedPoseTrackedCenterOffset = new Vector3D(0, 0, 0);
private bool _hasRailPreservedPoseTrackedCenterOffset = false;
// TimeLiner 集成
private TimeLinerIntegrationManager _timeLinerManager;
@ -475,6 +477,8 @@ namespace NavisworksTransport.Core.Animation
string objectName = isVirtual ? "虚拟物体" : objectToRestore.DisplayName;
_groundRealObjectStartCompensation = new Vector3D(0, 0, 0);
_hasGroundRealObjectStartCompensation = false;
_railPreservedPoseTrackedCenterOffset = new Vector3D(0, 0, 0);
_hasRailPreservedPoseTrackedCenterOffset = false;
LogManager.Info($"[归位] {objectName} 已彻底恢复到原始位置, yaw={_currentYaw:F3}");
}
catch (Exception ex)
@ -743,9 +747,19 @@ namespace NavisworksTransport.Core.Animation
{
Point3D previousPoint = _pathPoints[0];
Point3D nextPoint = _pathPoints.Count > 1 ? _pathPoints[1] : _pathPoints[0];
double objectHeight = GetAnimatedObjectRailNormalExtent(previousPoint, _pathPoints[0], nextPoint);
startPosition = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(_route, startPosition, previousPoint, nextPoint, objectHeight);
if (TryResolveRailPreservedPoseTrackedCenter(startPosition, out Point3D preservedTrackedCenter))
{
startPosition = preservedTrackedCenter;
LogManager.Debug(
$"[移动到起点] Rail保持姿态补偿: 参考点=({_pathPoints[0].X:F2},{_pathPoints[0].Y:F2},{_pathPoints[0].Z:F2}), " +
$"补偿后中心=({startPosition.X:F2},{startPosition.Y:F2},{startPosition.Z:F2})");
}
else
{
double objectHeight = GetAnimatedObjectRailNormalExtent(previousPoint, _pathPoints[0], nextPoint);
startPosition = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(_route, startPosition, previousPoint, nextPoint, objectHeight);
LogManager.Debug($"[移动到起点] Rail路径调整: 参考点=({_pathPoints[0].X:F2},{_pathPoints[0].Y:F2},{_pathPoints[0].Z:F2}), 物体中心=({startPosition.X:F2},{startPosition.Y:F2},{startPosition.Z:F2}), 物体高度={objectHeight:F2}, 安装={_route.RailMountMode}, 对接={(PathRoute.IsTopPayloadAnchorForMountMode(_route.RailMountMode) ? "" : "")}");
}
if (TryCreateRailPathRotation(
previousPoint,
@ -926,10 +940,16 @@ namespace NavisworksTransport.Core.Animation
}
else if (_route.PathType == PathType.Rail)
{
CaptureRailPreservedPoseTrackedCenterOffset();
Point3D previousPoint = _pathPoints[0];
Point3D nextPoint = _pathPoints.Count > 1 ? _pathPoints[1] : _pathPoints[0];
double objectHeight = GetAnimatedObjectRailNormalExtent(previousPoint, _pathPoints[0], nextPoint);
startPosition = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(_route, startPosition, previousPoint, nextPoint, objectHeight);
if (!TryResolveRailPreservedPoseTrackedCenter(startPosition, out Point3D preservedTrackedCenter))
{
double objectHeight = GetAnimatedObjectRailNormalExtent(previousPoint, _pathPoints[0], nextPoint);
preservedTrackedCenter = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(_route, startPosition, previousPoint, nextPoint, objectHeight);
}
startPosition = preservedTrackedCenter;
}
else
{
@ -1204,8 +1224,18 @@ namespace NavisworksTransport.Core.Animation
}
else if (_route.PathType == PathType.Rail)
{
framePosition = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(_route, framePosition, p1, p2, objectHeight);
LogManager.Debug($"[Rail路径] 调整物体位置: 参考点=({p1.X:F2},{p1.Y:F2},{p1.Z:F2})->({p2.X:F2},{p2.Y:F2},{p2.Z:F2}), 物体中心=({framePosition.X:F2},{framePosition.Y:F2},{framePosition.Z:F2}), 安装={_route.RailMountMode}, 对接={(PathRoute.IsTopPayloadAnchorForMountMode(_route.RailMountMode) ? "" : "")}");
if (TryResolveRailPreservedPoseTrackedCenter(framePosition, out Point3D preservedTrackedCenter))
{
framePosition = preservedTrackedCenter;
LogManager.Debug(
$"[Rail路径] 保持姿态补偿: 参考点=({p1.X:F2},{p1.Y:F2},{p1.Z:F2})->({p2.X:F2},{p2.Y:F2},{p2.Z:F2}), " +
$"物体中心=({framePosition.X:F2},{framePosition.Y:F2},{framePosition.Z:F2})");
}
else
{
framePosition = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(_route, framePosition, p1, p2, objectHeight);
LogManager.Debug($"[Rail路径] 调整物体位置: 参考点=({p1.X:F2},{p1.Y:F2},{p1.Z:F2})->({p2.X:F2},{p2.Y:F2},{p2.Z:F2}), 物体中心=({framePosition.X:F2},{framePosition.Y:F2},{framePosition.Z:F2}), 安装={_route.RailMountMode}, 对接={(PathRoute.IsTopPayloadAnchorForMountMode(_route.RailMountMode) ? "" : "")}");
}
}
}
else
@ -2164,13 +2194,16 @@ namespace NavisworksTransport.Core.Animation
Point3D expectedTrackedEndPoint;
if (_route.PathType == PathType.Rail)
{
double objectHeight = GetAnimatedObjectRailNormalExtent(previousAnchorPoint, terminalAnchorPoint, terminalAnchorPoint);
expectedTrackedEndPoint = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(
_route,
terminalAnchorPoint,
previousAnchorPoint,
terminalAnchorPoint,
objectHeight);
if (!TryResolveRailPreservedPoseTrackedCenter(terminalAnchorPoint, out expectedTrackedEndPoint))
{
double objectHeight = GetAnimatedObjectRailNormalExtent(previousAnchorPoint, terminalAnchorPoint, terminalAnchorPoint);
expectedTrackedEndPoint = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(
_route,
terminalAnchorPoint,
previousAnchorPoint,
terminalAnchorPoint,
objectHeight);
}
}
else if (_route.PathType == PathType.Hoisting)
{
@ -3789,6 +3822,53 @@ namespace NavisworksTransport.Core.Animation
return adapter.FromCanonicalPoint(new Point3D(canonicalCenter.X, canonicalCenter.Y, canonicalCenter.Z));
}
private bool TryResolveRailPreservedPoseTrackedCenter(Point3D referencePoint, out Point3D trackedCenter)
{
trackedCenter = referencePoint;
if (!IsRealObjectMode ||
_route?.PathType != PathType.Rail ||
_objectStartPlacementMode != ObjectStartPlacementMode.PreserveInitialPose ||
!_hasRailPreservedPoseTrackedCenterOffset)
{
return false;
}
trackedCenter = new Point3D(
referencePoint.X + _railPreservedPoseTrackedCenterOffset.X,
referencePoint.Y + _railPreservedPoseTrackedCenterOffset.Y,
referencePoint.Z + _railPreservedPoseTrackedCenterOffset.Z);
return true;
}
private void CaptureRailPreservedPoseTrackedCenterOffset()
{
_railPreservedPoseTrackedCenterOffset = new Vector3D(0, 0, 0);
_hasRailPreservedPoseTrackedCenterOffset = false;
if (!IsRealObjectMode ||
_route?.PathType != PathType.Rail ||
_objectStartPlacementMode != ObjectStartPlacementMode.PreserveInitialPose ||
_pathPoints == null ||
_pathPoints.Count == 0)
{
return;
}
Point3D currentTrackedCenter = GetTrackedObjectPosition(CurrentControlledObject ?? _animatedObject);
Point3D terminalAnchorPoint = _pathPoints[_pathPoints.Count - 1];
_railPreservedPoseTrackedCenterOffset = new Vector3D(
currentTrackedCenter.X - terminalAnchorPoint.X,
currentTrackedCenter.Y - terminalAnchorPoint.Y,
currentTrackedCenter.Z - terminalAnchorPoint.Z);
_hasRailPreservedPoseTrackedCenterOffset = true;
LogManager.Info(
$"[Rail保持姿态补偿] 当前跟踪中心=({currentTrackedCenter.X:F3},{currentTrackedCenter.Y:F3},{currentTrackedCenter.Z:F3}), " +
$"终点锚点=({terminalAnchorPoint.X:F3},{terminalAnchorPoint.Y:F3},{terminalAnchorPoint.Z:F3}), " +
$"固定偏移=({_railPreservedPoseTrackedCenterOffset.X:F3},{_railPreservedPoseTrackedCenterOffset.Y:F3},{_railPreservedPoseTrackedCenterOffset.Z:F3})");
}
private bool TryResolveGroundRealObjectBaseYaw(out double yawRadians)
{
yawRadians = 0.0;

View File

@ -375,6 +375,13 @@ namespace NavisworksTransport
/// </summary>
public double? CustomTurnRadius { get; set; }
/// <summary>
/// 仅用于视图显示的点直径(模型单位)。
/// 为 null 或非正数时使用默认点类型尺寸。
/// </summary>
[XmlIgnore]
public double? VisualizationDiameter { get; set; }
/// <summary>
/// 方向类型(仅吊装路径使用)
/// 用于标识吊装路径点的移动方向垂直、纵向X轴、横向Y轴
@ -395,6 +402,7 @@ namespace NavisworksTransport
Notes = string.Empty;
SpeedLimit = 0;
CustomTurnRadius = null;
VisualizationDiameter = null;
Direction = HoistingPointDirection.Vertical;
}
@ -415,9 +423,25 @@ namespace NavisworksTransport
Notes = string.Empty;
SpeedLimit = 0;
CustomTurnRadius = null;
VisualizationDiameter = null;
Direction = HoistingPointDirection.Vertical;
}
/// <summary>
/// 解析最终用于渲染的点半径(模型单位)。
/// </summary>
/// <param name="defaultRadius">默认点半径(模型单位)</param>
/// <returns>最终半径(模型单位)</returns>
public double ResolveVisualizationRadius(double defaultRadius)
{
if (!VisualizationDiameter.HasValue || VisualizationDiameter.Value <= 0)
{
return defaultRadius;
}
return VisualizationDiameter.Value * 0.5;
}
/// <summary>
/// 返回路径点描述字符串
/// </summary>

View File

@ -2268,7 +2268,8 @@ namespace NavisworksTransport
{
Center = point.Position,
Normal = GetHostUpVector(),
Radius = isGridVisualization ? GetRadiusForGridVisualization() : GetRadiusForPointType(point.Type),
Radius = point.ResolveVisualizationRadius(
isGridVisualization ? GetRadiusForGridVisualization() : GetRadiusForPointType(point.Type)),
Color = isGridVisualization ? gridStyle.Color : GetColorForPointType(point.Type),
Alpha = isGridVisualization ? gridStyle.Alpha : 1.0,
Filled = true,

View File

@ -125,6 +125,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private const double DefaultAssemblyReferenceRodLengthInMeters = 20.0;
private static readonly double DefaultAssemblyReferenceRodDiameterInMeters = CalculateDefaultAssemblyReferenceRodDiameterInMeters();
private const double DefaultAssemblyAnchorVerticalOffsetInMeters = 0.0;
private const double AssemblyVisualizationPointDiameterInMeters = 0.10;
private const double RailNormalOffsetNudgeStepInMeters = 0.1;
private const double DefaultAssemblySphereCenterX = 0.0;
private const double DefaultAssemblySphereCenterY = 0.0;
@ -2259,7 +2260,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
for (int i = 0; i < _assemblyEndFaceSeedPoints.Count; i++)
{
AddVisualizationPoint(markerRoute, _assemblyEndFaceSeedPoints[i], $"端面点{i + 1}", PathPointType.WayPoint);
AddVisualizationPoint(
markerRoute,
_assemblyEndFaceSeedPoints[i],
$"端面点{i + 1}",
PathPointType.WayPoint,
UnitsConverter.ConvertFromMeters(AssemblyVisualizationPointDiameterInMeters));
}
renderPlugin.RenderPointOnly(markerRoute);
@ -2280,11 +2286,21 @@ namespace NavisworksTransport.UI.WPF.ViewModels
Id = AssemblyEndFaceCenterPathId,
Description = "终端安装端面中心"
};
AddVisualizationPoint(markerRoute, centerPoint, "端面中心", PathPointType.WayPoint);
AddVisualizationPoint(
markerRoute,
centerPoint,
"端面中心",
PathPointType.WayPoint,
UnitsConverter.ConvertFromMeters(AssemblyVisualizationPointDiameterInMeters));
renderPlugin.RenderPointOnly(markerRoute);
}
private static void AddVisualizationPoint(PathRoute route, Point3D position, string name, PathPointType type)
private static void AddVisualizationPoint(
PathRoute route,
Point3D position,
string name,
PathPointType type,
double? visualizationDiameter = null)
{
if (route == null)
{
@ -2293,7 +2309,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
route.Points.Add(new PathPoint(position, name, type)
{
Index = route.Points.Count
Index = route.Points.Count,
VisualizationDiameter = visualizationDiameter
});
}
@ -2379,7 +2396,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
};
for (int i = 0; i < pickPoints.Count; i++)
{
AddVisualizationPoint(markerRoute, pickPoints[i], $"安装面点{i + 1}", PathPointType.WayPoint);
AddVisualizationPoint(
markerRoute,
pickPoints[i],
$"安装面点{i + 1}",
PathPointType.WayPoint,
UnitsConverter.ConvertFromMeters(AssemblyVisualizationPointDiameterInMeters));
}
renderPlugin.RenderPointOnly(markerRoute);
}
@ -2398,7 +2420,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
Id = AssemblyInstallationAnchorPointPathId,
Description = "终端安装点"
};
AddVisualizationPoint(markerRoute, anchorPoint, "安装点", PathPointType.WayPoint);
AddVisualizationPoint(
markerRoute,
anchorPoint,
"安装点",
PathPointType.WayPoint,
UnitsConverter.ConvertFromMeters(AssemblyVisualizationPointDiameterInMeters));
renderPlugin.RenderPointOnly(markerRoute);
}