Files
sbox-public/engine/Sandbox.Test.Unit/System/TransformTest.cs
Lorenz Junglas 5b069768da Fix transform equality regression from 7b853ce (#4504)
Fix transform update regression from 7b853ce

Rotation.AlmostEqual default threshold was too loose (0.0001 dot-product ~ 1.62° angular tolerance), causing SetLocalTransform to silently drop small transform updates.

- Rotation.AlmostEqual: default delta 0.0001 -> 0.0000001 (~0.05°), use MathF.Abs(Dot) to handle antipodal quaternions (q === -q)
- Transform.AlmostEqual: use Rotation's own default instead of passing the Position/Scale delta to a dot-product metric
2026-04-09 11:22:05 +00:00

153 lines
5.3 KiB
C#

namespace SystemTest;
[TestClass]
public class TransformTest
{
[TestInitialize]
public void SeedRandom()
{
SandboxSystem.SetRandomSeed( 0x1d655be6 );
}
[TestMethod]
public void ToLocalToWorld()
{
var parent = new Transform( SandboxSystem.Random.VectorInCube() * 100, Rotation.LookAt( SandboxSystem.Random.VectorInCube() ) );
var childWorld = new Transform( SandboxSystem.Random.VectorInCube() * 100, Rotation.LookAt( SandboxSystem.Random.VectorInCube() ) );
var childLocal = parent.ToLocal( childWorld );
var childWorldTest = parent.ToWorld( childLocal );
Assert.IsTrue( (childWorldTest.Position - childWorld.Position).Length < 0.0001f );
Assert.IsTrue( childWorldTest.Rotation.AlmostEqual( childWorld.Rotation, 0.0000004f ) );
Assert.IsTrue( childWorldTest.Scale.AlmostEqual( childWorld.Scale ) );
}
[TestMethod]
public void DefaultConstructorScaleIsOne()
{
var tx = new Transform();
Assert.IsTrue( tx.Scale == 1f );
}
[TestMethod]
public void ToLocalToWorld_UniformScale()
{
var parent = new Transform( SandboxSystem.Random.VectorInCube() * 100, Rotation.LookAt( SandboxSystem.Random.VectorInCube() ), 2 );
var childWorld = new Transform( SandboxSystem.Random.VectorInCube() * 100, Rotation.LookAt( SandboxSystem.Random.VectorInCube() ), 1 );
var childLocal = parent.ToLocal( childWorld );
var childWorldTest = parent.ToWorld( childLocal );
Assert.IsTrue( (childWorldTest.Position - childWorld.Position).Length < 0.0001f );
Assert.IsTrue( childWorldTest.Rotation.AlmostEqual( childWorld.Rotation, 0.0000004f ) );
Assert.IsTrue( childWorldTest.Scale.AlmostEqual( childWorld.Scale ) );
}
[TestMethod]
public void ToLocalToWorld_Scale()
{
var parent = new Transform( SandboxSystem.Random.VectorInCube() * 100, Rotation.LookAt( SandboxSystem.Random.VectorInCube() ), new Vector3( 1, 0.2f, 0.2f ) );
var childWorld = new Transform( SandboxSystem.Random.VectorInCube() * 100, Rotation.LookAt( SandboxSystem.Random.VectorInCube() ) );
var childLocal = parent.ToLocal( childWorld );
var childWorldTest = parent.ToWorld( childLocal );
Assert.IsTrue( childWorldTest.Rotation.AlmostEqual( childWorld.Rotation, 0.0000004f ) );
Assert.IsTrue( childWorldTest.Scale.AlmostEqual( childWorld.Scale ) );
Assert.IsTrue( (childWorldTest.Position - childWorld.Position).Length < 0.001f, $"{(childWorldTest.Position - childWorld.Position).Length}" );
}
[TestMethod]
[DataRow( 1.0f, 1.0f )]
[DataRow( 2.0f, 1.0f )]
[DataRow( 3.0f, 1.0f )]
[DataRow( 3.0f, 2.0f )]
[DataRow( 3.0f, 3.0f )]
[DataRow( 2.0f, 3.0f )]
[DataRow( 1.0f, 3.0f )]
[DataRow( 0.5f, 3.0f )]
[DataRow( 0.5f, 2.0f )]
[DataRow( 0.5f, 1.0f )]
[DataRow( 0.5f, 0.5f )]
public void ToLocalToWorldWithScale( float rootScale, float childScale )
{
var parent = new Transform( SandboxSystem.Random.VectorInCube() * 100, Rotation.LookAt( SandboxSystem.Random.VectorInCube() ), rootScale );
var childWorld = new Transform( SandboxSystem.Random.VectorInCube() * 100, Rotation.LookAt( SandboxSystem.Random.VectorInCube() ), childScale );
var childLocal = parent.ToLocal( childWorld );
var childWorldTest = parent.ToWorld( childLocal );
Assert.IsTrue( childWorldTest.Position == childWorld.Position );
Assert.IsTrue( childWorldTest.Rotation.AlmostEqual( childWorld.Rotation, 0.0000004f ) );
Assert.AreEqual( childWorldTest.Scale, childWorld.Scale );
}
[TestMethod]
public void PointToLocalWorld()
{
var point = SandboxSystem.Random.VectorInCube() * 100;
var parent = new Transform( SandboxSystem.Random.VectorInCube() * 100, Rotation.LookAt( SandboxSystem.Random.VectorInCube() ) );
var lp = parent.PointToLocal( point );
var wp = parent.PointToWorld( lp );
Assert.IsFalse( point == lp );
Assert.IsTrue( wp == point );
}
[TestMethod]
public void PointToLocalWorld_WithScale()
{
var point = new Vector3( 100, 100, 100 );
var parent = new Transform( new Vector3( 1000, 1000, 1000 ), new Angles( 45, 0, 0 ), new Vector3( 1, 0.5f, 0.25f ) );
var lp = parent.PointToLocal( point );
System.Console.WriteLine( $"To Local: {point} => {lp}" );
var wp = parent.PointToWorld( lp );
System.Console.WriteLine( $"To World: {lp} => {wp}" );
Assert.IsFalse( point == lp );
Assert.IsTrue( point.AlmostEqual( wp, 0.001f ), $"{point} doesn't equal {wp}" );
}
[TestMethod]
public void PointToWorld_ToWorld()
{
var parent = new Transform(
SandboxSystem.Random.VectorInCube() * 100,
Rotation.LookAt( SandboxSystem.Random.VectorInCube() ),
new Vector3( 1, 2, 0.5f )
);
var point = SandboxSystem.Random.VectorInCube() * 100;
var pointWorld = parent.PointToWorld( point );
var transformWorld = parent.ToWorld( new Transform( point ) ).Position;
Assert.IsTrue( pointWorld.AlmostEqual( transformWorld, 0.001f ), $"{pointWorld} does not match ToWorld result: {transformWorld}" );
}
[TestMethod]
public void PointToLocal_ToLocal()
{
var parent = new Transform(
SandboxSystem.Random.VectorInCube() * 100,
Rotation.LookAt( SandboxSystem.Random.VectorInCube() ),
new Vector3( 2, 1, 0.5f )
);
var point = SandboxSystem.Random.VectorInCube() * 100;
var pointLocal = parent.PointToLocal( point );
var transformLocal = parent.ToLocal( new Transform( point ) ).Position;
Assert.IsTrue( pointLocal.AlmostEqual( transformLocal, 0.001f ), $"{pointLocal} does not match ToLocal result: {transformLocal}" );
}
}