layers/craters/generation/population.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief Generating crater populations cell by cell, in size bands. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/distribution/size_frequency.h" | ||
| 6 | #include "terraingen/layers/craters/ages/age_model.h" | ||
| 7 | #include "terraingen/layers/craters/crater.h" | ||
| 8 | #include "terraingen/sources/field.h" | ||
| 9 | #include "terraingen/util/cells.h" | ||
| 10 | #include "terraingen/util/surface.h" | ||
| 11 | |||
| 12 | #include <cstddef> | ||
| 13 | #include <cstdint> | ||
| 14 | #include <functional> | ||
| 15 | #include <limits> | ||
| 16 | #include <memory> | ||
| 17 | #include <string> | ||
| 18 | #include <vector> | ||
| 19 | |||
| 20 | namespace terraingen { | ||
| 21 | |||
| 22 | /// Keeps craters of similar size apart: two conflict when their diameters differ by at most | ||
| 23 | /// `diameterRatio` and their centres are closer than `separation` times the larger diameter. | ||
| 24 | struct CraterSpacing { | ||
| 25 | 114806 | CraterSpacing() = default; | |
| 26 | /// Spacing with `diameterRatio` `ratio` and `separation` `minSeparation`. | ||
| 27 | 22 | CraterSpacing(double ratio, double minSeparation) | |
| 28 | 22 | : diameterRatio(ratio), separation(minSeparation) {} | |
| 29 | |||
| 30 | 39 | double diameterRatio = 1.5; ///< At least 1. | |
| 31 | 39 | double separation = 0.0; ///< In larger diameters; 0 disables spacing. | |
| 32 | /// Craters at least this large (m) take precedence over smaller ones whatever their ages, as a | ||
| 33 | /// crater layer sets for the craters that throw secondaries a total map measures; infinite | ||
| 34 | /// ranks by age first. Not part of a configuration. | ||
| 35 | 39 | double outrankFrom = std::numeric_limits<double>::infinity(); | |
| 36 | |||
| 37 | /// Whether spacing applies: a positive `separation`. | ||
| 38 | 6360355 | bool Enabled() const { | |
| 39 | 6360355 | return separation > 0.0; | |
| 40 | } | ||
| 41 | |||
| 42 | 39 | bool operator==(const CraterSpacing&) const = default; | |
| 43 | }; | ||
| 44 | |||
| 45 | /// A size band of crater generation: diameters [`low`, `high`) metres, from 2^`exponent` to | ||
| 46 | /// 2^(`exponent` + 1) clipped to the requested range. | ||
| 47 | struct CraterBand { | ||
| 48 | int exponent = 0; ///< The band's power of two. | ||
| 49 | double low = 0.0; ///< Smallest diameter (m). | ||
| 50 | double high = 0.0; ///< Largest diameter (m), exclusive. | ||
| 51 | double cellSize = 0.0; ///< Side of the cells the band is drawn in (m). | ||
| 52 | |||
| 53 | bool operator==(const CraterBand&) const = default; | ||
| 54 | }; | ||
| 55 | |||
| 56 | /// The bands `GenerateCraters` uses to cover [`dMin`, `dMax`) metres, smallest first. | ||
| 57 | /// @param cellSizeFactor Cell side over each band's largest whole diameter. | ||
| 58 | std::vector<CraterBand> CraterBands(double dMin, double dMax, double cellSizeFactor); | ||
| 59 | |||
| 60 | /// The craters `GenerateCraters` draws in one cell of one band, in the order drawn, before | ||
| 61 | /// regions, manual craters, spacing and caps apply. | ||
| 62 | CraterList GenerateCellCraters(const SurfaceCells& cells, const CraterBand& band, | ||
| 63 | const CellIndex& cell, const SizeFrequencyDistribution& density, | ||
| 64 | const CraterAgeModel& ages, std::uint32_t seed); | ||
| 65 | |||
| 66 | /// Supplies the craters of one cell of one band as `GenerateCellCraters` would, for example from | ||
| 67 | /// a cache; must be thread-safe. | ||
| 68 | using CellCraterSource = | ||
| 69 | std::function<std::shared_ptr<const CraterList>(const CraterBand& band, const CellIndex& cell)>; | ||
| 70 | |||
| 71 | /// The share of generated craters like `crater` that `GenerateCraters` keeps, in [0, 1]. | ||
| 72 | using CraterKeepShare = std::function<double(const Crater& crater)>; | ||
| 73 | |||
| 74 | /// Which craters `GenerateCraters` draws, and how. | ||
| 75 | struct CraterGenerationOptions { | ||
| 76 | /// Smallest diameter generated, in metres; must be set to a positive value. | ||
| 77 | double dMin = 0.0; | ||
| 78 | /// Largest diameter (m), exclusive; infinity means the top of the density's valid range, which | ||
| 79 | /// must then be finite. | ||
| 80 | double dMax = std::numeric_limits<double>::infinity(); | ||
| 81 | /// Also keep craters centred outside the region within this many radii of it. | ||
| 82 | double marginRadii = 0.0; | ||
| 83 | /// Cell side as a multiple of each band's largest diameter. | ||
| 84 | double cellSizeFactor = 16.0; | ||
| 85 | /// Upper limit on the expected number of craters, to catch requests that would exhaust memory. | ||
| 86 | double maxExpectedCraters = 5e7; | ||
| 87 | /// Removes conflicting craters (`SpacingConflicts`); disabled by default, since it lowers the | ||
| 88 | /// density below `density`. | ||
| 89 | CraterSpacing spacing{}; | ||
| 90 | /// Manual craters to merge in: those within the region and margin follow the generated craters, | ||
| 91 | /// flagged and given IDs as `AppendManualCraters` does; with spacing, they remove conflicting | ||
| 92 | /// generated craters. | ||
| 93 | CraterList manual{}; | ||
| 94 | /// Keep at most this many generated craters, the largest (ties to the smaller ID); 0 keeps | ||
| 95 | /// all. Manual craters are always kept and do not count. A capped region no longer equals its | ||
| 96 | /// tiles joined; `MinDiameterForCount` bounds counts tile-safely. | ||
| 97 | std::size_t maxCraters = 0; | ||
| 98 | /// Skip bands whose craters are all smaller than this (m) and, with spacing, too small to | ||
| 99 | /// remove a crater this large; craters at or above it are unchanged. 0 generates every band. | ||
| 100 | double minResolvedDiameter = 0.0; | ||
| 101 | /// Skip bands whose craters are all larger than this (m), unless spacing or `maxCraters` is on, | ||
| 102 | /// where larger craters change which smaller ones are kept; craters at or below it are | ||
| 103 | /// unchanged. Infinity generates every band. | ||
| 104 | double maxBandDiameter = std::numeric_limits<double>::infinity(); | ||
| 105 | /// Where each cell's craters come from; empty draws them with `GenerateCellCraters`. | ||
| 106 | CellCraterSource cellCraters{}; | ||
| 107 | /// The share of generated craters like each one kept, in [0, 1], from its position and size: | ||
| 108 | /// drawing at the density's highest local level and keeping each crater with its share, by a | ||
| 109 | /// uniform keyed to its ID, makes the density vary over the surface. Must be thread-safe and | ||
| 110 | /// depend only on the crater; unset keeps every crater. | ||
| 111 | CraterKeepShare keepShare{}; | ||
| 112 | /// The diameter (m) from which `manual` craters are a complete catalog at each point: generated | ||
| 113 | /// craters at least this large centred there are dropped, so the catalog stands in for them | ||
| 114 | /// rather than adding to them. Infinite or NaN where there is no catalog; unset keeps every | ||
| 115 | /// generated crater. | ||
| 116 | std::shared_ptr<const SurfaceField> completeness{}; | ||
| 117 | /// What the region and margins are measured on: on a sphere, regions are longitude and | ||
| 118 | /// latitude boxes and margins are metres, and `cells` must be `CubeSphereCells` of its radius. | ||
| 119 | Surface surface{}; | ||
| 120 | /// What the drawn features are called in the log: its category and the noun of its counts. | ||
| 121 | std::string logName = "craters"; | ||
| 122 | }; | ||
| 123 | |||
| 124 | /// The largest diameter generated in metres: `dMax`, or for an infinite `dMax` the top of the | ||
| 125 | /// density's valid range; infinite when both are unbounded. | ||
| 126 | double ResolveDMax(double dMax, const SizeFrequencyDistribution& density); | ||
| 127 | |||
| 128 | /// The smallest diameter in [`dMin`, `dMax`) metres from which `density` expects about `count` | ||
| 129 | /// craters over `area` m^2, found by bisection in log diameter; `dMin` if even that expects fewer. | ||
| 130 | /// | ||
| 131 | /// Raising `dMin` to it bounds the count while keeping the distribution's shape and tile joins. | ||
| 132 | /// @throws std::invalid_argument unless `count` and `area` are positive and finite and | ||
| 133 | /// 0 < `dMin` < `dMax`. | ||
| 134 | /// @throws std::domain_error if the interval leaves the density's valid range. | ||
| 135 | double MinDiameterForCount(const SizeFrequencyDistribution& density, double area, double count, | ||
| 136 | double dMin, double dMax); | ||
| 137 | |||
| 138 | /// Generates the craters of a density whose centres lie in `region` (surface coordinates) or | ||
| 139 | /// within `marginRadii` radii of it. | ||
| 140 | /// | ||
| 141 | /// Craters are drawn per cell of each size band from substreams keyed by (`seed`, band, cell), so | ||
| 142 | /// tiles join exactly, `dMin` and `dMax` do not move craters in whole bands, and the age model | ||
| 143 | /// never moves a crater. Order: band, cell, index. | ||
| 144 | /// @param density A `Density` distribution valid over [`dMin`, `dMax`), or extrapolating. | ||
| 145 | /// @throws std::invalid_argument for invalid options, an empty or non-finite region, a density that | ||
| 146 | /// is not a `Density`, or an expected count above `maxExpectedCraters`. | ||
| 147 | /// @throws std::domain_error if the diameter interval leaves the density's valid range. | ||
| 148 | CraterList GenerateCraters(const SurfaceCells& cells, const Bounds2d& region, | ||
| 149 | const SizeFrequencyDistribution& density, const CraterAgeModel& ages, | ||
| 150 | std::uint32_t seed, const CraterGenerationOptions& options); | ||
| 151 | |||
| 152 | } // namespace terraingen | ||
| 153 |