using System; using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Autodesk.Navisworks.Api; namespace NavisworksTransport.UnitTests.Core { /// /// PathCurveEngine 核心算法测试 /// 测试路径曲线化算法的正确性 /// [TestClass] public class PathCurveEngineTests { #region CalculateFillet 测试 [TestMethod] public void CalculateFillet_RightAngleTurn_ReturnsValidArc() { // Arrange - 创建90度直角转弯 var pPrev = new Point3D(0, 10, 0); // 上方点 var pCurr = new Point3D(0, 0, 0); // 转弯点 var pNext = new Point3D(10, 0, 0); // 右方点 double turnRadius = 2.0; // Act var trajectory = PathCurveEngine.CalculateFillet(pPrev, pCurr, pNext, turnRadius); // Assert Assert.IsNotNull(trajectory, "轨迹不应该为null"); Assert.AreEqual(turnRadius, trajectory.RequestedRadius, 0.01, "请求半径应该正确"); Assert.IsTrue(trajectory.ActualRadius > 0, "实际半径应该大于0"); Assert.IsTrue(trajectory.DeflectionAngle > 0, "偏转角应该大于0"); Assert.IsTrue(trajectory.ArcLength > 0, "圆弧长度应该大于0"); // 验证切点位置 Assert.IsTrue(trajectory.Ts.Y > 0, "进入切点应该在转弯点上方"); Assert.IsTrue(trajectory.Ts.X == 0, "进入切点X坐标应该为0"); Assert.IsTrue(trajectory.Te.X > 0, "退出切点应该在转弯点右侧"); Assert.IsTrue(trajectory.Te.Y == 0, "退出切点Y坐标应该为0"); } [TestMethod] public void CalculateFillet_AcuteAngleTurn_ReturnsValidArc() { // Arrange - 创建锐角转弯(约60度) var pPrev = new Point3D(0, 10, 0); var pCurr = new Point3D(0, 0, 0); var pNext = new Point3D(8.66, 5, 0); // 60度方向 double turnRadius = 2.0; // Act var trajectory = PathCurveEngine.CalculateFillet(pPrev, pCurr, pNext, turnRadius); // Assert Assert.IsNotNull(trajectory, "轨迹不应该为null"); Assert.IsTrue(trajectory.ActualRadius > 0, "实际半径应该大于0"); Assert.IsTrue(trajectory.DeflectionAngle > 0.5 && trajectory.DeflectionAngle < 1.5, "偏转角应该在60度左右"); } [TestMethod] public void CalculateFillet_ObtuseAngleTurn_ReturnsValidArc() { // Arrange - 创建钝角转弯(约120度) var pPrev = new Point3D(0, 10, 0); var pCurr = new Point3D(0, 0, 0); var pNext = new Point3D(8.66, -5, 0); // 120度方向 double turnRadius = 2.0; // Act var trajectory = PathCurveEngine.CalculateFillet(pPrev, pCurr, pNext, turnRadius); // Assert Assert.IsNotNull(trajectory, "轨迹不应该为null"); Assert.IsTrue(trajectory.ActualRadius > 0, "实际半径应该大于0"); Assert.IsTrue(trajectory.DeflectionAngle > 1.5 && trajectory.DeflectionAngle < 2.5, "偏转角应该在120度左右"); } [TestMethod] public void CalculateFillet_CollinearPoints_ReturnsInvalidArc() { // Arrange - 创建共线点(几乎直线) var pPrev = new Point3D(0, 10, 0); var pCurr = new Point3D(0, 0, 0); var pNext = new Point3D(0, -10, 0); // 同一直线 double turnRadius = 2.0; // Act var trajectory = PathCurveEngine.CalculateFillet(pPrev, pCurr, pNext, turnRadius); // Assert Assert.IsNotNull(trajectory, "轨迹不应该为null"); Assert.AreEqual(0, trajectory.ActualRadius, 0.01, "实际半径应该为0"); Assert.AreEqual(0, trajectory.ArcLength, 0.01, "圆弧长度应该为0"); Assert.AreEqual(pCurr, trajectory.Ts, "切点应该与转弯点相同"); } [TestMethod] public void CalculateFillet_SafetyTruncation_AdjustsRadius() { // Arrange - 创建短边场景,需要安全截断 var pPrev = new Point3D(0, 1, 0); // 短边 var pCurr = new Point3D(0, 0, 0); var pNext = new Point3D(1, 0, 0); // 短边 double turnRadius = 2.0; // 半径大于边长 // Act var trajectory = PathCurveEngine.CalculateFillet(pPrev, pCurr, pNext, turnRadius); // Assert Assert.IsNotNull(trajectory, "轨迹不应该为null"); Assert.IsTrue(trajectory.ActualRadius < turnRadius, "实际半径应该小于请求半径"); Assert.IsTrue(trajectory.ActualRadius > 0, "实际半径应该大于0"); } [TestMethod] public void CalculateFillet_3DTurn_ReturnsValidArc() { // Arrange - 创建3D空间中的转弯 var pPrev = new Point3D(0, 10, 0); var pCurr = new Point3D(0, 0, 0); var pNext = new Point3D(10, 0, 5); // 有Z轴变化 double turnRadius = 2.0; // Act var trajectory = PathCurveEngine.CalculateFillet(pPrev, pCurr, pNext, turnRadius); // Assert Assert.IsNotNull(trajectory, "轨迹不应该为null"); Assert.IsTrue(trajectory.ActualRadius > 0, "实际半径应该大于0"); Assert.IsTrue(trajectory.ArcCenter.Z != 0, "圆心Z坐标应该不为0"); } #endregion #region SampleArc 测试 [TestMethod] public void SampleArc_WithValidTrajectory_ReturnsSampledPoints() { // Arrange var trajectory = new ArcTrajectory { Ts = new Point3D(0, 2, 0), Te = new Point3D(2, 0, 0), ArcCenter = new Point3D(0, 0, 0), ActualRadius = 2.0, DeflectionAngle = Math.PI / 2, // 90度 ArcLength = Math.PI // 半圆周长 }; double samplingStep = 0.5; // Act var sampledPoints = PathCurveEngine.SampleArc(trajectory, samplingStep); // Assert Assert.IsNotNull(sampledPoints, "采样点列表不应该为null"); Assert.IsTrue(sampledPoints.Count >= 2, "采样点数量应该至少为2"); Assert.AreEqual(trajectory.Ts, sampledPoints.First(), "第一个点应该是进入切点"); Assert.AreEqual(trajectory.Te, sampledPoints.Last(), "最后一个点应该是退出切点"); } [TestMethod] public void SampleArc_SmallArcLength_ReturnsTwoPoints() { // Arrange var trajectory = new ArcTrajectory { Ts = new Point3D(0, 0.1, 0), Te = new Point3D(0.1, 0, 0), ArcCenter = new Point3D(0, 0, 0), ActualRadius = 0.1, DeflectionAngle = 0.1, ArcLength = 0.01 // 非常小的圆弧 }; double samplingStep = 0.05; // Act var sampledPoints = PathCurveEngine.SampleArc(trajectory, samplingStep); // Assert Assert.IsNotNull(sampledPoints, "采样点列表不应该为null"); Assert.AreEqual(2, sampledPoints.Count, "小圆弧应该返回2个点"); } [TestMethod] public void SampleArc_SamplingStep0_05_ReturnsCorrectCount() { // Arrange var trajectory = new ArcTrajectory { Ts = new Point3D(0, 2, 0), Te = new Point3D(2, 0, 0), ArcCenter = new Point3D(0, 0, 0), ActualRadius = 2.0, DeflectionAngle = Math.PI / 2, ArcLength = Math.PI }; double samplingStep = 0.05; // Act var sampledPoints = PathCurveEngine.SampleArc(trajectory, samplingStep); // Assert int expectedCount = (int)Math.Ceiling(Math.PI / 0.05) + 1; Assert.IsTrue(sampledPoints.Count >= expectedCount - 2 && sampledPoints.Count <= expectedCount + 2, $"采样点数量应该在 {expectedCount - 2} 到 {expectedCount + 2} 之间"); } #endregion #region ApplyCurvatureToRoute 测试 [TestMethod] public void ApplyCurvatureToRoute_SimplePath_GeneratesEdges() { // Arrange var route = new PathRoute { Id = Guid.NewGuid().ToString(), Name = "测试路径", TurnRadius = 2.0 }; route.Points = new List { new PathPoint(new Point3D(0, 0, 0), "起点", PathPointType.StartPoint), new PathPoint(new Point3D(10, 0, 0), "点2", PathPointType.WayPoint), new PathPoint(new Point3D(10, 10, 0), "终点", PathPointType.EndPoint) }; route.Points[0].Index = 0; route.Points[1].Index = 1; route.Points[2].Index = 2; double samplingStep = 0.5; // Act PathCurveEngine.ApplyCurvatureToRoute(route, samplingStep); // Assert Assert.IsTrue(route.IsCurved, "路径应该被标记为已曲线化"); Assert.IsTrue(route.Edges.Count > 0, "应该生成路径边"); Assert.IsTrue(route.TotalLength > 0, "路径总长度应该大于0"); // 验证每个边都有采样点 foreach (var edge in route.Edges) { Assert.IsNotNull(edge.SampledPoints, "每个边都应该有采样点"); Assert.IsTrue(edge.SampledPoints.Count > 0, "采样点数量应该大于0"); } } [TestMethod] public void ApplyCurvatureToRoute_LShapedPath_GeneratesArcEdge() { // Arrange var route = new PathRoute { Id = Guid.NewGuid().ToString(), Name = "L型路径", TurnRadius = 1.5 }; route.Points = new List { new PathPoint(new Point3D(0, 10, 0), "起点", PathPointType.StartPoint), new PathPoint(new Point3D(0, 0, 0), "转弯点", PathPointType.WayPoint), new PathPoint(new Point3D(10, 0, 0), "终点", PathPointType.EndPoint) }; route.Points[0].Index = 0; route.Points[1].Index = 1; route.Points[2].Index = 2; double samplingStep = 0.5; // Act PathCurveEngine.ApplyCurvatureToRoute(route, samplingStep); // Assert Assert.IsTrue(route.IsCurved, "路径应该被标记为已曲线化"); Assert.AreEqual(2, route.Edges.Count, "L型路径应该生成2条边"); // 第一条边是直线段 Assert.AreEqual(PathSegmentType.Straight, route.Edges[0].SegmentType, "第一条边应该是直线段"); // 第二条边包含圆弧 Assert.AreEqual(PathSegmentType.Arc, route.Edges[1].SegmentType, "第二条边应该是圆弧段"); Assert.IsNotNull(route.Edges[1].Trajectory, "圆弧边应该有轨迹数据"); Assert.IsTrue(route.Edges[1].Trajectory.ActualRadius > 0, "实际半径应该大于0"); } [TestMethod] public void ApplyCurvatureToRoute_ZShapedPath_GeneratesMultipleArcEdges() { // Arrange var route = new PathRoute { Id = Guid.NewGuid().ToString(), Name = "Z型路径", TurnRadius = 1.5 }; route.Points = new List { new PathPoint(new Point3D(0, 10, 0), "起点", PathPointType.StartPoint), new PathPoint(new Point3D(0, 0, 0), "转弯点1", PathPointType.WayPoint), new PathPoint(new Point3D(10, 0, 0), "转弯点2", PathPointType.WayPoint), new PathPoint(new Point3D(10, 10, 0), "终点", PathPointType.EndPoint) }; route.Points[0].Index = 0; route.Points[1].Index = 1; route.Points[2].Index = 2; route.Points[3].Index = 3; double samplingStep = 0.5; // Act PathCurveEngine.ApplyCurvatureToRoute(route, samplingStep); // Assert Assert.IsTrue(route.IsCurved, "路径应该被标记为已曲线化"); Assert.AreEqual(3, route.Edges.Count, "Z型路径应该生成3条边"); // 应该有两条圆弧边 int arcEdgeCount = route.Edges.Count(e => e.SegmentType == PathSegmentType.Arc); Assert.IsTrue(arcEdgeCount >= 1, "应该至少有一条圆弧边"); } [TestMethod] public void ApplyCurvatureToRoute_NullRoute_DoesNotThrow() { // Arrange PathRoute route = null; double samplingStep = 0.5; // Act & Assert try { PathCurveEngine.ApplyCurvatureToRoute(route, samplingStep); Assert.Fail("应该抛出ArgumentNullException"); } catch (ArgumentNullException) { // Expected } } [TestMethod] public void ApplyCurvatureToRoute_LessThanTwoPoints_DoesNotCurve() { // Arrange var route = new PathRoute { Id = Guid.NewGuid().ToString(), Name = "单点路径", TurnRadius = 2.0 }; route.Points = new List { new PathPoint(new Point3D(0, 0, 0), "起点", PathPointType.StartPoint) }; route.Points[0].Index = 0; double samplingStep = 0.5; // Act PathCurveEngine.ApplyCurvatureToRoute(route, samplingStep); // Assert Assert.IsFalse(route.IsCurved, "少于2个点的路径不应该被曲线化"); Assert.AreEqual(0, route.Edges.Count, "不应该生成任何边"); } #endregion #region RecalculateRouteLength 测试 [TestMethod] public void RecalculateRouteLength_StraightEdges_CalculatesCorrectly() { // Arrange var route = new PathRoute { Id = Guid.NewGuid().ToString(), Name = "测试路径" }; route.Edges = new List { new PathEdge { Id = Guid.NewGuid().ToString(), SegmentType = PathSegmentType.Straight, PhysicalLength = 10.0, SampledPoints = new List() }, new PathEdge { Id = Guid.NewGuid().ToString(), SegmentType = PathSegmentType.Straight, PhysicalLength = 5.0, SampledPoints = new List() } }; // Act PathCurveEngine.RecalculateRouteLength(route); // Assert Assert.AreEqual(15.0, route.TotalLength, 0.01, "路径总长度应该等于所有边长度之和"); } [TestMethod] public void RecalculateRouteLength_MixedEdgeTypes_CalculatesCorrectly() { // Arrange var route = new PathRoute { Id = Guid.NewGuid().ToString(), Name = "测试路径" }; route.Edges = new List { new PathEdge { Id = Guid.NewGuid().ToString(), SegmentType = PathSegmentType.Straight, PhysicalLength = 10.0, SampledPoints = new List() }, new PathEdge { Id = Guid.NewGuid().ToString(), SegmentType = PathSegmentType.Arc, PhysicalLength = Math.PI, // 半圆 SampledPoints = new List() }, new PathEdge { Id = Guid.NewGuid().ToString(), SegmentType = PathSegmentType.Straight, PhysicalLength = 5.0, SampledPoints = new List() } }; // Act PathCurveEngine.RecalculateRouteLength(route); // Assert double expectedLength = 10.0 + Math.PI + 5.0; Assert.AreEqual(expectedLength, route.TotalLength, 0.01, "路径总长度应该正确计算"); } [TestMethod] public void RecalculateRouteLength_NullRoute_DoesNotThrow() { // Arrange PathRoute route = null; // Act PathCurveEngine.RecalculateRouteLength(route); // Assert - 不应该抛出异常 Assert.IsTrue(true, "null路径不应该导致异常"); } [TestMethod] public void RecalculateRouteLength_EmptyEdges_SetsZeroLength() { // Arrange var route = new PathRoute { Id = Guid.NewGuid().ToString(), Name = "空路径", Edges = new List() }; // Act PathCurveEngine.RecalculateRouteLength(route); // Assert Assert.AreEqual(0, route.TotalLength, 0.01, "空边的路径长度应该为0"); } #endregion #region 集成测试 [TestMethod] public void FullWorkflow_CreateCurvePath_CalculatesCorrectLength() { // Arrange - 创建一个包含转弯的路径 var route = new PathRoute { Id = Guid.NewGuid().ToString(), Name = "完整测试路径", TurnRadius = 2.0 }; route.Points = new List { new PathPoint(new Point3D(0, 20, 0), "起点", PathPointType.StartPoint), new PathPoint(new Point3D(0, 0, 0), "转弯点", PathPointType.WayPoint), new PathPoint(new Point3D(20, 0, 0), "终点", PathPointType.EndPoint) }; route.Points[0].Index = 0; route.Points[1].Index = 1; route.Points[2].Index = 2; double samplingStep = 0.5; // Act - 应用曲线化 PathCurveEngine.ApplyCurvatureToRoute(route, samplingStep); // Assert Assert.IsTrue(route.IsCurved, "路径应该被曲线化"); Assert.IsTrue(route.TotalLength > 0, "路径总长度应该大于0"); // 验证路径总长度小于直线距离(因为圆弧比直线短) double straightDistance = 20 + 20; // 两条直线段 Assert.IsTrue(route.TotalLength < straightDistance, "曲线化后的路径长度应该小于直线距离"); } #endregion } }