refactor: 清理 fragment 参考姿态残留代码(0.18.2 默认轴约定后的死代码)
删除: - RealObjectReferencePoseResolver: TryResolveFromModelItem / TryResolveFromFragmentMatrices (fragment 代表姿态解析链,已无调用者;保留诊断用 TryDetectDefaultUpAxis) - RealObjectReferencePose.cs(无引用死文件) - PathAnimationManager: _realObjectReferenceHostSemantic* 字段(无消费) - FragmentRepresentativePoseHelper: TryGetRepresentativeRotation(ModelItem/矩阵集合) 与 TryInterpretRepresentativeFrame(仅死链使用;保留诊断用 TryGetRepresentativeFrame) - 测试:RealObjectReferencePoseResolverTests(测死链)、 FragmentRepresentativePoseHelperTests 缩减至在用方法 保留(非残留): - GeometryHelper/GeometryCacheManager fragment(几何三角形生成) - FragmentDefaultUpContext / RealObjectRailAxisConventionResolver(在用) - TestAutomationHttpService(集成测试服务) 验证:单元测试 187/187、集成真实物体动画 3/3、F1 Rail+42" Completed
This commit is contained in:
parent
10e93b1c32
commit
7c4783dca6
@ -103,7 +103,6 @@
|
||||
<Compile Include="UnitTests\CoordinateSystem\ProjectReferenceFrameTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\RealObjectPlanarPoseSolverTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\RealObjectProjectedExtentResolverTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\RealObjectReferencePoseResolverTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\RealObjectRailAxisConventionResolverTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\RealObjectRailExtentResolverTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\RotatedObjectExtentHelperTests.cs" />
|
||||
|
||||
@ -364,7 +364,6 @@
|
||||
<Compile Include="src\Utils\CoordinateSystem\CanonicalTrackedPositionResolver.cs" />
|
||||
<Compile Include="src\Utils\CoordinateSystem\FragmentDefaultUpContext.cs" />
|
||||
<Compile Include="src\Utils\CoordinateSystem\FragmentRepresentativePoseHelper.cs" />
|
||||
<Compile Include="src\Utils\CoordinateSystem\RealObjectReferencePose.cs" />
|
||||
<Compile Include="src\Utils\CoordinateSystem\RealObjectReferencePoseResolver.cs" />
|
||||
<Compile Include="src\Utils\CoordinateSystem\HoistingRealObjectPoseHelper.cs" />
|
||||
<Compile Include="src\Utils\CoordinateSystem\PathTargetFrame.cs" />
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using NavisworksTransport.Utils.CoordinateSystem;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace NavisworksTransport.UnitTests.CoordinateSystem
|
||||
@ -8,71 +7,6 @@ namespace NavisworksTransport.UnitTests.CoordinateSystem
|
||||
[TestClass]
|
||||
public class FragmentRepresentativePoseHelperTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldAverageFragmentMatrices_AndIgnoreTranslation()
|
||||
{
|
||||
Quaternion expectedRotation = Quaternion.Normalize(
|
||||
Quaternion.CreateFromAxisAngle(Vector3.UnitZ, (float)(Math.PI / 6.0)));
|
||||
|
||||
double[] fragmentMatrix1 = CreateFragmentMatrix(expectedRotation, new Vector3(10.0f, 20.0f, 30.0f));
|
||||
double[] fragmentMatrix2 = CreateFragmentMatrix(expectedRotation, new Vector3(-3.0f, 8.0f, 1.0f));
|
||||
|
||||
bool ok = FragmentRepresentativePoseHelper.TryGetRepresentativeRotation(
|
||||
new[] { fragmentMatrix1, fragmentMatrix2 },
|
||||
out Quaternion representativeRotation);
|
||||
|
||||
Assert.IsTrue(ok);
|
||||
AssertVector(
|
||||
Vector3.Transform(Vector3.UnitX, representativeRotation),
|
||||
Vector3.Transform(Vector3.UnitX, expectedRotation),
|
||||
1e-4);
|
||||
AssertVector(
|
||||
Vector3.Transform(Vector3.UnitY, representativeRotation),
|
||||
Vector3.Transform(Vector3.UnitY, expectedRotation),
|
||||
1e-4);
|
||||
AssertVector(
|
||||
Vector3.Transform(Vector3.UnitZ, representativeRotation),
|
||||
Vector3.Transform(Vector3.UnitZ, expectedRotation),
|
||||
1e-4);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldIgnoreQuaternionSignWhenAveragingFragments()
|
||||
{
|
||||
Quaternion expectedRotation = Quaternion.Normalize(
|
||||
Quaternion.CreateFromAxisAngle(Vector3.UnitX, (float)(Math.PI / 4.0)));
|
||||
Quaternion oppositeSignRotation = new Quaternion(
|
||||
-expectedRotation.X,
|
||||
-expectedRotation.Y,
|
||||
-expectedRotation.Z,
|
||||
-expectedRotation.W);
|
||||
|
||||
bool ok = FragmentRepresentativePoseHelper.TryGetRepresentativeRotation(
|
||||
new[] { expectedRotation, oppositeSignRotation },
|
||||
out Quaternion representativeRotation);
|
||||
|
||||
Assert.IsTrue(ok);
|
||||
AssertVector(
|
||||
Vector3.Transform(Vector3.UnitY, representativeRotation),
|
||||
Vector3.Transform(Vector3.UnitY, expectedRotation),
|
||||
1e-4);
|
||||
AssertVector(
|
||||
Vector3.Transform(Vector3.UnitZ, representativeRotation),
|
||||
Vector3.Transform(Vector3.UnitZ, expectedRotation),
|
||||
1e-4);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldFail_WhenNoFragmentRotationsAreAvailable()
|
||||
{
|
||||
bool ok = FragmentRepresentativePoseHelper.TryGetRepresentativeRotation(
|
||||
Array.Empty<double[]>(),
|
||||
out Quaternion representativeRotation);
|
||||
|
||||
Assert.IsFalse(ok);
|
||||
Assert.AreEqual(Quaternion.Identity, representativeRotation);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldPreserveAxes_ForObservedYUpRealObjectFragmentBasis()
|
||||
{
|
||||
@ -126,47 +60,11 @@ namespace NavisworksTransport.UnitTests.CoordinateSystem
|
||||
AssertVector(Vector3.Transform(Vector3.UnitZ, frame.Rotation), axisZ, 1e-4);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryInterpretRepresentativeFrame_YUpHost_WithFragmentDefaultUpZ_ShouldMapZToHostY()
|
||||
{
|
||||
var raw = new FragmentRepresentativePoseHelper.RepresentativeFrame(
|
||||
new Vector3(1.0f, 0.0f, 0.0f),
|
||||
new Vector3(0.0f, 0.0f, -1.0f),
|
||||
new Vector3(0.0f, 1.0f, 0.0f),
|
||||
Quaternion.Identity);
|
||||
|
||||
bool ok = FragmentRepresentativePoseHelper.TryInterpretRepresentativeFrame(
|
||||
raw,
|
||||
"Z",
|
||||
"Y",
|
||||
out var interpreted);
|
||||
|
||||
Assert.IsTrue(ok);
|
||||
AssertVector(interpreted.AxisX, new Vector3(1.0f, 0.0f, 0.0f), 1e-4);
|
||||
AssertVector(interpreted.AxisY, new Vector3(0.0f, 1.0f, 0.0f), 1e-4);
|
||||
AssertVector(interpreted.AxisZ, new Vector3(0.0f, 0.0f, 1.0f), 1e-4);
|
||||
}
|
||||
|
||||
private static double[] CreateFragmentMatrix(Quaternion rotation, Vector3 translation)
|
||||
{
|
||||
Vector3 axisX = Vector3.Transform(Vector3.UnitX, rotation);
|
||||
Vector3 axisY = Vector3.Transform(Vector3.UnitY, rotation);
|
||||
Vector3 axisZ = Vector3.Transform(Vector3.UnitZ, rotation);
|
||||
|
||||
return new[]
|
||||
{
|
||||
(double)axisX.X, (double)axisX.Y, (double)axisX.Z, 0.0,
|
||||
(double)axisY.X, (double)axisY.Y, (double)axisY.Z, 0.0,
|
||||
(double)axisZ.X, (double)axisZ.Y, (double)axisZ.Z, 0.0,
|
||||
(double)translation.X, (double)translation.Y, (double)translation.Z, 1.0
|
||||
};
|
||||
}
|
||||
|
||||
private static void AssertVector(Vector3 actual, Vector3 expected, double tolerance)
|
||||
{
|
||||
Assert.AreEqual(expected.X, actual.X, tolerance);
|
||||
Assert.AreEqual(expected.Y, actual.Y, tolerance);
|
||||
Assert.AreEqual(expected.Z, actual.Z, tolerance);
|
||||
Assert.AreEqual(expected.X, actual.X, tolerance, "X 分量");
|
||||
Assert.AreEqual(expected.Y, actual.Y, tolerance, "Y 分量");
|
||||
Assert.AreEqual(expected.Z, actual.Z, tolerance, "Z 分量");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,160 +0,0 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using NavisworksTransport.Utils.CoordinateSystem;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
namespace NavisworksTransport.UnitTests.CoordinateSystem
|
||||
{
|
||||
[TestClass]
|
||||
public class RealObjectReferencePoseResolverTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void YUp_HostWithFragmentDefaultZ_ShouldInterpretRepresentativeFrameToHostSemantics()
|
||||
{
|
||||
var fragmentMatrices = new List<double[]>
|
||||
{
|
||||
CreateMatrix(
|
||||
axisX: new Vector3(1.0f, 0.0f, 0.0f),
|
||||
axisY: new Vector3(0.0f, 1.0f, 0.0f),
|
||||
axisZ: new Vector3(0.0f, 0.0f, 1.0f))
|
||||
};
|
||||
|
||||
bool ok = RealObjectReferencePoseResolver.TryResolveFromFragmentMatrices(
|
||||
fragmentMatrices,
|
||||
fragmentDefaultUpAxis: "Z",
|
||||
hostUpAxis: "Y",
|
||||
out RealObjectReferencePose pose);
|
||||
|
||||
Assert.IsTrue(ok);
|
||||
AssertVector(pose.RawAxisX, 1.0, 0.0, 0.0);
|
||||
AssertVector(pose.RawAxisY, 0.0, 1.0, 0.0);
|
||||
AssertVector(pose.RawAxisZ, 0.0, 0.0, 1.0);
|
||||
AssertVector(pose.AxisX, 1.0, 0.0, 0.0);
|
||||
AssertVector(pose.AxisY, 0.0, 0.0, 1.0);
|
||||
AssertVector(pose.AxisZ, 0.0, -1.0, 0.0);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveX, pose.HostWorldXAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveY, pose.HostWorldYAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveZ, pose.HostWorldZAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveX, pose.HostSemanticXAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveZ, pose.HostSemanticYAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.NegativeY, pose.HostSemanticZAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveZ, pose.HostUpLocalAxis);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void YUp_WorldAxisMapping_ShouldFollowRawRepresentativeFrame()
|
||||
{
|
||||
var fragmentMatrices = new List<double[]>
|
||||
{
|
||||
CreateMatrix(
|
||||
axisX: new Vector3(0.0f, 0.0f, 1.0f),
|
||||
axisY: new Vector3(1.0f, 0.0f, 0.0f),
|
||||
axisZ: new Vector3(0.0f, 1.0f, 0.0f))
|
||||
};
|
||||
|
||||
bool ok = RealObjectReferencePoseResolver.TryResolveFromFragmentMatrices(
|
||||
fragmentMatrices,
|
||||
fragmentDefaultUpAxis: "Z",
|
||||
hostUpAxis: "Y",
|
||||
out RealObjectReferencePose pose);
|
||||
|
||||
Assert.IsTrue(ok);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveY, pose.HostWorldXAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveZ, pose.HostWorldYAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveX, pose.HostWorldZAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveX, pose.HostSemanticXAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveZ, pose.HostSemanticYAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.NegativeY, pose.HostSemanticZAxisLocalAxis);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DetectDefaultUpAxis_ShouldReturnZ_WhenRepresentativeZMatchesHostUp()
|
||||
{
|
||||
var fragmentMatrices = new List<double[]>
|
||||
{
|
||||
CreateMatrix(
|
||||
axisX: new Vector3(1.0f, 0.0f, 0.0f),
|
||||
axisY: new Vector3(0.0f, 0.0f, -1.0f),
|
||||
axisZ: new Vector3(0.0f, 1.0f, 0.0f))
|
||||
};
|
||||
|
||||
bool ok = RealObjectReferencePoseResolver.TryDetectDefaultUpAxis(
|
||||
fragmentMatrices,
|
||||
"Y",
|
||||
out string detectedAxis,
|
||||
out float yAlignment,
|
||||
out float zAlignment);
|
||||
|
||||
Assert.IsTrue(ok);
|
||||
Assert.AreEqual("Z", detectedAxis);
|
||||
Assert.AreEqual(0.0, yAlignment, 1e-6);
|
||||
Assert.AreEqual(1.0, zAlignment, 1e-6);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void YUp_WorldAxisMapping_ShouldAcceptSlightlyRotatedRawFragmentFrame()
|
||||
{
|
||||
var fragmentMatrices = new List<double[]>
|
||||
{
|
||||
CreateMatrix(
|
||||
axisX: Vector3.Normalize(new Vector3(0.9980f, 0.0638f, 0.0f)),
|
||||
axisY: new Vector3(0.0f, 0.0f, -1.0f),
|
||||
axisZ: Vector3.Normalize(new Vector3(-0.0638f, 0.9980f, 0.0f)))
|
||||
};
|
||||
|
||||
bool ok = RealObjectReferencePoseResolver.TryResolveFromFragmentMatrices(
|
||||
fragmentMatrices,
|
||||
fragmentDefaultUpAxis: "Y",
|
||||
hostUpAxis: "Y",
|
||||
out RealObjectReferencePose pose);
|
||||
|
||||
Assert.IsTrue(ok);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveX, pose.HostWorldXAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveZ, pose.HostWorldYAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.NegativeY, pose.HostWorldZAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveX, pose.HostSemanticXAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveY, pose.HostSemanticYAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveZ, pose.HostSemanticZAxisLocalAxis);
|
||||
Assert.AreEqual(LocalAxisDirection.PositiveY, pose.HostUpLocalAxis);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void YUp_WorldAxisMapping_ShouldRejectAmbiguousRawFragmentFrame()
|
||||
{
|
||||
var fragmentMatrices = new List<double[]>
|
||||
{
|
||||
CreateMatrix(
|
||||
axisX: Vector3.Normalize(new Vector3(0.80f, 0.60f, 0.0f)),
|
||||
axisY: new Vector3(0.0f, 0.0f, -1.0f),
|
||||
axisZ: Vector3.Normalize(new Vector3(-0.60f, 0.80f, 0.0f)))
|
||||
};
|
||||
|
||||
bool ok = RealObjectReferencePoseResolver.TryResolveFromFragmentMatrices(
|
||||
fragmentMatrices,
|
||||
fragmentDefaultUpAxis: "Y",
|
||||
hostUpAxis: "Y",
|
||||
out RealObjectReferencePose pose);
|
||||
|
||||
Assert.IsFalse(ok);
|
||||
Assert.IsNull(pose);
|
||||
}
|
||||
|
||||
private static double[] CreateMatrix(Vector3 axisX, Vector3 axisY, Vector3 axisZ)
|
||||
{
|
||||
return new double[]
|
||||
{
|
||||
axisX.X, axisX.Y, axisX.Z, 0.0,
|
||||
axisY.X, axisY.Y, axisY.Z, 0.0,
|
||||
axisZ.X, axisZ.Y, axisZ.Z, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
};
|
||||
}
|
||||
|
||||
private static void AssertVector(Vector3 actual, double x, double y, double z, double tolerance = 1e-6)
|
||||
{
|
||||
Assert.AreEqual(x, actual.X, tolerance);
|
||||
Assert.AreEqual(y, actual.Y, tolerance);
|
||||
Assert.AreEqual(z, actual.Z, tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,9 +119,6 @@ namespace NavisworksTransport.Core.Animation
|
||||
private LocalAxisDirection _realObjectReferenceHostWorldXAxisLocalAxis = LocalAxisDirection.PositiveX;
|
||||
private LocalAxisDirection _realObjectReferenceHostWorldYAxisLocalAxis = LocalAxisDirection.PositiveY;
|
||||
private LocalAxisDirection _realObjectReferenceHostWorldZAxisLocalAxis = LocalAxisDirection.PositiveZ;
|
||||
private LocalAxisDirection _realObjectReferenceHostSemanticXAxisLocalAxis = LocalAxisDirection.PositiveX;
|
||||
private LocalAxisDirection _realObjectReferenceHostSemanticYAxisLocalAxis = LocalAxisDirection.PositiveY;
|
||||
private LocalAxisDirection _realObjectReferenceHostSemanticZAxisLocalAxis = LocalAxisDirection.PositiveZ;
|
||||
private LocalAxisDirection _realObjectReferenceHostUpLocalAxis = LocalAxisDirection.PositiveY;
|
||||
private bool _hasRealObjectReferenceRotation = false;
|
||||
private LocalAxisDirection _realObjectPlanarSelectedForwardAxis = LocalAxisDirection.PositiveX;
|
||||
@ -5411,9 +5408,6 @@ namespace NavisworksTransport.Core.Animation
|
||||
_realObjectReferenceHostWorldXAxisLocalAxis = LocalAxisDirection.PositiveX;
|
||||
_realObjectReferenceHostWorldYAxisLocalAxis = LocalAxisDirection.PositiveY;
|
||||
_realObjectReferenceHostWorldZAxisLocalAxis = LocalAxisDirection.PositiveZ;
|
||||
_realObjectReferenceHostSemanticXAxisLocalAxis = LocalAxisDirection.PositiveX;
|
||||
_realObjectReferenceHostSemanticYAxisLocalAxis = hostUpLocal;
|
||||
_realObjectReferenceHostSemanticZAxisLocalAxis = LocalAxisDirection.PositiveZ;
|
||||
_hasRealObjectReferenceRotation = true;
|
||||
|
||||
LogManager.Info(
|
||||
@ -5513,9 +5507,6 @@ namespace NavisworksTransport.Core.Animation
|
||||
_realObjectReferenceHostWorldXAxisLocalAxis = LocalAxisDirection.PositiveX;
|
||||
_realObjectReferenceHostWorldYAxisLocalAxis = LocalAxisDirection.PositiveY;
|
||||
_realObjectReferenceHostWorldZAxisLocalAxis = LocalAxisDirection.PositiveZ;
|
||||
_realObjectReferenceHostSemanticXAxisLocalAxis = LocalAxisDirection.PositiveX;
|
||||
_realObjectReferenceHostSemanticYAxisLocalAxis = LocalAxisDirection.PositiveY;
|
||||
_realObjectReferenceHostSemanticZAxisLocalAxis = LocalAxisDirection.PositiveZ;
|
||||
_realObjectReferenceHostUpLocalAxis = LocalAxisDirection.PositiveY;
|
||||
_hasRealObjectReferenceRotation = false;
|
||||
_realObjectPlanarSelectedForwardAxis = LocalAxisDirection.PositiveX;
|
||||
|
||||
@ -2,19 +2,16 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using ComApi = Autodesk.Navisworks.Api.Interop.ComApi;
|
||||
using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
|
||||
|
||||
namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Fragment 代表姿态辅助工具。
|
||||
/// Fragment 代表帧辅助工具(诊断用途)。
|
||||
///
|
||||
/// 语义:
|
||||
/// - 从 fragment 的 world 矩阵中提取旋转
|
||||
/// - 对多个 fragment 旋转做同半球加权平均
|
||||
/// - 返回一个可作为真实物体参考位姿的代表旋转
|
||||
/// 说明:真实物体姿态链已不依赖 fragment(0.18.2 起使用默认轴约定,
|
||||
/// forward=+X、up=宿主 up)。本工具仅保留系统诊断面板
|
||||
/// (SystemManagementViewModel 方法5:Fragment 矩阵分析)所需的
|
||||
/// TryGetRepresentativeFrame / TryExtractRotationFromFragmentMatrix。
|
||||
/// </summary>
|
||||
public static class FragmentRepresentativePoseHelper
|
||||
{
|
||||
@ -37,58 +34,8 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 ModelItem 的 fragment 几何中提取代表旋转。
|
||||
/// 从 fragment 矩阵集合中提取代表帧。
|
||||
/// </summary>
|
||||
public static bool TryGetRepresentativeRotation(ModelItem item, out Quaternion representativeRotation)
|
||||
{
|
||||
representativeRotation = Quaternion.Identity;
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var selection = ComApiBridge.ToInwOpSelection(new ModelItemCollection { item });
|
||||
var fragmentInfos = GeometryHelper.GetAllFragments(selection);
|
||||
return TryGetRepresentativeRotation(
|
||||
fragmentInfos.Select(fragmentInfo => fragmentInfo.TransformMatrix),
|
||||
out representativeRotation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[fragment代表姿态] 从ModelItem提取失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 fragment 矩阵集合中提取代表旋转。
|
||||
/// </summary>
|
||||
public static bool TryGetRepresentativeRotation(
|
||||
IEnumerable<double[]> fragmentMatrices,
|
||||
out Quaternion representativeRotation)
|
||||
{
|
||||
representativeRotation = Quaternion.Identity;
|
||||
|
||||
if (fragmentMatrices == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var fragmentRotations = new List<Quaternion>();
|
||||
foreach (double[] fragmentMatrix in fragmentMatrices)
|
||||
{
|
||||
if (TryExtractRotationFromFragmentMatrix(fragmentMatrix, out Quaternion fragmentRotation))
|
||||
{
|
||||
fragmentRotations.Add(fragmentRotation);
|
||||
}
|
||||
}
|
||||
|
||||
return TryGetRepresentativeRotation(fragmentRotations, out representativeRotation);
|
||||
}
|
||||
|
||||
public static bool TryGetRepresentativeFrame(
|
||||
IEnumerable<double[]> fragmentMatrices,
|
||||
out RepresentativeFrame representativeFrame)
|
||||
@ -121,80 +68,10 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryInterpretRepresentativeFrame(
|
||||
RepresentativeFrame rawFrame,
|
||||
string fragmentDefaultUpAxis,
|
||||
string hostUpAxis,
|
||||
out RepresentativeFrame interpretedFrame)
|
||||
{
|
||||
interpretedFrame = default;
|
||||
|
||||
bool hostIsYUp = string.Equals(hostUpAxis, "Y", StringComparison.OrdinalIgnoreCase);
|
||||
bool fragmentIsYUp = string.Equals(fragmentDefaultUpAxis, "Y", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
Vector3 semanticAxisX = rawFrame.AxisX;
|
||||
Vector3 semanticAxisY;
|
||||
Vector3 semanticAxisZ;
|
||||
|
||||
if (hostIsYUp == fragmentIsYUp)
|
||||
{
|
||||
semanticAxisY = rawFrame.AxisY;
|
||||
semanticAxisZ = rawFrame.AxisZ;
|
||||
}
|
||||
else if (hostIsYUp)
|
||||
{
|
||||
// fragment 按 ZUp 解释,宿主按 YUp 解释:X 保持,Y 取原始 Z,Z 由右手系重建。
|
||||
semanticAxisY = rawFrame.AxisZ;
|
||||
semanticAxisZ = Vector3.Normalize(Vector3.Cross(semanticAxisX, semanticAxisY));
|
||||
}
|
||||
else
|
||||
{
|
||||
// fragment 按 YUp 解释,宿主按 ZUp 解释:X 保持,Z 取原始 Y,Y 由右手系重建。
|
||||
semanticAxisZ = rawFrame.AxisY;
|
||||
semanticAxisY = Vector3.Normalize(Vector3.Cross(semanticAxisZ, semanticAxisX));
|
||||
}
|
||||
|
||||
if (!TryNormalize(semanticAxisX, out semanticAxisX) ||
|
||||
!TryNormalize(semanticAxisY, out semanticAxisY) ||
|
||||
!TryNormalize(semanticAxisZ, out semanticAxisZ))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 用解释后的三轴重新正交化,保证右手系稳定。
|
||||
Vector3 axisX = semanticAxisX;
|
||||
Vector3 axisY = semanticAxisY - Vector3.Dot(semanticAxisY, axisX) * axisX;
|
||||
if (!TryNormalize(axisY, out axisY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3 axisZ = Vector3.Cross(axisX, axisY);
|
||||
if (!TryNormalize(axisZ, out axisZ))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hostIsYUp && Vector3.Dot(axisY, semanticAxisY) < 0.0f)
|
||||
{
|
||||
axisY = -axisY;
|
||||
axisZ = -axisZ;
|
||||
}
|
||||
else if (!hostIsYUp && Vector3.Dot(axisZ, semanticAxisZ) < 0.0f)
|
||||
{
|
||||
axisY = -axisY;
|
||||
axisZ = -axisZ;
|
||||
}
|
||||
|
||||
Quaternion rotation = Quaternion.Normalize(CreateQuaternionFromBasis(axisX, axisY, axisZ));
|
||||
interpretedFrame = new RepresentativeFrame(axisX, axisY, axisZ, rotation);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 fragment 旋转集合中提取代表旋转。
|
||||
/// 从 fragment 旋转集合中提取代表旋转(同半球加权平均)。
|
||||
/// </summary>
|
||||
public static bool TryGetRepresentativeRotation(
|
||||
private static bool TryGetRepresentativeRotation(
|
||||
IEnumerable<Quaternion> fragmentRotations,
|
||||
out Quaternion representativeRotation)
|
||||
{
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// 真实物体在当前宿主语义下解释后的参考姿态。
|
||||
/// 这不是 fragment 原始几何框架,而是已经结合 Fragment默认Up 解释后的结果。
|
||||
/// </summary>
|
||||
public sealed class RealObjectReferencePose
|
||||
{
|
||||
public RealObjectReferencePose(
|
||||
Quaternion rotation,
|
||||
Vector3 rawAxisX,
|
||||
Vector3 rawAxisY,
|
||||
Vector3 rawAxisZ,
|
||||
Vector3 axisX,
|
||||
Vector3 axisY,
|
||||
Vector3 axisZ,
|
||||
LocalAxisDirection hostWorldXAxisLocalAxis,
|
||||
LocalAxisDirection hostWorldYAxisLocalAxis,
|
||||
LocalAxisDirection hostWorldZAxisLocalAxis,
|
||||
LocalAxisDirection hostSemanticXAxisLocalAxis,
|
||||
LocalAxisDirection hostSemanticYAxisLocalAxis,
|
||||
LocalAxisDirection hostSemanticZAxisLocalAxis,
|
||||
LocalAxisDirection hostUpLocalAxis,
|
||||
int fragmentCount,
|
||||
string fragmentDefaultUpAxis,
|
||||
string hostUpAxis)
|
||||
{
|
||||
Rotation = rotation;
|
||||
RawAxisX = rawAxisX;
|
||||
RawAxisY = rawAxisY;
|
||||
RawAxisZ = rawAxisZ;
|
||||
AxisX = axisX;
|
||||
AxisY = axisY;
|
||||
AxisZ = axisZ;
|
||||
HostWorldXAxisLocalAxis = hostWorldXAxisLocalAxis;
|
||||
HostWorldYAxisLocalAxis = hostWorldYAxisLocalAxis;
|
||||
HostWorldZAxisLocalAxis = hostWorldZAxisLocalAxis;
|
||||
HostSemanticXAxisLocalAxis = hostSemanticXAxisLocalAxis;
|
||||
HostSemanticYAxisLocalAxis = hostSemanticYAxisLocalAxis;
|
||||
HostSemanticZAxisLocalAxis = hostSemanticZAxisLocalAxis;
|
||||
HostUpLocalAxis = hostUpLocalAxis;
|
||||
FragmentCount = fragmentCount;
|
||||
FragmentDefaultUpAxis = fragmentDefaultUpAxis;
|
||||
HostUpAxis = hostUpAxis;
|
||||
}
|
||||
|
||||
public Quaternion Rotation { get; }
|
||||
public Vector3 RawAxisX { get; }
|
||||
public Vector3 RawAxisY { get; }
|
||||
public Vector3 RawAxisZ { get; }
|
||||
public Vector3 AxisX { get; }
|
||||
public Vector3 AxisY { get; }
|
||||
public Vector3 AxisZ { get; }
|
||||
public LocalAxisDirection HostWorldXAxisLocalAxis { get; }
|
||||
public LocalAxisDirection HostWorldYAxisLocalAxis { get; }
|
||||
public LocalAxisDirection HostWorldZAxisLocalAxis { get; }
|
||||
public LocalAxisDirection HostSemanticXAxisLocalAxis { get; }
|
||||
public LocalAxisDirection HostSemanticYAxisLocalAxis { get; }
|
||||
public LocalAxisDirection HostSemanticZAxisLocalAxis { get; }
|
||||
public LocalAxisDirection HostUpLocalAxis { get; }
|
||||
public int FragmentCount { get; }
|
||||
public string FragmentDefaultUpAxis { get; }
|
||||
public string HostUpAxis { get; }
|
||||
}
|
||||
}
|
||||
@ -1,305 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
|
||||
|
||||
namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// 统一的真实物体参考姿态解析入口。
|
||||
/// 负责:
|
||||
/// 1. 从 fragment 提取代表姿态
|
||||
/// 2. 按 Fragment默认Up 解释成当前宿主语义下的真实姿态
|
||||
/// 3. 为 Ground / Hoisting / Rail 提供统一参考姿态来源
|
||||
/// 真实物体 fragment 姿态解析入口(仅保留诊断用途)。
|
||||
///
|
||||
/// 说明:真实物体姿态链已不依赖 fragment(0.18.2 起使用默认轴约定,
|
||||
/// forward=+X、up=宿主 up)。原 TryResolveFromModelItem /
|
||||
/// TryResolveFromFragmentMatrices(fragment 代表姿态解析链)已删除。
|
||||
/// 本类仅保留系统诊断面板(SystemManagementViewModel 方法5)使用的
|
||||
/// TryDetectDefaultUpAxis。
|
||||
/// </summary>
|
||||
public static class RealObjectReferencePoseResolver
|
||||
{
|
||||
private const float WorldAxisMinimumAlignment = 0.98f;
|
||||
private const float SemanticAxisMinimumAlignment = 0.985f;
|
||||
private const float MinimumBestToSecondGap = 0.02f;
|
||||
|
||||
public static bool TryResolveFromModelItem(
|
||||
ModelItem sourceObject,
|
||||
Document doc,
|
||||
out RealObjectReferencePose referencePose)
|
||||
{
|
||||
referencePose = null;
|
||||
|
||||
if (sourceObject == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var selection = ComApiBridge.ToInwOpSelection(new ModelItemCollection { sourceObject });
|
||||
var fragmentInfos = GeometryHelper.GetAllFragments(selection);
|
||||
try
|
||||
{
|
||||
var fragmentMatrices = fragmentInfos
|
||||
.Where(fragmentInfo => fragmentInfo.TransformMatrix != null && fragmentInfo.TransformMatrix.Length == 16)
|
||||
.Select(fragmentInfo => fragmentInfo.TransformMatrix)
|
||||
.ToList();
|
||||
|
||||
return TryResolveFromFragmentMatrices(fragmentMatrices, doc, out referencePose);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (fragmentInfos != null)
|
||||
{
|
||||
foreach (var fragmentInfo in fragmentInfos)
|
||||
{
|
||||
if (fragmentInfo?.Fragment != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Marshal.ReleaseComObject(fragmentInfo.Fragment);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selection != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Marshal.ReleaseComObject(selection);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[真实物体参考姿态] 从 fragment 提取代表姿态失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryResolveFromFragmentMatrices(
|
||||
IReadOnlyCollection<double[]> fragmentMatrices,
|
||||
Document doc,
|
||||
out RealObjectReferencePose referencePose)
|
||||
{
|
||||
string fragmentDefaultUpAxis = FragmentDefaultUpContext.GetCurrentDocumentFragmentDefaultUpAxis(doc);
|
||||
string hostUpAxis = FragmentDefaultUpContext.GetCurrentHostUpAxis(doc);
|
||||
return TryResolveFromFragmentMatrices(fragmentMatrices, fragmentDefaultUpAxis, hostUpAxis, out referencePose);
|
||||
}
|
||||
|
||||
public static bool TryResolveFromFragmentMatrices(
|
||||
IReadOnlyCollection<double[]> fragmentMatrices,
|
||||
string fragmentDefaultUpAxis,
|
||||
string hostUpAxis,
|
||||
out RealObjectReferencePose referencePose)
|
||||
{
|
||||
referencePose = null;
|
||||
|
||||
if (fragmentMatrices == null || fragmentMatrices.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FragmentRepresentativePoseHelper.TryGetRepresentativeFrame(fragmentMatrices, out var rawFrame))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FragmentRepresentativePoseHelper.TryInterpretRepresentativeFrame(
|
||||
rawFrame,
|
||||
fragmentDefaultUpAxis,
|
||||
hostUpAxis,
|
||||
out var interpretedFrame))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryResolveSemanticLocalAxes(
|
||||
rawFrame,
|
||||
interpretedFrame,
|
||||
out var hostSemanticXAxisLocalAxis,
|
||||
out var hostSemanticYAxisLocalAxis,
|
||||
out var hostSemanticZAxisLocalAxis))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryResolveWorldLocalAxes(
|
||||
rawFrame,
|
||||
out var hostWorldXAxisLocalAxis,
|
||||
out var hostWorldYAxisLocalAxis,
|
||||
out var hostWorldZAxisLocalAxis))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
referencePose = new RealObjectReferencePose(
|
||||
interpretedFrame.Rotation,
|
||||
rawFrame.AxisX,
|
||||
rawFrame.AxisY,
|
||||
rawFrame.AxisZ,
|
||||
interpretedFrame.AxisX,
|
||||
interpretedFrame.AxisY,
|
||||
interpretedFrame.AxisZ,
|
||||
hostWorldXAxisLocalAxis,
|
||||
hostWorldYAxisLocalAxis,
|
||||
hostWorldZAxisLocalAxis,
|
||||
hostSemanticXAxisLocalAxis,
|
||||
hostSemanticYAxisLocalAxis,
|
||||
hostSemanticZAxisLocalAxis,
|
||||
string.Equals(hostUpAxis, "Y", StringComparison.OrdinalIgnoreCase)
|
||||
? hostSemanticYAxisLocalAxis
|
||||
: hostSemanticZAxisLocalAxis,
|
||||
fragmentMatrices.Count,
|
||||
fragmentDefaultUpAxis,
|
||||
hostUpAxis);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryResolveWorldLocalAxes(
|
||||
FragmentRepresentativePoseHelper.RepresentativeFrame rawFrame,
|
||||
out LocalAxisDirection hostWorldXAxisLocalAxis,
|
||||
out LocalAxisDirection hostWorldYAxisLocalAxis,
|
||||
out LocalAxisDirection hostWorldZAxisLocalAxis)
|
||||
{
|
||||
hostWorldXAxisLocalAxis = LocalAxisDirection.PositiveX;
|
||||
hostWorldYAxisLocalAxis = LocalAxisDirection.PositiveY;
|
||||
hostWorldZAxisLocalAxis = LocalAxisDirection.PositiveZ;
|
||||
|
||||
if (!TryMatchRawAxisDirection(
|
||||
Vector3.UnitX,
|
||||
rawFrame.AxisX,
|
||||
rawFrame.AxisY,
|
||||
rawFrame.AxisZ,
|
||||
WorldAxisMinimumAlignment,
|
||||
out hostWorldXAxisLocalAxis))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryMatchRawAxisDirection(
|
||||
Vector3.UnitY,
|
||||
rawFrame.AxisX,
|
||||
rawFrame.AxisY,
|
||||
rawFrame.AxisZ,
|
||||
WorldAxisMinimumAlignment,
|
||||
out hostWorldYAxisLocalAxis))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryMatchRawAxisDirection(
|
||||
Vector3.UnitZ,
|
||||
rawFrame.AxisX,
|
||||
rawFrame.AxisY,
|
||||
rawFrame.AxisZ,
|
||||
WorldAxisMinimumAlignment,
|
||||
out hostWorldZAxisLocalAxis))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryResolveSemanticLocalAxes(
|
||||
FragmentRepresentativePoseHelper.RepresentativeFrame rawFrame,
|
||||
FragmentRepresentativePoseHelper.RepresentativeFrame interpretedFrame,
|
||||
out LocalAxisDirection hostSemanticXAxisLocalAxis,
|
||||
out LocalAxisDirection hostSemanticYAxisLocalAxis,
|
||||
out LocalAxisDirection hostSemanticZAxisLocalAxis)
|
||||
{
|
||||
hostSemanticXAxisLocalAxis = LocalAxisDirection.PositiveX;
|
||||
hostSemanticYAxisLocalAxis = LocalAxisDirection.PositiveY;
|
||||
hostSemanticZAxisLocalAxis = LocalAxisDirection.PositiveZ;
|
||||
|
||||
if (!TryMatchRawAxisDirection(
|
||||
interpretedFrame.AxisX,
|
||||
rawFrame.AxisX,
|
||||
rawFrame.AxisY,
|
||||
rawFrame.AxisZ,
|
||||
SemanticAxisMinimumAlignment,
|
||||
out hostSemanticXAxisLocalAxis))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryMatchRawAxisDirection(
|
||||
interpretedFrame.AxisY,
|
||||
rawFrame.AxisX,
|
||||
rawFrame.AxisY,
|
||||
rawFrame.AxisZ,
|
||||
SemanticAxisMinimumAlignment,
|
||||
out hostSemanticYAxisLocalAxis))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryMatchRawAxisDirection(
|
||||
interpretedFrame.AxisZ,
|
||||
rawFrame.AxisX,
|
||||
rawFrame.AxisY,
|
||||
rawFrame.AxisZ,
|
||||
SemanticAxisMinimumAlignment,
|
||||
out hostSemanticZAxisLocalAxis))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryMatchRawAxisDirection(
|
||||
Vector3 targetAxis,
|
||||
Vector3 rawAxisX,
|
||||
Vector3 rawAxisY,
|
||||
Vector3 rawAxisZ,
|
||||
float minimumAlignment,
|
||||
out LocalAxisDirection axisDirection)
|
||||
{
|
||||
axisDirection = LocalAxisDirection.PositiveX;
|
||||
float bestAlignment = float.NegativeInfinity;
|
||||
float secondBestAlignment = float.NegativeInfinity;
|
||||
|
||||
TryUpdateBestMatch(targetAxis, rawAxisX, LocalAxisDirection.PositiveX, ref bestAlignment, ref secondBestAlignment, ref axisDirection);
|
||||
TryUpdateBestMatch(targetAxis, -rawAxisX, LocalAxisDirection.NegativeX, ref bestAlignment, ref secondBestAlignment, ref axisDirection);
|
||||
TryUpdateBestMatch(targetAxis, rawAxisY, LocalAxisDirection.PositiveY, ref bestAlignment, ref secondBestAlignment, ref axisDirection);
|
||||
TryUpdateBestMatch(targetAxis, -rawAxisY, LocalAxisDirection.NegativeY, ref bestAlignment, ref secondBestAlignment, ref axisDirection);
|
||||
TryUpdateBestMatch(targetAxis, rawAxisZ, LocalAxisDirection.PositiveZ, ref bestAlignment, ref secondBestAlignment, ref axisDirection);
|
||||
TryUpdateBestMatch(targetAxis, -rawAxisZ, LocalAxisDirection.NegativeZ, ref bestAlignment, ref secondBestAlignment, ref axisDirection);
|
||||
|
||||
return bestAlignment >= minimumAlignment
|
||||
&& bestAlignment - secondBestAlignment >= MinimumBestToSecondGap;
|
||||
}
|
||||
|
||||
private static void TryUpdateBestMatch(
|
||||
Vector3 targetAxis,
|
||||
Vector3 candidateAxis,
|
||||
LocalAxisDirection candidateDirection,
|
||||
ref float bestAlignment,
|
||||
ref float secondBestAlignment,
|
||||
ref LocalAxisDirection bestDirection)
|
||||
{
|
||||
float alignment = Vector3.Dot(Vector3.Normalize(targetAxis), Vector3.Normalize(candidateAxis));
|
||||
if (alignment > bestAlignment)
|
||||
{
|
||||
secondBestAlignment = bestAlignment;
|
||||
bestAlignment = alignment;
|
||||
bestDirection = candidateDirection;
|
||||
}
|
||||
else if (alignment > secondBestAlignment)
|
||||
{
|
||||
secondBestAlignment = alignment;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 fragment 矩阵检测物体的默认 Up 轴(Y 或 Z 更贴近宿主 up)。
|
||||
/// </summary>
|
||||
public static bool TryDetectDefaultUpAxis(
|
||||
IReadOnlyCollection<double[]> fragmentMatrices,
|
||||
string hostUpAxis,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user