wip: L176 step 3 in progress (unverified, saved after machine crash)

Bore carves air only, stamp last, chambers/exemptions/plane fit removed.
Seed 0: seams 0, no solid over host air (control detected); pad audit 0/6
(surface landing lacks support - step 4). 16-seed sweep interrupted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011g9Ez42ZNqQGjigc4FWDih
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-11-04 16:00:06 +01:00
1 parent 72f2609507
commit 98835952a2
8 files changed
+1230 -2268

No files matched your search

@@ -517,7 +517,8 @@ namespace VoxelForgeCore
// v239 plans their floor planes as the passage's continuous floor profile. // v239 plans their floor planes as the passage's continuous floor profile.
// v240 adds level end runs and rejects passage tube overlaps and hairpins. // v240 adds level end runs and rejects passage tube overlaps and hairpins.
// v241 keeps passage composition sample-local and out of base-only X8 lanes. // v241 keeps passage composition sample-local and out of base-only X8 lanes.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 241; // v242 composes layer content, air-only passage bores, and bounded landing pads.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 242;
enum class FieldChannel : std::uint32_t enum class FieldChannel : std::uint32_t
{ {
@@ -1331,16 +1332,6 @@ namespace VoxelForgeCore
std::int64_t radiusSubvoxels = 0; std::int64_t radiusSubvoxels = 0;
}; };
struct ResolvedLayoutPassageLandingChamberV1
{
// The chamber floor plane passes through this point. Gradient is dz/dx,
// dz/dy in Q16 so the chamber continues the passage's authored h(s) grade.
std::array<std::int64_t, 3> floorCenterSubvoxels{};
std::array<std::int64_t, 2> floorGradientQ16{};
std::int64_t radiusSubvoxels = 0;
std::int64_t clearHeightSubvoxels = 0;
};
inline std::int64_t ResolvedLayoutPassageLandingDiscRadiusSubvoxels( inline std::int64_t ResolvedLayoutPassageLandingDiscRadiusSubvoxels(
std::int64_t mouthRadiusSubvoxels, std::int64_t mouthRadiusSubvoxels,
std::uint32_t coreVoxelSizeMicrometres) noexcept std::uint32_t coreVoxelSizeMicrometres) noexcept
@@ -1376,34 +1367,6 @@ namespace VoxelForgeCore
<=static_cast<double>(halfWidthSubvoxels); <=static_cast<double>(halfWidthSubvoxels);
} }
// Shared landing-volume predicate for planners, the passage composer, and audits.
// Query coordinates use the same world subvoxel units as the resolved passage.
inline double ResolvedLayoutPassageLandingFloorAtXYSubvoxels(
const ResolvedLayoutPassageLandingChamberV1& chamber,
double xSubvoxels,double ySubvoxels) noexcept
{
return static_cast<double>(chamber.floorCenterSubvoxels[2])
+static_cast<double>(chamber.floorGradientQ16[0])/65536.0
*(xSubvoxels-static_cast<double>(chamber.floorCenterSubvoxels[0]))
+static_cast<double>(chamber.floorGradientQ16[1])/65536.0
*(ySubvoxels-static_cast<double>(chamber.floorCenterSubvoxels[1]));
}
inline bool IsInsideResolvedLayoutPassageLandingChamberV1(
const ResolvedLayoutPassageLandingChamberV1& chamber,
double xSubvoxels,double ySubvoxels,double zSubvoxels) noexcept
{
if(chamber.radiusSubvoxels<=0||chamber.clearHeightSubvoxels<=0)return false;
const double dx=xSubvoxels-static_cast<double>(chamber.floorCenterSubvoxels[0]);
const double dy=ySubvoxels-static_cast<double>(chamber.floorCenterSubvoxels[1]);
const double radius=static_cast<double>(chamber.radiusSubvoxels);
const double floor=ResolvedLayoutPassageLandingFloorAtXYSubvoxels(
chamber,xSubvoxels,ySubvoxels);
return dx*dx+dy*dy<=radius*radius
&&zSubvoxels>=floor
&&zSubvoxels<=floor+static_cast<double>(chamber.clearHeightSubvoxels);
}
struct ResolvedLayoutPassageV1 struct ResolvedLayoutPassageV1
{ {
std::string stableId; std::string stableId;
@@ -1420,15 +1383,22 @@ namespace VoxelForgeCore
std::int64_t endRunSubvoxels = 0; std::int64_t endRunSubvoxels = 0;
// Derived from the mouth binding; the main descent is monotone through this point. // Derived from the mouth binding; the main descent is monotone through this point.
std::size_t lowerMouthControlIndex = 0; std::size_t lowerMouthControlIndex = 0;
ResolvedLayoutPassageLandingChamberV1 upperLandingChamber;
ResolvedLayoutPassageLandingChamberV1 lowerLandingChamber;
// When true, each control point's position.z - radius is the planned
// floor h(s), and both chamber planes continue that same endpoint profile.
// False preserves older resolved snapshots' runtime floor composition.
bool floorProfileIncludesLandingChambers = false;
std::vector<ResolvedLayoutPassagePointV1> controlPoints; std::vector<ResolvedLayoutPassagePointV1> controlPoints;
}; };
inline std::array<std::int64_t,3> ResolvedLayoutPassageLandingPadCenterSubvoxels(
const ResolvedLayoutPassageV1& passage,bool upper) noexcept
{
const auto& mouth=upper?passage.upperMouthSubvoxels:passage.lowerMouthSubvoxels;
std::array<std::int64_t,3> center=mouth;
if(passage.controlPoints.empty())return center;
const std::size_t endIndex=upper?0:
std::min(passage.lowerMouthControlIndex,passage.controlPoints.size()-1);
const auto& endpoint=passage.controlPoints[endIndex];
center[2]=endpoint.positionSubvoxels[2]-endpoint.radiusSubvoxels;
return center;
}
struct ResolvedLayoutPassageConfigV1 struct ResolvedLayoutPassageConfigV1
{ {
std::int32_t upperStrateIndex = -1; std::int32_t upperStrateIndex = -1;
@@ -2257,6 +2227,12 @@ namespace VoxelForgeCore
float unclampedDensityAt(Vec3d voxelPosition) const; float unclampedDensityAt(Vec3d voxelPosition) const;
// Research-only source field entering passage composition, with passage carving omitted. // Research-only source field entering passage composition, with passage carving omitted.
float passageSourceDensityAt(Vec3d voxelPosition) const; float passageSourceDensityAt(Vec3d voxelPosition) const;
// Research-only signed distance to the passage floor shell, without other composition.
float passageFloorShellDistanceAt(Vec3d voxelPosition) const;
// Audit-only influence bounds in Core voxel coordinates, shared with tile routing.
bool resolvedPassageLandingPadInfluenceBounds(std::size_t passageIndex,bool upper,
std::array<double,3>& minimumCoreVoxels,
std::array<double,3>& maximumCoreVoxels) const;
DetailFieldSample detailSampleAt(Vec3d voxelPosition) const; DetailFieldSample detailSampleAt(Vec3d voxelPosition) const;
float densityAtLattice(Vec3i voxelPosition) const; float densityAtLattice(Vec3i voxelPosition) const;
bool isSolidAt(Vec3d voxelPosition) const; bool isSolidAt(Vec3d voxelPosition) const;
+66 -327
View File
@@ -653,22 +653,7 @@ namespace
<<"],\"mouth_radius_q\":"<<p.mouthRadiusSubvoxels <<"],\"mouth_radius_q\":"<<p.mouthRadiusSubvoxels
<<",\"mid_radius_q\":"<<p.midRadiusSubvoxels <<",\"mid_radius_q\":"<<p.midRadiusSubvoxels
<<",\"end_run_q\":"<<p.endRunSubvoxels <<",\"end_run_q\":"<<p.endRunSubvoxels
<<",\"floor_profile_includes_landing_chambers\":" <<",\"control_points\":[";
<<(p.floorProfileIncludesLandingChambers?"true":"false");
const auto writeLandingChamber=[&](const char* name,
const ResolvedLayoutPassageLandingChamberV1& chamber)
{
out<<",\""<<name<<"\":{\"floor_center_q\":["
<<chamber.floorCenterSubvoxels[0]<<','
<<chamber.floorCenterSubvoxels[1]<<','
<<chamber.floorCenterSubvoxels[2]<<"],\"floor_gradient_q16\":["
<<chamber.floorGradientQ16[0]<<','<<chamber.floorGradientQ16[1]
<<"],\"radius_q\":"<<chamber.radiusSubvoxels
<<",\"clear_height_q\":"<<chamber.clearHeightSubvoxels<<'}';
};
writeLandingChamber("upper_landing_chamber",p.upperLandingChamber);
writeLandingChamber("lower_landing_chamber",p.lowerLandingChamber);
out<<",\"control_points\":[";
for(std::size_t j=0;j<p.controlPoints.size();++j) for(std::size_t j=0;j<p.controlPoints.size();++j)
{ {
if(j)out<<',';const auto& point=p.controlPoints[j]; if(j)out<<',';const auto& point=p.controlPoints[j];
@@ -1269,62 +1254,6 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
if(!insideContent(*upper,passage.upperMouthSubvoxels) if(!insideContent(*upper,passage.upperMouthSubvoxels)
||!insideContent(*lower,passage.lowerMouthSubvoxels)) ||!insideContent(*lower,passage.lowerMouthSubvoxels))
add("passage_mouth_content",passage.stableId+" mouth is outside its owned content band"); add("passage_mouth_content",passage.stableId+" mouth is outside its owned content band");
const auto validateLandingChamber=[&](
const ResolvedLayoutPassageLandingChamberV1& chamber,
const ResolvedLayoutStratumV2& host,
const std::array<std::int64_t,3>& mouth,const char* side)
{
const double playerCenterHeight=(side==std::string_view("upper")
&&host.generatorKind=="SurfaceWorld")?0.98:1.13;
const std::int64_t playerCenterHeightQ=static_cast<std::int64_t>(
std::llround(playerCenterHeight*WorldAssemblySubvoxelsPerVoxel
*1000000.0/std::max<std::uint32_t>(1,coreVoxelSizeMicrometres)));
if(chamber.floorCenterSubvoxels[0]!=mouth[0]
||chamber.floorCenterSubvoxels[1]!=mouth[1]
||chamber.floorCenterSubvoxels[2]+playerCenterHeightQ!=mouth[2]
||chamber.radiusSubvoxels<=0
||chamber.clearHeightSubvoxels<=0
||chamber.radiusSubvoxels<passage.mouthRadiusSubvoxels
||chamber.clearHeightSubvoxels<static_cast<std::int64_t>(
std::llround(2.2*WorldAssemblySubvoxelsPerVoxel*1000000.0
/std::max<std::uint32_t>(1,coreVoxelSizeMicrometres))))
{
add("passage_landing_chamber",passage.stableId+" "+side
+" landing chamber has invalid floor, radius, or capsule clearance");
return;
}
const std::uint64_t gradientSquared=static_cast<std::uint64_t>(
chamber.floorGradientQ16[0]*chamber.floorGradientQ16[0]
+chamber.floorGradientQ16[1]*chamber.floorGradientQ16[1]);
const std::int64_t gradientMagnitude=static_cast<std::int64_t>(
PassageIsqrt(gradientSquared));
if(gradientMagnitude>17560)
add("passage_landing_grade",passage.stableId+" "+side
+" landing chamber floor exceeds 15 degrees");
const std::int64_t planeReachQ=static_cast<std::int64_t>(
(chamber.radiusSubvoxels*gradientMagnitude+65535)/65536);
const std::int64_t shellQ=static_cast<std::int64_t>(std::llround(
0.75*WorldAssemblySubvoxelsPerVoxel*1000000.0
/std::max<std::uint32_t>(1,coreVoxelSizeMicrometres)));
const auto qToUm=[&](std::int64_t q)
{return q*static_cast<std::int64_t>(coreVoxelSizeMicrometres)
/WorldAssemblySubvoxelsPerVoxel;};
if(!host.content.presentInCurrentField
||qToUm(chamber.floorCenterSubvoxels[2]-planeReachQ-shellQ)
<host.content.bottomMicrometres
||qToUm(chamber.floorCenterSubvoxels[2]+planeReachQ
+chamber.clearHeightSubvoxels+shellQ)
>host.content.topMicrometres)
add("passage_landing_content",passage.stableId+" "+side
+" landing chamber or plain shell leaves its host content band");
};
if(upper&&lower)
{
validateLandingChamber(passage.upperLandingChamber,*upper,
passage.upperMouthSubvoxels,"upper");
validateLandingChamber(passage.lowerLandingChamber,*lower,
passage.lowerMouthSubvoxels,"lower");
}
if(passage.controlPoints.size()<2)continue; if(passage.controlPoints.size()<2)continue;
const std::int64_t PlayerCenterAboveFloorQ=coreVoxelSizeMicrometres>0 const std::int64_t PlayerCenterAboveFloorQ=coreVoxelSizeMicrometres>0
?static_cast<std::int64_t>(std::llround(1.13*WorldAssemblySubvoxelsPerVoxel ?static_cast<std::int64_t>(std::llround(1.13*WorldAssemblySubvoxelsPerVoxel
@@ -2113,27 +2042,10 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
p.placementRadiusSubvoxels=LayoutInt(v,"placement_radius_q"); p.placementRadiusSubvoxels=LayoutInt(v,"placement_radius_q");
p.mouthRadiusSubvoxels=LayoutInt(v,"mouth_radius_q");p.midRadiusSubvoxels=LayoutInt(v,"mid_radius_q"); p.mouthRadiusSubvoxels=LayoutInt(v,"mouth_radius_q");p.midRadiusSubvoxels=LayoutInt(v,"mid_radius_q");
p.endRunSubvoxels=LayoutInt(v,"end_run_q"); p.endRunSubvoxels=LayoutInt(v,"end_run_q");
p.floorProfileIncludesLandingChambers=
LayoutBool(v,"floor_profile_includes_landing_chambers");
const auto& upper=v.Get("upper_mouth_q").AsArray(); const auto& upper=v.Get("upper_mouth_q").AsArray();
const auto& lower=v.Get("lower_mouth_q").AsArray(); const auto& lower=v.Get("lower_mouth_q").AsArray();
for(std::size_t axis=0;axis<3&&axis<upper.size();++axis)p.upperMouthSubvoxels[axis]=upper[axis].Integer(); for(std::size_t axis=0;axis<3&&axis<upper.size();++axis)p.upperMouthSubvoxels[axis]=upper[axis].Integer();
for(std::size_t axis=0;axis<3&&axis<lower.size();++axis)p.lowerMouthSubvoxels[axis]=lower[axis].Integer(); for(std::size_t axis=0;axis<3&&axis<lower.size();++axis)p.lowerMouthSubvoxels[axis]=lower[axis].Integer();
const auto readLandingChamber=[&](const char* key,
ResolvedLayoutPassageLandingChamberV1& chamber)
{
const auto& value=v.Get(key);
const auto& center=value.Get("floor_center_q").AsArray();
const auto& gradient=value.Get("floor_gradient_q16").AsArray();
for(std::size_t axis=0;axis<3&&axis<center.size();++axis)
chamber.floorCenterSubvoxels[axis]=center[axis].Integer();
for(std::size_t axis=0;axis<2&&axis<gradient.size();++axis)
chamber.floorGradientQ16[axis]=gradient[axis].Integer();
chamber.radiusSubvoxels=LayoutInt(value,"radius_q");
chamber.clearHeightSubvoxels=LayoutInt(value,"clear_height_q");
};
readLandingChamber("upper_landing_chamber",p.upperLandingChamber);
readLandingChamber("lower_landing_chamber",p.lowerLandingChamber);
for(const auto& q:v.Get("control_points").AsArray()) for(const auto& q:v.Get("control_points").AsArray())
{ {
ResolvedLayoutPassagePointV1 point;const auto& position=q.Get("position_q").AsArray(); ResolvedLayoutPassagePointV1 point;const auto& position=q.Get("position_q").AsArray();
@@ -2940,7 +2852,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
std::size_t mouthIndex,bool lowerSide, std::size_t mouthIndex,bool lowerSide,
const std::array<std::int64_t,2>& authoredRouteTangentQ16, const std::array<std::int64_t,2>& authoredRouteTangentQ16,
std::int64_t authoredFloorSlopeQ16,const FPoint& mouthFloorPoint, std::int64_t authoredFloorSlopeQ16,const FPoint& mouthFloorPoint,
std::int64_t chamberRadiusMicrometres) std::int64_t landingRadiusMicrometres)
{ {
constexpr std::array<std::array<std::int64_t,2>,16> DirectionsQ16={{ constexpr std::array<std::array<std::int64_t,2>,16> DirectionsQ16={{
{{65536,0}},{{60547,25080}},{{46341,46341}},{{25080,60547}}, {{65536,0}},{{60547,25080}},{{46341,46341}},{{25080,60547}},
@@ -2959,7 +2871,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
/(65536ll*65536ll)); /(65536ll*65536ll));
constexpr std::int64_t MouthClearanceProbeExtent=48000000; constexpr std::int64_t MouthClearanceProbeExtent=48000000;
const std::int64_t firstClearanceProbe=std::max<std::int64_t>( const std::int64_t firstClearanceProbe=std::max<std::int64_t>(
2000000,chamberRadiusMicrometres+1000000); 2000000,landingRadiusMicrometres+1000000);
for(std::int64_t distance=firstClearanceProbe; for(std::int64_t distance=firstClearanceProbe;
distance<=MouthClearanceProbeExtent;distance+=2000000) distance<=MouthClearanceProbeExtent;distance+=2000000)
{ {
@@ -3057,13 +2969,13 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
bool lowerSide,FPoint& point,std::array<std::int64_t,2>& routeTangentQ16, bool lowerSide,FPoint& point,std::array<std::int64_t,2>& routeTangentQ16,
std::int64_t& floorSlopeQ16) std::int64_t& floorSlopeQ16)
{ {
const auto& chamber=lowerSide?passage.lowerLandingChamber
:passage.upperLandingChamber;
for(std::size_t axis=0;axis<3;++axis)
point[axis]=qToMicrometres(chamber.floorCenterSubvoxels[axis]);
if(passage.controlPoints.size()<2)return false; if(passage.controlPoints.size()<2)return false;
const std::size_t mouthIndex=lowerSide const std::size_t mouthIndex=lowerSide
?std::min(passage.lowerMouthControlIndex,passage.controlPoints.size()-1):0; ?std::min(passage.lowerMouthControlIndex,passage.controlPoints.size()-1):0;
const auto& mouth=lowerSide?passage.lowerMouthSubvoxels
:passage.upperMouthSubvoxels;
point[0]=qToMicrometres(mouth[0]);
point[1]=qToMicrometres(mouth[1]);
const std::size_t tangentIndex=lowerSide const std::size_t tangentIndex=lowerSide
?(mouthIndex>0?mouthIndex-1:1):1; ?(mouthIndex>0?mouthIndex-1:1):1;
// The lower arrival continues from the previous passage point through its // The lower arrival continues from the previous passage point through its
@@ -3080,26 +2992,23 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
if(tangentLength<=0)return false; if(tangentLength<=0)return false;
routeTangentQ16={tx*65536/tangentLength,ty*65536/tangentLength}; routeTangentQ16={tx*65536/tangentLength,ty*65536/tangentLength};
const auto& mouthControl=passage.controlPoints[mouthIndex]; const auto& mouthControl=passage.controlPoints[mouthIndex];
const std::int64_t mouthFloor=chamber.floorCenterSubvoxels[2]; const std::int64_t mouthFloor=mouthControl.positionSubvoxels[2]
-mouthControl.radiusSubvoxels;
point[2]=qToMicrometres(mouthFloor); point[2]=qToMicrometres(mouthFloor);
floorSlopeQ16=(chamber.floorGradientQ16[0]*routeTangentQ16[0] floorSlopeQ16=0;
+chamber.floorGradientQ16[1]*routeTangentQ16[1])/65536;
const auto authoredRouteTangentQ16=routeTangentQ16; const auto authoredRouteTangentQ16=routeTangentQ16;
routeTangentQ16=chooseMouthRouteTangent(passage,mouthIndex,lowerSide, routeTangentQ16=chooseMouthRouteTangent(passage,mouthIndex,lowerSide,
authoredRouteTangentQ16,floorSlopeQ16,point, authoredRouteTangentQ16,floorSlopeQ16,point,
qToMicrometres(chamber.radiusSubvoxels)); qToMicrometres(ResolvedLayoutPassageLandingDiscRadiusSubvoxels(
floorSlopeQ16=(chamber.floorGradientQ16[0]*routeTangentQ16[0] passage.mouthRadiusSubvoxels,layout.coreVoxelSizeMicrometres)));
+chamber.floorGradientQ16[1]*routeTangentQ16[1])/65536;
const std::int64_t offsetDirection=lowerSide?1:-1; const std::int64_t offsetDirection=lowerSide?1:-1;
const std::int64_t portOffset=std::max<std::int64_t>(0, const std::int64_t portOffset=std::max<std::int64_t>(0,
qToMicrometres(chamber.radiusSubvoxels)-LandingConnectorBoreRadius); qToMicrometres(ResolvedLayoutPassageLandingDiscRadiusSubvoxels(
passage.mouthRadiusSubvoxels,layout.coreVoxelSizeMicrometres))
-LandingConnectorBoreRadius);
point[0]+=offsetDirection*routeTangentQ16[0]*portOffset/65536; point[0]+=offsetDirection*routeTangentQ16[0]*portOffset/65536;
point[1]+=offsetDirection*routeTangentQ16[1]*portOffset/65536; point[1]+=offsetDirection*routeTangentQ16[1]*portOffset/65536;
point[2]=qToMicrometres(mouthFloor) point[2]=qToMicrometres(mouthFloor);
+(chamber.floorGradientQ16[0]*(point[0]
-qToMicrometres(chamber.floorCenterSubvoxels[0]))
+chamber.floorGradientQ16[1]*(point[1]
-qToMicrometres(chamber.floorCenterSubvoxels[1])))/65536;
return true; return true;
}; };
FPoint routeStart{},routeEnd{}; FPoint routeStart{},routeEnd{};
@@ -3932,7 +3841,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
const auto arrivalPoint=routeStart; const auto arrivalPoint=routeStart;
FPoint previousPoint=arrivalPoint; FPoint previousPoint=arrivalPoint;
std::int64_t previousProfileDistance=0; std::int64_t previousProfileDistance=0;
std::string previousAnchor=arrivalPassage->stableId+"/lower-landing-chamber"; std::string previousAnchor=arrivalPassage->stableId+"/lower-landing-pad";
for(std::size_t nodeIndex=0;nodeIndex<nodes.size();++nodeIndex) for(std::size_t nodeIndex=0;nodeIndex<nodes.size();++nodeIndex)
{ {
const auto& node=nodes[nodeIndex]; const auto& node=nodes[nodeIndex];
@@ -3950,7 +3859,7 @@ bool PlanResolvedCavernNetworkV1(const ResolvedLayoutSnapshotV3& layout,
{ {
const std::string departureEdgeId=routePrefix+"departure-edge"; const std::string departureEdgeId=routePrefix+"departure-edge";
auto edge=makeEdge(departureEdgeId, auto edge=makeEdge(departureEdgeId,
departurePassage->stableId+"/upper-landing-chamber", departurePassage->stableId+"/upper-landing-pad",
routeEnd,nodes.back(), routeEnd,nodes.back(),
std::min(selectedBoreRadius(departureEdgeId),LandingConnectorBoreRadius), std::min(selectedBoreRadius(departureEdgeId),LandingConnectorBoreRadius),
totalRouteLength,nodes.back().floorProfileDistanceMicrometres); totalRouteLength,nodes.back().floorProfileDistanceMicrometres);
@@ -4375,17 +4284,21 @@ static FPassageTubeSelfOverlapCensus CensusPassageTubeSelfOverlaps(
passage.mouthRadiusSubvoxels+qPerMetre)+qPerMetre); passage.mouthRadiusSubvoxels+qPerMetre)+qPerMetre);
const std::int64_t landingDiscRadius=ResolvedLayoutPassageLandingDiscRadiusSubvoxels( const std::int64_t landingDiscRadius=ResolvedLayoutPassageLandingDiscRadiusSubvoxels(
passage.mouthRadiusSubvoxels,coreVoxelSizeMicrometres); passage.mouthRadiusSubvoxels,coreVoxelSizeMicrometres);
const auto overlapsLandingColumn=[&]( const std::int64_t landingHeadroom=static_cast<std::int64_t>(
const ResolvedLayoutPassageLandingChamberV1& chamber, std::llround(4.5*qPerMetre));
const std::array<std::int64_t,3>& landingMouth, const auto overlapsLandingColumn=[&](bool upper,const std::array<double,3>& center)
const std::array<double,3>& center)
{ {
if(chamber.radiusSubvoxels<=0||chamber.clearHeightSubvoxels<=0)return false; if(landingDiscRadius<=0||passage.controlPoints.size()<2)return false;
const double floor=static_cast<double>(chamber.floorCenterSubvoxels[2]); const std::size_t endpointIndex=upper?0:
const double ceiling=floor+static_cast<double>(chamber.clearHeightSubvoxels); std::min(passage.lowerMouthControlIndex,passage.controlPoints.size()-1);
const auto& endpoint=passage.controlPoints[endpointIndex];
const double floor=static_cast<double>(endpoint.positionSubvoxels[2]
-endpoint.radiusSubvoxels);
const double ceiling=floor+static_cast<double>(landingHeadroom);
if(center[2]<floor||center[2]>ceiling)return false; if(center[2]<floor||center[2]>ceiling)return false;
const auto& mouth=upper?passage.upperMouthSubvoxels:passage.lowerMouthSubvoxels;
return IsInsideResolvedLayoutPassageLandingDiscV1( return IsInsideResolvedLayoutPassageLandingDiscV1(
{landingMouth[0],landingMouth[1]},landingDiscRadius,center[0],center[1]); {mouth[0],mouth[1]},landingDiscRadius,center[0],center[1]);
}; };
for(std::size_t segment=0;segment+1<passage.controlPoints.size();++segment) for(std::size_t segment=0;segment+1<passage.controlPoints.size();++segment)
{ {
@@ -4404,10 +4317,8 @@ static FPassageTubeSelfOverlapCensus CensusPassageTubeSelfOverlaps(
if(routeDistance<=endRun||horizontalAlong.back()-routeDistance<=endRun)continue; if(routeDistance<=endRun||horizontalAlong.back()-routeDistance<=endRun)continue;
const std::array<double,3> center={a[0]+t*(b[0]-a[0]), const std::array<double,3> center={a[0]+t*(b[0]-a[0]),
a[1]+t*(b[1]-a[1]),a[2]+t*(b[2]-a[2])}; a[1]+t*(b[1]-a[1]),a[2]+t*(b[2]-a[2])};
upperColumnViolation|=overlapsLandingColumn(passage.upperLandingChamber, upperColumnViolation|=overlapsLandingColumn(true,center);
passage.upperMouthSubvoxels,center); lowerColumnViolation|=overlapsLandingColumn(false,center);
lowerColumnViolation|=overlapsLandingColumn(passage.lowerLandingChamber,
passage.lowerMouthSubvoxels,center);
if(upperColumnViolation&&lowerColumnViolation)break; if(upperColumnViolation&&lowerColumnViolation)break;
} }
if((upperColumnViolation||lowerColumnViolation) if((upperColumnViolation||lowerColumnViolation)
@@ -4867,72 +4778,6 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
} }
} }
} }
// Reserve each landing envelope inside its host band before building h(s).
// SurfaceWorld can raise its floor by at most the 4.5 m headroom allowance,
// so include that bounded rise as well as the grade reach and floor shell.
constexpr std::int64_t MaximumLandingGradeQ16=17560;
const std::int64_t landingRadius=mouthRadius+3*qPerMetre;
const std::int64_t landingClearHeight=static_cast<std::int64_t>(
std::llround(4.5*qPerMetre));
const std::int64_t landingShell=static_cast<std::int64_t>(
std::llround(0.75*qPerMetre));
const std::int64_t landingPlaneReach=(landingRadius
*MaximumLandingGradeQ16+65535)/65536;
const std::array<std::int64_t,3> upperMouthBeforeBandFit=upperMouth;
const std::array<std::int64_t,3> lowerMouthBeforeBandFit=lowerMouth;
const auto fitMouth=[&](std::array<std::int64_t,3>& mouth,
const ResolvedLayoutStratumV2& host,std::int64_t playerHeightQ,
const char* side)
{
if(!host.content.presentInCurrentField)return true;
const std::int64_t minimumFloor=contentZQ(host.content.bottomMicrometres)
+landingPlaneReach+landingShell;
const std::int64_t nativeFloorRise=host.generatorKind=="SurfaceWorld"
?landingClearHeight:0;
const std::int64_t maximumFloor=contentZQ(host.content.topMicrometres)
-landingPlaneReach-landingShell-landingClearHeight-nativeFloorRise;
if(maximumFloor<minimumFloor)
{error=passage.stableId+" "+side
+" landing chamber envelope is taller than its host content band";return false;}
const std::int64_t requestedFloor=mouth[2]-playerHeightQ;
const std::int64_t fittedFloor=std::clamp(requestedFloor,
minimumFloor,maximumFloor);
if(fittedFloor!=requestedFloor)
{
mouth[2]=fittedFloor+playerHeightQ;
std::printf("[PassageLandingChamberPlacement] id=%s side=%s floor_shift_m=%.3f grade_bound_deg=15.000 clear_height_m=4.500 shell_m=0.750 status=PASS\n",
passage.stableId.c_str(),side,
(fittedFloor-requestedFloor)/static_cast<double>(qPerMetre));
}
return true;
};
if(!fitMouth(upperMouth,upper,upperPlayerCenterAboveFloorQ,"upper")
||!fitMouth(lowerMouth,lower,playerCenterAboveFloorQ,"lower"))return false;
const auto bindFittedMouthAnchor=[&](
const ResolvedLayoutStratumV2& host,
const std::array<std::int64_t,3>& before,
const std::array<std::int64_t,3>& after,const char* side)
{
if(before==after)return;
auto mutableHost=std::find_if(layout.strates.begin(),layout.strates.end(),
[&](const auto& stratum)
{return stratum.strateIndex==host.strateIndex;});
if(mutableHost!=layout.strates.end())
for(auto& anchor:mutableHost->anchors)
if((anchor.kind=="passage_arrival"
||anchor.kind=="passage_departure")
&&anchor.values.size()>=3
&&anchor.values[0]==after[0]
&&anchor.values[1]==after[1])
{
anchor.values[2]=after[2];
std::printf("[PassageLandingChamberArrivalBinding] id=%s side=%s anchor=%s status=PASS\n",
passage.stableId.c_str(),side,anchor.stableId.c_str());
break;
}
};
bindFittedMouthAnchor(upper,upperMouthBeforeBandFit,upperMouth,"upper");
bindFittedMouthAnchor(lower,lowerMouthBeforeBandFit,lowerMouth,"lower");
if(!radialWithin(upperMouth,placementRadius)||!radialWithin(lowerMouth,placementRadius)) if(!radialWithin(upperMouth,placementRadius)||!radialWithin(lowerMouth,placementRadius))
{error=passage.stableId+" route mouth is outside the resolved PassageConfig.DistanceMax radius";return false;} {error=passage.stableId+" route mouth is outside the resolved PassageConfig.DistanceMax radius";return false;}
passage.upperMouthSubvoxels=upperMouth;passage.lowerMouthSubvoxels=lowerMouth; passage.upperMouthSubvoxels=upperMouth;passage.lowerMouthSubvoxels=lowerMouth;
@@ -6007,18 +5852,18 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
passage.controlPoints[lowerMouthIndex].positionSubvoxels[2]=lowerMouth[2] passage.controlPoints[lowerMouthIndex].positionSubvoxels[2]=lowerMouth[2]
+passage.controlPoints[lowerMouthIndex].radiusSubvoxels +passage.controlPoints[lowerMouthIndex].radiusSubvoxels
-playerCenterAboveFloorQ; -playerCenterAboveFloorQ;
std::vector<VoxelForgeCore::PassageSpline::FPoint> chamberFloorControls; std::vector<VoxelForgeCore::PassageSpline::FPoint> endpointFloorControls;
std::vector<double> chamberFloorProfile; std::vector<double> endpointFloorProfile;
chamberFloorControls.reserve(passage.controlPoints.size()); endpointFloorControls.reserve(passage.controlPoints.size());
chamberFloorProfile.reserve(passage.controlPoints.size()); endpointFloorProfile.reserve(passage.controlPoints.size());
for(const auto& point:passage.controlPoints) for(const auto& point:passage.controlPoints)
{ {
chamberFloorControls.push_back({ endpointFloorControls.push_back({
static_cast<double>(point.positionSubvoxels[0]), static_cast<double>(point.positionSubvoxels[0]),
static_cast<double>(point.positionSubvoxels[1]), static_cast<double>(point.positionSubvoxels[1]),
static_cast<double>(point.positionSubvoxels[2]), static_cast<double>(point.positionSubvoxels[2]),
static_cast<double>(point.radiusSubvoxels)}); static_cast<double>(point.radiusSubvoxels)});
chamberFloorProfile.push_back(static_cast<double>( endpointFloorProfile.push_back(static_cast<double>(
point.positionSubvoxels[2]-point.radiusSubvoxels)); point.positionSubvoxels[2]-point.radiusSubvoxels));
} }
const auto routePoints=[&](const ResolvedLayoutStratumV2& host) const auto routePoints=[&](const ResolvedLayoutStratumV2& host)
@@ -6039,12 +5884,12 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
route.erase(route.begin(),route.end()-2); route.erase(route.begin(),route.end()-2);
double routeHeight=0.0,distanceSquared=0.0; double routeHeight=0.0,distanceSquared=0.0;
if(VoxelForgeCore::PassageSpline::ClosestRouteHeightAtXY(route, if(VoxelForgeCore::PassageSpline::ClosestRouteHeightAtXY(route,
chamberFloorControls.front()[0],chamberFloorControls.front()[1], endpointFloorControls.front()[0],endpointFloorControls.front()[1],
routeHeight,distanceSquared) routeHeight,distanceSquared)
&&distanceSquared<=std::pow(chamberFloorControls.front()[3] &&distanceSquared<=std::pow(endpointFloorControls.front()[3]
+2.0*qPerMetre,2.0)) +2.0*qPerMetre,2.0))
VoxelForgeCore::PassageSpline::BlendEndpointFloorToRoute( VoxelForgeCore::PassageSpline::BlendEndpointFloorToRoute(
chamberFloorControls,chamberFloorProfile,0,route,qPerMetre, endpointFloorControls,endpointFloorProfile,0,route,qPerMetre,
upper.generatorKind=="SurfaceWorld"?0.98:1.13); upper.generatorKind=="SurfaceWorld"?0.98:1.13);
} }
if(lower.generatorKind=="TunnelNetwork" if(lower.generatorKind=="TunnelNetwork"
@@ -6054,31 +5899,30 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
if(route.size()>2)route.resize(2); if(route.size()>2)route.resize(2);
double routeHeight=0.0,distanceSquared=0.0; double routeHeight=0.0,distanceSquared=0.0;
if(VoxelForgeCore::PassageSpline::ClosestRouteHeightAtXY(route, if(VoxelForgeCore::PassageSpline::ClosestRouteHeightAtXY(route,
chamberFloorControls[lowerMouthIndex][0], endpointFloorControls[lowerMouthIndex][0],
chamberFloorControls[lowerMouthIndex][1], endpointFloorControls[lowerMouthIndex][1],
routeHeight,distanceSquared) routeHeight,distanceSquared)
&&distanceSquared<=std::pow( &&distanceSquared<=std::pow(
chamberFloorControls[lowerMouthIndex][3] endpointFloorControls[lowerMouthIndex][3]
+2.0*qPerMetre,2.0)) +2.0*qPerMetre,2.0))
VoxelForgeCore::PassageSpline::BlendEndpointFloorToRoute( VoxelForgeCore::PassageSpline::BlendEndpointFloorToRoute(
chamberFloorControls,chamberFloorProfile, endpointFloorControls,endpointFloorProfile,
lowerMouthIndex,route,qPerMetre); lowerMouthIndex,route,qPerMetre);
} }
const double upperEndRunFloor=chamberFloorProfile.front(); const double upperEndRunFloor=endpointFloorProfile.front();
const double lowerEndRunFloor=chamberFloorProfile[lowerMouthIndex]; const double lowerEndRunFloor=endpointFloorProfile[lowerMouthIndex];
for(std::size_t i=0;i<chamberFloorProfile.size();++i) for(std::size_t i=0;i<endpointFloorProfile.size();++i)
{ {
if(cumulative[i]<=upperFlatEndDistance) if(cumulative[i]<=upperFlatEndDistance)
chamberFloorProfile[i]=upperEndRunFloor; endpointFloorProfile[i]=upperEndRunFloor;
else if(cumulative[i]>=lowerFlatStartDistance) else if(cumulative[i]>=lowerFlatStartDistance)
chamberFloorProfile[i]=lowerEndRunFloor; endpointFloorProfile[i]=lowerEndRunFloor;
} }
// Commit the endpoint route fit into h(s) before chamber records are // Store route binding and level end runs in h(s), then use that profile
// resolved. The passage field and both landing planes then consume the // directly for the bore and its landing-pad stamp.
// same deterministic profile instead of blending the chamber in later.
for(std::size_t i=0;i<passage.controlPoints.size();++i) for(std::size_t i=0;i<passage.controlPoints.size();++i)
passage.controlPoints[i].positionSubvoxels[2]=static_cast<std::int64_t>( passage.controlPoints[i].positionSubvoxels[2]=static_cast<std::int64_t>(
std::llround(chamberFloorProfile[i])) std::llround(endpointFloorProfile[i]))
+passage.controlPoints[i].radiusSubvoxels; +passage.controlPoints[i].radiusSubvoxels;
const auto endRunAudit=AuditResolvedLayoutPassageEndRunsV1( const auto endRunAudit=AuditResolvedLayoutPassageEndRunsV1(
passage,layout.coreVoxelSizeMicrometres,endRunLength); passage,layout.coreVoxelSizeMicrometres,endRunLength);
@@ -6090,117 +5934,6 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
endRunAudit.startMaximumGradeDegrees, endRunAudit.startMaximumGradeDegrees,
endRunAudit.endMaximumGradeDegrees, endRunAudit.endMaximumGradeDegrees,
endRunAudit.passed?"PASS":"FAIL"); endRunAudit.passed?"PASS":"FAIL");
const auto resolveLandingChamber=[&](
ResolvedLayoutPassageLandingChamberV1& chamber,
const std::array<std::int64_t,3>& mouth,std::size_t endpointIndex,
std::size_t neighborIndex,const ResolvedLayoutStratumV2& host,
const char* side)
{
chamber.floorCenterSubvoxels=mouth;
chamber.radiusSubvoxels=mouthRadius+3*qPerMetre;
chamber.clearHeightSubvoxels=static_cast<std::int64_t>(
std::llround(4.5*qPerMetre));
const auto& endpoint=passage.controlPoints[endpointIndex];
const auto& neighbor=passage.controlPoints[neighborIndex];
const std::int64_t endpointFloor=static_cast<std::int64_t>(
std::llround(chamberFloorProfile[endpointIndex]));
const std::int64_t neighborFloor=static_cast<std::int64_t>(
std::llround(chamberFloorProfile[neighborIndex]));
chamber.floorCenterSubvoxels[2]=endpointFloor;
const std::int64_t dx=endpoint.positionSubvoxels[0]
-neighbor.positionSubvoxels[0];
const std::int64_t dy=endpoint.positionSubvoxels[1]
-neighbor.positionSubvoxels[1];
const std::int64_t dz=endpointFloor-neighborFloor;
const std::int64_t horizontalSquared=dx*dx+dy*dy;
if(horizontalSquared<=0)
{error=passage.stableId+" "+side+" landing chamber has no endpoint tangent";return false;}
chamber.floorGradientQ16={
PassageRoundRatio(dx<0?-dz:dz,std::abs(dx)*65536,
horizontalSquared),
PassageRoundRatio(dy<0?-dz:dz,std::abs(dy)*65536,
horizontalSquared)};
if(host.generatorKind=="SurfaceWorld"&&side[0]=='u')
{
// The surface route enters the outer half of this chamber. Fit
// its landing plane to that receiving route, while keeping its
// center height equal to the passage's planned h(s) endpoint.
const auto surfaceRoute=routePoints(host);
const double radiusSquared=static_cast<double>(
chamber.radiusSubvoxels)*chamber.radiusSubvoxels;
double farthestDistanceSquared=0.0;
std::array<double,3> farthestPoint{};
for(const auto& routePoint:surfaceRoute)
{
const double routeDx=routePoint[0]-mouth[0];
const double routeDy=routePoint[1]-mouth[1];
const double distanceSquared=routeDx*routeDx+routeDy*routeDy;
if(distanceSquared<=radiusSquared
&&distanceSquared>farthestDistanceSquared)
{
farthestDistanceSquared=distanceSquared;
farthestPoint=routePoint;
}
}
double mouthRouteHeight=0.0,mouthRouteDistanceSquared=0.0;
if(farthestDistanceSquared>0.0
&&VoxelForgeCore::PassageSpline::ClosestRouteHeightAtXY(
surfaceRoute,static_cast<double>(mouth[0]),
static_cast<double>(mouth[1]),mouthRouteHeight,
mouthRouteDistanceSquared))
{
const std::int64_t surfaceDx=static_cast<std::int64_t>(
std::llround(farthestPoint[0]-mouth[0]));
const std::int64_t surfaceDy=static_cast<std::int64_t>(
std::llround(farthestPoint[1]-mouth[1]));
const std::int64_t surfaceDz=static_cast<std::int64_t>(
std::llround(farthestPoint[2]-mouthRouteHeight));
const std::int64_t surfaceHorizontalSquared=surfaceDx*surfaceDx
+surfaceDy*surfaceDy;
if(surfaceHorizontalSquared>0)
chamber.floorGradientQ16={
PassageRoundRatio(surfaceDx<0?-surfaceDz:surfaceDz,
std::abs(surfaceDx)*65536,
surfaceHorizontalSquared),
PassageRoundRatio(surfaceDy<0?-surfaceDz:surfaceDz,
std::abs(surfaceDy)*65536,
surfaceHorizontalSquared)};
}
}
const std::uint64_t gradientSquared=static_cast<std::uint64_t>(
chamber.floorGradientQ16[0]*chamber.floorGradientQ16[0]
+chamber.floorGradientQ16[1]*chamber.floorGradientQ16[1]);
const std::int64_t gradientMagnitude=static_cast<std::int64_t>(
PassageIsqrt(gradientSquared));
constexpr std::int64_t MaximumLandingGradeQ16=17560;
if(gradientMagnitude>MaximumLandingGradeQ16)
{error=passage.stableId+" "+side+" landing chamber floor exceeds 15 degrees";return false;}
const std::int64_t planeReachQ=PassageRoundRatio(
chamber.radiusSubvoxels,gradientMagnitude,65536);
const std::int64_t shellQ=static_cast<std::int64_t>(
std::llround(0.75*qPerMetre));
const auto qToUm=[&](std::int64_t q)
{return q*static_cast<std::int64_t>(layout.coreVoxelSizeMicrometres)
/WorldAssemblySubvoxelsPerVoxel;};
if(!host.content.presentInCurrentField
||qToUm(chamber.floorCenterSubvoxels[2]-planeReachQ-shellQ)
<host.content.bottomMicrometres
||qToUm(chamber.floorCenterSubvoxels[2]+planeReachQ
+chamber.clearHeightSubvoxels+shellQ)
>host.content.topMicrometres)
{
error=passage.stableId+" "+side
+" landing chamber does not fit inside its host content band";
return false;
}
return true;
};
if(!resolveLandingChamber(passage.upperLandingChamber,upperMouth,0,1,
upper,"upper")
||!resolveLandingChamber(passage.lowerLandingChamber,lowerMouth,
lowerMouthIndex,lowerMouthIndex-1,lower,"lower"))
return false;
passage.floorProfileIncludesLandingChambers=true;
const auto overlapCensus=AuditResolvedLayoutPassageSelfOverlapV1( const auto overlapCensus=AuditResolvedLayoutPassageSelfOverlapV1(
passage,layout.coreVoxelSizeMicrometres); passage,layout.coreVoxelSizeMicrometres);
std::printf("[PassageTubeSelfOverlapCensus] id=%s segment_pairs=%llu measured_pairs=%llu local_exempt_pairs=%llu overlap_pairs=%llu first_overlap_segments=(%llu,%llu) end_turn_max_deg=%.3f end_turn_violations=%u landing_column_violations=%llu minimum_clearance_m=%.6f status=%s\n", std::printf("[PassageTubeSelfOverlapCensus] id=%s segment_pairs=%llu measured_pairs=%llu local_exempt_pairs=%llu overlap_pairs=%llu first_overlap_segments=(%llu,%llu) end_turn_max_deg=%.3f end_turn_violations=%u landing_column_violations=%llu minimum_clearance_m=%.6f status=%s\n",
@@ -6256,14 +5989,20 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
endRunLength/static_cast<double>(qPerMetre)); endRunLength/static_cast<double>(qPerMetre));
} }
} }
std::printf("[PassageLandingChamberPlan] id=%s upper_radius_m=%.3f lower_radius_m=%.3f clear_height_m=4.500 upper_floor_grade_q16=(%lld,%lld) lower_floor_grade_q16=(%lld,%lld) status=PASS\n", std::printf("[PassageLandingPadPlan] id=%s upper_radius_m=%.3f lower_radius_m=%.3f upper_floor_z_m=%.3f lower_floor_z_m=%.3f source=h(s) status=PASS\n",
passage.stableId.c_str(), passage.stableId.c_str(),
passage.upperLandingChamber.radiusSubvoxels/static_cast<double>(qPerMetre), ResolvedLayoutPassageLandingDiscRadiusSubvoxels(
passage.lowerLandingChamber.radiusSubvoxels/static_cast<double>(qPerMetre), passage.mouthRadiusSubvoxels,layout.coreVoxelSizeMicrometres)
static_cast<long long>(passage.upperLandingChamber.floorGradientQ16[0]), /static_cast<double>(qPerMetre),
static_cast<long long>(passage.upperLandingChamber.floorGradientQ16[1]), ResolvedLayoutPassageLandingDiscRadiusSubvoxels(
static_cast<long long>(passage.lowerLandingChamber.floorGradientQ16[0]), passage.mouthRadiusSubvoxels,layout.coreVoxelSizeMicrometres)
static_cast<long long>(passage.lowerLandingChamber.floorGradientQ16[1])); /static_cast<double>(qPerMetre),
(passage.controlPoints.front().positionSubvoxels[2]
-passage.controlPoints.front().radiusSubvoxels)
/static_cast<double>(qPerMetre),
(passage.controlPoints[passage.lowerMouthControlIndex].positionSubvoxels[2]
-passage.controlPoints[passage.lowerMouthControlIndex].radiusSubvoxels)
/static_cast<double>(qPerMetre));
layout.passages.push_back(std::move(passage)); layout.passages.push_back(std::move(passage));
} }
} }
File diff suppressed because it is too large. Load diff
+14
View File
@@ -1875,6 +1875,20 @@ namespace VoxelForgeCore
{ return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->DensityAtUnclamped(static_cast<float>(position.x),static_cast<float>(position.y),static_cast<float>(position.z)):0.0f; } { return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->DensityAtUnclamped(static_cast<float>(position.x),static_cast<float>(position.y),static_cast<float>(position.z)):0.0f; }
float World::passageSourceDensityAt(Vec3d position) const float World::passageSourceDensityAt(Vec3d position) const
{ return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->PassageSourceDensityAt(static_cast<float>(position.x),static_cast<float>(position.y),static_cast<float>(position.z)):0.0f; } { return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->PassageSourceDensityAt(static_cast<float>(position.x),static_cast<float>(position.y),static_cast<float>(position.z)):0.0f; }
float World::passageFloorShellDistanceAt(Vec3d position) const
{ return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->PassageFloorShellDistanceAt(static_cast<float>(position.x),static_cast<float>(position.y),static_cast<float>(position.z)):std::numeric_limits<float>::infinity(); }
bool World::resolvedPassageLandingPadInfluenceBounds(std::size_t passageIndex,
bool upper,std::array<double,3>& minimumCoreVoxels,
std::array<double,3>& maximumCoreVoxels) const
{
if(!impl_||!impl_->shapeReady.load(std::memory_order_acquire)||!impl_->shape
||!impl_->shape->ResolvedLayout
||passageIndex>=impl_->shape->ResolvedLayout->passages.size())return false;
return FStandaloneShapeWorld::ResolvedLandingPadInfluenceBounds(
impl_->shape->ResolvedLayout->passages[passageIndex],upper,
impl_->shape->ResolvedLayout->coreVoxelSizeMicrometres,
minimumCoreVoxels,maximumCoreVoxels);
}
DetailFieldSample World::detailSampleAt(Vec3d position) const DetailFieldSample World::detailSampleAt(Vec3d position) const
{ return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->DetailSampleAt(static_cast<float>(position.x),static_cast<float>(position.y),static_cast<float>(position.z)):DetailFieldSample{}; } { return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->DetailSampleAt(static_cast<float>(position.x),static_cast<float>(position.y),static_cast<float>(position.z)):DetailFieldSample{}; }
float World::densityAtLattice(Vec3i position) const float World::densityAtLattice(Vec3i position) const
@@ -0,0 +1,139 @@
#!/usr/bin/env python3
"""Verify v242 canonical hashes across Clang/GCC and W1/W8/W12."""
from __future__ import annotations
import json
import re
import subprocess
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l176/hashes"
DATASETS = {
"owner4": ROOT / "Standalone/data/owner_world.json",
"owner2": ROOT / "Standalone/data/regression/owner_world_2strate_l61.json",
}
RECIPES = {
"owner4": ("surface-world",),
"owner2": ("surface-world", "floating-islands"),
}
BINARIES = {
"clang": ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l176-clang",
"gcc": ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l176-gcc",
}
BASELINE_ROOT = ROOT / "Standalone/Tools/verify/baselines/owner-hash/canonical"
BASELINES = {
("owner2", "surface-world"): (
"owner-hash-surface-world-style1-v241.baseline",
"owner-hash-surface-world-style1-v242.baseline"),
("owner2", "floating-islands"): (
"owner-hash-floating-islands-style2-v241.baseline",
"owner-hash-floating-islands-style2-v242.baseline"),
}
CANONICAL_RE = re.compile(
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M)
ALL_HASH_RE = re.compile(
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|MaterialHash|LODHash|ExactQueryHash)\].*$", re.M)
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
def run(dataset: str, recipe: str, compiler: str, workers: int,
expected_stamp: str) -> dict[str, object]:
label = f"{dataset}-{recipe}-{compiler}-w{workers}"
log = OUT / f"{label}.log"
layout = OUT / f"{label}.layout.json"
command = [
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(BINARIES[compiler]), str(DATASETS[dataset]),
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
"--force-generate", f"--workers={workers}", "--clip-radius=3",
"--max-clip-level=8", "--max-level=8", f"--recipe={recipe}",
"--stream-hash", f"--resolved-layout-output={layout}",
]
if dataset == "owner4":
command.append("--seed=0")
started = time.monotonic()
try:
with log.open("w", encoding="utf-8") as output:
result = subprocess.run(command, cwd=ROOT, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
code: int | str = result.returncode
except subprocess.TimeoutExpired:
code = "OUTER_TIMEOUT"
content = log.read_text(encoding="utf-8", errors="replace") if log.is_file() else ""
hashes = ALL_HASH_RE.findall(content)
canonical = CANONICAL_RE.findall(content)
stamp = STAMP_RE.search(content)
row: dict[str, object] = {
"dataset": dataset, "recipe": recipe, "compiler": compiler,
"workers": workers, "exit_code": code,
"run_passed": code == 0 and len(canonical) == 12 and len(hashes) == 13
and stamp is not None and stamp.group(1) == expected_stamp,
"canonical_hashes": canonical, "all_pipeline_hashes": hashes,
"source_stamp": stamp.group(1) if stamp else None,
"elapsed_s": round(time.monotonic() - started, 3),
"log": str(log.relative_to(ROOT)),
}
print("[L176CanonicalHashRun] " + json.dumps(row, sort_keys=True), flush=True)
return row
def main() -> int:
if any(not path.is_file() for path in (*DATASETS.values(), *BINARIES.values())):
raise SystemExit("missing v242 owner input or final compiler binary")
OUT.mkdir(parents=True, exist_ok=True)
expected_stamp = subprocess.check_output([
sys.executable, str(ROOT / "Standalone/Tools/verify/source_stamp.py"),
"stamp", "--root", str(ROOT)], text=True).strip()
rows: list[dict[str, object]] = []
groups: dict[str, dict[str, object]] = {}
partial = OUT / "runs-partial.json"
for dataset, recipes in RECIPES.items():
for recipe in recipes:
current: list[dict[str, object]] = []
for workers in (1, 8, 12):
for compiler in ("clang", "gcc"):
row = run(dataset, recipe, compiler, workers, expected_stamp)
rows.append(row)
current.append(row)
partial.write_text(json.dumps(rows, indent=2) + "\n",
encoding="utf-8")
deterministic = (len(current) == 6 and all(row["run_passed"] for row in current)
and all(row["all_pipeline_hashes"] == current[0]["all_pipeline_hashes"]
for row in current))
group = f"{dataset}/{recipe}"
groups[group] = {
"all_compilers_workers_match": deterministic,
"canonical_rows": len(current[0]["canonical_hashes"]) if current else 0,
"pipeline_rows": len(current[0]["all_pipeline_hashes"]) if current else 0,
}
print("[L176CanonicalHashGroup] " + json.dumps(
{"group": group, **groups[group]}, sort_keys=True), flush=True)
passed = len(rows) == 18 and all(row["run_passed"] for row in rows) \
and all(group["all_compilers_workers_match"] for group in groups.values())
if passed:
for key, (old_name, new_name) in BASELINES.items():
canonical = next(row["canonical_hashes"] for row in rows
if (row["dataset"], row["recipe"], row["compiler"], row["workers"])
== (*key, "clang", 12))
(BASELINE_ROOT / new_name).write_text("\n".join(canonical) + "\n",
encoding="utf-8")
old = CANONICAL_RE.findall((BASELINE_ROOT / old_name).read_text(
encoding="utf-8", errors="replace"))
groups[f"{key[0]}/{key[1]}"]["changed_v241_rows"] = sum(
a != b for a, b in zip(old, canonical))
summary = {"status": "PASS" if passed else "FAIL",
"source_stamp": expected_stamp, "runs": rows, "groups": groups}
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n",
encoding="utf-8")
print(f"[L176CanonicalHashSummary] status={summary['status']} runs={len(rows)} "
f"groups={len(groups)}", flush=True)
return 0 if passed else 1
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,144 @@
#!/usr/bin/env python3
"""Run L176 step-3 audits and route gates for owner seeds 0..15."""
from __future__ import annotations
import json
import os
import re
import subprocess
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4]
OUT = ROOT / "Saved/Linux/l176/seeds"
BIN = ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l176-clang"
DATA = ROOT / "Standalone/data/owner_world.json"
STAMP_TOOL = ROOT / "Standalone/Tools/verify/source_stamp.py"
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
def one(pattern: str, content: str) -> re.Match[str] | None:
return re.search(pattern, content, re.M)
def evaluate(content: str, expected_stamp: str, require_l130: bool) -> dict[str, object]:
stamp = STAMP_RE.search(content)
seam = one(r"^\[PassageTileSeamAudit\].*$", content)
seam_fields = dict(re.findall(r"\b([a-z0-9_]+)=([^ ]+)", seam.group(0))) if seam else {}
pad = one(r"^\[LandingPadAuditSummary\].*$", content)
air = one(r"^\[NoAddedSolidOverHostAirAudit\].*$", content)
floor = one(r"^\[PassageFloorShellAuditSummary\].*$", content)
interior = one(r"^\[PassageInteriorCapsuleAuditSummary\].*$", content)
end_run = one(r"^\[PassageEndRunAuditSummary\].*$", content)
l130 = one(r"^\[L130PassageReachProofAudit\].*$", content)
route_rows = {}
for name, strate in (("tunnel_network", 2), ("vertical_shafts", 3)):
route_rows[name] = one(rf"^\[ResolvedStrateRouteAudit\] strate={strate} .*status=(PASS|FAIL)$", content)
controls = {
name: bool(one(rf"^\[{label}BlockedRouteControl\].*expected=FAIL status=PASS$", content))
for name, label in (("tunnel_network", "TunnelNetwork"),
("vertical_shafts", "VerticalShafts"))
}
checks = {
"source_stamp": bool(stamp and stamp.group(1) == expected_stamp),
"seam_zero_mismatches": seam_fields.get("mismatches") == "0",
"seam_no_exempt_pairs": seam_fields.get("exempt_pairs") == "0",
"landing_pads": bool(pad and re.search(
r"pads=6 passed=6 negative_controls=24/24 status=PASS$", pad.group(0))),
"no_added_solid_over_host_air": bool(air and re.search(
r"added_solid_samples=0 .*negative_control=DETECTED status=PASS$", air.group(0))),
"floor_shell": bool(floor and "status=PASS" in floor.group(0)),
"passage_interior_air": bool(interior and "status=PASS" in interior.group(0)),
"end_runs": bool(end_run and re.search(
r"passages=3 measured=3 passed=3 status=PASS$", end_run.group(0))),
"tunnel_network_route": bool(route_rows["tunnel_network"] and
"status=PASS" in route_rows["tunnel_network"].group(0)),
"vertical_shafts_route": bool(route_rows["vertical_shafts"] and
"status=PASS" in route_rows["vertical_shafts"].group(0)),
"route_blocked_controls": all(controls.values()),
}
if l130:
fields = dict(re.findall(r"\b([a-z0-9_]+)=([^ ]+)", l130.group(0)))
checks["l130_pad_bounds"] = (fields.get("status") == "PASS"
and fields.get("pad_proof_misses") == "0"
and fields.get("pad_feature_misses") == "0"
and fields.get("pad_stamp_misses") == "0")
elif require_l130:
checks["l130_pad_bounds"] = False
else:
checks["l130_pad_bounds"] = True
return {
"checks": checks,
"step3_passed": all(checks.values()),
"seam_summary": seam_fields,
"route_rows": {key: value.group(0) if value else None
for key, value in route_rows.items()},
"resolved_passage_summary": (one(r"^\[ResolvedPassageAuditSummary\].*$", content).group(0)
if one(r"^\[ResolvedPassageAuditSummary\].*$", content) else None),
"l130_line": l130.group(0) if l130 else None,
}
def main() -> int:
import argparse
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--seed-start", type=int, default=0)
parser.add_argument("--seed-stop", type=int, default=16, help="exclusive")
parser.add_argument("--no-resume", action="store_true")
args = parser.parse_args()
if not 0 <= args.seed_start <= args.seed_stop <= 16:
raise SystemExit("seed range must be within 0..16")
if not BIN.is_file() or not DATA.is_file():
raise SystemExit(f"missing binary or owner fixture: {BIN}")
OUT.mkdir(parents=True, exist_ok=True)
stamp = subprocess.check_output(
[sys.executable, str(STAMP_TOOL), "stamp", "--root", str(ROOT)], text=True).strip()
rows: list[dict[str, object]] = []
for seed in range(args.seed_start, args.seed_stop):
log, layout, exit_file = (OUT / f"seed{seed}.log", OUT / f"seed{seed}.layout.json",
OUT / f"seed{seed}.exit")
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
"timeout", "1740s", str(BIN), str(DATA), "--world-assembler-prototype",
"--stream-benchmark", "--stream-pipeline", "--workers=8", "--clip-radius=3",
"--max-clip-level=8", "--max-level=0", "--force-generate", f"--seed={seed}",
"--recipe=surface-world", "--density-dispatch-report",
"--audit-resolved-layout-passages", "--audit-passage-tile-seams",
f"--resolved-layout-output={layout}"]
env = os.environ.copy()
if seed == 0:
env["VOXELFORGE_L130_PASSAGE_REACH_PROOF_AUDIT"] = "1"
resumed = False
started = time.monotonic()
if not args.no_resume and log.is_file() and layout.is_file() and exit_file.is_file():
code: int | str = int(exit_file.read_text().strip())
resumed = True
else:
try:
with log.open("w", encoding="utf-8") as output:
proc = subprocess.run(command, cwd=ROOT, env=env, stdout=output,
stderr=subprocess.STDOUT, check=False, timeout=1740)
code = proc.returncode
except subprocess.TimeoutExpired:
code = "OUTER_TIMEOUT"
if isinstance(code, int):
exit_file.write_text(f"{code}\n", encoding="utf-8")
content = log.read_text(encoding="utf-8", errors="replace") if log.is_file() else ""
result = evaluate(content, stamp, require_l130=(seed == 0))
row = {"seed": seed, "process_exit": code, "source_stamp": stamp,
"elapsed_s": round(time.monotonic() - started, 3), "resumed": resumed,
"step3_passed": result["step3_passed"], **result,
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
rows.append(row)
(OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n")
print("[L176SeedRun] " + json.dumps(row, sort_keys=True), flush=True)
passed = sum(bool(row["step3_passed"]) for row in rows)
summary = {"source_stamp": stamp, "seeds": list(range(args.seed_start, args.seed_stop)),
"step3_passed": passed, "step3_failed": len(rows) - passed, "rows": rows}
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n")
print(f"[L176SeedSummary] step3={passed}/{len(rows)}", flush=True)
return 0 if passed == len(rows) else 1
if __name__ == "__main__":
raise SystemExit(main())
File diff suppressed because it is too large. Load diff
+4 -4
View File
@@ -125,11 +125,11 @@ RECIPE_RE = re.compile(
def owner_hash_baseline(recipe: str, style: int) -> tuple[Path, str]: def owner_hash_baseline(recipe: str, style: int) -> tuple[Path, str]:
if recipe == "surface-world" and style == 1: if recipe == "surface-world" and style == 1:
return (BASELINES / "owner-hash/canonical/owner-hash-surface-world-style1-v241.baseline", return (BASELINES / "owner-hash/canonical/owner-hash-surface-world-style1-v242.baseline",
"L175-v241-passage-tile-seam-fix") "L176-v242-air-only-passage-clamp")
if recipe == "floating-islands" and style == 2: if recipe == "floating-islands" and style == 2:
return (BASELINES / "owner-hash/canonical/owner-hash-floating-islands-style2-v241.baseline", return (BASELINES / "owner-hash/canonical/owner-hash-floating-islands-style2-v242.baseline",
"L175-v241-passage-tile-seam-fix") "L176-v242-air-only-passage-clamp")
raise VerificationFailure(f"no pinned owner hash baseline for {recipe} style {style}") raise VerificationFailure(f"no pinned owner hash baseline for {recipe} style {style}")