TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
481 / 494
97.4%
Branches:
592 / 662
89.4%
Functions:
115 / 117
98.3%

config/validate.cpp
Line Branch Exec Source
1 #include "terraingen/config/validate.h"
2
3 #include "terraingen/albedo/crater_albedo.h"
4 #include "terraingen/albedo/raster_albedo.h"
5 #include "terraingen/albedo/synthetic.h"
6 #include "terraingen/config/build.h"
7 #include "terraingen/config/fields/checks.h"
8 #include "terraingen/config/generate.h"
9 #include "terraingen/layers/craters/generation/spacing.h"
10 #include "terraingen/layers/detail.h"
11 #include "terraingen/layers/transforms.h"
12 #include "terraingen/mesh/io/icq.h"
13 #include "terraingen/mesh/io/mesh_io.h"
14 #include "terraingen/noise/noise_layers.h"
15 #include "terraingen/sources/flat_source.h"
16 #include "terraingen/util/overloaded.h"
17
18 #include <algorithm>
19 #include <cmath>
20 #include <optional>
21 #include <string>
22 #include <string_view>
23 #include <type_traits>
24 #include <variant>
25 #include <vector>
26
27 namespace terraingen {
28
29 namespace {
30
31 using config::At;
32 using config::BuildBoulderOptions;
33 using config::BuildRegionShape;
34 using config::Child;
35 using config::Element;
36 using config::RequireNonEmpty;
37 using config::RequireNonNegative;
38 using config::RequirePositive;
39 using config::Resolve;
40
41 454 void CheckGridSize(std::size_t width, std::size_t height, const std::string& path) {
42
7/8
✓ Branch 0 taken 449 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 449 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 448 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 447 times.
454 if (width < 2 || height < 2 || width > kMaxConfigGridSize || height > kMaxConfigGridSize) {
43
5/5
✓ Branch 1 taken 7 times.
✓ Branch 4 taken 7 times.
✓ Branch 7 taken 7 times.
✓ Branch 10 taken 7 times.
✓ Branch 13 taken 7 times.
14 throw ConfigError(path, std::to_string(width) + "x" + std::to_string(height) +
44
1/1
✓ Branch 1 taken 7 times.
7 " cells; each side must be between 2 and " +
45
3/3
✓ Branch 2 taken 7 times.
✓ Branch 5 taken 7 times.
✓ Branch 8 taken 7 times.
21 std::to_string(kMaxConfigGridSize));
46 }
47 447 }
48
49 /// @throws ConfigError at `path` for an expected count of `items` above the generation limit.
50 409 void CheckExpectedCount(double expected, std::string_view items, std::string_view remedy,
51 const std::string& path) {
52
1/11
✓ Branch 9 taken 409 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
818 const double limit = CraterGenerationOptions{}.maxExpectedCraters;
53
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 407 times.
409 if (expected > limit) {
54
4/4
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
4 throw ConfigError(path, "about " + std::to_string(static_cast<long long>(expected)) + " " +
55
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
8 std::string(items) + " expected, more than the limit of " +
56
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
8 std::to_string(static_cast<long long>(limit)) + "; " +
57
3/3
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
✓ Branch 8 taken 2 times.
10 std::string(remedy));
58 }
59 407 }
60
61 /// @throws ConfigError for a stack of more than `kMaxNoiseLayers` layers, or none unless
62 /// `allowEmpty`, or an invalid layer.
63 479 void ValidateNoiseStack(const std::vector<NoiseLayerConfig>& layers, const std::string& path,
64 bool allowEmpty) {
65
8/8
✓ Branch 1 taken 160 times.
✓ Branch 2 taken 319 times.
✓ Branch 3 taken 157 times.
✓ Branch 4 taken 3 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 475 times.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 475 times.
479 if ((layers.empty() && !allowEmpty) || layers.size() > kMaxNoiseLayers) {
66
6/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 4 times.
✓ Branch 6 taken 4 times.
✓ Branch 9 taken 4 times.
✓ Branch 12 taken 4 times.
12 throw ConfigError(path, std::string("needs ") + (allowEmpty ? "0" : "1") + " to " +
67
3/3
✓ Branch 3 taken 4 times.
✓ Branch 6 taken 4 times.
✓ Branch 9 taken 4 times.
12 std::to_string(kMaxNoiseLayers) + " layers");
68 }
69
2/2
✓ Branch 1 taken 325 times.
✓ Branch 2 taken 470 times.
795 for (std::size_t i = 0; i < layers.size(); ++i) {
70
1/1
✓ Branch 1 taken 325 times.
325 const std::string layer = Element(path, i);
71
2/2
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 321 times.
325 if (layers[i].hurst) {
72
2/2
✓ Branch 2 taken 4 times.
✓ Branch 7 taken 2 times.
6 config::RequireFinite(*layers[i].hurst, Child(layer, "hurst"));
73 }
74
3/3
✓ Branch 1 taken 320 times.
✓ Branch 5 taken 323 times.
✓ Branch 8 taken 320 times.
646 At(layer, [&] { ValidateNoiseLayer(layers[i].Effective()); });
75 325 }
76 470 }
77
78 /// Where an output evaluates the layers: the area it covers and its sample spacing.
79 struct Extent {
80 double area = 0.0; ///< Square metres.
81 double spacing = 0.0; ///< Metres.
82 };
83
84 /// What a layer is checked against, and what the layers before it found.
85 struct LayerChecks {
86 const std::vector<Extent>& extents;
87 const std::filesystem::path& directory;
88 const Surface& surface;
89 bool craters = false; ///< Whether a crater layer came before.
90 bool boulders = false; ///< Whether a boulder layer came before.
91 /// Craters the crater layers so far are expected to generate over each extent; empty when
92 /// none generate.
93 std::vector<double> expectedCraters{};
94 };
95
96 250 void CheckManualCraters(const std::vector<Crater>& manual, const std::string& path) {
97
2/2
✓ Branch 1 taken 56 times.
✓ Branch 2 taken 247 times.
303 for (std::size_t n = 0; n < manual.size(); ++n) {
98 56 const Crater& crater = manual[n];
99
1/1
✓ Branch 1 taken 56 times.
56 const std::string at = Element(path, n);
100
5/6
✓ Branch 1 taken 55 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 55 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 55 times.
56 if (!(std::isfinite(crater.x) && std::isfinite(crater.y))) {
101
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(at, "position must be finite");
102 }
103
2/2
✓ Branch 2 taken 55 times.
✓ Branch 5 taken 54 times.
56 RequirePositive(crater.diameter, Child(at, "diameter"));
104
2/2
✓ Branch 2 taken 54 times.
✓ Branch 5 taken 53 times.
55 RequireNonNegative(crater.ageGyr, Child(at, "age_gyr"));
105 56 }
106 247 }
107
108 250 void ValidateCraters(const CraterLayerSpec& craters, const std::string& path,
109 LayerChecks& context) {
110
2/2
✓ Branch 2 taken 250 times.
✓ Branch 5 taken 247 times.
253 CheckManualCraters(craters.manual, Child(path, "manual"));
111
2/2
✓ Branch 2 taken 247 times.
✓ Branch 5 taken 246 times.
495 At(Child(path, "spacing"), [&] { ValidateCraterSpacing(craters.spacing); });
112
2/2
✓ Branch 2 taken 246 times.
✓ Branch 5 taken 243 times.
495 At(Child(path, "apply"), [&] { ValidateCraterApplyOptions(craters.apply); });
113
1/1
✓ Branch 1 taken 234 times.
243 const CraterModels models = BuildCraterModels(craters, path);
114 234 context.craters = true;
115
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 222 times.
234 if (!craters.generate) {
116 12 return;
117 }
118
1/1
✓ Branch 2 taken 222 times.
222 const std::string minPath = Child(path, "min_diameter");
119
1/1
✓ Branch 1 taken 220 times.
222 RequirePositive(craters.minDiameter, minPath);
120
6/6
✓ Branch 1 taken 219 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 218 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 218 times.
220 if (!(std::isfinite(craters.maxDiameter) && craters.maxDiameter > craters.minDiameter)) {
121
3/3
✓ Branch 2 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
6 throw ConfigError(Child(path, "max_diameter"), "must exceed min_diameter");
122 }
123
1/1
✓ Branch 2 taken 218 times.
218 context.expectedCraters.resize(context.extents.size(), 0.0);
124
2/2
✓ Branch 1 taken 316 times.
✓ Branch 2 taken 216 times.
532 for (std::size_t i = 0; i < context.extents.size(); ++i) {
125
1/1
✓ Branch 1 taken 315 times.
316 const double expected = At(minPath, [&] {
126 632 return models.density->ExpectedCount(context.extents[i].area, craters.minDiameter,
127 631 craters.maxDiameter);
128 });
129
1/1
✓ Branch 3 taken 314 times.
315 CheckExpectedCount(expected, "craters", "raise min_diameter or set max_craters", minPath);
130 314 context.expectedCraters[i] += expected;
131 }
132
2/2
✓ Branch 2 taken 216 times.
✓ Branch 3 taken 12 times.
240 }
133
134 71 void ValidateBoulders(const BoulderLayerSpec& spec, const std::string& path,
135 const LayerChecks& context) {
136
1/1
✓ Branch 1 taken 68 times.
71 const BoulderLayerOptions boulders = BuildBoulderOptions(spec, path, context.directory);
137
1/1
✓ Branch 1 taken 65 times.
136 At(path, [&] { ValidateBoulderLayerOptions(boulders); });
138
7/7
✓ Branch 1 taken 65 times.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 49 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 14 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 63 times.
65 if (boulders.ejecta.MinCraterDiameter() > 0.0 && !context.craters) {
139
2/2
✓ Branch 3 taken 2 times.
✓ Branch 6 taken 2 times.
4 throw ConfigError(Child(Child(path, "ejecta"), "min_crater_diameter"),
140
2/2
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
10 "ejecta need a crater layer before this one");
141 }
142 63 const BoulderSizes& sizes = boulders.sizes;
143
2/2
✓ Branch 5 taken 112 times.
✓ Branch 6 taken 62 times.
174 for (const Extent& extent : context.extents) {
144 const double minDiameter =
145 112 std::fmax(sizes.minDiameter, boulders.fade.startSamples * extent.spacing);
146
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 94 times.
112 if (minDiameter >= sizes.maxDiameter) {
147 18 continue;
148 }
149 const double expected =
150
2/2
✓ Branch 1 taken 94 times.
✓ Branch 5 taken 94 times.
94 BoulderCandidates(boulders)->ExpectedCount(extent.area, minDiameter, sizes.maxDiameter);
151
1/1
✓ Branch 3 taken 93 times.
94 CheckExpectedCount(expected, "candidate boulders", "raise min_diameter or lower coverage",
152
1/1
✓ Branch 2 taken 94 times.
189 Child(path, "min_diameter"));
153 }
154 68 }
155
156 13 void ValidateSecondaries(const SecondaryLayerSpec& layer, const std::string& path,
157 const LayerChecks& context) {
158
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 12 times.
13 if (!context.craters) {
159
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(path, "secondaries need a crater layer before this one");
160 }
161
5/6
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 11 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 11 times.
12 if (!(layer.secondaries.onsetCount && *layer.secondaries.onsetCount > 0.0)) {
162
1/1
✓ Branch 2 taken 1 time.
2 throw ConfigError(Child(path, "onset_count"),
163
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
5 "required: the sources give secondary sizes, not their number");
164 }
165 22 At(path, [&] { ValidateSecondaryCraterOptions(layer.secondaries); });
166
2/2
✓ Branch 2 taken 10 times.
✓ Branch 5 taken 9 times.
11 RequirePositive(layer.depthRatio, Child(path, "depth_ratio"));
167 9 }
168
169 /// @throws ConfigError at `path` for a raster file that cannot be read.
170 54 GeoTiffInfo ReadRasterInfo(const std::string& file, const std::filesystem::path& directory,
171 const std::string& path) {
172 try {
173
2/2
✓ Branch 1 taken 54 times.
✓ Branch 4 taken 49 times.
59 return ReadGeoTiffInfo(Resolve(file, directory));
174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 } catch (const GeoTiffError& error) {
175
3/3
✓ Branch 3 taken 5 times.
✓ Branch 6 taken 5 times.
✓ Branch 9 taken 5 times.
15 throw ConfigError(path, error.what());
176 5 }
177 }
178
179 /// @throws ConfigError at `path` for a band the raster does not have.
180 49 void CheckBand(std::size_t band, const GeoTiffInfo& info, const std::string& path) {
181
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 47 times.
49 if (band >= info.bands) {
182
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
4 throw ConfigError(path, "the file has " + std::to_string(info.bands) +
183
3/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
6 (info.bands == 1 ? " band" : " bands"));
184 }
185 47 }
186
187 RasterGrid DescribeSourceAt(const SourceSpec& source, const std::filesystem::path& directory,
188 const std::string& path, const Surface& surface);
189
190 6 RasterGrid DescribeChain(const ChainSourceSpec& chain, const std::filesystem::path& directory,
191 const std::string& path, const Surface& surface) {
192
1/1
✓ Branch 2 taken 6 times.
6 const std::string sources = Child(path, "sources");
193
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5 times.
6 if (chain.sources.empty()) {
194
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(sources, "must not be empty");
195 }
196 5 RasterGrid grid;
197
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
10 for (std::size_t n = 0; n < chain.sources.size(); ++n) {
198 const RasterGrid described =
199
2/2
✓ Branch 1 taken 8 times.
✓ Branch 5 taken 5 times.
11 DescribeSourceAt(chain.sources[n], directory, Element(sources, n), surface);
200
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if (n == 0) {
201 3 grid = described;
202 }
203 }
204 4 return grid;
205 6 }
206
207 424 RasterGrid DescribeRegion(const GridRegionSpec& region, const std::string& path,
208 const Surface& surface) {
209
2/2
✓ Branch 2 taken 424 times.
✓ Branch 5 taken 422 times.
426 RequirePositive(region.pixelSize, Child(path, "pixel_size"));
210
2/2
✓ Branch 2 taken 422 times.
✓ Branch 5 taken 417 times.
427 CheckGridSize(region.width, region.height, Child(path, "width"));
211
5/6
✓ Branch 1 taken 416 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 416 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 416 times.
417 if (!(std::isfinite(region.centreX) && std::isfinite(region.centreY))) {
212
3/3
✓ Branch 2 taken 1 time.
✓ Branch 6 taken 1 time.
✓ Branch 9 taken 1 time.
3 throw ConfigError(Child(path, "centre_x"), "the centre must be finite");
213 }
214 416 const double cell = region.pixelSize / surface.UnitLength();
215
4/4
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 378 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 415 times.
454 if (surface.IsBody() &&
216
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 1 time.
38 !(std::fabs(region.centreY) + 0.5 * cell * static_cast<double>(region.height) <= 90.0 &&
217
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 cell * static_cast<double>(region.width) <= 360.0)) {
218
1/1
✓ Branch 2 taken 1 time.
2 throw ConfigError(Child(path, "height"),
219
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
5 "the grid must lie within latitudes -90 to 90 and one turn of longitude");
220 }
221 415 return config::RegionGrid(region, surface);
222 }
223
224 /// @throws ConfigError for a raster whose coordinates do not suit the surface.
225 38 void CheckRasterCrs(const GeoTiffInfo& info, const Surface& surface, const std::string& path) {
226
5/6
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 37 times.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 37 times.
38 if (info.geoKeys.IsGeographic() && !surface.IsBody()) {
227 throw ConfigError(path, "the raster's CRS is geographic (degrees); craters are sized in "
228 "metres, so reproject it first, for example with gdalwarp -t_srs, "
229
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 "or set the body to a sphere");
230 }
231
6/8
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 36 times.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✓ Branch 10 taken 36 times.
37 if (surface.IsBody() && !info.geoKeys.Empty() && !info.geoKeys.IsGeographic()) {
232 throw ConfigError(
233
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 path, "on a body the raster must be in longitude and latitude (a geographic CRS)");
234 }
235 36 }
236
237 674 void ValidateLayer(const LayerSpec& spec, const std::string& path, LayerChecks& context) {
238 674 std::visit(
239
1/1
✓ Branch 1 taken 618 times.
674 Overloaded{
240 272 [&](const NoiseLayerSpec& noise) {
241
2/2
✓ Branch 2 taken 272 times.
✓ Branch 5 taken 266 times.
278 ValidateNoiseStack(noise.stack, Child(path, "stack"), false);
242 266 },
243 250 [&](const CraterLayerSpec& layer) { ValidateCraters(layer, path, context); },
244 71 [&](const BoulderLayerSpec& layer) {
245 71 ValidateBoulders(layer, path, context);
246 62 context.boulders = true;
247 62 },
248 4 [&](const SandShadowLayerSpec& layer) {
249
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
4 if (!context.boulders) {
250
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(path, "sand shadows need a boulder layer before this one");
251 }
252 6 At(path, [&] { ValidateSandShadowOptions(layer.shadows); });
253 2 },
254 13 [&](const SecondaryLayerSpec& layer) { ValidateSecondaries(layer, path, context); },
255 8 [&](const RoughnessLayerSpec& layer) {
256 16 At(path, [&] { ValidateRoughnessOptions(layer.roughness); });
257 7 },
258 7 [&](const DuneLayerSpec& layer) {
259 14 At(path, [&] { ValidateDuneOptions(layer.dunes); });
260 6 },
261 4 [&](const PavementLayerSpec& layer) {
262 8 At(path, [&] { ValidatePavementOptions(layer.pavement); });
263 3 },
264 4 [&](const StrataLayerSpec& layer) {
265 8 At(path, [&] { ValidateStrataOptions(layer.strata); });
266 3 },
267 5 [&](const SandFillLayerSpec& layer) {
268 10 At(path, [&] { ValidateSandFillOptions(layer.sandFill); });
269 4 },
270 7 [&](const BedformLayerSpec& layer) {
271 14 At(path, [&] { ValidateBedformOptions(layer.bedforms); });
272 6 },
273 6 [&](const ScaleLayerSpec& layer) {
274
1/1
✓ Branch 2 taken 6 times.
12 At(path, [&] { ScaleLayer(layer.scale, layer.offset); });
275 6 },
276 6 [&](const ClampLayerSpec& layer) {
277
1/1
✓ Branch 2 taken 4 times.
12 At(path, [&] { ClampLayer(layer.min, layer.max); });
278 4 },
279 2 [&](const DemDetailLayerSpec& layer) {
280
1/1
✓ Branch 2 taken 2 times.
2 const std::string file = Child(path, "path");
281
1/1
✓ Branch 1 taken 2 times.
2 RequireNonEmpty(layer.path, file);
282
1/1
✓ Branch 1 taken 2 times.
2 const GeoTiffInfo info = ReadRasterInfo(layer.path, context.directory, file);
283
2/2
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
2 CheckBand(layer.read.band, info, Child(path, "band"));
284
1/1
✓ Branch 1 taken 2 times.
2 CheckRasterCrs(info, context.surface, file);
285
2/2
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 1 time.
3 RequirePositive(layer.read.lowPass, Child(path, "low_pass"));
286
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
1 At(Child(path, "weight"), [&] {
287
2/2
✓ Branch 3 taken 1 time.
✓ Branch 7 taken 1 time.
2 DetailLayer(std::make_shared<FlatSource>(2, 2, 1.0),
288
1/1
✓ Branch 1 taken 1 time.
2 std::make_shared<FlatSource>(2, 2, 1.0), layer.weight);
289 1 });
290 3 },
291 15 [&](const RegionLayerSpec& layer) {
292
2/2
✓ Branch 2 taken 15 times.
✓ Branch 5 taken 13 times.
17 At(Child(path, "shape"),
293
2/2
✓ Branch 1 taken 15 times.
✓ Branch 4 taken 13 times.
15 [&] { ValidateRegionShape(BuildRegionShape(layer.shape)); });
294
2/2
✓ Branch 2 taken 13 times.
✓ Branch 5 taken 12 times.
14 RequireNonNegative(layer.feather, Child(path, "feather"));
295 // Crater layers inside a region count for neither later layers nor the summary.
296
1/1
✓ Branch 1 taken 12 times.
12 LayerChecks inner = context;
297
2/2
✓ Branch 2 taken 12 times.
✓ Branch 6 taken 11 times.
13 ValidateLayer(*layer.layer, Child(path, "layer"), inner);
298 12 },
299 },
300 spec);
301 618 }
302
303 43 RasterGrid DescribeRaster(const RasterSourceSpec& raster, const std::filesystem::path& directory,
304 const std::string& path, const Surface& surface) {
305
1/1
✓ Branch 2 taken 43 times.
43 const std::string file = Child(path, "path");
306
1/1
✓ Branch 1 taken 41 times.
43 RequireNonEmpty(raster.path, file);
307
1/1
✓ Branch 1 taken 37 times.
41 const GeoTiffInfo info = ReadRasterInfo(raster.path, directory, file);
308
2/2
✓ Branch 2 taken 37 times.
✓ Branch 5 taken 36 times.
38 CheckBand(raster.read.band, info, Child(path, "band"));
309
1/1
✓ Branch 1 taken 34 times.
36 CheckRasterCrs(info, surface, file);
310
2/2
✓ Branch 2 taken 34 times.
✓ Branch 5 taken 34 times.
34 RequireNonNegative(raster.read.lowPass, Child(path, "low_pass"));
311 34 RasterGrid grid{info.width, info.height, info.transform};
312
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 6 times.
34 if (!raster.read.pixelSize) {
313
1/1
✓ Branch 1 taken 27 times.
28 CheckGridSize(grid.width, grid.height, file);
314 27 return grid;
315 }
316
1/1
✓ Branch 2 taken 6 times.
6 const std::string pixelSize = Child(path, "pixel_size");
317
1/1
✓ Branch 2 taken 5 times.
6 RequirePositive(*raster.read.pixelSize, pixelSize);
318
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
5 if (grid.transform.rowRotation != 0.0 || grid.transform.colRotation != 0.0) {
319
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(pixelSize, "rotated rasters cannot be resampled");
320 }
321 4 RasterGrid resampled;
322 try {
323
1/1
✓ Branch 3 taken 4 times.
4 resampled = ResampledGrid(grid, *raster.read.pixelSize / surface.UnitLength());
324
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } catch (const std::invalid_argument&) {
325
0/1
✗ Branch 1 not taken.
✗ throw ConfigError(pixelSize, "resampling would exceed " +
326
0/4
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ std::to_string(kMaxConfigGridSize) + " cells per side");
327 }
328
1/1
✓ Branch 1 taken 3 times.
4 CheckGridSize(resampled.width, resampled.height, pixelSize);
329 3 return resampled;
330 53 }
331
332 49 RasterGrid DescribeSourceAt(const SourceSpec& source, const std::filesystem::path& directory,
333 const std::string& path, const Surface& surface) {
334 49 return std::visit(
335
1/1
✓ Branch 1 taken 32 times.
81 Overloaded{
336 43 [&](const RasterSourceSpec& s) { return DescribeRaster(s, directory, path, surface); },
337 6 [&](const ChainSourceSpec& s) { return DescribeChain(s, directory, path, surface); },
338 },
339 64 source);
340 }
341
342 /// Rows of a whole-body list's grid above which its spacing is too fine.
343 constexpr double kMaxWholeBodyRows = 1e9;
344
345 /// The grid the output at `path` covers (`OutputGrid`), its region and spacing checked; none for a
346 /// mesh.
347 /// @throws ConfigError for an invalid region or spacing, or an output with no grid to cover.
348 471 std::optional<RasterGrid> CheckOutputGrid(const OutputSpec& output, const std::string& path,
349 const std::optional<RasterGrid>& base,
350 const Surface& surface) {
351
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 462 times.
471 if (std::holds_alternative<MeshOutput>(output)) {
352 9 return std::nullopt;
353 }
354 462 bool listed = false;
355 462 std::visit(
356 462 [&](const auto& o) {
357 if constexpr (requires { o.region; }) {
358
13/16
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::AlbedoOutput>(terraingen::AlbedoOutput const&) const:
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 2 times.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderListOutput>(terraingen::BoulderListOutput const&) const:
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderModelsOutput>(terraingen::BoulderModelsOutput const&) const:
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 1 time.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::CraterListOutput>(terraingen::CraterListOutput const&) const:
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 10 times.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::HeightmapOutput>(terraingen::HeightmapOutput const&) const:
✓ Branch 1 taken 282 times.
✓ Branch 2 taken 24 times.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::MaterialsOutput>(terraingen::MaterialsOutput const&) const:
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::PhotometryOutput>(terraingen::PhotometryOutput const&) const:
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::TerrainMeshOutput>(terraingen::TerrainMeshOutput const&) const:
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 1 time.
462 if (o.region) {
359
16/16
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::AlbedoOutput>(terraingen::AlbedoOutput const&) const:
✓ Branch 2 taken 39 times.
✓ Branch 6 taken 39 times.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderListOutput>(terraingen::BoulderListOutput const&) const:
✓ Branch 2 taken 18 times.
✓ Branch 6 taken 18 times.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderModelsOutput>(terraingen::BoulderModelsOutput const&) const:
✓ Branch 2 taken 12 times.
✓ Branch 6 taken 12 times.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::CraterListOutput>(terraingen::CraterListOutput const&) const:
✓ Branch 2 taken 48 times.
✓ Branch 6 taken 48 times.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::HeightmapOutput>(terraingen::HeightmapOutput const&) const:
✓ Branch 2 taken 282 times.
✓ Branch 6 taken 273 times.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::MaterialsOutput>(terraingen::MaterialsOutput const&) const:
✓ Branch 2 taken 6 times.
✓ Branch 6 taken 6 times.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::PhotometryOutput>(terraingen::PhotometryOutput const&) const:
✓ Branch 2 taken 13 times.
✓ Branch 6 taken 13 times.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::TerrainMeshOutput>(terraingen::TerrainMeshOutput const&) const:
✓ Branch 2 taken 6 times.
✓ Branch 6 taken 6 times.
433 DescribeRegion(*o.region, Child(path, "region"), surface);
360 }
361 }
362 if constexpr (config::FeatureOutput<std::decay_t<decltype(o)>>) {
363 89 listed = true;
364
5/6
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderListOutput>(terraingen::BoulderListOutput const&) const:
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderModelsOutput>(terraingen::BoulderModelsOutput const&) const:
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 12 times.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::CraterListOutput>(terraingen::CraterListOutput const&) const:
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 51 times.
89 if (o.spacing) {
365
2/3
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderListOutput>(terraingen::BoulderListOutput const&) const:
✗ Branch 2 not taken.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderModelsOutput>(terraingen::BoulderModelsOutput const&) const:
✓ Branch 2 taken 1 time.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::CraterListOutput>(terraingen::CraterListOutput const&) const:
✓ Branch 2 taken 7 times.
8 const std::string spacing = Child(path, "spacing");
366
2/3
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderListOutput>(terraingen::BoulderListOutput const&) const:
✗ Branch 2 not taken.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderModelsOutput>(terraingen::BoulderModelsOutput const&) const:
✓ Branch 2 taken 1 time.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::CraterListOutput>(terraingen::CraterListOutput const&) const:
✓ Branch 2 taken 6 times.
8 RequirePositive(*o.spacing, spacing);
367
10/24
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderListOutput>(terraingen::BoulderListOutput const&) const:
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderModelsOutput>(terraingen::BoulderModelsOutput const&) const:
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 time.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 time.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::CraterListOutput>(terraingen::CraterListOutput const&) const:
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 1 time.
✓ Branch 9 taken 1 time.
✓ Branch 10 taken 5 times.
13 if (!o.region && !base && surface.IsBody() &&
368
3/6
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderListOutput>(terraingen::BoulderListOutput const&) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderModelsOutput>(terraingen::BoulderModelsOutput const&) const:
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::CraterListOutput>(terraingen::CraterListOutput const&) const:
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 4 times.
6 180.0 * surface.UnitLength() / *o.spacing > kMaxWholeBodyRows) {
369
3/9
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderListOutput>(terraingen::BoulderListOutput const&) const:
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderModelsOutput>(terraingen::BoulderModelsOutput const&) const:
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
auto terraingen::(anonymous namespace)::CheckOutputGrid(std::variant<terraingen::HeightmapOutput, terraingen::CraterListOutput, terraingen::BoulderListOutput, terraingen::BoulderModelsOutput, terraingen::AlbedoOutput, terraingen::MeshOutput, terraingen::TerrainMeshOutput, terraingen::MaterialsOutput, terraingen::PhotometryOutput> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::optional<terraingen::RasterGrid> const&, terraingen::Surface const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::CraterListOutput>(terraingen::CraterListOutput const&) const:
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(spacing, "is too fine to list features over the body");
370 }
371 8 }
372 }
373 451 },
374 output);
375
1/1
✓ Branch 1 taken 451 times.
451 std::optional<RasterGrid> grid = config::OutputGrid(output, base, surface);
376
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 444 times.
451 if (!grid) {
377
1/1
✓ Branch 2 taken 7 times.
14 throw ConfigError(Child(path, "region"),
378
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
3 listed && surface.IsBody()
379 ? "required without a base, or a spacing to list the whole body"
380
4/4
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 4 times.
✓ Branch 4 taken 7 times.
✓ Branch 7 taken 7 times.
38 : "required without a base: the grid of square cells to cover");
381 }
382 444 return grid;
383 }
384
385 /// What outputs are checked against.
386 struct OutputContext {
387 bool body = false; ///< Whether the surface is a body.
388 bool albedo = false; ///< Whether the configuration has an albedo.
389 bool materials = false; ///< Whether the configuration has a material map.
390 };
391
392 32 void RequireMeshFile(const std::string& file, const std::string& path) {
393
4/4
✓ Branch 1 taken 32 times.
✓ Branch 4 taken 32 times.
✓ Branch 7 taken 3 times.
✓ Branch 8 taken 29 times.
32 if (!IsMeshPath(file)) {
394
3/3
✓ Branch 2 taken 3 times.
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
9 throw ConfigError(path, "must end in .obj, .gltf, .glb or .stl");
395 }
396 29 }
397
398 372 void ValidateGeoTiffOutput(const GeoTiffOutput& output, const std::string& path) {
399
2/2
✓ Branch 2 taken 372 times.
✓ Branch 5 taken 370 times.
374 RequireNonEmpty(output.path, Child(path, "path"));
400
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 368 times.
370 if (output.compression == TiffCompression::PackBits) {
401
1/1
✓ Branch 2 taken 2 times.
4 throw ConfigError(Child(path, "compression"),
402
2/2
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
10 "packbits is read-only; use none, lzw or deflate");
403 }
404
3/4
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 366 times.
368 if (output.tileSize == 0 || output.tileSize % 16 != 0) {
405
3/3
✓ Branch 2 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
6 throw ConfigError(Child(path, "tile_size"), "must be a positive multiple of 16");
406 }
407 366 }
408
409 309 void ValidateOutput(const HeightmapOutput& output, const std::string& path,
410 const OutputContext& /*context*/) {
411 309 ValidateGeoTiffOutput(output, path);
412 306 }
413
414 45 void ValidateOutput(const AlbedoOutput& output, const std::string& path,
415 const OutputContext& context) {
416
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 44 times.
45 if (!context.albedo) {
417
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(path, "the configuration has no albedo");
418 }
419 44 ValidateGeoTiffOutput(output, path);
420 41 }
421
422 7 void ValidateOutput(const MaterialsOutput& output, const std::string& path,
423 const OutputContext& context) {
424
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6 times.
7 if (!context.materials) {
425
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(path, "the configuration has no materials");
426 }
427 6 ValidateGeoTiffOutput(output, path);
428 6 }
429
430 14 void ValidateOutput(const PhotometryOutput& output, const std::string& path,
431 const OutputContext& context) {
432
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 13 times.
14 if (!context.materials) {
433
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(path, "the configuration has no materials");
434 }
435 13 ValidateGeoTiffOutput(output, path);
436 13 }
437
438 /// Whether a layer lays sand over the material map.
439 34 bool LaysSand(const LayerSpec& layer) {
440
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
67 return std::holds_alternative<DuneLayerSpec>(layer) ||
441
2/2
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 4 times.
66 std::holds_alternative<BedformLayerSpec>(layer) ||
442
4/4
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 28 times.
100 std::holds_alternative<SandFillLayerSpec>(layer) ||
443 63 std::holds_alternative<SandShadowLayerSpec>(layer);
444 }
445
446 /// @throws ConfigError under "materials" for invalid options or photometry, or a photometry output
447 /// without parameters for a class the surface can have.
448 16 void ValidateMaterials(const TerrainConfig& config) {
449 16 const MaterialsSpec& materials = *config.materials;
450
2/2
✓ Branch 1 taken 16 times.
✓ Branch 4 taken 15 times.
49 At("materials", [&] { ValidateMaterialOptions(materials.options); });
451
1/1
✓ Branch 1 taken 15 times.
15 const MaterialPhotometry photometry = config::BuildPhotometry(materials.photometry);
452
2/2
✓ Branch 1 taken 15 times.
✓ Branch 4 taken 14 times.
46 At("materials.photometry", [&] { ValidateMaterialPhotometry(photometry); });
453 const bool photometryOutput =
454
1/1
✓ Branch 3 taken 14 times.
14 std::any_of(config.outputs.begin(), config.outputs.end(), [](const OutputSpec& output) {
455 34 return std::holds_alternative<PhotometryOutput>(output);
456 });
457
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 13 times.
14 if (!photometryOutput) {
458 1 return;
459 }
460
1/1
✓ Branch 1 taken 13 times.
26 std::vector<std::pair<MaterialClass, const char*>> needed{{MaterialClass::Soil, "soil"}};
461
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 4 times.
19 if (materials.options.exposure ||
462
3/3
✓ Branch 3 taken 6 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 4 times.
6 std::any_of(config.layers.begin(), config.layers.end(), [](const LayerSpec& layer) {
463
4/4
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 15 times.
33 return std::holds_alternative<PavementLayerSpec>(layer) ||
464 33 std::holds_alternative<StrataLayerSpec>(layer);
465 13 })) {
466
1/1
✓ Branch 1 taken 9 times.
9 needed.emplace_back(MaterialClass::Bedrock, "bedrock");
467 }
468
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11 times.
13 if (materials.options.dustCover > 0.0) {
469
1/1
✓ Branch 1 taken 2 times.
2 needed.emplace_back(MaterialClass::Dust, "dust");
470 }
471
3/3
✓ Branch 3 taken 13 times.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 7 times.
13 if (std::any_of(config.layers.begin(), config.layers.end(), LaysSand)) {
472
1/1
✓ Branch 1 taken 6 times.
6 needed.emplace_back(MaterialClass::Sand, "sand");
473 }
474
3/3
✓ Branch 3 taken 13 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 11 times.
13 if (std::any_of(config.layers.begin(), config.layers.end(), [](const LayerSpec& layer) {
475 34 return std::holds_alternative<BoulderLayerSpec>(layer);
476 })) {
477
1/1
✓ Branch 1 taken 2 times.
2 needed.emplace_back(MaterialClass::Rock, "rock");
478 }
479
2/2
✓ Branch 7 taken 32 times.
✓ Branch 8 taken 7 times.
39 for (const auto& [material, name] : needed) {
480
2/2
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 26 times.
32 if (!photometry[static_cast<std::size_t>(material)]) {
481
2/2
✓ Branch 2 taken 6 times.
✓ Branch 5 taken 6 times.
18 throw ConfigError(Child("materials.photometry", name),
482
2/2
✓ Branch 2 taken 6 times.
✓ Branch 5 taken 6 times.
30 "required by the photometry output: the surface has some");
483 }
484 }
485 13 }
486
487 78 void ValidateOutput(const ListOutput& output, const std::string& path,
488 const OutputContext& /*context*/) {
489
2/2
✓ Branch 2 taken 78 times.
✓ Branch 5 taken 76 times.
80 RequireNonEmpty(output.path, Child(path, "path"));
490 76 }
491
492 15 void ValidateOutput(const BoulderModelsOutput& output, const std::string& path,
493 const OutputContext& /*context*/) {
494
2/2
✓ Branch 2 taken 15 times.
✓ Branch 5 taken 14 times.
16 RequireMeshFile(output.path, Child(path, "path"));
495
3/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 13 times.
14 if (!(output.creaseAngle >= 0.0 && output.creaseAngle <= 180.0)) {
496
3/3
✓ Branch 2 taken 1 time.
✓ Branch 6 taken 1 time.
✓ Branch 9 taken 1 time.
3 throw ConfigError(Child(path, "crease_angle"), "must be between 0 and 180 degrees");
497 }
498 13 }
499
500 13 void ValidateOutput(const MeshOutput& output, const std::string& path,
501 const OutputContext& context) {
502
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 12 times.
13 if (!context.body) {
503 throw ConfigError(path, "a mesh needs a whole body: a sphere, ellipsoid or shape model; "
504
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 "use terrain_mesh on a plane");
505 }
506
4/4
✓ Branch 1 taken 12 times.
✓ Branch 4 taken 12 times.
✓ Branch 7 taken 9 times.
✓ Branch 8 taken 3 times.
12 if (!IsIcqPath(output.path)) {
507
2/2
✓ Branch 2 taken 9 times.
✓ Branch 5 taken 8 times.
10 RequireMeshFile(output.path, Child(path, "path"));
508 }
509
3/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
11 if (output.cellsPerFace == 0 || output.cellsPerFace > 4096) {
510
3/3
✓ Branch 2 taken 1 time.
✓ Branch 6 taken 1 time.
✓ Branch 9 taken 1 time.
3 throw ConfigError(Child(path, "cells_per_face"), "must be 1 to 4096");
511 }
512
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 8 times.
10 if (output.spacing) {
513
2/2
✓ Branch 2 taken 2 times.
✓ Branch 6 taken 1 time.
3 RequirePositive(*output.spacing, Child(path, "spacing"));
514 }
515 9 }
516
517 8 void ValidateOutput(const TerrainMeshOutput& output, const std::string& path,
518 const OutputContext& /*context*/) {
519
2/2
✓ Branch 2 taken 8 times.
✓ Branch 5 taken 7 times.
9 RequireMeshFile(output.path, Child(path, "path"));
520 7 }
521
522 70 void CheckSyntheticAlbedo(const SyntheticAlbedoSpec& spec, const std::string& path) {
523 const std::vector<std::pair<const std::vector<NoiseLayerConfig>*, std::string>> stacks{
524
1/1
✓ Branch 1 taken 70 times.
70 {&spec.hueNoise, Child(path, "hue_noise")},
525
1/1
✓ Branch 1 taken 70 times.
140 {&spec.saturationNoise, Child(path, "saturation_noise")},
526
4/6
✓ Branch 1 taken 70 times.
✓ Branch 4 taken 70 times.
✓ Branch 6 taken 210 times.
✓ Branch 7 taken 70 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
420 {&spec.valueNoise, Child(path, "value_noise")}};
527
2/2
✓ Branch 7 taken 207 times.
✓ Branch 8 taken 67 times.
274 for (const auto& [stack, at] : stacks) {
528
1/1
✓ Branch 1 taken 204 times.
207 ValidateNoiseStack(*stack, at, true);
529 }
530
3/3
✓ Branch 1 taken 65 times.
✓ Branch 4 taken 67 times.
✓ Branch 7 taken 65 times.
134 At(path, [&] { ValidateSyntheticAlbedo(spec.Params()); });
531
0/4
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
350 }
532
533 13 void CheckRasterAlbedo(const RasterAlbedoSpec& spec, const std::filesystem::path& directory,
534 const std::string& path) {
535
1/1
✓ Branch 2 taken 13 times.
13 const std::string file = Child(path, "path");
536
1/1
✓ Branch 2 taken 13 times.
13 const std::string bands = Child(path, "bands");
537
1/1
✓ Branch 1 taken 12 times.
13 RequireNonEmpty(spec.path, file);
538
5/6
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 11 times.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 11 times.
12 if (spec.bands.size() != 1 && spec.bands.size() != kMaxAlbedoBands) {
539
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(bands, "needs 1 or 3 bands");
540 }
541
1/1
✓ Branch 1 taken 10 times.
11 const GeoTiffInfo info = ReadRasterInfo(spec.path, directory, file);
542
2/2
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 9 times.
19 for (const std::size_t band : spec.bands) {
543
1/1
✓ Branch 1 taken 9 times.
10 CheckBand(band, info, bands);
544 }
545
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
9 if (info.transform.rowRotation != 0.0 || info.transform.colRotation != 0.0) {
546
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(file, "rotated albedo maps are not supported");
547 }
548
8/8
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 5 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 3 times.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 6 times.
18 if ((spec.scale && !std::isfinite(*spec.scale)) ||
549
2/2
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 2 times.
10 (spec.offset && !std::isfinite(*spec.offset))) {
550
3/3
✓ Branch 2 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
6 throw ConfigError(Child(path, "scale"), "the scale and offset must be finite");
551 }
552
2/2
✓ Branch 2 taken 6 times.
✓ Branch 5 taken 5 times.
13 At(Child(path, "detail"), [&] { ValidateAlbedoDetail(spec.options.detail); });
553 26 }
554
555 35 void CheckCraterAlbedo(const CraterAlbedoSpec& craters, std::size_t craterLayers,
556 const std::string& path) {
557
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 34 times.
35 if (craterLayers == 0) {
558
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(path, "crater albedo needs a crater layer");
559 }
560
6/6
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 29 times.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 33 times.
34 if (craters.craterLayer && *craters.craterLayer >= craterLayers) {
561
1/1
✓ Branch 2 taken 1 time.
2 throw ConfigError(Child(path, "crater_layer"),
562
4/4
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
✓ Branch 11 taken 1 time.
3 "there are " + std::to_string(craterLayers) + " crater layers");
563 }
564 66 At(path, [&] { ValidateCraterAlbedo(craters.options); });
565 32 }
566
567 7 void CheckRockAlbedo(const RockAlbedoSpec& rocks, std::size_t boulderLayers,
568 const std::string& path) {
569
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6 times.
7 if (boulderLayers == 0) {
570
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 throw ConfigError(path, "rock albedo needs a top-level boulder layer");
571 }
572
5/6
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5 times.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 5 times.
6 if (rocks.boulderLayer && *rocks.boulderLayer >= boulderLayers) {
573
1/1
✓ Branch 2 taken 1 time.
2 throw ConfigError(Child(path, "boulder_layer"),
574
4/4
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
✓ Branch 11 taken 1 time.
3 "there are " + std::to_string(boulderLayers) + " boulder layers");
575 }
576 10 At(path, [&] { ValidateRockAlbedo(rocks.options); });
577 4 }
578
579 } // namespace
580
581 41 RasterGrid DescribeSource(const SourceSpec& source, const std::filesystem::path& directory,
582 const Surface& surface) {
583
2/2
✓ Branch 1 taken 41 times.
✓ Branch 4 taken 27 times.
82 return DescribeSourceAt(source, directory, "base", surface);
584 }
585
586 83 void ValidateAlbedo(const TerrainConfig& config, const std::filesystem::path& directory) {
587 83 const AlbedoSpec& albedo = *config.albedo;
588 83 std::visit(
589 13 Overloaded{
590
2/2
✓ Branch 1 taken 70 times.
✓ Branch 4 taken 65 times.
210 [&](const SyntheticAlbedoSpec& s) { CheckSyntheticAlbedo(s, "albedo.source"); },
591
2/2
✓ Branch 1 taken 13 times.
✓ Branch 4 taken 5 times.
39 [&](const RasterAlbedoSpec& s) { CheckRasterAlbedo(s, directory, "albedo.source"); },
592 },
593
1/1
✓ Branch 1 taken 70 times.
83 albedo.source);
594 70 const auto craterLayers = static_cast<std::size_t>(
595
1/1
✓ Branch 3 taken 70 times.
70 std::count_if(config.layers.begin(), config.layers.end(), [](const LayerSpec& layer) {
596 140 return std::holds_alternative<CraterLayerSpec>(layer);
597 70 }));
598 70 const auto boulderLayers = static_cast<std::size_t>(
599
1/1
✓ Branch 3 taken 70 times.
70 std::count_if(config.layers.begin(), config.layers.end(), [](const LayerSpec& layer) {
600 140 return std::holds_alternative<BoulderLayerSpec>(layer);
601 70 }));
602
2/2
✓ Branch 1 taken 55 times.
✓ Branch 2 taken 61 times.
116 for (std::size_t i = 0; i < albedo.layers.size(); ++i) {
603
2/2
✓ Branch 1 taken 55 times.
✓ Branch 4 taken 55 times.
55 const std::string path = Element("albedo.layers", i);
604 55 std::visit(
605
1/1
✓ Branch 1 taken 46 times.
55 Overloaded{
606 35 [&](const CraterAlbedoSpec& craters) {
607 35 CheckCraterAlbedo(craters, craterLayers, path);
608 32 },
609 5 [&](const SplotchAlbedoSpec& splotches) {
610 10 At(path, [&] { ValidateSplotches(splotches.options); });
611 4 },
612 3 [&](const BlastZoneAlbedoSpec& zones) {
613
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
6 for (std::size_t z = 0; z < zones.zones.size(); ++z) {
614
3/3
✓ Branch 2 taken 4 times.
✓ Branch 5 taken 4 times.
✓ Branch 8 taken 3 times.
6 At(Element(Child(path, "zones"), z),
615 4 [&] { ValidateBlastZone(zones.zones[z]); });
616 }
617 2 },
618 7 [&](const RockAlbedoSpec& rocks) { CheckRockAlbedo(rocks, boulderLayers, path); },
619 5 [&](const SlopeAlbedoSpec& slopes) {
620 10 At(path, [&] { ValidateSlopeMaturity(slopes.options); });
621 4 },
622 },
623 55 albedo.layers[i]);
624 55 }
625 61 }
626
627 385 ConfigSummary ValidateConfig(const TerrainConfig& config, const std::filesystem::path& directory) {
628 385 ConfigSummary summary;
629
1/1
✓ Branch 1 taken 379 times.
385 const Surface surface = BuildSurface(config.body, directory);
630
2/2
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 340 times.
379 if (config.base) {
631
1/1
✓ Branch 2 taken 25 times.
39 summary.base = DescribeSource(*config.base, directory, surface);
632 }
633 365 const OutputContext outputs{surface.IsBody(), config.albedo.has_value(),
634 365 config.materials.has_value()};
635 365 std::vector<Extent> extents;
636
2/2
✓ Branch 1 taken 489 times.
✓ Branch 2 taken 329 times.
818 for (std::size_t i = 0; i < config.outputs.size(); ++i) {
637 489 const OutputSpec& output = config.outputs[i];
638
27/27
auto terraingen::ValidateConfig(terraingen::TerrainConfig const&, std::filesystem::__cxx11::path const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::AlbedoOutput>(terraingen::AlbedoOutput const&) const:
✓ Branch 1 taken 45 times.
✓ Branch 4 taken 45 times.
✓ Branch 7 taken 41 times.
auto terraingen::ValidateConfig(terraingen::TerrainConfig const&, std::filesystem::__cxx11::path const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderListOutput>(terraingen::BoulderListOutput const&) const:
✓ Branch 1 taken 19 times.
✓ Branch 4 taken 19 times.
✓ Branch 7 taken 18 times.
auto terraingen::ValidateConfig(terraingen::TerrainConfig const&, std::filesystem::__cxx11::path const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::BoulderModelsOutput>(terraingen::BoulderModelsOutput const&) const:
✓ Branch 1 taken 15 times.
✓ Branch 4 taken 15 times.
✓ Branch 7 taken 13 times.
auto terraingen::ValidateConfig(terraingen::TerrainConfig const&, std::filesystem::__cxx11::path const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::CraterListOutput>(terraingen::CraterListOutput const&) const:
✓ Branch 1 taken 59 times.
✓ Branch 4 taken 59 times.
✓ Branch 7 taken 58 times.
auto terraingen::ValidateConfig(terraingen::TerrainConfig const&, std::filesystem::__cxx11::path const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::HeightmapOutput>(terraingen::HeightmapOutput const&) const:
✓ Branch 1 taken 309 times.
✓ Branch 4 taken 309 times.
✓ Branch 7 taken 306 times.
auto terraingen::ValidateConfig(terraingen::TerrainConfig const&, std::filesystem::__cxx11::path const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::MaterialsOutput>(terraingen::MaterialsOutput const&) const:
✓ Branch 1 taken 7 times.
✓ Branch 4 taken 7 times.
✓ Branch 7 taken 6 times.
auto terraingen::ValidateConfig(terraingen::TerrainConfig const&, std::filesystem::__cxx11::path const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::MeshOutput>(terraingen::MeshOutput const&) const:
✓ Branch 1 taken 13 times.
✓ Branch 4 taken 13 times.
✓ Branch 7 taken 9 times.
auto terraingen::ValidateConfig(terraingen::TerrainConfig const&, std::filesystem::__cxx11::path const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::PhotometryOutput>(terraingen::PhotometryOutput const&) const:
✓ Branch 1 taken 14 times.
✓ Branch 4 taken 14 times.
✓ Branch 7 taken 13 times.
auto terraingen::ValidateConfig(terraingen::TerrainConfig const&, std::filesystem::__cxx11::path const&)::{lambda(auto:1 const&)#1}::operator()<terraingen::TerrainMeshOutput>(terraingen::TerrainMeshOutput const&) const:
✓ Branch 1 taken 8 times.
✓ Branch 4 taken 8 times.
✓ Branch 7 taken 7 times.
1485 std::visit([&](const auto& o) { ValidateOutput(o, Element("outputs", i), outputs); },
639 output);
640 const std::optional<RasterGrid> grid =
641
3/3
✓ Branch 1 taken 471 times.
✓ Branch 4 taken 471 times.
✓ Branch 7 taken 453 times.
978 CheckOutputGrid(output, Element("outputs", i), summary.base, surface);
642
1/1
✓ Branch 1 taken 453 times.
453 summary.outputGrids.push_back(grid);
643
2/2
✓ Branch 1 taken 444 times.
✓ Branch 2 taken 9 times.
453 if (grid) {
644
1/1
✓ Branch 1 taken 444 times.
444 const double spacing = std::visit(
645 444 [](const auto& o) {
646 if constexpr (config::FeatureOutput<std::decay_t<decltype(o)>>) {
647 84 return o.spacing.value_or(0.0);
648 }
649 360 return 0.0;
650 },
651 output);
652 444 extents.push_back(
653
3/3
✓ Branch 2 taken 444 times.
✓ Branch 6 taken 444 times.
✓ Branch 9 taken 444 times.
444 {grid->SurfaceArea(surface), std::fmax(spacing, grid->Spacing(surface))});
654 }
655 }
656 329 LayerChecks layers{extents, directory, surface};
657
2/2
✓ Branch 1 taken 662 times.
✓ Branch 2 taken 274 times.
936 for (std::size_t i = 0; i < config.layers.size(); ++i) {
658
3/3
✓ Branch 1 taken 662 times.
✓ Branch 4 taken 662 times.
✓ Branch 8 taken 607 times.
1379 ValidateLayer(config.layers[i], Element("layers", i), layers);
659 }
660
2/2
✓ Branch 1 taken 173 times.
✓ Branch 2 taken 101 times.
274 if (!layers.expectedCraters.empty()) {
661 173 summary.expectedCraters =
662
1/1
✓ Branch 3 taken 173 times.
346 *std::max_element(layers.expectedCraters.begin(), layers.expectedCraters.end());
663 }
664
2/2
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 214 times.
274 if (config.albedo) {
665
1/1
✓ Branch 1 taken 39 times.
60 ValidateAlbedo(config, directory);
666 }
667
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 237 times.
253 if (config.materials) {
668
1/1
✓ Branch 1 taken 8 times.
16 ValidateMaterials(config);
669 }
670 490 return summary;
671 723 }
672
673 } // namespace terraingen
674