layers/sand_fill.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "terraingen/layers/sand_fill.h" | ||
| 2 | |||
| 3 | #include "terraingen/layers/fade.h" | ||
| 4 | #include "terraingen/util/checks.h" | ||
| 5 | |||
| 6 | #include <cmath> | ||
| 7 | #include <numbers> | ||
| 8 | #include <stdexcept> | ||
| 9 | |||
| 10 | namespace terraingen { | ||
| 11 | |||
| 12 | namespace { | ||
| 13 | |||
| 14 | constexpr const char* kGroundReader = "A sand fill layer"; | ||
| 15 | /// Points on each ring the surroundings are read at; the inner ring is turned half a step. | ||
| 16 | constexpr int kRingPoints = 8; | ||
| 17 | |||
| 18 | } // namespace | ||
| 19 | |||
| 20 | 19 | void ValidateSandFillOptions(const SandFillOptions& options) { | |
| 21 | 19 | RequirePositive(options.reach, "Sand fill reach"); | |
| 22 | 16 | RequirePositive(options.maxThickness, "Sand fill maxThickness"); | |
| 23 | 15 | RequirePositive(options.coverThickness, "Sand fill coverThickness"); | |
| 24 | 14 | if (!(options.fill >= 0.0 && options.fill <= 1.0)) { | |
| 25 | 2 | throw std::invalid_argument("Sand fill share must be in [0, 1]"); | |
| 26 | } | ||
| 27 | 12 | } | |
| 28 | |||
| 29 | 14 | SandFillLayer::SandFillLayer(SandFillOptions options, Surface surface) | |
| 30 | 14 | : FormulaLayer(surface), options_(options) { | |
| 31 | 14 | ValidateSandFillOptions(options_); | |
| 32 | 20 | } | |
| 33 | |||
| 34 | 421878 | double SandFillLayer::Thickness(const Ground& ground, double x, double y, double height, | |
| 35 | double spacing) const { | ||
| 36 | 421878 | if (std::isnan(height) || options_.fill <= 0.0) { | |
| 37 | 1 | return 0.0; | |
| 38 | } | ||
| 39 | 421877 | double mean = 0.0; | |
| 40 | 1265631 | for (int ring = 0; ring < 2; ++ring) { | |
| 41 | 843754 | const double radius = options_.reach * (ring == 0 ? 0.5 : 1.0); | |
| 42 | 843754 | const double turn = ring == 0 ? std::numbers::pi / kRingPoints : 0.0; | |
| 43 | 7593786 | for (int k = 0; k < kRingPoints; ++k) { | |
| 44 | 6750032 | const double angle = turn + 2.0 * std::numbers::pi * k / kRingPoints; | |
| 45 | const Vec2d at = | ||
| 46 | 6750032 | surface_.Locate({x, y}, {radius * std::cos(angle), radius * std::sin(angle)}); | |
| 47 | 6750032 | const float there = ground.HeightAt(at.x, at.y, spacing); | |
| 48 | 6750032 | mean += std::isnan(there) ? height : static_cast<double>(there); | |
| 49 | } | ||
| 50 | } | ||
| 51 | 421877 | mean /= 2.0 * kRingPoints; | |
| 52 | 421877 | const double depth = options_.fill * (mean - height); | |
| 53 | 421877 | if (depth <= 0.0) { | |
| 54 | 201545 | return 0.0; | |
| 55 | } | ||
| 56 | 220332 | return options_.maxThickness * -std::expm1(-depth / options_.maxThickness); | |
| 57 | } | ||
| 58 | |||
| 59 | 207741 | double SandFillLayer::ThicknessAt(const Ground& ground, double x, double y, double spacing) const { | |
| 60 | 207741 | return Thickness(ground, x, y, static_cast<double>(ground.HeightAt(x, y, spacing)), spacing); | |
| 61 | } | ||
| 62 | |||
| 63 | 207739 | double SandFillLayer::MaterialCoverAt(double x, double y, double spacing, | |
| 64 | const Ground* ground) const { | ||
| 65 | 207739 | RequireGround(ground, kGroundReader); | |
| 66 | 207738 | return Smoothstep(ThicknessAt(*ground, x, y, spacing) / options_.coverThickness); | |
| 67 | } | ||
| 68 | |||
| 69 | 214137 | float SandFillLayer::HeightAt(float height, double x, double y, double spacing, | |
| 70 | const Ground* ground) const { | ||
| 71 | 214137 | RequireGround(ground, kGroundReader); | |
| 72 | 428274 | return static_cast<float>(static_cast<double>(height) + | |
| 73 | 214137 | Thickness(*ground, x, y, static_cast<double>(height), spacing)); | |
| 74 | } | ||
| 75 | |||
| 76 | 12 | void SandFillLayer::CheckContext(const LayerContext& context) const { | |
| 77 | 12 | RequireGround(context.ground, kGroundReader); | |
| 78 | 11 | } | |
| 79 | |||
| 80 | } // namespace terraingen | ||
| 81 |