layers/craters/shaping/datum.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief Crater datums, the surfaces crater interiors are cut to, fitted to the ground. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/layers/craters/profiles/profile.h" | ||
| 6 | #include "terraingen/layers/craters/shaping/apply_options.h" | ||
| 7 | #include "terraingen/layers/craters/shaping/prepared.h" | ||
| 8 | #include "terraingen/layers/ground.h" | ||
| 9 | |||
| 10 | #include <cstddef> | ||
| 11 | #include <cstdint> | ||
| 12 | #include <optional> | ||
| 13 | #include <unordered_map> | ||
| 14 | #include <vector> | ||
| 15 | |||
| 16 | namespace terraingen { | ||
| 17 | |||
| 18 | /// A least-squares plane h = a + b dx + c dy through points added one at a time. | ||
| 19 | class PlaneFit { | ||
| 20 | public: | ||
| 21 | /// Adds the point `dx` east and `dy` north (m) at height `h` (m). | ||
| 22 | void Add(double dx, double dy, double h); | ||
| 23 | |||
| 24 | /// Whether no point was added. | ||
| 25 | 38487 | bool Empty() const { | |
| 26 | 38487 | return n_ == 0.0; | |
| 27 | } | ||
| 28 | |||
| 29 | /// The mean height of the points; call only when not `Empty`. | ||
| 30 | 1044 | double Mean() const { | |
| 31 | 1044 | return sh_ / n_; | |
| 32 | } | ||
| 33 | |||
| 34 | /// The least-squares plane by Cramer's rule, or the flat plane at the mean for fewer than | ||
| 35 | /// three points or a degenerate layout; call only when not `Empty`. | ||
| 36 | Plane Solve() const; | ||
| 37 | |||
| 38 | private: | ||
| 39 | double n_ = 0.0; | ||
| 40 | double sx_ = 0.0; | ||
| 41 | double sy_ = 0.0; | ||
| 42 | double sxx_ = 0.0; | ||
| 43 | double sxy_ = 0.0; | ||
| 44 | double syy_ = 0.0; | ||
| 45 | double sh_ = 0.0; | ||
| 46 | double sxh_ = 0.0; | ||
| 47 | double syh_ = 0.0; | ||
| 48 | }; | ||
| 49 | |||
| 50 | /// Points per side of the lattice crater datums are fitted over. | ||
| 51 | constexpr int kDatumLatticeSide = 7; | ||
| 52 | |||
| 53 | /// The offset (m) from a crater's centre of lattice point `index` (0 to `kDatumLatticeSide` - 1) | ||
| 54 | /// along one axis of a fit footprint of `fitRadius` (m). | ||
| 55 | 4309984 | inline double DatumLatticeOffset(int index, double fitRadius) { | |
| 56 | 4309984 | return fitRadius * ((2.0 * index + 1.0) / kDatumLatticeSide - 1.0); | |
| 57 | } | ||
| 58 | |||
| 59 | /// The datum a crater is cut to, fitted to the ground beneath it; nothing for | ||
| 60 | /// `CraterDatum::None` or where the ground has no data. | ||
| 61 | /// | ||
| 62 | /// `CentreHeight` reads the ground at the centre; `MeanHeight` and `Plane` fit it at the points of | ||
| 63 | /// a 7 x 7 lattice inside the `datumFitRadii` footprint. | ||
| 64 | std::optional<Plane> FitCraterDatum(const Crater& crater, const CraterApplyOptions& options, | ||
| 65 | const HeightSampler& ground); | ||
| 66 | |||
| 67 | /// Prepared craters indexed by position, to sum the relief of the craters before any one of | ||
| 68 | /// them at a point quickly. | ||
| 69 | /// | ||
| 70 | /// Craters are bucketed by size band, each band in square buckets twice its largest reach. | ||
| 71 | class CraterReliefIndex { | ||
| 72 | public: | ||
| 73 | /// Indexes craters; they and `profile` must outlive the index. | ||
| 74 | /// @param craters In the order applied; their datums are not read, so they may be set after the | ||
| 75 | /// index is built. | ||
| 76 | /// @param outlineScale Widens footprints; at least every crater's `CraterOutline::MaxScale`. | ||
| 77 | CraterReliefIndex(const std::vector<PreparedCrater>& craters, const CraterProfile& profile, | ||
| 78 | double outlineScale = 1.0, const Surface& surface = {}); | ||
| 79 | |||
| 80 | /// The relief in metres at surface (x, y) of the craters before crater `k`: `resolution` times | ||
| 81 | /// the profile height of each, shaped by its slope as it is applied, summed in an order | ||
| 82 | /// independent of the other craters. | ||
| 83 | double ReliefBefore(double x, double y, std::size_t k) const; | ||
| 84 | |||
| 85 | /// The indexed craters, in the order applied. | ||
| 86 | 1 | const std::vector<PreparedCrater>& Craters() const { | |
| 87 | 1 | return *craters_; | |
| 88 | } | ||
| 89 | |||
| 90 | private: | ||
| 91 | struct Key { | ||
| 92 | 6652 | int band; | |
| 93 | 6652 | std::int64_t i; | |
| 94 | 3680 | std::int64_t j; | |
| 95 | 2274 | std::int64_t k; | |
| 96 | 6652 | bool operator==(const Key&) const = default; | |
| 97 | }; | ||
| 98 | struct KeyHash { | ||
| 99 | std::size_t operator()(const Key& key) const; | ||
| 100 | }; | ||
| 101 | |||
| 102 | /// Adds to `relief` the relief at (`x`, `y`) of the craters of `bucket` before crater `k`. | ||
| 103 | void AddBucket(const std::vector<std::size_t>& bucket, double x, double y, std::size_t k, | ||
| 104 | double& relief) const; | ||
| 105 | |||
| 106 | const std::vector<PreparedCrater>* craters_; | ||
| 107 | const CraterProfile& profile_; | ||
| 108 | double outlineScale_ = 1.0; | ||
| 109 | Surface surface_; | ||
| 110 | std::vector<int> bands_; | ||
| 111 | std::unordered_map<Key, std::vector<std::size_t>, KeyHash> buckets_; | ||
| 112 | }; | ||
| 113 | |||
| 114 | /// The ground crater `k` of `index` fits its datum to: `ground` plus the relief of the craters | ||
| 115 | /// applied before it, as if added without their own datums; NaN where `ground` has no data. | ||
| 116 | /// `ground` and `index` must outlive the result. | ||
| 117 | HeightSampler DatumGround(const HeightSampler& ground, const CraterReliefIndex& index, | ||
| 118 | std::size_t k); | ||
| 119 | |||
| 120 | /// How far (m) beyond a grid craters with a datum can change it through their datum fit: the fit | ||
| 121 | /// footprint plus the profile's reach for the largest crater; 0 without a datum. Craters for a | ||
| 122 | /// grid or point should be generated this far around it. | ||
| 123 | /// @param maxDiameter The largest crater's diameter, in metres. | ||
| 124 | double DatumMargin(const CraterApplyOptions& options, const CraterProfile& profile, | ||
| 125 | double maxDiameter); | ||
| 126 | |||
| 127 | } // namespace terraingen | ||
| 128 |