#include #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(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; a.simplifyTol = 0; ContourOptions b; b.upsample = 2; b.smooth = 0; b.simplifyTol = 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; opt.simplifyTol = 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(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; opt.simplifyTol = 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.simplifyTol = 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()); }