passages: continuous floor h(s) along the dense route; shell-probe oracle fix (L115, v223)

Floor height from the planner profile h(s) by horizontal projection + arc-length interpolation;
adjacent fine segments share endpoint values. Floor zero-crossing offsets max 7.8 mm (L114: up
to 73 mm); rock-floor close-ups show no ribs. The 2 L114 shell misses were oracle false positives
(points inside an overlapping bore); the oracle now checks every overlapping span; no gate relaxed.
Fast tier PASS; Clang/GCC W1/W8/W12 deterministic. Speed regression grows: near-ready +17..+31% vs
L109 (fine-segment/sample pairs 49.0M vs 19.8M). Not merged.

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-30 01:48:29 +01:00
1 parent 5e875ae504
commit f403af1fc1
37 files changed
+2741 -183

No files matched your search

+285 -116
View File
@@ -3166,18 +3166,58 @@ namespace StandaloneStreamBenchmark
std::lerp(a.floorZ,b.floorZ,t),
std::lerp(a.radius,b.radius,t),distance,b.segmentIndex};
};
const char* l114DensityProfile=std::getenv("VOXELFORGE_L114_DENSITY_PROFILE");
if(l114DensityProfile&&l114DensityProfile[0]=='1'&&l114DensityProfile[1]=='\0')
struct FPassageRouteProjection
{
for(const char* passageId:{"passage/0-1/0","passage/1-2/0"})
double centerDistanceSquared=std::numeric_limits<double>::infinity();
double tubeDistance=std::numeric_limits<double>::infinity();
double centerAlong=0.0,tubeAlong=0.0;
};
const auto projectPassageRoute=[](
const std::vector<FPassageAuditRoutePoint>& route,
double x,double y,double z)
{
FPassageRouteProjection projection;
for(std::size_t segment=1;segment<route.size();++segment)
{
const auto passage=std::find_if(ResolvedLayout->passages.begin(),
ResolvedLayout->passages.end(),[&](const auto& candidate)
{return candidate.stableId==passageId;});
if(passage==ResolvedLayout->passages.end())continue;
const std::size_t passageIndex=static_cast<std::size_t>(
passage-ResolvedLayout->passages.begin());
const auto& a=route[segment-1];
const auto& b=route[segment];
const double dx=b.x-a.x,dy=b.y-a.y,dz=b.z-a.z;
const double lengthSquared=dx*dx+dy*dy+dz*dz;
if(lengthSquared<=1.0e-12)continue;
const double horizontalLengthSquared=dx*dx+dy*dy;
const double horizontalT=horizontalLengthSquared>1.0e-12
?((x-a.x)*dx+(y-a.y)*dy)/horizontalLengthSquared
:std::clamp((z-a.z)*dz/lengthSquared,0.0,1.0);
const double t=std::clamp(((x-a.x)*dx+(y-a.y)*dy
+(z-a.z)*dz)/lengthSquared,0.0,1.0);
const double ex=x-(a.x+t*dx),ey=y-(a.y+t*dy),ez=z-(a.z+t*dz);
const double distanceSquared=ex*ex+ey*ey+ez*ez;
const double along=std::lerp(a.distance,b.distance,t);
const double centerAlong=std::lerp(a.distance,b.distance,horizontalT);
if(distanceSquared<projection.centerDistanceSquared)
{
projection.centerDistanceSquared=distanceSquared;
projection.centerAlong=centerAlong;
}
const double radius=std::lerp(a.radius,b.radius,t);
const double roundDistance=std::sqrt(distanceSquared)-radius;
if(roundDistance<projection.tubeDistance)
{
projection.tubeDistance=roundDistance;
projection.tubeAlong=along;
}
}
return projection;
};
const char* l115DensityProfile=std::getenv("VOXELFORGE_L115_DENSITY_PROFILE");
if(l115DensityProfile&&l115DensityProfile[0]=='1'&&l115DensityProfile[1]=='\0')
{
for(std::size_t passageIndex=0;
passageIndex<ResolvedLayout->passages.size();++passageIndex)
{
const auto& passage=ResolvedLayout->passages[passageIndex];
const auto& route=allPassageSmoothRoutes[passageIndex];
if(route.size()<2)continue;
const auto densityMetres=[&](double x,double y,double z)
{
return static_cast<double>(Shape.densityAt({x/coreMetres,
@@ -3198,34 +3238,38 @@ namespace StandaloneStreamBenchmark
}
return (low+high)*0.5;
};
const std::array<std::pair<const char*,double>,2> targets={{
{"rock-station-50",route.empty()?0.0:route.back().distance*0.5},
{"open-air-section-3",1265.3425}}};
for(const auto& [label,targetDistance]:targets)
constexpr std::array<const char*,3> labels{{
"route-station-25","route-station-50","route-station-75"}};
for(std::size_t targetIndex=0;targetIndex<labels.size();++targetIndex)
{
if(route.size()<2||targetDistance>route.back().distance)continue;
const double targetDistance=route.back().distance
*static_cast<double>(targetIndex+1)/4.0;
const char* label=labels[targetIndex];
const auto center=interpolatePassageRoute(route,targetDistance);
const VoxelForgeCore::Vec3d centerVoxel={center.x/coreMetres,
center.y/coreMetres,center.floorZ/coreMetres};
std::printf("[L114DensityColumn] passage=%s section=%s x_m=%.6f y_m=%.6f planned_floor_m=%.6f dispatch=%s source_density_m=%.9f unclamped_density_m=%.9f\n",
passage->stableId.c_str(),label,center.x,center.y,center.floorZ,
std::printf("[L115DensityColumn] passage=%s station=%s route_s_m=%.3f route_length_m=%.3f x_m=%.6f y_m=%.6f planned_floor_m=%.6f dispatch=%s source_density_m=%.9f unclamped_density_m=%.9f\n",
passage.stableId.c_str(),label,targetDistance,
route.back().distance,center.x,center.y,center.floorZ,
Shape.densityDispatchPathAt(centerVoxel).c_str(),
static_cast<double>(Shape.passageSourceDensityAt(centerVoxel))*coreMetres,
static_cast<double>(Shape.unclampedDensityAt(centerVoxel))*coreMetres);
for(int sample=0;sample<=40;++sample)
{
const double offset=-0.50+sample*0.025;
std::printf("[L114DensityColumnSample] section=%s dz_m=%.3f density_m=%.9f\n",
label,offset,densityMetres(center.x,center.y,
std::printf("[L115DensityColumnSample] passage=%s station=%s dz_m=%.3f density_m=%.9f\n",
passage.stableId.c_str(),label,offset,densityMetres(center.x,center.y,
center.floorZ+offset));
}
for(int sample=-25;sample<=25;++sample)
{
const double distance=targetDistance+sample*0.10;
const double distance=std::clamp(targetDistance
+sample*0.10,0.0,route.back().distance);
const auto pose=interpolatePassageRoute(route,distance);
const double root=floorRoot(pose);
std::printf("[L114DensitySlopeSample] section=%s s_m=%.3f x_m=%.6f y_m=%.6f planned_floor_m=%.6f root_floor_m=%.6f residual_m=%.6f density_minus_0p1_m=%.9f density_plane_m=%.9f density_plus_0p1_m=%.9f\n",
label,distance,pose.x,pose.y,pose.floorZ,root,
std::printf("[L115DensitySlopeSample] passage=%s station=%s s_m=%.3f x_m=%.6f y_m=%.6f planned_floor_m=%.6f root_floor_m=%.6f residual_m=%.6f density_minus_0p1_m=%.9f density_plane_m=%.9f density_plus_0p1_m=%.9f\n",
passage.stableId.c_str(),label,distance,pose.x,pose.y,
pose.floorZ,root,
root-pose.floorZ,
densityMetres(pose.x,pose.y,pose.floorZ-0.1),
densityMetres(pose.x,pose.y,pose.floorZ),
@@ -3240,6 +3284,13 @@ namespace StandaloneStreamBenchmark
{
constexpr double CornerBlendMetres=0.50;
constexpr double WallReliefMetres=0.21;
const auto smoothMax=[](double left,double right,double radius)
{
if(!(radius>0.0))return std::max(left,right);
const double h=std::clamp(radius-std::abs(left-right),0.0,radius)
/radius;
return std::max(left,right)+h*h*h*(radius/6.0);
};
for(std::size_t passageIndex=0;
passageIndex<ResolvedLayout->passages.size();++passageIndex)
{
@@ -3247,9 +3298,12 @@ namespace StandaloneStreamBenchmark
if(passage.controlPoints.size()<2)continue;
const auto& route=allPassageSmoothRoutes[passageIndex];
if(route.size()<2)continue;
double bestDistance=std::numeric_limits<double>::infinity();
double bestRoundDistance=0.0,bestCenterZ=0.0;
std::size_t bestSegment=std::numeric_limits<std::size_t>::max();
double bestRoundDistance=std::numeric_limits<double>::infinity();
double bestFloorCenterDistanceSquared=
std::numeric_limits<double>::infinity();
double bestCenterZ=0.0,bestFloorCenterZ=0.0,bestRadius=0.0;
std::size_t bestTubeSegment=std::numeric_limits<std::size_t>::max();
std::size_t bestFloorSegment=std::numeric_limits<std::size_t>::max();
for(std::size_t segment=1;segment<route.size();++segment)
{
const auto& a=route[segment-1];
@@ -3259,57 +3313,67 @@ namespace StandaloneStreamBenchmark
const double dx=b.x-a.x,dy=b.y-a.y,dz=b.z-a.z;
const double lengthSquared=dx*dx+dy*dy+dz*dz;
if(lengthSquared<=1.0e-12)continue;
const double horizontalLengthSquared=dx*dx+dy*dy;
const double horizontalT=horizontalLengthSquared>1.0e-12
?((xMetres-a.x)*dx+(yMetres-a.y)*dy)
/horizontalLengthSquared
:std::clamp((zMetres-a.z)*dz/lengthSquared,0.0,1.0);
const double floorAlong=std::lerp(a.distance,b.distance,horizontalT);
const double t=std::clamp(((xMetres-a.x)*dx+(yMetres-a.y)*dy
+(zMetres-a.z)*dz)/lengthSquared,0.0,1.0);
const double centerX=a.x+t*dx,centerY=a.y+t*dy,centerZ=a.z+t*dz;
const double radius=std::lerp(a.radius,b.radius,t);
const double horizontalLengthSquared=dx*dx+dy*dy;
const double floorT=horizontalLengthSquared>1.0e-12
?std::clamp(((xMetres-a.x)*dx+(yMetres-a.y)*dy)
/horizontalLengthSquared,0.0,1.0):t;
const double ex=xMetres-centerX,ey=yMetres-centerY,
ez=zMetres-centerZ;
const double roundDistance=std::sqrt(ex*ex+ey*ey+ez*ez)-radius;
const double floorCenterDistanceSquared=ex*ex+ey*ey+ez*ez;
const auto floorPose=interpolatePassageRoute(route,floorAlong);
const double floorCenterZ=passageFloorAtXY(passageIndex,
xMetres,yMetres,std::lerp(a.floorZ,b.floorZ,floorT));
const double roundDistance=std::sqrt((xMetres-centerX)*(xMetres-centerX)
+(yMetres-centerY)*(yMetres-centerY)
+(zMetres-centerZ)*(zMetres-centerZ))-radius;
const double floorDistance=floorCenterZ-zMetres;
const double h=std::max(CornerBlendMetres-
std::abs(roundDistance-floorDistance),0.0)/CornerBlendMetres;
const double flatDistance=std::max(roundDistance,floorDistance)
+h*h*h*(CornerBlendMetres/6.0);
if(flatDistance<bestDistance)
xMetres,yMetres,floorPose.floorZ);
// A self-overlap can place another route span's open bore
// through this nominal floor probe. Such a point is interior,
// not a missing floor shell, even when a different span is
// nearest by a few floating-point ulps.
if(smoothMax(roundDistance,floorCenterZ-zMetres,
CornerBlendMetres)<=0.0)
return true;
if(floorCenterDistanceSquared<bestFloorCenterDistanceSquared
||(floorCenterDistanceSquared==bestFloorCenterDistanceSquared
&&segment<bestFloorSegment))
{
bestFloorCenterDistanceSquared=floorCenterDistanceSquared;
bestFloorCenterZ=floorCenterZ;
bestFloorSegment=segment;
}
if(roundDistance<bestRoundDistance
||(roundDistance==bestRoundDistance
&&segment<bestTubeSegment))
{
bestDistance=flatDistance;
bestRoundDistance=roundDistance;
bestCenterZ=centerZ;
bestSegment=segment;
bestRadius=radius;
bestTubeSegment=segment;
}
}
if(bestSegment==std::numeric_limits<std::size_t>::max())continue;
const auto& a=route[bestSegment-1];
const auto& b=route[bestSegment];
if(bestTubeSegment==std::numeric_limits<std::size_t>::max())continue;
if(!std::isfinite(bestFloorCenterDistanceSquared))continue;
const double floorCenterZ=bestFloorCenterZ;
const double floorDistance=bestFloorCenterZ-zMetres;
// The composer uses the nearest round tube segment, then the
// nearest route station for its continuous floor plane.
const auto& a=route[bestTubeSegment-1];
const auto& b=route[bestTubeSegment];
const double dx=b.x-a.x,dy=b.y-a.y,dz=b.z-a.z;
const double lengthSquared=dx*dx+dy*dy+dz*dz;
if(lengthSquared<=1.0e-12)continue;
const double t=std::clamp(((xMetres-a.x)*dx+(yMetres-a.y)*dy
+(zMetres-a.z)*dz)/lengthSquared,0.0,1.0);
const double radius=std::lerp(a.radius,b.radius,t);
const double centerX=a.x+t*dx,centerY=a.y+t*dy;
const double centerZ=a.z+t*dz;
const double distanceToCenter=std::max(1.0e-9,
bestRoundDistance+radius);
bestRoundDistance+bestRadius);
const double verticalWallAlignment=std::clamp(
-(zMetres-centerZ)/distanceToCenter,0.0,1.0);
const auto& aFloor=route[bestSegment-1];
const auto& bFloor=route[bestSegment];
const double floorDx=bFloor.x-aFloor.x;
const double floorDy=bFloor.y-aFloor.y;
const double horizontalSquared=floorDx*floorDx+floorDy*floorDy;
const double floorT=horizontalSquared>1.0e-12
?std::clamp(((xMetres-aFloor.x)*floorDx
+(yMetres-aFloor.y)*floorDy)/horizontalSquared,0.0,1.0):t;
const double floorCenterZ=passageFloorAtXY(passageIndex,
xMetres,yMetres,std::lerp(aFloor.floorZ,bFloor.floorZ,floorT));
const double wallElevationMetres=zMetres-floorCenterZ;
const double crestFade=std::clamp((1.0-wallElevationMetres)/0.25,
0.0,1.0);
@@ -3325,11 +3389,8 @@ namespace StandaloneStreamBenchmark
const double roughRoundDistance=bestRoundDistance
+static_cast<double>(std::clamp(relief,-1.0f,1.0f))
*WallReliefMetres*wallReliefWeight;
const double floorDistance=floorCenterZ-zMetres;
const double blend=std::max(CornerBlendMetres-
std::abs(roughRoundDistance-floorDistance),0.0)/CornerBlendMetres;
const double shapedDistance=std::max(roughRoundDistance,floorDistance)
+blend*blend*blend*(CornerBlendMetres/6.0);
const double shapedDistance=smoothMax(roughRoundDistance,
floorDistance,CornerBlendMetres);
if(shapedDistance<=0.0)return true;
}
return false;
@@ -4129,9 +4190,9 @@ namespace StandaloneStreamBenchmark
std::uint64_t floorMouthBlendSkips=0,wallMouthBlendSkips=0;
std::uint64_t interiorSamples=0,interiorViolations=0;
std::vector<double> openAirDistancesMetres;
std::array<std::array<double,4>,8> firstViolations{};
std::array<std::array<double,10>,8> firstViolations{};
std::size_t firstViolationCount=0;
std::array<std::array<double,4>,8> firstWallViolations{};
std::array<std::array<double,13>,8> firstWallViolations{};
std::size_t firstWallViolationCount=0;
};
constexpr double PassageShellAuditSpacingMetres=0.25;
@@ -4142,13 +4203,10 @@ namespace StandaloneStreamBenchmark
{
constexpr double CornerBlendMetres=0.50;
constexpr double ReliefBoundMetres=0.21;
const auto smoothstep=[](double value)
{
value=std::clamp(value,0.0,1.0);
return value*value*(3.0-2.0*value);
};
bool ambiguous=false,expectedSolid=false,mouthBlend=false;
if(passageInteriorWinsAt(xMetres,yMetres,zMetres))return 0;
double nearestWallShellDistance=std::numeric_limits<double>::infinity();
bool mouthBlend=false;
if(passageInteriorWinsAt(xMetres,yMetres,zMetres))
return std::pair<int,double>{0,nearestWallShellDistance};
for(std::size_t passageIndex=0;
passageIndex<ResolvedLayout->passages.size();++passageIndex)
{
@@ -4165,17 +4223,20 @@ namespace StandaloneStreamBenchmark
const double dx=b.x-a.x,dy=b.y-a.y,dz=b.z-a.z;
const double lengthSquared=dx*dx+dy*dy+dz*dz;
if(lengthSquared<=1.0e-12)continue;
const double horizontalLengthSquared=dx*dx+dy*dy;
const double horizontalT=horizontalLengthSquared>1.0e-12
?((xMetres-ax)*dx+(yMetres-ay)*dy)
/horizontalLengthSquared
:std::clamp((zMetres-az)*dz/lengthSquared,0.0,1.0);
const double floorAlong=std::lerp(a.distance,b.distance,horizontalT);
const double t=std::clamp(((xMetres-ax)*dx+(yMetres-ay)*dy
+(zMetres-az)*dz)/lengthSquared,0.0,1.0);
const double centerX=ax+t*dx,centerY=ay+t*dy,centerZ=az+t*dz;
const double radius=std::lerp(a.radius,b.radius,t);
const double horizontalSquared=dx*dx+dy*dy;
const double floorT=horizontalSquared>1.0e-12
?std::clamp(((xMetres-ax)*dx+(yMetres-ay)*dy)
/horizontalSquared,0.0,1.0):t;
const double floorCenterZ=passageFloorAtXY(passageIndex,
xMetres,yMetres,std::lerp(a.floorZ,b.floorZ,floorT));
const double along=std::lerp(a.distance,b.distance,t);
const auto floorPose=interpolatePassageRoute(path,floorAlong);
const double floorCenterZ=passageFloorAtXY(passageIndex,
xMetres,yMetres,floorPose.floorZ);
const double roundDistance=std::sqrt((xMetres-centerX)*(xMetres-centerX)
+(yMetres-centerY)*(yMetres-centerY)
+(zMetres-centerZ)*(zMetres-centerZ))-radius;
@@ -4189,19 +4250,33 @@ namespace StandaloneStreamBenchmark
if(flatDistance<=0.0)continue;
const double fade=std::clamp(mouthDistance/5.0,0.0,1.0);
const double smoothFade=fade*fade*(3.0-2.0*fade);
const double floorBlend=smoothstep(floorDistance/0.25);
const double wallElevation=-floorDistance;
const double wallThickness=wallElevation>=0.0&&wallElevation<=1.0
?smoothFade:0.0;
const double thickness=0.75*std::max(wallThickness,
smoothFade*floorBlend);
if(flatDistance+ReliefBoundMetres<thickness)expectedSolid=true;
else if(flatDistance-ReliefBoundMetres<thickness)ambiguous=true;
?smoothFade*0.75:0.0;
if(wallThickness>1.0e-9)
{
const double shellRadius=wallThickness*0.5;
const double capCenter=1.0-shellRadius;
const double x=flatDistance-shellRadius;
const double boxHalfHeight=capCenter*0.5;
const double boxX=std::abs(x)-shellRadius;
const double boxY=std::abs(wallElevation-boxHalfHeight)
-boxHalfHeight;
const double boxDistance=std::min(std::max(boxX,boxY),0.0)
+std::hypot(std::max(boxX,0.0),std::max(boxY,0.0));
const double capDistance=std::hypot(x,wallElevation-capCenter)
-shellRadius;
nearestWallShellDistance=std::min(nearestWallShellDistance,
std::min(boxDistance,capDistance));
}
else if(mouthDistance<5.0)mouthBlend=true;
}
}
if(expectedSolid)return 1;
return ambiguous||mouthBlend?-1:0;
if(nearestWallShellDistance<-ReliefBoundMetres)
return std::pair<int,double>{1,nearestWallShellDistance};
if(nearestWallShellDistance<ReliefBoundMetres||mouthBlend)
return std::pair<int,double>{-1,nearestWallShellDistance};
return std::pair<int,double>{0,nearestWallShellDistance};
};
const auto auditPassageShell=[&](const VoxelForgeCore::World& Field,
const VoxelForgeCore::World& Native,
@@ -4216,16 +4291,66 @@ namespace StandaloneStreamBenchmark
constexpr std::array<std::array<double,2>,5> floorOffsets{{
{{0.0,0.0}},{{0.30,0.0}},{{-0.30,0.0}},
{{0.0,0.30}},{{0.0,-0.30}}}};
const auto expectedFloorThickness=[&](double along,double floorDistance)
const auto expectedFloorShellMarginAt=[&](double xMetres,
double yMetres,double zMetres)
{
const double mouthDistance=std::max(0.0,std::min(along,
totalRouteLength-along));
const double fade=std::clamp(mouthDistance/5.0,0.0,1.0);
const double smoothFade=fade*fade*(3.0-2.0*fade);
const double floorBlend=std::clamp(floorDistance/0.25,0.0,1.0);
const double smoothFloorBlend=floorBlend*floorBlend
*(3.0-2.0*floorBlend);
return 0.75*smoothFade*smoothFloorBlend;
constexpr double CornerBlendMetres=0.50;
double minimumShellDistance=std::numeric_limits<double>::infinity();
for(std::size_t candidatePassage=0;
candidatePassage<allPassageSmoothRoutes.size();++candidatePassage)
{
const auto& candidateRoute=
allPassageSmoothRoutes[candidatePassage];
if(candidateRoute.size()<2)continue;
const double candidateLength=candidateRoute.back().distance;
for(std::size_t candidateSegment=1;
candidateSegment<candidateRoute.size();++candidateSegment)
{
const auto& a=candidateRoute[candidateSegment-1];
const auto& b=candidateRoute[candidateSegment];
const double dx=b.x-a.x,dy=b.y-a.y,dz=b.z-a.z;
const double lengthSquared=dx*dx+dy*dy+dz*dz;
if(lengthSquared<=1.0e-12)continue;
const double t=std::clamp(((xMetres-a.x)*dx
+(yMetres-a.y)*dy+(zMetres-a.z)*dz)/lengthSquared,
0.0,1.0);
const double horizontalLengthSquared=dx*dx+dy*dy;
const double floorT=horizontalLengthSquared>1.0e-12
?((xMetres-a.x)*dx+(yMetres-a.y)*dy)
/horizontalLengthSquared:t;
const double centerX=a.x+t*dx,centerY=a.y+t*dy,
centerZ=a.z+t*dz;
const double radius=std::lerp(a.radius,b.radius,t);
const double along=std::lerp(a.distance,b.distance,t);
const double floorAlong=std::lerp(a.distance,b.distance,floorT);
const double plannedFloor=passageFloorAtXY(candidatePassage,
xMetres,yMetres,interpolatePassageRoute(candidateRoute,
floorAlong).floorZ);
const double roundDistance=std::sqrt(
(xMetres-centerX)*(xMetres-centerX)
+(yMetres-centerY)*(yMetres-centerY)
+(zMetres-centerZ)*(zMetres-centerZ))-radius;
const double floorDistance=plannedFloor-zMetres;
const double blend=std::max(CornerBlendMetres
-std::abs(roundDistance-floorDistance),0.0)
/CornerBlendMetres;
const double flatDistance=std::max(roundDistance,floorDistance)
+blend*blend*blend*(CornerBlendMetres/6.0);
const double mouthDistance=std::max(0.0,std::min(along,
candidateLength-along));
const double fade=std::clamp(mouthDistance/5.0,0.0,1.0);
const double smoothFade=fade*fade*(3.0-2.0*fade);
const double floorBlend=std::clamp(floorDistance/0.25,0.0,1.0);
const double smoothFloorBlend=floorBlend*floorBlend
*(3.0-2.0*floorBlend);
const double thickness=0.75*smoothFade*smoothFloorBlend;
const double shellDistance=std::max(-flatDistance,
flatDistance-thickness);
minimumShellDistance=std::min(minimumShellDistance,
shellDistance);
}
}
return minimumShellDistance;
};
for(std::size_t segment=1;segment<route.size();++segment)
{
@@ -4249,14 +4374,20 @@ namespace StandaloneStreamBenchmark
for(const auto& offset:floorOffsets)
for(const double depth:PassageShellAuditDepthsMetres)
{
const double horizontalLengthSquared=dx*dx+dy*dy;
const double sampleX=centerX+offset[0];
const double sampleY=centerY+offset[1];
const double floorT=horizontalLengthSquared>1.0e-12
?std::clamp(((sampleX-a.x)*dx+(sampleY-a.y)*dy)
/horizontalLengthSquared,0.0,1.0):t;
const double floorZ=passageFloorAtXY(passageIndex,
sampleX,sampleY,std::lerp(a.floorZ,b.floorZ,floorT));
double floorZ=passageFloorAtXY(passageIndex,
sampleX,sampleY,interpolatePassageRoute(route,along).floorZ);
FPassageRouteProjection floorProjection;
for(int projectionStep=0;projectionStep<2;++projectionStep)
{
floorProjection=projectPassageRoute(route,sampleX,sampleY,
floorZ-depth);
const auto floorPose=interpolatePassageRoute(route,
floorProjection.centerAlong);
floorZ=passageFloorAtXY(passageIndex,sampleX,sampleY,
floorPose.floorZ);
}
const VoxelForgeCore::Vec3d position{
sampleX/coreMetres,sampleY/coreMetres,
(floorZ-depth)/coreMetres};
@@ -4271,8 +4402,14 @@ namespace StandaloneStreamBenchmark
{
++audit.openAirSamples;
audit.openAirDistancesMetres.push_back(along);
const bool shellExpected=depth+0.011
<expectedFloorThickness(along,depth);
const double shellMargin=expectedFloorShellMarginAt(
sampleX,sampleY,sampleZ);
if(std::abs(shellMargin)<=0.011)
{
++audit.floorMouthBlendSkips;
continue;
}
const bool shellExpected=shellMargin<-0.011;
const float density=Field.densityAt(position);
if((shellExpected&&density>=PassageShellDensityTolerance)
||(!shellExpected&&density<-PassageShellDensityTolerance))
@@ -4280,7 +4417,10 @@ namespace StandaloneStreamBenchmark
++audit.floorViolations;
if(audit.firstViolationCount<audit.firstViolations.size())
audit.firstViolations[audit.firstViolationCount++]={
sampleX,sampleY,sampleZ,density};
sampleX,sampleY,sampleZ,density,along,depth,
floorZ,floorProjection.centerAlong,
floorProjection.tubeAlong,
shellMargin};
}
}
}
@@ -4294,8 +4434,8 @@ namespace StandaloneStreamBenchmark
}
const double nx=-dy/tangentLength,ny=dx/tangentLength;
const double floorDepth=passageTubeDepthAt(radius,bodyFloorDepth);
const double floorZ=passageFloorAtXY(passageIndex,centerX,centerY,
std::lerp(a.floorZ,b.floorZ,t));
double floorZ=passageFloorAtXY(passageIndex,centerX,centerY,
interpolatePassageRoute(route,along).floorZ);
struct FWallProbe {double side=0.0,height=0.0;bool roof=false;};
const std::array<FWallProbe,5> wallProbes{{
{0.0,0.0,true},{-1.0,0.25,false},{1.0,0.25,false},
@@ -4313,12 +4453,21 @@ namespace StandaloneStreamBenchmark
const double wallRadius=std::sqrt(wallRadiusSquared);
sampleX+=nx*probe.side*(wallRadius+0.375);
sampleY+=ny*probe.side*(wallRadius+0.375);
for(int projectionStep=0;projectionStep<2;++projectionStep)
{
sampleZ=floorZ+probe.height;
const auto floorProjection=projectPassageRoute(route,
sampleX,sampleY,sampleZ);
const auto floorPose=interpolatePassageRoute(route,
floorProjection.centerAlong);
floorZ=passageFloorAtXY(passageIndex,sampleX,sampleY,
floorPose.floorZ);
}
sampleZ=floorZ+probe.height;
}
const VoxelForgeCore::Vec3d wallPosition{sampleX/coreMetres,
sampleY/coreMetres,sampleZ/coreMetres};
if(passageInteriorWinsAt(sampleX,sampleY,sampleZ,
passageIndex,b.segmentIndex))
if(passageInteriorWinsAt(sampleX,sampleY,sampleZ))
{
++audit.wallInteriorWins;
continue;
@@ -4328,8 +4477,8 @@ namespace StandaloneStreamBenchmark
++audit.wallSamples;
audit.openAirDistancesMetres.push_back(along);
const float density=Field.densityAt(wallPosition);
const int shellExpected=expectedPassageShellAt(
sampleX,sampleY,sampleZ);
const auto [shellExpected,wallShellMargin]=
expectedPassageShellAt(sampleX,sampleY,sampleZ);
if(shellExpected>=0
&&((shellExpected==1
&&density>=PassageShellDensityTolerance)
@@ -4340,7 +4489,9 @@ namespace StandaloneStreamBenchmark
if(audit.firstWallViolationCount
<audit.firstWallViolations.size())
audit.firstWallViolations[audit.firstWallViolationCount++]={
sampleX,sampleY,sampleZ,density};
sampleX,sampleY,sampleZ,density,along,probe.height,
probe.side,centerX,centerY,centerZ,floorZ,radius,
wallShellMargin};
}
else if(shellExpected<0)
++audit.wallMouthBlendSkips;
@@ -4533,23 +4684,41 @@ namespace StandaloneStreamBenchmark
&&shellAudit.interiorViolations==0
?"PASS":"FAIL");
for(std::size_t i=0;i<shellAudit.firstViolationCount;++i)
std::printf("[PassageShellAuditSample] id=%s kind=floor_missing xyz_m=(%.3f,%.3f,%.3f) density=%.6f\n",
passage.stableId.c_str(),shellAudit.firstViolations[i][0],
shellAudit.firstViolations[i][1],shellAudit.firstViolations[i][2],
shellAudit.firstViolations[i][3]);
{
const auto& sample=shellAudit.firstViolations[i];
const VoxelForgeCore::Vec3d position{
sample[0]/coreMetres,sample[1]/coreMetres,
sample[2]/coreMetres};
std::printf("[PassageShellAuditSample] id=%s kind=floor_missing xyz_m=(%.3f,%.3f,%.3f) density=%.6f native_density=%.6f native_path=%s probe_s_m=%.3f projected_center_s_m=%.3f projected_tube_s_m=%.3f depth_m=%.3f floor_z_m=%.3f expected_shell_margin_m=%.6f\n",
passage.stableId.c_str(),sample[0],sample[1],sample[2],sample[3],
blockedWorld.densityAt(position),
blockedWorld.densityDispatchPathAt(position).c_str(),
sample[4],sample[7],sample[8],sample[5],sample[6],sample[9]);
}
for(std::size_t i=0;i<shellAudit.firstWallViolationCount;++i)
{
const auto& sample=shellAudit.firstWallViolations[i];
const int expected=expectedPassageShellAt(sample[0],sample[1],sample[2]);
const auto [expected,wallShellMargin]=
expectedPassageShellAt(sample[0],sample[1],sample[2]);
const bool passageInterior=passageInteriorWinsAt(
sample[0],sample[1],sample[2]);
std::printf("[PassageShellAuditSample] id=%s kind=wall_missing xyz_m=(%.3f,%.3f,%.3f) density=%.6f expected=%d passage_interior=%s native_density=%.6f\n",
std::printf("[PassageShellAuditSample] id=%s kind=wall_missing xyz_m=(%.3f,%.3f,%.3f) density=%.6f expected=%d wall_shell_margin_m=%.6f passage_interior=%s native_density=%.6f probe_s_m=%.3f probe_height_m=%.3f probe_side=%.0f route_center_m=(%.3f,%.3f,%.3f) floor_z_m=%.3f radius_m=%.3f dispatch=%s passage_source=%.6f unclamped=%.6f\n",
passage.stableId.c_str(),shellAudit.firstWallViolations[i][0],
shellAudit.firstWallViolations[i][1],shellAudit.firstWallViolations[i][2],
shellAudit.firstWallViolations[i][3],expected,
shellAudit.firstWallViolations[i][3],expected,wallShellMargin,
passageInterior?"true":"false",
blockedWorld.densityAt({sample[0]/coreMetres,
sample[1]/coreMetres,sample[2]/coreMetres}));
sample[1]/coreMetres,sample[2]/coreMetres}),
sample[4],sample[5],sample[6],sample[7],sample[8],sample[9],
sample[10],sample[11],
Shape.densityDispatchPathAt({sample[0]/coreMetres,
sample[1]/coreMetres,sample[2]/coreMetres}).c_str(),
static_cast<double>(Shape.passageSourceDensityAt({
sample[0]/coreMetres,sample[1]/coreMetres,
sample[2]/coreMetres})),
static_cast<double>(Shape.unclampedDensityAt({
sample[0]/coreMetres,sample[1]/coreMetres,
sample[2]/coreMetres})));
}
for(std::size_t i=0;i<passageAudit.firstBlockedCount;++i)
{