feat(core): native lattice generator, slice 1 -- helix-ramp spine + small maze (L58); research R27
Core.Lattice.PrimitiveField v1, opt-in per strate (legacy Maze/VerticalShaft stay legacy): resolves the real arrival and departure mouths first, plans a mandatory spine, then maze cells and two dead ends (9 rooms, 9 edges, stable IDs). The spine descends 20 m through four helix turns (6 m radius, 3 m clear width, 5 m pitch, 2.6 m clearance; max grade 10.0 deg); each feature is one filleted void (0.57 m measured). Final-field reachability passes at 883 stations; an orphaned shaft control is rejected; exact intervals (0 misses) and a whole-surface audit with 0 flags. Whole view 1.69 s (above the 1 s target). fast/medium/full PASS. Research R27 (self-checked): static stream-to-pool water design. 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
1e3e04bbac
commit
6c6cc37e1d
25 files changed
+280497
-117
No files matched your search
@@ -159,6 +159,16 @@ namespace
|
||||
for(unsigned char c:stableId){hash^=c;hash*=1099511628211ull;}
|
||||
return LayoutStampMix(hash);
|
||||
}
|
||||
std::uint64_t LayoutLatticeFeatureSeed(std::int32_t seed,std::int32_t strate,
|
||||
std::uint32_t recipeVersion,std::string_view stableId)
|
||||
{
|
||||
std::uint64_t hash=14695981039346656037ull;
|
||||
TransitionHashWord(hash,static_cast<std::uint32_t>(seed));
|
||||
TransitionHashWord(hash,static_cast<std::uint32_t>(strate));
|
||||
TransitionHashWord(hash,recipeVersion);
|
||||
for(unsigned char c:stableId){hash^=c;hash*=1099511628211ull;}
|
||||
return LayoutStampMix(hash);
|
||||
}
|
||||
std::uint64_t LayoutStampNext(std::uint64_t& state)
|
||||
{state=LayoutStampMix(state+0x9e3779b97f4a7c15ull);return state;}
|
||||
bool LayoutStampKindValid(ResolvedSdfStampKindV1 kind)
|
||||
@@ -611,7 +621,10 @@ namespace
|
||||
<<room.halfExtentsMicrometres[1]<<','<<room.halfExtentsMicrometres[2]
|
||||
<<"],\"floor_um\":"<<room.floorMicrometres
|
||||
<<",\"smooth_width_um\":"<<room.smoothWidthMicrometres
|
||||
<<",\"irregularity_amplitude_um\":"<<room.irregularityAmplitudeMicrometres
|
||||
;
|
||||
if(room.unionWidthMicrometres>0)
|
||||
out<<",\"union_width_um\":"<<room.unionWidthMicrometres;
|
||||
out<<",\"irregularity_amplitude_um\":"<<room.irregularityAmplitudeMicrometres
|
||||
<<",\"irregularity_wavelength_um\":"<<room.irregularityWavelengthMicrometres
|
||||
<<",\"irregularity_seed\":"<<room.irregularitySeed
|
||||
<<",\"bounds_min_um\":["<<room.boundsMinimumMicrometres[0]<<','
|
||||
@@ -646,8 +659,10 @@ namespace
|
||||
out<<",\"bore_radius_um\":"<<edge.boreRadiusMicrometres
|
||||
<<",\"bore_height_um\":"<<edge.boreHeightMicrometres
|
||||
<<",\"floor_support_depth_um\":"<<edge.floorSupportDepthMicrometres
|
||||
<<",\"smooth_width_um\":"<<edge.smoothWidthMicrometres
|
||||
<<",\"irregularity_amplitude_um\":"<<edge.irregularityAmplitudeMicrometres
|
||||
<<",\"smooth_width_um\":"<<edge.smoothWidthMicrometres;
|
||||
if(edge.unionWidthMicrometres>0)
|
||||
out<<",\"union_width_um\":"<<edge.unionWidthMicrometres;
|
||||
out<<",\"irregularity_amplitude_um\":"<<edge.irregularityAmplitudeMicrometres
|
||||
<<",\"irregularity_wavelength_um\":"<<edge.irregularityWavelengthMicrometres
|
||||
<<",\"irregularity_seed\":"<<edge.irregularitySeed
|
||||
<<",\"bounds_min_um\":["<<edge.boundsMinimumMicrometres[0]<<','
|
||||
@@ -867,8 +882,10 @@ bool ResolvedLayoutSnapshotV3::finalize(std::string& error)
|
||||
x.lowerInterfaceCapabilities={};
|
||||
if(x.generatorKind=="SurfaceWorld")
|
||||
x.passageCapabilities.hasSurfaceLanding=true;
|
||||
else if(x.generatorKind=="Caves")
|
||||
else if(IsNativeCaveFieldStratum(x))
|
||||
x.passageCapabilities.hasNativeCaveField=true;
|
||||
else if(IsNativeLatticeFieldStratum(x))
|
||||
x.passageCapabilities.hasNativeLatticeField=true;
|
||||
else if(x.generatorKind=="FloatingIslands")
|
||||
{
|
||||
x.passageCapabilities.hasFloatingIslandLanding=true;
|
||||
@@ -1245,6 +1262,7 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
||||
validExtents=validExtents&&extent>=500000&&extent<=100000000;
|
||||
if(!validExtents||room.smoothWidthMicrometres<0
|
||||
||room.smoothWidthMicrometres>10000000
|
||||
||room.unionWidthMicrometres<0||room.unionWidthMicrometres>10000000
|
||||
||room.irregularityAmplitudeMicrometres<0
|
||||
||room.irregularityAmplitudeMicrometres>10000000
|
||||
||room.irregularityWavelengthMicrometres<0
|
||||
@@ -1253,7 +1271,7 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
||||
&&room.irregularityWavelengthMicrometres<=0))
|
||||
add("cavern_room_parameters",room.stableId+" has invalid or unbounded parameters");
|
||||
const std::int64_t pad=room.smoothWidthMicrometres
|
||||
+room.irregularityAmplitudeMicrometres;
|
||||
+room.unionWidthMicrometres+room.irregularityAmplitudeMicrometres;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
if(room.boundsMinimumMicrometres[axis]
|
||||
!=room.centerMicrometres[axis]-room.halfExtentsMicrometres[axis]-pad
|
||||
@@ -1276,11 +1294,15 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
||||
add("cavern_edge_binding",edge.stableId+" does not terminate at a room in its strate");
|
||||
if(!findStratum(edge.strateIndex))
|
||||
add("cavern_graph_strate",edge.stableId+" names an unknown strate");
|
||||
const auto* edgeStratum=findStratum(edge.strateIndex);
|
||||
const bool nativeLattice=edgeStratum
|
||||
&&IsNativeLatticeFieldStratum(*edgeStratum);
|
||||
if(edge.boreRadiusMicrometres<500000||edge.boreRadiusMicrometres>10000000
|
||||
||edge.boreHeightMicrometres<1000000||edge.boreHeightMicrometres>20000000
|
||||
||edge.floorSupportDepthMicrometres<0
|
||||
||edge.floorSupportDepthMicrometres>2000000
|
||||
||edge.smoothWidthMicrometres<0||edge.smoothWidthMicrometres>10000000
|
||||
||edge.unionWidthMicrometres<0||edge.unionWidthMicrometres>10000000
|
||||
||edge.irregularityAmplitudeMicrometres<0
|
||||
||edge.irregularityAmplitudeMicrometres>10000000
|
||||
||edge.irregularityWavelengthMicrometres<0
|
||||
@@ -1290,13 +1312,21 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
||||
add("cavern_edge_parameters",edge.stableId+" has invalid or unbounded parameters");
|
||||
std::array<std::int64_t,3> low=edge.controlPointsMicrometres[0];
|
||||
std::array<std::int64_t,3> high=low;
|
||||
for(const auto& point:edge.controlPointsMicrometres)
|
||||
if(nativeLattice&&!edge.polylineMicrometres.empty())
|
||||
{low=edge.polylineMicrometres.front();high=low;}
|
||||
const auto includeBoundPoint=[&](const auto& point)
|
||||
{
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{low[axis]=std::min(low[axis],point[axis]);high[axis]=std::max(high[axis],point[axis]);}
|
||||
};
|
||||
if(nativeLattice&&!edge.polylineMicrometres.empty())
|
||||
for(const auto& point:edge.polylineMicrometres)includeBoundPoint(point);
|
||||
else for(const auto& point:edge.controlPointsMicrometres)includeBoundPoint(point);
|
||||
const std::int64_t xyPad=edge.boreRadiusMicrometres
|
||||
+edge.smoothWidthMicrometres+edge.irregularityAmplitudeMicrometres;
|
||||
const std::int64_t zPad=edge.smoothWidthMicrometres
|
||||
+edge.smoothWidthMicrometres+edge.unionWidthMicrometres
|
||||
+edge.irregularityAmplitudeMicrometres;
|
||||
const std::int64_t zPad=edge.smoothWidthMicrometres
|
||||
+edge.unionWidthMicrometres+edge.irregularityAmplitudeMicrometres;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
const std::int64_t pad=axis==2?zPad:xyPad;
|
||||
@@ -1314,17 +1344,26 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
||||
||endpoint[1]<room->centerMicrometres[1]-room->halfExtentsMicrometres[1]
|
||||
||endpoint[1]>room->centerMicrometres[1]+room->halfExtentsMicrometres[1]
|
||||
||std::abs(endpoint[2]-room->floorMicrometres)>500000)
|
||||
add("cavern_edge_portal",edge.stableId+" endpoint does not meet its room floor portal");
|
||||
{
|
||||
add("cavern_edge_portal",edge.stableId+" endpoint_um=("
|
||||
+std::to_string(endpoint[0])+","+std::to_string(endpoint[1])+","+
|
||||
std::to_string(endpoint[2])+") room="+room->stableId+" center_um=("
|
||||
+std::to_string(room->centerMicrometres[0])+","+
|
||||
std::to_string(room->centerMicrometres[1])+","+
|
||||
std::to_string(room->centerMicrometres[2])+") floor_um="+
|
||||
std::to_string(room->floorMicrometres)+" half_um=("
|
||||
+std::to_string(room->halfExtentsMicrometres[0])+","+
|
||||
std::to_string(room->halfExtentsMicrometres[1])+","+
|
||||
std::to_string(room->halfExtentsMicrometres[2])+")");
|
||||
}
|
||||
}
|
||||
const auto* edgeStratum=findStratum(edge.strateIndex);
|
||||
if(edgeStratum&&edgeStratum->generatorKind=="Caves"
|
||||
&&edgeStratum->recipeId=="Core.Caves.PrimitiveField"
|
||||
&&edgeStratum->recipeVersion==1
|
||||
if(edgeStratum&&HasNativeGraphField(*edgeStratum)
|
||||
&&(edge.polylineMicrometres.size()<2
|
||||
||edge.polylineMicrometres.front()!=edge.controlPointsMicrometres.front()
|
||||
||edge.polylineMicrometres.back()!=edge.controlPointsMicrometres.back()))
|
||||
||edge.polylineMicrometres.back()!=edge.controlPointsMicrometres.back()
|
||||
||edge.polylineMicrometres.size()>65536))
|
||||
add("cavern_edge_polyline",edge.stableId+
|
||||
" has no resolve-time polyline with the cubic endpoints");
|
||||
" has no bounded resolve-time polyline with matching endpoints");
|
||||
}
|
||||
}
|
||||
if(oreVeins.empty())
|
||||
@@ -1376,7 +1415,12 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
|
||||
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;}
|
||||
if(strict&&!issues.empty())
|
||||
{
|
||||
error="strict resolved-layout validation rejected "+issues.front().code+": "
|
||||
+issues.front().detail;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1485,6 +1529,7 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
|
||||
readTriplet("bounds_max_um",room.boundsMaximumMicrometres);
|
||||
room.floorMicrometres=LayoutInt(v,"floor_um");
|
||||
room.smoothWidthMicrometres=LayoutInt(v,"smooth_width_um");
|
||||
room.unionWidthMicrometres=LayoutInt(v,"union_width_um");
|
||||
room.irregularityAmplitudeMicrometres=LayoutInt(v,"irregularity_amplitude_um");
|
||||
room.irregularityWavelengthMicrometres=LayoutInt(v,"irregularity_wavelength_um");
|
||||
room.irregularitySeed=static_cast<std::uint64_t>(LayoutInt(v,"irregularity_seed"));
|
||||
@@ -1522,6 +1567,7 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
|
||||
edge.boreHeightMicrometres=LayoutInt(v,"bore_height_um");
|
||||
edge.floorSupportDepthMicrometres=LayoutInt(v,"floor_support_depth_um");
|
||||
edge.smoothWidthMicrometres=LayoutInt(v,"smooth_width_um");
|
||||
edge.unionWidthMicrometres=LayoutInt(v,"union_width_um");
|
||||
edge.irregularityAmplitudeMicrometres=LayoutInt(v,"irregularity_amplitude_um");
|
||||
edge.irregularityWavelengthMicrometres=LayoutInt(v,"irregularity_wavelength_um");
|
||||
edge.irregularitySeed=static_cast<std::uint64_t>(LayoutInt(v,"irregularity_seed"));
|
||||
@@ -1842,17 +1888,24 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
{
|
||||
const auto found=std::find_if(candidate.strates.begin(),candidate.strates.end(),
|
||||
[strateIndex](const auto& stratum){return stratum.strateIndex==strateIndex;});
|
||||
return found!=candidate.strates.end()&&found->generatorKind=="Caves"
|
||||
&&found->recipeId=="Core.Caves.PrimitiveField"&&found->recipeVersion==1;
|
||||
return found!=candidate.strates.end()&&IsNativeCaveFieldStratum(*found);
|
||||
};
|
||||
const auto isNativeLatticeStrate=[&candidate](std::int32_t strateIndex)
|
||||
{
|
||||
const auto found=std::find_if(candidate.strates.begin(),candidate.strates.end(),
|
||||
[strateIndex](const auto& stratum){return stratum.strateIndex==strateIndex;});
|
||||
return found!=candidate.strates.end()&&IsNativeLatticeFieldStratum(*found);
|
||||
};
|
||||
for(auto& room:candidate.cavernRooms)
|
||||
{
|
||||
// The shared JSON value type stores numbers as IEEE-754 doubles. Keep the
|
||||
// serialized seed in the exact integer range, as the stamp vocabulary does.
|
||||
room.irregularitySeed=LayoutStampSeed(seed,room.strateIndex,room.stableId)
|
||||
room.irregularitySeed=(isNativeLatticeStrate(room.strateIndex)
|
||||
?LayoutLatticeFeatureSeed(seed,room.strateIndex,1,room.stableId)
|
||||
:LayoutStampSeed(seed,room.strateIndex,room.stableId))
|
||||
&0x001fffffffffffffull;
|
||||
const std::int64_t pad=room.smoothWidthMicrometres
|
||||
+room.irregularityAmplitudeMicrometres;
|
||||
+room.unionWidthMicrometres+room.irregularityAmplitudeMicrometres;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
room.boundsMinimumMicrometres[axis]=room.centerMicrometres[axis]
|
||||
@@ -1863,7 +1916,10 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
}
|
||||
for(auto& edge:candidate.cavernEdges)
|
||||
{
|
||||
edge.irregularitySeed=LayoutStampSeed(seed,edge.strateIndex,edge.stableId)
|
||||
const bool nativeLattice=isNativeLatticeStrate(edge.strateIndex);
|
||||
edge.irregularitySeed=(nativeLattice
|
||||
?LayoutLatticeFeatureSeed(seed,edge.strateIndex,1,edge.stableId)
|
||||
:LayoutStampSeed(seed,edge.strateIndex,edge.stableId))
|
||||
&0x001fffffffffffffull;
|
||||
using FCurvePoint=std::array<double,3>;
|
||||
std::array<FCurvePoint,4> curve{};
|
||||
@@ -1889,6 +1945,8 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
}
|
||||
return std::sqrt(distanceSquared);
|
||||
};
|
||||
std::vector<std::array<std::int64_t,3>> authoredPolyline;
|
||||
if(nativeLattice)authoredPolyline=std::move(edge.polylineMicrometres);
|
||||
edge.polylineMicrometres.clear();
|
||||
if(isNativeCavesStrate(edge.strateIndex))
|
||||
{
|
||||
@@ -1934,15 +1992,31 @@ bool ResolveResolvedCavernGraphV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
|
||||
if(!polylineValid)
|
||||
{error="cavern edge "+edge.stableId+" exceeds the bounded polyline subdivision budget";return false;}
|
||||
}
|
||||
else if(nativeLattice)
|
||||
{
|
||||
if(authoredPolyline.size()<2||authoredPolyline.size()>65536
|
||||
||authoredPolyline.front()!=edge.controlPointsMicrometres.front()
|
||||
||authoredPolyline.back()!=edge.controlPointsMicrometres.back())
|
||||
{error="lattice edge "+edge.stableId+
|
||||
" needs a bounded explicit path with matching endpoints";return false;}
|
||||
edge.polylineMicrometres=std::move(authoredPolyline);
|
||||
}
|
||||
std::array<std::int64_t,3> low=edge.controlPointsMicrometres[0];
|
||||
std::array<std::int64_t,3> high=low;
|
||||
for(const auto& point:edge.controlPointsMicrometres)
|
||||
const auto includeBoundPoint=[&](const auto& point)
|
||||
{
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{low[axis]=std::min(low[axis],point[axis]);high[axis]=std::max(high[axis],point[axis]);}
|
||||
};
|
||||
if(nativeLattice&&!edge.polylineMicrometres.empty())
|
||||
{low=edge.polylineMicrometres.front();high=low;
|
||||
for(const auto& point:edge.polylineMicrometres)includeBoundPoint(point);}
|
||||
else for(const auto& point:edge.controlPointsMicrometres)includeBoundPoint(point);
|
||||
const std::int64_t xyPad=edge.boreRadiusMicrometres
|
||||
+edge.smoothWidthMicrometres+edge.irregularityAmplitudeMicrometres;
|
||||
const std::int64_t zPad=edge.smoothWidthMicrometres
|
||||
+edge.smoothWidthMicrometres+edge.unionWidthMicrometres
|
||||
+edge.irregularityAmplitudeMicrometres;
|
||||
const std::int64_t zPad=edge.smoothWidthMicrometres
|
||||
+edge.unionWidthMicrometres+edge.irregularityAmplitudeMicrometres;
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
{
|
||||
const std::int64_t pad=axis==2?zPad:xyPad;
|
||||
|
||||
Reference in new issue
Block a user