geopro/src/app/ContourLevels.cpp

83 lines
3.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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