TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
106 / 106
100.0%
Branches:
72 / 80
90.0%
Functions:
13 / 13
100.0%

albedo/slope_albedo.cpp
Line Branch Exec Source
1 #include "terraingen/albedo/slope_albedo.h"
2
3 #include "terraingen/util/parallel.h"
4
5 #include <cmath>
6 #include <limits>
7 #include <numbers>
8 #include <stdexcept>
9 #include <utility>
10
11 namespace terraingen {
12
13 struct SlopeAlbedo::Lattice {
14 double i0 = 0.0; ///< Column of the first node, in steps from the origin.
15 double j0 = 0.0; ///< Row of the first node.
16 std::size_t columns = 0;
17 std::size_t rows = 0;
18 std::vector<double> slopes; ///< Degrees, row-major; 0 on the outer ring.
19
20 648272 double At(std::size_t i, std::size_t j) const {
21 648272 return slopes[j * columns + i];
22 }
23 };
24
25 19 void ValidateSlopeMaturity(const SlopeMaturityOptions& options) {
26 19 const SlopeMaturityOptions& o = options;
27
3/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 18 times.
19 if (!(o.onsetDegrees >= 0.0 && o.onsetDegrees < 90.0)) {
28
1/1
✓ Branch 2 taken 1 time.
1 throw std::invalid_argument("Slope maturity onset must be in [0, 90) degrees");
29 }
30
5/6
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 17 times.
18 if (!(o.maturityPerDegree >= 0.0 && std::isfinite(o.maturityPerDegree))) {
31
1/1
✓ Branch 2 taken 1 time.
1 throw std::invalid_argument("Slope maturity per degree must be finite and not negative");
32 }
33
5/6
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 15 times.
33 if (!(o.backgroundMaturity > 0.0 && std::isfinite(o.maxMaturity) &&
34
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 15 times.
16 o.maxMaturity >= o.backgroundMaturity)) {
35 throw std::invalid_argument(
36
1/1
✓ Branch 2 taken 2 times.
2 "Slope background maturity must be positive and the cap finite and not below it");
37 }
38
5/6
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 12 times.
15 if (!(o.baseline > 0.0 && std::isfinite(o.baseline))) {
39
1/1
✓ Branch 2 taken 3 times.
3 throw std::invalid_argument("Slope baseline must be positive and finite");
40 }
41
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 11 times.
12 if (!std::isfinite(o.originReflectance)) {
42
1/1
✓ Branch 2 taken 1 time.
1 throw std::invalid_argument("Slope origin reflectance must be finite");
43 }
44 11 }
45
46 7 SlopeAlbedo::SlopeAlbedo(SlopeMaturityOptions options, Surface surface)
47 7 : options_(options), surface_(surface) {
48
1/1
✓ Branch 1 taken 7 times.
7 ValidateSlopeMaturity(options_);
49
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 step_ = surface_.IsBody() ? options_.baseline / (surface_.Radius() * std::numbers::pi / 180.0)
50 : options_.baseline;
51 7 }
52
53 162070 double SlopeAlbedo::MaturityAt(double slopeDegrees) const {
54 162070 const double excess =
55 162070 options_.maturityPerDegree * std::fmax(0.0, slopeDegrees - options_.onsetDegrees);
56 162070 return std::fmin(options_.backgroundMaturity + excess, options_.maxMaturity);
57 }
58
59 69 SlopeAlbedo::Lattice SlopeAlbedo::ReadLattice(const Bounds2d& region, const Ground& ground) const {
60 69 Lattice lattice;
61 69 lattice.i0 = std::floor(region.minX / step_) - 1.0;
62 69 lattice.j0 = std::floor(region.minY / step_) - 1.0;
63 69 lattice.columns =
64 69 static_cast<std::size_t>(std::floor(region.maxX / step_) + 2.0 - lattice.i0) + 1;
65 69 lattice.rows = static_cast<std::size_t>(std::floor(region.maxY / step_) + 2.0 - lattice.j0) + 1;
66 16643 const auto node = [&](std::size_t i, std::size_t j) {
67 16643 return Vec2d{(lattice.i0 + static_cast<double>(i)) * step_,
68 16643 (lattice.j0 + static_cast<double>(j)) * step_};
69 69 };
70 69 std::vector<Vec2d> points;
71
1/1
✓ Branch 1 taken 69 times.
69 points.reserve(lattice.columns * lattice.rows);
72
2/2
✓ Branch 0 taken 335 times.
✓ Branch 1 taken 69 times.
404 for (std::size_t j = 0; j < lattice.rows; ++j) {
73
2/2
✓ Branch 0 taken 4183 times.
✓ Branch 1 taken 335 times.
4518 for (std::size_t i = 0; i < lattice.columns; ++i) {
74
1/1
✓ Branch 2 taken 4183 times.
4183 points.push_back(node(i, j));
75 }
76 }
77
1/1
✓ Branch 2 taken 69 times.
69 std::vector<float> heights(points.size());
78
1/1
✓ Branch 3 taken 69 times.
69 ground.HeightsAt(points, heights, options_.baseline);
79
1/1
✓ Branch 2 taken 69 times.
69 lattice.slopes.assign(points.size(), 0.0);
80
2/2
✓ Branch 0 taken 197 times.
✓ Branch 1 taken 69 times.
266 for (std::size_t j = 1; j + 1 < lattice.rows; ++j) {
81
2/2
✓ Branch 0 taken 3115 times.
✓ Branch 1 taken 197 times.
3312 for (std::size_t i = 1; i + 1 < lattice.columns; ++i) {
82 12460 const auto z = [&](std::size_t a, std::size_t b) {
83 12460 return static_cast<double>(heights[b * lattice.columns + a]);
84 3115 };
85 const double dx =
86
1/1
✓ Branch 5 taken 3115 times.
3115 (z(i + 1, j) - z(i - 1, j)) / surface_.Distance(node(i - 1, j), node(i + 1, j));
87 const double dy =
88
1/1
✓ Branch 5 taken 3115 times.
3115 (z(i, j + 1) - z(i, j - 1)) / surface_.Distance(node(i, j - 1), node(i, j + 1));
89 3115 const double slope = std::atan(std::hypot(dx, dy)) * 180.0 / std::numbers::pi;
90
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3115 times.
3115 lattice.slopes[j * lattice.columns + i] = std::isnan(slope) ? 0.0 : slope;
91 }
92 }
93 138 return lattice;
94 69 }
95
96 162068 double SlopeAlbedo::Interpolate(const Lattice& lattice, double x, double y) const {
97 162068 const double u = x / step_;
98 162068 const double v = y / step_;
99 162068 const double fu = std::floor(u);
100 162068 const double fv = std::floor(v);
101 162068 const auto i = static_cast<std::size_t>(fu - lattice.i0);
102 162068 const auto j = static_cast<std::size_t>(fv - lattice.j0);
103 162068 const double tu = u - fu;
104 162068 const double tv = v - fv;
105 162068 return (1.0 - tv) * ((1.0 - tu) * lattice.At(i, j) + tu * lattice.At(i + 1, j)) +
106 162068 tv * ((1.0 - tu) * lattice.At(i, j + 1) + tu * lattice.At(i + 1, j + 1));
107 }
108
109 67 double SlopeAlbedo::SlopeAt(double x, double y, const Ground& ground) const {
110
1/1
✓ Branch 1 taken 67 times.
67 return Interpolate(ReadLattice({x, y, x, y}, ground), x, y);
111 }
112
113 162064 void SlopeAlbedo::Mature(AlbedoSample& value, std::size_t bands, double slopeDegrees) const {
114 162064 const double maturity = MaturityAt(slopeDegrees);
115
2/2
✓ Branch 0 taken 160238 times.
✓ Branch 1 taken 1826 times.
162064 if (maturity == options_.backgroundMaturity) {
116 160238 return;
117 }
118
2/2
✓ Branch 0 taken 1826 times.
✓ Branch 1 taken 1826 times.
3652 for (std::size_t band = 0; band < bands; ++band) {
119
1/2
✓ Branch 2 taken 1826 times.
✗ Branch 3 not taken.
1826 if (!std::isnan(value[band])) {
120 1826 value[band] = static_cast<float>(ReflectanceAtMaturity(
121 1826 value[band], options_.backgroundMaturity, maturity, options_.originReflectance));
122 }
123 }
124 }
125
126 64 void SlopeAlbedo::ApplyAt(AlbedoSample& value, std::size_t bands, double x, double y,
127 const AlbedoContext& context) const {
128 64 ValidateAlbedoContext(context);
129
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 1 time.
64 if (context.ground != nullptr) {
130 63 Mature(value, bands, SlopeAt(x, y, *context.ground));
131 }
132 64 }
133
134 3 void SlopeAlbedo::Apply(std::vector<Raster<float>>& bands, const AlbedoContext& context) const {
135
1/1
✓ Branch 1 taken 3 times.
3 ValidateAlbedoContext(context);
136
5/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 2 times.
3 if (context.ground == nullptr || bands.empty()) {
137 1 return;
138 }
139
1/1
✓ Branch 2 taken 2 times.
2 const RasterGrid grid = RasterGrid::FromRaster(bands[0]);
140 2 Bounds2d centres{
141 std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity(),
142 -std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity()};
143 10 for (const auto& [col, row] : {std::pair<std::size_t, std::size_t>{0, 0},
144 4 {grid.width - 1, 0},
145 4 {0, grid.height - 1},
146
2/2
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 2 times.
12 {grid.width - 1, grid.height - 1}}) {
147 8 const Vec2d c = bands[0].CellCenter(col, row);
148 8 centres = {std::fmin(centres.minX, c.x), std::fmin(centres.minY, c.y),
149 8 std::fmax(centres.maxX, c.x), std::fmax(centres.maxY, c.y)};
150 }
151
1/1
✓ Branch 1 taken 2 times.
2 const Lattice lattice = ReadLattice(centres, *context.ground);
152
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 ParallelFor(grid.height, context.threads, [&](std::size_t row) {
153
2/2
✓ Branch 0 taken 162001 times.
✓ Branch 1 taken 431 times.
162432 for (std::size_t col = 0; col < grid.width; ++col) {
154 162001 const Vec2d centre = bands[0].CellCenter(col, row);
155 162001 AlbedoSample value{};
156
2/2
✓ Branch 1 taken 162001 times.
✓ Branch 2 taken 162001 times.
324002 for (std::size_t band = 0; band < bands.size(); ++band) {
157 162001 value[band] = bands[band](col, row);
158 }
159
1/1
✓ Branch 3 taken 162001 times.
162001 Mature(value, bands.size(), Interpolate(lattice, centre.x, centre.y));
160
2/2
✓ Branch 1 taken 162001 times.
✓ Branch 2 taken 162001 times.
324002 for (std::size_t band = 0; band < bands.size(); ++band) {
161 162001 bands[band](col, row) = value[band];
162 }
163 }
164 431 });
165 2 }
166
167 } // namespace terraingen
168