geopro/src/controller/VtkSceneController.cpp

309 lines
14 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 "VtkSceneController.hpp"
#include <algorithm>
#include <set>
#include <utility>
#include <QPointer>
#include "I3dSceneView.hpp"
#include "repo/IDatasetRepository.hpp"
namespace geopro::controller {
namespace {
// 二维足迹「顶部/底部」摆放相对参考高程(Z=0)的偏移(米):控制器无地形/参考高程源
// (地形异步、帘面经纬未必到场),故退化为 Z=0 上/下固定偏移,使足迹不与帘面顶/底面重叠遮挡。
constexpr double kTopOffsetZ = 50.0; // 顶部:参考面上方
constexpr double kBottomOffsetZ = -50.0; // 底部:参考面下方
} // namespace
VtkSceneController::VtkSceneController(data::IDatasetRepository& dsRepo,
data::I3dSceneRepository& sceneRepo, I3dSceneView& view,
QObject* parent)
: QObject(parent), dsRepo_(dsRepo), sceneRepo_(sceneRepo), view_(view) {}
void VtkSceneController::setCheckedDatasets(const QStringList& dsIds) {
std::vector<std::string> newDs;
newDs.reserve(static_cast<std::size_t>(dsIds.size()));
for (const QString& id : dsIds) newDs.push_back(id.toStdString());
// 2D 俯视测线:保持全量重建(测线非按 ds 跟踪移除)。
if (mode_ == ViewMode::Map2D) {
checkedDs_ = std::move(newDs);
rebuildInternal();
return;
}
// 3D增量 diff —— 只处理新增/移除,不全量重建(底图、其余 ds、相机均不动
const std::set<std::string> oldSet(checkedDs_.begin(), checkedDs_.end());
const std::set<std::string> newSet(newDs.begin(), newDs.end());
const bool wasEmpty = checkedDs_.empty();
for (const auto& id : checkedDs_)
if (!newSet.count(id)) view_.removeDataset(id); // 移除:旧有新无 → 仅删该 ds 图元
checkedDs_ = std::move(newDs);
fitOnArrival_ = wasEmpty; // 仅从空开始时让到场数据自动取景;增量追加保持当前相机不跳
const unsigned long long gen = rebuildGeneration_; // 不自增:并发增量互不作废
for (const auto& id : checkedDs_)
if (!oldSet.count(id)) addDatasetAsync(id, gen); // 新增:新有旧无 → 异步取数增量入场
view_.renderIncremental(); // 立即反映移除 / 触发坐标轴重算
}
void VtkSceneController::setChecked2DDatasets(const QStringList& dsIds) {
std::vector<std::string> newDs;
newDs.reserve(static_cast<std::size_t>(dsIds.size()));
for (const QString& id : dsIds) newDs.push_back(id.toStdString());
// 二维足迹始终画进 View3D 场景,且按 dsId 跟踪 → 一律增量 diff不全量重建不打断 3D 帘面/体)。
const std::set<std::string> oldSet(checked2dDs_.begin(), checked2dDs_.end());
const std::set<std::string> newSet(newDs.begin(), newDs.end());
// 此前空场景(无 3D 数据且无 2D 足迹) → 首批足迹到场自动取景;否则增量追加保持相机不跳。
const bool wasEmpty = checkedDs_.empty() && checked2dDs_.empty();
for (const auto& id : checked2dDs_)
if (!newSet.count(id)) view_.removeDataset(id); // 取消勾选 → 移除该足迹图元
checked2dDs_ = std::move(newDs);
fitOnArrival_ = wasEmpty; // 首批足迹(空场景)取景;否则保持当前相机不跳
// 足迹画进 View3D 场景mode=0 关闭 → 仅记录勾选不渲染(见 set2DPlacement 切回时补画)。
if (placement2dMode_ != 0 && mode_ == ViewMode::View3D) {
const unsigned long long gen = rebuildGeneration_; // 不自增:与 3D 增量互不作废
for (const auto& id : checked2dDs_)
if (!oldSet.count(id)) add2DDatasetAsync(id, gen); // 新增 → 异步取足迹增量入场
}
view_.renderIncremental(); // 立即反映移除
}
void VtkSceneController::set2DPlacement(int mode, double customZ) {
const bool changed = (mode != placement2dMode_) || (mode == 4 && customZ != customZ2d_);
placement2dMode_ = mode;
customZ2d_ = customZ;
if (!changed || checked2dDs_.empty()) return;
// 摆放变化 → 对已勾选足迹重摆:先全部移除,再按新 Z 重加mode=0 关闭则只移除不重加)。
for (const auto& id : checked2dDs_) view_.removeDataset(id);
if (placement2dMode_ != 0 && mode_ == ViewMode::View3D) {
const unsigned long long gen = rebuildGeneration_;
fitOnArrival_ = false; // 重摆:保持相机
for (const auto& id : checked2dDs_) add2DDatasetAsync(id, gen);
}
view_.renderIncremental();
}
double VtkSceneController::placementZ() const {
const double surf = view_.zRefElev(); // 真实地表高程基准(测线地表高程)
switch (placement2dMode_) {
case 1: return 0.0; // Z=0世界原点
case 2: return surf + kTopOffsetZ; // 顶部:贴真实地表上方
case 3: return surf + kBottomOffsetZ; // 底部:真实地表下方
case 4: return customZ2d_; // 自定义
default: return 0.0; // 关闭(0) 不应走到此(调用方拦截)
}
}
void VtkSceneController::add2DDatasetAsync(const std::string& dsId, unsigned long long gen) {
if (loadingDs_.count(dsId)) return; // 已在加载(重复勾选竞态)→ 不重复请求
loadingDs_.insert(dsId);
QPointer<VtkSceneController> self(this);
sceneRepo_.loadMapLine(
dsId,
[self, gen, dsId](data::MapLine line) {
if (!self) return;
self->loadingDs_.erase(dsId);
// gen 作废 / 已取消勾选 / 摆放已关闭 → 丢弃迟到回调。
if (gen != self->rebuildGeneration_ || !self->is2DChecked(dsId) ||
self->placement2dMode_ == 0) {
return;
}
// 落地时按当前摆放 Z非请求时快照→ 加载期间摆放变化也取最新高程。
self->view_.addMapLine(dsId, line, self->placementZ());
self->onDatasetArrived();
},
[self, gen, dsId](const std::string& m) {
if (!self) return;
self->loadingDs_.erase(dsId);
if (gen != self->rebuildGeneration_) return;
emit self->loadFailed(QString::fromStdString(m));
});
}
void VtkSceneController::addDatasetAsync(const std::string& dsId, unsigned long long gen) {
if (loadingDs_.count(dsId)) return; // 已在加载(重复勾选竞态)→ 不重复请求
QPointer<VtkSceneController> self(this);
// 按数据集类型分流(取代旧全局 showCurtain_/showVoxel_ 开关):
// 三维体dd_voxel客户端创建→ 体素渲染其余剖面dd_section 等)→ 帘面渲染。
if (sceneRepo_.isVolumeDataset(dsId)) {
auto cachedGrid = volumeCache_.find(dsId);
auto cachedScale = volumeScaleCache_.find(dsId);
if (cachedGrid != volumeCache_.end() && cachedScale != volumeScaleCache_.end()) {
view_.addVolume(dsId, cachedGrid->second, cachedScale->second); // 缓存命中(色阶随体缓存)
onDatasetArrived();
return;
}
loadingDs_.insert(dsId);
sceneRepo_.loadVolume(
dsId,
[self, gen, dsId](data::VolumeGrid g, core::ColorScale cs) {
if (!self) return;
self->loadingDs_.erase(dsId);
if (gen != self->rebuildGeneration_ || !self->isChecked(dsId)) return;
self->volumeScaleCache_[dsId] = cs; // 色阶随体一起缓存mock 体在 dsRepo_ 无条目)
auto it = self->volumeCache_.emplace(dsId, std::move(g)).first;
self->view_.addVolume(dsId, it->second, self->volumeScaleCache_[dsId]);
self->onDatasetArrived();
},
[self, gen, dsId](const std::string& m) {
if (!self) return;
self->loadingDs_.erase(dsId);
if (gen != self->rebuildGeneration_) return;
emit self->loadFailed(QString::fromStdString(m));
});
return;
}
// 剖面 → 帘面(着色用 loadSection 返回的 s.scale与体的源色阶同源
loadingDs_.insert(dsId);
sceneRepo_.loadSection(
dsId,
[self, gen, dsId](data::SectionData s) {
if (!self) return;
self->loadingDs_.erase(dsId);
if (gen != self->rebuildGeneration_ || !self->isChecked(dsId)) return; // 作废/已取消
self->view_.addCurtain(dsId, s.grid, s.scale);
self->onDatasetArrived();
},
[self, gen, dsId](const std::string& m) {
if (!self) return;
self->loadingDs_.erase(dsId);
if (gen != self->rebuildGeneration_) return;
emit self->loadFailed(QString::fromStdString(m));
});
}
void VtkSceneController::onDatasetArrived() {
view_.renderIncremental();
if (fitOnArrival_) view_.fitView(); // 全量重建/首批数据 → 自动取景;增量追加保持相机
}
bool VtkSceneController::isChecked(const std::string& dsId) const {
return std::find(checkedDs_.begin(), checkedDs_.end(), dsId) != checkedDs_.end();
}
bool VtkSceneController::is2DChecked(const std::string& dsId) const {
return std::find(checked2dDs_.begin(), checked2dDs_.end(), dsId) != checked2dDs_.end();
}
void VtkSceneController::setViewMode(ViewMode mode) {
mode_ = mode;
rebuildInternal();
}
void VtkSceneController::setLayer(SceneLayer layer, bool on) {
switch (layer) {
case SceneLayer::Curtain: showCurtain_ = on; break;
case SceneLayer::Voxel: showVoxel_ = on; break;
case SceneLayer::Terrain: showTerrain_ = on; break;
}
rebuildInternal();
}
void VtkSceneController::setVerticalExaggeration(double ve) {
verticalExaggeration_ = ve;
rebuildInternal();
}
void VtkSceneController::rebuild() { rebuildInternal(); }
void VtkSceneController::setVolumeColorScale(const std::string& dsId,
const geopro::core::ColorScale& cs) {
volumeScaleCache_[dsId] = cs; // 会话级 mock 持久(再勾选命中缓存,见 addDatasetAsync
if (!isChecked(dsId)) return; // 未渲染 → 仅更缓存,下次勾选生效
auto git = volumeCache_.find(dsId);
if (git == volumeCache_.end()) return; // 体网格尚未到场 → 同上
// 移除旧体素 → 以新色阶重建addVolume 内部置 currentColorScale_ 并触发 onVolumeChanged
// InteractionManager 据此以新色阶重建该体下已勾选切片。
view_.removeDataset(dsId);
view_.addVolume(dsId, git->second, cs);
view_.renderIncremental();
}
void VtkSceneController::setAxesMode(AxesMode mode) {
axesMode_ = mode;
rebuildInternal(); // 坐标轴随场景重建clear 会移除旧坐标轴 prop
}
void VtkSceneController::setAxesUnit(AxesUnit unit) {
axesUnit_ = unit;
rebuildInternal();
}
// 快捷视图 / 缩放:仅改相机,不重建场景(无须取数/重装图元)。
void VtkSceneController::applyView(ViewDir dir) { view_.applyCameraView(dir); }
void VtkSceneController::zoomIn() { view_.zoom(1.2); }
void VtkSceneController::zoomOut() { view_.zoom(1.0 / 1.2); }
void VtkSceneController::fit() { view_.fitView(); }
const geopro::core::Grid& VtkSceneController::grid(const std::string& dsId) {
auto it = gridCache_.find(dsId);
if (it == gridCache_.end()) it = gridCache_.emplace(dsId, dsRepo_.loadGrid(dsId)).first;
return it->second;
}
const geopro::core::ColorScale& VtkSceneController::colorScale(const std::string& dsId) {
auto it = colorScaleCache_.find(dsId);
if (it == colorScaleCache_.end())
it = colorScaleCache_.emplace(dsId, dsRepo_.loadColorScale(dsId)).first;
return it->second;
}
void VtkSceneController::rebuildInternal() {
const unsigned long long gen = ++rebuildGeneration_; // 自增:作废此前所有在途增量回调
const bool is2D = (mode_ == ViewMode::Map2D);
view_.clear(); // 移除全部数据图元(保留底图)frame 重锚标志复位
loadingDs_.clear(); // 旧在途加载随之作废(回调按 gen 丢弃)
view_.setVerticalExaggeration(verticalExaggeration_);
// 坐标轴设置在 clear 后下发render 末尾据当前场景包围盒重建坐标轴 prop。
view_.setAxes(axesMode_, axesUnit_, kAxesFontSize);
fitOnArrival_ = true; // 全量重建:到场数据自动取景
// 坏 dsIdloadGrid/loadColorScale 抛异常)= best-effort 跳过emit loadFailed 但不中断。
try {
if (is2D) {
for (const auto& dsId : checkedDs_) view_.addSurveyLine(grid(dsId));
} else {
// 回调用 QPointer<self> 守对象存活 + gen 守数据新鲜:迟到回调若已析构/作废则丢弃。
QPointer<VtkSceneController> self(this);
if (showTerrain_) {
sceneRepo_.loadTerrainPaths(
[self, gen](data::TerrainPaths p) {
if (!self || gen != self->rebuildGeneration_) return; // 已析构/迟到:丢弃
self->view_.addTerrain(std::move(p));
self->onDatasetArrived();
},
[self, gen](const std::string& m) {
if (!self || gen != self->rebuildGeneration_) return;
emit self->loadFailed(QString::fromStdString(m));
});
}
for (const auto& dsId : checkedDs_) addDatasetAsync(dsId, gen);
// 二维足迹随全量重建一并重画clear 已移除其图元mode=0 关闭则跳过。
if (placement2dMode_ != 0)
for (const auto& dsId : checked2dDs_) add2DDatasetAsync(dsId, gen);
}
} catch (const std::exception& e) {
emit loadFailed(QString::fromStdString(e.what()));
}
view_.render(is2D); // 设背景/相机预设/坐标轴 + ResetCamera数据到场再由 onDatasetArrived 取景)
}
} // namespace geopro::controller