Projects an XY point onto a cell's floor polygons via brute-force triangle iteration + barycentric Z interpolation. Fan-triangulates quads and larger polygons. Returns null when outside all floor surfaces. Accepts pre-transformed world-space vertex positions so the caller handles EnvCell coordinate transforms. Second component of the physics collision engine. 4 new tests, all green. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
90 lines
2.7 KiB
C#
90 lines
2.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Numerics;
|
|
using AcDream.Core.Physics;
|
|
using Xunit;
|
|
|
|
namespace AcDream.Core.Tests.Physics;
|
|
|
|
public class CellSurfaceTests
|
|
{
|
|
/// <summary>
|
|
/// Build a minimal CellSurface representing a flat square floor
|
|
/// centered at (originX, originY) with the given half-size and Z.
|
|
/// The floor polygon is a quad: 4 vertices at the corners.
|
|
/// </summary>
|
|
private static CellSurface MakeFlatFloor(
|
|
uint cellId, float originX, float originY, float z,
|
|
float halfSize = 10f)
|
|
{
|
|
// 4 vertices forming a square floor at the given Z, in WORLD space.
|
|
var vertices = new Dictionary<ushort, Vector3>
|
|
{
|
|
[0] = new(originX - halfSize, originY - halfSize, z),
|
|
[1] = new(originX + halfSize, originY - halfSize, z),
|
|
[2] = new(originX + halfSize, originY + halfSize, z),
|
|
[3] = new(originX - halfSize, originY + halfSize, z),
|
|
};
|
|
|
|
// One quad polygon with 4 vertex IDs.
|
|
var polygonVertexIds = new List<List<short>>
|
|
{
|
|
new() { 0, 1, 2, 3 },
|
|
};
|
|
|
|
return new CellSurface(cellId, vertices, polygonVertexIds);
|
|
}
|
|
|
|
[Fact]
|
|
public void SampleFloorZ_InsideFlat_ReturnsZ()
|
|
{
|
|
var surface = MakeFlatFloor(0x0100, originX: 50f, originY: 50f, z: 10f);
|
|
|
|
float? z = surface.SampleFloorZ(50f, 50f);
|
|
|
|
Assert.NotNull(z);
|
|
Assert.Equal(10f, z!.Value, precision: 2);
|
|
}
|
|
|
|
[Fact]
|
|
public void SampleFloorZ_OutsideFloor_ReturnsNull()
|
|
{
|
|
var surface = MakeFlatFloor(0x0100, originX: 50f, originY: 50f, z: 10f, halfSize: 5f);
|
|
|
|
float? z = surface.SampleFloorZ(100f, 100f);
|
|
|
|
Assert.Null(z);
|
|
}
|
|
|
|
[Fact]
|
|
public void SampleFloorZ_AtEdge_ReturnsZ()
|
|
{
|
|
var surface = MakeFlatFloor(0x0100, originX: 50f, originY: 50f, z: 10f, halfSize: 10f);
|
|
|
|
// Right at the edge of the polygon.
|
|
float? z = surface.SampleFloorZ(60f, 50f);
|
|
|
|
Assert.NotNull(z);
|
|
Assert.Equal(10f, z!.Value, precision: 2);
|
|
}
|
|
|
|
[Fact]
|
|
public void SampleFloorZ_SlopedFloor_InterpolatesZ()
|
|
{
|
|
// A triangular floor that slopes from Z=0 to Z=20.
|
|
var vertices = new Dictionary<ushort, Vector3>
|
|
{
|
|
[0] = new(0f, 0f, 0f),
|
|
[1] = new(20f, 0f, 0f),
|
|
[2] = new(10f, 20f, 20f),
|
|
};
|
|
var polygons = new List<List<short>> { new() { 0, 1, 2 } };
|
|
var surface = new CellSurface(0x0100, vertices, polygons);
|
|
|
|
// At the centroid (10, 6.67): Z should be roughly 6.67
|
|
float? z = surface.SampleFloorZ(10f, 6.67f);
|
|
|
|
Assert.NotNull(z);
|
|
Assert.InRange(z!.Value, 5f, 8f); // approximate
|
|
}
|
|
}
|