tune: edge warp 5 m @12/island -> 12 m @20/island (terrain-water task 10)

Sized from measurement, not taste-first. |grad raw| at the K3/K4/K5
contours on seed 1375359975 is p50 0.00088 raw/px, so the first try (5 m,
12 undulations/island) displaced the shelf boundary a MEAN of 3.7 px with
a roughness ratio of 1.03 -- real, and invisible at map scale. At 12 m
and 20/island the boundary moves a mean 8.4 px (bench) / 9.1 px
(plateau), roughness ratio 1.09, and the outline grows the notches, coves
and peninsulas the sketch asked for.

Measured cost at 12 m (A off vs B, same seed): lowland and toe/red bands
bit-identical in both pixel count and slope -- the K2 pin holding exactly,
as designed. Foothill riser p50 0.681 -> 0.676 m/px, p90 1.445 -> 1.478,
max 3.09 -> 3.81. Bench p50 0.21 -> 0.232 (micro-relief included), still
buildable. Peaks max identical at 6.55.

Safety bound restated from "half the margin to a fixed knot" to 2/3 of
the smaller adjacent band, which is the constraint that actually matters:
the squeezed band never compresses below a third of its nominal width, so
its slope never more than triples. That puts the ceiling at 16.45 m for
the v5 knots and leaves the 12 m default real headroom if the gate says
"more".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stewart Howe 2026-08-08 20:52:08 -04:00
parent 492b56a87c
commit ed16a7f668
2 changed files with 25 additions and 13 deletions

View file

@ -39,10 +39,10 @@ namespace IslaApocalypse.Core // Change this if your namespace is different
// of OUTPUT height. ShelfEdgeVariation is the shelf-edge warp amplitude in
// metres of INPUT height — how far the shelf/riser boundary contour is
// displaced, not an elevation change; it is clamped at load time to the
// largest shift that keeps the curve's knots ordered. Defaults: v1, 3 m, 5 m.
// largest shift the curve's bands can absorb. Defaults: v1, 3 m, 12 m.
public static string TerrainDetail = "v1";
public static float ShelfReliefAmp = 3.0f;
public static float ShelfEdgeVariation = 5.0f;
public static float ShelfEdgeVariation = 12.0f;
public static void LoadConfig()
{

View file

@ -34,19 +34,31 @@ public static class TerrainDetailPass
// Pass B — shelf-edge variation. The amplitude is stated in metres of INPUT
// height (raw × 251): it is how far, in raw-height terms, a shelf boundary
// contour is displaced — not an output elevation change. Lateral wander on
// the map is that displacement divided by the local raw gradient.
public const float EDGE_AMP_DEFAULT_M = 5f; // config dial: ShelfEdgeVariation (metres of input height)
public const float EDGE_FREQ_ISLANDS = 12f; // ~12 undulations per island width — a handful of notches
public const int EDGE_SEED_OFFSET = 7507; // per shelf perimeter, not hundreds of teeth
public const float EDGE_SAFETY_FRACTION = 0.5f; // shift ≤ half the smallest margin to a fixed knot
// contour is displaced — not an output elevation change. What the eye sees is
// the LATERAL wander, which is that displacement divided by the local raw
// gradient. Measured on seed 1375359975: |∇raw| at the K3/K4/K5 contours is
// p50 0.00088 raw/px, so 12 m of input height buys a median peak displacement
// of ~54 px and a mean of ~8 px along the boundary — coves and notches, which
// is where the developer's sketch sits. 5 m (the first try) moved the boundary
// a mean 3.7 px and was invisible at map scale.
public const float EDGE_AMP_DEFAULT_M = 12f; // config dial: ShelfEdgeVariation (metres of input height)
public const float EDGE_FREQ_ISLANDS = 20f; // ~410 px wavelength at 8K — coves and notches at the
public const int EDGE_SEED_OFFSET = 7507; // scale of the sketch, not a fringe of teeth
// The shift squeezes whichever of the foothill riser / plateau bands it moves
// into. Bounding it at 2/3 of the smaller band means that band never compresses
// below a THIRD of its nominal width — i.e. its slope never more than triples,
// even where peak noise lands on a boundary. That is the real constraint; knot
// ordering follows from it.
public const float EDGE_SAFETY_FRACTION = 2f / 3f;
/// <summary>
/// The largest per-column knot shift this preset can take while keeping the
/// knot set strictly ordered (K2 &lt; K3+d, K5+d &lt; K6) with margin to spare.
/// K1/K2/K6 never move, so the toe, the orange/red bands and the summit spike
/// are bit-identical whatever the warp does — which is what makes the
/// red-ceiling and peak-height guarantees exact rather than statistical.
/// The largest per-column knot shift this preset allows: bounded by the band
/// squeeze above, which also keeps the knot set strictly ordered
/// (K2 &lt; K3+d, K5+d &lt; K6) with a third of each band to spare. K1/K2/K6 never
/// move, so the toe, the orange/red bands and the summit spike are
/// bit-identical whatever the warp does — which is what makes the red-ceiling
/// floor and the 420 m cap exact rather than statistical.
/// </summary>
public static float MaxEdgeShift(CurveKnots k)
{