passages: four-strate passage floors supported (L67, evaluator v212); 1-2 wall beat still red

- Shared composer: a passage's restored support is applied after a physically overlapping
  later bore, instead of being carved away by it (0-1: 59 -> 0 unsupported).
- Capsule clearance for support restoration is tested against nearby route segments only
  (1-2: 3 -> 0, 2-3: 2 -> 0 unsupported).
- L66 index switches replaced by a physical criterion (both mouths inside their strata bands,
  neither a SurfaceWorld landing).
- Audit: floor zero-crossing refined by bisection (the 0.2498 m "step" was the gauge);
  wall ripple keeps the authored radius, cubic detrend per 32 m window. Controls detected:
  L35 ribs, beads, synthetic 0.25 m step, L36 washboard, L23 terrace.
- Result: 0-1 and 2-3 pass all audits; 1-2 fails wall ripple 7.27 cm (limit 3.5) from an
  authored-radius beat at 7.06 m. Fast tier was green before the final gauge change and was
  not re-run after it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-27 02:27:40 +01:00
1 parent 5e2fc53d39
commit 087333bf8a
17 files changed
+1439 -43

No files matched your search

+324 -16
View File
@@ -971,6 +971,7 @@ public:
const double floorThicknessVoxels=qPerMetre/Q;
const double playerCenterAboveFloorQ=1.13*qPerMetre;
float composed=density;
float deferredSupportDensity=std::numeric_limits<float>::infinity();
const auto composeRouteMouthSupport=[&](std::int32_t strateIndex,
const std::array<std::int64_t,3>& mouth,bool departure)
{
@@ -1045,7 +1046,6 @@ public:
{
const auto& passage=ResolvedLayout->passages[passageIndex];
if(passage.controlPoints.size()<2)continue;
const bool internalChainPassage=passageIndex>0;
const auto supportStratum=[&](std::int32_t strateIndex)
->const VoxelForgeCore::ResolvedLayoutStratumV2*
{
@@ -1056,6 +1056,31 @@ public:
};
const auto* supportUpper=supportStratum(passage.upperStrateIndex);
const auto* supportLower=supportStratum(passage.lowerStrateIndex);
const auto mouthInsideContent=[&](
const VoxelForgeCore::ResolvedLayoutStratumV2* stratum,
const std::array<std::int64_t,3>& mouth)
{
if(!stratum||!stratum->content.presentInCurrentField)return false;
const auto zMicrometres=static_cast<std::int64_t>(std::llround(
static_cast<double>(mouth[2])
*ResolvedLayout->coreVoxelSizeMicrometres/Q));
return zMicrometres>=stratum->content.bottomMicrometres
&&zMicrometres<stratum->content.topMicrometres;
};
// A passage is internal when both mouths are bounded by their strate
// content and neither endpoint is a SurfaceWorld landing. Route order
// and the total number of strata are irrelevant.
const bool internalChainPassage=supportUpper&&supportLower
&&!supportUpper->passageCapabilities.hasSurfaceLanding
&&!supportLower->passageCapabilities.hasSurfaceLanding
&&mouthInsideContent(supportUpper,passage.upperMouthSubvoxels)
&&mouthInsideContent(supportLower,passage.lowerMouthSubvoxels);
const auto laterPassageMayCarve=[&](std::ptrdiff_t supportSegment,
double sampleX,double sampleY,double sampleZ)
{
return PassageSupportMayBeCarvedByLaterBore(passageIndex,
supportSegment,sampleX,sampleY,sampleZ,0.35*qPerMetre);
};
const bool floatingIslandPassage=supportUpper
&&supportUpper->passageCapabilities.hasFloatingIslandLanding;
const bool surfaceToTunnelSupport=supportUpper&&supportLower
@@ -1223,7 +1248,8 @@ public:
+playerVertical*playerVertical)-0.34*qPerMetre);
}
double cx=ax+t*dx,cy=ay+t*dy,cz=az+t*dz;
const bool smoothCarveJoin=index>2&&index+12<lastIndex
const bool smoothCarveJoin=index>2
&&index+(internalChainPassage?2:12)<lowerMouthIndex
&&DeterministicSqrt(lengthSq)<=8.0*qPerMetre
&&!IsPassageShortSpanSegment(passageIndex,segmentIndex-1)
&&!IsPassageShortSpanSegment(passageIndex,segmentIndex)
@@ -1301,8 +1327,9 @@ public:
?std::clamp(-(qz-(nearestA.positionSubvoxels[2]+bestT*(
static_cast<double>(nearestB.positionSubvoxels[2])-nearestA.positionSubvoxels[2])))
/distanceToCenter,0.0,1.0):0.0;
const float relief=VF_FieldPassageWallRelief(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z),static_cast<std::uint32_t>(Owner?Owner->Seed:0));
const float relief=VF_FieldPassageWallRelief(static_cast<float>(X),
static_cast<float>(Y),static_cast<float>(Z),
static_cast<std::uint32_t>(Owner?Owner->Seed:0));
const float mouthSurfaceRelief=VF_FieldSurfaceRelief(static_cast<float>(X)*1.5f,
static_cast<float>(Y)*1.5f,static_cast<float>(Z)*1.5f,
static_cast<std::uint32_t>(Owner?Owner->Seed:0)^0x4D4F5554u);
@@ -1427,7 +1454,8 @@ public:
static_cast<double>(mouthTerrainRelief),-1.0,1.0)
*0.20*qPerMetre*mouthFloorBlend*floorReliefWeight
*(upperMouthFloorFade?1.0:0.0);
const double wallReliefWeight=0.05+0.95*(1.0-verticalWallAlignment);
const double wallOpening=1.0-verticalWallAlignment;
const double wallReliefWeight=0.05+0.95*wallOpening;
const double roughSignedDistance=bestSignedDistance
+static_cast<double>(std::clamp(relief,-1.0f,1.0f))*0.21*qPerMetre*wallReliefWeight
+floorReliefOffset;
@@ -1472,6 +1500,7 @@ public:
static_cast<float>(-roughSignedDistance/Q));
composed=std::lerp(composed,fullyCarvedDensity,
static_cast<float>(floorCarveWeight));
const float composedAfterCarve=composed;
const auto considerSupportSegment=[&](std::ptrdiff_t index)
{
const auto& segmentA=passage.controlPoints[static_cast<std::size_t>(index)];
@@ -1570,15 +1599,25 @@ public:
const double minimumSupportQueryDepth=index==selectedSupportSegmentIndex
&&sparseRouteSupport?0.65:0.93;
if(supportQueryDepth<minimumSupportQueryDepth*qPerMetre
||supportQueryDepth>1.30*qPerMetre)return;
||supportQueryDepth>1.30*qPerMetre)
return;
const bool fallbackSupport=qz>=supportPlayerCenterZ-1.50*qPerMetre;
const double supportThicknessVoxels=fallbackSupport
?qPerMetre/Q:0.5*qPerMetre/Q;
// Keep the supported ramp to a comfortable walking width. Extending a
// full bore-radius shelf across overlapping helix coils can block the
// capsule on the neighbouring turn.
const bool lowerMouthBodyOverlap=ResolvedLayout->strates.size()>2
&&index>=lowerMouthIndex-2
const double lowerMouthDx=centerX
-static_cast<double>(passage.lowerMouthSubvoxels[0]);
const double lowerMouthDy=centerY
-static_cast<double>(passage.lowerMouthSubvoxels[1]);
const double lowerMouthDz=centerZ-radius
-static_cast<double>(passage.lowerMouthSubvoxels[2]);
const double lowerMouthBodyDistance=DeterministicSqrt(
lowerMouthDx*lowerMouthDx+lowerMouthDy*lowerMouthDy
+lowerMouthDz*lowerMouthDz);
const bool lowerMouthBodyOverlap=internalChainPassage
&&lowerMouthBodyDistance<=2.0*qPerMetre
&&supportQueryDepth<0.98*qPerMetre;
// Keep a narrow walk core, but place its top on the native surface
// so the support remains a worn ramp instead of a route-height slab.
@@ -1731,7 +1770,8 @@ public:
bestSupportUsesSignedDistance=signedDistanceSupport;
bestSupportMouthBlend=mouthSupportBlend;
bestSupportNativeBlend=mouthTread
?0.85*mouthSupportBlend*(1.0-supportEdgeSmooth):0.0;
?(internalChainPassage?1.0:0.85)
*mouthSupportBlend*(1.0-supportEdgeSmooth):0.0;
};
std::ptrdiff_t supportCenterIndex=bestIndex;
if(bestIndex<lowerMouthIndex)
@@ -1775,6 +1815,8 @@ public:
lowerMouthIndex,mouthSearchBegin,mouthSearchEnd,lastIndex,qx,qy,
capsuleQueryZ,qPerMetre,capsuleClearanceMetres)
:bestPlayerCapsuleSignedDistance>=capsuleClearanceMetres*qPerMetre;
bool supportApplied=false;
bool supportNeedsDeferral=false;
// Outside bridge and short-span segments, add the tread only where the
// native field already provides a solid envelope; the capsule check keeps
// that support below the player's walk space.
@@ -1782,6 +1824,8 @@ public:
||shortSpanFloorSupport)
&&(capsuleAllowsSupport||bestSupportDensity>=0.0))
{
supportNeedsDeferral=laterPassageMayCarve(bestSupportSegmentIndex,
qx,qy,qz);
if(bestSupportIsMouthTread)
{
// Preserve the owner's native ground through the shallow mouth
@@ -1797,13 +1841,36 @@ public:
const double nativeSurfaceDetail=std::clamp(
static_cast<double>(mouthTerrainRelief),-1.0,1.0)
*0.18*qPerMetre/Q;
const float nativeGroundWithRelief=static_cast<float>(density
-nativeSurfaceDetail);
const float nativeGroundWithRelief=static_cast<float>(
(supportNeedsDeferral?std::max(-1.0f,density):density)
-nativeSurfaceDetail);
composed=std::lerp(composed,nativeGroundWithRelief,
static_cast<float>(bestSupportNativeBlend));
supportApplied=composed<composedAfterCarve;
}
}
else composed=std::min(composed,static_cast<float>(bestSupportDensity));
else
{
const float densityBeforeSupport=composed;
composed=std::min(composed,static_cast<float>(bestSupportDensity));
supportApplied=composed<densityBeforeSupport;
}
}
if(supportApplied)
{
if(PassageOtherCapsuleBlocksSupport(passageIndex,
bestSupportSegmentIndex,qx,qy,qz,qPerMetre,capsuleClearanceMetres))
{
composed=composedAfterCarve;
supportApplied=false;
}
}
if(supportApplied&&supportNeedsDeferral)
{
deferredSupportDensity=std::min(deferredSupportDensity,composed);
// Preserve a tread only when a later passage bore reaches this
// sample; otherwise retain the ordinary composition order.
composed=composedAfterCarve;
}
}
// Restore the surface route where the passage cut crossed otherwise solid
@@ -1818,6 +1885,7 @@ public:
composeRouteMouthSupport(passage.upperStrateIndex,
passage.upperMouthSubvoxels,true);
}
composed=std::min(composed,deferredSupportDensity);
return composed;
}
@@ -3282,6 +3350,17 @@ private:
{
std::array<double,3> minimum{},maximum{};
};
struct FPassageLaterBoreCandidate
{
std::array<double,3> minimum{},maximum{};
double reach=0.0;
};
struct FPassageLaterBoreSource
{
std::array<double,3> minimum{},maximum{};
std::vector<FPassageLaterBoreCandidate> candidates;
std::vector<std::size_t> capsuleCandidatePassages;
};
struct FPassageProofNode
{
std::array<double,3> minimum{},maximum{};
@@ -4237,6 +4316,7 @@ private:
PassageNearestNodes.clear();
PassageProofBounds.clear();PassageProofNodes.clear();
PassageNearestNodes.resize(ResolvedLayout?ResolvedLayout->passages.size():0);
PassageLaterBoreSources.clear();
PassageColumnSegmentFlags.clear();PassageShortSpanSegmentFlags.clear();
PassageHasShortSpanFlags.clear();
if(!ResolvedLayout||ResolvedLayout->passages.empty())return;
@@ -4336,6 +4416,75 @@ private:
}
}
}
// Precompute the later-bore leaves that can intersect each tread segment.
// Passage support samples normally stay within this source envelope, so the
// hot query can test a short list of physical bounds instead of walking a
// full spatial tree for every density sample.
PassageLaterBoreSources.resize(ResolvedLayout->passages.size());
const double qPerMetre=Q*voxelsPerMetre;
for(std::size_t sourcePassageIndex=0;
sourcePassageIndex<ResolvedLayout->passages.size();++sourcePassageIndex)
{
const auto& sourcePassage=ResolvedLayout->passages[sourcePassageIndex];
const std::size_t sourceSegmentCount=sourcePassage.controlPoints.size()>1
?sourcePassage.controlPoints.size()-1:0;
auto& sourceSegments=PassageLaterBoreSources[sourcePassageIndex];
sourceSegments.resize(sourceSegmentCount);
for(std::size_t sourceSegment=0;sourceSegment<sourceSegmentCount;++sourceSegment)
{
const auto& sourceA=sourcePassage.controlPoints[sourceSegment];
const auto& sourceB=sourcePassage.controlPoints[sourceSegment+1];
const double sourceRadius=std::max(sourceA.radiusSubvoxels,
sourceB.radiusSubvoxels);
auto& source=sourceSegments[sourceSegment];
for(std::size_t axis=0;axis<3;++axis)
{
source.minimum[axis]=std::min(sourceA.positionSubvoxels[axis],
sourceB.positionSubvoxels[axis])-sourceRadius-3.0*qPerMetre;
source.maximum[axis]=std::max(sourceA.positionSubvoxels[axis],
sourceB.positionSubvoxels[axis])+sourceRadius+3.0*qPerMetre;
}
for(std::size_t laterPassageIndex=sourcePassageIndex+1;
laterPassageIndex<ResolvedLayout->passages.size();++laterPassageIndex)
{
for(const auto& node:PassageNearestNodes[laterPassageIndex])
{
if(node.left>=0)continue;
const double reach=node.maximumRadius+0.35*qPerMetre+1.0e-6;
bool intersectsSource=true;
for(std::size_t axis=0;axis<3;++axis)
intersectsSource=intersectsSource
&&node.minimum[axis]-reach<=source.maximum[axis]
&&node.maximum[axis]+reach>=source.minimum[axis];
if(!intersectsSource)continue;
source.candidates.push_back({node.minimum,node.maximum,reach});
}
}
const double capsuleXYReach=0.39*qPerMetre;
for(std::size_t otherPassageIndex=0;
otherPassageIndex<ResolvedLayout->passages.size();++otherPassageIndex)
{
if(otherPassageIndex==sourcePassageIndex
||PassageNearestNodes[otherPassageIndex].empty())continue;
const auto& otherRoot=PassageNearestNodes[otherPassageIndex].front();
const std::array<double,3> blockerMinimum={
otherRoot.minimum[0]-capsuleXYReach,
otherRoot.minimum[1]-capsuleXYReach,
otherRoot.minimum[2]-otherRoot.maximumRadius+0.20*qPerMetre};
const std::array<double,3> blockerMaximum={
otherRoot.maximum[0]+capsuleXYReach,
otherRoot.maximum[1]+capsuleXYReach,
otherRoot.maximum[2]+2.06*qPerMetre};
bool intersectsSource=true;
for(std::size_t axis=0;axis<3;++axis)
intersectsSource=intersectsSource
&&blockerMinimum[axis]<=source.maximum[axis]
&&blockerMaximum[axis]>=source.minimum[axis];
if(intersectsSource)
source.capsuleCandidatePassages.push_back(otherPassageIndex);
}
}
}
for(const auto& crossing:PassageVoidCrossings)
{
if(crossing.passageIndex<PassageColumnSegmentFlags.size())
@@ -4473,6 +4622,88 @@ private:
signedDistanceLimit);
}
bool PassageSupportMayBeCarvedByLaterBore(std::size_t passageIndex,
std::ptrdiff_t supportSegment,double qx,double qy,double qz,
double signedDistanceLimit) const
{
if(!ResolvedLayout||passageIndex+1>=ResolvedLayout->passages.size())return false;
if(passageIndex<PassageLaterBoreSources.size()&&supportSegment>=0
&&static_cast<std::size_t>(supportSegment)
<PassageLaterBoreSources[passageIndex].size())
{
const auto& source=PassageLaterBoreSources[passageIndex][
static_cast<std::size_t>(supportSegment)];
const bool insideSourceEnvelope=qx>=source.minimum[0]&&qx<=source.maximum[0]
&&qy>=source.minimum[1]&&qy<=source.maximum[1]
&&qz>=source.minimum[2]&&qz<=source.maximum[2];
if(insideSourceEnvelope)
{
for(const auto& candidate:source.candidates)
{
double distanceSquared=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double q=axis==0?qx:axis==1?qy:qz;
const double distance=q<candidate.minimum[axis]
?candidate.minimum[axis]-q
:q>candidate.maximum[axis]?q-candidate.maximum[axis]:0.0;
distanceSquared+=distance*distance;
}
const double reach=candidate.reach;
if(distanceSquared<=reach*reach)return true;
}
return false;
}
}
for(std::size_t otherPassageIndex=passageIndex+1;
otherPassageIndex<ResolvedLayout->passages.size();++otherPassageIndex)
{
const auto& otherPassage=ResolvedLayout->passages[otherPassageIndex];
if(otherPassage.controlPoints.size()<2)continue;
if(PassagePotentialWithinNode(otherPassageIndex,0,0,
otherPassage.controlPoints.size()-1,qx,qy,qz,signedDistanceLimit))return true;
}
return false;
}
bool PassageOtherCapsuleBlocksSupport(std::size_t passageIndex,
std::ptrdiff_t supportSegment,double qx,double qy,double qz,
double qPerMetre,double minimumClearanceMetres) const
{
if(passageIndex<PassageLaterBoreSources.size()&&supportSegment>=0
&&static_cast<std::size_t>(supportSegment)
<PassageLaterBoreSources[passageIndex].size())
{
const auto& source=PassageLaterBoreSources[passageIndex][
static_cast<std::size_t>(supportSegment)];
const bool insideSourceEnvelope=qx>=source.minimum[0]&&qx<=source.maximum[0]
&&qy>=source.minimum[1]&&qy<=source.maximum[1]
&&qz>=source.minimum[2]&&qz<=source.maximum[2];
if(insideSourceEnvelope)
{
for(const std::size_t otherPassageIndex:source.capsuleCandidatePassages)
{
const auto& otherPassage=ResolvedLayout->passages[otherPassageIndex];
if(PassagePlayerCapsuleBlocksSupportNode(otherPassageIndex,0,0,
otherPassage.controlPoints.size()-1,qx,qy,qz,qPerMetre,
minimumClearanceMetres))return true;
}
return false;
}
}
for(std::size_t otherPassageIndex=0;
otherPassageIndex<ResolvedLayout->passages.size();++otherPassageIndex)
{
if(otherPassageIndex==passageIndex)continue;
const auto& otherPassage=ResolvedLayout->passages[otherPassageIndex];
if(otherPassage.controlPoints.size()<2)continue;
if(PassagePlayerCapsuleBlocksSupportNode(otherPassageIndex,0,0,
otherPassage.controlPoints.size()-1,qx,qy,qz,qPerMetre,
minimumClearanceMetres))return true;
}
return false;
}
bool PassageMayAffectDensity(std::size_t passageIndex,std::ptrdiff_t carveBegin,
std::ptrdiff_t carveEnd,std::ptrdiff_t lowerMouthIndex,
std::ptrdiff_t mouthSearchBegin,std::ptrdiff_t mouthSearchEnd,std::ptrdiff_t lastIndex,
@@ -4529,7 +4760,10 @@ private:
const double vertical=std::max(0.0,std::abs(qz-centerZ)-0.54*qPerMetre);
const double distance=DeterministicSqrt(horizontalX*horizontalX+horizontalY*horizontalY
+vertical*vertical)-0.34*qPerMetre;
if(distance<minimumClearanceMetres*qPerMetre)return true;
if(distance<minimumClearanceMetres*qPerMetre)
{
return true;
}
}
return false;
}
@@ -4749,8 +4983,12 @@ private:
const double irregularity=1.25*qPerMetre
+std::clamp(static_cast<double>(broadRelief),-1.0,1.0)*1.10*qPerMetre
+std::clamp(static_cast<double>(surfaceRelief),-1.0,1.0)*0.35*qPerMetre;
// Add a subtle host-rock root only when the unmodified column lacks the
// vertical relief required for walkable passage support.
const double rootTaper=crossing.needsRootTaper
?0.08*qPerMetre*smoothStep((t-0.35)/0.50):0.0;
const double radius=static_cast<double>(crossing.minimumRadiusSubvoxels)
+std::max(0.15*qPerMetre,irregularity)+baseFlare+ceilingFlare;
+std::max(0.15*qPerMetre,irregularity)+rootTaper+baseFlare+ceilingFlare;
const double radialDistanceField=radialDistance-radius;
const double topDistance=qz-crossing.topZSubvoxels;
const double bottomDistance=crossing.bottomZSubvoxels-qz;
@@ -4780,6 +5018,67 @@ private:
return ApplyResolvedStrateBoundaryBand(density,X,Y,Z);
}
double PassageColumnNaturalReliefMetres(
const FStandalonePassageVoidCrossing& crossing,double qPerMetre) const
{
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
constexpr std::array<double,6> HeightFractions{{0.25,0.35,0.45,0.55,0.65,0.75}};
constexpr std::size_t DirectionCount=16;
const auto smoothStep=[](double value)
{
value=std::clamp(value,0.0,1.0);
return value*value*(3.0-2.0*value);
};
std::array<double,HeightFractions.size()> meanRadii{};
std::size_t heightCount=0;
const auto seed=static_cast<std::uint32_t>(Owner?Owner->Seed:0);
const double height=static_cast<double>(crossing.topZSubvoxels
-crossing.bottomZSubvoxels);
if(height<=0.0)return 0.0;
for(const double fraction:HeightFractions)
{
const double qz=crossing.topZSubvoxels-height*fraction;
const float z=static_cast<float>(qz/Q);
const float scaledZ=z*0.12f;
const float driftX=VF_FieldSurfaceRelief(0.0f,0.0f,scaledZ,seed^0x434f4c58u);
const float driftY=VF_FieldSurfaceRelief(0.0f,0.0f,scaledZ,seed^0x434f4c59u);
const double axisX=crossing.centerXSubvoxels
+std::clamp(static_cast<double>(driftX),-1.0,1.0)*0.35*qPerMetre;
const double axisY=crossing.centerYSubvoxels
+std::clamp(static_cast<double>(driftY),-1.0,1.0)*0.35*qPerMetre;
const double t=(crossing.topZSubvoxels-qz)/height;
const double baseFlare=6.0*qPerMetre*smoothStep((t-0.82)/0.18);
const double ceilingFlare=2.0*qPerMetre*(1.0-smoothStep(t/0.075));
double meanRadius=0.0;
for(std::size_t direction=0;direction<DirectionCount;++direction)
{
const double angle=2.0*3.14159265358979323846*direction/DirectionCount;
double radius=crossing.minimumRadiusSubvoxels+1.25*qPerMetre
+baseFlare+ceilingFlare;
for(int iteration=0;iteration<8;++iteration)
{
const float sampleX=static_cast<float>((axisX+radius*std::cos(angle))/Q);
const float sampleY=static_cast<float>((axisY+radius*std::sin(angle))/Q);
const float broadRelief=VF_FieldSurfaceRelief(sampleX*0.12f,
sampleY*0.12f,scaledZ,seed^0x434f4c52u);
const float surfaceRelief=VF_FieldSurfaceRelief(sampleX,sampleY,z,
seed^0x434f4c53u);
const double irregularity=1.25*qPerMetre
+std::clamp(static_cast<double>(broadRelief),-1.0,1.0)*1.10*qPerMetre
+std::clamp(static_cast<double>(surfaceRelief),-1.0,1.0)*0.35*qPerMetre;
radius=crossing.minimumRadiusSubvoxels
+std::max(0.15*qPerMetre,irregularity)+baseFlare+ceilingFlare;
}
meanRadius+=radius;
}
meanRadii[heightCount++]=meanRadius/DirectionCount/qPerMetre;
}
if(heightCount<2)return 0.0;
const auto begin=meanRadii.begin();
const auto end=begin+static_cast<std::ptrdiff_t>(heightCount);
return *std::max_element(begin,end)-*std::min_element(begin,end);
}
void BuildPassageVoidCrossingPlan()
{
BuildResolvedBoundaryZIntervals();
@@ -4789,7 +5088,8 @@ private:
{BuildPassageSpatialIndices();return;}
auto& cache=*Owner->PassageVoidCrossingCache;
std::lock_guard<std::mutex> lock(cache.mutex);
const std::string cacheKey=ResolvedLayout->stableIdentity+"/passage-void-columns-v1";
const std::string cacheKey=ResolvedLayout->stableIdentity+"/passage-void-columns-v2-seed-"
+std::to_string(Owner->Seed);
const auto cached=cache.byLayout.find(cacheKey);
if(cached!=cache.byLayout.end())
{PassageVoidCrossings=cached->second;BuildPassageSpatialIndices();return;}
@@ -4968,6 +5268,12 @@ private:
maximumCrossingZ+5.0*qPerMetre));
crossing.bottomZSubvoxels=static_cast<std::int64_t>(std::llround(
minimumCrossingZ-6.0*qPerMetre));
crossing.naturalSurfaceReliefMetres=PassageColumnNaturalReliefMetres(
crossing,qPerMetre);
// The support-column audit requires 20 cm of vertical relief.
// Add a shallow root flare only when the existing field profile
// falls short, so naturally varied legacy columns remain intact.
crossing.needsRootTaper=crossing.naturalSurfaceReliefMetres<0.20;
}
PassageVoidCrossings.push_back(crossing);
if(crossing.needsColumn)
@@ -4975,7 +5281,7 @@ private:
const double coreRadius=crossing.nominalRadiusSubvoxels/qPerMetre;
const double minimumWall=coreRadius-crossing.maximumRouteOffsetMetres
-crossing.maximumBoreRadiusMetres;
std::printf("[PassageVoidColumnPlan] id=%s segments=%zu..%zu unsupported_samples=%llu span_m=%.3f threshold_m=%.3f connected=%d/%d axis_m=(%.3f,%.3f) z_m=%.3f..%.3f route_radius_m=%.3f bore_radius_m=%.3f min_radius_m=%.3f nominal_radius_m=%.3f max_radius_m=%.3f nominal_wall_m=%.3f flare_m=6.000 status=%s\n",
std::printf("[PassageVoidColumnPlan] id=%s segments=%zu..%zu unsupported_samples=%llu span_m=%.3f threshold_m=%.3f connected=%d/%d axis_m=(%.3f,%.3f) z_m=%.3f..%.3f route_radius_m=%.3f bore_radius_m=%.3f min_radius_m=%.3f nominal_radius_m=%.3f max_radius_m=%.3f nominal_wall_m=%.3f natural_relief_m=%.3f root_taper=%d flare_m=6.000 status=%s\n",
passage.stableId.c_str(),crossing.firstSegment,crossing.lastSegment,
static_cast<unsigned long long>(crossing.unsupportedSamples),gapLength,
shortSpanMaximumMetres,static_cast<int>(crossing.connectedAtStart),
@@ -4985,6 +5291,7 @@ private:
crossing.maximumRouteOffsetMetres,crossing.maximumBoreRadiusMetres,
crossing.minimumRadiusSubvoxels/qPerMetre,coreRadius,
crossing.maximumRadiusSubvoxels/qPerMetre,minimumWall,
crossing.naturalSurfaceReliefMetres,static_cast<int>(crossing.needsRootTaper),
crossing.connectedAtStart&&crossing.connectedAtEnd?"PLANNED":"UNATTACHED");
}
else
@@ -10801,6 +11108,7 @@ private:
std::vector<FPassageFeatureNode> PassageFeatureNodes;
bool PassageFeatureBoundsFailOpen=false;
std::vector<std::vector<FPassageNearestNode>> PassageNearestNodes;
std::vector<std::vector<FPassageLaterBoreSource>> PassageLaterBoreSources;
std::vector<FPassageProofBound> PassageProofBounds;
std::vector<FPassageProofNode> PassageProofNodes;
std::vector<std::vector<std::uint8_t>> PassageColumnSegmentFlags,PassageShortSpanSegmentFlags;