fix(measure): measure the strate, not the band above its floor

The window excluded a whole chunk (32 voxels) at each end to avoid a
4-voxel seal band -- 8x too much. On 4-chunk strates that discarded half
of every strate, floor included, so the pass was measuring the open air
above the ground and calling it the strate.

The margin now derives from the seal that actually exists:
ceil(2 x BoundarySealThickness), clamped to [1, height/4], overridable
via InteriorMarginVoxels. The fixture resolves to 8 voxels.

FlatPlain/CrystalChamber walkable fraction: 0.00044 -> 0.0578, ~132x.
The "99.95% empty void" reading was the window, not the world.

Metrics now carry ResolvedMarginVoxels, SampledMinZ and SampledMaxZ, and
the test prints them. A measurement that does not state the volume it
measured is exactly how the previous defect survived review.

VerticalShafts largest-component share rose only 0.074 -> 0.119 across
57 components: the window explained part of the swiss-cheese signal, not
all of it. That one is at least partly real.

Coarse-lie guard still load-bearing: 24 routes checked, 3 refuted at full
resolution (was 21 / 2).

Build clean, 18 succeeded / 0 warnings / 0 failed, confirmed in
Saved/Logs/VoxelM.log rather than from the agent's summary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bp7FxoQcSoiuhxjuLr6aBM
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-08-30 13:46:28 +02:00
1 parent 6988e5ace1
commit 13399529b5
4 files changed
+245 -69

No files matched your search

@@ -26,6 +26,10 @@ namespace VoxelStrateMeasurePrivate
float ExtentY = 0.0f;
float ExtentZ = 0.0f;
int32 ResolvedMarginVoxels = 0;
int32 SampledMinZ = 0;
int32 SampledMaxZ = 0;
// 1 = air, 0 = solid. The polarity is deliberately explicit at the sampling site below.
TArray<uint8> Air;
@@ -114,14 +118,74 @@ namespace VoxelStrateMeasurePrivate
return Refuse(OutReason, TEXT("The requested strate has no positive vertical extent."));
}
// Exclude the complete top and bottom chunks. Those outer chunks contain the seal bands;
// the measurement is about the strate interior, not the boundary bedrock.
const int64 InteriorMinZ = (static_cast<int64>(Slot.BottomChunkZ) + 1) * CHUNK_SIZE;
const int64 InteriorMaxZ = static_cast<int64>(Slot.TopChunkZ) * CHUNK_SIZE;
const int64 StrateBottomZ = static_cast<int64>(Slot.BottomChunkZ) * CHUNK_SIZE;
const int64 StrateTopZ = (static_cast<int64>(Slot.TopChunkZ) + 1) * CHUNK_SIZE;
const int64 StrateHeight = StrateTopZ - StrateBottomZ;
if (StrateHeight <= 0)
{
return Refuse(OutReason, TEXT("The requested strate has no positive voxel height."));
}
// Resolve the margin from the same blended parameter query used by generation. The
// representative chunk is deliberately in the middle of this slot and uses the sample
// centre's XY chunk, so a future non-Hard transition resolves through the manager exactly
// as generation does at that representative location.
const int32 MidChunkZ = Slot.BottomChunkZ
+ (Slot.TopChunkZ - Slot.BottomChunkZ) / 2;
const FIntVector RepresentativeChunk(
FMath::FloorToInt(Settings.CenterXY.X / static_cast<float>(CHUNK_SIZE)),
FMath::FloorToInt(Settings.CenterXY.Y / static_cast<float>(CHUNK_SIZE)),
MidChunkZ);
const FStrateGenerationParams RepresentativeParams =
Manager.GetGenerationParams(RepresentativeChunk);
const int64 MaxMargin64 = FMath::Max<int64>(1, StrateHeight / 4);
const int32 MaxMargin = static_cast<int32>(
FMath::Min<int64>(MaxMargin64, static_cast<int64>(INT32_MAX)));
int32 RequestedMargin = Settings.InteriorMarginVoxels;
if (RequestedMargin < 0)
{
const float DoubleSealThickness = 2.0f * RepresentativeParams.BoundarySealThickness;
if (FMath::IsNaN(RepresentativeParams.BoundarySealThickness))
{
return Refuse(OutReason, TEXT("BoundarySealThickness is NaN; the interior margin cannot be derived."));
}
// Avoid converting an infinite value to int. A positive overflow is still an
// intentionally absurd seal request and therefore resolves to the safe maximum;
// negative infinity resolves to the minimum.
if (!FMath::IsFinite(DoubleSealThickness))
{
RequestedMargin = DoubleSealThickness > 0.0f ? MaxMargin : 1;
}
else if (DoubleSealThickness >= static_cast<float>(INT32_MAX))
{
RequestedMargin = MaxMargin;
}
else if (DoubleSealThickness <= static_cast<float>(INT32_MIN))
{
RequestedMargin = 1;
}
else
{
RequestedMargin = FMath::CeilToInt(DoubleSealThickness);
}
}
const int32 ResolvedMargin = FMath::Clamp(RequestedMargin, 1, MaxMargin);
const int64 InteriorMinZ = StrateBottomZ + static_cast<int64>(ResolvedMargin);
const int64 InteriorMaxZ = StrateTopZ - static_cast<int64>(ResolvedMargin);
const int64 InteriorHeight = InteriorMaxZ - InteriorMinZ;
if (InteriorHeight <= 0)
{
return Refuse(OutReason, TEXT("The strate is too short after excluding its top and bottom chunks."));
return Refuse(OutReason, TEXT("The strate has no measurable height after applying its interior margin."));
}
if (InteriorMinZ < static_cast<int64>(INT32_MIN)
|| InteriorMinZ > static_cast<int64>(INT32_MAX)
|| InteriorMaxZ < static_cast<int64>(INT32_MIN)
|| InteriorMaxZ > static_cast<int64>(INT32_MAX))
{
return Refuse(OutReason, TEXT("The sampled Z window is outside the metrics struct's int32 range."));
}
const int64 XYExtent = static_cast<int64>(Settings.RadiusInVoxels) * 2;
@@ -188,6 +252,9 @@ namespace VoxelStrateMeasurePrivate
OutGrid.ExtentX = MaxX - MinX;
OutGrid.ExtentY = MaxY - MinY;
OutGrid.ExtentZ = MaxZ - MinZ;
OutGrid.ResolvedMarginVoxels = ResolvedMargin;
OutGrid.SampledMinZ = static_cast<int32>(InteriorMinZ);
OutGrid.SampledMaxZ = static_cast<int32>(InteriorMaxZ);
OutGrid.Air.SetNumUninitialized(OutGrid.CellCount);
bool bSawNonFiniteDensity = false;
@@ -627,6 +694,9 @@ FVoxelStrateMetrics VF_MeasureStrate(
return Result;
}
Result.ResolvedMarginVoxels = Grid.ResolvedMarginVoxels;
Result.SampledMinZ = Grid.SampledMinZ;
Result.SampledMaxZ = Grid.SampledMaxZ;
VoxelStrateMeasurePrivate::BuildMetricsFromGrid(Grid, Settings, Result);
Result.bValid = Result.NumSampled > 0;
if (!Result.bValid)