diff --git a/NavisworksTransport.UnitTests.csproj b/NavisworksTransport.UnitTests.csproj index 5c6b277..77e0d33 100644 --- a/NavisworksTransport.UnitTests.csproj +++ b/NavisworksTransport.UnitTests.csproj @@ -96,7 +96,6 @@ - @@ -112,7 +111,7 @@ - + diff --git a/TransportPlugin.csproj b/TransportPlugin.csproj index 96c805d..57fa132 100644 --- a/TransportPlugin.csproj +++ b/TransportPlugin.csproj @@ -362,9 +362,7 @@ - - - + diff --git a/UnitTests/CoordinateSystem/FragmentDefaultUpContextTests.cs b/UnitTests/CoordinateSystem/DocumentKeyContextTests.cs similarity index 76% rename from UnitTests/CoordinateSystem/FragmentDefaultUpContextTests.cs rename to UnitTests/CoordinateSystem/DocumentKeyContextTests.cs index 17b135b..3c9edcd 100644 --- a/UnitTests/CoordinateSystem/FragmentDefaultUpContextTests.cs +++ b/UnitTests/CoordinateSystem/DocumentKeyContextTests.cs @@ -4,12 +4,12 @@ using NavisworksTransport.Utils.CoordinateSystem; namespace NavisworksTransport.UnitTests.CoordinateSystem { [TestClass] - public class FragmentDefaultUpContextTests + public class DocumentKeyContextTests { [TestInitialize] public void SetUp() { - FragmentDefaultUpContext.ResetForTests(); + DocumentKeyContext.ResetForTests(); } [TestMethod] @@ -17,12 +17,12 @@ namespace NavisworksTransport.UnitTests.CoordinateSystem { const int runtimeDocumentId = 42; - string initialKey = FragmentDefaultUpContext.GetOrCreateDocumentKey( + string initialKey = DocumentKeyContext.GetOrCreateDocumentKey( runtimeDocumentId, @"C:\models\floor2.nwd", "Floor2"); - string transientKey = FragmentDefaultUpContext.GetOrCreateDocumentKey( + string transientKey = DocumentKeyContext.GetOrCreateDocumentKey( runtimeDocumentId, string.Empty, "无标题"); @@ -36,7 +36,7 @@ namespace NavisworksTransport.UnitTests.CoordinateSystem { const int runtimeDocumentId = 7; - string key = FragmentDefaultUpContext.GetOrCreateDocumentKey( + string key = DocumentKeyContext.GetOrCreateDocumentKey( runtimeDocumentId, string.Empty, "无标题"); diff --git a/UnitTests/CoordinateSystem/FragmentRepresentativePoseHelperTests.cs b/UnitTests/CoordinateSystem/FragmentRepresentativePoseHelperTests.cs deleted file mode 100644 index cea12c2..0000000 --- a/UnitTests/CoordinateSystem/FragmentRepresentativePoseHelperTests.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NavisworksTransport.Utils.CoordinateSystem; -using System.Numerics; - -namespace NavisworksTransport.UnitTests.CoordinateSystem -{ - [TestClass] - public class FragmentRepresentativePoseHelperTests - { - [TestMethod] - public void ShouldPreserveAxes_ForObservedYUpRealObjectFragmentBasis() - { - Vector3 axisX = new Vector3(1.0f, 0.0f, 0.0f); - Vector3 axisY = new Vector3(0.0f, 0.0f, 1.0f); - Vector3 axisZ = new Vector3(0.0f, -1.0f, 0.0f); - - double[] fragmentMatrix = - { - 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 - }; - - bool ok = FragmentRepresentativePoseHelper.TryExtractRotationFromFragmentMatrix( - fragmentMatrix, - out Quaternion rotation); - - Assert.IsTrue(ok); - AssertVector(Vector3.Transform(Vector3.UnitX, rotation), axisX, 1e-4); - AssertVector(Vector3.Transform(Vector3.UnitY, rotation), axisY, 1e-4); - AssertVector(Vector3.Transform(Vector3.UnitZ, rotation), axisZ, 1e-4); - } - - [TestMethod] - public void RepresentativeFrame_ShouldExposeSameAxes_AsRepresentativeRotation() - { - Vector3 axisX = new Vector3(1.0f, 0.0f, 0.0f); - Vector3 axisY = new Vector3(0.0f, 0.0f, 1.0f); - Vector3 axisZ = new Vector3(0.0f, -1.0f, 0.0f); - - double[] fragmentMatrix = - { - 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 - }; - - bool ok = FragmentRepresentativePoseHelper.TryGetRepresentativeFrame( - new[] { fragmentMatrix }, - out FragmentRepresentativePoseHelper.RepresentativeFrame frame); - - Assert.IsTrue(ok); - AssertVector(frame.AxisX, axisX, 1e-4); - AssertVector(frame.AxisY, axisY, 1e-4); - AssertVector(frame.AxisZ, axisZ, 1e-4); - AssertVector(Vector3.Transform(Vector3.UnitX, frame.Rotation), axisX, 1e-4); - AssertVector(Vector3.Transform(Vector3.UnitY, frame.Rotation), axisY, 1e-4); - AssertVector(Vector3.Transform(Vector3.UnitZ, frame.Rotation), axisZ, 1e-4); - } - - private static void AssertVector(Vector3 actual, Vector3 expected, double 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 分量"); - } - } -} diff --git a/src/UI/WPF/ViewModels/SystemManagementViewModel.cs b/src/UI/WPF/ViewModels/SystemManagementViewModel.cs index 793c354..b29bfda 100644 --- a/src/UI/WPF/ViewModels/SystemManagementViewModel.cs +++ b/src/UI/WPF/ViewModels/SystemManagementViewModel.cs @@ -454,7 +454,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels private string GetCurrentDocumentKey() { - return FragmentDefaultUpContext.GetCurrentDocumentKey(); + return DocumentKeyContext.GetCurrentDocumentKey(); } private void ShowRootModelRotationHintIfNeeded() @@ -1463,11 +1463,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels TransformDiagnosticInfo geometryInfo = geometryDescendants.Count > 0 ? CreateTransformDiagnosticInfo(geometryDescendants[0], "首个几何后代") : null; - FragmentOrientationAnalysis fragmentAnalysis = AnalyzeFragmentOrientations(item); - - Quaternion visualizationQuaternion = fragmentAnalysis != null && fragmentAnalysis.HasRepresentativeOrientation - ? fragmentAnalysis.RepresentativeRotation - : geometryInfo?.RotationQuaternion ?? selectedInfo.RotationQuaternion; + Quaternion visualizationQuaternion = geometryInfo?.RotationQuaternion ?? selectedInfo.RotationQuaternion; double axisLength = GetTransformAxisVisualizationLength(bounds); RenderCapturedRealObjectTransformAxes(boundsCenter, visualizationQuaternion, axisLength); @@ -1499,12 +1495,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels info.AppendLine(); } - AppendFragmentAnalysis(info, fragmentAnalysis); info.AppendLine(); info.AppendLine("【可视化说明】"); info.AppendLine($"三轴原点使用包围盒中心: ({boundsCenter.X:F3}, {boundsCenter.Y:F3}, {boundsCenter.Z:F3})"); info.AppendLine($"轴长度 = {axisLength:F3}"); - info.AppendLine($"三轴方向来源 = {(fragmentAnalysis != null && fragmentAnalysis.HasRepresentativeOrientation ? "fragment代表姿态" : geometryInfo != null ? "首个几何后代Transform" : "选中节点Transform")}"); + info.AppendLine($"三轴方向来源 = {(geometryInfo != null ? "首个几何后代Transform" : "选中节点Transform")}"); info.AppendLine("红色=X轴,绿色=Y轴,蓝色=Z轴"); string message = info.ToString(); @@ -1667,196 +1662,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } - private static FragmentOrientationAnalysis AnalyzeFragmentOrientations(ModelItem item) - { - if (item == null) - { - return null; - } - - Autodesk.Navisworks.Api.ModelItemCollection collection = new Autodesk.Navisworks.Api.ModelItemCollection { item }; - var comSelection = ComApiBridge.ToInwOpSelection(collection); - List fragments = null; - try - { - fragments = GeometryHelper.GetAllFragments(comSelection); - var validMatrices = fragments - .Where(f => f.TransformMatrix != null && f.TransformMatrix.Length == 16) - .Select(f => f.TransformMatrix) - .ToList(); - - var analysis = new FragmentOrientationAnalysis - { - FragmentCount = fragments.Count, - ValidTransformCount = validMatrices.Count, - ValidMatrices = validMatrices - }; - - if (validMatrices.Count == 0) - { - return analysis; - } - - Vector3 referenceX = NormalizeOrUnitX(GetMatrixAxis(validMatrices[0], 0)); - Vector3 referenceY = NormalizeOrUnitY(GetMatrixAxis(validMatrices[0], 1)); - Vector3 referenceZ = NormalizeOrUnitZ(GetMatrixAxis(validMatrices[0], 2)); - - Vector3 sumX = referenceX; - Vector3 sumY = referenceY; - Vector3 sumZ = referenceZ; - double xDotSum = 1.0; - double yDotSum = 1.0; - double zDotSum = 1.0; - - for (int i = 1; i < validMatrices.Count; i++) - { - Vector3 axisX = NormalizeWithReference(GetMatrixAxis(validMatrices[i], 0), referenceX); - Vector3 axisY = NormalizeWithReference(GetMatrixAxis(validMatrices[i], 1), referenceY); - Vector3 axisZ = NormalizeWithReference(GetMatrixAxis(validMatrices[i], 2), referenceZ); - - xDotSum += Math.Abs(Vector3.Dot(axisX, referenceX)); - yDotSum += Math.Abs(Vector3.Dot(axisY, referenceY)); - zDotSum += Math.Abs(Vector3.Dot(axisZ, referenceZ)); - - sumX += axisX; - sumY += axisY; - sumZ += axisZ; - } - - Vector3 representativeX = NormalizeOrUnitX(sumX); - Vector3 representativeY = sumY - Vector3.Dot(sumY, representativeX) * representativeX; - representativeY = NormalizeOrUnitY(representativeY); - Vector3 representativeZ = Vector3.Normalize(Vector3.Cross(representativeX, representativeY)); - if (Vector3.Dot(representativeZ, NormalizeOrUnitZ(sumZ)) < 0) - { - representativeZ = -representativeZ; - } - representativeY = Vector3.Normalize(Vector3.Cross(representativeZ, representativeX)); - - Matrix4x4 linear = new Matrix4x4( - representativeX.X, representativeY.X, representativeZ.X, 0f, - representativeX.Y, representativeY.Y, representativeZ.Y, 0f, - representativeX.Z, representativeY.Z, representativeZ.Z, 0f, - 0f, 0f, 0f, 1f); - - analysis.HasRepresentativeOrientation = true; - analysis.RepresentativeRotation = Quaternion.Normalize(Quaternion.CreateFromRotationMatrix(linear)); - analysis.RepresentativeXAxis = representativeX; - analysis.RepresentativeYAxis = representativeY; - analysis.RepresentativeZAxis = representativeZ; - analysis.AverageXAxisConsistency = xDotSum / validMatrices.Count; - analysis.AverageYAxisConsistency = yDotSum / validMatrices.Count; - analysis.AverageZAxisConsistency = zDotSum / validMatrices.Count; - analysis.SampleTranslation = GetMatrixTranslation(validMatrices[0]); - return analysis; - } - finally - { - if (fragments != null) - { - foreach (var fragment in fragments) - { - if (fragment?.Fragment != null) - { - try - { - Marshal.ReleaseComObject(fragment.Fragment); - } - catch - { - } - } - } - } - - if (comSelection != null) - { - try - { - Marshal.ReleaseComObject(comSelection); - } - catch - { - } - } - } - } - - private static void AppendFragmentAnalysis(StringBuilder info, FragmentOrientationAnalysis analysis) - { - info.AppendLine("【Fragment 姿态分析】"); - if (analysis == null) - { - info.AppendLine("未能执行 fragment 分析"); - info.AppendLine(); - return; - } - - info.AppendLine($"Fragment 总数 = {analysis.FragmentCount}"); - info.AppendLine($"有效变换矩阵数 = {analysis.ValidTransformCount}"); - if (!analysis.HasRepresentativeOrientation) - { - info.AppendLine("未得到可用的 fragment 代表姿态"); - info.AppendLine(); - return; - } - - info.AppendLine($"示例平移 = ({analysis.SampleTranslation.X:F3}, {analysis.SampleTranslation.Y:F3}, {analysis.SampleTranslation.Z:F3})"); - info.AppendLine($"代表X轴 = ({analysis.RepresentativeXAxis.X:F4}, {analysis.RepresentativeXAxis.Y:F4}, {analysis.RepresentativeXAxis.Z:F4})"); - info.AppendLine($"代表Y轴 = ({analysis.RepresentativeYAxis.X:F4}, {analysis.RepresentativeYAxis.Y:F4}, {analysis.RepresentativeYAxis.Z:F4})"); - info.AppendLine($"代表Z轴 = ({analysis.RepresentativeZAxis.X:F4}, {analysis.RepresentativeZAxis.Y:F4}, {analysis.RepresentativeZAxis.Z:F4})"); - info.AppendLine($"X轴一致性 = {analysis.AverageXAxisConsistency:F4}"); - info.AppendLine($"Y轴一致性 = {analysis.AverageYAxisConsistency:F4}"); - info.AppendLine($"Z轴一致性 = {analysis.AverageZAxisConsistency:F4}"); - info.AppendLine(); - } - - private static Vector3 GetMatrixAxis(double[] matrix, int axisIndex) - { - switch (axisIndex) - { - case 0: - return new Vector3((float)matrix[0], (float)matrix[1], (float)matrix[2]); - case 1: - return new Vector3((float)matrix[4], (float)matrix[5], (float)matrix[6]); - case 2: - return new Vector3((float)matrix[8], (float)matrix[9], (float)matrix[10]); - default: - throw new ArgumentOutOfRangeException(nameof(axisIndex)); - } - } - - private static Vector3 GetMatrixTranslation(double[] matrix) - { - return new Vector3((float)matrix[12], (float)matrix[13], (float)matrix[14]); - } - - private static Vector3 NormalizeWithReference(Vector3 axis, Vector3 reference) - { - Vector3 normalized = NormalizeOrFallback(axis, reference); - return Vector3.Dot(normalized, reference) < 0 ? -normalized : normalized; - } - - private static Vector3 NormalizeOrUnitX(Vector3 value) - { - return NormalizeOrFallback(value, Vector3.UnitX); - } - - private static Vector3 NormalizeOrUnitY(Vector3 value) - { - return NormalizeOrFallback(value, Vector3.UnitY); - } - - private static Vector3 NormalizeOrUnitZ(Vector3 value) - { - return NormalizeOrFallback(value, Vector3.UnitZ); - } - - private static Vector3 NormalizeOrFallback(Vector3 value, Vector3 fallback) - { - return value.LengthSquared() < 1e-8f ? fallback : Vector3.Normalize(value); - } - private sealed class TransformDiagnosticInfo { public string Label { get; set; } @@ -1873,22 +1678,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels public Vector3 HostZAxis { get; set; } } - private sealed class FragmentOrientationAnalysis - { - public int FragmentCount { get; set; } - public int ValidTransformCount { get; set; } - public List ValidMatrices { get; set; } - public bool HasRepresentativeOrientation { get; set; } - public Quaternion RepresentativeRotation { get; set; } - public Vector3 RepresentativeXAxis { get; set; } - public Vector3 RepresentativeYAxis { get; set; } - public Vector3 RepresentativeZAxis { get; set; } - public double AverageXAxisConsistency { get; set; } - public double AverageYAxisConsistency { get; set; } - public double AverageZAxisConsistency { get; set; } - public Vector3 SampleTranslation { get; set; } - } - /// /// 快速取面测试 — 激活 FaceInferToolPlugin,用户在模型上点击后显示面法向量 /// @@ -2284,44 +2073,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels } sb.AppendLine(); - // 方法5: Fragment 矩阵分析(选一个水平物体后更精确) - sb.AppendLine($"方法5 - Fragment 矩阵分析 (选择物体后启用):"); - var selection = doc.CurrentSelection.SelectedItems; - if (selection.Count > 0) - { - ModelItem selectedItem = selection[0]; - FragmentOrientationAnalysis fragAnalysis = AnalyzeFragmentOrientations(selectedItem); - if (fragAnalysis != null && fragAnalysis.HasRepresentativeOrientation) - { - string hostUpAxis = FragmentDefaultUpContext.GetCurrentHostUpAxis(doc); - if (RealObjectReferencePoseResolver.TryDetectDefaultUpAxis( - fragAnalysis.ValidMatrices, - hostUpAxis, - out string fragDetectedAxis, - out float yAlignment, - out float zAlignment)) - { - sb.AppendLine($" 对象: {selectedItem.DisplayName}"); - sb.AppendLine($" Fragment Y 对齐度: {yAlignment:F4}"); - sb.AppendLine($" Fragment Z 对齐度: {zAlignment:F4}"); - sb.AppendLine($" 判定: Fragment 默认 Up = {fragDetectedAxis} ({(zAlignment > yAlignment ? "Z更贴近" : "Y更贴近")}向上轴)"); - } - else - { - sb.AppendLine($" 未能从 fragment 姿态中检测默认Up。"); - } - } - else - { - sb.AppendLine($" 选中物体 ({selectedItem.DisplayName}) 无有效 fragment 姿态。"); - } - } - else - { - sb.AppendLine($" (未选择对象,跳过此方法。请先选中一个水平放置的物体)"); - } - sb.AppendLine(); - // 最终建议 sb.AppendLine($"【最终建议】"); if (detectedCoordinateSystem == "YUp") diff --git a/src/Utils/CoordinateSystem/FragmentDefaultUpContext.cs b/src/Utils/CoordinateSystem/DocumentKeyContext.cs similarity index 54% rename from src/Utils/CoordinateSystem/FragmentDefaultUpContext.cs rename to src/Utils/CoordinateSystem/DocumentKeyContext.cs index cde723e..612e684 100644 --- a/src/Utils/CoordinateSystem/FragmentDefaultUpContext.cs +++ b/src/Utils/CoordinateSystem/DocumentKeyContext.cs @@ -6,10 +6,12 @@ using Autodesk.Navisworks.Api; namespace NavisworksTransport.Utils.CoordinateSystem { /// - /// 当前文档级的 fragment 默认 Up 运行时状态。 - /// 自动从 CoordinateSystemManager 派生,不暴露独立设置。 + /// 当前文档运行时 key 缓存。 + /// 用于"模型根旋转提示"等按文档去重的交互(同一文档只提示一次)。 + /// 原名 FragmentDefaultUpContext(曾承担 fragment 默认 Up 运行时状态), + /// 0.18.2 去掉 fragment 参考姿态依赖后,仅保留文档 key 功能。 /// - internal static class FragmentDefaultUpContext + internal static class DocumentKeyContext { private static readonly ConcurrentDictionary RuntimeDocumentKeys = new ConcurrentDictionary(); @@ -57,43 +59,6 @@ namespace NavisworksTransport.Utils.CoordinateSystem return resolvedKey; } - public static string GetCurrentHostUpAxis(Document doc = null) - { - doc = doc ?? Autodesk.Navisworks.Api.Application.ActiveDocument; - if (doc != null && !doc.IsClear) - { - UnitVector3D worldUp = doc.CurrentViewpoint.Value.WorldUpVector; - if (!worldUp.IsZero) - { - return Math.Abs(worldUp.Y) >= Math.Abs(worldUp.Z) ? "Y" : "Z"; - } - - Vector3D documentUp = doc.UpVector; - if (!documentUp.IsZero) - { - return Math.Abs(documentUp.Y) >= Math.Abs(documentUp.Z) ? "Y" : "Z"; - } - } - - return "Z"; - } - - /// - /// 获取当前文档的 Fragment Default Up 轴。 - /// 自动从 CoordinateSystemManager 的 ResolvedType 派生。 - /// YUp → "Y", ZUp → "Z"。 - /// - public static string GetCurrentDocumentFragmentDefaultUpAxis(Document doc = null) - { - return CoordinateSystemManager.Instance.ResolvedType == CoordinateSystemType.YUp ? "Y" : "Z"; - } - - public static bool IsSupportedAxis(string axisName) - { - return string.Equals(axisName, "Y", StringComparison.OrdinalIgnoreCase) || - string.Equals(axisName, "Z", StringComparison.OrdinalIgnoreCase); - } - internal static void ResetForTests() { RuntimeDocumentKeys.Clear(); diff --git a/src/Utils/CoordinateSystem/FragmentRepresentativePoseHelper.cs b/src/Utils/CoordinateSystem/FragmentRepresentativePoseHelper.cs deleted file mode 100644 index 830085f..0000000 --- a/src/Utils/CoordinateSystem/FragmentRepresentativePoseHelper.cs +++ /dev/null @@ -1,246 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; - -namespace NavisworksTransport.Utils.CoordinateSystem -{ - /// - /// Fragment 代表帧辅助工具(诊断用途)。 - /// - /// 说明:真实物体姿态链已不依赖 fragment(0.18.2 起使用默认轴约定, - /// forward=+X、up=宿主 up)。本工具仅保留系统诊断面板 - /// (SystemManagementViewModel 方法5:Fragment 矩阵分析)所需的 - /// TryGetRepresentativeFrame / TryExtractRotationFromFragmentMatrix。 - /// - public static class FragmentRepresentativePoseHelper - { - private const float AxisEpsilon = 1e-6f; - - public readonly struct RepresentativeFrame - { - public Vector3 AxisX { get; } - public Vector3 AxisY { get; } - public Vector3 AxisZ { get; } - public Quaternion Rotation { get; } - - public RepresentativeFrame(Vector3 axisX, Vector3 axisY, Vector3 axisZ, Quaternion rotation) - { - AxisX = axisX; - AxisY = axisY; - AxisZ = axisZ; - Rotation = rotation; - } - } - - /// - /// 从 fragment 矩阵集合中提取代表帧。 - /// - public static bool TryGetRepresentativeFrame( - IEnumerable fragmentMatrices, - out RepresentativeFrame representativeFrame) - { - representativeFrame = default; - - if (fragmentMatrices == null) - { - return false; - } - - var rotations = new List(); - foreach (double[] fragmentMatrix in fragmentMatrices) - { - if (TryExtractRotationFromFragmentMatrix(fragmentMatrix, out Quaternion fragmentRotation)) - { - rotations.Add(fragmentRotation); - } - } - - if (!TryGetRepresentativeRotation(rotations, out Quaternion representativeRotation)) - { - return false; - } - - Vector3 axisX = Vector3.Normalize(Vector3.Transform(Vector3.UnitX, representativeRotation)); - Vector3 axisY = Vector3.Normalize(Vector3.Transform(Vector3.UnitY, representativeRotation)); - Vector3 axisZ = Vector3.Normalize(Vector3.Transform(Vector3.UnitZ, representativeRotation)); - representativeFrame = new RepresentativeFrame(axisX, axisY, axisZ, representativeRotation); - return true; - } - - /// - /// 从 fragment 旋转集合中提取代表旋转(同半球加权平均)。 - /// - private static bool TryGetRepresentativeRotation( - IEnumerable fragmentRotations, - out Quaternion representativeRotation) - { - representativeRotation = Quaternion.Identity; - - if (fragmentRotations == null) - { - return false; - } - - var rotations = fragmentRotations - .Where(rotation => TryNormalize(rotation, out _)) - .Select(rotation => Quaternion.Normalize(rotation)) - .ToList(); - - if (rotations.Count == 0) - { - return false; - } - - Quaternion seed = rotations[0]; - Vector4 sum = Vector4.Zero; - foreach (Quaternion rotation in rotations) - { - Quaternion aligned = Quaternion.Dot(seed, rotation) < 0.0f - ? new Quaternion(-rotation.X, -rotation.Y, -rotation.Z, -rotation.W) - : rotation; - - sum += new Vector4(aligned.X, aligned.Y, aligned.Z, aligned.W); - } - - if (sum.LengthSquared() < AxisEpsilon) - { - return false; - } - - representativeRotation = Quaternion.Normalize(new Quaternion(sum.X, sum.Y, sum.Z, sum.W)); - return true; - } - - /// - /// 从 fragment 的 4x4 world matrix 中提取旋转。 - /// 仅使用旋转部分,忽略平移。 - /// - public static bool TryExtractRotationFromFragmentMatrix(double[] fragmentMatrix, out Quaternion rotation) - { - rotation = Quaternion.Identity; - - if (fragmentMatrix == null || fragmentMatrix.Length != 16) - { - return false; - } - - Vector3 axisX = new Vector3( - (float)fragmentMatrix[0], - (float)fragmentMatrix[1], - (float)fragmentMatrix[2]); - Vector3 axisY = new Vector3( - (float)fragmentMatrix[4], - (float)fragmentMatrix[5], - (float)fragmentMatrix[6]); - Vector3 axisZ = new Vector3( - (float)fragmentMatrix[8], - (float)fragmentMatrix[9], - (float)fragmentMatrix[10]); - - if (!TryNormalize(axisX, out axisX) || - !TryNormalize(axisY, out axisY) || - !TryNormalize(axisZ, out axisZ)) - { - return false; - } - - // 先将三个轴正交化,避免 fragment 矩阵里存在轻微数值误差或尺度干扰。 - Vector3 orthogonalY = axisY - Vector3.Dot(axisY, axisX) * axisX; - if (!TryNormalize(orthogonalY, out orthogonalY)) - { - orthogonalY = Vector3.Normalize(Vector3.Cross(axisZ, axisX)); - } - - Vector3 orthogonalZ = Vector3.Cross(axisX, orthogonalY); - if (!TryNormalize(orthogonalZ, out orthogonalZ)) - { - return false; - } - - orthogonalY = Vector3.Normalize(Vector3.Cross(orthogonalZ, axisX)); - - rotation = Quaternion.Normalize(CreateQuaternionFromBasis(axisX, orthogonalY, orthogonalZ)); - return true; - } - - private static Quaternion CreateQuaternionFromBasis(Vector3 axisX, Vector3 axisY, Vector3 axisZ) - { - float m00 = axisX.X; - float m01 = axisY.X; - float m02 = axisZ.X; - float m10 = axisX.Y; - float m11 = axisY.Y; - float m12 = axisZ.Y; - float m20 = axisX.Z; - float m21 = axisY.Z; - float m22 = axisZ.Z; - - float trace = m00 + m11 + m22; - float qx; - float qy; - float qz; - float qw; - - if (trace > 0.0f) - { - float s = (float)Math.Sqrt(trace + 1.0f) * 2.0f; - qw = 0.25f * s; - qx = (m21 - m12) / s; - qy = (m02 - m20) / s; - qz = (m10 - m01) / s; - } - else if (m00 > m11 && m00 > m22) - { - float s = (float)Math.Sqrt(1.0f + m00 - m11 - m22) * 2.0f; - qw = (m21 - m12) / s; - qx = 0.25f * s; - qy = (m01 + m10) / s; - qz = (m02 + m20) / s; - } - else if (m11 > m22) - { - float s = (float)Math.Sqrt(1.0f + m11 - m00 - m22) * 2.0f; - qw = (m02 - m20) / s; - qx = (m01 + m10) / s; - qy = 0.25f * s; - qz = (m12 + m21) / s; - } - else - { - float s = (float)Math.Sqrt(1.0f + m22 - m00 - m11) * 2.0f; - qw = (m10 - m01) / s; - qx = (m02 + m20) / s; - qy = (m12 + m21) / s; - qz = 0.25f * s; - } - - return new Quaternion(qx, qy, qz, qw); - } - - private static bool TryNormalize(Quaternion value, out Quaternion normalized) - { - float lengthSquared = value.X * value.X + value.Y * value.Y + value.Z * value.Z + value.W * value.W; - if (lengthSquared < AxisEpsilon) - { - normalized = Quaternion.Identity; - return false; - } - - normalized = Quaternion.Normalize(value); - return true; - } - - private static bool TryNormalize(Vector3 value, out Vector3 normalized) - { - if (value.LengthSquared() < AxisEpsilon) - { - normalized = Vector3.Zero; - return false; - } - - normalized = Vector3.Normalize(value); - return true; - } - } -} diff --git a/src/Utils/CoordinateSystem/RealObjectReferencePoseResolver.cs b/src/Utils/CoordinateSystem/RealObjectReferencePoseResolver.cs deleted file mode 100644 index caf7508..0000000 --- a/src/Utils/CoordinateSystem/RealObjectReferencePoseResolver.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; - -namespace NavisworksTransport.Utils.CoordinateSystem -{ - /// - /// 真实物体 fragment 姿态解析入口(仅保留诊断用途)。 - /// - /// 说明:真实物体姿态链已不依赖 fragment(0.18.2 起使用默认轴约定, - /// forward=+X、up=宿主 up)。原 TryResolveFromModelItem / - /// TryResolveFromFragmentMatrices(fragment 代表姿态解析链)已删除。 - /// 本类仅保留系统诊断面板(SystemManagementViewModel 方法5)使用的 - /// TryDetectDefaultUpAxis。 - /// - public static class RealObjectReferencePoseResolver - { - /// - /// 从 fragment 矩阵检测物体的默认 Up 轴(Y 或 Z 更贴近宿主 up)。 - /// - public static bool TryDetectDefaultUpAxis( - IReadOnlyCollection fragmentMatrices, - string hostUpAxis, - out string detectedAxis, - out float yAlignment, - out float zAlignment) - { - detectedAxis = "Y"; - yAlignment = 0.0f; - zAlignment = 0.0f; - - if (fragmentMatrices == null || fragmentMatrices.Count == 0) - { - return false; - } - - if (!FragmentRepresentativePoseHelper.TryGetRepresentativeFrame(fragmentMatrices, out var rawFrame)) - { - return false; - } - - Vector3 hostUpVector = string.Equals(hostUpAxis, "Y", StringComparison.OrdinalIgnoreCase) - ? Vector3.UnitY - : Vector3.UnitZ; - - yAlignment = Math.Abs(Vector3.Dot(Vector3.Normalize(rawFrame.AxisY), hostUpVector)); - zAlignment = Math.Abs(Vector3.Dot(Vector3.Normalize(rawFrame.AxisZ), hostUpVector)); - detectedAxis = zAlignment > yAlignment ? "Z" : "Y"; - return true; - } - } -}