diff --git a/src/core/model/ColorScale.cpp b/src/core/model/ColorScale.cpp index 62eb2ef..dce6ad6 100644 --- a/src/core/model/ColorScale.cpp +++ b/src/core/model/ColorScale.cpp @@ -33,6 +33,13 @@ void ColorScale::addStop(double value, Rgba color) { [](const Stop& x, const Stop& y) { return x.value < y.value; }); } +std::vector ColorScale::stopValues() const { + std::vector v; + v.reserve(stops_.size()); + for (const auto& s : stops_) v.push_back(s.value); // stops_ 已升序 + return v; +} + Rgba ColorScale::colorAt(double value) const { if (std::isnan(value)) return nan_.value_or(Rgba{0, 0, 0, 0}); if (stops_.empty()) return Rgba{0, 0, 0, 0}; diff --git a/src/core/model/ColorScale.hpp b/src/core/model/ColorScale.hpp index c744162..b859478 100644 --- a/src/core/model/ColorScale.hpp +++ b/src/core/model/ColorScale.hpp @@ -14,6 +14,8 @@ class ColorScale { public: void addStop(double value, Rgba color); // 内部保持按 value 升序 Rgba colorAt(double value) const; // 含 under/over/NaN 处理 + std::vector stopValues() const; // 升序分段值(供等值线用真实非均匀级) + std::size_t stopCount() const { return stops_.size(); } void setUnder(Rgba c) { under_ = c; } void setOver(Rgba c) { over_ = c; } void setNan(Rgba c) { nan_ = c; } diff --git a/src/render/actors/GridContourActor.cpp b/src/render/actors/GridContourActor.cpp index 15def7b..2eec1cb 100644 --- a/src/render/actors/GridContourActor.cpp +++ b/src/render/actors/GridContourActor.cpp @@ -50,17 +50,19 @@ GridActors buildGridContour(const geopro::core::Grid& g, const geopro::core::Col sgrid->SetPoints(points); sgrid->GetPointData()->SetScalars(sc); - // vmin/vmax 来自 Grid;若退化(==)则用数据极值兜底,避免 LUT/contour 退化。 - double vmin = g.vmin, vmax = g.vmax; - if (vmin >= vmax) { - const auto& vals = g.values(); - vmin = vals.empty() ? 0.0 : vals.front(); - vmax = vmin; - for (double v : vals) { - if (v < vmin) vmin = v; - if (v > vmax) vmax = v; + // 等值线级用 colorBar 的**真实非均匀分段值**(电阻率低值密集),而非均匀分级—— + // 否则数据全挤进低端、一片蓝。范围/色阶都以 colorBar 为准。 + const std::vector stops = cs.stopValues(); + double vmin, vmax; + if (stops.size() >= 2) { vmin = stops.front(); vmax = stops.back(); } + else { + vmin = g.vmin; vmax = g.vmax; + if (vmin >= vmax) { + const auto& vals = g.values(); + vmin = vals.empty() ? 0.0 : vals.front(); vmax = vmin; + for (double v : vals) { if (v < vmin) vmin = v; if (v > vmax) vmax = v; } + if (vmin >= vmax) vmax = vmin + 1.0; } - if (vmin >= vmax) vmax = vmin + 1.0; } auto lut = buildLut(cs, vmin, vmax, kLutLevels); @@ -70,7 +72,12 @@ GridActors buildGridContour(const geopro::core::Grid& g, const geopro::core::Col vtkNew banded; banded->SetInputConnection(surf->GetOutputPort()); - banded->GenerateValues(kBandCount, vmin, vmax); + if (stops.size() >= 2) { + banded->SetNumberOfContours(static_cast(stops.size())); + for (int i = 0; i < static_cast(stops.size()); ++i) banded->SetValue(i, stops[i]); + } else { + banded->GenerateValues(kBandCount, vmin, vmax); + } banded->GenerateContourEdgesOn(); banded->SetScalarModeToValue(); diff --git a/tests/spike/render_verify.cpp b/tests/spike/render_verify.cpp index 69c1779..c5f0bb4 100644 --- a/tests/spike/render_verify.cpp +++ b/tests/spike/render_verify.cpp @@ -1,12 +1,12 @@ // 离屏渲染验证:把真实数据渲成 PNG,肉眼核对方向/2D-3D 差异(不靠"进程活着"自欺)。 // 产出(D:/dev/spike_data/): -// verify_section.png —— 平面反演剖面(#18, 数据详情),应浅部在上、深部在下 -// verify_curtain_top.png —— 帘面俯视(二维),应看到一条沿测线的带(像地图线) -// verify_curtain_3d.png —— 帘面透视(三维),应看到立着的彩色断面墙 +// verify_section.png —— 平面反演剖面(#18, 数据详情), 浅部在上、深部在下, 纵向夸张填满 +// verify_curtain_top.png —— 帘面俯视(二维) +// verify_curtain_3d.png —— 帘面透视(三维) #include +#include #include #include -#include #include #include @@ -15,12 +15,12 @@ #include #include -#include "parse/SampleParsers.hpp" -#include "geo/GeoLocalFrame.hpp" -#include "actors/GridContourActor.hpp" -#include "actors/CurtainActor.hpp" -#include "ColorLutBuilder.hpp" #include "CameraPreset.hpp" +#include "ColorLutBuilder.hpp" +#include "actors/CurtainActor.hpp" +#include "actors/GridContourActor.hpp" +#include "geo/GeoLocalFrame.hpp" +#include "parse/SampleParsers.hpp" static std::string slurp(const char* p) { std::ifstream f(p); @@ -44,17 +44,18 @@ int main() { core::Grid g = data::parseGrid(slurp((dir + "grid.json").c_str())); core::ColorScale cs = data::parseColorScale(slurp((dir + "colorbar.json").c_str())); - // 1) 平面剖面 (#18) — 俯视看 XY 平面(face-on) + // 1) 平面剖面 (#18) — 俯视看 XY 平面(face-on); 纵向夸张 1.5x 填满宽面板(物探惯例) { auto a = render::buildGridContour(g, cs); + const double exag = 1.5; vtkNew ren; ren->SetBackground(1, 1, 1); - if (a.bands) ren->AddActor(a.bands); - if (a.edges) ren->AddActor(a.edges); + if (a.bands) { a.bands->SetScale(1.0, exag, 1.0); ren->AddActor(a.bands); } + if (a.edges) { a.edges->SetScale(1.0, exag, 1.0); ren->AddActor(a.edges); } render::applyTop2D(ren); - renderToPng(ren, (dir + "verify_section.png").c_str(), 1100, 320); + renderToPng(ren, (dir + "verify_section.png").c_str(), 1100, 360); } - // GeoLocalFrame:原点取 lat/lon 均值 + // GeoLocalFrame: 原点取 lat/lon 均值 double lat0 = std::accumulate(g.lat.begin(), g.lat.end(), 0.0) / (g.lat.empty() ? 1 : g.lat.size()); double lon0 = std::accumulate(g.lon.begin(), g.lon.end(), 0.0) / (g.lon.empty() ? 1 : g.lon.size()); core::GeoLocalFrame frame(lat0, lon0);