feat/vtk-3d-view #7
107
src/app/main.cpp
107
src/app/main.cpp
|
|
@ -90,6 +90,7 @@
|
||||||
#include "ApiClient.hpp"
|
#include "ApiClient.hpp"
|
||||||
#include "AuthService.hpp"
|
#include "AuthService.hpp"
|
||||||
#include "DatasetDimension.hpp"
|
#include "DatasetDimension.hpp"
|
||||||
|
#include "DatasetCategory.hpp"
|
||||||
#include "Credential.hpp"
|
#include "Credential.hpp"
|
||||||
#include "Glyphs.hpp"
|
#include "Glyphs.hpp"
|
||||||
#include "Logging.hpp"
|
#include "Logging.hpp"
|
||||||
|
|
@ -135,6 +136,7 @@
|
||||||
#include "panels/ObjectExceptionPanel.hpp"
|
#include "panels/ObjectExceptionPanel.hpp"
|
||||||
#include "TileBasemap.hpp"
|
#include "TileBasemap.hpp"
|
||||||
#include "panels/columns/ColumnDrawer.hpp"
|
#include "panels/columns/ColumnDrawer.hpp"
|
||||||
|
#include "panels/columns/CategoryAnalysisTab.hpp"
|
||||||
#include "panels/columns/Column3DDataset.hpp"
|
#include "panels/columns/Column3DDataset.hpp"
|
||||||
#include "panels/columns/Column2DDataset.hpp"
|
#include "panels/columns/Column2DDataset.hpp"
|
||||||
#include "panels/columns/Column3DAnalysis.hpp"
|
#include "panels/columns/Column3DAnalysis.hpp"
|
||||||
|
|
@ -444,12 +446,15 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
// 三维分析栏 = 后端 Analysis 行(dd_slice) + 客户端创建的三维体(mock)。生成的三维体是"分析产物"
|
// 三维分析栏 = 后端 Analysis 行(dd_slice) + 客户端创建的三维体(mock)。生成的三维体是"分析产物"
|
||||||
// (设计 §2.1:三维分析栏按 对象/三维体模型/切片 三级树),归三维分析栏(非数据集栏)。
|
// (设计 §2.1:三维分析栏按 对象/三维体模型/切片 三级树),归三维分析栏(非数据集栏)。
|
||||||
// 后端列表每次勾选测线全量覆盖,故客户端体单独维护、刷新时合并注入(不被冲掉)。
|
// 后端列表每次勾选测线全量覆盖,故客户端体单独维护、刷新时合并注入(不被冲掉)。
|
||||||
auto lastAnalysisRows = std::make_shared<std::vector<geopro::data::DsRow>>();
|
// 三维分析数据源 = 最近对象树勾选拉取的 ds + 客户端三维体(mock) + 已保存切片;
|
||||||
auto refreshAnalysis = [drawer, scene3dRepo, lastAnalysisRows]() {
|
// splitByCategory 后注入 5 段(电阻率/视电阻率/瞬变/三维体/切片);二维(足迹)经 dim2D 仍走 col2D。
|
||||||
std::vector<geopro::data::DsRow> rows = *lastAnalysisRows;
|
auto lastSourceRows = std::make_shared<std::vector<geopro::data::DsRow>>();
|
||||||
|
auto refreshAnalysis = [drawer, scene3dRepo, lastSourceRows]() {
|
||||||
|
std::vector<geopro::data::DsRow> rows = *lastSourceRows;
|
||||||
for (auto& vr : scene3dRepo->volumeRows()) rows.push_back(std::move(vr)); // 客户端三维体
|
for (auto& vr : scene3dRepo->volumeRows()) rows.push_back(std::move(vr)); // 客户端三维体
|
||||||
for (auto& sr : scene3dRepo->sliceRows()) rows.push_back(std::move(sr)); // 已保存切片(挂父体下)
|
for (auto& sr : scene3dRepo->sliceRows()) rows.push_back(std::move(sr)); // 已保存切片(挂父体下)
|
||||||
drawer->colAnalysis()->setDatasets(rows);
|
drawer->analysisTab()->setBuckets(geopro::app::splitByCategory(rows));
|
||||||
|
drawer->col2D()->setDatasets(geopro::app::splitByDimension(rows).dim2D);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 渲染勾选聚合:三维数据集栏(剖面→帘面)+ 三维分析栏(三维体/切片→体素/切片)两套勾选并集
|
// 渲染勾选聚合:三维数据集栏(剖面→帘面)+ 三维分析栏(三维体/切片→体素/切片)两套勾选并集
|
||||||
|
|
@ -643,6 +648,69 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
*checkedProfiles = ids;
|
*checkedProfiles = ids;
|
||||||
pushChecked();
|
pushChecked();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── 三维分析 tab(5 段)信号接线(Task 12)──────────────────────────────────
|
||||||
|
auto* analysisTab = drawer->analysisTab();
|
||||||
|
// 5 段勾选并集 → 按类型分流渲染:反演剖面→帘面(checkedProfiles);三维体→体素(checkedAnalysis);
|
||||||
|
// 切片(dd_slice)→不进控制器,经 syncSlices 在父体上还原。
|
||||||
|
QObject::connect(analysisTab, &geopro::app::CategoryAnalysisTab::checkedDatasetsChanged, sceneCtrl,
|
||||||
|
[checkedProfiles, checkedAnalysis, checkedSliceIds, syncSlices, pushChecked,
|
||||||
|
scene3dRepo](const QStringList& ids) {
|
||||||
|
QStringList profiles, analysis;
|
||||||
|
checkedSliceIds->clear();
|
||||||
|
for (const QString& id : ids) {
|
||||||
|
const std::string s = id.toStdString();
|
||||||
|
if (scene3dRepo->isSliceDataset(s))
|
||||||
|
checkedSliceIds->insert(s);
|
||||||
|
else if (scene3dRepo->isVolumeDataset(s))
|
||||||
|
analysis << id;
|
||||||
|
else
|
||||||
|
profiles << id; // 反演剖面 → 帘面
|
||||||
|
}
|
||||||
|
*checkedProfiles = profiles;
|
||||||
|
*checkedAnalysis = analysis;
|
||||||
|
pushChecked();
|
||||||
|
syncSlices();
|
||||||
|
});
|
||||||
|
// 段头「+新增三维体」:弹参数对话框 → 组装真实 VoxelGenerateRequest → createVolume(mock) → 刷新。
|
||||||
|
QObject::connect(analysisTab, &geopro::app::CategoryAnalysisTab::generateVolumeRequested, &window,
|
||||||
|
[&window, &nav, scene3dRepo, refreshAnalysis](const QString& /*dsTypeCode*/,
|
||||||
|
const QStringList& sourceIds) {
|
||||||
|
if (sourceIds.isEmpty()) return;
|
||||||
|
geopro::app::VolumeParamsDialog dlg(static_cast<int>(sourceIds.size()), &window);
|
||||||
|
if (dlg.exec() != QDialog::Accepted) return;
|
||||||
|
const geopro::data::VolumeBuildParams p = dlg.params();
|
||||||
|
geopro::data::VoxelGenerateRequest req;
|
||||||
|
req.projectId = nav.currentProjectId().toStdString();
|
||||||
|
req.name = dlg.volumeName().toStdString();
|
||||||
|
for (const QString& id : sourceIds) req.sourceDatasetIds.push_back(id.toStdString());
|
||||||
|
req.interpModel =
|
||||||
|
(p.interpModel == geopro::data::VolumeBuildParams::Model::Kriging) ? "Kriging" : "Idw";
|
||||||
|
req.cellXY = p.cellXY;
|
||||||
|
req.cellZ = p.cellZ;
|
||||||
|
req.power = p.power;
|
||||||
|
req.maxDist = p.maxDist;
|
||||||
|
req.colorScaleId = p.colorScaleId;
|
||||||
|
scene3dRepo->createVolume(req);
|
||||||
|
refreshAnalysis();
|
||||||
|
});
|
||||||
|
// 双击数据详情:dd_slice→切片属性;dd_voxel→三维体属性(同 colAnalysis 详情口径)。
|
||||||
|
QObject::connect(analysisTab, &geopro::app::CategoryAnalysisTab::detailRequested, &window,
|
||||||
|
[&window, scene3dRepo](const QString& dsId, const QString& ddCode, const QString& name) {
|
||||||
|
if (ddCode == QStringLiteral("dd_slice")) {
|
||||||
|
geopro::data::I3dSceneRepository::SliceSpec sp;
|
||||||
|
if (scene3dRepo->sliceSpec(dsId.toStdString(), sp)) {
|
||||||
|
geopro::app::SlicePropertiesDialog dlg(name, sp, &window);
|
||||||
|
dlg.exec();
|
||||||
|
}
|
||||||
|
} else if (ddCode == QStringLiteral("dd_voxel")) {
|
||||||
|
geopro::data::Api3dRepository::VolumeInfo info;
|
||||||
|
if (scene3dRepo->volumeInfo(dsId.toStdString(), info)) {
|
||||||
|
geopro::app::VolumePropertiesDialog dlg(name, info, &window);
|
||||||
|
dlg.exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
// O点位置/字体本期 stub(TODO P4:弹框)。
|
// O点位置/字体本期 stub(TODO P4:弹框)。
|
||||||
QObject::connect(c3, &geopro::app::Column3DDataset::oPointClicked, vtkWidget,
|
QObject::connect(c3, &geopro::app::Column3DDataset::oPointClicked, vtkWidget,
|
||||||
[]() { /* TODO P4: O点位置弹框 */ });
|
[]() { /* TODO P4: O点位置弹框 */ });
|
||||||
|
|
@ -1115,27 +1183,22 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
auto generation = std::make_shared<unsigned long long>(0);
|
auto generation = std::make_shared<unsigned long long>(0);
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
objectTree, &geopro::app::ObjectTreePanel::checkedTmsChanged, &window,
|
objectTree, &geopro::app::ObjectTreePanel::checkedTmsChanged, &window,
|
||||||
[&projectRepo, &nav, drawer, emptyState, generation, lastAnalysisRows,
|
[&projectRepo, &nav, drawer, emptyState, generation, lastSourceRows,
|
||||||
refreshAnalysis](const QStringList& tmIds) {
|
refreshAnalysis](const QStringList& tmIds) {
|
||||||
const unsigned long long myGen = ++(*generation);
|
const unsigned long long myGen = ++(*generation);
|
||||||
emptyState->setVisible(tmIds.isEmpty()); // 有勾选→隐藏引导层,露出中央渲染
|
emptyState->setVisible(tmIds.isEmpty()); // 有勾选→隐藏引导层,露出中央渲染
|
||||||
if (tmIds.isEmpty()) {
|
if (tmIds.isEmpty()) {
|
||||||
drawer->col3D()->setDatasets({});
|
*lastSourceRows = {};
|
||||||
drawer->col2D()->setDatasets({});
|
refreshAnalysis(); // 清空 5 段(客户端三维体仍驻留) + col2D
|
||||||
*lastAnalysisRows = {};
|
|
||||||
refreshAnalysis(); // 后端分析行清空,但客户端三维体仍驻留三维分析栏
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 多 TM 异步汇总:每个 TM 取整棵 ds 子树,全部回来后按维度分发到三栏。
|
// 多 TM 异步汇总:每个 TM 取整棵 ds 子树,全部回来后 splitByCategory 分发到 5 段。
|
||||||
auto acc = std::make_shared<std::vector<geopro::data::DsRow>>();
|
auto acc = std::make_shared<std::vector<geopro::data::DsRow>>();
|
||||||
auto remaining = std::make_shared<int>(tmIds.size());
|
auto remaining = std::make_shared<int>(tmIds.size());
|
||||||
auto finish = [acc, drawer, generation, myGen, lastAnalysisRows, refreshAnalysis]() {
|
auto finish = [acc, generation, myGen, lastSourceRows, refreshAnalysis]() {
|
||||||
if (*generation != myGen) return; // 已被更新的勾选批次取代→丢弃陈旧结果
|
if (*generation != myGen) return; // 已被更新的勾选批次取代→丢弃陈旧结果
|
||||||
geopro::app::DimBuckets b = geopro::app::splitByDimension(*acc);
|
*lastSourceRows = *acc; // 全部对象树 ds 作分析数据源
|
||||||
drawer->col3D()->setDatasets(b.dim3D);
|
refreshAnalysis(); // splitByCategory→5段 + 合并三维体/切片 + dim2D→col2D
|
||||||
drawer->col2D()->setDatasets(b.dim2D);
|
|
||||||
*lastAnalysisRows = b.analysis;
|
|
||||||
refreshAnalysis(); // 后端切片 + 客户端三维体合并注入三维分析栏
|
|
||||||
};
|
};
|
||||||
for (const QString& tm : tmIds) {
|
for (const QString& tm : tmIds) {
|
||||||
geopro::data::NavRequest* req = projectRepo.loadRowsAsync(
|
geopro::data::NavRequest* req = projectRepo.loadRowsAsync(
|
||||||
|
|
@ -1208,14 +1271,12 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
|
|
||||||
// 切换到「不同项目」时先清空中央区,避免新项目残留旧项目的三栏数据与 VTK 渲染。
|
// 切换到「不同项目」时先清空中央区,避免新项目残留旧项目的三栏数据与 VTK 渲染。
|
||||||
// 仅真正换项目用(delete-refresh 等 switchProject(currentProjectId) 不走此处,避免误清)。
|
// 仅真正换项目用(delete-refresh 等 switchProject(currentProjectId) 不走此处,避免误清)。
|
||||||
auto clearCentral = [drawer, sceneCtrl, emptyState, checkedProfiles, checkedAnalysis,
|
auto clearCentral = [sceneCtrl, emptyState, checkedProfiles, checkedAnalysis,
|
||||||
pushChecked, lastAnalysisRows, refreshAnalysis, checkedSliceIds,
|
pushChecked, lastSourceRows, refreshAnalysis, checkedSliceIds,
|
||||||
syncSlices, basemap, sceneView]() {
|
syncSlices, basemap, sceneView]() {
|
||||||
// 三栏清空(col2D/col3D setDatasets({}) 会顺带发空勾选 → setChecked2DDatasets({})/帘面清空)。
|
// 数据源清空 → 5 段 + col2D 清空(refreshAnalysis 内 setBuckets/dim2D;客户端三维体仍驻留)。
|
||||||
drawer->col3D()->setDatasets({});
|
*lastSourceRows = {};
|
||||||
drawer->col2D()->setDatasets({});
|
refreshAnalysis();
|
||||||
*lastAnalysisRows = {};
|
|
||||||
refreshAnalysis(); // 后端分析行清空(客户端三维体仍按设计驻留三维分析栏)
|
|
||||||
// 勾选集清空并下发空到 VTK(帘面/体素/切片/2D 足迹全部撤场)。
|
// 勾选集清空并下发空到 VTK(帘面/体素/切片/2D 足迹全部撤场)。
|
||||||
checkedProfiles->clear();
|
checkedProfiles->clear();
|
||||||
checkedAnalysis->clear();
|
checkedAnalysis->clear();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue