From 1802eda971df80447d9e647ed4da89e1973c3e25 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Sun, 22 Mar 2026 13:30:35 +0800 Subject: [PATCH] Separate asset axis conventions from host coordinates --- .../ModelAxisConventionTests.cs | 34 +++++++ src/Core/Animation/PathAnimationManager.cs | 4 +- src/Core/AssemblyReferencePathManager.cs | 10 +- src/Core/VirtualObjectManager.cs | 91 +++---------------- .../CoordinateSystem/ModelAxisConvention.cs | 64 +++++++++++++ src/Utils/RailPathPoseHelper.cs | 4 +- 6 files changed, 123 insertions(+), 84 deletions(-) diff --git a/UnitTests/CoordinateSystem/ModelAxisConventionTests.cs b/UnitTests/CoordinateSystem/ModelAxisConventionTests.cs index 74f16e0..8526f21 100644 --- a/UnitTests/CoordinateSystem/ModelAxisConventionTests.cs +++ b/UnitTests/CoordinateSystem/ModelAxisConventionTests.cs @@ -59,6 +59,40 @@ namespace NavisworksTransport.UnitTests.CoordinateSystem AssertColumn(linear, 2, 1, 0, 0); } + [TestMethod] + public void ReferenceRodAssetConvention_ShouldUseXForwardAndZUp() + { + ModelAxisConvention convention = ModelAxisConvention.CreateReferenceRodAssetConvention(); + + Assert.AreEqual(LocalAxisDirection.PositiveX, convention.ForwardAxis); + Assert.AreEqual(LocalAxisDirection.PositiveZ, convention.UpAxis); + Assert.AreEqual(LocalAxisDirection.NegativeY, convention.SideAxis); + } + + [TestMethod] + public void VirtualObjectAssetConvention_CreateScaleVector_ShouldMapLengthWidthHeightToLocalAxes() + { + ModelAxisConvention convention = ModelAxisConvention.CreateVirtualObjectAssetConvention(); + + var scale = convention.CreateScaleVector3(12.0, 5.0, 7.0); + + Assert.AreEqual(12.0, scale.X, 1e-6); + Assert.AreEqual(5.0, scale.Y, 1e-6); + Assert.AreEqual(7.0, scale.Z, 1e-6); + } + + [TestMethod] + public void YUpConvention_CreateScaleVector_ShouldMapUpSizeToLocalY() + { + ModelAxisConvention convention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.YUp); + + var scale = convention.CreateScaleVector3(12.0, 5.0, 7.0); + + Assert.AreEqual(12.0, scale.X, 1e-6); + Assert.AreEqual(7.0, scale.Y, 1e-6); + Assert.AreEqual(5.0, scale.Z, 1e-6); + } + private static void AssertColumn(Matrix4x4 matrix, int column, double x, double y, double z) { switch (column) diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs index 9f75699..dc5978a 100644 --- a/src/Core/Animation/PathAnimationManager.cs +++ b/src/Core/Animation/PathAnimationManager.cs @@ -3368,7 +3368,7 @@ namespace NavisworksTransport.Core.Animation { if (_isVirtualObject) { - return ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.ZUp); + return ModelAxisConvention.CreateVirtualObjectAssetConvention(); } var adapter = CoordinateSystemManager.Instance.CreateHostAdapter(); @@ -3383,7 +3383,7 @@ namespace NavisworksTransport.Core.Animation { if (_isVirtualObject) { - return ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.ZUp); + return ModelAxisConvention.CreateVirtualObjectAssetConvention(); } var adapter = CoordinateSystemManager.Instance.CreateHostAdapter(); diff --git a/src/Core/AssemblyReferencePathManager.cs b/src/Core/AssemblyReferencePathManager.cs index 7191415..986604f 100644 --- a/src/Core/AssemblyReferencePathManager.cs +++ b/src/Core/AssemblyReferencePathManager.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using Autodesk.Navisworks.Api; using NavisworksTransport.Utils; +using NavisworksTransport.Utils.CoordinateSystem; namespace NavisworksTransport.Core { @@ -15,6 +16,7 @@ namespace NavisworksTransport.Core public class AssemblyReferencePathManager { private const double ReferenceRodBaseSizeInMeters = 0.01; + private static readonly ModelAxisConvention ReferenceRodAxisConvention = ModelAxisConvention.CreateReferenceRodAssetConvention(); private static AssemblyReferencePathManager _instance; private static readonly object _lock = new object(); @@ -220,7 +222,7 @@ namespace NavisworksTransport.Core var currentTransform = _referenceRodModel.Transform; var components = currentTransform.Factor(); - components.Scale = new Vector3D( + components.Scale = ReferenceRodAxisConvention.CreateScaleVector( lengthInMeters / ReferenceRodBaseSizeInMeters, diameterInMeters / ReferenceRodBaseSizeInMeters, diameterInMeters / ReferenceRodBaseSizeInMeters); @@ -265,11 +267,11 @@ namespace NavisworksTransport.Core endPoint.Y - startPoint.Y, endPoint.Z - startPoint.Z)); - // 参考杆资源约定:几何中心在原点,长度轴沿局部 +X。 + // 参考杆资源约定:几何中心在原点,长度轴沿本地 ForwardAxis。 // Navisworks API 文档中 Rotation3D(vector1, vector2) 的语义 - // 是“将 vector1 旋转到与 vector2 同方向”,这里直接将本地 X 轴对齐参考线方向。 + // 是“将 vector1 旋转到与 vector2 同方向”,这里直接将资源本地 forward 轴对齐参考线方向。 return new Rotation3D( - new UnitVector3D(1, 0, 0), + new UnitVector3D(ReferenceRodAxisConvention.ForwardVector), new UnitVector3D(tangent)); } diff --git a/src/Core/VirtualObjectManager.cs b/src/Core/VirtualObjectManager.cs index 2809375..f8e7647 100644 --- a/src/Core/VirtualObjectManager.cs +++ b/src/Core/VirtualObjectManager.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using Autodesk.Navisworks.Api; using NavisworksTransport.Utils; +using NavisworksTransport.Utils.CoordinateSystem; namespace NavisworksTransport.Core { @@ -17,6 +18,7 @@ namespace NavisworksTransport.Core /// public class VirtualObjectManager { + private static readonly ModelAxisConvention VirtualObjectAxisConvention = ModelAxisConvention.CreateVirtualObjectAssetConvention(); private static VirtualObjectManager _instance; private static readonly object _lock = new object(); @@ -188,62 +190,15 @@ namespace NavisworksTransport.Core try { - var doc = Application.ActiveDocument; - var modelItems = new ModelItemCollection { _virtualObjectModelItem }; - - // 获取当前缩放 - var currentTransform = _virtualObjectModelItem.Transform; - var currentComponents = currentTransform.Factor(); - var currentScale = currentComponents.Scale; - - // 重置到CAD原始状态 - doc.Models.ResetPermanentTransform(modelItems); - - // 获取CAD原始状态 - var originalBounds = _virtualObjectModelItem.BoundingBox(); - var originalGroundPos = new Point3D( - originalBounds.Center.X, - originalBounds.Center.Y, - originalBounds.Min.Z - ); - var originalYaw = GetYawFromTransform(_virtualObjectModelItem.Transform); - - // 计算增量 - var deltaPos = new Vector3D( - position.X - originalGroundPos.X, - position.Y - originalGroundPos.Y, - position.Z - originalGroundPos.Z - ); - double deltaYaw = yawRadians - originalYaw; - - // 构建变换,保留当前缩放 - var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0)); - var components = identity.Factor(); - components.Scale = currentScale; - - if (Math.Abs(deltaYaw) > 0.001) - { - double cos = Math.Cos(deltaYaw); - double sin = Math.Sin(deltaYaw); - double rotatedX = originalGroundPos.X * cos - originalGroundPos.Y * sin; - double rotatedY = originalGroundPos.X * sin + originalGroundPos.Y * cos; - - components.Rotation = new Rotation3D(new UnitVector3D(0, 0, 1), deltaYaw); - components.Translation = new Vector3D( - position.X - rotatedX, - position.Y - rotatedY, - deltaPos.Z - ); - } - else - { - components.Translation = deltaPos; - } - - Transform3D transform = components.Combine(); - doc.Models.OverridePermanentTransform(modelItems, transform, false); - - LogManager.Debug($"虚拟物体已移动"); + var adapter = CoordinateSystemManager.Instance.CreateHostAdapter(); + var hostForward = adapter.FromCanonicalVector(new Vector3D(Math.Cos(yawRadians), Math.Sin(yawRadians), 0)); + var hostUp = adapter.FromCanonicalVector(new Vector3D( + HostCoordinateAdapter.CanonicalUpVector3.X, + HostCoordinateAdapter.CanonicalUpVector3.Y, + HostCoordinateAdapter.CanonicalUpVector3.Z)); + var rotation = VirtualObjectAxisConvention.CreateRotation(hostForward, hostUp); + MoveVirtualObject(position, rotation); + LogManager.Debug($"虚拟物体已按Canonical平面yaw应用完整姿态"); } catch (Exception ex) { @@ -367,16 +322,14 @@ namespace NavisworksTransport.Core if (_virtualObjectModel == null || _virtualObjectModelItem == null) return; var doc = Application.ActiveDocument; - double metersToUnits = Utils.UnitsConverter.GetMetersToUnitsConversionFactor(doc.Units); - double baseSizeMeters = 0.01; - double scaleX = lengthMeters / baseSizeMeters; - double scaleY = widthMeters / baseSizeMeters; - double scaleZ = heightMeters / baseSizeMeters; var currentTransform = _virtualObjectModel.Transform; var transformComponents = currentTransform.Factor(); - transformComponents.Scale = new Vector3D(scaleX, scaleY, scaleZ); + transformComponents.Scale = VirtualObjectAxisConvention.CreateScaleVector( + lengthMeters / baseSizeMeters, + widthMeters / baseSizeMeters, + heightMeters / baseSizeMeters); Transform3D newTransform = transformComponents.Combine(); doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, _virtualObjectModel.Units, newTransform, false); @@ -409,20 +362,6 @@ namespace NavisworksTransport.Core } } - /// - /// 从Transform中提取Yaw角度(简化版本) - /// - private double GetYawFromTransform(Transform3D transform) - { - try - { - // 使用ModelItemTransformHelper中的方法 - return ModelItemTransformHelper.GetYawFromTransform(transform); - } - catch { } - return 0.0; - } - /// /// 清理虚拟物体(用于程序退出或切换文档时) /// diff --git a/src/Utils/CoordinateSystem/ModelAxisConvention.cs b/src/Utils/CoordinateSystem/ModelAxisConvention.cs index 7affc79..f1c9fb4 100644 --- a/src/Utils/CoordinateSystem/ModelAxisConvention.cs +++ b/src/Utils/CoordinateSystem/ModelAxisConvention.cs @@ -108,6 +108,33 @@ namespace NavisworksTransport.Utils.CoordinateSystem } } + /// + /// 虚拟物体资源的本地轴约定。 + /// 该约定仅描述资源文件本身的 local forward/up,不代表当前宿主文档的坐标系。 + /// + public static ModelAxisConvention CreateVirtualObjectAssetConvention() + { + return new ModelAxisConvention(LocalAxisDirection.PositiveX, LocalAxisDirection.PositiveZ); + } + + /// + /// 参考杆资源的本地轴约定。 + /// 当前资源约定长度轴沿本地 +X,截面 up 沿本地 +Z。 + /// + public static ModelAxisConvention CreateReferenceRodAssetConvention() + { + return new ModelAxisConvention(LocalAxisDirection.PositiveX, LocalAxisDirection.PositiveZ); + } + + /// + /// Rail 资源的本地轴约定。 + /// 当前资源约定前进方向沿本地 +X,向上沿本地 +Z。 + /// + public static ModelAxisConvention CreateRailAssetConvention() + { + return new ModelAxisConvention(LocalAxisDirection.PositiveX, LocalAxisDirection.PositiveZ); + } + public double GetForwardSize(BoundingBox3D bounds) { return GetAxisExtent(bounds, ForwardAxis); @@ -123,6 +150,23 @@ namespace NavisworksTransport.Utils.CoordinateSystem return GetAxisExtent(bounds, SideAxis); } + /// + /// 根据 forward/side/up 语义尺寸,构造资源本地 X/Y/Z 的缩放向量。 + /// + public Vector3D CreateScaleVector(double forwardSize, double sideSize, double upSize) + { + Vector3 scale = CreateScaleVector3(forwardSize, sideSize, upSize); + return new Vector3D(scale.X, scale.Y, scale.Z); + } + + public Vector3 CreateScaleVector3(double forwardSize, double sideSize, double upSize) + { + float scaleX = (float)ResolveLocalAxisScale(0, forwardSize, sideSize, upSize); + float scaleY = (float)ResolveLocalAxisScale(1, forwardSize, sideSize, upSize); + float scaleZ = (float)ResolveLocalAxisScale(2, forwardSize, sideSize, upSize); + return new Vector3(scaleX, scaleY, scaleZ); + } + private static Vector3 GetAxisVector3(LocalAxisDirection axis) { switch (axis) @@ -203,6 +247,26 @@ namespace NavisworksTransport.Utils.CoordinateSystem throw new InvalidOperationException("模型局部第三轴不是标准正交轴,无法确定局部轴约定。"); } + private double ResolveLocalAxisScale(int localAxisIndex, double forwardSize, double sideSize, double upSize) + { + if (MatchesAxis(ForwardAxis, localAxisIndex, out _)) + { + return forwardSize; + } + + if (MatchesAxis(UpAxis, localAxisIndex, out _)) + { + return upSize; + } + + if (MatchesAxis(SideAxis, localAxisIndex, out _)) + { + return sideSize; + } + + throw new InvalidOperationException("无法为模型局部轴解析缩放尺寸。"); + } + private static bool IsApproximately(Vector3 vector, double x, double y, double z) { return Math.Abs(vector.X - x) < 1e-6 && diff --git a/src/Utils/RailPathPoseHelper.cs b/src/Utils/RailPathPoseHelper.cs index c668a4d..82b507c 100644 --- a/src/Utils/RailPathPoseHelper.cs +++ b/src/Utils/RailPathPoseHelper.cs @@ -168,7 +168,7 @@ namespace NavisworksTransport.Utils previousPoint, currentPoint, nextPoint, - ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.ZUp), + ModelAxisConvention.CreateRailAssetConvention(), out linearTransform); } @@ -247,7 +247,7 @@ namespace NavisworksTransport.Utils previousPoint, currentPoint, nextPoint, - ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.ZUp), + ModelAxisConvention.CreateRailAssetConvention(), out rotation); }