using Microsoft.VisualStudio.TestTools.UnitTesting; using NavisworksTransport.Utils.CoordinateSystem; using System.Numerics; namespace NavisworksTransport.UnitTests.CoordinateSystem { [TestClass] public class RealObjectProjectedExtentResolverTests { [TestMethod] public void Ground_DualAxisCorrection_ShouldProjectAgainstFinalPose() { var convention = new ModelAxisConvention(LocalAxisDirection.PositiveX, LocalAxisDirection.PositiveY); Vector3 targetForward = Vector3.Normalize(new Vector3(-0.8987f, 0.0f, 0.4386f)); Vector3 targetUp = Vector3.UnitY; Quaternion baseline = Quaternion.Normalize(Quaternion.CreateFromRotationMatrix(new Matrix4x4( -0.8987f, 0.0000f, 0.4386f, 0f, 0.0000f, 1.0000f, 0.0000f, 0f, -0.4386f, 0.0000f,-0.8987f, 0f, 0f, 0f, 0f, 1f))); var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp); Quaternion final = adapter.ComposeHostQuaternion( baseline, new LocalEulerRotationCorrection(90.0, 0.0, 90.0)); var extents = RealObjectProjectedExtentResolver.CalculateProjectedSemanticExtents( convention, forwardSize: 6.0, sideSize: 2.0, upSize: 4.0, baseline, final, targetForward, targetUp); 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 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, "双轴旋转后,Ground 的法线尺寸不应停留在旧高度。"); } [TestMethod] public void Ground_DisplayedHostAxesProjection_ShouldKeepXAxisLengthAndSwapXZAsAxesRotate() { Vector3 targetForward = Vector3.UnitX; Vector3 targetUp = Vector3.UnitY; var noCorrection = RealObjectProjectedExtentResolver.CalculateProjectedDisplayedExtents( displayedAxisXSize: 6.0, displayedAxisYSize: 2.0, displayedAxisZSize: 4.0, displayedHostAxisX: Vector3.UnitX, displayedHostAxisY: Vector3.UnitZ, displayedHostAxisZ: -Vector3.UnitY, targetForward, targetUp); Assert.AreEqual(6.0, noCorrection.forwardExtent, 1e-5); Assert.AreEqual(2.0, noCorrection.sideExtent, 1e-5); Assert.AreEqual(4.0, noCorrection.upExtent, 1e-5); var xRotated = RealObjectProjectedExtentResolver.CalculateProjectedDisplayedExtents( displayedAxisXSize: 6.0, displayedAxisYSize: 2.0, displayedAxisZSize: 4.0, displayedHostAxisX: Vector3.UnitX, displayedHostAxisY: Vector3.UnitY, displayedHostAxisZ: Vector3.UnitZ, targetForward, targetUp); Assert.AreEqual(6.0, xRotated.forwardExtent, 1e-5); Assert.AreEqual(4.0, xRotated.sideExtent, 1e-5); Assert.AreEqual(2.0, xRotated.upExtent, 1e-5); var zRotated = RealObjectProjectedExtentResolver.CalculateProjectedDisplayedExtents( displayedAxisXSize: 6.0, displayedAxisYSize: 2.0, displayedAxisZSize: 4.0, displayedHostAxisX: -Vector3.UnitZ, displayedHostAxisY: Vector3.UnitX, displayedHostAxisZ: -Vector3.UnitY, targetForward, targetUp); Assert.AreEqual(2.0, zRotated.forwardExtent, 1e-5); Assert.AreEqual(6.0, zRotated.sideExtent, 1e-5); Assert.AreEqual(4.0, zRotated.upExtent, 1e-5); } [TestMethod] public void Ground_DisplayedHostAxesProjection_ShouldIgnoreVerticalComponentInForward() { Vector3 targetUp = Vector3.UnitY; Vector3 planarForward = Vector3.Normalize(new Vector3(1f, 0f, 1f)); Vector3 slopedForward = Vector3.Normalize(new Vector3(1f, 2f, 1f)); var planar = RealObjectProjectedExtentResolver.CalculateProjectedDisplayedExtents( displayedAxisXSize: 6.0, displayedAxisYSize: 2.0, displayedAxisZSize: 4.0, displayedHostAxisX: Vector3.UnitX, displayedHostAxisY: Vector3.UnitZ, displayedHostAxisZ: -Vector3.UnitY, planarForward, targetUp); var sloped = RealObjectProjectedExtentResolver.CalculateProjectedDisplayedExtents( displayedAxisXSize: 6.0, displayedAxisYSize: 2.0, displayedAxisZSize: 4.0, displayedHostAxisX: Vector3.UnitX, displayedHostAxisY: Vector3.UnitZ, displayedHostAxisZ: -Vector3.UnitY, slopedForward, targetUp); Assert.AreNotEqual(planar.forwardExtent, sloped.forwardExtent, 1e-3, "原始带竖直分量的路径方向会污染 Ground 尺寸投影。"); } private static double ProjectExtent( Vector3 localSize, Vector3 rotatedLocalX, Vector3 rotatedLocalY, Vector3 rotatedLocalZ, Vector3 targetAxis) { return System.Math.Abs(Vector3.Dot(rotatedLocalX, targetAxis)) * localSize.X + System.Math.Abs(Vector3.Dot(rotatedLocalY, targetAxis)) * localSize.Y + System.Math.Abs(Vector3.Dot(rotatedLocalZ, targetAxis)) * localSize.Z; } } }