wip(assembler): round L35 -- smooth passage walls without regular ribs (evaluator v21)
The shared passage carve took the minimum of piecewise-linear capsules (scallops at joins) and added wall relief on regular 8 m / 4 m octaves, which read as evenly spaced ribs. Interior sweep joins are now Catmull-Rom smoothed and the relief uses irregular rotated 43/29/19 m scales; the natural wobble remains. New axial ripple audit (0.125 m radius samples, 0.5-8 m periods): beaded control and L34 geometry fail, all 24 passage rows pass. FI mouth tread apron tapered. 30/30 determinism, 16/16 invariants, ring audits LOD0-8 pass; cost within noise. 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
49e29fcd36
commit
ba017077d1
28 files changed
+1727
-17
No files matched your search
@@ -812,6 +812,14 @@ namespace StandaloneStreamBenchmark
|
||||
std::uint64_t floorSamples=0,wallSamples=0,missingFloorSamples=0,missingWallSamples=0;
|
||||
bool passed=false;
|
||||
};
|
||||
struct FPassageRippleAudit
|
||||
{
|
||||
double periodMetres=0.0,amplitudeMetres=0.0,localPeakAmplitudeMetres=0.0;
|
||||
double coherentPeriodMetres=0.0,coherentAmplitudeMetres=0.0;
|
||||
double controlAmplitudeMetres=0.0;
|
||||
std::uint64_t samples=0,expectedSamples=0;
|
||||
bool controlDetected=false,passed=false;
|
||||
};
|
||||
constexpr double MaximumFlatRunMetres=8.0;
|
||||
constexpr double MinimumWallReliefMetres=0.20;
|
||||
constexpr double PassageProfileSpacingMetres=0.125;
|
||||
@@ -1113,6 +1121,174 @@ namespace StandaloneStreamBenchmark
|
||||
&&audit.wallSamples>=8;
|
||||
return audit;
|
||||
};
|
||||
const auto auditPassageRipples=[&](const VoxelForgeCore::World& Field,
|
||||
const VoxelForgeCore::ResolvedLayoutPassageV1& passage)
|
||||
{
|
||||
FPassageRippleAudit audit;
|
||||
constexpr double SampleSpacingMetres=0.125;
|
||||
constexpr double WindowLengthMetres=32.0;
|
||||
constexpr double MinimumPeriodMetres=0.5;
|
||||
constexpr double MaximumPeriodMetres=8.0;
|
||||
constexpr double PeriodStepMetres=0.0625;
|
||||
constexpr double RippleAmplitudeLimitMetres=0.035;
|
||||
struct FPathPoint
|
||||
{
|
||||
double x=0.0,y=0.0,z=0.0,radius=0.0,distance=0.0;
|
||||
};
|
||||
const std::size_t end=std::min<std::size_t>(passage.lowerMouthControlIndex,
|
||||
passage.controlPoints.size()-1);
|
||||
if(end<2)return audit;
|
||||
std::vector<FPathPoint> path;
|
||||
path.reserve(end+1);
|
||||
for(std::size_t i=0;i<=end;++i)
|
||||
{
|
||||
const auto& point=passage.controlPoints[i];
|
||||
FPathPoint value{qToMetres(point.positionSubvoxels[0]),
|
||||
qToMetres(point.positionSubvoxels[1]),
|
||||
qToMetres(point.positionSubvoxels[2]),
|
||||
qToMetres(point.radiusSubvoxels),0.0};
|
||||
if(!path.empty())
|
||||
{
|
||||
const auto& previous=path.back();
|
||||
value.distance=previous.distance+std::sqrt(
|
||||
(value.x-previous.x)*(value.x-previous.x)
|
||||
+(value.y-previous.y)*(value.y-previous.y)
|
||||
+(value.z-previous.z)*(value.z-previous.z));
|
||||
}
|
||||
path.push_back(value);
|
||||
}
|
||||
const double total=path.back().distance;
|
||||
if(total<WindowLengthMetres)return audit;
|
||||
const auto interpolate=[&](double distance)
|
||||
{
|
||||
distance=std::clamp(distance,0.0,total);
|
||||
const auto upper=std::lower_bound(path.begin()+1,path.end(),distance,
|
||||
[](const FPathPoint& point,double value){return point.distance<value;});
|
||||
const auto& b=*upper;
|
||||
const auto& a=*(upper-1);
|
||||
const double length=b.distance-a.distance;
|
||||
const double t=length>1.0e-9?(distance-a.distance)/length:0.0;
|
||||
return FPathPoint{std::lerp(a.x,b.x,t),std::lerp(a.y,b.y,t),
|
||||
std::lerp(a.z,b.z,t),std::lerp(a.radius,b.radius,t),distance};
|
||||
};
|
||||
const auto sinusoidAmplitude=[&](const std::vector<std::array<double,2>>& samples,
|
||||
double& bestPeriod)
|
||||
{
|
||||
if(samples.size()<64)return 0.0;
|
||||
double best=0.0;
|
||||
for(double period=MinimumPeriodMetres;
|
||||
period<=MaximumPeriodMetres+1.0e-9;period+=PeriodStepMetres)
|
||||
{
|
||||
const double angular=6.2831853071795864769/period;
|
||||
double cc=0.0,ss=0.0,cs=0.0,yc=0.0,ys=0.0;
|
||||
for(const auto& sample:samples)
|
||||
{
|
||||
const double phase=angular*sample[0];
|
||||
const double c=std::cos(phase),s=std::sin(phase);
|
||||
cc+=c*c;ss+=s*s;cs+=c*s;
|
||||
yc+=sample[1]*c;ys+=sample[1]*s;
|
||||
}
|
||||
const double determinant=cc*ss-cs*cs;
|
||||
if(determinant<=1.0e-9)continue;
|
||||
const double cosine=(yc*ss-ys*cs)/determinant;
|
||||
const double sine=(ys*cc-yc*cs)/determinant;
|
||||
const double amplitude=std::hypot(cosine,sine);
|
||||
if(amplitude>best){best=amplitude;bestPeriod=period;}
|
||||
}
|
||||
return best;
|
||||
};
|
||||
const auto appendDetrendedProfile=[&](const std::vector<double>& values,
|
||||
double startDistance,std::vector<std::array<double,2>>& output)
|
||||
{
|
||||
if(values.size()<64)return;
|
||||
const double meanIndex=0.5*static_cast<double>(values.size()-1);
|
||||
double mean=0.0,xy=0.0,xx=0.0;
|
||||
for(double value:values)mean+=value;
|
||||
mean/=static_cast<double>(values.size());
|
||||
for(std::size_t i=0;i<values.size();++i)
|
||||
{
|
||||
const double x=(static_cast<double>(i)-meanIndex)*SampleSpacingMetres;
|
||||
xy+=x*(values[i]-mean);xx+=x*x;
|
||||
}
|
||||
const double slope=xx>0.0?xy/xx:0.0;
|
||||
for(std::size_t i=0;i<values.size();++i)
|
||||
{
|
||||
const double x=(static_cast<double>(i)-meanIndex)*SampleSpacingMetres;
|
||||
output.push_back({startDistance+static_cast<double>(i)*SampleSpacingMetres,
|
||||
values[i]-mean-slope*x});
|
||||
}
|
||||
};
|
||||
std::vector<double> beadControl;
|
||||
beadControl.reserve(static_cast<std::size_t>(WindowLengthMetres/SampleSpacingMetres)+1);
|
||||
constexpr double BeadSpacingMetres=1.5;
|
||||
constexpr double BeadSphereRadiusMetres=2.0;
|
||||
for(std::size_t i=0;i<=static_cast<std::size_t>(WindowLengthMetres/SampleSpacingMetres);++i)
|
||||
{
|
||||
const double x=static_cast<double>(i)*SampleSpacingMetres;
|
||||
const double nearest=std::abs(std::remainder(x,BeadSpacingMetres));
|
||||
beadControl.push_back(std::sqrt(std::max(0.0,
|
||||
BeadSphereRadiusMetres*BeadSphereRadiusMetres-nearest*nearest)));
|
||||
}
|
||||
std::vector<std::array<double,2>> beadSamples;
|
||||
appendDetrendedProfile(beadControl,0.0,beadSamples);
|
||||
double ignoredPeriod=0.0;
|
||||
audit.controlAmplitudeMetres=sinusoidAmplitude(beadSamples,ignoredPeriod);
|
||||
audit.controlDetected=audit.controlAmplitudeMetres>=RippleAmplitudeLimitMetres;
|
||||
const std::array<double,4> windowStarts{{0.03,0.31,0.61,0.91}};
|
||||
std::vector<std::array<double,2>> coherentSamples;
|
||||
for(const double fraction:windowStarts)
|
||||
{
|
||||
const double start=(total-WindowLengthMetres)*fraction;
|
||||
std::vector<double> radii;
|
||||
radii.reserve(static_cast<std::size_t>(WindowLengthMetres/SampleSpacingMetres)+1);
|
||||
for(std::size_t sample=0;
|
||||
sample<=static_cast<std::size_t>(WindowLengthMetres/SampleSpacingMetres);
|
||||
++sample)
|
||||
{
|
||||
++audit.expectedSamples;
|
||||
const double distance=start+static_cast<double>(sample)*SampleSpacingMetres;
|
||||
const auto pose=interpolate(distance);
|
||||
const auto before=interpolate(std::max(0.0,distance-0.25));
|
||||
const auto after=interpolate(std::min(total,distance+0.25));
|
||||
const double tangentX=after.x-before.x,tangentY=after.y-before.y;
|
||||
const double tangentLength=std::hypot(tangentX,tangentY);
|
||||
if(tangentLength<=1.0e-6)continue;
|
||||
const double nx=-tangentY/tangentLength,ny=tangentX/tangentLength;
|
||||
const double sampleRadius=std::max(0.20,pose.radius-0.18);
|
||||
const double x0=pose.x/coreMetres,y0=pose.y/coreMetres,z0=pose.z/coreMetres;
|
||||
const double offset=sampleRadius/coreMetres;
|
||||
const double left=static_cast<double>(Field.densityAt(
|
||||
{x0+nx*offset,y0+ny*offset,z0}))*coreMetres;
|
||||
const double right=static_cast<double>(Field.densityAt(
|
||||
{x0-nx*offset,y0-ny*offset,z0}))*coreMetres;
|
||||
if(!std::isfinite(left)||!std::isfinite(right)
|
||||
||left< -0.75||left>0.75||right< -0.75||right>0.75)continue;
|
||||
radii.push_back(sampleRadius+0.5*(left+right));
|
||||
++audit.samples;
|
||||
}
|
||||
if(radii.size()<static_cast<std::size_t>(WindowLengthMetres/
|
||||
SampleSpacingMetres)*3/4)continue;
|
||||
std::vector<std::array<double,2>> windowSamples;
|
||||
appendDetrendedProfile(radii,start,windowSamples);
|
||||
double localPeriod=0.0;
|
||||
const double localAmplitude=sinusoidAmplitude(windowSamples,localPeriod);
|
||||
if(localAmplitude>audit.localPeakAmplitudeMetres)
|
||||
{
|
||||
audit.localPeakAmplitudeMetres=localAmplitude;
|
||||
audit.periodMetres=localPeriod;
|
||||
}
|
||||
coherentSamples.insert(coherentSamples.end(),windowSamples.begin(),
|
||||
windowSamples.end());
|
||||
}
|
||||
audit.coherentAmplitudeMetres=sinusoidAmplitude(coherentSamples,
|
||||
audit.coherentPeriodMetres);
|
||||
audit.amplitudeMetres=audit.localPeakAmplitudeMetres;
|
||||
const double coverage=audit.expectedSamples>0
|
||||
?static_cast<double>(audit.samples)/audit.expectedSamples:0.0;
|
||||
audit.passed=audit.controlDetected&&coverage>=0.75
|
||||
&&audit.localPeakAmplitudeMetres<RippleAmplitudeLimitMetres;
|
||||
return audit;
|
||||
};
|
||||
const auto looksNatural=[&](double flatRun,double reliefPeakToPeak)
|
||||
{return flatRun<=MaximumFlatRunMetres
|
||||
&&reliefPeakToPeak>=MinimumWallReliefMetres;};
|
||||
@@ -1731,7 +1907,7 @@ namespace StandaloneStreamBenchmark
|
||||
return zUm>=stratum->content.bottomMicrometres&&zUm<stratum->content.topMicrometres;
|
||||
};
|
||||
std::uint64_t passageCount=0,passagePasses=0,blockedControls=0,naturalPasses=0;
|
||||
std::uint64_t mouthProfilePasses=0;
|
||||
std::uint64_t mouthProfilePasses=0,ripplePasses=0,rippleControlsDetected=0;
|
||||
std::uint64_t openAirViolations=0,openAirLegacyViolations=0,openAirSamples=0;
|
||||
std::uint64_t openAirAudits=0,openAirColumnSamples=0,openAirShortSpanSamples=0;
|
||||
std::uint64_t columnCrossings=0,shortGroundedSpans=0,columnConnectedEnds=0;
|
||||
@@ -1781,6 +1957,7 @@ namespace StandaloneStreamBenchmark
|
||||
const FPassageNaturalnessAudit naturalAudit=auditPassageNaturalness(Shape,passage);
|
||||
const FPassageMouthProfileAudit mouthProfileAudit=
|
||||
auditPassageMouthProfiles(Shape,passage);
|
||||
const FPassageRippleAudit rippleAudit=auditPassageRipples(Shape,passage);
|
||||
const FPassageOpenAirAudit openAirAudit=auditPassageOpenAir(Shape,blockedWorld,
|
||||
passage,passageIndex);
|
||||
const FPassageColumnAudit columnAudit=auditPassageColumns(Shape,blockedWorld,
|
||||
@@ -1811,8 +1988,10 @@ namespace StandaloneStreamBenchmark
|
||||
&&mouthInContent(passage.lowerStrateIndex,passage.lowerMouthSubvoxels);
|
||||
++passageCount;naturalPasses+=naturalAudit.passed;
|
||||
mouthProfilePasses+=mouthProfileAudit.passed;
|
||||
ripplePasses+=rippleAudit.passed;
|
||||
rippleControlsDetected+=rippleAudit.controlDetected;
|
||||
passagePasses+=passageAudit.passed&&mouthsInRadius&&mouthsInContent
|
||||
&&naturalAudit.passed&&mouthProfileAudit.passed;
|
||||
&&naturalAudit.passed&&mouthProfileAudit.passed&&rippleAudit.passed;
|
||||
std::printf("[ResolvedPassageAudit] id=%s interface=%d-%d control_points=%zu center_samples=%llu capsule_samples=%llu blocked=%llu support_samples=%llu unsupported=%llu grade_failures=%llu mouth_content=%d/2 mouth_radius=%d/2 walkability=%s\n",
|
||||
passage.stableId.c_str(),passage.upperStrateIndex,passage.lowerStrateIndex,
|
||||
passage.controlPoints.size(),static_cast<unsigned long long>(passageAudit.centers),
|
||||
@@ -1837,6 +2016,16 @@ namespace StandaloneStreamBenchmark
|
||||
mouthProfileAudit.wallFlatStepRunMetres,mouthProfileAudit.wallStepMetres,
|
||||
PassageProfileSpacingMetres,PassageProfilePlateauMinimumMetres,
|
||||
PassageProfileStepThresholdMetres,mouthProfileAudit.passed?"PASS":"FAIL");
|
||||
std::printf("[PassageRippleAudit] id=%s window_m=32.000 spacing_m=0.125 period_m=%.3f amplitude_m=%.4f amplitude_cm=%.2f coherent_period_m=%.3f coherent_amplitude_m=%.4f coherent_amplitude_cm=%.2f local_peak_amplitude_m=%.4f coverage=%llu/%llu threshold_m=0.035 synthetic_spheres_period_m=1.500 synthetic_spheres_amplitude_m=%.4f synthetic_spheres_detected=%s status=%s\n",
|
||||
passage.stableId.c_str(),rippleAudit.periodMetres,
|
||||
rippleAudit.amplitudeMetres,rippleAudit.amplitudeMetres*100.0,
|
||||
rippleAudit.coherentPeriodMetres,rippleAudit.coherentAmplitudeMetres,
|
||||
rippleAudit.coherentAmplitudeMetres*100.0,
|
||||
rippleAudit.localPeakAmplitudeMetres,
|
||||
static_cast<unsigned long long>(rippleAudit.samples),
|
||||
static_cast<unsigned long long>(rippleAudit.expectedSamples),
|
||||
rippleAudit.controlAmplitudeMetres,rippleAudit.controlDetected?"true":"false",
|
||||
rippleAudit.passed?"PASS":"FAIL");
|
||||
std::printf("[PassageOpenAirAudit] id=%s support_samples=%llu violations=%llu column_samples=%llu short_span_samples=%llu l24_ungated_violations=%llu route_support_samples=%llu route_support_violations=%llu spacing_m=%.3f status=%s\n",
|
||||
passage.stableId.c_str(),static_cast<unsigned long long>(openAirAudit.supportSamples),
|
||||
static_cast<unsigned long long>(openAirAudit.violations),
|
||||
@@ -2151,9 +2340,15 @@ namespace StandaloneStreamBenchmark
|
||||
PassageProfilePlateauMinimumMetres,PassageProfileStepThresholdMetres,
|
||||
l23TerraceControlDetected?"DETECTED":"MISSED",
|
||||
mouthProfilePasses==passageCount&&l23TerraceControlDetected?"PASS":"FAIL");
|
||||
std::printf("[PassageRippleAuditSummary] passed=%llu/%llu synthetic_spheres_negative_control=%s max_amplitude_m=0.035 status=%s\n",
|
||||
static_cast<unsigned long long>(ripplePasses),
|
||||
static_cast<unsigned long long>(passageCount),
|
||||
rippleControlsDetected==passageCount?"DETECTED":"MISSED",
|
||||
ripplePasses==passageCount?"PASS":"FAIL");
|
||||
const bool auditPassed=passageCount>0&&passagePasses==passageCount
|
||||
&&naturalPasses==passageCount&&l23SmoothWallDetected
|
||||
&&mouthProfilePasses==passageCount&&l23TerraceControlDetected
|
||||
&&ripplePasses==passageCount
|
||||
&&openAirAudits==passageCount&&openAirViolations==0&&l24OpenAirControlDetected
|
||||
&&columnAuditsPassed&&noLongBridgeAuditPassed
|
||||
&&routePasses>0&&routePasses==routeWalkPasses&&routePasses==routeBindings
|
||||
|
||||
Reference in new issue
Block a user