islaApocalypse-v2/Core/README.md
beezm ea291eaab5 chat2/11: hydraulic erosion — the faithful droplet pass on the locked shape, render-only, judged off vs on
Step 0: tag terrain-shape-v1 on a59e52f (the frag_4-locked, gallery-confirmed state).

Core/Scripts/HydraulicErosion.cs is the reference's pass ported verbatim - the four governors
(250,000 droplets, lifetime 384, carve cap 15 m, deposit cap 6 m) on one net-displacement
ledger read both ways and PROVEN on exit (the CARVE-CAP / DEPOSIT-CAP violations throw), the
sea clamp (spawn rejected below sea, death at sea, both brushes skip below-sea cells, the 0.5 m
margin floor on the erode brush), the cone-weighted normalized brush shared by erode and
deposit, the droplet physics (inertia 0.35, capacity 4, min slope 0.02, erode 0.12, deposit
0.15, evaporation 0.004, gravity 4, brush 2), PCG32 seeded at seed + 9271, the crater
exclusion whole. Engine-free; every metres<->raw conversion through WorldScale (no literal
251 - the same multiply/divide, so bit-identical arithmetic).

Tools/Scripts/ErosionPass.cs is the caller (pass 2b): render field only (copied if aliased to
classify), governors clamped as the reference ConfigManager clamped them, the crater exclusion
passed through INERT (no crater => radius 0 => weight 1 everywhere; the reference's
"core < carve factor" warning dormant), and the FLOOD GUARD - render water pixels counted
before and after, any change throws. TerrainGenConfig gains Erosion (default OFF, the anchor
rule) and the governors/physics/crater fields. Pass2Result.WithHeight hands back the eroded
render field. ShadeRenderer is a pure-hillshade plate (land only) because the palette relief's
0.30 hillshade hides half-metre drainage. ErosionTool carries TerrainShapeV1 (the locked shape's
values, pinned once) and the batch: 4 gallery seeds x off/on at 8192, grayscale + .f32 +
relief + shade per field, a mid-slope 1024-px crop off/on (relief and shade), the stats.

Faithful first, no tune: at 8192 the pass touches ~88 % of land cells at a mean 0.14 m, carve
15.00 / deposit 6.00 m at the caps, 0.5 m mean on the massif with 18 % of its cells moved more
than a metre. It reads as dissected summits with radial gully fans and carve/deposit bands
along the slope breaks, not dendritic networks: droplets die of lifetime (170k of 224k) before
they converge. A lifetime probe at 4096 (scratch) shows 1536 and 4096 identical - every
droplet is dead of evaporation by ~1,300 steps - so lifetime is not the lever; droplet count
is. The mid-slope green->yellow transition is not softened at the faithful tune.

Oracle, all passing on 4 seeds: erosion OFF bit-identical to terrain-shape-v1 (the task-10
gallery dumps, 67 M cells each); classify bit-identical off vs on and == the pass-1 field;
region labeling + island tag identical; flood guard (27.5 M water pixels unchanged, every
seed); caps proven; tag/coastline; classify == raw; centre is land; eroded field
bit-identical across two runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EY3ZTF6NwzF8ukBHQXSK7
2026-08-22 12:19:20 -04:00

71 lines
5.5 KiB
Markdown

