91 lines
3.9 KiB
C++
91 lines
3.9 KiB
C++
#include <gtest/gtest.h>
|
||
#include "ContourBands.hpp"
|
||
using namespace geopro::core;
|
||
using namespace geopro::render;
|
||
|
||
// 上采样 2x:色带边界更密 → 多边形数应多于不上采样。
|
||
TEST(ContourBands, UpsampleIncreasesDetail) {
|
||
Grid g(3, 3);
|
||
g.x = {0, 1, 2}; g.y = {0, 1, 2};
|
||
for (int j = 0; j < 3; ++j)
|
||
for (int i = 0; i < 3; ++i) g.valueAt(i, j) = static_cast<double>(i * i + j);
|
||
g.vmin = 0; g.vmax = 6;
|
||
ColorScale cs;
|
||
cs.addStop(0, Rgba{0,0,255,255}); cs.addStop(3, Rgba{0,255,0,255}); cs.addStop(6, Rgba{255,0,0,255});
|
||
ContourOptions a; a.upsample = 1; a.smooth = 0;
|
||
ContourOptions b; b.upsample = 2; b.smooth = 0;
|
||
auto ra = buildContourBands(g, cs, a);
|
||
auto rb = buildContourBands(g, cs, b);
|
||
EXPECT_GT(rb.bands.size(), ra.bands.size());
|
||
}
|
||
|
||
// 含 NaN 无效角的网格:裁剪后该角不应被任何色带覆盖(所有多边形顶点远离该角)。
|
||
TEST(ContourBands, ClipsNaNRegion) {
|
||
Grid g(3, 3);
|
||
g.x = {0, 1, 2}; g.y = {0, 1, 2};
|
||
for (int j = 0; j < 3; ++j) for (int i = 0; i < 3; ++i) g.valueAt(i, j) = 5.0;
|
||
g.valueAt(2, 2) = std::nan(""); // 右上角无效
|
||
g.vmin = 0; g.vmax = 10;
|
||
ColorScale cs; cs.addStop(0, Rgba{0,0,255,255}); cs.addStop(10, Rgba{255,0,0,255});
|
||
ContourOptions opt; opt.upsample = 1; opt.smooth = 0;
|
||
auto r = buildContourBands(g, cs, opt);
|
||
bool coversCorner = false;
|
||
for (const auto& b : r.bands)
|
||
for (const auto& p : b.ring)
|
||
if (p.x > 1.5 && p.y > 1.5) coversCorner = true;
|
||
EXPECT_FALSE(coversCorner);
|
||
}
|
||
|
||
// 2x2 平滑梯度网格 + 2 段色阶 → 至少 1 个色带多边形,多边形顶点 >=3。
|
||
TEST(ContourBands, ProducesNonEmptyBands) {
|
||
Grid g(3, 3);
|
||
g.x = {0, 1, 2}; g.y = {0, 1, 2};
|
||
for (int j = 0; j < 3; ++j)
|
||
for (int i = 0; i < 3; ++i) g.valueAt(i, j) = static_cast<double>(i + j); // 0..4
|
||
g.vmin = 0; g.vmax = 4;
|
||
ColorScale cs;
|
||
cs.addStop(0.0, Rgba{0, 0, 255, 255});
|
||
cs.addStop(2.0, Rgba{0, 255, 0, 255});
|
||
cs.addStop(4.0, Rgba{255, 0, 0, 255});
|
||
|
||
ContourOptions opt; opt.upsample = 1; opt.smooth = 0;
|
||
auto r = buildContourBands(g, cs, opt);
|
||
ASSERT_FALSE(r.bands.empty());
|
||
for (const auto& b : r.bands) EXPECT_GE(b.ring.size(), 3u);
|
||
}
|
||
|
||
// 诊断:等值线(lines)应非空——网格跨越多个色阶级时应产生等值线。
|
||
TEST(ContourBands, ProducesContourLines) {
|
||
Grid g(10, 10);
|
||
g.x.resize(10); g.y.resize(10);
|
||
for (int i = 0; i < 10; ++i) { g.x[i] = i; g.y[i] = i; }
|
||
for (int j = 0; j < 10; ++j)
|
||
for (int i = 0; i < 10; ++i) g.valueAt(i, j) = i + j; // 0..18
|
||
g.vmin = 0; g.vmax = 18;
|
||
ColorScale cs;
|
||
cs.addStop(0, Rgba{0,0,255,255}); cs.addStop(6, Rgba{0,255,0,255});
|
||
cs.addStop(12, Rgba{255,255,0,255}); cs.addStop(18, Rgba{255,0,0,255});
|
||
ContourOptions opt; opt.upsample = 1; opt.smooth = 0; opt.makeLines = true;
|
||
auto r = buildContourBands(g, cs, opt);
|
||
EXPECT_FALSE(r.lines.empty());
|
||
}
|
||
|
||
// 诊断(真实参数复现):大网格 + 默认 opt(upsample=2,smooth=0.3,simplifyTol=0.5) + 17 级色阶。
|
||
TEST(ContourBands, ProducesLinesRealisticOptions) {
|
||
Grid g(60, 40);
|
||
g.x.resize(60); g.y.resize(40);
|
||
for (int i = 0; i < 60; ++i) g.x[i] = i * 3.0;
|
||
for (int j = 0; j < 40; ++j) g.y[j] = -33.0 + j * 1.2;
|
||
for (int j = 0; j < 40; ++j)
|
||
for (int i = 0; i < 60; ++i)
|
||
g.valueAt(i, j) = 2.0 + 340.0 * (0.5 + 0.5 * std::sin(i * 0.2) * std::cos(j * 0.25));
|
||
g.vmin = 2; g.vmax = 348;
|
||
ColorScale cs;
|
||
const double bars[17] = {2.08,4.59,7.81,11.02,14.26,17.25,20.56,23.55,27.05,
|
||
31.16,36.83,43.64,51.98,62.09,81.14,108.24,348.52};
|
||
for (int i = 0; i < 17; ++i) cs.addStop(bars[i], Rgba{(unsigned char)(i*15),0,200,255});
|
||
ContourOptions opt; // 默认 upsample=2, smooth=0.3, simplifyTol=0.5, makeLines=true
|
||
auto r = buildContourBands(g, cs, opt);
|
||
EXPECT_FALSE(r.lines.empty());
|
||
}
|