geopro/src/app/VtkSceneView.hpp

191 lines
11 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.

#pragma once
#include <functional>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include <vtkCubeAxesActor.h>
#include <vtkImageData.h>
#include <vtkSmartPointer.h>
#include "I3dSceneView.hpp"
#include "model/ColorScale.hpp"
namespace geopro::core { class GeoLocalFrame; }
namespace geopro::render { class Scene; }
class vtkRenderer;
class vtkRenderWindow;
class vtkProp;
class vtkActor;
class vtkVolume;
namespace geopro::app {
// I3dSceneView 的真实实现:把编排层的"加图元"指令翻译为 render actor + Scene 调用。
// 持有 Scene / renderer / renderWindow非拥有+ 共享 GeoLocalFrame多视图空间配准
// 纵向夸张统一作用:帘面/地形 actor SetScale(1,1,VE),体素 z 原点/间距烤入 VE。
// render 层零业务actor 只吃 core::*,本类负责装配。
class VtkSceneView : public geopro::controller::I3dSceneView {
public:
// 入参生命周期须覆盖本对象由调用方保证。zRefElev地形 z 基准(测线地表高程)。
VtkSceneView(geopro::render::Scene& scene, vtkRenderWindow* renderWindow,
std::shared_ptr<geopro::core::GeoLocalFrame> frame, double zRefElev);
void clear() override;
void setVerticalExaggeration(double ve) override;
void setVolumeOpacity(double maxOpacity) override; // 运行时调已渲染体 + 后续新体的最大不透明度
double zRefElev() const override { return zRefElev_; }
void addSurveyLine(const geopro::core::Grid& grid) override;
void addCurtain(const std::string& dsId, const geopro::core::Grid& grid,
const geopro::core::ColorScale& cs) override;
void addVolume(const std::string& dsId, const geopro::data::VolumeGrid& vol,
const geopro::core::ColorScale& cs) override;
bool updateVolumeColorInPlace(const std::string& dsId,
const geopro::core::ColorScale& cs) override;
void addMapLine(const std::string& dsId, const geopro::data::MapLine& line,
double worldZ) override;
void addTerrain(const geopro::data::TerrainPaths& paths) override;
void removeDataset(const std::string& dsId) override;
void addAnomaly(const geopro::core::Anomaly& a) override;
void removeAnomaly(const std::string& anomalyId) override;
void clearAnomalies() override;
void setAnomalyVisible(const std::string& anomalyId, bool visible) override;
void setSelectedAnomaly(const std::string& anomalyId) override;
void setAxes(geopro::controller::AxesMode mode, geopro::controller::AxesUnit unit,
int fontSize) override;
void setAxesRanges(const geopro::controller::AxisRangeCfg& x,
const geopro::controller::AxisRangeCfg& y,
const geopro::controller::AxisRangeCfg& z) override;
void applyCameraView(geopro::controller::ViewDir dir) override;
void zoom(double factor) override;
void fitView() override;
void render(bool is2D, bool resetCamera = true) override;
void renderIncremental() override;
// ── P3 切片交互:暴露当前体素 image含 VE 烤入的 origin/spacing供切片附着 ──
// addVolume 用暴露 image 的 buildVoxel 重载保留clear/无体素时置空。
vtkImageData* currentVolumeImage() const { return currentVolumeImage_.Get(); }
const geopro::core::ColorScale& currentColorScale() const { return currentColorScale_; }
double currentVmin() const { return currentVmin_; }
double currentVmax() const { return currentVmax_; }
bool hasVolume() const { return currentVolumeImage_ != nullptr; }
const std::string& currentVolumeDsId() const { return volumeOwnerDs_; } // 当前体归属 ds保存切片用
// 体素 image 变化addVolume 附着新 image / clear 置空)时回调,供上层把新 image 推给
// InteractionManager重附着或关闭切片。clear 时以 nullptr 触发。
std::function<void()> onVolumeChanged;
// frame 原点重锚(首个带经纬剖面到达)后回调,供底图等随之刷新到数据所在位置。
std::function<void()> onFrameReanchored;
// 复位"已按数据重锚"标志:切换项目清场后调,使新项目首个数据重新触发重锚(→ onFrameReanchored
// → 底图按新项目位置重显)。否则增量勾选不走 clear(),旧标志残留 → 不重锚 → 底图不再显示。
void resetFrameAnchor() { frameAnchoredToData_ = false; }
// 相机程序化变化(取景/预设/缩放)后回调,供底图按新视锥重算覆盖(否则首帧部分瓦片要手动微动才出)。
std::function<void()> onCameraChanged;
// ── 二维分析改造 A 期:一场景两相机 ──────────────────────────────────────────
// 切「二维分析」(is2D=true):相机锁近俯视、显 2D 足迹/隐 3D(体/帘面/异常);切回反之。
// 只翻 actor 可见标志(不清空、不重建)→ 切换瞬时、零重插值。地形/底图常驻不动。
// 切片显隐 + 交互锁由 InteractionManager::setMode2D 配合(上层在同一处调两者)。
void setAnalysisMode2D(bool is2D);
bool isAnalysisMode2D() const { return analysisMode2D_; }
// ── 二维分析改造 B 期:选中 2D 足迹沿高程 Z 拖动 ───────────────────────────────
// 仅二维分析下用。pickMapLineAt在屏幕(x,y)拾取足迹(只考虑可见足迹,不被地形/底图干扰);命中则
// 选中(additive=Ctrl 多选切换,否则单选替换)并高亮,返回是否有选中(交互样式据此决定 Z 拖动/平移)。
// nudgeSelectedMapLinesZ选中足迹世界 Z += worldDz(锁 XY);偏移按 dsId 持久(切走再回/全量重建保留)。
// selectedMapLineZ代表性当前世界 Z(高程读数浮层用);无选中返回 0。
bool pickMapLineAt(int screenX, int screenY, bool additive);
void clearMapLineSelection();
bool hasMapLineSelection() const { return !selectedMapLines_.empty(); }
void nudgeSelectedMapLinesZ(double worldDz);
double selectedMapLineZ() const;
// 双向选择联动列表↔VTK。selectedMapLines 取当前选中 dsIdsetSelectedMapLines 由列表设置选中
// (高亮,不回调,避免环)。VTK 内拾取改变选中时触发 onMapLineSelectionChanged → 上层同步列表。
std::vector<std::string> selectedMapLines() const;
void setSelectedMapLines(const std::vector<std::string>& dsIds);
std::function<void()> onMapLineSelectionChanged;
private:
// 首个带经纬数据(剖面/足迹)到达时把共享 frame 重锚到其 lat/lon 包围盒中心:使数据落在世界原点近旁
// (否则样本默认原点可能离真实数据数百公里→图元在视锥外、移动视角也找不到)。已锚或无经纬则跳过。
void anchorFrameIfNeeded(const std::vector<double>& lat, const std::vector<double>& lon, int n);
// 按当前坐标轴设置 + 场景包围盒重建坐标轴 proprender 末尾调)。
void rebuildAxes();
void removeProps(std::vector<vtkSmartPointer<vtkProp>>& props); // 从 renderer 移除并清空
// 仅数据图元(剖面/体素/地形/测线)的包围盒,不含底图 → 坐标轴/取景不被~公里级底图撑大。
bool computeDataBounds(double out[6]) const;
public:
// 当前所有数据图元(剖面等)合并范围的水平半径(米);无数据返回 0。供底图动态定最大范围。
double dataHorizontalRadius() const;
private:
geopro::render::Scene& scene_;
vtkRenderWindow* renderWindow_;
std::shared_ptr<geopro::core::GeoLocalFrame> frame_;
double zRefElev_;
double verticalExaggeration_ = 1.0;
// 是否已按真实剖面 lat/lon 重锚 frame 原点(每次 clear 重置):默认原点取自样本、可能离真实数据
// 很远→局部坐标巨大、轴刻度无意义;首个带经纬剖面到达时重锚到其中心,同选择内多剖面共用配准。
bool frameAnchoredToData_ = false;
// 坐标轴设置P2默认标准 + 米。
geopro::controller::AxesMode axesMode_ = geopro::controller::AxesMode::Standard;
geopro::controller::AxesUnit axesUnit_ = geopro::controller::AxesUnit::Meter;
int axesFontSize_ = 12;
geopro::controller::AxisRangeCfg axisX_, axisY_, axisZ_; // per-axis 可见性 + 自定义范围
// 当前坐标轴 proprender 可能多次调用 rebuildAxesrebuild 末尾 + 异步回灌),
// 持引用以便重建前移除旧 prop避免叠加评审 HIGH
vtkSmartPointer<vtkCubeAxesActor> currentAxes_;
// 当前体素 image + 色阶P3 切片附着源);无体素时为空。「当前」=最后添加/活动的体(保存切片/
// 创建异常的默认归属)。多体并发下各体 image 另存 volumes_。
vtkSmartPointer<vtkImageData> currentVolumeImage_;
geopro::core::ColorScale currentColorScale_;
double currentVmin_ = 0.0;
double currentVmax_ = 0.0;
// 多体并发:按 dsId 持各已渲染体的 image + 色阶(供 InteractionManager 让各体切片各用自己的 image
struct VolumeRec {
vtkSmartPointer<vtkImageData> image;
geopro::core::ColorScale cs;
double vmin = 0.0, vmax = 0.0;
vtkSmartPointer<vtkVolume> volume; // 体 actor运行时调不透明度改其 property 的不透明度传递函数)
};
std::map<std::string, VolumeRec> volumes_;
public:
const std::map<std::string, VolumeRec>& volumes() const { return volumes_; } // 已渲染各体 image/色阶
bool isVolumeRendered(const std::string& dsId) const { return volumes_.count(dsId) > 0; }
const VolumeRec* volume(const std::string& dsId) const { // 取指定已渲染体的 image/色阶(缺=nullptr
auto it = volumes_.find(dsId);
return it != volumes_.end() ? &it->second : nullptr;
}
private:
// 增量渲染:按 dsId 跟踪该数据集的 props帘面/体素),支持单独移除而不全量重建;
// miscProps_ 为非数据集 prop地形/测线),仅随 clear 全量移除。底图由 TileBasemap 自管、不在此。
std::map<std::string, std::vector<vtkSmartPointer<vtkProp>>> dsProps_;
std::vector<vtkSmartPointer<vtkProp>> miscProps_;
std::string volumeOwnerDs_; // 当前 currentVolumeImage_ 归属的 ds其被移除时置空切片源
std::map<std::string, vtkSmartPointer<vtkActor>> anomalyProps_; // 异常 id → 3D actor
// ── 二维分析改造 A 期 ──
// 哪些 dsProps_ 条目是 2D 足迹(addMapLine):切 tab 按此区分维度翻可见(其余 dsProps_=帘面/体=3D)。
std::set<std::string> mapLineDs_;
bool analysisMode2D_ = false; // 当前是否处二维分析(默认三维启动在「三维分析」tab)
// B 期:选中的足迹 dsId(Z 拖动目标) + 各足迹累计 Z 偏移(持久,全量重建后 addMapLine 复用)。
std::set<std::string> selectedMapLines_;
std::map<std::string, double> mapLineZOffset_;
void applyMapLineSelectionVisual(); // 选中足迹加粗变亮、其余复原(橙 3.0)
};
} // namespace geopro::app