TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
53 / 53
100.0%
Branches:
46 / 92
50.0%
Functions:
5 / 5, 1 excl
100.0%

layers/dunes.h
Line Branch Exec Source
1 /// @file
2 /// @brief `DuneLayer`, dune fields of barchans, domes or crest-line dunes, with ripples on them.
3 #pragma once
4
5 #include "terraingen/layers/fade.h"
6 #include "terraingen/layers/layer.h"
7 #include "terraingen/layers/material.h"
8 #include "terraingen/util/geometry.h"
9
10 #include <cstdint>
11
12 namespace terraingen {
13
14 /// The kind of dune a `DuneLayer` builds.
15 enum class DuneShape {
16 Barchan, ///< Isolated crescents with a slip face, horns downwind; domes below `minWidth`.
17 Dome, ///< Isolated rounded heaps without a slip face.
18 Transverse, ///< Continuous crests across the wind, each with a slip face.
19 Barchanoid, ///< Transverse crests bent into linked crescents.
20 Linear, ///< Symmetric crests along the wind, as under a bimodal wind on scarce sand.
21 };
22
23 /// Ripples superposed on dunes, their crests bent by the dune's slope: each rises along a straight
24 /// stoss to a sharp crest and falls along a straight lee.
25 struct DuneRipples {
26 6 double wavelength = 0.0; ///< Crest to crest on flat sand (m); 0 for no ripples.
27 6 double height = 0.0; ///< Trough to crest on flat sand (m).
28 /// Share of a wavelength the lee face takes, in (0, 1): 0.5 is symmetric, less is steeper.
29 6 double leeFraction = 0.5;
30 6 double sinuosity = 0.3; ///< How far a crest wanders along its length, in wavelengths.
31 /// How strongly the dune's slope turns and tightens the ripples: their wave vector is the wind
32 /// minus this times the dune's gradient, so they shorten and lower on steep lee faces and turn
33 /// on flanks; they never grow above `height`.
34 6 double slopeCoupling = 1.0;
35 6 double interduneCover = 0.0; ///< Share of the ripples' height kept off the dunes, in [0, 1].
36 /// Defect density: pairs of crest ends (forked crests, Y-junctions) per metre of crest, as
37 /// Ewing et al. (2010) measure it; 0 for none.
38 6 double defectDensity = 0.0;
39 /// How far crests straighten off the stoss, in [0, 1]: 0 keeps `sinuosity` everywhere, 1
40 /// keeps it on flat sand and stoss slopes and takes it away on flanks and lee slopes as steep
41 /// as the stoss.
42 6 double flankStraightening = 0.0;
43 6 ResolutionFade fade{}; ///< How ripples too fine for the spacing fade out.
44
45
9/18
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 6 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 6 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 6 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 6 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 6 times.
6 bool operator==(const DuneRipples&) const = default;
46 };
47
48 /// Grainflows on slip faces: avalanches that leave a theatre-headed scarp below the brink and a
49 /// tongue-shaped lobe of sand below it. Each is drawn on a lattice aligned with the wind, its sizes
50 /// uniform between the bounds, and shown only where the ground faces downwind near the slip face
51 /// angle. Lengths are in metres, along the slope; widths across it.
52 struct DuneGrainflows {
53 6 double cover = 0.0; ///< Chance, in [0, 1], that each lattice cell holds one; 0 for none.
54 6 double minScarpWidth = 0.5;
55 6 double maxScarpWidth = 0.5;
56 6 double minScarpLength = 1.0;
57 6 double maxScarpLength = 1.0;
58 6 double minLobeWidth = 0.5;
59 6 double maxLobeWidth = 0.5;
60 6 double minLobeLength = 3.0;
61 6 double maxLobeLength = 3.0;
62 6 double thickness = 0.015; ///< Depth of a scarp and height of a lobe (m).
63 6 ResolutionFade fade{}; ///< How grainflows too narrow for the spacing fade out.
64
65
11/22
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 6 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 6 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 6 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 6 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 6 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 6 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 6 times.
6 bool operator==(const DuneGrainflows&) const = default;
66 };
67
68 /// The dunes a `DuneLayer` builds, their sizes and slopes. Lengths are in metres, angles in
69 /// radians.
70 struct DuneOptions {
71 6 DuneShape shape = DuneShape::Barchan;
72 /// Where the wind blows toward, radians counter-clockwise from +x; slip faces face it.
73 6 double windAzimuth = 0.0;
74 6 double azimuthSpread = 0.0; ///< Each barchan or dome's wind differs by up to this.
75 6 double slipFaceAngle = 0.5585053606381855; ///< Lee slip face slope; 32 deg.
76 6 double stossSlope = 0.17453292519943295; ///< Mean windward slope; 10 deg.
77 6 double stossExponent = 3.0; ///< Exponent of the cos^alpha windward profile of a dune.
78 6 double domeExponent = 1.8; ///< Exponent of the cos^alpha profile of a dome.
79
80 /// Barchans and domes: one candidate per lattice cell of `spacing`.
81 6 double spacing = 800.0;
82 6 double density = 0.5; ///< Chance a cell holds a dune, in [0, 1].
83 6 double width = 400.0; ///< Mean width, horn to horn (m).
84 6 double widthSpread = 0.3; ///< Each width varies from (1 - it) to (1 + it) times `width`.
85 6 double minWidth = 200.0; ///< Width below which a barchan is a dome without a slip face.
86 6 double heightRatio = 1.0 / 15.0; ///< Height over width.
87 6 double hornLength = 9.1; ///< How far the horns reach downwind of the crest, in heights.
88 6 double domeLength = 2.0; ///< Length of a dome over its width.
89
90 /// Transverse, barchanoid and linear dunes: crest-to-crest spacing (m).
91 6 double wavelength = 550.0;
92 6 double height = 20.0; ///< Crest height (m).
93 6 double sinuosity = 0.3; ///< How far a crest wanders along its length, in wavelengths.
94 /// Share of the height that varies along crests, ending some, in [0, 1].
95 6 double defects = 0.3;
96 /// Crest-line dunes: pairs of crest ends per metre of crest, as Ewing et al. (2010) measure it;
97 /// crests break into segments that stand above half their height for 1 / `defectDensity`
98 /// metres on average, falling to nothing over a wavelength either side of each break. 0 for
99 /// none.
100 6 double defectDensity = 0.0;
101 6 double flankSlope = 0.3490658503988659; ///< Mean flank slope of linear dunes; 20 deg.
102
103 6 std::uint32_t seed = 1; ///< Varies the dunes and ripples.
104 /// On a body, the surface point whose tangent frame the dunes are laid out in; true to scale
105 /// near it, and no dunes on the far hemisphere.
106 6 Vec2d anchor{};
107 ResolutionFade
108 6 fade{}; ///< How dunes too fine for the spacing fade out, by samples across them.
109 6 DuneRipples ripples{};
110 6 DuneGrainflows grainflows{}; ///< Avalanches on the slip faces; none by default.
111
112
26/52
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 6 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 6 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 6 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 6 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 6 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 6 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 6 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 6 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 6 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 6 times.
✗ Branch 28 not taken.
✓ Branch 29 taken 6 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 6 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 6 times.
✗ Branch 34 not taken.
✓ Branch 35 taken 6 times.
✗ Branch 36 not taken.
✓ Branch 37 taken 6 times.
✗ Branch 38 not taken.
✓ Branch 39 taken 6 times.
✗ Branch 40 not taken.
✓ Branch 41 taken 6 times.
✗ Branch 42 not taken.
✓ Branch 43 taken 6 times.
✗ Branch 45 not taken.
✓ Branch 46 taken 6 times.
✗ Branch 48 not taken.
✓ Branch 49 taken 6 times.
✗ Branch 51 not taken.
✓ Branch 52 taken 6 times.
✗ Branch 54 not taken.
✓ Branch 55 taken 6 times.
6 bool operator==(const DuneOptions&) const = default;
113 };
114
115 /// Checks dune options.
116 /// @throws std::invalid_argument for non-positive sizes or slopes, a slip face or flank of 90 deg
117 /// or more, a stoss slope not below the slip face, a density, defects or interdune cover outside
118 /// [0, 1], a width spread outside [0, 1), negative spreads or sinuosity, or invalid ripples or
119 /// fade.
120 void ValidateDuneOptions(const DuneOptions& options);
121
122 /// Martian large wind ripples to lay on dunes: 2.1 m wavelength, 0.2 m height and a 30 deg lee;
123 /// 0.099 pairs of crest ends per metre, as Olympia Undae's 2.1 m wind ripples (Ewing et al. 2010);
124 /// sinuous on stoss slopes and straighter on flanks and lee slopes (Lapotre et al. 2016; Ewing et
125 /// al. 2017).
126 DuneRipples MarsDuneRipples();
127
128 /// The grainflows of Namib Dune's lee slope (Ewing et al. 2017, Table 1): scarps 0.54 to 0.67 m
129 /// wide and 1.0 to 1.6 m long, lobes 0.57 to 0.66 m wide and 2.9 to 6 m long. Their thickness is
130 /// not measured on Mars; 0.015 m is that of a Namibian barchan's grainflows (Nield et al. 2017, in
131 /// Ewing et al. 2017). Every cell holds one, as palimpsest grainflows are abundant on that slope.
132 DuneGrainflows MarsDuneGrainflows();
133
134 /// Martian barchans with large ripples: 29 deg slip faces, 8 deg stoss slopes, 370 m wide, domes
135 /// below 200 m wide and twice as long as wide; spacing and density are placeholders to set per
136 /// site.
137 DuneOptions MarsBarchans();
138
139 /// The transverse dunes of Mars's north polar erg with large ripples: 248 m apart and 28 m high,
140 /// with 29 deg slip faces and 8 deg stoss slopes, and the defect density of Olympia Undae's primary
141 /// crests, 0.0027 pairs of ends per metre (Ewing et al. 2010).
142 DuneOptions MarsTransverseDunes();
143
144 /// Wind-blown dunes added to the terrain.
145 ///
146 /// Barchans are crescents: cross-wind slices of height H(1 - (y/Y)^2), a cos^alpha windward
147 /// profile up to a brink bent downwind toward the horns, and a slip face falling away from the
148 /// brink at `slipFaceAngle`; overlapping dunes join as their upper envelope. Crest-line dunes
149 /// follow a warped crest phase whose lee width keeps the slope at the slip face angle, to within a
150 /// few degrees where crests bend. A function
151 /// of position only, so grids, tiles and points agree.
152 class DuneLayer final : public FormulaLayer, public MaterialSource {
153 public:
154 /// On a body, dunes are laid out in the tangent frame of `options.anchor`, with the azimuth
155 /// counter-clockwise from local east.
156 /// @throws std::invalid_argument as `ValidateDuneOptions`.
157 explicit DuneLayer(DuneOptions options, Surface surface = {});
158
159 /// The dunes' height at world (x, y), without ripples (m).
160 double DuneHeightAt(double x, double y) const;
161
162 /// The options the layer was made with.
163 1 const DuneOptions& Options() const {
164 1 return options_;
165 }
166
167 434 MaterialClass Material() const override {
168 434 return MaterialClass::Sand;
169 }
170 /// How fully dune sand covers (x, y): the dunes, and with ripples their `interduneCover`.
171 double MaterialCoverAt(double x, double y, double spacing, const Ground* ground) const override;
172
173 protected:
174 float HeightAt(float height, double x, double y, double spacing,
175 const Ground* ground) const override;
176
177 private:
178 /// (x, y) in the plane the dunes are laid out in (m), or false on a body's far hemisphere.
179 bool Planar(double x, double y, Vec2d& planar) const;
180 double Height(const Vec2d& q) const;
181 double Barchans(const Vec2d& q) const;
182 double Crests(const Vec2d& q) const;
183 double Ripples(const Vec2d& q, double dune) const;
184 /// How fully a dune `dune` metres high covers the ground with sand, in [0, 1].
185 double SandOf(double dune) const;
186 /// How much of crest `crest` stands at `along` metres along it, in [0, 1]: 0 at its breaks.
187 double CrestBreaks(std::int64_t crest, double along) const;
188 /// The phase that forked crests `wavelength` metres apart, `density` pairs of ends per metre of
189 /// crest, add near planar point `q`, in wavelengths; `core` becomes the weight in [0, 1] that
190 /// fades the crests out at the forks' ends.
191 double Forks(const Vec2d& q, double wavelength, double density, double& core) const;
192 /// The dunes' gradient at planar point `q`, by central differences `step` metres apart.
193 Vec2d Gradient(const Vec2d& q, double step) const;
194 /// How far the ground with gradient `g` is a slip face, in [0, 1]: facing downwind near the
195 /// slip face angle.
196 double SlipFaceWeight(const Vec2d& g) const;
197 /// The grainflows' relief at planar point `q` (m), on the slip face share `face`.
198 double Grainflows(const Vec2d& q, double face) const;
199
200 DuneOptions options_;
201 Vec2d wind_{1.0, 0.0};
202 std::int64_t rings_ = 1;
203 Vec3d anchorDirection_{};
204 };
205
206 } // namespace terraingen
207