518 lines
19 KiB
C#
518 lines
19 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||
using Autodesk.Navisworks.Api;
|
||
|
||
namespace NavisworksTransport.UnitTests.Core
|
||
{
|
||
/// <summary>
|
||
/// PathCurveEngine 核心算法测试
|
||
/// 测试路径曲线化算法的正确性
|
||
/// </summary>
|
||
[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<PathPoint>
|
||
{
|
||
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<PathPoint>
|
||
{
|
||
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<PathPoint>
|
||
{
|
||
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<PathPoint>
|
||
{
|
||
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<PathEdge>
|
||
{
|
||
new PathEdge
|
||
{
|
||
Id = Guid.NewGuid().ToString(),
|
||
SegmentType = PathSegmentType.Straight,
|
||
PhysicalLength = 10.0,
|
||
SampledPoints = new List<Point3D>()
|
||
},
|
||
new PathEdge
|
||
{
|
||
Id = Guid.NewGuid().ToString(),
|
||
SegmentType = PathSegmentType.Straight,
|
||
PhysicalLength = 5.0,
|
||
SampledPoints = new List<Point3D>()
|
||
}
|
||
};
|
||
|
||
// 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<PathEdge>
|
||
{
|
||
new PathEdge
|
||
{
|
||
Id = Guid.NewGuid().ToString(),
|
||
SegmentType = PathSegmentType.Straight,
|
||
PhysicalLength = 10.0,
|
||
SampledPoints = new List<Point3D>()
|
||
},
|
||
new PathEdge
|
||
{
|
||
Id = Guid.NewGuid().ToString(),
|
||
SegmentType = PathSegmentType.Arc,
|
||
PhysicalLength = Math.PI, // 半圆
|
||
SampledPoints = new List<Point3D>()
|
||
},
|
||
new PathEdge
|
||
{
|
||
Id = Guid.NewGuid().ToString(),
|
||
SegmentType = PathSegmentType.Straight,
|
||
PhysicalLength = 5.0,
|
||
SampledPoints = new List<Point3D>()
|
||
}
|
||
};
|
||
|
||
// 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<PathEdge>()
|
||
};
|
||
|
||
// 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<PathPoint>
|
||
{
|
||
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
|
||
}
|
||
} |