geopro/tests/spike/grid_contour_spike.cpp

142 lines
5.1 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.

// Spike S3: 用真实网格样本验证 VTK banded contour 管线(设计 §4.3 B-1/B-2 修正)。
// vtkImageData(规则栅格) -> vtkDataSetSurfaceFilter -> vtkBandedPolyDataContourFilter
// (GenerateContourEdgesOn 同出 banded 面 + 等值线) -> 离屏渲染 PNG。
// 目视对照 docs/_validate/ref_18_grid.png。
//
// 数据由 build 脚本拷成 ASCII 路径(Windows 下 ifstream 读中文路径会失败):
// D:/dev/spike_data/grid.json, colorbar.json -> spike_grid_contour.png
#include <algorithm>
#include <fstream>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include <vtkActor.h>
#include <vtkBandedPolyDataContourFilter.h>
#include <vtkCamera.h>
#include <vtkDataSetSurfaceFilter.h>
#include <vtkDoubleArray.h>
#include <vtkImageData.h>
#include <vtkLookupTable.h>
#include <vtkNew.h>
#include <vtkPNGWriter.h>
#include <vtkPointData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkWindowToImageFilter.h>
using json = nlohmann::json;
static const std::string kDir = "D:/dev/spike_data/";
struct Rgb { unsigned char r, g, b; };
static Rgb parseHexOrRgb(const std::string& s) {
if (!s.empty() && s[0] == '#') {
auto h = [&](int i) { return (unsigned char)std::stoi(s.substr(i, 2), nullptr, 16); };
return Rgb{h(1), h(3), h(5)};
}
int r = 0, g = 0, b = 0;
auto p = s.find('(');
if (p != std::string::npos) std::sscanf(s.c_str() + p, "(%d,%d,%d", &r, &g, &b);
return Rgb{(unsigned char)r, (unsigned char)g, (unsigned char)b};
}
int main() {
// ---- 读网格 ----
json g = json::parse(std::ifstream(kDir + "grid.json"))["data"];
auto x = g["x"].get<std::vector<double>>(); // 100, 规则
auto y = g["y"].get<std::vector<double>>(); // 22, 规则
auto v = g["v"].get<std::vector<std::vector<double>>>(); // [22][100] = [j=y][i=x]
const int nx = (int)x.size(), ny = (int)y.size();
const double dx = x[1] - x[0], dy = y[1] - y[0];
vtkNew<vtkImageData> img;
img->SetDimensions(nx, ny, 1);
img->SetOrigin(x[0], y[0], 0.0);
img->SetSpacing(dx, dy, 1.0);
vtkNew<vtkDoubleArray> sc;
sc->SetName("v");
sc->SetNumberOfTuples((vtkIdType)nx * ny);
for (int j = 0; j < ny; ++j)
for (int i = 0; i < nx; ++i) sc->SetValue((vtkIdType)j * nx + i, v[j][i]); // i 最快
img->GetPointData()->SetScalars(sc);
// ---- 色阶 stops ----
json cb = json::parse(std::ifstream(kDir + "colorbar.json"))["data"]["properties"]["colorBar"];
std::vector<std::pair<double, Rgb>> stops;
for (auto& pr : cb)
stops.emplace_back(std::stod(pr[0].get<std::string>()), parseHexOrRgb(pr[1].get<std::string>()));
std::sort(stops.begin(), stops.end(), [](auto& a, auto& b) { return a.first < b.first; });
const double vmin = stops.front().first, vmax = stops.back().first;
// 离散 LUT阶梯取下界
vtkNew<vtkLookupTable> lut;
lut->SetNumberOfTableValues((vtkIdType)stops.size());
lut->SetTableRange(vmin, vmax);
for (size_t k = 0; k < stops.size(); ++k) {
auto c = stops[k].second;
lut->SetTableValue((vtkIdType)k, c.r / 255.0, c.g / 255.0, c.b / 255.0, 1.0);
}
lut->Build();
// ---- 管线imageData -> surface(polydata) -> banded contour(+edges) ----
vtkNew<vtkDataSetSurfaceFilter> surf;
surf->SetInputData(img);
vtkNew<vtkBandedPolyDataContourFilter> banded;
banded->SetInputConnection(surf->GetOutputPort());
std::vector<double> levels;
for (auto& s : stops) levels.push_back(s.first);
banded->SetNumberOfContours((int)levels.size());
for (int k = 0; k < (int)levels.size(); ++k) banded->SetValue(k, levels[k]);
banded->GenerateContourEdgesOn();
banded->SetScalarModeToValue();
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(banded->GetOutputPort());
mapper->SetScalarModeToUseCellData();
mapper->SetLookupTable(lut);
mapper->SetScalarRange(vmin, vmax);
vtkNew<vtkActor> bands;
bands->SetMapper(mapper);
// 等值线边(黑)
vtkNew<vtkPolyDataMapper> edgeMapper;
edgeMapper->SetInputConnection(banded->GetOutputPort(1)); // contour edges
edgeMapper->ScalarVisibilityOff();
vtkNew<vtkActor> edges;
edges->SetMapper(edgeMapper);
edges->GetProperty()->SetColor(0, 0, 0);
edges->GetProperty()->SetLineWidth(0.5);
// ---- 离屏渲染 ----
vtkNew<vtkRenderer> ren;
ren->AddActor(bands);
ren->AddActor(edges);
ren->SetBackground(1, 1, 1);
ren->GetActiveCamera()->ParallelProjectionOn();
ren->ResetCamera();
vtkNew<vtkRenderWindow> rw;
rw->SetOffScreenRendering(1);
rw->AddRenderer(ren);
rw->SetSize(1100, 320);
rw->Render();
vtkNew<vtkWindowToImageFilter> w2i;
w2i->SetInput(rw);
vtkNew<vtkPNGWriter> png;
png->SetFileName((kDir + "spike_grid_contour.png").c_str());
png->SetInputConnection(w2i->GetOutputPort());
png->Write();
std::printf("SPIKE_S3_DONE grid=%dx%d stops=%zu vmin=%.1f vmax=%.1f\n", nx, ny, stops.size(),
vmin, vmax);
return 0;
}