TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
17 / 17
100.0%
Branches:
11 / 22
50.0%
Functions:
4 / 4, 1 excl
100.0%

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
6/12
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 time.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 time.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 time.
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
5/10
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 time.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 time.
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