using Microsoft.VisualStudio.TestTools.UnitTesting; using NavisworksTransport.Utils.CoordinateSystem; using System; using System.Numerics; namespace NavisworksTransport.UnitTests.CoordinateSystem { [TestClass] public class RealObjectRailExtentResolverTests { [TestMethod] public void YUp_ZeroCorrection_ShouldKeepResolvedRailUpExtent() { Vector3 referenceAxisX = new Vector3(-1.0f, 0.0f, 0.0f); Vector3 referenceAxisY = new Vector3(0.0f, 1.0f, 0.0f); Vector3 referenceAxisZ = new Vector3(0.0f, 0.0f, -1.0f); Vector3 desiredForward = Vector3.Normalize(new Vector3(-0.8987f, 0.0f, 0.4386f)); Quaternion baseline = Quaternion.Identity; bool ok = RealObjectRailExtentResolver.TryResolveProjectedSemanticExtents( referenceAxisX, referenceAxisY, referenceAxisZ, desiredForward, CoordinateSystemType.YUp, forwardSize: 6.0, sideSize: 2.0, upSize: 4.0, baseline, baseline, out ModelAxisConvention convention, out var extents); Assert.IsTrue(ok); Assert.AreEqual(LocalAxisDirection.PositiveX, convention.ForwardAxis); Assert.AreEqual(LocalAxisDirection.PositiveY, convention.UpAxis); Assert.AreEqual(6.0, extents.forwardExtent, 1e-6); Assert.AreEqual(2.0, extents.sideExtent, 1e-6); Assert.AreEqual(4.0, extents.upExtent, 1e-6); } [TestMethod] public void YUp_HostY90_ShouldKeepResolvedRailUpExtentAndSwapForwardSide() { Vector3 referenceAxisX = new Vector3(-1.0f, 0.0f, 0.0f); Vector3 referenceAxisY = new Vector3(0.0f, 1.0f, 0.0f); Vector3 referenceAxisZ = new Vector3(0.0f, 0.0f, -1.0f); Vector3 desiredForward = Vector3.Normalize(new Vector3(-0.8987f, 0.0f, 0.4386f)); Quaternion baseline = Quaternion.Identity; var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp); Quaternion final = adapter.ComposeHostQuaternion( baseline, new LocalEulerRotationCorrection(0.0, 90.0, 0.0)); bool ok = RealObjectRailExtentResolver.TryResolveProjectedSemanticExtents( referenceAxisX, referenceAxisY, referenceAxisZ, desiredForward, CoordinateSystemType.YUp, forwardSize: 6.0, sideSize: 2.0, upSize: 4.0, baseline, final, out _, out var extents); Assert.IsTrue(ok); Assert.AreEqual(2.0, extents.forwardExtent, 1e-6); Assert.AreEqual(6.0, extents.sideExtent, 1e-6); Assert.AreEqual(4.0, extents.upExtent, 1e-6); } [TestMethod] public void YUp_DualAxisCorrection_WithRailBaseline_ShouldProjectAgainstFinalRailPose() { Vector3 referenceAxisX = new Vector3(-1.0f, 0.0f, 0.0f); Vector3 referenceAxisY = new Vector3(0.0f, 1.0f, 0.0f); Vector3 referenceAxisZ = new Vector3(0.0f, 0.0f, -1.0f); Vector3 desiredForward = Vector3.Normalize(new Vector3(-0.8987f, 0.0f, 0.4386f)); Quaternion baseline = Quaternion.Normalize(Quaternion.CreateFromRotationMatrix(new Matrix4x4( 0.9900f, -0.1403f, -0.0161f, 0f, 0.1403f, 0.9901f, -0.0023f, 0f, 0.0163f, 0.0000f, 0.9999f, 0f, 0f, 0f, 0f, 1f))); var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp); Quaternion final = adapter.ComposeHostQuaternion( baseline, new LocalEulerRotationCorrection(0.0, 90.0, 90.0)); bool ok = RealObjectRailExtentResolver.TryResolveProjectedSemanticExtents( referenceAxisX, referenceAxisY, referenceAxisZ, desiredForward, CoordinateSystemType.YUp, forwardSize: 6.0, sideSize: 2.0, upSize: 4.0, baseline, final, out ModelAxisConvention convention, out var extents); Assert.IsTrue(ok); Assert.AreEqual(LocalAxisDirection.PositiveX, convention.ForwardAxis); Assert.AreEqual(LocalAxisDirection.PositiveY, convention.UpAxis); Vector3 localSize = convention.CreateScaleVector3(6.0, 2.0, 4.0); Vector3 rotatedLocalX = Vector3.Normalize(Vector3.Transform(Vector3.UnitX, final)); Vector3 rotatedLocalY = Vector3.Normalize(Vector3.Transform(Vector3.UnitY, final)); Vector3 rotatedLocalZ = Vector3.Normalize(Vector3.Transform(Vector3.UnitZ, final)); Vector3 targetForward = Vector3.Normalize(Vector3.Transform(convention.ForwardUnitVector, baseline)); Vector3 targetUp = Vector3.Normalize(Vector3.Transform(convention.UpUnitVector, baseline)); Vector3 targetSide = Vector3.Normalize(Vector3.Cross(targetForward, targetUp)); double expectedForward = ProjectExtent(localSize, rotatedLocalX, rotatedLocalY, rotatedLocalZ, targetForward); double expectedSide = ProjectExtent(localSize, rotatedLocalX, rotatedLocalY, rotatedLocalZ, targetSide); double expectedUp = ProjectExtent(localSize, rotatedLocalX, rotatedLocalY, rotatedLocalZ, targetUp); Assert.AreEqual(expectedForward, extents.forwardExtent, 1e-5); Assert.AreEqual(expectedSide, extents.sideExtent, 1e-5); Assert.AreEqual(expectedUp, extents.upExtent, 1e-5); Assert.AreNotEqual(4.0, extents.upExtent, 1e-3, "双轴旋转后,法向尺寸不应仍停留在单轴结果。"); } private static double ProjectExtent( Vector3 localSize, Vector3 rotatedLocalX, Vector3 rotatedLocalY, Vector3 rotatedLocalZ, Vector3 targetAxis) { return Math.Abs(Vector3.Dot(rotatedLocalX, targetAxis)) * localSize.X + Math.Abs(Vector3.Dot(rotatedLocalY, targetAxis)) * localSize.Y + Math.Abs(Vector3.Dot(rotatedLocalZ, targetAxis)) * localSize.Z; } } }