feat(core): native lattice generator, slice 1 -- helix-ramp spine + small maze (L58); research R27
Core.Lattice.PrimitiveField v1, opt-in per strate (legacy Maze/VerticalShaft stay legacy): resolves the real arrival and departure mouths first, plans a mandatory spine, then maze cells and two dead ends (9 rooms, 9 edges, stable IDs). The spine descends 20 m through four helix turns (6 m radius, 3 m clear width, 5 m pitch, 2.6 m clearance; max grade 10.0 deg); each feature is one filleted void (0.57 m measured). Final-field reachability passes at 883 stations; an orphaned shaft control is rejected; exact intervals (0 misses) and a whole-surface audit with 0 flags. Whole view 1.69 s (above the 1 s target). fast/medium/full PASS. Research R27 (self-checked): static stream-to-pool water design. 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
1e3e04bbac
commit
6c6cc37e1d
25 files changed
+280497
-117
No files matched your search
@@ -64,6 +64,8 @@ namespace
|
||||
int RenderDistanceOverride=0;
|
||||
bool FloatingIslandsRecipe=false;
|
||||
bool CavesRecipe=false;
|
||||
bool LatticeRecipe=false;
|
||||
int LatticeStrateIndex=-1;
|
||||
bool AuditHeightfieldProfile=false;
|
||||
bool LandmarkChamberDemo=false;
|
||||
bool LandmarkL45RampControl=false;
|
||||
@@ -71,6 +73,8 @@ namespace
|
||||
bool AuditOreVeinYield=false;
|
||||
bool AuditLandmarkReachability=false;
|
||||
bool AuditNativeCaves=false;
|
||||
bool AuditNativeLattice=false;
|
||||
bool LatticeOrphanControl=false;
|
||||
bool AuditMotifIntervals=false;
|
||||
bool TlsProbeRequested=false;
|
||||
int TlsProbeWorkers=0;
|
||||
@@ -946,6 +950,283 @@ namespace
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MakeNativeLatticeSpine(const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
|
||||
std::int32_t seed,std::int32_t latticeStrateIndex,bool orphanControl,
|
||||
std::vector<VoxelForgeCore::ResolvedCavernRoomV1>& rooms,
|
||||
std::vector<VoxelForgeCore::ResolvedCavernEdgeV1>& edges,std::string& error)
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
rooms.clear();edges.clear();error.clear();
|
||||
const auto lattice=std::find_if(layout.strates.begin(),layout.strates.end(),
|
||||
[latticeStrateIndex](const auto& value)
|
||||
{return value.strateIndex==latticeStrateIndex
|
||||
&&IsNativeLatticeFieldStratum(value);});
|
||||
if(lattice==layout.strates.end())
|
||||
{error="native lattice recipe does not name an opt-in Maze or VerticalShafts strate";return false;}
|
||||
|
||||
std::vector<const ResolvedLayoutPassageV1*> arrivals,departures;
|
||||
for(const auto& passage:layout.passages)
|
||||
{
|
||||
if(passage.lowerStrateIndex==latticeStrateIndex)arrivals.push_back(&passage);
|
||||
if(passage.upperStrateIndex==latticeStrateIndex)departures.push_back(&passage);
|
||||
}
|
||||
const auto byStableId=[](const auto* left,const auto* right)
|
||||
{return left->stableId<right->stableId;};
|
||||
std::sort(arrivals.begin(),arrivals.end(),byStableId);
|
||||
std::sort(departures.begin(),departures.end(),byStableId);
|
||||
if(arrivals.size()!=1||departures.size()!=1)
|
||||
{error="native lattice spine requires exactly one resolved arrival and one departure mouth";return false;}
|
||||
if(layout.coreVoxelSizeMicrometres==0)
|
||||
{error="native lattice spine requires a resolved Core voxel size";return false;}
|
||||
|
||||
const double micrometresPerSubvoxel=static_cast<double>(layout.coreVoxelSizeMicrometres)
|
||||
/WorldAssemblySubvoxelsPerVoxel;
|
||||
const auto toMicrometres=[&](const std::array<std::int64_t,3>& point)
|
||||
{
|
||||
std::array<std::int64_t,3> result{};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
result[axis]=static_cast<std::int64_t>(std::llround(
|
||||
point[axis]*micrometresPerSubvoxel));
|
||||
return result;
|
||||
};
|
||||
const auto toMetres=[](const std::array<std::int64_t,3>& point)
|
||||
{return std::array<double,3>{point[0]/1.0e6,point[1]/1.0e6,point[2]/1.0e6};};
|
||||
const auto micronPoint=[](const std::array<double,3>& point)
|
||||
{
|
||||
std::array<std::int64_t,3> result{};
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
result[axis]=static_cast<std::int64_t>(std::llround(point[axis]*1.0e6));
|
||||
return result;
|
||||
};
|
||||
const auto tangentAt=[&](const ResolvedLayoutPassageV1& passage,bool lower,
|
||||
std::array<double,2>& direction)
|
||||
{
|
||||
if(passage.controlPoints.size()<2)return false;
|
||||
const std::size_t index=lower?std::min(passage.lowerMouthControlIndex,
|
||||
passage.controlPoints.size()-1):0;
|
||||
const std::size_t other=lower?(index>0?index-1:passage.controlPoints.size()-2)
|
||||
:std::min<std::size_t>(1,passage.controlPoints.size()-1);
|
||||
const auto& a=passage.controlPoints[lower?other:index].positionSubvoxels;
|
||||
const auto& b=passage.controlPoints[lower?index:other].positionSubvoxels;
|
||||
const double dx=static_cast<double>(b[0]-a[0]);
|
||||
const double dy=static_cast<double>(b[1]-a[1]);
|
||||
const double length=std::hypot(dx,dy);
|
||||
if(!(length>1.0e-9))return false;
|
||||
direction={dx/length,dy/length};
|
||||
return true;
|
||||
};
|
||||
std::array<double,2> arrivalTangent{},departureTangent{};
|
||||
if(!tangentAt(*arrivals.front(),true,arrivalTangent)
|
||||
||!tangentAt(*departures.front(),false,departureTangent))
|
||||
{error="native lattice mouth has no finite resolved passage tangent";return false;}
|
||||
const auto arrival=toMetres(toMicrometres(arrivals.front()->lowerMouthSubvoxels));
|
||||
const auto departure=toMetres(toMicrometres(departures.front()->upperMouthSubvoxels));
|
||||
constexpr double MouthPlayerCenterHeight=1.13;
|
||||
const double arrivalFloor=arrival[2]-MouthPlayerCenterHeight;
|
||||
const double departureFloor=departure[2]-MouthPlayerCenterHeight;
|
||||
const std::array<double,3> helixStart={arrival[0]+arrivalTangent[0]*3.0,
|
||||
arrival[1]+arrivalTangent[1]*3.0,arrivalFloor};
|
||||
constexpr double Pi=3.1415926535897932384626433832795;
|
||||
constexpr double CenterlineRadius=6.0;
|
||||
constexpr double ClearWidth=3.0;
|
||||
constexpr double FloorToSoffit=2.6;
|
||||
constexpr double Pitch=5.0;
|
||||
constexpr int Turns=4;
|
||||
constexpr double FloorBlend=1.9;
|
||||
constexpr double FeatureUnionBlend=0.05;
|
||||
constexpr double CorridorSpacing=14.0;
|
||||
constexpr double MazeCellSpan=16.0;
|
||||
constexpr double RouteGradeLimit=13.0;
|
||||
const std::array<double,2> lateral={-arrivalTangent[1],arrivalTangent[0]};
|
||||
const std::array<double,2> helixCenter={
|
||||
helixStart[0]+lateral[0]*CenterlineRadius,
|
||||
helixStart[1]+lateral[1]*CenterlineRadius};
|
||||
const double helixEndFloor=arrivalFloor-Pitch*Turns;
|
||||
const std::array<double,3> helixEnd={helixStart[0],helixStart[1],helixEndFloor};
|
||||
const double mouthEndX=departure[0]-departureTangent[0]*3.0;
|
||||
const double mouthEndY=departure[1]-departureTangent[1]*3.0;
|
||||
|
||||
const auto featureRoll=[&](std::string_view stableId)
|
||||
{
|
||||
std::uint64_t hash=14695981039346656037ull;
|
||||
const auto addWord=[&](std::uint64_t word)
|
||||
{for(int byte=0;byte<8;++byte){hash^=static_cast<std::uint8_t>(word>>(byte*8));hash*=1099511628211ull;}};
|
||||
addWord(static_cast<std::uint32_t>(seed));
|
||||
addWord(static_cast<std::uint32_t>(latticeStrateIndex));
|
||||
addWord(1);
|
||||
for(unsigned char value:stableId){hash^=value;hash*=1099511628211ull;}
|
||||
hash^=hash>>30;hash*=0xbf58476d1ce4e5b9ull;
|
||||
hash^=hash>>27;hash*=0x94d049bb133111ebull;
|
||||
return hash^(hash>>31);
|
||||
};
|
||||
const auto addRoom=[&](std::string id,const std::array<double,3>& floorCenter)
|
||||
{
|
||||
ResolvedCavernRoomV1 room;room.stableId=std::move(id);
|
||||
room.strateIndex=latticeStrateIndex;
|
||||
room.centerMicrometres=micronPoint({floorCenter[0],floorCenter[1],
|
||||
floorCenter[2]+FloorToSoffit*0.5});
|
||||
room.halfExtentsMicrometres={2000000,2000000,
|
||||
static_cast<std::int64_t>(std::llround(FloorToSoffit*500000.0))};
|
||||
room.floorMicrometres=static_cast<std::int64_t>(std::llround(floorCenter[2]*1.0e6));
|
||||
room.smoothWidthMicrometres=static_cast<std::int64_t>(std::llround(FloorBlend*1.0e6));
|
||||
room.unionWidthMicrometres=static_cast<std::int64_t>(
|
||||
std::llround(FeatureUnionBlend*1.0e6));
|
||||
room.irregularitySeed=featureRoll(room.stableId);
|
||||
rooms.push_back(std::move(room));
|
||||
};
|
||||
const auto addEdge=[&](std::string id,std::string startAnchor,std::string endRoom,
|
||||
std::vector<std::array<double,3>> path)
|
||||
{
|
||||
ResolvedCavernEdgeV1 edge;edge.stableId=std::move(id);
|
||||
edge.startAnchorStableId=std::move(startAnchor);
|
||||
edge.endRoomStableId=std::move(endRoom);edge.strateIndex=latticeStrateIndex;
|
||||
edge.boreRadiusMicrometres=1500000;
|
||||
edge.boreHeightMicrometres=static_cast<std::int64_t>(
|
||||
std::llround(FloorToSoffit*1.0e6));
|
||||
edge.floorSupportDepthMicrometres=1000000;
|
||||
edge.smoothWidthMicrometres=static_cast<std::int64_t>(std::llround(FloorBlend*1.0e6));
|
||||
edge.unionWidthMicrometres=static_cast<std::int64_t>(
|
||||
std::llround(FeatureUnionBlend*1.0e6));
|
||||
edge.irregularitySeed=featureRoll(edge.stableId);
|
||||
if(path.size()<2)return edge;
|
||||
for(const auto& point:path)edge.polylineMicrometres.push_back(micronPoint(point));
|
||||
const auto& first=path.front();const auto& last=path.back();
|
||||
for(std::size_t control=0;control<4;++control)
|
||||
{
|
||||
const double t=static_cast<double>(control)/3.0;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
edge.controlPointsMicrometres[control][axis]=static_cast<std::int64_t>(
|
||||
std::llround(std::lerp(first[axis],last[axis],t)*1.0e6));
|
||||
}
|
||||
return edge;
|
||||
};
|
||||
|
||||
const std::string arrivalRoomId="lattice/spine/arrival-room-v1";
|
||||
const std::string mazeCellZeroId="lattice/spine/maze-cell-00-v1";
|
||||
const std::string helixEntryRoomId=orphanControl
|
||||
?"lattice/control/orphan-shaft-entry-room-v1"
|
||||
:arrivalRoomId;
|
||||
const std::string helixEndRoomId=orphanControl
|
||||
?"lattice/control/orphan-shaft-end-room-v1":mazeCellZeroId;
|
||||
const std::array<double,3> orphanOffset=orphanControl
|
||||
?std::array<double,3>{lateral[0]*20.0,lateral[1]*20.0,0.0}
|
||||
:std::array<double,3>{0.0,0.0,0.0};
|
||||
addRoom(arrivalRoomId,helixStart);
|
||||
if(orphanControl)
|
||||
addRoom(helixEntryRoomId,{helixStart[0]+orphanOffset[0],
|
||||
helixStart[1]+orphanOffset[1],helixStart[2]});
|
||||
addRoom(mazeCellZeroId,helixEnd);
|
||||
if(orphanControl)
|
||||
addRoom(helixEndRoomId,{helixEnd[0]+orphanOffset[0],
|
||||
helixEnd[1]+orphanOffset[1],helixEnd[2]});
|
||||
const std::string departureRoomId="lattice/spine/departure-room-v1";
|
||||
addRoom(departureRoomId,{departure[0],departure[1],departureFloor});
|
||||
edges.push_back(addEdge("lattice/spine/00-arrival-portal-v1",
|
||||
arrivals.front()->stableId+"/lower-mouth",arrivalRoomId,
|
||||
{{arrival[0],arrival[1],arrivalFloor},helixStart}));
|
||||
std::vector<std::array<double,3>> helixPath;
|
||||
constexpr double HelixSampleArcLength=0.5;
|
||||
const double helixLength=std::hypot(2.0*Pi*CenterlineRadius*Turns,
|
||||
Pitch*Turns);
|
||||
const int helixSteps=std::max(64,static_cast<int>(std::ceil(
|
||||
helixLength/HelixSampleArcLength)));
|
||||
helixPath.reserve(static_cast<std::size_t>(helixSteps)+1);
|
||||
for(int sample=0;sample<=helixSteps;++sample)
|
||||
{
|
||||
const double u=static_cast<double>(sample)/helixSteps;
|
||||
const double angle=2.0*Pi*Turns*u;
|
||||
helixPath.push_back({
|
||||
helixCenter[0]-lateral[0]*CenterlineRadius*std::cos(angle)
|
||||
+arrivalTangent[0]*CenterlineRadius*std::sin(angle)+orphanOffset[0],
|
||||
helixCenter[1]-lateral[1]*CenterlineRadius*std::cos(angle)
|
||||
+arrivalTangent[1]*CenterlineRadius*std::sin(angle)+orphanOffset[1],
|
||||
arrivalFloor-Pitch*Turns*u});
|
||||
}
|
||||
edges.push_back(addEdge("lattice/spine/01-helix-ramp-v1",
|
||||
helixEntryRoomId,helixEndRoomId,std::move(helixPath)));
|
||||
|
||||
double dx=mouthEndX-helixEnd[0],dy=mouthEndY-helixEnd[1];
|
||||
double horizontal=std::hypot(dx,dy);
|
||||
double routeX=horizontal>1.0e-9?dx/horizontal:arrivalTangent[0];
|
||||
double routeY=horizontal>1.0e-9?dy/horizontal:arrivalTangent[1];
|
||||
double routeSideX=-routeY,routeSideY=routeX;
|
||||
const double vertical=departureFloor-helixEndFloor;
|
||||
const int routeEdges=4;
|
||||
const double requiredRun=std::abs(vertical)/std::tan(RouteGradeLimit*Pi/180.0);
|
||||
const double sideSpan=std::max(MazeCellSpan,
|
||||
(requiredRun-horizontal)/3.0);
|
||||
if(sideSpan>24.0)
|
||||
{error="native lattice maze spine requires more than 24 m cells to preserve its grade";return false;}
|
||||
std::vector<std::array<double,2>> mazeXY={
|
||||
{helixEnd[0],helixEnd[1]},
|
||||
{helixEnd[0]+routeX*sideSpan,helixEnd[1]+routeY*sideSpan},
|
||||
{helixEnd[0]+routeX*sideSpan+routeSideX*sideSpan,
|
||||
helixEnd[1]+routeY*sideSpan+routeSideY*sideSpan},
|
||||
{mouthEndX+routeSideX*sideSpan,mouthEndY+routeSideY*sideSpan},
|
||||
{mouthEndX,mouthEndY}};
|
||||
std::array<double,5> routeDistance{};
|
||||
for(std::size_t index=1;index<mazeXY.size();++index)
|
||||
routeDistance[index]=routeDistance[index-1]
|
||||
+std::hypot(mazeXY[index][0]-mazeXY[index-1][0],
|
||||
mazeXY[index][1]-mazeXY[index-1][1]);
|
||||
const double mazeRun=routeDistance.back();
|
||||
if(!(mazeRun>=requiredRun))
|
||||
{error="native lattice maze spine cannot satisfy the 13 degree grade limit";return false;}
|
||||
std::vector<std::array<double,3>> cells(mazeXY.size());
|
||||
for(std::size_t index=0;index<mazeXY.size();++index)
|
||||
cells[index]={mazeXY[index][0],mazeXY[index][1],
|
||||
helixEndFloor+vertical*(routeDistance[index]/mazeRun)};
|
||||
std::vector<std::string> cellIds(static_cast<std::size_t>(routeEdges)+1);
|
||||
cellIds[0]=mazeCellZeroId;
|
||||
for(int index=1;index<=routeEdges;++index)
|
||||
{
|
||||
char suffix[16]{};std::snprintf(suffix,sizeof(suffix),"%02d",index);
|
||||
cellIds[static_cast<std::size_t>(index)]=std::string("lattice/spine/maze-cell-")
|
||||
+suffix+"-v1";
|
||||
addRoom(cellIds[static_cast<std::size_t>(index)],cells[static_cast<std::size_t>(index)]);
|
||||
}
|
||||
for(int index=0;index<routeEdges;++index)
|
||||
{
|
||||
char suffix[16]{};std::snprintf(suffix,sizeof(suffix),"%02d",index);
|
||||
edges.push_back(addEdge("lattice/spine/maze-edge-"+std::string(suffix)+"-v1",
|
||||
cellIds[static_cast<std::size_t>(index)],
|
||||
cellIds[static_cast<std::size_t>(index+1)],
|
||||
{cells[static_cast<std::size_t>(index)],cells[static_cast<std::size_t>(index+1)]}));
|
||||
}
|
||||
|
||||
const int branchIndices[2]={1,routeEdges-1};
|
||||
for(int branch=0;branch<2;++branch)
|
||||
{
|
||||
const int index=branchIndices[branch];
|
||||
const auto& base=cells[static_cast<std::size_t>(index)];
|
||||
const std::string roomId=branch==0?"lattice/branch/dead-end-a-room-v1"
|
||||
:"lattice/branch/dead-end-b-room-v1";
|
||||
const std::string edgeId=branch==0?"lattice/branch/dead-end-a-edge-v1"
|
||||
:"lattice/branch/dead-end-b-edge-v1";
|
||||
const int sign=branch==0?-1:1;
|
||||
const std::array<double,3> branchEnd={base[0]+routeSideX*sign*CorridorSpacing,
|
||||
base[1]+routeSideY*sign*CorridorSpacing,base[2]};
|
||||
addRoom(roomId,branchEnd);
|
||||
edges.push_back(addEdge(edgeId,cellIds[static_cast<std::size_t>(index)],
|
||||
roomId,{base,branchEnd}));
|
||||
}
|
||||
edges.push_back(addEdge("lattice/spine/99-departure-portal-v1",
|
||||
departures.front()->stableId+"/upper-mouth",departureRoomId,
|
||||
{cells.back(),{departure[0],departure[1],departureFloor}}));
|
||||
|
||||
const double innerEdgeGrade=std::atan(Pitch/(2.0*Pi*(CenterlineRadius-ClearWidth*0.5)))
|
||||
*180.0/Pi;
|
||||
const double centerlineGrade=std::atan(Pitch/(2.0*Pi*CenterlineRadius))*180.0/Pi;
|
||||
std::printf("[LatticeResolvedMouths] arrival=%s side=lower xyz_m=(%.3f,%.3f,%.3f) departure=%s side=upper xyz_m=(%.3f,%.3f,%.3f) binding=actual-resolved status=PASS\n",
|
||||
arrivals.front()->stableId.c_str(),arrival[0],arrival[1],arrival[2],
|
||||
departures.front()->stableId.c_str(),departure[0],departure[1],departure[2]);
|
||||
std::printf("[LatticeSpinePlan] recipe=Core.Lattice.PrimitiveField version=1 strate=%d stable_key=seed,strate,recipe_version,stable_id spine_first=true arrival=%s departure=%s helix_id=lattice/spine/01-helix-ramp-v1 centerline_radius_m=%.3f clear_width_m=%.3f pitch_m=%.3f turns=%d floor_to_soffit_m=%.3f centerline_grade_deg=%.3f inner_edge_grade_deg=%.3f floor_blend_m=%.3f union_envelope_m=%.3f maze_cells=%d dead_ends=2 orphan_control=%s status=PASS\n",
|
||||
latticeStrateIndex,arrivals.front()->stableId.c_str(),departures.front()->stableId.c_str(),
|
||||
CenterlineRadius,ClearWidth,Pitch,Turns,FloorToSoffit,centerlineGrade,innerEdgeGrade,
|
||||
FloorBlend,FeatureUnionBlend,routeEdges+1,orphanControl?"enabled":"disabled");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MakeOreVeinDemoRequests(
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutOreVeinRequestV1>& requests,
|
||||
@@ -1131,7 +1412,8 @@ namespace
|
||||
" --mesh-export=FILE (write the exact generated tile meshes for offline rendering)\n"
|
||||
" --world-assembler-manifest-output=FILE (write the canonical Core recipe and plan)\n"
|
||||
" --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n"
|
||||
" --seed=N --recipe=surface-world|floating-islands|caves\n"
|
||||
" --seed=N --recipe=surface-world|floating-islands|caves|lattice\n"
|
||||
" --lattice-strate=N (opt one Maze or VerticalShafts strate into Core.Lattice)\n"
|
||||
" --landmark-chamber-demo (opt-in SW cavern room and swept tunnel)\n"
|
||||
" --ore-veins-demo (opt-in material-only SW capsule veins)\n"
|
||||
" --audit-ore-vein-yield (measure one deterministic sphere carve)\n"
|
||||
@@ -1146,6 +1428,8 @@ namespace
|
||||
" --audit-motif-intervals (brute-force motif bounds, BVH and shape-free guards)\n"
|
||||
" --audit-landmark-reachability (capsule, support, grade and buried-L43 control)\n"
|
||||
" --audit-native-caves (arrival-to-departure capsule flood fill and interval oracle)\n"
|
||||
" --audit-native-lattice (mouth-to-mouth capsule, grade, support and interval proof)\n"
|
||||
" --lattice-orphan-control (disconnect a required edge for the negative control)\n"
|
||||
" --audit-surface-clearance (sample biome terrain/caps against the resolved ceiling)\n"
|
||||
" --auto-surface-camera (place the 27-tile cube on this seed's surface)\n"
|
||||
" --mesh-coverage (report non-empty tiles and indexed triangles)\n"
|
||||
@@ -1179,6 +1463,8 @@ namespace
|
||||
else if(arg=="--audit-resolved-layout-passages")options.AuditResolvedLayoutPassages=true;
|
||||
else if(arg=="--audit-landmark-reachability")options.AuditLandmarkReachability=true;
|
||||
else if(arg=="--audit-native-caves")options.AuditNativeCaves=true;
|
||||
else if(arg=="--audit-native-lattice")options.AuditNativeLattice=true;
|
||||
else if(arg=="--lattice-orphan-control")options.LatticeOrphanControl=true;
|
||||
else if(arg=="--audit-surface-clearance")options.AuditSurfaceClearance=true;
|
||||
else if(arg=="--audit-strate-transitions")options.AuditStrateTransitions=true;
|
||||
else if(arg.starts_with("--transition-mode="))
|
||||
@@ -1233,13 +1519,21 @@ namespace
|
||||
{
|
||||
const std::string_view recipe=arg.substr(std::string_view("--recipe=").size());
|
||||
if(recipe=="surface-world")
|
||||
{options.FloatingIslandsRecipe=false;options.CavesRecipe=false;}
|
||||
{options.FloatingIslandsRecipe=false;options.CavesRecipe=false;options.LatticeRecipe=false;}
|
||||
else if(recipe=="floating-islands")
|
||||
{options.FloatingIslandsRecipe=true;options.CavesRecipe=false;}
|
||||
{options.FloatingIslandsRecipe=true;options.CavesRecipe=false;options.LatticeRecipe=false;}
|
||||
else if(recipe=="caves")
|
||||
{options.FloatingIslandsRecipe=false;options.CavesRecipe=true;}
|
||||
{options.FloatingIslandsRecipe=false;options.CavesRecipe=true;options.LatticeRecipe=false;}
|
||||
else if(recipe=="lattice")
|
||||
{options.FloatingIslandsRecipe=false;options.CavesRecipe=false;options.LatticeRecipe=true;}
|
||||
else {std::fprintf(stderr,"Invalid --recipe value: %.*s\n",static_cast<int>(recipe.size()),recipe.data());return false;}
|
||||
}
|
||||
else if(arg.starts_with("--lattice-strate="))
|
||||
{
|
||||
if(!readInt("--lattice-strate=",options.LatticeStrateIndex)
|
||||
||options.LatticeStrateIndex<0)
|
||||
{std::fprintf(stderr,"--lattice-strate must be a non-negative strate index\n");return false;}
|
||||
}
|
||||
else if(arg=="--landmark-chamber-demo")options.LandmarkChamberDemo=true;
|
||||
else if(arg=="--landmark-l45-ramp-control")options.LandmarkL45RampControl=true;
|
||||
else if(arg=="--ore-veins-demo")options.OreVeinsDemo=true;
|
||||
@@ -1330,6 +1624,10 @@ namespace
|
||||
{std::fprintf(stderr,"--landmark-l45-ramp-control requires --landmark-chamber-demo\n");return false;}
|
||||
if(options.AuditOreVeinYield&&!options.OreVeinsDemo)
|
||||
{std::fprintf(stderr,"--audit-ore-vein-yield requires --ore-veins-demo\n");return false;}
|
||||
if(options.LatticeRecipe&&options.LatticeStrateIndex<0)
|
||||
{std::fprintf(stderr,"--recipe=lattice requires an explicit --lattice-strate=N selection\n");return false;}
|
||||
if((options.AuditNativeLattice||options.LatticeOrphanControl)&&!options.LatticeRecipe)
|
||||
{std::fprintf(stderr,"native lattice audits require --recipe=lattice\n");return false;}
|
||||
if(options.TlsProbeRequested)return true;
|
||||
if(!options.WorldAssemblerPrototype||!options.StreamBenchmark||!options.StreamPipeline)
|
||||
{
|
||||
@@ -1427,8 +1725,10 @@ int main(int argc,char** argv)
|
||||
}
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV3 resolvedLayout;
|
||||
{
|
||||
const std::string_view variant=options.CavesRecipe?"caves"
|
||||
:options.FloatingIslandsRecipe?"floating-islands":"surface-world";
|
||||
const std::string variant=options.LatticeRecipe
|
||||
?"lattice/"+std::to_string(options.LatticeStrateIndex)
|
||||
:options.CavesRecipe?"caves"
|
||||
:options.FloatingIslandsRecipe?"floating-islands":"surface-world";
|
||||
if(!StandaloneWorldAssemblyPrototype::BuildResolvedLayoutSnapshot(
|
||||
*ownerWorld,worldAssembly,variant,resolvedLayout,error))
|
||||
{std::fprintf(stderr,"Resolved-layout snapshot failed: %s\n",error.c_str());return 2;}
|
||||
@@ -1515,6 +1815,18 @@ int main(int argc,char** argv)
|
||||
resolvedLayout,seed,rooms,edges,error))
|
||||
{std::fprintf(stderr,"Native caves spine resolution failed: %s\n",error.c_str());return 2;}
|
||||
}
|
||||
if(options.LatticeRecipe)
|
||||
{
|
||||
if(options.LatticeStrateIndex<0)
|
||||
{std::fprintf(stderr,"--recipe=lattice requires an explicit --lattice-strate=N selection\n");return 2;}
|
||||
std::vector<VoxelForgeCore::ResolvedCavernRoomV1> rooms;
|
||||
std::vector<VoxelForgeCore::ResolvedCavernEdgeV1> edges;
|
||||
if(!MakeNativeLatticeSpine(resolvedLayout,seed,options.LatticeStrateIndex,
|
||||
options.LatticeOrphanControl,rooms,edges,error)
|
||||
||!VoxelForgeCore::ResolveResolvedCavernGraphV1(
|
||||
resolvedLayout,seed,rooms,edges,error))
|
||||
{std::fprintf(stderr,"Native lattice graph resolution failed: %s\n",error.c_str());return 2;}
|
||||
}
|
||||
if(options.OreVeinsDemo)
|
||||
{
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutOreVeinRequestV1> requests;
|
||||
@@ -1609,7 +1921,8 @@ int main(int argc,char** argv)
|
||||
slot.StrateIndex,resolvedStrate->generatorKind.c_str(),
|
||||
static_cast<std::uint32_t>(slot.Definition->PassageConfig.Style));
|
||||
}
|
||||
const auto requestedRecipe=options.CavesRecipe?std::string_view("caves")
|
||||
const auto requestedRecipe=options.LatticeRecipe?std::string_view("lattice")
|
||||
:options.CavesRecipe?std::string_view("caves")
|
||||
:options.FloatingIslandsRecipe?std::string_view("floating-islands")
|
||||
:std::string_view("surface-world");
|
||||
const auto topStratum=std::max_element(resolvedLayout.strates.begin(),resolvedLayout.strates.end(),
|
||||
@@ -1626,10 +1939,13 @@ int main(int argc,char** argv)
|
||||
[&](const auto& plan){return plan.strateIndex==stratum.strateIndex;});
|
||||
});
|
||||
const bool hasNativeCavesRecipe=std::any_of(resolvedLayout.strates.begin(),resolvedLayout.strates.end(),
|
||||
[](const auto& stratum){return stratum.generatorKind=="Caves"
|
||||
&&stratum.recipeId=="Core.Caves.PrimitiveField"&&stratum.recipeVersion==1
|
||||
[](const auto& stratum){return VoxelForgeCore::IsNativeCaveFieldStratum(stratum)
|
||||
&&stratum.passageCapabilities.hasNativeCaveField;});
|
||||
const auto actualRecipe=hasNativeCavesRecipe?std::string_view("caves")
|
||||
const bool hasNativeLatticeRecipe=std::any_of(resolvedLayout.strates.begin(),resolvedLayout.strates.end(),
|
||||
[](const auto& stratum){return VoxelForgeCore::IsNativeLatticeFieldStratum(stratum)
|
||||
&&stratum.passageCapabilities.hasNativeLatticeField;});
|
||||
const auto actualRecipe=hasNativeLatticeRecipe?std::string_view("lattice")
|
||||
:hasNativeCavesRecipe?std::string_view("caves")
|
||||
:hasFloatingIslands?std::string_view("floating-islands")
|
||||
:hasSurfaceWorldPlan?std::string_view("surface-world")
|
||||
:std::string_view("missing");
|
||||
@@ -1834,5 +2150,6 @@ int main(int argc,char** argv)
|
||||
options.AuditMotifIntervals,
|
||||
options.ExactMidpointDensityMemo,options.ActiveLaneBatching,
|
||||
options.ProofBlockCells,options.HasSignEdgeRepairOverride
|
||||
?options.ParallelSignEdgeRepair:options.FloatingIslandsRecipe);
|
||||
?options.ParallelSignEdgeRepair:options.FloatingIslandsRecipe,
|
||||
options.AuditNativeLattice,options.LatticeOrphanControl);
|
||||
}
|
||||
@@ -1403,6 +1403,342 @@ namespace StandaloneStreamBenchmark
|
||||
return reachable&&intervalOk&&measured>=0.5;
|
||||
}
|
||||
|
||||
inline bool AuditNativeLatticeField(const VoxelForgeCore::World& shape,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
|
||||
std::uint32_t voxelMicrometres,bool orphanControl)
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
constexpr double Pi=3.1415926535897932384626433832795;
|
||||
constexpr double Q=static_cast<double>(WorldAssemblySubvoxelsPerVoxel);
|
||||
constexpr double MouthCenterHeight=1.13;
|
||||
const double vm=std::max<std::uint32_t>(1,voxelMicrometres)/1.0e6;
|
||||
const auto metres=[](const std::array<std::int64_t,3>& p)
|
||||
{return std::array<double,3>{p[0]/1.0e6,p[1]/1.0e6,p[2]/1.0e6};};
|
||||
const auto density=[&](double x,double y,double z)
|
||||
{return static_cast<double>(shape.densityAt({x/vm,y/vm,z/vm}))*vm;};
|
||||
const auto lattice=std::find_if(layout.strates.begin(),layout.strates.end(),
|
||||
[](const auto& s){return IsNativeLatticeFieldStratum(s)
|
||||
&&s.passageCapabilities.hasNativeLatticeField;});
|
||||
if(lattice==layout.strates.end()||layout.cavernGraphVocabularyVersion!=1)
|
||||
{std::printf("[LatticeAudit] recipe=%s graph=%u status=FAIL\n",
|
||||
lattice==layout.strates.end()?"missing":"present",
|
||||
layout.cavernGraphVocabularyVersion);return false;}
|
||||
const ResolvedLayoutPassageV1* arrival=nullptr;
|
||||
const ResolvedLayoutPassageV1* departure=nullptr;
|
||||
for(const auto& passage:layout.passages)
|
||||
{
|
||||
if(passage.lowerStrateIndex==lattice->strateIndex)
|
||||
{if(arrival)return false;arrival=&passage;}
|
||||
if(passage.upperStrateIndex==lattice->strateIndex)
|
||||
{if(departure)return false;departure=&passage;}
|
||||
}
|
||||
if(!arrival||!departure)return false;
|
||||
const auto mouthMetres=[&](const std::array<std::int64_t,3>& point)
|
||||
{return std::array<double,3>{point[0]*vm/Q,point[1]*vm/Q,
|
||||
point[2]*vm/Q-MouthCenterHeight};};
|
||||
const auto arrivalPoint=mouthMetres(arrival->lowerMouthSubvoxels);
|
||||
const auto departurePoint=mouthMetres(departure->upperMouthSubvoxels);
|
||||
std::vector<const ResolvedCavernEdgeV1*> mazeEdges;
|
||||
const ResolvedCavernEdgeV1* arrivalPortal=nullptr;
|
||||
const ResolvedCavernEdgeV1* helix=nullptr;
|
||||
const ResolvedCavernEdgeV1* departurePortal=nullptr;
|
||||
for(const auto& edge:layout.cavernEdges)
|
||||
{
|
||||
if(edge.strateIndex!=lattice->strateIndex)continue;
|
||||
if(edge.stableId=="lattice/spine/00-arrival-portal-v1")arrivalPortal=&edge;
|
||||
else if(edge.stableId=="lattice/spine/01-helix-ramp-v1")helix=&edge;
|
||||
else if(edge.stableId=="lattice/spine/99-departure-portal-v1")departurePortal=&edge;
|
||||
else if(edge.stableId.starts_with("lattice/spine/maze-edge-"))mazeEdges.push_back(&edge);
|
||||
}
|
||||
std::sort(mazeEdges.begin(),mazeEdges.end(),[](const auto* a,const auto* b)
|
||||
{return a->stableId<b->stableId;});
|
||||
if(!arrivalPortal||!helix||!departurePortal||mazeEdges.size()<4
|
||||
||layout.cavernRooms.size()<mazeEdges.size()+1)return false;
|
||||
std::vector<const ResolvedCavernEdgeV1*> ordered={arrivalPortal,helix};
|
||||
ordered.insert(ordered.end(),mazeEdges.begin(),mazeEdges.end());
|
||||
ordered.push_back(departurePortal);
|
||||
std::vector<std::array<double,3>> route;
|
||||
bool continuous=true;
|
||||
std::size_t pointCount=0;
|
||||
for(const auto* edge:ordered)
|
||||
{
|
||||
if(edge->polylineMicrometres.size()<2)return false;
|
||||
for(std::size_t i=0;i<edge->polylineMicrometres.size();++i)
|
||||
{
|
||||
const auto point=metres(edge->polylineMicrometres[i]);
|
||||
if(!route.empty())
|
||||
{
|
||||
const auto& prior=route.back();
|
||||
const double gap=std::hypot(std::hypot(point[0]-prior[0],point[1]-prior[1]),
|
||||
point[2]-prior[2]);
|
||||
if(i==0&&gap>0.05)continuous=false;
|
||||
if(i==0&&gap<=0.05)continue;
|
||||
}
|
||||
route.push_back(point);++pointCount;
|
||||
}
|
||||
}
|
||||
const auto endpointGap=[](const auto& a,const auto& b)
|
||||
{return std::hypot(std::hypot(a[0]-b[0],a[1]-b[1]),a[2]-b[2]);};
|
||||
const double arrivalGap=endpointGap(metres(arrivalPortal->polylineMicrometres.front()),
|
||||
arrivalPoint);
|
||||
const double departureGap=endpointGap(metres(departurePortal->polylineMicrometres.back()),
|
||||
departurePoint);
|
||||
continuous=continuous&&arrivalGap<=0.05&&departureGap<=0.05;
|
||||
|
||||
double maximumGrade=0.0;
|
||||
for(std::size_t i=1;i<route.size();++i)
|
||||
{
|
||||
const double horizontal=std::hypot(route[i][0]-route[i-1][0],
|
||||
route[i][1]-route[i-1][1]);
|
||||
const double vertical=std::abs(route[i][2]-route[i-1][2]);
|
||||
if(horizontal<1.0e-9&&vertical>1.0e-6)maximumGrade=90.0;
|
||||
else if(horizontal>1.0e-9)
|
||||
maximumGrade=std::max(maximumGrade,std::atan2(vertical,horizontal)*180.0/Pi);
|
||||
}
|
||||
constexpr double CenterlineRadius=6.0,ClearWidth=3.0,Pitch=5.0;
|
||||
const double helixInnerGrade=std::atan(Pitch/
|
||||
(2.0*Pi*(CenterlineRadius-ClearWidth*0.5)))*180.0/Pi;
|
||||
maximumGrade=std::max(maximumGrade,helixInnerGrade);
|
||||
|
||||
std::uint64_t stationCount=0,capsuleSamples=0,supportSamples=0,headSamples=0;
|
||||
std::uint64_t capsuleFailures=0,supportFailures=0,headFailures=0;
|
||||
std::array<double,3> firstCapsuleFailure{},firstSupportFailure{},firstHeadFailure{};
|
||||
bool hasCapsuleFailure=false,hasSupportFailure=false,hasHeadFailure=false;
|
||||
double minimumHeadroom=std::numeric_limits<double>::infinity();
|
||||
bool capsuleClear=continuous,allSupported=continuous,headroomClear=continuous;
|
||||
if(continuous)
|
||||
{
|
||||
constexpr double CapsuleRadius=0.34,CapsuleHeight=1.76,StationStep=0.25;
|
||||
constexpr std::array<std::array<double,2>,5> offsets={{{0.0,0.0},{0.20,0.0},
|
||||
{-0.20,0.0},{0.0,0.20},{0.0,-0.20}}};
|
||||
for(std::size_t segment=1;segment<route.size();++segment)
|
||||
{
|
||||
const auto& a=route[segment-1];const auto& b=route[segment];
|
||||
const double dx=b[0]-a[0],dy=b[1]-a[1],dz=b[2]-a[2];
|
||||
const double length=std::hypot(std::hypot(dx,dy),dz);
|
||||
const int steps=std::max(1,static_cast<int>(std::ceil(length/StationStep)));
|
||||
for(int step=0;step<steps;++step)
|
||||
{
|
||||
const double t=static_cast<double>(step)/steps;
|
||||
const double x=a[0]+dx*t,y=a[1]+dy*t,z=a[2]+dz*t;
|
||||
const double horizontal=std::hypot(dx,dy);
|
||||
const double nx=horizontal>1.0e-9?-dy/horizontal:0.0;
|
||||
const double ny=horizontal>1.0e-9?dx/horizontal:1.0;
|
||||
++stationCount;
|
||||
for(double h=0.10;h<=CapsuleHeight+1.0e-9;h+=0.17)
|
||||
{
|
||||
double cap=0.0;
|
||||
if(h<CapsuleRadius)cap=CapsuleRadius-h;
|
||||
else if(h>CapsuleHeight-CapsuleRadius)
|
||||
cap=h-(CapsuleHeight-CapsuleRadius);
|
||||
const double reach=std::sqrt(std::max(0.0,
|
||||
CapsuleRadius*CapsuleRadius-cap*cap));
|
||||
for(const auto& offset:offsets)
|
||||
{
|
||||
if(std::hypot(offset[0],offset[1])>reach+1.0e-8)continue;
|
||||
++capsuleSamples;
|
||||
const bool clear=density(x+nx*offset[0]-ny*offset[1],
|
||||
y+ny*offset[0]+nx*offset[1],z+0.12+h)>=0.0;
|
||||
capsuleClear=capsuleClear&&clear;
|
||||
if(!clear)
|
||||
{
|
||||
++capsuleFailures;
|
||||
if(!hasCapsuleFailure)
|
||||
{firstCapsuleFailure={x,y,z+0.12+h};hasCapsuleFailure=true;}
|
||||
}
|
||||
}
|
||||
}
|
||||
constexpr 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}}};
|
||||
for(const auto& offset:supportOffsets)
|
||||
{
|
||||
++supportSamples;bool supported=false;
|
||||
for(double depth=0.08;depth<=0.62+1.0e-9;depth+=0.06)
|
||||
supported=supported||density(x+nx*offset[0]-ny*offset[1],
|
||||
y+ny*offset[0]+nx*offset[1],z-depth)<0.0;
|
||||
allSupported=allSupported&&supported;
|
||||
if(!supported)
|
||||
{
|
||||
++supportFailures;
|
||||
if(!hasSupportFailure)
|
||||
{firstSupportFailure={x+nx*offset[0]-ny*offset[1],
|
||||
y+ny*offset[0]+nx*offset[1],z};hasSupportFailure=true;}
|
||||
}
|
||||
}
|
||||
for(const auto& offset:offsets)
|
||||
{
|
||||
++headSamples;
|
||||
const double clearance=density(x+nx*offset[0]-ny*offset[1],
|
||||
y+ny*offset[0]+nx*offset[1],z+2.20);
|
||||
headroomClear=headroomClear&&clearance>=0.0;
|
||||
if(clearance<0.0)
|
||||
{
|
||||
++headFailures;
|
||||
if(!hasHeadFailure)
|
||||
{firstHeadFailure={x+nx*offset[0]-ny*offset[1],
|
||||
y+ny*offset[0]+nx*offset[1],z+2.20};hasHeadFailure=true;}
|
||||
}
|
||||
if(clearance>=0.0)minimumHeadroom=std::min(minimumHeadroom,2.20);
|
||||
else minimumHeadroom=std::min(minimumHeadroom,0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(hasSupportFailure)
|
||||
{
|
||||
std::printf("[LatticeSupportProbe] first_xyz_m=(%.3f,%.3f,%.3f) density_below_m=",
|
||||
firstSupportFailure[0],firstSupportFailure[1],firstSupportFailure[2]);
|
||||
for(double depth=0.08;depth<=0.62+1.0e-9;depth+=0.06)
|
||||
std::printf("%s%.3f",depth>0.08?",":"",
|
||||
density(firstSupportFailure[0],firstSupportFailure[1],
|
||||
firstSupportFailure[2]-depth));
|
||||
std::printf("\n");
|
||||
}
|
||||
|
||||
const auto filletEdge=mazeEdges.front();
|
||||
const auto p0=metres(filletEdge->polylineMicrometres[
|
||||
filletEdge->polylineMicrometres.size()/2-1]);
|
||||
const auto p1=metres(filletEdge->polylineMicrometres[
|
||||
filletEdge->polylineMicrometres.size()/2]);
|
||||
const double tx=p1[0]-p0[0],ty=p1[1]-p0[1];
|
||||
const double horizontal=std::hypot(tx,ty);
|
||||
const double grade=horizontal>1.0e-9?(p1[2]-p0[2])/horizontal:0.0;
|
||||
const double width=filletEdge->smoothWidthMicrometres/1.0e6;
|
||||
double measuredFillet=0.0;
|
||||
if(horizontal>1.0e-9&&width>0.0)
|
||||
{
|
||||
const double nx=-ty/horizontal,ny=tx/horizontal;
|
||||
const double inverseLength=1.0/std::sqrt(1.0+grade*grade);
|
||||
const double upX=-grade*tx/horizontal*inverseLength;
|
||||
const double upY=-grade*ty/horizontal*inverseLength;
|
||||
const double upZ=inverseLength;
|
||||
const double cx=(p0[0]+p1[0])*0.5,cy=(p0[1]+p1[1])*0.5,
|
||||
cz=(p0[2]+p1[2])*0.5;
|
||||
const double radius=filletEdge->boreRadiusMicrometres/1.0e6;
|
||||
const auto contour=[&](double v)
|
||||
{
|
||||
double lo=0.0,hi=width;
|
||||
const auto sample=[&](double u)
|
||||
{
|
||||
const double inside=(u+v)/std::sqrt(2.0);
|
||||
const double rise=(u-v)/std::sqrt(2.0);
|
||||
return density(cx+nx*(radius-inside)+upX*rise,
|
||||
cy+ny*(radius-inside)+upY*rise,cz+upZ*rise);
|
||||
};
|
||||
if(!(sample(lo)<0.0&&sample(hi)>=0.0))
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
for(int iteration=0;iteration<28;++iteration)
|
||||
{const double mid=(lo+hi)*0.5;if(sample(mid)<0.0)lo=mid;else hi=mid;}
|
||||
return (lo+hi)*0.5;
|
||||
};
|
||||
constexpr double delta=0.02;
|
||||
const double left=contour(-delta),center=contour(0.0),right=contour(delta);
|
||||
if(std::isfinite(left)&&std::isfinite(center)&&std::isfinite(right))
|
||||
measuredFillet=delta*delta/(left+right-2.0*center);
|
||||
}
|
||||
const double idealFillet=width/(2.0*std::sqrt(2.0));
|
||||
|
||||
struct FIntervalCheck {const char* name;std::array<double,3> p;};
|
||||
const auto mazeA=metres(mazeEdges.front()->polylineMicrometres.front());
|
||||
const auto mazeB=metres(mazeEdges.front()->polylineMicrometres.back());
|
||||
const std::array<double,3> mazeMid={(mazeA[0]+mazeB[0])*0.5,
|
||||
(mazeA[1]+mazeB[1])*0.5,(mazeA[2]+mazeB[2])*0.5};
|
||||
const double mazeLength=std::hypot(mazeB[0]-mazeA[0],mazeB[1]-mazeA[1]);
|
||||
const double wallX=mazeLength>1.0e-9?-(mazeB[1]-mazeA[1])/mazeLength:1.0;
|
||||
const double wallY=mazeLength>1.0e-9?(mazeB[0]-mazeA[0])/mazeLength:0.0;
|
||||
double maxX=-std::numeric_limits<double>::infinity();
|
||||
double minY=std::numeric_limits<double>::infinity();
|
||||
double maxY=-std::numeric_limits<double>::infinity();
|
||||
double midZ=mazeMid[2];
|
||||
for(const auto& edge:layout.cavernEdges)if(edge.strateIndex==lattice->strateIndex)
|
||||
for(const auto& point:edge.polylineMicrometres)
|
||||
{const auto p=metres(point);maxX=std::max(maxX,p[0]);minY=std::min(minY,p[1]);maxY=std::max(maxY,p[1]);}
|
||||
const std::array<FIntervalCheck,4> checks={{{"host-rock",{maxX+8.0,
|
||||
(minY+maxY)*0.5,midZ+1.2}},
|
||||
{"maze-air",{mazeMid[0],mazeMid[1],mazeMid[2]+1.25}},
|
||||
{"maze-wall-foot",{mazeMid[0]+wallX*1.30,mazeMid[1]+wallY*1.30,
|
||||
mazeMid[2]+0.25}},
|
||||
{"helix-air",{helix->polylineMicrometres[helix->polylineMicrometres.size()/2][0]/1.0e6,
|
||||
helix->polylineMicrometres[helix->polylineMicrometres.size()/2][1]/1.0e6,
|
||||
helix->polylineMicrometres[helix->polylineMicrometres.size()/2][2]/1.0e6+1.25}}}};
|
||||
std::uint64_t boxes=0,samples=0,misses=0,allAir=0,allSolid=0,mixed=0;
|
||||
bool falseAllAirRejected=false,falseAllSolidRejected=false,wallMixed=false;
|
||||
bool isTileShapeFreeAware=false,latticeTileFree=false;
|
||||
for(const auto& check:checks)
|
||||
{
|
||||
const Vec3i origin{static_cast<int>(std::llround(check.p[0]/vm)),
|
||||
static_cast<int>(std::llround(check.p[1]/vm)),
|
||||
static_cast<int>(std::llround(check.p[2]/vm))};
|
||||
const TileClass verdict=shape.classifyBox(origin,1,1);
|
||||
bool bruteAir=true,bruteSolid=true;
|
||||
for(int z=0;z<=6;++z)for(int y=0;y<=6;++y)for(int x=0;x<=6;++x)
|
||||
{
|
||||
const double d=density((origin.x-1.0+0.5*x)*vm,
|
||||
(origin.y-1.0+0.5*y)*vm,(origin.z-1.0+0.5*z)*vm);
|
||||
bruteAir=bruteAir&&d>=0.0;bruteSolid=bruteSolid&&d<0.0;++samples;
|
||||
if((verdict==TileClass::allAir&&d<0.0)
|
||||
||(verdict==TileClass::allSolid&&d>=0.0))++misses;
|
||||
}
|
||||
++boxes;allAir+=verdict==TileClass::allAir;
|
||||
allSolid+=verdict==TileClass::allSolid;mixed+=verdict==TileClass::mixed;
|
||||
falseAllAirRejected|=bruteSolid&&verdict!=TileClass::allAir;
|
||||
falseAllSolidRejected|=bruteAir&&verdict!=TileClass::allSolid;
|
||||
if(std::string_view(check.name)=="maze-wall-foot")wallMixed=verdict==TileClass::mixed;
|
||||
if(std::string_view(check.name)=="maze-air")
|
||||
{
|
||||
const int32 tileWidth=CoreTileVoxelCount;
|
||||
const TileKey key{{static_cast<std::int32_t>(std::floor(
|
||||
static_cast<double>(origin.x)/tileWidth)),
|
||||
static_cast<std::int32_t>(std::floor(static_cast<double>(origin.y)/tileWidth)),
|
||||
static_cast<std::int32_t>(std::floor(static_cast<double>(origin.z)/tileWidth))},0};
|
||||
latticeTileFree=shape.isTileShapeFree(key);
|
||||
isTileShapeFreeAware=!latticeTileFree;
|
||||
}
|
||||
std::printf("[LatticeIntervalBox] id=%s proof=%s brute_air=%s brute_solid=%s miss=%s\n",
|
||||
check.name,verdict==TileClass::allAir?"all-air":
|
||||
verdict==TileClass::allSolid?"all-solid":"mixed",
|
||||
bruteAir?"yes":"no",bruteSolid?"yes":"no",
|
||||
(verdict==TileClass::allAir?!bruteAir:
|
||||
verdict==TileClass::allSolid?!bruteSolid:false)?"yes":"no");
|
||||
}
|
||||
const bool intervalOk=boxes>=4&&allSolid>=1&&mixed>=1
|
||||
&&misses==0&&falseAllAirRejected&&falseAllSolidRejected&&wallMixed
|
||||
&&isTileShapeFreeAware;
|
||||
const bool pathOk=continuous&&capsuleClear&&allSupported&&headroomClear
|
||||
&&maximumGrade<=15.0&&minimumHeadroom>=2.2;
|
||||
std::printf("[LatticeCapsuleReachability] arrival=%s departure=%s edges=%zu path_points=%zu stations=%llu capsule_samples=%llu capsule_failures=%llu first_capsule_failure_m=(%.3f,%.3f,%.3f) support_samples=%llu support_failures=%llu first_support_failure_m=(%.3f,%.3f,%.3f) head_samples=%llu head_failures=%llu first_head_failure_m=(%.3f,%.3f,%.3f) max_grade_deg=%.3f min_headroom_m=%.3f required_headroom_m=2.200 support=every-station reachable=%s status=%s\n",
|
||||
arrival->stableId.c_str(),departure->stableId.c_str(),ordered.size(),pointCount,
|
||||
static_cast<unsigned long long>(stationCount),
|
||||
static_cast<unsigned long long>(capsuleSamples),
|
||||
static_cast<unsigned long long>(capsuleFailures),firstCapsuleFailure[0],
|
||||
firstCapsuleFailure[1],firstCapsuleFailure[2],
|
||||
static_cast<unsigned long long>(supportSamples),
|
||||
static_cast<unsigned long long>(supportFailures),firstSupportFailure[0],
|
||||
firstSupportFailure[1],firstSupportFailure[2],
|
||||
static_cast<unsigned long long>(headSamples),
|
||||
static_cast<unsigned long long>(headFailures),firstHeadFailure[0],
|
||||
firstHeadFailure[1],firstHeadFailure[2],maximumGrade,
|
||||
minimumHeadroom, pathOk?"yes":"no",pathOk?"PASS":"FAIL");
|
||||
std::printf("[LatticeFilletAudit] smooth_max=cubic width_m=%.6f measured_radius_m=%.6f ideal_radius_m=%.6f status=%s\n",
|
||||
width,measuredFillet,idealFillet,measuredFillet>=0.5?"PASS":"FAIL");
|
||||
std::printf("[LatticeIntervalOracle] boxes=%llu all_air=%llu all_solid=%llu mixed=%llu samples=%llu misses=%llu false_all_air=%s false_all_solid=%s wall_foot=%s isTileShapeFree=%s tile_free=%s status=%s\n",
|
||||
static_cast<unsigned long long>(boxes),static_cast<unsigned long long>(allAir),
|
||||
static_cast<unsigned long long>(allSolid),static_cast<unsigned long long>(mixed),
|
||||
static_cast<unsigned long long>(samples),static_cast<unsigned long long>(misses),
|
||||
falseAllAirRejected?"REJECTED":"FAILED",
|
||||
falseAllSolidRejected?"REJECTED":"FAILED",wallMixed?"mixed":"not-mixed",
|
||||
isTileShapeFreeAware?"BLOCKED":"FAILED",latticeTileFree?"yes":"no",
|
||||
intervalOk?"PASS":"FAIL");
|
||||
if(orphanControl)
|
||||
{
|
||||
const bool rejected=!continuous||!pathOk;
|
||||
std::printf("[LatticeReachabilityControl] orphaned_shaft=true result=%s expected=FAIL status=%s\n",
|
||||
rejected?"FAIL":"PASS",rejected?"PASS":"FAIL");
|
||||
return rejected&&intervalOk&&measuredFillet>=0.5;
|
||||
}
|
||||
return pathOk&&intervalOk&&measuredFillet>=0.5;
|
||||
}
|
||||
|
||||
inline bool AuditMotifIntervals(const VoxelForgeCore::World& shape,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
|
||||
std::uint32_t voxelMicrometres)
|
||||
@@ -1535,7 +1871,8 @@ namespace StandaloneStreamBenchmark
|
||||
bool bInstrumentTileDrain=false,bool bAuditMotifIntervals=false,
|
||||
bool bExactMidpointDensityMemo=false,
|
||||
bool bActiveLaneBatching=false,int32 ProofBlockCells=8,
|
||||
bool bParallelSignEdgeRepair=false)
|
||||
bool bParallelSignEdgeRepair=false,bool bAuditNativeLattice=false,
|
||||
bool bLatticeOrphanControl=false)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
FStandaloneOwnerWorld& World=*OwnerWorld;
|
||||
@@ -1741,6 +2078,14 @@ namespace StandaloneStreamBenchmark
|
||||
{std::fprintf(stderr,"[CavesAudit] missing resolved layout\n");return 2;}
|
||||
return AuditNativeCavesField(Shape,*ResolvedLayout,World.CoreVoxelSizeMicrons)?0:14;
|
||||
}
|
||||
if(bAuditNativeLattice)
|
||||
{
|
||||
Streamer.Stop();
|
||||
if(!ResolvedLayout||!CoreSnapshot.resolvedLayout())
|
||||
{std::fprintf(stderr,"[LatticeAudit] missing resolved layout\n");return 2;}
|
||||
return AuditNativeLatticeField(Shape,*ResolvedLayout,
|
||||
World.CoreVoxelSizeMicrons,bLatticeOrphanControl)?0:14;
|
||||
}
|
||||
if(bAuditMotifIntervals)
|
||||
{
|
||||
Streamer.Stop();
|
||||
|
||||
@@ -1109,7 +1109,6 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
if(!owner.Manager){error="resolved layout builder requires the initialized director";return false;}
|
||||
(void)recipeVariant;
|
||||
out={};out.worldIdentity="owner-world/seed/"+std::to_string(owner.Seed);
|
||||
out.ownerVoxelSizeMicrometres=owner.OwnerVoxelSizeMicrons;
|
||||
out.coreVoxelSizeMicrometres=owner.CoreVoxelSizeMicrons;
|
||||
@@ -1138,6 +1137,8 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
const auto authored=sealAndDensity(*slot.Definition,type);
|
||||
const bool nativeCaves=recipeVariant=="caves"
|
||||
&&type==ECaveGeneratorType::SurfaceWorld;
|
||||
const bool nativeLattice=recipeVariant==("lattice/"+std::to_string(slot.StrateIndex))
|
||||
&&(type==ECaveGeneratorType::Maze||type==ECaveGeneratorType::VerticalShafts);
|
||||
const std::int64_t bottom=static_cast<std::int64_t>(slot.BottomChunkZ)*owner.StrateLayoutChunkSizeMicrometres;
|
||||
const std::int64_t top=static_cast<std::int64_t>(slot.TopChunkZ+1)*owner.StrateLayoutChunkSizeMicrometres;
|
||||
const std::int64_t seal=static_cast<std::int64_t>(std::llround(
|
||||
@@ -1148,10 +1149,11 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
s.generatorKind=nativeCaves?"Caves":GeneratorKind(type);
|
||||
s.stableId=out.worldIdentity+"/strate/"+std::to_string(slot.StrateIndex)
|
||||
+"/generator/"+s.generatorKind;
|
||||
s.recipeId=nativeCaves?"Core.Caves.PrimitiveField"
|
||||
s.recipeId=nativeLattice?"Core.Lattice.PrimitiveField"
|
||||
:nativeCaves?"Core.Caves.PrimitiveField"
|
||||
:type==ECaveGeneratorType::SurfaceWorld?"SurfaceWorld.Heightfield2D"
|
||||
:type==ECaveGeneratorType::FloatingIslands?"FloatingIslands.ClusterField":"legacy."+std::string(GeneratorKind(type));
|
||||
s.recipeVersion=nativeCaves?1
|
||||
s.recipeVersion=(nativeCaves||nativeLattice)?1
|
||||
:type==ECaveGeneratorType::SurfaceWorld?assembly.recipe.schemaVersion
|
||||
:type==ECaveGeneratorType::FloatingIslands&&!assembly.floatingIslands.empty()
|
||||
?assembly.floatingIslands.front().manifest.recipe.geometryVersion:0;
|
||||
|
||||
Reference in new issue
Block a user