From f1ad4900964bf0b83ab793d2d414089d95c544dd Mon Sep 17 00:00:00 2001 From: gaozheng Date: Thu, 11 Jun 2026 11:37:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(render):=20ContourBands=20=E4=BB=8E=20VTK?= =?UTF-8?q?=20banded=20=E6=8F=90=E5=8F=96=E8=89=B2=E5=B8=A6=E5=A4=9A?= =?UTF-8?q?=E8=BE=B9=E5=BD=A2+=E7=AD=89=E5=80=BC=E7=BA=BF=E5=87=A0?= =?UTF-8?q?=E4=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/render/CMakeLists.txt | 2 +- src/render/ContourBands.cpp | 100 ++++++++++++++++++++++++++++ src/render/ContourBands.hpp | 32 +++++++++ tests/CMakeLists.txt | 1 + tests/render/test_contour_bands.cpp | 22 ++++++ 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 src/render/ContourBands.cpp create mode 100644 src/render/ContourBands.hpp create mode 100644 tests/render/test_contour_bands.cpp diff --git a/src/render/CMakeLists.txt b/src/render/CMakeLists.txt index 9250e21..0d25920 100644 --- a/src/render/CMakeLists.txt +++ b/src/render/CMakeLists.txt @@ -1,7 +1,7 @@ find_package(VTK REQUIRED COMPONENTS CommonCore CommonDataModel FiltersGeometry FiltersModeling RenderingCore RenderingOpenGL2 RenderingVolumeOpenGL2 InteractionStyle InteractionWidgets IOImage) find_package(GDAL CONFIG REQUIRED) add_library(geopro_render STATIC - Scene.cpp ColorLutBuilder.cpp CameraPreset.cpp VoxelFromScatters.cpp actors/GridContourActor.cpp actors/VoxelActor.cpp actors/CurtainActor.cpp actors/MapLineActor.cpp actors/ScatterActor.cpp actors/AnomalyActor.cpp actors/ElectrodeActor.cpp actors/TerrainActor.cpp) + Scene.cpp ColorLutBuilder.cpp CameraPreset.cpp VoxelFromScatters.cpp ContourBands.cpp actors/GridContourActor.cpp actors/VoxelActor.cpp actors/CurtainActor.cpp actors/MapLineActor.cpp actors/ScatterActor.cpp actors/AnomalyActor.cpp actors/ElectrodeActor.cpp actors/TerrainActor.cpp) target_include_directories(geopro_render PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(geopro_render PUBLIC geopro_core ${VTK_LIBRARIES} GDAL::GDAL) target_compile_features(geopro_render PUBLIC cxx_std_17) diff --git a/src/render/ContourBands.cpp b/src/render/ContourBands.cpp new file mode 100644 index 0000000..8840013 --- /dev/null +++ b/src/render/ContourBands.cpp @@ -0,0 +1,100 @@ +#include "ContourBands.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace geopro::render { +using geopro::core::Grid; +using geopro::core::ColorScale; +using geopro::core::Vec2; +using geopro::core::Rgba; + +namespace { +// 用 Grid 构 vtkStructuredGrid(点 (x[i], y[j], 0);NaN 值置 0 但记录于掩膜—— +// 首版裁剪在 Task 1.4 接入,这里先全量)。 +vtkSmartPointer toStructuredGrid(const Grid& g) { + const int nx = g.nx(), ny = g.ny(); + auto sg = vtkSmartPointer::New(); + sg->SetDimensions(nx, ny, 1); + vtkNew pts; pts->SetNumberOfPoints(static_cast(nx) * ny); + vtkNew sc; sc->SetName("v"); + sc->SetNumberOfTuples(static_cast(nx) * ny); + for (int j = 0; j < ny; ++j) + for (int i = 0; i < nx; ++i) { + const vtkIdType id = static_cast(j) * nx + i; + pts->SetPoint(id, g.x[i], g.y[j], 0.0); + const double v = g.valueAt(i, j); + sc->SetValue(id, std::isnan(v) ? 0.0 : v); + } + sg->SetPoints(pts); + sg->GetPointData()->SetScalars(sc); + return sg; +} +} // namespace + +ContourBandsResult buildContourBands(const Grid& g, const ColorScale& cs, const ContourOptions&) { + ContourBandsResult out; + const int nx = g.nx(), ny = g.ny(); + if (nx < 2 || ny < 2 || g.x.size() < 2 || g.y.size() < 2) return out; + + const std::vector stops = cs.stopValues(); + if (stops.size() < 2) return out; + + auto sg = toStructuredGrid(g); + vtkNew surf; surf->SetInputData(sg); + vtkNew banded; + banded->SetInputConnection(surf->GetOutputPort()); + banded->SetNumberOfContours(static_cast(stops.size())); + for (int i = 0; i < static_cast(stops.size()); ++i) banded->SetValue(i, stops[i]); + banded->GenerateContourEdgesOn(); + banded->SetScalarModeToValue(); + banded->Update(); + + // port0:色带多边形(cell 标量=带的代表值)→ 按 ColorScale 上色。 + vtkPolyData* poly = banded->GetOutput(); + vtkDataArray* cellScalars = poly->GetCellData()->GetScalars(); + const vtkIdType nCells = poly->GetNumberOfCells(); + for (vtkIdType c = 0; c < nCells; ++c) { + vtkCell* cell = poly->GetCell(c); + vtkPoints* cp = cell->GetPoints(); + if (cp->GetNumberOfPoints() < 3) continue; + BandPolygon bp; + const double val = cellScalars ? cellScalars->GetTuple1(c) : 0.0; + bp.color = cs.colorAt(val); + for (vtkIdType p = 0; p < cp->GetNumberOfPoints(); ++p) { + double xyz[3]; cp->GetPoint(p, xyz); + bp.ring.push_back(Vec2{xyz[0], xyz[1]}); + } + out.bands.push_back(std::move(bp)); + } + + // port1:等值线(polylines)。 + vtkPolyData* edges = banded->GetContourEdgesOutput(); + if (edges) { + edges->BuildCells(); + const vtkIdType nLines = edges->GetNumberOfCells(); + for (vtkIdType c = 0; c < nLines; ++c) { + vtkCell* cell = edges->GetCell(c); + vtkPoints* cp = cell->GetPoints(); + ContourLine cl; cl.level = 0.0; + for (vtkIdType p = 0; p < cp->GetNumberOfPoints(); ++p) { + double xyz[3]; cp->GetPoint(p, xyz); + cl.pts.push_back(Vec2{xyz[0], xyz[1]}); + } + if (cl.pts.size() >= 2) out.lines.push_back(std::move(cl)); + } + } + return out; +} +} // namespace geopro::render diff --git a/src/render/ContourBands.hpp b/src/render/ContourBands.hpp new file mode 100644 index 0000000..6b6a642 --- /dev/null +++ b/src/render/ContourBands.hpp @@ -0,0 +1,32 @@ +#pragma once +#include +#include "model/Field.hpp" +#include "model/ColorScale.hpp" +#include "model/Anomaly.hpp" +namespace geopro::render { + +struct BandPolygon { + geopro::core::Rgba color; + std::vector ring; // 单环多边形(局部坐标,y=高程向上为正) +}; +struct ContourLine { + double level; + std::vector pts; +}; +struct ContourBandsResult { + std::vector bands; + std::vector lines; +}; +struct ContourOptions { + int upsample = 2; // 双线性上采样倍数(1=不采样) + double smooth = 0.3; // 平滑强度 0..1(0=不平滑) + double simplifyTol = 0.5; // 等值线简化容差(数据单位,0=不简化) + bool makeLines = true; +}; + +// core::Grid(NaN=无效)+ ColorScale 分段 → 色带多边形 + 等值线几何。 +// VTK 仅作算法(vtkBandedPolyDataContourFilter),无 render window。 +ContourBandsResult buildContourBands(const geopro::core::Grid& g, + const geopro::core::ColorScale& cs, + const ContourOptions& opt = {}); +} // namespace geopro::render diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7296aaf..bbb4d4b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -61,6 +61,7 @@ endif() # 需 vtkLookupTable(VTK::CommonCore);geopro_render 已 PUBLIC 传递其余 VTK 组件。 find_package(VTK REQUIRED COMPONENTS CommonCore CommonDataModel RenderingCore) target_sources(geopro_tests PRIVATE render/test_color_lut.cpp) +target_sources(geopro_tests PRIVATE render/test_contour_bands.cpp) # dd_voxel:buildVoxel(ScalarVolume->vtkImageData->GPU 体绘制) 构建不崩 + dims 正确。 target_sources(geopro_tests PRIVATE render/test_voxel_build.cpp) # dd_voxel 回归:buildVoxelFromScatters(散点 projX/Y -EPSG:4547-> 世界系 + IDW) 配准+充填(需 PROJ_DATA)。 diff --git a/tests/render/test_contour_bands.cpp b/tests/render/test_contour_bands.cpp new file mode 100644 index 0000000..c844cc9 --- /dev/null +++ b/tests/render/test_contour_bands.cpp @@ -0,0 +1,22 @@ +#include +#include "ContourBands.hpp" +using namespace geopro::core; +using namespace geopro::render; + +// 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); +}