| … | … | @@ -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" |
| 30 | namespace dcerosion { |
| 31 | #include "hypso_curve.inc" |
| 32 | } |
| 33 | using dcerosion::vec2; |
| 34 | using dcerosion::vec3; |
| 35 | using dcerosion::fractal_multifractal_2d_lod; |
| 36 | using dcerosion::hypso_curve; |
| 37 | |
| 38 | // map_dc_erosion.gd's constants. |
| 39 | static const float SEA = 0.00f; |
| 40 | static const float SHELF_W = 0.12f; |
| 41 | static const float SLOPE_W = 0.05f; |
| 42 | static const float ABYSS = -0.62f; |
| 43 | static const float PLAT_SLOPE = 0.13f; |
| 44 | static const float MTN_ONSET = 0.35f; |
| 45 | static const float MTN_WIDTH = 0.10f; |
| 46 | static const float MTN_SLOPE = 1.30f; |
| 47 | static const float PEAK_ONSET = 0.60f; |
| 48 | static const float PEAK_GAIN = 0.30f; |
| 49 | |
| 50 | static const float TH = 60000.0f, MSCALE = 0.1f, LAC = 1.87f, MGAIN = 0.68f; |
| 51 | static const int MOCT = 8; |
| 52 | static const float VARIANCE = 0.0f; |
| 53 | static const float M_PER_UNIT = 7790.0f; // curve units -> world metres |
| 54 | |
| 55 | // Surface elevation in metres relative to the waterline. Negative = underwater. |
| 56 | static 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 | |
| 66 | int 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 | } |