TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
79 / 79
100.0%
Branches:
71 / 80
88.8%
Functions:
9 / 9
100.0%

albedo/blast_zones.cpp
Line Branch Exec Source
1 #include "terraingen/albedo/blast_zones.h"
2
3 #include "terraingen/layers/fade.h"
4 #include "terraingen/noise/fractal.h"
5 #include "terraingen/util/checks.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <numbers>
10 #include <stdexcept>
11 #include <utility>
12
13 namespace terraingen {
14
15 namespace {
16
17 constexpr double kFullRadius = 0.3;
18 constexpr double kLobeRing = 180.0 / (std::numbers::pi * 45.0);
19
20 /// The zone's radial profile at `rho` of its radius: 1 to `kFullRadius`, fading to 0 at 1.
21 200532 double Profile(double rho) {
22
2/2
✓ Branch 1 taken 200532 times.
✓ Branch 4 taken 200532 times.
200532 return 1.0 - Smoothstep(std::clamp((rho - kFullRadius) / (1.0 - kFullRadius), 0.0, 1.0));
23 }
24
25 /// The semi-axes (long, short) in metres of `zone`.
26 1927697 std::pair<double, double> SemiAxes(const BlastZone& zone) {
27 1927697 const double minor = std::sqrt(zone.area / (std::numbers::pi * zone.elongation));
28 1927697 return {zone.elongation * minor, minor};
29 }
30
31 } // namespace
32
33 30 void ValidateBlastZone(const BlastZone& zone) {
34
6/8
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 28 times.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 28 times.
30 if (!(std::isfinite(zone.x) && std::isfinite(zone.y) && std::isfinite(zone.headingDegrees))) {
35
1/1
✓ Branch 2 taken 2 times.
2 throw std::invalid_argument("A blast zone needs a finite position and heading");
36 }
37
1/1
✓ Branch 2 taken 24 times.
28 RequirePositive(zone.area, "A blast zone's area");
38
7/8
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 22 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 20 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 20 times.
44 if (!(zone.elongation >= 1.0 && std::isfinite(zone.elongation) && zone.haloReach >= 1.0 &&
39
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
20 std::isfinite(zone.haloReach))) {
40
1/1
✓ Branch 2 taken 4 times.
4 throw std::invalid_argument("A blast zone's elongation and halo reach must be at least 1");
41 }
42
2/2
✓ Branch 2 taken 56 times.
✓ Branch 3 taken 16 times.
72 for (const double change : {zone.change, zone.haloChange, zone.coreChange}) {
43
6/6
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 52 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 52 times.
56 if (!(change > -1.0 && std::isfinite(change))) {
44
1/1
✓ Branch 2 taken 4 times.
4 throw std::invalid_argument("Blast zone changes must be finite and above -1");
45 }
46 }
47
1/1
✓ Branch 2 taken 14 times.
16 RequireNonNegative(zone.coreRadius, "A blast zone's core radius");
48
3/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 12 times.
14 if (!(zone.raggedness >= 0.0 && zone.raggedness < 1.0)) {
49
1/1
✓ Branch 2 taken 2 times.
2 throw std::invalid_argument("A blast zone's raggedness must be in [0, 1)");
50 }
51 12 }
52
53 451218 double BlastZoneMeanShare() {
54 5 static const double share = [] {
55 5 constexpr int kSteps = 1 << 14;
56 5 double sum = 0.0;
57
2/2
✓ Branch 0 taken 81920 times.
✓ Branch 1 taken 5 times.
81925 for (int i = 0; i < kSteps; ++i) {
58 81920 const double rho = (i + 0.5) / kSteps;
59 81920 sum += Profile(rho) * 2.0 * rho / kSteps;
60 }
61 5 return sum;
62
4/7
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 451213 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
451218 }();
63 451218 return share;
64 }
65
66 15 BlastZoneAlbedo::BlastZoneAlbedo(std::vector<BlastZone> zones, Surface surface)
67 15 : zones_(std::move(zones)), surface_(std::move(surface)) {
68
2/2
✓ Branch 5 taken 15 times.
✓ Branch 6 taken 7 times.
22 for (const BlastZone& zone : zones_) {
69
1/1
✓ Branch 1 taken 7 times.
15 ValidateBlastZone(zone);
70 }
71 31 }
72
73 1476481 double BlastZoneAlbedo::Reach(const BlastZone& zone) {
74 1476481 return std::fmax(SemiAxes(zone).first * zone.haloReach, zone.coreRadius);
75 }
76
77 1476479 double BlastZoneAlbedo::Change(const BlastZone& zone, double x, double y) const {
78
1/1
✓ Branch 1 taken 1476479 times.
1476479 const Vec2d offset = surface_.Offset({zone.x, zone.y}, {x, y});
79 1476479 const double distance = std::hypot(offset.x, offset.y);
80
2/2
✓ Branch 1 taken 1025263 times.
✓ Branch 2 taken 451216 times.
1476479 if (distance >= Reach(zone)) {
81 1025263 return 0.0;
82 }
83 451216 const auto [major, minor] = SemiAxes(zone);
84 451216 const double heading = zone.headingDegrees * std::numbers::pi / 180.0;
85 451216 const double u = (offset.x * std::cos(heading) + offset.y * std::sin(heading)) / major;
86 451216 const double v = (offset.y * std::cos(heading) - offset.x * std::sin(heading)) / minor;
87 451216 double lobe = 1.0;
88
2/2
✓ Branch 0 taken 422429 times.
✓ Branch 1 taken 28787 times.
451216 if (zone.raggedness > 0.0) {
89 422429 FractalParams params;
90 422429 params.seed = zone.seed ^ 0x424c5354u;
91 422429 params.wavelength = 1.0f;
92 422429 params.amplitude = 1.0f;
93 422429 params.octaves = 3;
94 422429 const double angle = std::atan2(v, u);
95 const double n = static_cast<double>(
96
1/1
✓ Branch 1 taken 422429 times.
422429 Fractal3(kLobeRing * std::cos(angle), kLobeRing * std::sin(angle), 0.0, params));
97
1/1
✓ Branch 1 taken 422429 times.
422429 lobe = 1.0 - zone.raggedness * std::clamp(0.5 - n, 0.0, 1.0);
98 }
99 451216 const double rho = std::hypot(u, v) / lobe;
100
1/1
✓ Branch 1 taken 451216 times.
451216 const double share = BlastZoneMeanShare();
101 451216 const double peak = (zone.change - zone.haloChange * (1.0 - share)) / share;
102
3/3
✓ Branch 0 taken 118612 times.
✓ Branch 1 taken 332604 times.
✓ Branch 3 taken 118612 times.
451216 const double inner = rho < 1.0 ? Profile(rho) : 0.0;
103 const double halo =
104
2/2
✓ Branch 0 taken 118612 times.
✓ Branch 1 taken 332604 times.
783820 rho <= 1.0 ? 1.0
105 332604 : zone.haloReach > 1.0
106
4/4
✓ Branch 0 taken 332449 times.
✓ Branch 1 taken 155 times.
✓ Branch 3 taken 332449 times.
✓ Branch 6 taken 332449 times.
332604 ? 1.0 - Smoothstep(std::clamp((rho - 1.0) / (zone.haloReach - 1.0), 0.0, 1.0))
107 451216 : 0.0;
108 451216 double change = peak * inner + (1.0 - inner) * zone.haloChange * halo;
109
2/2
✓ Branch 0 taken 422433 times.
✓ Branch 1 taken 28783 times.
451216 if (zone.coreRadius > 0.0) {
110 const double core =
111
2/2
✓ Branch 1 taken 422433 times.
✓ Branch 4 taken 422433 times.
422433 1.0 - Smoothstep(std::clamp((distance / zone.coreRadius - 0.7) / 0.3, 0.0, 1.0));
112 422433 change = zone.coreChange * core + (1.0 - core) * change;
113 }
114 451216 return change;
115 }
116
117 1447329 void BlastZoneAlbedo::ApplyAt(AlbedoSample& value, std::size_t bands, double x, double y,
118 const AlbedoContext& context) const {
119 1447329 ValidateAlbedoContext(context);
120 1447329 double change = 0.0;
121
2/2
✓ Branch 4 taken 1447329 times.
✓ Branch 5 taken 1447329 times.
2894658 for (const BlastZone& zone : zones_) {
122
1/1
✓ Branch 1 taken 1447329 times.
1447329 change += Change(zone, x, y);
123 }
124
2/2
✓ Branch 0 taken 1123757 times.
✓ Branch 1 taken 323572 times.
1447329 if (change == 0.0) {
125 1123757 return;
126 }
127 323572 const double factor = std::fmax(0.0, 1.0 + change);
128
2/2
✓ Branch 0 taken 323572 times.
✓ Branch 1 taken 323572 times.
647144 for (std::size_t band = 0; band < bands; ++band) {
129
1/2
✓ Branch 2 taken 323572 times.
✗ Branch 3 not taken.
323572 if (!std::isnan(value[band])) {
130 323572 value[band] = static_cast<float>(factor * static_cast<double>(value[band]));
131 }
132 }
133 }
134
135 } // namespace terraingen
136