NavisworksTransport/UnitTests/Utils/BoundingBoxGeometryUtilsTests.cs

401 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using Autodesk.Navisworks.Api;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NavisworksTransport.Utils;
namespace NavisworksTransport.UnitTests.Utils
{
/// <summary>
/// BoundingBoxGeometryUtils 的单元测试
/// 重点验证旋转后包围盒中心偏移计算的数学正确性
/// 使用大尺寸和夸张比例,确保结果清晰可见
/// </summary>
[TestClass]
public class BoundingBoxGeometryUtilsTests
{
#region CalculateRotatedBoundingBoxCenterOffset Tests -
/// <summary>
/// X方向超长的长方体类似长沙发/长桌绕Z轴旋转90度
/// 尺寸: 10000 x 100 x 50
/// 预期: X方向大偏移
/// </summary>
[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");
}
/// <summary>
/// Y方向超长的长方体绕X轴旋转90度
/// 尺寸: 100 x 10000 x 50
/// </summary>
[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");
}
/// <summary>
/// Z方向超长的长方体绕Y轴旋转90度
/// 尺寸: 100 x 100 x 10000
/// </summary>
[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");
}
/// <summary>
/// 三轴都不同比例的长方体绕Z轴旋转45度
/// 尺寸: 1000 x 200 x 100
/// </summary>
[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");
}
/// <summary>
/// 实际沙发案例放大版
/// 模拟沙发: 长2000宽100高80中心偏移
/// </summary>
[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");
}
/// <summary>
/// X方向超长绕Z轴旋转45度 - 测试非90度旋转
/// 尺寸: 10000 x 100 x 50
/// </summary>
[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");
}
/// <summary>
/// XY方向都非对称的长方体绕Z轴旋转45度 - 测试双向偏移
/// 尺寸: 8000 x 4000 x 100
/// </summary>
[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");
}
/// <summary>
/// 零旋转验证 - 无论什么形状不旋转偏移都应为0
/// </summary>
[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");
}
/// <summary>
/// 180度旋转验证 - 包围盒应该不变(只是翻转)
/// </summary>
[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
}
}