Fix host rotation adapter split for real and virtual objects
This commit is contained in:
parent
029c7e37ad
commit
aabf263f0d
@ -72,6 +72,8 @@
|
||||
<Compile Include="UnitTests\CoordinateSystem\PathTargetFrameResolverTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\AssemblyEndFaceAnalyzerTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\AssemblyInstallationReferenceBuilderTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\FragmentDefaultUpContextTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\VirtualGroundPoseCharacterizationTests.cs" />
|
||||
<Compile Include="UnitTests\Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@ -31,4 +31,5 @@ using System.Runtime.InteropServices;
|
||||
//
|
||||
// 主版本和次版本手动维护,Build和Revision在编译时自动更新
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: InternalsVisibleTo("NavisworksTransport.UnitTests")]
|
||||
|
||||
@ -522,6 +522,11 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>resources\unit_cube.nwc</Link>
|
||||
</None>
|
||||
<!-- Unit Cube YUp NWC Model File -->
|
||||
<None Include="resources\unit_cube_yup.nwc">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>resources\unit_cube_yup.nwc</Link>
|
||||
</None>
|
||||
<!-- Unit Cylinder NWC Model File -->
|
||||
<None Include="resources\unit_cylinder.nwc">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
||||
47
UnitTests/CoordinateSystem/FragmentDefaultUpContextTests.cs
Normal file
47
UnitTests/CoordinateSystem/FragmentDefaultUpContextTests.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using NavisworksTransport.Utils.CoordinateSystem;
|
||||
|
||||
namespace NavisworksTransport.UnitTests.CoordinateSystem
|
||||
{
|
||||
[TestClass]
|
||||
public class FragmentDefaultUpContextTests
|
||||
{
|
||||
[TestInitialize]
|
||||
public void SetUp()
|
||||
{
|
||||
FragmentDefaultUpContext.ResetForTests();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetOrCreateDocumentKey_ReusesFirstStableKey_WhenDocumentMetadataTemporarilyChanges()
|
||||
{
|
||||
const int runtimeDocumentId = 42;
|
||||
|
||||
string initialKey = FragmentDefaultUpContext.GetOrCreateDocumentKey(
|
||||
runtimeDocumentId,
|
||||
@"C:\models\floor2.nwd",
|
||||
"Floor2");
|
||||
|
||||
string transientKey = FragmentDefaultUpContext.GetOrCreateDocumentKey(
|
||||
runtimeDocumentId,
|
||||
string.Empty,
|
||||
"无标题");
|
||||
|
||||
Assert.AreEqual(initialKey, transientKey);
|
||||
Assert.AreEqual("file:C:\\models\\floor2.nwd", transientKey);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetOrCreateDocumentKey_UsesRuntimeScopedFallback_WhenFileNameIsUnavailable()
|
||||
{
|
||||
const int runtimeDocumentId = 7;
|
||||
|
||||
string key = FragmentDefaultUpContext.GetOrCreateDocumentKey(
|
||||
runtimeDocumentId,
|
||||
string.Empty,
|
||||
"无标题");
|
||||
|
||||
Assert.AreEqual("runtime:7|title:无标题", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using NavisworksTransport.Utils.CoordinateSystem;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Autodesk.Navisworks.Api;
|
||||
|
||||
namespace NavisworksTransport.UnitTests.CoordinateSystem
|
||||
{
|
||||
[TestClass]
|
||||
public class VirtualGroundPoseCharacterizationTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void YUp_GroundVirtualPose_WithYUpVirtualAsset_ShouldUseHostYAsUp()
|
||||
{
|
||||
var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
|
||||
var convention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.YUp);
|
||||
|
||||
Vector3 hostForward = Vector3.Normalize(new Vector3(-0.8987f, 0.0f, 0.4386f));
|
||||
Vector3 canonicalForward = adapter.ToCanonicalVector3(hostForward);
|
||||
|
||||
bool ok = CanonicalPlanarPoseBuilder.TryCreateQuaternionFromForward(
|
||||
canonicalForward,
|
||||
HostCoordinateAdapter.CanonicalUpVector3,
|
||||
convention,
|
||||
out Quaternion canonicalRotation);
|
||||
|
||||
Assert.IsTrue(ok);
|
||||
|
||||
Matrix4x4 hostLinear = adapter.FromCanonicalLinearTransform(
|
||||
Matrix4x4.CreateFromQuaternion(canonicalRotation));
|
||||
|
||||
AssertColumn(hostLinear, 0, -0.8987, 0.0000, 0.4386);
|
||||
AssertColumn(hostLinear, 1, 0.0000, 1.0000, 0.0000);
|
||||
AssertColumn(hostLinear, 2, -0.4386, 0.0000, -0.8987);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void YUp_GroundVirtualPose_WithYUpVirtualAsset_ShouldMatchHostYUpDefaultPlanarConvention()
|
||||
{
|
||||
var adapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
|
||||
Vector3 hostForward = Vector3.Normalize(new Vector3(-0.8987f, 0.0f, 0.4386f));
|
||||
Vector3 canonicalForward = adapter.ToCanonicalVector3(hostForward);
|
||||
|
||||
Assert.IsTrue(CanonicalPlanarPoseBuilder.TryCreateQuaternionFromForward(
|
||||
canonicalForward,
|
||||
HostCoordinateAdapter.CanonicalUpVector3,
|
||||
ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.YUp),
|
||||
out Quaternion assetRotation));
|
||||
|
||||
Assert.IsTrue(CanonicalPlanarPoseBuilder.TryCreateQuaternionFromForward(
|
||||
canonicalForward,
|
||||
HostCoordinateAdapter.CanonicalUpVector3,
|
||||
ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.YUp),
|
||||
out Quaternion hostDefaultRotation));
|
||||
|
||||
Matrix4x4 assetHostLinear = adapter.FromCanonicalLinearTransform(
|
||||
Matrix4x4.CreateFromQuaternion(assetRotation));
|
||||
Matrix4x4 hostDefaultLinear = adapter.FromCanonicalLinearTransform(
|
||||
Matrix4x4.CreateFromQuaternion(hostDefaultRotation));
|
||||
|
||||
Assert.AreEqual(1.0, assetHostLinear.M22, 1e-3, "YUp 虚拟物体资源应把 local Y 对到宿主 up。");
|
||||
Assert.AreEqual(hostDefaultLinear.M11, assetHostLinear.M11, 1e-3);
|
||||
Assert.AreEqual(hostDefaultLinear.M21, assetHostLinear.M21, 1e-3);
|
||||
Assert.AreEqual(hostDefaultLinear.M31, assetHostLinear.M31, 1e-3);
|
||||
Assert.AreEqual(hostDefaultLinear.M12, assetHostLinear.M12, 1e-3);
|
||||
Assert.AreEqual(hostDefaultLinear.M22, assetHostLinear.M22, 1e-3);
|
||||
Assert.AreEqual(hostDefaultLinear.M32, assetHostLinear.M32, 1e-3);
|
||||
Assert.AreEqual(hostDefaultLinear.M13, assetHostLinear.M13, 1e-3);
|
||||
Assert.AreEqual(hostDefaultLinear.M23, assetHostLinear.M23, 1e-3);
|
||||
Assert.AreEqual(hostDefaultLinear.M33, assetHostLinear.M33, 1e-3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void VirtualObjectReferencePose_ShouldMatchSelectedVirtualAssetConvention()
|
||||
{
|
||||
var yUpAdapter = new HostCoordinateAdapter(CoordinateSystemType.YUp);
|
||||
var zUpAdapter = new HostCoordinateAdapter(CoordinateSystemType.ZUp);
|
||||
var yUpConvention = ModelAxisConvention.CreateDefaultForHost(CoordinateSystemType.YUp);
|
||||
var zUpConvention = ModelAxisConvention.CreateVirtualObjectAssetConvention();
|
||||
|
||||
Matrix4x4 yUpLinear = Matrix4x4.CreateFromQuaternion(
|
||||
yUpConvention.CreateQuaternion(new Vector3(1, 0, 0), yUpAdapter.HostUpVector3));
|
||||
Matrix4x4 zUpLinear = Matrix4x4.CreateFromQuaternion(
|
||||
zUpConvention.CreateQuaternion(new Vector3(1, 0, 0), zUpAdapter.HostUpVector3));
|
||||
|
||||
AssertColumn(yUpLinear, 0, 1.0, 0.0, 0.0);
|
||||
AssertColumn(yUpLinear, 1, 0.0, 1.0, 0.0);
|
||||
AssertColumn(yUpLinear, 2, 0.0, 0.0, 1.0);
|
||||
|
||||
AssertColumn(zUpLinear, 0, 1.0, 0.0, 0.0);
|
||||
AssertColumn(zUpLinear, 1, 0.0, 1.0, 0.0);
|
||||
AssertColumn(zUpLinear, 2, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
private static void AssertColumn(Matrix4x4 matrix, int column, double x, double y, double z)
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case 0:
|
||||
Assert.AreEqual(x, matrix.M11, 1e-3);
|
||||
Assert.AreEqual(y, matrix.M21, 1e-3);
|
||||
Assert.AreEqual(z, matrix.M31, 1e-3);
|
||||
break;
|
||||
case 1:
|
||||
Assert.AreEqual(x, matrix.M12, 1e-3);
|
||||
Assert.AreEqual(y, matrix.M22, 1e-3);
|
||||
Assert.AreEqual(z, matrix.M32, 1e-3);
|
||||
break;
|
||||
case 2:
|
||||
Assert.AreEqual(x, matrix.M13, 1e-3);
|
||||
Assert.AreEqual(y, matrix.M23, 1e-3);
|
||||
Assert.AreEqual(z, matrix.M33, 1e-3);
|
||||
break;
|
||||
default:
|
||||
Assert.Fail("Only first 3 columns are valid.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
100
UnitTests/CoordinateSystem/VirtualObjectModelTransformTests.cs
Normal file
100
UnitTests/CoordinateSystem/VirtualObjectModelTransformTests.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using NavisworksTransport.Core;
|
||||
|
||||
namespace NavisworksTransport.UnitTests.CoordinateSystem
|
||||
{
|
||||
[TestClass]
|
||||
public class VirtualObjectModelTransformTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void BuildModelTransformPreservingScale_ShouldKeepExistingScaleAndReplaceRotationTranslation()
|
||||
{
|
||||
const double scaleX = 4.9213;
|
||||
const double scaleY = 3.2808;
|
||||
const double scaleZ = 6.5617;
|
||||
var targetRotation = new QuaternionData(0.0, 0.7071068, 0.0, 0.7071068);
|
||||
|
||||
VirtualObjectManager.ModelTransformComponents result = VirtualObjectManager.CreateModelTransformPreservingScale(
|
||||
scaleX,
|
||||
scaleY,
|
||||
scaleZ,
|
||||
-177.129,
|
||||
18.114,
|
||||
-2.793,
|
||||
targetRotation.ToRotation3D());
|
||||
|
||||
Assert.AreEqual(scaleX, result.ScaleX, 1e-4);
|
||||
Assert.AreEqual(scaleY, result.ScaleY, 1e-4);
|
||||
Assert.AreEqual(scaleZ, result.ScaleZ, 1e-4);
|
||||
AssertRotationEquivalent(targetRotation, result.Rotation);
|
||||
Assert.AreEqual(-177.129, result.TranslationX, 1e-6);
|
||||
Assert.AreEqual(18.114, result.TranslationY, 1e-6);
|
||||
Assert.AreEqual(-2.793, result.TranslationZ, 1e-6);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BuildModelTransformPreservingScale_ResetPose_ShouldOnlyClearRotationAndTranslation()
|
||||
{
|
||||
VirtualObjectManager.ModelTransformComponents result = VirtualObjectManager.CreateModelTransformPreservingScale(
|
||||
12.0,
|
||||
5.0,
|
||||
7.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
QuaternionData.Identity.ToRotation3D());
|
||||
|
||||
Assert.AreEqual(12.0, result.ScaleX, 1e-6);
|
||||
Assert.AreEqual(5.0, result.ScaleY, 1e-6);
|
||||
Assert.AreEqual(7.0, result.ScaleZ, 1e-6);
|
||||
AssertRotationEquivalent(QuaternionData.Identity, result.Rotation);
|
||||
Assert.AreEqual(0.0, result.TranslationX, 1e-6);
|
||||
Assert.AreEqual(0.0, result.TranslationY, 1e-6);
|
||||
Assert.AreEqual(0.0, result.TranslationZ, 1e-6);
|
||||
}
|
||||
|
||||
private static void AssertRotationEquivalent(QuaternionData expected, Autodesk.Navisworks.Api.Rotation3D actual)
|
||||
{
|
||||
bool same =
|
||||
NearlyEqual(expected.X, actual.A) &&
|
||||
NearlyEqual(expected.Y, actual.B) &&
|
||||
NearlyEqual(expected.Z, actual.C) &&
|
||||
NearlyEqual(expected.W, actual.D);
|
||||
bool negatedSame =
|
||||
NearlyEqual(expected.X, -actual.A) &&
|
||||
NearlyEqual(expected.Y, -actual.B) &&
|
||||
NearlyEqual(expected.Z, -actual.C) &&
|
||||
NearlyEqual(expected.W, -actual.D);
|
||||
|
||||
Assert.IsTrue(same || negatedSame, "Rotation3D quaternion should match target rotation up to sign.");
|
||||
}
|
||||
|
||||
private static bool NearlyEqual(double left, double right)
|
||||
{
|
||||
return System.Math.Abs(left - right) < 1e-6;
|
||||
}
|
||||
|
||||
private struct QuaternionData
|
||||
{
|
||||
public QuaternionData(double x, double y, double z, double w)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
public double X { get; }
|
||||
public double Y { get; }
|
||||
public double Z { get; }
|
||||
public double W { get; }
|
||||
|
||||
public Autodesk.Navisworks.Api.Rotation3D ToRotation3D()
|
||||
{
|
||||
return new Autodesk.Navisworks.Api.Rotation3D(X, Y, Z, W);
|
||||
}
|
||||
|
||||
public static QuaternionData Identity => new QuaternionData(0, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
resources/unit_cube_yup.nwc
Normal file
BIN
resources/unit_cube_yup.nwc
Normal file
Binary file not shown.
26
resources/unit_cube_yup.obj
Normal file
26
resources/unit_cube_yup.obj
Normal file
@ -0,0 +1,26 @@
|
||||
# unit cube for virtual object (Y-up)
|
||||
# dimensions: 1.0 x 1.0 x 1.0
|
||||
# centered at origin
|
||||
# local +X = forward, local +Y = up, local +Z = side
|
||||
# when Navisworks imports OBJ as centimeters, generated NWC becomes 0.01m base size
|
||||
o unit_cube_yup
|
||||
v -0.500000 -0.500000 -0.500000
|
||||
v 0.500000 -0.500000 -0.500000
|
||||
v 0.500000 0.500000 -0.500000
|
||||
v -0.500000 0.500000 -0.500000
|
||||
v -0.500000 -0.500000 0.500000
|
||||
v 0.500000 -0.500000 0.500000
|
||||
v 0.500000 0.500000 0.500000
|
||||
v -0.500000 0.500000 0.500000
|
||||
f 1 2 3
|
||||
f 1 3 4
|
||||
f 5 8 7
|
||||
f 5 7 6
|
||||
f 1 5 6
|
||||
f 1 6 2
|
||||
f 2 6 7
|
||||
f 2 7 3
|
||||
f 3 7 8
|
||||
f 3 8 4
|
||||
f 4 8 5
|
||||
f 4 5 1
|
||||
@ -449,7 +449,16 @@ namespace NavisworksTransport.Core.Animation
|
||||
|
||||
// 2. 重置内部跟踪变量(同步到原始状态)
|
||||
// 注意:ResetPermanentTransform 后物体的 Transform 属性会自动变回原始值
|
||||
SyncTrackedRotationToObjectReference(objectToRestore, isVirtual);
|
||||
if (isVirtual)
|
||||
{
|
||||
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(objectToRestore.Transform);
|
||||
_trackedRotation = objectToRestore.Transform.Factor().Rotation;
|
||||
_hasTrackedRotation = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SyncTrackedRotationToObjectReference(objectToRestore, isVirtualObject: false);
|
||||
}
|
||||
|
||||
var originalBoundingBox = objectToRestore.BoundingBox();
|
||||
_trackedPosition = GetTrackedObjectPosition(objectToRestore);
|
||||
@ -652,7 +661,16 @@ namespace NavisworksTransport.Core.Animation
|
||||
_originalTransform = animatedObject.Transform;
|
||||
_originalCenter = animatedObject.BoundingBox().Center;
|
||||
_trackedPosition = GetTrackedObjectPosition(animatedObject);
|
||||
SyncTrackedRotationToObjectReference(animatedObject, isVirtualObject);
|
||||
if (isVirtualObject)
|
||||
{
|
||||
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform);
|
||||
_trackedRotation = _originalTransform.Factor().Rotation;
|
||||
_hasTrackedRotation = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SyncTrackedRotationToObjectReference(animatedObject, isVirtualObject: false);
|
||||
}
|
||||
|
||||
LogManager.Info(
|
||||
$"[移动到起点] 更新动画对象内部状态: 对象={animatedObject.DisplayName}, 虚拟物体={IsVirtualObjectMode}");
|
||||
@ -2452,75 +2470,89 @@ namespace NavisworksTransport.Core.Animation
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isRailRealObject = _route?.PathType == PathType.Rail && IsRealObjectMode && _animatedObject != null;
|
||||
if (IsVirtualObjectMode)
|
||||
{
|
||||
VirtualObjectManager.Instance.MoveVirtualObject(newPosition, newRotation);
|
||||
}
|
||||
else if (_animatedObject != null)
|
||||
{
|
||||
Point3D currentPositionForTransform = _trackedPosition;
|
||||
Rotation3D currentRotation;
|
||||
if (_hasTrackedRotation)
|
||||
{
|
||||
currentRotation = _trackedRotation;
|
||||
}
|
||||
else if (IsRealObjectMode && _hasRealObjectReferenceRotation)
|
||||
{
|
||||
currentRotation = CoordinateSystemManager.Instance
|
||||
.CreateHostAdapter()
|
||||
.FromHostQuaternion(_realObjectReferenceRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentRotation = _animatedObject.Transform.Factor().Rotation;
|
||||
}
|
||||
try
|
||||
{
|
||||
var actualHostPosition = GetTrackedObjectPosition(_animatedObject);
|
||||
currentPositionForTransform = actualHostPosition;
|
||||
LogManager.Info(
|
||||
$"[动画姿态入口] {_animatedObject.DisplayName} 宿主即时读回点=({actualHostPosition.X:F3},{actualHostPosition.Y:F3},{actualHostPosition.Z:F3})");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[动画姿态入口] 读取宿主实际状态失败: {ex.Message}");
|
||||
}
|
||||
var currentLinear = new Transform3D(currentRotation).Linear;
|
||||
var targetLinear = new Transform3D(newRotation).Linear;
|
||||
LogManager.Info(
|
||||
$"[动画姿态入口] {_animatedObject.DisplayName} 跟踪点=({_trackedPosition.X:F3},{_trackedPosition.Y:F3},{_trackedPosition.Z:F3}), " +
|
||||
$"目标点=({newPosition.X:F3},{newPosition.Y:F3},{newPosition.Z:F3})");
|
||||
LogManager.Info(
|
||||
$"[动画姿态入口] {_animatedObject.DisplayName} 跟踪姿态: " +
|
||||
$"X=({currentLinear.Get(0, 0):F4},{currentLinear.Get(1, 0):F4},{currentLinear.Get(2, 0):F4}), " +
|
||||
$"Y=({currentLinear.Get(0, 1):F4},{currentLinear.Get(1, 1):F4},{currentLinear.Get(2, 1):F4}), " +
|
||||
$"Z=({currentLinear.Get(0, 2):F4},{currentLinear.Get(1, 2):F4},{currentLinear.Get(2, 2):F4})");
|
||||
LogManager.Info(
|
||||
$"[动画姿态入口] {_animatedObject.DisplayName} 目标姿态: " +
|
||||
$"X=({targetLinear.Get(0, 0):F4},{targetLinear.Get(1, 0):F4},{targetLinear.Get(2, 0):F4}), " +
|
||||
$"Y=({targetLinear.Get(0, 1):F4},{targetLinear.Get(1, 1):F4},{targetLinear.Get(2, 1):F4}), " +
|
||||
$"Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})");
|
||||
ModelItemTransformHelper.MoveItemIncrementallyToPositionAndRotation(
|
||||
_animatedObject,
|
||||
currentPositionForTransform,
|
||||
currentRotation,
|
||||
newPosition,
|
||||
newRotation);
|
||||
}
|
||||
else
|
||||
ModelItem controlledObject = CurrentControlledObject;
|
||||
bool isRailRealObject = _route?.PathType == PathType.Rail && IsRealObjectMode && controlledObject != null;
|
||||
if (controlledObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsVirtualObjectMode)
|
||||
{
|
||||
VirtualObjectManager.Instance.MoveVirtualObject(newPosition, newRotation);
|
||||
_trackedPosition = newPosition;
|
||||
_trackedRotation = newRotation;
|
||||
_hasTrackedRotation = true;
|
||||
_currentYaw = ModelItemTransformHelper.GetYawFromRotation(newRotation);
|
||||
|
||||
if (_route?.PathType == PathType.Ground || _route?.PathType == PathType.Hoisting)
|
||||
{
|
||||
LogHostActualPoseAxes(CurrentControlledObject, "[平面姿态应用后宿主姿态]", false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Point3D currentPositionForTransform = _trackedPosition;
|
||||
Rotation3D currentRotation;
|
||||
Rotation3D appliedTargetRotation = newRotation;
|
||||
if (_hasTrackedRotation)
|
||||
{
|
||||
currentRotation = _trackedRotation;
|
||||
}
|
||||
else if (IsRealObjectMode && _hasRealObjectReferenceRotation)
|
||||
{
|
||||
currentRotation = CoordinateSystemManager.Instance
|
||||
.CreateHostAdapter()
|
||||
.FromHostQuaternionDirect(_realObjectReferenceRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentRotation = controlledObject.Transform.Factor().Rotation;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var actualHostPosition = GetTrackedObjectPosition(controlledObject);
|
||||
currentPositionForTransform = actualHostPosition;
|
||||
LogManager.Info(
|
||||
$"[动画姿态入口] {controlledObject.DisplayName} 宿主即时读回点=({actualHostPosition.X:F3},{actualHostPosition.Y:F3},{actualHostPosition.Z:F3})");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[动画姿态入口] 读取宿主实际状态失败: {ex.Message}");
|
||||
}
|
||||
|
||||
var currentLinear = new Transform3D(currentRotation).Linear;
|
||||
var targetLinear = new Transform3D(appliedTargetRotation).Linear;
|
||||
LogManager.Info(
|
||||
$"[动画姿态入口] {controlledObject.DisplayName} 跟踪点=({_trackedPosition.X:F3},{_trackedPosition.Y:F3},{_trackedPosition.Z:F3}), " +
|
||||
$"目标点=({newPosition.X:F3},{newPosition.Y:F3},{newPosition.Z:F3})");
|
||||
LogManager.Info(
|
||||
$"[动画姿态入口] {controlledObject.DisplayName} 跟踪姿态: " +
|
||||
$"X=({currentLinear.Get(0, 0):F4},{currentLinear.Get(1, 0):F4},{currentLinear.Get(2, 0):F4}), " +
|
||||
$"Y=({currentLinear.Get(0, 1):F4},{currentLinear.Get(1, 1):F4},{currentLinear.Get(2, 1):F4}), " +
|
||||
$"Z=({currentLinear.Get(0, 2):F4},{currentLinear.Get(1, 2):F4},{currentLinear.Get(2, 2):F4})");
|
||||
LogManager.Info(
|
||||
$"[动画姿态入口] {controlledObject.DisplayName} 目标姿态: " +
|
||||
$"X=({targetLinear.Get(0, 0):F4},{targetLinear.Get(1, 0):F4},{targetLinear.Get(2, 0):F4}), " +
|
||||
$"Y=({targetLinear.Get(0, 1):F4},{targetLinear.Get(1, 1):F4},{targetLinear.Get(2, 1):F4}), " +
|
||||
$"Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})");
|
||||
ModelItemTransformHelper.MoveItemIncrementallyToPositionAndRotation(
|
||||
controlledObject,
|
||||
currentPositionForTransform,
|
||||
currentRotation,
|
||||
newPosition,
|
||||
appliedTargetRotation);
|
||||
|
||||
_trackedPosition = newPosition;
|
||||
_trackedRotation = newRotation;
|
||||
_trackedRotation = appliedTargetRotation;
|
||||
_hasTrackedRotation = true;
|
||||
_currentYaw = ModelItemTransformHelper.GetYawFromRotation(newRotation);
|
||||
_currentYaw = ModelItemTransformHelper.GetYawFromRotation(appliedTargetRotation);
|
||||
|
||||
if (isRailRealObject)
|
||||
{
|
||||
Point3D hostActualAfter = GetTrackedObjectPosition(_animatedObject);
|
||||
Point3D hostActualAfter = GetTrackedObjectPosition(controlledObject);
|
||||
LogManager.Debug(
|
||||
$"[Rail姿态应用] 宿主最终跟踪中心=({hostActualAfter.X:F3},{hostActualAfter.Y:F3},{hostActualAfter.Z:F3}), " +
|
||||
$"目标跟踪中心=({newPosition.X:F3},{newPosition.Y:F3},{newPosition.Z:F3}), " +
|
||||
@ -3782,7 +3814,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
Quaternion composedHostQuaternion = adapter.ComposeHostQuaternion(
|
||||
baselineHostQuaternion,
|
||||
_objectRotationCorrection);
|
||||
rotation = adapter.FromHostQuaternion(composedHostQuaternion);
|
||||
rotation = adapter.FromHostQuaternionDirect(composedHostQuaternion);
|
||||
|
||||
Matrix4x4 baselineLinear = Matrix4x4.CreateFromQuaternion(baselineHostQuaternion);
|
||||
Matrix4x4 composedLinear = Matrix4x4.CreateFromQuaternion(composedHostQuaternion);
|
||||
@ -3958,6 +3990,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private bool TryCreateRealObjectPlanarRotationFromHostForward(Vector3 hostForward, out Rotation3D rotation)
|
||||
{
|
||||
rotation = Rotation3D.Identity;
|
||||
@ -3980,7 +4013,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
|
||||
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
Quaternion hostComposedQuaternion = adapter.ComposeHostQuaternion(solution.BaselineRotation, _objectRotationCorrection);
|
||||
rotation = adapter.FromHostQuaternion(hostComposedQuaternion);
|
||||
rotation = adapter.FromHostQuaternionDirect(hostComposedQuaternion);
|
||||
|
||||
Matrix4x4 baselineLinear = Matrix4x4.CreateFromQuaternion(solution.BaselineRotation);
|
||||
Matrix4x4 hostComposedLinear = Matrix4x4.CreateFromQuaternion(hostComposedQuaternion);
|
||||
@ -4192,10 +4225,25 @@ namespace NavisworksTransport.Core.Animation
|
||||
return;
|
||||
}
|
||||
|
||||
if (isVirtualObject)
|
||||
{
|
||||
_trackedRotation = CreateVirtualObjectReferenceRotation();
|
||||
_hasTrackedRotation = true;
|
||||
_currentYaw = ModelItemTransformHelper.GetYawFromRotation(_trackedRotation);
|
||||
|
||||
var linear = new Transform3D(_trackedRotation).Linear;
|
||||
LogManager.Info(
|
||||
$"[虚拟物体参考姿态] {sourceObject.DisplayName} 使用资产约定基姿态: " +
|
||||
$"X=({linear.Get(0, 0):F4},{linear.Get(1, 0):F4},{linear.Get(2, 0):F4}), " +
|
||||
$"Y=({linear.Get(0, 1):F4},{linear.Get(1, 1):F4},{linear.Get(2, 1):F4}), " +
|
||||
$"Z=({linear.Get(0, 2):F4},{linear.Get(1, 2):F4},{linear.Get(2, 2):F4})");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isVirtualObject && TryCaptureRealObjectReferenceRotation(sourceObject, out Quaternion referenceRotation))
|
||||
{
|
||||
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
_trackedRotation = adapter.FromHostQuaternion(referenceRotation);
|
||||
_trackedRotation = adapter.FromHostQuaternionDirect(referenceRotation);
|
||||
_hasTrackedRotation = true;
|
||||
_currentYaw = ModelItemTransformHelper.GetYawFromRotation(_trackedRotation);
|
||||
return;
|
||||
@ -4206,6 +4254,18 @@ namespace NavisworksTransport.Core.Animation
|
||||
_hasTrackedRotation = true;
|
||||
}
|
||||
|
||||
private static Rotation3D CreateVirtualObjectReferenceRotation()
|
||||
{
|
||||
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
var convention = GetVirtualObjectAssetConvention();
|
||||
return convention.CreateRotation(new Vector3D(1, 0, 0), adapter.HostUpVector);
|
||||
}
|
||||
|
||||
private static ModelAxisConvention GetVirtualObjectAssetConvention()
|
||||
{
|
||||
return ModelAxisConvention.CreateVirtualObjectAssetConvention();
|
||||
}
|
||||
|
||||
private void ResetRealObjectReferenceRotation()
|
||||
{
|
||||
_realObjectReferenceRotation = Quaternion.Identity;
|
||||
|
||||
@ -94,7 +94,6 @@ namespace NavisworksTransport.Core
|
||||
|
||||
var doc = Application.ActiveDocument;
|
||||
var unitCubePath = GetUnitCubeFilePath();
|
||||
|
||||
if (string.IsNullOrEmpty(unitCubePath) || !File.Exists(unitCubePath))
|
||||
{
|
||||
throw new FileNotFoundException($"找不到单位立方体文件: {unitCubePath}");
|
||||
@ -213,9 +212,7 @@ namespace NavisworksTransport.Core
|
||||
try
|
||||
{
|
||||
LogVirtualObjectGeometry("[虚拟物体姿态] 应用前");
|
||||
// 虚拟物体当前尺寸是通过永久变换中的缩放实现的。
|
||||
// 应用完整姿态时必须保留当前缩放,否则会破坏虚拟物体的资产姿态和几何语义。
|
||||
ModelItemTransformHelper.MoveItemToPositionAndRotationWithCurrentScale(_virtualObjectModelItem, position, rotation);
|
||||
ModelItemTransformHelper.MoveItemToPositionAndRotation(_virtualObjectModelItem, position, rotation);
|
||||
var actualBounds = _virtualObjectModelItem.BoundingBox();
|
||||
Point3D actualCenter = actualBounds?.Center ?? new Point3D(0, 0, 0);
|
||||
LogManager.Info(
|
||||
@ -239,20 +236,15 @@ namespace NavisworksTransport.Core
|
||||
{
|
||||
var doc = Application.ActiveDocument;
|
||||
var modelItems = new ModelItemCollection { _virtualObjectModelItem };
|
||||
|
||||
// 获取当前缩放
|
||||
var currentTransform = _virtualObjectModelItem.Transform;
|
||||
var currentComponents = currentTransform.Factor();
|
||||
var currentScale = currentComponents.Scale;
|
||||
|
||||
// 重置到CAD原始状态
|
||||
doc.Models.ResetPermanentTransform(modelItems);
|
||||
|
||||
// 重新应用缩放
|
||||
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
||||
var components = identity.Factor();
|
||||
components.Scale = currentScale;
|
||||
|
||||
Transform3D newTransform = components.Combine();
|
||||
doc.Models.OverridePermanentTransform(modelItems, newTransform, false);
|
||||
|
||||
@ -280,18 +272,15 @@ namespace NavisworksTransport.Core
|
||||
_currentWidthMeters = widthMeters;
|
||||
_currentHeightMeters = heightMeters;
|
||||
|
||||
// 获取当前位置和旋转
|
||||
// 应用新缩放
|
||||
ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
|
||||
|
||||
var currentTransform = _virtualObjectModelItem.Transform;
|
||||
var currentComponents = currentTransform.Factor();
|
||||
var currentTranslation = currentComponents.Translation;
|
||||
var currentRotation = currentComponents.Rotation;
|
||||
|
||||
// 应用新缩放
|
||||
ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
|
||||
|
||||
// 如果之前有位置/旋转,重新应用
|
||||
bool hasTranslation = currentTranslation.X != 0 || currentTranslation.Y != 0 || currentTranslation.Z != 0;
|
||||
// 使用 ToAxisAndAngle 检查是否有旋转
|
||||
var rotationResult = currentRotation.ToAxisAndAngle();
|
||||
bool hasRotation = Math.Abs(rotationResult.Angle) > 0.001;
|
||||
if (hasTranslation || hasRotation)
|
||||
@ -300,7 +289,7 @@ namespace NavisworksTransport.Core
|
||||
var newComponents = newTransform.Factor();
|
||||
newComponents.Translation = currentTranslation;
|
||||
newComponents.Rotation = currentRotation;
|
||||
|
||||
|
||||
Transform3D combinedTransform = newComponents.Combine();
|
||||
var doc = Application.ActiveDocument;
|
||||
var modelItems = new ModelItemCollection { _virtualObjectModelItem };
|
||||
@ -332,7 +321,6 @@ namespace NavisworksTransport.Core
|
||||
lengthMeters / baseSizeMeters,
|
||||
widthMeters / baseSizeMeters,
|
||||
heightMeters / baseSizeMeters);
|
||||
|
||||
Transform3D newTransform = transformComponents.Combine();
|
||||
doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, _virtualObjectModel.Units, newTransform, false);
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Autodesk.Navisworks.Api;
|
||||
|
||||
namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
@ -13,6 +14,9 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
private static readonly ConcurrentDictionary<string, string> DocumentFragmentDefaultUpAxes =
|
||||
new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private static readonly ConcurrentDictionary<int, string> RuntimeDocumentKeys =
|
||||
new ConcurrentDictionary<int, string>();
|
||||
|
||||
public static string GetCurrentDocumentKey(Document doc = null)
|
||||
{
|
||||
doc = doc ?? Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||||
@ -21,12 +25,39 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(doc.FileName))
|
||||
int runtimeDocumentId = RuntimeHelpers.GetHashCode(doc);
|
||||
return GetOrCreateDocumentKey(runtimeDocumentId, doc.FileName, doc.Title);
|
||||
}
|
||||
|
||||
internal static string GetOrCreateDocumentKey(int runtimeDocumentId, string fileName, string title)
|
||||
{
|
||||
if (runtimeDocumentId <= 0)
|
||||
{
|
||||
return doc.FileName;
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return doc.Title ?? string.Empty;
|
||||
if (RuntimeDocumentKeys.TryGetValue(runtimeDocumentId, out string existingKey) &&
|
||||
!string.IsNullOrWhiteSpace(existingKey))
|
||||
{
|
||||
return existingKey;
|
||||
}
|
||||
|
||||
string resolvedKey;
|
||||
if (!string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
resolvedKey = $"file:{fileName}";
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
resolvedKey = $"runtime:{runtimeDocumentId}|title:{title}";
|
||||
}
|
||||
else
|
||||
{
|
||||
resolvedKey = $"runtime:{runtimeDocumentId}";
|
||||
}
|
||||
|
||||
RuntimeDocumentKeys[runtimeDocumentId] = resolvedKey;
|
||||
return resolvedKey;
|
||||
}
|
||||
|
||||
public static string GetCurrentHostUpAxis(Document doc = null)
|
||||
@ -86,5 +117,11 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
return string.Equals(axisName, "Y", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(axisName, "Z", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
internal static void ResetForTests()
|
||||
{
|
||||
DocumentFragmentDefaultUpAxes.Clear();
|
||||
RuntimeDocumentKeys.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,6 +179,21 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
}
|
||||
|
||||
public Rotation3D FromCanonicalRotation(Quaternion canonicalRotation)
|
||||
{
|
||||
return FromCanonicalRotationLegacy(canonicalRotation);
|
||||
}
|
||||
|
||||
public Rotation3D FromCanonicalRotationLegacy(Quaternion canonicalRotation)
|
||||
{
|
||||
Matrix4x4 canonicalLinear = Matrix4x4.CreateFromQuaternion(canonicalRotation);
|
||||
Matrix4x4 hostLinear = FromCanonicalLinearTransform(canonicalLinear);
|
||||
return CreateRotationFromLinearComponents(
|
||||
hostLinear.M11, hostLinear.M12, hostLinear.M13,
|
||||
hostLinear.M21, hostLinear.M22, hostLinear.M23,
|
||||
hostLinear.M31, hostLinear.M32, hostLinear.M33);
|
||||
}
|
||||
|
||||
public Rotation3D FromCanonicalRotationDirect(Quaternion canonicalRotation)
|
||||
{
|
||||
Matrix4x4 canonicalLinear = Matrix4x4.CreateFromQuaternion(canonicalRotation);
|
||||
Matrix4x4 hostLinear = FromCanonicalLinearTransform(canonicalLinear);
|
||||
@ -196,6 +211,20 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
/// 这里不做任何轴交换、符号翻转或重新解释,只负责把已经确定好的宿主四元数写入 Navisworks。
|
||||
/// </summary>
|
||||
public Rotation3D FromHostQuaternion(Quaternion hostRotation)
|
||||
{
|
||||
return FromHostQuaternionLegacy(hostRotation);
|
||||
}
|
||||
|
||||
public Rotation3D FromHostQuaternionLegacy(Quaternion hostRotation)
|
||||
{
|
||||
Matrix4x4 hostLinear = Matrix4x4.CreateFromQuaternion(hostRotation);
|
||||
return CreateRotationFromLinearComponents(
|
||||
hostLinear.M11, hostLinear.M12, hostLinear.M13,
|
||||
hostLinear.M21, hostLinear.M22, hostLinear.M23,
|
||||
hostLinear.M31, hostLinear.M32, hostLinear.M33);
|
||||
}
|
||||
|
||||
public Rotation3D FromHostQuaternionDirect(Quaternion hostRotation)
|
||||
{
|
||||
Quaternion normalizedHostRotation = Quaternion.Normalize(hostRotation);
|
||||
return new Rotation3D(
|
||||
@ -276,6 +305,53 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
return Quaternion.Normalize(qz * qy * qx);
|
||||
}
|
||||
|
||||
private static Rotation3D CreateRotationFromLinearComponents(
|
||||
double m00, double m01, double m02,
|
||||
double m10, double m11, double m12,
|
||||
double m20, double m21, double m22)
|
||||
{
|
||||
double qx;
|
||||
double qy;
|
||||
double qz;
|
||||
double qw;
|
||||
|
||||
double trace = m00 + m11 + m22;
|
||||
if (trace > 0.0)
|
||||
{
|
||||
double s = Math.Sqrt(trace + 1.0) * 2.0;
|
||||
qw = 0.25 * s;
|
||||
qx = (m21 - m12) / s;
|
||||
qy = (m02 - m20) / s;
|
||||
qz = (m10 - m01) / s;
|
||||
}
|
||||
else if (m00 > m11 && m00 > m22)
|
||||
{
|
||||
double s = Math.Sqrt(1.0 + m00 - m11 - m22) * 2.0;
|
||||
qw = (m21 - m12) / s;
|
||||
qx = 0.25 * s;
|
||||
qy = (m01 + m10) / s;
|
||||
qz = (m02 + m20) / s;
|
||||
}
|
||||
else if (m11 > m22)
|
||||
{
|
||||
double s = Math.Sqrt(1.0 + m11 - m00 - m22) * 2.0;
|
||||
qw = (m02 - m20) / s;
|
||||
qx = (m01 + m10) / s;
|
||||
qy = 0.25 * s;
|
||||
qz = (m12 + m21) / s;
|
||||
}
|
||||
else
|
||||
{
|
||||
double s = Math.Sqrt(1.0 + m22 - m00 - m11) * 2.0;
|
||||
qw = (m10 - m01) / s;
|
||||
qx = (m02 + m20) / s;
|
||||
qy = (m12 + m21) / s;
|
||||
qz = 0.25 * s;
|
||||
}
|
||||
|
||||
return new Rotation3D(qx, qy, qz, qw);
|
||||
}
|
||||
|
||||
public CanonicalBounds3 ToCanonicalBounds3(CanonicalBounds3 hostBounds)
|
||||
{
|
||||
return RebuildBounds3(hostBounds, ToCanonicalPoint3);
|
||||
|
||||
@ -589,6 +589,41 @@ namespace NavisworksTransport.Utils
|
||||
LogIncrementalTransformActual(item, targetPosition, targetRotation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取物体当前实际几何姿态。
|
||||
/// 优先使用 ModelGeometry.ActiveTransform,因为 ModelItem.Transform 只反映原始设计变换。
|
||||
/// </summary>
|
||||
public static bool TryGetCurrentGeometryRotation(ModelItem item, out Rotation3D rotation)
|
||||
{
|
||||
rotation = Rotation3D.Identity;
|
||||
if (item == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ModelGeometry geometry = item.FindFirstGeometry() ?? item.Geometry;
|
||||
if (geometry?.ActiveTransform == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rotation = geometry.ActiveTransform.Factor().Rotation;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[当前几何姿态] 读取 ActiveTransform 失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogCurrentGeometryTransformsForDebug(ModelItem item, string prefix)
|
||||
{
|
||||
LogGeometryLevelTransforms(item, prefix);
|
||||
}
|
||||
|
||||
private static void LogIncrementalTransformActual(ModelItem item, Point3D targetPosition, Rotation3D targetRotation)
|
||||
{
|
||||
try
|
||||
@ -614,6 +649,8 @@ namespace NavisworksTransport.Utils
|
||||
$"期望X=({targetLinear.Get(0, 0):F4},{targetLinear.Get(1, 0):F4},{targetLinear.Get(2, 0):F4}), " +
|
||||
$"期望Y=({targetLinear.Get(0, 1):F4},{targetLinear.Get(1, 1):F4},{targetLinear.Get(2, 1):F4}), " +
|
||||
$"期望Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})");
|
||||
|
||||
LogGeometryLevelTransforms(item, "[模型增量姿态应用后][GeometryAPI]");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -792,19 +829,21 @@ namespace NavisworksTransport.Utils
|
||||
var doc = Application.ActiveDocument;
|
||||
var modelItems = new ModelItemCollection { item };
|
||||
Vector3D currentScale = new Vector3D(1, 1, 1);
|
||||
ModelGeometry geometry = item.FindFirstGeometry() ?? item.Geometry;
|
||||
|
||||
if (preserveCurrentScale)
|
||||
{
|
||||
var currentComponents = item.Transform.Factor();
|
||||
var currentBaseTransform = geometry?.PermanentOverrideTransform ?? item.Transform;
|
||||
var currentComponents = currentBaseTransform.Factor();
|
||||
currentScale = currentComponents.Scale;
|
||||
}
|
||||
|
||||
doc.Models.ResetPermanentTransform(modelItems);
|
||||
|
||||
var originalTransform = item.Transform;
|
||||
var originalTransform = geometry?.OriginalTransform ?? item.Transform;
|
||||
var originalComponents = originalTransform.Factor();
|
||||
var originalRotation = originalComponents.Rotation;
|
||||
var originalBounds = item.BoundingBox();
|
||||
var originalBounds = geometry?.BoundingBox ?? item.BoundingBox();
|
||||
var originalCenterPos = originalBounds.Center;
|
||||
|
||||
Rotation3D deltaRotation = BuildDeltaRotation(originalRotation, targetRotation);
|
||||
@ -840,6 +879,7 @@ namespace NavisworksTransport.Utils
|
||||
|
||||
LogAbsoluteTransformDiagnostics(item, originalRotation, targetRotation, deltaRotation, originalCenterPos, targetPosition, translation);
|
||||
doc.Models.OverridePermanentTransform(modelItems, transform, false);
|
||||
LogAbsoluteTransformActual(item, preserveCurrentScale, currentScale, targetPosition, targetRotation);
|
||||
}
|
||||
|
||||
private static Rotation3D BuildDeltaRotation(Rotation3D originalRotation, Rotation3D targetRotation)
|
||||
@ -893,6 +933,108 @@ namespace NavisworksTransport.Utils
|
||||
}
|
||||
}
|
||||
|
||||
private static void LogAbsoluteTransformActual(
|
||||
ModelItem item,
|
||||
bool preserveCurrentScale,
|
||||
Vector3D currentScale,
|
||||
Point3D targetPosition,
|
||||
Rotation3D targetRotation)
|
||||
{
|
||||
try
|
||||
{
|
||||
Transform3D actualTransform = item.Transform;
|
||||
var actualComponents = actualTransform.Factor();
|
||||
var actualRotation = actualComponents.Rotation;
|
||||
var actualScale = actualComponents.Scale;
|
||||
var actualLinear = new Transform3D(actualRotation).Linear;
|
||||
var targetLinear = new Transform3D(targetRotation).Linear;
|
||||
var actualBounds = item.BoundingBox();
|
||||
Point3D actualCenter = actualBounds?.Center ?? new Point3D(0, 0, 0);
|
||||
|
||||
LogManager.Info(
|
||||
$"[模型姿态应用后] {item.DisplayName} PreserveScale={preserveCurrentScale}, " +
|
||||
$"输入Scale=({currentScale.X:F4},{currentScale.Y:F4},{currentScale.Z:F4}), " +
|
||||
$"实际Scale=({actualScale.X:F4},{actualScale.Y:F4},{actualScale.Z:F4}), " +
|
||||
$"目标中心=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3}), " +
|
||||
$"实际中心=({actualCenter.X:F3},{actualCenter.Y:F3},{actualCenter.Z:F3})");
|
||||
|
||||
LogManager.Info(
|
||||
$"[模型姿态应用后] {item.DisplayName} 目标Transform轴: " +
|
||||
$"X=({targetLinear.Get(0, 0):F4},{targetLinear.Get(1, 0):F4},{targetLinear.Get(2, 0):F4}), " +
|
||||
$"Y=({targetLinear.Get(0, 1):F4},{targetLinear.Get(1, 1):F4},{targetLinear.Get(2, 1):F4}), " +
|
||||
$"Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})");
|
||||
|
||||
LogManager.Info(
|
||||
$"[模型姿态应用后] {item.DisplayName} 实际Transform轴: " +
|
||||
$"X=({actualLinear.Get(0, 0):F4},{actualLinear.Get(1, 0):F4},{actualLinear.Get(2, 0):F4}), " +
|
||||
$"Y=({actualLinear.Get(0, 1):F4},{actualLinear.Get(1, 1):F4},{actualLinear.Get(2, 1):F4}), " +
|
||||
$"Z=({actualLinear.Get(0, 2):F4},{actualLinear.Get(1, 2):F4},{actualLinear.Get(2, 2):F4})");
|
||||
|
||||
LogGeometryLevelTransforms(item, "[模型姿态应用后][GeometryAPI]");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[模型姿态应用后] 输出诊断日志失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void LogGeometryLevelTransforms(ModelItem item, string prefix)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ModelGeometry geometry = item.FindFirstGeometry() ?? item.Geometry;
|
||||
if (geometry == null)
|
||||
{
|
||||
LogManager.Info($"{prefix} Geometry=null");
|
||||
return;
|
||||
}
|
||||
|
||||
LogManager.Info(
|
||||
$"{prefix} GeometryType={geometry.GetType().FullName}, " +
|
||||
$"FragmentCount={geometry.FragmentCount}, " +
|
||||
$"BoundsCenter=({geometry.BoundingBox.Center.X:F3},{geometry.BoundingBox.Center.Y:F3},{geometry.BoundingBox.Center.Z:F3})");
|
||||
|
||||
LogTransformProperty(geometry.OriginalTransform, $"{prefix} OriginalTransform");
|
||||
LogTransformProperty(geometry.PermanentOverrideTransform, $"{prefix} PermanentOverrideTransform");
|
||||
LogTransformProperty(geometry.PermanentTransform, $"{prefix} PermanentTransform");
|
||||
LogTransformProperty(geometry.ActiveTransform, $"{prefix} ActiveTransform");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"{prefix} 读取几何层当前变换失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void LogTransformProperty(Transform3D transform, string prefix)
|
||||
{
|
||||
if (transform == null)
|
||||
{
|
||||
LogManager.Info($"{prefix}=null");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var linear = transform.Linear;
|
||||
var components = transform.Factor();
|
||||
LogManager.Info(
|
||||
$"{prefix}: " +
|
||||
$"X=({linear.Get(0, 0):F4},{linear.Get(1, 0):F4},{linear.Get(2, 0):F4}), " +
|
||||
$"Y=({linear.Get(0, 1):F4},{linear.Get(1, 1):F4},{linear.Get(2, 1):F4}), " +
|
||||
$"Z=({linear.Get(0, 2):F4},{linear.Get(1, 2):F4},{linear.Get(2, 2):F4}), " +
|
||||
$"T=({components.Translation.X:F3},{components.Translation.Y:F3},{components.Translation.Z:F3})");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"{prefix} 读取失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将物体移动到指定位置和朝向,同时保持缩放比例
|
||||
/// 专为虚拟物体设计,避免缩放被覆盖
|
||||
|
||||
Loading…
Reference in New Issue
Block a user