feat(render): render 层(Scene/ColorLut/GridContourActor/相机预设) + 2D/3D 切换
This commit is contained in:
parent
1f55763a8a
commit
cdf49020af
|
|
@ -11,4 +11,5 @@
|
|||
add_subdirectory(core)
|
||||
add_subdirectory(data)
|
||||
add_subdirectory(net)
|
||||
add_subdirectory(render)
|
||||
add_subdirectory(app)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ target_link_libraries(geopro_desktop PRIVATE
|
|||
geopro_core # Phase 1:ColorScale 上色
|
||||
geopro_data # Phase 2:本地样本仓储(对象树 / 网格 / 色阶)
|
||||
geopro_net # Phase 3:登录(验证码 + RSA + login2)
|
||||
geopro_render # Phase 4:render 层(Scene / GridContourActor / 相机预设)
|
||||
)
|
||||
|
||||
vtk_module_autoinit(TARGETS geopro_desktop MODULES ${VTK_LIBRARIES})
|
||||
|
|
|
|||
177
src/app/main.cpp
177
src/app/main.cpp
|
|
@ -4,17 +4,21 @@
|
|||
// 数据:docs/剖面网格数据的色阶数据2等文件/(真实样本,UTF-8 中文路径,经 QFile 读取)。
|
||||
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <QActionGroup>
|
||||
#include <QApplication>
|
||||
#include <QDialog>
|
||||
#include <QFormLayout>
|
||||
#include <QLabel>
|
||||
#include <QMainWindow>
|
||||
#include <QSurfaceFormat>
|
||||
#include <QToolBar>
|
||||
#include <QTreeWidget>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include <DockManager.h>
|
||||
|
|
@ -28,106 +32,16 @@
|
|||
#include "AuthService.hpp"
|
||||
#include "login/LoginWindow.hpp"
|
||||
|
||||
#include "CameraPreset.hpp"
|
||||
#include "Scene.hpp"
|
||||
#include "actors/GridContourActor.hpp"
|
||||
|
||||
#include <QVTKOpenGLStereoWidget.h>
|
||||
#include <vtkActor.h>
|
||||
#include <vtkBandedPolyDataContourFilter.h>
|
||||
#include <vtkCamera.h>
|
||||
#include <vtkDataSetSurfaceFilter.h>
|
||||
#include <vtkDoubleArray.h>
|
||||
#include <vtkGenericOpenGLRenderWindow.h>
|
||||
#include <vtkImageData.h>
|
||||
#include <vtkLookupTable.h>
|
||||
#include <vtkNew.h>
|
||||
#include <vtkPointData.h>
|
||||
#include <vtkPolyDataMapper.h>
|
||||
#include <vtkProperty.h>
|
||||
#include <vtkRenderer.h>
|
||||
|
||||
namespace {
|
||||
|
||||
// 把 core 模型(Grid + ColorScale)渲染为 banded contour(填充面 + 黑色等值线)。
|
||||
// 入参是已加载的 core 模型,不读文件(数据加载由 Repository 负责)。
|
||||
void renderGrid(vtkRenderer* ren, const geopro::core::Grid& g, const geopro::core::ColorScale& cs)
|
||||
{
|
||||
ren->RemoveAllViewProps(); // 清旧 actor,支持重复切换数据集
|
||||
|
||||
const int nx = g.nx(), ny = g.ny();
|
||||
if (nx < 2 || ny < 2 || g.x.size() < 2 || g.y.size() < 2) {
|
||||
ren->SetBackground(1, 1, 1);
|
||||
ren->ResetCamera();
|
||||
return;
|
||||
}
|
||||
|
||||
vtkNew<vtkImageData> img;
|
||||
img->SetDimensions(nx, ny, 1);
|
||||
img->SetOrigin(g.x[0], g.y[0], 0.0);
|
||||
img->SetSpacing(g.x[1] - g.x[0], g.y[1] - g.y[0], 1.0);
|
||||
|
||||
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)
|
||||
sc->SetValue(static_cast<vtkIdType>(j) * nx + i, g.valueAt(i, j)); // i 最快
|
||||
img->GetPointData()->SetScalars(sc);
|
||||
|
||||
// vmin/vmax 来自 Grid;若退化(==)则用数据极值兜底,避免 LUT/contour 退化。
|
||||
double 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;
|
||||
}
|
||||
|
||||
// 256 级 LUT,按 ColorScale 阶梯取色。
|
||||
const int N = 256;
|
||||
vtkNew<vtkLookupTable> lut;
|
||||
lut->SetNumberOfTableValues(N);
|
||||
lut->SetTableRange(vmin, vmax);
|
||||
for (int t = 0; t < N; ++t) {
|
||||
const double val = vmin + (vmax - vmin) * t / (N - 1);
|
||||
const auto c = cs.colorAt(val);
|
||||
lut->SetTableValue(t, c.r / 255.0, c.g / 255.0, c.b / 255.0, 1.0);
|
||||
}
|
||||
lut->Build();
|
||||
|
||||
vtkNew<vtkDataSetSurfaceFilter> surf;
|
||||
surf->SetInputData(img);
|
||||
|
||||
vtkNew<vtkBandedPolyDataContourFilter> banded;
|
||||
banded->SetInputConnection(surf->GetOutputPort());
|
||||
banded->GenerateValues(20, vmin, vmax);
|
||||
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.6);
|
||||
|
||||
ren->AddActor(bands);
|
||||
ren->AddActor(edges);
|
||||
ren->SetBackground(1, 1, 1);
|
||||
ren->GetActiveCamera()->ParallelProjectionOn();
|
||||
ren->ResetCamera();
|
||||
}
|
||||
|
||||
// 从对象结构树构建 QTreeWidget:GS → TM → DS 三层;DS 项在 UserRole 存 dsId。
|
||||
void populateTree(QTreeWidget* tree, const std::vector<geopro::data::GsNode>& gss)
|
||||
{
|
||||
|
|
@ -159,20 +73,69 @@ std::string readPem(const std::string& path)
|
|||
|
||||
// 在给定 QMainWindow 上构建 M1 工作台:ADS 三栏 + 对象树 → 渲染联动 + 属性面板。
|
||||
// repo 生命周期须覆盖到事件循环结束(由调用方保证)。
|
||||
// 相机模式:默认二维俯视。
|
||||
enum class CameraMode { Top2D, Free3D };
|
||||
|
||||
void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& repo)
|
||||
{
|
||||
// 中央 QVTK 视图(指针供联动回调使用)。
|
||||
// 中央 QVTK 视图(指针供联动回调使用)。renderer 由 render::Scene 持有。
|
||||
// Scene 需在 lambda 回调期间存活 ⇒ 堆分配,挂到 window 父链随窗口销毁。
|
||||
auto* scene = new geopro::render::Scene();
|
||||
auto* vtkWidget = new QVTKOpenGLStereoWidget();
|
||||
// Scene 非 QObject:用 widget 销毁信号清理,避免泄漏(widget 随 window 销毁)。
|
||||
QObject::connect(vtkWidget, &QObject::destroyed, [scene]() { delete scene; });
|
||||
vtkNew<vtkGenericOpenGLRenderWindow> renderWindow;
|
||||
vtkWidget->setRenderWindow(renderWindow);
|
||||
vtkNew<vtkRenderer> renderer;
|
||||
renderWindow->AddRenderer(renderer);
|
||||
renderWindow->AddRenderer(scene->renderer());
|
||||
|
||||
// 当前相机模式(默认二维)。用 shared_ptr 让多个 lambda 共享同一状态。
|
||||
auto cameraMode = std::make_shared<CameraMode>(CameraMode::Top2D);
|
||||
vtkRenderer* rendererPtr = scene->renderer();
|
||||
vtkGenericOpenGLRenderWindow* renderWindowPtr = renderWindow.Get();
|
||||
auto applyCurrentCamera = [rendererPtr, cameraMode]() {
|
||||
if (*cameraMode == CameraMode::Top2D)
|
||||
geopro::render::applyTop2D(rendererPtr);
|
||||
else
|
||||
geopro::render::applyFree3D(rendererPtr);
|
||||
};
|
||||
|
||||
auto* dockManager = new ads::CDockManager(&window);
|
||||
window.setCentralWidget(dockManager);
|
||||
|
||||
auto* vtkDock = new ads::CDockWidget(QStringLiteral("二维剖面视图"));
|
||||
vtkDock->setWidget(vtkWidget);
|
||||
// 中央剖面容器:顶部 2D/3D 工具条 + 下方 QVTK 视图。
|
||||
auto* centerWidget = new QWidget();
|
||||
auto* centerLayout = new QVBoxLayout(centerWidget);
|
||||
centerLayout->setContentsMargins(0, 0, 0, 0);
|
||||
centerLayout->setSpacing(0);
|
||||
|
||||
auto* viewToolBar = new QToolBar();
|
||||
auto* cameraGroup = new QActionGroup(viewToolBar);
|
||||
cameraGroup->setExclusive(true);
|
||||
auto* act2D = viewToolBar->addAction(QStringLiteral("二维"));
|
||||
auto* act3D = viewToolBar->addAction(QStringLiteral("三维"));
|
||||
act2D->setCheckable(true);
|
||||
act3D->setCheckable(true);
|
||||
cameraGroup->addAction(act2D);
|
||||
cameraGroup->addAction(act3D);
|
||||
act2D->setChecked(true); // 默认二维
|
||||
centerLayout->addWidget(viewToolBar);
|
||||
centerLayout->addWidget(vtkWidget, 1);
|
||||
|
||||
QObject::connect(act2D, &QAction::triggered, vtkWidget,
|
||||
[cameraMode, rendererPtr, renderWindowPtr]() {
|
||||
*cameraMode = CameraMode::Top2D;
|
||||
geopro::render::applyTop2D(rendererPtr);
|
||||
renderWindowPtr->Render();
|
||||
});
|
||||
QObject::connect(act3D, &QAction::triggered, vtkWidget,
|
||||
[cameraMode, rendererPtr, renderWindowPtr]() {
|
||||
*cameraMode = CameraMode::Free3D;
|
||||
geopro::render::applyFree3D(rendererPtr);
|
||||
renderWindowPtr->Render();
|
||||
});
|
||||
|
||||
auto* vtkDock = new ads::CDockWidget(QStringLiteral("剖面视图"));
|
||||
vtkDock->setWidget(centerWidget);
|
||||
dockManager->addDockWidget(ads::CenterDockWidgetArea, vtkDock);
|
||||
|
||||
// 左 dock:对象树。
|
||||
|
|
@ -193,18 +156,20 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
dockManager->addDockWidget(ads::RightDockWidgetArea, rightDock);
|
||||
|
||||
// 联动:点击 DS 项 → 加载 grid/colorScale → 渲染 + 更新属性。
|
||||
// VTK 对象(renderer/renderWindow)按【裸指针值】捕获:底层对象被 widget/renderWindow
|
||||
// 引用计数持有(widget 父链挂到 window),生命周期覆盖事件循环;按值捕获避免
|
||||
// buildWorkbench 返回后 vtkNew 局部变量析构导致悬空引用。repo 由调用方保活。
|
||||
vtkRenderer* rendererPtr = renderer.Get();
|
||||
vtkGenericOpenGLRenderWindow* renderWindowPtr = renderWindow.Get();
|
||||
auto renderDataset = [&repo, rendererPtr, renderWindowPtr, propLabel](QTreeWidgetItem* item) {
|
||||
// Scene/renderWindow 按【裸指针值】捕获:Scene 挂 window 父链、renderer 被 renderWindow
|
||||
// 引用计数持有,生命周期覆盖事件循环。repo 由调用方保活。
|
||||
auto renderDataset = [&repo, scene, renderWindowPtr, applyCurrentCamera, propLabel](
|
||||
QTreeWidgetItem* item) {
|
||||
const QString id = item->data(0, Qt::UserRole).toString();
|
||||
if (id.isEmpty()) return; // GS/TM 节点无 dsId,忽略
|
||||
const std::string dsId = id.toStdString();
|
||||
const auto g = repo.loadGrid(dsId);
|
||||
const auto cs = repo.loadColorScale(dsId);
|
||||
renderGrid(rendererPtr, g, cs);
|
||||
scene->clear();
|
||||
const auto actors = geopro::render::buildGridContour(g, cs);
|
||||
scene->addActor(actors.bands);
|
||||
scene->addActor(actors.edges);
|
||||
applyCurrentCamera(); // 按当前 2D/3D 模式重设相机
|
||||
renderWindowPtr->Render();
|
||||
propLabel->setText(QStringLiteral("数据集: %1\n网格: %2 x %3\nvmin / vmax: %4 / %5")
|
||||
.arg(item->text(0))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
find_package(VTK REQUIRED COMPONENTS CommonCore CommonDataModel FiltersGeometry FiltersModeling RenderingOpenGL2 InteractionStyle)
|
||||
add_library(geopro_render STATIC
|
||||
Scene.cpp ColorLutBuilder.cpp CameraPreset.cpp actors/GridContourActor.cpp)
|
||||
target_include_directories(geopro_render PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(geopro_render PUBLIC geopro_core ${VTK_LIBRARIES})
|
||||
target_compile_features(geopro_render PUBLIC cxx_std_17)
|
||||
set_target_properties(geopro_render PROPERTIES AUTOMOC OFF AUTOUIC OFF AUTORCC OFF)
|
||||
vtk_module_autoinit(TARGETS geopro_render MODULES ${VTK_LIBRARIES})
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#include "CameraPreset.hpp"
|
||||
|
||||
#include <vtkCamera.h>
|
||||
|
||||
namespace geopro::render {
|
||||
|
||||
namespace {
|
||||
// 三维斜视方位角 / 仰角。
|
||||
constexpr double kAzimuth = 30.0;
|
||||
constexpr double kElevation = 25.0;
|
||||
} // namespace
|
||||
|
||||
void applyTop2D(vtkRenderer* r)
|
||||
{
|
||||
if (!r) return;
|
||||
auto* c = r->GetActiveCamera();
|
||||
c->ParallelProjectionOn();
|
||||
// 正对 XY 平面:position 在 +Z,focalpoint 在原点(ResetCamera 会重定位到场景中心),viewUp = +Y。
|
||||
c->SetFocalPoint(0, 0, 0);
|
||||
c->SetPosition(0, 0, 1);
|
||||
c->SetViewUp(0, 1, 0);
|
||||
r->ResetCamera();
|
||||
}
|
||||
|
||||
void applyFree3D(vtkRenderer* r)
|
||||
{
|
||||
if (!r) return;
|
||||
auto* c = r->GetActiveCamera();
|
||||
c->ParallelProjectionOff();
|
||||
// 先回到俯视基准,再叠加方位 / 仰角,得到稳定的斜视立体视角。
|
||||
c->SetFocalPoint(0, 0, 0);
|
||||
c->SetPosition(0, 0, 1);
|
||||
c->SetViewUp(0, 1, 0);
|
||||
c->Azimuth(kAzimuth);
|
||||
c->Elevation(kElevation);
|
||||
c->OrthogonalizeViewUp();
|
||||
r->ResetCamera();
|
||||
}
|
||||
|
||||
} // namespace geopro::render
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
#include <vtkRenderer.h>
|
||||
namespace geopro::render {
|
||||
|
||||
// 俯视二维:正交投影,相机在 +Z 正对 XY 平面。
|
||||
void applyTop2D(vtkRenderer* r);
|
||||
|
||||
// 自由三维:透视投影,斜视方位看到剖面立体。
|
||||
void applyFree3D(vtkRenderer* r);
|
||||
|
||||
} // namespace geopro::render
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#include "ColorLutBuilder.hpp"
|
||||
|
||||
namespace geopro::render {
|
||||
|
||||
vtkSmartPointer<vtkLookupTable> buildLut(const geopro::core::ColorScale& cs, double vmin, double vmax, int n)
|
||||
{
|
||||
if (n < 2) n = 2; // 至少两级,避免 (n-1) 退化
|
||||
auto lut = vtkSmartPointer<vtkLookupTable>::New();
|
||||
lut->SetNumberOfTableValues(n);
|
||||
lut->SetTableRange(vmin, vmax);
|
||||
for (int t = 0; t < n; ++t) {
|
||||
const double val = vmin + (vmax - vmin) * t / (n - 1);
|
||||
const auto c = cs.colorAt(val);
|
||||
lut->SetTableValue(t, c.r / 255.0, c.g / 255.0, c.b / 255.0, 1.0);
|
||||
}
|
||||
lut->Build();
|
||||
return lut;
|
||||
}
|
||||
|
||||
} // namespace geopro::render
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkLookupTable.h>
|
||||
#include "model/ColorScale.hpp"
|
||||
namespace geopro::render {
|
||||
|
||||
// 由 core 阶梯色阶构建 N 级 vtkLookupTable,区间 [vmin, vmax]。
|
||||
vtkSmartPointer<vtkLookupTable> buildLut(const geopro::core::ColorScale& cs, double vmin, double vmax, int n = 256);
|
||||
|
||||
} // namespace geopro::render
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#include "Scene.hpp"
|
||||
|
||||
namespace geopro::render {
|
||||
|
||||
Scene::Scene() : renderer_(vtkSmartPointer<vtkRenderer>::New())
|
||||
{
|
||||
renderer_->SetBackground(1, 1, 1); // 白底
|
||||
}
|
||||
|
||||
void Scene::clear()
|
||||
{
|
||||
renderer_->RemoveAllViewProps();
|
||||
}
|
||||
|
||||
void Scene::addActor(vtkActor* a)
|
||||
{
|
||||
if (a) renderer_->AddActor(a);
|
||||
}
|
||||
|
||||
} // namespace geopro::render
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkRenderer.h>
|
||||
#include <vtkActor.h>
|
||||
namespace geopro::render {
|
||||
|
||||
// 单一渲染场景:持有 vtkRenderer(白底),统一管理 actor 的加入与清除。
|
||||
// 不持有 RenderWindow(由 app 的 QVTK widget 承载,把 renderer() 加入其 RenderWindow)。
|
||||
class Scene {
|
||||
public:
|
||||
Scene();
|
||||
|
||||
vtkRenderer* renderer() const { return renderer_.Get(); }
|
||||
|
||||
void clear(); // 移除所有 view prop,支持重复切换数据集
|
||||
void addActor(vtkActor* a); // actor 由 renderer 引用计数保活
|
||||
|
||||
private:
|
||||
vtkSmartPointer<vtkRenderer> renderer_;
|
||||
};
|
||||
|
||||
} // namespace geopro::render
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
#include "actors/GridContourActor.hpp"
|
||||
|
||||
#include <vtkBandedPolyDataContourFilter.h>
|
||||
#include <vtkDataSetSurfaceFilter.h>
|
||||
#include <vtkDoubleArray.h>
|
||||
#include <vtkImageData.h>
|
||||
#include <vtkNew.h>
|
||||
#include <vtkPointData.h>
|
||||
#include <vtkPolyDataMapper.h>
|
||||
#include <vtkProperty.h>
|
||||
|
||||
#include "ColorLutBuilder.hpp"
|
||||
|
||||
namespace geopro::render {
|
||||
|
||||
namespace {
|
||||
|
||||
// banded contour 的色带级数(设计默认 20)。
|
||||
constexpr int kBandCount = 20;
|
||||
// LUT 级数。
|
||||
constexpr int kLutLevels = 256;
|
||||
|
||||
} // namespace
|
||||
|
||||
GridActors buildGridContour(const geopro::core::Grid& g, const geopro::core::ColorScale& cs)
|
||||
{
|
||||
const int nx = g.nx(), ny = g.ny();
|
||||
|
||||
// 退化网格:返回空 actor(调用方仍可安全 addActor,但 mapper 无输入则不绘制)。
|
||||
if (nx < 2 || ny < 2 || g.x.size() < 2 || g.y.size() < 2) {
|
||||
return GridActors{};
|
||||
}
|
||||
|
||||
vtkNew<vtkImageData> img;
|
||||
img->SetDimensions(nx, ny, 1);
|
||||
img->SetOrigin(g.x[0], g.y[0], 0.0);
|
||||
img->SetSpacing(g.x[1] - g.x[0], g.y[1] - g.y[0], 1.0);
|
||||
|
||||
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)
|
||||
sc->SetValue(static_cast<vtkIdType>(j) * nx + i, g.valueAt(i, j)); // i 最快
|
||||
img->GetPointData()->SetScalars(sc);
|
||||
|
||||
// vmin/vmax 来自 Grid;若退化(==)则用数据极值兜底,避免 LUT/contour 退化。
|
||||
double 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);
|
||||
|
||||
vtkNew<vtkDataSetSurfaceFilter> surf;
|
||||
surf->SetInputData(img);
|
||||
|
||||
vtkNew<vtkBandedPolyDataContourFilter> banded;
|
||||
banded->SetInputConnection(surf->GetOutputPort());
|
||||
banded->GenerateValues(kBandCount, vmin, vmax);
|
||||
banded->GenerateContourEdgesOn();
|
||||
banded->SetScalarModeToValue();
|
||||
|
||||
vtkNew<vtkPolyDataMapper> mapper;
|
||||
mapper->SetInputConnection(banded->GetOutputPort());
|
||||
mapper->SetScalarModeToUseCellData();
|
||||
mapper->SetLookupTable(lut);
|
||||
mapper->SetScalarRange(vmin, vmax);
|
||||
|
||||
auto bands = vtkSmartPointer<vtkActor>::New();
|
||||
bands->SetMapper(mapper);
|
||||
|
||||
vtkNew<vtkPolyDataMapper> edgeMapper;
|
||||
edgeMapper->SetInputConnection(banded->GetOutputPort(1)); // contour edges
|
||||
edgeMapper->ScalarVisibilityOff();
|
||||
|
||||
auto edges = vtkSmartPointer<vtkActor>::New();
|
||||
edges->SetMapper(edgeMapper);
|
||||
edges->GetProperty()->SetColor(0, 0, 0);
|
||||
edges->GetProperty()->SetLineWidth(0.6);
|
||||
|
||||
return GridActors{bands, edges};
|
||||
}
|
||||
|
||||
} // namespace geopro::render
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkActor.h>
|
||||
#include "model/Field.hpp"
|
||||
#include "model/ColorScale.hpp"
|
||||
namespace geopro::render {
|
||||
|
||||
// banded contour 的两个 actor:填充色带 + 黑色等值线。
|
||||
struct GridActors {
|
||||
vtkSmartPointer<vtkActor> bands;
|
||||
vtkSmartPointer<vtkActor> edges;
|
||||
};
|
||||
|
||||
// 把 core 网格 + 色阶渲染为 banded contour actor(不操作 renderer,由调用方加入场景)。
|
||||
GridActors buildGridContour(const geopro::core::Grid& g, const geopro::core::ColorScale& cs);
|
||||
|
||||
} // namespace geopro::render
|
||||
|
|
@ -55,4 +55,11 @@ if(WIN32)
|
|||
COMMAND_EXPAND_LISTS)
|
||||
endif()
|
||||
|
||||
# render 层:ColorLutBuilder(core ColorScale -> vtkLookupTable)。
|
||||
# 需 vtkLookupTable(VTK::CommonCore);geopro_render 已 PUBLIC 传递其余 VTK 组件。
|
||||
find_package(VTK REQUIRED COMPONENTS CommonCore)
|
||||
target_sources(geopro_tests PRIVATE render/test_color_lut.cpp)
|
||||
target_link_libraries(geopro_tests PRIVATE geopro_render ${VTK_LIBRARIES})
|
||||
vtk_module_autoinit(TARGETS geopro_tests MODULES ${VTK_LIBRARIES})
|
||||
|
||||
add_subdirectory(spike) # spike S3: banded contour 渲染验证
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include "ColorLutBuilder.hpp"
|
||||
#include "model/ColorScale.hpp"
|
||||
|
||||
using namespace geopro::core;
|
||||
|
||||
// buildLut 对阶梯色阶取下界:val=2.0 落在 [0,10) -> 蓝档,蓝分量应大于红分量。
|
||||
TEST(ColorLut, BuildsSteppedLutFromColorScale) {
|
||||
ColorScale cs;
|
||||
cs.addStop(0.0, Rgba{0, 0, 255, 255}); // 蓝
|
||||
cs.addStop(10.0, Rgba{255, 0, 0, 255}); // 红
|
||||
|
||||
auto lut = geopro::render::buildLut(cs, 0.0, 10.0, 256);
|
||||
ASSERT_NE(lut.GetPointer(), nullptr);
|
||||
|
||||
double rgb[3];
|
||||
lut->GetColor(2.0, rgb); // [0,10) -> 蓝
|
||||
|
||||
EXPECT_GT(rgb[2], rgb[0]); // 蓝 > 红
|
||||
EXPECT_GT(rgb[2], 0.5); // 接近纯蓝
|
||||
}
|
||||
Loading…
Reference in New Issue