fix(N.5b T7): tighten conformance sample upper bound to 191.975f

Code review identified a latent false-positive flake risk: physics
path clamps fx = localX/24 to (CellsPerSide - 0.001f) = 7.999, which
corresponds to localX <= 191.976. With samples up to 191.999f,
physics computes Z at the clamped position while the mesh sampler
uses the actual position — a difference of up to 23 mm at the upper
edge, which on a steep slope would falsely trip the 1 mm sentinel.

Tighten upper bound to 191.975f (strictly below the clamp boundary)
so both oracles compute Z at the same (cellX, tx). Also restored the
"worst-case from SplitFormulaDivergenceTest" inline comment for
landblock 0x4D96 per code review suggestion #3.

Test still passes: 10/10 landblocks, 1000 samples, max |delta|
= 0.0153 mm (previously 0.0305 mm — confirms the prior worst-case
was indeed at the boundary).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-09 08:59:01 +02:00
parent e54d5ca2cf
commit 4ed79207a6

View file

@ -41,7 +41,7 @@ public class TerrainModernConformanceTests
("Mid-map 0x7F7F", 0x7F, 0x7F), ("Mid-map 0x7F7F", 0x7F, 0x7F),
("MapCorner 0xFEFE", 0xFE, 0xFE), ("MapCorner 0xFEFE", 0xFE, 0xFE),
("Subway outdoor 0x0185", 0x01, 0x85), ("Subway outdoor 0x0185", 0x01, 0x85),
("North continent 0x4D96", 0x4D, 0x96), ("North continent 0x4D96", 0x4D, 0x96), // worst-case landblock from SplitFormulaDivergenceTest
}; };
[Fact] [Fact]
@ -102,14 +102,19 @@ public class TerrainModernConformanceTests
var surfaceCache = new Dictionary<uint, SurfaceInfo>(); var surfaceCache = new Dictionary<uint, SurfaceInfo>();
var meshData = LandblockMesh.Build(landblock, lbX, lbY, heightTable, ctx, surfaceCache); var meshData = LandblockMesh.Build(landblock, lbX, lbY, heightTable, ctx, surfaceCache);
// Sample 100 (localX, localY) points uniformly in [0, 192). // Sample 100 (localX, localY) points uniformly in [0, 191.975].
// We avoid the exact upper bound (192) because that maps to // The physics path clamps fx = localX/24 to (CellsPerSide - 0.001f)
// cell index 8 which the physics path clamps; the pure mesh // = 7.999, which corresponds to localX <= 7.999 * 24 = 191.976.
// sampler doesn't have triangles past 192 anyway. // Sampling beyond that boundary makes physics compute Z at the
// clamped position while the mesh sampler uses the actual
// position — a difference of up to 23 mm at the upper edge,
// which on a steep slope would falsely trip the 1 mm sentinel.
// Stay strictly below the clamp boundary so both oracles
// compute Z at the same (cellX, tx).
for (int s = 0; s < 100; s++) for (int s = 0; s < 100; s++)
{ {
float lx = (float)rng.NextDouble() * 191.999f; float lx = (float)rng.NextDouble() * 191.975f;
float ly = (float)rng.NextDouble() * 191.999f; float ly = (float)rng.NextDouble() * 191.975f;
float meshZ = SampleMeshZ(meshData, lx, ly); float meshZ = SampleMeshZ(meshData, lx, ly);
float physicsZ = TerrainSurface.SampleZFromHeightmap( float physicsZ = TerrainSurface.SampleZFromHeightmap(