Diagnostic first (SouthernStretchTool, ISLA_DIAG_ONLY; scratch/southern_diagnosis.md). Along the central south profile the mask blend climbs 0.018 per 0.01 N, edge noise adds 0.03-0.06, and the southern sinker is zero until 0.75 N then adds 0.024 per 0.01 N before the 2.5 power; the coast sits at 0.76-0.82 N, ~0.06 N north of where the blend alone would end. The stretch compresses the southward distance the mask sees inside a fixed feathered latitude band, y' = yB + (y - yB)/(1 + s*ramp), texture (base noise, edge noise, latitude) untouched, so the extended mass keeps the elevation of the rows it came from. With the sinker reading the REAL y it cancels the stretch outright: median coast saturates at 0.83-0.84 from s = 1 to 16 and the south stays 1-10 tiny nubs on both seeds. The lever is therefore two things - extend reach AND hold the sinker back - and this pass couples them 1:1 (StretchSinker = true; the real-y mode is kept as the config's other setting). Ladder chosen from the sweep, non-linear: 0.5, 1, 2, 3, 5 (peninsula / onset / few big pieces / big pieces consolidating / over-stretched sheet); 8 and 16 are indistinguishable from 5. Topography: cells above the band take the untouched code path (fy == y), so the classify field north of the band is bit-identical by construction. SouthernStretch.cs holds the band constants (start 0.70 N, feather 0.05 N - fixed for the batch) and the ramp. TerrainGenConfig: SouthStretch (the swept axis, 0 = off), SouthBandStartFrac, SouthBandFeatherFrac, StretchSinker. Oracle, all passing: a1/a3 stretch OFF bit-identical; a3b stretch 5 north of the band bit-identical to the task-03 dump (2.93 M cells), a4b the same at 8192 against the terrain-curve-v1 gallery dump (46.97 M cells north of the band, 20.1 M differ below - the relaxation); per field north bit-locked classify and render vs stretch 0 (p, p2), northern island set invariant (q), centre is land, tag/coastline, classify == raw; ids and heights deterministic. Batch: BatchRoot(8, "southern_stretch_explore") - 5 levels x 2 seeds (1063685222, 999999937 - the two 07 seeds with the most mainland south of the band), lean render (regions overlay + relief + .f32), the hemisphere-split count/size table. Region labeling on, offshore / shelf / speck revert off: the pure fragmentation signal. The read at 4096: s 0.5 peninsula (coast +0.04 N); s 1 onset (999999937 6 -> 12 southern islands, 11 substantial); s 2 few big pieces (35k / 86k-cell fragments); s 3-5 a solid sheet to the trench whose southern edge is the trench wall - a ruler-straight coast - and whose island count falls as pieces re-merge. Gravel never dominates. Graduation held, per the task. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013EY3ZTF6NwzF8ukBHQXSK7
57 lines
2.9 KiB
C#
57 lines
2.9 KiB
C#
using System;
|
||
|
||
namespace IslaApocalypse.Tools
|
||
{
|
||
/// <summary>
|
||
/// ⭐ THE SOUTHERN STRETCH (chat2/08, EXPLORATION) — the one deliberate relaxation of sea identity,
|
||
/// confined to a FIXED feathered latitude band.
|
||
///
|
||
/// ═══ THE MECHANISM (read from pass 1, chat2/08 diagnostic) ═══
|
||
///
|
||
/// Pass 1's mask is falloff = ½·ellipse + ½·squircle (+ edge noise · squircle), then the SOUTHERN
|
||
/// SINKER adds 0.6 · (y − 0.75N)/(0.25N) for y > 0.75N, BEFORE the 2.5 power; the coast sits
|
||
/// where rawBase − falloff^2.5 crosses sea, i.e. near falloff ≈ 0.66. Every term that grows with y
|
||
/// is a function of the SOUTHWARD DISTANCE. The stretch compresses that distance inside the band:
|
||
///
|
||
/// y' = yB + (y − yB) / (1 + stretch · ramp(y)) ramp = smoothstep over the feather
|
||
///
|
||
/// so a cell at y takes the mask geometry of the row y' north of it — the mass reaches further
|
||
/// south AND keeps the elevation profile of the rows it came from (the base noise, edge noise and
|
||
/// latitude field keep the real y — texture stays, only the mask's geometry stretches). Where the
|
||
/// stretched thin edge drops below sea it fragments organically. Nothing is stamped.
|
||
///
|
||
/// ⚠ North of the band (y ≤ yB) the caller takes the untouched code path: bit-identical by
|
||
/// construction, asserted by the oracle. The band line and feather are constants for a batch;
|
||
/// <c>TerrainGenConfig.SouthStretch</c> is the only swept axis.
|
||
///
|
||
/// The SINKER: <c>TerrainGenConfig.StretchSinker</c> decides whether it rides y' (pushed out with
|
||
/// the geometry — held back inside the band) or the real y (keeps pulling the extended mass down
|
||
/// where it always did). The chat2/08 diagnostic measured both — see the report.
|
||
/// </summary>
|
||
public static class SouthernStretch
|
||
{
|
||
/// <summary>The band's fixed latitude line, fraction of the map. Chosen by the chat2/08 diagnostic (see the report).</summary>
|
||
public const float DefaultBandStartFrac = 0.70f;
|
||
|
||
/// <summary>The feather width, fraction of the map.</summary>
|
||
public const float DefaultBandFeatherFrac = 0.05f;
|
||
|
||
/// <summary>Whether the sinker rides the stretched distance by default. Set by the chat2/08 diagnostic.</summary>
|
||
public const bool DefaultStretchSinker = true;
|
||
|
||
/// <summary>The smoothstep ramp across the feather: 0 at the band line, 1 a feather-width below it.</summary>
|
||
public static float Ramp(float y, float bandStart, float bandFeather)
|
||
{
|
||
float t = Math.Clamp((y - bandStart) / bandFeather, 0f, 1f);
|
||
return t * t * (3f - 2f * t);
|
||
}
|
||
|
||
/// <summary>The y the mask geometry sees. For y ≤ bandStart returns y unchanged.</summary>
|
||
public static float StretchedY(float y, float bandStart, float bandFeather, float stretch)
|
||
{
|
||
if (y <= bandStart || stretch <= 0f) return y;
|
||
float r = Ramp(y, bandStart, bandFeather);
|
||
return bandStart + (y - bandStart) / (1f + stretch * r);
|
||
}
|
||
}
|
||
}
|