TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
261 / 274
95.3%
Branches:
164 / 331
49.5%
Functions:
84 / 85, 2 excl
98.8%

config/config.h
Line Branch Exec Source
1 /// @file
2 /// @brief The configuration specs: what a terrain configuration holds, as plain structs.
3 #pragma once
4
5 #include "terraingen/albedo/blast_zones.h"
6 #include "terraingen/albedo/crater_albedo.h"
7 #include "terraingen/albedo/raster_albedo.h"
8 #include "terraingen/albedo/rock_albedo.h"
9 #include "terraingen/albedo/slope_albedo.h"
10 #include "terraingen/albedo/splotches.h"
11 #include "terraingen/albedo/synthetic.h"
12 #include "terraingen/distribution/moore.h"
13 #include "terraingen/distribution/piecewise_power_law.h"
14 #include "terraingen/distribution/piecewise_uniform.h"
15 #include "terraingen/distribution/sp8023.h"
16 #include "terraingen/distribution/synthterrain.h"
17 #include "terraingen/distribution/viper.h"
18 #include "terraingen/layers/bedforms.h"
19 #include "terraingen/layers/boulders/boulder_layer.h"
20 #include "terraingen/layers/craters/degradation/diffusion.h"
21 #include "terraingen/layers/craters/generation/population.h"
22 #include "terraingen/layers/craters/profiles/fassett_thomson.h"
23 #include "terraingen/layers/craters/secondaries.h"
24 #include "terraingen/layers/craters/shaping/apply.h"
25 #include "terraingen/layers/density_map.h"
26 #include "terraingen/layers/dunes.h"
27 #include "terraingen/layers/pavement.h"
28 #include "terraingen/layers/region.h"
29 #include "terraingen/layers/roughness.h"
30 #include "terraingen/layers/sand_fill.h"
31 #include "terraingen/layers/sand_shadow.h"
32 #include "terraingen/layers/strata.h"
33 #include "terraingen/materials/materials.h"
34 #include "terraingen/noise/noise_layers.h"
35 #include "terraingen/raster/geotiff/geotiff.h"
36 #include "terraingen/raster/resample.h"
37 #include "terraingen/sources/geotiff_source.h"
38 #include "terraingen/sources/synthetic_terrain.h"
39
40 #include <concepts>
41 #include <cstddef>
42 #include <cstdint>
43 #include <memory>
44 #include <optional>
45 #include <string>
46 #include <type_traits>
47 #include <utility>
48 #include <variant>
49 #include <vector>
50
51 namespace terraingen {
52
53 /// Version of the configuration format that this library reads and writes.
54 inline constexpr int kConfigSchemaVersion = 1;
55
56 /// One noise layer, its octave gain optionally derived from a Hurst exponent.
57 struct NoiseLayerConfig {
58 9 NoiseLayer layer{};
59 /// When set, the octave gain comes from it (`GainFromHurst`) instead of `layer.fractal.gain`.
60 9 std::optional<float> hurst;
61
62 /// `layer` with its gain replaced from `hurst` when that is set.
63 NoiseLayer Effective() const;
64
65
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
9 bool operator==(const NoiseLayerConfig&) const = default;
66 };
67
68 /// Base "raster": a heightmap read from a GeoTIFF, in its own projected coordinates
69 /// (`GeoTiffSource`).
70 ///
71 /// Relative paths resolve against the configuration's folder. The raster can be resampled to
72 /// `pixelSize` metres over the same extent, as when upscaling a DEM; its coordinates must then be
73 /// a projection in metres.
74 struct RasterSourceSpec {
75 6 std::string path;
76 6 GeoTiffSourceOptions read{}; ///< How the band is read; `pixelSize` and `lowPass` in metres.
77
78
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
6 bool operator==(const RasterSourceSpec&) const = default;
79 };
80
81 /// A scalar field over the surface (`SurfaceField`): a constant `value`, or a GeoTIFF band read
82 /// in the surface's coordinates (`RasterField`) when `path` is set.
83 struct FieldSpec {
84 14 std::string path{}; ///< GeoTIFF, relative to the configuration's folder; empty for `value`.
85 14 double value = 0.0; ///< The field everywhere, without a `path`.
86 14 std::size_t band = 0; ///< The GeoTIFF band, from 0.
87 14 ResampleMethod method = ResampleMethod::Bilinear;
88 14 double scale = 1.0; ///< Multiplies every cell.
89 14 std::optional<double> fallback{}; ///< Where the GeoTIFF has no value; unset leaves none.
90 14 bool periodicLongitude = false; ///< The GeoTIFF's x is longitude repeating every 360 deg.
91
92
7/14
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 14 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 14 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 14 times.
14 bool operator==(const FieldSpec&) const = default;
93 };
94
95 /// A density map (`DensityMap`) of a crater or boulder population.
96 struct DensityMapSpec {
97 6 FieldSpec field{};
98 6 DensityMeasure measure = DensityMeasure::Scale;
99 6 double minDiameter = 0.0; ///< Metres.
100 6 std::optional<double> maxDiameter{}; ///< Metres; unset for no upper limit.
101 6 double areaUnit = 1.0; ///< Square metres per map area unit.
102 6 bool total = true;
103
104
6/12
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 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.
6 bool operator==(const DensityMapSpec&) const = default;
105 };
106
107 /// A size slope map (`SizeSlopeMap`) of a boulder population.
108 struct SizeSlopeMapSpec {
109 2 FieldSpec field{};
110 2 SizeSlopeMeasure measure = SizeSlopeMeasure::Exponent;
111 2 double referenceDiameter = 1.0; ///< Metres.
112 2 double minExponent = 1.0;
113 2 double maxExponent = 6.0;
114
115
5/10
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
2 bool operator==(const SizeSlopeMapSpec&) const = default;
116 };
117
118 struct ChainSourceSpec;
119
120 /// The starting heights of a terrain, its base, named in JSON by its "type".
121 using SourceSpec = std::variant<RasterSourceSpec, ChainSourceSpec>;
122
123 /// Base "chain": `sources` tried in order, each filling where those before it have no
124 /// data (`SourceChain`); its grid is the first source's.
125 struct ChainSourceSpec {
126 2 std::vector<SourceSpec> sources;
127
128
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 bool operator==(const ChainSourceSpec&) const = default;
129 };
130
131 /// Layer "noise": a stack of 1 to `kMaxNoiseLayers` noise layers added to the terrain
132 /// (`NoiseRelief`), measured from the base's reference point.
133 struct NoiseLayerSpec {
134 5 std::vector<NoiseLayerConfig> stack{NoiseLayerConfig{}};
135
136 /// The stack as terrain parameters, with each layer's Hurst exponent applied.
137 SyntheticTerrainParams Params() const;
138
139
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 bool operator==(const NoiseLayerSpec&) const = default;
140 };
141
142 /// Density "power_law": `PowerLawSfd`, N per m^2 with D in metres.
143 struct PowerLawSpec {
144 19 double coefficient = 0.1;
145 19 double exponent = 2.0;
146
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
19 bool operator==(const PowerLawSpec&) const = default;
147 };
148 /// Density "piecewise_power_law": `PiecewisePowerLawSfd`.
149 struct PiecewisePowerLawSpec {
150 4 std::vector<PowerLawSegment> segments{PowerLawSegment{}};
151
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 bool operator==(const PiecewisePowerLawSpec&) const = default;
152 };
153 /// Density "tabulated": `TabulatedSfd` from (D, N(>=D)) rows.
154 struct TabulatedSpec {
155 3 std::vector<double> diameters;
156 3 std::vector<double> cumulative;
157 3 double diameterUnit = 1.0; ///< Metres per table diameter unit; 1000 for km.
158 3 double areaUnit = 1.0; ///< Square metres per table area unit; 1e6 for km^2.
159
4/8
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
3 bool operator==(const TabulatedSpec&) const = default;
160 };
161 /// Density "piecewise_uniform": `PiecewiseUniformSfd`, bands with diameters spread uniformly.
162 struct PiecewiseUniformSpec {
163 3 std::vector<UniformBand> bands{UniformBand{}};
164 3 double total = 0.0; ///< Per m^2 across all bands, shared by `density`; 0 for as given.
165
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 bool operator==(const PiecewiseUniformSpec&) const = default;
166 };
167 /// Crater density "trask": `TraskEquilibrium`.
168 struct TraskSpec {
169 12 bool operator==(const TraskSpec&) const = default;
170 };
171 /// Crater density "hartmann": `HartmannEquilibrium`.
172 struct HartmannSpec {
173 1 bool operator==(const HartmannSpec&) const = default;
174 };
175 /// Crater density "moore": `MooreDistribution(terrain)`.
176 struct MooreSpec {
177 1 MooreTerrain terrain = MooreTerrain::RoughMare;
178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 bool operator==(const MooreSpec&) const = default;
179 };
180 /// Crater density "viper": `ViperSpecification`.
181 struct ViperSpec {
182 1 bool operator==(const ViperSpec&) const = default;
183 };
184 /// A published crater production function, a `ProductionRate`.
185 enum class ProductionFunction {
186 Neukum, ///< "neukum": `NeukumProductionFunction`, 10 m to 300 km.
187 ExtendedNeukum, ///< "extended_neukum": `ExtendedNeukumProductionFunction`, 2.5 m to 300 km.
188 Ivanov, ///< "ivanov": `IvanovMarsProductionFunction`, 15 m to 362 km.
189 };
190 /// A published crater chronology, N(>=1 km) by surface age.
191 enum class ChronologyModel {
192 Neukum, ///< "neukum": `NeukumLunarChronology`.
193 Ivanov, ///< "ivanov": `IvanovMarsChronology`.
194 };
195 /// Crater density "production_surface": `ProductionSurface`, the craters `production`
196 /// leaves on a surface `ageGyr` old by `chronology`, with no erosion.
197 struct ProductionSurfaceSpec {
198 15 ProductionFunction production = ProductionFunction::ExtendedNeukum;
199 15 ChronologyModel chronology = ChronologyModel::Neukum;
200 15 double ageGyr = 3.5;
201
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
15 bool operator==(const ProductionSurfaceSpec&) const = default;
202 };
203 struct MinimumSpec;
204 /// A crater density, named in JSON by its "type".
205 using DensitySpec =
206 std::variant<PowerLawSpec, PiecewisePowerLawSpec, TabulatedSpec, ProductionSurfaceSpec,
207 MinimumSpec, TraskSpec, HartmannSpec, MooreSpec, ViperSpec, PiecewiseUniformSpec>;
208 /// Crater density "minimum": `MinimumSfd`, the least of `distributions` at each diameter,
209 /// such as a production surface capped by an equilibrium.
210 struct MinimumSpec {
211 13 std::vector<DensitySpec> distributions;
212
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 bool operator==(const MinimumSpec&) const = default;
213 };
214
215 /// Crater ages "fresh": `FreshAges`.
216 struct FreshAgesSpec {
217 1 bool operator==(const FreshAgesSpec&) const = default;
218 };
219 /// Crater ages "uniform": `UniformAges(lifetimeGyr)`.
220 struct UniformAgesSpec {
221 28 double lifetimeGyr = 1.0;
222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 bool operator==(const UniformAgesSpec&) const = default;
223 };
224 /// Equilibrium densities for `SteadyStateAgesSpec`.
225 enum class EquilibriumModel { Trask, Hartmann };
226 /// Crater ages "steady_state": `SteadyStateAges` from `production`, `equilibrium` and
227 /// `chronology`.
228 struct SteadyStateAgesSpec {
229 12 double ageGyr = 3.5;
230 12 ProductionFunction production = ProductionFunction::ExtendedNeukum;
231 12 EquilibriumModel equilibrium = EquilibriumModel::Trask;
232 12 ChronologyModel chronology = ChronologyModel::Neukum;
233
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 12 times.
12 bool operator==(const SteadyStateAgesSpec&) const = default;
234 };
235 /// A crater age model, named in JSON by its "type".
236 using AgesSpec = std::variant<UniformAgesSpec, FreshAgesSpec, SteadyStateAgesSpec>;
237
238 /// Degradation "none": `NoDegradation`, optionally with a fixed depth-to-diameter ratio.
239 struct NoDegradationSpec {
240 4 std::optional<double> depthRatio;
241
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 bool operator==(const NoDegradationSpec&) const = default;
242 };
243 /// Degradation "freshness": `FreshnessDegradation`.
244 struct FreshnessDegradationSpec {
245 30 double cutoffDepthRatio = 0.04;
246
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 bool operator==(const FreshnessDegradationSpec&) const = default;
247 };
248 /// Where "diffusion" degradation takes its diffusion ages from.
249 enum class DiffusivityModel {
250 /// "matched": the Fassett et al. (2022) diffusivity fitted to the steady-state ages'
251 /// equilibrium, which keeps craters as deep as that equilibrium needs; Hartmann's for other
252 /// ages.
253 Matched,
254 Fassett2022Hartmann, ///< "fassett_2022_hartmann": `Fassett2022HartmannDiffusivity`.
255 Fassett2022Trask, ///< "fassett_2022_trask": `Fassett2022TraskDiffusivity`.
256 FassettThomson2014, ///< "fassett_thomson_2014": `FassettThomson2014Diffusivity`.
257 /// "fassett_thomson_2014_calibration": `FassettThomson2014DiffusionAge`.
258 FassettThomson2014Calibration,
259 PowerLaw, ///< "power_law": the spec's `powerLaw`.
260 };
261
262 /// Degradation "diffusion": `DiffusionDegradation`, the layer's profile worn by topographic
263 /// diffusion (`DiffusedProfile`).
264 struct DiffusionDegradationSpec {
265 5 DiffusivityModel diffusivity = DiffusivityModel::Matched;
266 5 PowerLawDiffusivity powerLaw{}; ///< Used when `diffusivity` is `PowerLaw`.
267 5 double reachRadii = 3.0; ///< How far the diffused profile reaches, in crater radii.
268
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
5 bool operator==(const DiffusionDegradationSpec&) const = default;
269 };
270 /// A degradation model, named in JSON by its "type".
271 using DegradationSpec =
272 std::variant<FreshnessDegradationSpec, NoDegradationSpec, DiffusionDegradationSpec>;
273
274 /// Crater profile "fassett_thomson": `FassettThomsonProfile(rim)`.
275 struct FassettThomsonSpec {
276 5 FassettThomsonRim rim = FassettThomsonRim::Original;
277
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 bool operator==(const FassettThomsonSpec&) const = default;
278 };
279 /// Crater profile "martin_parkes_dunstan": `MartinParkesDunstanProfile`.
280 struct MartinParkesDunstanSpec {
281 34 bool operator==(const MartinParkesDunstanSpec&) const = default;
282 };
283 /// A simple crater profile, named in JSON by its "type".
284 using SimpleProfileSpec = std::variant<FassettThomsonSpec, MartinParkesDunstanSpec>;
285 /// Crater profile "complex": `simple` below the simple-to-complex transition of the layer's
286 /// morphometry, and complex craters, with flat floors and central peaks, from it
287 /// (`ComplexCraterProfile` with `LunarComplexCraterShape` or `MarsComplexCraterShape`).
288 struct ComplexProfileSpec {
289 4 SimpleProfileSpec simple{MartinParkesDunstanSpec{}};
290
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 bool operator==(const ComplexProfileSpec&) const = default;
291 };
292 /// A crater profile, named in JSON by its "type".
293 using ProfileSpec = std::variant<FassettThomsonSpec, MartinParkesDunstanSpec, ComplexProfileSpec>;
294
295 /// The published fits of fresh crater depth, rim height and complex crater shape that a
296 /// crater layer shapes its craters with.
297 enum class CraterMorphometryFit {
298 Lunar, ///< "lunar": `LunarFreshCraterMorphometry` and `LunarComplexCraterShape`.
299 Mars, ///< "mars": `MarsFreshCraterMorphometry` and `MarsComplexCraterShape`.
300 };
301
302 /// Layer "craters": craters generated over the grid and applied to it (`CraterLayer`).
303 struct CraterLayerSpec {
304 39 CraterMorphometryFit morphometry = CraterMorphometryFit::Lunar;
305 39 bool generate = true; ///< Generate craters from `density`; otherwise only `manual` ones.
306 39 std::uint32_t seed = 1;
307 39 DensitySpec density{};
308 39 AgesSpec ages{};
309 39 double minDiameter = 4.0; ///< Metres.
310 39 double maxDiameter = 250.0; ///< Metres.
311 39 CraterSpacing spacing{};
312 39 std::size_t maxCraters = 0; ///< Keep at most this many generated craters; 0 keeps all.
313 /// Manual craters, in the surface's coordinates; parsing sets their `manual` flag.
314 39 std::vector<Crater> manual{};
315 39 ProfileSpec profile{MartinParkesDunstanSpec{}};
316 39 DegradationSpec degradation{};
317 39 CraterApplyOptions apply{};
318 39 std::optional<DensityMapSpec> densityMap{}; ///< Gives `CraterLayerOptions::densityMap`.
319 /// Gives `generation.completeness`: where `manual` is a complete catalog, from which diameter.
320 39 std::optional<FieldSpec> completeness{};
321
322
15/30
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 39 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 39 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 39 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 39 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 39 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 39 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 39 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 39 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 39 times.
✗ Branch 28 not taken.
✓ Branch 29 taken 39 times.
✗ Branch 31 not taken.
✓ Branch 32 taken 39 times.
✗ Branch 34 not taken.
✓ Branch 35 taken 39 times.
✗ Branch 37 not taken.
✓ Branch 38 taken 39 times.
39 bool operator==(const CraterLayerSpec&) const = default;
323 };
324
325 /// Boulder sizes "golombek_rapp": the layer's coverage, qa and qb.
326 struct GolombekRappBouldersSpec {
327 10 bool operator==(const GolombekRappBouldersSpec&) const = default;
328 };
329 /// Boulder sizes "sp8023_intercrater": `Sp8023IntercraterBlocks(curve)`.
330 struct Sp8023IntercraterBouldersSpec {
331 2 SpecificationCurve curve = SpecificationCurve::Nominal;
332
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 bool operator==(const Sp8023IntercraterBouldersSpec&) const = default;
333 };
334 /// Boulder sizes "sp8023_crater": `Sp8023CraterBlocks(curve)`.
335 struct Sp8023CraterBouldersSpec {
336 2 SpecificationCurve curve = SpecificationCurve::Nominal;
337
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 bool operator==(const Sp8023CraterBouldersSpec&) const = default;
338 };
339 /// Boulder sizes "synthterrain": `SynthterrainBoulderDistribution(model)`.
340 struct SynthterrainBouldersSpec {
341 2 SynthterrainBoulders model = SynthterrainBoulders::InterCrater;
342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 bool operator==(const SynthterrainBouldersSpec&) const = default;
343 };
344 /// A boulder size distribution, named in JSON by its "type"; "power_law",
345 /// "piecewise_power_law", "tabulated" and "piecewise_uniform" are as for crater densities.
346 using BoulderSizesSpec =
347 std::variant<GolombekRappBouldersSpec, Sp8023IntercraterBouldersSpec, Sp8023CraterBouldersSpec,
348 SynthterrainBouldersSpec, PowerLawSpec, PiecewisePowerLawSpec, TabulatedSpec,
349 PiecewiseUniformSpec>;
350
351 /// Layer "boulders": boulders scattered over the terrain (`BoulderLayer`).
352 ///
353 /// Ejecta come from the nearest crater layer before this one, which must exist when crater blocks
354 /// are enabled. `boulders.cacheBoulders` is not part of the configuration.
355 struct BoulderLayerSpec {
356 11 BoulderLayerOptions boulders{};
357 11 BoulderSizesSpec sizes{}; ///< Gives `boulders.sizes.distribution`.
358 11 BoulderSizesSpec ejectaSizes{}; ///< Gives `boulders.ejecta.distribution`.
359 11 bool extrapolate = false; ///< Let tables be evaluated beyond their published diameters.
360 /// Rock meshes (.obj, .gltf or .glb) that replace the procedural shapes, as
361 /// `BoulderShapeOptions::library`; relative to the configuration's folder.
362 11 std::vector<std::string> shapeFiles{};
363 11 std::optional<DensityMapSpec> densityMap{}; ///< Gives `boulders.densityMap`.
364 11 std::optional<SizeSlopeMapSpec> slopeMap{}; ///< Gives `boulders.slopeMap`.
365 /// Catalog boulders in the surface's coordinates, as `boulders.manual`.
366 11 std::vector<Crater> manual{};
367 11 std::optional<FieldSpec> completeness{}; ///< Gives `boulders.completeness`.
368
369
9/18
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 11 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 11 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 11 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 11 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 11 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 11 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 11 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 11 times.
11 bool operator==(const BoulderLayerSpec&) const = default;
370 };
371
372 /// Layer "roughness": fine roughness of a given RMS slope (`RoughnessLayer`).
373 struct RoughnessLayerSpec {
374 4 RoughnessOptions roughness{};
375
376
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 bool operator==(const RoughnessLayerSpec&) const = default;
377 };
378
379 /// Layer "bedforms": fields of wind-blown ripples or dunes (`BedformLayer`); angles in
380 /// radians.
381 struct BedformLayerSpec {
382 2 BedformOptions bedforms{};
383
384
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 bool operator==(const BedformLayerSpec&) const = default;
385 };
386
387 /// Layer "dunes": barchans, domes or crest-line dunes with ripples on them (`DuneLayer`); angles
388 /// in radians.
389 struct DuneLayerSpec {
390 6 DuneOptions dunes{};
391
392
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 bool operator==(const DuneLayerSpec&) const = default;
393 };
394
395 /// Layer "pavement": fractured bedrock plates parted by cracks or ridges (`PavementLayer`);
396 /// angles in radians.
397 struct PavementLayerSpec {
398 4 PavementOptions pavement{};
399
400
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 bool operator==(const PavementLayerSpec&) const = default;
401 };
402
403 /// Layer "strata": the ground beneath terraced into beds of benches and cliffs (`StrataLayer`);
404 /// angles in radians.
405 struct StrataLayerSpec {
406 4 StrataOptions strata{};
407
408
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 bool operator==(const StrataLayerSpec&) const = default;
409 };
410
411 /// Layer "sand_shadow": sand tails in the lee of the boulders of the nearest boulder layer before
412 /// it (`SandShadowLayer`); angles in radians.
413 struct SandShadowLayerSpec {
414 4 SandShadowOptions shadows{};
415
416
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 bool operator==(const SandShadowLayerSpec&) const = default;
417 };
418
419 /// Layer "sand_fill": sand pooled in the lows of the ground beneath it (`SandFillLayer`).
420 struct SandFillLayerSpec {
421 2 SandFillOptions sandFill{};
422
423
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 bool operator==(const SandFillLayerSpec&) const = default;
424 };
425
426 /// Layer "secondaries": secondary craters around the craters of the last crater layer
427 /// before this one (`SecondaryCraterLayer`), shaped with that layer's profile at a fixed depth
428 /// ratio and the fresh rim of `morphometry`. `secondaries.onsetCount` must be given.
429 struct SecondaryLayerSpec {
430 1 SecondaryCraterOptions secondaries{};
431 /// Depth-to-diameter ratio.
432 1 double depthRatio = 0.11;
433 1 CraterMorphometryFit morphometry = CraterMorphometryFit::Lunar;
434
435
3/6
✗ 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.
1 bool operator==(const SecondaryLayerSpec&) const = default;
436 };
437
438 /// Layer "dem_detail": the detail a GeoTIFF DEM has above its own low-pass, added to the heights
439 /// (`DetailLayer`), to restore measured relief over a source that keeps only the DEM's low
440 /// frequencies.
441 struct DemDetailLayerSpec {
442 ✗ std::string path;
443 ✗ GeoTiffSourceOptions read{.lowPass =
444 50.0}; ///< How the DEM is read; its `lowPass` is required.
445 ✗ double weight = 1.0; ///< Share of the detail added; 1 restores it whole.
446
447
0/6
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ bool operator==(const DemDetailLayerSpec&) const = default;
448 };
449
450 /// Layer "scale": height * `scale` + `offset` (`ScaleLayer`).
451 struct ScaleLayerSpec {
452 5 double scale = 1.0;
453 4 double offset = 0.0; ///< Metres.
454
455
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
5 bool operator==(const ScaleLayerSpec&) const = default;
456 };
457
458 /// Layer "clamp": heights clamped to [`min`, `max`], either optional (`ClampLayer`).
459 struct ClampLayerSpec {
460 5 std::optional<double> min; ///< Metres; unset for no lower bound.
461 5 std::optional<double> max; ///< Metres; unset for no upper bound.
462
463
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
5 bool operator==(const ClampLayerSpec&) const = default;
464 };
465
466 /// Region shape "rectangle": [`minX`, `maxX`] x [`minY`, `maxY`] in surface coordinates.
467 struct RectangleRegionSpec {
468 4 double minX = 0.0;
469 4 double minY = 0.0;
470 4 double maxX = 0.0;
471 4 double maxY = 0.0;
472
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
4 bool operator==(const RectangleRegionSpec&) const = default;
473 };
474 /// Region shape "circle": centred on (`x`, `y`) in surface coordinates.
475 struct CircleRegionSpec {
476 3 double x = 0.0;
477 3 double y = 0.0;
478 3 double radius = 0.0; ///< Metres.
479
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 bool operator==(const CircleRegionSpec&) const = default;
480 };
481 /// A region shape, named in JSON by its "type".
482 using RegionShapeSpec = std::variant<RectangleRegionSpec, CircleRegionSpec>;
483
484 /// A value held by pointer, so that a spec can hold the variant it belongs to; it is copied
485 /// and compared by value.
486 template <typename T> class Boxed {
487 public:
488 /// A default-constructed value.
489 53 Boxed() : value_(std::make_unique<T>()) {}
490 /// A value made from anything `T` can be made from, such as one alternative of a variant.
491 template <typename U>
492 requires(!std::same_as<std::remove_cvref_t<U>, Boxed> && std::constructible_from<T, U &&>)
493 19 Boxed(U&& value) // NOLINT(google-explicit-constructor): converts implicitly by design
494 19 : value_(std::make_unique<T>(std::forward<U>(value))) {}
495 59 Boxed(const Boxed& other) : value_(std::make_unique<T>(*other.value_)) {}
496 60 Boxed(Boxed&& other) noexcept = default;
497 1 Boxed& operator=(const Boxed& other) {
498
1/1
✓ Branch 2 taken 1 time.
1 value_ = std::make_unique<T>(*other.value_);
499 1 return *this;
500 }
501 2 Boxed& operator=(Boxed&& other) noexcept = default;
502 191 ~Boxed() = default;
503
504 22 T& operator*() {
505 22 return *value_;
506 }
507 63 const T& operator*() const {
508 63 return *value_;
509 }
510 1 T* operator->() {
511 1 return value_.get();
512 }
513 1 const T* operator->() const {
514 1 return value_.get();
515 }
516
517 8 friend bool operator==(const Boxed& a, const Boxed& b) {
518 8 return *a.value_ == *b.value_;
519 }
520
521 private:
522 std::unique_ptr<T> value_;
523 };
524
525 struct RegionLayerSpec;
526
527 /// A layer, named in JSON by its "type".
528 using LayerSpec =
529 std::variant<NoiseLayerSpec, CraterLayerSpec, BoulderLayerSpec, RoughnessLayerSpec,
530 BedformLayerSpec, ScaleLayerSpec, ClampLayerSpec, RegionLayerSpec,
531 SecondaryLayerSpec, DemDetailLayerSpec, DuneLayerSpec, SandFillLayerSpec,
532 PavementLayerSpec, StrataLayerSpec, SandShadowLayerSpec>;
533
534 /// Layer "region": `layer` applied only inside `shape` (`RegionLayer`). Boulder ejecta do
535 /// not see crater layers inside regions.
536 struct RegionLayerSpec {
537 6 RegionShapeSpec shape{};
538 6 double feather = 0.0; ///< Metres outside the shape over which the layer fades out.
539 6 BlendShape blend = BlendShape::Smoothstep;
540 6 Boxed<LayerSpec> layer{};
541
542
4/8
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 6 times.
6 bool operator==(const RegionLayerSpec&) const = default;
543 };
544
545 /// Formats for crater and boulder lists.
546 enum class ListFormat { Csv, Json };
547
548 /// The north-up grid of square cells an output covers: `width` x `height` cells of `pixelSize`
549 /// centred on (`centreX`, `centreY`).
550 ///
551 /// On a body the centre is a longitude and latitude, and cells are `pixelSize` metres along a
552 /// meridian, the same in degrees of longitude.
553 struct GridRegionSpec {
554 22 std::size_t width = 513; ///< Cells.
555 22 std::size_t height = 513; ///< Cells.
556 22 double pixelSize = 2.0; ///< Metres.
557 22 double centreX = 0.0; ///< Centre in surface coordinates.
558 22 double centreY = 0.0; ///< Centre in surface coordinates.
559
560
5/10
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 22 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 22 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 22 times.
22 bool operator==(const GridRegionSpec&) const = default;
561 };
562
563 /// A list file to write; the format follows the extension (.json or else CSV) unless given.
564 ///
565 /// The list covers `region`, or the base's grid without one, or else the whole body on a body.
566 struct ListOutput {
567 9 std::string path;
568 9 ListFormat format = ListFormat::Csv;
569 9 std::optional<GridRegionSpec> region{};
570 /// Sample spacing in metres the features are listed at, as a heightmap of that pixel size
571 /// applies them; unset for the grid's. A whole-body list needs it.
572 9 std::optional<double> spacing{};
573
4/8
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 9 times.
9 bool operator==(const ListOutput&) const = default;
574 };
575
576 /// A float32 GeoTIFF file to write over `region`, or the base's grid without one, with the base's
577 /// projection.
578 struct GeoTiffOutput {
579 12 std::string path;
580 12 TiffCompression compression = TiffCompression::Lzw; ///< None, Lzw or Deflate.
581 12 std::uint32_t tileSize = 256; ///< Pixels per tile side.
582 12 std::optional<GridRegionSpec> region{};
583
4/8
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 12 times.
12 bool operator==(const GeoTiffOutput&) const = default;
584 };
585
586 /// Output "crater_list": writes the generated and manual craters.
587 struct CraterListOutput : ListOutput {};
588
589 /// Output "heightmap": writes the generated heights.
590 struct HeightmapOutput : GeoTiffOutput {};
591
592 /// Output "boulder_list": writes every boulder the layers placed, models and relief alike.
593 struct BoulderListOutput : ListOutput {};
594
595 /// Output "boulder_models": writes the boulders placed as 3D models
596 /// (`BoulderModelThreshold`) on the ground beneath them, as `WriteBoulderModels` does, over the
597 /// grid a list would cover.
598 struct BoulderModelsOutput {
599 2 std::string path;
600 2 double creaseAngle = 60.0; ///< Degrees between faces above which vertices are split.
601 2 std::optional<GridRegionSpec> region{};
602 2 std::optional<double> spacing{}; ///< As `ListOutput::spacing`.
603 2 bool instanced = true; ///< Shared shapes written once and drawn per boulder.
604
5/10
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 2 times.
2 bool operator==(const BoulderModelsOutput&) const = default;
605 };
606
607 /// Output "albedo": writes the albedo (`TerrainConfig::albedo`) with one band, or three
608 /// (red, green, blue).
609 struct AlbedoOutput : GeoTiffOutput {};
610
611 /// Output "mesh": the whole body, as a mesh (`BodyMesh`) with smooth normals in any format
612 /// `WriteMesh` writes, or as an ICQ shape model (`BodyIcq`) for an .icq path.
613 struct MeshOutput {
614 3 std::string path;
615 3 std::size_t cellsPerFace = 128; ///< Quads along each edge of the six cube faces.
616 3 std::optional<double> spacing{}; ///< Sample spacing in metres; unset for a quad's side.
617
3/6
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
3 bool operator==(const MeshOutput&) const = default;
618 };
619
620 /// Output "terrain_mesh": the heights over `region`, or the base's grid without one, as a mesh
621 /// (`HeightMesh`) with smooth normals, in any format `WriteMesh` writes and in the frame boulder
622 /// models are written in.
623 struct TerrainMeshOutput {
624 2 std::string path;
625 2 std::optional<GridRegionSpec> region{};
626
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
2 bool operator==(const TerrainMeshOutput&) const = default;
627 };
628
629 /// Output "materials": the share of each material class (`TerrainConfig::materials`), one band
630 /// per `MaterialClass` in its order.
631 struct MaterialsOutput : GeoTiffOutput {};
632
633 /// Output "photometry": the Hapke albedo, b, c and roughness the materials mix to, in four bands.
634 struct PhotometryOutput : GeoTiffOutput {};
635
636 /// An output, named in JSON by its "type".
637 using OutputSpec =
638 std::variant<HeightmapOutput, CraterListOutput, BoulderListOutput, BoulderModelsOutput,
639 AlbedoOutput, MeshOutput, TerrainMeshOutput, MaterialsOutput, PhotometryOutput>;
640
641 /// Albedo source "synthetic": `SyntheticAlbedo`, a base colour with a noise stack per
642 /// channel (each 0 to `kMaxNoiseLayers` layers), measured from the base's reference
643 /// point.
644 struct SyntheticAlbedoSpec {
645 2 bool rgb = false;
646 2 double hue = 0.0;
647 2 double saturation = 0.0;
648 2 double value = kDefaultSyntheticAlbedoValue;
649 2 std::vector<NoiseLayerConfig> hueNoise;
650 2 std::vector<NoiseLayerConfig> saturationNoise;
651 2 std::vector<NoiseLayerConfig> valueNoise;
652
653 /// The albedo parameters, with each noise layer's Hurst exponent applied.
654 SyntheticAlbedoParams Params() const;
655
656
7/14
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 2 times.
2 bool operator==(const SyntheticAlbedoSpec&) const = default;
657 };
658
659 /// Albedo source "raster": `RasterAlbedo` from one or three bands of a GeoTIFF in the
660 /// terrain's projected coordinates, such as an orbital normal albedo map.
661 ///
662 /// Relative paths resolve against the configuration's folder. Samples are scaled as the file's
663 /// GDAL metadata says, or by `scale` and `offset` when given, for maps stored as integers.
664 struct RasterAlbedoSpec {
665 1 std::string path;
666 1 std::vector<std::size_t> bands{0}; ///< One or three bands, from 0.
667 1 std::optional<double> scale{};
668 1 std::optional<double> offset{};
669 1 RasterAlbedoOptions options{};
670
671
5/10
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 time.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 time.
✗ Branch 13 not taken.
✓ Branch 14 taken 1 time.
1 bool operator==(const RasterAlbedoSpec&) const = default;
672 };
673
674 /// An albedo source, named in JSON by its "type".
675 using AlbedoSourceSpec = std::variant<SyntheticAlbedoSpec, RasterAlbedoSpec>;
676
677 /// Albedo layer "craters": `CraterAlbedo`, bright ejecta and rays of the craters of one
678 /// top-level crater layer, or of every one.
679 struct CraterAlbedoSpec {
680 /// Index of the top-level crater layer, from 0; unset for every crater layer.
681 3 std::optional<std::size_t> craterLayer;
682 3 CraterAlbedoOptions options{};
683
684
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 bool operator==(const CraterAlbedoSpec&) const = default;
685 };
686
687 /// Albedo layer "splotches": `SplotchAlbedo`, the marks of secondary impacts over the terrain.
688 struct SplotchAlbedoSpec {
689 1 SplotchOptions options{};
690
691
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 bool operator==(const SplotchAlbedoSpec&) const = default;
692 };
693
694 /// Albedo layer "blast_zones": `BlastZoneAlbedo`, the zones landers' engines scoured.
695 struct BlastZoneAlbedoSpec {
696 1 std::vector<BlastZone> zones;
697
698
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 bool operator==(const BlastZoneAlbedoSpec&) const = default;
699 };
700
701 /// Albedo layer "rocks": `RockAlbedo`, the rock of the terrain's boulder layers, each read on the
702 /// ground beneath it.
703 struct RockAlbedoSpec {
704 /// Index of the top-level boulder layer, from 0; unset for every top-level boulder layer.
705 1 std::optional<std::size_t> boulderLayer;
706 1 RockAlbedoOptions options{};
707
708
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
1 bool operator==(const RockAlbedoSpec&) const = default;
709 };
710
711 /// Albedo layer "slopes": `SlopeAlbedo`, steep ground kept fresh by mass wasting.
712 struct SlopeAlbedoSpec {
713 1 SlopeMaturityOptions options{};
714
715
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 bool operator==(const SlopeAlbedoSpec&) const = default;
716 };
717
718 /// An albedo layer, named in JSON by its "type".
719 using AlbedoLayerSpec = std::variant<CraterAlbedoSpec, SplotchAlbedoSpec, BlastZoneAlbedoSpec,
720 RockAlbedoSpec, SlopeAlbedoSpec>;
721
722 /// The albedo of the terrain: a source and the layers that change it, evaluated over the
723 /// terrain's grid.
724 struct AlbedoSpec {
725 3 AlbedoSourceSpec source{};
726 3 std::vector<AlbedoLayerSpec> layers;
727
728
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 bool operator==(const AlbedoSpec&) const = default;
729 };
730
731 /// Body "plane": a projected frame in metres, the default.
732 struct PlaneSurfaceSpec {
733 41 bool operator==(const PlaneSurfaceSpec&) const = default;
734 };
735
736 /// Body "sphere": a body such as a planet or moon (`Surface`); grids and positions are
737 /// longitude and latitude in degrees, and features are generated over the whole body.
738 struct SphereSurfaceSpec {
739 3 double radius = 0.0; ///< Metres; must be given.
740
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 bool operator==(const SphereSurfaceSpec&) const = default;
741 };
742
743 /// Body "ellipsoid": a body shaped as a triaxial ellipsoid (`Surface::Ellipsoid`), with
744 /// planetocentric longitude and latitude in degrees and heights along the direction from the
745 /// centre.
746 struct EllipsoidSurfaceSpec {
747 1 std::vector<double> radii; ///< Semi-axes a, b, c in metres along x, y and z; exactly three.
748
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 bool operator==(const EllipsoidSurfaceSpec&) const = default;
749 };
750
751 /// Body "shape_model": a star-shaped body from a shape model file (`Surface::ShapeModel`), an
752 /// ICQ model or a closed mesh around its origin in body-fixed coordinates, as a `RadialShape`.
753 struct ShapeModelSurfaceSpec {
754 2 std::string path;
755 /// Intervals per face edge of the radial grid; unset keeps an ICQ model's own Q, and uses 256
756 /// for a mesh.
757 2 std::optional<std::size_t> q{};
758 /// Metres per file unit; unset is 1000 for an ICQ model (kilometres) and 1 for a mesh.
759 2 std::optional<double> metresPerUnit{};
760
3/6
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
2 bool operator==(const ShapeModelSurfaceSpec&) const = default;
761 };
762
763 /// The body a terrain lies on, a plane or a whole body, named in JSON by its "type".
764 using SurfaceSpec =
765 std::variant<PlaneSurfaceSpec, SphereSurfaceSpec, EllipsoidSurfaceSpec, ShapeModelSurfaceSpec>;
766
767 /// A published set of Hapke parameters per material class.
768 enum class PhotometryReference {
769 None, ///< No parameters; each class present must be given.
770 MarsPancam, ///< `MarsPancamPhotometry`: Pancam fits at 753 nm.
771 };
772
773 /// The Hapke parameters of each material class: `reference`'s, replaced by any given.
774 struct PhotometrySpec {
775 2 PhotometryReference reference = PhotometryReference::None;
776 2 MaterialPhotometry classes{}; ///< Parameters given per class, over the reference's.
777
778
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 bool operator==(const PhotometrySpec&) const = default;
779 };
780
781 /// The material map (`MaterialsAt`), read by "materials" and "photometry" outputs.
782 struct MaterialsSpec {
783 2 MaterialOptions options{};
784 2 PhotometrySpec photometry{};
785
786
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
2 bool operator==(const MaterialsSpec&) const = default;
787 };
788
789 /// Everything needed to generate a terrain: the body it lies on, optionally its base heights, the
790 /// layers added to them in order, the outputs to write, and optionally its albedo and materials.
791 struct TerrainConfig {
792 46 int schemaVersion = kConfigSchemaVersion;
793 46 SurfaceSpec body{};
794 46 std::optional<SourceSpec> base{}; ///< None for the body's datum, 0 m, everywhere.
795 46 std::vector<LayerSpec> layers;
796 46 std::vector<OutputSpec> outputs;
797 46 std::optional<AlbedoSpec> albedo; ///< None by default.
798 46 std::optional<MaterialsSpec> materials; ///< None by default.
799
800
7/14
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 46 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 46 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 46 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 46 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 46 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 46 times.
46 bool operator==(const TerrainConfig&) const = default;
801 };
802
803 } // namespace terraingen
804