# Core — math and data only
**Holds:** the column model, the material schemas, the scaling discipline, tooling path
resolution and the file-safety rails. Constants and contracts.
**Boundary: Core depends on nothing above it.** Not on `Server/`, not on `Client/`, not on
`Tools/`. The dependency arrow points one way into this folder and never out of it.
> ### ⚠ Additional boundary, tighter than the layer rule: **Core is engine-free.**
>
> No `using Godot;` anywhere in this folder. Core is plain C# — no `Node`, no `GD.Print`, no
> `ProjectSettings`, no engine types in any signature.
>
> **Two reasons, both load-bearing.** (1) D-049 commits to *"C# for fast iteration, with C++-ready
> seams… port path-by-path when settled and hot"* — the hot paths that get ported first are exactly
> the ones in here, and a type that names `Godot.Vector2` cannot cross that seam. (2) It keeps the
> column model testable and reviewable without standing up an engine.
>
> Where Core genuinely needs something the engine knows — the resolved `user://` directory — the
> engine layer **hands it in** (`ToolingPaths.Configure`), rather than Core reaching for it.
> If you find yourself wanting an engine type here, the type belongs one layer up.
## What is in here
| File | What it is |
|---|---|
| `Scripts/Column.cs` | ★★ **The load-bearing contract** (D-053). A column as run-length bands; `MaterialRun`; `RunOrigin`. Read its header before touching anything that stores world data. |
| `Scripts/ColumnGrid.cs` | The 2D grid of columns. Shape only — chunking and streaming are later phases. |
| `Scripts/MaterialKey.cs` | Material **identity** — a registry key, never an ordinal. |
| `Scripts/TerrainMaterial.cs` | A row in the terrain schema (D-054). |
| `Scripts/BuildingMaterial.cs` | A row in the building schema (D-054). |
| `Scripts/MaterialRegistry.cs` | Both registries, append-only, seed rows only. |
| `Scripts/RecipeRegistry.cs` | The transformation seam — **deliberately empty**. |
| `Scripts/GenerationScale.cs` | ⭐ The scaling discipline. `MapSize`, `ScaleFactor`, normalized offsets, the two frequency conventions. |
| `Scripts/WorldScale.cs` | ⭐ **The vertical yardstick.** The ONE metres↔raw conversion (251 m/unit). No literal `251f` anywhere else. |
| `Scripts/HeightCurve.cs` | ⭐⭐ The height-redistribution curve (v5), 7 bands. **Identity at and below sea.** |
| `Scripts/CurveKnots.cs` | The six INPUT knots — percentiles of the measured land CDF, plus the reference's for comparison. |
| `Scripts/CurveAnchors.cs` | The OUTPUT anchors — the storm-ladder elevations each band lands at. |
| `Scripts/TerrainDetailPass.cs` | Shelf micro-relief + the shelf-edge **knot warp**. Output-height only. |
| `Scripts/HydraulicErosion.cs` | ⭐⭐ **Droplet (hydraulic) erosion** (chat2/11) — the reference's pass ported VERBATIM: four governors (count, lifetime, carve cap, deposit cap) on a net-displacement ledger proven on exit, the sea clamp (below-sea read-only both ways), the cone brush shared by erode and deposit, the crater exclusion (inert until the carve exists). Engine-free, own PCG32, `WorldScale`-denominated (no literal 251). Render-map only — the caller (`Tools/ErosionPass`) owns the split and the flood guard. |
| `Scripts/RegionLabeling.cs` | ⭐⭐ **The region-labeling layer** (chat2/07) — shared infrastructure. 8-connected land components on the CLASSIFY field; mainland = the centre component; per component id / size / centroid / hemisphere (by centroid) / isMainland. Pure, engine-free, C++-candidate; a **contract** downstream phases consume (islands first; biomes, placement, rivers, the crater later). The hemisphere convention lives here. |
| `Scripts/ToolingPaths.cs` | Every tooling path, env-overridable, resolved in one place. |
| `Scripts/FileSafety.cs` | The permanent file-safety rules, as throws rather than sentences. |
## The three rules this layer exists to make unbreakable
1. **Air is the top run.** There is no "air vs solid" height branch, anywhere, ever.
`Scripts/Column.cs`.
2. **Properties come from the registry, never from storage.** A run stores an identity.
`Scripts/MaterialRegistry.cs`.
3. **Nothing uses a raw pixel number.** Every distance is a fraction of `MapSize`.
`Scripts/GenerationScale.cs`.
4. **There is one metres-per-raw-unit number.** The prototype scattered `251` across three
duplicate constants and ~20 literals, and the generator never used the derived one at all.
`Scripts/WorldScale.cs`.
## Not here, and not by accident
- **No water.** Water is an overlay over the columns (levels-not-cells), never a band.
- **No biomes.** Biomes are a later *classification* of finished shape, not an input to it (D-049).
- **No algorithms** *beyond the height curve and the region layer*. Phase 2 added `HeightCurve` and
`TerrainDetailPass` here because they are pure, engine-free, C++-candidate math that defines the
world's elevation profile — a contract, not a tool's dial. chat2/07 added `RegionLabeling` on the
same grounds: region identity is a contract every later phase reads, and the flood fill is a hot
path. (The speck REVERT that uses it is a pass, and lives in `Tools/``RegionPass`.) Stratigraphy, feature passes, meshing
and run-splitting on dig are still later phases.
- **No erosion, rivers, water bodies, crater carve, coast shelf or offshore islets.** Later chat2
tasks; the curve is deliberately the only pass-2 element present.
`Design - Data - Column Model.md`, `Design - Data - Material Schema.md`,
`Design - Tooling - Scaling Discipline.md`