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\PathTargetFrameResolverTests.cs" />
|
||||||
<Compile Include="UnitTests\CoordinateSystem\AssemblyEndFaceAnalyzerTests.cs" />
|
<Compile Include="UnitTests\CoordinateSystem\AssemblyEndFaceAnalyzerTests.cs" />
|
||||||
<Compile Include="UnitTests\CoordinateSystem\AssemblyInstallationReferenceBuilderTests.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" />
|
<Compile Include="UnitTests\Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -32,3 +32,4 @@ using System.Runtime.InteropServices;
|
|||||||
// 主版本和次版本手动维护,Build和Revision在编译时自动更新
|
// 主版本和次版本手动维护,Build和Revision在编译时自动更新
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
[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>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<Link>resources\unit_cube.nwc</Link>
|
<Link>resources\unit_cube.nwc</Link>
|
||||||
</None>
|
</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 -->
|
<!-- Unit Cylinder NWC Model File -->
|
||||||
<None Include="resources\unit_cylinder.nwc">
|
<None Include="resources\unit_cylinder.nwc">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<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. 重置内部跟踪变量(同步到原始状态)
|
// 2. 重置内部跟踪变量(同步到原始状态)
|
||||||
// 注意:ResetPermanentTransform 后物体的 Transform 属性会自动变回原始值
|
// 注意: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();
|
var originalBoundingBox = objectToRestore.BoundingBox();
|
||||||
_trackedPosition = GetTrackedObjectPosition(objectToRestore);
|
_trackedPosition = GetTrackedObjectPosition(objectToRestore);
|
||||||
@ -652,7 +661,16 @@ namespace NavisworksTransport.Core.Animation
|
|||||||
_originalTransform = animatedObject.Transform;
|
_originalTransform = animatedObject.Transform;
|
||||||
_originalCenter = animatedObject.BoundingBox().Center;
|
_originalCenter = animatedObject.BoundingBox().Center;
|
||||||
_trackedPosition = GetTrackedObjectPosition(animatedObject);
|
_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(
|
LogManager.Info(
|
||||||
$"[移动到起点] 更新动画对象内部状态: 对象={animatedObject.DisplayName}, 虚拟物体={IsVirtualObjectMode}");
|
$"[移动到起点] 更新动画对象内部状态: 对象={animatedObject.DisplayName}, 虚拟物体={IsVirtualObjectMode}");
|
||||||
@ -2452,75 +2470,89 @@ namespace NavisworksTransport.Core.Animation
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool isRailRealObject = _route?.PathType == PathType.Rail && IsRealObjectMode && _animatedObject != null;
|
ModelItem controlledObject = CurrentControlledObject;
|
||||||
if (IsVirtualObjectMode)
|
bool isRailRealObject = _route?.PathType == PathType.Rail && IsRealObjectMode && controlledObject != null;
|
||||||
{
|
if (controlledObject == null)
|
||||||
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
|
|
||||||
{
|
{
|
||||||
return;
|
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;
|
_trackedPosition = newPosition;
|
||||||
_trackedRotation = newRotation;
|
_trackedRotation = appliedTargetRotation;
|
||||||
_hasTrackedRotation = true;
|
_hasTrackedRotation = true;
|
||||||
_currentYaw = ModelItemTransformHelper.GetYawFromRotation(newRotation);
|
_currentYaw = ModelItemTransformHelper.GetYawFromRotation(appliedTargetRotation);
|
||||||
|
|
||||||
if (isRailRealObject)
|
if (isRailRealObject)
|
||||||
{
|
{
|
||||||
Point3D hostActualAfter = GetTrackedObjectPosition(_animatedObject);
|
Point3D hostActualAfter = GetTrackedObjectPosition(controlledObject);
|
||||||
LogManager.Debug(
|
LogManager.Debug(
|
||||||
$"[Rail姿态应用] 宿主最终跟踪中心=({hostActualAfter.X:F3},{hostActualAfter.Y:F3},{hostActualAfter.Z:F3}), " +
|
$"[Rail姿态应用] 宿主最终跟踪中心=({hostActualAfter.X:F3},{hostActualAfter.Y:F3},{hostActualAfter.Z:F3}), " +
|
||||||
$"目标跟踪中心=({newPosition.X:F3},{newPosition.Y:F3},{newPosition.Z:F3}), " +
|
$"目标跟踪中心=({newPosition.X:F3},{newPosition.Y:F3},{newPosition.Z:F3}), " +
|
||||||
@ -3782,7 +3814,7 @@ namespace NavisworksTransport.Core.Animation
|
|||||||
Quaternion composedHostQuaternion = adapter.ComposeHostQuaternion(
|
Quaternion composedHostQuaternion = adapter.ComposeHostQuaternion(
|
||||||
baselineHostQuaternion,
|
baselineHostQuaternion,
|
||||||
_objectRotationCorrection);
|
_objectRotationCorrection);
|
||||||
rotation = adapter.FromHostQuaternion(composedHostQuaternion);
|
rotation = adapter.FromHostQuaternionDirect(composedHostQuaternion);
|
||||||
|
|
||||||
Matrix4x4 baselineLinear = Matrix4x4.CreateFromQuaternion(baselineHostQuaternion);
|
Matrix4x4 baselineLinear = Matrix4x4.CreateFromQuaternion(baselineHostQuaternion);
|
||||||
Matrix4x4 composedLinear = Matrix4x4.CreateFromQuaternion(composedHostQuaternion);
|
Matrix4x4 composedLinear = Matrix4x4.CreateFromQuaternion(composedHostQuaternion);
|
||||||
@ -3958,6 +3990,7 @@ namespace NavisworksTransport.Core.Animation
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private bool TryCreateRealObjectPlanarRotationFromHostForward(Vector3 hostForward, out Rotation3D rotation)
|
private bool TryCreateRealObjectPlanarRotationFromHostForward(Vector3 hostForward, out Rotation3D rotation)
|
||||||
{
|
{
|
||||||
rotation = Rotation3D.Identity;
|
rotation = Rotation3D.Identity;
|
||||||
@ -3980,7 +4013,7 @@ namespace NavisworksTransport.Core.Animation
|
|||||||
|
|
||||||
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||||
Quaternion hostComposedQuaternion = adapter.ComposeHostQuaternion(solution.BaselineRotation, _objectRotationCorrection);
|
Quaternion hostComposedQuaternion = adapter.ComposeHostQuaternion(solution.BaselineRotation, _objectRotationCorrection);
|
||||||
rotation = adapter.FromHostQuaternion(hostComposedQuaternion);
|
rotation = adapter.FromHostQuaternionDirect(hostComposedQuaternion);
|
||||||
|
|
||||||
Matrix4x4 baselineLinear = Matrix4x4.CreateFromQuaternion(solution.BaselineRotation);
|
Matrix4x4 baselineLinear = Matrix4x4.CreateFromQuaternion(solution.BaselineRotation);
|
||||||
Matrix4x4 hostComposedLinear = Matrix4x4.CreateFromQuaternion(hostComposedQuaternion);
|
Matrix4x4 hostComposedLinear = Matrix4x4.CreateFromQuaternion(hostComposedQuaternion);
|
||||||
@ -4192,10 +4225,25 @@ namespace NavisworksTransport.Core.Animation
|
|||||||
return;
|
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))
|
if (!isVirtualObject && TryCaptureRealObjectReferenceRotation(sourceObject, out Quaternion referenceRotation))
|
||||||
{
|
{
|
||||||
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||||
_trackedRotation = adapter.FromHostQuaternion(referenceRotation);
|
_trackedRotation = adapter.FromHostQuaternionDirect(referenceRotation);
|
||||||
_hasTrackedRotation = true;
|
_hasTrackedRotation = true;
|
||||||
_currentYaw = ModelItemTransformHelper.GetYawFromRotation(_trackedRotation);
|
_currentYaw = ModelItemTransformHelper.GetYawFromRotation(_trackedRotation);
|
||||||
return;
|
return;
|
||||||
@ -4206,6 +4254,18 @@ namespace NavisworksTransport.Core.Animation
|
|||||||
_hasTrackedRotation = true;
|
_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()
|
private void ResetRealObjectReferenceRotation()
|
||||||
{
|
{
|
||||||
_realObjectReferenceRotation = Quaternion.Identity;
|
_realObjectReferenceRotation = Quaternion.Identity;
|
||||||
|
|||||||
@ -94,7 +94,6 @@ namespace NavisworksTransport.Core
|
|||||||
|
|
||||||
var doc = Application.ActiveDocument;
|
var doc = Application.ActiveDocument;
|
||||||
var unitCubePath = GetUnitCubeFilePath();
|
var unitCubePath = GetUnitCubeFilePath();
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(unitCubePath) || !File.Exists(unitCubePath))
|
if (string.IsNullOrEmpty(unitCubePath) || !File.Exists(unitCubePath))
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException($"找不到单位立方体文件: {unitCubePath}");
|
throw new FileNotFoundException($"找不到单位立方体文件: {unitCubePath}");
|
||||||
@ -213,9 +212,7 @@ namespace NavisworksTransport.Core
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
LogVirtualObjectGeometry("[虚拟物体姿态] 应用前");
|
LogVirtualObjectGeometry("[虚拟物体姿态] 应用前");
|
||||||
// 虚拟物体当前尺寸是通过永久变换中的缩放实现的。
|
ModelItemTransformHelper.MoveItemToPositionAndRotation(_virtualObjectModelItem, position, rotation);
|
||||||
// 应用完整姿态时必须保留当前缩放,否则会破坏虚拟物体的资产姿态和几何语义。
|
|
||||||
ModelItemTransformHelper.MoveItemToPositionAndRotationWithCurrentScale(_virtualObjectModelItem, position, rotation);
|
|
||||||
var actualBounds = _virtualObjectModelItem.BoundingBox();
|
var actualBounds = _virtualObjectModelItem.BoundingBox();
|
||||||
Point3D actualCenter = actualBounds?.Center ?? new Point3D(0, 0, 0);
|
Point3D actualCenter = actualBounds?.Center ?? new Point3D(0, 0, 0);
|
||||||
LogManager.Info(
|
LogManager.Info(
|
||||||
@ -239,20 +236,15 @@ namespace NavisworksTransport.Core
|
|||||||
{
|
{
|
||||||
var doc = Application.ActiveDocument;
|
var doc = Application.ActiveDocument;
|
||||||
var modelItems = new ModelItemCollection { _virtualObjectModelItem };
|
var modelItems = new ModelItemCollection { _virtualObjectModelItem };
|
||||||
|
|
||||||
// 获取当前缩放
|
|
||||||
var currentTransform = _virtualObjectModelItem.Transform;
|
var currentTransform = _virtualObjectModelItem.Transform;
|
||||||
var currentComponents = currentTransform.Factor();
|
var currentComponents = currentTransform.Factor();
|
||||||
var currentScale = currentComponents.Scale;
|
var currentScale = currentComponents.Scale;
|
||||||
|
|
||||||
// 重置到CAD原始状态
|
|
||||||
doc.Models.ResetPermanentTransform(modelItems);
|
doc.Models.ResetPermanentTransform(modelItems);
|
||||||
|
|
||||||
// 重新应用缩放
|
|
||||||
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
||||||
var components = identity.Factor();
|
var components = identity.Factor();
|
||||||
components.Scale = currentScale;
|
components.Scale = currentScale;
|
||||||
|
|
||||||
Transform3D newTransform = components.Combine();
|
Transform3D newTransform = components.Combine();
|
||||||
doc.Models.OverridePermanentTransform(modelItems, newTransform, false);
|
doc.Models.OverridePermanentTransform(modelItems, newTransform, false);
|
||||||
|
|
||||||
@ -280,18 +272,15 @@ namespace NavisworksTransport.Core
|
|||||||
_currentWidthMeters = widthMeters;
|
_currentWidthMeters = widthMeters;
|
||||||
_currentHeightMeters = heightMeters;
|
_currentHeightMeters = heightMeters;
|
||||||
|
|
||||||
// 获取当前位置和旋转
|
// 应用新缩放
|
||||||
|
ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
|
||||||
|
|
||||||
var currentTransform = _virtualObjectModelItem.Transform;
|
var currentTransform = _virtualObjectModelItem.Transform;
|
||||||
var currentComponents = currentTransform.Factor();
|
var currentComponents = currentTransform.Factor();
|
||||||
var currentTranslation = currentComponents.Translation;
|
var currentTranslation = currentComponents.Translation;
|
||||||
var currentRotation = currentComponents.Rotation;
|
var currentRotation = currentComponents.Rotation;
|
||||||
|
|
||||||
// 应用新缩放
|
|
||||||
ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
|
|
||||||
|
|
||||||
// 如果之前有位置/旋转,重新应用
|
|
||||||
bool hasTranslation = currentTranslation.X != 0 || currentTranslation.Y != 0 || currentTranslation.Z != 0;
|
bool hasTranslation = currentTranslation.X != 0 || currentTranslation.Y != 0 || currentTranslation.Z != 0;
|
||||||
// 使用 ToAxisAndAngle 检查是否有旋转
|
|
||||||
var rotationResult = currentRotation.ToAxisAndAngle();
|
var rotationResult = currentRotation.ToAxisAndAngle();
|
||||||
bool hasRotation = Math.Abs(rotationResult.Angle) > 0.001;
|
bool hasRotation = Math.Abs(rotationResult.Angle) > 0.001;
|
||||||
if (hasTranslation || hasRotation)
|
if (hasTranslation || hasRotation)
|
||||||
@ -332,7 +321,6 @@ namespace NavisworksTransport.Core
|
|||||||
lengthMeters / baseSizeMeters,
|
lengthMeters / baseSizeMeters,
|
||||||
widthMeters / baseSizeMeters,
|
widthMeters / baseSizeMeters,
|
||||||
heightMeters / baseSizeMeters);
|
heightMeters / baseSizeMeters);
|
||||||
|
|
||||||
Transform3D newTransform = transformComponents.Combine();
|
Transform3D newTransform = transformComponents.Combine();
|
||||||
doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, _virtualObjectModel.Units, newTransform, false);
|
doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, _virtualObjectModel.Units, newTransform, false);
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using Autodesk.Navisworks.Api;
|
using Autodesk.Navisworks.Api;
|
||||||
|
|
||||||
namespace NavisworksTransport.Utils.CoordinateSystem
|
namespace NavisworksTransport.Utils.CoordinateSystem
|
||||||
@ -13,6 +14,9 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
|||||||
private static readonly ConcurrentDictionary<string, string> DocumentFragmentDefaultUpAxes =
|
private static readonly ConcurrentDictionary<string, string> DocumentFragmentDefaultUpAxes =
|
||||||
new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
private static readonly ConcurrentDictionary<int, string> RuntimeDocumentKeys =
|
||||||
|
new ConcurrentDictionary<int, string>();
|
||||||
|
|
||||||
public static string GetCurrentDocumentKey(Document doc = null)
|
public static string GetCurrentDocumentKey(Document doc = null)
|
||||||
{
|
{
|
||||||
doc = doc ?? Autodesk.Navisworks.Api.Application.ActiveDocument;
|
doc = doc ?? Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||||||
@ -21,12 +25,39 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
|||||||
return string.Empty;
|
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)
|
public static string GetCurrentHostUpAxis(Document doc = null)
|
||||||
@ -86,5 +117,11 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
|||||||
return string.Equals(axisName, "Y", StringComparison.OrdinalIgnoreCase) ||
|
return string.Equals(axisName, "Y", StringComparison.OrdinalIgnoreCase) ||
|
||||||
string.Equals(axisName, "Z", 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)
|
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 canonicalLinear = Matrix4x4.CreateFromQuaternion(canonicalRotation);
|
||||||
Matrix4x4 hostLinear = FromCanonicalLinearTransform(canonicalLinear);
|
Matrix4x4 hostLinear = FromCanonicalLinearTransform(canonicalLinear);
|
||||||
@ -196,6 +211,20 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
|||||||
/// 这里不做任何轴交换、符号翻转或重新解释,只负责把已经确定好的宿主四元数写入 Navisworks。
|
/// 这里不做任何轴交换、符号翻转或重新解释,只负责把已经确定好的宿主四元数写入 Navisworks。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Rotation3D FromHostQuaternion(Quaternion hostRotation)
|
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);
|
Quaternion normalizedHostRotation = Quaternion.Normalize(hostRotation);
|
||||||
return new Rotation3D(
|
return new Rotation3D(
|
||||||
@ -276,6 +305,53 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
|||||||
return Quaternion.Normalize(qz * qy * qx);
|
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)
|
public CanonicalBounds3 ToCanonicalBounds3(CanonicalBounds3 hostBounds)
|
||||||
{
|
{
|
||||||
return RebuildBounds3(hostBounds, ToCanonicalPoint3);
|
return RebuildBounds3(hostBounds, ToCanonicalPoint3);
|
||||||
|
|||||||
@ -589,6 +589,41 @@ namespace NavisworksTransport.Utils
|
|||||||
LogIncrementalTransformActual(item, targetPosition, targetRotation);
|
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)
|
private static void LogIncrementalTransformActual(ModelItem item, Point3D targetPosition, Rotation3D targetRotation)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -614,6 +649,8 @@ namespace NavisworksTransport.Utils
|
|||||||
$"期望X=({targetLinear.Get(0, 0):F4},{targetLinear.Get(1, 0):F4},{targetLinear.Get(2, 0):F4}), " +
|
$"期望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}), " +
|
$"期望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})");
|
$"期望Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})");
|
||||||
|
|
||||||
|
LogGeometryLevelTransforms(item, "[模型增量姿态应用后][GeometryAPI]");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -792,19 +829,21 @@ namespace NavisworksTransport.Utils
|
|||||||
var doc = Application.ActiveDocument;
|
var doc = Application.ActiveDocument;
|
||||||
var modelItems = new ModelItemCollection { item };
|
var modelItems = new ModelItemCollection { item };
|
||||||
Vector3D currentScale = new Vector3D(1, 1, 1);
|
Vector3D currentScale = new Vector3D(1, 1, 1);
|
||||||
|
ModelGeometry geometry = item.FindFirstGeometry() ?? item.Geometry;
|
||||||
|
|
||||||
if (preserveCurrentScale)
|
if (preserveCurrentScale)
|
||||||
{
|
{
|
||||||
var currentComponents = item.Transform.Factor();
|
var currentBaseTransform = geometry?.PermanentOverrideTransform ?? item.Transform;
|
||||||
|
var currentComponents = currentBaseTransform.Factor();
|
||||||
currentScale = currentComponents.Scale;
|
currentScale = currentComponents.Scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.Models.ResetPermanentTransform(modelItems);
|
doc.Models.ResetPermanentTransform(modelItems);
|
||||||
|
|
||||||
var originalTransform = item.Transform;
|
var originalTransform = geometry?.OriginalTransform ?? item.Transform;
|
||||||
var originalComponents = originalTransform.Factor();
|
var originalComponents = originalTransform.Factor();
|
||||||
var originalRotation = originalComponents.Rotation;
|
var originalRotation = originalComponents.Rotation;
|
||||||
var originalBounds = item.BoundingBox();
|
var originalBounds = geometry?.BoundingBox ?? item.BoundingBox();
|
||||||
var originalCenterPos = originalBounds.Center;
|
var originalCenterPos = originalBounds.Center;
|
||||||
|
|
||||||
Rotation3D deltaRotation = BuildDeltaRotation(originalRotation, targetRotation);
|
Rotation3D deltaRotation = BuildDeltaRotation(originalRotation, targetRotation);
|
||||||
@ -840,6 +879,7 @@ namespace NavisworksTransport.Utils
|
|||||||
|
|
||||||
LogAbsoluteTransformDiagnostics(item, originalRotation, targetRotation, deltaRotation, originalCenterPos, targetPosition, translation);
|
LogAbsoluteTransformDiagnostics(item, originalRotation, targetRotation, deltaRotation, originalCenterPos, targetPosition, translation);
|
||||||
doc.Models.OverridePermanentTransform(modelItems, transform, false);
|
doc.Models.OverridePermanentTransform(modelItems, transform, false);
|
||||||
|
LogAbsoluteTransformActual(item, preserveCurrentScale, currentScale, targetPosition, targetRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Rotation3D BuildDeltaRotation(Rotation3D originalRotation, Rotation3D 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>
|
/// <summary>
|
||||||
/// 将物体移动到指定位置和朝向,同时保持缩放比例
|
/// 将物体移动到指定位置和朝向,同时保持缩放比例
|
||||||
/// 专为虚拟物体设计,避免缩放被覆盖
|
/// 专为虚拟物体设计,避免缩放被覆盖
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user