layers/bedforms.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief `BedformLayer`, fields of wind-blown ripples or dunes. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/layers/exposure.h" | ||
| 6 | #include "terraingen/layers/fade.h" | ||
| 7 | #include "terraingen/layers/layer.h" | ||
| 8 | #include "terraingen/layers/material.h" | ||
| 9 | #include "terraingen/random/stream.h" | ||
| 10 | #include "terraingen/util/cells.h" | ||
| 11 | |||
| 12 | #include <cstdint> | ||
| 13 | #include <optional> | ||
| 14 | |||
| 15 | namespace terraingen { | ||
| 16 | |||
| 17 | /// The bedform fields a `BedformLayer` scatters: their ripples, cover and wind. | ||
| 18 | struct BedformOptions { | ||
| 19 | 3 | double wavelength = 1.0; ///< Crest to crest (m); each field's varies from 0.8 to 1.2 times it. | |
| 20 | 3 | double height = 0.1; ///< Trough to crest (m), scaled with each field's wavelength. | |
| 21 | /// Where the wind blows toward, radians counter-clockwise from +x; crests lie across it. | ||
| 22 | 3 | double windAzimuth = 0.0; | |
| 23 | 3 | double azimuthSpread = 0.0; ///< Each field's wind differs by up to this (radians). | |
| 24 | /// Share of a wavelength the lee face takes, in (0, 1): 0.5 is symmetric, less is steeper. | ||
| 25 | 3 | double leeFraction = 0.5; | |
| 26 | 3 | double sinuosity = 0.5; ///< How far a crest wanders along its length, in wavelengths. | |
| 27 | 3 | double cover = 0.0; ///< Share of the ground under fields, in [0, 0.9]. | |
| 28 | 3 | double fieldSize = 100.0; ///< Typical width of a field (m). | |
| 29 | 3 | std::uint32_t seed = 1; ///< Varies the fields. | |
| 30 | /// When set, a field is left out with probability the exposure at its centre, read from the | ||
| 31 | /// ground beneath the layer. | ||
| 32 | 3 | std::optional<ExposureParams> exposure; | |
| 33 | 3 | double exposureSpacing = 1.0; ///< Spacing the ground is read at for the exposure field (m). | |
| 34 | /// How bedforms too fine for the spacing fade out, by samples per wavelength. | ||
| 35 | 3 | ResolutionFade fade{}; | |
| 36 | |||
| 37 | 3 | bool operator==(const BedformOptions&) const = default; | |
| 38 | }; | ||
| 39 | |||
| 40 | /// Checks bedform options. | ||
| 41 | /// @throws std::invalid_argument for a non-positive wavelength or field size, a negative height, | ||
| 42 | /// a lee fraction outside (0, 1), a cover outside [0, 0.9], a negative sinuosity or spread, a | ||
| 43 | /// non-positive exposure spacing, or an invalid fade or exposure. | ||
| 44 | void ValidateBedformOptions(const BedformOptions& options); | ||
| 45 | |||
| 46 | /// Martian large wind ripples: 2 m wavelength, 0.2 m height, a 30 degree lee slope and sinuous | ||
| 47 | /// crests; cover and field size are placeholders to set per site. | ||
| 48 | BedformOptions MarsLargeRipples(); | ||
| 49 | |||
| 50 | /// Martian transverse aeolian ridges: symmetric, 25.8 m wavelength and 1.3 m height; cover and | ||
| 51 | /// field size are placeholders to set per site. | ||
| 52 | BedformOptions MarsTransverseAeolianRidges(); | ||
| 53 | |||
| 54 | /// Fields of wind-blown bedforms, ripples or dunes, added to the terrain. | ||
| 55 | /// | ||
| 56 | /// Each lattice cell may hold a field with a ragged edge whose crests run across its wind; relief | ||
| 57 | /// is C1 and zero in the troughs, and overlapping fields average by cover. A function of position | ||
| 58 | /// only, so grids, tiles and points agree. | ||
| 59 | class BedformLayer final : public FormulaLayer, public MaterialSource { | ||
| 60 | public: | ||
| 61 | /// On a sphere, fields are drawn in `CubeSphereCells`, shaped in their centres' local frames, | ||
| 62 | /// with the azimuth counter-clockwise from local east. Applying the layer with the exposure | ||
| 63 | /// field enabled needs `context.ground`. | ||
| 64 | /// @throws std::invalid_argument as `ValidateBedformOptions`. | ||
| 65 | explicit BedformLayer(BedformOptions options, Surface surface = {}); | ||
| 66 | |||
| 67 | /// How fully world (x, y) lies in a field, in [0, 1]; independent of the spacing. | ||
| 68 | /// @param ground Read by the exposure field; may be null without one. | ||
| 69 | /// @throws std::invalid_argument if the exposure field is enabled and `ground` is null. | ||
| 70 | double CoverAt(double x, double y, const Ground* ground = nullptr) const; | ||
| 71 | |||
| 72 | /// The options the layer was made with. | ||
| 73 | 1 | const BedformOptions& Options() const { | |
| 74 | 1 | return options_; | |
| 75 | } | ||
| 76 | |||
| 77 | 2 | MaterialClass Material() const override { | |
| 78 | 2 | return MaterialClass::Sand; | |
| 79 | } | ||
| 80 | /// `CoverAt`: the bedform fields are sand. | ||
| 81 | 66078 | double MaterialCoverAt(double x, double y, double /*spacing*/, | |
| 82 | const Ground* ground) const override { | ||
| 83 | 66078 | return CoverAt(x, y, ground); | |
| 84 | } | ||
| 85 | |||
| 86 | protected: | ||
| 87 | float HeightAt(float height, double x, double y, double spacing, | ||
| 88 | const Ground* ground) const override; | ||
| 89 | void CheckContext(const LayerContext& context) const override; | ||
| 90 | |||
| 91 | private: | ||
| 92 | struct Point { | ||
| 93 | double cover = 0.0; | ||
| 94 | double relief = 0.0; | ||
| 95 | }; | ||
| 96 | Point Evaluate(double x, double y, const Ground* ground) const; | ||
| 97 | void AddField(Point& point, RandomStream& random, const Vec2d& centre, const Vec2d& offset, | ||
| 98 | const Ground* ground) const; | ||
| 99 | |||
| 100 | BedformOptions options_; | ||
| 101 | double cell_ = 1.0; | ||
| 102 | double density_ = 0.0; | ||
| 103 | std::optional<ExposureField> exposure_; | ||
| 104 | std::optional<CubeSphereCells> cells_; | ||
| 105 | }; | ||
| 106 | |||
| 107 | } // namespace terraingen | ||
| 108 |