proto: continuous swept road deck; coarse-LOD proxies keep thin pillars intact (L143)

Road: one swept-deck stamp on a shared cubic-Hermite centerline (stamp vocabulary V5), 376.8 m, max 8.5 deg, walk
audit clean, short-road control fails as expected. Pillars: regular fillPillar stamps get deterministic 24-sided
capped-cylinder proxies once the LOD step exceeds their radius or half-height (applies to all worlds). Owner hashes
unchanged (v227); five-fixture Clang/GCC determinism; fast tier green.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-11-01 10:08:37 +01:00
1 parent 6cd9e4da76
commit a4e214561f
19 files changed
+1167 -73

No files matched your search

+128 -9
View File
@@ -599,6 +599,7 @@ namespace
<<",\"irregularity_seed\":"<<stamp.irregularitySeed
<<",\"yaw_quarter_turns\":"<<static_cast<unsigned>(stamp.yawQuarterTurns);
if(stamp.kind==ResolvedSdfStampKindV1::fillSweptDeckSegment)
{
out<<",\"sweep_endpoints_um\":[["
<<stamp.sweepEndpointsMicrometres[0][0]<<','
<<stamp.sweepEndpointsMicrometres[0][1]<<','
@@ -606,6 +607,19 @@ namespace
<<stamp.sweepEndpointsMicrometres[1][0]<<','
<<stamp.sweepEndpointsMicrometres[1][1]<<','
<<stamp.sweepEndpointsMicrometres[1][2]<<"]]";
if(!stamp.sweepPathMicrometres.empty())
{
out<<",\"sweep_path_um\":[";
for(std::size_t pointIndex=0;pointIndex<stamp.sweepPathMicrometres.size();
++pointIndex)
{
if(pointIndex)out<<',';
const auto& point=stamp.sweepPathMicrometres[pointIndex];
out<<'['<<point[0]<<','<<point[1]<<','<<point[2]<<']';
}
out<<']';
}
}
if(s.stampVocabularyVersion>=ResolvedLayoutStampVocabularyVersionV3)
out<<",\"rounding_radius_um\":"<<stamp.roundingRadiusMicrometres
<<",\"minimum_spacing_um\":"<<stamp.minimumSpacingMicrometres
@@ -1143,8 +1157,9 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
else if(stampVocabularyVersion!=ResolvedLayoutStampVocabularyVersionV1
&&stampVocabularyVersion!=ResolvedLayoutStampVocabularyVersionV2
&&stampVocabularyVersion!=ResolvedLayoutStampVocabularyVersionV3
&&stampVocabularyVersion!=ResolvedLayoutStampVocabularyVersionV4)
add("stamp_vocabulary_version","resolved stamps require supported vocabulary version 1, 2, 3 or 4");
&&stampVocabularyVersion!=ResolvedLayoutStampVocabularyVersionV4
&&stampVocabularyVersion!=ResolvedLayoutStampVocabularyVersionV5)
add("stamp_vocabulary_version","resolved stamps require supported vocabulary version 1 through 5");
std::vector<std::string> stampIds;
for(const auto& stamp:stamps)
{
@@ -1184,9 +1199,30 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
-stamp.sweepEndpointsMicrometres[0][axis];
lengthSquared+=delta*delta;
}
if(!(lengthSquared>0.0)
||std::sqrt(lengthSquared)>2.0*stamp.halfExtentsMicrometres[0]+1.0)
if(stamp.sweepPathMicrometres.empty()&&(!(lengthSquared>0.0)
||std::sqrt(lengthSquared)>2.0*stamp.halfExtentsMicrometres[0]+1.0))
add("stamp_sweep",stamp.stableId+" has invalid endpoints or longitudinal extent");
if(!stamp.sweepPathMicrometres.empty())
{
bool validPath=stampVocabularyVersion>=ResolvedLayoutStampVocabularyVersionV5
&&stamp.sweepPathMicrometres.size()>=2
&&stamp.sweepPathMicrometres.size()<=1025
&&stamp.sweepPathMicrometres.front()==stamp.sweepEndpointsMicrometres[0]
&&stamp.sweepPathMicrometres.back()==stamp.sweepEndpointsMicrometres[1];
for(std::size_t pointIndex=0;pointIndex<stamp.sweepPathMicrometres.size();
++pointIndex)
{
const auto& point=stamp.sweepPathMicrometres[pointIndex];
for(const auto coordinate:point)
validPath=validPath&&coordinate>=-1000000000000ll
&&coordinate<=1000000000000ll;
if(pointIndex>0)
validPath=validPath
&&stamp.sweepPathMicrometres[pointIndex-1][0]<point[0];
}
if(!validPath)add("stamp_sweep_path",stamp.stableId
+" has an invalid V5 path or non-increasing longitudinal axis");
}
}
if(motif)
{
@@ -1215,7 +1251,8 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
const std::int64_t pad=motif
?(stamp.smoothWidthMicrometres+5)/6+stamp.irregularityAmplitudeMicrometres
:stamp.smoothWidthMicrometres+stamp.irregularityAmplitudeMicrometres;
if(stamp.kind==ResolvedSdfStampKindV1::fillSweptDeckSegment)
if(stamp.kind==ResolvedSdfStampKindV1::fillSweptDeckSegment
&&stamp.sweepPathMicrometres.empty())
{
double radiusSquared=0.0;
for(const auto extent:stamp.halfExtentsMicrometres)
@@ -1235,6 +1272,29 @@ bool ResolvedLayoutSnapshotV3::validate(ResolvedLayoutValidationModeV3 mode,
add("stamp_bounds",stamp.stableId+" conservative sweep bounds do not match its parameters");
}
}
else if(stamp.kind==ResolvedSdfStampKindV1::fillSweptDeckSegment)
{
double radiusSquared=0.0;
for(std::size_t axis=1;axis<3;++axis)
{
const double expanded=static_cast<double>(stamp.halfExtentsMicrometres[axis]+pad);
radiusSquared+=expanded*expanded;
}
const auto radius=static_cast<std::int64_t>(std::ceil(std::sqrt(radiusSquared)));
std::array<std::int64_t,3> minimum=stamp.sweepPathMicrometres.front();
std::array<std::int64_t,3> maximum=minimum;
for(const auto& point:stamp.sweepPathMicrometres)
for(std::size_t axis=0;axis<3;++axis)
{
minimum[axis]=std::min(minimum[axis],point[axis]);
maximum[axis]=std::max(maximum[axis],point[axis]);
}
for(std::size_t axis=0;axis<3;++axis)
if(stamp.boundsMinimumMicrometres[axis]!=minimum[axis]-radius
||stamp.boundsMaximumMicrometres[axis]!=maximum[axis]+radius)
add("stamp_bounds",stamp.stableId
+" conservative continuous-sweep bounds do not match its path");
}
else
{
const std::array<std::int64_t,3> extents={
@@ -1556,6 +1616,14 @@ std::optional<ResolvedLayoutSnapshotV3> ResolvedLayoutSnapshotV3::FromJson(
for(std::size_t axis=0;axis<3&&axis<values.size();++axis)
stamp.sweepEndpointsMicrometres[endpoint][axis]=values[axis].Integer();
}
for(const auto& value:v.Get("sweep_path_um").AsArray())
{
std::array<std::int64_t,3> point{};
const auto& values=value.AsArray();
for(std::size_t axis=0;axis<3&&axis<values.size();++axis)
point[axis]=values[axis].Integer();
stamp.sweepPathMicrometres.push_back(point);
}
readTriplet("bounds_min_um",stamp.boundsMinimumMicrometres);
readTriplet("bounds_max_um",stamp.boundsMaximumMicrometres);
readTriplet("half_extents_um",stamp.halfExtentsMicrometres);
@@ -1678,10 +1746,15 @@ bool ResolveResolvedLayoutStampsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
std::sort(ordered.begin(),ordered.end(),[](const auto& a,const auto& b)
{return a.stableId<b.stableId;});
ResolvedLayoutSnapshotV3 candidate=layout;
const bool hasContinuousSweptDeck=std::any_of(ordered.begin(),ordered.end(),[](const auto& request)
{return request.kind==ResolvedSdfStampKindV1::fillSweptDeckSegment
&&!request.sweepPathMicrometres.empty();});
const bool hasSweptDeck=std::any_of(ordered.begin(),ordered.end(),[](const auto& request)
{return request.kind==ResolvedSdfStampKindV1::fillSweptDeckSegment;});
candidate.stampVocabularyVersion=hasSweptDeck
?ResolvedLayoutStampVocabularyVersionV4:ResolvedLayoutStampVocabularyVersionV1;
candidate.stampVocabularyVersion=hasContinuousSweptDeck
?ResolvedLayoutStampVocabularyVersionV5
:hasSweptDeck?ResolvedLayoutStampVocabularyVersionV4
:ResolvedLayoutStampVocabularyVersionV1;
candidate.stamps.clear();candidate.stamps.reserve(ordered.size());
for(std::size_t requestIndex=0;requestIndex<ordered.size();++requestIndex)
{
@@ -1712,7 +1785,8 @@ bool ResolveResolvedLayoutStampsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
for(const auto coordinate:request.anchorPositionMicrometres)
if(coordinate<-1000000000000ll||coordinate>1000000000000ll)
{error="stamp request "+request.stableId+" has an unbounded anchor";return false;}
if(request.kind==ResolvedSdfStampKindV1::fillSweptDeckSegment)
if(request.kind==ResolvedSdfStampKindV1::fillSweptDeckSegment
&&request.sweepPathMicrometres.empty())
{
double lengthSquared=0.0;
for(std::size_t axis=0;axis<3;++axis)
@@ -1729,6 +1803,25 @@ bool ResolveResolvedLayoutStampsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
||std::sqrt(lengthSquared)>2.0*request.halfExtentsMicrometres[0]+1.0)
{error="stamp request "+request.stableId+" has invalid swept-segment extents";return false;}
}
if(request.kind==ResolvedSdfStampKindV1::fillSweptDeckSegment
&&!request.sweepPathMicrometres.empty())
{
if(request.sweepPathMicrometres.size()<2
||request.sweepPathMicrometres.size()>1025
||request.sweepPathMicrometres.front()!=request.sweepEndpointsMicrometres[0]
||request.sweepPathMicrometres.back()!=request.sweepEndpointsMicrometres[1])
{error="stamp request "+request.stableId+" has an invalid continuous deck path";return false;}
for(std::size_t pointIndex=0;pointIndex<request.sweepPathMicrometres.size();
++pointIndex)
{
const auto& point=request.sweepPathMicrometres[pointIndex];
for(const auto coordinate:point)
if(coordinate<-1000000000000ll||coordinate>1000000000000ll)
{error="stamp request "+request.stableId+" has an unbounded path point";return false;}
if(pointIndex>0&&request.sweepPathMicrometres[pointIndex-1][0]>=point[0])
{error="stamp request "+request.stableId+" path X coordinates must increase strictly";return false;}
}
}
// The shared JSON value type stores numbers as IEEE-754 doubles; keep the
// serialized seed in its exact integer range so the canonical round trip holds.
@@ -1748,6 +1841,7 @@ bool ResolveResolvedLayoutStampsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
ResolvedLayoutStampV1 stamp;stamp.stableId=request.stableId;
stamp.strateIndex=request.strateIndex;stamp.kind=request.kind;
stamp.sweepEndpointsMicrometres=request.sweepEndpointsMicrometres;
stamp.sweepPathMicrometres=request.sweepPathMicrometres;
stamp.halfExtentsMicrometres=request.halfExtentsMicrometres;
stamp.smoothWidthMicrometres=request.smoothWidthMicrometres;
stamp.irregularityAmplitudeMicrometres=request.irregularityAmplitudeMicrometres;
@@ -1769,7 +1863,8 @@ bool ResolveResolvedLayoutStampsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
stamp.positionMicrometres[axis]=request.anchorPositionMicrometres[axis]+offset[axis];
const std::int64_t pad=stamp.smoothWidthMicrometres
+stamp.irregularityAmplitudeMicrometres;
if(stamp.kind==ResolvedSdfStampKindV1::fillSweptDeckSegment)
if(stamp.kind==ResolvedSdfStampKindV1::fillSweptDeckSegment
&&stamp.sweepPathMicrometres.empty())
{
double radiusSquared=0.0;
for(const auto extent:stamp.halfExtentsMicrometres)
@@ -1788,6 +1883,30 @@ bool ResolveResolvedLayoutStampsV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
stamp.boundsMaximumMicrometres[axis]=maximum+radius;
}
}
else if(stamp.kind==ResolvedSdfStampKindV1::fillSweptDeckSegment)
{
double radiusSquared=0.0;
for(std::size_t axis=1;axis<3;++axis)
{
const double expanded=static_cast<double>(stamp.halfExtentsMicrometres[axis]+pad);
radiusSquared+=expanded*expanded;
}
const std::int64_t radius=static_cast<std::int64_t>(
std::ceil(std::sqrt(radiusSquared)));
std::array<std::int64_t,3> minimum=stamp.sweepPathMicrometres.front();
std::array<std::int64_t,3> maximum=minimum;
for(const auto& point:stamp.sweepPathMicrometres)
for(std::size_t axis=0;axis<3;++axis)
{
minimum[axis]=std::min(minimum[axis],point[axis]);
maximum[axis]=std::max(maximum[axis],point[axis]);
}
for(std::size_t axis=0;axis<3;++axis)
{
stamp.boundsMinimumMicrometres[axis]=minimum[axis]-radius;
stamp.boundsMaximumMicrometres[axis]=maximum[axis]+radius;
}
}
else
{
const std::array<std::int64_t,3> extents={