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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 4 | bool operator==(const NoDegradationSpec&) const = default; | |
| 242 | }; | ||
| 243 | /// Degradation "freshness": `FreshnessDegradation`. | ||
| 244 | struct FreshnessDegradationSpec { | ||
| 245 | 30 | double cutoffDepthRatio = 0.04; | |
| 246 | 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 | 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 | 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 | 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 | 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 | 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 | 2 | bool operator==(const Sp8023CraterBouldersSpec&) const = default; | |
| 338 | }; | ||
| 339 | /// Boulder sizes "synthterrain": `SynthterrainBoulderDistribution(model)`. | ||
| 340 | struct SynthterrainBouldersSpec { | ||
| 341 | 2 | SynthterrainBoulders model = SynthterrainBoulders::InterCrater; | |
| 342 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | ✗ | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 46 | bool operator==(const TerrainConfig&) const = default; | |
| 801 | }; | ||
| 802 | |||
| 803 | } // namespace terraingen | ||
| 804 |