feat(core): Phase B.3 — PortalPlane (plane math + crossing detection)
Adds the foundational portal-plane record for cell transition detection. PortalPlane.FromVertices computes a normalised plane from 3 coplanar polygon vertices via cross product + dot product; IsCrossing tests whether a movement vector straddles the plane (strictly negative dot-product product — exact-on-plane position returns false as specified). 4 new unit tests: normal construction, opposite-side crossing, same-side no-crossing, start-on-plane no-crossing. All 269 tests green. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e4f3f6bfab
commit
cb46d892d5
2 changed files with 112 additions and 0 deletions
67
tests/AcDream.Core.Tests/Physics/PortalPlaneTests.cs
Normal file
67
tests/AcDream.Core.Tests/Physics/PortalPlaneTests.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.Physics;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.Core.Tests.Physics;
|
||||
|
||||
public class PortalPlaneTests
|
||||
{
|
||||
// Helper: build an XY-plane portal (z = 0) from three known vertices.
|
||||
private static PortalPlane XyPlane() =>
|
||||
PortalPlane.FromVertices(
|
||||
new Vector3(0, 0, 0),
|
||||
new Vector3(1, 0, 0),
|
||||
new Vector3(0, 1, 0),
|
||||
targetCellId: 42,
|
||||
ownerCellId: 7,
|
||||
flags: 0);
|
||||
|
||||
[Fact]
|
||||
public void FromVertices_ComputesCorrectNormal()
|
||||
{
|
||||
var plane = XyPlane();
|
||||
|
||||
// Cross((1,0,0)-(0,0,0), (0,1,0)-(0,0,0)) = Cross((1,0,0),(0,1,0)) = (0,0,1)
|
||||
// The normalised result must be either (0,0,1) or (0,0,-1).
|
||||
Assert.Equal(0f, plane.Normal.X, precision: 5);
|
||||
Assert.Equal(0f, plane.Normal.Y, precision: 5);
|
||||
Assert.Equal(1f, MathF.Abs(plane.Normal.Z), precision: 5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsCrossing_PositionsOnOppositeSides_ReturnsTrue()
|
||||
{
|
||||
var plane = XyPlane(); // normal is (0,0,±1), D = 0
|
||||
|
||||
// One position above the XY plane, one below.
|
||||
var above = new Vector3(0, 0, 1f);
|
||||
var below = new Vector3(0, 0, -1f);
|
||||
|
||||
Assert.True(plane.IsCrossing(above, below));
|
||||
Assert.True(plane.IsCrossing(below, above));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsCrossing_PositionsOnSameSide_ReturnsFalse()
|
||||
{
|
||||
var plane = XyPlane();
|
||||
|
||||
var pos1 = new Vector3(0, 0, 1f);
|
||||
var pos2 = new Vector3(0, 0, 2f);
|
||||
|
||||
Assert.False(plane.IsCrossing(pos1, pos2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsCrossing_StartOnPlane_ReturnsFalse()
|
||||
{
|
||||
var plane = XyPlane();
|
||||
|
||||
// oldPos is exactly on the plane (z = 0) → distance = 0.
|
||||
// Product (0 * anything) == 0, which is NOT < 0 → no crossing.
|
||||
var onPlane = new Vector3(0.5f, 0.5f, 0f);
|
||||
var above = new Vector3(0.5f, 0.5f, 1f);
|
||||
|
||||
Assert.False(plane.IsCrossing(onPlane, above));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue