TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
222 / 225
98.7%
Branches:
150 / 155
96.8%
Functions:
20 / 20
100.0%

layers/craters/secondaries.cpp
Line Branch Exec Source
1 #include "terraingen/layers/craters/secondaries.h"
2
3 #include "terraingen/layers/fade.h"
4 #include "terraingen/noise/value.h"
5 #include "terraingen/random/philox.h"
6 #include "terraingen/random/samplers.h"
7 #include "terraingen/random/stream.h"
8 #include "terraingen/raster/grid.h"
9 #include "terraingen/util/checks.h"
10
11 #include <algorithm>
12 #include <cmath>
13 #include <limits>
14 #include <numbers>
15 #include <stdexcept>
16 #include <string>
17 #include <unordered_map>
18 #include <utility>
19 #include <vector>
20
21 namespace terraingen {
22
23 namespace {
24
25 constexpr std::uint32_t kSecondaryStream = 0x53454344u;
26
27 /// The primary's ID mixed with a cell's indices into a secondary's ID.
28 16440 std::uint64_t SecondaryId(std::uint64_t primary, int band, std::int64_t ring, std::int64_t sector,
29 std::uint32_t index) {
30 16440 const IdWords id = SplitId(primary);
31 16440 const U32x4 block = Philox4x32(
32 16440 {id.low, id.high,
33 16440 static_cast<std::uint32_t>(band) ^ (static_cast<std::uint32_t>(ring) << 8), index},
34 {static_cast<std::uint32_t>(sector), kSecondaryStream});
35 16440 return (static_cast<std::uint64_t>(block.x) << 32) | block.y;
36 }
37
38 /// The smallest and largest distances, and the directions (radians counter-clockwise from east,
39 /// as an interval around `mid` of half-width `half`), from `centre` to `region` on `surface`;
40 /// the whole circle when `region` holds the centre.
41 struct Footprint {
42 double near = 0.0;
43 double far = 0.0;
44 double mid = 0.0;
45 double half = std::numbers::pi;
46 };
47
48 1073 Footprint FootprintOf(const Vec2d& centre, const Bounds2d& region, const Surface& surface) {
49 1073 Footprint footprint;
50 1073 std::vector<Vec2d> corners;
51
2/2
✓ Branch 2 taken 2146 times.
✓ Branch 3 taken 1073 times.
3219 for (const double x : {region.minX, region.maxX}) {
52
2/2
✓ Branch 2 taken 4292 times.
✓ Branch 3 taken 2146 times.
6438 for (const double y : {region.minY, region.maxY}) {
53
2/2
✓ Branch 1 taken 4292 times.
✓ Branch 4 taken 4292 times.
4292 corners.push_back(surface.Offset(centre, {x, y}));
54 }
55 }
56 1073 double minX = INFINITY;
57 1073 double maxX = -INFINITY;
58 1073 double minY = INFINITY;
59 1073 double maxY = -INFINITY;
60
2/2
✓ Branch 4 taken 4292 times.
✓ Branch 5 taken 1073 times.
5365 for (const Vec2d& c : corners) {
61 4292 minX = std::fmin(minX, c.x);
62 4292 maxX = std::fmax(maxX, c.x);
63 4292 minY = std::fmin(minY, c.y);
64 4292 maxY = std::fmax(maxY, c.y);
65 4292 footprint.far = std::fmax(footprint.far, std::hypot(c.x, c.y));
66 }
67 1073 const double dx = std::fmax(0.0, std::fmax(minX, -maxX));
68 1073 const double dy = std::fmax(0.0, std::fmax(minY, -maxY));
69 1073 footprint.near = std::hypot(dx, dy);
70
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 1053 times.
1073 if (footprint.near == 0.0) {
71 20 return footprint;
72 }
73 1053 footprint.mid = std::atan2(0.5 * (minY + maxY), 0.5 * (minX + maxX));
74 1053 double half = 0.0;
75
2/2
✓ Branch 4 taken 4212 times.
✓ Branch 5 taken 1053 times.
5265 for (const Vec2d& c : corners) {
76 4212 half = std::fmax(half, std::fabs(std::remainder(std::atan2(c.y, c.x) - footprint.mid,
77 2.0 * std::numbers::pi)));
78 }
79 1053 footprint.half = std::fmin(std::numbers::pi, half);
80 1053 return footprint;
81 1073 }
82
83 /// The sectors [first, last) of a ring of `sectors` sectors of angle `width` at inner radius `r0`
84 /// that can reach a footprint, in cells of side `cell`; indices may run past a turn.
85 6742 std::pair<std::int64_t, std::int64_t> SectorRange(const Footprint& footprint, double cell,
86 double r0, double width, std::int64_t sectors) {
87
2/2
✓ Branch 0 taken 6386 times.
✓ Branch 1 taken 356 times.
6742 if (footprint.half < std::numbers::pi) {
88 6386 const double margin = cell / r0 + width;
89 6386 const double from = footprint.mid - footprint.half - margin;
90 6386 const double to = footprint.mid + footprint.half + margin;
91
1/2
✓ Branch 0 taken 6386 times.
✗ Branch 1 not taken.
6386 if (to - from < 2.0 * std::numbers::pi) {
92 12772 return {static_cast<std::int64_t>(std::floor(from / width)),
93 6386 static_cast<std::int64_t>(std::ceil(to / width))};
94 }
95 }
96 356 return {0, sectors};
97 }
98
99 /// One primary, with what its secondaries depend on.
100 struct Primary {
101 Crater crater;
102 double onset = 0.0; ///< `SecondaryOnsetRange`.
103 double outer = 0.0; ///< The outer range.
104 double density0 = 0.0; ///< Secondaries per m^2 at the onset at least `reference` across.
105 double reference = 0.0; ///< `countFraction` times the diameter.
106 Footprint footprint; ///< The region secondaries are wanted in, seen from the primary.
107 /// Where its secondaries gather; none, without rays, spreads them evenly.
108 std::optional<SecondaryRayField> rays;
109 };
110
111 /// One ring of a size band of a primary's polar cells.
112 struct Ring {
113 int band = 0;
114 double low = 0.0; ///< Smallest diameter of the band.
115 double bandShare = 0.0; ///< The band's share of the cumulative count at `reference`.
116 std::int64_t index = 0;
117 double r0 = 0.0;
118 double r1 = 0.0;
119 double r = 0.0; ///< Chapman's distance, 1 + (r0 - onset) / D.
120 double width = 0.0; ///< Angle of a sector.
121 double mean = 0.0; ///< Secondaries expected in a sector.
122 double peak = 1.0; ///< `SecondaryRayField::Peak` over the ring; 1 without rays.
123 };
124
125 } // namespace
126
127 2156 double SecondaryStretch(const SecondaryShapes& shapes) {
128
2/2
✓ Branch 0 taken 2057 times.
✓ Branch 1 taken 99 times.
2156 return shapes.elongatedShare > 0.0 ? std::sqrt(shapes.ellipticity.Highest()) : 1.0;
129 }
130
131 namespace {
132
133 /// Draws the secondaries of primaries into one list, keeping count of those expected.
134 class Scatter {
135 public:
136 1077 Scatter(const SecondaryCraterLayer& layer, const Surface& surface, const Bounds2d& region,
137 const Bounds2d& wanted, double reachRadii, double skipBelow)
138 1077 : layer_(layer), options_(layer.Options()), surface_(surface), region_(region),
139 1077 wanted_(wanted), reachRadii_(reachRadii), skipBelow_(skipBelow),
140
1/1
✓ Branch 4 taken 1077 times.
1077 maxStretch_(SecondaryStretch(layer.Options().shapes)) {}
141
142 /// Draws the secondaries `crater` throws, if it is a selected primary.
143 479837 void Throws(const Crater& crater) {
144
3/3
✓ Branch 1 taken 479837 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 479834 times.
479837 if (!options_.selection.Includes(crater)) {
145 478764 return;
146 }
147 479834 const double diameter = crater.diameter;
148 479834 Primary primary;
149 479834 primary.crater = crater;
150
1/1
✓ Branch 1 taken 479834 times.
479834 primary.onset = SecondaryOnsetRange(diameter);
151
1/1
✓ Branch 1 taken 479834 times.
479834 primary.outer = layer_.OuterRange(diameter);
152
2/2
✓ Branch 0 taken 478761 times.
✓ Branch 1 taken 1073 times.
479834 if (!(primary.outer > primary.onset)) {
153 478761 return;
154 }
155
1/1
✓ Branch 1 taken 1073 times.
1073 primary.footprint = FootprintOf({crater.x, crater.y}, wanted_, surface_);
156 1073 primary.density0 = *options_.onsetCount / (0.25 * std::numbers::pi * diameter * diameter);
157 1073 primary.reference = options_.countFraction * diameter;
158
1/1
✓ Branch 1 taken 1073 times.
1073 primary.rays = SecondaryRayField::Make(crater, options_.rays);
159
2/2
✓ Branch 1 taken 1073 times.
✓ Branch 4 taken 1073 times.
1073 const double biggest = std::fmin(
160 1073 options_.maxDiameter, std::fmax(SingerMaxSecondaryDiameter(diameter, primary.onset),
161 SingerMaxSecondaryDiameter(diameter, primary.outer)));
162 1073 const int firstBand = std::ilogb(std::fmax(options_.minDiameter, 1e-9));
163 1073 const int lastBand = std::ilogb(biggest);
164
2/2
✓ Branch 0 taken 4263 times.
✓ Branch 1 taken 1072 times.
5335 for (int band = firstBand; band <= lastBand; ++band) {
165
1/1
✓ Branch 1 taken 4262 times.
4263 DrawBand(primary, band);
166 }
167
2/2
✓ Branch 1 taken 1072 times.
✓ Branch 2 taken 478761 times.
479834 }
168
169 1076 CraterList Take() {
170 1076 return std::move(secondaries_);
171 }
172
173 /// The outlines of the elongated secondaries drawn, by ID.
174 1076 std::unordered_map<std::uint64_t, SecondaryOutline> TakeOutlines() {
175 1076 return std::move(outlines_);
176 }
177
178 private:
179 4263 void DrawBand(const Primary& primary, int band) {
180 4263 const double low = std::fmax(options_.minDiameter, std::ldexp(1.0, band));
181 4263 const double high = std::fmin(options_.maxDiameter, std::ldexp(1.0, band + 1));
182
3/4
✓ Branch 0 taken 4263 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1044 times.
✓ Branch 3 taken 3219 times.
4263 if (!(high > low) || high <= skipBelow_) {
183 1050 return;
184 }
185 3219 const double diameter = primary.crater.diameter;
186
1/1
✓ Branch 1 taken 3219 times.
3219 const double far = std::fmin(primary.outer, SingerRangeForSize(diameter, low));
187 3219 const double cell = options_.cellSizeFactor * high;
188 3219 const double inner = std::fmax(primary.onset, primary.footprint.near - cell);
189 3219 const double outerEdge = std::fmin(far, primary.footprint.far + cell);
190
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3213 times.
3219 if (!(outerEdge > inner)) {
191 6 return;
192 }
193 3213 const auto firstRing =
194 3213 static_cast<std::int64_t>(std::floor((inner - primary.onset) / cell));
195 3213 const auto lastRing =
196 3213 static_cast<std::int64_t>(std::ceil((outerEdge - primary.onset) / cell));
197 3213 const double s = options_.sizeSlope;
198 3213 Ring ring;
199 3213 ring.band = band;
200 3213 ring.low = low;
201 3213 ring.bandShare =
202 3213 std::pow(low / primary.reference, -s) - std::pow(high / primary.reference, -s);
203
2/2
✓ Branch 0 taken 6742 times.
✓ Branch 1 taken 3212 times.
9954 for (ring.index = firstRing; ring.index < lastRing; ++ring.index) {
204 6742 ring.r0 = primary.onset + static_cast<double>(ring.index) * cell;
205 6742 ring.r1 = std::fmin(far, ring.r0 + cell);
206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6742 times.
6742 if (!(ring.r1 > ring.r0)) {
207 ✗ continue;
208 }
209
1/1
✓ Branch 1 taken 6741 times.
6742 DrawRing(primary, ring, cell);
210 }
211 }
212
213 6742 void DrawRing(const Primary& primary, Ring& ring, double cell) {
214 6742 const auto sectors = std::max<std::int64_t>(
215 13484 1, static_cast<std::int64_t>(
216 6742 std::ceil(2.0 * std::numbers::pi * 0.5 * (ring.r0 + ring.r1) / cell)));
217 6742 ring.width = 2.0 * std::numbers::pi / static_cast<double>(sectors);
218 6742 const auto [first, last] =
219 6742 SectorRange(primary.footprint, cell, ring.r0, ring.width, sectors);
220 6742 ring.r = 1.0 + (ring.r0 - primary.onset) / primary.crater.diameter;
221 6742 const double area = 0.5 * ring.width * (ring.r1 * ring.r1 - ring.r0 * ring.r0);
222 6742 ring.mean = primary.density0 / (ring.r * ring.r) * ring.bandShare * area;
223
2/2
✓ Branch 1 taken 6251 times.
✓ Branch 2 taken 491 times.
6742 if (primary.rays) {
224
1/1
✓ Branch 2 taken 6251 times.
6251 ring.peak = primary.rays->Peak(ring.r0, ring.r1);
225 6251 ring.mean *= ring.peak;
226 }
227
2/2
✓ Branch 0 taken 61445 times.
✓ Branch 1 taken 6741 times.
68186 for (std::int64_t raw = first; raw < last; ++raw) {
228 61445 expected_ += ring.mean;
229
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 61444 times.
61445 if (expected_ > options_.maxExpected) {
230
1/1
✓ Branch 2 taken 1 time.
1 throw std::invalid_argument("Expected secondary count exceeds maxExpected");
231 }
232
1/1
✓ Branch 1 taken 61444 times.
61444 DrawSector(primary, ring, ((raw % sectors) + sectors) % sectors);
233 }
234 6741 }
235
236 61444 void DrawSector(const Primary& primary, const Ring& ring, std::int64_t sector) {
237 61444 const Crater& crater = primary.crater;
238 61444 const IdWords id = SplitId(crater.id);
239 61444 RandomStream random(options_.seed, kSecondaryStream, id.low ^ id.high,
240 61444 static_cast<std::uint32_t>(ring.band) ^
241 61444 (static_cast<std::uint32_t>(ring.index) << 8),
242
1/1
✓ Branch 1 taken 61444 times.
61444 static_cast<std::uint32_t>(sector));
243 61444 const double s = options_.sizeSlope;
244 61444 const double reference = primary.reference;
245
1/1
✓ Branch 1 taken 61444 times.
61444 const std::uint64_t count = SamplePoisson(random, ring.mean);
246
2/2
✓ Branch 0 taken 3146873 times.
✓ Branch 1 taken 61444 times.
3208317 for (std::uint64_t index = 0; index < count; ++index) {
247 3146873 const double range = std::sqrt(
248
1/1
✓ Branch 1 taken 3146873 times.
3146873 ring.r0 * ring.r0 + random.NextDouble() * (ring.r1 * ring.r1 - ring.r0 * ring.r0));
249
1/1
✓ Branch 1 taken 3146873 times.
3146873 const double azimuth = (static_cast<double>(sector) + random.NextDouble()) * ring.width;
250
1/1
✓ Branch 1 taken 3146873 times.
3146873 const double u = random.NextDouble();
251 const double size =
252 reference *
253 3146873 std::pow(std::pow(ring.low / reference, -s) - u * ring.bandShare, -1.0 / s);
254
1/1
✓ Branch 1 taken 3146873 times.
3146873 const double keep = random.NextDouble();
255
3/3
✓ Branch 1 taken 3129697 times.
✓ Branch 2 taken 17176 times.
✓ Branch 4 taken 3129697 times.
3146873 const double rayKeep = primary.rays ? random.NextDouble() : 0.0;
256 3146873 const SecondaryShapes& shapes = options_.shapes;
257
3/3
✓ Branch 0 taken 3087511 times.
✓ Branch 1 taken 59362 times.
✓ Branch 3 taken 3087511 times.
3146873 const double e0 = shapes.elongatedShare > 0.0 ? random.NextDouble() : 1.0;
258
3/3
✓ Branch 0 taken 3087511 times.
✓ Branch 1 taken 59362 times.
✓ Branch 3 taken 3087511 times.
3146873 const double e1 = shapes.elongatedShare > 0.0 ? random.NextDouble() : 0.0;
259
3/3
✓ Branch 0 taken 3087511 times.
✓ Branch 1 taken 59362 times.
✓ Branch 3 taken 3087511 times.
3146873 const double e2 = shapes.elongatedShare > 0.0 ? random.NextDouble() : 0.0;
260 3146873 const double here = 1.0 + (range - primary.onset) / crater.diameter;
261
4/4
✓ Branch 0 taken 2664931 times.
✓ Branch 1 taken 481942 times.
✓ Branch 2 taken 494523 times.
✓ Branch 3 taken 2652350 times.
5811804 if (keep >= (ring.r * ring.r) / (here * here) ||
262
3/3
✓ Branch 1 taken 2664931 times.
✓ Branch 3 taken 12581 times.
✓ Branch 4 taken 2652350 times.
2664931 size >= SingerMaxSecondaryDiameter(crater.diameter, range)) {
263 3130433 continue;
264 }
265
7/7
✓ Branch 1 taken 2637802 times.
✓ Branch 2 taken 14548 times.
✓ Branch 5 taken 2637802 times.
✓ Branch 7 taken 2372684 times.
✓ Branch 8 taken 265118 times.
✓ Branch 9 taken 2372684 times.
✓ Branch 10 taken 279666 times.
2652350 if (primary.rays && rayKeep * ring.peak >= primary.rays->Density(range, azimuth)) {
266 2372684 continue;
267 }
268 ✗ const Vec2d at = surface_.Locate(
269
1/1
✓ Branch 1 taken 279666 times.
279666 {crater.x, crater.y}, {range * std::cos(azimuth), range * std::sin(azimuth)});
270 279666 const double margin = 0.5 * reachRadii_ * maxStretch_ * size;
271
3/3
✓ Branch 1 taken 279666 times.
✓ Branch 4 taken 263226 times.
✓ Branch 5 taken 16440 times.
279666 if (!surface_.Contains(surface_.Expand(region_, margin), at)) {
272 263226 continue;
273 }
274 16440 const std::uint64_t secondary = SecondaryId(crater.id, ring.band, ring.index, sector,
275 16440 static_cast<std::uint32_t>(index));
276
1/1
✓ Branch 1 taken 16440 times.
16440 secondaries_.Append({at.x, at.y, size, crater.ageGyr, secondary});
277
2/2
✓ Branch 0 taken 7315 times.
✓ Branch 1 taken 9125 times.
16440 if (e0 < shapes.elongatedShare) {
278 const double toPrimary =
279
1/1
✓ Branch 1 taken 7315 times.
7315 std::clamp((primary.outer - range) / (primary.outer - primary.onset), 0.0, 1.0);
280
1/1
✓ Branch 1 taken 7315 times.
7315 const Vec2d back = surface_.Offset(at, {crater.x, crater.y});
281
2/2
✓ Branch 1 taken 7315 times.
✓ Branch 4 taken 7315 times.
7315 outlines_[secondary] = {1.0 + (shapes.ellipticity.Sample(e1, e2) - 1.0) * toPrimary,
282 7315 std::atan2(back.y, back.x)};
283 }
284 }
285 61444 }
286
287 const SecondaryCraterLayer& layer_;
288 const SecondaryCraterOptions& options_;
289 const Surface& surface_;
290 const Bounds2d& region_;
291 const Bounds2d& wanted_;
292 double reachRadii_;
293 double skipBelow_;
294 CraterList secondaries_;
295 std::unordered_map<std::uint64_t, SecondaryOutline> outlines_;
296 double expected_ = 0.0;
297 double maxStretch_ = 1.0;
298 };
299
300 } // namespace
301
302 46 SecondaryCraterLayer::SecondaryCraterLayer(std::shared_ptr<const CraterLayer> primaries,
303 CraterShapeModels models, SecondaryCraterOptions options,
304 46 CraterApplyOptions apply)
305 46 : primaries_(std::move(primaries)), models_(std::move(models)), options_(options),
306 92 apply_(apply) {
307
7/8
✓ Branch 1 taken 45 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 44 times.
✓ Branch 5 taken 1 time.
✗ Branch 7 not taken.
✓ Branch 8 taken 44 times.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 44 times.
46 if (!primaries_ || !models_.profile || !models_.degradation) {
308
1/1
✓ Branch 2 taken 2 times.
2 throw std::invalid_argument("Secondary craters need primaries, a profile and degradation");
309 }
310
1/1
✓ Branch 1 taken 34 times.
44 ValidateSecondaryCraterOptions(options_);
311
1/1
✓ Branch 1 taken 34 times.
34 ValidateCraterApplyOptions(apply_);
312
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 33 times.
34 if (apply_.datum != CraterDatum::None) {
313
1/1
✓ Branch 2 taken 1 time.
1 throw std::invalid_argument("Secondary craters are additive; datums are not supported");
314 }
315 33 apply_.surface = primaries_->Options().surface;
316 98 }
317
318 480921 double SecondaryCraterLayer::OuterRange(double diameter) const {
319 480921 return SecondaryOuterRange(diameter, options_);
320 }
321
322 130 CraterAttributes SecondaryCraterLayer::Attributes(const Crater& crater) const {
323
2/2
✓ Branch 3 taken 130 times.
✓ Branch 6 taken 130 times.
130 return ComputeCraterAttributes(crater, models_.degradation->State(crater), *models_.profile);
324 }
325
326 8 CraterFeatures SecondaryCraterLayer::CratersOver(const RasterGrid& grid, double spacing) const {
327 8 CraterFeatures placed{
328
3/3
✓ Branch 1 taken 8 times.
✓ Branch 5 taken 8 times.
✓ Branch 8 taken 8 times.
8 Secondaries(grid.Bounds(), std::max(spacing, grid.Spacing(apply_.surface))), {}};
329
2/2
✓ Branch 1 taken 128 times.
✓ Branch 2 taken 8 times.
136 for (std::size_t n = 0; n < placed.craters.Size(); ++n) {
330
3/3
✓ Branch 1 taken 128 times.
✓ Branch 4 taken 128 times.
✓ Branch 7 taken 128 times.
128 placed.attributes.push_back(Attributes(placed.craters.At(n)));
331 }
332 8 return placed;
333 }
334
335 1078 double SecondaryCraterLayer::MaxFeatureDiameter() const {
336 1078 const double largest = primaries_->MaxFeatureDiameter();
337
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1078 times.
1078 if (!(largest > 0.0)) {
338 ✗ return 0.0;
339 }
340 1078 return std::fmin(options_.maxDiameter,
341 1078 SingerMaxSecondaryDiameter(largest, SecondaryOnsetRange(largest)));
342 }
343
344 29 CraterList SecondaryCraterLayer::Secondaries(const Bounds2d& region, double spacing) const {
345 29 std::unordered_map<std::uint64_t, SecondaryOutline> outlines;
346
1/1
✓ Branch 1 taken 28 times.
57 return Secondaries(region, spacing, outlines);
347 29 }
348
349 1077 CraterList SecondaryCraterLayer::Secondaries(
350 const Bounds2d& region, double spacing,
351 std::unordered_map<std::uint64_t, SecondaryOutline>& outlines) const {
352 1077 const Surface& surface = apply_.surface;
353
1/1
✓ Branch 2 taken 1077 times.
1077 const double reachRadii = CraterReachRadii(*models_.profile, apply_);
354
1/1
✓ Branch 2 taken 1077 times.
1077 const double largest = primaries_->MaxFeatureDiameter();
355 1077 const Bounds2d wanted = surface.Expand(
356
3/3
✓ Branch 1 taken 1077 times.
✓ Branch 4 taken 1077 times.
✓ Branch 7 taken 1077 times.
1077 region, 0.5 * reachRadii * SecondaryStretch(options_.shapes) * MaxFeatureDiameter());
357 const CraterList primaries =
358
3/3
✓ Branch 3 taken 1077 times.
✓ Branch 6 taken 1077 times.
✓ Branch 9 taken 1077 times.
1077 primaries_->Craters(surface.Expand(wanted, OuterRange(largest)), 0.0);
359
2/2
✓ Branch 0 taken 1059 times.
✓ Branch 1 taken 18 times.
1077 const double skipBelow = spacing > 0.0 ? apply_.fade.startSamples * spacing : 0.0;
360
1/1
✓ Branch 1 taken 1077 times.
1077 Scatter scatter(*this, surface, region, wanted, reachRadii, skipBelow);
361
2/2
✓ Branch 1 taken 479837 times.
✓ Branch 2 taken 1076 times.
480913 for (std::size_t n = 0; n < primaries.Size(); ++n) {
362
2/2
✓ Branch 1 taken 479837 times.
✓ Branch 4 taken 479836 times.
479837 scatter.Throws(primaries.At(n));
363 }
364 1076 outlines = scatter.TakeOutlines();
365 2152 return scatter.Take();
366 1078 }
367
368 1047 void SecondaryCraterLayer::ApplySamples(SampleBlock& samples, const LayerContext& context) const {
369 1047 const Raster<float>* grid = samples.Grid();
370 const double spacing =
371 grid != nullptr
372
4/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1032 times.
✓ Branch 3 taken 15 times.
✓ Branch 6 taken 15 times.
1047 ? std::max(context.spacing, RasterGrid::FromRaster(*grid).Spacing(apply_.surface))
373 1047 : context.spacing;
374 1047 std::unordered_map<std::uint64_t, SecondaryOutline> outlines;
375
2/2
✓ Branch 1 taken 1047 times.
✓ Branch 4 taken 1047 times.
1047 const CraterList secondaries = Secondaries(samples.Bounds(), spacing, outlines);
376 1047 CraterSampling sampling{spacing, context.threads};
377
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1045 times.
1047 if (!outlines.empty()) {
378 2 sampling.reshape = [&outlines](const Crater& crater, CraterOutline& outline) {
379
1/1
✓ Branch 1 taken 1480 times.
1480 const auto found = outlines.find(crater.id);
380
2/2
✓ Branch 2 taken 1183 times.
✓ Branch 3 taken 297 times.
1480 if (found != outlines.end()) {
381 1183 outline.ellipticity = found->second.ellipticity;
382 1183 outline.heading = found->second.heading;
383 }
384 1482 };
385 }
386
1/1
✓ Branch 1 taken 1047 times.
1047 ApplyCraters(samples, secondaries, models_, apply_, sampling);
387 1047 }
388
389 } // namespace terraingen
390