- 新增 ObjectPassageProjectionOptimizer(SO(3)粗搜索+Nelder-Mead) - EditRotationWindow 加入"自动调整"按钮 - AnimationControlViewModel 实现黑盒实测评估(临时摆位→读BoundingBox→恢复) - 设计文档和实现计划
145 lines
5.7 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|