refactor: 清理方法5 fragment 矩阵分析依赖链(保留坐标系检测其余方法)
删除(fragment 矩阵分析诊断链): - SystemManagementViewModel: 方法5 文本块、AnalyzeFragmentOrientations、 AppendFragmentAnalysis、FragmentOrientationAnalysis 类 - FragmentRepresentativePoseHelper.cs(TryGetRepresentativeFrame 无调用者) - RealObjectReferencePoseResolver.cs(TryDetectDefaultUpAxis 只被方法5 用) - FragmentRepresentativePoseHelperTests.cs - FragmentDefaultUpContext: 删 GetCurrentHostUpAxis / GetCurrentDocumentFragmentDefaultUpAxis / IsSupportedAxis(均无调用者) 保留: - 坐标系检测 方法1/2/3/4/6(文档级检测) - 真实物体变换诊断(TransformDiagnosticInfo 保留,轴可视化 fallback 到几何后代/选中节点 Transform,不再用 fragment) - FragmentDefaultUpContext 改名 DocumentKeyContext(仅文档 key 缓存, 用于模型根旋转提示;名字不再含 fragment) 验证:单元测试 185/185、集成测试 25/25
This commit is contained in:
parent
e0b01247ab
commit
29da68f6d3
@ -96,7 +96,6 @@
|
||||
<Compile Include="UnitTests\CoordinateSystem\CanonicalTrackedPositionResolverTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\GroundPathObjectLiftOffsetTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\GroundPassageSpaceOffsetTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\FragmentRepresentativePoseHelperTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\HoistingCoordinateHelperTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\HoistingRealObjectPoseHelperTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\ModelAxisConventionTests.cs" />
|
||||
@ -112,7 +111,7 @@
|
||||
<Compile Include="UnitTests\CoordinateSystem\RailAssemblyWorkflowContextTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\ViewpointHelperTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\PathPointVisualizationTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\FragmentDefaultUpContextTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\DocumentKeyContextTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\VirtualGroundPoseCharacterizationTests.cs" />
|
||||
<Compile Include="UnitTests\Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -362,9 +362,7 @@
|
||||
<Compile Include="src\Utils\CoordinateSystem\CanonicalRailOffsetResolver.cs" />
|
||||
<Compile Include="src\Utils\CoordinateSystem\CanonicalRailPoseBuilder.cs" />
|
||||
<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\RealObjectReferencePoseResolver.cs" />
|
||||
<Compile Include="src\Utils\CoordinateSystem\DocumentKeyContext.cs" />
|
||||
<Compile Include="src\Utils\CoordinateSystem\HoistingRealObjectPoseHelper.cs" />
|
||||
<Compile Include="src\Utils\CoordinateSystem\PathTargetFrame.cs" />
|
||||
<Compile Include="src\Utils\CoordinateSystem\PathTargetFrameResolver.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,
|
||||
"无标题");
|
||||
@ -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 分量");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<FragmentInfo> 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<double[]> 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; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快速取面测试 — 激活 FaceInferToolPlugin,用户在模型上点击后显示面法向量
|
||||
/// </summary>
|
||||
@ -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")
|
||||
|
||||
@ -6,10 +6,12 @@ using Autodesk.Navisworks.Api;
|
||||
namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前文档级的 fragment 默认 Up 运行时状态。
|
||||
/// 自动从 CoordinateSystemManager 派生,不暴露独立设置。
|
||||
/// 当前文档运行时 key 缓存。
|
||||
/// 用于"模型根旋转提示"等按文档去重的交互(同一文档只提示一次)。
|
||||
/// 原名 FragmentDefaultUpContext(曾承担 fragment 默认 Up 运行时状态),
|
||||
/// 0.18.2 去掉 fragment 参考姿态依赖后,仅保留文档 key 功能。
|
||||
/// </summary>
|
||||
internal static class FragmentDefaultUpContext
|
||||
internal static class DocumentKeyContext
|
||||
{
|
||||
private static readonly ConcurrentDictionary<int, string> RuntimeDocumentKeys =
|
||||
new ConcurrentDictionary<int, string>();
|
||||
@ -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";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前文档的 Fragment Default Up 轴。
|
||||
/// 自动从 CoordinateSystemManager 的 ResolvedType 派生。
|
||||
/// YUp → "Y", ZUp → "Z"。
|
||||
/// </summary>
|
||||
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();
|
||||
@ -1,246 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Fragment 代表帧辅助工具(诊断用途)。
|
||||
///
|
||||
/// 说明:真实物体姿态链已不依赖 fragment(0.18.2 起使用默认轴约定,
|
||||
/// forward=+X、up=宿主 up)。本工具仅保留系统诊断面板
|
||||
/// (SystemManagementViewModel 方法5:Fragment 矩阵分析)所需的
|
||||
/// TryGetRepresentativeFrame / TryExtractRotationFromFragmentMatrix。
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 fragment 矩阵集合中提取代表帧。
|
||||
/// </summary>
|
||||
public static bool TryGetRepresentativeFrame(
|
||||
IEnumerable<double[]> fragmentMatrices,
|
||||
out RepresentativeFrame representativeFrame)
|
||||
{
|
||||
representativeFrame = default;
|
||||
|
||||
if (fragmentMatrices == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var rotations = new List<Quaternion>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 fragment 旋转集合中提取代表旋转(同半球加权平均)。
|
||||
/// </summary>
|
||||
private static bool TryGetRepresentativeRotation(
|
||||
IEnumerable<Quaternion> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 fragment 的 4x4 world matrix 中提取旋转。
|
||||
/// 仅使用旋转部分,忽略平移。
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// 真实物体 fragment 姿态解析入口(仅保留诊断用途)。
|
||||
///
|
||||
/// 说明:真实物体姿态链已不依赖 fragment(0.18.2 起使用默认轴约定,
|
||||
/// forward=+X、up=宿主 up)。原 TryResolveFromModelItem /
|
||||
/// TryResolveFromFragmentMatrices(fragment 代表姿态解析链)已删除。
|
||||
/// 本类仅保留系统诊断面板(SystemManagementViewModel 方法5)使用的
|
||||
/// TryDetectDefaultUpAxis。
|
||||
/// </summary>
|
||||
public static class RealObjectReferencePoseResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// 从 fragment 矩阵检测物体的默认 Up 轴(Y 或 Z 更贴近宿主 up)。
|
||||
/// </summary>
|
||||
public static bool TryDetectDefaultUpAxis(
|
||||
IReadOnlyCollection<double[]> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user