TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
84 / 85
98.8%
Branches:
83 / 88
94.3%
Functions:
6 / 6
100.0%

util/radial_shape.cpp
Line Branch Exec Source
1 #include "terraingen/util/radial_shape.h"
2
3 #include "terraingen/util/cube_faces.h"
4
5 #include <algorithm>
6 #include <array>
7 #include <cmath>
8 #include <map>
9 #include <numbers>
10 #include <stdexcept>
11 #include <utility>
12
13 namespace terraingen {
14
15 namespace {
16
17 constexpr double kQuarter = std::numbers::pi / 4.0;
18
19 /// Factor on the largest area per steradian found at quadrilateral corners, for its variation
20 /// within the curved bilinear quadrilaterals.
21 constexpr double kAreaMargin = 1.25;
22
23 /// The gnomonic coordinate of label `k` of `q`, exactly -1, 0 or 1 at multiples of 45 degrees and
24 /// odd in `k - q/2`, so labels shared between faces get bitwise the same direction.
25 15068710 double Gnomonic(std::size_t k, std::size_t q) {
26
2/2
✓ Branch 0 taken 4951770 times.
✓ Branch 1 taken 10116940 times.
15068710 if (2 * k > q) {
27 4951770 return -Gnomonic(q - k, q);
28 }
29
2/2
✓ Branch 0 taken 341584 times.
✓ Branch 1 taken 9775356 times.
10116940 if (k == 0) {
30 341584 return -1.0;
31 }
32
2/2
✓ Branch 0 taken 213320 times.
✓ Branch 1 taken 9562036 times.
9775356 if (2 * k == q) {
33 213320 return 0.0;
34 }
35 9562036 return std::tan(kQuarter * (2.0 * static_cast<double>(k) / static_cast<double>(q) - 1.0));
36 }
37
38 } // namespace
39
40 5058476 Vec3d RadialShape::LabelDirection(std::size_t q, std::size_t i, std::size_t j, std::size_t face) {
41
9/10
✓ Branch 0 taken 5058474 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 5058473 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 5058473 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5058471 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 1 time.
✓ Branch 9 taken 5058470 times.
5058476 if (q == 0 || i > q || j > q || face < 1 || face > 6) {
42
1/1
✓ Branch 2 taken 6 times.
6 throw std::invalid_argument("An ICQ label needs Q > 0, I and J <= Q and F from 1 to 6");
43 }
44 const Vec3d p =
45
2/2
✓ Branch 1 taken 5058470 times.
✓ Branch 4 taken 5058470 times.
5058470 detail::CubeFacePoint(detail::kIcqFaces[face - 1], Gnomonic(i, q), Gnomonic(j, q));
46 5058470 const double length = Length(p);
47 5058470 return {p.x / length, p.y / length, p.z / length};
48 }
49
50 81 RadialShape RadialShape::Sample(std::size_t q, const std::function<double(const Vec3d&)>& radius) {
51
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 80 times.
81 if (q == 0) {
52
1/1
✓ Branch 2 taken 1 time.
1 throw std::invalid_argument("A radial shape needs Q > 0");
53 }
54
1/1
✓ Branch 1 taken 80 times.
80 std::vector<double> radii(6 * (q + 1) * (q + 1));
55
2/2
✓ Branch 0 taken 470 times.
✓ Branch 1 taken 78 times.
548 for (std::size_t face = 1; face <= 6; ++face) {
56
2/2
✓ Branch 0 taken 20714 times.
✓ Branch 1 taken 468 times.
21182 for (std::size_t j = 0; j <= q; ++j) {
57
2/2
✓ Branch 0 taken 1012502 times.
✓ Branch 1 taken 20712 times.
1033214 for (std::size_t i = 0; i <= q; ++i) {
58 2025000 radii[((face - 1) * (q + 1) + j) * (q + 1) + i] =
59
2/2
✓ Branch 1 taken 1012502 times.
✓ Branch 4 taken 1012500 times.
1012502 radius(LabelDirection(q, i, j, face));
60 }
61 }
62 }
63
1/1
✓ Branch 3 taken 78 times.
156 return RadialShape(q, std::move(radii));
64 80 }
65
66 91 RadialShape::RadialShape(std::size_t q, std::vector<double> radii)
67 91 : q_(q), radii_(std::move(radii)) {
68
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 91 times.
91 if (q_ == 0) {
69
0/1
✗ Branch 2 not taken.
✗ throw std::invalid_argument("A radial shape needs Q > 0");
70 }
71
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 89 times.
91 if (radii_.size() != 6 * (q_ + 1) * (q_ + 1)) {
72
1/1
✓ Branch 2 taken 2 times.
2 throw std::invalid_argument("A radial shape needs 6 (Q + 1)^2 label radii");
73 }
74
2/2
✓ Branch 5 taken 1023605 times.
✓ Branch 6 taken 88 times.
1023693 for (const double r : radii_) {
75
5/6
✓ Branch 1 taken 1023605 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1023604 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 1023604 times.
1023605 if (!(std::isfinite(r) && r > 0.0)) {
76
1/1
✓ Branch 2 taken 1 time.
1 throw std::invalid_argument("A radial shape's radii must be positive and finite");
77 }
78 }
79
1/1
✓ Branch 3 taken 88 times.
88 const auto [lo, hi] = std::minmax_element(radii_.begin(), radii_.end());
80 88 min_ = *lo;
81 88 max_ = *hi;
82 88 std::map<std::array<double, 3>, double> shared;
83
2/2
✓ Branch 0 taken 525 times.
✓ Branch 1 taken 87 times.
612 for (std::size_t face = 1; face <= 6; ++face) {
84
2/2
✓ Branch 0 taken 21361 times.
✓ Branch 1 taken 524 times.
21885 for (std::size_t j = 0; j <= q_; ++j) {
85
2/2
✓ Branch 0 taken 1023565 times.
✓ Branch 1 taken 21360 times.
1044925 for (std::size_t i = 0; i <= q_; ++i) {
86
8/8
✓ Branch 0 taken 1002204 times.
✓ Branch 1 taken 21361 times.
✓ Branch 2 taken 980844 times.
✓ Branch 3 taken 21360 times.
✓ Branch 4 taken 960532 times.
✓ Branch 5 taken 20312 times.
✓ Branch 6 taken 940220 times.
✓ Branch 7 taken 20312 times.
1023565 if (i != 0 && i != q_ && j != 0 && j != q_) {
87 940220 continue;
88 }
89
1/1
✓ Branch 1 taken 83345 times.
83345 const Vec3d d = LabelDirection(q_, i, j, face);
90 83345 const double r = radii_[LabelIndex(i, j, face)];
91
1/1
✓ Branch 1 taken 83345 times.
83345 const auto [it, inserted] = shared.emplace(std::array<double, 3>{d.x, d.y, d.z}, r);
92
6/6
✓ Branch 0 taken 42016 times.
✓ Branch 1 taken 41329 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 42015 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 83344 times.
83345 if (!inserted && std::fabs(it->second - r) > 1e-9 * max_) {
93 throw std::invalid_argument(
94
1/1
✓ Branch 2 taken 1 time.
1 "A radial shape's labels shared between faces must have equal radii");
95 }
96 }
97 }
98 }
99
2/2
✓ Branch 0 taken 522 times.
✓ Branch 1 taken 87 times.
609 for (std::size_t face = 1; face <= 6; ++face) {
100
2/2
✓ Branch 0 taken 20832 times.
✓ Branch 1 taken 522 times.
21354 for (std::size_t j = 0; j < q_; ++j) {
101
2/2
✓ Branch 0 taken 981360 times.
✓ Branch 1 taken 20832 times.
1002192 for (std::size_t i = 0; i < q_; ++i) {
102 const std::array<std::pair<std::size_t, std::size_t>, 4> corners{
103 981360 {{i, j}, {i + 1, j}, {i + 1, j + 1}, {i, j + 1}}};
104 981360 std::array<Vec3d, 4> p{};
105
2/2
✓ Branch 0 taken 3925440 times.
✓ Branch 1 taken 981360 times.
4906800 for (std::size_t k = 0; k < 4; ++k) {
106 3925440 const auto [ci, cj] = corners[k];
107
1/1
✓ Branch 3 taken 3925440 times.
3925440 p[k] = LabelDirection(q_, ci, cj, face) * radii_[LabelIndex(ci, cj, face)];
108 }
109 981360 Vec3d normal = Cross(p[2] - p[0], p[3] - p[1]);
110 981360 normal = normal * (1.0 / Length(normal));
111
1/2
✓ Branch 2 taken 981360 times.
✗ Branch 3 not taken.
981360 if (Dot(normal, p[0]) < 0.0) {
112 981360 normal = normal * -1.0;
113 }
114
2/2
✓ Branch 0 taken 3925440 times.
✓ Branch 1 taken 981360 times.
4906800 for (const Vec3d& corner : p) {
115 3925440 const double r = Length(corner);
116 3925440 const double cosine = Dot(corner, normal) / r;
117 3925440 maxArea_ = std::max(maxArea_, r * r / std::max(cosine, 1e-3));
118 }
119 }
120 }
121 }
122 87 maxArea_ *= kAreaMargin;
123 92 }
124
125 7606049 double RadialShape::At(const Vec3d& direction) const {
126 7606049 const std::size_t index = detail::CubeFaceOf(detail::kIcqFaces, direction);
127 7606049 const detail::CubeFace& f = detail::kIcqFaces[index];
128 7606049 const double along = Dot(direction, f.normal);
129 15212098 const auto position = [&](const Vec3d& axis) {
130 15212098 const double angle = std::atan(Dot(direction, axis) / along);
131 15212098 const double s = (angle + kQuarter) / (2.0 * kQuarter) * static_cast<double>(q_);
132
1/1
✓ Branch 1 taken 15212098 times.
15212098 const double k = std::clamp(std::floor(s), 0.0, static_cast<double>(q_ - 1));
133 15212098 return std::pair<std::size_t, double>{static_cast<std::size_t>(k),
134
1/1
✓ Branch 1 taken 15212098 times.
30424196 std::clamp(s - k, 0.0, 1.0)};
135 7606049 };
136
1/1
✓ Branch 1 taken 7606049 times.
7606049 const auto [i, fi] = position(f.u);
137
1/1
✓ Branch 1 taken 7606049 times.
7606049 const auto [j, fj] = position(f.v);
138 7606049 const std::size_t face = index + 1;
139 7606049 const double r00 = radii_[LabelIndex(i, j, face)];
140 7606049 const double r10 = radii_[LabelIndex(i + 1, j, face)];
141 7606049 const double r01 = radii_[LabelIndex(i, j + 1, face)];
142 7606049 const double r11 = radii_[LabelIndex(i + 1, j + 1, face)];
143 7606049 return (1.0 - fj) * ((1.0 - fi) * r00 + fi * r10) + fj * ((1.0 - fi) * r01 + fi * r11);
144 }
145
146 } // namespace terraingen
147