feat(core): reachable SW landmark chamber demo with a reachability audit (L45); research R12
Stamp vocabulary v2; the flagged SW demo opens through a side-facing portal and a bent ramp (max 14 deg, two landings) into a 17 x 15 m chamber with a rock dais. New reachability audit: a capsule surface search from walkable ground near the arrival mouth to the chamber floor (66 samples, 0 blocked/unsupported); the buried L43 layout fails it. Demo off: owner hashes identical to L44. fast/medium/full PASS (Clang+GCC). Research R12 (read-only): composite cavern-graph generator design and slices. 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
036bd530fc
commit
f13b145f23
14 files changed
+1404
-45
No files matched your search
@@ -17,6 +17,8 @@
|
||||
#include <future>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <queue>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@@ -122,6 +124,363 @@ namespace StandaloneStreamBenchmark
|
||||
return Result;
|
||||
}
|
||||
|
||||
struct FLandmarkReachabilityResult
|
||||
{
|
||||
std::uint64_t pathSamples=0,capsuleSamples=0,blockedCapsuleSamples=0;
|
||||
std::uint64_t supportSamples=0,unsupportedSamples=0,gradeFailures=0;
|
||||
double maximumGradeDegrees=0.0,maximumSupportDepthMetres=0.0;
|
||||
double minimumClearanceMetres=std::numeric_limits<double>::infinity();
|
||||
double latestPointMinimumClearanceMetres=std::numeric_limits<double>::infinity();
|
||||
std::array<double,3> latestPointMinimumClearancePosition{};
|
||||
bool passed=false;
|
||||
};
|
||||
|
||||
inline FLandmarkReachabilityResult AuditLandmarkWalkPath(
|
||||
const VoxelForgeCore::World& shape,
|
||||
const VoxelForgeCore::WorldAssemblyManifest* assembly,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
|
||||
std::uint32_t coreVoxelSizeMicrometres,std::string_view label)
|
||||
{
|
||||
FLandmarkReachabilityResult result;
|
||||
const auto surface=std::find_if(layout.strates.begin(),layout.strates.end(),
|
||||
[](const auto& value){return value.generatorKind=="SurfaceWorld";});
|
||||
if(surface==layout.strates.end())return result;
|
||||
const auto passage=std::find_if(layout.passages.begin(),layout.passages.end(),
|
||||
[&](const auto& value){return value.lowerStrateIndex==surface->strateIndex;});
|
||||
if(passage==layout.passages.end()||assembly==nullptr)return result;
|
||||
const auto surfacePlan=std::find_if(assembly->plan.strates.begin(),assembly->plan.strates.end(),
|
||||
[&](const auto& value){return value.strateIndex==surface->strateIndex;});
|
||||
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 double voxelMetres=static_cast<double>(std::max<std::uint32_t>(1,
|
||||
coreVoxelSizeMicrometres))/1000000.0;
|
||||
const double metresPerSubvoxel=voxelMetres
|
||||
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
struct FPathPoint { double x=0.0,y=0.0,z=0.0;bool tunnel=false; };
|
||||
std::vector<FPathPoint> path;
|
||||
const auto& arrival=passage->lowerMouthSubvoxels;
|
||||
const double arrivalX=arrival[0]*metresPerSubvoxel;
|
||||
const double arrivalY=arrival[1]*metresPerSubvoxel;
|
||||
const double arrivalCenterZ=arrival[2]*metresPerSubvoxel+0.9;
|
||||
|
||||
const auto densityMetres=[&](double x,double y,double z)
|
||||
{
|
||||
return static_cast<double>(shape.densityAt({x/voxelMetres,
|
||||
y/voxelMetres,z/voxelMetres}))*voxelMetres;
|
||||
};
|
||||
const auto samplePoint=[&](double x,double y,double centerZ,bool tunnel)
|
||||
{
|
||||
constexpr double FloorStep=0.01;
|
||||
double floorZ=centerZ-5.0;
|
||||
bool foundFloor=false;
|
||||
if(tunnel)
|
||||
{
|
||||
if(densityMetres(x,y,centerZ)>0.025)
|
||||
for(double offset=-0.10;offset>=-10.0;offset-=FloorStep)
|
||||
if(densityMetres(x,y,centerZ+offset)<0.0)
|
||||
{floorZ=centerZ+offset+FloorStep;foundFloor=true;break;}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto xSubvoxels=static_cast<std::int64_t>(std::llround(x/metresPerSubvoxel));
|
||||
const auto ySubvoxels=static_cast<std::int64_t>(std::llround(y/metresPerSubvoxel));
|
||||
floorZ=surfaceField.point(xSubvoxels,ySubvoxels,0).groundHeightSubvoxels
|
||||
*metresPerSubvoxel;
|
||||
foundFloor=std::isfinite(floorZ);
|
||||
}
|
||||
++result.pathSamples;
|
||||
if(!foundFloor)
|
||||
{
|
||||
++result.unsupportedSamples;
|
||||
return std::pair<double,bool>{floorZ,false};
|
||||
}
|
||||
|
||||
// Reserve a footfall margin above the sampled ground for the vertical
|
||||
// capsule; it prevents the lower hemisphere from grazing adjacent floor
|
||||
// samples on a gently sloped ramp.
|
||||
floorZ+=0.15;
|
||||
|
||||
constexpr double CapsuleRadius=0.34;
|
||||
constexpr double CapsuleHeight=1.76;
|
||||
constexpr double RequiredClearance=0.025;
|
||||
constexpr double VerticalStep=0.18;
|
||||
constexpr double HorizontalStep=0.17;
|
||||
result.latestPointMinimumClearanceMetres=std::numeric_limits<double>::infinity();
|
||||
for(double height=0.08;height<=CapsuleHeight;height+=VerticalStep)
|
||||
{
|
||||
double capOffset=0.0;
|
||||
if(height<CapsuleRadius)capOffset=CapsuleRadius-height;
|
||||
else if(height>CapsuleHeight-CapsuleRadius)
|
||||
capOffset=height-(CapsuleHeight-CapsuleRadius);
|
||||
const double radial=std::sqrt(std::max(0.0,
|
||||
CapsuleRadius*CapsuleRadius-capOffset*capOffset));
|
||||
for(double oy=-radial;oy<=radial+1.0e-9;oy+=HorizontalStep)
|
||||
for(double ox=-radial;ox<=radial+1.0e-9;ox+=HorizontalStep)
|
||||
{
|
||||
if(ox*ox+oy*oy>radial*radial+1.0e-9)continue;
|
||||
const double clearance=densityMetres(x+ox,y+oy,floorZ+height);
|
||||
++result.capsuleSamples;
|
||||
result.minimumClearanceMetres=std::min(
|
||||
result.minimumClearanceMetres,clearance);
|
||||
if(clearance<result.latestPointMinimumClearanceMetres)
|
||||
{
|
||||
result.latestPointMinimumClearanceMetres=clearance;
|
||||
result.latestPointMinimumClearancePosition={x+ox,y+oy,floorZ+height};
|
||||
}
|
||||
if(clearance<RequiredClearance)++result.blockedCapsuleSamples;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
for(const auto& offset:supportOffsets)
|
||||
{
|
||||
++result.supportSamples;
|
||||
bool hit=false;
|
||||
for(double depth=0.10;depth<=0.55;depth+=0.08)
|
||||
if(densityMetres(x+offset[0],y+offset[1],floorZ-depth)<0.0)
|
||||
{hit=true;result.maximumSupportDepthMetres=std::max(
|
||||
result.maximumSupportDepthMetres,depth);break;}
|
||||
if(hit)++supported;
|
||||
}
|
||||
const bool supportOk=supported>=3;
|
||||
if(!supportOk)++result.unsupportedSamples;
|
||||
return std::pair<double,bool>{floorZ,supportOk};
|
||||
};
|
||||
|
||||
// Find a walkable surface route around the passage's support rock. The nearby grid is
|
||||
// evaluated against the same composed density field as the ramp and both L43/L45 runs.
|
||||
constexpr int GridRadius=52;
|
||||
constexpr int GridWidth=GridRadius*2+1;
|
||||
constexpr double GridStep=0.50;
|
||||
constexpr double MaximumGradeRadians=15.0*3.14159265358979323846/180.0;
|
||||
struct FSurfaceNode
|
||||
{
|
||||
bool evaluated=false,walkable=false,closed=false;
|
||||
double floorZ=0.0,cost=std::numeric_limits<double>::infinity();
|
||||
int parent=-1;
|
||||
};
|
||||
const int goalX=GridRadius+static_cast<int>(std::llround(
|
||||
(ramp.front()->positionMicrometres[0]/1000000.0-arrivalX)/GridStep));
|
||||
const int goalY=GridRadius+static_cast<int>(std::llround(
|
||||
(ramp.front()->positionMicrometres[1]/1000000.0-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;
|
||||
const auto evaluateNode=[&](int ix,int iy)->FSurfaceNode*
|
||||
{
|
||||
if(ix<0||iy<0||ix>=GridWidth||iy>=GridWidth)return nullptr;
|
||||
auto& node=surfaceNodes[static_cast<std::size_t>(nodeIndex(ix,iy))];
|
||||
if(node.evaluated)return &node;
|
||||
node.evaluated=true;++exploredNodes;
|
||||
const double x=arrivalX+(ix-GridRadius)*GridStep;
|
||||
const double y=arrivalY+(iy-GridRadius)*GridStep;
|
||||
const auto blockedBefore=result.blockedCapsuleSamples;
|
||||
const auto unsupportedBefore=result.unsupportedSamples;
|
||||
const auto pathBefore=result.pathSamples;
|
||||
const double scanCenter=arrivalCenterZ;
|
||||
const auto [floorZ,supported]=samplePoint(x,y,scanCenter,false);
|
||||
node.floorZ=floorZ;
|
||||
node.walkable=supported&&result.pathSamples>pathBefore
|
||||
&&result.blockedCapsuleSamples==blockedBefore
|
||||
&&result.unsupportedSamples==unsupportedBefore;
|
||||
return &node;
|
||||
};
|
||||
struct FOpenNode { double score=0.0;int index=0; };
|
||||
const auto farther=[](const FOpenNode& a,const FOpenNode& b)
|
||||
{return a.score>b.score;};
|
||||
std::priority_queue<FOpenNode,std::vector<FOpenNode>,decltype(farther)> open(farther);
|
||||
const int goalIndex=goalX>=0&&goalY>=0&&goalX<GridWidth&&goalY<GridWidth
|
||||
?nodeIndex(goalX,goalY):-1;
|
||||
if(goalIndex>=0)
|
||||
{
|
||||
constexpr double GroundStartRadius=18.0;
|
||||
const int sourceRadius=static_cast<int>(std::ceil(GroundStartRadius/GridStep));
|
||||
for(int iy=GridRadius-sourceRadius;iy<=GridRadius+sourceRadius;++iy)
|
||||
for(int ix=GridRadius-sourceRadius;ix<=GridRadius+sourceRadius;++ix)
|
||||
{
|
||||
const double dx=(ix-GridRadius)*GridStep,dy=(iy-GridRadius)*GridStep;
|
||||
const double distance=std::hypot(dx,dy);
|
||||
if(distance>GroundStartRadius)continue;
|
||||
auto* start=evaluateNode(ix,iy);
|
||||
if(!start||!start->walkable)continue;
|
||||
start->cost=distance;start->parent=-1;
|
||||
open.push({distance+GridStep*std::hypot(goalX-ix,goalY-iy),
|
||||
nodeIndex(ix,iy)});
|
||||
}
|
||||
constexpr std::array<std::array<int,2>,8> neighbours={{{1,0},{-1,0},{0,1},{0,-1},
|
||||
{1,1},{1,-1},{-1,1},{-1,-1}}};
|
||||
while(!open.empty())
|
||||
{
|
||||
const int currentIndex=open.top().index;open.pop();
|
||||
auto& current=surfaceNodes[static_cast<std::size_t>(currentIndex)];
|
||||
if(current.closed)continue;
|
||||
current.closed=true;
|
||||
if(currentIndex==goalIndex)break;
|
||||
const int currentX=currentIndex%GridWidth,currentY=currentIndex/GridWidth;
|
||||
for(const auto& direction:neighbours)
|
||||
{
|
||||
const int nextX=currentX+direction[0],nextY=currentY+direction[1];
|
||||
auto* next=evaluateNode(nextX,nextY);
|
||||
if(!next||!next->walkable||next->closed)continue;
|
||||
const double run=GridStep*std::hypot(direction[0],direction[1]);
|
||||
const double rise=std::abs(next->floorZ-current.floorZ);
|
||||
if(std::atan2(rise,run)>MaximumGradeRadians)continue;
|
||||
if(direction[0]!=0&&direction[1]!=0)
|
||||
{
|
||||
const auto* sideA=evaluateNode(currentX+direction[0],currentY);
|
||||
const auto* sideB=evaluateNode(currentX,currentY+direction[1]);
|
||||
if(!sideA||!sideA->walkable||!sideB||!sideB->walkable)continue;
|
||||
}
|
||||
const double cost=current.cost+run+rise*0.25;
|
||||
if(cost>=next->cost)continue;
|
||||
next->cost=cost;next->parent=currentIndex;
|
||||
const double heuristic=GridStep*std::hypot(goalX-nextX,goalY-nextY);
|
||||
open.push({cost+heuristic,nodeIndex(nextX,nextY)});
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<int> surfaceRoute;
|
||||
if(goalIndex>=0&&surfaceNodes[static_cast<std::size_t>(goalIndex)].closed)
|
||||
{
|
||||
for(int current=goalIndex;current>=0;current=surfaceNodes[static_cast<std::size_t>(current)].parent)
|
||||
surfaceRoute.push_back(current);
|
||||
std::reverse(surfaceRoute.begin(),surfaceRoute.end());
|
||||
}
|
||||
const bool surfaceGoalWalkable=goalIndex>=0
|
||||
&&evaluateNode(goalX,goalY)->walkable;
|
||||
if(surfaceRoute.size()<2)surfaceRoute.clear();
|
||||
const auto surfaceRouteSamples=surfaceRoute.size();
|
||||
double surfaceStartDistance=std::numeric_limits<double>::infinity();
|
||||
if(!surfaceRoute.empty())
|
||||
{
|
||||
path.reserve(surfaceRoute.size()+ramp.size());
|
||||
const int startX=surfaceRoute.front()%GridWidth;
|
||||
const int startY=surfaceRoute.front()/GridWidth;
|
||||
surfaceStartDistance=GridStep*std::hypot(startX-GridRadius,startY-GridRadius);
|
||||
for(const int index:surfaceRoute)
|
||||
{
|
||||
const int ix=index%GridWidth,iy=index/GridWidth;
|
||||
const auto& node=surfaceNodes[static_cast<std::size_t>(index)];
|
||||
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});
|
||||
}
|
||||
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,
|
||||
static_cast<unsigned long long>(exploredNodes),surfaceRouteSamples);
|
||||
result={};
|
||||
if(path.size()<2)return result;
|
||||
|
||||
bool allSupport=true,allGrade=true;
|
||||
bool havePrior=false;double priorX=0.0,priorY=0.0,priorFloor=0.0;
|
||||
std::size_t diagnosticCount=0;
|
||||
for(std::size_t segment=1;segment<path.size();++segment)
|
||||
{
|
||||
const auto& from=path[segment-1];const auto& to=path[segment];
|
||||
const double horizontal=std::hypot(to.x-from.x,to.y-from.y);
|
||||
const std::size_t steps=std::max<std::size_t>(1,
|
||||
static_cast<std::size_t>(std::ceil(horizontal/0.8)));
|
||||
for(std::size_t step=segment==1?0:1;step<=steps;++step)
|
||||
{
|
||||
const double t=static_cast<double>(step)/steps;
|
||||
const double x=from.x+(to.x-from.x)*t;
|
||||
const double y=from.y+(to.y-from.y)*t;
|
||||
const double centerZ=from.z+(to.z-from.z)*t;
|
||||
const auto blockedBefore=result.blockedCapsuleSamples;
|
||||
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);
|
||||
allSupport&=supportOk;
|
||||
double grade=0.0;
|
||||
if(havePrior)
|
||||
{
|
||||
const double run=std::hypot(x-priorX,y-priorY);
|
||||
grade=run>1.0e-6
|
||||
?std::atan2(std::abs(floorZ-priorFloor),run)*180.0/3.14159265358979323846
|
||||
:std::numeric_limits<double>::infinity();
|
||||
result.maximumGradeDegrees=std::max(result.maximumGradeDegrees,grade);
|
||||
if(grade>15.0) {++result.gradeFailures;allGrade=false;}
|
||||
}
|
||||
if(diagnosticCount<16&&(result.blockedCapsuleSamples>blockedBefore
|
||||
||!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,
|
||||
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),
|
||||
static_cast<unsigned long long>(result.supportSamples-supportBefore),
|
||||
grade,densityMetres(x,y,centerZ),result.latestPointMinimumClearanceMetres,
|
||||
result.latestPointMinimumClearancePosition[0],
|
||||
result.latestPointMinimumClearancePosition[1],
|
||||
result.latestPointMinimumClearancePosition[2]);
|
||||
++diagnosticCount;
|
||||
}
|
||||
havePrior=true;priorX=x;priorY=y;priorFloor=floorZ;
|
||||
}
|
||||
}
|
||||
result.passed=result.pathSamples>1&&result.capsuleSamples>0
|
||||
&&result.blockedCapsuleSamples==0&&allSupport&&allGrade
|
||||
&&result.unsupportedSamples==0&&result.gradeFailures==0;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool MakeL43BuriedLandmarkControl(
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3& source,
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV3& control,std::string& error)
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
control=source;control.stamps.clear();control.stampVocabularyVersion=1;
|
||||
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,
|
||||
std::int64_t rough,std::int64_t wavelength,std::uint64_t seed,
|
||||
std::uint8_t yaw)
|
||||
{
|
||||
ResolvedLayoutStampV1 stamp;stamp.stableId=std::move(id);
|
||||
stamp.strateIndex=1;stamp.kind=kind;stamp.positionMicrometres=position;
|
||||
stamp.halfExtentsMicrometres=half;stamp.smoothWidthMicrometres=smooth;
|
||||
stamp.irregularityAmplitudeMicrometres=rough;
|
||||
stamp.irregularityWavelengthMicrometres=wavelength;
|
||||
stamp.irregularitySeed=seed;stamp.yawQuarterTurns=yaw;
|
||||
const std::int64_t pad=smooth+rough;
|
||||
const std::array<std::int64_t,3> bounds={
|
||||
half[yaw%2?1:0]+pad,half[yaw%2?0:1]+pad,half[2]+pad};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{stamp.boundsMinimumMicrometres[axis]=position[axis]-bounds[axis];
|
||||
stamp.boundsMaximumMicrometres[axis]=position[axis]+bounds[axis];}
|
||||
control.stamps.push_back(std::move(stamp));
|
||||
};
|
||||
add("landmark/sw-arrival-chamber-v1",ResolvedSdfStampKindV1::carveEllipsoid,
|
||||
{-8917798,31949858,-513083425},{8500000,7500000,6000000},
|
||||
800000,700000,4800000,4704841454711558ull,0);
|
||||
add("landmark/sw-arrival-chamber-entrance-v1",ResolvedSdfStampKindV1::carveArch,
|
||||
{-12289139,37026886,-513310578},{2600000,4200000,3200000},
|
||||
350000,250000,2600000,2371181934872559ull,0);
|
||||
add("landmark/sw-arrival-chamber-dais-v1",ResolvedSdfStampKindV1::fillDais,
|
||||
{-7489021,30688194,-515838454},{3200000,2800000,900000},
|
||||
300000,180000,2200000,7240637434535735ull,0);
|
||||
if(!control.finalize(error))return false;
|
||||
std::vector<ResolvedLayoutIssueV2> issues;
|
||||
return control.validate(ResolvedLayoutValidationModeV3::strict,issues,error);
|
||||
}
|
||||
|
||||
inline bool SameTileOutput(const VoxelForgeCore::TileMesh& A,
|
||||
const VoxelForgeCore::TileMesh& B)
|
||||
{
|
||||
@@ -424,7 +783,7 @@ namespace StandaloneStreamBenchmark
|
||||
bool bAuditResolvedLayoutPassages=false,
|
||||
bool bAuditStrateTransitions=false,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3* ResolvedLayout=nullptr,
|
||||
bool bPrintTileHashes=false)
|
||||
bool bPrintTileHashes=false,bool bAuditLandmarkReachability=false)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
FStandaloneOwnerWorld& World=*OwnerWorld;
|
||||
@@ -609,6 +968,67 @@ namespace StandaloneStreamBenchmark
|
||||
Streamer.Stop();
|
||||
std::fprintf(stderr,"[StreamPipeline] full shape build failed: %s\n",BuildError.c_str());return 2;
|
||||
}
|
||||
if(bAuditLandmarkReachability)
|
||||
{
|
||||
Streamer.Stop();
|
||||
if(ResolvedLayout==nullptr||!CoreSnapshot.resolvedLayout()
|
||||
||ResolvedLayout->stamps.empty())
|
||||
{std::fprintf(stderr,"[LandmarkReachabilityAudit] missing demo layout and stamps\n");return 2;}
|
||||
const auto candidate=AuditLandmarkWalkPath(Shape,Assembly,*ResolvedLayout,
|
||||
World.CoreVoxelSizeMicrons,"L45");
|
||||
const auto daisStamp=std::find_if(ResolvedLayout->stamps.begin(),
|
||||
ResolvedLayout->stamps.end(),[](const auto& stamp)
|
||||
{return stamp.stableId=="landmark/sw-arrival-chamber-v1-dais";});
|
||||
if(daisStamp!=ResolvedLayout->stamps.end())
|
||||
{
|
||||
const double voxelMetres=std::max<std::uint32_t>(1,
|
||||
World.CoreVoxelSizeMicrons)/1000000.0;
|
||||
for(const double offset:{-0.5,0.0,0.5,0.9,1.2})
|
||||
{
|
||||
const double x=daisStamp->positionMicrometres[0]/1000000.0;
|
||||
const double y=daisStamp->positionMicrometres[1]/1000000.0;
|
||||
const double z=daisStamp->positionMicrometres[2]/1000000.0+offset;
|
||||
const double density=Shape.densityAt({x/voxelMetres,y/voxelMetres,
|
||||
z/voxelMetres})*voxelMetres;
|
||||
std::printf("[LandmarkDaisProfile] offset_m=%.2f sample_z_m=%.3f density_m=%.4f\n",
|
||||
offset,z,density);
|
||||
}
|
||||
}
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV3 oldLayout;
|
||||
std::string oldError;
|
||||
if(!MakeL43BuriedLandmarkControl(*ResolvedLayout,oldLayout,oldError))
|
||||
{std::fprintf(stderr,"[LandmarkReachabilityAudit] L43 control layout failed: %s\n",
|
||||
oldError.c_str());return 2;}
|
||||
auto oldSnapshot=CoreSnapshot.withResolvedLayout(oldLayout);
|
||||
if(!oldSnapshot)
|
||||
{std::fprintf(stderr,"[LandmarkReachabilityAudit] could not construct L43 control snapshot\n");return 2;}
|
||||
VoxelForgeCore::World oldShape(oldSnapshot);
|
||||
std::string oldBuildError;
|
||||
if(!oldShape.compile(ShapeRequest,Scheduler,oldBuildError))
|
||||
{std::fprintf(stderr,"[LandmarkReachabilityAudit] L43 control field build failed: %s\n",
|
||||
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",
|
||||
static_cast<unsigned long long>(candidate.pathSamples),
|
||||
static_cast<unsigned long long>(candidate.capsuleSamples),
|
||||
static_cast<unsigned long long>(candidate.blockedCapsuleSamples),
|
||||
static_cast<unsigned long long>(candidate.supportSamples),
|
||||
static_cast<unsigned long long>(candidate.unsupportedSamples),
|
||||
static_cast<unsigned long long>(candidate.gradeFailures),
|
||||
candidate.maximumGradeDegrees,candidate.minimumClearanceMetres,
|
||||
candidate.maximumSupportDepthMetres,candidate.passed?"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;
|
||||
std::printf("[LandmarkReachabilityAudit] positive=PASS control=%s status=%s\n",
|
||||
buried.passed?"FAILED":"EXPECTED_FAIL",passed?"PASS":"FAIL");
|
||||
return passed?0:13;
|
||||
}
|
||||
if(bAuditStrateTransitions)
|
||||
{
|
||||
if(ResolvedLayout==nullptr||!CoreSnapshot.resolvedLayout())
|
||||
|
||||
Reference in new issue
Block a user