TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
56 / 56
100.0%
Branches:
63 / 63
100.0%
Functions:
7 / 7
100.0%

distribution/piecewise_uniform.cpp
Line Branch Exec Source
1 #include "terraingen/distribution/piecewise_uniform.h"
2
3 #include "terraingen/util/checks.h"
4
5 #include <algorithm>
6 #include <cmath>
7 #include <cstddef>
8 #include <stdexcept>
9 #include <utility>
10
11 namespace terraingen {
12
13 namespace {
14
15 /// The diameters the bands cover, up to the largest `max`, after checking the bands.
16 28 DiameterRange BandRange(const std::vector<UniformBand>& bands) {
17
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 27 times.
28 if (bands.empty()) {
18
1/1
✓ Branch 2 taken 1 time.
1 throw std::invalid_argument("A piecewise uniform distribution needs at least one band");
19 }
20 27 double top = 0.0;
21
2/2
✓ Branch 4 taken 46 times.
✓ Branch 5 taken 22 times.
68 for (const UniformBand& band : bands) {
22
8/8
✓ Branch 1 taken 45 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 44 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 43 times.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 43 times.
46 if (!IsPositive(band.min) || !IsPositive(band.max) || !(band.max > band.min)) {
23 throw std::invalid_argument(
24
1/1
✓ Branch 2 taken 3 times.
3 "Band diameters must be positive and finite, with max above min");
25 }
26
6/6
✓ Branch 1 taken 42 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 41 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 41 times.
43 if (!std::isfinite(band.density) || band.density < 0.0) {
27
1/1
✓ Branch 2 taken 2 times.
2 throw std::invalid_argument("Band densities must be finite and >= 0");
28 }
29 41 top = std::max(top, band.max);
30 }
31 22 return DiameterRange{0.0, top};
32 }
33
34 /// `bands` with densities scaled to sum to `total` when it is positive.
35 22 std::vector<UniformBand> ScaleBands(std::vector<UniformBand> bands, double total) {
36
6/6
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 20 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 20 times.
22 if (!std::isfinite(total) || total < 0.0) {
37
1/1
✓ Branch 2 taken 2 times.
2 throw std::invalid_argument("A band total must be finite and >= 0");
38 }
39 20 double sum = 0.0;
40
2/2
✓ Branch 4 taken 39 times.
✓ Branch 5 taken 20 times.
59 for (const UniformBand& band : bands) {
41 39 sum += band.density;
42 }
43
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 19 times.
20 if (!(sum > 0.0)) {
44
1/1
✓ Branch 2 taken 1 time.
1 throw std::invalid_argument("A piecewise uniform distribution needs a positive density");
45 }
46
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 12 times.
19 if (total > 0.0) {
47
2/2
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 7 times.
21 for (UniformBand& band : bands) {
48 14 band.density *= total / sum;
49 }
50 }
51 19 return bands;
52 }
53
54 } // namespace
55
56 28 PiecewiseUniformSfd::PiecewiseUniformSfd(std::vector<UniformBand> bands, SfdKind kind, double total)
57 : SizeFrequencyDistribution(kind, BandRange(bands)),
58
1/1
✓ Branch 5 taken 19 times.
31 bands_(ScaleBands(std::move(bands), total)) {
59 19 SetAllowExtrapolation(true);
60
2/2
✓ Branch 5 taken 38 times.
✓ Branch 6 taken 19 times.
57 for (const UniformBand& band : bands_) {
61
1/1
✓ Branch 1 taken 38 times.
38 edges_.push_back(band.min);
62
1/1
✓ Branch 1 taken 38 times.
38 edges_.push_back(band.max);
63 }
64
1/1
✓ Branch 3 taken 19 times.
19 std::sort(edges_.begin(), edges_.end());
65
2/2
✓ Branch 5 taken 19 times.
✓ Branch 9 taken 19 times.
19 edges_.erase(std::unique(edges_.begin(), edges_.end()), edges_.end());
66
2/2
✓ Branch 4 taken 69 times.
✓ Branch 5 taken 19 times.
88 for (const double edge : edges_) {
67
2/2
✓ Branch 1 taken 69 times.
✓ Branch 4 taken 69 times.
69 edgeCumulative_.push_back(CumulativeAt(edge));
68 }
69 22 }
70
71 131555 double PiecewiseUniformSfd::CumulativeAt(double diameter) const {
72 131555 double total = 0.0;
73
2/2
✓ Branch 4 taken 274293 times.
✓ Branch 5 taken 131555 times.
405848 for (const UniformBand& band : bands_) {
74
1/1
✓ Branch 1 taken 274293 times.
274293 total += band.density * std::clamp((band.max - diameter) / (band.max - band.min), 0.0, 1.0);
75 }
76 131555 return total;
77 }
78
79 5 double PiecewiseUniformSfd::IncrementalAt(double diameter) const {
80 5 double total = 0.0;
81
2/2
✓ Branch 5 taken 13 times.
✓ Branch 6 taken 5 times.
18 for (const UniformBand& band : bands_) {
82
4/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 3 times.
13 if (diameter >= band.min && diameter < band.max) {
83 5 total += band.density / (band.max - band.min);
84 }
85 }
86 5 return total;
87 }
88
89 43781 double PiecewiseUniformSfd::InvertCumulative(double target, double /*dMin*/,
90 double /*dMax*/) const {
91
1/1
✓ Branch 3 taken 43781 times.
43781 const auto below = std::partition_point(edgeCumulative_.begin(), edgeCumulative_.end(),
92 101435 [target](double value) { return value > target; });
93 43781 const auto i = static_cast<std::size_t>(below - edgeCumulative_.begin());
94
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 43779 times.
43781 if (i == 0) {
95 2 return edges_.front();
96 }
97
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 43778 times.
43779 if (i == edges_.size()) {
98 1 return edges_.back();
99 }
100 43778 const double high = edgeCumulative_[i - 1];
101 43778 const double low = edgeCumulative_[i];
102 43778 return edges_[i - 1] + (high - target) / (high - low) * (edges_[i] - edges_[i - 1]);
103 }
104
105 } // namespace terraingen
106