191 lines
7.7 KiB
C++
191 lines
7.7 KiB
C++
#include "VtkSceneController.hpp"
|
||
|
||
#include <algorithm>
|
||
#include <set>
|
||
#include <utility>
|
||
|
||
#include <QPointer>
|
||
|
||
#include "I3dSceneView.hpp"
|
||
#include "repo/IDatasetRepository.hpp"
|
||
|
||
namespace geopro::controller {
|
||
|
||
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::addDatasetAsync(const std::string& dsId, unsigned long long gen) {
|
||
if (loadingDs_.count(dsId)) return; // 已在加载(重复勾选竞态)→ 不重复请求
|
||
QPointer<VtkSceneController> self(this);
|
||
if (showCurtain_) {
|
||
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));
|
||
});
|
||
}
|
||
if (showVoxel_) {
|
||
auto cached = volumeCache_.find(dsId);
|
||
if (cached != volumeCache_.end()) {
|
||
view_.addVolume(dsId, cached->second, colorScale(dsId));
|
||
onDatasetArrived();
|
||
} else {
|
||
sceneRepo_.loadVolume(
|
||
dsId,
|
||
[self, gen, dsId](data::VolumeGrid g) {
|
||
if (!self || gen != self->rebuildGeneration_ || !self->isChecked(dsId)) return;
|
||
auto it = self->volumeCache_.emplace(dsId, std::move(g)).first;
|
||
self->view_.addVolume(dsId, it->second, self->colorScale(dsId));
|
||
self->onDatasetArrived();
|
||
},
|
||
[self, gen](const std::string& m) {
|
||
if (!self || 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();
|
||
}
|
||
|
||
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::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; // 全量重建:到场数据自动取景
|
||
|
||
// 坏 dsId(loadGrid/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);
|
||
}
|
||
} catch (const std::exception& e) {
|
||
emit loadFailed(QString::fromStdString(e.what()));
|
||
}
|
||
|
||
view_.render(is2D); // 设背景/相机预设/坐标轴 + ResetCamera(数据到场再由 onDatasetArrived 取景)
|
||
}
|
||
|
||
} // namespace geopro::controller
|