113 lines
3.8 KiB
C++
113 lines
3.8 KiB
C++
#include "actors/CurtainActor.hpp"
|
||
|
||
#include <vtkBandedPolyDataContourFilter.h>
|
||
#include <vtkDataSetSurfaceFilter.h>
|
||
#include <vtkDoubleArray.h>
|
||
#include <vtkNew.h>
|
||
#include <vtkPointData.h>
|
||
#include <vtkPoints.h>
|
||
#include <vtkPolyDataMapper.h>
|
||
#include <vtkStructuredGrid.h>
|
||
|
||
#include <vector>
|
||
|
||
#include <cstddef>
|
||
|
||
#include "ColorLutBuilder.hpp"
|
||
|
||
namespace geopro::render {
|
||
|
||
namespace {
|
||
// LUT 级数。
|
||
constexpr int kLutLevels = 256;
|
||
} // namespace
|
||
|
||
vtkSmartPointer<vtkActor> buildCurtain(const geopro::core::Grid& g,
|
||
const geopro::core::ColorScale& cs,
|
||
const geopro::core::GeoLocalFrame& frame)
|
||
{
|
||
const int nx = g.nx(), ny = g.ny();
|
||
|
||
// 退化网格:返回空 actor(调用方仍可安全 addActor,mapper 无输入则不绘制)。
|
||
if (nx < 1 || ny < 1 || g.y.size() < static_cast<size_t>(ny)) {
|
||
return vtkSmartPointer<vtkActor>::New();
|
||
}
|
||
|
||
const bool hasLatLon =
|
||
g.lat.size() >= static_cast<size_t>(nx) && g.lon.size() >= static_cast<size_t>(nx);
|
||
|
||
// 结构化网格:维度 (nx, ny, 1),点序 i 最快、j 次之(id = j*nx + i)。
|
||
vtkNew<vtkStructuredGrid> sgrid;
|
||
sgrid->SetDimensions(nx, ny, 1);
|
||
|
||
vtkNew<vtkPoints> points;
|
||
points->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) {
|
||
double px, py;
|
||
if (hasLatLon) {
|
||
auto p = frame.toLocal(g.lat[i], g.lon[i]);
|
||
px = p.x;
|
||
py = p.y;
|
||
} else {
|
||
// 退化:用 g.x[i] 作 x、0 作 y。
|
||
px = (g.x.size() > static_cast<size_t>(i)) ? g.x[i] : static_cast<double>(i);
|
||
py = 0.0;
|
||
}
|
||
const vtkIdType id = static_cast<vtkIdType>(j) * nx + i;
|
||
// g.y 是深度(越大越深);VTK Z 向上 → 取负,使深部在下、浅部在上(剖面不倒置)。
|
||
points->SetPoint(id, px, py, -g.y[j]);
|
||
sc->SetValue(id, g.valueAt(i, j));
|
||
}
|
||
}
|
||
|
||
sgrid->SetPoints(points);
|
||
sgrid->GetPointData()->SetScalars(sc);
|
||
|
||
// 用 colorBar 真实分段值做色带(与数据详情#18一致的清晰色带,而非连续插值的糊色)。
|
||
const std::vector<double> 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;
|
||
}
|
||
}
|
||
|
||
auto lut = buildLut(cs, vmin, vmax, kLutLevels);
|
||
|
||
// structuredGrid → 表面 polydata → banded contour(分段色带)
|
||
vtkNew<vtkDataSetSurfaceFilter> surf;
|
||
surf->SetInputData(sgrid);
|
||
vtkNew<vtkBandedPolyDataContourFilter> banded;
|
||
banded->SetInputConnection(surf->GetOutputPort());
|
||
if (stops.size() >= 2) {
|
||
banded->SetNumberOfContours(static_cast<int>(stops.size()));
|
||
for (int i = 0; i < static_cast<int>(stops.size()); ++i) banded->SetValue(i, stops[i]);
|
||
} else {
|
||
banded->GenerateValues(20, vmin, vmax);
|
||
}
|
||
banded->SetScalarModeToValue();
|
||
|
||
vtkNew<vtkPolyDataMapper> mapper;
|
||
mapper->SetInputConnection(banded->GetOutputPort());
|
||
mapper->SetScalarModeToUseCellData();
|
||
mapper->SetLookupTable(lut);
|
||
mapper->SetScalarRange(vmin, vmax);
|
||
|
||
auto actor = vtkSmartPointer<vtkActor>::New();
|
||
actor->SetMapper(mapper);
|
||
return actor;
|
||
}
|
||
|
||
} // namespace geopro::render
|