diff --git a/UnitTests/Utils/BoundingBoxGeometryUtilsTests.cs b/UnitTests/Utils/BoundingBoxGeometryUtilsTests.cs new file mode 100644 index 0000000..a0bc7c2 --- /dev/null +++ b/UnitTests/Utils/BoundingBoxGeometryUtilsTests.cs @@ -0,0 +1,400 @@ +using System; +using Autodesk.Navisworks.Api; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NavisworksTransport.Utils; + +namespace NavisworksTransport.UnitTests.Utils +{ + /// + /// BoundingBoxGeometryUtils 的单元测试 + /// 重点验证旋转后包围盒中心偏移计算的数学正确性 + /// 使用大尺寸和夸张比例,确保结果清晰可见 + /// + [TestClass] + public class BoundingBoxGeometryUtilsTests + { + #region CalculateRotatedBoundingBoxCenterOffset Tests - 大尺寸非对称包围盒 + + /// + /// X方向超长的长方体(类似长沙发/长桌)绕Z轴旋转90度 + /// 尺寸: 10000 x 100 x 50 + /// 预期: X方向大偏移 + /// + [TestMethod] + public void CalculateRotatedBoundingBoxCenterOffset_LongX_Rotate90AroundZ_LargeXOffset() + { + // X方向从 -5000 到 +5000(总长10000) + // Y方向从 -50 到 +50(总长100) + // Z方向从 -25 到 +25(总长50) + var bounds = new BoundingBox3D( + new Point3D(-5000, -50, -25), + new Point3D(5000, 50, 25)); + + // 原中心: (0, 0, 0) + // 8角点: (±5000, ±50, ±25) + + // 绕Z轴旋转90度 (x'=-y, y'=x): + // (5000, 50) -> (-50, 5000) + // (5000, -50) -> (50, 5000) + // (-5000, 50) -> (-50, -5000) + // (-5000, -50) -> (50, -5000) + // + // 新X范围: [-50, 50](来自原Y范围) + // 新Y范围: [-5000, 5000](来自原X范围) + // 新中心: (0, 0, 0) + // 偏移: (0, 0, 0) - 等等,这不应该是0 + + // 啊不对,让我重新算: + // 原中心是 (0, 0, 0) + // 旋转后各角点分布在 [-50,50]x[-5000,5000]x[-25,25] + // 新AABB中心仍然是 (0, 0, 0) + // 所以偏移应该是 0? + + // 等等,问题在于原中心是 (0,0,0),所以新中心也是 (0,0,0) + // 我需要测试非对称的,即原中心不在原点的情况 + + // 重新设计:让包围盒中心不在原点 + // Min=(-1000, -50, -25), Max=(9000, 50, 25) + // 中心: (4000, 0, 0) + bounds = new BoundingBox3D( + new Point3D(-1000, -50, -25), + new Point3D(9000, 50, 25)); + + var rotation = new Rotation3D(new UnitVector3D(0, 0, 1), Math.PI / 2); + + var offset = BoundingBoxGeometryUtils.CalculateRotatedBoundingBoxCenterOffset(bounds, rotation); + + // 原中心: (4000, 0, 0) + // 8角点相对中心: (-5000,±50,±25), (5000,±50,±25) + // + // 旋转90度后: + // (-5000, 50) -> (-50, -5000) + // (-5000, -50) -> (50, -5000) + // (5000, 50) -> (-50, 5000) + // (5000, -50) -> (50, 5000) + // + // 新X范围: [-50, 50] + // 新Y范围: [-5000, 5000] + // 新中心: (0, 0, 0) + // 偏移: (0-4000, 0-0, 0-0) = (-4000, 0, 0) + + Console.WriteLine($"LongX-Rotate90: offset=({offset.X:F1}, {offset.Y:F1}, {offset.Z:F1})"); + + Assert.AreEqual(-4000, offset.X, 1, "X方向大偏移应为-4000"); + Assert.AreEqual(0, offset.Y, 1, "Y偏移应为0"); + Assert.AreEqual(0, offset.Z, 0.1, "Z偏移应为0"); + } + + /// + /// Y方向超长的长方体绕X轴旋转90度 + /// 尺寸: 100 x 10000 x 50 + /// + [TestMethod] + public void CalculateRotatedBoundingBoxCenterOffset_LongY_Rotate90AroundX_LargeYOffset() + { + // Y方向从 -1000 到 +9000(总长10000,中心在4000) + // X方向从 -50 到 +50(总长100) + // Z方向从 -25 到 +25(总长50) + var bounds = new BoundingBox3D( + new Point3D(-50, -1000, -25), + new Point3D(50, 9000, 25)); + + // 绕X轴旋转90度 (y'=-z, z'=y) + var rotation = new Rotation3D(new UnitVector3D(1, 0, 0), Math.PI / 2); + + var offset = BoundingBoxGeometryUtils.CalculateRotatedBoundingBoxCenterOffset(bounds, rotation); + + // 原中心: (0, 4000, 0) + // 8角点相对中心: (±50, -5000, ±25), (±50, 5000, ±25) + // + // 旋转90度后: + // y' = -z, z' = y + // (50, -5000, 25) -> (50, -25, -5000) + // (50, -5000, -25) -> (50, 25, -5000) + // (50, 5000, 25) -> (50, -25, 5000) + // (50, 5000, -25) -> (50, 25, 5000) + // (-50, -5000, 25) -> (-50, -25, -5000) + // (-50, -5000, -25) -> (-50, 25, -5000) + // (-50, 5000, 25) -> (-50, -25, 5000) + // (-50, 5000, -25) -> (-50, 25, 5000) + // + // 新X范围: [-50, 50] + // 新Y范围: [-25, 25] + // 新Z范围: [-5000, 5000] + // 新中心: (0, 0, 0) + // 偏移: (0-0, 0-4000, 0-0) = (0, -4000, 0) + + Console.WriteLine($"LongY-Rotate90: offset=({offset.X:F1}, {offset.Y:F1}, {offset.Z:F1})"); + + Assert.AreEqual(0, offset.X, 1, "X偏移应为0"); + Assert.AreEqual(-4000, offset.Y, 1, "Y方向大偏移应为-4000"); + Assert.AreEqual(0, offset.Z, 0.1, "Z偏移应为0"); + } + + /// + /// Z方向超长的长方体绕Y轴旋转90度 + /// 尺寸: 100 x 100 x 10000 + /// + [TestMethod] + public void CalculateRotatedBoundingBoxCenterOffset_LongZ_Rotate90AroundY_LargeZOffset() + { + // Z方向从 -1000 到 +9000(总长10000,中心在4000) + // X方向从 -50 到 +50(总长100) + // Y方向从 -50 到 +50(总长100) + var bounds = new BoundingBox3D( + new Point3D(-50, -50, -1000), + new Point3D(50, 50, 9000)); + + // 绕Y轴旋转90度 (z'=x, x'=-z) - 等等,让我确认旋转矩阵 + // 绕Y轴旋转θ: x'=x*cosθ+z*sinθ, z'=-x*sinθ+z*cosθ + // 90度时: x'=z, z'=-x + var rotation = new Rotation3D(new UnitVector3D(0, 1, 0), Math.PI / 2); + + var offset = BoundingBoxGeometryUtils.CalculateRotatedBoundingBoxCenterOffset(bounds, rotation); + + // 原中心: (0, 0, 4000) + // 8角点相对中心: (±50,±50,-5000), (±50,±50,5000) + // + // 旋转90度后 (x'=z, z'=-x): + // (50, 50, -5000) -> (-5000, 50, -50) + // (50, 50, 5000) -> (5000, 50, -50) + // (-50, 50, -5000) -> (-5000, 50, 50) + // (-50, 50, 5000) -> (5000, 50, 50) + // 以及Y=-50的四个点... + // + // 新X范围: [-5000, 5000] + // 新Y范围: [-50, 50] + // 新Z范围: [-50, 50] + // 新中心: (0, 0, 0) + // 偏移: (0-0, 0-0, 0-4000) = (0, 0, -4000) + + Console.WriteLine($"LongZ-Rotate90: offset=({offset.X:F1}, {offset.Y:F1}, {offset.Z:F1})"); + + Assert.AreEqual(0, offset.X, 1, "X偏移应为0"); + Assert.AreEqual(0, offset.Y, 0.1, "Y偏移应为0"); + Assert.AreEqual(-4000, offset.Z, 1, "Z方向大偏移应为-4000"); + } + + /// + /// 三轴都不同比例的长方体绕Z轴旋转45度 + /// 尺寸: 1000 x 200 x 100 + /// + [TestMethod] + public void CalculateRotatedBoundingBoxCenterOffset_AsymmetricXYZ_Rotate45AroundZ_MultipleOffsets() + { + // X: -500 ~ +500 (中心0,但非对称也可测) + // Y: -100 ~ +100 (中心0) + // Z: -50 ~ +50 (中心0) + // 等等,中心0的话偏移还是0... + + // 改用非中心对称的 + // X: -100 ~ +900 (总长1000,中心400) + // Y: -80 ~ +120 (总长200,中心20) + // Z: -30 ~ +70 (总长100,中心20) + var bounds = new BoundingBox3D( + new Point3D(-100, -80, -30), + new Point3D(900, 120, 70)); + + // 绕Z轴旋转45度 + var rotation = new Rotation3D(new UnitVector3D(0, 0, 1), Math.PI / 4); + + var offset = BoundingBoxGeometryUtils.CalculateRotatedBoundingBoxCenterOffset(bounds, rotation); + + // 原中心: (400, 20, 20) + // + // 8角点相对中心: + // (-500,-100,-50), (500,-100,-50), (-500,100,-50), (500,100,-50) + // (-500,-100,50), (500,-100,50), (-500,100,50), (500,100,50) + // + // 旋转45度后 (x'=(x-y)/√2, y'=(x+y)/√2): + // (-500,-100) -> (-400/√2, -600/√2) ≈ (-283, -424) + // (500,-100) -> (600/√2, 400/√2) ≈ (424, 283) + // (-500,100) -> (-600/√2, -400/√2) ≈ (-424, -283) + // (500,100) -> (400/√2, 600/√2) ≈ (283, 424) + // + // 新X范围: [-424, 424] + // 新Y范围: [-424, 424] + // 新中心: (0, 0, 20) - Z不变 + // 偏移: (0-400, 0-20, 20-20) = (-400, -20, 0) + + Console.WriteLine($"Asymmetric-Rotate45: offset=({offset.X:F1}, {offset.Y:F1}, {offset.Z:F1})"); + + // 允许一定误差(因为手动计算是近似值) + Assert.IsTrue(Math.Abs(offset.X - (-400)) < 50, $"X偏移应约-400,实际是{offset.X:F1}"); + Assert.IsTrue(Math.Abs(offset.Y - (-20)) < 50, $"Y偏移应约-20,实际是{offset.Y:F1}"); + Assert.AreEqual(0, offset.Z, 0.1, "Z偏移应为0"); + } + + /// + /// 实际沙发案例放大版 + /// 模拟沙发: 长2000,宽100,高80,中心偏移 + /// + [TestMethod] + public void CalculateRotatedBoundingBoxCenterOffset_BigSofa_Rotate90AroundZ_RealisticCase() + { + // 沙发包围盒放大版 + // X: -200 ~ +1800 (总长2000,中心800) + // Y: -50 ~ +50 (总长100,中心0) + // Z: 0 ~ +80 (总高80,中心40) + var bounds = new BoundingBox3D( + new Point3D(-200, -50, 0), + new Point3D(1800, 50, 80)); + + var rotation = new Rotation3D(new UnitVector3D(0, 0, 1), Math.PI / 2); + + var offset = BoundingBoxGeometryUtils.CalculateRotatedBoundingBoxCenterOffset(bounds, rotation); + + // 原中心: (800, 0, 40) + // + // 8角点相对中心: + // (-1000,-50,-40), (1000,-50,-40), (-1000,50,-40), (1000,50,-40) + // (-1000,-50,40), (1000,-50,40), (-1000,50,40), (1000,50,40) + // + // 旋转90度后 (x'=-y, y'=x): + // (-1000,-50) -> (50, -1000) + // (1000,-50) -> (50, 1000) + // (-1000,50) -> (-50, -1000) + // (1000,50) -> (-50, 1000) + // + // 新X范围: [-50, 50] + // 新Y范围: [-1000, 1000] + // 新中心: (0, 0, 40) - Z不变 + // 偏移: (0-800, 0-0, 40-40) = (-800, 0, 0) + + Console.WriteLine($"BigSofa-Rotate90: offset=({offset.X:F1}, {offset.Y:F1}, {offset.Z:F1})"); + + Assert.AreEqual(-800, offset.X, 10, "X方向偏移应为-800"); + Assert.AreEqual(0, offset.Y, 10, "Y偏移应为0"); + Assert.AreEqual(0, offset.Z, 0.1, "Z偏移应为0"); + } + + /// + /// X方向超长绕Z轴旋转45度 - 测试非90度旋转 + /// 尺寸: 10000 x 100 x 50 + /// + [TestMethod] + public void CalculateRotatedBoundingBoxCenterOffset_LongX_Rotate45AroundZ_DiagonalOffset() + { + // X方向从 -1000 到 +9000(总长10000,中心4000) + // Y方向从 -50 到 +50(总长100) + var bounds = new BoundingBox3D( + new Point3D(-1000, -50, -25), + new Point3D(9000, 50, 25)); + + // 绕Z轴旋转45度 + var rotation = new Rotation3D(new UnitVector3D(0, 0, 1), Math.PI / 4); + + var offset = BoundingBoxGeometryUtils.CalculateRotatedBoundingBoxCenterOffset(bounds, rotation); + + // 原中心: (4000, 0, 0) + // 8角点相对中心: (-5000,±50,±25), (5000,±50,±25) + // + // 旋转45度后 (x'=(x-y)/√2, y'=(x+y)/√2): + // (-5000, 50) -> (-5050/√2, -4950/√2) ≈ (-3571, -3500) + // (-5000, -50) -> (-4950/√2, -5050/√2) ≈ (-3500, -3571) + // (5000, 50) -> (4950/√2, 5050/√2) ≈ (3500, 3571) + // (5000, -50) -> (5050/√2, 4950/√2) ≈ (3571, 3500) + // + // 新X范围: [-3571, 3571] + // 新Y范围: [-3571, 3571] + // 新中心: (0, 0, 0) + // 偏移: (0-4000, 0-0, 0-0) = (-4000, 0, 0) + + Console.WriteLine($"LongX-Rotate45: offset=({offset.X:F1}, {offset.Y:F1}, {offset.Z:F1})"); + + // X方向应该有较大负偏移(接近-4000) + Assert.IsTrue(offset.X < -3500, $"45度旋转后X偏移应小于-3500,实际是{offset.X:F1}"); + Assert.IsTrue(offset.X > -4500, $"45度旋转后X偏移应大于-4500,实际是{offset.X:F1}"); + // Y方向偏移应该很小(因为原Y中心是0) + Assert.IsTrue(Math.Abs(offset.Y) < 100, $"45度旋转后Y偏移应接近0,实际是{offset.Y:F1}"); + Assert.AreEqual(0, offset.Z, 0.1, "Z偏移应为0"); + } + + /// + /// XY方向都非对称的长方体绕Z轴旋转45度 - 测试双向偏移 + /// 尺寸: 8000 x 4000 x 100 + /// + [TestMethod] + public void CalculateRotatedBoundingBoxCenterOffset_XYBothAsymmetric_Rotate45AroundZ_BothOffsets() + { + // X方向从 -3000 到 +5000(总长8000,中心2000) + // Y方向从 -1000 到 +3000(总长4000,中心1000) + var bounds = new BoundingBox3D( + new Point3D(-3000, -1000, -50), + new Point3D(5000, 3000, 50)); + + // 绕Z轴旋转45度 + var rotation = new Rotation3D(new UnitVector3D(0, 0, 1), Math.PI / 4); + + var offset = BoundingBoxGeometryUtils.CalculateRotatedBoundingBoxCenterOffset(bounds, rotation); + + // 原中心: (2000, 1000, 0) + // 8角点相对中心: (-5000,-2000,±50), (3000,-2000,±50), (-5000,2000,±50), (3000,2000,±50) + // + // 旋转45度后: + // (-5000, -2000) -> (-3000/√2, -7000/√2) ≈ (-2121, -4950) + // (3000, -2000) -> (5000/√2, 1000/√2) ≈ (3536, 707) + // (-5000, 2000) -> (-7000/√2, -3000/√2) ≈ (-4950, -2121) + // (3000, 2000) -> (1000/√2, 5000/√2) ≈ (707, 3536) + // + // 新X范围: [-4950, 3536] + // 新Y范围: [-4950, 3536] + // 新中心: ((-4950+3536)/2, (-4950+3536)/2, 0) = (-707, -707, 0) + // 偏移: (-707-2000, -707-1000, 0-0) = (-2707, -1707, 0) + + Console.WriteLine($"XY-Asymmetric-Rotate45: offset=({offset.X:F1}, {offset.Y:F1}, {offset.Z:F1})"); + + // X方向应该有较大负偏移 + Assert.IsTrue(offset.X < -2000, $"45度旋转后X偏移应小于-2000,实际是{offset.X:F1}"); + Assert.IsTrue(offset.X > -3500, $"45度旋转后X偏移应大于-3500,实际是{offset.X:F1}"); + // Y方向也应该有负偏移,但比X小 + Assert.IsTrue(offset.Y < -1000, $"45度旋转后Y偏移应小于-1000,实际是{offset.Y:F1}"); + Assert.IsTrue(offset.Y > -2500, $"45度旋转后Y偏移应大于-2500,实际是{offset.Y:F1}"); + Assert.AreEqual(0, offset.Z, 0.1, "Z偏移应为0"); + } + + /// + /// 零旋转验证 - 无论什么形状,不旋转偏移都应为0 + /// + [TestMethod] + public void CalculateRotatedBoundingBoxCenterOffset_NoRotation_ZeroOffset() + { + var bounds = new BoundingBox3D( + new Point3D(-1000, -100, -50), + new Point3D(9000, 100, 50)); + + var rotation = Rotation3D.Identity; + + var offset = BoundingBoxGeometryUtils.CalculateRotatedBoundingBoxCenterOffset(bounds, rotation); + + Console.WriteLine($"No-Rotation: offset=({offset.X:F1}, {offset.Y:F1}, {offset.Z:F1})"); + + Assert.AreEqual(0, offset.X, 1e-6, "无旋转时X偏移应为0"); + Assert.AreEqual(0, offset.Y, 1e-6, "无旋转时Y偏移应为0"); + Assert.AreEqual(0, offset.Z, 1e-6, "无旋转时Z偏移应为0"); + } + + /// + /// 180度旋转验证 - 包围盒应该不变(只是翻转) + /// + [TestMethod] + public void CalculateRotatedBoundingBoxCenterOffset_180DegreeRotation_ZeroOffset() + { + var bounds = new BoundingBox3D( + new Point3D(-1000, -100, -50), + new Point3D(9000, 100, 50)); + + var rotation = new Rotation3D(new UnitVector3D(0, 0, 1), Math.PI); + + var offset = BoundingBoxGeometryUtils.CalculateRotatedBoundingBoxCenterOffset(bounds, rotation); + + Console.WriteLine($"180-Rotation: offset=({offset.X:F1}, {offset.Y:F1}, {offset.Z:F1})"); + + Assert.AreEqual(0, offset.X, 1e-6, "180度旋转后X偏移应为0"); + Assert.AreEqual(0, offset.Y, 1e-6, "180度旋转后Y偏移应为0"); + Assert.AreEqual(0, offset.Z, 1e-6, "180度旋转后Z偏移应为0"); + } + + #endregion + } +} diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs index 5c49d14..61754d8 100644 --- a/src/Core/Animation/PathAnimationManager.cs +++ b/src/Core/Animation/PathAnimationManager.cs @@ -5562,7 +5562,15 @@ namespace NavisworksTransport.Core.Animation if (frameData.HasCustomRotation) { - UpdateObjectPosition(frameData.Position, frameData.Rotation); + // 🔥 Ground路径动画播放:使用 _trackedPosition 计算增量,不重新读取包围盒 + if (_route?.PathType == PathType.Ground && IsRealObjectMode) + { + ApplyGroundAnimationFrame(frameData.Position, frameData.Rotation); + } + else + { + UpdateObjectPosition(frameData.Position, frameData.Rotation); + } } else if (_route?.PathType == PathType.Ground || _route?.PathType == PathType.Hoisting) { @@ -5575,6 +5583,33 @@ namespace NavisworksTransport.Core.Animation } } + /// + /// 专门用于Ground路径动画帧播放。使用 _trackedPosition 作为当前位置,不重新读取包围盒。 + /// + private void ApplyGroundAnimationFrame(Point3D targetPosition, Rotation3D targetRotation) + { + var controlledObject = CurrentControlledObject; + if (controlledObject == null) return; + + // 使用 _trackedPosition 作为当前位置(几何中心),不重新读取包围盒 + Point3D currentPosition = _trackedPosition; + Rotation3D currentRotation = _hasTrackedRotation ? _trackedRotation : Rotation3D.Identity; + + // 简单增量变换,不使用包围盒 + ModelItemTransformHelper.MoveItemIncrementallyToPositionAndRotation( + controlledObject, + currentPosition, + currentRotation, + targetPosition, + targetRotation); + + // 更新跟踪状态 + _trackedPosition = targetPosition; + _trackedRotation = targetRotation; + _hasTrackedRotation = true; + _currentYaw = ModelItemTransformHelper.GetYawFromRotation(targetRotation); + } + /// /// 更新FPS计数器(DispatcherTimer模式) diff --git a/src/Utils/BoundingBoxGeometryUtils.cs b/src/Utils/BoundingBoxGeometryUtils.cs index 8c29671..3fd3d42 100644 --- a/src/Utils/BoundingBoxGeometryUtils.cs +++ b/src/Utils/BoundingBoxGeometryUtils.cs @@ -272,5 +272,66 @@ namespace NavisworksTransport.Utils new Point3D(minX, minY, minZ), new Point3D(maxX, maxY, maxZ)); } + + /// + /// 计算包围盒旋转后,新AABB中心相对于原中心的偏移。 + /// 这是解决"旋转后BoundingBox中心漂移"问题的核心数学方法。 + /// + /// [备注] 此方法仅供参考,当前代码中未使用。保留作为数学工具方法。 + /// + /// 原始包围盒 + /// 旋转(世界坐标系) + /// 新AABB中心相对于原中心的偏移量 + public static Vector3D CalculateRotatedBoundingBoxCenterOffset(BoundingBox3D originalBounds, Rotation3D rotation) + { + // 原包围盒中心 + Point3D originalCenter = new Point3D( + (originalBounds.Min.X + originalBounds.Max.X) / 2.0, + (originalBounds.Min.Y + originalBounds.Max.Y) / 2.0, + (originalBounds.Min.Z + originalBounds.Max.Z) / 2.0); + + // 旋转矩阵 + var linear = new Transform3D(rotation).Linear; + + // 原始包围盒的8个角点(相对于原中心) + var corners = new[] + { + new Point3D(originalBounds.Min.X - originalCenter.X, originalBounds.Min.Y - originalCenter.Y, originalBounds.Min.Z - originalCenter.Z), + new Point3D(originalBounds.Max.X - originalCenter.X, originalBounds.Min.Y - originalCenter.Y, originalBounds.Min.Z - originalCenter.Z), + new Point3D(originalBounds.Min.X - originalCenter.X, originalBounds.Max.Y - originalCenter.Y, originalBounds.Min.Z - originalCenter.Z), + new Point3D(originalBounds.Max.X - originalCenter.X, originalBounds.Max.Y - originalCenter.Y, originalBounds.Min.Z - originalCenter.Z), + new Point3D(originalBounds.Min.X - originalCenter.X, originalBounds.Min.Y - originalCenter.Y, originalBounds.Max.Z - originalCenter.Z), + new Point3D(originalBounds.Max.X - originalCenter.X, originalBounds.Min.Y - originalCenter.Y, originalBounds.Max.Z - originalCenter.Z), + new Point3D(originalBounds.Min.X - originalCenter.X, originalBounds.Max.Y - originalCenter.Y, originalBounds.Max.Z - originalCenter.Z), + new Point3D(originalBounds.Max.X - originalCenter.X, originalBounds.Max.Y - originalCenter.Y, originalBounds.Max.Z - originalCenter.Z) + }; + + // 计算旋转后的角点,并求新AABB + double minX = double.MaxValue, minY = double.MaxValue, minZ = double.MaxValue; + double maxX = double.MinValue, maxY = double.MinValue, maxZ = double.MinValue; + + foreach (var corner in corners) + { + // 应用旋转 + double rx = linear.Get(0, 0) * corner.X + linear.Get(0, 1) * corner.Y + linear.Get(0, 2) * corner.Z; + double ry = linear.Get(1, 0) * corner.X + linear.Get(1, 1) * corner.Y + linear.Get(1, 2) * corner.Z; + double rz = linear.Get(2, 0) * corner.X + linear.Get(2, 1) * corner.Y + linear.Get(2, 2) * corner.Z; + + minX = Math.Min(minX, rx); + minY = Math.Min(minY, ry); + minZ = Math.Min(minZ, rz); + maxX = Math.Max(maxX, rx); + maxY = Math.Max(maxY, ry); + maxZ = Math.Max(maxZ, rz); + } + + // 新AABB中心(相对于原中心) + Vector3D newCenterOffset = new Vector3D( + (minX + maxX) / 2.0, + (minY + maxY) / 2.0, + (minZ + maxZ) / 2.0); + + return newCenterOffset; + } } } \ No newline at end of file