OverviewFilesCommitsIssuesDocsReleasesPeers

dc-terrain commits / 589ebb7

dc-terrain: measure the surf zone before building an ocean on it
An FFT ocean's whole shore treatment -- displacement attenuation, shoaling, breaker foam -- is driven by depth, and all of it lives between the waterline and ~20 m down. The hypso curve's continental slope is steep (gradient 9.13 at its steepest), so it was worth checking whether that band is wide enough to put a wave in before writing any of it. Measured over 59 transects of the real mountain field: waterline -> 5 m depth: median 362 m (min 178) waterline -> 20 m depth: median 1460 m (min 706) So there is a surf zone, comfortably. The steep part of the curve is the shelf BREAK at -71 m, well below the wave-affected band; the shelf itself is gentle by construction (SHELF_W centred below sea level). The concern was misplaced and is now recorded as a number rather than an intuition. Mountain + hypso only, no erosion filter -- the envelope, not the coastline. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
589ebb749577b99b0e1c34dd843b061376e57a47
by plankatron <fleeky@gmail.com> · 2026-09-07T17:44:21.000Z

Changed files (1) +132 -0

tests/test_shore_profile.cpp +132 -0
……@@ -0,0 +1,132 @@
1// =========================================================================
2// SHORE PROFILE GATE — is there a beach to put waves on?
3//
4// An FFT ocean's entire shore treatment -- displacement attenuation, shoaling,
5// breaker foam -- is driven by DEPTH. All of it lives in the band between the
6// waterline and roughly 20 m down, where waves feel the bottom. If the hypso
7// curve's continental slope carries the seabed through that band in a few
8// horizontal metres, none of it is visible at any wave scale and the coastal
9// buffer has to come first.
10//
11// So measure the band before building anything on it: walk transects across
12// the real mountain field, find the waterline crossing, and report how far you
13// have to walk seaward to reach 5 m and 20 m of depth.
14//
15// ⚠ MOUNTAIN + HYPSO ONLY, NO EROSION FILTER. This reuses the same chain
16// test_hypso_curve measures land fraction with. Erosion perturbs the shore on
17// top of this; the large-scale profile that decides whether a surf zone exists
18// at all is set here. Treat the numbers as the envelope, not the coastline.
19//
20// Build:
21// g++ -O2 -std=c++17 -I ../proc-nodes/src/shared \
22// -o /tmp/shore_gate tests/test_shore_profile.cpp
23// =========================================================================
24#include <cstdio>
25#include <cmath>
26#include <vector>
27#include <algorithm>
28
29#include "erosion_cpp_shim.h"
30namespace dcerosion {
31#include "hypso_curve.inc"
32}
33using dcerosion::vec2;
34using dcerosion::vec3;
35using dcerosion::fractal_multifractal_2d_lod;
36using dcerosion::hypso_curve;
37
38// map_dc_erosion.gd's constants.
39static const float SEA = 0.00f;
40static const float SHELF_W = 0.12f;
41static const float SLOPE_W = 0.05f;
42static const float ABYSS = -0.62f;
43static const float PLAT_SLOPE = 0.13f;
44static const float MTN_ONSET = 0.35f;
45static const float MTN_WIDTH = 0.10f;
46static const float MTN_SLOPE = 1.30f;
47static const float PEAK_ONSET = 0.60f;
48static const float PEAK_GAIN = 0.30f;
49
50static const float TH = 60000.0f, MSCALE = 0.1f, LAC = 1.87f, MGAIN = 0.68f;
51static const int MOCT = 8;
52static const float VARIANCE = 0.0f;
53static const float M_PER_UNIT = 7790.0f; // curve units -> world metres
54
55// Surface elevation in metres relative to the waterline. Negative = underwater.
56static float depth_at(float x, float z, float waterline) {
57 vec3 m = fractal_multifractal_2d_lod(
58 vec2(x / TH * MSCALE, z / TH * MSCALE), 1.0f,
59 MOCT, LAC, MGAIN, VARIANCE, 999.0f);
60 float o = hypso_curve(m.x, SEA, SHELF_W, SLOPE_W, ABYSS, PLAT_SLOPE,
61 MTN_ONSET, MTN_WIDTH, MTN_SLOPE,
62 PEAK_ONSET, PEAK_GAIN).x;
63 return (waterline - o) * M_PER_UNIT; // +ve = water depth
64}
65
66int main() {
67 float waterline = hypso_curve(SEA, SEA, SHELF_W, SLOPE_W, ABYSS, PLAT_SLOPE,
68 MTN_ONSET, MTN_WIDTH, MTN_SLOPE,
69 PEAK_ONSET, PEAK_GAIN).x;
70 printf("SHORE PROFILE GATE — width of the band where waves feel the bottom\n");
71 printf(" waterline at curve %+.4f, %.0f m per curve unit\n", waterline, M_PER_UNIT);
72
73 const float HALF = 458800.0f;
74 const int TRANSECTS = 64;
75 const float COARSE = 200.0f; // m, to bracket the crossing
76 const float FINE = 2.0f; // m, to walk the band
77
78 std::vector<float> w5, w20;
79 int found = 0;
80
81 for (int t = 0; t < TRANSECTS; t++) {
82 // Transects along +x at assorted z, which cross the diagonal shore.
83 float z = -HALF + 2.0f * HALF * (t + 0.5f) / TRANSECTS;
84 float prev = depth_at(-HALF, z, waterline);
85 for (float x = -HALF + COARSE; x < HALF; x += COARSE) {
86 float d = depth_at(x, z, waterline);
87 if ((prev < 0.0f) != (d < 0.0f)) {
88 // Bracketed a waterline crossing. Bisect to ~1 m.
89 float lo = x - COARSE, hi = x;
90 for (int k = 0; k < 24; k++) {
91 float mid = 0.5f * (lo + hi);
92 if ((depth_at(lo, z, waterline) < 0.0f) != (depth_at(mid, z, waterline) < 0.0f)) hi = mid;
93 else lo = mid;
94 }
95 float shore = 0.5f * (lo + hi);
96 // Walk seaward (whichever direction deepens) to 5 m and 20 m.
97 float dir = (depth_at(shore + FINE, z, waterline) > depth_at(shore - FINE, z, waterline)) ? 1.0f : -1.0f;
98 float d5 = -1.0f, d20 = -1.0f;
99 for (float s = 0.0f; s < 20000.0f; s += FINE) {
100 float dd = depth_at(shore + dir * s, z, waterline);
101 if (d5 < 0.0f && dd >= 5.0f) d5 = s;
102 if (d20 < 0.0f && dd >= 20.0f) { d20 = s; break; }
103 }
104 if (d5 >= 0.0f && d20 >= 0.0f) { w5.push_back(d5); w20.push_back(d20); found++; }
105 break;
106 }
107 prev = d;
108 }
109 }
110
111 if (found == 0) { printf(" FAIL — no waterline crossing found on any transect\n"); return 1; }
112 std::sort(w5.begin(), w5.end());
113 std::sort(w20.begin(), w20.end());
114 auto med = [](std::vector<float>& v) { return v[v.size() / 2]; };
115 printf(" %d transects crossed the shore\n", found);
116 printf(" horizontal run from waterline to 5 m depth: median %8.1f m (min %7.1f, max %8.1f)\n",
117 med(w5), w5.front(), w5.back());
118 printf(" horizontal run from waterline to 20 m depth: median %8.1f m (min %7.1f, max %8.1f)\n",
119 med(w20), w20.front(), w20.back());
120 float m20 = med(w20);
121 printf("\n");
122 if (m20 < 50.0f) {
123 printf(" VERDICT: NO SURF ZONE. The seabed reaches 20 m within %.0f m of\n", m20);
124 printf(" the shore, so shoaling, breakers and displacement attenuation all\n");
125 printf(" collapse into a band thinner than a single wave. A coastal buffer\n");
126 printf(" must flatten the nearshore BEFORE any of the ocean shore work.\n");
127 return 1;
128 }
129 printf(" VERDICT: surf zone is %.0f m wide at the median — wide enough to\n", m20);
130 printf(" carry shoaling and breakers at ocean wavelengths (tens of metres).\n");
131 return 0;
132}