TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
80 / 80
100.0%
Branches:
68 / 73
93.2%
Functions:
10 / 10
100.0%

albedo/rock_albedo.cpp
Line Branch Exec Source
1 #include "terraingen/albedo/rock_albedo.h"
2
3 #include "terraingen/random/philox.h"
4 #include "terraingen/random/value_distribution.h"
5 #include "terraingen/util/parallel.h"
6
7 #include <algorithm>
8 #include <bit>
9 #include <cmath>
10 #include <stdexcept>
11 #include <utility>
12
13 namespace terraingen {
14
15 namespace {
16
17 constexpr std::uint32_t kRockStream = 0x524F434Bu;
18
19 /// Multiplies every band of `value` by 1 + `share` (`ratio` - 1); NaN stays NaN.
20 169188 void Brighten(AlbedoSample& value, std::size_t bands, double share, double ratio) {
21
2/2
✓ Branch 0 taken 3906 times.
✓ Branch 1 taken 165282 times.
169188 if (share == 0.0) {
22 3906 return;
23 }
24 165282 const double factor = 1.0 + share * (ratio - 1.0);
25
2/2
✓ Branch 0 taken 165410 times.
✓ Branch 1 taken 165282 times.
330692 for (std::size_t band = 0; band < bands; ++band) {
26
1/2
✓ Branch 2 taken 165410 times.
✗ Branch 3 not taken.
165410 if (!std::isnan(value[band])) {
27 165410 value[band] = static_cast<float>(factor * static_cast<double>(value[band]));
28 }
29 }
30 }
31
32 } // namespace
33
34 15 void ValidateRockAlbedo(const RockAlbedoOptions& options) {
35
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 (!(options.reflectanceRatio > 0.0 && std::isfinite(options.reflectanceRatio))) {
36
1/1
✓ Branch 2 taken 3 times.
3 throw std::invalid_argument("Rock reflectance ratio must be positive and finite");
37 }
38 12 }
39
40 369258 double RockShare(double resolved, const UnresolvedCover& unresolved, double normal) {
41 369258 double drawn = 0.0;
42
2/2
✓ Branch 0 taken 365159 times.
✓ Branch 1 taken 4099 times.
369258 if (unresolved.mean > 0.0) {
43 365159 const double s2 = std::log1p(unresolved.variance / (unresolved.mean * unresolved.mean));
44 drawn =
45 365159 std::fmin(1.0, std::exp(std::log(unresolved.mean) - 0.5 * s2 + std::sqrt(s2) * normal));
46 }
47
1/1
✓ Branch 1 taken 369258 times.
369258 const double kept = std::clamp(resolved, 0.0, 1.0);
48 369258 return kept + (1.0 - kept) * drawn;
49 }
50
51 8 RockAlbedo::RockAlbedo(RockAlbedoOptions options, std::vector<RockSource> sources)
52 8 : options_(options), sources_(std::move(sources)) {
53
1/1
✓ Branch 1 taken 8 times.
8 ValidateRockAlbedo(options_);
54
2/2
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 6 times.
14 for (const RockSource& source : sources_) {
55
6/6
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 6 times.
8 if (!source.layer || !source.ground) {
56 throw std::invalid_argument(
57
1/1
✓ Branch 2 taken 2 times.
2 "A rock albedo source needs a boulder layer and its ground");
58 }
59 }
60 10 }
61
62 165156 double RockAlbedo::Draw(double x, double y, std::size_t source) const {
63 165156 const auto xs = std::bit_cast<std::uint64_t>(x);
64 165156 const auto ys = std::bit_cast<std::uint64_t>(y);
65 const U32x4 bits =
66 165156 Philox4x32({static_cast<std::uint32_t>(xs), static_cast<std::uint32_t>(xs >> 32),
67 165156 static_cast<std::uint32_t>(ys), static_cast<std::uint32_t>(ys >> 32)},
68 165156 {options_.seed ^ kRockStream, static_cast<std::uint32_t>(source)});
69 330312 return StandardNormal(std::ldexp(static_cast<double>(bits.x), -32),
70
1/1
✓ Branch 1 taken 165156 times.
330312 std::ldexp(static_cast<double>(bits.y), -32));
71 }
72
73 132 double RockAlbedo::ShareAt(double x, double y, const AlbedoContext& context) const {
74 132 ValidateAlbedoContext(context);
75 132 double bare = 1.0;
76
2/2
✓ Branch 1 taken 132 times.
✓ Branch 2 taken 132 times.
264 for (std::size_t n = 0; n < sources_.size(); ++n) {
77 132 const RockSource& source = sources_[n];
78 const double resolved =
79
1/1
✓ Branch 3 taken 132 times.
132 source.layer->MaterialCoverAt(x, y, context.spacing, source.ground.get());
80 const UnresolvedCover unresolved =
81 132 options_.unresolved
82
3/3
✓ Branch 0 taken 131 times.
✓ Branch 1 taken 1 time.
✓ Branch 5 taken 131 times.
132 ? source.layer->UnresolvedCoverAt(x, y, context.spacing, source.ground.get())
83 1 : UnresolvedCover{};
84
4/4
✓ Branch 0 taken 131 times.
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 131 times.
✓ Branch 6 taken 132 times.
132 bare *= 1.0 - RockShare(resolved, unresolved, options_.unresolved ? Draw(x, y, n) : 0.0);
85 }
86 132 return 1.0 - bare;
87 }
88
89 131 void RockAlbedo::ApplyAt(AlbedoSample& value, std::size_t bands, double x, double y,
90 const AlbedoContext& context) const {
91 131 Brighten(value, bands, ShareAt(x, y, context), options_.reflectanceRatio);
92 131 }
93
94 5 void RockAlbedo::Apply(std::vector<Raster<float>>& bands, const AlbedoContext& context) const {
95
1/1
✓ Branch 1 taken 5 times.
5 ValidateAlbedoContext(context);
96
5/6
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 4 times.
5 if (bands.empty() || sources_.empty()) {
97 1 return;
98 }
99
1/1
✓ Branch 2 taken 4 times.
4 const RasterGrid grid = RasterGrid::FromRaster(bands[0]);
100
1/1
✓ Branch 1 taken 4 times.
4 std::vector<double> bare(grid.width * grid.height, 1.0);
101
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 4 times.
9 for (std::size_t n = 0; n < sources_.size(); ++n) {
102 5 const RockSource& source = sources_[n];
103 5 const Raster<double> resolved = source.layer->MaterialCoverOver(
104
1/1
✓ Branch 3 taken 5 times.
5 grid, context.spacing, source.ground.get(), context.threads);
105 const std::vector<UnresolvedCover> unresolved =
106 9 options_.unresolved ? source.layer->UnresolvedCoverOver(
107 8 grid, context.spacing, source.ground.get(), context.threads)
108
6/8
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 time.
✓ Branch 5 taken 4 times.
✓ Branch 9 taken 1 time.
✓ Branch 11 taken 1 time.
✓ Branch 12 taken 4 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
6 : std::vector<UnresolvedCover>(bare.size());
109
2/2
✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
5 ParallelFor(grid.height, context.threads, [&](std::size_t row) {
110
2/2
✓ Branch 0 taken 169121 times.
✓ Branch 1 taken 545 times.
169666 for (std::size_t col = 0; col < grid.width; ++col) {
111 169121 const std::size_t cell = row * grid.width + col;
112 169121 const Vec2d centre = bands[0].CellCenter(col, row);
113 169121 bare[cell] *=
114
1/1
✓ Branch 3 taken 169121 times.
169121 1.0 - RockShare(resolved(col, row), unresolved[cell],
115
3/3
✓ Branch 0 taken 165025 times.
✓ Branch 1 taken 4096 times.
✓ Branch 3 taken 165025 times.
169121 options_.unresolved ? Draw(centre.x, centre.y, n) : 0.0);
116 }
117 545 });
118 5 }
119
2/2
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
4 ParallelFor(grid.height, context.threads, [&](std::size_t row) {
120
2/2
✓ Branch 0 taken 169057 times.
✓ Branch 1 taken 537 times.
169594 for (std::size_t col = 0; col < grid.width; ++col) {
121 169057 AlbedoSample value{};
122
2/2
✓ Branch 1 taken 169185 times.
✓ Branch 2 taken 169057 times.
338242 for (std::size_t band = 0; band < bands.size(); ++band) {
123 169185 value[band] = bands[band](col, row);
124 }
125 169057 Brighten(value, bands.size(), 1.0 - bare[row * grid.width + col],
126 169057 options_.reflectanceRatio);
127
2/2
✓ Branch 1 taken 169185 times.
✓ Branch 2 taken 169057 times.
338242 for (std::size_t band = 0; band < bands.size(); ++band) {
128 169185 bands[band](col, row) = value[band];
129 }
130 }
131 537 });
132 4 }
133
134 } // namespace terraingen
135