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\PathTargetFrameResolverTests.cs" />
<Compile Include="UnitTests\CoordinateSystem\AssemblyEndFaceAnalyzerTests.cs" /> <Compile Include="UnitTests\CoordinateSystem\AssemblyEndFaceAnalyzerTests.cs" />
<Compile Include="UnitTests\CoordinateSystem\AssemblyInstallationReferenceBuilderTests.cs" /> <Compile Include="UnitTests\CoordinateSystem\AssemblyInstallationReferenceBuilderTests.cs" />
<Compile Include="UnitTests\CoordinateSystem\PathPointVisualizationTests.cs" />
<Compile Include="UnitTests\CoordinateSystem\FragmentDefaultUpContextTests.cs" /> <Compile Include="UnitTests\CoordinateSystem\FragmentDefaultUpContextTests.cs" />
<Compile Include="UnitTests\CoordinateSystem\VirtualGroundPoseCharacterizationTests.cs" /> <Compile Include="UnitTests\CoordinateSystem\VirtualGroundPoseCharacterizationTests.cs" />
<Compile Include="UnitTests\Properties\AssemblyInfo.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 Vector3D _groundRealObjectStartCompensation = new Vector3D(0, 0, 0);
private bool _hasGroundRealObjectStartCompensation = false; private bool _hasGroundRealObjectStartCompensation = false;
private bool _suppressGroundRealObjectCompensation = false; private bool _suppressGroundRealObjectCompensation = false;
private Vector3D _railPreservedPoseTrackedCenterOffset = new Vector3D(0, 0, 0);
private bool _hasRailPreservedPoseTrackedCenterOffset = false;
// TimeLiner 集成 // TimeLiner 集成
private TimeLinerIntegrationManager _timeLinerManager; private TimeLinerIntegrationManager _timeLinerManager;
@ -475,6 +477,8 @@ namespace NavisworksTransport.Core.Animation
string objectName = isVirtual ? "虚拟物体" : objectToRestore.DisplayName; string objectName = isVirtual ? "虚拟物体" : objectToRestore.DisplayName;
_groundRealObjectStartCompensation = new Vector3D(0, 0, 0); _groundRealObjectStartCompensation = new Vector3D(0, 0, 0);
_hasGroundRealObjectStartCompensation = false; _hasGroundRealObjectStartCompensation = false;
_railPreservedPoseTrackedCenterOffset = new Vector3D(0, 0, 0);
_hasRailPreservedPoseTrackedCenterOffset = false;
LogManager.Info($"[归位] {objectName} 已彻底恢复到原始位置, yaw={_currentYaw:F3}"); LogManager.Info($"[归位] {objectName} 已彻底恢复到原始位置, yaw={_currentYaw:F3}");
} }
catch (Exception ex) catch (Exception ex)
@ -743,9 +747,19 @@ namespace NavisworksTransport.Core.Animation
{ {
Point3D previousPoint = _pathPoints[0]; Point3D previousPoint = _pathPoints[0];
Point3D nextPoint = _pathPoints.Count > 1 ? _pathPoints[1] : _pathPoints[0]; Point3D nextPoint = _pathPoints.Count > 1 ? _pathPoints[1] : _pathPoints[0];
double objectHeight = GetAnimatedObjectRailNormalExtent(previousPoint, _pathPoints[0], nextPoint); if (TryResolveRailPreservedPoseTrackedCenter(startPosition, out Point3D preservedTrackedCenter))
startPosition = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(_route, startPosition, previousPoint, nextPoint, objectHeight); {
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) ? "" : "")}"); 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( if (TryCreateRailPathRotation(
previousPoint, previousPoint,
@ -926,10 +940,16 @@ namespace NavisworksTransport.Core.Animation
} }
else if (_route.PathType == PathType.Rail) else if (_route.PathType == PathType.Rail)
{ {
CaptureRailPreservedPoseTrackedCenterOffset();
Point3D previousPoint = _pathPoints[0]; Point3D previousPoint = _pathPoints[0];
Point3D nextPoint = _pathPoints.Count > 1 ? _pathPoints[1] : _pathPoints[0]; Point3D nextPoint = _pathPoints.Count > 1 ? _pathPoints[1] : _pathPoints[0];
double objectHeight = GetAnimatedObjectRailNormalExtent(previousPoint, _pathPoints[0], nextPoint); if (!TryResolveRailPreservedPoseTrackedCenter(startPosition, out Point3D preservedTrackedCenter))
startPosition = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(_route, startPosition, previousPoint, nextPoint, objectHeight); {
double objectHeight = GetAnimatedObjectRailNormalExtent(previousPoint, _pathPoints[0], nextPoint);
preservedTrackedCenter = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(_route, startPosition, previousPoint, nextPoint, objectHeight);
}
startPosition = preservedTrackedCenter;
} }
else else
{ {
@ -1204,8 +1224,18 @@ namespace NavisworksTransport.Core.Animation
} }
else if (_route.PathType == PathType.Rail) else if (_route.PathType == PathType.Rail)
{ {
framePosition = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(_route, framePosition, p1, p2, objectHeight); if (TryResolveRailPreservedPoseTrackedCenter(framePosition, out Point3D 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}), 安装={_route.RailMountMode}, 对接={(PathRoute.IsTopPayloadAnchorForMountMode(_route.RailMountMode) ? "" : "")}"); {
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 else
@ -2164,13 +2194,16 @@ namespace NavisworksTransport.Core.Animation
Point3D expectedTrackedEndPoint; Point3D expectedTrackedEndPoint;
if (_route.PathType == PathType.Rail) if (_route.PathType == PathType.Rail)
{ {
double objectHeight = GetAnimatedObjectRailNormalExtent(previousAnchorPoint, terminalAnchorPoint, terminalAnchorPoint); if (!TryResolveRailPreservedPoseTrackedCenter(terminalAnchorPoint, out expectedTrackedEndPoint))
expectedTrackedEndPoint = RailPathPoseHelper.ResolveObjectSpaceCenterPosition( {
_route, double objectHeight = GetAnimatedObjectRailNormalExtent(previousAnchorPoint, terminalAnchorPoint, terminalAnchorPoint);
terminalAnchorPoint, expectedTrackedEndPoint = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(
previousAnchorPoint, _route,
terminalAnchorPoint, terminalAnchorPoint,
objectHeight); previousAnchorPoint,
terminalAnchorPoint,
objectHeight);
}
} }
else if (_route.PathType == PathType.Hoisting) 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)); 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) private bool TryResolveGroundRealObjectBaseYaw(out double yawRadians)
{ {
yawRadians = 0.0; yawRadians = 0.0;

View File

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

View File

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

View File

@ -125,6 +125,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private const double DefaultAssemblyReferenceRodLengthInMeters = 20.0; private const double DefaultAssemblyReferenceRodLengthInMeters = 20.0;
private static readonly double DefaultAssemblyReferenceRodDiameterInMeters = CalculateDefaultAssemblyReferenceRodDiameterInMeters(); private static readonly double DefaultAssemblyReferenceRodDiameterInMeters = CalculateDefaultAssemblyReferenceRodDiameterInMeters();
private const double DefaultAssemblyAnchorVerticalOffsetInMeters = 0.0; private const double DefaultAssemblyAnchorVerticalOffsetInMeters = 0.0;
private const double AssemblyVisualizationPointDiameterInMeters = 0.10;
private const double RailNormalOffsetNudgeStepInMeters = 0.1; private const double RailNormalOffsetNudgeStepInMeters = 0.1;
private const double DefaultAssemblySphereCenterX = 0.0; private const double DefaultAssemblySphereCenterX = 0.0;
private const double DefaultAssemblySphereCenterY = 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++) 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); renderPlugin.RenderPointOnly(markerRoute);
@ -2280,11 +2286,21 @@ namespace NavisworksTransport.UI.WPF.ViewModels
Id = AssemblyEndFaceCenterPathId, Id = AssemblyEndFaceCenterPathId,
Description = "终端安装端面中心" Description = "终端安装端面中心"
}; };
AddVisualizationPoint(markerRoute, centerPoint, "端面中心", PathPointType.WayPoint); AddVisualizationPoint(
markerRoute,
centerPoint,
"端面中心",
PathPointType.WayPoint,
UnitsConverter.ConvertFromMeters(AssemblyVisualizationPointDiameterInMeters));
renderPlugin.RenderPointOnly(markerRoute); 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) if (route == null)
{ {
@ -2293,7 +2309,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
route.Points.Add(new PathPoint(position, name, type) 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++) 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); renderPlugin.RenderPointOnly(markerRoute);
} }
@ -2398,7 +2420,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
Id = AssemblyInstallationAnchorPointPathId, Id = AssemblyInstallationAnchorPointPathId,
Description = "终端安装点" Description = "终端安装点"
}; };
AddVisualizationPoint(markerRoute, anchorPoint, "安装点", PathPointType.WayPoint); AddVisualizationPoint(
markerRoute,
anchorPoint,
"安装点",
PathPointType.WayPoint,
UnitsConverter.ConvertFromMeters(AssemblyVisualizationPointDiameterInMeters));
renderPlugin.RenderPointOnly(markerRoute); renderPlugin.RenderPointOnly(markerRoute);
} }