diff --git a/NavisworksTransport.UnitTests.csproj b/NavisworksTransport.UnitTests.csproj
index 30ef294..0a8140c 100644
--- a/NavisworksTransport.UnitTests.csproj
+++ b/NavisworksTransport.UnitTests.csproj
@@ -73,6 +73,7 @@
+
diff --git a/UnitTests/CoordinateSystem/PathPointVisualizationTests.cs b/UnitTests/CoordinateSystem/PathPointVisualizationTests.cs
new file mode 100644
index 0000000..e05657f
--- /dev/null
+++ b/UnitTests/CoordinateSystem/PathPointVisualizationTests.cs
@@ -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);
+ }
+ }
+}
diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs
index 9af4ca4..c991f10 100644
--- a/src/Core/Animation/PathAnimationManager.cs
+++ b/src/Core/Animation/PathAnimationManager.cs
@@ -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;
diff --git a/src/Core/PathPlanningModels.cs b/src/Core/PathPlanningModels.cs
index c99356e..2cbd84b 100644
--- a/src/Core/PathPlanningModels.cs
+++ b/src/Core/PathPlanningModels.cs
@@ -375,6 +375,13 @@ namespace NavisworksTransport
///
public double? CustomTurnRadius { get; set; }
+ ///
+ /// 仅用于视图显示的点直径(模型单位)。
+ /// 为 null 或非正数时使用默认点类型尺寸。
+ ///
+ [XmlIgnore]
+ public double? VisualizationDiameter { get; set; }
+
///
/// 方向类型(仅吊装路径使用)
/// 用于标识吊装路径点的移动方向:垂直、纵向(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;
}
+ ///
+ /// 解析最终用于渲染的点半径(模型单位)。
+ ///
+ /// 默认点半径(模型单位)
+ /// 最终半径(模型单位)
+ public double ResolveVisualizationRadius(double defaultRadius)
+ {
+ if (!VisualizationDiameter.HasValue || VisualizationDiameter.Value <= 0)
+ {
+ return defaultRadius;
+ }
+
+ return VisualizationDiameter.Value * 0.5;
+ }
+
///
/// 返回路径点描述字符串
///
diff --git a/src/Core/PathPointRenderPlugin.cs b/src/Core/PathPointRenderPlugin.cs
index e6b5efd..5823d70 100644
--- a/src/Core/PathPointRenderPlugin.cs
+++ b/src/Core/PathPointRenderPlugin.cs
@@ -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,
diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs
index cb660e2..902e4ae 100644
--- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs
+++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs
@@ -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);
}