feat(composer): showcase of all 8 archetypes -- and "not navigable" answered

Jahni asked for one strate per archetype to look at, and said the previews
looked "not navigable nor anything except maybe the mazes". Both delivered.

⭐ PART A -- HIS EYE WAS RIGHT, AND SO WAS THE METRIC. BOTH EXPLANATIONS
WERE TRUE, and they split by archetype:

  archetype        walkable   FLOOR AREA   largest connected surface
  CrystalChamber     .059        .9999          1.00
  FlatPlain          .048        .9985           .9998
  SurfaceWorld       .064        .9374           .9936
  Maze               .285        .2629           .9905
  Underwater         .229        .2061           .3312
  TunnelNetwork      .155        .2084           .4640
  VerticalShafts     .108        .1138           .1449
  FloatingIslands    .004        .1016           .4992

1. THE METRIC WAS WRONG FOR CHAMBERS. Walkable fraction is volumetric: an
   air cell with solid one step below and headroom above. In a cathedral
   almost no air cell is near a floor, so a huge open room scores ~.06 while
   a tight maze scores ~.29. It was measuring CORRIDOR-NESS, not
   navigability -- and mazes win that by construction, which is exactly the
   pattern he spotted. Slab families actually have 94-100% walkable FLOOR
   AREA, fully connected. They were never the problem.

2. AND FOUR ARCHETYPES ARE GENUINELY FRAGMENTED, which is a real content
   finding, not an artifact. Largest connected walkable surface:
   VerticalShafts .14, Underwater .33, TunnelNetwork .46, FloatingIslands
   .50. In VerticalShafts, 86% of the floor is unreachable from the biggest
   patch. The primordial law still passes -- you CAN get from arrival to
   departure -- but most of the floor is not part of that. That is precisely
   "not navigable" and it needed a new metric to see.

New metrics: WalkableFloorAreaFraction, NumWalkableSurfaceComponents,
LargestWalkableSurfaceShare. A floor you cannot cross is not a floor.

Web references checked, per his suggestion -- we invented every metric in
this project without once testing them against reality: NPS solution-cave
and Mammoth Cave morphology, Lehman Cave dimensions (Talus Room 90x376 ft,
21,511 sq ft floor, 113 ft ceiling), Deep Rock Galactic cave-design notes,
Team Cherry on Metroidvania maps. They support heterogeneous room+passage
space with branches and shafts -- NOT a universal floor-to-volume target.
So there is no single number to optimise, which is itself worth knowing.

PART B -- Saved/VoxelForge/Showcase/index.html, 8 cards, 64 PNGs, fine
renders (step 1, radius 64, centred on LargestComponentPoint), 0 blanks.
Each card carries the exact PIE values to type in, because navigability is
judged by walking:

  CrystalChamber  seed 7331 idx 34 slot 1     Maze            seed 0 idx 58 slot 1
  FlatPlain       seed 0    idx  4 slot 1     SurfaceWorld    seed 0 idx 11 slot 2
  FloatingIslands seed 0    idx 34 slot 1     TunnelNetwork   seed 0 idx 61 slot 1
  Underwater      seed 0    idx 46 slot 1     VerticalShafts  seed 0 idx 15 slot 1

All bComposerRollStructure = false. Selection searched seeds 0/7331 x
indices 0-63, applied the three hard gates, then scored
floor_area + .25*clearance + .10*largest_surface.

Caveats kept honest: floor area is a sampled XY proxy, projected
connectivity is not a 3D traversal proof, "best" means best within the
searched range.

Build clean, 30 succeeded / 0 failed, verified by me.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-05 06:59:51 +02:00
1 parent dd38832bb6
commit 088b40216d
10 files changed
+1420 -16

No files matched your search

