feat(render): ContourBands 从 VTK banded 提取色带多边形+等值线几何

This commit is contained in:
gaozheng 2026-06-11 11:37:09 +08:00
parent 179f46b42c
commit f1ad490096
5 changed files with 156 additions and 1 deletions

View File

@ -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)

100
src/render/ContourBands.cpp Normal file
View File

@ -0,0 +1,100 @@
#include "ContourBands.hpp"
#include <vtkBandedPolyDataContourFilter.h>
#include <vtkCell.h>
#include <vtkCellData.h>
#include <vtkDataSetSurfaceFilter.h>
#include <vtkDoubleArray.h>
#include <vtkIdList.h>
#include <vtkNew.h>
#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkStructuredGrid.h>
#include <cmath>
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<vtkStructuredGrid> toStructuredGrid(const Grid& g) {
const int nx = g.nx(), ny = g.ny();
auto sg = vtkSmartPointer<vtkStructuredGrid>::New();
sg->SetDimensions(nx, ny, 1);
vtkNew<vtkPoints> pts; pts->SetNumberOfPoints(static_cast<vtkIdType>(nx) * ny);
vtkNew<vtkDoubleArray> sc; sc->SetName("v");
sc->SetNumberOfTuples(static_cast<vtkIdType>(nx) * ny);
for (int j = 0; j < ny; ++j)
for (int i = 0; i < nx; ++i) {
const vtkIdType id = static_cast<vtkIdType>(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<double> stops = cs.stopValues();
if (stops.size() < 2) return out;
auto sg = toStructuredGrid(g);
vtkNew<vtkDataSetSurfaceFilter> surf; surf->SetInputData(sg);
vtkNew<vtkBandedPolyDataContourFilter> banded;
banded->SetInputConnection(surf->GetOutputPort());
banded->SetNumberOfContours(static_cast<int>(stops.size()));
for (int i = 0; i < static_cast<int>(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

View File

@ -0,0 +1,32 @@
#pragma once
#include <vector>
#include "model/Field.hpp"
#include "model/ColorScale.hpp"
#include "model/Anomaly.hpp"
namespace geopro::render {
struct BandPolygon {
geopro::core::Rgba color;
std::vector<geopro::core::Vec2> ring; // 单环多边形局部坐标y=高程向上为正)
};
struct ContourLine {
double level;
std::vector<geopro::core::Vec2> pts;
};
struct ContourBandsResult {
std::vector<BandPolygon> bands;
std::vector<ContourLine> lines;
};
struct ContourOptions {
int upsample = 2; // 双线性上采样倍数1=不采样)
double smooth = 0.3; // 平滑强度 0..10=不平滑)
double simplifyTol = 0.5; // 等值线简化容差数据单位0=不简化)
bool makeLines = true;
};
// core::GridNaN=无效)+ ColorScale 分段 → 色带多边形 + 等值线几何。
// VTK 仅作算法vtkBandedPolyDataContourFilter无 render window。
ContourBandsResult buildContourBands(const geopro::core::Grid& g,
const geopro::core::ColorScale& cs,
const ContourOptions& opt = {});
} // namespace geopro::render

View File

@ -61,6 +61,7 @@ endif()
# vtkLookupTableVTK::CommonCoregeopro_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_voxelbuildVoxel(ScalarVolume->vtkImageData->GPU 体绘制) + dims
target_sources(geopro_tests PRIVATE render/test_voxel_build.cpp)
# dd_voxel buildVoxelFromScatters(散点 projX/Y -EPSG:4547-> 世界系 + IDW) +充填( PROJ_DATA)

View File

@ -0,0 +1,22 @@
#include <gtest/gtest.h>
#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<double>(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);
}