albedo/albedo.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief The albedo source and layer interfaces, albedo compositions and their evaluation. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/layers/ground.h" | ||
| 6 | #include "terraingen/raster/grid.h" | ||
| 7 | #include "terraingen/raster/raster.h" | ||
| 8 | #include "terraingen/util/surface.h" | ||
| 9 | |||
| 10 | #include <array> | ||
| 11 | #include <cstddef> | ||
| 12 | #include <functional> | ||
| 13 | #include <memory> | ||
| 14 | #include <vector> | ||
| 15 | |||
| 16 | namespace terraingen { | ||
| 17 | |||
| 18 | /// The most bands an albedo has: three for red, green and blue. | ||
| 19 | inline constexpr std::size_t kMaxAlbedoBands = 3; | ||
| 20 | |||
| 21 | /// An albedo at one point: the first `Bands()` values are used, the rest are 0. | ||
| 22 | using AlbedoSample = std::array<float, kMaxAlbedoBands>; | ||
| 23 | |||
| 24 | /// How an albedo is evaluated. | ||
| 25 | struct AlbedoContext { | ||
| 26 | /// Sample spacing in metres, positive and finite: detail that cannot be resolved at it is left | ||
| 27 | /// out. | ||
| 28 | double spacing = 1.0; | ||
| 29 | /// Threads a grid may be split over; 0 for every hardware thread. The result does not depend | ||
| 30 | /// on it. | ||
| 31 | unsigned threads = 1; | ||
| 32 | /// The terrain's heights, for layers shaped by the ground; null leaves it flat. It must outlive | ||
| 33 | /// the evaluation. | ||
| 34 | const Ground* ground = nullptr; | ||
| 35 | }; | ||
| 36 | |||
| 37 | /// Checks an evaluation context. | ||
| 38 | /// @throws std::invalid_argument if `context.spacing` is not positive and finite. | ||
| 39 | void ValidateAlbedoContext(const AlbedoContext& context); | ||
| 40 | |||
| 41 | /// Where an albedo starts: a map from orbit, a synthetic colour, or a source of your own. | ||
| 42 | /// Values are reflectances (normal albedo, or reflectance at a wavelength), one per band. | ||
| 43 | /// | ||
| 44 | /// Sources must be deterministic and thread safe, and `Fill` must give every cell the bits | ||
| 45 | /// `ValueAt` gives at its centre, so tiles agree with the whole. | ||
| 46 | class AlbedoSource { | ||
| 47 | public: | ||
| 48 | 111 | virtual ~AlbedoSource() = default; | |
| 49 | |||
| 50 | /// The number of bands: 1 for a single band, or 3 for red, green and blue. | ||
| 51 | virtual std::size_t Bands() const = 0; | ||
| 52 | |||
| 53 | /// The albedo at world point (`x`, `y`); NaN in every band where the source has no data. | ||
| 54 | virtual AlbedoSample ValueAt(double x, double y, const AlbedoContext& context) const = 0; | ||
| 55 | |||
| 56 | /// Sets every cell of each band to `ValueAt` at its centre; the default does exactly | ||
| 57 | /// that, cell by cell, on `context.threads` threads. | ||
| 58 | /// @param bands `Bands()` rasters on one grid. | ||
| 59 | virtual void Fill(std::vector<Raster<float>>& bands, const AlbedoContext& context) const; | ||
| 60 | }; | ||
| 61 | |||
| 62 | /// A change to an albedo: bright ejecta around fresh craters, or a layer of your own. | ||
| 63 | /// | ||
| 64 | /// Layers must be deterministic and thread safe, and `Apply` must give every cell the bits | ||
| 65 | /// `ApplyAt` gives at its centre. | ||
| 66 | class AlbedoLayer { | ||
| 67 | public: | ||
| 68 | 466 | virtual ~AlbedoLayer() = default; | |
| 69 | |||
| 70 | /// Applies the layer in place to `value`, the albedo at world point (`x`, `y`); NaN stays NaN. | ||
| 71 | /// @param bands Bands of `value` in use. | ||
| 72 | virtual void ApplyAt(AlbedoSample& value, std::size_t bands, double x, double y, | ||
| 73 | const AlbedoContext& context) const = 0; | ||
| 74 | |||
| 75 | /// Applies the layer in place to every cell of `bands` (one raster per band on one grid); the | ||
| 76 | /// default calls `ApplyAt` at each cell centre on `context.threads` threads. | ||
| 77 | virtual void Apply(std::vector<Raster<float>>& bands, const AlbedoContext& context) const; | ||
| 78 | }; | ||
| 79 | |||
| 80 | /// An albedo as a composition: values from `source`, then each of `layers` in order. | ||
| 81 | struct Albedo { | ||
| 82 | std::shared_ptr<const AlbedoSource> source; ///< Where values start; required. | ||
| 83 | std::vector<std::shared_ptr<const AlbedoLayer>> layers; ///< Applied in order; none null. | ||
| 84 | /// Sets how a grid's cell size turns into a spacing in metres. | ||
| 85 | Surface surface{}; | ||
| 86 | }; | ||
| 87 | |||
| 88 | /// Evaluates `albedo` over `grid`: one raster per band, with no-data NaN. | ||
| 89 | /// @param context `spacing` in metres, raised to the grid's spacing on the albedo's surface | ||
| 90 | /// (`RasterGrid::Spacing`) when smaller or 0. | ||
| 91 | /// @throws std::invalid_argument if `albedo` has no source, holds a null layer, the source has a | ||
| 92 | /// band count other than 1 or 3, `grid` is empty, or the spacing is negative or not finite. | ||
| 93 | std::vector<Raster<float>> EvaluateAlbedo(const Albedo& albedo, const RasterGrid& grid, | ||
| 94 | AlbedoContext context); | ||
| 95 | |||
| 96 | /// The albedo at world point (`x`, `y`): the cell centred there of `EvaluateAlbedo` over a grid | ||
| 97 | /// of `context.spacing`, to the same bits. | ||
| 98 | /// @throws std::invalid_argument as `EvaluateAlbedo`. | ||
| 99 | AlbedoSample EvaluateAlbedoAt(const Albedo& albedo, double x, double y, | ||
| 100 | const AlbedoContext& context); | ||
| 101 | |||
| 102 | /// Visits the albedo at one cell centre: `visit(value, x, y)` may change `value`. | ||
| 103 | using AlbedoCellVisitor = std::function<void(AlbedoSample& value, double x, double y)>; | ||
| 104 | |||
| 105 | /// Calls `visit` for the albedo at every cell centre, writing back what it leaves in | ||
| 106 | /// `value`: the cell loop shared by `AlbedoSource::Fill` and `AlbedoLayer::Apply`. | ||
| 107 | /// @param bands The bands, one raster per band on one grid. | ||
| 108 | /// @param threads Threads to split the rows over; 0 for every hardware thread. | ||
| 109 | void ForEachAlbedoCell(std::vector<Raster<float>>& bands, unsigned threads, | ||
| 110 | const AlbedoCellVisitor& visit); | ||
| 111 | |||
| 112 | /// `ForEachAlbedoCell` in blocks of rows, for visitors that prepare per block, such as a | ||
| 113 | /// list of the features near it. | ||
| 114 | /// @param threads Threads to split the blocks over; 0 for every hardware thread. | ||
| 115 | /// @param blockRows Rows per block; positive. | ||
| 116 | /// @param visitorFor `visitorFor(rowBegin, rowEnd)` returns the visitor of the cells of rows | ||
| 117 | /// [`rowBegin`, `rowEnd`), called once per block on the thread that visits it. | ||
| 118 | void ForEachAlbedoBlock( | ||
| 119 | std::vector<Raster<float>>& bands, unsigned threads, std::size_t blockRows, | ||
| 120 | const std::function<AlbedoCellVisitor(std::size_t rowBegin, std::size_t rowEnd)>& visitorFor); | ||
| 121 | |||
| 122 | } // namespace terraingen | ||
| 123 |