File diff suppressed because it is too large. Load diff
@@ -873,6 +873,16 @@ namespace VoxelSeasonManifestPrivate
Object->SetNumberField(TEXT("num_components_at_least_1_pct"),
Metrics.NumComponentsAtLeast1Pct);
SetMetricFloat(Object, TEXT("walkable_fraction"), Metrics.WalkableFraction);
Object->SetNumberField(TEXT("walkable_floor_columns"),
static_cast<double>(Metrics.WalkableFloorColumns));
SetMetricFloat(Object, TEXT("walkable_floor_area_fraction"),
Metrics.WalkableFloorAreaFraction);
Object->SetNumberField(TEXT("num_walkable_surface_components"),
Metrics.NumWalkableSurfaceComponents);
Object->SetNumberField(TEXT("largest_walkable_surface_columns"),
static_cast<double>(Metrics.LargestWalkableSurfaceColumns));
SetMetricFloat(Object, TEXT("largest_walkable_surface_share"),
Metrics.LargestWalkableSurfaceShare);
SetMetricFloat(Object, TEXT("median_feature_scale"), Metrics.MedianFeatureScale);
Object->SetNumberField(TEXT("median_vertical_clearance"), Metrics.MedianVerticalClearance);
Object->SetNumberField(TEXT("resolved_margin_voxels"), Metrics.ResolvedMarginVoxels);
@@ -914,6 +924,19 @@ namespace VoxelSeasonManifestPrivate
bValid = ReadJsonInt64(Object, TEXT("largest_component_cells"), OutMetrics.LargestComponentCells) && bValid;
bValid = ReadJsonInt32(Object, TEXT("num_components_at_least_1_pct"), OutMetrics.NumComponentsAtLeast1Pct) && bValid;
bValid = ReadMetricFloat(Object, TEXT("walkable_fraction"), OutMetrics.WalkableFraction) && bValid;
if (Object->HasField(TEXT("walkable_floor_columns")))
{
bValid = ReadJsonInt64(Object, TEXT("walkable_floor_columns"),
OutMetrics.WalkableFloorColumns) && bValid;
bValid = ReadMetricFloat(Object, TEXT("walkable_floor_area_fraction"),
OutMetrics.WalkableFloorAreaFraction) && bValid;
bValid = ReadJsonInt32(Object, TEXT("num_walkable_surface_components"),
OutMetrics.NumWalkableSurfaceComponents) && bValid;
bValid = ReadJsonInt64(Object, TEXT("largest_walkable_surface_columns"),
OutMetrics.LargestWalkableSurfaceColumns) && bValid;
bValid = ReadMetricFloat(Object, TEXT("largest_walkable_surface_share"),
OutMetrics.LargestWalkableSurfaceShare) && bValid;
}
bValid = ReadMetricFloat(Object, TEXT("median_feature_scale"), OutMetrics.MedianFeatureScale) && bValid;
bValid = ReadJsonInt32(Object, TEXT("median_vertical_clearance"), OutMetrics.MedianVerticalClearance) && bValid;
bValid = ReadJsonInt32(Object, TEXT("resolved_margin_voxels"), OutMetrics.ResolvedMarginVoxels) && bValid;
@@ -2275,6 +2275,8 @@ bool FVoxelStrateMeasuredMetrics::IsUsable() const
&& FMath::IsFinite(AirFraction)
&& FMath::IsFinite(LargestComponentShare)
&& FMath::IsFinite(WalkableFraction)
&& FMath::IsFinite(WalkableFloorAreaFraction)
&& FMath::IsFinite(LargestWalkableSurfaceShare)
&& FMath::IsFinite(MedianFeatureScale)
&& FMath::IsFinite(LargestComponentPoint.X)
&& FMath::IsFinite(LargestComponentPoint.Y)
@@ -2296,6 +2298,11 @@ FVoxelStrateMeasuredMetrics VF_SummarizeStrateMetrics(
Result.LargestComponentCells = Metrics.LargestComponentCells;
Result.NumComponentsAtLeast1Pct = Metrics.NumComponentsAtLeast1Pct;
Result.WalkableFraction = Metrics.WalkableFraction;
Result.WalkableFloorColumns = Metrics.WalkableFloorColumns;
Result.WalkableFloorAreaFraction = Metrics.WalkableFloorAreaFraction;
Result.NumWalkableSurfaceComponents = Metrics.NumWalkableSurfaceComponents;
Result.LargestWalkableSurfaceColumns = Metrics.LargestWalkableSurfaceColumns;
Result.LargestWalkableSurfaceShare = Metrics.LargestWalkableSurfaceShare;
Result.MedianFeatureScale = Metrics.MedianFeatureScale;
Result.MedianVerticalClearance = Metrics.MedianVerticalClearance;
Result.ResolvedMarginVoxels = Metrics.ResolvedMarginVoxels;
@@ -2779,6 +2786,16 @@ namespace
Object->SetNumberField(TEXT("num_components_at_least_1_pct"),
Metrics.NumComponentsAtLeast1Pct);
Object->SetNumberField(TEXT("walkable_fraction"), Metrics.WalkableFraction);
Object->SetNumberField(TEXT("walkable_floor_columns"),
static_cast<double>(Metrics.WalkableFloorColumns));
Object->SetNumberField(TEXT("walkable_floor_area_fraction"),
Metrics.WalkableFloorAreaFraction);
Object->SetNumberField(TEXT("num_walkable_surface_components"),
Metrics.NumWalkableSurfaceComponents);
Object->SetNumberField(TEXT("largest_walkable_surface_columns"),
static_cast<double>(Metrics.LargestWalkableSurfaceColumns));
Object->SetNumberField(TEXT("largest_walkable_surface_share"),
Metrics.LargestWalkableSurfaceShare);
Object->SetNumberField(TEXT("median_feature_scale"), Metrics.MedianFeatureScale);
Object->SetNumberField(TEXT("median_vertical_clearance"), Metrics.MedianVerticalClearance);
Object->SetNumberField(TEXT("resolved_margin_voxels"), Metrics.ResolvedMarginVoxels);
@@ -2836,6 +2853,21 @@ namespace
bValid = VF_ReadJsonInt32(Object, TEXT("num_components_at_least_1_pct"),
OutMetrics.NumComponentsAtLeast1Pct) && bValid;
bValid = VF_ReadJsonFloat(Object, TEXT("walkable_fraction"), OutMetrics.WalkableFraction) && bValid;
// These fields were added after the first promotion-store schema. Keep old records
// readable: their absence means the new diagnostic is unknown, not zero evidence.
if (Object->HasField(TEXT("walkable_floor_columns")))
{
bValid = VF_ReadJsonInt64(Object, TEXT("walkable_floor_columns"),
OutMetrics.WalkableFloorColumns) && bValid;
bValid = VF_ReadJsonFloat(Object, TEXT("walkable_floor_area_fraction"),
OutMetrics.WalkableFloorAreaFraction) && bValid;
bValid = VF_ReadJsonInt32(Object, TEXT("num_walkable_surface_components"),
OutMetrics.NumWalkableSurfaceComponents) && bValid;
bValid = VF_ReadJsonInt64(Object, TEXT("largest_walkable_surface_columns"),
OutMetrics.LargestWalkableSurfaceColumns) && bValid;
bValid = VF_ReadJsonFloat(Object, TEXT("largest_walkable_surface_share"),
OutMetrics.LargestWalkableSurfaceShare) && bValid;
}
bValid = VF_ReadJsonFloat(Object, TEXT("median_feature_scale"),
OutMetrics.MedianFeatureScale) && bValid;
bValid = VF_ReadJsonInt32(Object, TEXT("median_vertical_clearance"),
@@ -2976,6 +3008,11 @@ namespace
&& A.LargestComponentCells == B.LargestComponentCells
&& A.NumComponentsAtLeast1Pct == B.NumComponentsAtLeast1Pct
&& A.WalkableFraction == B.WalkableFraction
&& A.WalkableFloorColumns == B.WalkableFloorColumns
&& A.WalkableFloorAreaFraction == B.WalkableFloorAreaFraction
&& A.NumWalkableSurfaceComponents == B.NumWalkableSurfaceComponents
&& A.LargestWalkableSurfaceColumns == B.LargestWalkableSurfaceColumns
&& A.LargestWalkableSurfaceShare == B.LargestWalkableSurfaceShare
&& A.MedianFeatureScale == B.MedianFeatureScale
&& A.MedianVerticalClearance == B.MedianVerticalClearance
&& A.ResolvedMarginVoxels == B.ResolvedMarginVoxels
@@ -3055,6 +3092,8 @@ namespace
return FMath::IsFinite(Metrics.AirFraction)
&& FMath::IsFinite(Metrics.LargestComponentShare)
&& FMath::IsFinite(Metrics.WalkableFraction)
&& FMath::IsFinite(Metrics.WalkableFloorAreaFraction)
&& FMath::IsFinite(Metrics.LargestWalkableSurfaceShare)
&& FMath::IsFinite(Metrics.MedianFeatureScale)
&& FMath::IsFinite(Metrics.LargestComponentPoint.X)
&& FMath::IsFinite(Metrics.LargestComponentPoint.Y)
@@ -639,6 +639,10 @@ namespace VoxelStrateMeasurePrivate
TArray<int32> ClearanceValues;
ClearanceValues.Reserve(static_cast<int32>(NumAir));
int64 NumWalkable = 0;
const int64 XYColumnCount64 = static_cast<int64>(Grid.NumX)
* static_cast<int64>(Grid.NumY);
TArray<uint8> WalkableColumns;
WalkableColumns.Init(0u, static_cast<int32>(XYColumnCount64));
for (int32 Z = 0; Z < Grid.NumZ; ++Z)
{
@@ -667,6 +671,7 @@ namespace VoxelStrateMeasurePrivate
if (!bHasHeadroom) continue;
++NumWalkable;
WalkableColumns[X + Grid.NumX * Y] = 1u;
int32 ClearanceCells = 0;
while (static_cast<int64>(Z) + ClearanceCells < Grid.NumZ
&& Grid.Air[Grid.Index(X, Y, Z + ClearanceCells)] != 0u)
@@ -685,6 +690,64 @@ namespace VoxelStrateMeasurePrivate
? static_cast<float>(static_cast<double>(NumWalkable)
/ static_cast<double>(NumAir))
: 0.0f;
for (const uint8 bWalkable : WalkableColumns)
{
InOutMetrics.WalkableFloorColumns += bWalkable != 0u ? 1 : 0;
}
InOutMetrics.WalkableFloorAreaFraction = XYColumnCount64 > 0
? static_cast<float>(static_cast<double>(InOutMetrics.WalkableFloorColumns)
/ static_cast<double>(XYColumnCount64))
: 0.0f;
// The area graph is intentionally projected to XY: each column contributes one floor
// footprint even when several sampled Z cells in it meet the walkability predicate.
// Keep the flood fill deterministic and bounded by the already bounded XY grid.
TArray<uint8> VisitedWalkableColumns;
VisitedWalkableColumns.Init(0u, static_cast<int32>(XYColumnCount64));
TArray<int32> SurfaceQueue;
SurfaceQueue.Reserve(static_cast<int32>(XYColumnCount64));
for (int32 Column = 0; Column < WalkableColumns.Num(); ++Column)
{
if (WalkableColumns[Column] == 0u || VisitedWalkableColumns[Column] != 0u)
{
continue;
}
++InOutMetrics.NumWalkableSurfaceComponents;
VisitedWalkableColumns[Column] = 1u;
SurfaceQueue.Reset();
SurfaceQueue.Add(Column);
int64 ComponentColumns = 0;
for (int32 QueueIndex = 0; QueueIndex < SurfaceQueue.Num(); ++QueueIndex)
{
const int32 CurrentColumn = SurfaceQueue[QueueIndex];
++ComponentColumns;
const int32 CurrentX = CurrentColumn % Grid.NumX;
const int32 CurrentY = CurrentColumn / Grid.NumX;
const int32 NeighbourColumns[4] = {
CurrentX > 0 ? CurrentColumn - 1 : INDEX_NONE,
CurrentX + 1 < Grid.NumX ? CurrentColumn + 1 : INDEX_NONE,
CurrentY > 0 ? CurrentColumn - Grid.NumX : INDEX_NONE,
CurrentY + 1 < Grid.NumY ? CurrentColumn + Grid.NumX : INDEX_NONE
};
for (const int32 Neighbour : NeighbourColumns)
{
if (Neighbour != INDEX_NONE
&& WalkableColumns[Neighbour] != 0u
&& VisitedWalkableColumns[Neighbour] == 0u)
{
VisitedWalkableColumns[Neighbour] = 1u;
SurfaceQueue.Add(Neighbour);
}
}
}
InOutMetrics.LargestWalkableSurfaceColumns = FMath::Max(
InOutMetrics.LargestWalkableSurfaceColumns, ComponentColumns);
}
InOutMetrics.LargestWalkableSurfaceShare = InOutMetrics.WalkableFloorColumns > 0
? static_cast<float>(static_cast<double>(InOutMetrics.LargestWalkableSurfaceColumns)
/ static_cast<double>(InOutMetrics.WalkableFloorColumns))
: 0.0f;
if (ClearanceValues.Num() > 0)
{
+128 -10
View File
@@ -89,6 +89,11 @@ namespace
return bValid ? FString::Printf(TEXT("%d"), Value) : TEXT("invalid");
}
FString VF_FormatInteger64Metric(bool bValid, int64 Value)
{
return bValid ? FString::Printf(TEXT("%lld"), static_cast<long long>(Value)) : TEXT("invalid");
}
bool VF_IsWalkableCell(const FVoxelStrateSampleGrid& Grid,
int32 X, int32 Y, int32 Z, int32 HeadroomCells)
{
@@ -704,6 +709,11 @@ bool VF_WriteStratePreviewCandidate(
OutCandidate.AirFraction = Metrics.AirFraction;
OutCandidate.LargestComponentShare = Metrics.LargestComponentShare;
OutCandidate.WalkableFraction = Metrics.WalkableFraction;
OutCandidate.WalkableFloorColumns = Metrics.WalkableFloorColumns;
OutCandidate.WalkableFloorAreaFraction = Metrics.WalkableFloorAreaFraction;
OutCandidate.NumWalkableSurfaceComponents = Metrics.NumWalkableSurfaceComponents;
OutCandidate.LargestWalkableSurfaceColumns = Metrics.LargestWalkableSurfaceColumns;
OutCandidate.LargestWalkableSurfaceShare = Metrics.LargestWalkableSurfaceShare;
OutCandidate.MedianFeatureScale = Metrics.MedianFeatureScale;
OutCandidate.MedianVerticalClearance = Metrics.MedianVerticalClearance;
OutCandidate.DistanceFromCorpusCentroid = FMath::IsFinite(DistanceFromCorpusCentroid)
@@ -961,7 +971,8 @@ bool VF_WriteStratePreviewIndex(
const FVoxelStratePreviewWindow& Window,
const TArray<FVoxelStratePreviewCandidate>& Candidates,
FString& OutIndexPath,
FString& OutError)
FString& OutError,
const TArray<FVoxelStratePreviewArchetypeSummary>* ArchetypeSummaries)
{
if (!Window.IsValid())
{
@@ -979,6 +990,15 @@ bool VF_WriteStratePreviewIndex(
SortedCandidates.Sort([](const FVoxelStratePreviewCandidate& A,
const FVoxelStratePreviewCandidate& B)
{
if (A.bShowcaseCard != B.bShowcaseCard)
{
return A.bShowcaseCard;
}
if (A.bShowcaseCard && B.bShowcaseCard
&& A.ArchetypeName != B.ArchetypeName)
{
return A.ArchetypeName < B.ArchetypeName;
}
if (A.bSeasonOrder != B.bSeasonOrder)
{
return A.bSeasonOrder;
@@ -1036,10 +1056,71 @@ bool VF_WriteStratePreviewIndex(
".reason{color:#ffb4b8;margin:6px 0}.recipe{color:#c9d1d9;overflow-wrap:anywhere;margin-bottom:8px}.recipe code{font:12px ui-monospace,SFMono-Regular,monospace}\n"
".window{font-size:11px;color:#8b949e;margin:5px 0 10px}.view-section{margin:12px 0}.view-section h3{font-size:12px;letter-spacing:.03em;color:#e6edf3;margin:10px 0 3px}.pair{display:grid;grid-template-columns:1fr 1fr;gap:8px}.pair figure{margin:0}.pair img{display:block;width:100%;height:auto;background:#090c11;image-rendering:pixelated;border:1px solid #30363d}.pair figcaption{font-size:11px;color:#aab4c0;margin-top:3px}.fine-section{border-top:1px solid #6e4e1f;margin-top:14px;padding-top:4px}.fine-section h3{color:#ffd580}.not-requested{color:#8b949e;font-size:12px;margin:8px 0}.contour-note{color:#8cebd1}\n"
".no-image{min-height:40px;border:1px dashed #8b3b42;color:#ffb4b8;padding:12px;font-size:12px}\n"
".pie-banner{border:2px solid #f5a636;background:#2b2112;color:#ffe5b0;border-radius:8px;padding:14px;margin:16px 0;font-size:15px}\n"
".pie-card{border:2px solid #f5a636;background:#2b2112;color:#ffe5b0;border-radius:6px;padding:10px;margin:8px 0 12px;font-size:13px;line-height:1.6}\n"
".pie-card code{font:13px ui-monospace,SFMono-Regular,monospace;color:#fff1ce}\n"
".sources a{color:#8cebd1}.summary{display:block;overflow-x:auto;white-space:nowrap}.summary th,.summary td{vertical-align:top}.summary-window{white-space:normal;min-width:260px}\n"
"table{width:100%;border-collapse:collapse;margin-top:10px;font-size:12px}th,td{padding:3px 4px;border-bottom:1px solid #30363d;text-align:left}th{color:#aab4c0;font-weight:500}td{text-align:right;font-variant-numeric:tabular-nums}.verdict{font-weight:650}\n"
".footer{color:#8b949e;font-size:11px;margin-top:18px}\n"
"</style></head><body>\n");
Html += FString::Printf(TEXT("<h1>%s</h1>\n"), *VF_HtmlEscape(RunTitle));
const bool bShowcase = ArchetypeSummaries != nullptr && !ArchetypeSummaries->IsEmpty();
if (bShowcase)
{
Html += TEXT(
"<div class=\"pie-banner\"><strong>PIE WALK-THROUGH — exact values per card</strong> "
"Set the four displayed properties on the VoxelWorld actor, enter PIE, then click "
"<strong>Apply Composer Candidate</strong>. The candidate seed/index are the same "
"offline inputs used to produce these images. This page never changes runtime "
"generation by itself.</div>\n"
"<p class=\"note\"><strong>Showcase scope:</strong> all cards use the native "
"single-region path with <code>bComposerRollStructure=false</code>; lateral regions "
"are deliberately gated off. The target slot is reported explicitly on each card. "
"The candidate archetype is the density replacement, while the live slot layout, "
"height, passages, placement, and world seed remain unchanged.</p>\n"
"<h2>Part A — what the numbers mean</h2>\n"
"<p class=\"note\">Walkable fraction is <em>walkable sampled air cells / all sampled air "
"cells</em>; it is a volumetric occupancy ratio and is expected to understate a large "
"room. Walkable floor area is <em>distinct XY columns containing at least one "
"walkable cell / sampled XY footprint</em>. Largest connected surface is the largest "
"four-neighbour component of those projected columns / all walkable columns. It is "
"not a 3D traversal proof; the arrival → departure route check is separate.</p>\n"
"<p class=\"note\"><strong>Part A measurement settings:</strong> step=4 voxels, "
"HeadroomCells=2, XY radius=256 voxels, MaxCells=8,000,000. Each summary row lists "
"the exact target, derived interior margin, Z range, XY range, and grid for every "
"hard-gate survivor; each card repeats its selected window.</p>\n"
"<p class=\"note\"><strong>Reality check:</strong> real caves mix long, narrow passages "
"with rooms, branches, multiple levels, and occasional vertical shafts; there is no "
"universal target floor-to-volume ratio. The quantitative table below is therefore a "
"diagnostic comparison across this corpus, not a tuned magic number.</p>\n");
Html += TEXT(
"<p class=\"note sources\"><strong>References used:</strong> "
"<a href=\"https://www.nps.gov/subjects/caves/solution-caves.htm\">NPS Solution Caves</a>; "
"<a href=\"https://home.nps.gov/maca/learn/nature/how-mammoth-cave-formed.htm\">NPS Mammoth Cave morphology</a>; "
"<a href=\"https://www.nps.gov/grba/learn/nature/lehman-caves-dimensions.htm\">NPS Lehman Caves dimensions</a>; "
"<a href=\"https://store.steampowered.com/news/posts/?appgroupname=Deep+Rock+Galactic&amp;appids=548430&amp;enddate=1729500950&amp;feed=steam_community_announcements\">Deep Rock Galactic procedural cave design</a>; "
"<a href=\"https://www.pcgamer.com/how-to-design-a-great-metroidvania-map/\">Team Cherry map-design interview</a>.</p>\n"
"<table class=\"summary\"><caption>Part A — survivor distributions; every value uses the exact window printed in its row</caption>\n"
"<tr><th>archetype</th><th>eligible single-region rolls / hard-gate survivors</th><th>walkable fraction<br>(median; min–max)</th>"
"<th>floor area fraction<br>(median; min–max)</th><th>median clearance over walkable cells<br>(median; min–max voxels)</th>"
"<th>largest connected surface<br>(median; min–max)</th><th>window</th></tr>\n");
for (const FVoxelStratePreviewArchetypeSummary& Summary : *ArchetypeSummaries)
{
const FString SummaryWindow = !Summary.WindowSummary.IsEmpty()
? Summary.WindowSummary
: Summary.Window.IsValid() ? Summary.Window.Describe() : TEXT("unavailable");
Html += FString::Printf(
TEXT("<tr><th>%s</th><td>%d / %d</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td class=\"summary-window\">%s</td></tr>\n"),
*VF_HtmlEscape(Summary.ArchetypeName), Summary.EvaluatedCandidates,
Summary.HardGateSurvivors,
*VF_HtmlEscape(Summary.WalkableFractionSummary),
*VF_HtmlEscape(Summary.WalkableFloorAreaSummary),
*VF_HtmlEscape(Summary.MedianVerticalClearanceSummary),
*VF_HtmlEscape(Summary.LargestWalkableSurfaceSummary),
*VF_HtmlEscape(SummaryWindow));
}
Html += TEXT("</table>\n");
}
Html += FString::Printf(
TEXT("<p class=\"note\"><strong>How to read coarse images:</strong> At sample step %d, "
"hard edges can come from sampling, not necessarily hard world walls. A "
@@ -1085,20 +1166,41 @@ bool VF_WriteStratePreviewIndex(
? Candidate.Window.Describe() : WindowDescription;
const int32 DisplayIndex = Candidate.bSeasonOrder && Candidate.DepthIndex != INDEX_NONE
? Candidate.DepthIndex : Candidate.CandidateIndex;
const FString CardTitle = Candidate.bShowcaseCard && !Candidate.ArchetypeName.IsEmpty()
? FString::Printf(TEXT("%s · candidate %d"), *Candidate.ArchetypeName, Candidate.CandidateIndex)
: FString::Printf(TEXT("#%d"), DisplayIndex);
Html += FString::Printf(
TEXT("<section class=\"%s\"><h2>#%d <span class=\"badge %s\">%s</span></h2>\n"),
*CardClass, DisplayIndex, *StatusClass, *Status);
Html += FString::Printf(TEXT("<div class=\"recipe\"><code>%s</code></div>\n"),
*VF_HtmlEscape(Candidate.RecipeString));
if (Candidate.bSeasonOrder)
TEXT("<section class=\"%s\"><h2>%s <span class=\"badge %s\">%s</span></h2>\n"),
*CardClass, *VF_HtmlEscape(CardTitle), *StatusClass, *Status);
if (Candidate.bShowcaseCard)
{
Html += FString::Printf(
TEXT("<div class=\"pie-card\"><strong>PIE — type exactly, then click Apply Composer Candidate</strong>"
"<br><code>ComposerSeed = %d</code>"
"<br><code>ComposerCandidateIndex = %d</code>"
"<br><code>ComposerTargetStrateIndex = %d</code>"
"<br><code>bComposerRollStructure = %s</code></div>\n"),
Candidate.ComposerSeed, Candidate.CandidateIndex,
Candidate.ComposerTargetStrateIndex,
Candidate.bComposerRollStructure ? TEXT("true") : TEXT("false"));
}
Html += FString::Printf(TEXT("<div class=\"recipe\"><code>%s</code></div>\n"),
*VF_HtmlEscape(Candidate.RecipeString));
if (Candidate.bSeasonOrder || Candidate.bShowcaseCard)
{
const FString SelectionExtra = Candidate.bSeasonOrder
? FString::Printf(TEXT("<br><strong>Boss slot:</strong> %s"),
Candidate.bBossSlot ? TEXT("yes") : TEXT("no"))
: FString();
Html += FString::Printf(
TEXT("<p class=\"reason\" style=\"color:#d7e3f4\"><strong>Why selected:</strong> %s"
"<br><strong>Boss slot:</strong> %s</p>\n"),
"%s</p>\n"),
*VF_HtmlEscape(Candidate.SelectionReason.IsEmpty()
? TEXT("selected by the provisional season policy")
? (Candidate.bShowcaseCard
? TEXT("selected from the hard-gate survivor set by floor area, then vertical clearance")
: TEXT("selected by the provisional season policy"))
: Candidate.SelectionReason),
Candidate.bBossSlot ? TEXT("yes") : TEXT("no"));
*SelectionExtra);
}
if (Candidate.bRejected)
{
@@ -1182,6 +1284,12 @@ bool VF_WriteStratePreviewIndex(
TEXT("<div class=\"no-image\">Fine ROI requested but refused/not rendered: %s</div>\n"),
*VF_HtmlEscape(FineFailureText));
}
else if (Candidate.bFineBlank)
{
Html += TEXT("<div class=\"no-image\"><strong>Fine ROI is blank at both selected slices.</strong> "
"That is reported as a content finding; empty images are intentionally not "
"shown as evidence of a visible room.</div>\n");
}
else
{
const FString FineWindowDescription = Candidate.FineWindow.Describe();
@@ -1235,6 +1343,10 @@ bool VF_WriteStratePreviewIndex(
TEXT("<tr><th>air fraction</th><td>%s</td></tr>\n"
"<tr><th>largest component share</th><td>%s</td></tr>\n"
"<tr><th>walkable fraction</th><td>%s</td></tr>\n"
"<tr><th>walkable floor area fraction</th><td>%s</td></tr>\n"
"<tr><th>walkable floor columns</th><td>%s</td></tr>\n"
"<tr><th>largest connected surface share</th><td>%s</td></tr>\n"
"<tr><th>walkable surface components</th><td>%s</td></tr>\n"
"<tr><th>feature scale (voxels)</th><td>%s</td></tr>\n"
"<tr><th>vertical clearance (voxels)</th><td>%s</td></tr>\n"
"<tr><th>arrival → departure</th><td class=\"verdict\">%s</td></tr>\n"
@@ -1242,6 +1354,10 @@ bool VF_WriteStratePreviewIndex(
*VF_FormatMetric(Candidate.bMetricsValid, Candidate.AirFraction),
*VF_FormatMetric(Candidate.bMetricsValid, Candidate.LargestComponentShare),
*VF_FormatMetric(Candidate.bMetricsValid, Candidate.WalkableFraction),
*VF_FormatMetric(Candidate.bMetricsValid, Candidate.WalkableFloorAreaFraction),
*VF_FormatInteger64Metric(Candidate.bMetricsValid, Candidate.WalkableFloorColumns),
*VF_FormatMetric(Candidate.bMetricsValid, Candidate.LargestWalkableSurfaceShare),
*VF_FormatIntegerMetric(Candidate.bMetricsValid, Candidate.NumWalkableSurfaceComponents),
*VF_FormatMetric(Candidate.bMetricsValid, Candidate.MedianFeatureScale),
*VF_FormatIntegerMetric(Candidate.bMetricsValid, Candidate.MedianVerticalClearance),
*VF_HtmlEscape(Candidate.ArrivalDepartureVerdict),
@@ -1317,12 +1433,14 @@ bool VF_WriteStratePreviewIndex(
const FVoxelStratePreviewWindow& Window,
const TArray<FVoxelStratePreviewCandidate>& Candidates,
FString& OutIndexPath,
FString& OutError)
FString& OutError,
const TArray<FVoxelStratePreviewArchetypeSummary>* ArchetypeSummaries)
{
(void)OutputDirectory;
(void)RunTitle;
(void)Window;
(void)Candidates;
(void)ArchetypeSummaries;
OutIndexPath.Reset();
OutError = TEXT("strate preview rendering is editor-only");
return false;
@@ -192,6 +192,11 @@ struct VOXELFORGE_API FVoxelStrateMeasuredMetrics
int64 LargestComponentCells = 0;
int32 NumComponentsAtLeast1Pct = 0;
float WalkableFraction = 0.0f;
int64 WalkableFloorColumns = 0;
float WalkableFloorAreaFraction = 0.0f;
int32 NumWalkableSurfaceComponents = 0;
int64 LargestWalkableSurfaceColumns = 0;
float LargestWalkableSurfaceShare = 0.0f;
float MedianFeatureScale = 0.0f;
int32 MedianVerticalClearance = 0;
@@ -121,6 +121,17 @@ struct VOXELFORGE_API FVoxelStrateMetrics
int64 LargestComponentCells = 0;
int32 NumComponentsAtLeast1Pct = 0;
float WalkableFraction = 0.f;
// Distinct sampled XY columns that contain at least one walkable cell, divided by the
// sampled XY footprint. This is a floor-area proxy, not a volume ratio: a large chamber
// can have a small WalkableFraction while retaining a substantial walkable floor.
int64 WalkableFloorColumns = 0;
float WalkableFloorAreaFraction = 0.f;
// Four-neighbour connected components of the projected walkable columns. This deliberately
// remains a 2D surface indicator; it does not prove that floors at different Z values are
// traversable in 3D. The separate arrival/departure route check is the traversal gate.
int32 NumWalkableSurfaceComponents = 0;
int64 LargestWalkableSurfaceColumns = 0;
float LargestWalkableSurfaceShare = 0.f;
float MedianFeatureScale = 0.f;
int32 MedianVerticalClearance = 0;
+29 -1
View File
@@ -71,6 +71,13 @@ struct VOXELFORGE_API FVoxelStrateFinePreviewSettings
struct VOXELFORGE_API FVoxelStratePreviewCandidate
{
int32 CandidateIndex = INDEX_NONE;
// Showcase cards carry their archetype and the exact editor hand-off values. Ordinary
// contact-sheet callers may leave these unset and retain the legacy candidate heading.
FString ArchetypeName;
bool bShowcaseCard = false;
int32 ComposerSeed = INDEX_NONE;
int32 ComposerTargetStrateIndex = INDEX_NONE;
bool bComposerRollStructure = false;
// Season pages deliberately opt into descent order. The ordinary candidate contact sheet
// keeps its survivor/outlier ordering; a season review must read top-to-bottom like play.
bool bSeasonOrder = false;
@@ -110,6 +117,11 @@ struct VOXELFORGE_API FVoxelStratePreviewCandidate
float AirFraction = 0.0f;
float LargestComponentShare = 0.0f;
float WalkableFraction = 0.0f;
int64 WalkableFloorColumns = 0;
float WalkableFloorAreaFraction = 0.0f;
int32 NumWalkableSurfaceComponents = 0;
int64 LargestWalkableSurfaceColumns = 0;
float LargestWalkableSurfaceShare = 0.0f;
float MedianFeatureScale = 0.0f;
int32 MedianVerticalClearance = 0;
double DistanceFromCorpusCentroid = 0.0;
@@ -135,6 +147,21 @@ struct VOXELFORGE_API FVoxelStratePreviewCandidate
int32 FinePlanImageHeight = 0;
};
/** Aggregate Part A row for a stable archetype-level review table. */
struct VOXELFORGE_API FVoxelStratePreviewArchetypeSummary
{
FString ArchetypeName;
int32 EvaluatedCandidates = 0;
int32 HardGateSurvivors = 0;
FVoxelStratePreviewWindow Window;
FString WindowSummary;
FString WalkableFractionSummary;
FString WalkableFloorAreaSummary;
FString MedianVerticalClearanceSummary;
FString LargestWalkableSurfaceSummary;
};
/** Copy the exact measurement-window metadata needed by the index page. */
VOXELFORGE_API FVoxelStratePreviewWindow VF_GetStratePreviewWindow(
const FVoxelStrateSampleGrid& Grid);
@@ -184,4 +211,5 @@ VOXELFORGE_API bool VF_WriteStratePreviewIndex(
const FVoxelStratePreviewWindow& Window,
const TArray<FVoxelStratePreviewCandidate>& Candidates,
FString& OutIndexPath,
FString& OutError);
FString& OutError,
const TArray<FVoxelStratePreviewArchetypeSummary>* ArchetypeSummaries = nullptr);