wip(l59): open helix shaft (ledge open to the core) -- PARKED: jagged inner ramp edge, audit ROI missed it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-25 20:27:04 +01:00
1 parent 6c6cc37e1d
commit 54ba1b49ab
17 files changed
+1167 -172

No files matched your search

@@ -1004,11 +1004,13 @@ namespace VoxelForgeCore
&&stratum.recipeVersion==1;
}
inline constexpr std::uint32_t NativeLatticeFieldRecipeVersion=2;
inline bool IsNativeLatticeFieldStratum(const ResolvedLayoutStratumV2& stratum) noexcept
{
return (stratum.generatorKind=="Maze"||stratum.generatorKind=="VerticalShafts")
&&stratum.recipeId=="Core.Lattice.PrimitiveField"
&&stratum.recipeVersion==1;
&&stratum.recipeVersion==NativeLatticeFieldRecipeVersion;
}
inline bool HasNativeGraphField(const ResolvedLayoutStratumV2& stratum) noexcept
+4 -2
View File
@@ -1901,7 +1901,8 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
// The shared JSON value type stores numbers as IEEE-754 doubles. Keep the
// serialized seed in the exact integer range, as the stamp vocabulary does.
room.irregularitySeed=(isNativeLatticeStrate(room.strateIndex)
?LayoutLatticeFeatureSeed(seed,room.strateIndex,1,room.stableId)
?LayoutLatticeFeatureSeed(seed,room.strateIndex,
NativeLatticeFieldRecipeVersion,room.stableId)
:LayoutStampSeed(seed,room.strateIndex,room.stableId))
&0x001fffffffffffffull;
const std::int64_t pad=room.smoothWidthMicrometres
@@ -1918,7 +1919,8 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
{
const bool nativeLattice=isNativeLatticeStrate(edge.strateIndex);
edge.irregularitySeed=(nativeLattice
?LayoutLatticeFeatureSeed(seed,edge.strateIndex,1,edge.stableId)
?LayoutLatticeFeatureSeed(seed,edge.strateIndex,
NativeLatticeFieldRecipeVersion,edge.stableId)
:LayoutStampSeed(seed,edge.strateIndex,edge.stableId))
&0x001fffffffffffffull;
using FCurvePoint=std::array<double,3>;
+412 -90
View File
@@ -3332,6 +3332,14 @@ private:
const VoxelForgeCore::ResolvedCavernRoomV1* room=nullptr;
const VoxelForgeCore::ResolvedCavernEdgeV1* edge=nullptr;
bool nativeGraph=false;
bool openShaftRamp=false;
std::array<double,2> openShaftCenterMetres{};
std::array<double,2> openShaftStartDirection{1.0,0.0};
double openShaftCenterlineRadiusMetres=0.0;
double openShaftStartFloorMetres=0.0;
double openShaftPitchPerTurnMetres=0.0;
double openShaftTurnCount=0.0;
double openShaftRotationSign=1.0;
std::array<std::int64_t,3> boundsMinimumMicrometres{},boundsMaximumMicrometres{};
std::array<double,3> centerMetres{},halfExtentsMetres{};
struct FSegment
@@ -3792,6 +3800,15 @@ private:
:found->passageCapabilities.hasNativeLatticeField);
};
const float micronsPerVoxel=static_cast<float>(Owner->CoreVoxelSizeMicrons);
const auto shaftCoreRoom=std::find_if(ResolvedLayout->cavernRooms.begin(),
ResolvedLayout->cavernRooms.end(),[](const auto& room)
{return room.stableId=="lattice/spine/shaft-core-v2";});
std::array<double,2> shaftCoreCenter{};
if(shaftCoreRoom!=ResolvedLayout->cavernRooms.end())
{
shaftCoreCenter={shaftCoreRoom->centerMicrometres[0]/1.0e6,
shaftCoreRoom->centerMicrometres[1]/1.0e6};
}
for(const auto& room:ResolvedLayout->cavernRooms)
{
FCavernRuntimeFeature runtime;runtime.room=&room;
@@ -3822,6 +3839,11 @@ private:
{
FCavernRuntimeFeature runtime;runtime.edge=&edge;
runtime.nativeGraph=isNativeGraphStrate(edge.strateIndex);
runtime.openShaftRamp=runtime.nativeGraph
&&edge.stableId=="lattice/spine/01-helix-ramp-v1"
&&shaftCoreRoom!=ResolvedLayout->cavernRooms.end();
if(runtime.openShaftRamp)
runtime.openShaftCenterMetres=shaftCoreCenter;
runtime.boundsMinimumMicrometres=edge.boundsMinimumMicrometres;
runtime.boundsMaximumMicrometres=edge.boundsMaximumMicrometres;
for(std::size_t point=0;point<4;++point)
@@ -3865,6 +3887,37 @@ private:
std::abs((segment.end[2]-segment.start[2])/horizontal));
runtime.segments.push_back(segment);
}
if(runtime.openShaftRamp&&!runtime.segments.empty())
{
const auto& first=runtime.segments.front().start;
const auto& last=runtime.segments.back().end;
const double startX=first[0]-shaftCoreCenter[0];
const double startY=first[1]-shaftCoreCenter[1];
runtime.openShaftCenterlineRadiusMetres=std::hypot(startX,startY);
if(runtime.openShaftCenterlineRadiusMetres>1.0e-9)
runtime.openShaftStartDirection={
startX/runtime.openShaftCenterlineRadiusMetres,
startY/runtime.openShaftCenterlineRadiusMetres};
runtime.openShaftStartFloorMetres=first[2];
double totalAngle=0.0;
for(std::size_t segment=0;segment<runtime.segments.size();++segment)
{
const auto& previous=runtime.segments[segment].start;
const auto& current=runtime.segments[segment].end;
const double previousX=previous[0]-shaftCoreCenter[0];
const double previousY=previous[1]-shaftCoreCenter[1];
const double currentX=current[0]-shaftCoreCenter[0];
const double currentY=current[1]-shaftCoreCenter[1];
totalAngle+=std::atan2(previousX*currentY-previousY*currentX,
previousX*currentX+previousY*currentY);
}
constexpr double TwoPi=6.283185307179586476925286766559;
runtime.openShaftTurnCount=std::max(1.0,
std::round(std::abs(totalAngle)/TwoPi));
runtime.openShaftRotationSign=totalAngle<0.0?-1.0:1.0;
runtime.openShaftPitchPerTurnMetres=(first[2]-last[2])
/runtime.openShaftTurnCount;
}
runtime.segmentOrder.resize(runtime.segments.size());
for(std::size_t index=0;index<runtime.segmentOrder.size();++index)
runtime.segmentOrder[index]=static_cast<std::uint32_t>(index);
@@ -5245,9 +5298,50 @@ public:
const std::int32_t featureStrate=feature.room?feature.room->strateIndex
:feature.edge?feature.edge->strateIndex:-1;
if(featureStrate!=strateIndex)continue;
bool featureOverlapsBox=true,boxWithinFeatureBounds=true;
for(std::size_t axis=0;axis<3;++axis)
{
const double featureMinimum=feature.boundsMinimumMicrometres[axis]/1.0e6;
const double featureMaximum=feature.boundsMaximumMicrometres[axis]/1.0e6;
if(hi[axis]<featureMinimum||lo[axis]>featureMaximum)
{featureOverlapsBox=false;break;}
boxWithinFeatureBounds=boxWithinFeatureBounds
&&lo[axis]>=featureMinimum&&hi[axis]<=featureMaximum;
}
if(!featureOverlapsBox)continue;
// The point field omits a record outside its resolved bounds. A partial
// overlap cannot reuse the record's unbounded SDF for an all-air proof.
if(!boxWithinFeatureBounds)return {0.0,0.0,false};
FMetricInterval featureVoid;
bool openHelixSupportCouldApply=true;
double openHelixSupportLowerBound=std::numeric_limits<double>::infinity();
if(feature.room)
{
if(nativeLattice
&&feature.room->stableId=="lattice/spine/shaft-core-v2")
{
const double radius=feature.halfExtentsMetres[0];
if(!(radius>0.0))return {0.0,0.0,false};
double radialMinimumSquared=0.0,radialMaximumSquared=0.0;
for(std::size_t axis=0;axis<2;++axis)
{
const double center=feature.centerMetres[axis];
const double deltaMinimum=center<lo[axis]?lo[axis]-center
:center>hi[axis]?center-hi[axis]:0.0;
const double deltaMaximum=std::max(std::abs(lo[axis]-center),
std::abs(hi[axis]-center));
radialMinimumSquared+=deltaMinimum*deltaMinimum;
radialMaximumSquared+=deltaMaximum*deltaMaximum;
}
const double radialMinimum=std::sqrt(radialMinimumSquared)-radius;
const double radialMaximum=std::sqrt(radialMaximumSquared)-radius;
const double floorZ=feature.room->floorMicrometres/1000000.0;
const FMetricInterval bottom={floorZ-hi[2],floorZ-lo[2],true};
featureVoid={std::max(radialMinimum,bottom.minimum),
std::max(radialMaximum,bottom.maximum),true};
}
else
{
double normalizedMinimumSquared=0.0,normalizedMaximumSquared=0.0;
double meanRadius=0.0;
for(std::size_t axis=0;axis<3;++axis)
@@ -5271,14 +5365,135 @@ public:
const double width=feature.room->smoothWidthMicrometres/1000000.0;
featureVoid={SmoothMaxMetres(ellipsoid.minimum,floor.minimum,width),
SmoothMaxMetres(ellipsoid.maximum,floor.maximum,width),true};
}
}
else if(feature.edge&&!feature.segments.empty())
{
const bool openHelixRamp=nativeLattice&&feature.openShaftRamp;
const double radius=feature.edge->boreRadiusMicrometres/1000000.0;
const double height=feature.edge->boreHeightMicrometres/1000000.0;
if(!(radius>0.0&&height>0.0))return {0.0,0.0,false};
double tubeMinimum=std::numeric_limits<double>::infinity();
double tubeMaximum=std::numeric_limits<double>::infinity();
if(openHelixRamp)
{
double radialMinimumSquared=0.0,radialMaximumSquared=0.0;
for(std::size_t axis=0;axis<2;++axis)
{
const double center=feature.openShaftCenterMetres[axis];
const double deltaMinimum=center<lo[axis]?lo[axis]-center
:center>hi[axis]?center-hi[axis]:0.0;
const double deltaMaximum=std::max(std::abs(lo[axis]-center),
std::abs(hi[axis]-center));
radialMinimumSquared+=deltaMinimum*deltaMinimum;
radialMaximumSquared+=deltaMaximum*deltaMaximum;
}
const double radialMinimum=std::sqrt(radialMinimumSquared);
const double radialMaximum=std::sqrt(radialMaximumSquared);
const double centerline=feature.openShaftCenterlineRadiusMetres;
const double nearestRingDistance=centerline<radialMinimum
?radialMinimum-centerline:centerline>radialMaximum
?centerline-radialMaximum:0.0;
tubeMinimum=nearestRingDistance-radius;
tubeMaximum=std::max(std::abs(radialMinimum-centerline),
std::abs(radialMaximum-centerline))-radius;
openHelixSupportCouldApply=tubeMinimum<0.0;
const double startFloor=feature.openShaftStartFloorMetres;
const double pitch=feature.openShaftPitchPerTurnMetres;
const double turnCount=feature.openShaftTurnCount;
if(!std::isfinite(tubeMinimum)||!std::isfinite(tubeMaximum)
||!std::isfinite(startFloor)||!(pitch>0.0)
||!(turnCount>=1.0))return {0.0,0.0,false};
const FMetricInterval tube={tubeMinimum,tubeMaximum,true};
const double grade=pitch/(6.283185307179586476925286766559
*feature.openShaftCenterlineRadiusMetres);
const double verticalScale=1.0/std::sqrt(1.0+grade*grade);
double phaseMinimum=0.0,phaseMaximum=1.0;
const double boxCenterX=(lo[0]+hi[0])*0.5;
const double boxCenterY=(lo[1]+hi[1])*0.5;
const double centerDx=boxCenterX-feature.openShaftCenterMetres[0];
const double centerDy=boxCenterY-feature.openShaftCenterMetres[1];
const double centerDistance=std::hypot(centerDx,centerDy);
const double halfDiagonal=0.5*std::hypot(hi[0]-lo[0],hi[1]-lo[1]);
if(centerDistance>halfDiagonal)
{
const double angle=std::atan2(feature.openShaftRotationSign
*(feature.openShaftStartDirection[0]*centerDy
-feature.openShaftStartDirection[1]*centerDx),
feature.openShaftStartDirection[0]*centerDx
+feature.openShaftStartDirection[1]*centerDy);
constexpr double TwoPi=6.283185307179586476925286766559;
double centerPhase=angle/TwoPi;
const double angleRadius=std::asin(std::clamp(
halfDiagonal/centerDistance,0.0,1.0));
const double phaseRadius=angleRadius/TwoPi;
// Point evaluation normalizes phase into [0,1). If this box
// crosses the start ray, retain the full phase range; otherwise
// unwrap its center into the same range as the point field.
if(std::abs(angle)>angleRadius)
{
if(centerPhase<0.0)centerPhase+=1.0;
phaseMinimum=centerPhase-phaseRadius;
phaseMaximum=centerPhase+phaseRadius;
}
}
double verticalMinimum=std::numeric_limits<double>::infinity();
double verticalMaximum=-std::numeric_limits<double>::infinity();
const double coordinateMinimum=(startFloor-hi[2])/pitch-phaseMaximum;
const double coordinateMaximum=(startFloor-lo[2])/pitch-phaseMinimum;
if(!std::isfinite(coordinateMinimum)||!std::isfinite(coordinateMaximum))
return {0.0,0.0,false};
const int lastTurn=std::max(0,static_cast<int>(turnCount)-1);
const int firstPossibleTurn=std::clamp(
static_cast<int>(std::floor(coordinateMinimum)),-1,lastTurn+1);
const int lastPossibleTurn=std::clamp(
static_cast<int>(std::floor(coordinateMaximum)),-1,lastTurn+1);
const auto corridorInterval=[&](int turn)
{
const double floorMinimum=startFloor-pitch*(turn+phaseMaximum);
const double floorMaximum=startFloor-pitch*(turn+phaseMinimum);
const double floorToQueryMinimum=(floorMinimum-hi[2])*verticalScale;
const double floorToQueryMaximum=(floorMaximum-lo[2])*verticalScale;
const double queryToCeilingMinimum=(lo[2]-floorMaximum-height)
*verticalScale;
const double queryToCeilingMaximum=(hi[2]-floorMinimum-height)
*verticalScale;
const double turnMinimum=std::max(floorToQueryMinimum,
queryToCeilingMinimum);
const double turnMaximum=std::max(floorToQueryMaximum,
queryToCeilingMaximum);
return FMetricInterval{turnMinimum,turnMaximum,true};
};
for(int turn=firstPossibleTurn;turn<=lastPossibleTurn;++turn)
{
const int turnA=std::clamp(turn,0,lastTurn);
const int turnB=std::clamp(turn+1,0,lastTurn);
const FMetricInterval corridorA=corridorInterval(turnA);
const FMetricInterval corridorB=corridorInterval(turnB);
verticalMinimum=std::min(verticalMinimum,
std::min(corridorA.minimum,corridorB.minimum));
verticalMaximum=std::max(verticalMaximum,
std::min(corridorA.maximum,corridorB.maximum));
const double supportDepth=feature.edge->floorSupportDepthMicrometres
/1000000.0;
const double progressMinimum=turn+phaseMinimum;
const double progressMaximum=turn+phaseMaximum;
const double floorMinimum=startFloor-pitch*progressMaximum;
const double floorMaximum=startFloor-pitch*progressMinimum;
const double supportFloorMinimum=(floorMinimum-hi[2])*verticalScale;
const double supportUndersideMinimum=(lo[2]-floorMaximum+supportDepth)
*verticalScale;
openHelixSupportLowerBound=std::min(openHelixSupportLowerBound,
std::max({tubeMinimum,supportFloorMinimum,
supportUndersideMinimum}));
}
const FMetricInterval vertical={verticalMinimum,verticalMaximum,true};
const double width=feature.edge->smoothWidthMicrometres/1000000.0;
featureVoid={SmoothMaxMetres(tube.minimum,vertical.minimum,width),
SmoothMaxMetres(tube.maximum,vertical.maximum,width),true};
}
else
{
const std::array<double,3> inverseScale={1.0/radius,1.0/radius,
1.0/height};
const std::array<double,3> boxMinimum={lo[0]*inverseScale[0],
@@ -5387,6 +5602,7 @@ public:
const double width=feature.edge->smoothWidthMicrometres/1000000.0;
featureVoid={SmoothMaxMetres(tube.minimum,floor.minimum,width),
SmoothMaxMetres(tube.maximum,floor.maximum,width),true};
}
}
else return {0.0,0.0,false};
if(nativeLattice)
@@ -5401,10 +5617,17 @@ public:
&&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
const bool supportCouldApply=overlapsXY
&&(!feature.openShaftRamp||openHelixSupportCouldApply);
if(supportDepth>0.0&&supportCouldApply
&&hi[2]>=floorMinimum-supportDepth&&lo[2]<=floorMaximum)
{
double supportLower=std::max(-supportDepth,lo[2]-floorMaximum);
if(feature.openShaftRamp)
supportLower=openHelixSupportLowerBound;
latticeFloorSupportLowerBound=std::min(latticeFloorSupportLowerBound,
std::max(-supportDepth,lo[2]-floorMaximum));
supportLower);
}
}
if(!std::isfinite(featureVoid.minimum)||!std::isfinite(featureVoid.maximum))
return {0.0,0.0,false};
@@ -6962,6 +7185,10 @@ private:
return;
}
if(!feature.edge)return;
// The open helix is already carved with its exact cylindrical/turn
// field above. Its four serialized control points are a compact record
// envelope, not a walkable Bezier centerline for this legacy cap.
if(feature.openShaftRamp)return;
const auto bezier=[&](float t,std::size_t axis)
{
const float one=1.0f-t;
@@ -7098,114 +7325,209 @@ private:
const std::int32_t strateIndex=feature.room?feature.room->strateIndex
:feature.edge?feature.edge->strateIndex:-1;
if(strateIndex!=bound.StrateIndex)continue;
bool insideFeatureBounds=true;
for(std::size_t axis=0;axis<3;++axis)
insideFeatureBounds=insideFeatureBounds
&&point[axis]*1000000.0>=feature.boundsMinimumMicrometres[axis]
&&point[axis]*1000000.0<=feature.boundsMaximumMicrometres[axis];
if(!insideFeatureBounds)continue;
double featureVoid=std::numeric_limits<double>::infinity();
if(feature.room)
{
double normalizedSquared=0.0,meanRadius=0.0;
for(std::size_t axis=0;axis<3;++axis)
if(nativeLattice
&&feature.room->stableId=="lattice/spine/shaft-core-v2")
{
const double radius=feature.halfExtentsMetres[axis];
if(!(radius>0.0)){normalizedSquared=std::numeric_limits<double>::infinity();break;}
const double delta=(point[axis]-feature.centerMetres[axis])/radius;
normalizedSquared+=delta*delta;meanRadius+=radius/3.0;
const double radius=feature.halfExtentsMetres[0];
if(!(radius>0.0))continue;
const double radial=std::hypot(
point[0]-feature.centerMetres[0],
point[1]-feature.centerMetres[1])-radius;
const double bottom=feature.room->floorMicrometres/1000000.0-point[2];
featureVoid=std::max(radial,bottom);
}
else
{
double normalizedSquared=0.0,meanRadius=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double radius=feature.halfExtentsMetres[axis];
if(!(radius>0.0))
{normalizedSquared=std::numeric_limits<double>::infinity();break;}
const double delta=(point[axis]-feature.centerMetres[axis])/radius;
normalizedSquared+=delta*delta;meanRadius+=radius/3.0;
}
const double ellipsoid=(std::sqrt(normalizedSquared)-1.0)*meanRadius;
const double floor=feature.room->floorMicrometres/1000000.0-point[2];
const double width=feature.room->smoothWidthMicrometres/1000000.0;
featureVoid=SmoothMaxMetres(ellipsoid,floor,width);
}
const double ellipsoid=(std::sqrt(normalizedSquared)-1.0)*meanRadius;
const double floor=feature.room->floorMicrometres/1000000.0-point[2];
const double width=feature.room->smoothWidthMicrometres/1000000.0;
featureVoid=SmoothMaxMetres(ellipsoid,floor,width);
}
else if(feature.edge&&!feature.segments.empty())
{
const bool openHelixRamp=nativeLattice&&feature.openShaftRamp;
double tube=std::numeric_limits<double>::infinity();
double nearestFloor=0.0,nearestGrade=0.0,nearestRadial=0.0;
double openHelixVerticalSdf=std::numeric_limits<double>::infinity();
double openHelixPhase=0.0;
int openHelixTurnA=0,openHelixTurnB=0;
double openHelixVerticalScale=1.0;
const double radius=feature.edge->boreRadiusMicrometres/1000000.0;
const double height=feature.edge->boreHeightMicrometres/1000000.0;
if(!(radius>0.0&&height>0.0))continue;
const double minScale=std::min(radius,height);
std::uint32_t nearestSegment=std::numeric_limits<std::uint32_t>::max();
const auto evaluateSegment=[&](std::uint32_t segmentIndex)
if(openHelixRamp)
{
if(segmentIndex>=feature.segments.size())return;
const auto& segment=feature.segments[segmentIndex];
double lengthSquared=0.0,dot=0.0;
for(std::size_t axis=0;axis<3;++axis)
const double dx=point[0]-feature.openShaftCenterMetres[0];
const double dy=point[1]-feature.openShaftCenterMetres[1];
const double shaftRadial=std::hypot(dx,dy);
nearestRadial=std::abs(shaftRadial
-feature.openShaftCenterlineRadiusMetres);
tube=nearestRadial-radius;
const double phase=std::atan2(feature.openShaftRotationSign
*(feature.openShaftStartDirection[0]*dy
-feature.openShaftStartDirection[1]*dx),
feature.openShaftStartDirection[0]*dx
+feature.openShaftStartDirection[1]*dy);
constexpr double TwoPi=6.283185307179586476925286766559;
openHelixPhase=(phase<0.0?phase+TwoPi:phase)/TwoPi;
const double pitch=feature.openShaftPitchPerTurnMetres;
if(!(pitch>0.0)||!(feature.openShaftTurnCount>=1.0))continue;
const double grade=pitch/(TwoPi
*feature.openShaftCenterlineRadiusMetres);
openHelixVerticalScale=1.0/std::sqrt(1.0+grade*grade);
const double turnCoordinate=(feature.openShaftStartFloorMetres-point[2])
/pitch-openHelixPhase;
const int turnFloor=static_cast<int>(std::floor(turnCoordinate));
const int lastTurn=std::max(0,
static_cast<int>(feature.openShaftTurnCount)-1);
openHelixTurnA=std::clamp(turnFloor,0,lastTurn);
openHelixTurnB=std::clamp(turnFloor+1,0,lastTurn);
const auto floorForTurn=[&](int turn)
{
const double delta=segment.end[axis]-segment.start[axis];
lengthSquared+=delta*delta;
dot+=(point[axis]-segment.start[axis])*delta;
}
const double t=lengthSquared>0.0
?std::clamp(dot/lengthSquared,0.0,1.0):0.0;
double radialSquared=0.0,vertical=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double delta=point[axis]-(segment.start[axis]
+t*(segment.end[axis]-segment.start[axis]));
if(axis<2)radialSquared+=delta*delta;
else vertical=delta;
}
const double normalized=std::sqrt(radialSquared/(radius*radius)
+vertical*vertical/(height*height));
const double distance=(normalized-1.0)*std::min(radius,height);
if(distance<tube||(distance==tube&&segmentIndex<nearestSegment))
{
tube=distance;nearestSegment=segmentIndex;
nearestFloor=segment.start[2]+t*(segment.end[2]-segment.start[2]);
nearestRadial=std::sqrt(radialSquared);
const double horizontal=std::hypot(
segment.end[0]-segment.start[0],segment.end[1]-segment.start[1]);
nearestGrade=horizontal>1.0e-9
?(segment.end[2]-segment.start[2])/horizontal:0.0;
}
};
const auto nodeLowerBound=[&](const FCavernRuntimeFeature::FSegmentNode& node)
{
double gapSquared=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double scale=axis==2?height:radius;
const double q=point[axis]/scale;
const double low=node.minimum[axis]/scale;
const double high=node.maximum[axis]/scale;
const double gap=q<low?low-q:q>high?q-high:0.0;
gapSquared+=gap*gap;
}
return (std::sqrt(gapSquared)-1.0)*minScale;
};
if(!feature.segmentNodes.empty())
{
const auto visitNode=[&](const auto& self,int nodeIndex)->void
{
if(nodeIndex<0||static_cast<std::size_t>(nodeIndex)>=feature.segmentNodes.size())return;
const auto& node=feature.segmentNodes[static_cast<std::size_t>(nodeIndex)];
if(nodeLowerBound(node)>tube)return;
if(node.left<0)
{
for(std::size_t offset=node.first;offset<node.end;++offset)
evaluateSegment(feature.segmentOrder[offset]);
return;
}
const double leftBound=nodeLowerBound(feature.segmentNodes[
static_cast<std::size_t>(node.left)]);
const double rightBound=nodeLowerBound(feature.segmentNodes[
static_cast<std::size_t>(node.right)]);
if(leftBound<=rightBound)
{self(self,node.left);self(self,node.right);}
else {self(self,node.right);self(self,node.left);}
return feature.openShaftStartFloorMetres
-pitch*(turn+openHelixPhase);
};
visitNode(visitNode,0);
const auto corridorSdf=[&](int turn)
{
const double floorZ=floorForTurn(turn);
return std::max((floorZ-point[2])*openHelixVerticalScale,
(point[2]-(floorZ+height))*openHelixVerticalScale);
};
openHelixVerticalSdf=std::min(corridorSdf(openHelixTurnA),
corridorSdf(openHelixTurnB));
}
else
{
std::uint32_t nearestSegment=std::numeric_limits<std::uint32_t>::max();
const auto evaluateSegment=[&](std::uint32_t segmentIndex)
{
if(segmentIndex>=feature.segments.size())return;
const auto& segment=feature.segments[segmentIndex];
double lengthSquared=0.0,dot=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double delta=segment.end[axis]-segment.start[axis];
lengthSquared+=delta*delta;
dot+=(point[axis]-segment.start[axis])*delta;
}
const double t=lengthSquared>0.0
?std::clamp(dot/lengthSquared,0.0,1.0):0.0;
double radialSquared=0.0,vertical=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double delta=point[axis]-(segment.start[axis]
+t*(segment.end[axis]-segment.start[axis]));
if(axis<2)radialSquared+=delta*delta;
else vertical=delta;
}
const double normalized=std::sqrt(radialSquared/(radius*radius)
+vertical*vertical/(height*height));
const double distance=(normalized-1.0)*minScale;
if(distance<tube||(distance==tube&&segmentIndex<nearestSegment))
{
tube=distance;nearestSegment=segmentIndex;
nearestFloor=segment.start[2]+t*(segment.end[2]-segment.start[2]);
nearestRadial=std::sqrt(radialSquared);
const double horizontal=std::hypot(
segment.end[0]-segment.start[0],segment.end[1]-segment.start[1]);
nearestGrade=horizontal>1.0e-9
?(segment.end[2]-segment.start[2])/horizontal:0.0;
}
};
const auto nodeLowerBound=[&](const FCavernRuntimeFeature::FSegmentNode& node)
{
double gapSquared=0.0;
for(std::size_t axis=0;axis<3;++axis)
{
const double scale=axis==2?height:radius;
const double q=point[axis]/scale;
const double low=node.minimum[axis]/scale;
const double high=node.maximum[axis]/scale;
const double gap=q<low?low-q:q>high?q-high:0.0;
gapSquared+=gap*gap;
}
return (std::sqrt(gapSquared)-1.0)*minScale;
};
if(!feature.segmentNodes.empty())
{
const auto visitNode=[&](const auto& self,int nodeIndex)->void
{
if(nodeIndex<0||static_cast<std::size_t>(nodeIndex)>=feature.segmentNodes.size())return;
const auto& node=feature.segmentNodes[static_cast<std::size_t>(nodeIndex)];
if(nodeLowerBound(node)>tube)return;
if(node.left<0)
{
for(std::size_t offset=node.first;offset<node.end;++offset)
evaluateSegment(feature.segmentOrder[offset]);
return;
}
const double leftBound=nodeLowerBound(feature.segmentNodes[
static_cast<std::size_t>(node.left)]);
const double rightBound=nodeLowerBound(feature.segmentNodes[
static_cast<std::size_t>(node.right)]);
if(leftBound<=rightBound)
{self(self,node.left);self(self,node.right);}
else {self(self,node.right);self(self,node.left);}
};
visitNode(visitNode,0);
}
else for(std::uint32_t segment=0;segment<feature.segments.size();++segment)
evaluateSegment(segment);
}
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 floor=(nearestFloor-point[2])
/std::sqrt(1.0+nearestGrade*nearestGrade);
if(nativeLattice&&supportDepth>0.0)
{
if(openHelixRamp)
{
const auto supportSdf=[&](int turn)
{
const double floorZ=feature.openShaftStartFloorMetres
-feature.openShaftPitchPerTurnMetres
*(turn+openHelixPhase);
const double vertical=std::max(
(floorZ-point[2])*openHelixVerticalScale,
(point[2]-(floorZ-supportDepth))
*openHelixVerticalScale);
return std::max(nearestRadial-radius,vertical);
};
latticeFloorSupportMinimum=std::min(latticeFloorSupportMinimum,
std::min(supportSdf(openHelixTurnA),
supportSdf(openHelixTurnB)));
}
else if(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 width=feature.edge->smoothWidthMicrometres/1000000.0;
featureVoid=SmoothMaxMetres(tube,floor,width);
if(openHelixRamp)
featureVoid=SmoothMaxMetres(tube,openHelixVerticalSdf,width);
else
{
const double floor=(nearestFloor-point[2])
/std::sqrt(1.0+nearestGrade*nearestGrade);
featureVoid=SmoothMaxMetres(tube,floor,width);
}
}
if(!std::isfinite(featureVoid))continue;
if(!std::isfinite(caveVoid))caveVoid=featureVoid;