189 lines
7.6 KiB
C#
189 lines
7.6 KiB
C#
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using NavisworksTransport.Utils.CoordinateSystem;
|
|
using System;
|
|
using System.Numerics;
|
|
|
|
namespace NavisworksTransport.UnitTests.CoordinateSystem
|
|
{
|
|
[TestClass]
|
|
public class CanonicalPlanarPoseBuilderTests
|
|
{
|
|
[TestMethod]
|
|
public void StraightForward_ZUpConvention_ShouldKeepLocalZAsWorldUp()
|
|
{
|
|
bool ok = CanonicalPlanarPoseBuilder.TryCreateQuaternionFromForward(
|
|
new Vector3(5, 0, 0),
|
|
Vector3.UnitZ,
|
|
ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.ZUp),
|
|
out Quaternion rotation);
|
|
|
|
Assert.IsTrue(ok);
|
|
Matrix4x4 linear = Matrix4x4.CreateFromQuaternion(rotation);
|
|
AssertColumn(linear, 0, 1, 0, 0);
|
|
AssertColumn(linear, 1, 0, 1, 0);
|
|
AssertColumn(linear, 2, 0, 0, 1);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void StraightForward_YUpConvention_ShouldMapLocalYToWorldUp()
|
|
{
|
|
bool ok = CanonicalPlanarPoseBuilder.TryCreateQuaternionFromForward(
|
|
new Vector3(5, 0, 0),
|
|
Vector3.UnitZ,
|
|
ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.YUp),
|
|
out Quaternion rotation);
|
|
|
|
Assert.IsTrue(ok);
|
|
Matrix4x4 linear = Matrix4x4.CreateFromQuaternion(rotation);
|
|
AssertColumn(linear, 0, 1, 0, 0);
|
|
AssertColumn(linear, 1, 0, 0, 1);
|
|
AssertColumn(linear, 2, 0, -1, 0);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ForwardWithVerticalComponent_ShouldProjectToHorizontalPlane()
|
|
{
|
|
bool ok = CanonicalPlanarPoseBuilder.TryCreateQuaternionFromForward(
|
|
new Vector3(10, 0, 3),
|
|
Vector3.UnitZ,
|
|
ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.ZUp),
|
|
out Quaternion rotation);
|
|
|
|
Assert.IsTrue(ok);
|
|
Matrix4x4 linear = Matrix4x4.CreateFromQuaternion(rotation);
|
|
Assert.AreEqual(0.0, linear.M31, 1e-6);
|
|
Assert.AreEqual(0.0, linear.M32, 1e-6);
|
|
Assert.AreEqual(1.0, linear.M33, 1e-6);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ZeroLocalEulerCorrection_ShouldMatchCurrentPlanarBaseline()
|
|
{
|
|
var convention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.ZUp);
|
|
|
|
Assert.IsTrue(CanonicalPlanarPoseBuilder.TryCreateQuaternionFromForward(
|
|
new Vector3(5, 0, 0),
|
|
Vector3.UnitZ,
|
|
convention,
|
|
out Quaternion baselineRotation));
|
|
|
|
Assert.IsTrue(CanonicalPlanarPoseBuilder.TryCreateQuaternionFromForward(
|
|
new Vector3(5, 0, 0),
|
|
Vector3.UnitZ,
|
|
convention,
|
|
LocalEulerRotationCorrection.Zero,
|
|
out Quaternion correctedRotation));
|
|
|
|
AssertQuaternionEquivalent(baselineRotation, correctedRotation);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void LocalEulerCorrection_ShouldAlignCorrectedLocalForwardWithPlanarForward()
|
|
{
|
|
var convention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.ZUp);
|
|
var correction = new LocalEulerRotationCorrection(0.0, 0.0, 90.0);
|
|
var adapter = new HostCoordinateAdapter(CoordinateSystemType.ZUp);
|
|
Quaternion correctionQuaternion = adapter.CreateCanonicalRotationCorrection(correction);
|
|
|
|
Assert.IsTrue(CanonicalPlanarPoseBuilder.TryCreateQuaternionFromForward(
|
|
new Vector3(5, 0, 0),
|
|
Vector3.UnitZ,
|
|
convention,
|
|
correctionQuaternion,
|
|
out Quaternion rotation));
|
|
|
|
LocalAxisPoseBuilder.ApplyLocalPreRotation(convention, correctionQuaternion, out var correctedLocalForward, out var correctedLocalUp);
|
|
Matrix4x4 linear = Matrix4x4.CreateFromQuaternion(rotation);
|
|
|
|
Vector3 mappedForward = TransformLocalVector(linear, correctedLocalForward);
|
|
Vector3 mappedUp = TransformLocalVector(linear, correctedLocalUp);
|
|
|
|
AssertVector(mappedForward, 1, 0, 0);
|
|
AssertVector(mappedUp, 0, 0, 1);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void WorldCorrection_ShouldRotateBaselinePoseAroundHostYAxisInYUp()
|
|
{
|
|
var convention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.YUp);
|
|
var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
|
|
Quaternion worldCorrection = adapter.CreateCanonicalRotationCorrection(
|
|
new LocalEulerRotationCorrection(0.0, 90.0, 0.0));
|
|
|
|
Assert.IsTrue(CanonicalPlanarPoseBuilder.TryCreateQuaternionFromForward(
|
|
new Vector3(5, 0, 0),
|
|
Vector3.UnitZ,
|
|
convention,
|
|
out Quaternion baselineRotation));
|
|
|
|
Assert.IsTrue(CanonicalPlanarPoseBuilder.TryCreateQuaternionFromForwardWithWorldCorrection(
|
|
new Vector3(5, 0, 0),
|
|
Vector3.UnitZ,
|
|
convention,
|
|
worldCorrection,
|
|
out Quaternion correctedRotation));
|
|
|
|
Matrix4x4 baseline = Matrix4x4.CreateFromQuaternion(baselineRotation);
|
|
|
|
AssertColumn(baseline, 0, 1, 0, 0);
|
|
AssertColumn(baseline, 1, 0, 0, 1);
|
|
AssertColumn(baseline, 2, 0, -1, 0);
|
|
|
|
Quaternion expectedRotation = Quaternion.Normalize(worldCorrection * baselineRotation);
|
|
AssertQuaternionEquivalent(expectedRotation, correctedRotation);
|
|
}
|
|
|
|
private static void AssertColumn(Matrix4x4 matrix, int column, double x, double y, double z)
|
|
{
|
|
switch (column)
|
|
{
|
|
case 0:
|
|
Assert.AreEqual(x, matrix.M11, 1e-6);
|
|
Assert.AreEqual(y, matrix.M21, 1e-6);
|
|
Assert.AreEqual(z, matrix.M31, 1e-6);
|
|
break;
|
|
case 1:
|
|
Assert.AreEqual(x, matrix.M12, 1e-6);
|
|
Assert.AreEqual(y, matrix.M22, 1e-6);
|
|
Assert.AreEqual(z, matrix.M32, 1e-6);
|
|
break;
|
|
case 2:
|
|
Assert.AreEqual(x, matrix.M13, 1e-6);
|
|
Assert.AreEqual(y, matrix.M23, 1e-6);
|
|
Assert.AreEqual(z, matrix.M33, 1e-6);
|
|
break;
|
|
default:
|
|
Assert.Fail("Only first 3 columns are valid.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static void AssertVector(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);
|
|
}
|
|
|
|
private static void AssertQuaternionEquivalent(Quaternion expected, Quaternion actual)
|
|
{
|
|
double dot = Math.Abs(
|
|
expected.X * actual.X +
|
|
expected.Y * actual.Y +
|
|
expected.Z * actual.Z +
|
|
expected.W * actual.W);
|
|
|
|
Assert.AreEqual(1.0, dot, 1e-6);
|
|
}
|
|
|
|
private static Vector3 TransformLocalVector(Matrix4x4 linear, Vector3 localVector)
|
|
{
|
|
Vector3 world = new Vector3(
|
|
linear.M11 * localVector.X + linear.M12 * localVector.Y + linear.M13 * localVector.Z,
|
|
linear.M21 * localVector.X + linear.M22 * localVector.Y + linear.M23 * localVector.Z,
|
|
linear.M31 * localVector.X + linear.M32 * localVector.Y + linear.M33 * localVector.Z);
|
|
return Vector3.Normalize(world);
|
|
}
|
|
}
|
|
}
|