wip(assembler): round L22 -- one 8 m layout chunk for every strate; real ceiling -> content -> bedrock
StrateLayoutChunkSizeMeters (world setting, default 8 m; owner decision) is resolved once in the director/adapter and standalone loader; every generator reads the resolved width (no CHUNK_SIZE / Core-tile inference). Every strate gets solid ceiling and bedrock bands from its BoundarySealThickness in the shared composition path (diggable by default); faces come from the shared boundary path. Owner world: FloatingIslands +8..-120 m (128 m, now with a bedrock floor), SurfaceWorld -120..-632 m (unchanged; full-detail and exact-query hashes equal L21); the 60 m gap is gone. The strict layout validator passes on the real world, with 0 endpoint dispatch disagreements. Spawn/benchmark camera derived from route.front. Ledges under the islands gone (reviewer capture). Evaluator 7, recipe schema 2, layout snapshot 2, FI geometry 4. Open: SW cap profiles exceed the ceiling band (owner call); transitions still Hard; Unreal setting/manager/adapter edits uncompiled here. 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
8753f6dbe4
commit
55c26a05a7
28 files changed
+1432
-579
No files matched your search
@@ -39,37 +39,42 @@ namespace
|
||||
}
|
||||
std::string LayoutHex(std::uint64_t value)
|
||||
{std::ostringstream out;out<<std::hex<<std::setfill('0')<<std::setw(16)<<value;return out.str();}
|
||||
std::int64_t LayoutTransitionLimit(const ResolvedLayoutStratumV1& stratum)
|
||||
std::int64_t LayoutTransitionLimit(const ResolvedLayoutStratumV2& stratum)
|
||||
{
|
||||
if(stratum.transitionMode=="Hard")return 0;
|
||||
if(stratum.transitionMode=="Hard"||stratum.transitionFallsBackToHard)return 0;
|
||||
return stratum.transitionWidthMicrometres
|
||||
+(stratum.transitionMode=="Interleaved"?stratum.transitionWarpAmplitudeMicrometres:0);
|
||||
}
|
||||
std::string LayoutBandJson(const ResolvedLayoutBandV1& band)
|
||||
std::string LayoutBandJson(const ResolvedLayoutBandV2& band)
|
||||
{
|
||||
std::ostringstream out;out<<"{\"kind\":"<<LayoutEscape(band.kind)
|
||||
<<",\"bottom_um\":"<<band.bottomMicrometres<<",\"top_um\":"<<band.topMicrometres
|
||||
<<",\"present\":"<<(band.presentInCurrentField?"true":"false")
|
||||
<<",\"diggable\":"<<(band.diggable?"true":"false")
|
||||
<<",\"protected\":"<<(band.protectedBand?"true":"false")
|
||||
<<",\"solid_density_q16\":"<<band.solidDensityQ16
|
||||
<<",\"behaviour\":"<<LayoutEscape(band.behaviour)
|
||||
<<",\"density_side\":"<<LayoutEscape(band.densitySide)
|
||||
<<",\"cap_path\":"<<LayoutEscape(band.capPath)<<'}';return out.str();
|
||||
}
|
||||
ResolvedLayoutBandV1 ParseLayoutBand(const VFJson::Value& value)
|
||||
ResolvedLayoutBandV2 ParseLayoutBand(const VFJson::Value& value)
|
||||
{
|
||||
ResolvedLayoutBandV1 b;b.kind=value.Get("kind").String();
|
||||
ResolvedLayoutBandV2 b;b.kind=value.Get("kind").String();
|
||||
b.bottomMicrometres=value.Get("bottom_um").Integer();b.topMicrometres=value.Get("top_um").Integer();
|
||||
b.presentInCurrentField=value.Get("present").Boolean();b.diggable=value.Get("diggable").Boolean(true);
|
||||
b.protectedBand=value.Get("protected").Boolean();b.behaviour=value.Get("behaviour").String();
|
||||
b.protectedBand=value.Get("protected").Boolean();b.solidDensityQ16=value.Get("solid_density_q16").Integer(65536);
|
||||
b.behaviour=value.Get("behaviour").String();
|
||||
b.densitySide=value.Get("density_side").String();b.capPath=value.Get("cap_path").String();return b;
|
||||
}
|
||||
std::string LayoutBodyJson(const ResolvedLayoutSnapshotV1& s)
|
||||
std::string LayoutBodyJson(const ResolvedLayoutSnapshotV2& s)
|
||||
{
|
||||
std::ostringstream out;out<<"{\"schema_version\":"<<s.schemaVersion
|
||||
<<",\"world_identity\":"<<LayoutEscape(s.worldIdentity)
|
||||
<<",\"owner_voxel_size_um\":"<<s.ownerVoxelSizeMicrometres
|
||||
<<",\"core_voxel_size_um\":"<<s.coreVoxelSizeMicrometres<<",\"strates\":[";
|
||||
<<",\"core_voxel_size_um\":"<<s.coreVoxelSizeMicrometres
|
||||
<<",\"strate_layout_chunk_size_um\":"<<s.strateLayoutChunkSizeMicrometres
|
||||
<<",\"strate_layout_chunk_width_subvoxels\":"<<s.strateLayoutChunkWidthSubvoxels
|
||||
<<",\"strates\":[";
|
||||
for(std::size_t i=0;i<s.strates.size();++i)
|
||||
{
|
||||
if(i)out<<',';const auto& x=s.strates[i];
|
||||
@@ -90,6 +95,7 @@ namespace
|
||||
<<",\"transition_warp_amplitude_um\":"<<x.transitionWarpAmplitudeMicrometres
|
||||
<<",\"transition_interpenetration_um\":"<<x.transitionInterpenetrationMicrometres
|
||||
<<",\"transition_honoured_in_core\":"<<(x.transitionHonouredInCore?"true":"false")
|
||||
<<",\"transition_falls_back_to_hard\":"<<(x.transitionFallsBackToHard?"true":"false")
|
||||
<<",\"transition_dispatch_path\":"<<LayoutEscape(x.transitionDispatchPath)
|
||||
<<",\"bound_dispatch_path\":"<<LayoutEscape(x.boundDispatchPath)
|
||||
<<",\"query_dispatch_path\":"<<LayoutEscape(x.queryDispatchPath)
|
||||
@@ -117,17 +123,18 @@ namespace
|
||||
{return obj.Get(key).Boolean(fallback);}
|
||||
}
|
||||
|
||||
std::string ResolvedLayoutSnapshotV1::ToJson() const
|
||||
std::string ResolvedLayoutSnapshotV2::ToJson() const
|
||||
{
|
||||
const std::string body=LayoutBodyJson(*this);
|
||||
return body.substr(0,body.size()-1)+",\"stable_identity\":"
|
||||
+LayoutEscape(stableIdentity.empty()?"resolved-layout-v1-fnv64:"+LayoutHex(LayoutHash(body)):stableIdentity)+"}";
|
||||
+LayoutEscape(stableIdentity.empty()?"resolved-layout-v2-fnv64:"+LayoutHex(LayoutHash(body)):stableIdentity)+"}";
|
||||
}
|
||||
|
||||
bool ResolvedLayoutSnapshotV1::finalize(std::string& error)
|
||||
bool ResolvedLayoutSnapshotV2::finalize(std::string& error)
|
||||
{
|
||||
error.clear();
|
||||
if(schemaVersion!=ResolvedLayoutSchemaVersionV1||worldIdentity.empty()||strates.empty())
|
||||
if(schemaVersion!=ResolvedLayoutSchemaVersionV2||worldIdentity.empty()||strates.empty()
|
||||
||strateLayoutChunkSizeMicrometres==0||strateLayoutChunkWidthSubvoxels<=0)
|
||||
{error="resolved layout has an unsupported version, empty world identity, or no strates";return false;}
|
||||
std::stable_sort(strates.begin(),strates.end(),[](const auto& a,const auto& b)
|
||||
{return a.interiorTopMicrometres!=b.interiorTopMicrometres
|
||||
@@ -143,20 +150,21 @@ bool ResolvedLayoutSnapshotV1::finalize(std::string& error)
|
||||
{error="resolved layout contains an invalid interior interval or voxel size";return false;}
|
||||
}
|
||||
stableIdentity.clear();const std::string body=LayoutBodyJson(*this);
|
||||
stableIdentity="resolved-layout-v1-fnv64:"+LayoutHex(LayoutHash(body));
|
||||
std::vector<ResolvedLayoutIssueV1> issues;
|
||||
return validate(ResolvedLayoutValidationModeV1::compatibility,issues,error);
|
||||
stableIdentity="resolved-layout-v2-fnv64:"+LayoutHex(LayoutHash(body));
|
||||
std::vector<ResolvedLayoutIssueV2> issues;
|
||||
return validate(ResolvedLayoutValidationModeV2::compatibility,issues,error);
|
||||
}
|
||||
|
||||
bool ResolvedLayoutSnapshotV1::validate(ResolvedLayoutValidationModeV1 mode,
|
||||
std::vector<ResolvedLayoutIssueV1>& issues,std::string& error) const
|
||||
bool ResolvedLayoutSnapshotV2::validate(ResolvedLayoutValidationModeV2 mode,
|
||||
std::vector<ResolvedLayoutIssueV2>& issues,std::string& error) const
|
||||
{
|
||||
issues.clear();error.clear();
|
||||
if(schemaVersion!=ResolvedLayoutSchemaVersionV1||worldIdentity.empty()||strates.empty())
|
||||
if(schemaVersion!=ResolvedLayoutSchemaVersionV2||worldIdentity.empty()||strates.empty()
|
||||
||strateLayoutChunkSizeMicrometres==0||strateLayoutChunkWidthSubvoxels<=0)
|
||||
{error="resolved layout schema/world/strata header is invalid";return false;}
|
||||
const auto add=[&](std::string code,std::string detail)
|
||||
{issues.push_back({std::move(code),std::move(detail)});};
|
||||
const bool strict=mode==ResolvedLayoutValidationModeV1::strict;
|
||||
const bool strict=mode==ResolvedLayoutValidationModeV2::strict;
|
||||
for(std::size_t i=0;i<strates.size();++i)
|
||||
{
|
||||
const auto& s=strates[i];
|
||||
@@ -169,7 +177,8 @@ bool ResolvedLayoutSnapshotV1::validate(ResolvedLayoutValidationModeV1 mode,
|
||||
if(s.managerChunkWidthMicrometres>0&&s.dispatchChunkWidthMicrometres>0
|
||||
&&s.managerChunkWidthMicrometres!=s.dispatchChunkWidthMicrometres)
|
||||
add("ambiguous_dispatch_units",s.stableId+" manager chunk width differs from Core dispatch chunk width");
|
||||
if(i+1<strates.size()&&s.transitionMode!="Hard"&&!s.transitionHonouredInCore)
|
||||
if(i+1<strates.size()&&s.transitionMode!="Hard"&&!s.transitionHonouredInCore
|
||||
&&!s.transitionFallsBackToHard)
|
||||
add("transition_not_honoured",s.stableId+" declares a blend/interleave that Core does not apply on this interface");
|
||||
if(!s.ceiling.presentInCurrentField)add("missing_ceiling",s.stableId+" has no explicit ceiling band in today's field");
|
||||
if(!s.bedrock.presentInCurrentField)add("missing_bedrock",s.stableId+" has no explicit bedrock floor in today's field");
|
||||
@@ -180,12 +189,14 @@ bool ResolvedLayoutSnapshotV1::validate(ResolvedLayoutValidationModeV1 mode,
|
||||
add(!s.ceiling.presentInCurrentField?"missing_ceiling":"missing_bedrock",s.stableId+" lacks a required boundary band");
|
||||
if(!s.content.presentInCurrentField)
|
||||
add("missing_content",s.stableId+" has no content generator band");
|
||||
const auto bandShape=[&](const ResolvedLayoutBandV1& b,std::string_view expected)
|
||||
const auto bandShape=[&](const ResolvedLayoutBandV2& b,std::string_view expected)
|
||||
{
|
||||
if(b.kind!=expected||b.topMicrometres<=b.bottomMicrometres)
|
||||
add("wrong_order",s.stableId+" has an invalid "+std::string(expected)+" band");
|
||||
if(b.protectedBand&&b.diggable)
|
||||
add("invalid_protection",s.stableId+" band is both protected and diggable");
|
||||
if(b.solidDensityQ16<=0)
|
||||
add("invalid_solid_density",s.stableId+" has a non-positive solid band strength");
|
||||
};
|
||||
bandShape(s.ceiling,"ceiling");bandShape(s.content,"content");bandShape(s.bedrock,"bedrock");
|
||||
if(s.ceiling.bottomMicrometres!=s.content.topMicrometres
|
||||
@@ -255,18 +266,20 @@ bool ResolvedLayoutSnapshotV1::validate(ResolvedLayoutValidationModeV1 mode,
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<ResolvedLayoutSnapshotV1> ResolvedLayoutSnapshotV1::FromJson(
|
||||
std::optional<ResolvedLayoutSnapshotV2> ResolvedLayoutSnapshotV2::FromJson(
|
||||
std::string_view json,std::string& error)
|
||||
{
|
||||
VFJson::Value root;if(!VFJson::Parser(std::string(json)).Parse(root,error))return std::nullopt;
|
||||
if(!root.IsObject()){error="resolved layout JSON root must be an object";return std::nullopt;}
|
||||
ResolvedLayoutSnapshotV1 s;s.schemaVersion=static_cast<std::uint32_t>(LayoutInt(root,"schema_version"));
|
||||
ResolvedLayoutSnapshotV2 s;s.schemaVersion=static_cast<std::uint32_t>(LayoutInt(root,"schema_version"));
|
||||
s.worldIdentity=LayoutString(root,"world_identity");
|
||||
s.ownerVoxelSizeMicrometres=static_cast<std::uint32_t>(LayoutInt(root,"owner_voxel_size_um"));
|
||||
s.coreVoxelSizeMicrometres=static_cast<std::uint32_t>(LayoutInt(root,"core_voxel_size_um"));
|
||||
s.strateLayoutChunkSizeMicrometres=static_cast<std::uint32_t>(LayoutInt(root,"strate_layout_chunk_size_um"));
|
||||
s.strateLayoutChunkWidthSubvoxels=LayoutInt(root,"strate_layout_chunk_width_subvoxels");
|
||||
for(const auto& v:root.Get("strates").AsArray())
|
||||
{
|
||||
ResolvedLayoutStratumV1 x;x.stableId=LayoutString(v,"stable_id");
|
||||
ResolvedLayoutStratumV2 x;x.stableId=LayoutString(v,"stable_id");
|
||||
x.order=static_cast<std::int32_t>(LayoutInt(v,"order"));x.strateIndex=static_cast<std::int32_t>(LayoutInt(v,"strate_index"));
|
||||
x.generatorKind=LayoutString(v,"generator_kind");x.recipeId=LayoutString(v,"recipe_id");
|
||||
x.recipeVersion=static_cast<std::uint32_t>(LayoutInt(v,"recipe_version"));
|
||||
@@ -278,12 +291,14 @@ std::optional<ResolvedLayoutSnapshotV1> ResolvedLayoutSnapshotV1::FromJson(
|
||||
x.transitionWidthMicrometres=LayoutInt(v,"transition_width_um");
|
||||
x.transitionWarpAmplitudeMicrometres=LayoutInt(v,"transition_warp_amplitude_um");
|
||||
x.transitionInterpenetrationMicrometres=LayoutInt(v,"transition_interpenetration_um");
|
||||
x.transitionHonouredInCore=LayoutBool(v,"transition_honoured_in_core");x.transitionDispatchPath=LayoutString(v,"transition_dispatch_path");
|
||||
x.transitionHonouredInCore=LayoutBool(v,"transition_honoured_in_core");
|
||||
x.transitionFallsBackToHard=LayoutBool(v,"transition_falls_back_to_hard");
|
||||
x.transitionDispatchPath=LayoutString(v,"transition_dispatch_path");
|
||||
x.boundDispatchPath=LayoutString(v,"bound_dispatch_path");x.queryDispatchPath=LayoutString(v,"query_dispatch_path");
|
||||
x.managerLookup=LayoutString(v,"manager_lookup");x.managerChunkWidthMicrometres=LayoutInt(v,"manager_chunk_width_um");
|
||||
x.dispatchChunkWidthMicrometres=LayoutInt(v,"dispatch_chunk_width_um");
|
||||
for(const auto& a:v.Get("anchors").AsArray())
|
||||
{ResolvedLayoutAnchorV1 p;p.kind=LayoutString(a,"kind");p.stableId=LayoutString(a,"stable_id");p.units=LayoutString(a,"units");
|
||||
{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));}
|
||||
s.strates.push_back(std::move(x));
|
||||
}
|
||||
@@ -293,12 +308,12 @@ std::optional<ResolvedLayoutSnapshotV1> ResolvedLayoutSnapshotV1::FromJson(
|
||||
return s;
|
||||
}
|
||||
|
||||
ResolvedLayoutValidatorAuditV1 AuditResolvedLayoutValidatorV1()
|
||||
ResolvedLayoutValidatorAuditV2 AuditResolvedLayoutValidatorV2()
|
||||
{
|
||||
ResolvedLayoutValidatorAuditV1 result;
|
||||
ResolvedLayoutValidatorAuditV2 result;
|
||||
auto make=[](std::string id,std::int32_t order,std::int64_t top,std::int64_t bottom)
|
||||
{
|
||||
ResolvedLayoutStratumV1 s;s.stableId=std::move(id);s.order=order;s.strateIndex=order;s.generatorKind="synthetic";
|
||||
ResolvedLayoutStratumV2 s;s.stableId=std::move(id);s.order=order;s.strateIndex=order;s.generatorKind="synthetic";
|
||||
s.interiorTopMicrometres=top;s.interiorBottomMicrometres=bottom;s.voxelSizeMicrometres=250000;
|
||||
s.transitionMode="Gradient";s.transitionWidthMicrometres=4000000;
|
||||
s.transitionHonouredInCore=true;
|
||||
@@ -307,15 +322,15 @@ ResolvedLayoutValidatorAuditV1 AuditResolvedLayoutValidatorV1()
|
||||
s.bedrock={"bedrock",bottom-1000000,bottom,true,true,false,"breakable bedrock","lower face","none"};
|
||||
return s;
|
||||
};
|
||||
const auto expectReject=[&](std::string name,ResolvedLayoutSnapshotV1 candidate)
|
||||
const auto expectReject=[&](std::string name,ResolvedLayoutSnapshotV2 candidate)
|
||||
{
|
||||
++result.controlsRun;std::vector<ResolvedLayoutIssueV1> issues;std::string error;
|
||||
if(!candidate.validate(ResolvedLayoutValidationModeV1::strict,issues,error))++result.controlsRejectedAsExpected;
|
||||
++result.controlsRun;std::vector<ResolvedLayoutIssueV2> issues;std::string error;
|
||||
if(!candidate.validate(ResolvedLayoutValidationModeV2::strict,issues,error))++result.controlsRejectedAsExpected;
|
||||
else result.failures.push_back({"negative_control_accepted",std::move(name)+" was accepted by strict validator"});
|
||||
};
|
||||
auto base=ResolvedLayoutSnapshotV1{};base.worldIdentity="synthetic";base.strates={make("a",0,10000000,0),make("b",1,-2000000,-12000000)};
|
||||
{std::vector<ResolvedLayoutIssueV1> issues;std::string error;
|
||||
if(!base.validate(ResolvedLayoutValidationModeV1::strict,issues,error))
|
||||
auto base=ResolvedLayoutSnapshotV2{};base.worldIdentity="synthetic";base.strates={make("a",0,10000000,0),make("b",1,-2000000,-12000000)};
|
||||
{std::vector<ResolvedLayoutIssueV2> issues;std::string error;
|
||||
if(!base.validate(ResolvedLayoutValidationModeV2::strict,issues,error))
|
||||
result.failures.push_back({"positive_control_rejected","valid ceiling/content/bedrock layout was rejected"});}
|
||||
auto boundaryGap=base;boundaryGap.strates[1].ceiling.topMicrometres-=1;
|
||||
expectReject("bedrock/next-ceiling gap",boundaryGap);
|
||||
|
||||
Reference in new issue
Block a user