geopro/src/app/VtkSceneView.cpp

186 lines
7.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "VtkSceneView.hpp"
#include <algorithm>
#include <memory>
#include <utility>
#include <vtkActor.h>
#include <vtkCubeAxesActor.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkVolume.h>
#include "CameraPreset.hpp"
#include "Scene.hpp"
#include "Theme.hpp"
#include "actors/AxesActor.hpp"
#include "actors/CurtainActor.hpp"
#include "actors/MapLineActor.hpp"
#include "actors/TerrainActor.hpp"
#include "actors/VoxelActor.hpp"
#include "geo/GeoLocalFrame.hpp"
namespace geopro::app {
namespace {
// 控制器层枚举 → render 层枚举(保持控制器不依赖 render
geopro::render::AxesMode toRenderMode(geopro::controller::AxesMode m) {
switch (m) {
case geopro::controller::AxesMode::Standard: return geopro::render::AxesMode::Standard;
case geopro::controller::AxesMode::Stereo: return geopro::render::AxesMode::Stereo;
case geopro::controller::AxesMode::None: return geopro::render::AxesMode::None;
}
return geopro::render::AxesMode::Standard;
}
geopro::render::AxesUnit toRenderUnit(geopro::controller::AxesUnit u) {
switch (u) {
case geopro::controller::AxesUnit::None: return geopro::render::AxesUnit::None;
case geopro::controller::AxesUnit::Meter: return geopro::render::AxesUnit::Meter;
case geopro::controller::AxesUnit::Feet: return geopro::render::AxesUnit::Feet;
case geopro::controller::AxesUnit::LatLon: return geopro::render::AxesUnit::LatLon;
}
return geopro::render::AxesUnit::Meter;
}
geopro::render::ViewDir toRenderViewDir(geopro::controller::ViewDir d) {
switch (d) {
case geopro::controller::ViewDir::Front: return geopro::render::ViewDir::Front;
case geopro::controller::ViewDir::Back: return geopro::render::ViewDir::Back;
case geopro::controller::ViewDir::Left: return geopro::render::ViewDir::Left;
case geopro::controller::ViewDir::Right: return geopro::render::ViewDir::Right;
case geopro::controller::ViewDir::Top: return geopro::render::ViewDir::Top;
case geopro::controller::ViewDir::Bottom: return geopro::render::ViewDir::Bottom;
}
return geopro::render::ViewDir::Front;
}
} // namespace
VtkSceneView::VtkSceneView(geopro::render::Scene& scene, vtkRenderWindow* renderWindow,
std::shared_ptr<geopro::core::GeoLocalFrame> frame, double zRefElev)
: scene_(scene),
renderWindow_(renderWindow),
frame_(std::move(frame)),
zRefElev_(zRefElev) {}
void VtkSceneView::clear() {
scene_.clear(); // RemoveAllViewProps连同坐标轴一并移除
currentAxes_ = nullptr; // 旧坐标轴已随 clear 移除,置空避免悬留引用
// 体素 image 失效:置空并通知上层关闭切片(防切片附着到已移除的 image
currentVolumeImage_ = nullptr;
frameAnchoredToData_ = false; // 新一轮选择重新按其首个真实剖面重锚原点
if (onVolumeChanged) onVolumeChanged();
}
void VtkSceneView::setVerticalExaggeration(double ve) { verticalExaggeration_ = ve; }
void VtkSceneView::addSurveyLine(const geopro::core::Grid& grid) {
auto line = geopro::render::buildSurveyLine(grid, *frame_);
if (line) scene_.addActor(line);
}
void VtkSceneView::addCurtain(const geopro::core::Grid& grid, const geopro::core::ColorScale& cs) {
// 首个带经纬度的剖面到达 → 把 GeoLocalFrame 原点重锚到该剖面 lat/lon 中心:使局部坐标从 0 附近起
// (轴刻度有意义),同一选择内多条剖面共用此原点 → 相互地理配准。无经纬剖面是平面、不受原点影响。
const int nx = grid.nx();
if (!frameAnchoredToData_ && nx > 0 && static_cast<int>(grid.lat.size()) >= nx &&
static_cast<int>(grid.lon.size()) >= nx) {
double la0 = grid.lat[0], la1 = grid.lat[0], lo0 = grid.lon[0], lo1 = grid.lon[0];
for (int i = 1; i < nx; ++i) {
la0 = std::min(la0, grid.lat[i]); la1 = std::max(la1, grid.lat[i]);
lo0 = std::min(lo0, grid.lon[i]); lo1 = std::max(lo1, grid.lon[i]);
}
frame_ = std::make_shared<geopro::core::GeoLocalFrame>((la0 + la1) / 2.0, (lo0 + lo1) / 2.0);
frameAnchoredToData_ = true;
}
auto curtain = geopro::render::buildCurtain(grid, cs, *frame_);
if (curtain) {
curtain->SetScale(1.0, 1.0, verticalExaggeration_); // 纵向夸张成墙
scene_.addActor(curtain);
}
}
void VtkSceneView::addVolume(const geopro::data::VolumeGrid& vol, const geopro::core::ColorScale& cs) {
// 纵向夸张烤进 image 的 z 原点/间距(与帘面 SetScale 同倍,保证纵向一致)。
// 用暴露 image 的 buildVoxel 重载:保留 currentVolumeImage_ 供 P3 切片附着(几何含 VE
vtkSmartPointer<vtkImageData> image;
auto volume = geopro::render::buildVoxel(
vol.vol, cs, vol.origin[0], vol.origin[1], vol.origin[2] * verticalExaggeration_,
vol.spacing[0], vol.spacing[1], vol.spacing[2] * verticalExaggeration_, vol.vmin, vol.vmax,
image);
if (volume) {
scene_.addViewProp(volume);
currentVolumeImage_ = image;
currentColorScale_ = cs;
currentVmin_ = vol.vmin;
currentVmax_ = vol.vmax;
if (onVolumeChanged) onVolumeChanged();
}
}
void VtkSceneView::addTerrain(const geopro::data::TerrainPaths& paths) {
auto terrain = geopro::render::buildTerrain(paths.demPath, paths.imagePath, *frame_, zRefElev_,
verticalExaggeration_);
if (terrain) scene_.addActor(terrain);
}
void VtkSceneView::setAxes(geopro::controller::AxesMode mode, geopro::controller::AxesUnit unit,
int fontSize) {
axesMode_ = mode;
axesUnit_ = unit;
axesFontSize_ = fontSize;
}
void VtkSceneView::applyCameraView(geopro::controller::ViewDir dir) {
geopro::render::applyView(scene_.renderer(), toRenderViewDir(dir));
if (renderWindow_) renderWindow_->Render();
}
void VtkSceneView::zoom(double factor) {
geopro::render::zoomBy(scene_.renderer(), factor);
if (renderWindow_) renderWindow_->Render();
}
void VtkSceneView::fitView() {
geopro::render::fitView(scene_.renderer());
if (renderWindow_) renderWindow_->Render();
}
void VtkSceneView::rebuildAxes() {
// 先移除上一次的坐标轴 proprender 可能在一次 rebuild 内多次调用(末尾统一 render +
// 异步回灌 render不先移除会叠加坐标轴评审 HIGH。移除后再算 bounds仅数据图元
if (currentAxes_) {
scene_.renderer()->RemoveViewProp(currentAxes_);
currentAxes_ = nullptr;
}
// 坐标轴随数据包围盒重建:按已加入的数据图元算 bounds再造 vtkCubeAxesActor 入场。
// None 模式或无内容 → buildAxes 返回 nullptr场景无坐标轴。
double bounds[6];
scene_.renderer()->ComputeVisiblePropBounds(bounds);
geopro::render::AxesOptions opts;
opts.mode = toRenderMode(axesMode_);
opts.unit = toRenderUnit(axesUnit_);
opts.fontSize = axesFontSize_;
opts.frame = frame_.get();
auto axes = geopro::render::buildAxes(bounds, opts, scene_.renderer());
if (axes) {
scene_.addViewProp(axes);
currentAxes_ = axes;
}
}
void VtkSceneView::render(bool is2D) {
// 视图区背景永远深色(规范 §0.5:不随明暗切换),让色阶数据更突出。
double bgR, bgG, bgB;
geopro::app::vtkBackground(bgR, bgG, bgB);
scene_.renderer()->SetBackground(bgR, bgG, bgB);
// 坐标轴仅三维视图显示2D 俯视测线不需要立体坐标轴)。
if (!is2D) rebuildAxes();
if (is2D)
geopro::render::applyTop2D(scene_.renderer());
else
geopro::render::applyFree3D(scene_.renderer());
scene_.renderer()->ResetCamera();
if (renderWindow_) renderWindow_->Render();
}
} // namespace geopro::app