test(measure): the VerticalShafts disconnection is real, not a coarse lie
Our guard was one-directional. FullResolutionPathIsAir catches the false POSITIVE -- coarse says joined, reality says walled -- which is the dangerous direction for safety, so we built it first. The opposite error was unguarded: a corridor narrower than SampleStep falls between sample points and vanishes, so connected regions read as separate. That is exactly what a network of narrow vertical shafts would fake at step 4. Refinement sweep, radius held constant for the controls: step 4 / r256 NOT_CONNECTED 57 comp largest .119 458,752 cells step 4 / r192 NOT_CONNECTED 32 comp largest .220 258,048 step 2 / r192 NOT_CONNECTED 27 comp largest .220 2,064,384 step 4 / r128 NOT_CONNECTED 20 comp largest .291 114,688 step 1 / r128 NOT_CONNECTED 11 comp largest .303 7,340,032 Both fixed-radius pairs stayed negative, so neither the step nor the radius explains it. At one-voxel sampling the arrival and departure mouths are still in different air components. VerticalShafts is a live primordial-law violation: a player descends into it and cannot reach its exit. NotConnected is renamed NotConnectedAtThisResolution and documents its own limit at the declaration, so a bare negative can never again be read as proof. CoarseLied names one direction; this names the other. Sweep split into VoxelForge.Generation.StrateConnectivityRefinement so the fast test stays fast. 4.4 s. The MaxCells refusal path was exercised and still refuses. Build clean, 19 succeeded / 0 warnings / 0 failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bp7FxoQcSoiuhxjuLr6aBM
This commit is contained in:
1 parent
efcc8fcb65
commit
cd31dec869
4 files changed
+252
-11
No files matched your search
+5
-3
@@ -312,13 +312,15 @@ representative point, cell count, and count of components holding at least 1% of
|
|||||||
`VF_AreConnected` recovers a deterministic BFS parent path from the same kind of grid and checks
|
`VF_AreConnected` recovers a deterministic BFS parent path from the same kind of grid and checks
|
||||||
only that route at full voxel resolution before returning `Connected`. It returns an explicit
|
only that route at full voxel resolution before returning `Connected`. It returns an explicit
|
||||||
`EVoxelConnectivityResult`; a solid endpoint cell is repaired by snapping to the nearest air cell
|
`EVoxelConnectivityResult`; a solid endpoint cell is repaired by snapping to the nearest air cell
|
||||||
among its 26 neighbours when possible, and reports the snap through its out flags. No UObject state,
|
among its 26 neighbours when possible, and reports the snap through its out flags. A missing coarse
|
||||||
cache, actor, world, or PIE is required.
|
route returns `NotConnectedAtThisResolution`: that is evidence only at the grid's resolution,
|
||||||
|
because a corridor thinner than `SampleStep` is invisible. No UObject state, cache, actor, world,
|
||||||
|
or PIE is required.
|
||||||
| Symbol | Role |
|
| Symbol | Role |
|
||||||
|--------|------|
|
|--------|------|
|
||||||
| `FVoxelStrateMeasureSettings` / `FVoxelStrateMetrics` | Plain settings/result structs for bounded strate sampling and derived measurements; callers may override the interior margin, and results identify the resolved margin/Z window and deterministic component representatives. |
|
| `FVoxelStrateMeasureSettings` / `FVoxelStrateMetrics` | Plain settings/result structs for bounded strate sampling and derived measurements; callers may override the interior margin, and results identify the resolved margin/Z window and deterministic component representatives. |
|
||||||
| `VF_MeasureStrate` | One-grid/one-flood-fill strate metrics; refuses invalid bounds or a grid over `MaxCells`. |
|
| `VF_MeasureStrate` | One-grid/one-flood-fill strate metrics; refuses invalid bounds or a grid over `MaxCells`. |
|
||||||
| `VF_AreConnected` | Coarse 6-connected BFS plus one full-resolution recheck of its recovered route, with explicit endpoint-solid, out-of-window, coarse-lie, and connectivity outcomes. |
|
| `VF_AreConnected` | Coarse 6-connected BFS plus one full-resolution recheck of its recovered route, with explicit endpoint-solid, out-of-window, `CoarseLied`, `NotConnectedAtThisResolution`, and connected outcomes. |
|
||||||
|
|
||||||
### 3.7 Cave morphology (SDF rooms/tunnels) — `Public/VoxelCaveMorphology.h` + `.cpp`
|
### 3.7 Cave morphology (SDF rooms/tunnels) — `Public/VoxelCaveMorphology.h` + `.cpp`
|
||||||
Header is rich with inline docs. Two namespaces + a per-chunk cache system.
|
Header is rich with inline docs. Two namespaces + a per-chunk cache system.
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#if WITH_DEV_AUTOMATION_TESTS
|
#if WITH_DEV_AUTOMATION_TESTS
|
||||||
|
|
||||||
|
#include "HAL/PlatformTime.h"
|
||||||
#include "Misc/AutomationTest.h"
|
#include "Misc/AutomationTest.h"
|
||||||
|
|
||||||
#include "VoxelForgeTestFixture.h"
|
#include "VoxelForgeTestFixture.h"
|
||||||
@@ -13,6 +14,11 @@ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|||||||
"VoxelForge.Generation.StrateConnectivity",
|
"VoxelForge.Generation.StrateConnectivity",
|
||||||
EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::EngineFilter)
|
EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::EngineFilter)
|
||||||
|
|
||||||
|
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||||
|
FVoxelForgeStrateConnectivityRefinementTest,
|
||||||
|
"VoxelForge.Generation.StrateConnectivityRefinement",
|
||||||
|
EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::EngineFilter)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
struct FArchetypeReport
|
struct FArchetypeReport
|
||||||
@@ -99,7 +105,8 @@ namespace
|
|||||||
switch (Result)
|
switch (Result)
|
||||||
{
|
{
|
||||||
case EVoxelConnectivityResult::Connected: return TEXT("CONNECTED");
|
case EVoxelConnectivityResult::Connected: return TEXT("CONNECTED");
|
||||||
case EVoxelConnectivityResult::NotConnected: return TEXT("NOT_CONNECTED");
|
case EVoxelConnectivityResult::NotConnectedAtThisResolution:
|
||||||
|
return TEXT("NOT_CONNECTED_AT_THIS_RESOLUTION");
|
||||||
case EVoxelConnectivityResult::StartCellSolid: return TEXT("START_CELL_SOLID");
|
case EVoxelConnectivityResult::StartCellSolid: return TEXT("START_CELL_SOLID");
|
||||||
case EVoxelConnectivityResult::GoalCellSolid: return TEXT("GOAL_CELL_SOLID");
|
case EVoxelConnectivityResult::GoalCellSolid: return TEXT("GOAL_CELL_SOLID");
|
||||||
case EVoxelConnectivityResult::OutOfWindow: return TEXT("OUT_OF_WINDOW");
|
case EVoxelConnectivityResult::OutOfWindow: return TEXT("OUT_OF_WINDOW");
|
||||||
@@ -234,6 +241,40 @@ namespace
|
|||||||
/ static_cast<double>(OutSampleCount))
|
/ static_cast<double>(OutSampleCount))
|
||||||
: 0.0f;
|
: 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FRefinementSweepRow
|
||||||
|
{
|
||||||
|
int32 SampleStep = 0;
|
||||||
|
int32 RadiusInVoxels = 0;
|
||||||
|
FVoxelStrateMetrics Metrics;
|
||||||
|
FConnectivityProbe Probe;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool FindChainMouths(
|
||||||
|
const TArray<FVoxelPassage>& Passages,
|
||||||
|
int32 StrateIndex,
|
||||||
|
FVector& OutArrivalPoint,
|
||||||
|
FVector& OutDeparturePoint)
|
||||||
|
{
|
||||||
|
int32 ArrivalCount = 0;
|
||||||
|
int32 DepartureCount = 0;
|
||||||
|
for (const FVoxelPassage& Passage : Passages)
|
||||||
|
{
|
||||||
|
if (Passage.LowerStrateIndex == StrateIndex
|
||||||
|
&& Passage.UpperStrateIndex + 1 == StrateIndex)
|
||||||
|
{
|
||||||
|
OutArrivalPoint = Passage.LowerPoint;
|
||||||
|
++ArrivalCount;
|
||||||
|
}
|
||||||
|
if (Passage.UpperStrateIndex == StrateIndex
|
||||||
|
&& Passage.LowerStrateIndex == StrateIndex + 1)
|
||||||
|
{
|
||||||
|
OutDeparturePoint = Passage.UpperPoint;
|
||||||
|
++DepartureCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ArrivalCount == 1 && DepartureCount == 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FVoxelForgeStrateConnectivityTest::RunTest(const FString& Parameters)
|
bool FVoxelForgeStrateConnectivityTest::RunTest(const FString& Parameters)
|
||||||
@@ -348,6 +389,25 @@ bool FVoxelForgeStrateConnectivityTest::RunTest(const FString& Parameters)
|
|||||||
}
|
}
|
||||||
World.DiffLayer->Clear();
|
World.DiffLayer->Clear();
|
||||||
|
|
||||||
|
// The refinement test deliberately approaches MaxCells at step 1. Keep a separate,
|
||||||
|
// cheap over-cap control here so a future allocation/refusal regression cannot hide behind
|
||||||
|
// the connectivity API's endpoint result.
|
||||||
|
FVoxelStrateMeasureSettings TooLargeSettings = Settings;
|
||||||
|
TooLargeSettings.SampleStep = 1;
|
||||||
|
TooLargeSettings.RadiusInVoxels = 256;
|
||||||
|
const FVoxelStrateMetrics TooLargeMetrics = VF_MeasureStrate(
|
||||||
|
*World.Generator, *World.StrateManager, 0, TooLargeSettings);
|
||||||
|
if (TooLargeMetrics.bValid
|
||||||
|
|| !TooLargeMetrics.RefusalReason.Contains(TEXT("MaxCells")))
|
||||||
|
{
|
||||||
|
AddError(FString::Printf(
|
||||||
|
TEXT("HARD FAILURE: the over-cap grid was not refused by MaxCells (valid=%s, "
|
||||||
|
"reason='%s')."),
|
||||||
|
TooLargeMetrics.bValid ? TEXT("true") : TEXT("false"),
|
||||||
|
*TooLargeMetrics.RefusalReason));
|
||||||
|
bAllChecksPassed = false;
|
||||||
|
}
|
||||||
|
|
||||||
TArray<FArchetypeReport> ArchetypeReports;
|
TArray<FArchetypeReport> ArchetypeReports;
|
||||||
const TArray<FStrateSlot>& Layout = World.StrateManager->GetLayout();
|
const TArray<FStrateSlot>& Layout = World.StrateManager->GetLayout();
|
||||||
ArchetypeReports.Reserve(Layout.Num());
|
ArchetypeReports.Reserve(Layout.Num());
|
||||||
@@ -448,6 +508,9 @@ bool FVoxelForgeStrateConnectivityTest::RunTest(const FString& Parameters)
|
|||||||
GapAirFraction,
|
GapAirFraction,
|
||||||
GapAirSamples,
|
GapAirSamples,
|
||||||
GapSamples);
|
GapSamples);
|
||||||
|
Summary += FString::Printf(
|
||||||
|
TEXT("MaxCells refusal control: over-cap measurement refused with reason '%s'.\n"),
|
||||||
|
*TooLargeMetrics.RefusalReason);
|
||||||
Summary += TEXT("Archetypes (old one-chunk vs derived window; derived InteriorMarginVoxels<0 => 2x BoundarySealThickness, clamped):\n");
|
Summary += TEXT("Archetypes (old one-chunk vs derived window; derived InteriorMarginVoxels<0 => 2x BoundarySealThickness, clamped):\n");
|
||||||
Summary += TEXT(" name | old margin | old Z span [min,max) | old Air | old Largest | old LargestCells | old >=1% | old Walkable | old FeatureScale vox | old Clearance vox | old Components | old Samples | old Solid | derived margin | derived Z span [min,max) | derived Air | derived Largest | derived LargestCells | derived >=1% | derived Walkable | derived FeatureScale vox | derived Clearance vox | derived Components | derived Samples | derived Solid\n");
|
Summary += TEXT(" name | old margin | old Z span [min,max) | old Air | old Largest | old LargestCells | old >=1% | old Walkable | old FeatureScale vox | old Clearance vox | old Components | old Samples | old Solid | derived margin | derived Z span [min,max) | derived Air | derived Largest | derived LargestCells | derived >=1% | derived Walkable | derived FeatureScale vox | derived Clearance vox | derived Components | derived Samples | derived Solid\n");
|
||||||
for (const FArchetypeReport& Report : ArchetypeReports)
|
for (const FArchetypeReport& Report : ArchetypeReports)
|
||||||
@@ -496,7 +559,7 @@ bool FVoxelForgeStrateConnectivityTest::RunTest(const FString& Parameters)
|
|||||||
|
|
||||||
int32 NumAnchorProbes = 0;
|
int32 NumAnchorProbes = 0;
|
||||||
int32 NumAnchorConnected = 0;
|
int32 NumAnchorConnected = 0;
|
||||||
int32 NumAnchorNotConnected = 0;
|
int32 NumAnchorNotConnectedAtThisResolution = 0;
|
||||||
int32 NumAnchorStartCellSolid = 0;
|
int32 NumAnchorStartCellSolid = 0;
|
||||||
int32 NumAnchorGoalCellSolid = 0;
|
int32 NumAnchorGoalCellSolid = 0;
|
||||||
int32 NumAnchorOutOfWindow = 0;
|
int32 NumAnchorOutOfWindow = 0;
|
||||||
@@ -552,7 +615,9 @@ bool FVoxelForgeStrateConnectivityTest::RunTest(const FString& Parameters)
|
|||||||
switch (Probe.Result)
|
switch (Probe.Result)
|
||||||
{
|
{
|
||||||
case EVoxelConnectivityResult::Connected: ++NumAnchorConnected; break;
|
case EVoxelConnectivityResult::Connected: ++NumAnchorConnected; break;
|
||||||
case EVoxelConnectivityResult::NotConnected: ++NumAnchorNotConnected; break;
|
case EVoxelConnectivityResult::NotConnectedAtThisResolution:
|
||||||
|
++NumAnchorNotConnectedAtThisResolution;
|
||||||
|
break;
|
||||||
case EVoxelConnectivityResult::StartCellSolid: ++NumAnchorStartCellSolid; break;
|
case EVoxelConnectivityResult::StartCellSolid: ++NumAnchorStartCellSolid; break;
|
||||||
case EVoxelConnectivityResult::GoalCellSolid: ++NumAnchorGoalCellSolid; break;
|
case EVoxelConnectivityResult::GoalCellSolid: ++NumAnchorGoalCellSolid; break;
|
||||||
case EVoxelConnectivityResult::OutOfWindow: ++NumAnchorOutOfWindow; break;
|
case EVoxelConnectivityResult::OutOfWindow: ++NumAnchorOutOfWindow; break;
|
||||||
@@ -836,11 +901,11 @@ bool FVoxelForgeStrateConnectivityTest::RunTest(const FString& Parameters)
|
|||||||
NumArrivalDepartureRoutesChecked);
|
NumArrivalDepartureRoutesChecked);
|
||||||
Summary += FString::Printf(
|
Summary += FString::Printf(
|
||||||
TEXT("Correct largest-component endpoint probes (%d, including the surface-entry auxiliary): "
|
TEXT("Correct largest-component endpoint probes (%d, including the surface-entry auxiliary): "
|
||||||
"connected=%d, not-connected=%d, start-cell-solid=%d, goal-cell-solid=%d, "
|
"connected=%d, not-connected-at-resolution=%d, start-cell-solid=%d, goal-cell-solid=%d, "
|
||||||
"out-of-window=%d, coarse-lied=%d, snapped endpoint events=%d.\n"),
|
"out-of-window=%d, coarse-lied=%d, snapped endpoint events=%d.\n"),
|
||||||
NumAnchorProbes,
|
NumAnchorProbes,
|
||||||
NumAnchorConnected,
|
NumAnchorConnected,
|
||||||
NumAnchorNotConnected,
|
NumAnchorNotConnectedAtThisResolution,
|
||||||
NumAnchorStartCellSolid,
|
NumAnchorStartCellSolid,
|
||||||
NumAnchorGoalCellSolid,
|
NumAnchorGoalCellSolid,
|
||||||
NumAnchorOutOfWindow,
|
NumAnchorOutOfWindow,
|
||||||
@@ -926,4 +991,176 @@ bool FVoxelForgeStrateConnectivityTest::RunTest(const FString& Parameters)
|
|||||||
return bAllChecksPassed;
|
return bAllChecksPassed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FVoxelForgeStrateConnectivityRefinementTest::RunTest(const FString& Parameters)
|
||||||
|
{
|
||||||
|
using namespace VoxelForgeTest;
|
||||||
|
|
||||||
|
FTestWorld World;
|
||||||
|
World.Build(/*InSeed=*/1337, /*InGapChunks=*/2);
|
||||||
|
if (!World.IsValid())
|
||||||
|
{
|
||||||
|
AddError(World.WhyInvalid());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TArray<FStrateSlot>& Layout = World.StrateManager->GetLayout();
|
||||||
|
const int32 VerticalShaftsIndex = FTestWorld::SlotVerticalShafts;
|
||||||
|
if (!Layout.IsValidIndex(VerticalShaftsIndex)
|
||||||
|
|| Layout[VerticalShaftsIndex].Definition == nullptr
|
||||||
|
|| Layout[VerticalShaftsIndex].Definition->GeneratorType
|
||||||
|
!= ECaveGeneratorType::VerticalShafts)
|
||||||
|
{
|
||||||
|
AddError(TEXT("HARD FAILURE: the pinned VerticalShafts fixture slot is missing or changed."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FVector ArrivalPoint = FVector::ZeroVector;
|
||||||
|
FVector DeparturePoint = FVector::ZeroVector;
|
||||||
|
if (!FindChainMouths(
|
||||||
|
World.StrateManager->GetPassages(),
|
||||||
|
VerticalShaftsIndex,
|
||||||
|
ArrivalPoint,
|
||||||
|
DeparturePoint))
|
||||||
|
{
|
||||||
|
AddError(TEXT("HARD FAILURE: VerticalShafts does not have exactly one arrival and one "
|
||||||
|
"departure mouth between consecutive strates."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FVoxelStrateMeasureSettings BaseSettings;
|
||||||
|
BaseSettings.CenterXY = FVector2D::ZeroVector;
|
||||||
|
BaseSettings.MaxCells = 8000000;
|
||||||
|
BaseSettings.HeadroomCells = 2;
|
||||||
|
BaseSettings.InteriorMarginVoxels = -1;
|
||||||
|
|
||||||
|
struct FRefinementCase
|
||||||
|
{
|
||||||
|
int32 SampleStep;
|
||||||
|
int32 RadiusInVoxels;
|
||||||
|
};
|
||||||
|
static constexpr FRefinementCase Cases[] = {
|
||||||
|
{4, 256},
|
||||||
|
{4, 192},
|
||||||
|
{2, 192},
|
||||||
|
{4, 128},
|
||||||
|
{1, 128},
|
||||||
|
};
|
||||||
|
|
||||||
|
bool bAllChecksPassed = true;
|
||||||
|
TArray<FRefinementSweepRow> Rows;
|
||||||
|
Rows.Reserve(UE_ARRAY_COUNT(Cases));
|
||||||
|
|
||||||
|
const double SweepStartSeconds = FPlatformTime::Seconds();
|
||||||
|
for (const FRefinementCase& TestCase : Cases)
|
||||||
|
{
|
||||||
|
FRefinementSweepRow& Row = Rows.AddDefaulted_GetRef();
|
||||||
|
Row.SampleStep = TestCase.SampleStep;
|
||||||
|
Row.RadiusInVoxels = TestCase.RadiusInVoxels;
|
||||||
|
|
||||||
|
FVoxelStrateMeasureSettings RowSettings = BaseSettings;
|
||||||
|
RowSettings.SampleStep = TestCase.SampleStep;
|
||||||
|
RowSettings.RadiusInVoxels = TestCase.RadiusInVoxels;
|
||||||
|
|
||||||
|
Row.Metrics = VF_MeasureStrate(
|
||||||
|
*World.Generator,
|
||||||
|
*World.StrateManager,
|
||||||
|
VerticalShaftsIndex,
|
||||||
|
RowSettings);
|
||||||
|
|
||||||
|
Row.Probe.bChecked = true;
|
||||||
|
Row.Probe.Result = VF_AreConnected(
|
||||||
|
*World.Generator,
|
||||||
|
*World.StrateManager,
|
||||||
|
VerticalShaftsIndex,
|
||||||
|
ArrivalPoint,
|
||||||
|
DeparturePoint,
|
||||||
|
RowSettings,
|
||||||
|
Row.Probe.bStartSnapped,
|
||||||
|
Row.Probe.bGoalSnapped);
|
||||||
|
|
||||||
|
if (!Row.Metrics.bValid
|
||||||
|
|| Row.Metrics.NumSampled <= 0
|
||||||
|
|| Row.Metrics.NumSampled > BaseSettings.MaxCells
|
||||||
|
|| Row.Metrics.NumAir <= 0
|
||||||
|
|| Row.Metrics.NumSolid <= 0)
|
||||||
|
{
|
||||||
|
AddError(FString::Printf(
|
||||||
|
TEXT("HARD FAILURE: VerticalShafts refinement row step=%d radius=%d did not "
|
||||||
|
"produce a bounded non-vacuous measurement (valid=%s, reason='%s', "
|
||||||
|
"cells=%lld, air=%lld, solid=%lld)."),
|
||||||
|
Row.SampleStep,
|
||||||
|
Row.RadiusInVoxels,
|
||||||
|
Row.Metrics.bValid ? TEXT("true") : TEXT("false"),
|
||||||
|
*Row.Metrics.RefusalReason,
|
||||||
|
Row.Metrics.NumSampled,
|
||||||
|
Row.Metrics.NumAir,
|
||||||
|
Row.Metrics.NumSolid));
|
||||||
|
bAllChecksPassed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Row.Probe.Result == EVoxelConnectivityResult::OutOfWindow
|
||||||
|
|| Row.Probe.Result == EVoxelConnectivityResult::StartCellSolid
|
||||||
|
|| Row.Probe.Result == EVoxelConnectivityResult::GoalCellSolid)
|
||||||
|
{
|
||||||
|
const FString ProbeText = ConnectivityProbeText(Row.Probe);
|
||||||
|
AddError(FString::Printf(
|
||||||
|
TEXT("HARD FAILURE: VerticalShafts refinement row step=%d radius=%d could "
|
||||||
|
"not query both in-window air mouths: %s."),
|
||||||
|
Row.SampleStep,
|
||||||
|
Row.RadiusInVoxels,
|
||||||
|
*ProbeText));
|
||||||
|
bAllChecksPassed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Row.Probe.bStartSnapped || Row.Probe.bGoalSnapped)
|
||||||
|
{
|
||||||
|
AddError(FString::Printf(
|
||||||
|
TEXT("HARD FAILURE: VerticalShafts refinement row step=%d radius=%d snapped "
|
||||||
|
"an endpoint; the controlled comparison is not a mouth-to-mouth query."),
|
||||||
|
Row.SampleStep,
|
||||||
|
Row.RadiusInVoxels));
|
||||||
|
bAllChecksPassed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
const double SweepSeconds = FPlatformTime::Seconds() - SweepStartSeconds;
|
||||||
|
|
||||||
|
FString Summary = TEXT(
|
||||||
|
"VerticalShafts arrival->departure refinement (seed 1337; source mouth pair; "
|
||||||
|
"derived interior window):\n");
|
||||||
|
Summary += TEXT(
|
||||||
|
" SampleStep | RadiusInVoxels | verdict | components | largest share | cells | margin | "
|
||||||
|
"Z window | endpoint snaps\n");
|
||||||
|
for (const FRefinementSweepRow& Row : Rows)
|
||||||
|
{
|
||||||
|
const FString VerdictText = ConnectivityProbeText(Row.Probe);
|
||||||
|
FString SnapText = TEXT("none");
|
||||||
|
if (Row.Probe.bStartSnapped || Row.Probe.bGoalSnapped)
|
||||||
|
{
|
||||||
|
SnapText = ConnectivityProbeText(Row.Probe);
|
||||||
|
}
|
||||||
|
Summary += FString::Printf(
|
||||||
|
TEXT(" %d | %d | %s | %d | %.9g | %lld | %d | [%d,%d) | %s\n"),
|
||||||
|
Row.SampleStep,
|
||||||
|
Row.RadiusInVoxels,
|
||||||
|
*VerdictText,
|
||||||
|
Row.Metrics.NumAirComponents,
|
||||||
|
Row.Metrics.LargestComponentShare,
|
||||||
|
Row.Metrics.NumSampled,
|
||||||
|
Row.Metrics.ResolvedMarginVoxels,
|
||||||
|
Row.Metrics.SampledMinZ,
|
||||||
|
Row.Metrics.SampledMaxZ,
|
||||||
|
*SnapText);
|
||||||
|
}
|
||||||
|
Summary += TEXT(
|
||||||
|
"Controlled comparisons hold radius constant: (step 4, radius 192) vs (step 2, "
|
||||||
|
"radius 192), and (step 4, radius 128) vs (step 1, radius 128).\n");
|
||||||
|
Summary += FString::Printf(
|
||||||
|
TEXT("VerticalShafts refinement sweep wall-clock: %.3f seconds.\n"),
|
||||||
|
SweepSeconds);
|
||||||
|
AddInfo(Summary);
|
||||||
|
|
||||||
|
return bAllChecksPassed;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // WITH_DEV_AUTOMATION_TESTS
|
#endif // WITH_DEV_AUTOMATION_TESTS
|
||||||
@@ -882,7 +882,7 @@ EVoxelConnectivityResult VF_AreConnected(
|
|||||||
TArray<int32> Path;
|
TArray<int32> Path;
|
||||||
if (!VoxelStrateMeasurePrivate::FindCoarsePath(Grid, Start, Goal, Path))
|
if (!VoxelStrateMeasurePrivate::FindCoarsePath(Grid, Start, Goal, Path))
|
||||||
{
|
{
|
||||||
return EVoxelConnectivityResult::NotConnected;
|
return EVoxelConnectivityResult::NotConnectedAtThisResolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The coarse route is evidence, not a pass. Re-check this one BFS route at one-voxel spacing
|
// The coarse route is evidence, not a pass. Re-check this one BFS route at one-voxel spacing
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class UVoxelStrateManager;
|
|||||||
enum class EVoxelConnectivityResult : uint8
|
enum class EVoxelConnectivityResult : uint8
|
||||||
{
|
{
|
||||||
Connected,
|
Connected,
|
||||||
NotConnected,
|
NotConnectedAtThisResolution, // Evidence only at this grid's resolution; corridors thinner than SampleStep are invisible.
|
||||||
StartCellSolid,
|
StartCellSolid,
|
||||||
GoalCellSolid,
|
GoalCellSolid,
|
||||||
OutOfWindow,
|
OutOfWindow,
|
||||||
@@ -74,7 +74,9 @@ VOXELFORGE_API FVoxelStrateMetrics VF_MeasureStrate(
|
|||||||
* If an endpoint's own coarse cell is solid, the query searches the 26 neighbouring cells and
|
* If an endpoint's own coarse cell is solid, the query searches the 26 neighbouring cells and
|
||||||
* uses the nearest air cell when one exists. The snap flags report those repairs. Solid-cell
|
* uses the nearest air cell when one exists. The snap flags report those repairs. Solid-cell
|
||||||
* results are returned only when no adjacent air cell exists. A coarse route that fails its
|
* results are returned only when no adjacent air cell exists. A coarse route that fails its
|
||||||
* full-resolution re-check returns CoarseLied.
|
* full-resolution re-check returns CoarseLied; a missing coarse route returns
|
||||||
|
* NotConnectedAtThisResolution because a corridor thinner than SampleStep is invisible to the
|
||||||
|
* grid. A bare "not connected" result is evidence only at the grid's own resolution.
|
||||||
*/
|
*/
|
||||||
VOXELFORGE_API EVoxelConnectivityResult VF_AreConnected(
|
VOXELFORGE_API EVoxelConnectivityResult VF_AreConnected(
|
||||||
const UVoxelGenerator& Generator,
|
const UVoxelGenerator& Generator,
|
||||||
|
|||||||
Reference in new issue
Block a user