406 lines
15 KiB
C#
406 lines
15 KiB
C#
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||
using NavisworksTransport.Core;
|
||
using NavisworksTransport.PathPlanning;
|
||
using NavisworksTransport.Utils.CoordinateSystem;
|
||
using System.Numerics;
|
||
using System.Reflection;
|
||
using System.Collections.Generic;
|
||
using Autodesk.Navisworks.Api;
|
||
|
||
namespace NavisworksTransport.UnitTests.CoordinateSystem
|
||
{
|
||
[TestClass]
|
||
public class AutoPathPlanningCoordinateSemanticsTests
|
||
{
|
||
[TestMethod]
|
||
public void YUp_HostPlanarGridHelper_CreateHostPoint_ShouldApplyElevationOnHostY()
|
||
{
|
||
var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
|
||
|
||
Vector3 point = HostPlanarGridHelper.CreateHostPoint3(2.0, 3.0, 14.83, adapter);
|
||
|
||
AssertPoint(point, 2.0, 14.83, 3.0);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void ZUp_HostPlanarGridHelper_CreateHostPoint_ShouldApplyElevationOnHostZ()
|
||
{
|
||
var adapter = new HostCoordinateAdapter(CoordinateSystemType.ZUp);
|
||
|
||
Vector3 point = HostPlanarGridHelper.CreateHostPoint3(2.0, 3.0, 6.25, adapter);
|
||
|
||
AssertPoint(point, 2.0, 3.0, 6.25);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_HostPlanarGridHelper_GetAndSetElevation_ShouldUseHostY()
|
||
{
|
||
var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
|
||
var hostPoint = new Vector3(4.0f, 15.0f, 6.0f);
|
||
|
||
double elevation = HostPlanarGridHelper.GetElevation3(hostPoint, adapter);
|
||
Vector3 adjusted = HostPlanarGridHelper.SetElevation3(hostPoint, 20.0, adapter);
|
||
|
||
Assert.AreEqual(15.0, elevation, 1e-6);
|
||
AssertPoint(adjusted, 4.0, 20.0, 6.0);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_HostPlanarGridHelper_GetHorizontalCoords_ShouldProjectToHostXZPlane()
|
||
{
|
||
var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
|
||
var hostPoint = new Vector3(8.0f, 14.83f, -5.35f);
|
||
|
||
(double h1, double h2) = HostPlanarGridHelper.GetHorizontalCoords3(hostPoint, adapter);
|
||
|
||
Assert.AreEqual(8.0, h1, 1e-6);
|
||
Assert.AreEqual(-5.35, h2, 1e-6);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_HostPlanarGridHelper_HorizontalRange_ShouldMatchHostXZPlane()
|
||
{
|
||
var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
|
||
var hostMin = new Vector3(-10.0f, 2.0f, -30.0f);
|
||
var hostMax = new Vector3(40.0f, 12.0f, 5.0f);
|
||
|
||
(double min1, double max1, double min2, double max2) =
|
||
HostPlanarGridHelper.GetHorizontalRange3(hostMin, hostMax, adapter);
|
||
|
||
Assert.AreEqual(-10.0, min1, 1e-6);
|
||
Assert.AreEqual(40.0, max1, 1e-6);
|
||
Assert.AreEqual(-30.0, min2, 1e-6);
|
||
Assert.AreEqual(5.0, max2, 1e-6);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_GridLikeWorldPointConstruction_ShouldWriteElevationToHostY()
|
||
{
|
||
var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
|
||
double originH1 = -210.0;
|
||
double originH2 = -10.0;
|
||
double cellSize = 1.0;
|
||
|
||
Vector3 worldPoint = HostPlanarGridHelper.CreateHostPoint3(
|
||
originH1 + 4 * cellSize,
|
||
originH2 + 6 * cellSize,
|
||
14.83,
|
||
adapter);
|
||
|
||
Assert.AreEqual(-206.0, worldPoint.X, 1e-6);
|
||
Assert.AreEqual(14.83, worldPoint.Y, 1e-6);
|
||
Assert.AreEqual(-4.0, worldPoint.Z, 1e-6);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_GridLikeWorldToGrid_ShouldReadHostXZAsPlanarAxes()
|
||
{
|
||
var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
|
||
var origin = new Vector3(-210.0f, 14.83f, -10.0f);
|
||
var endPoint = new Vector3(-169.20f, 14.83f, -5.35f);
|
||
const double cellSize = 1.0;
|
||
|
||
(double pointH1, double pointH2) = HostPlanarGridHelper.GetHorizontalCoords3(endPoint, adapter);
|
||
(double originH1, double originH2) = HostPlanarGridHelper.GetHorizontalCoords3(origin, adapter);
|
||
int gridX = (int)System.Math.Round((pointH1 - originH1) / cellSize);
|
||
int gridY = (int)System.Math.Round((pointH2 - originH2) / cellSize);
|
||
|
||
Assert.AreEqual(41, gridX);
|
||
Assert.AreEqual(5, gridY);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void ZUp_GridMap_CellPlanarBounds_ShouldMatchNearestNodeRoundSemantics()
|
||
{
|
||
var bounds = GridMap.CalculateNearestNodePlanarBounds(2.0, 3.0, 1.0);
|
||
|
||
Assert.AreEqual(1.5, bounds.minH1, 1e-9);
|
||
Assert.AreEqual(2.5, bounds.maxH1, 1e-9);
|
||
Assert.AreEqual(2.5, bounds.minH2, 1e-9);
|
||
Assert.AreEqual(3.5, bounds.maxH2, 1e-9);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void ZUp_GridMap_SegmentIntersection_ShouldDetectInteriorNotBoundaryTouch()
|
||
{
|
||
var bounds = GridMap.CalculateNearestNodePlanarBounds(2.0, 3.0, 1.0);
|
||
|
||
bool crossesInterior = GridMap.TryGetSegmentRectangleInteriorIntersection(
|
||
1.75,
|
||
3.0,
|
||
2.25,
|
||
3.0,
|
||
bounds.minH1,
|
||
bounds.maxH1,
|
||
bounds.minH2,
|
||
bounds.maxH2,
|
||
1.0,
|
||
out double enterT,
|
||
out double exitT);
|
||
|
||
Assert.IsTrue(crossesInterior);
|
||
Assert.IsTrue(enterT >= 0.0 && enterT <= exitT && exitT <= 1.0);
|
||
|
||
bool onlyTouchesBoundary = GridMap.TryGetSegmentRectangleInteriorIntersection(
|
||
1.5,
|
||
2.0,
|
||
1.5,
|
||
4.0,
|
||
bounds.minH1,
|
||
bounds.maxH1,
|
||
bounds.minH2,
|
||
bounds.maxH2,
|
||
1.0,
|
||
out _,
|
||
out _);
|
||
|
||
Assert.IsFalse(onlyTouchesBoundary, "贴着归属边界行走不应被当成穿过格子内部。");
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_GridMapGenerator_GetObstacleElevationRange_ShouldUseHostYInsteadOfWorldZ()
|
||
{
|
||
var generator = new GridMapGenerator();
|
||
|
||
var method = typeof(GridMapGenerator).GetMethod(
|
||
"GetObstacleElevationRange",
|
||
BindingFlags.Instance | BindingFlags.NonPublic,
|
||
null,
|
||
new[]
|
||
{
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(CoordinateSystemType)
|
||
},
|
||
null);
|
||
|
||
Assert.IsNotNull(method, "未找到 GetObstacleElevationRange 私有方法。");
|
||
|
||
var elevationRange = ((double min, double max))method.Invoke(generator, new object[]
|
||
{
|
||
-200.0, 10.0, -30.0,
|
||
-198.0, 30.0, -10.0,
|
||
CoordinateSystemType.YUp
|
||
});
|
||
|
||
Assert.AreEqual(10.0, elevationRange.min, 1e-6, "YUp 下障碍物最小高程应来自宿主 Y。");
|
||
Assert.AreEqual(30.0, elevationRange.max, 1e-6, "YUp 下障碍物最大高程应来自宿主 Y。");
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_GridMapGenerator_CalculateBoundingBoxGridCoverage_ShouldProjectToHostXZPlane()
|
||
{
|
||
var generator = new GridMapGenerator();
|
||
|
||
var method = typeof(GridMapGenerator).GetMethod(
|
||
"CalculateGridCoverageFromWorldExtents",
|
||
BindingFlags.Instance | BindingFlags.NonPublic,
|
||
null,
|
||
new[]
|
||
{
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(int), typeof(int), typeof(double),
|
||
typeof(CoordinateSystemType)
|
||
},
|
||
null);
|
||
|
||
Assert.IsNotNull(method, "未找到 CalculateGridCoverageFromWorldExtents 私有方法。");
|
||
|
||
var coveredCells = (List<(int x, int y)>)method.Invoke(generator, new object[]
|
||
{
|
||
-200.0, 10.0, -30.0,
|
||
-198.0, 30.0, -10.0,
|
||
-210.0, 14.83, -80.0,
|
||
200, 200, 1.0,
|
||
CoordinateSystemType.YUp
|
||
});
|
||
|
||
Assert.AreEqual(63, coveredCells.Count, "YUp 下障碍物包围盒应按宿主 XZ 平面覆盖 3x21 个网格。");
|
||
CollectionAssert.Contains(coveredCells, (10, 50));
|
||
CollectionAssert.Contains(coveredCells, (12, 70));
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_GridMapGenerator_CalculateDoorOpeningCoverage_ShouldProjectToHostXZPlane()
|
||
{
|
||
var generator = new GridMapGenerator();
|
||
|
||
var method = typeof(GridMapGenerator).GetMethod(
|
||
"CalculateDoorOpeningCoverageFromWorldExtents",
|
||
BindingFlags.Instance | BindingFlags.NonPublic,
|
||
null,
|
||
new[]
|
||
{
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(double),
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(int), typeof(int), typeof(double),
|
||
typeof(CoordinateSystemType)
|
||
},
|
||
null);
|
||
|
||
Assert.IsNotNull(method, "未找到 CalculateDoorOpeningCoverageFromWorldExtents 私有方法。");
|
||
|
||
var coveredCells = (List<(int x, int y)>)method.Invoke(generator, new object[]
|
||
{
|
||
10.0, 14.0, 20.0,
|
||
12.0, 16.0, 30.0,
|
||
4.0,
|
||
0.0, 0.0, 0.0,
|
||
100, 100, 1.0,
|
||
CoordinateSystemType.YUp
|
||
});
|
||
|
||
Assert.AreEqual(15, coveredCells.Count, "YUp 下门开口应按宿主 XZ 平面覆盖 3x5 个网格。");
|
||
CollectionAssert.Contains(coveredCells, (10, 23));
|
||
CollectionAssert.Contains(coveredCells, (12, 27));
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_PathPlanningManager_CalculateOptimalGridSize_ShouldUseHostXZHorizontalRange()
|
||
{
|
||
var method = typeof(PathPlanningManager).GetMethod(
|
||
"CalculateOptimalGridSizeFromBounds",
|
||
BindingFlags.Static | BindingFlags.NonPublic,
|
||
null,
|
||
new[]
|
||
{
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(CoordinateSystemType)
|
||
},
|
||
null);
|
||
|
||
Assert.IsNotNull(method, "未找到 CalculateOptimalGridSizeFromBounds 私有方法。");
|
||
|
||
var gridSize = (double)method.Invoke(null, new object[]
|
||
{
|
||
-10.0, 100.0, -300.0,
|
||
90.0, 120.0, 400.0,
|
||
CoordinateSystemType.YUp
|
||
});
|
||
|
||
Assert.AreEqual(2.0, gridSize, 1e-6, "YUp 下网格大小应基于宿主 XZ 平面最大跨度 700 计算。");
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_ChannelHeightDetector_GetBoundsElevationRange_ShouldUseHostY()
|
||
{
|
||
var method = typeof(ChannelHeightDetector).GetMethod(
|
||
"GetBoundsElevationRange",
|
||
BindingFlags.Static | BindingFlags.NonPublic,
|
||
null,
|
||
new[]
|
||
{
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(CoordinateSystemType)
|
||
},
|
||
null);
|
||
|
||
Assert.IsNotNull(method, "未找到 GetBoundsElevationRange 私有方法。");
|
||
|
||
var elevationRange = ((double min, double max))method.Invoke(null, new object[]
|
||
{
|
||
-10.0, 14.83, -30.0,
|
||
20.0, 18.50, 40.0,
|
||
CoordinateSystemType.YUp
|
||
});
|
||
|
||
Assert.AreEqual(14.83, elevationRange.min, 1e-6);
|
||
Assert.AreEqual(18.50, elevationRange.max, 1e-6);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_ChannelHeightDetector_CreateHeightProfileSample_ShouldUseHostXZPlaneAndHostYElevation()
|
||
{
|
||
var method = typeof(ChannelHeightDetector).GetMethod(
|
||
"CreateHeightProfileSamplePoint3",
|
||
BindingFlags.Static | BindingFlags.NonPublic,
|
||
null,
|
||
new[]
|
||
{
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(CoordinateSystemType),
|
||
typeof(double)
|
||
},
|
||
null);
|
||
|
||
Assert.IsNotNull(method, "未找到 CreateHeightProfileSamplePoint3 私有方法。");
|
||
|
||
var samplePoint = (Vector3)method.Invoke(null, new object[]
|
||
{
|
||
-10.0, 14.83, -30.0,
|
||
20.0, 18.50, 40.0,
|
||
CoordinateSystemType.YUp,
|
||
0.5
|
||
});
|
||
|
||
Assert.AreEqual(5.0, samplePoint.X, 1e-6, "YUp 下采样点第一水平轴应沿宿主 X 插值。");
|
||
Assert.AreEqual(14.83, samplePoint.Y, 1e-6, "YUp 下采样点高程应落在宿主底面 Y。");
|
||
Assert.AreEqual(5.0, samplePoint.Z, 1e-6, "YUp 下采样点第二水平轴应沿宿主 Z 插值。");
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_ChannelHeightDetector_GetPickedSurfaceElevation_ShouldUseHostYInsteadOfWorldZ()
|
||
{
|
||
var method = typeof(ChannelHeightDetector).GetMethod(
|
||
"GetPickedSurfaceElevation",
|
||
BindingFlags.Static | BindingFlags.NonPublic,
|
||
null,
|
||
new[]
|
||
{
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(CoordinateSystemType)
|
||
},
|
||
null);
|
||
|
||
Assert.IsNotNull(method, "未找到 GetPickedSurfaceElevation 私有方法。");
|
||
|
||
var elevation = (double)method.Invoke(null, new object[]
|
||
{
|
||
11.0, 22.0, 33.0,
|
||
CoordinateSystemType.YUp
|
||
});
|
||
|
||
Assert.AreEqual(22.0, elevation, 1e-6, "YUp 下拾取表面点的高程应来自宿主 Y。");
|
||
}
|
||
|
||
[TestMethod]
|
||
public void YUp_GridMapGenerator_ResolveBoundsMinElevation_ShouldUseHostYInsteadOfWorldZ()
|
||
{
|
||
var method = typeof(GridMapGenerator).GetMethod(
|
||
"ResolveBoundsMinElevation",
|
||
BindingFlags.Static | BindingFlags.NonPublic,
|
||
null,
|
||
new[]
|
||
{
|
||
typeof(double), typeof(double), typeof(double),
|
||
typeof(CoordinateSystemType)
|
||
},
|
||
null);
|
||
|
||
Assert.IsNotNull(method, "未找到 ResolveBoundsMinElevation 私有方法。");
|
||
|
||
var elevation = (double)method.Invoke(null, new object[]
|
||
{
|
||
0.0, 12.5, -100.0,
|
||
CoordinateSystemType.YUp
|
||
});
|
||
|
||
Assert.AreEqual(12.5, elevation, 1e-6, "YUp 下边界最小高程应来自宿主 Y,而不是 bounds.Min.Z。");
|
||
}
|
||
|
||
private static void AssertPoint(Vector3 actual, double x, double y, double z)
|
||
{
|
||
Assert.AreEqual(x, actual.X, 1e-6);
|
||
Assert.AreEqual(y, actual.Y, 1e-6);
|
||
Assert.AreEqual(z, actual.Z, 1e-6);
|
||
}
|
||
}
|
||
}
|