distribution/spliced.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "terraingen/distribution/spliced.h" | ||
| 2 | |||
| 3 | #include "terraingen/distribution/combined.h" | ||
| 4 | |||
| 5 | #include <cmath> | ||
| 6 | #include <stdexcept> | ||
| 7 | #include <utility> | ||
| 8 | |||
| 9 | namespace terraingen { | ||
| 10 | |||
| 11 | namespace { | ||
| 12 | |||
| 13 | /// The kind of `pieces`, after checking there is one and that they share it. | ||
| 14 | 764 | SfdKind CheckedKind(const std::vector<SfdPiece>& pieces) { | |
| 15 | 764 | std::vector<const SizeFrequencyDistribution*> distributions; | |
| 16 | 2287 | for (const SfdPiece& piece : pieces) { | |
| 17 | 1523 | distributions.push_back(piece.distribution.get()); | |
| 18 | } | ||
| 19 | 1528 | return detail::CommonKind(distributions, "A spliced distribution"); | |
| 20 | 764 | } | |
| 21 | |||
| 22 | /// Whether `distribution` may be evaluated from `low` to `high` metres. | ||
| 23 | 1515 | bool Covers(const SizeFrequencyDistribution& distribution, double low, double high) { | |
| 24 | 1515 | const DiameterRange usable = detail::UsableRange(distribution); | |
| 25 | 3030 | return usable.min <= low && usable.max >= high; | |
| 26 | } | ||
| 27 | |||
| 28 | } // namespace | ||
| 29 | |||
| 30 | 764 | SplicedSfd::SplicedSfd(std::vector<SfdPiece> pieces, DiameterRange range, double tolerance) | |
| 31 | 764 | : SizeFrequencyDistribution(CheckedKind(pieces), range), pieces_(std::move(pieces)) { | |
| 32 | 760 | if (pieces_.front().start != range.min) { | |
| 33 | 1 | throw std::invalid_argument("The first piece must start at the range minimum"); | |
| 34 | } | ||
| 35 | 2271 | for (std::size_t i = 0; i < pieces_.size(); ++i) { | |
| 36 | 1517 | const SfdPiece& piece = pieces_[i]; | |
| 37 | 1517 | const double end = i + 1 < pieces_.size() ? pieces_[i + 1].start : range.max; | |
| 38 | 2274 | if (i > 0 && !(std::isfinite(piece.start) && piece.start > pieces_[i - 1].start && | |
| 39 | 757 | piece.start < range.max)) { | |
| 40 | 2 | throw std::invalid_argument("Piece starts must increase within the range"); | |
| 41 | } | ||
| 42 | 1515 | if (!Covers(*piece.distribution, piece.start, end)) { | |
| 43 | 1 | throw std::invalid_argument("Each piece must be valid over its diameter interval"); | |
| 44 | } | ||
| 45 | 1514 | if (i > 0) { | |
| 46 | 755 | detail::CheckJoin(CumulativeOf(*pieces_[i - 1].distribution, piece.start), | |
| 47 | 755 | CumulativeOf(*piece.distribution, piece.start), piece.start, | |
| 48 | tolerance, false); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | 766 | } | |
| 52 | |||
| 53 | 7863851 | std::size_t SplicedSfd::PieceAt(double diameter) const { | |
| 54 | 7863851 | return detail::IndexOfStart(pieces_, diameter); | |
| 55 | } | ||
| 56 | |||
| 57 | 6786539 | double SplicedSfd::CumulativeAt(double diameter) const { | |
| 58 | 6786539 | return CumulativeOf(*pieces_[PieceAt(diameter)].distribution, diameter); | |
| 59 | } | ||
| 60 | |||
| 61 | 1017112 | double SplicedSfd::IncrementalAt(double diameter) const { | |
| 62 | 1017112 | return IncrementalOf(*pieces_[PieceAt(diameter)].distribution, diameter); | |
| 63 | } | ||
| 64 | |||
| 65 | 60200 | double SplicedSfd::InvertCumulative(double target, double dMin, double dMax) const { | |
| 66 | 60200 | std::size_t index = PieceAt(dMin); | |
| 67 | 66021 | while (index + 1 < pieces_.size()) { | |
| 68 | 60150 | const double high = std::fmin(dMax, pieces_[index + 1].start); | |
| 69 | 60150 | if (target >= CumulativeOf(*pieces_[index].distribution, high)) { | |
| 70 | 54329 | break; | |
| 71 | } | ||
| 72 | 5821 | ++index; | |
| 73 | } | ||
| 74 | 60200 | const SizeFrequencyDistribution& piece = *pieces_[index].distribution; | |
| 75 | 60200 | const double low = std::fmax(dMin, pieces_[index].start); | |
| 76 | const double high = | ||
| 77 | 60200 | index + 1 < pieces_.size() ? std::fmin(dMax, pieces_[index + 1].start) : dMax; | |
| 78 | 60200 | if (target >= CumulativeOf(piece, low)) { | |
| 79 | 522 | return low; | |
| 80 | } | ||
| 81 | 59678 | return InvertCumulativeOf(piece, target, low, high); | |
| 82 | } | ||
| 83 | |||
| 84 | } // namespace terraingen | ||
| 85 |