feat(core): cavern graph v1, one continuous floor-capped tunnel into the landmark chamber (L46); R13
The flagged SW demo's ramp (57 carves + 107 support pillars, visibly ribbed) becomes one cubic-Bezier sweep: closest point by 17-point seed plus 5 Newton steps, a cubic floor profile, a rounded 2.6 m bore, and a hard floor cap applied after unions and relief that only adds support where the field is air. Optional resolved cavern-graph records (one room, one edge) enter canonical identity only when present; their bounds extend World::isTileShapeFree. Reachability passes (L43 control fails), headroom 2.8 m, floor ripple 1.8 mm (L45 ramp control fails at 17.9 mm), shape limits met; demo off: owner hashes identical to L45. fast/medium/full PASS. Research R13 (read-only): continuous SDF sweeps along curves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
f13b145f23
commit
e016f6a20f
14 files changed
+1753
-48
No files matched your search
@@ -130,6 +130,7 @@ namespace StandaloneStreamBenchmark
|
||||
std::uint64_t supportSamples=0,unsupportedSamples=0,gradeFailures=0;
|
||||
double maximumGradeDegrees=0.0,maximumSupportDepthMetres=0.0;
|
||||
double minimumClearanceMetres=std::numeric_limits<double>::infinity();
|
||||
double minimumHeadroomMetres=std::numeric_limits<double>::infinity();
|
||||
double latestPointMinimumClearanceMetres=std::numeric_limits<double>::infinity();
|
||||
std::array<double,3> latestPointMinimumClearancePosition{};
|
||||
bool passed=false;
|
||||
@@ -153,13 +154,19 @@ namespace StandaloneStreamBenchmark
|
||||
if(surfacePlan==assembly->plan.strates.end())return result;
|
||||
const VoxelForgeCore::Heightfield2D surfaceField(assembly->recipe,*surfacePlan);
|
||||
if(!surfaceField.isValid())return result;
|
||||
std::vector<const VoxelForgeCore::ResolvedLayoutStampV1*> ramp;
|
||||
for(const auto& stamp:layout.stamps)
|
||||
if(stamp.stableId.starts_with("landmark/sw-arrival-chamber-ramp-"))
|
||||
ramp.push_back(&stamp);
|
||||
std::sort(ramp.begin(),ramp.end(),[](const auto* a,const auto* b)
|
||||
{return a->stableId<b->stableId;});
|
||||
if(ramp.size()<2)return result;
|
||||
const auto tunnel=std::find_if(layout.cavernEdges.begin(),layout.cavernEdges.end(),
|
||||
[](const auto& edge)
|
||||
{return edge.stableId=="landmark/sw-arrival-chamber-tunnel-v1";});
|
||||
std::vector<const VoxelForgeCore::ResolvedLayoutStampV1*> legacyRamp;
|
||||
if(tunnel==layout.cavernEdges.end())
|
||||
{
|
||||
for(const auto& stamp:layout.stamps)
|
||||
if(stamp.stableId.starts_with("landmark/sw-arrival-chamber-ramp-"))
|
||||
legacyRamp.push_back(&stamp);
|
||||
std::sort(legacyRamp.begin(),legacyRamp.end(),[](const auto* a,const auto* b)
|
||||
{return a->stableId<b->stableId;});
|
||||
if(legacyRamp.size()<2)return result;
|
||||
}
|
||||
|
||||
const double voxelMetres=static_cast<double>(std::max<std::uint32_t>(1,
|
||||
coreVoxelSizeMicrometres))/1000000.0;
|
||||
@@ -177,12 +184,13 @@ namespace StandaloneStreamBenchmark
|
||||
return static_cast<double>(shape.densityAt({x/voxelMetres,
|
||||
y/voxelMetres,z/voxelMetres}))*voxelMetres;
|
||||
};
|
||||
const auto samplePoint=[&](double x,double y,double centerZ,bool tunnel)
|
||||
const auto samplePoint=[&](double x,double y,double centerZ,bool inTunnel,
|
||||
bool measureHeadroom=false)
|
||||
{
|
||||
constexpr double FloorStep=0.01;
|
||||
double floorZ=centerZ-5.0;
|
||||
bool foundFloor=false;
|
||||
if(tunnel)
|
||||
if(inTunnel)
|
||||
{
|
||||
if(densityMetres(x,y,centerZ)>0.025)
|
||||
for(double offset=-0.10;offset>=-10.0;offset-=FloorStep)
|
||||
@@ -240,6 +248,23 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
}
|
||||
|
||||
if(measureHeadroom)
|
||||
{
|
||||
constexpr std::array<std::array<double,2>,5> headOffsets={{{0.0,0.0},
|
||||
{0.24,0.0},{-0.24,0.0},{0.0,0.24},{0.0,-0.24}}};
|
||||
constexpr double HeadStep=0.05,HeadLimit=4.0;
|
||||
for(const auto& offset:headOffsets)
|
||||
{
|
||||
double headroom=HeadLimit;
|
||||
for(double rise=0.0;rise<=HeadLimit;rise+=HeadStep)
|
||||
if(densityMetres(x+offset[0],y+offset[1],
|
||||
floorZ+CapsuleHeight+rise)<0.0)
|
||||
{headroom=std::max(0.0,rise-HeadStep);break;}
|
||||
result.minimumHeadroomMetres=std::min(
|
||||
result.minimumHeadroomMetres,headroom);
|
||||
}
|
||||
}
|
||||
|
||||
const std::array<std::array<double,2>,5> supportOffsets={{{0.0,0.0},
|
||||
{0.28,0.0},{-0.28,0.0},{0.0,0.28},{0.0,-0.28}}};
|
||||
std::uint32_t supported=0;
|
||||
@@ -270,10 +295,16 @@ namespace StandaloneStreamBenchmark
|
||||
double floorZ=0.0,cost=std::numeric_limits<double>::infinity();
|
||||
int parent=-1;
|
||||
};
|
||||
const double edgeStartX=tunnel!=layout.cavernEdges.end()
|
||||
?tunnel->controlPointsMicrometres[0][0]/1000000.0
|
||||
:legacyRamp.front()->positionMicrometres[0]/1000000.0;
|
||||
const double edgeStartY=tunnel!=layout.cavernEdges.end()
|
||||
?tunnel->controlPointsMicrometres[0][1]/1000000.0
|
||||
:legacyRamp.front()->positionMicrometres[1]/1000000.0;
|
||||
const int goalX=GridRadius+static_cast<int>(std::llround(
|
||||
(ramp.front()->positionMicrometres[0]/1000000.0-arrivalX)/GridStep));
|
||||
(edgeStartX-arrivalX)/GridStep));
|
||||
const int goalY=GridRadius+static_cast<int>(std::llround(
|
||||
(ramp.front()->positionMicrometres[1]/1000000.0-arrivalY)/GridStep));
|
||||
(edgeStartY-arrivalY)/GridStep));
|
||||
const auto nodeIndex=[](int x,int y){return y*GridWidth+x;};
|
||||
std::vector<FSurfaceNode> surfaceNodes(static_cast<std::size_t>(GridWidth*GridWidth));
|
||||
std::uint64_t exploredNodes=0;
|
||||
@@ -364,7 +395,7 @@ namespace StandaloneStreamBenchmark
|
||||
double surfaceStartDistance=std::numeric_limits<double>::infinity();
|
||||
if(!surfaceRoute.empty())
|
||||
{
|
||||
path.reserve(surfaceRoute.size()+ramp.size());
|
||||
path.reserve(surfaceRoute.size()+128);
|
||||
const int startX=surfaceRoute.front()%GridWidth;
|
||||
const int startY=surfaceRoute.front()/GridWidth;
|
||||
surfaceStartDistance=GridStep*std::hypot(startX-GridRadius,startY-GridRadius);
|
||||
@@ -375,10 +406,39 @@ namespace StandaloneStreamBenchmark
|
||||
path.push_back({arrivalX+(ix-GridRadius)*GridStep,
|
||||
arrivalY+(iy-GridRadius)*GridStep,node.floorZ+0.9,false});
|
||||
}
|
||||
for(const auto* stamp:ramp)
|
||||
path.push_back({stamp->positionMicrometres[0]/1000000.0,
|
||||
stamp->positionMicrometres[1]/1000000.0,
|
||||
stamp->positionMicrometres[2]/1000000.0,true});
|
||||
if(tunnel!=layout.cavernEdges.end())
|
||||
{
|
||||
const auto curveValue=[&](double t,std::size_t axis)
|
||||
{
|
||||
const double one=1.0-t;
|
||||
return (one*one*one*tunnel->controlPointsMicrometres[0][axis]
|
||||
+3.0*one*one*t*tunnel->controlPointsMicrometres[1][axis]
|
||||
+3.0*one*t*t*tunnel->controlPointsMicrometres[2][axis]
|
||||
+t*t*t*tunnel->controlPointsMicrometres[3][axis])/1000000.0;
|
||||
};
|
||||
double curveLength=0.0,previousCurveX=curveValue(0.0,0),previousCurveY=curveValue(0.0,1);
|
||||
for(int sample=1;sample<=128;++sample)
|
||||
{
|
||||
const double t=static_cast<double>(sample)/128.0;
|
||||
const double x=curveValue(t,0),y=curveValue(t,1);
|
||||
curveLength+=std::hypot(x-previousCurveX,y-previousCurveY);
|
||||
previousCurveX=x;previousCurveY=y;
|
||||
}
|
||||
const int curveSteps=std::max(2,static_cast<int>(std::ceil(curveLength/0.60)));
|
||||
for(int sample=0;sample<=curveSteps;++sample)
|
||||
{
|
||||
const double t=static_cast<double>(sample)/curveSteps;
|
||||
path.push_back({curveValue(t,0),curveValue(t,1),
|
||||
curveValue(t,2)+0.9,true});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(const auto* stamp:legacyRamp)
|
||||
path.push_back({stamp->positionMicrometres[0]/1000000.0,
|
||||
stamp->positionMicrometres[1]/1000000.0,
|
||||
stamp->positionMicrometres[2]/1000000.0,true});
|
||||
}
|
||||
}
|
||||
std::printf("[LandmarkSurfaceRoute] reachable=%s goal_walkable=%s grid_step_m=%.2f start_offset_m=%.2f explored=%llu surface_points=%zu grade_limit_deg=15.000\n",
|
||||
surfaceRoute.empty()?"false":"true",surfaceGoalWalkable?"true":"false",GridStep,surfaceStartDistance,
|
||||
@@ -405,7 +465,7 @@ namespace StandaloneStreamBenchmark
|
||||
const auto capsuleBefore=result.capsuleSamples;
|
||||
const auto supportBefore=result.supportSamples;
|
||||
const bool tunnel=from.tunnel&&to.tunnel;
|
||||
const auto [floorZ,supportOk]=samplePoint(x,y,centerZ,tunnel);
|
||||
const auto [floorZ,supportOk]=samplePoint(x,y,centerZ,tunnel,true);
|
||||
allSupport&=supportOk;
|
||||
double grade=0.0;
|
||||
if(havePrior)
|
||||
@@ -421,7 +481,7 @@ namespace StandaloneStreamBenchmark
|
||||
||!supportOk||grade>15.0))
|
||||
{
|
||||
std::printf("[LandmarkWalkPoint] field=%.*s mode=%s x=%.3f y=%.3f center_z=%.3f floor_z=%.3f previous_x=%.3f previous_y=%.3f previous_floor_z=%.3f horizontal_step_m=%.3f blocked=%llu/%llu support_probes=%llu grade_deg=%.3f density_center_m=%.3f capsule_min_m=%.3f capsule_min_at_m=(%.3f,%.3f,%.3f)\n",
|
||||
static_cast<int>(label.size()),label.data(),tunnel?"ramp":"surface",x,y,centerZ,floorZ,
|
||||
static_cast<int>(label.size()),label.data(),tunnel?"tunnel":"surface",x,y,centerZ,floorZ,
|
||||
priorX,priorY,priorFloor,std::hypot(x-priorX,y-priorY),
|
||||
static_cast<unsigned long long>(result.blockedCapsuleSamples-blockedBefore),
|
||||
static_cast<unsigned long long>(result.capsuleSamples-capsuleBefore),
|
||||
@@ -437,7 +497,214 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
result.passed=result.pathSamples>1&&result.capsuleSamples>0
|
||||
&&result.blockedCapsuleSamples==0&&allSupport&&allGrade
|
||||
&&result.unsupportedSamples==0&&result.gradeFailures==0;
|
||||
&&result.unsupportedSamples==0&&result.gradeFailures==0
|
||||
&&result.minimumHeadroomMetres>=0.300;
|
||||
return result;
|
||||
}
|
||||
|
||||
struct FLandmarkTunnelRippleResult
|
||||
{
|
||||
std::size_t floorSamples=0,wallSamples=0;
|
||||
double floorPeakPeriodMetres=0.0,floorPeakAmplitudeMetres=0.0;
|
||||
double wallPeakPeriodMetres=0.0,wallPeakAmplitudeMetres=0.0;
|
||||
bool valid=false,passed=false;
|
||||
};
|
||||
|
||||
inline FLandmarkTunnelRippleResult AuditLandmarkTunnelRipples(
|
||||
const VoxelForgeCore::World& shape,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
|
||||
std::uint32_t coreVoxelSizeMicrometres)
|
||||
{
|
||||
FLandmarkTunnelRippleResult result;
|
||||
const double voxelMetres=static_cast<double>(std::max<std::uint32_t>(1,
|
||||
coreVoxelSizeMicrometres))/1000000.0;
|
||||
const auto densityMetres=[&](double x,double y,double z)
|
||||
{return static_cast<double>(shape.densityAt({x/voxelMetres,y/voxelMetres,
|
||||
z/voxelMetres}))*voxelMetres;};
|
||||
struct FRoutePoint { double x=0.0,y=0.0,floor=0.0; };
|
||||
std::vector<FRoutePoint> route;
|
||||
const auto edge=std::find_if(layout.cavernEdges.begin(),layout.cavernEdges.end(),
|
||||
[](const auto& value)
|
||||
{return value.stableId=="landmark/sw-arrival-chamber-tunnel-v1";});
|
||||
if(edge!=layout.cavernEdges.end())
|
||||
{
|
||||
constexpr int CurveSamples=512;
|
||||
route.reserve(CurveSamples+1);
|
||||
for(int index=0;index<=CurveSamples;++index)
|
||||
{
|
||||
const double t=static_cast<double>(index)/CurveSamples,one=1.0-t;
|
||||
const std::array<double,4> weights={one*one*one,3.0*one*one*t,
|
||||
3.0*one*t*t,t*t*t};
|
||||
FRoutePoint point;
|
||||
point.x=point.y=point.floor=0.0;
|
||||
for(std::size_t control=0;control<4;++control)
|
||||
{
|
||||
point.x+=weights[control]*edge->controlPointsMicrometres[control][0]/1e6;
|
||||
point.y+=weights[control]*edge->controlPointsMicrometres[control][1]/1e6;
|
||||
point.floor+=weights[control]*edge->controlPointsMicrometres[control][2]/1e6;
|
||||
}
|
||||
route.push_back(point);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<const VoxelForgeCore::ResolvedLayoutStampV1*> ramp;
|
||||
for(const auto& stamp:layout.stamps)
|
||||
if(stamp.stableId.starts_with("landmark/sw-arrival-chamber-ramp-"))
|
||||
ramp.push_back(&stamp);
|
||||
std::sort(ramp.begin(),ramp.end(),[](const auto* a,const auto* b)
|
||||
{return a->stableId<b->stableId;});
|
||||
for(const auto* stamp:ramp)
|
||||
route.push_back({stamp->positionMicrometres[0]/1e6,
|
||||
stamp->positionMicrometres[1]/1e6,
|
||||
stamp->positionMicrometres[2]/1e6-1.6});
|
||||
}
|
||||
if(route.size()<2)return result;
|
||||
std::vector<double> arc(route.size(),0.0);
|
||||
for(std::size_t index=1;index<route.size();++index)
|
||||
arc[index]=arc[index-1]+std::hypot(route[index].x-route[index-1].x,
|
||||
route[index].y-route[index-1].y);
|
||||
const double length=arc.back();
|
||||
if(length<4.0)return result;
|
||||
const auto atDistance=[&](double distance)
|
||||
{
|
||||
distance=std::clamp(distance,0.0,length);
|
||||
const auto upper=std::lower_bound(arc.begin(),arc.end(),distance);
|
||||
const std::size_t hi=std::min<std::size_t>(route.size()-1,
|
||||
static_cast<std::size_t>(upper-arc.begin()));
|
||||
const std::size_t lo=hi?hi-1:0;
|
||||
const double span=arc[hi]-arc[lo];
|
||||
const double t=span>0.0?(distance-arc[lo])/span:0.0;
|
||||
return FRoutePoint{std::lerp(route[lo].x,route[hi].x,t),
|
||||
std::lerp(route[lo].y,route[hi].y,t),
|
||||
std::lerp(route[lo].floor,route[hi].floor,t)};
|
||||
};
|
||||
const auto floorRoot=[&](double x,double y,double expected)->std::optional<double>
|
||||
{
|
||||
constexpr double Step=0.025,Below=0.65,Above=0.35;
|
||||
double priorZ=expected-Below;
|
||||
double priorDensity=densityMetres(x,y,priorZ);
|
||||
std::optional<double> closest;
|
||||
double closestError=std::numeric_limits<double>::infinity();
|
||||
for(double z=priorZ+Step;z<=expected+Above+1.0e-9;z+=Step)
|
||||
{
|
||||
const double density=densityMetres(x,y,z);
|
||||
if(priorDensity<0.0&&density>=0.0)
|
||||
{
|
||||
const double span=density-priorDensity;
|
||||
const double root=span>0.0?priorZ+Step*(-priorDensity)/span:z;
|
||||
const double error=std::abs(root-expected);
|
||||
if(error<closestError){closest=root;closestError=error;}
|
||||
}
|
||||
priorZ=z;priorDensity=density;
|
||||
}
|
||||
return closest;
|
||||
};
|
||||
std::vector<std::pair<double,double>> floorResiduals;
|
||||
for(double distance=0.75;distance<=length-0.75;distance+=0.125)
|
||||
{
|
||||
const auto point=atDistance(distance);
|
||||
const auto prior=atDistance(std::max(0.0,distance-0.1));
|
||||
const auto next=atDistance(std::min(length,distance+0.1));
|
||||
const double tx=next.x-prior.x,ty=next.y-prior.y;
|
||||
const double tangentLength=std::hypot(tx,ty);
|
||||
if(tangentLength<1.0e-6)continue;
|
||||
const double normalX=-ty/tangentLength,normalY=tx/tangentLength;
|
||||
double offsetSum=0.0;std::size_t offsetCount=0;
|
||||
for(const double lateral:{-0.35,0.0,0.35})
|
||||
{
|
||||
const auto root=floorRoot(point.x+normalX*lateral,
|
||||
point.y+normalY*lateral,point.floor);
|
||||
if(root){offsetSum+=*root-point.floor;++offsetCount;}
|
||||
}
|
||||
if(offsetCount)
|
||||
{floorResiduals.push_back({distance,offsetSum/offsetCount});}
|
||||
}
|
||||
result.floorSamples=floorResiduals.size();
|
||||
const auto peakPeriod=[&](const std::vector<std::pair<double,double>>& samples,
|
||||
double minimumPeriod,double maximumPeriod,
|
||||
double& outPeriod)
|
||||
{
|
||||
double amplitude=0.0;outPeriod=0.0;
|
||||
if(samples.size()<16)return amplitude;
|
||||
for(double period=minimumPeriod;period<=maximumPeriod+1.0e-9;period+=0.125)
|
||||
{
|
||||
double cosine=0.0,sine=0.0;
|
||||
for(const auto& [distance,residual]:samples)
|
||||
{
|
||||
const double phase=2.0*3.14159265358979323846*distance/period;
|
||||
cosine+=residual*std::cos(phase);sine+=residual*std::sin(phase);
|
||||
}
|
||||
const double candidate=2.0*std::hypot(cosine,sine)/samples.size();
|
||||
if(candidate>amplitude){amplitude=candidate;outPeriod=period;}
|
||||
}
|
||||
return amplitude;
|
||||
};
|
||||
result.floorPeakAmplitudeMetres=peakPeriod(floorResiduals,0.375,
|
||||
std::min(8.0,length/2.0),result.floorPeakPeriodMetres);
|
||||
|
||||
std::vector<std::pair<double,double>> wallResiduals;
|
||||
const double expectedWallRadius=edge!=layout.cavernEdges.end()
|
||||
?edge->boreRadiusMicrometres/1e6*std::sqrt(1.0
|
||||
-std::pow(2.0/(edge->boreHeightMicrometres/1e6),2.0))
|
||||
:2.6*std::sqrt(1.0-std::pow(0.4/2.0,2.0));
|
||||
// Keep the edge audit away from the ground portal and the room union;
|
||||
// those are transitions between features, not tunnel-wall ripple.
|
||||
const double wallAuditStart=std::max(4.0,length*0.38);
|
||||
const double wallAuditEnd=std::min(length*0.55,length-4.0);
|
||||
for(double distance=wallAuditStart;distance<=wallAuditEnd;distance+=0.0625)
|
||||
{
|
||||
const auto point=atDistance(distance);
|
||||
const auto prior=atDistance(std::max(0.0,distance-0.1));
|
||||
const auto next=atDistance(std::min(length,distance+0.1));
|
||||
const double tx=next.x-prior.x,ty=next.y-prior.y;
|
||||
const double tangentLength=std::hypot(tx,ty);
|
||||
if(tangentLength<1.0e-6)continue;
|
||||
const double normalX=-ty/tangentLength,normalY=tx/tangentLength;
|
||||
const double height=point.floor+2.0;
|
||||
for(const double sign:{-1.0,1.0})
|
||||
{
|
||||
double priorRadius=0.0;
|
||||
double priorDensity=densityMetres(point.x,point.y,height);
|
||||
bool found=false;
|
||||
for(double radius=0.025;radius<=8.0;radius+=0.025)
|
||||
{
|
||||
const double density=densityMetres(point.x+normalX*sign*radius,
|
||||
point.y+normalY*sign*radius,height);
|
||||
if(priorDensity>=0.0&&density<0.0)
|
||||
{
|
||||
const double span=priorDensity-density;
|
||||
const double root=span>0.0?priorRadius+0.025*priorDensity/span:radius;
|
||||
wallResiduals.push_back({distance,root-expectedWallRadius});
|
||||
found=true;break;
|
||||
}
|
||||
priorRadius=radius;priorDensity=density;
|
||||
}
|
||||
if(!found)continue;
|
||||
}
|
||||
}
|
||||
result.wallSamples=wallResiduals.size();
|
||||
// Remove the slow wall envelope from the amplitude profile before testing
|
||||
// for segment-scale ribs. The moving baseline covers 2 m, while the audit
|
||||
// band covers the shorter repeated seams seen in the L35/L36 controls.
|
||||
std::vector<std::pair<double,double>> wallRippleResiduals;
|
||||
wallRippleResiduals.reserve(wallResiduals.size());
|
||||
for(const auto& sample:wallResiduals)
|
||||
{
|
||||
double baseline=0.0;std::size_t neighbours=0;
|
||||
for(const auto& other:wallResiduals)
|
||||
if(std::abs(other.first-sample.first)<=1.0)
|
||||
{baseline+=other.second;++neighbours;}
|
||||
if(neighbours>0&&sample.first>=wallAuditStart+1.0
|
||||
&&sample.first<=wallAuditEnd-1.0)
|
||||
wallRippleResiduals.push_back({sample.first,
|
||||
sample.second-baseline/static_cast<double>(neighbours)});
|
||||
}
|
||||
result.wallPeakAmplitudeMetres=peakPeriod(wallRippleResiduals,0.25,
|
||||
std::min(1.0,length/2.0),result.wallPeakPeriodMetres);
|
||||
result.valid=result.floorSamples>=16&&result.wallSamples>=16;
|
||||
result.passed=result.valid&&result.floorPeakAmplitudeMetres<=0.015
|
||||
&&result.wallPeakAmplitudeMetres<=0.035;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -447,6 +714,8 @@ namespace StandaloneStreamBenchmark
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
control=source;control.stamps.clear();control.stampVocabularyVersion=1;
|
||||
control.cavernGraphVocabularyVersion=0;
|
||||
control.cavernRooms.clear();control.cavernEdges.clear();
|
||||
const auto add=[&](std::string id,ResolvedSdfStampKindV1 kind,
|
||||
std::array<std::int64_t,3> position,
|
||||
std::array<std::int64_t,3> half,std::int64_t smooth,
|
||||
@@ -971,11 +1240,32 @@ namespace StandaloneStreamBenchmark
|
||||
if(bAuditLandmarkReachability)
|
||||
{
|
||||
Streamer.Stop();
|
||||
if(ResolvedLayout==nullptr||!CoreSnapshot.resolvedLayout()
|
||||
||ResolvedLayout->stamps.empty())
|
||||
{std::fprintf(stderr,"[LandmarkReachabilityAudit] missing demo layout and stamps\n");return 2;}
|
||||
if(ResolvedLayout==nullptr||!CoreSnapshot.resolvedLayout())
|
||||
{std::fprintf(stderr,"[LandmarkReachabilityAudit] missing resolved layout\n");return 2;}
|
||||
if(ResolvedLayout->cavernEdges.empty())
|
||||
{
|
||||
const auto control=AuditLandmarkTunnelRipples(Shape,*ResolvedLayout,
|
||||
World.CoreVoxelSizeMicrons);
|
||||
const bool expectedFail=control.valid
|
||||
&&control.floorPeakAmplitudeMetres>0.015;
|
||||
std::printf("[CavernTunnelRippleControl] layout=L45-ramp floor_samples=%zu floor_peak_period_m=%.3f floor_peak_amplitude_m=%.4f floor_limit_m=0.015 wall_samples=%zu wall_peak_period_m=%.3f wall_peak_amplitude_m=%.4f wall_limit_m=0.035 observed=%s expected=FAIL status=%s\n",
|
||||
control.floorSamples,control.floorPeakPeriodMetres,
|
||||
control.floorPeakAmplitudeMetres,control.wallSamples,
|
||||
control.wallPeakPeriodMetres,control.wallPeakAmplitudeMetres,
|
||||
expectedFail?"FAIL":"PASS",expectedFail?"PASS":"FAIL");
|
||||
return expectedFail?0:14;
|
||||
}
|
||||
if(ResolvedLayout->cavernRooms.empty())
|
||||
{std::fprintf(stderr,"[LandmarkReachabilityAudit] missing cavern room node\n");return 2;}
|
||||
const auto ripples=AuditLandmarkTunnelRipples(Shape,*ResolvedLayout,
|
||||
World.CoreVoxelSizeMicrons);
|
||||
std::printf("[CavernTunnelRipple] layout=L46 floor_samples=%zu floor_peak_period_m=%.3f floor_peak_amplitude_m=%.4f floor_limit_m=0.015 wall_samples=%zu wall_peak_period_m=%.3f wall_peak_amplitude_m=%.4f wall_period_band_m=0.250..1.000 wall_detrend_window_m=2.000 wall_limit_m=0.035 status=%s\n",
|
||||
ripples.floorSamples,ripples.floorPeakPeriodMetres,
|
||||
ripples.floorPeakAmplitudeMetres,ripples.wallSamples,
|
||||
ripples.wallPeakPeriodMetres,ripples.wallPeakAmplitudeMetres,
|
||||
ripples.passed?"PASS":"FAIL");
|
||||
const auto candidate=AuditLandmarkWalkPath(Shape,Assembly,*ResolvedLayout,
|
||||
World.CoreVoxelSizeMicrons,"L45");
|
||||
World.CoreVoxelSizeMicrons,"L46");
|
||||
const auto daisStamp=std::find_if(ResolvedLayout->stamps.begin(),
|
||||
ResolvedLayout->stamps.end(),[](const auto& stamp)
|
||||
{return stamp.stableId=="landmark/sw-arrival-chamber-v1-dais";});
|
||||
@@ -1009,7 +1299,7 @@ namespace StandaloneStreamBenchmark
|
||||
oldBuildError.c_str());return 2;}
|
||||
const auto buried=AuditLandmarkWalkPath(oldShape,Assembly,*ResolvedLayout,
|
||||
World.CoreVoxelSizeMicrons,"L43");
|
||||
std::printf("[LandmarkWalkability] layout=L45 path_samples=%llu capsule_samples=%llu blocked=%llu support_samples=%llu unsupported=%llu grade_failures=%llu max_grade_deg=%.3f limit_deg=15.000 min_clearance_m=%.3f max_support_depth_m=%.3f status=%s\n",
|
||||
std::printf("[LandmarkWalkability] layout=L46 path_samples=%llu capsule_samples=%llu blocked=%llu support_samples=%llu unsupported=%llu grade_failures=%llu max_grade_deg=%.3f limit_deg=15.000 min_clearance_m=%.3f max_support_depth_m=%.3f min_headroom_m=%.3f headroom_required_m=0.300 status=%s\n",
|
||||
static_cast<unsigned long long>(candidate.pathSamples),
|
||||
static_cast<unsigned long long>(candidate.capsuleSamples),
|
||||
static_cast<unsigned long long>(candidate.blockedCapsuleSamples),
|
||||
@@ -1017,14 +1307,18 @@ namespace StandaloneStreamBenchmark
|
||||
static_cast<unsigned long long>(candidate.unsupportedSamples),
|
||||
static_cast<unsigned long long>(candidate.gradeFailures),
|
||||
candidate.maximumGradeDegrees,candidate.minimumClearanceMetres,
|
||||
candidate.maximumSupportDepthMetres,candidate.passed?"PASS":"FAIL");
|
||||
candidate.maximumSupportDepthMetres,candidate.minimumHeadroomMetres,
|
||||
candidate.passed?"PASS":"FAIL");
|
||||
std::printf("[LandmarkHeadroom] minimum_m=%.3f required_m=0.300 status=%s\n",
|
||||
candidate.minimumHeadroomMetres,
|
||||
candidate.minimumHeadroomMetres>=0.300?"PASS":"FAIL");
|
||||
std::printf("[LandmarkReachabilityControl] layout=L43-buried path_samples=%llu blocked=%llu unsupported=%llu grade_failures=%llu result=%s expected=FAIL status=%s\n",
|
||||
static_cast<unsigned long long>(buried.pathSamples),
|
||||
static_cast<unsigned long long>(buried.blockedCapsuleSamples),
|
||||
static_cast<unsigned long long>(buried.unsupportedSamples),
|
||||
static_cast<unsigned long long>(buried.gradeFailures),
|
||||
buried.passed?"PASS":"FAIL",buried.passed?"FAIL":"PASS");
|
||||
const bool passed=candidate.passed&&!buried.passed;
|
||||
const bool passed=ripples.passed&&candidate.passed&&!buried.passed;
|
||||
std::printf("[LandmarkReachabilityAudit] positive=PASS control=%s status=%s\n",
|
||||
buried.passed?"FAILED":"EXPECTED_FAIL",passed?"PASS":"FAIL");
|
||||
return passed?0:13;
|
||||
|
||||
Reference in new issue
Block a user