layers/density_map.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief Density maps: how a map of feature counts or cover sets a layer's local density. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/distribution/size_frequency.h" | ||
| 6 | #include "terraingen/sources/field.h" | ||
| 7 | |||
| 8 | #include <limits> | ||
| 9 | #include <memory> | ||
| 10 | #include <optional> | ||
| 11 | |||
| 12 | namespace terraingen { | ||
| 13 | |||
| 14 | /// What a density map's values measure. | ||
| 15 | enum class DensityMeasure { | ||
| 16 | Scale, ///< The local density over the layer's own; 1 keeps it as given. | ||
| 17 | Count, ///< Features in [`minDiameter`, `maxDiameter`) per `areaUnit` square metres. | ||
| 18 | ArealFraction, ///< Share of the ground covered by features in [`minDiameter`, `maxDiameter`). | ||
| 19 | }; | ||
| 20 | |||
| 21 | /// A map of a crater or boulder population that sets a layer's density point by point. | ||
| 22 | /// | ||
| 23 | /// The layer's size distribution keeps its shape and is scaled at each point until it measures | ||
| 24 | /// what the map does, so a map of the larger, detectable features also sets the smaller ones | ||
| 25 | /// below its detection limit. Features the layer places by other means are not counted twice: | ||
| 26 | /// manual catalog features stand in for generated ones above the catalog's completeness limit | ||
| 27 | /// (`CraterGenerationOptions::completeness`). With `total`, a crater layer takes the secondaries | ||
| 28 | /// thrown from its craters (`CraterLayerOptions::secondaries`) out of the map for craters too | ||
| 29 | /// small to throw them, and a boulder layer takes out the blocks it throws around craters. Under a | ||
| 30 | /// map, boulder patches, clusters and bedrock only move boulders within it, keeping its mean. | ||
| 31 | /// Where the map has no value the layer's density applies as given. | ||
| 32 | struct DensityMap { | ||
| 33 | 1 | std::shared_ptr<const SurfaceField> field; ///< The map; not null. | |
| 34 | 1 | DensityMeasure measure = DensityMeasure::Scale; ///< What its values measure. | |
| 35 | 1 | double minDiameter = 0.0; ///< Smallest diameter measured (m); positive unless `Scale`. | |
| 36 | /// Largest diameter measured (m), exclusive; finite for `ArealFraction` unless the size | ||
| 37 | /// distribution's valid range is. | ||
| 38 | 1 | double maxDiameter = std::numeric_limits<double>::infinity(); | |
| 39 | 1 | double areaUnit = 1.0; ///< Square metres per map area unit for `Count`: 1e6 for per km^2. | |
| 40 | /// The map measures every feature of its kind, including those a layer adds by other means, | ||
| 41 | /// which are taken out of it; otherwise it measures only what the layer generates. | ||
| 42 | 1 | bool total = true; | |
| 43 | |||
| 44 | 1 | bool operator==(const DensityMap&) const = default; | |
| 45 | }; | ||
| 46 | |||
| 47 | /// What a size slope map's values measure. | ||
| 48 | enum class SizeSlopeMeasure { | ||
| 49 | Exponent, ///< The cumulative exponent alpha of N(>=D), which falls as D^-alpha. | ||
| 50 | /// The mean diameter (m) of the features at least `referenceDiameter` across, from which | ||
| 51 | /// alpha = mean / (mean - D0) as for a power law above D0. | ||
| 52 | MeanDiameter, | ||
| 53 | }; | ||
| 54 | |||
| 55 | /// A map of how steeply feature counts fall with size, such as a mean boulder diameter map. | ||
| 56 | /// | ||
| 57 | /// At each point it bends the layer's size distribution into a power law through the count the | ||
| 58 | /// layer has there at `referenceDiameter`: N(>=D) = N(>=D0) (D / D0)^-alpha. A density map that | ||
| 59 | /// counts from D0 is kept exactly; where the map has no value the sizes keep their own shape. | ||
| 60 | struct SizeSlopeMap { | ||
| 61 | 1 | std::shared_ptr<const SurfaceField> field; ///< The map; not null. | |
| 62 | 1 | SizeSlopeMeasure measure = SizeSlopeMeasure::Exponent; ///< What its values measure. | |
| 63 | 1 | double referenceDiameter = 1.0; ///< D0 (m), where the power law meets the layer's count. | |
| 64 | /// The exponents alpha is clamped to, which bound the features drawn; positive and below | ||
| 65 | /// `maxExponent`. Generous bounds draw more candidates to thin. | ||
| 66 | 1 | double minExponent = 1.0; | |
| 67 | 1 | double maxExponent = 6.0; ///< The largest exponent; finite. | |
| 68 | |||
| 69 | 1 | bool operator==(const SizeSlopeMap&) const = default; | |
| 70 | }; | ||
| 71 | |||
| 72 | /// Checks a size slope map. | ||
| 73 | /// @throws std::invalid_argument for no field, a reference diameter that is not positive and | ||
| 74 | /// finite, or exponents that are not 0 < min < max finite. | ||
| 75 | void ValidateSizeSlopeMap(const SizeSlopeMap& map); | ||
| 76 | |||
| 77 | /// The exponent alpha `map` gives at surface (x, y), clamped to its bounds; none where it has no | ||
| 78 | /// value. | ||
| 79 | std::optional<double> SizeSlopeAt(const SizeSlopeMap& map, double x, double y); | ||
| 80 | |||
| 81 | /// Checks a density map. | ||
| 82 | /// @throws std::invalid_argument for no field, a diameter range that is not 0 < min < max for a | ||
| 83 | /// `Count` or `ArealFraction`, or an area unit that is not positive and finite. | ||
| 84 | void ValidateDensityMap(const DensityMap& map); | ||
| 85 | |||
| 86 | /// What `map` measures of `distribution` as it is: its count per square metre in the map's range, | ||
| 87 | /// the share of ground its features there cover, or 1 for `Scale`. | ||
| 88 | /// @throws std::domain_error if the range leaves the distribution's valid range, or an | ||
| 89 | /// `ArealFraction` has no finite largest diameter. | ||
| 90 | double MapMeasure(const DensityMap& map, const SizeFrequencyDistribution& distribution); | ||
| 91 | |||
| 92 | /// The map's value at surface (x, y) in square metres for a `Count`; NaN where it has none. | ||
| 93 | double MapValueAt(const DensityMap& map, double x, double y); | ||
| 94 | |||
| 95 | /// The local scale a density map gives a size distribution: the map's value over its measure of | ||
| 96 | /// the distribution, 1 where the map has no value. | ||
| 97 | class DensityScale { | ||
| 98 | public: | ||
| 99 | /// @throws std::invalid_argument for an invalid map or one measuring nothing of | ||
| 100 | /// `distribution`. | ||
| 101 | /// @throws std::domain_error as `MapMeasure`. | ||
| 102 | DensityScale(DensityMap map, const SizeFrequencyDistribution& distribution); | ||
| 103 | |||
| 104 | /// The scale at surface (x, y), not negative. | ||
| 105 | double At(double x, double y) const; | ||
| 106 | |||
| 107 | /// The scale where the map reads `value` (`MapValueAt`), clamped to [0, `Max`]; 1 for NaN. | ||
| 108 | double Scale(double value) const; | ||
| 109 | |||
| 110 | /// What the map measures of the distribution at a scale of 1. | ||
| 111 | 1 | double Measure() const { | |
| 112 | 1 | return measure_; | |
| 113 | } | ||
| 114 | |||
| 115 | /// The largest scale anywhere, at least 1. | ||
| 116 | 1044555 | double Max() const { | |
| 117 | 1044555 | return max_; | |
| 118 | } | ||
| 119 | |||
| 120 | /// The map. | ||
| 121 | const DensityMap& Map() const { | ||
| 122 | return map_; | ||
| 123 | } | ||
| 124 | |||
| 125 | private: | ||
| 126 | DensityMap map_; | ||
| 127 | double measure_ = 1.0; | ||
| 128 | double max_ = 1.0; | ||
| 129 | }; | ||
| 130 | |||
| 131 | } // namespace terraingen | ||
| 132 |