feat(core): ore vein records and a materialAt query, off by default (L50); research R18, R19
Versioned capsule vein records (stable ID, resource ID, endpoints, radius, seeded irregularity) resolved into the snapshot only when requested, placed beside the route and away from passages, landings and landmarks (forced-passage control rejected). The bound field table gains materialAt (host + two resource IDs + Q16 weights) behind a per-tile BVH; mesh export v2 carries per-vertex materials; a measurement-only yield computes removed resource volume (0 on already-air space). With veins on, every density, mesh, payload and topology hash is unchanged; demo off: owner hashes identical to L47. fast/medium/full PASS. Research R18 (cavern-graph planner) and R19 (why FloatingIslands costs ~10x SurfaceWorld). 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
b9ccff4ad6
commit
ca85e876ad
15 files changed
+1573
-19
No files matched your search
@@ -283,6 +283,187 @@ namespace
|
||||
}
|
||||
return true;
|
||||
}
|
||||
double LayoutSegmentDistance(const std::array<double,3>& p0,
|
||||
const std::array<double,3>& p1,const std::array<double,3>& q0,
|
||||
const std::array<double,3>& q1)
|
||||
{
|
||||
std::array<double,3> u{},v{},w{};
|
||||
double a=0.0,b=0.0,c=0.0,d=0.0,e=0.0;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
u[axis]=p1[axis]-p0[axis];v[axis]=q1[axis]-q0[axis];
|
||||
w[axis]=p0[axis]-q0[axis];
|
||||
a+=u[axis]*u[axis];b+=u[axis]*v[axis];
|
||||
c+=v[axis]*v[axis];d+=u[axis]*w[axis];e+=v[axis]*w[axis];
|
||||
}
|
||||
const double denominator=a*c-b*b;
|
||||
double sN=0.0,sD=denominator,tN=0.0,tD=denominator;
|
||||
if(denominator<1.0e-18){sN=0.0;sD=1.0;tN=e;tD=c;}
|
||||
else
|
||||
{
|
||||
sN=b*e-c*d;tN=a*e-b*d;
|
||||
if(sN<0.0){sN=0.0;tN=e;tD=c;}
|
||||
else if(sN>sD){sN=sD;tN=e+b;tD=c;}
|
||||
}
|
||||
if(tN<0.0)
|
||||
{
|
||||
tN=0.0;
|
||||
if(-d<0.0)sN=0.0;
|
||||
else if(-d>a)sN=sD;
|
||||
else{sN=-d;sD=a;}
|
||||
}
|
||||
else if(tN>tD)
|
||||
{
|
||||
tN=tD;
|
||||
if(-d+b<0.0)sN=0.0;
|
||||
else if(-d+b>a)sN=sD;
|
||||
else{sN=-d+b;sD=a;}
|
||||
}
|
||||
const double s=std::abs(sN)<1.0e-18?0.0:sN/sD;
|
||||
const double t=std::abs(tN)<1.0e-18?0.0:tN/tD;
|
||||
double distanceSquared=0.0;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
const double delta=w[axis]+s*u[axis]-t*v[axis];
|
||||
distanceSquared+=delta*delta;
|
||||
}
|
||||
return std::sqrt(std::max(0.0,distanceSquared));
|
||||
}
|
||||
std::array<double,3> LayoutOrePointUm(const std::array<std::int64_t,3>& point)
|
||||
{return {static_cast<double>(point[0]),static_cast<double>(point[1]),
|
||||
static_cast<double>(point[2])};}
|
||||
bool LayoutOreVeinSafe(const ResolvedLayoutSnapshotV3& layout,
|
||||
const ResolvedLayoutOreVeinV1& vein,std::string* reason=nullptr)
|
||||
{
|
||||
const auto fail=[&](const char* text)
|
||||
{if(reason)*reason=text;return false;};
|
||||
const auto strate=std::find_if(layout.strates.begin(),layout.strates.end(),
|
||||
[&](const auto& value){return value.strateIndex==vein.strateIndex;});
|
||||
if(strate==layout.strates.end()||!strate->content.presentInCurrentField)
|
||||
return fail("vein strate has no current content band");
|
||||
if(vein.boundsMinimumMicrometres[2]<=strate->content.bottomMicrometres
|
||||
||vein.boundsMaximumMicrometres[2]>=strate->content.topMicrometres)
|
||||
return fail("vein bounds touch or cross the ceiling/bedrock content faces");
|
||||
for(const auto* band:{&strate->ceiling,&strate->bedrock})
|
||||
if(band->presentInCurrentField
|
||||
&&vein.boundsMinimumMicrometres[2]<band->topMicrometres
|
||||
&&vein.boundsMaximumMicrometres[2]>band->bottomMicrometres)
|
||||
return fail("vein bounds overlap a ceiling or bedrock band");
|
||||
const auto veinA=LayoutOrePointUm(vein.endpointsMicrometres[0]);
|
||||
const auto veinB=LayoutOrePointUm(vein.endpointsMicrometres[1]);
|
||||
const double coreRadius=static_cast<double>(vein.radiusMicrometres
|
||||
+vein.irregularityAmplitudeMicrometres+vein.blendWidthMicrometres);
|
||||
const double coreMicrometres=std::max<std::uint32_t>(1,layout.coreVoxelSizeMicrometres);
|
||||
constexpr double RouteClearanceMicrometres=100000.0;
|
||||
const auto qToUm=[&](const std::array<std::int64_t,3>& q)
|
||||
{return std::array<double,3>{q[0]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel,
|
||||
q[1]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel,
|
||||
q[2]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel};};
|
||||
for(const auto& passage:layout.passages)
|
||||
{
|
||||
if(passage.upperStrateIndex!=vein.strateIndex
|
||||
&&passage.lowerStrateIndex!=vein.strateIndex)continue;
|
||||
const auto upper=qToUm(passage.upperMouthSubvoxels);
|
||||
const auto lower=qToUm(passage.lowerMouthSubvoxels);
|
||||
const double mouthRadius=passage.mouthRadiusSubvoxels*coreMicrometres
|
||||
/WorldAssemblySubvoxelsPerVoxel;
|
||||
if(LayoutSegmentDistance(veinA,veinB,upper,upper)
|
||||
<=coreRadius+mouthRadius+RouteClearanceMicrometres
|
||||
||LayoutSegmentDistance(veinA,veinB,lower,lower)
|
||||
<=coreRadius+mouthRadius+RouteClearanceMicrometres)
|
||||
return fail("vein overlaps a clear passage landing");
|
||||
for(std::size_t i=0;i+1<passage.controlPoints.size();++i)
|
||||
{
|
||||
const auto a=qToUm(passage.controlPoints[i].positionSubvoxels);
|
||||
const auto b=qToUm(passage.controlPoints[i+1].positionSubvoxels);
|
||||
const double radius=std::max(passage.controlPoints[i].radiusSubvoxels,
|
||||
passage.controlPoints[i+1].radiusSubvoxels)*coreMicrometres
|
||||
/WorldAssemblySubvoxelsPerVoxel;
|
||||
if(LayoutSegmentDistance(veinA,veinB,a,b)
|
||||
<=coreRadius+radius+RouteClearanceMicrometres)
|
||||
return fail("vein enters a clear passage corridor");
|
||||
}
|
||||
}
|
||||
std::vector<const ResolvedLayoutAnchorV2*> route;
|
||||
for(const auto& anchor:strate->anchors)
|
||||
{
|
||||
if(anchor.values.size()<3)continue;
|
||||
if(anchor.kind=="route_point")route.push_back(&anchor);
|
||||
if(anchor.kind.find("landing")!=std::string::npos)
|
||||
{
|
||||
const std::array<double,3> landing={
|
||||
anchor.values[0]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel,
|
||||
anchor.values[1]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel,
|
||||
anchor.values[2]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel};
|
||||
const double radius=anchor.values.size()>=4
|
||||
?anchor.values[3]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel:3000000.0;
|
||||
if(LayoutSegmentDistance(veinA,veinB,landing,landing)
|
||||
<=coreRadius+radius+RouteClearanceMicrometres)
|
||||
return fail("vein overlaps a resolved landing footprint");
|
||||
}
|
||||
}
|
||||
for(std::size_t i=0;i+1<route.size();++i)
|
||||
{
|
||||
const auto qPoint=[&](const ResolvedLayoutAnchorV2& anchor)
|
||||
{return std::array<double,3>{anchor.values[0]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel,
|
||||
anchor.values[1]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel,
|
||||
anchor.values[2]*coreMicrometres/WorldAssemblySubvoxelsPerVoxel};};
|
||||
const auto a=qPoint(*route[i]),b=qPoint(*route[i+1]);
|
||||
const double routeRadius=std::max(route[i]->values.size()>=4?route[i]->values[3]:0,
|
||||
route[i+1]->values.size()>=4?route[i+1]->values[3]:0)
|
||||
*coreMicrometres/WorldAssemblySubvoxelsPerVoxel;
|
||||
if(LayoutSegmentDistance(veinA,veinB,a,b)
|
||||
<=coreRadius+routeRadius+RouteClearanceMicrometres)
|
||||
return fail("vein enters a clear strate route corridor");
|
||||
}
|
||||
for(const auto& room:layout.cavernRooms)
|
||||
if(room.strateIndex==vein.strateIndex)
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
if(vein.boundsMinimumMicrometres[axis]
|
||||
<=room.boundsMaximumMicrometres[axis]
|
||||
&&vein.boundsMaximumMicrometres[axis]
|
||||
>=room.boundsMinimumMicrometres[axis])
|
||||
return fail("vein overlaps a cavern landmark footprint");
|
||||
for(const auto& edge:layout.cavernEdges)
|
||||
{
|
||||
if(edge.strateIndex!=vein.strateIndex)continue;
|
||||
std::array<double,3> previous{};
|
||||
const auto bezier=[&](double t)
|
||||
{
|
||||
const double one=1.0-t;
|
||||
const std::array<double,4> weights={one*one*one,3.0*one*one*t,
|
||||
3.0*one*t*t,t*t*t};
|
||||
std::array<double,3> point{};
|
||||
for(std::size_t i=0;i<4;++i)
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
point[axis]+=weights[i]*edge.controlPointsMicrometres[i][axis];
|
||||
return point;
|
||||
};
|
||||
previous=bezier(0.0);
|
||||
for(int step=1;step<=256;++step)
|
||||
{
|
||||
const auto next=bezier(static_cast<double>(step)/256.0);
|
||||
if(LayoutSegmentDistance(veinA,veinB,previous,next)
|
||||
<=coreRadius+edge.boreRadiusMicrometres+RouteClearanceMicrometres)
|
||||
return fail("vein enters a landmark passage corridor");
|
||||
previous=next;
|
||||
}
|
||||
}
|
||||
for(const auto& stamp:layout.stamps)
|
||||
{
|
||||
if(stamp.strateIndex!=vein.strateIndex
|
||||
||(stamp.kind!=ResolvedSdfStampKindV1::fillDais
|
||||
&&stamp.kind!=ResolvedSdfStampKindV1::fillPillar))continue;
|
||||
bool overlaps=true;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
overlaps=overlaps&&vein.boundsMinimumMicrometres[axis]
|
||||
<=stamp.boundsMaximumMicrometres[axis]
|
||||
&&vein.boundsMaximumMicrometres[axis]
|
||||
>=stamp.boundsMinimumMicrometres[axis];
|
||||
if(overlaps)return fail("vein overlaps a resolved landmark solid footprint");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
std::string LayoutHex(std::uint64_t value)
|
||||
{std::ostringstream out;out<<std::hex<<std::setfill('0')<<std::setw(16)<<value;return out.str();}
|
||||
std::string LayoutBandJson(const ResolvedLayoutBandV2& band)
|
||||
@@ -454,6 +635,32 @@ namespace
|
||||
}
|
||||
out<<']';
|
||||
}
|
||||
if(!s.oreVeins.empty())
|
||||
{
|
||||
out<<",\"ore_vein_vocabulary_version\":"<<s.oreVeinVocabularyVersion
|
||||
<<",\"ore_veins\":[";
|
||||
for(std::size_t i=0;i<s.oreVeins.size();++i)
|
||||
{
|
||||
if(i)out<<',';const auto& vein=s.oreVeins[i];
|
||||
out<<"{\"stable_id\":"<<LayoutEscape(vein.stableId)
|
||||
<<",\"strate_index\":"<<vein.strateIndex
|
||||
<<",\"resource_material_id\":"<<vein.resourceMaterialId
|
||||
<<",\"endpoints_um\":[["<<vein.endpointsMicrometres[0][0]<<','
|
||||
<<vein.endpointsMicrometres[0][1]<<','<<vein.endpointsMicrometres[0][2]
|
||||
<<"],["<<vein.endpointsMicrometres[1][0]<<','
|
||||
<<vein.endpointsMicrometres[1][1]<<','<<vein.endpointsMicrometres[1][2]
|
||||
<<"]],\"bounds_min_um\":["<<vein.boundsMinimumMicrometres[0]<<','
|
||||
<<vein.boundsMinimumMicrometres[1]<<','<<vein.boundsMinimumMicrometres[2]
|
||||
<<"],\"bounds_max_um\":["<<vein.boundsMaximumMicrometres[0]<<','
|
||||
<<vein.boundsMaximumMicrometres[1]<<','<<vein.boundsMaximumMicrometres[2]
|
||||
<<"],\"radius_um\":"<<vein.radiusMicrometres
|
||||
<<",\"irregularity_amplitude_um\":"<<vein.irregularityAmplitudeMicrometres
|
||||
<<",\"irregularity_wavelength_um\":"<<vein.irregularityWavelengthMicrometres
|
||||
<<",\"blend_width_um\":"<<vein.blendWidthMicrometres
|
||||
<<",\"irregularity_seed\":"<<vein.irregularitySeed<<'}';
|
||||
}
|
||||
out<<']';
|
||||
}
|
||||
out<<'}';return out.str();
|
||||
}
|
||||
std::int64_t LayoutInt(const VFJson::Value& obj,std::string_view key)
|
||||
@@ -620,6 +827,8 @@ bool ResolvedLayoutSnapshotV3::finalize(std::string& error)
|
||||
{return a.stableId<b.stableId;});
|
||||
std::sort(cavernEdges.begin(),cavernEdges.end(),[](const auto& a,const auto& b)
|
||||
{return a.stableId<b.stableId;});
|
||||
std::sort(oreVeins.begin(),oreVeins.end(),[](const auto& a,const auto& b)
|
||||
{return a.stableId<b.stableId;});
|
||||
for(std::size_t i=0;i<strates.size();++i)
|
||||
{
|
||||
auto& x=strates[i];x.order=static_cast<std::int32_t>(i);
|
||||
@@ -1028,6 +1237,55 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
if(oreVeins.empty())
|
||||
{
|
||||
if(oreVeinVocabularyVersion!=0)
|
||||
add("ore_vein_vocabulary_version",
|
||||
"ore-vein vocabulary version is set without records");
|
||||
}
|
||||
else if(oreVeinVocabularyVersion!=ResolvedLayoutOreVeinVocabularyVersionV1)
|
||||
add("ore_vein_vocabulary_version",
|
||||
"ore-vein records require vocabulary version 1");
|
||||
std::vector<std::string> oreVeinIds;
|
||||
for(const auto& vein:oreVeins)
|
||||
{
|
||||
if(vein.stableId.empty()
|
||||
||std::find(oreVeinIds.begin(),oreVeinIds.end(),vein.stableId)!=oreVeinIds.end())
|
||||
add("ore_vein_identity","ore vein has an empty or duplicate stable identity");
|
||||
else oreVeinIds.push_back(vein.stableId);
|
||||
if(vein.resourceMaterialId==0||vein.radiusMicrometres<250000
|
||||
||vein.radiusMicrometres>20000000
|
||||
||vein.irregularityAmplitudeMicrometres<0
|
||||
||vein.irregularityAmplitudeMicrometres>10000000
|
||||
||vein.irregularityWavelengthMicrometres<0
|
||||
||vein.irregularityWavelengthMicrometres>100000000
|
||||
||(vein.irregularityAmplitudeMicrometres>0
|
||||
&&vein.irregularityWavelengthMicrometres<=0)
|
||||
||vein.blendWidthMicrometres<50000
|
||||
||vein.blendWidthMicrometres>2000000
|
||||
||vein.irregularitySeed>0x001fffffffffffffull)
|
||||
add("ore_vein_parameters",vein.stableId+" has invalid or unbounded parameters");
|
||||
const std::int64_t pad=vein.radiusMicrometres
|
||||
+vein.irregularityAmplitudeMicrometres+vein.blendWidthMicrometres;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
if(vein.endpointsMicrometres[0][axis]<-1000000000000ll
|
||||
||vein.endpointsMicrometres[0][axis]>1000000000000ll
|
||||
||vein.endpointsMicrometres[1][axis]<-1000000000000ll
|
||||
||vein.endpointsMicrometres[1][axis]>1000000000000ll)
|
||||
add("ore_vein_bounds",vein.stableId+" endpoint is outside the bounded world envelope");
|
||||
if(vein.boundsMinimumMicrometres[axis]
|
||||
!=std::min(vein.endpointsMicrometres[0][axis],
|
||||
vein.endpointsMicrometres[1][axis])-pad
|
||||
||vein.boundsMaximumMicrometres[axis]
|
||||
!=std::max(vein.endpointsMicrometres[0][axis],
|
||||
vein.endpointsMicrometres[1][axis])+pad)
|
||||
add("ore_vein_bounds",vein.stableId+" conservative bounds do not match its parameters");
|
||||
}
|
||||
std::string safetyReason;
|
||||
if(!LayoutOreVeinSafe(*this,vein,&safetyReason))
|
||||
add("ore_vein_placement",vein.stableId+" is unsafe: "+safetyReason);
|
||||
}
|
||||
if(strict&&!issues.empty()){error="strict resolved-layout validation rejected "+issues.front().code;return false;}
|
||||
return true;
|
||||
}
|
||||
@@ -1046,6 +1304,8 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
|
||||
s.stampVocabularyVersion=static_cast<std::uint32_t>(LayoutInt(root,"stamp_vocabulary_version"));
|
||||
s.cavernGraphVocabularyVersion=static_cast<std::uint32_t>(
|
||||
LayoutInt(root,"cavern_graph_vocabulary_version"));
|
||||
s.oreVeinVocabularyVersion=static_cast<std::uint32_t>(
|
||||
LayoutInt(root,"ore_vein_vocabulary_version"));
|
||||
for(const auto& v:root.Get("strates").AsArray())
|
||||
{
|
||||
ResolvedLayoutStratumV2 x;x.stableId=LayoutString(v,"stable_id");
|
||||
@@ -1166,6 +1426,33 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
|
||||
edge.irregularitySeed=static_cast<std::uint64_t>(LayoutInt(v,"irregularity_seed"));
|
||||
s.cavernEdges.push_back(std::move(edge));
|
||||
}
|
||||
for(const auto& v:root.Get("ore_veins").AsArray())
|
||||
{
|
||||
ResolvedLayoutOreVeinV1 vein;vein.stableId=LayoutString(v,"stable_id");
|
||||
vein.strateIndex=static_cast<std::int32_t>(LayoutInt(v,"strate_index"));
|
||||
vein.resourceMaterialId=static_cast<std::uint16_t>(LayoutInt(v,"resource_material_id"));
|
||||
const auto& endpoints=v.Get("endpoints_um").AsArray();
|
||||
for(std::size_t endpoint=0;endpoint<2&&endpoint<endpoints.size();++endpoint)
|
||||
{
|
||||
const auto& values=endpoints[endpoint].AsArray();
|
||||
for(std::size_t axis=0;axis<3&&axis<values.size();++axis)
|
||||
vein.endpointsMicrometres[endpoint][axis]=values[axis].Integer();
|
||||
}
|
||||
const auto readTriplet=[&](std::string_view key,std::array<std::int64_t,3>& target)
|
||||
{
|
||||
const auto& values=v.Get(key).AsArray();
|
||||
for(std::size_t axis=0;axis<3&&axis<values.size();++axis)
|
||||
target[axis]=values[axis].Integer();
|
||||
};
|
||||
readTriplet("bounds_min_um",vein.boundsMinimumMicrometres);
|
||||
readTriplet("bounds_max_um",vein.boundsMaximumMicrometres);
|
||||
vein.radiusMicrometres=LayoutInt(v,"radius_um");
|
||||
vein.irregularityAmplitudeMicrometres=LayoutInt(v,"irregularity_amplitude_um");
|
||||
vein.irregularityWavelengthMicrometres=LayoutInt(v,"irregularity_wavelength_um");
|
||||
vein.blendWidthMicrometres=LayoutInt(v,"blend_width_um");
|
||||
vein.irregularitySeed=static_cast<std::uint64_t>(LayoutInt(v,"irregularity_seed"));
|
||||
s.oreVeins.push_back(std::move(vein));
|
||||
}
|
||||
const std::string serializedIdentity=LayoutString(root,"stable_identity");
|
||||
if(!s.finalize(error))return std::nullopt;
|
||||
if(s.stableIdentity!=serializedIdentity){error="resolved layout stable identity does not match its canonical payload";return std::nullopt;}
|
||||
@@ -1352,6 +1639,148 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
layout=std::move(candidate);return true;
|
||||
}
|
||||
|
||||
bool ResolveResolvedLayoutOreVeinsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t seed,
|
||||
std::span<const ResolvedLayoutOreVeinRequestV1> requests,std::string& error)
|
||||
{
|
||||
error.clear();
|
||||
ResolvedLayoutSnapshotV3 candidate=layout;
|
||||
candidate.oreVeins.clear();candidate.oreVeinVocabularyVersion=0;
|
||||
if(requests.empty())
|
||||
{
|
||||
if(!candidate.finalize(error))return false;
|
||||
layout=std::move(candidate);return true;
|
||||
}
|
||||
candidate.oreVeinVocabularyVersion=ResolvedLayoutOreVeinVocabularyVersionV1;
|
||||
std::vector<ResolvedLayoutOreVeinRequestV1> ordered(requests.begin(),requests.end());
|
||||
std::sort(ordered.begin(),ordered.end(),[](const auto& a,const auto& b)
|
||||
{return a.stableId<b.stableId;});
|
||||
std::vector<std::string> requestIds;
|
||||
candidate.oreVeins.reserve(ordered.size());
|
||||
for(const auto& request:ordered)
|
||||
{
|
||||
if(request.stableId.empty()
|
||||
||std::find(requestIds.begin(),requestIds.end(),request.stableId)!=requestIds.end())
|
||||
{error="ore-vein requests require non-empty unique stable IDs";return false;}
|
||||
requestIds.push_back(request.stableId);
|
||||
if(request.resourceMaterialId==0||request.placementRadiusMicrometres<0
|
||||
||request.placementRadiusMicrometres>10000000
|
||||
||request.radiusMicrometres<250000||request.radiusMicrometres>20000000
|
||||
||request.irregularityAmplitudeMicrometres<0
|
||||
||request.irregularityAmplitudeMicrometres>10000000
|
||||
||request.irregularityWavelengthMicrometres<0
|
||||
||request.irregularityWavelengthMicrometres>100000000
|
||||
||(request.irregularityAmplitudeMicrometres>0
|
||||
&&request.irregularityWavelengthMicrometres<=0)
|
||||
||request.blendWidthMicrometres<50000
|
||||
||request.blendWidthMicrometres>2000000)
|
||||
{error="ore-vein request "+request.stableId+" has invalid or unbounded parameters";return false;}
|
||||
const auto strate=std::find_if(candidate.strates.begin(),candidate.strates.end(),
|
||||
[&](const auto& value){return value.strateIndex==request.strateIndex;});
|
||||
if(strate==candidate.strates.end())
|
||||
{error="ore-vein request "+request.stableId+" names an unknown strate";return false;}
|
||||
|
||||
const std::uint64_t baseSeed=LayoutStampSeed(seed,request.strateIndex,
|
||||
std::string("ore-vein/")+request.stableId)&0x001fffffffffffffull;
|
||||
std::uint64_t state=baseSeed;
|
||||
const auto randomOffset=[&](std::uint64_t bits)
|
||||
{
|
||||
const std::int64_t radius=request.placementRadiusMicrometres;
|
||||
if(radius==0)return std::int64_t{0};
|
||||
const std::uint64_t span=static_cast<std::uint64_t>(radius)*2+1;
|
||||
return static_cast<std::int64_t>(bits%span)-radius;
|
||||
};
|
||||
bool placed=false;
|
||||
std::string lastUnsafeReason="all candidates were outside the bounded content volume";
|
||||
const std::uint32_t attempts=request.placementRadiusMicrometres==0?1u:512u;
|
||||
for(std::uint32_t attempt=0;attempt<attempts;++attempt)
|
||||
{
|
||||
std::array<std::int64_t,3> offset{};
|
||||
if(request.placementRadiusMicrometres>0)
|
||||
{
|
||||
offset={randomOffset(LayoutStampNext(state)),
|
||||
randomOffset(LayoutStampNext(state)),
|
||||
randomOffset(LayoutStampNext(state))/3};
|
||||
const long double radius=request.placementRadiusMicrometres;
|
||||
const long double distanceSquared=static_cast<long double>(offset[0])*offset[0]
|
||||
+static_cast<long double>(offset[1])*offset[1]
|
||||
+static_cast<long double>(offset[2])*offset[2];
|
||||
if(distanceSquared>radius*radius)continue;
|
||||
}
|
||||
ResolvedLayoutOreVeinV1 vein;vein.stableId=request.stableId;
|
||||
vein.strateIndex=request.strateIndex;
|
||||
vein.resourceMaterialId=request.resourceMaterialId;
|
||||
vein.radiusMicrometres=request.radiusMicrometres;
|
||||
vein.irregularityAmplitudeMicrometres=request.irregularityAmplitudeMicrometres;
|
||||
vein.irregularityWavelengthMicrometres=request.irregularityWavelengthMicrometres;
|
||||
vein.blendWidthMicrometres=request.blendWidthMicrometres;
|
||||
vein.irregularitySeed=baseSeed;
|
||||
for(std::size_t endpoint=0;endpoint<2;++endpoint)
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
vein.endpointsMicrometres[endpoint][axis]
|
||||
=request.anchorEndpointsMicrometres[endpoint][axis]+offset[axis];
|
||||
const std::int64_t pad=vein.radiusMicrometres
|
||||
+vein.irregularityAmplitudeMicrometres+vein.blendWidthMicrometres;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
vein.boundsMinimumMicrometres[axis]=std::min(
|
||||
vein.endpointsMicrometres[0][axis],vein.endpointsMicrometres[1][axis])-pad;
|
||||
vein.boundsMaximumMicrometres[axis]=std::max(
|
||||
vein.endpointsMicrometres[0][axis],vein.endpointsMicrometres[1][axis])+pad;
|
||||
}
|
||||
if(!LayoutOreVeinSafe(candidate,vein,&lastUnsafeReason))continue;
|
||||
candidate.oreVeins.push_back(std::move(vein));placed=true;break;
|
||||
}
|
||||
if(!placed)
|
||||
{error="ore-vein request "+request.stableId
|
||||
+" has no deterministic passage-safe content placement: "+lastUnsafeReason;return false;}
|
||||
}
|
||||
if(!candidate.finalize(error))return false;
|
||||
std::vector<ResolvedLayoutIssueV2> issues;std::string strictError;
|
||||
if(!candidate.validate(ResolvedLayoutValidationModeV3::strict,issues,strictError))
|
||||
{error="strict ore-vein placement validation failed: "+strictError;return false;}
|
||||
layout=std::move(candidate);return true;
|
||||
}
|
||||
|
||||
ResolvedLayoutOreVeinPlacementAuditV1 AuditResolvedLayoutOreVeinPlacementV1(
|
||||
const ResolvedLayoutSnapshotV3& layout)
|
||||
{
|
||||
ResolvedLayoutOreVeinPlacementAuditV1 result;
|
||||
if(layout.passages.empty()||layout.passages.front().controlPoints.empty())return result;
|
||||
const auto& passage=layout.passages.front();
|
||||
const auto& control=passage.controlPoints.front().positionSubvoxels;
|
||||
ResolvedLayoutOreVeinV1 forced;forced.stableId="negative-control/forced-clear-passage";
|
||||
forced.strateIndex=passage.upperStrateIndex;forced.resourceMaterialId=1;
|
||||
forced.radiusMicrometres=500000;forced.blendWidthMicrometres=500000;
|
||||
forced.irregularitySeed=1;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
const std::int64_t point=static_cast<std::int64_t>(std::llround(
|
||||
static_cast<double>(control[axis])*layout.coreVoxelSizeMicrometres
|
||||
/WorldAssemblySubvoxelsPerVoxel));
|
||||
forced.endpointsMicrometres[0][axis]=point;
|
||||
forced.endpointsMicrometres[1][axis]=point;
|
||||
forced.boundsMinimumMicrometres[axis]=point-1000000;
|
||||
forced.boundsMaximumMicrometres[axis]=point+1000000;
|
||||
}
|
||||
ResolvedLayoutSnapshotV3 candidate=layout;
|
||||
candidate.oreVeinVocabularyVersion=ResolvedLayoutOreVeinVocabularyVersionV1;
|
||||
candidate.oreVeins.push_back(forced);
|
||||
std::string finalizeError;
|
||||
++result.negativeControlsRun;
|
||||
if(candidate.finalize(finalizeError))
|
||||
{
|
||||
std::vector<ResolvedLayoutIssueV2> issues;std::string validationError;
|
||||
if(!candidate.validate(ResolvedLayoutValidationModeV3::strict,issues,validationError)
|
||||
&&std::any_of(issues.begin(),issues.end(),[](const auto& issue)
|
||||
{return issue.code=="ore_vein_placement"
|
||||
&&issue.detail.find("clear passage")!=std::string::npos;}))
|
||||
++result.negativeControlsRejected;
|
||||
}
|
||||
result.passed=result.negativeControlsRun>0
|
||||
&&result.negativeControlsRejected==result.negativeControlsRun;
|
||||
return result;
|
||||
}
|
||||
|
||||
ResolvedLayoutStampPlacementAuditV1 AuditResolvedLayoutStampPlacementV1(
|
||||
const ResolvedLayoutSnapshotV3& layout)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user