fix: round the mountain-spine crest, the real centre line (terrain-water task 11)

The faint mid-map ridge is NOT the squircle/ellipse falloff blend that the
task suspected. Measured, read-only, before touching anything:

  bare falloff, slope step at centre   +2.05e-07  (SMALLER than the
    off-axis controls +2.36e-06 / +3.05e-06 -- Pow(2.5) crushes it,
    because the falloff is ~0 there)
  mountain spine, slope step at centre -7.58e-04  (170x the controls)

On real terrain, with the by-design Trench walls excluded, x = 4096..4099
hold the top FOUR slope-step locations out of 5999 columns. Column-mean
height runs 109.6 -> 135.5 -> 110.7 m across it: a symmetric ridge peaking
exactly on the centre column. Not the noise domain either -- the noise is
sampled over [0, MapSize], so coordinate zero is at the map CORNER. And not
seed-dependent: present on all 10 seeds checked, 6 positive AND 4 negative
(ranks 0, 18, 0, 83), so the "positive seeds only" impression does not hold.

Cause: the spine is Pow(1 - |x - cx|, 3), and |.| peaks at the axis with a
slope discontinuity of magnitude 2. SmoothAbs replaces |d| with
d^2/sqrt(d^2+e^2) -- zero with zero slope at the axis, converging to |d|
away from it -- so the crest rounds without dropping. e = 0.03 (~141 px)
cuts the 1-px kink by 99.3% (-7.64e-04 -> -5.41e-06, against a natural
profile curvature of ~1.1e-07), fills at most 3.9 m, and decays under
0.5 m by ~1100 px.

DEFERRED, reported not fixed: the spine's AXIS is still hard-coded dead
straight down x = centre for the full map height. That is what the
derivative render actually shows -- everything west of centre slopes east,
everything east slopes west. Rounding the crest does not make the range
meander. Per the task's "fix only if trivial, else report and defer" rule,
and confirmed with the developer, the axis is left alone: a wandering spine
is a world-shape change that rebaselines biomes on every seed, and it
deserves its own task and its own gate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stewart Howe 2026-08-08 23:28:45 -04:00
parent 59f49ac8a2
commit 896a428d47
2 changed files with 40 additions and 3 deletions

View file

@ -0,0 +1,33 @@
using Godot;
/// <summary>
/// The island-falloff shaping functions (terrain-water task 11) — pure numeric
/// functions of their inputs (D-035; a named future C++ candidate, kept standalone).
///
/// SMOOTH CREST — the mountain spine's ridge axis is the line x = centre, and
/// `1 - |x - cx|` peaks there with a slope discontinuity. Measured on the shipped
/// terrain, that crease is the single largest slope step anywhere on the map once
/// the by-design Trench walls are excluded (top 4 of 5999 columns). SmoothAbs
/// rounds the crest without moving its height.
/// </summary>
public static class IslandFalloff
{
// ---- centre-line crest ------------------------------------------------
// Rounding radius in normalized spine-width units (1.0 = MapSize/2 * 1.15
// ~ 4710 px at 8K). 0.03 ~ 141 px: it cuts the crest's 1-px kink by 99.3%
// (-7.64e-04 -> -5.41e-06 raw, against a natural profile curvature of ~1.1e-07)
// while filling at most 3.9 m, decaying under 0.5 m by ~1100 px from the axis.
public const float CREST_EPSILON = 0.03f;
/// <summary>
/// A C¹ stand-in for |d|: exactly 0 with zero slope at d = 0, and converging to
/// |d| within e²/(2|d|) away from it. Replaces the V-shaped crest of the spine
/// with a rounded one WITHOUT lowering it — SmoothAbs(0) is 0, so the peak keeps
/// its full height.
/// </summary>
public static float SmoothAbs(float d, float epsilon)
{
float a = Mathf.Abs(d);
return a * a / Mathf.Sqrt(a * a + epsilon * epsilon);
}
}

View file

@ -473,11 +473,15 @@ public partial class MapGenerator : TextureRect
if (distY > 0.90f) finalFalloff += (distY - 0.90f) * 15.0f; if (distY > 0.90f) finalFalloff += (distY - 0.90f) * 15.0f;
// --- 3. THE MOUNTAIN SPINE --- // --- 3. THE MOUNTAIN SPINE ---
// The ridge axis is the line x = centre. `1 - |x - cx|` peaks there with
// a slope discontinuity, which put the single largest slope step on the
// whole map at exactly that column (task-11 diagnostic). SmoothAbs rounds
// the crest — same peak height, continuous slope through it.
float mountainSpine = 0f; float mountainSpine = 0f;
if (temperature < 0.65f) if (temperature < 0.65f)
{ {
float distanceToCenterX = Mathf.Abs(x - center.X) / (MapSize / 2.0f * 1.15f); float distanceToCenterX = Mathf.Abs(x - center.X) / (MapSize / 2.0f * 1.15f);
mountainSpine = 1.0f - distanceToCenterX; mountainSpine = 1.0f - IslandFalloff.SmoothAbs(distanceToCenterX, IslandFalloff.CREST_EPSILON);
float southernFade = Mathf.Clamp((0.65f - temperature) * 4.0f, 0.0f, 1.0f); float southernFade = Mathf.Clamp((0.65f - temperature) * 4.0f, 0.0f, 1.0f);
mountainSpine = Mathf.Pow(mountainSpine, 3.0f) * southernFade * 0.6f; mountainSpine = Mathf.Pow(mountainSpine, 3.0f) * southernFade * 0.6f;
} }