Files
VoxelForge/Standalone/Research/WorldAssemblerM3/Linux159/l159-pad-stamp.patch
T
Fr0zkaandClaude Opus 5.5 c5fcedeb5b research: flat pad stamp only (L159, one attempt; kept as patch)
Clamp-between-cones pad at each tunnel end, last composition step, chamber cases removed, bounds extended (proof and
feature samplers 16/16 clean). Open-ground result looks as intended; but pads pass 8/96 (flatness up to 0.75 m, blend
up to 81 deg), interior air fails 16/16 (839-1,588 solid capsule samples), route audits fail, FI band check fails at
seed 0. Owner asked for one attempt, no tuning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-11-02 08:02:15 +01:00

1069 lines
63 KiB
Diff

diff --git a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h
index 4799b9e..13440df 100644
--- a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h
+++ b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h
@@ -512,7 +512,8 @@ namespace VoxelForgeCore
// boundary-blended host rock from widening the authored passage wall.
// v238 plans one resolved passage spline with explicit landing chambers.
// v239 plans their floor planes as the passage's continuous floor profile.
- inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 239;
+ // v240 replaces chamber boxes and shells with bounded clamp-and-cone landing pads.
+ inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 240;
enum class FieldChannel : std::uint32_t
{
diff --git a/Standalone/Core/src/ShapeWorldInternal.h b/Standalone/Core/src/ShapeWorldInternal.h
index 6b1a6af..3ce93e4 100644
--- a/Standalone/Core/src/ShapeWorldInternal.h
+++ b/Standalone/Core/src/ShapeWorldInternal.h
@@ -56,6 +56,11 @@ static bool VF_IsL130PassageReachProofAuditEnabled()
static constexpr double VF_PassageShellThicknessMetres=0.75;
static constexpr double VF_PassageWallReliefMetres=0.21;
static constexpr double VF_PassageCornerBlendMetres=0.50;
+static constexpr double VF_LandingPadConeSlope=0.2125565616700221; // tan(12 degrees)
+static constexpr double VF_LandingPadFillDepthMetres=1.25;
+static constexpr double VF_LandingPadHeadroomMetres=3.0;
+static constexpr double VF_LandingPadBlendRadiusMetres=8.0;
+static constexpr double VF_LandingPadSmoothMinMaximumMetres=0.50;
static constexpr double VF_PassageReachMarginMetres=0.01;
// Radial support in metres: shell + maximum relief + the smooth-max support
// (corner-blend width / 6) + a 1 cm guard for float and bound rounding.
@@ -1234,8 +1239,11 @@ public:
float ApplyResolvedStratePassageCarves(float density,float X,float Y,float Z,
float nativeTunnelFieldDensity=std::numeric_limits<float>::lowest(),
- float nativeFloorSupport=std::numeric_limits<float>::lowest()) const
+ float nativeFloorSupport=std::numeric_limits<float>::lowest(),
+ float* outTubeOnlyDistance=nullptr) const
{
+ if(outTubeOnlyDistance)
+ *outTubeOnlyDistance=std::numeric_limits<float>::infinity();
VoxelForgeCore::Linux77Profiler::FScopedPhase l77PassagePhase(
VoxelForgeCore::Linux77Profiler::EPhase::Passage);
if(!ResolvedLayout||ResolvedLayout->passages.empty()||!std::isfinite(X)
@@ -1280,8 +1288,6 @@ public:
double unionDistance=std::numeric_limits<double>::infinity();
double unionShellDistance=std::numeric_limits<double>::infinity();
double unionFloorShellDistance=std::numeric_limits<double>::infinity();
- double unionLandingChamberFloorShellDistance=
- std::numeric_limits<double>::infinity();
double plannedTunnelPassageShellDistance=std::numeric_limits<double>::infinity();
const std::uint32_t reliefSeed=static_cast<std::uint32_t>(Owner?Owner->Seed:0);
const auto* queryStratum=FindBoundStratumForManagerZ(Z);
@@ -1324,48 +1330,6 @@ public:
if(passageProfile)++passageProfile->passageIterations;
const auto& constants=PassageComposerConstants[passageIndex];
- const VoxelForgeCore::ResolvedLayoutPassageLandingChamberV1* landingChamber=nullptr;
- if(queryStratum&&queryStratum->Resolved)
- {
- if(queryStratum->StrateIndex==passage.upperStrateIndex)
- landingChamber=&passage.upperLandingChamber;
- else if(queryStratum->StrateIndex==passage.lowerStrateIndex)
- landingChamber=&passage.lowerLandingChamber;
- }
- if(landingChamber&&landingChamber->radiusSubvoxels>0
- &&landingChamber->clearHeightSubvoxels>0)
- {
- const double centerX=static_cast<double>(
- landingChamber->floorCenterSubvoxels[0]);
- const double centerY=static_cast<double>(
- landingChamber->floorCenterSubvoxels[1]);
- const double floorCenterZ=static_cast<double>(
- landingChamber->floorCenterSubvoxels[2])
- +(landingChamber->floorGradientQ16[0]*(qx-centerX)
- +landingChamber->floorGradientQ16[1]*(qy-centerY))/65536.0;
- const double radialDistance=DeterministicSqrt(
- (qx-centerX)*(qx-centerX)+(qy-centerY)*(qy-centerY))
- -landingChamber->radiusSubvoxels;
- const double floorDistance=floorCenterZ-qz;
- const double ceilingDistance=qz-(floorCenterZ
- +landingChamber->clearHeightSubvoxels);
- const double chamberDistance=std::max({radialDistance,
- floorDistance,ceilingDistance});
- const double chamberShellDistance=std::max(-chamberDistance,
- chamberDistance-shellThickness);
- const double chamberFloorShellDistance=std::max(
- std::max(-floorDistance,floorDistance-shellThickness),
- radialDistance);
- unionDistance=std::min(unionDistance,chamberDistance);
- unionShellDistance=std::min(unionShellDistance,chamberShellDistance);
- unionFloorShellDistance=std::min(unionFloorShellDistance,
- chamberFloorShellDistance);
- unionLandingChamberFloorShellDistance=std::min(
- unionLandingChamberFloorShellDistance,chamberFloorShellDistance);
- if(passageTargetsPlannedTunnelNetwork)
- plannedTunnelPassageShellDistance=std::min(
- plannedTunnelPassageShellDistance,chamberShellDistance);
- }
if(passageTargetsNativeCaveField&&!passageTargetsPlannedTunnelNetwork)
continue;
@@ -1658,12 +1622,9 @@ public:
}
if(!std::isfinite(unionDistance))return density;
+ if(outTubeOnlyDistance)
+ *outTubeOnlyDistance=static_cast<float>(unionDistance/Q);
const float passageAirDistance=static_cast<float>(-unionDistance/Q);
- // Keep the landing floor shell where it is exposed to the chamber. At
- // the tunnel joint, the passage void must be able to open through that
- // floor shell so its own continuous floor and shell define the throat.
- if(unionLandingChamberFloorShellDistance<0.0&&unionDistance>0.0)
- return static_cast<float>(unionLandingChamberFloorShellDistance/Q);
const auto plannedPortalWalkCorridorContains=[&]()
{
if(!queryStratum||!queryStratum->Resolved
@@ -1746,90 +1707,6 @@ public:
departureTailDistance)
||nearRouteSegment(*lastTwo,*last,PortalWalkLeadMetres,true,0.0);
};
- const auto connectedSurfaceLandingShellContains=[&]()
- {
- if(!queryStratum||!queryStratum->Resolved
- ||queryStratum->Resolved->generatorKind!="SurfaceWorld")return false;
- const double queryX=qx/qPerMetre,queryY=qy/qPerMetre,queryZ=qz/qPerMetre;
- constexpr double ChamberShellThicknessMetres=0.75;
- constexpr double RouteClearanceMetres=1.25;
- constexpr double RouteHeightClearanceMetres=1.5;
- constexpr double PortalWalkLeadMetres=9.0;
- for(const auto& passage:ResolvedLayout->passages)
- {
- if(passage.upperStrateIndex!=queryStratum->StrateIndex)continue;
- const auto& chamber=passage.upperLandingChamber;
- if(chamber.radiusSubvoxels<=0||chamber.clearHeightSubvoxels<=0)continue;
- const double cx=static_cast<double>(chamber.floorCenterSubvoxels[0])/qPerMetre;
- const double cy=static_cast<double>(chamber.floorCenterSubvoxels[1])/qPerMetre;
- const double floorZ=static_cast<double>(chamber.floorCenterSubvoxels[2])/qPerMetre
- +chamber.floorGradientQ16[0]/65536.0*(queryX-cx)
- +chamber.floorGradientQ16[1]/65536.0*(queryY-cy);
- const double radialDistance=std::hypot(queryX-cx,queryY-cy)
- -static_cast<double>(chamber.radiusSubvoxels)/qPerMetre;
- const double floorDistance=floorZ-queryZ;
- const double ceilingDistance=queryZ-(floorZ
- +static_cast<double>(chamber.clearHeightSubvoxels)/qPerMetre);
- const double chamberDistance=std::max({radialDistance,
- floorDistance,ceilingDistance});
- const double chamberShellDistance=std::max(-chamberDistance,
- chamberDistance-ChamberShellThicknessMetres);
- if(chamberDistance<=0.0||chamberShellDistance>=0.0)continue;
-
- std::vector<const VoxelForgeCore::ResolvedLayoutAnchorV2*> route;
- for(const auto& anchor:queryStratum->Resolved->anchors)
- if(anchor.kind=="route_point"&&anchor.values.size()>=3)
- route.push_back(&anchor);
- if(route.size()<2)continue;
- const double mouthZ=static_cast<double>(passage.upperMouthSubvoxels[2])
- /qPerMetre;
- std::size_t endpoint=route.size();
- double endpointDistanceSquared=1.0;
- for(std::size_t index=0;index<route.size();++index)
- {
- const double x=route[index]->values[0]/qPerMetre;
- const double y=route[index]->values[1]/qPerMetre;
- const double z=route[index]->values[2]/qPerMetre;
- const double dx=x-cx,dy=y-cy;
- const double distanceSquared=dx*dx+dy*dy;
- if(distanceSquared<endpointDistanceSquared
- &&std::abs(z-mouthZ)<=1.5)
- {endpoint=index;endpointDistanceSquared=distanceSquared;}
- }
- if(endpoint==route.size())continue;
- std::vector<double> routeDistance(route.size(),0.0);
- for(std::size_t index=1;index<route.size();++index)
- {
- const double dx=(route[index]->values[0]-route[index-1]->values[0])
- /qPerMetre;
- const double dy=(route[index]->values[1]-route[index-1]->values[1])
- /qPerMetre;
- routeDistance[index]=routeDistance[index-1]+std::hypot(dx,dy);
- }
- for(std::size_t index=1;index<route.size();++index)
- {
- const auto& a=*route[index-1];
- const auto& b=*route[index];
- const double ax=a.values[0]/qPerMetre,ay=a.values[1]/qPerMetre;
- const double az=a.values[2]/qPerMetre;
- const double dx=(b.values[0]-a.values[0])/qPerMetre;
- const double dy=(b.values[1]-a.values[1])/qPerMetre;
- const double horizontalLengthSquared=dx*dx+dy*dy;
- if(horizontalLengthSquared<=1.0e-12)continue;
- const double t=std::clamp(((queryX-ax)*dx+(queryY-ay)*dy)
- /horizontalLengthSquared,0.0,1.0);
- const double alongFromEndpoint=std::abs(
- std::lerp(routeDistance[index-1],routeDistance[index],t)
- -routeDistance[endpoint]);
- if(alongFromEndpoint>PortalWalkLeadMetres)continue;
- const double routeX=ax+t*dx,routeY=ay+t*dy;
- const double routeZ=az+t*(b.values[2]-a.values[2])/qPerMetre;
- if(std::hypot(queryX-routeX,queryY-routeY)<=RouteClearanceMetres
- &&std::abs(queryZ-routeZ)<=RouteHeightClearanceMetres)return true;
- }
- }
- return false;
- };
if(plannedTunnelStratum
&&nativeTunnelFieldDensity!=std::numeric_limits<float>::lowest()
&&nativeTunnelFieldDensity<0.0f)
@@ -1893,13 +1770,95 @@ public:
return std::max(density,passageAirDistance);
if(unionDistance>0.0&&unionShellDistance<0.0&&density>=0.0f)
{
- if(connectedSurfaceLandingShellContains())
- return std::max(density,passageAirDistance);
return static_cast<float>(unionShellDistance/Q);
}
return std::max(density,passageAirDistance);
}
+ float ApplyResolvedLandingPadStamps(float density,float X,float Y,float Z,
+ float tubeOnlyDistance) const
+ {
+ if(!ResolvedLayout||ResolvedLayout->passages.empty()||!std::isfinite(X)
+ ||!std::isfinite(Y)||!std::isfinite(Z))return density;
+ const auto* bound=FindBoundStratumForManagerZ(Z);
+ if(!bound||!bound->Resolved)return density;
+ const double coreMetres=std::max<std::uint32_t>(1,
+ ResolvedLayout->coreVoxelSizeMicrometres)/1000000.0;
+ constexpr double Q=static_cast<double>(
+ VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
+ const double qPerMetre=Q/coreMetres;
+ const double xMetres=static_cast<double>(X)*coreMetres;
+ const double yMetres=static_cast<double>(Y)*coreMetres;
+ const double zMetres=static_cast<double>(Z)*coreMetres;
+ const double tubeDistanceMetres=static_cast<double>(tubeOnlyDistance)*coreMetres;
+ const auto* tileQueries=FScopedPassageTileQueries::CurrentFor(*this);
+ const auto* candidatePassages=tileQueries
+ ?&tileQueries->routePassageIndices():nullptr;
+ const std::size_t candidateCount=candidatePassages
+ ?candidatePassages->size():ResolvedLayout->passages.size();
+ float result=density;
+ constexpr double CosTwelveDegrees=0.9781476007338057;
+ constexpr double OuterReachMetres=VF_PassageInfluenceReachMetres;
+ for(std::size_t offset=0;offset<candidateCount;++offset)
+ {
+ const std::size_t passageIndex=candidatePassages
+ ?(*candidatePassages)[offset]:offset;
+ if(passageIndex>=ResolvedLayout->passages.size())continue;
+ const auto& passage=ResolvedLayout->passages[passageIndex];
+ const auto applyPad=[&](
+ const VoxelForgeCore::ResolvedLayoutPassageLandingChamberV1& pad)
+ {
+ if(pad.radiusSubvoxels<=0)return;
+ const double centerX=static_cast<double>(pad.floorCenterSubvoxels[0])
+ /qPerMetre;
+ const double centerY=static_cast<double>(pad.floorCenterSubvoxels[1])
+ /qPerMetre;
+ const double floorP=static_cast<double>(pad.floorCenterSubvoxels[2])
+ /qPerMetre;
+ const double radius=std::max(3.0,
+ static_cast<double>(std::max<std::int64_t>(0,
+ passage.mouthRadiusSubvoxels))/qPerMetre+1.0);
+ const double dx=xMetres-centerX,dy=yMetres-centerY;
+ const double radial=DeterministicSqrt(dx*dx+dy*dy);
+ const double outerRadius=radius+VF_LandingPadBlendRadiusMetres;
+ if(radial>outerRadius+OuterReachMetres
+ ||zMetres<floorP-VF_LandingPadFillDepthMetres
+ -VF_LandingPadConeSlope*VF_LandingPadBlendRadiusMetres
+ -OuterReachMetres
+ ||zMetres>floorP+VF_LandingPadHeadroomMetres
+ +VF_LandingPadConeSlope*VF_LandingPadBlendRadiusMetres
+ +OuterReachMetres)return;
+ const double coneRun=std::max(0.0,radial-radius);
+ const double lower=floorP-VF_LandingPadConeSlope*coneRun;
+ const double upper=floorP+VF_LandingPadConeSlope*coneRun;
+ const double fillDistanceMetres=std::max({
+ CosTwelveDegrees*(zMetres-lower),
+ CosTwelveDegrees*(lower-VF_LandingPadFillDepthMetres-zMetres),
+ radial-outerRadius});
+ const double airDistanceMetres=std::min({
+ CosTwelveDegrees*(zMetres-upper),
+ CosTwelveDegrees*(upper+VF_LandingPadHeadroomMetres-zMetres),
+ outerRadius-radial});
+ const double fillVoxels=fillDistanceMetres/coreMetres;
+ const double airVoxels=airDistanceMetres/coreMetres;
+ const double maskedFill=std::max(fillVoxels,-tubeDistanceMetres/coreMetres);
+ const double smoothWidthMetres=VF_LandingPadSmoothMinMaximumMetres
+ *std::clamp(radial-radius,0.0,1.0);
+ const float next=static_cast<float>(result);
+ const float fill=static_cast<float>(maskedFill);
+ const float width=static_cast<float>(smoothWidthMetres/coreMetres);
+ const float clamped=width>0.0f
+ ?SmoothMinDistance(next,fill,width):std::min(next,fill);
+ result=std::max(clamped,static_cast<float>(airVoxels));
+ };
+ if(passage.upperStrateIndex==bound->StrateIndex)
+ applyPad(passage.upperLandingChamber);
+ if(passage.lowerStrateIndex==bound->StrateIndex)
+ applyPad(passage.lowerLandingChamber);
+ }
+ return result;
+ }
+
float ApplyResolvedStrateComposition(float density,float X,float Y,float Z) const
{
float graphDensity=0.0f;
@@ -1930,6 +1889,7 @@ public:
}
float composed=boundary;
float stamped=composed;
+ float tubeOnlyDistance=std::numeric_limits<float>::infinity();
if(!StampRuntime.empty())
{
VoxelForgeCore::Linux77Profiler::FScopedPhase l77StampPhase(
@@ -1963,12 +1923,13 @@ public:
const float nativeTunnelFieldDensity=nativeTunnelField
?graphDensity:std::numeric_limits<float>::lowest();
stamped=ApplyResolvedStratePassageCarves(stamped,X,Y,Z,
- nativeTunnelFieldDensity,nativeGraphFloorSupport);
+ nativeTunnelFieldDensity,nativeGraphFloorSupport,
+ &tubeOnlyDistance);
}
if(bTraceTnComposition)
std::printf("[L75TNCompositionTrace] input=%.9g graph=%.9g boundary=%.9g passage=%.9g stamp=%.9g native=1\n",
density,graphDensity,boundary,composed,stamped);
- return stamped;
+ return ApplyResolvedLandingPadStamps(stamped,X,Y,Z,tubeOnlyDistance);
}
// The legacy landmark demo retains its compatibility floor cap. Native caves
// already combine each floor with its wall before the single void carve above.
@@ -1985,12 +1946,14 @@ public:
VoxelForgeCore::Linux77Profiler::EPhase::Passage);
FScopedLegacyFieldTermTimer TermTimer(
ELegacyFieldTerm::ComposerPassage);
- final=ApplyResolvedStratePassageCarves(final,X,Y,Z);
+ final=ApplyResolvedStratePassageCarves(final,X,Y,Z,
+ std::numeric_limits<float>::lowest(),
+ std::numeric_limits<float>::lowest(),&tubeOnlyDistance);
}
if(bTraceTnComposition)
std::printf("[L75TNCompositionTrace] input=%.9g graph=%.9g boundary=%.9g passage=%.9g stamp=%.9g final=%.9g native=0\n",
density,graphDensity,boundary,composed,stamped,final);
- return final;
+ return ApplyResolvedLandingPadStamps(final,X,Y,Z,tubeOnlyDistance);
}
bool ResolvedPassageIntersectsBox(const int32 minimum[3],const int32 maximum[3]) const
@@ -2033,26 +1996,35 @@ public:
}
if(overlaps(low,high))return true;
}
- const auto chamberOverlaps=[&](
- const VoxelForgeCore::ResolvedLayoutPassageLandingChamberV1& chamber)
- {
- const double cx=static_cast<double>(chamber.floorCenterSubvoxels[0])/Q;
- const double cy=static_cast<double>(chamber.floorCenterSubvoxels[1])/Q;
- const double cz=static_cast<double>(chamber.floorCenterSubvoxels[2])/Q;
- const double radius=static_cast<double>(chamber.radiusSubvoxels)/Q;
- const double floorExtent=radius*std::hypot(
- static_cast<double>(chamber.floorGradientQ16[0])/65536.0,
- static_cast<double>(chamber.floorGradientQ16[1])/65536.0);
+ const auto padOverlaps=[&](
+ const VoxelForgeCore::ResolvedLayoutPassageLandingChamberV1& pad,
+ std::int64_t mouthRadiusSubvoxels)
+ {
+ if(pad.radiusSubvoxels<=0)return false;
+ const double cx=static_cast<double>(pad.floorCenterSubvoxels[0])/Q;
+ const double cy=static_cast<double>(pad.floorCenterSubvoxels[1])/Q;
+ const double pz=static_cast<double>(pad.floorCenterSubvoxels[2])/Q;
+ const double qPerMetre=Q*1000000.0/
+ std::max<std::uint32_t>(1,ResolvedLayout->coreVoxelSizeMicrometres);
+ const double coreMetres=Q/qPerMetre;
+ const double radius=std::max(3.0,
+ static_cast<double>(std::max<std::int64_t>(0,mouthRadiusSubvoxels))
+ /qPerMetre+1.0);
+ const double outerRadius=radius+VF_LandingPadBlendRadiusMetres;
const double extent=reachCoreVoxels+0.01;
- const double low[3]={cx-radius-extent,cy-radius-extent,
- cz-floorExtent-extent};
- const double high[3]={cx+radius+extent,cy+radius+extent,
- cz+floorExtent+static_cast<double>(
- chamber.clearHeightSubvoxels)/Q+extent};
+ const double coneHeight=VF_LandingPadConeSlope
+ *VF_LandingPadBlendRadiusMetres;
+ const double low[3]={cx-outerRadius/coreMetres-extent,
+ cy-outerRadius/coreMetres-extent,
+ pz-(VF_LandingPadFillDepthMetres+coneHeight)/coreMetres-extent};
+ const double high[3]={cx+outerRadius/coreMetres+extent,
+ cy+outerRadius/coreMetres+extent,
+ pz+(VF_LandingPadHeadroomMetres+coneHeight)/coreMetres+extent};
return overlaps(low,high);
};
- if(chamberOverlaps(passage.upperLandingChamber)
- ||chamberOverlaps(passage.lowerLandingChamber))return true;
+ if(padOverlaps(passage.upperLandingChamber,passage.mouthRadiusSubvoxels)
+ ||padOverlaps(passage.lowerLandingChamber,passage.mouthRadiusSubvoxels))
+ return true;
}
return false;
}
@@ -5415,6 +5387,9 @@ private:
if(!ResolvedLayout||ResolvedLayout->passages.empty())return;
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
constexpr std::size_t CandidateFineSamplesPerSpan=5;
+ const double coreMetres=std::max<std::uint32_t>(1,
+ ResolvedLayout->coreVoxelSizeMicrometres)/1000000.0;
+ const double qPerMetre=Q/coreMetres;
const double requiredReachCoreVoxels=VF_PassageInfluenceReachCoreVoxels(
ResolvedLayout->coreVoxelSizeMicrometres);
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();
@@ -5471,40 +5446,47 @@ private:
std::ceil((localMaximum[axis]+localExtent)*Q));
}
}
- const auto includeLandingChamber=[&](
- const VoxelForgeCore::ResolvedLayoutPassageLandingChamberV1& chamber)
- {
- const double cx=static_cast<double>(chamber.floorCenterSubvoxels[0])/Q;
- const double cy=static_cast<double>(chamber.floorCenterSubvoxels[1])/Q;
- const double cz=static_cast<double>(chamber.floorCenterSubvoxels[2])/Q;
- const double radius=static_cast<double>(chamber.radiusSubvoxels)/Q;
- const double gx=static_cast<double>(chamber.floorGradientQ16[0])/65536.0;
- const double gy=static_cast<double>(chamber.floorGradientQ16[1])/65536.0;
- const double floorExtent=radius*std::hypot(gx,gy);
- const double height=static_cast<double>(
- chamber.clearHeightSubvoxels)/Q;
- const double extent=requiredReachCoreVoxels+0.01;
- const std::array<double,3> minimum{
- cx-radius-extent,cy-radius-extent,cz-floorExtent-extent};
- const std::array<double,3> maximum{
- cx+radius+extent,cy+radius+extent,
- cz+floorExtent+height+extent};
+ const auto includeLandingPad=[&](
+ const VoxelForgeCore::ResolvedLayoutPassageLandingChamberV1& pad,
+ std::int64_t mouthRadiusSubvoxels)
+ {
+ if(pad.radiusSubvoxels<=0)return;
+ const double cx=static_cast<double>(pad.floorCenterSubvoxels[0])/Q;
+ const double cy=static_cast<double>(pad.floorCenterSubvoxels[1])/Q;
+ const double pz=static_cast<double>(pad.floorCenterSubvoxels[2])/Q;
+ const double radius=std::max(3.0,
+ static_cast<double>(std::max<std::int64_t>(0,mouthRadiusSubvoxels))
+ /qPerMetre+1.0);
+ const double outerRadius=radius+VF_LandingPadBlendRadiusMetres;
+ const double coneHeight=VF_LandingPadConeSlope
+ *VF_LandingPadBlendRadiusMetres;
+ const std::array<double,3> baseMinimum{
+ cx-outerRadius/coreMetres,
+ cy-outerRadius/coreMetres,
+ pz-(VF_LandingPadFillDepthMetres+coneHeight)/coreMetres};
+ const std::array<double,3> baseMaximum{
+ cx+outerRadius/coreMetres,
+ cy+outerRadius/coreMetres,
+ pz+(VF_LandingPadHeadroomMetres+coneHeight)/coreMetres};
FPassageProofBound proof;
+ const double extent=requiredReachCoreVoxels+0.01;
for(std::size_t axis=0;axis<3;++axis)
{
- routeMinimum[axis]=std::min(routeMinimum[axis],minimum[axis]);
- routeMaximum[axis]=std::max(routeMaximum[axis],maximum[axis]);
- proof.minimum[axis]=minimum[axis];
- proof.maximum[axis]=maximum[axis];
+ routeMinimum[axis]=std::min(routeMinimum[axis],baseMinimum[axis]);
+ routeMaximum[axis]=std::max(routeMaximum[axis],baseMaximum[axis]);
+ proof.minimum[axis]=baseMinimum[axis]-extent;
+ proof.maximum[axis]=baseMaximum[axis]+extent;
}
PassageProofBounds.push_back(proof);
};
if(segmentIndex==0)
- includeLandingChamber(
- ResolvedLayout->passages[passageIndex].upperLandingChamber);
+ includeLandingPad(ResolvedLayout->passages[passageIndex]
+ .upperLandingChamber,ResolvedLayout->passages[passageIndex]
+ .mouthRadiusSubvoxels);
if(segmentEndIndex==segmentCount)
- includeLandingChamber(
- ResolvedLayout->passages[passageIndex].lowerLandingChamber);
+ includeLandingPad(ResolvedLayout->passages[passageIndex]
+ .lowerLandingChamber,ResolvedLayout->passages[passageIndex]
+ .mouthRadiusSubvoxels);
const double routeExtent=maximumRadiusSubvoxels/Q
+requiredReachCoreVoxels+0.01;
const double tileExtent=routeExtent;
@@ -5629,6 +5611,108 @@ private:
}
}
}
+ for(std::size_t passageIndex=0;
+ passageIndex<ResolvedLayout->passages.size();++passageIndex)
+ {
+ const auto& passage=ResolvedLayout->passages[passageIndex];
+ const auto sampleLandingPad=[&](
+ const VoxelForgeCore::ResolvedLayoutPassageLandingChamberV1& pad)
+ {
+ if(pad.radiusSubvoxels<=0)return;
+ const double centerX=static_cast<double>(
+ pad.floorCenterSubvoxels[0])/qPerMetre;
+ const double centerY=static_cast<double>(
+ pad.floorCenterSubvoxels[1])/qPerMetre;
+ const double floorP=static_cast<double>(
+ pad.floorCenterSubvoxels[2])/qPerMetre;
+ const double radius=std::max(3.0,
+ static_cast<double>(std::max<std::int64_t>(0,
+ passage.mouthRadiusSubvoxels))/qPerMetre+1.0);
+ const double probeRadius=radius+VF_LandingPadBlendRadiusMetres-0.25;
+ const std::array<double,2> probeHeights{
+ floorP-0.5*VF_LandingPadFillDepthMetres,
+ floorP+0.5*VF_LandingPadHeadroomMetres};
+ for(const double probeZ:probeHeights)
+ {
+ const double sampleQ[3]={centerX*qPerMetre,
+ centerY*qPerMetre,probeZ*qPerMetre};
+ const double sampleVoxel[3]={sampleQ[0]/Q,
+ sampleQ[1]/Q,sampleQ[2]/Q};
+ const float contribution=ApplyResolvedLandingPadStamps(
+ 0.0f,static_cast<float>(sampleVoxel[0]),
+ static_cast<float>(sampleVoxel[1]),
+ static_cast<float>(sampleVoxel[2]),
+ std::numeric_limits<float>::infinity());
+ ++sampled;
+ if(std::abs(contribution)<=1.0e-6f)continue;
+ ++signRelevant;
+ if(PassageProofNodes.empty()
+ ||!PassageProofBoundsOverlapNode(0,sampleVoxel,sampleVoxel))
+ ++proofMisses;
+ std::array<std::int64_t,3> sampleMinimum{},sampleMaximum{};
+ for(std::size_t axis=0;axis<3;++axis)
+ {
+ sampleMinimum[axis]=static_cast<std::int64_t>(
+ std::floor(sampleQ[axis]));
+ sampleMaximum[axis]=static_cast<std::int64_t>(
+ std::ceil(sampleQ[axis]));
+ }
+ const auto candidates=BuildPassageFeatureTileList(
+ sampleMinimum,sampleMaximum);
+ const bool featureContainsPassage=std::any_of(
+ candidates.begin(),candidates.end(),
+ [&](std::uint32_t featureIndex)
+ {
+ return featureIndex<PassageTileFeatures.size()
+ &&PassageTileFeatures[featureIndex].passageIndex
+ ==passageIndex;
+ });
+ if(!featureContainsPassage)++featureMisses;
+
+ const double edgeSampleQ[3]={
+ (centerX+probeRadius)*qPerMetre,
+ centerY*qPerMetre,
+ (probeZ<=floorP
+ ?floorP-VF_LandingPadConeSlope
+ *(probeRadius-radius)-0.125
+ :floorP+VF_LandingPadConeSlope
+ *(probeRadius-radius)+0.25)*qPerMetre};
+ const double edgeSampleVoxel[3]={edgeSampleQ[0]/Q,
+ edgeSampleQ[1]/Q,edgeSampleQ[2]/Q};
+ const float edgeContribution=ApplyResolvedLandingPadStamps(
+ 0.0f,static_cast<float>(edgeSampleVoxel[0]),
+ static_cast<float>(edgeSampleVoxel[1]),
+ static_cast<float>(edgeSampleVoxel[2]),
+ std::numeric_limits<float>::infinity());
+ ++sampled;
+ if(std::abs(edgeContribution)<=1.0e-6f)continue;
+ ++signRelevant;
+ if(PassageProofNodes.empty()
+ ||!PassageProofBoundsOverlapNode(0,edgeSampleVoxel,edgeSampleVoxel))
+ ++proofMisses;
+ for(std::size_t axis=0;axis<3;++axis)
+ {
+ sampleMinimum[axis]=static_cast<std::int64_t>(
+ std::floor(edgeSampleQ[axis]));
+ sampleMaximum[axis]=static_cast<std::int64_t>(
+ std::ceil(edgeSampleQ[axis]));
+ }
+ const auto edgeCandidates=BuildPassageFeatureTileList(
+ sampleMinimum,sampleMaximum);
+ const bool edgeFeatureContainsPassage=std::any_of(
+ edgeCandidates.begin(),edgeCandidates.end(),
+ [&](std::uint32_t featureIndex)
+ {
+ return featureIndex<PassageTileFeatures.size()
+ &&PassageTileFeatures[featureIndex].passageIndex
+ ==passageIndex;
+ });
+ if(!edgeFeatureContainsPassage)++featureMisses;
+ }
+ };
+ sampleLandingPad(passage.upperLandingChamber);
+ sampleLandingPad(passage.lowerLandingChamber);
+ }
const bool auditPassed=sampled>0&&signRelevant>0
&&proofMisses==0&&featureMisses==0;
std::printf("[L130PassageReachProofAudit] reach_m=%.3f sampled=%llu sign_relevant=%llu proof_misses=%llu feature_misses=%llu status=%s\n",
diff --git a/Standalone/Source/StreamBenchmarkPipeline.h b/Standalone/Source/StreamBenchmarkPipeline.h
index 2fcf4b5..1b502ed 100644
--- a/Standalone/Source/StreamBenchmarkPipeline.h
+++ b/Standalone/Source/StreamBenchmarkPipeline.h
@@ -4703,78 +4703,6 @@ namespace StandaloneStreamBenchmark
[index](const auto& s){return s.strateIndex==index;});
return it==ResolvedLayout->strates.end()?nullptr:&*it;
};
- const auto landingChamberConnectionContains=[&](
- const VoxelForgeCore::ResolvedLayoutPassageV1& passage,bool upper,
- double xMetres,double yMetres,double zMetres)
- {
- const auto& chamber=upper?passage.upperLandingChamber
- :passage.lowerLandingChamber;
- const auto* stratum=findStratum(upper?passage.upperStrateIndex
- :passage.lowerStrateIndex);
- if(!stratum||chamber.radiusSubvoxels<=0)return false;
- std::vector<const VoxelForgeCore::ResolvedLayoutAnchorV2*> route;
- for(const auto& anchor:stratum->anchors)
- if(anchor.kind=="route_point"&&anchor.values.size()>=3)
- route.push_back(&anchor);
- if(route.size()<2)return false;
- const double cx=qToMetres(chamber.floorCenterSubvoxels[0]);
- const double cy=qToMetres(chamber.floorCenterSubvoxels[1]);
- const double mouthZ=qToMetres(upper?passage.upperMouthSubvoxels[2]
- :passage.lowerMouthSubvoxels[2]);
- std::size_t endpoint=route.size();
- double endpointDistanceSquared=1.0;
- for(std::size_t index=0;index<route.size();++index)
- {
- const double dx=qToMetres(route[index]->values[0])-cx;
- const double dy=qToMetres(route[index]->values[1])-cy;
- const double z=qToMetres(route[index]->values[2]);
- const double distanceSquared=dx*dx+dy*dy;
- if(distanceSquared<endpointDistanceSquared
- &&std::abs(z-mouthZ)<=1.5)
- {endpoint=index;endpointDistanceSquared=distanceSquared;}
- }
- if(endpoint==route.size())return false;
- std::vector<double> routeDistance(route.size(),0.0);
- for(std::size_t index=1;index<route.size();++index)
- {
- const double dx=qToMetres(route[index]->values[0]
- -route[index-1]->values[0]);
- const double dy=qToMetres(route[index]->values[1]
- -route[index-1]->values[1]);
- routeDistance[index]=routeDistance[index-1]+std::hypot(dx,dy);
- }
- for(std::size_t index=1;index<route.size();++index)
- {
- const auto& a=*route[index-1];
- const auto& b=*route[index];
- const double ax=qToMetres(a.values[0]),ay=qToMetres(a.values[1]);
- const double az=qToMetres(a.values[2]);
- const double dx=qToMetres(b.values[0]-a.values[0]);
- const double dy=qToMetres(b.values[1]-a.values[1]);
- const double lengthSquared=dx*dx+dy*dy;
- if(lengthSquared<=1.0e-12)continue;
- const double t=std::clamp(((xMetres-ax)*dx+(yMetres-ay)*dy)
- /lengthSquared,0.0,1.0);
- const double fromEndpoint=std::abs(std::lerp(
- routeDistance[index-1],routeDistance[index],t)
- -routeDistance[endpoint]);
- if(fromEndpoint>9.0)continue;
- const double routeX=ax+t*dx,routeY=ay+t*dy;
- const double routeZ=az+t*qToMetres(b.values[2]-a.values[2]);
- const double lateralDistance=std::hypot(xMetres-routeX,
- yMetres-routeY);
- if(stratum->generatorKind=="TunnelNetwork"
- ||stratum->generatorKind=="VerticalShafts")
- {
- const double routeFloor=routeZ-1.13;
- if(lateralDistance<=0.80&&zMetres>=routeFloor-0.30
- &&zMetres<=routeFloor+2.25)return true;
- }
- else if(lateralDistance<=1.25
- &&std::abs(zMetres-routeZ)<=1.5)return true;
- }
- return false;
- };
const auto controlSnapshot=CoreSnapshot.withoutResolvedLayoutPassages();
VoxelForgeCore::World blockedWorld(controlSnapshot);
std::string blockedCompileError;
@@ -5153,89 +5081,6 @@ namespace StandaloneStreamBenchmark
if(interior<=0.0f)++audit.interiorViolations;
}
}
- constexpr std::array<double,3> chamberFloorRadii{{0.0,2.5,5.0}};
- constexpr std::array<double,2> chamberWallHeights{{1.13,3.20}};
- constexpr std::size_t ChamberAngularSamples=16;
- for(const bool upper:{true,false})
- {
- const auto& chamber=upper?passage.upperLandingChamber
- :passage.lowerLandingChamber;
- if(chamber.radiusSubvoxels<=0||chamber.clearHeightSubvoxels<=0)continue;
- const double centerX=qToMetres(chamber.floorCenterSubvoxels[0]);
- const double centerY=qToMetres(chamber.floorCenterSubvoxels[1]);
- const double baseFloorZ=qToMetres(chamber.floorCenterSubvoxels[2]);
- const double radius=qToMetres(chamber.radiusSubvoxels);
- const double gx=chamber.floorGradientQ16[0]/65536.0;
- const double gy=chamber.floorGradientQ16[1]/65536.0;
- for(const double radial:chamberFloorRadii)
- {
- if(radial>=radius-1.0)continue;
- const std::size_t angles=radial==0.0?1:ChamberAngularSamples;
- for(std::size_t angleIndex=0;angleIndex<angles;++angleIndex)
- {
- const double angle=2.0*3.14159265358979323846
- *static_cast<double>(angleIndex)/ChamberAngularSamples;
- const double sampleX=centerX+radial*std::cos(angle);
- const double sampleY=centerY+radial*std::sin(angle);
- const double floorZ=baseFloorZ+gx*(sampleX-centerX)
- +gy*(sampleY-centerY);
- for(const double depth:PassageShellAuditDepthsMetres)
- {
- const double sampleZ=floorZ-depth;
- const VoxelForgeCore::Vec3d position{sampleX/coreMetres,
- sampleY/coreMetres,sampleZ/coreMetres};
- if(Native.densityAt(position)<0.0f)continue;
- if(landingChamberConnectionContains(passage,upper,
- sampleX,sampleY,sampleZ))
- {++audit.chamberConnectionSkips;continue;}
- ++audit.chamberFloorSamples;
- const float density=Field.densityAt(position);
- if(density>=PassageShellDensityTolerance)
- {
- ++audit.chamberFloorViolations;
- std::printf("[PassageLandingChamberShellSample] id=%s side=%s kind=floor_missing xyz_m=(%.3f,%.3f,%.3f) native=%.6f density=%.6f\n",
- passage.stableId.c_str(),upper?"upper":"lower",
- sampleX,sampleY,sampleZ,Native.densityAt(position),density);
- }
- }
- }
- }
- for(std::size_t angleIndex=0;angleIndex<ChamberAngularSamples;
- ++angleIndex)
- {
- const double angle=2.0*3.14159265358979323846
- *static_cast<double>(angleIndex)/ChamberAngularSamples;
- const double nx=std::cos(angle),ny=std::sin(angle);
- for(const double depth:PassageShellAuditDepthsMetres)
- for(const double height:chamberWallHeights)
- {
- const double sampleX=centerX+(radius+depth)*nx;
- const double sampleY=centerY+(radius+depth)*ny;
- const double floorZ=baseFloorZ+gx*(sampleX-centerX)
- +gy*(sampleY-centerY);
- const double sampleZ=floorZ+height;
- const VoxelForgeCore::Vec3d position{sampleX/coreMetres,
- sampleY/coreMetres,sampleZ/coreMetres};
- if(Native.densityAt(position)<0.0f)continue;
- if(passageInteriorWinsAt(sampleX,sampleY,sampleZ,
- std::numeric_limits<std::size_t>::max(),
- std::numeric_limits<std::size_t>::max(),passageIndex)
- ||landingChamberConnectionContains(passage,upper,
- sampleX,sampleY,sampleZ))
- {++audit.chamberConnectionSkips;continue;}
- ++audit.chamberWallSamples;
- const float density=Field.densityAt(position);
- if(density>=PassageShellDensityTolerance)
- {
- ++audit.chamberWallViolations;
- std::printf("[PassageLandingChamberShellSample] id=%s side=%s kind=wall_missing xyz_m=(%.3f,%.3f,%.3f) native=%.6f density=%.6f radial_m=%.3f height_m=%.3f\n",
- passage.stableId.c_str(),upper?"upper":"lower",
- sampleX,sampleY,sampleZ,Native.densityAt(position),density,
- radius+depth,height);
- }
- }
- }
- }
return audit;
};
const auto mouthInContent=[&](std::int32_t index,
@@ -5305,10 +5150,257 @@ namespace StandaloneStreamBenchmark
}
return total;
};
+ struct FLandingPadAudit
+ {
+ std::uint64_t flatSamples=0,flatFailures=0;
+ std::uint64_t supportSamples=0,supportFailures=0;
+ std::uint64_t headroomSamples=0,headroomFailures=0;
+ std::uint64_t blendRootSamples=0,blendMissing=0,blendGradeFailures=0;
+ std::uint64_t negativeControlsDetected=0;
+ double maximumFlatnessMetres=0.0,maximumGradeDegrees=0.0;
+ bool passed=false;
+ };
+ const auto auditLandingPad=[&](const VoxelForgeCore::World& field,
+ const VoxelForgeCore::ResolvedLayoutPassageV1& passage,bool upper)
+ {
+ constexpr double ConeSlope=0.2125565616700221;
+ constexpr double CosTwelveDegrees=0.9781476007338057;
+ constexpr double FillDepthMetres=1.25,HeadroomMetres=3.0;
+ constexpr double BlendRadiusMetres=8.0;
+ constexpr double FlatnessLimitMetres=0.020;
+ constexpr double GradeLimitDegrees=15.0;
+ constexpr std::size_t RadialDirections=36;
+ constexpr double FloorSearchStepMetres=0.125;
+ FLandingPadAudit audit;
+ const auto& pad=upper?passage.upperLandingChamber
+ :passage.lowerLandingChamber;
+ if(pad.radiusSubvoxels<=0)return audit;
+ const double centerX=qToMetres(pad.floorCenterSubvoxels[0]);
+ const double centerY=qToMetres(pad.floorCenterSubvoxels[1]);
+ const double floorP=qToMetres(pad.floorCenterSubvoxels[2]);
+ const double radius=std::max(3.0,
+ qToMetres(std::max<std::int64_t>(0,passage.mouthRadiusSubvoxels))+1.0);
+ const auto densityMetres=[&](const VoxelForgeCore::World& source,
+ double x,double y,double z)
+ {
+ const float value=source.densityAt({x/coreMetres,y/coreMetres,
+ z/coreMetres});
+ return std::isfinite(value)?static_cast<double>(value)*coreMetres
+ :std::numeric_limits<double>::quiet_NaN();
+ };
+ struct FFloorRoot
+ {double z=0.0;std::uint32_t crossings=0;bool found=false;};
+ const auto findFloorRoot=[&](auto&& sampleDensity,double x,double y,
+ double low,double high)
+ {
+ FFloorRoot result;
+ double previousZ=low;
+ double previous=sampleDensity(x,y,previousZ);
+ if(!std::isfinite(previous))return result;
+ for(double z=low+FloorSearchStepMetres;
+ z<high+FloorSearchStepMetres*0.25;
+ z=std::min(high,z+FloorSearchStepMetres))
+ {
+ const double currentZ=std::min(z,high);
+ const double current=sampleDensity(x,y,currentZ);
+ if(!std::isfinite(current))return FFloorRoot{};
+ if(previous<0.0&&current>=0.0)
+ {
+ const double denominator=current-previous;
+ const double t=std::abs(denominator)>1.0e-12
+ ?std::clamp(-previous/denominator,0.0,1.0):0.0;
+ const double root=std::lerp(previousZ,currentZ,t);
+ ++result.crossings;
+ if(!result.found)result.z=root;
+ result.found=true;
+ }
+ previousZ=currentZ;
+ previous=current;
+ if(currentZ>=high)break;
+ }
+ return result;
+ };
+ const auto fieldDensity=[&](double x,double y,double z)
+ {return densityMetres(field,x,y,z);};
+ const auto sourceFieldDensity=[&](double x,double y,double z)
+ {
+ const float value=Shape.passageSourceDensityAt({x/coreMetres,
+ y/coreMetres,z/coreMetres});
+ return std::isfinite(value)?static_cast<double>(value)*coreMetres
+ :std::numeric_limits<double>::quiet_NaN();
+ };
+ struct FFlatProbe {double x=0.0,y=0.0,z=0.0;};
+ std::vector<FFlatProbe> flatProbes;
+ const auto probeFlatness=[&](double radial,double angle)
+ {
+ const double x=centerX+radial*std::cos(angle);
+ const double y=centerY+radial*std::sin(angle);
+ const auto root=findFloorRoot(fieldDensity,x,y,
+ floorP-0.75,floorP+0.75);
+ ++audit.flatSamples;
+ if(!root.found||root.crossings!=1)
+ {++audit.flatFailures;return;}
+ const double error=std::abs(root.z-floorP);
+ audit.maximumFlatnessMetres=std::max(
+ audit.maximumFlatnessMetres,error);
+ if(error>FlatnessLimitMetres)++audit.flatFailures;
+ flatProbes.push_back({x,y,root.z});
+ };
+ probeFlatness(0.0,0.0);
+ for(std::size_t direction=0;direction<RadialDirections;++direction)
+ {
+ const double angle=2.0*3.14159265358979323846
+ *static_cast<double>(direction)/RadialDirections;
+ probeFlatness(0.90*radius,angle);
+ }
+ constexpr std::array<double,3> SupportRadii{{0.0,0.5,0.9}};
+ for(const double radialFraction:SupportRadii)
+ for(std::size_t direction=0;direction<RadialDirections;++direction)
+ {
+ const double angle=2.0*3.14159265358979323846
+ *static_cast<double>(direction)/RadialDirections;
+ const double x=centerX+radialFraction*radius*std::cos(angle);
+ const double y=centerY+radialFraction*radius*std::sin(angle);
+ ++audit.supportSamples;
+ if(!(fieldDensity(x,y,floorP-0.10)<0.0))
+ ++audit.supportFailures;
+ ++audit.headroomSamples;
+ if(!(fieldDensity(x,y,floorP+2.0)>0.0))
+ ++audit.headroomFailures;
+ }
+ constexpr std::size_t BlendStations=16;
+ const double blendEnd=BlendRadiusMetres-0.25;
+ for(std::size_t direction=0;direction<RadialDirections;++direction)
+ {
+ const double angle=2.0*3.14159265358979323846
+ *static_cast<double>(direction)/RadialDirections;
+ bool previousFound=false;
+ double previousRadius=0.0,previousFloor=0.0;
+ for(std::size_t station=0;station<=BlendStations;++station)
+ {
+ const double radial=radius+blendEnd
+ *static_cast<double>(station)/BlendStations;
+ const double x=centerX+radial*std::cos(angle);
+ const double y=centerY+radial*std::sin(angle);
+ const double coneRun=std::max(0.0,radial-radius);
+ const double coneHeight=ConeSlope*coneRun;
+ const auto root=findFloorRoot(fieldDensity,x,y,
+ floorP-coneHeight-0.75,floorP+coneHeight+0.75);
+ ++audit.blendRootSamples;
+ if(!root.found||root.crossings!=1)
+ {
+ ++audit.blendMissing;
+ previousFound=false;
+ continue;
+ }
+ if(previousFound)
+ {
+ const double grade=std::atan2(
+ std::abs(root.z-previousFloor),radial-previousRadius)
+ *180.0/3.14159265358979323846;
+ audit.maximumGradeDegrees=std::max(
+ audit.maximumGradeDegrees,grade);
+ if(grade>GradeLimitDegrees)++audit.blendGradeFailures;
+ }
+ previousFound=true;
+ previousRadius=radial;
+ previousFloor=root.z;
+ }
+ }
+ const auto sourceField=[&](double x,double y,double z)
+ {return sourceFieldDensity(x,y,z);};
+ bool padOffRejected=false;
+ for(std::size_t direction=0;direction<RadialDirections;++direction)
+ {
+ const double angle=2.0*3.14159265358979323846
+ *static_cast<double>(direction)/RadialDirections;
+ const double x=centerX+0.90*radius*std::cos(angle);
+ const double y=centerY+0.90*radius*std::sin(angle);
+ const auto root=findFloorRoot(sourceField,x,y,
+ floorP-0.75,floorP+0.75);
+ if(!root.found||root.crossings!=1
+ ||std::abs(root.z-floorP)>FlatnessLimitMetres)
+ {padOffRejected=true;break;}
+ }
+ const bool tiltRejected=std::any_of(flatProbes.begin(),flatProbes.end(),
+ [&](const FFlatProbe& probe)
+ {
+ return std::abs((probe.z-floorP)
+ +0.02*(probe.x-centerX))>FlatnessLimitMetres;
+ });
+ const auto controlStamp=[&](double blendRadius,double radial,double z,
+ double native,double tubeDistance)
+ {
+ const double coneRun=std::max(0.0,radial-radius);
+ const double lower=floorP-ConeSlope*coneRun;
+ const double upper=floorP+ConeSlope*coneRun;
+ const double outer=radius+blendRadius;
+ const double fill=std::max({CosTwelveDegrees*(z-lower),
+ CosTwelveDegrees*(lower-FillDepthMetres-z),radial-outer});
+ const double air=std::min({CosTwelveDegrees*(z-upper),
+ CosTwelveDegrees*(upper+HeadroomMetres-z),outer-radial});
+ const double masked=std::max(fill,-tubeDistance);
+ const double smoothWidth=0.5*std::clamp(radial-radius,0.0,1.0);
+ double smoothMinimum=std::min(native,masked);
+ if(smoothWidth>0.0)
+ {
+ const double h=std::max(smoothWidth
+ -std::abs(native-masked),0.0)/smoothWidth;
+ smoothMinimum-=h*h*h*(smoothWidth/6.0);
+ }
+ return std::max(smoothMinimum,air);
+ };
+ const double outerProbe=radius+BlendRadiusMetres-0.25;
+ const double outerProbeZ=floorP-ConeSlope*(outerProbe-radius)-0.05;
+ const double fullBlendProbe=controlStamp(BlendRadiusMetres,
+ outerProbe,outerProbeZ,0.0,std::numeric_limits<double>::infinity());
+ const double halfBlendProbe=controlStamp(BlendRadiusMetres*0.5,
+ outerProbe,outerProbeZ,0.0,std::numeric_limits<double>::infinity());
+ const bool halfBlendRejected=fullBlendProbe< -0.01
+ &&std::abs(halfBlendProbe)<1.0e-6;
+ const double boreMasked=controlStamp(BlendRadiusMetres,
+ radius*0.5,floorP-0.25,0.25,-0.50);
+ const double boreUnmasked=controlStamp(BlendRadiusMetres,
+ radius*0.5,floorP-0.25,0.25,
+ std::numeric_limits<double>::infinity());
+ const bool boreMaskRejected=boreMasked>0.0&&boreUnmasked<0.0;
+ audit.negativeControlsDetected=static_cast<std::uint64_t>(padOffRejected)
+ +static_cast<std::uint64_t>(tiltRejected)
+ +static_cast<std::uint64_t>(halfBlendRejected)
+ +static_cast<std::uint64_t>(boreMaskRejected);
+ audit.passed=audit.flatSamples>0&&audit.flatFailures==0
+ &&audit.supportSamples>0&&audit.supportFailures==0
+ &&audit.headroomSamples>0&&audit.headroomFailures==0
+ &&audit.blendRootSamples>0&&audit.blendMissing==0
+ &&audit.blendGradeFailures==0
+ &&audit.negativeControlsDetected==4;
+ std::printf("[LandingPadAudit] id=%s side=%s radius_m=%.3f blend_m=%.3f flatness_samples=%llu flatness_max_m=%.5f flatness_failures=%llu support=%llu/%llu headroom=%llu/%llu blend_transects=%zu blend_roots=%llu blend_missing=%llu blend_grade_max_deg=%.3f blend_grade_failures=%llu negative_controls=%llu/4 pad_off=%s tilt_2pct=%s blend_B_half=%s bore_mask_removed=%s status=%s\n",
+ passage.stableId.c_str(),upper?"upper":"lower",radius,
+ BlendRadiusMetres,static_cast<unsigned long long>(audit.flatSamples),
+ audit.maximumFlatnessMetres,
+ static_cast<unsigned long long>(audit.flatFailures),
+ static_cast<unsigned long long>(audit.supportSamples
+ -audit.supportFailures),
+ static_cast<unsigned long long>(audit.supportSamples),
+ static_cast<unsigned long long>(audit.headroomSamples
+ -audit.headroomFailures),
+ static_cast<unsigned long long>(audit.headroomSamples),
+ RadialDirections,
+ static_cast<unsigned long long>(audit.blendRootSamples),
+ static_cast<unsigned long long>(audit.blendMissing),
+ audit.maximumGradeDegrees,
+ static_cast<unsigned long long>(audit.blendGradeFailures),
+ static_cast<unsigned long long>(audit.negativeControlsDetected),
+ padOffRejected?"detected":"MISSED",tiltRejected?"detected":"MISSED",
+ halfBlendRejected?"detected":"MISSED",
+ boreMaskRejected?"detected":"MISSED",audit.passed?"PASS":"FAIL");
+ return audit;
+ };
std::uint64_t passageCount=0,passagePasses=0,blockedControls=0,naturalPasses=0;
std::uint64_t passageInteriorCapsuleSamples=0,passageInteriorSolidSamples=0;
std::uint64_t passageInteriorControls=0;
std::uint64_t landingChamberCount=0,landingChamberNegativeControls=0;
+ std::uint64_t landingPadCount=0,landingPadPasses=0,landingPadControls=0;
std::uint64_t mouthProfilePasses=0,ripplePasses=0,rippleControlsDetected=0;
std::uint64_t floorRippleControlsDetected=0,floorStepRippleControlsDetected=0;
std::uint64_t shellAudits=0,shellFloorSamples=0,shellFloorViolations=0;
@@ -5346,6 +5438,13 @@ namespace StandaloneStreamBenchmark
passage,passageIndex,true);
const FWalkAudit lowerChamberAudit=auditLandingChamber(Shape,
passage,passageIndex,false);
+ const FLandingPadAudit upperPadAudit=auditLandingPad(Shape,passage,true);
+ const FLandingPadAudit lowerPadAudit=auditLandingPad(Shape,passage,false);
+ landingPadCount+=2;
+ landingPadPasses+=static_cast<std::uint64_t>(upperPadAudit.passed)
+ +static_cast<std::uint64_t>(lowerPadAudit.passed);
+ landingPadControls+=upperPadAudit.negativeControlsDetected
+ +lowerPadAudit.negativeControlsDetected;
const bool landingChambersWalkable=upperChamberAudit.passed
&&lowerChamberAudit.passed;
const bool passageInteriorAirPassed=
@@ -5426,6 +5525,7 @@ namespace StandaloneStreamBenchmark
floorRippleControlsDetected+=rippleAudit.floorControlDetected;
floorStepRippleControlsDetected+=rippleAudit.floorStepControlDetected;
passagePasses+=passageAudit.passed&&landingChambersWalkable
+ &&upperPadAudit.passed&&lowerPadAudit.passed
&&passageInteriorAirPassed
&&mouthsInRadius&&mouthsInContent
&&naturalAudit.passed&&mouthProfileAudit.passed&&rippleAudit.passed;
@@ -5889,6 +5989,13 @@ namespace StandaloneStreamBenchmark
static_cast<unsigned long long>(landingChamberCount),
static_cast<unsigned long long>(landingChamberCount
-landingChamberNegativeControls));
+ std::printf("[LandingPadAuditSummary] pads=%llu passed=%llu negative_controls=%llu/%llu status=%s\n",
+ static_cast<unsigned long long>(landingPadCount),
+ static_cast<unsigned long long>(landingPadPasses),
+ static_cast<unsigned long long>(landingPadControls),
+ static_cast<unsigned long long>(landingPadCount*4),
+ landingPadCount>0&&landingPadPasses==landingPadCount
+ &&landingPadControls==landingPadCount*4?"PASS":"FAIL");
const bool shellAuditPassed=shellAudits==passageCount
&&shellFloorViolations==0&&shellWallViolations==0
&&shellChamberFloorViolations==0&&shellChamberWallViolations==0
@@ -5935,6 +6042,8 @@ namespace StandaloneStreamBenchmark
ripplePasses==passageCount?"PASS":"FAIL");
const bool auditPassed=passageCount>0&&passagePasses==passageCount
&&passageInteriorAuditPassed
+ &&landingPadCount>0&&landingPadPasses==landingPadCount
+ &&landingPadControls==landingPadCount*4
&&naturalPasses==passageCount&&l23SmoothWallDetected
&&mouthProfilePasses==passageCount&&l23TerraceControlDetected
&&ripplePasses==passageCount