layers/craters/shaping/slope.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief Craters formed on slopes: floors offset downslope and raised rims lowered upslope. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/layers/craters/crater.h" | ||
| 6 | #include "terraingen/random/value_distribution.h" | ||
| 7 | #include "terraingen/util/geometry.h" | ||
| 8 | |||
| 9 | #include <cstdint> | ||
| 10 | #include <optional> | ||
| 11 | |||
| 12 | namespace terraingen { | ||
| 13 | |||
| 14 | /// How craters formed on a slope become asymmetric along it and wear faster. | ||
| 15 | /// | ||
| 16 | /// Lunar craters on slopes have their deepest point offset downslope, an upslope margin that is | ||
| 17 | /// often only a break in slope with little raised rim, and ejecta thrown mostly downslope; the | ||
| 18 | /// asymmetry shows from slopes of a few degrees and does not grow on steeper ones, and they are | ||
| 19 | /// on average no longer downslope than across (Plescia et al. 2019, | ||
| 20 | /// https://www.hou.usra.edu/meetings/lpsc2019/pdf/2984.pdf). | ||
| 21 | /// | ||
| 22 | /// The slope is that of the ground beneath the layer, its source and earlier layers, across each | ||
| 23 | /// crater's datum footprint; the craters of the layer itself are left out, so each crater's shape | ||
| 24 | /// depends on no other and the relief its datum is fitted to is the relief applied. A crater on the | ||
| 25 | /// wall of a larger one responds to it when the larger craters are in an earlier layer. Needs the | ||
| 26 | /// `Plane` datum. | ||
| 27 | struct CraterSlopeResponse { | ||
| 28 | 39 | bool enabled = false; ///< Off: craters keep their shape on any slope. | |
| 29 | 39 | std::uint32_t seed = 0; ///< Varies the floor offsets. | |
| 30 | /// How far the deepest point lies downslope of the centre, in diameters, drawn per crater; | ||
| 31 | /// below 0.23. The default spans the interquartile range of the upslope wall width over the | ||
| 32 | /// downslope diameter, 0.55 to 0.69 with a median of 0.61, digitized from Plescia et al. | ||
| 33 | /// (2019, Fig. 3). | ||
| 34 | 39 | ValueDistribution floorOffset{ValueDistribution::Kind::Uniform, 0.045, 0.194}; | |
| 35 | /// Share of the raised rim and ejecta kept straight upslope, in [0, 1]; the rest returns as | ||
| 36 | /// the squared cosine of the angle from upslope falls, all of it across the slope and | ||
| 37 | /// downslope. | ||
| 38 | 39 | double upslopeRim = 0.0; | |
| 39 | /// Slope (radians) at which the asymmetry is complete, rising from none on level ground along | ||
| 40 | /// a smoothstep; 5 degrees, the "few degrees" of Plescia et al. (2019). | ||
| 41 | 39 | double fullSlope = 0.08726646259971647; | |
| 42 | /// The critical gradient Sc (rise over run) toward which downslope transport grows without | ||
| 43 | /// bound: a crater on a regional gradient S wears 1 / (1 - (S / Sc)^2) times as fast, the | ||
| 44 | /// nonlinear hillslope transport of Roering et al. (1999, | ||
| 45 | /// https://doi.org/10.1029/1998WR900090) that Fassett and Thomson (2014, | ||
| 46 | /// https://doi.org/10.1002/2014JE004698) suggest for steep lunar slopes. Unset wears craters | ||
| 47 | /// alike on any slope; it applies whether or not `enabled` is set. No lunar value is | ||
| 48 | /// published, so it has no default. | ||
| 49 | 39 | std::optional<double> criticalGradient{}; | |
| 50 | /// The fastest a slope makes craters wear, over level ground; at least 1. It caps the rate as | ||
| 51 | /// S nears Sc, where the form grows without bound; a numerical limit, not a measured one. | ||
| 52 | 39 | double maxWearRate = 10.0; | |
| 53 | |||
| 54 | 39 | bool operator==(const CraterSlopeResponse&) const = default; | |
| 55 | }; | ||
| 56 | |||
| 57 | /// How many times as fast a crater wears on ground rising `east` and `north` metres per metre, | ||
| 58 | /// under `response`'s critical gradient; 1 without one. | ||
| 59 | double SlopeWearRate(const CraterSlopeResponse& response, double east, double north); | ||
| 60 | |||
| 61 | /// Checks slope response settings. | ||
| 62 | /// @throws std::invalid_argument for an invalid floor offset distribution or one outside | ||
| 63 | /// [0, 0.23), an `upslopeRim` outside [0, 1], or a `fullSlope` that is not positive and finite. | ||
| 64 | void ValidateCraterSlopeResponse(const CraterSlopeResponse& response); | ||
| 65 | |||
| 66 | /// One crater's asymmetry on its slope. | ||
| 67 | struct CraterSlopeShape { | ||
| 68 | Vec2d shift{}; ///< Where the deepest point moves, east and north of the centre (m). | ||
| 69 | Vec2d upslope{}; ///< Upslope unit vector times the share of raised relief removed there. | ||
| 70 | |||
| 71 | /// Whether the crater keeps its level-ground shape. | ||
| 72 | 1587521 | bool Level() const { | |
| 73 | 1587521 | return shift.x == 0.0 && shift.y == 0.0 && upslope.x == 0.0 && upslope.y == 0.0; | |
| 74 | } | ||
| 75 | }; | ||
| 76 | |||
| 77 | /// The asymmetry `response` gives `crater` on ground rising `east` and `north` metres per metre, | ||
| 78 | /// its floor offset drawn from its ID; level when disabled or on level ground. | ||
| 79 | CraterSlopeShape SlopeShape(const Crater& crater, double east, double north, | ||
| 80 | const CraterSlopeResponse& response); | ||
| 81 | |||
| 82 | /// Where the round profile is read for a point at `offset` metres from the centre: moved back by | ||
| 83 | /// `shape`'s shift in full within 0.3 radii, fading along a smoothstep to none at the rim. | ||
| 84 | /// @param radius Crater radius in metres. | ||
| 85 | Vec2d SlopeShiftedOffset(const CraterSlopeShape& shape, const Vec2d& offset, double radius); | ||
| 86 | |||
| 87 | /// The share of a crater's raised relief, rim and ejecta, kept at `offset` from its centre: 1 less | ||
| 88 | /// the removed share times the squared cosine of the angle from upslope, smooth across the slope. | ||
| 89 | double SlopeRaisedShare(const CraterSlopeShape& shape, const Vec2d& offset); | ||
| 90 | |||
| 91 | } // namespace terraingen | ||
| 92 |