wip: cave planner route support fixed (L134 design 3); clearance at edge joints open

Arrival edge floor aligned to the passage mouth, 0.75 m backbone floor-support budget, anchors on the planned floor:
0 unsupported, 0 grade failures. Open: 196/50,967 capsule samples blocked where a higher descending edge's floor
backing meets the next edge (route-edge-6/7).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-31 11:58:33 +01:00
1 parent 75c6ad251d
commit 379093401e
8 files changed
+102 -54

No files matched your search

@@ -498,7 +498,7 @@ namespace VoxelForgeCore
// voxels for every broadphase and proof bound. Version 227 uses the exact smooth-max
// support width/6 when deriving that reach. Version 228 composes deterministic cave
// graphs from fixed passage mouths and the owner Tunnel Network settings.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 228;
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 229;
enum class FieldChannel : std::uint32_t
{
@@ -1001,6 +1001,9 @@ namespace VoxelForgeCore
std::int64_t minimumRoomRadiusMicrometres = 2000000;
std::int64_t maximumRoomRadiusMicrometres = 5000000;
std::int64_t destinationRadiusMicrometres = 6000000;
// Solid floor backing below the planned edge floor, preserved after graph
// unions and passage composition. This is separate from bore height.
std::int64_t floorSupportDepthMicrometres = 750000;
std::int64_t minimumTunnelRadiusMicrometres = 750000;
std::int64_t maximumTunnelRadiusMicrometres = 1000000;
std::int64_t maximumTunnelLengthMicrometres = 45000000;
+12 -7
View File
@@ -2048,6 +2048,8 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
||settings.maximumRoomRadiusMicrometres>100000000
||settings.destinationRadiusMicrometres<settings.maximumRoomRadiusMicrometres
||settings.destinationRadiusMicrometres>100000000
||settings.floorSupportDepthMicrometres<250000
||settings.floorSupportDepthMicrometres>2000000
||settings.maximumTunnelLengthMicrometres<=0
||settings.minimumTunnelRadiusMicrometres<0
||settings.maximumTunnelRadiusMicrometres<settings.minimumTunnelRadiusMicrometres
@@ -2081,7 +2083,6 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
constexpr std::int64_t MinimumBoreHeight=2200000;
constexpr std::int64_t PlannedBoreHeight=4500000;
constexpr std::int64_t MouthOverlap=3000000;
constexpr std::int64_t MouthFloorOffset=750000;
constexpr std::int64_t MaximumBoreRadius=10000000;
constexpr std::int64_t MaximumWalkGradeDenominator=5; // 1:5 = 11.31 degrees.
const auto integerSqrt=[](std::uint64_t value)
@@ -2113,7 +2114,6 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
for(std::size_t axis=0;axis<3;++axis)
point[axis]=qToMicrometres(lowerSide?passage.lowerMouthSubvoxels[axis]
:passage.upperMouthSubvoxels[axis]);
point[2]-=MouthFloorOffset;
if(passage.controlPoints.size()<2)return false;
const std::size_t mouthIndex=lowerSide
?std::min(passage.lowerMouthControlIndex,passage.controlPoints.size()-1):0;
@@ -2381,7 +2381,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
edge.endRoomStableId=componentRoomId(endNode);edge.strateIndex=strateIndex;
edge.boreRadiusMicrometres=boreRadius;
edge.boreHeightMicrometres=std::max(PlannedBoreHeight,MinimumBoreHeight);
edge.floorSupportDepthMicrometres=0;
edge.floorSupportDepthMicrometres=settings.floorSupportDepthMicrometres;
edge.smoothWidthMicrometres=smooth;
edge.irregularityAmplitudeMicrometres=irregularity;
edge.irregularityWavelengthMicrometres=settings.irregularityWavelengthMicrometres;
@@ -2447,12 +2447,17 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
makeCompound(branch,false,rooms);
const std::string branchEdgeId="tunnel-network/strate-"
+std::to_string(strateIndex)+"/side-edge-"+std::to_string(branchCount);
ResolvedCavernEdgeV1 branchEdge;
if(settings.tunnelsFlowTowardOrigin)
edges.push_back(makeEdge(branchEdgeId,componentRoomId(branch),
branch.point,parent,selectedBoreRadius(branchEdgeId)));
branchEdge=makeEdge(branchEdgeId,componentRoomId(branch),
branch.point,parent,selectedBoreRadius(branchEdgeId));
else
edges.push_back(makeEdge(branchEdgeId,componentRoomId(parent),
parent.point,branch,selectedBoreRadius(branchEdgeId)));
branchEdge=makeEdge(branchEdgeId,componentRoomId(parent),
parent.point,branch,selectedBoreRadius(branchEdgeId));
// Side branches retain their cave floor SDF, but they do not cap the
// descending backbone when their tubes overlap it near a junction.
branchEdge.floorSupportDepthMicrometres=0;
edges.push_back(std::move(branchEdge));
++branchCount;
}
if(nodes.empty()||rooms.empty()||edges.empty())
+47 -35
View File
@@ -1513,13 +1513,13 @@ public:
float ApplyResolvedStrateComposition(float density,float X,float Y,float Z) const
{
float graphDensity=0.0f;
float nativeLatticeFloorSupport=std::numeric_limits<float>::lowest();
float nativeGraphFloorSupport=std::numeric_limits<float>::lowest();
{
VoxelForgeCore::Linux77Profiler::FScopedPhase l77GraphPhase(
VoxelForgeCore::Linux77Profiler::EPhase::CompositionGraph);
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::ComposerCavern);
graphDensity=ApplyResolvedCavernGraph(density,X,Y,Z,
&nativeLatticeFloorSupport);
&nativeGraphFloorSupport);
}
const auto* bound=FindBoundStratumForManagerZ(Z);
const bool nativeLattice=bound&&bound->Resolved
@@ -1573,7 +1573,7 @@ public:
const float nativeTunnelFieldDensity=nativeTunnelField
?graphDensity:std::numeric_limits<float>::lowest();
stamped=ApplyResolvedStratePassageCarves(stamped,X,Y,Z,
nativeTunnelFieldDensity,nativeLatticeFloorSupport);
nativeTunnelFieldDensity,nativeGraphFloorSupport);
}
if(bTraceTnComposition)
std::printf("[L75TNCompositionTrace] input=%.9g graph=%.9g boundary=%.9g passage=%.9g stamp=%.9g native=1\n",
@@ -5284,11 +5284,7 @@ public:
FMetricInterval caveVoid{std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::infinity(),true};
bool haveFeature=false;
double latticeFloorSupportLowerBound=std::numeric_limits<double>::infinity();
const auto* resolvedStratum=FindResolvedLayoutStratum(strateIndex);
const bool nativeLattice=resolvedStratum
&&VoxelForgeCore::IsNativeLatticeFieldStratum(*resolvedStratum)
&&resolvedStratum->passageCapabilities.hasNativeLatticeField;
double nativeFloorSupportLowerBound=std::numeric_limits<double>::infinity();
for(const std::uint32_t index:candidates)
{
if(index>=CavernRuntime.size())continue;
@@ -5440,21 +5436,19 @@ public:
SmoothMaxMetres(tube.maximum,floor.maximum,width),true};
}
else return {0.0,0.0,false};
if(nativeLattice)
if(feature.edge)
{
const double supportDepth=feature.edge
?feature.edge->floorSupportDepthMicrometres/1000000.0:0.0;
const double floorMinimum=feature.edge
?feature.minimumFloorMetres:std::numeric_limits<double>::infinity();
const double floorMaximum=feature.edge
?feature.maximumFloorMetres:-std::numeric_limits<double>::infinity();
const double supportDepth=
feature.edge->floorSupportDepthMicrometres/1000000.0;
const double floorMinimum=feature.minimumFloorMetres;
const double floorMaximum=feature.maximumFloorMetres;
const bool overlapsXY=hi[0]>=feature.boundsMinimumMicrometres[0]/1.0e6
&&lo[0]<=feature.boundsMaximumMicrometres[0]/1.0e6
&&hi[1]>=feature.boundsMinimumMicrometres[1]/1.0e6
&&lo[1]<=feature.boundsMaximumMicrometres[1]/1.0e6;
if(supportDepth>0.0&&overlapsXY
&&hi[2]>=floorMinimum-supportDepth&&lo[2]<=floorMaximum)
latticeFloorSupportLowerBound=std::min(latticeFloorSupportLowerBound,
nativeFloorSupportLowerBound=std::min(nativeFloorSupportLowerBound,
std::max(-supportDepth,lo[2]-floorMaximum));
}
if(!std::isfinite(featureVoid.minimum)||!std::isfinite(featureVoid.maximum))
@@ -5483,11 +5477,11 @@ public:
result.minimum=std::max(HostRockMetres,-caveVoid.maximum);
result.maximum=std::max(HostRockMetres,-caveVoid.minimum);
}
// The final lattice field owns an extra support cap after feature union.
// Native graph edges may own a support cap after feature union.
// Lower only the interval minimum when a box could touch that cap; the
// untouched maximum remains a sound bound when only part of the box does.
if(std::isfinite(latticeFloorSupportLowerBound))
result.minimum=std::min(result.minimum,latticeFloorSupportLowerBound);
if(std::isfinite(nativeFloorSupportLowerBound))
result.minimum=std::min(result.minimum,nativeFloorSupportLowerBound);
// Cover floating-point rounding in interval endpoints and the field's fixed-point
// coordinate conversion while keeping the interval tight compared with one micron.
constexpr double OutwardPaddingMetres=1.0e-6;
@@ -7465,10 +7459,10 @@ private:
candidates=&localCandidates;
}
double caveVoid=std::numeric_limits<double>::infinity();
double latticeFloorSupportMinimum=std::numeric_limits<double>::infinity();
const bool nativeLattice=bound.Resolved
&&VoxelForgeCore::IsNativeLatticeFieldStratum(*bound.Resolved)
&&bound.Resolved->passageCapabilities.hasNativeLatticeField;
double nativeFloorSupportMinimum=std::numeric_limits<double>::infinity();
double floorSupportScore=std::numeric_limits<double>::infinity();
double floorSupportDepth=0.0,floorSupportFloor=0.0,floorSupportGrade=0.0;
const std::string* floorSupportEdgeId=nullptr;
for(const std::uint32_t index:*candidates)
{
if(index>=CavernRuntime.size())continue;
@@ -7598,11 +7592,25 @@ private:
}
else for(std::uint32_t segment=0;segment<feature.segments.size();++segment)
evaluateSegment(segment);
const double supportDepth=feature.edge->floorSupportDepthMicrometres/1000000.0;
if(nativeLattice&&supportDepth>0.0&&nearestRadial<radius-0.12
&&point[2]<nearestFloor&&nearestFloor-point[2]<=supportDepth)
latticeFloorSupportMinimum=std::min(latticeFloorSupportMinimum,
(point[2]-nearestFloor)/std::sqrt(1.0+nearestGrade*nearestGrade));
const double candidateSupportDepth=
feature.edge->floorSupportDepthMicrometres/1000000.0;
if(candidateSupportDepth>0.0&&nearestRadial<radius-0.12
&&std::abs(point[2]-nearestFloor)<=height)
{
const double deltaZ=point[2]-nearestFloor;
const double candidateScore=nearestRadial*nearestRadial+deltaZ*deltaZ;
if(candidateScore<floorSupportScore
||(candidateScore==floorSupportScore
&&(!floorSupportEdgeId
||feature.edge->stableId<*floorSupportEdgeId)))
{
floorSupportScore=candidateScore;
floorSupportDepth=candidateSupportDepth;
floorSupportFloor=nearestFloor;
floorSupportGrade=nearestGrade;
floorSupportEdgeId=&feature.edge->stableId;
}
}
const double floor=(nearestFloor-point[2])
/std::sqrt(1.0+nearestGrade*nearestGrade);
const double width=feature.edge->smoothWidthMicrometres/1000000.0;
@@ -7619,6 +7627,10 @@ private:
?feature.edge->unionWidthMicrometres
:feature.edge->smoothWidthMicrometres)/1000000.0);
}
if(std::isfinite(floorSupportScore)&&point[2]<floorSupportFloor
&&floorSupportFloor-point[2]<=floorSupportDepth)
nativeFloorSupportMinimum=(point[2]-floorSupportFloor)
/std::sqrt(1.0+floorSupportGrade*floorSupportGrade);
// A versioned native caves recipe owns a solid host-rock field. The
// resolved interval oracle uses the same finite -32 m solid value; using
// the underlying legacy generator density here would leak its unrelated
@@ -7627,13 +7639,13 @@ private:
const double hostDistance=HostRockMetres;
double composed=std::isfinite(caveVoid)
?SmoothMaxMetres(hostDistance,-caveVoid,0.0):hostDistance;
// Keep the floor support owned by the lattice route after smooth-unioning
// neighboring room and corridor voids. This cap is limited to each record's
// explicit support depth so separated helix turns remain independent.
if(std::isfinite(latticeFloorSupportMinimum))
composed=std::min(composed,latticeFloorSupportMinimum);
if(nativeFloorSupport&&std::isfinite(latticeFloorSupportMinimum))
*nativeFloorSupport=static_cast<float>(latticeFloorSupportMinimum/metresPerVoxel);
// Preserve each native graph edge's planned floor backing after smooth
// unions. The record's finite depth keeps independent routes from sealing
// one another when they pass at different elevations.
if(std::isfinite(nativeFloorSupportMinimum))
composed=std::min(composed,nativeFloorSupportMinimum);
if(nativeFloorSupport&&std::isfinite(nativeFloorSupportMinimum))
*nativeFloorSupport=static_cast<float>(nativeFloorSupportMinimum/metresPerVoxel);
return static_cast<float>(composed/metresPerVoxel);
}