spike(S3): VTK banded contour 管线跑通真实网格样本(图#18)
vtkImageData(规则栅格)->vtkDataSetSurfaceFilter->vtkBandedPolyDataContourFilter(GenerateContourEdges) 离屏渲染成功, 验证设计§4.3管线选型(B-1/B-2修正)。 颜色精确映射(colorBar非均匀值)留 render 层用 vtkColorTransferFunction 做。
This commit is contained in:
parent
bb32e9890c
commit
85e4dbea56
|
|
@ -8,3 +8,5 @@ target_link_libraries(geopro_tests PRIVATE GTest::gtest GTest::gtest_main)
|
||||||
|
|
||||||
include(GoogleTest)
|
include(GoogleTest)
|
||||||
gtest_discover_tests(geopro_tests)
|
gtest_discover_tests(geopro_tests)
|
||||||
|
|
||||||
|
add_subdirectory(spike) # spike S3: banded contour 渲染验证
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Spike S3: 真实样本 banded contour 离屏渲染(验证设计 §4.3 管线选型)
|
||||||
|
find_package(nlohmann_json CONFIG REQUIRED)
|
||||||
|
find_package(VTK REQUIRED COMPONENTS
|
||||||
|
CommonCore
|
||||||
|
CommonDataModel
|
||||||
|
CommonColor
|
||||||
|
FiltersGeometry
|
||||||
|
FiltersModeling
|
||||||
|
RenderingOpenGL2
|
||||||
|
IOImage
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(grid_contour_spike grid_contour_spike.cpp)
|
||||||
|
target_link_libraries(grid_contour_spike PRIVATE ${VTK_LIBRARIES} nlohmann_json::nlohmann_json)
|
||||||
|
vtk_module_autoinit(TARGETS grid_contour_spike MODULES ${VTK_LIBRARIES})
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Geopro 3.0 desktop client (Qt6 + VTK9) - M1. 方案②-修订: Qt/VTK/ADS/QtKeychain 对接官方 MSVC Qt(不走 vcpkg); 仅非 Qt 依赖走 vcpkg, 按层递增。",
|
"description": "Geopro 3.0 desktop client (Qt6 + VTK9) - M1. 方案②-修订: Qt/VTK/ADS/QtKeychain 对接官方 MSVC Qt(不走 vcpkg); 仅非 Qt 依赖走 vcpkg, 按层递增。",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"gtest"
|
"gtest",
|
||||||
|
"nlohmann-json"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue