NavisworksTransport/UnitTests/CoordinateSystem/ObjectPassageProjectionOptimizerTests.cs
tian 7187bcf622 自动最优朝向: 黑盒实测模式
- 新增 ObjectPassageProjectionOptimizer(SO(3)粗搜索+Nelder-Mead)
- EditRotationWindow 加入"自动调整"按钮
- AnimationControlViewModel 实现黑盒实测评估(临时摆位→读BoundingBox→恢复)
- 设计文档和实现计划
2026-05-27 12:24:01 +08:00

145 lines
5.7 KiB
C#

using Microsoft.VisualStudio.TestTools.UnitTesting;
using NavisworksTransport.Utils.CoordinateSystem;
using System;
using System.Numerics;
namespace NavisworksTransport.UnitTests.CoordinateSystem
{
[TestClass]
public class ObjectPassageProjectionOptimizerTests
{
[TestMethod]
public void Optimize_LongTallBox_ShouldFindThreeAxisPoseBetterThanYawOnly()
{
var request = CreateZUpRequest(
sizeX: 8.0,
sizeY: 4.0,
sizeZ: 2.0,
pathForward: Vector3.UnitX);
ObjectPassageProjectionOptimizationResult result = ObjectPassageProjectionOptimizer.Optimize(request);
ObjectPassageProjectionScore yawOnly = ObjectPassageProjectionOptimizer.Evaluate(
request,
new LocalEulerRotationCorrection(0.0, 0.0, 90.0));
Assert.IsTrue(result.Success, result.ErrorMessage);
Assert.IsTrue(result.Score.Area < yawOnly.Area - 1e-6,
$"三轴优化面积 {result.Score.Area:F6} 应小于单轴 yaw 面积 {yawOnly.Area:F6}");
Assert.AreEqual(4.0, result.Score.WidthAcrossPath, 1e-4);
Assert.AreEqual(2.0, result.Score.HeightAlongHostUp, 1e-4);
}
[TestMethod]
public void Optimize_WhenAreasAreClose_ShouldPreferLowerHeight()
{
var request = CreateZUpRequest(
sizeX: 6.0,
sizeY: 3.0,
sizeZ: 2.0,
pathForward: Vector3.UnitX,
areaRelativeTieTolerance: 0.05);
ObjectPassageProjectionOptimizationResult result = ObjectPassageProjectionOptimizer.Optimize(request);
Assert.IsTrue(result.Success, result.ErrorMessage);
Assert.AreEqual(3.0, result.Score.WidthAcrossPath, 1e-4);
Assert.AreEqual(2.0, result.Score.HeightAlongHostUp, 1e-4);
}
[TestMethod]
public void Optimize_WhenBaselineAlreadyAlignedToPath_ShouldKeepZeroCorrection()
{
Quaternion baselineRotation = Quaternion.CreateFromAxisAngle(
Vector3.UnitZ,
(float)(Math.PI / 4.0));
var request = new ObjectPassageProjectionOptimizationRequest(
sizeX: 6.0,
sizeY: 3.0,
sizeZ: 2.0,
hostPathForward: Vector3.Normalize(new Vector3(1.0f, 1.0f, 0.0f)),
hostUp: Vector3.UnitZ,
areaRelativeTieTolerance: 0.01,
baselineHostRotation: baselineRotation);
ObjectPassageProjectionOptimizationResult result = ObjectPassageProjectionOptimizer.Optimize(request);
Assert.IsTrue(result.Success, result.ErrorMessage);
Assert.AreEqual(0.0, result.Correction.XDegrees, 1e-6);
Assert.AreEqual(0.0, result.Correction.YDegrees, 1e-6);
Assert.AreEqual(0.0, result.Correction.ZDegrees, 1e-6);
Assert.AreEqual(3.0, result.Score.WidthAcrossPath, 1e-4);
Assert.AreEqual(2.0, result.Score.HeightAlongHostUp, 1e-4);
}
[TestMethod]
public void Evaluate_YUp_ShouldUseHostYAsHeight()
{
var request = new ObjectPassageProjectionOptimizationRequest(
sizeX: 6.0,
sizeY: 2.0,
sizeZ: 4.0,
hostPathForward: Vector3.UnitX,
hostUp: Vector3.UnitY);
ObjectPassageProjectionScore score = ObjectPassageProjectionOptimizer.Evaluate(
request,
LocalEulerRotationCorrection.Zero);
Assert.AreEqual(4.0, score.WidthAcrossPath, 1e-6);
Assert.AreEqual(2.0, score.HeightAlongHostUp, 1e-6);
Assert.AreEqual(8.0, score.Area, 1e-6);
}
[TestMethod]
public void Optimize_SameInputTwice_ShouldReturnSameCorrection()
{
var request = CreateZUpRequest(
sizeX: 9.0,
sizeY: 4.0,
sizeZ: 2.0,
pathForward: Vector3.Normalize(new Vector3(1.0f, 1.0f, 0.0f)));
ObjectPassageProjectionOptimizationResult first = ObjectPassageProjectionOptimizer.Optimize(request);
ObjectPassageProjectionOptimizationResult second = ObjectPassageProjectionOptimizer.Optimize(request);
Assert.IsTrue(first.Success, first.ErrorMessage);
Assert.IsTrue(second.Success, second.ErrorMessage);
Assert.AreEqual(first.Correction.XDegrees, second.Correction.XDegrees, 1e-9);
Assert.AreEqual(first.Correction.YDegrees, second.Correction.YDegrees, 1e-9);
Assert.AreEqual(first.Correction.ZDegrees, second.Correction.ZDegrees, 1e-9);
Assert.AreEqual(first.Score.Area, second.Score.Area, 1e-9);
}
[TestMethod]
public void Optimize_PathForwardParallelToHostUp_ShouldFail()
{
var request = CreateZUpRequest(
sizeX: 6.0,
sizeY: 4.0,
sizeZ: 2.0,
pathForward: Vector3.UnitZ);
ObjectPassageProjectionOptimizationResult result = ObjectPassageProjectionOptimizer.Optimize(request);
Assert.IsFalse(result.Success);
StringAssert.Contains(result.ErrorMessage, "路径方向退化");
}
private static ObjectPassageProjectionOptimizationRequest CreateZUpRequest(
double sizeX,
double sizeY,
double sizeZ,
Vector3 pathForward,
double areaRelativeTieTolerance = 0.01)
{
return new ObjectPassageProjectionOptimizationRequest(
sizeX,
sizeY,
sizeZ,
pathForward,
Vector3.UnitZ,
areaRelativeTieTolerance);
}
}
}