wip: opt-in landing-pad contract + Core.Liminal.Field v2 pads (L145)
Layers may publish certified arrival/departure pads; the passage planner binds endpoints and tangents to them; no-pad layouts serialize unchanged (owner canonical hashes match v227). Pads removed the endpoint grade failures; seeds 0-4 passed at a 45 m margin, but seed 5 still has 289 blocked passage-capsule samples ~29 m before the departure pad, where the composed field stays solid (-0.162) inside the tube over native ground (-3.6). Ripple audit now keeps true route distance across masked gaps (with a synthetic control). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
a45ba9bb10
commit
7d654ff861
14 files changed
+1228
-66
No files matched your search
@@ -613,7 +613,28 @@ namespace
|
||||
for(std::size_t v=0;v<anchor.values.size();++v){if(v)out<<',';out<<anchor.values[v];}
|
||||
out<<"]}";
|
||||
}
|
||||
out<<"]}";
|
||||
out<<']';
|
||||
if(!x.landingPads.empty())
|
||||
{
|
||||
out<<",\"landing_pads\":[";
|
||||
for(std::size_t p=0;p<x.landingPads.size();++p)
|
||||
{
|
||||
if(p)out<<',';const auto& pad=x.landingPads[p];
|
||||
out<<"{\"stable_id\":"<<LayoutEscape(pad.stableId)
|
||||
<<",\"role\":"<<static_cast<std::uint32_t>(pad.role)
|
||||
<<",\"position_q\":["<<pad.positionSubvoxels[0]<<','
|
||||
<<pad.positionSubvoxels[1]<<','<<pad.positionSubvoxels[2]
|
||||
<<"],\"floor_q\":"<<pad.floorHeightSubvoxels
|
||||
<<",\"approach_q16\":["<<pad.approachDirectionQ16[0]<<','
|
||||
<<pad.approachDirectionQ16[1]<<']'
|
||||
<<",\"clearance_q\":"<<pad.clearanceSubvoxels
|
||||
<<",\"support_depth_q\":"<<pad.supportDepthSubvoxels
|
||||
<<",\"certified\":"
|
||||
<<(pad.certifiedWalkableWithSupport?"true":"false")<<'}';
|
||||
}
|
||||
out<<']';
|
||||
}
|
||||
out<<'}';
|
||||
}
|
||||
out<<"],\"passages\":[";
|
||||
for(std::size_t i=0;i<s.passages.size();++i)
|
||||
@@ -957,6 +978,9 @@ bool ResolvedLayoutSnapshotV3::finalize(std::string& error)
|
||||
for(std::size_t i=0;i<strates.size();++i)
|
||||
{
|
||||
auto& x=strates[i];x.order=static_cast<std::int32_t>(i);
|
||||
std::stable_sort(x.landingPads.begin(),x.landingPads.end(),[](const auto& a,const auto& b)
|
||||
{return a.role!=b.role?static_cast<std::uint32_t>(a.role)
|
||||
<static_cast<std::uint32_t>(b.role):a.stableId<b.stableId;});
|
||||
if(x.stableId.empty())x.stableId=worldIdentity+"/strate/"+std::to_string(x.strateIndex);
|
||||
if(x.content.kind.empty())x.content.kind="content";
|
||||
if(x.ceiling.kind.empty())x.ceiling.kind="ceiling";
|
||||
@@ -1017,9 +1041,40 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
||||
const auto add=[&](std::string code,std::string detail)
|
||||
{issues.push_back({std::move(code),std::move(detail)});};
|
||||
const bool strict=mode==ResolvedLayoutValidationModeV3::strict;
|
||||
const std::int64_t playerCenterAboveFloorQ=static_cast<std::int64_t>(std::llround(
|
||||
1.13*WorldAssemblySubvoxelsPerVoxel*1000000.0/coreVoxelSizeMicrometres));
|
||||
for(std::size_t i=0;i<strates.size();++i)
|
||||
{
|
||||
const auto& s=strates[i];
|
||||
std::vector<std::string> landingPadIds;
|
||||
for(const auto& pad:s.landingPads)
|
||||
{
|
||||
if(pad.stableId.empty()
|
||||
||std::find(landingPadIds.begin(),landingPadIds.end(),pad.stableId)
|
||||
!=landingPadIds.end())
|
||||
add("landing_pad_identity",s.stableId+" has an empty or duplicate landing-pad ID");
|
||||
else landingPadIds.push_back(pad.stableId);
|
||||
const auto role=static_cast<std::uint32_t>(pad.role);
|
||||
const std::int64_t directionX=pad.approachDirectionQ16[0];
|
||||
const std::int64_t directionY=pad.approachDirectionQ16[1];
|
||||
const std::int64_t directionLengthSquared=directionX*directionX+directionY*directionY;
|
||||
constexpr std::int64_t UnitQ16Squared=1ll<<32;
|
||||
const bool unitDirection=directionLengthSquared>=UnitQ16Squared*98/100
|
||||
&&directionLengthSquared<=UnitQ16Squared*102/100;
|
||||
const std::int64_t floorMicrometres=pad.floorHeightSubvoxels
|
||||
*static_cast<std::int64_t>(coreVoxelSizeMicrometres)
|
||||
/WorldAssemblySubvoxelsPerVoxel;
|
||||
if(role>static_cast<std::uint32_t>(ResolvedLayoutLandingPadRoleV1::departure)
|
||||
||pad.positionSubvoxels[2]
|
||||
!=pad.floorHeightSubvoxels+playerCenterAboveFloorQ
|
||||
||!s.content.presentInCurrentField
|
||||
||floorMicrometres<s.content.bottomMicrometres
|
||||
||floorMicrometres>=s.content.topMicrometres
|
||||
||!unitDirection||pad.clearanceSubvoxels<=0
|
||||
||pad.supportDepthSubvoxels<=0
|
||||
||!pad.certifiedWalkableWithSupport)
|
||||
add("landing_pad_contract",s.stableId+" publishes an uncertified or malformed landing pad");
|
||||
}
|
||||
const std::int64_t effectiveTransitionWidth=LayoutTransitionLimit(s);
|
||||
if(s.interiorTopMicrometres<=s.interiorBottomMicrometres)
|
||||
add("invalid_interior",s.stableId+" has non-positive content height");
|
||||
@@ -1643,6 +1698,22 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
|
||||
for(const auto& a:v.Get("anchors").AsArray())
|
||||
{ResolvedLayoutAnchorV2 p;p.kind=LayoutString(a,"kind");p.stableId=LayoutString(a,"stable_id");p.units=LayoutString(a,"units");
|
||||
for(const auto& value:a.Get("values").AsArray())p.values.push_back(value.Integer());x.anchors.push_back(std::move(p));}
|
||||
for(const auto& value:v.Get("landing_pads").AsArray())
|
||||
{
|
||||
ResolvedLayoutLandingPadV1 pad;pad.stableId=LayoutString(value,"stable_id");
|
||||
pad.role=static_cast<ResolvedLayoutLandingPadRoleV1>(LayoutInt(value,"role"));
|
||||
const auto& position=value.Get("position_q").AsArray();
|
||||
for(std::size_t axis=0;axis<3&&axis<position.size();++axis)
|
||||
pad.positionSubvoxels[axis]=position[axis].Integer();
|
||||
pad.floorHeightSubvoxels=LayoutInt(value,"floor_q");
|
||||
const auto& approach=value.Get("approach_q16").AsArray();
|
||||
for(std::size_t axis=0;axis<2&&axis<approach.size();++axis)
|
||||
pad.approachDirectionQ16[axis]=static_cast<std::int32_t>(approach[axis].Integer());
|
||||
pad.clearanceSubvoxels=LayoutInt(value,"clearance_q");
|
||||
pad.supportDepthSubvoxels=LayoutInt(value,"support_depth_q");
|
||||
pad.certifiedWalkableWithSupport=LayoutBool(value,"certified");
|
||||
x.landingPads.push_back(std::move(pad));
|
||||
}
|
||||
s.strates.push_back(std::move(x));
|
||||
}
|
||||
for(const auto& v:root.Get("passages").AsArray())
|
||||
@@ -2566,6 +2637,17 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
{return c.upperStrateIndex==strate;});
|
||||
return it==configs.end()?nullptr:&*it;
|
||||
};
|
||||
const auto landingPadFor=[](const ResolvedLayoutStratumV2& stratum,
|
||||
ResolvedLayoutLandingPadRoleV1 role,std::uint32_t ordinal)
|
||||
->const ResolvedLayoutLandingPadV1*
|
||||
{
|
||||
std::vector<const ResolvedLayoutLandingPadV1*> candidates;
|
||||
for(const auto& pad:stratum.landingPads)
|
||||
if(pad.role==role)candidates.push_back(&pad);
|
||||
std::sort(candidates.begin(),candidates.end(),[](const auto* a,const auto* b)
|
||||
{return a->stableId<b->stableId;});
|
||||
return ordinal<candidates.size()?candidates[ordinal]:nullptr;
|
||||
};
|
||||
const auto routeAnchor=[](const ResolvedLayoutStratumV2& s,bool departure,
|
||||
std::array<std::int64_t,3>& out)
|
||||
{
|
||||
@@ -2662,9 +2744,37 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
passage.upperStrateIndex=upper.strateIndex;passage.lowerStrateIndex=lower.strateIndex;
|
||||
passage.placementRadiusSubvoxels=placementRadius;
|
||||
passage.mouthRadiusSubvoxels=mouthRadius;passage.midRadiusSubvoxels=midRadius;
|
||||
const auto* upperDeparturePad=landingPadFor(upper,
|
||||
ResolvedLayoutLandingPadRoleV1::departure,connection);
|
||||
const auto* lowerArrivalPad=landingPadFor(lower,
|
||||
ResolvedLayoutLandingPadRoleV1::arrival,connection);
|
||||
const auto validateLandingPad=[&](const ResolvedLayoutLandingPadV1* pad,
|
||||
std::string_view side)
|
||||
{
|
||||
if(!pad)return true;
|
||||
const std::int64_t requiredClearance=mouthRadius
|
||||
+static_cast<std::int64_t>(std::llround(0.34*qPerMetre));
|
||||
if(!pad->certifiedWalkableWithSupport||pad->clearanceSubvoxels<requiredClearance
|
||||
||pad->supportDepthSubvoxels<static_cast<std::int64_t>(std::llround(0.50*qPerMetre))
|
||||
||!radialWithin(pad->positionSubvoxels,placementRadius)
|
||||
||pad->positionSubvoxels[2]
|
||||
!=pad->floorHeightSubvoxels+playerCenterAboveFloorQ)
|
||||
{
|
||||
error=passage.stableId+" "+std::string(side)
|
||||
+" landing pad is uncertified, unsupported, too tight, or outside PassageConfig.DistanceMax";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if(!validateLandingPad(upperDeparturePad,"departure")
|
||||
||!validateLandingPad(lowerArrivalPad,"arrival"))return false;
|
||||
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 hasUpperRoute=upperDeparturePad
|
||||
?(upperMouth=upperDeparturePad->positionSubvoxels,true)
|
||||
:connection==0&&routeAnchor(upper,true,upperMouth);
|
||||
const bool hasLowerRoute=lowerArrivalPad
|
||||
?(lowerMouth=lowerArrivalPad->positionSubvoxels,true)
|
||||
:connection==0&&routeAnchor(lower,false,lowerMouth);
|
||||
const bool surfaceGroundDeparture=hasUpperRoute&&upperIsSurfaceWorld;
|
||||
const ResolvedLayoutAnchorV2* passageAnchor=nullptr;
|
||||
if(connection==0)
|
||||
@@ -2713,13 +2823,17 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
|
||||
constexpr std::int64_t LandingExtensionQ=8;
|
||||
std::array<std::int64_t,2> upperTangent{},lowerTangent{};
|
||||
const bool hasUpperTangent=hasUpperRoute
|
||||
&&routeTangent(upper,true,upperTangent);
|
||||
const bool hasUpperTangent=upperDeparturePad
|
||||
?(upperTangent={upperDeparturePad->approachDirectionQ16[0],
|
||||
upperDeparturePad->approachDirectionQ16[1]},true)
|
||||
:hasUpperRoute&&routeTangent(upper,true,upperTangent);
|
||||
const bool hasPassageArrival=std::any_of(lower.anchors.begin(),lower.anchors.end(),
|
||||
[](const auto& anchor){return anchor.kind=="passage_arrival"
|
||||
&&anchor.values.size()>=3;});
|
||||
const bool hasLowerTangent=hasLowerRoute&&!hasPassageArrival
|
||||
&&routeTangent(lower,false,lowerTangent);
|
||||
const bool hasLowerTangent=lowerArrivalPad
|
||||
?(lowerTangent={lowerArrivalPad->approachDirectionQ16[0],
|
||||
lowerArrivalPad->approachDirectionQ16[1]},true)
|
||||
:hasLowerRoute&&!hasPassageArrival&&routeTangent(lower,false,lowerTangent);
|
||||
const bool profiledSurfaceJoin=hasLowerTangent
|
||||
&&std::any_of(lower.anchors.begin(),lower.anchors.end(),
|
||||
[](const auto& anchor){return anchor.kind=="heightfield_profile"
|
||||
|
||||
Reference in new issue
Block a user