TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
88 / 89
98.9%
Branches:
90 / 97
92.8%
Functions:
9 / 9
100.0%

layers/strata.cpp
Line Branch Exec Source
1 #include "terraingen/layers/strata.h"
2
3 #include "terraingen/layers/fade.h"
4 #include "terraingen/random/stream.h"
5 #include "terraingen/util/checks.h"
6 #include "terraingen/util/surface.h"
7
8 #include <algorithm>
9 #include <cmath>
10 #include <numbers>
11 #include <stdexcept>
12
13 namespace terraingen {
14
15 namespace {
16
17 constexpr std::uint32_t kStrataStream = 0x53545241u;
18 constexpr std::uint32_t kHardnessStream = 0x48415244u;
19 constexpr const char* kGroundReader = "A strata material cover";
20 /// Width of the fade from bench to cliff in the material map, in bed heights.
21 constexpr double kCliffFade = 0.05;
22
23 } // namespace
24
25 36 void ValidateStrataOptions(const StrataOptions& options) {
26
1/1
✓ Branch 2 taken 35 times.
36 RequirePositive(options.bedThickness, "Strata bedThickness");
27
4/4
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 33 times.
35 if (!(options.thicknessVariation >= 0.0 && options.thicknessVariation < 1.0)) {
28
1/1
✓ Branch 2 taken 2 times.
2 throw std::invalid_argument("Strata thicknessVariation must be in [0, 1)");
29 }
30
3/4
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 32 times.
33 if (!(options.dip >= 0.0 && options.dip < 0.5 * std::numbers::pi)) {
31
1/1
✓ Branch 2 taken 1 time.
1 throw std::invalid_argument("Strata dip must be in [0, 90) deg");
32 }
33
6/6
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 28 times.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 27 times.
60 if (!(options.sharpness >= 1.0 && std::isfinite(options.sharpness) &&
34
3/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 27 times.
28 options.softSharpness >= 1.0 && std::isfinite(options.softSharpness))) {
35 throw std::invalid_argument(
36
1/1
✓ Branch 2 taken 5 times.
5 "Strata sharpness and softSharpness must be finite and at least 1");
37 }
38
4/6
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
27 if (!(options.strength >= 0.0 && options.strength <= 1.0 && options.hardShare >= 0.0 &&
39
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 25 times.
26 options.hardShare <= 1.0)) {
40
1/1
✓ Branch 2 taken 2 times.
2 throw std::invalid_argument("Strata strength and hardShare must be in [0, 1]");
41 }
42
3/4
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
25 if (!(options.cliffShare > 0.0 && options.cliffShare <= 1.0)) {
43
1/1
✓ Branch 2 taken 1 time.
1 throw std::invalid_argument("Strata cliffShare must be in (0, 1]");
44 }
45
6/6
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 22 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 22 times.
46 if (!(std::isfinite(options.dipAzimuth) && std::isfinite(options.anchor.x) &&
46
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 std::isfinite(options.anchor.y))) {
47
1/1
✓ Branch 2 taken 2 times.
2 throw std::invalid_argument("Strata dipAzimuth and anchor must be finite");
48 }
49 22 }
50
51 32 StrataLayer::StrataLayer(StrataOptions options, Surface surface)
52 32 : FormulaLayer(surface), options_(options) {
53
1/1
✓ Branch 1 taken 19 times.
32 ValidateStrataOptions(options_);
54 19 dipDirection_ = {std::cos(options_.dipAzimuth), std::sin(options_.dipAzimuth)};
55 19 dipSlope_ = std::tan(options_.dip);
56
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 18 times.
19 if (surface_.IsBody()) {
57
1/1
✓ Branch 1 taken 1 time.
1 anchorDirection_ = UnitVector(options_.anchor.x, options_.anchor.y);
58 }
59 45 }
60
61 5382791 double StrataLayer::BoundaryOf(std::int64_t k) const {
62 5382791 const auto bits = static_cast<std::uint64_t>(k);
63 5382791 RandomStream random(options_.seed, kStrataStream, static_cast<std::uint32_t>(bits),
64
1/1
✓ Branch 1 taken 5382791 times.
5382791 static_cast<std::uint32_t>(bits >> 32));
65
1/1
✓ Branch 1 taken 5382791 times.
5382791 const double offset = options_.thicknessVariation * (random.NextDouble() - 0.5);
66 5382791 return options_.bedThickness * (static_cast<double>(k) + offset);
67 }
68
69 2635043 StrataLayer::Bed StrataLayer::BedOf(double level) const {
70 2635043 auto k = static_cast<std::int64_t>(std::floor(level / options_.bedThickness));
71 2635043 double bottom = BoundaryOf(k);
72
2/2
✓ Branch 0 taken 92391 times.
✓ Branch 1 taken 2635043 times.
2727434 while (level < bottom) {
73 92391 --k;
74 92391 bottom = BoundaryOf(k);
75 }
76 2635043 double top = BoundaryOf(k + 1);
77
2/2
✓ Branch 0 taken 20314 times.
✓ Branch 1 taken 2635043 times.
2655357 while (level >= top) {
78 20314 ++k;
79 20314 bottom = top;
80 20314 top = BoundaryOf(k + 1);
81 }
82 2635043 bool hard = options_.hardShare >= 1.0;
83
2/2
✓ Branch 0 taken 801 times.
✓ Branch 1 taken 2634242 times.
2635043 if (!hard) {
84 801 const auto bits = static_cast<std::uint64_t>(k);
85 801 RandomStream random(options_.seed, kHardnessStream, static_cast<std::uint32_t>(bits),
86
1/1
✓ Branch 1 taken 801 times.
801 static_cast<std::uint32_t>(bits >> 32));
87
1/1
✓ Branch 1 taken 801 times.
801 hard = random.NextDouble() < options_.hardShare;
88 }
89 2635043 return {bottom, top, hard};
90 }
91
92 2635074 bool StrataLayer::LevelOf(double height, double x, double y, double& level) const {
93 2635074 Vec2d q{x, y};
94
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2635070 times.
2635074 if (surface_.IsBody()) {
95
3/3
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1 time.
4 if (Dot(UnitVector(x, y), anchorDirection_) <= 0.0) {
96 3 return false;
97 }
98
1/1
✓ Branch 1 taken 1 time.
1 q = surface_.Offset(options_.anchor, {x, y});
99 }
100 2635071 level = height + dipSlope_ * (q.x * dipDirection_.x + q.y * dipDirection_.y);
101 2635071 return std::isfinite(level);
102 }
103
104 280010 double StrataLayer::BedPositionAt(double height, double x, double y) const {
105 280010 double level = 0.0;
106
3/3
✓ Branch 1 taken 280010 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 280009 times.
280010 if (!LevelOf(height, x, y, level)) {
107 1 return 0.0;
108 }
109
1/1
✓ Branch 1 taken 280009 times.
280009 const Bed bed = BedOf(level);
110
1/1
✓ Branch 1 taken 280009 times.
280009 return std::clamp((level - bed.bottom) / (bed.top - bed.bottom), 0.0, 1.0);
111 }
112
113 2288612 double StrataLayer::TerracedAt(double height, double x, double y) const {
114 2288612 double level = 0.0;
115
7/7
✓ Branch 1 taken 2288612 times.
✓ Branch 3 taken 2288610 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 28 times.
✓ Branch 6 taken 2288582 times.
✓ Branch 7 taken 30 times.
✓ Branch 8 taken 2288582 times.
2288612 if (!LevelOf(height, x, y, level) || options_.strength <= 0.0) {
116 30 return height;
117 }
118
1/1
✓ Branch 1 taken 2288582 times.
2288582 const Bed bed = BedOf(level);
119 2288582 const double rise = bed.top - bed.bottom;
120
1/1
✓ Branch 1 taken 2288582 times.
2288582 const double u = std::clamp((level - bed.bottom) / rise, 0.0, 1.0);
121
2/2
✓ Branch 0 taken 2288368 times.
✓ Branch 1 taken 214 times.
2288582 const double sharpness = bed.hard ? options_.sharpness : options_.softSharpness;
122 2288582 const double terraced = height + rise * (std::pow(u, sharpness) - u);
123 2288582 return height + options_.strength * (terraced - height);
124 }
125
126 66455 double StrataLayer::MaterialCoverAt(double x, double y, double spacing,
127 const Ground* ground) const {
128
1/1
✓ Branch 2 taken 66454 times.
66455 RequireGround(ground, kGroundReader);
129
1/1
✓ Branch 1 taken 66454 times.
66454 const double height = static_cast<double>(ground->HeightAt(x, y, spacing));
130
6/6
✓ Branch 1 taken 66453 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 66452 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 66452 times.
66454 if (!std::isfinite(height) || options_.strength <= 0.0) {
131 2 return 0.0;
132 }
133 66452 double level = 0.0;
134
2/3
✓ Branch 1 taken 66452 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 66452 times.
66452 if (!LevelOf(height, x, y, level)) {
135 ✗ return 0.0;
136 }
137
1/1
✓ Branch 1 taken 66452 times.
66452 const Bed bed = BedOf(level);
138
2/2
✓ Branch 0 taken 213 times.
✓ Branch 1 taken 66239 times.
66452 if (!bed.hard) {
139 213 return 0.0;
140 }
141
1/1
✓ Branch 1 taken 66239 times.
66239 const double u = std::clamp((level - bed.bottom) / (bed.top - bed.bottom), 0.0, 1.0);
142
1/1
✓ Branch 1 taken 66239 times.
66239 return Smoothstep((u - (1.0 - options_.cliffShare)) / kCliffFade + 0.5);
143 }
144
145 2248067 float StrataLayer::HeightAt(float height, double x, double y, double /*spacing*/,
146 const Ground* /*ground*/) const {
147 2248067 return static_cast<float>(TerracedAt(static_cast<double>(height), x, y));
148 }
149
150 } // namespace terraingen
151