wip(assembler): round L33 -- SurfaceWorld can be an upper strate; FloorProfileBlend measures seams
Route planning for an upper SurfaceWorld failed on long candidate chords over steep relief; chords are now capped to the arrival/departure radii on an 8 m centre grid with an origin-proximity cost (21.5 m walk, about 4 deg). The departure passage starts at the SW ground and tunnels down through rock. FI -> SW -> TN fixture: all 4 styles pass every passage audit on both compilers. FloorProfileBlend now checks endpoint height/tangent seams (step and tangent-step negative controls detected). Owner world hashes unchanged; 30/30 determinism; cost within noise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
1 parent
d0158c6019
commit
b7a47bef95
17 files changed
+1116
-32
No files matched your search
@@ -2076,6 +2076,40 @@ transition one micrometre wider than declared and a band one micrometre below th
|
||||
[`Standalone/Research/WorldAssemblerM3/Linux29/REPORT.md`](Standalone/Research/WorldAssemblerM3/Linux29/REPORT.md)
|
||||
for audit measurements, hashes, timings, captures and run history.
|
||||
|
||||
### 8.47 Ground-bound SurfaceWorld departure routes — World Assembler M3 Linux L33
|
||||
|
||||
An intra-strate SurfaceWorld route follows the resolved terrain ground profile. Its route-point Z
|
||||
is the ground height plus the player's half-height; the route does not descend to the strate's
|
||||
bedrock boundary. Bedrock belongs to the separate inter-strate passage, which begins at the
|
||||
SurfaceWorld departure mouth on the ground and tunnels outward and down through native rock and
|
||||
the boundary into the next strate.
|
||||
|
||||
The old fixed candidate envelope used a 64 m chord and 16 m centre spacing. On the L33 upper-SW
|
||||
fixture, replaying that envelope sampled ground from −507.767 to −464.857 m; 366/648 candidate paths
|
||||
exceeded the 15° grade limit. The worst failures clustered near the outer west/northwest terrain
|
||||
shoulder (up to 79.971°) and the steep approach slopes around the origin. Candidate diagnostics
|
||||
separate grade failures from endpoints outside the bounded arrival/departure placement disks.
|
||||
The bounded search now limits its longest chord to the two mouth radii minus a 3 m clearance
|
||||
margin, checks centres every 8 m, and ranks endpoints near the route anchors. It finds a 21.5 m
|
||||
route from (−2.75, 0) to (18.75, 0), sampled on ground and accepted by the full capsule/support/
|
||||
grade audit.
|
||||
|
||||
The SurfaceWorld departure passage starts at the ground mouth with a 1.5 m portal, then smoothsteps
|
||||
to a 3.5 m rock tunnel over 9 m. Its ring is sized and oriented outward from the mouth, so the
|
||||
approach does not cut back under the walk route. The `FloatingIslands → SurfaceWorld → TunnelNetwork`
|
||||
fixture passes all four passage styles, including both interfaces, capsule clearance, support,
|
||||
grade, walkability, naturalness, open-air, column ends, long-bridge and route-binding audits.
|
||||
|
||||
`FloorProfileBlend` previously compared each curved floor profile with its straight chord and
|
||||
applied a 2-voxel limit. That is a curvature measurement, not a seam measurement. It now checks
|
||||
endpoint height and tangent continuity at 0.01 voxel and 0.001 voxel/m; the 3-voxel step and
|
||||
0.25 voxel/m tangent-step controls are both detected. SurfaceWorld owner hashes remain identical
|
||||
to L32 because the route-mouth, expanded column sizing and short-span support changes apply only
|
||||
when SurfaceWorld is the upper strate. No evaluator or recipe version changed.
|
||||
|
||||
[`Standalone/Research/WorldAssemblerM3/Linux33/REPORT.md`](Standalone/Research/WorldAssemblerM3/Linux33/REPORT.md)
|
||||
records the four-style audits, captures, determinism, owner hashes, costs and retries.
|
||||
|
||||
## 9. Multiplayer model (listen-server first, dedicated-friendly)
|
||||
|
||||
> **Status: transport is DESIGN ONLY — nothing is networked in-tree yet** (no `Replicated`/`HasAuthority`/RPCs; a
|
||||
|
||||
+14
@@ -1229,6 +1229,20 @@ The Unreal adapter now forwards the authored transition type and chunk width; th
|
||||
were not compiled here. See ARCHITECTURE §8.46 and
|
||||
[`Standalone/Research/WorldAssemblerM3/Linux29/REPORT.md`](Standalone/Research/WorldAssemblerM3/Linux29/REPORT.md).
|
||||
|
||||
### 3.30 SurfaceWorld as an upper strate and floor-profile joins — World Assembler L33
|
||||
|
||||
| Symbol | Canonical file | Role |
|
||||
|---|---|---|
|
||||
| `BuildFiniteStrateRoute` | `Standalone/Source/WorldAssemblyPrototype.h` | Samples route heights from the composed terrain ground; bounds candidate chords by the arrival/departure placement radii and records per-segment grade failures. |
|
||||
| `PlanResolvedLayoutPassagesV1` | `Standalone/Core/src/ResolvedLayoutInternal.h` | Starts an upper-SurfaceWorld departure at its terrain mouth, opens a small portal and smoothsteps to the rock tunnel while orienting the ring outward. |
|
||||
| `BuildPassageVoidCrossingPlan`, `PassageBridgeSignedDistance` | `Standalone/Core/src/ShapeWorldInternal.h` | Sizes unsupported ranges from the full control-segment window only for upper SurfaceWorld; other upper generators keep the previous bracketing-sample bounds. |
|
||||
| `--audit-resolved-layout-passages` | `Standalone/Source/StreamBenchmarkPipeline.h` | Uses floor height rather than bore-centre height for naturalness and records failed-only wall probes for column audits. |
|
||||
| `FloorProfileBlend` | `Standalone/Core/src/ShapeWorldInternal.h` | Measures endpoint height/tangent continuity; chord deviation is informational, with step and tangent-step negative controls. |
|
||||
|
||||
The owner SurfaceWorld hashes match L32 and evaluator/recipe versions are unchanged. See
|
||||
ARCHITECTURE §8.47 and
|
||||
[`Standalone/Research/WorldAssemblerM3/Linux33/REPORT.md`](Standalone/Research/WorldAssemblerM3/Linux33/REPORT.md).
|
||||
|
||||
## 4. The density pipeline (most-edited hot path)
|
||||
|
||||
### 4.1 `GetDensityWithParams` (TunnelNetwork) — VoxelGenerator.cpp:3749
|
||||
|
||||
@@ -788,10 +788,13 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
cfg->distanceMaximumSubvoxels):8*qPerMetre;
|
||||
const std::int64_t placementRadius=std::max<std::int64_t>(1,outer);
|
||||
// Leave room for the capsule and bounded wall relief at the floor/wall seam.
|
||||
const std::int64_t mouthRadius=std::max<std::int64_t>(static_cast<std::int64_t>(std::llround(1.5*qPerMetre)),
|
||||
const bool upperIsSurfaceWorld=upper.generatorKind=="SurfaceWorld";
|
||||
const std::int64_t minimumMouthRadius=static_cast<std::int64_t>(std::llround(
|
||||
(upperIsSurfaceWorld?2.0:1.5)*qPerMetre));
|
||||
const std::int64_t mouthRadius=std::max<std::int64_t>(minimumMouthRadius,
|
||||
cfg?cfg->mouthRadiusSubvoxels:0);
|
||||
const std::int64_t midRadius=std::max<std::int64_t>(
|
||||
static_cast<std::int64_t>(std::llround(1.5*qPerMetre)),
|
||||
static_cast<std::int64_t>(std::llround((upperIsSurfaceWorld?3.5:1.5)*qPerMetre)),
|
||||
cfg?cfg->midRadiusSubvoxels:mouthRadius);
|
||||
const std::int64_t reachMinimum=cfg?std::max<std::int64_t>(0,cfg->reachMinimumSubvoxels):8*qPerMetre;
|
||||
const std::int64_t reachMaximum=cfg?std::max<std::int64_t>(reachMinimum,cfg->reachMaximumSubvoxels):24*qPerMetre;
|
||||
@@ -813,6 +816,7 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
std::array<std::int64_t,3> upperMouth{},lowerMouth{};
|
||||
const bool hasUpperRoute=connection==0&&routeAnchor(upper,true,upperMouth);
|
||||
const bool hasLowerRoute=connection==0&&routeAnchor(lower,false,lowerMouth);
|
||||
const bool surfaceGroundDeparture=hasUpperRoute&&upperIsSurfaceWorld;
|
||||
const ResolvedLayoutAnchorV2* passageAnchor=nullptr;
|
||||
if(connection==0)
|
||||
for(const auto& anchor:upper.anchors)
|
||||
@@ -870,6 +874,17 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
std::max<std::int64_t>(0,cfg?cfg->cascadeLedgeSubvoxels:0)
|
||||
*static_cast<std::int64_t>(cfg?cfg->cascadeSteps:4)),
|
||||
mouthRadius+2*qPerMetre,outerRingRadius);
|
||||
if(surfaceGroundDeparture)
|
||||
{
|
||||
const double departureRadius=std::hypot(static_cast<double>(upperMouth[0]),
|
||||
static_cast<double>(upperMouth[1]));
|
||||
const std::int64_t clearRingRadius=static_cast<std::int64_t>(std::ceil(
|
||||
departureRadius+mouthRadius+0.5*qPerMetre));
|
||||
const std::int64_t maximumRingRadius=std::max<std::int64_t>(1,
|
||||
placementRadius-qPerMetre);
|
||||
ringRadius=std::min(maximumRingRadius,
|
||||
std::max(ringRadius,clearRingRadius));
|
||||
}
|
||||
if(anchoredIslandPassage)
|
||||
ringRadius=std::min(ringRadius,mouthRadius+qPerMetre/2);
|
||||
std::uint32_t phase=static_cast<std::uint32_t>(PassageSeed(seed,
|
||||
@@ -890,6 +905,23 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
{bestAlignment=alignment;phase=candidate;}
|
||||
}
|
||||
}
|
||||
if(hasUpperRoute&&upperIsSurfaceWorld)
|
||||
{
|
||||
const auto& directionAtPhase=directions[phase];
|
||||
const std::int64_t radialAlignment=upperMouth[0]*directionAtPhase[0]
|
||||
+upperMouth[1]*directionAtPhase[1];
|
||||
if(radialAlignment<0)
|
||||
{
|
||||
std::int64_t bestOutwardAlignment=std::numeric_limits<std::int64_t>::min();
|
||||
for(std::uint32_t candidate=0;candidate<directions.size();++candidate)
|
||||
{
|
||||
const std::int64_t alignment=upperMouth[0]*directions[candidate][0]
|
||||
+upperMouth[1]*directions[candidate][1];
|
||||
if(alignment>bestOutwardAlignment)
|
||||
{bestOutwardAlignment=alignment;phase=candidate;}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool clockwise=(PassageSeed(seed,upper.strateIndex,lower.strateIndex,
|
||||
connection,0x44495245u)&1u)!=0;
|
||||
if(anchoredSpiral&&passageAnchor&&hasUpperTangent)
|
||||
@@ -1159,10 +1191,23 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
if(hasAnchorHelix&&helixHorizontal==0)
|
||||
{error=passage.stableId+" anchor spiral has no horizontal reach";return false;}
|
||||
passage.controlPoints.reserve(xy.size());
|
||||
const std::int64_t surfacePortalRadius=surfaceGroundDeparture
|
||||
?std::min(mouthRadius,static_cast<std::int64_t>(std::llround(1.5*qPerMetre)))
|
||||
:mouthRadius;
|
||||
const std::uint64_t surfaceMouthBlendLength=static_cast<std::uint64_t>(
|
||||
std::max<std::int64_t>(8*qPerMetre,2*mouthRadius));
|
||||
for(std::size_t i=0;i<xy.size();++i)
|
||||
{
|
||||
const std::int64_t radius=(i<=1||i==lowerMouthIndex
|
||||
std::int64_t radius=(i<=1||i==lowerMouthIndex
|
||||
||i+1>=xy.size()-1)?mouthRadius:midRadius;
|
||||
if(surfaceGroundDeparture&&i<lowerMouthIndex)
|
||||
{
|
||||
const double t=std::clamp(static_cast<double>(cumulative[i])
|
||||
/surfaceMouthBlendLength,0.0,1.0);
|
||||
const double smooth=t*t*(3.0-2.0*t);
|
||||
radius=surfacePortalRadius+static_cast<std::int64_t>(std::llround(
|
||||
(midRadius-surfacePortalRadius)*smooth));
|
||||
}
|
||||
std::int64_t playerZ=0;
|
||||
if(i>lowerMouthIndex)
|
||||
playerZ=lowerLandingPoints[i-lowerMouthIndex-1][2];
|
||||
@@ -1183,9 +1228,30 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
point.positionSubvoxels={xy[i][0],xy[i][1],playerZ+radius-playerCenterAboveFloorQ};
|
||||
point.radiusSubvoxels=radius;passage.controlPoints.push_back(point);
|
||||
}
|
||||
passage.controlPoints.front().positionSubvoxels[2]=upperMouth[2]+mouthRadius-playerCenterAboveFloorQ;
|
||||
passage.controlPoints[lowerMouthIndex].positionSubvoxels[2]=
|
||||
lowerMouth[2]+mouthRadius-playerCenterAboveFloorQ;
|
||||
if(surfaceGroundDeparture)
|
||||
std::printf("[ResolvedSurfaceWorldDepartureMouth] passage=%s ground_floor_z_m=%.3f portal_radius_m=%.3f tunnel_radius_m=%.3f blend_length_m=%.3f departure_xy_m=(%.3f,%.3f) first_tunnel_xy_m=(%.3f,%.3f) first_tunnel_floor_z_m=%.3f first_tunnel_radius_m=%.3f\n",
|
||||
passage.stableId.c_str(),(upperMouth[2]-static_cast<std::int64_t>(std::llround(0.88*qPerMetre)))/static_cast<double>(qPerMetre),
|
||||
surfacePortalRadius/static_cast<double>(qPerMetre),
|
||||
midRadius/static_cast<double>(qPerMetre),
|
||||
surfaceMouthBlendLength/static_cast<double>(qPerMetre),
|
||||
upperMouth[0]/static_cast<double>(qPerMetre),
|
||||
upperMouth[1]/static_cast<double>(qPerMetre),
|
||||
passage.controlPoints.size()>1?passage.controlPoints[1].positionSubvoxels[0]
|
||||
/static_cast<double>(qPerMetre):0.0,
|
||||
passage.controlPoints.size()>1?passage.controlPoints[1].positionSubvoxels[1]
|
||||
/static_cast<double>(qPerMetre):0.0,
|
||||
passage.controlPoints.size()>1?
|
||||
(passage.controlPoints[1].positionSubvoxels[2]
|
||||
-passage.controlPoints[1].radiusSubvoxels+playerCenterAboveFloorQ
|
||||
-static_cast<std::int64_t>(std::llround(0.88*qPerMetre)))
|
||||
/static_cast<double>(qPerMetre):0.0,
|
||||
passage.controlPoints.size()>1?passage.controlPoints[1].radiusSubvoxels
|
||||
/static_cast<double>(qPerMetre):0.0);
|
||||
passage.controlPoints.front().positionSubvoxels[2]=upperMouth[2]
|
||||
+passage.controlPoints.front().radiusSubvoxels-playerCenterAboveFloorQ;
|
||||
passage.controlPoints[lowerMouthIndex].positionSubvoxels[2]=lowerMouth[2]
|
||||
+passage.controlPoints[lowerMouthIndex].radiusSubvoxels
|
||||
-playerCenterAboveFloorQ;
|
||||
layout.passages.push_back(std::move(passage));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1309,6 +1309,10 @@ public:
|
||||
double MaxUnmarkedFloorDirectionSlopeDelta = 0.0;
|
||||
double MaxMarkedFloorSlopeDelta = 0.0;
|
||||
double MaxFloorCurveDeviation = 0.0;
|
||||
double MaxFloorProfileEndpointValueError = 0.0;
|
||||
double MaxFloorProfileEndpointSlopeError = 0.0;
|
||||
bool FloorProfileEndpointStepControlDetected = false;
|
||||
bool FloorProfileEndpointSlopeControlDetected = false;
|
||||
double LocalPrimitiveSeconds = 0.0;
|
||||
double LocalLineBinSeconds = 0.0;
|
||||
double CandidateMergeSeconds = 0.0;
|
||||
@@ -1380,6 +1384,8 @@ public:
|
||||
double FloorCurveStartBlend = 0.0;
|
||||
double FloorCurveEndBlend = 0.0;
|
||||
double FloorCurveMaxDeviation = 0.0;
|
||||
double FloorCurveEndpointValueError = 0.0;
|
||||
double FloorCurveEndpointSlopeError = 0.0;
|
||||
};
|
||||
|
||||
struct FInterval { int32 Lo = 0, Hi = -1; };
|
||||
@@ -1517,6 +1523,7 @@ public:
|
||||
if(NodeCount>160000000ull)
|
||||
{Error="shape interval region exceeds the 160-million-node safety bound";return false;}
|
||||
Stats={};Stats.SignNodes=NodeCount;
|
||||
AuditFloorProfileBlendNegativeControls();
|
||||
|
||||
auto Stage=Clock::now();
|
||||
BuildLayerBounds();
|
||||
@@ -1622,13 +1629,19 @@ public:
|
||||
static_cast<unsigned long long>(Stats.RoomFloorJoinCount));
|
||||
const double SmoothUnionGrowthBound=JoinBlendRadius/6.0;
|
||||
const bool bFloorSlopePass=Stats.MaxUnmarkedFloorSlopeDelta<=0.75;
|
||||
const bool bFloorBlendPass=Stats.MaxFloorCurveDeviation<=2.0;
|
||||
const bool bFloorBlendPass=Stats.MaxFloorProfileEndpointValueError<=0.01
|
||||
&&Stats.MaxFloorProfileEndpointSlopeError<=0.001
|
||||
&&Stats.FloorProfileEndpointStepControlDetected
|
||||
&&Stats.FloorProfileEndpointSlopeControlDetected;
|
||||
std::printf("[FloorSlopeJoinAudit] raw_unmarked_max_delta=%.5f vox generated_tangent_delta=%.5f vox bend_direction_slope_change=%.5f vox marked_max_delta=%.5f vox marked_joins=%llu threshold=0.750 status=%s\n",
|
||||
Stats.MaxRawUnmarkedFloorSlopeDelta,Stats.MaxUnmarkedFloorSlopeDelta,
|
||||
Stats.MaxUnmarkedFloorDirectionSlopeDelta,Stats.MaxMarkedFloorSlopeDelta,
|
||||
static_cast<unsigned long long>(Stats.MarkedFloorSlopeJoins),bFloorSlopePass?"PASS":"FAIL");
|
||||
std::printf("[FloorProfileBlend] c1_segments=%llu max_curve_deviation=%.3f vox threshold=2.000 status=%s\n",
|
||||
std::printf("[FloorProfileBlend] c1_segments=%llu max_chord_deviation=%.3f vox informational endpoint_value_residual=%.8f vox endpoint_slope_residual=%.8f vox_per_m thresholds=(0.010,0.001) negative_controls=%s/%s status=%s\n",
|
||||
static_cast<unsigned long long>(Stats.CurvedFloorSegments),Stats.MaxFloorCurveDeviation,
|
||||
Stats.MaxFloorProfileEndpointValueError,Stats.MaxFloorProfileEndpointSlopeError,
|
||||
Stats.FloorProfileEndpointStepControlDetected?"step_detected":"step_missed",
|
||||
Stats.FloorProfileEndpointSlopeControlDetected?"slope_detected":"slope_missed",
|
||||
bFloorBlendPass?"PASS":"FAIL");
|
||||
std::printf("[TunnelRadiusAudit] shared_joins=%llu legacy_fixed_segment_max_step=%.4f vox smoothed_profile_max_step=%.4f vox tapered_endpoint_max_delta=%.6f vox radius_slope_join_delta=%.8f floor_endpoint_max_delta=%.6f vox floor_slope_join_delta=%.5f unmarked_floor_slope_delta=%.5f marked_floor_slope_delta=%.5f marked_slope_joins=%llu smooth_union_growth_bound=%.4f vox thresholds=(0.005,0.000001,0.005,0.25) status=%s\n",
|
||||
static_cast<unsigned long long>(Stats.CapsuleJoinCount),Stats.LegacySegmentRadiusStep,
|
||||
@@ -2751,8 +2764,23 @@ private:
|
||||
auto& flags= crossing.needsColumn
|
||||
?PassageColumnSegmentFlags[crossing.passageIndex]
|
||||
:PassageShortSpanSegmentFlags[crossing.passageIndex];
|
||||
const std::size_t first=std::min(crossing.firstSegment,flags.size());
|
||||
const std::size_t last=std::min(crossing.lastSegment,flags.empty()?0:flags.size()-1);
|
||||
bool surfaceWorldUpper=false;
|
||||
if(crossing.passageIndex<ResolvedLayout->passages.size())
|
||||
{
|
||||
const std::int32_t strate=ResolvedLayout->passages[
|
||||
crossing.passageIndex].upperStrateIndex;
|
||||
const auto upper=std::find_if(ResolvedLayout->strates.begin(),
|
||||
ResolvedLayout->strates.end(),[strate](const auto& value)
|
||||
{return value.strateIndex==strate;});
|
||||
surfaceWorldUpper=upper!=ResolvedLayout->strates.end()
|
||||
&&upper->generatorKind=="SurfaceWorld";
|
||||
}
|
||||
const std::size_t shortSpanPadding=!crossing.needsColumn
|
||||
&&surfaceWorldUpper&&!flags.empty()?2:0;
|
||||
const std::size_t first=std::min(crossing.firstSegment>shortSpanPadding
|
||||
?crossing.firstSegment-shortSpanPadding:0,flags.size());
|
||||
const std::size_t last=std::min(crossing.lastSegment+shortSpanPadding,
|
||||
flags.empty()?0:flags.size()-1);
|
||||
if(!flags.empty()&&first<=last)
|
||||
std::fill(flags.begin()+first,flags.begin()+last+1,1);
|
||||
}
|
||||
@@ -3011,6 +3039,11 @@ private:
|
||||
ResolvedLayout->coreVoxelSizeMicrometres);
|
||||
const auto& a=passage.controlPoints[SegmentIndex];
|
||||
const auto& b=passage.controlPoints[SegmentIndex+1];
|
||||
const auto upperStratum=std::find_if(ResolvedLayout->strates.begin(),
|
||||
ResolvedLayout->strates.end(),[&passage](const auto& value)
|
||||
{return value.strateIndex==passage.upperStrateIndex;});
|
||||
const bool surfaceWorldUpper=upperStratum!=ResolvedLayout->strates.end()
|
||||
&&upperStratum->generatorKind=="SurfaceWorld";
|
||||
const double ax=static_cast<double>(a.positionSubvoxels[0]);
|
||||
const double ay=static_cast<double>(a.positionSubvoxels[1]);
|
||||
const double dx=static_cast<double>(b.positionSubvoxels[0])-ax;
|
||||
@@ -3031,7 +3064,8 @@ private:
|
||||
+PassageBridgeDeckOffsetMetres(PassageIndex,SegmentIndex)*qPerMetre;
|
||||
const double relief=std::clamp(static_cast<double>(VF_FieldSurfaceRelief(X,Y,Z,
|
||||
static_cast<std::uint32_t>(Owner?Owner->Seed:0))),-1.0,1.0);
|
||||
const double halfWidth=(3.30+0.20*relief)*qPerMetre;
|
||||
const double halfWidth=(surfaceWorldUpper?3.65+0.10*relief:3.30+0.20*relief)
|
||||
*qPerMetre;
|
||||
const double thickness=(4.0+1.0*relief)*qPerMetre;
|
||||
const double horizontalDistance=DeterministicSqrt((qx-centerX)*(qx-centerX)
|
||||
+(qy-centerY)*(qy-centerY));
|
||||
@@ -3214,6 +3248,11 @@ private:
|
||||
++passageIndex)
|
||||
{
|
||||
const auto& passage=ResolvedLayout->passages[passageIndex];
|
||||
const auto upperStratum=std::find_if(ResolvedLayout->strates.begin(),
|
||||
ResolvedLayout->strates.end(),[&passage](const auto& value)
|
||||
{return value.strateIndex==passage.upperStrateIndex;});
|
||||
const bool surfaceWorldUpper=upperStratum!=ResolvedLayout->strates.end()
|
||||
&&upperStratum->generatorKind=="SurfaceWorld";
|
||||
if(passage.controlPoints.size()<2)continue;
|
||||
std::vector<FPathSample> samples;
|
||||
samples.reserve(passage.controlPoints.size()*2);
|
||||
@@ -3303,14 +3342,32 @@ private:
|
||||
}
|
||||
double maximumOffsetSubvoxels=0.0;
|
||||
double maximumBoreSubvoxels=0.0;
|
||||
for(std::size_t index=startSample;index<=endSample;++index)
|
||||
double minimumCrossingZ=std::min(crossing.startPoint[2],crossing.endPoint[2]);
|
||||
double maximumCrossingZ=std::max(crossing.startPoint[2],crossing.endPoint[2]);
|
||||
for(std::size_t sampleIndex=0;sampleIndex<samples.size();++sampleIndex)
|
||||
{
|
||||
const auto& point=samples[index].point;
|
||||
const auto& sample=samples[sampleIndex];
|
||||
// The audit and density field use whole control segments, including
|
||||
// the neighboring segments that extend the column through its caps.
|
||||
// Size the column from that same segment window instead of only the
|
||||
// unsupported half-metre samples inside the original gap.
|
||||
if(surfaceWorldUpper)
|
||||
{
|
||||
if(sample.segment<crossing.firstSegment
|
||||
||sample.segment>crossing.lastSegment)continue;
|
||||
}
|
||||
else if(sampleIndex<startSample||sampleIndex>endSample)continue;
|
||||
const auto& point=sample.point;
|
||||
const double dx=point[0]-crossing.centerXSubvoxels;
|
||||
const double dy=point[1]-crossing.centerYSubvoxels;
|
||||
maximumOffsetSubvoxels=std::max(maximumOffsetSubvoxels,
|
||||
DeterministicSqrt(dx*dx+dy*dy));
|
||||
maximumBoreSubvoxels=std::max(maximumBoreSubvoxels,point[3]);
|
||||
if(surfaceWorldUpper)
|
||||
{
|
||||
minimumCrossingZ=std::min(minimumCrossingZ,point[2]);
|
||||
maximumCrossingZ=std::max(maximumCrossingZ,point[2]);
|
||||
}
|
||||
}
|
||||
const double maximumOffsetMetres=maximumOffsetSubvoxels/qPerMetre;
|
||||
const double maximumBoreMetres=maximumBoreSubvoxels/qPerMetre;
|
||||
@@ -3324,12 +3381,10 @@ private:
|
||||
nominalRadiusMetres*qPerMetre));
|
||||
crossing.maximumRadiusSubvoxels=crossing.minimumRadiusSubvoxels
|
||||
+static_cast<std::int64_t>(std::ceil(11.25*qPerMetre));
|
||||
const double startZ=crossing.startPoint[2];
|
||||
const double endZ=crossing.endPoint[2];
|
||||
crossing.topZSubvoxels=static_cast<std::int64_t>(std::llround(
|
||||
std::max(startZ,endZ)+5.0*qPerMetre));
|
||||
maximumCrossingZ+5.0*qPerMetre));
|
||||
crossing.bottomZSubvoxels=static_cast<std::int64_t>(std::llround(
|
||||
std::min(startZ,endZ)-6.0*qPerMetre));
|
||||
minimumCrossingZ-6.0*qPerMetre));
|
||||
}
|
||||
PassageVoidCrossings.push_back(crossing);
|
||||
if(crossing.needsColumn)
|
||||
@@ -3350,7 +3405,7 @@ private:
|
||||
crossing.connectedAtStart&&crossing.connectedAtEnd?"PLANNED":"UNATTACHED");
|
||||
}
|
||||
else
|
||||
std::printf("[PassageShortGroundedSpanPlan] id=%s segments=%zu..%zu span_m=%.3f threshold_m=%.3f connected=%d/%d width_m=6.000 thickness_m=3.000..5.000 status=%s\n",
|
||||
std::printf("[PassageShortGroundedSpanPlan] id=%s segments=%zu..%zu span_m=%.3f threshold_m=%.3f connected=%d/%d width_m=7.300 thickness_m=3.000..5.000 status=%s\n",
|
||||
passage.stableId.c_str(),crossing.firstSegment,crossing.lastSegment,
|
||||
gapLength,shortSpanMaximumMetres,static_cast<int>(crossing.connectedAtStart),
|
||||
static_cast<int>(crossing.connectedAtEnd),
|
||||
@@ -4193,7 +4248,10 @@ public:
|
||||
static_cast<unsigned long long>(S.NearZero),static_cast<unsigned long long>(S.AxisLocked));
|
||||
}
|
||||
const bool bFloorProfilePass=Stats.MaxUnmarkedFloorSlopeDelta<=0.75
|
||||
&&Stats.MaxFloorCurveDeviation<=2.0;
|
||||
&&Stats.MaxFloorProfileEndpointValueError<=0.01
|
||||
&&Stats.MaxFloorProfileEndpointSlopeError<=0.001
|
||||
&&Stats.FloorProfileEndpointStepControlDetected
|
||||
&&Stats.FloorProfileEndpointSlopeControlDetected;
|
||||
return bDetailPass&&bGridPass&&bThinSheetPass&&bFloorProfilePass;
|
||||
}
|
||||
|
||||
@@ -4949,6 +5007,44 @@ private:
|
||||
return Result;
|
||||
}
|
||||
|
||||
static double EvaluateFloorProfileSlope(double A,double B,double TangentA,double TangentB,
|
||||
double Length,double StartBlend,double EndBlend,double S)
|
||||
{
|
||||
if(S<0.0)return TangentA;
|
||||
if(S>Length)return TangentB;
|
||||
const double Secant=(B-A)/Length;
|
||||
double Result=Secant;
|
||||
const auto CorrectionSlope=[](double DeltaSlope,double T)
|
||||
{return DeltaSlope*(1.0-4.0*T+3.0*T*T);};
|
||||
if(S<StartBlend)
|
||||
Result+=CorrectionSlope(TangentA-Secant,S/StartBlend);
|
||||
if(Length-S<EndBlend)
|
||||
Result+=CorrectionSlope(TangentB-Secant,(Length-S)/EndBlend);
|
||||
return Result;
|
||||
}
|
||||
|
||||
void AuditFloorProfileBlendNegativeControls()
|
||||
{
|
||||
constexpr double A=2.0,B=11.0,TangentA=0.35,TangentB=-0.20;
|
||||
constexpr double Length=20.0,StartBlend=7.0,EndBlend=7.0;
|
||||
constexpr double Step=3.0,SlopeStep=0.25;
|
||||
constexpr double ValueThreshold=0.01,SlopeThreshold=0.001;
|
||||
const double endValue=EvaluateFloorProfile(A,B,TangentA,TangentB,
|
||||
Length,StartBlend,EndBlend,Length);
|
||||
const double startSlope=EvaluateFloorProfileSlope(A,B,TangentA,TangentB,
|
||||
Length,StartBlend,EndBlend,0.0);
|
||||
Stats.FloorProfileEndpointStepControlDetected=
|
||||
std::abs((endValue+Step)-B)>ValueThreshold;
|
||||
Stats.FloorProfileEndpointSlopeControlDetected=
|
||||
std::abs((startSlope+SlopeStep)-TangentA)>SlopeThreshold;
|
||||
std::printf("[FloorProfileBlendNegativeControls] endpoint_step=%.3f_vox tangent_step=%.3f_vox_per_m detected=%s/%s status=%s\n",
|
||||
Step,SlopeStep,
|
||||
Stats.FloorProfileEndpointStepControlDetected?"true":"false",
|
||||
Stats.FloorProfileEndpointSlopeControlDetected?"true":"false",
|
||||
Stats.FloorProfileEndpointStepControlDetected
|
||||
&&Stats.FloorProfileEndpointSlopeControlDetected?"PASS":"FAIL");
|
||||
}
|
||||
|
||||
static double FloorSurfaceRelief(double X,double Y,uint32 Seed)
|
||||
{
|
||||
// A shared XY field keeps independently authored floor clips from ending on
|
||||
@@ -5014,6 +5110,16 @@ private:
|
||||
const double Linear=std::lerp(FloorA,FloorB,T);
|
||||
P.FloorCurveMaxDeviation=std::max(P.FloorCurveMaxDeviation,std::abs(Curve-Linear));
|
||||
}
|
||||
P.FloorCurveEndpointValueError=std::max(
|
||||
std::abs(EvaluateFloorProfile(FloorA,FloorB,TangentA,TangentB,Length,
|
||||
P.FloorCurveStartBlend,P.FloorCurveEndBlend,0.0)-FloorA),
|
||||
std::abs(EvaluateFloorProfile(FloorA,FloorB,TangentA,TangentB,Length,
|
||||
P.FloorCurveStartBlend,P.FloorCurveEndBlend,Length)-FloorB));
|
||||
P.FloorCurveEndpointSlopeError=std::max(
|
||||
std::abs(EvaluateFloorProfileSlope(FloorA,FloorB,TangentA,TangentB,Length,
|
||||
P.FloorCurveStartBlend,P.FloorCurveEndBlend,0.0)-TangentA),
|
||||
std::abs(EvaluateFloorProfileSlope(FloorA,FloorB,TangentA,TangentB,Length,
|
||||
P.FloorCurveStartBlend,P.FloorCurveEndBlend,Length)-TangentB));
|
||||
}
|
||||
|
||||
double FloorAt(const FPrimitive& P,double X,double Y) const
|
||||
@@ -5224,6 +5330,12 @@ private:
|
||||
++Stats.CurvedFloorSegments;
|
||||
Stats.MaxFloorCurveDeviation=std::max(Stats.MaxFloorCurveDeviation,
|
||||
Primitives.back().FloorCurveMaxDeviation);
|
||||
Stats.MaxFloorProfileEndpointValueError=std::max(
|
||||
Stats.MaxFloorProfileEndpointValueError,
|
||||
Primitives.back().FloorCurveEndpointValueError);
|
||||
Stats.MaxFloorProfileEndpointSlopeError=std::max(
|
||||
Stats.MaxFloorProfileEndpointSlopeError,
|
||||
Primitives.back().FloorCurveEndpointSlopeError);
|
||||
}
|
||||
FitCapsuleRadiiAtEndpoints(Primitives.back(),Points[I],R0,Points[I+1],R1);
|
||||
SetCapsuleRadiusProfile(Primitives.back(),Points[I],R0,RadiusSlope0,
|
||||
@@ -5348,6 +5460,12 @@ private:
|
||||
++Stats.CurvedFloorSegments;
|
||||
Stats.MaxFloorCurveDeviation=std::max(Stats.MaxFloorCurveDeviation,
|
||||
Primitives.back().FloorCurveMaxDeviation);
|
||||
Stats.MaxFloorProfileEndpointValueError=std::max(
|
||||
Stats.MaxFloorProfileEndpointValueError,
|
||||
Primitives.back().FloorCurveEndpointValueError);
|
||||
Stats.MaxFloorProfileEndpointSlopeError=std::max(
|
||||
Stats.MaxFloorProfileEndpointSlopeError,
|
||||
Primitives.back().FloorCurveEndpointSlopeError);
|
||||
FitCapsuleRadiiAtEndpoints(Primitives.back(),Points[I],RadiusA,Points[I+1],RadiusB);
|
||||
SetCapsuleRadiusProfile(Primitives.back(),Points[I],RadiusA,RadiusSlopeA,
|
||||
Points[I+1],RadiusB,RadiusSlopeB);
|
||||
|
||||
@@ -83,6 +83,12 @@ Costs below are design estimates, not timings. Low means bounded analytic featur
|
||||
|
||||
A passage planner must produce a traversable route or pair a non-walkable visual passage with another guaranteed route. A narrow crack or drop cannot be assumed walkable because its centerline is air.
|
||||
|
||||
For a SurfaceWorld strate, the supported arrival-to-departure route follows the sampled terrain
|
||||
ground and stays within the mouth placement bounds. The inter-strate departure passage owns the
|
||||
descent from that ground mouth through rock and bedrock. Treating the bottom of a tall SurfaceWorld
|
||||
strate as the route's departure height would ask the walk planner to cross open terrain air; it is
|
||||
the wrong endpoint for the intra-strate route.
|
||||
|
||||
| Passage type | Planner output | Bound and likely cost |
|
||||
|---|---|---|
|
||||
| SlopedTunnel | Straight-style path with switchbacks as needed, capsule clearance, supported floor, maximum grade and landing geometry. | Swept capsule bounds plus floor envelope; low–medium. |
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build owner-like FloatingIslands -> SurfaceWorld -> TunnelNetwork fixtures."""
|
||||
import copy
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
SOURCE = ROOT / "Standalone/data/owner_world.json"
|
||||
OUT = ROOT / "Saved/Linux/l33/fixtures"
|
||||
|
||||
|
||||
def make_fixture(style: int) -> Path:
|
||||
world = json.loads(SOURCE.read_text(encoding="utf-8"))
|
||||
source_tunnel, source_surface = world["strates"]
|
||||
|
||||
floating = copy.deepcopy(source_tunnel)
|
||||
floating.update(index=0, top_chunk_z=0, bottom_chunk_z=-15, height_chunks=16)
|
||||
floating["asset"]["StrateName"] = "Linux33 Upper FloatingIslands"
|
||||
floating["asset"]["StrateHeightInChunks"] = 16
|
||||
floating["asset"]["GeneratorType"] = 6
|
||||
|
||||
surface = copy.deepcopy(source_surface)
|
||||
surface.update(index=1, top_chunk_z=-16, bottom_chunk_z=-79, height_chunks=64)
|
||||
surface["asset"]["StrateName"] = "Linux33 Middle SurfaceWorld"
|
||||
surface["asset"]["StrateHeightInChunks"] = 64
|
||||
surface["asset"]["PassageConfig"]["Style"] = style
|
||||
surface["passage_config"]["Style"] = style
|
||||
|
||||
tunnel = copy.deepcopy(source_tunnel)
|
||||
tunnel.update(index=2, top_chunk_z=-80, bottom_chunk_z=-143, height_chunks=64)
|
||||
tunnel["asset"]["StrateName"] = "Linux33 Lower TunnelNetwork"
|
||||
tunnel["asset"]["StrateHeightInChunks"] = 64
|
||||
|
||||
world["strates"] = [floating, surface, tunnel]
|
||||
world["total_strates"] = 3
|
||||
world["settings_asset"]["TotalStrates"] = 3
|
||||
world["settings_asset"]["StratePool"] = [
|
||||
"/Standalone/Linux33/UpperFloatingIslands",
|
||||
"/Standalone/Linux33/MiddleSurfaceWorld",
|
||||
"/Standalone/Linux33/LowerTunnelNetwork",
|
||||
]
|
||||
world["passages"] = [] # The L23 legacy coordinates are not part of this resolved stack.
|
||||
world["source"] = "VoxelForge Linux33 owner-like SurfaceWorld-upper fixture"
|
||||
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
path = OUT / f"sw-upper-tunnel-network-style{style}.json"
|
||||
path.write_text(json.dumps(world, separators=(",", ":")), encoding="utf-8")
|
||||
return path
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for style in range(4):
|
||||
path = make_fixture(style)
|
||||
print(f"style={style} fixture={path}")
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Export and render two bounded SurfaceWorld departure views."""
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l33"
|
||||
FIXTURE = OUT / "fixtures/sw-upper-tunnel-network-style0.json"
|
||||
HEADLESS = OUT / "bin/VoxelForgeCoreHeadless-clang-l33-final"
|
||||
CAPTURE = OUT / "bin/VoxelForgeOffscreenCapture-clang-l33-final"
|
||||
PNG_DIR = OUT / "capture-png"
|
||||
DEST = Path(__file__).resolve().parent / "captures"
|
||||
PNG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
DEST.mkdir(parents=True, exist_ok=True)
|
||||
env = os.environ.copy()
|
||||
env["LP_NUM_THREADS"] = "12"
|
||||
env["XDG_CACHE_HOME"] = str(OUT / "xdg-cache")
|
||||
|
||||
views = (
|
||||
{
|
||||
"name": "sw-departure-mouth",
|
||||
"export_camera": "18.75,-12,-499,0,-22",
|
||||
"render_camera": "18.75,0,-490",
|
||||
"yaw": "0",
|
||||
"pitch": "-90",
|
||||
"fov": "38",
|
||||
"height_center": "-503.166",
|
||||
"height_range": "4",
|
||||
"light": "0,-1,0.2",
|
||||
"headlamp": "off",
|
||||
"clip": None,
|
||||
},
|
||||
{
|
||||
"name": "sw-departure-rock-entry",
|
||||
"export_camera": "26,-16,-499,64,-16",
|
||||
"render_camera": "14,-4,-500",
|
||||
"yaw": "78",
|
||||
"pitch": "-16",
|
||||
"fov": "32",
|
||||
"height_center": "-503.166",
|
||||
"height_range": "4",
|
||||
"light": "0,-1,0.2",
|
||||
"headlamp": "off",
|
||||
"clip": "1,0,0,-18.75",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def run(name, command):
|
||||
logfile = OUT / f"capture-{name}.log"
|
||||
wrapped = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1800s", *command,
|
||||
]
|
||||
started = time.monotonic()
|
||||
with logfile.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(wrapped, cwd=ROOT, env=env, stdout=stream,
|
||||
stderr=subprocess.STDOUT, timeout=1800, check=False)
|
||||
print(f"[L33CaptureRun] step={name} elapsed={time.monotonic()-started:.1f}s "
|
||||
f"exit={result.returncode} log={logfile}", flush=True)
|
||||
if result.returncode != 0:
|
||||
raise SystemExit(f"{name} failed; see {logfile}")
|
||||
|
||||
|
||||
for view in views:
|
||||
name = view["name"]
|
||||
mesh = OUT / f"{name}.vfmesh"
|
||||
png = PNG_DIR / f"{name}.png"
|
||||
jpg = DEST / f"{name}.jpg"
|
||||
run(f"{name}-export", [
|
||||
str(HEADLESS), str(FIXTURE), "--workers=12", "--world-assembler-prototype",
|
||||
"--stream-benchmark", "--stream-pipeline", "--force-generate", "--clip-radius=2",
|
||||
"--max-clip-level=0", "--max-level=0", "--render-distance-chunks=0",
|
||||
"--recipe=floating-islands",
|
||||
f"--camera={view['export_camera']}", f"--mesh-export={mesh}",
|
||||
])
|
||||
command = [
|
||||
str(CAPTURE), str(mesh), str(png), "--size=1600x900", f"--fov-deg={view['fov']}",
|
||||
f"--camera-m={view['render_camera']}", f"--yaw-deg={view['yaw']}",
|
||||
f"--pitch-deg={view['pitch']}", "--shade=readable",
|
||||
f"--headlamp={view['headlamp']}", f"--light-direction={view['light']}",
|
||||
"--height-tint=on", f"--height-center-m={view['height_center']}",
|
||||
f"--height-range-m={view['height_range']}", "--tile-edges=off",
|
||||
]
|
||||
if view["clip"]:
|
||||
command.append(f"--clip-plane={view['clip']}")
|
||||
run(f"{name}-render", command)
|
||||
run(f"{name}-jpeg", [
|
||||
"ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error", "-y",
|
||||
"-i", str(png), "-frames:v", "1", "-q:v", "4", str(jpg),
|
||||
])
|
||||
size = jpg.stat().st_size if jpg.exists() else 0
|
||||
ok = 0 < size <= 300_000
|
||||
print(f"[L33CaptureJPEG] file={jpg.relative_to(ROOT)} bytes={size} "
|
||||
f"status={'PASS' if ok else 'FAIL'}", flush=True)
|
||||
if not ok:
|
||||
raise SystemExit(f"{jpg.name} is missing or exceeds 300 KB")
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check representative owner hashes against L32 before the full matrix."""
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l33"
|
||||
L32 = ROOT / "Saved/Linux/l32"
|
||||
BINARIES = {
|
||||
"clang": OUT / "bin/VoxelForgeCoreHeadless-clang-l33-final",
|
||||
"gcc": OUT / "bin/VoxelForgeCoreHeadless-gcc-l33-final",
|
||||
}
|
||||
HASH_PATTERN = re.compile(
|
||||
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M)
|
||||
failed = False
|
||||
|
||||
for compiler, binary in BINARIES.items():
|
||||
for recipe in ("surface-world", "floating-islands"):
|
||||
name = f"owner-hash-preflight-{compiler}-{recipe}-w12"
|
||||
logfile = OUT / f"{name}.log"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
str(binary), str(ROOT / "Standalone/data/owner_world.json"),
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--stream-hash", "--force-generate", "--clip-radius=3", "--max-clip-level=8",
|
||||
"--far-horizon-lod8-step=0", "--workers=12", f"--recipe={recipe}",
|
||||
]
|
||||
started = time.monotonic()
|
||||
try:
|
||||
with logfile.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(command, stdout=stream, stderr=subprocess.STDOUT,
|
||||
timeout=900, check=False)
|
||||
except subprocess.TimeoutExpired:
|
||||
result = None
|
||||
text = logfile.read_text(encoding="utf-8", errors="replace")
|
||||
hashes = HASH_PATTERN.findall(text)
|
||||
baseline_path = L32 / f"l25-settled-final-{compiler}-{recipe}-w12.log"
|
||||
baseline = HASH_PATTERN.findall(baseline_path.read_text(
|
||||
encoding="utf-8", errors="replace")) if baseline_path.exists() else []
|
||||
stamps = re.findall(
|
||||
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$",
|
||||
text, re.M)
|
||||
same = len(hashes) == 12 and hashes == baseline
|
||||
ok = (result is not None and result.returncode == 0 and same
|
||||
and stamps == [(recipe, recipe, "PASS")])
|
||||
failed |= not ok
|
||||
print(f"{name} elapsed={time.monotonic()-started:.1f}s hashes={len(hashes)}/12 "
|
||||
f"same_as_l32={'yes' if same else 'no'} "
|
||||
f"{'PASS' if ok else 'FAIL'}", flush=True)
|
||||
|
||||
raise SystemExit(1 if failed else 0)
|
||||
@@ -0,0 +1,58 @@
|
||||
# Linux33 report
|
||||
|
||||
## Upper SurfaceWorld route
|
||||
|
||||
The old fixed candidate set used 64 m chords, 2 m waypoint spacing and 16 m centre spacing. On the refreshed upper-stack fixture, the legacy replay measured 366/648 candidates above the 15° grade limit; its worst segment was 79.971° at XY (−84,−48) to (−82,−48). These were long paths over steep terrain relief near the outer search area. L23’s earlier fixture reported 648/648 grade rejects; the refreshed fixture gives a different count. The route Z samples are terrain-ground heights (about −503 m at the chosen mouth), not a target at the bottom of the 499 m strate. There was no unit or bedrock-reference error.
|
||||
|
||||
The planner now caps chords at 58.5 m for the 24 m arrival and 37.5 m departure radii, searches six lengths and places route centres on an 8 m grid, with a proximity cost favouring the origin. It found a 21.5 m walk from (−2.75,0) to departure (18.75,0), average grade about 4.2°. The SW passage starts at the ground floor (−503.166 m), with a 1.5 m portal blending to a 3.5 m tunnel over 9 m, then descends through SW rock. The first tunnel point is at (32.5,0), floor −505.637 m for style 0.
|
||||
|
||||
## Upper-stack fixture audits
|
||||
|
||||
Fixture: FloatingIslands → SurfaceWorld → TunnelNetwork; recipe stamp `floating-islands`; layout identity `resolved-layout-v3-fnv64:fc4e73619037f88c`. Clang and GCC passed every row for every style.
|
||||
|
||||
| Passage style | Floor chord deviation (voxels) | Passage and route audits | Open air | Naturalness | Bridge / columns | Bindings / walkability |
|
||||
|---:|---:|---|---|---|---|---|
|
||||
| 0 | 2.081 | 2/2 each pass | 0 violations | 2/2 pass | pass / 0 failures | 0 failures / pass |
|
||||
| 1 | 10.901 | 2/2 each pass | 0 violations | 2/2 pass | pass / 0 failures | 0 failures / pass |
|
||||
| 2 | 14.822 | 2/2 each pass | 0 violations | 2/2 pass | pass / 0 failures | 0 failures / pass |
|
||||
| 3 | 16.374 | 2/2 each pass | 0 violations | 2/2 pass | pass / 0 failures | 0 failures / pass |
|
||||
|
||||
All rows are recipe-stamped and passed on both compilers. Capsule blocking, unsupported samples and grade failures were zero; mouth-content and mouth-radius checks were 2/2. This includes open-air, naturalness, no-long-bridge, rock-column wall/support, route binding and walkability audits.
|
||||
|
||||
## FloorProfileBlend
|
||||
|
||||
The old 2-voxel check measured maximum curve-to-straight-chord deviation, which grows with valid curvature and does not measure a seam. The check now compares endpoint height and tangent residuals. All styles have 0.00000000 voxel height residual and 0.00000000 voxel/m tangent residual (limits 0.010 and 0.001). Negative controls detected a 3-voxel step and a 0.25 voxel/m tangent step. Verdict: check corrected; no floor geometry change was needed.
|
||||
|
||||
## Captures
|
||||
|
||||
Both 1600×900 JPEGs are under 300 KB. The first shows the SW ground mouth; the second is a cutaway through the rock entry.
|
||||
|
||||
Sizes and SHA-256: mouth 26,939 B, `561d3a1e334ea3a3115fb781a1130ec850d8777b74c1be3704c70b865878640a`; rock entry 37,572 B, `f85d150bf44cd2cdae1ee49de023ad9e4c657b8fa7e8d8f8b93abc86586ad6e5`.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Hashes, determinism and cost
|
||||
|
||||
Owner-world hashes match L32 exactly: all 12 hash lines per run across Clang/GCC and both recipes passed the owner preflight; the L25 and 24 m crossing matrix also passed at W1/W8/W12 (24/24 owner rows). W12 whole-view hashes: SurfaceWorld density `953eb1df1705f277`, mesh `d8006cb43f44aa5b`, payload `9217bff766970de5`, topology `83e3aa6f3a063e60`; FloatingIslands density `806537cdd98f8afb`, mesh `a4b06dd4d260943d`, payload `763a99aa04a73eae`, topology `baa37d80d3620d87`. The upper fixture was stable across W1/W8/W12 on both compilers. Total determinism: 30/30. Invariant audits: 16/16.
|
||||
|
||||
Five interleaved W12 cost pairs per recipe against L32: median whole-view ready time changed +0.99% for SurfaceWorld and +0.41% for FloatingIslands; paired medians were +0.57% and +0.41%, within run noise. Recipe/evaluator versions remain 2/20. No Unreal files changed; no Unreal build was run. No version bump was needed because the owner hashes are unchanged.
|
||||
|
||||
## Failed and retried runs
|
||||
|
||||
- CLI setup rejected `--compiler clang`; the accepted form was `--compiler=clang++-19`. One shell quoting attempt ended at EOF. A FloatingIslands fixture initially had a `surface-world` recipe stamp and was regenerated with `floating-islands`.
|
||||
- A translated ring centre failed the strict placement disk and was reverted. Fixture audit iterations: r1 had style 1–3 failures (TN capsule pinch, route undercut, naturalness); r2 had 141 open-air violations plus route/naturalness failures; r3 had strict ring-placement and route-support failures; r4 had four edge open-air samples. The broad column-window fix passed passage audits but changed owner hashes. Its first scoped retry dropped supported bracket samples and failed SurfaceWorld hash preflight on both compilers; restoring the original non-SW bracket bounds fixed it.
|
||||
- An intermediate Clang build failed on `coreVoxelSizeMetres` and `gradeSegments`; both names were corrected. Final Clang 19 and GCC 14 `--fp-contract=off`, `-j12` builds succeeded. An early determinism run on stale binaries was stopped after 21 rows and showed the interim hash mismatch; the final binaries passed all 30 rows.
|
||||
- The first capture export included the far ring and grew to 2.49 GB; it was stopped before rendering. The LOD0 streamed export reduced the scene to about 2.2 MB. Capture framings r5–r8 rendered successfully but obscured the mouth; r9 is the inspected final framing.
|
||||
|
||||
No run exceeded 30 minutes and no step remained stuck for 15 minutes. Logs and exports are in `Saved/Linux/l33/`; no commit, push or stash was made.
|
||||
|
||||
## Orchestrator review (Claude)
|
||||
|
||||
- Merged: the owner world is unchanged (hashes equal L32); SW-as-upper now plans its route and passes the full audit set
|
||||
on the FI→SW→TN fixture. The FloorProfileBlend check now measures seams (endpoint height/tangent residuals) with
|
||||
step/tangent negative controls. Accepted: chord deviation measured curvature, not a defect.
|
||||
- Captures: `sw-departure-mouth.jpg` is out of focus and unreadable; `sw-departure-rock-entry.jpg` shows a natural opening
|
||||
but **horizontal step-like ledges inside**, reminiscent of the L23 terraces. L34 checks passage mouths and entries for
|
||||
stepping across styles and fixes the blurry capture setup.
|
||||
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run five interleaved W12 whole-view pairs against the L32 binary."""
|
||||
import re
|
||||
import statistics
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l33"
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
BINARIES = {
|
||||
"l32": ROOT / "Saved/Linux/l32/bin/VoxelForgeCoreHeadless-l32-clang-v20",
|
||||
"l33": OUT / "bin/VoxelForgeCoreHeadless-clang-l33-final",
|
||||
}
|
||||
ORDERS = (("l32", "l33"), ("l33", "l32"), ("l32", "l33"),
|
||||
("l33", "l32"), ("l32", "l33"))
|
||||
READY = re.compile(r"\bwhole_view_ready=([0-9.]+)s")
|
||||
rows = []
|
||||
failed = False
|
||||
|
||||
|
||||
for recipe in ("surface-world", "floating-islands"):
|
||||
for pair, order in enumerate(ORDERS, 1):
|
||||
for build in order:
|
||||
name = f"cost-{recipe}-pair{pair}-{build}"
|
||||
logfile = OUT / f"{name}.log"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
str(BINARIES[build]), str(ROOT / "Standalone/data/owner_world.json"),
|
||||
"--workers=12", "--world-assembler-prototype", "--stream-benchmark",
|
||||
"--stream-pipeline", "--force-generate", "--clip-radius=3",
|
||||
"--max-clip-level=8", "--max-level=-1", f"--recipe={recipe}",
|
||||
]
|
||||
started = time.monotonic()
|
||||
try:
|
||||
with logfile.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(command, stdout=stream, stderr=subprocess.STDOUT,
|
||||
timeout=900, check=False)
|
||||
except subprocess.TimeoutExpired:
|
||||
result = None
|
||||
elapsed = time.monotonic() - started
|
||||
text = logfile.read_text(encoding="utf-8", errors="replace")
|
||||
matches = READY.findall(text)
|
||||
stamps = re.findall(
|
||||
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$",
|
||||
text, re.M)
|
||||
stamp_ok = stamps == [(recipe, recipe, "PASS")]
|
||||
valid = (result is not None and result.returncode == 0 and len(matches) == 1
|
||||
and "hash=off" in text and "cache_hits=0" in text and stamp_ok)
|
||||
ready = float(matches[0]) if len(matches) == 1 else float("nan")
|
||||
rows.append((recipe, pair, build, ready, elapsed,
|
||||
result.returncode if result else "TIMEOUT", valid))
|
||||
print(f"{recipe} pair={pair} build={build} whole_view_ready={ready:.3f}s "
|
||||
f"process_elapsed={elapsed:.1f}s {'PASS' if valid else 'FAIL'}", flush=True)
|
||||
failed |= not valid
|
||||
|
||||
summary = OUT / "cost-runs.csv"
|
||||
summary.write_text(
|
||||
"recipe,pair,build,whole_view_ready_seconds,process_elapsed_seconds,exit_code,status\n" +
|
||||
"".join(f"{recipe},{pair},{build},{ready:.6f},{elapsed:.3f},{code},"
|
||||
f"{'PASS' if ok else 'FAIL'}\n"
|
||||
for recipe, pair, build, ready, elapsed, code, ok in rows), encoding="utf-8")
|
||||
|
||||
for recipe in ("surface-world", "floating-islands"):
|
||||
by_build = {build: [row[3] for row in rows if row[0] == recipe and row[2] == build]
|
||||
for build in BINARIES}
|
||||
baseline = statistics.median(by_build["l32"])
|
||||
current = statistics.median(by_build["l33"])
|
||||
paired = []
|
||||
for pair in range(1, 6):
|
||||
values = {row[2]: row[3] for row in rows if row[0] == recipe and row[1] == pair}
|
||||
paired.append((values["l33"] - values["l32"]) / values["l32"] * 100.0)
|
||||
print(f"[L33CostSummary] recipe={recipe} pairs=5 median_l32_s={baseline:.3f} "
|
||||
f"median_l33_s={current:.3f} median_change_pct={(current/baseline-1)*100.0:+.2f} "
|
||||
f"paired_change_pct={','.join(f'{value:+.2f}' for value in paired)}", flush=True)
|
||||
|
||||
print(f"[L33Cost] runs={len(rows)} status={'FAIL' if failed else 'PASS'} summary={summary}",
|
||||
flush=True)
|
||||
raise SystemExit(1 if failed or len(rows) != 20 else 0)
|
||||
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run owner L25 hashes, 24 m crossings, and upper-SurfaceWorld fixture determinism."""
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l33"
|
||||
L32 = ROOT / "Saved/Linux/l32"
|
||||
FIXTURE = OUT / "fixtures/sw-upper-tunnel-network-style0.json"
|
||||
BINARIES = {
|
||||
"clang": OUT / "bin/VoxelForgeCoreHeadless-clang-l33-final",
|
||||
"gcc": OUT / "bin/VoxelForgeCoreHeadless-gcc-l33-final",
|
||||
}
|
||||
HASH_PATTERN = re.compile(
|
||||
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M)
|
||||
LAYOUT_PATTERN = re.compile(
|
||||
r"^\[ResolvedLayoutSnapshot\] schema=\d+ stable_identity=([^ ]+) strata=\d+ bytes=\d+ round_trip=PASS$",
|
||||
re.M)
|
||||
PLAN_PATTERN = re.compile(
|
||||
r"^\[WorldAssemblerPlanSerialization\].*canonical_round_trip=true .*"
|
||||
r"plan_hash=([0-9a-f]+) manifest_hash=([0-9a-f]+) status=PASS$", re.M)
|
||||
ROUTE_PATTERN = re.compile(
|
||||
r"^\[WorldAssemblerRouteAccepted\] arrival_xy_m=\([^)]*\) "
|
||||
r"departure_xy_m=\([^)]*\) endpoint_proximity_cost=\d+ average_grade_q16=\d+$", re.M)
|
||||
rows = []
|
||||
failed = False
|
||||
owner_reference = {}
|
||||
fixture_reference = None
|
||||
|
||||
|
||||
def run(command, logfile):
|
||||
started = time.monotonic()
|
||||
try:
|
||||
with logfile.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(command, stdout=stream, stderr=subprocess.STDOUT,
|
||||
timeout=1800, check=False)
|
||||
except subprocess.TimeoutExpired:
|
||||
result = None
|
||||
return result, time.monotonic() - started, logfile.read_text(
|
||||
encoding="utf-8", errors="replace")
|
||||
|
||||
|
||||
for kind in ("l25", "crossing24"):
|
||||
for recipe in ("surface-world", "floating-islands"):
|
||||
for compiler, binary in BINARIES.items():
|
||||
for workers in (1, 8, 12):
|
||||
name = f"{kind}-{compiler}-{recipe}-w{workers}"
|
||||
logfile = OUT / f"determinism-{name}.log"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
str(binary), str(ROOT / "Standalone/data/owner_world.json"),
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--stream-hash", "--force-generate", "--clip-radius=3",
|
||||
"--max-clip-level=8", "--far-horizon-lod8-step=0",
|
||||
f"--workers={workers}", f"--recipe={recipe}",
|
||||
]
|
||||
if kind == "crossing24":
|
||||
command.extend(("--camera-step=24,0,0", "--region-crossing"))
|
||||
result, elapsed, text = run(command, logfile)
|
||||
hashes = HASH_PATTERN.findall(text)
|
||||
stamps = re.findall(
|
||||
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$",
|
||||
text, re.M)
|
||||
ref = L32 / f"{kind}-settled-final-{compiler}-{recipe}-w{workers}.log"
|
||||
reference_hashes = HASH_PATTERN.findall(ref.read_text(
|
||||
encoding="utf-8", errors="replace")) if ref.exists() else []
|
||||
stable_key = (kind, recipe)
|
||||
stable = stable_key not in owner_reference or hashes == owner_reference[stable_key]
|
||||
if stable_key not in owner_reference and hashes:
|
||||
owner_reference[stable_key] = hashes
|
||||
equal_l32 = len(hashes) == 12 and hashes == reference_hashes
|
||||
ok = (result is not None and result.returncode == 0
|
||||
and stamps == [(recipe, recipe, "PASS")]
|
||||
and "hash=on" in text and len(hashes) == 12 and stable and equal_l32)
|
||||
rows.append((kind, recipe, compiler, workers, elapsed,
|
||||
result.returncode if result else "TIMEOUT", ok))
|
||||
print(f"{name} elapsed={elapsed:.1f}s hashes={len(hashes)}/12 "
|
||||
f"same_across_matrix={'yes' if stable else 'no'} "
|
||||
f"same_as_l32={'yes' if equal_l32 else 'no'} {'PASS' if ok else 'FAIL'}",
|
||||
flush=True)
|
||||
failed |= not ok
|
||||
|
||||
|
||||
for compiler, binary in BINARIES.items():
|
||||
for workers in (1, 8, 12):
|
||||
name = f"upper-fixture-{compiler}-w{workers}"
|
||||
logfile = OUT / f"determinism-{name}.log"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
str(binary), str(FIXTURE), "--workers=" + str(workers),
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--force-generate", "--clip-radius=3", "--max-clip-level=8",
|
||||
"--max-level=-1", "--recipe=floating-islands", "--audit-resolved-layout-passages",
|
||||
]
|
||||
result, elapsed, text = run(command, logfile)
|
||||
layouts = LAYOUT_PATTERN.findall(text)
|
||||
plans = PLAN_PATTERN.findall(text)
|
||||
routes = ROUTE_PATTERN.findall(text)
|
||||
stamps = re.findall(
|
||||
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$",
|
||||
text, re.M)
|
||||
passage_summary = re.search(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", text, re.M)
|
||||
signature = (tuple(layouts), tuple(plans), tuple(routes))
|
||||
stable = fixture_reference is None or signature == fixture_reference
|
||||
if fixture_reference is None and layouts and plans and routes:
|
||||
fixture_reference = signature
|
||||
ok = (result is not None and result.returncode == 0 and len(layouts) == 1
|
||||
and len(plans) == 1 and len(routes) == 1
|
||||
and stamps == [("floating-islands", "floating-islands", "PASS")]
|
||||
and passage_summary is not None and stable)
|
||||
rows.append(("upper-fixture", "floating-islands", compiler, workers, elapsed,
|
||||
result.returncode if result else "TIMEOUT", ok))
|
||||
print(f"{name} elapsed={elapsed:.1f}s layout={layouts[0] if layouts else 'MISSING'} "
|
||||
f"stable_across_matrix={'yes' if stable else 'no'} {'PASS' if ok else 'FAIL'}",
|
||||
flush=True)
|
||||
failed |= not ok
|
||||
|
||||
|
||||
summary = OUT / "determinism-l33.csv"
|
||||
summary.write_text(
|
||||
"kind,recipe,compiler,workers,elapsed_seconds,exit_code,status\n" +
|
||||
"".join(f"{kind},{recipe},{compiler},{workers},{elapsed:.3f},{code},"
|
||||
f"{'PASS' if ok else 'FAIL'}\n"
|
||||
for kind, recipe, compiler, workers, elapsed, code, ok in rows),
|
||||
encoding="utf-8")
|
||||
print(f"[L33Determinism] runs={len(rows)} pass={sum(row[-1] for row in rows)} "
|
||||
f"fail={sum(not row[-1] for row in rows)} summary={summary}", flush=True)
|
||||
raise SystemExit(1 if failed or len(rows) != 30 else 0)
|
||||
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run L33 route/proof and transition invariants with recipe-stamped logs."""
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l33"
|
||||
BINARIES = {
|
||||
"clang": OUT / "bin/VoxelForgeCoreHeadless-clang-l33-final",
|
||||
"gcc": OUT / "bin/VoxelForgeCoreHeadless-gcc-l33-final",
|
||||
}
|
||||
rows = []
|
||||
failed = False
|
||||
|
||||
|
||||
def run(name, binary, data, recipe, extra, checks):
|
||||
global failed
|
||||
logfile = OUT / f"{name}.log"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
str(binary), str(data), "--workers=12", "--world-assembler-prototype",
|
||||
"--stream-benchmark", "--stream-pipeline", "--force-generate", "--clip-radius=3",
|
||||
"--max-clip-level=8", "--max-level=-1", f"--recipe={recipe}", *extra,
|
||||
]
|
||||
started = time.monotonic()
|
||||
try:
|
||||
with logfile.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(command, stdout=stream, stderr=subprocess.STDOUT,
|
||||
timeout=900, check=False)
|
||||
except subprocess.TimeoutExpired:
|
||||
result = None
|
||||
elapsed = time.monotonic() - started
|
||||
text = logfile.read_text(encoding="utf-8", errors="replace")
|
||||
stamps = re.findall(
|
||||
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$",
|
||||
text, re.M)
|
||||
stamp_ok = stamps == [(recipe, recipe, "PASS")]
|
||||
checks_ok = all(check(text) for check in checks)
|
||||
ok = result is not None and result.returncode == 0 and stamp_ok and checks_ok
|
||||
rows.append((name, recipe, elapsed, result.returncode if result else "TIMEOUT", ok))
|
||||
print(f"{name} elapsed={elapsed:.1f}s recipe={stamps[0][1] if stamps else 'MISSING'} "
|
||||
f"stamp={'PASS' if stamp_ok else 'FAIL'} checks={'PASS' if checks_ok else 'FAIL'} "
|
||||
f"{'PASS' if ok else 'FAIL'}", flush=True)
|
||||
failed |= not ok
|
||||
|
||||
|
||||
owner = ROOT / "Standalone/data/owner_world.json"
|
||||
for recipe in ("surface-world", "floating-islands"):
|
||||
for compiler, binary in BINARIES.items():
|
||||
name = f"invariant-route-proof-{recipe}-{compiler}"
|
||||
run(name, binary, owner, recipe,
|
||||
["--stream-hash", "--audit-uniform-block-proofs", "--audit-route-parity"],
|
||||
[lambda text: bool(re.search(
|
||||
r"^\[RouteParityAudit\].*status=PASS$", text, re.M)),
|
||||
lambda text: bool(re.search(
|
||||
r"^\[UniformBlockProofAudit\].*violations=0(?:\s|$)", text, re.M))])
|
||||
|
||||
for recipe in ("surface-world", "floating-islands"):
|
||||
for compiler, binary in BINARIES.items():
|
||||
for mode in ("hard", "gradient", "interleaved"):
|
||||
name = f"invariant-transition-{recipe}-{mode}-{compiler}"
|
||||
run(name, binary, owner, recipe,
|
||||
[f"--transition-mode={mode}", "--audit-strate-transitions"],
|
||||
[lambda text: bool(re.search(
|
||||
r"^\[StrateTransitionAuditSummary\].*hole_columns=0 air_samples=0 .*status=PASS$",
|
||||
text, re.M)),
|
||||
lambda text: bool(re.search(
|
||||
r"^\[StrateTransitionNegativeControls\].*status=PASS$", text, re.M))])
|
||||
|
||||
summary = OUT / "invariant-audits.csv"
|
||||
summary.write_text(
|
||||
"run,recipe,elapsed_seconds,exit_code,status\n" +
|
||||
"".join(f"{name},{recipe},{elapsed:.3f},{code},{'PASS' if ok else 'FAIL'}\n"
|
||||
for name, recipe, elapsed, code, ok in rows),
|
||||
encoding="utf-8")
|
||||
print(f"[L33InvariantAudits] runs={len(rows)} pass={sum(row[-1] for row in rows)} "
|
||||
f"fail={sum(not row[-1] for row in rows)} summary={summary}", flush=True)
|
||||
raise SystemExit(1 if failed or len(rows) != 16 else 0)
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Recipe-stamped resolved-passage audits for the L33 upper-SurfaceWorld stack."""
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l33"
|
||||
TAG = os.environ.get("VOXELFORGE_L33_AUDIT_TAG", "clang").strip() or "clang"
|
||||
BINARY = Path(os.environ.get("VOXELFORGE_L33_AUDIT_BINARY",
|
||||
str(OUT / f"bin/VoxelForgeCoreHeadless-{TAG}-l33-final")))
|
||||
ROWS = []
|
||||
FAILED = False
|
||||
|
||||
for style in range(4):
|
||||
name = f"upper-sw-three-strata-{TAG}-style{style}"
|
||||
fixture = OUT / f"fixtures/sw-upper-tunnel-network-style{style}.json"
|
||||
logfile = OUT / f"{name}.log"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
str(BINARY), str(fixture), "--workers=12", "--world-assembler-prototype",
|
||||
"--stream-benchmark", "--stream-pipeline", "--force-generate",
|
||||
"--clip-radius=3", "--max-clip-level=8", "--max-level=-1",
|
||||
"--recipe=floating-islands", "--audit-resolved-layout-passages",
|
||||
]
|
||||
started = time.monotonic()
|
||||
try:
|
||||
with logfile.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(command, stdout=stream, stderr=subprocess.STDOUT,
|
||||
timeout=1800, check=False)
|
||||
except subprocess.TimeoutExpired:
|
||||
result = None
|
||||
elapsed = time.monotonic() - started
|
||||
text = logfile.read_text(encoding="utf-8", errors="replace")
|
||||
stamp_ok = re.findall(
|
||||
r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=([^ ]+)$",
|
||||
text, re.M) == [("floating-islands", "floating-islands", "PASS")]
|
||||
summary = re.search(r"^\[ResolvedPassageAuditSummary\].*$", text, re.M)
|
||||
summary_ok = bool(summary and "interfaces=2 passed=2 natural=2/2" in summary.group()
|
||||
and "route_walkable=2" in summary.group()
|
||||
and "route_binding_failures=0" in summary.group()
|
||||
and "status=PASS" in summary.group())
|
||||
markers = (
|
||||
r"^\[PassageOpenAirAuditSummary\].*violations=0.*status=PASS$",
|
||||
r"^\[PassageNoLongBridgeAudit\].*status=PASS$",
|
||||
r"^\[PassageRockColumnAuditSummary\].*wall_or_support_failures=0.*status=PASS$",
|
||||
r"^\[PassageNaturalnessSummary\].*status=PASS$",
|
||||
)
|
||||
markers_ok = all(re.search(pattern, text, re.M) for pattern in markers)
|
||||
passage_rows = re.findall(
|
||||
r"^\[ResolvedPassageAudit\].*blocked=0 support_samples=\d+ unsupported=0 "
|
||||
r"grade_failures=0 mouth_content=2/2 mouth_radius=2/2 walkability=PASS$",
|
||||
text, re.M)
|
||||
floor_rows = re.findall(r"^\[FloorProfileBlend\].*status=PASS$", text, re.M)
|
||||
route_rows = re.findall(
|
||||
r"^\[ResolvedStrateRouteAudit\].*blocked=0 support_samples=\d+ unsupported=0 "
|
||||
r"grade_failures=0 mouth_binding=PASS status=PASS$", text, re.M)
|
||||
ok = (result is not None and result.returncode == 0 and stamp_ok and summary_ok
|
||||
and markers_ok and len(passage_rows) == 2 and len(route_rows) == 2
|
||||
and len(floor_rows) >= 1 and "[Walkability] PASS" in text)
|
||||
FAILED |= not ok
|
||||
ROWS.append((style, elapsed, result.returncode if result else "TIMEOUT", ok))
|
||||
print(f"{name} elapsed={elapsed:.1f}s exit={result.returncode if result else 'TIMEOUT'} "
|
||||
f"recipe={'PASS' if stamp_ok else 'FAIL'} passages={len(passage_rows)}/2 "
|
||||
f"routes={len(route_rows)}/2 floor_blend_rows={len(floor_rows)} "
|
||||
f"{'PASS' if ok else 'FAIL'}", flush=True)
|
||||
|
||||
summary_path = OUT / f"upper-sw-three-strata-{TAG}-audits.csv"
|
||||
summary_path.write_text(
|
||||
"style,elapsed_seconds,exit_code,status\n" +
|
||||
"".join(f"{style},{elapsed:.3f},{code},{'PASS' if ok else 'FAIL'}\n"
|
||||
for style, elapsed, code, ok in ROWS), encoding="utf-8")
|
||||
print(f"[L33UpperSurfaceFixtureAudit] compiler={TAG} runs={len(ROWS)} pass={sum(row[-1] for row in ROWS)} "
|
||||
f"fail={sum(not row[-1] for row in ROWS)} summary={summary_path}", flush=True)
|
||||
raise SystemExit(1 if FAILED or len(ROWS) != 4 else 0)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
@@ -820,7 +820,9 @@ namespace StandaloneStreamBenchmark
|
||||
const auto& b=passage.controlPoints[i];
|
||||
const double dx=qToMetres(b.positionSubvoxels[0]-a.positionSubvoxels[0]);
|
||||
const double dy=qToMetres(b.positionSubvoxels[1]-a.positionSubvoxels[1]);
|
||||
const double dz=qToMetres(b.positionSubvoxels[2]-a.positionSubvoxels[2]);
|
||||
const double floorA=qToMetres(a.positionSubvoxels[2]-a.radiusSubvoxels)+1.13;
|
||||
const double floorB=qToMetres(b.positionSubvoxels[2]-b.radiusSubvoxels)+1.13;
|
||||
const double dz=floorB-floorA;
|
||||
const double horizontal=std::hypot(dx,dy);
|
||||
if(horizontal>1.0e-6&&std::abs(dz)/horizontal
|
||||
<std::tan(0.5*3.14159265358979323846/180.0))
|
||||
@@ -1313,6 +1315,9 @@ namespace StandaloneStreamBenchmark
|
||||
++audit.wallSections;
|
||||
audit.wallFailures+=!wallSolid;
|
||||
crossingWallFailures+=!wallSolid;
|
||||
if(!wallSolid&&crossingWallFailures<=8)
|
||||
std::printf("[PassageRockColumnWallProbe] id=%s segment=%zu xyz_m=(%.3f,%.3f,%.3f) axis_m=(%.3f,%.3f) centerline_radius_m=%.3f bore_radius_m=%.3f wall_probe_radius_m=%.3f\n",
|
||||
passage.stableId.c_str(),segment,x,y,z,axisX,axisY,radial,radius,wallRadius);
|
||||
}
|
||||
double crossingMinimumSurfaceRadius=std::numeric_limits<double>::infinity();
|
||||
double crossingMaximumSurfaceRadius=0.0;
|
||||
@@ -1378,12 +1383,13 @@ namespace StandaloneStreamBenchmark
|
||||
&&crossingWallFailures==0&&std::isfinite(measuredWall)
|
||||
&&measuredWall>=1.50&&crossingSurfaceRelief>=0.20;
|
||||
audit.passed&=columnPass;
|
||||
std::printf("[PassageRockColumnAudit] id=%s segments=%zu..%zu span_m=%.3f axis_m=(%.3f,%.3f) connected=%d/2 route_support=%llu/%llu wall_margin_m=%.3f threshold_m=1.500 radii_m=%.3f..%.3f relief_p2p_m=%.3f nominal_radius_m=%.3f status=%s\n",
|
||||
std::printf("[PassageRockColumnAudit] id=%s segments=%zu..%zu span_m=%.3f axis_m=(%.3f,%.3f) connected=%d/2 route_support=%llu/%llu wall_failures=%llu wall_margin_m=%.3f threshold_m=1.500 radii_m=%.3f..%.3f relief_p2p_m=%.3f nominal_radius_m=%.3f status=%s\n",
|
||||
passage.stableId.c_str(),crossing.firstSegment,crossing.lastSegment,
|
||||
crossing.unsupportedLengthMetres,axisX,axisY,
|
||||
static_cast<int>(topCapConnected)+static_cast<int>(bottomCapConnected),
|
||||
static_cast<unsigned long long>(probeCount-crossingRouteFailures),
|
||||
static_cast<unsigned long long>(probeCount),measuredWall,
|
||||
static_cast<unsigned long long>(probeCount),
|
||||
static_cast<unsigned long long>(crossingWallFailures),measuredWall,
|
||||
crossingMinimumSurfaceRadius,crossingMaximumSurfaceRadius,
|
||||
crossingSurfaceRelief,qToMetres(crossing.columnNominalRadiusSubvoxels),
|
||||
columnPass?"PASS":"FAIL");
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
@@ -44,7 +45,8 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
inline bool BuildFiniteStrateRoute(VoxelForgeCore::WorldAssemblyRecipeV1& recipe,
|
||||
VoxelForgeCore::WorldAssemblyStratumPlanV1& stratum,std::uint64_t& tested,
|
||||
std::uint64_t& gradeRejected,std::uint64_t& placementRejected,
|
||||
std::uint64_t& sweepRejected,std::int64_t arrivalRadiusSubvoxels,
|
||||
std::uint64_t& sweepRejected,std::uint32_t coreVoxelSizeMicrons,
|
||||
std::int64_t arrivalRadiusSubvoxels,
|
||||
std::int64_t departureRadiusSubvoxels,std::string& error)
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
@@ -63,9 +65,20 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
struct Candidate
|
||||
{
|
||||
std::int64_t score=0;
|
||||
std::uint64_t endpointProximityCost=0;
|
||||
std::int32_t centerX=0,centerY=0,direction=0;
|
||||
std::vector<WorldAssemblyRoutePointV1> points;
|
||||
};
|
||||
struct GradeSegmentAudit
|
||||
{
|
||||
std::uint64_t failures=0;
|
||||
double maximumGrade=0.0;
|
||||
std::int64_t fromGround=0,toGround=0,fromX=0,fromY=0,toX=0,toY=0;
|
||||
};
|
||||
std::array<GradeSegmentAudit,32> placementValidGradeSegments{};
|
||||
std::uint64_t gradeFailCandidates=0;
|
||||
double minimumGround=std::numeric_limits<double>::infinity();
|
||||
double maximumGround=-std::numeric_limits<double>::infinity();
|
||||
|
||||
auto& constraints=stratum.routeConstraints;
|
||||
constraints.version=1;
|
||||
@@ -84,10 +97,112 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
if(!finalField.isValid())
|
||||
{error="route planner received an invalid final composed field";return false;}
|
||||
std::vector<Candidate> candidates;
|
||||
constexpr std::int32_t halfLength=128;
|
||||
constexpr std::int32_t waypointSpacing=8;
|
||||
for(std::int32_t centerY=-256;centerY<=256;centerY+=64)
|
||||
for(std::int32_t centerX=-256;centerX<=256;centerX+=64)
|
||||
std::vector<std::int32_t> candidateHalfLengths{128};
|
||||
std::int32_t candidateCenterSpacing=64;
|
||||
if(arrivalRadiusSubvoxels>0&&departureRadiusSubvoxels>0)
|
||||
{
|
||||
// A centered line cannot join two bounded mouths when its chord is
|
||||
// longer than the sum of their placement radii. Leave 3 m for the
|
||||
// capsule and terrain-profile repair, then search centers at 8 m
|
||||
// spacing so asymmetric arrival/departure radii can both be reached.
|
||||
constexpr std::int64_t placementMarginQ=12*WorldAssemblySubvoxelsPerVoxel;
|
||||
const std::int64_t availableLengthVoxels=std::max<std::int64_t>(1,
|
||||
(arrivalRadiusSubvoxels+departureRadiusSubvoxels-placementMarginQ)
|
||||
/WorldAssemblySubvoxelsPerVoxel);
|
||||
const std::int32_t maximumHalfLength=static_cast<std::int32_t>(std::min<std::int64_t>(128,
|
||||
std::max<std::int64_t>(1,availableLengthVoxels/2)));
|
||||
candidateHalfLengths.clear();
|
||||
for(const std::int32_t numerator:std::array<std::int32_t,6>{{8,7,6,5,4,3}})
|
||||
{
|
||||
const std::int32_t halfLength=std::max(1,maximumHalfLength*numerator/8);
|
||||
if(std::find(candidateHalfLengths.begin(),candidateHalfLengths.end(),halfLength)
|
||||
==candidateHalfLengths.end())candidateHalfLengths.push_back(halfLength);
|
||||
}
|
||||
candidateCenterSpacing=32;
|
||||
}
|
||||
const double coreVoxelMetres=static_cast<double>(coreVoxelSizeMicrons)/1000000.0;
|
||||
if(std::getenv("VOXELFORGE_AUDIT_LEGACY_ROUTE_GRADE"))
|
||||
{
|
||||
constexpr std::int32_t legacyCenterSpacing=64;
|
||||
constexpr std::int32_t legacyWaypointSpacing=8;
|
||||
std::array<GradeSegmentAudit,32> legacySegments{};
|
||||
std::uint64_t legacyGradeFailCandidates=0;
|
||||
double legacyMinimumGround=std::numeric_limits<double>::infinity();
|
||||
double legacyMaximumGround=-std::numeric_limits<double>::infinity();
|
||||
for(std::int32_t centerY=-256;centerY<=256;centerY+=legacyCenterSpacing)
|
||||
for(std::int32_t centerX=-256;centerX<=256;centerX+=legacyCenterSpacing)
|
||||
for(const Direction& direction:directions)
|
||||
{
|
||||
bool candidateGradeFail=false;
|
||||
std::int64_t previousGround=0,previousX=0,previousY=0;
|
||||
for(std::int32_t index=0;index<=32;++index)
|
||||
{
|
||||
const std::int64_t along=(index-16)*legacyWaypointSpacing;
|
||||
const std::int64_t x=centerX+std::llround(
|
||||
static_cast<double>(along)*direction.x/65536.0);
|
||||
const std::int64_t y=centerY+std::llround(
|
||||
static_cast<double>(along)*direction.y/65536.0);
|
||||
const auto column=finalField.point(WorldAssemblyFixed(static_cast<double>(x)),
|
||||
WorldAssemblyFixed(static_cast<double>(y)),0);
|
||||
const double groundMetres=static_cast<double>(column.groundHeightSubvoxels)
|
||||
*coreVoxelMetres/WorldAssemblySubvoxelsPerVoxel;
|
||||
legacyMinimumGround=std::min(legacyMinimumGround,groundMetres);
|
||||
legacyMaximumGround=std::max(legacyMaximumGround,groundMetres);
|
||||
if(index>0)
|
||||
{
|
||||
const std::int64_t horizontalQ=static_cast<std::int64_t>(std::llround(
|
||||
std::hypot(static_cast<double>(x-previousX),
|
||||
static_cast<double>(y-previousY))*WorldAssemblySubvoxelsPerVoxel));
|
||||
const std::int64_t riseQ=std::abs(
|
||||
column.groundHeightSubvoxels-previousGround);
|
||||
if(horizontalQ==0||riseQ*65536>horizontalQ*GradeQ16)
|
||||
{
|
||||
candidateGradeFail=true;
|
||||
GradeSegmentAudit& segment=legacySegments[
|
||||
static_cast<std::size_t>(index-1)];
|
||||
++segment.failures;
|
||||
const double grade=horizontalQ>0
|
||||
?static_cast<double>(riseQ)/static_cast<double>(horizontalQ)
|
||||
:std::numeric_limits<double>::infinity();
|
||||
if(grade>segment.maximumGrade)
|
||||
{
|
||||
segment.maximumGrade=grade;
|
||||
segment.fromGround=previousGround;
|
||||
segment.toGround=column.groundHeightSubvoxels;
|
||||
segment.fromX=previousX;segment.fromY=previousY;
|
||||
segment.toX=x;segment.toY=y;
|
||||
}
|
||||
}
|
||||
}
|
||||
previousGround=column.groundHeightSubvoxels;previousX=x;previousY=y;
|
||||
}
|
||||
if(candidateGradeFail)++legacyGradeFailCandidates;
|
||||
}
|
||||
std::printf("[WorldAssemblerLegacyRouteGradeProfile] candidates=648 grade_fail_candidates=%llu ground_range_m=%.3f..%.3f grade_limit_deg=15.000 chord_m=%.3f waypoint_step_m=%.3f center_step_m=%.3f\n",
|
||||
static_cast<unsigned long long>(legacyGradeFailCandidates),
|
||||
legacyMinimumGround,legacyMaximumGround,256.0*coreVoxelMetres,
|
||||
legacyWaypointSpacing*coreVoxelMetres,legacyCenterSpacing*coreVoxelMetres);
|
||||
const double metresPerSubvoxel=coreVoxelMetres/WorldAssemblySubvoxelsPerVoxel;
|
||||
for(std::size_t segmentIndex=0;segmentIndex<legacySegments.size();++segmentIndex)
|
||||
{
|
||||
const GradeSegmentAudit& segment=legacySegments[segmentIndex];
|
||||
if(segment.failures==0)continue;
|
||||
std::printf("[WorldAssemblerLegacyRouteGradeSegment] index=%zu rejected_candidates=%llu max_grade_deg=%.3f ground_m=%.3f->%.3f xy_m=(%.3f,%.3f)->(%.3f,%.3f)\n",
|
||||
segmentIndex,static_cast<unsigned long long>(segment.failures),
|
||||
std::atan(segment.maximumGrade)*57.29577951308232,
|
||||
segment.fromGround*metresPerSubvoxel,segment.toGround*metresPerSubvoxel,
|
||||
segment.fromX*coreVoxelMetres,segment.fromY*coreVoxelMetres,
|
||||
segment.toX*coreVoxelMetres,segment.toY*coreVoxelMetres);
|
||||
}
|
||||
}
|
||||
std::printf("[WorldAssemblerRouteCandidateEnvelope] max_chord_m=%.3f length_variants=%zu center_step_m=%.3f arrival_radius_m=%.3f departure_radius_m=%.3f\n",
|
||||
2.0*candidateHalfLengths.front()*coreVoxelMetres,candidateHalfLengths.size(),
|
||||
candidateCenterSpacing*coreVoxelMetres,
|
||||
arrivalRadiusSubvoxels*coreVoxelMetres/WorldAssemblySubvoxelsPerVoxel,
|
||||
departureRadiusSubvoxels*coreVoxelMetres/WorldAssemblySubvoxelsPerVoxel);
|
||||
for(const std::int32_t candidateHalfLength:candidateHalfLengths)
|
||||
for(std::int32_t centerY=-256;centerY<=256;centerY+=candidateCenterSpacing)
|
||||
for(std::int32_t centerX=-256;centerX<=256;centerX+=candidateCenterSpacing)
|
||||
for(std::int32_t directionIndex=0;directionIndex<static_cast<std::int32_t>(directions.size());++directionIndex)
|
||||
{
|
||||
Candidate candidate;
|
||||
@@ -95,22 +210,36 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
candidate.points.reserve(33);
|
||||
std::int64_t previousGround=0,previousX=0,previousY=0;
|
||||
bool gradePass=true,placementPass=true;
|
||||
std::array<GradeSegmentAudit,32> candidateGradeSegments{};
|
||||
for(std::int32_t index=0;index<=32;++index)
|
||||
{
|
||||
const std::int64_t along=(index-16)*waypointSpacing;
|
||||
const std::int64_t along=(index-16)*candidateHalfLength/16;
|
||||
const std::int64_t x=centerX+std::llround(
|
||||
static_cast<double>(along)*directions[directionIndex].x/65536.0);
|
||||
const std::int64_t y=centerY+std::llround(
|
||||
static_cast<double>(along)*directions[directionIndex].y/65536.0);
|
||||
const std::int64_t xQ=WorldAssemblyFixed(static_cast<double>(x));
|
||||
const std::int64_t yQ=WorldAssemblyFixed(static_cast<double>(y));
|
||||
if(arrivalRadiusSubvoxels>0&&departureRadiusSubvoxels>0
|
||||
&&(index==0||index==32))
|
||||
{
|
||||
const std::uint64_t radiusSquared=static_cast<std::uint64_t>(
|
||||
xQ*xQ+yQ*yQ);
|
||||
candidate.endpointProximityCost+=radiusSquared*(index==0?16ull:1ull);
|
||||
}
|
||||
const auto inRadius=[](std::int64_t xValue,std::int64_t yValue,std::int64_t radius)
|
||||
{return radius<=0||xValue*xValue+yValue*yValue<=radius*radius;};
|
||||
if((index==0&&!inRadius(xQ,yQ,arrivalRadiusSubvoxels))
|
||||
||(index==32&&!inRadius(xQ,yQ,departureRadiusSubvoxels)))
|
||||
{placementPass=false;break;}
|
||||
placementPass=false;
|
||||
const auto column=finalField.point(WorldAssemblyFixed(static_cast<double>(x)),
|
||||
WorldAssemblyFixed(static_cast<double>(y)),0);
|
||||
const double metresPerSubvoxel=static_cast<double>(coreVoxelSizeMicrons)
|
||||
/(WorldAssemblySubvoxelsPerVoxel*1000000.0);
|
||||
const double groundMetres=static_cast<double>(column.groundHeightSubvoxels)
|
||||
*metresPerSubvoxel;
|
||||
minimumGround=std::min(minimumGround,groundMetres);
|
||||
maximumGround=std::max(maximumGround,groundMetres);
|
||||
const std::int64_t horizontalQ=static_cast<std::int64_t>(std::llround(
|
||||
std::hypot(static_cast<double>(x-previousX),static_cast<double>(y-previousY))
|
||||
*WorldAssemblySubvoxelsPerVoxel));
|
||||
@@ -118,7 +247,23 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
{
|
||||
const std::int64_t riseQ=std::abs(column.groundHeightSubvoxels-previousGround);
|
||||
if(horizontalQ==0||riseQ*65536>horizontalQ*GradeQ16)
|
||||
{gradePass=false;break;}
|
||||
{
|
||||
gradePass=false;
|
||||
GradeSegmentAudit& segment=candidateGradeSegments[
|
||||
static_cast<std::size_t>(index-1)];
|
||||
++segment.failures;
|
||||
const double grade=horizontalQ>0
|
||||
?static_cast<double>(riseQ)/static_cast<double>(horizontalQ)
|
||||
:std::numeric_limits<double>::infinity();
|
||||
if(grade>segment.maximumGrade)
|
||||
{
|
||||
segment.maximumGrade=grade;
|
||||
segment.fromGround=previousGround;
|
||||
segment.toGround=column.groundHeightSubvoxels;
|
||||
segment.fromX=previousX;segment.fromY=previousY;
|
||||
segment.toX=x;segment.toY=y;
|
||||
}
|
||||
}
|
||||
candidate.score+=riseQ*65536/std::max<std::int64_t>(1,horizontalQ);
|
||||
}
|
||||
WorldAssemblyRoutePointV1 point;
|
||||
@@ -131,13 +276,60 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
candidate.points.push_back(point);
|
||||
previousGround=column.groundHeightSubvoxels;previousX=x;previousY=y;
|
||||
}
|
||||
candidate.score/=32;
|
||||
++tested;
|
||||
if(!gradePass)++gradeFailCandidates;
|
||||
if(placementPass&&gradePass)candidates.push_back(std::move(candidate));
|
||||
else if(!placementPass)++placementRejected;
|
||||
else ++gradeRejected;
|
||||
else
|
||||
{
|
||||
++gradeRejected;
|
||||
for(std::size_t segmentIndex=0;segmentIndex<candidateGradeSegments.size();
|
||||
++segmentIndex)
|
||||
{
|
||||
const GradeSegmentAudit& rejected=candidateGradeSegments[segmentIndex];
|
||||
if(rejected.failures==0)continue;
|
||||
GradeSegmentAudit& aggregate=placementValidGradeSegments[segmentIndex];
|
||||
aggregate.failures+=rejected.failures;
|
||||
if(rejected.maximumGrade>aggregate.maximumGrade)
|
||||
{
|
||||
aggregate.maximumGrade=rejected.maximumGrade;
|
||||
aggregate.fromGround=rejected.fromGround;
|
||||
aggregate.toGround=rejected.toGround;
|
||||
aggregate.fromX=rejected.fromX;aggregate.fromY=rejected.fromY;
|
||||
aggregate.toX=rejected.toX;aggregate.toY=rejected.toY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(gradeRejected>0||placementRejected>0)
|
||||
{
|
||||
const double metresPerSubvoxel=static_cast<double>(coreVoxelSizeMicrons)
|
||||
/(WorldAssemblySubvoxelsPerVoxel*1000000.0);
|
||||
std::printf("[WorldAssemblerRouteGradeProfile] tested=%llu grade_fail_candidates=%llu grade_rejected_after_placement=%llu placement_rejected=%llu ground_range_m=%.3f..%.3f grade_limit_deg=15.000\n",
|
||||
static_cast<unsigned long long>(tested),
|
||||
static_cast<unsigned long long>(gradeFailCandidates),
|
||||
static_cast<unsigned long long>(gradeRejected),
|
||||
static_cast<unsigned long long>(placementRejected),minimumGround,maximumGround);
|
||||
for(std::size_t segmentIndex=0;segmentIndex<placementValidGradeSegments.size();
|
||||
++segmentIndex)
|
||||
{
|
||||
const GradeSegmentAudit& segment=placementValidGradeSegments[segmentIndex];
|
||||
if(segment.failures==0)continue;
|
||||
const double gradeDegrees=std::atan(segment.maximumGrade)*57.29577951308232;
|
||||
std::printf("[WorldAssemblerRouteGradeSegment] index=%zu rejected_candidates=%llu max_grade_deg=%.3f ground_m=%.3f->%.3f xy_m=(%.3f,%.3f)->(%.3f,%.3f)\n",
|
||||
segmentIndex,static_cast<unsigned long long>(segment.failures),gradeDegrees,
|
||||
segment.fromGround*metresPerSubvoxel,segment.toGround*metresPerSubvoxel,
|
||||
segment.fromX*coreVoxelSizeMicrons/1000000.0,
|
||||
segment.fromY*coreVoxelSizeMicrons/1000000.0,
|
||||
segment.toX*coreVoxelSizeMicrons/1000000.0,
|
||||
segment.toY*coreVoxelSizeMicrons/1000000.0);
|
||||
}
|
||||
}
|
||||
std::stable_sort(candidates.begin(),candidates.end(),[](const Candidate& a,const Candidate& b)
|
||||
{
|
||||
if(a.endpointProximityCost!=b.endpointProximityCost)
|
||||
return a.endpointProximityCost<b.endpointProximityCost;
|
||||
if(a.score!=b.score)return a.score<b.score;
|
||||
if(a.centerY!=b.centerY)return a.centerY<b.centerY;
|
||||
if(a.centerX!=b.centerX)return a.centerX<b.centerX;
|
||||
@@ -165,6 +357,15 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
if(liftQ>0)
|
||||
std::printf("[WorldAssemblerRouteRepaired] uniform_lift_q=%lld support_limit_q=%lld\n",
|
||||
static_cast<long long>(liftQ),static_cast<long long>(SupportDepthQ));
|
||||
const double metresPerSubvoxel=coreVoxelMetres
|
||||
/WorldAssemblySubvoxelsPerVoxel;
|
||||
std::printf("[WorldAssemblerRouteAccepted] arrival_xy_m=(%.3f,%.3f) departure_xy_m=(%.3f,%.3f) endpoint_proximity_cost=%llu average_grade_q16=%lld\n",
|
||||
candidate.points.front().positionSubvoxels[0]*metresPerSubvoxel,
|
||||
candidate.points.front().positionSubvoxels[1]*metresPerSubvoxel,
|
||||
candidate.points.back().positionSubvoxels[0]*metresPerSubvoxel,
|
||||
candidate.points.back().positionSubvoxels[1]*metresPerSubvoxel,
|
||||
static_cast<unsigned long long>(candidate.endpointProximityCost),
|
||||
static_cast<long long>(candidate.score));
|
||||
return true;
|
||||
}
|
||||
if(audit.blockedCapsuleSamples==0||liftQ>=MaximumRepairLiftQ)break;
|
||||
@@ -410,7 +611,8 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
}
|
||||
std::uint64_t routeCandidates=0,routeGradeRejected=0,routePlacementRejected=0,routeSweepRejected=0;
|
||||
if(!BuildFiniteStrateRoute(recipe,stratum,routeCandidates,routeGradeRejected,
|
||||
routePlacementRejected,routeSweepRejected,arrivalRadiusQ,departureRadiusQ,error))return false;
|
||||
routePlacementRejected,routeSweepRejected,owner.CoreVoxelSizeMicrons,
|
||||
arrivalRadiusQ,departureRadiusQ,error))return false;
|
||||
std::printf("[WorldAssemblerRoutePlan] candidates=%llu grade_rejected=%llu placement_rejected=%llu sweep_rejected=%llu accepted_points=%zu capsule_m=0.34x1.76 corridor_radius_vox=4 support_probe_points=5 minimum_support_probes=%u arrival_radius_m=%.3f departure_radius_m=%.3f status=PASS\n",
|
||||
static_cast<unsigned long long>(routeCandidates),
|
||||
static_cast<unsigned long long>(routeGradeRejected),
|
||||
|
||||
Reference in new issue
Block a user