83 lines
3.3 KiB
C++
83 lines
3.3 KiB
C++
#include "ContourLevels.hpp"
|
||
|
||
#include <algorithm>
|
||
#include <cmath>
|
||
|
||
namespace geopro::app {
|
||
|
||
namespace {
|
||
constexpr int kMaxLevels = 100000; // 纯函数自身的 OOM 兜底(UI 另有 ≤50 校验)
|
||
|
||
std::vector<double> normalLevels(double mn, double mx, double interval) {
|
||
std::vector<double> levels;
|
||
if (!std::isfinite(mn) || !std::isfinite(mx) || !std::isfinite(interval)) return levels;
|
||
if (!(interval > 0) || !(mx > mn)) return levels;
|
||
const double lend = std::ceil((mx - mn) / interval);
|
||
if (lend > kMaxLevels) return levels; // 极小间隔 → 防爆内存
|
||
const int len = static_cast<int>(lend);
|
||
for (int i = 0; i < len; ++i) levels.push_back(mn + interval * i);
|
||
return levels;
|
||
}
|
||
|
||
std::vector<double> logarithmicLevels(double mn, double mx, int logLines) {
|
||
std::vector<double> levels;
|
||
if (!std::isfinite(mn) || !std::isfinite(mx)) return levels; // 防 +Inf 致死循环
|
||
if (logLines <= 0 || !(mx > 0)) return levels;
|
||
auto nextPow10 = [](double v) { return std::pow(10.0, std::ceil(std::log10(v))); };
|
||
const double A = (mn <= 0) ? 0.1 : std::max(0.1, nextPow10(mn) / 10.0);
|
||
const double H = nextPow10(mx);
|
||
std::vector<double> powers;
|
||
for (double cur = A; cur <= H; cur *= 10.0) powers.push_back(cur);
|
||
levels.push_back(mn);
|
||
for (std::size_t i = 0; i + 1 < powers.size(); ++i) {
|
||
const double start = powers[i], end = powers[i + 1];
|
||
const double step = (end - start) / logLines;
|
||
for (int j = 1; j < logLines; ++j) {
|
||
const double v = start + step * j;
|
||
if (v <= mx) levels.push_back(v);
|
||
}
|
||
if (end <= mx) levels.push_back(end);
|
||
}
|
||
if (!powers.empty() && powers.back() < mx) levels.push_back(mx);
|
||
std::sort(levels.begin(), levels.end());
|
||
return levels;
|
||
}
|
||
|
||
std::vector<double> equalAreaLevels(double mn, double mx, int cnt,
|
||
const std::vector<double>& samples) {
|
||
std::vector<double> levels;
|
||
if (cnt <= 0) return levels;
|
||
std::vector<double> z;
|
||
z.reserve(samples.size());
|
||
for (double v : samples)
|
||
if (std::isfinite(v)) z.push_back(v);
|
||
if (static_cast<int>(z.size()) >= cnt) { // 分位
|
||
std::sort(z.begin(), z.end());
|
||
const int chunk = static_cast<int>(z.size()) / cnt;
|
||
for (int i = 0; i < cnt - 1; ++i)
|
||
levels.push_back(
|
||
z[static_cast<std::size_t>(std::min(i * chunk, static_cast<int>(z.size()) - 1))]);
|
||
if (levels.empty() || levels.back() < z.back()) levels.push_back(z.back());
|
||
} else { // 兜底:等距 cnt 段(线性)
|
||
const double step = (mx - mn) / cnt;
|
||
for (int i = 0; i < cnt; ++i) levels.push_back(mn + step * i);
|
||
}
|
||
return levels;
|
||
}
|
||
} // namespace
|
||
|
||
std::vector<double> generateContourLevels(const ContourLevelParams& p,
|
||
const std::vector<double>& samples) {
|
||
switch (p.method) {
|
||
case ContourLevelParams::Method::Normal:
|
||
return normalLevels(p.minValue, p.maxValue, p.interval);
|
||
case ContourLevelParams::Method::Logarithmic:
|
||
return logarithmicLevels(p.minValue, p.maxValue, p.logLinesCount);
|
||
case ContourLevelParams::Method::EqualArea:
|
||
return equalAreaLevels(p.minValue, p.maxValue, p.equalAreaLayerCount, samples);
|
||
}
|
||
return {};
|
||
}
|
||
|
||
} // namespace geopro::app
|