random/value_distribution.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "terraingen/random/value_distribution.h" | ||
| 2 | |||
| 3 | #include <algorithm> | ||
| 4 | #include <cmath> | ||
| 5 | #include <numbers> | ||
| 6 | #include <stdexcept> | ||
| 7 | |||
| 8 | namespace terraingen { | ||
| 9 | |||
| 10 | 1035847 | double StandardNormal(double u1, double u2) { | |
| 11 | 1035847 | return std::sqrt(-2.0 * std::log1p(-u1)) * std::cos(2.0 * std::numbers::pi * u2); | |
| 12 | } | ||
| 13 | |||
| 14 | 61778477 | double ValueDistribution::Sample(double u1, double u2) const { | |
| 15 | 61778477 | double value = a; | |
| 16 | 61778477 | switch (kind) { | |
| 17 | 60589429 | case Kind::Constant: | |
| 18 | 60589429 | break; | |
| 19 | 545708 | case Kind::Uniform: | |
| 20 | 545708 | value = a + (b - a) * u1; | |
| 21 | 545708 | break; | |
| 22 | 64107 | case Kind::Normal: | |
| 23 | 64107 | value = a + b * StandardNormal(u1, u2); | |
| 24 | 64107 | break; | |
| 25 | 579233 | case Kind::LogNormal: | |
| 26 | 579233 | value = a * std::exp(b * StandardNormal(u1, u2)); | |
| 27 | 579233 | break; | |
| 28 | } | ||
| 29 | 123556954 | return std::clamp(value, min, max); | |
| 30 | } | ||
| 31 | |||
| 32 | 992208 | double ValueDistribution::Lowest() const { | |
| 33 | 992208 | switch (kind) { | |
| 34 | 990016 | case Kind::Constant: | |
| 35 | case Kind::Uniform: | ||
| 36 | 990016 | return std::clamp(a, min, max); | |
| 37 | 1276 | case Kind::Normal: | |
| 38 | 1276 | return min; | |
| 39 | 916 | case Kind::LogNormal: | |
| 40 | 916 | return std::fmax(0.0, min); | |
| 41 | } | ||
| 42 | ✗ | return min; | |
| 43 | } | ||
| 44 | |||
| 45 | 763115 | double ValueDistribution::Highest() const { | |
| 46 | 763115 | switch (kind) { | |
| 47 | 15277 | case Kind::Constant: | |
| 48 | 15277 | return std::clamp(a, min, max); | |
| 49 | 745256 | case Kind::Uniform: | |
| 50 | 745256 | return std::clamp(b, min, max); | |
| 51 | 2582 | case Kind::Normal: | |
| 52 | case Kind::LogNormal: | ||
| 53 | 2582 | return max; | |
| 54 | } | ||
| 55 | ✗ | return max; | |
| 56 | } | ||
| 57 | |||
| 58 | 1239418 | void ValidateValueDistribution(const ValueDistribution& d) { | |
| 59 | 2478835 | if (!(std::isfinite(d.a) && std::isfinite(d.b)) || std::isnan(d.min) || std::isnan(d.max) || | |
| 60 | 1239417 | d.min > d.max) { | |
| 61 | 2 | throw std::invalid_argument("Distribution parameters must be finite with min <= max"); | |
| 62 | } | ||
| 63 | 1239416 | if (d.kind == ValueDistribution::Kind::Uniform && d.b < d.a) { | |
| 64 | 3 | throw std::invalid_argument("A uniform distribution needs b >= a"); | |
| 65 | } | ||
| 66 | 3716952 | if ((d.kind == ValueDistribution::Kind::Normal || | |
| 67 | 1241625 | d.kind == ValueDistribution::Kind::LogNormal) && | |
| 68 | 2212 | (d.b < 0.0 || !std::isfinite(d.max))) { | |
| 69 | throw std::invalid_argument( | ||
| 70 | 11 | "Normal and log-normal distributions need a non-negative spread and a finite max"); | |
| 71 | } | ||
| 72 | 1239402 | if (d.kind == ValueDistribution::Kind::LogNormal && !(d.a > 0.0)) { | |
| 73 | 1 | throw std::invalid_argument("A log-normal distribution needs a positive median"); | |
| 74 | } | ||
| 75 | 1239401 | } | |
| 76 | |||
| 77 | } // namespace terraingen | ||
| 78 |