#pragma once #include #include #include #include #include #include #include #include #include #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 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 setMapLinesZ(const std::vector& dsIds, double z) 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; // ── 视图导航基元(spec §3.1;T1)────────────────────────────────────────────── // 给定 dsIds 的已渲染 actor 世界包围盒并集;无有效返回 false,否则填 out=[xmin,xmax,…,zmax]。 bool datasetBounds(const std::vector& dsIds, double outB[6]) const; // 相机适配到指定包围盒,保持当前朝向(ResetCamera(b)),用于双击适配/贴合。 void fitToBounds(const double b[6]); // 绕 pivot 转到沿 dir 轴看向 pivot,保留当前 focal-to-camera 距离(缩放不变)。 void orbitToAxis(geopro::controller::ViewDir dir, const double pivot[3]); 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 onVolumeChanged; // frame 原点重锚(首个带经纬剖面到达)后回调,供底图等随之刷新到数据所在位置。 std::function onFrameReanchored; // 复位"已按数据重锚"标志:切换项目清场后调,使新项目首个数据重新触发重锚(→ onFrameReanchored // → 底图按新项目位置重显)。否则增量勾选不走 clear(),旧标志残留 → 不重锚 → 底图不再显示。 void resetFrameAnchor() { frameAnchoredToData_ = false; } // 相机程序化变化(取景/预设/缩放)后回调,供底图按新视锥重算覆盖(否则首帧部分瓦片要手动微动才出)。 std::function onCameraChanged; // ── B 方案#2:沿线位置巡航(雷达超长测线)────────────────────────────────────── // t∈[0,1] 沿数据【最长轴】定位;取景到该位置一段【窗口】(windowFrac=窗口占长轴比例), // 保持当前朝向(ResetCamera 只重定位+缩放、不转向)→ 像滚动读长 radargram。短轴满幅、长轴只取一段。 void focusAlongLongAxis(double t, double windowFrac); // 数据包围盒长短轴比(max/min 跨度)。用于判是否细长(雷达)→ 决定沿线滑块显隐。无数据返回 0。 double longAxisElongation() const; private: // 首个带经纬数据(剖面/足迹)到达时把共享 frame 重锚到其 lat/lon 包围盒中心:使数据落在世界原点近旁 // (否则样本默认原点可能离真实数据数百公里→图元在视锥外、移动视角也找不到)。已锚或无经纬则跳过。 void anchorFrameIfNeeded(const std::vector& lat, const std::vector& lon, int n); // 按当前坐标轴设置 + 场景包围盒重建坐标轴 prop(render 末尾调)。 void rebuildAxes(); void removeProps(std::vector>& props); // 从 renderer 移除并清空 // 仅数据图元(剖面/体素/地形/测线)的包围盒,不含底图 → 坐标轴/取景不被~公里级底图撑大。 bool computeDataBounds(double out[6]) const; public: // 当前所有数据图元(剖面等)合并范围的水平半径(米);无数据返回 0。供底图动态定最大范围。 double dataHorizontalRadius() const; private: geopro::render::Scene& scene_; vtkRenderWindow* renderWindow_; std::shared_ptr 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 可见性 + 自定义范围 // 当前坐标轴 prop:render 可能多次调用 rebuildAxes(rebuild 末尾 + 异步回灌), // 持引用以便重建前移除旧 prop,避免叠加(评审 HIGH)。 vtkSmartPointer currentAxes_; // 当前体素 image + 色阶(P3 切片附着源);无体素时为空。「当前」=最后添加/活动的体(保存切片/ // 创建异常的默认归属)。多体并发下各体 image 另存 volumes_。 vtkSmartPointer currentVolumeImage_; geopro::core::ColorScale currentColorScale_; double currentVmin_ = 0.0; double currentVmax_ = 0.0; // 多体并发:按 dsId 持各已渲染体的 image + 色阶(供 InteractionManager 让各体切片各用自己的 image)。 struct VolumeRec { vtkSmartPointer image; geopro::core::ColorScale cs; double vmin = 0.0, vmax = 0.0; vtkSmartPointer volume; // 体 actor(运行时调不透明度:改其 property 的不透明度传递函数) }; std::map volumes_; public: const std::map& 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>> dsProps_; std::vector> miscProps_; std::string volumeOwnerDs_; // 当前 currentVolumeImage_ 归属的 ds(其被移除时置空切片源) std::map> anomalyProps_; // 异常 id → 3D actor // 哪些 dsProps_ 条目是 2D 足迹(addMapLine):供足迹 actor 归属识别(Task E2/F2 用)。 std::set mapLineDs_; }; } // namespace geopro::app