diff --git a/src/app/main.cpp b/src/app/main.cpp index 3701170..736792d 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -1,14 +1,15 @@ // M1 工作台(视图重构 Task B):正确产品模型。 -// - 左 对象树:GS→TM→DS(复选框)。勾选 dd_section → 中央场景显示该测线竖直帘面(立体断面墙),可多条共存。 -// - 中央「二维/三维视图」:单一 VTK 3D 场景,内容是测线竖直帘面。工具条仅「二维/三维」相机开关 -// (作用整场景):二维=俯视正交(看测线俯视布局)、三维=透视(看立体墙)。因内容立体,2D/3D 有真实区别。 +// - 左 对象树:GS→TM→DS(复选框)。勾选 dd_section → 在中央当前视图显示该数据集,可多条共存。 +// - 中央「二维地图 / 三维视图」:两个互斥视图(内容不同,不是同一物体换相机)。 +// 二维地图 = 对每个勾选数据集 buildSurveyLine(lat/lon 红线俯视,z=0)+ applyTop2D(浅底背景)。 +// 三维视图 = 对每个勾选数据集 buildCurtain(竖直断面墙),actor SetScale(1,1,3) 纵向夸张 + applyFree3D(白底)。 +// 切视图 / 勾选变化 → 按当前勾选集重建对应内容。 // - 下方「数据详情」:独立 QVTK 小视图。单击某 DS → 显示该数据集平面反演剖面(#18 banded 等值面+等值线, -// 平躺俯视正交)+ 属性。 +// 两 actor SetScale(1,1.5,1) 纵向夸张,平躺俯视正交)+ 属性。 // - 右 属性:选中数据集属性文本。 -// 世界系:启动 loadGrid("grid1") 取一次,用其 lat/lon 中位/均值作 GeoLocalFrame(全项目共享,保证多帘面配准)。 +// 世界系:启动 loadGrid("grid1") 取一次,用其 lat/lon 中位/均值作 GeoLocalFrame(全项目共享,保证多视图配准)。 #include -#include #include #include #include @@ -41,6 +42,7 @@ #include "Scene.hpp" #include "actors/CurtainActor.hpp" #include "actors/GridContourActor.hpp" +#include "actors/MapLineActor.hpp" #include "geo/GeoLocalFrame.hpp" @@ -49,7 +51,6 @@ #include #include -#include #include #include @@ -104,8 +105,12 @@ double median(std::vector v) return n % 2 ? v[n / 2] : 0.5 * (v[n / 2 - 1] + v[n / 2]); } -// 当前相机模式(默认二维俯视)。 -enum class CameraMode { Top2D, Free3D }; +// 当前中央视图(默认二维地图)。二维地图=测线红线俯视;三维视图=断面墙。 +enum class ViewMode { Map2D, View3D }; + +// 纵向夸张倍数:三维断面墙沿 z 拉伸成墙;数据详情 #18 沿 y 拉伸填面板。 +constexpr double kCurtainZScale = 3.0; +constexpr double kDetailYScale = 1.5; // 在给定 QMainWindow 上构建 M1 工作台。 // repo 生命周期须覆盖到事件循环结束(由调用方保证)。 @@ -130,38 +135,33 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re vtkRenderer* rendererPtr = scene->renderer(); vtkGenericOpenGLRenderWindow* renderWindowPtr = renderWindow.Get(); - auto cameraMode = std::make_shared(CameraMode::Top2D); - auto applyCurrentCamera = [rendererPtr, cameraMode]() { - if (*cameraMode == CameraMode::Top2D) - geopro::render::applyTop2D(rendererPtr); - else - geopro::render::applyFree3D(rendererPtr); - }; + // 当前视图模式(全局共享,切视图/勾选时据此重建内容)。默认二维地图。 + auto viewMode = std::make_shared(ViewMode::Map2D); auto* dockManager = new ads::CDockManager(&window); window.setCentralWidget(dockManager); - // 中央容器:顶部 2D/3D 工具条 + 下方帘面 QVTK 视图。 + // 中央容器:顶部「二维地图/三维视图」工具条 + 下方 QVTK 视图。 auto* centerWidget = new QWidget(); auto* centerLayout = new QVBoxLayout(centerWidget); centerLayout->setContentsMargins(0, 0, 0, 0); centerLayout->setSpacing(0); - // 工具条:仅「二维/三维」相机开关,作用整场景(不改内容)。互斥二选一,默认二维。 + // 工具条:「二维地图/三维视图」两个互斥可勾选 action。切换=按当前勾选集重建对应内容。默认二维地图。 auto* viewToolBar = new QToolBar(); - auto* cameraGroup = new QActionGroup(viewToolBar); - cameraGroup->setExclusive(true); - auto* act2D = viewToolBar->addAction(QStringLiteral("二维")); - auto* act3D = viewToolBar->addAction(QStringLiteral("三维")); + auto* viewGroup = new QActionGroup(viewToolBar); + viewGroup->setExclusive(true); + auto* act2D = viewToolBar->addAction(QStringLiteral("二维地图")); + auto* act3D = viewToolBar->addAction(QStringLiteral("三维视图")); act2D->setCheckable(true); act3D->setCheckable(true); - cameraGroup->addAction(act2D); - cameraGroup->addAction(act3D); - act2D->setChecked(true); // 默认二维 + viewGroup->addAction(act2D); + viewGroup->addAction(act3D); + act2D->setChecked(true); // 默认二维地图 centerLayout->addWidget(viewToolBar); centerLayout->addWidget(vtkWidget, 1); - auto* vtkDock = new ads::CDockWidget(QStringLiteral("二维/三维视图")); + auto* vtkDock = new ads::CDockWidget(QStringLiteral("二维地图/三维视图")); vtkDock->setWidget(centerWidget); auto* centerDockArea = dockManager->addDockWidget(ads::CenterDockWidgetArea, vtkDock); @@ -198,68 +198,59 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re rightDock->setWidget(propLabel); dockManager->addDockWidget(ads::RightDockWidgetArea, rightDock); - // ── 勾选驱动帘面显示(核心)───────────────────────────────────────── - // 已显示数据集 → 其 VTK props 映射(帘面=单个 actor)。多数据集叠加共存,不 clear 场景。 - using PropList = std::vector>; - auto dsProps = std::make_shared>(); + // ── 中央视图重建(核心)───────────────────────────────────────────── + // 两个互斥视图按当前勾选集整体重建:scene.clear() → 对每个勾选 dd_section 加对应 actor。 + // 二维地图 = buildSurveyLine(红线俯视,浅底背景)+ applyTop2D。 + // 三维视图 = buildCurtain(断面墙)SetScale(1,1,kCurtainZScale) + applyFree3D(白底)。 + // frame 全局共享;切视图/勾选变化都调用此函数重建当前视图。 + auto rebuildCentral = [scene, rendererPtr, renderWindowPtr, viewMode, &repo, frame, tree]() { + scene->clear(); - // 初始化守卫:populateTree 程序化 setCheckState 触发 itemChanged,需避免重入。 - auto building = std::make_shared(false); + const bool is2D = (*viewMode == ViewMode::Map2D); + rendererPtr->SetBackground(is2D ? 0.96 : 1.0, is2D ? 0.97 : 1.0, is2D ? 0.99 : 1.0); - // 是否已有任意可见对象(用于首个对象时套用当前相机预设)。 - auto hasVisible = [dsProps]() { - for (const auto& kv : *dsProps) { - if (!kv.second.empty()) return true; - } - return false; - }; + // 遍历对象树收集所有勾选的 dd_section,逐个加入当前视图内容。 + QList stack; + for (int i = 0; i < tree->topLevelItemCount(); ++i) stack.append(tree->topLevelItem(i)); + while (!stack.isEmpty()) { + QTreeWidgetItem* cur = stack.takeFirst(); + for (int i = 0; i < cur->childCount(); ++i) stack.append(cur->child(i)); + + const QString dsId = cur->data(0, kRoleDsId).toString(); + if (dsId.isEmpty()) continue; // GS/TM 节点忽略 + if (cur->checkState(0) != Qt::Checked) continue; // 仅显示勾选的 + const QString ddType = cur->data(0, kRoleDdType).toString(); + if (ddType != "dd_section") continue; // 当前仅支持剖面网格 - // 构建某测线竖直帘面(dd_section)并返回其 props。其它类型暂不入中央场景。 - auto buildCurtainProps = [&repo, frame](const QString& dsId, - const QString& ddType) -> PropList { - PropList props; - if (ddType == "dd_section") { const std::string id = dsId.toStdString(); const auto g = repo.loadGrid(id); - const auto cs = repo.loadColorScale(id); - auto curtain = geopro::render::buildCurtain(g, cs, *frame); - if (curtain) props.push_back(curtain); - } - return props; - }; - - // 勾选态变化:勾选 → 构建/显示帘面;取消 → 移除其 props。多数据集叠加共存。 - QObject::connect( - tree, &QTreeWidget::itemChanged, tree, - [dsProps, building, buildCurtainProps, hasVisible, applyCurrentCamera, rendererPtr, - renderWindowPtr](QTreeWidgetItem* item, int) { - if (*building) return; // 程序化改动期间不处理,避免初始化递归 - const QString dsId = item->data(0, kRoleDsId).toString(); - if (dsId.isEmpty()) return; // GS/TM 节点忽略 - const QString ddType = item->data(0, kRoleDdType).toString(); - const bool checked = item->checkState(0) == Qt::Checked; - - if (checked) { - const bool wasEmpty = !hasVisible(); - auto it = dsProps->find(dsId); - if (it == dsProps->end() || it->second.empty()) { - PropList props = buildCurtainProps(dsId, ddType); - for (const auto& p : props) rendererPtr->AddViewProp(p); - (*dsProps)[dsId] = std::move(props); - } else { - for (const auto& p : it->second) p->SetVisibility(1); - } - if (wasEmpty) applyCurrentCamera(); // 首个可见对象 → 套用当前相机 - rendererPtr->ResetCamera(); + if (is2D) { + auto line = geopro::render::buildSurveyLine(g, *frame); + if (line) scene->addActor(line); } else { - auto it = dsProps->find(dsId); - if (it != dsProps->end()) { - for (const auto& p : it->second) rendererPtr->RemoveViewProp(p); - dsProps->erase(it); + const auto cs = repo.loadColorScale(id); + auto curtain = geopro::render::buildCurtain(g, cs, *frame); + if (curtain) { + curtain->SetScale(1.0, 1.0, kCurtainZScale); // 纵向夸张成墙 + scene->addActor(curtain); } } - renderWindowPtr->Render(); - }); + } + + if (is2D) + geopro::render::applyTop2D(rendererPtr); + else + geopro::render::applyFree3D(rendererPtr); + rendererPtr->ResetCamera(); + renderWindowPtr->Render(); + }; + + // 勾选/取消某 dd_section → 重建当前视图内容(勾的才显示;可多条共存)。 + QObject::connect(tree, &QTreeWidget::itemChanged, tree, + [rebuildCentral](QTreeWidgetItem* item, int) { + if (item->data(0, kRoleDsId).toString().isEmpty()) return; // GS/TM 忽略 + rebuildCentral(); + }); // ── 单击 DS → 下方数据详情显示平面反演剖面 + 右侧属性(与勾选区分;不改帘面可见性)── QObject::connect( @@ -276,10 +267,17 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re const auto cs = repo.loadColorScale(id); // 下方数据详情:平面反演剖面(#18 banded 等值面 + 等值线),平躺俯视正交。 + // 两个 actor 都纵向夸张 1.5x(沿 y)填满面板。 const auto actors = geopro::render::buildGridContour(g, cs); detailRendererPtr->RemoveAllViewProps(); - if (actors.bands) detailRendererPtr->AddViewProp(actors.bands); - if (actors.edges) detailRendererPtr->AddViewProp(actors.edges); + if (actors.bands) { + actors.bands->SetScale(1.0, kDetailYScale, 1.0); + detailRendererPtr->AddViewProp(actors.bands); + } + if (actors.edges) { + actors.edges->SetScale(1.0, kDetailYScale, 1.0); + detailRendererPtr->AddViewProp(actors.edges); + } geopro::render::applyTop2D(detailRendererPtr); detailRendererPtr->ResetCamera(); detailRenderWindowPtr->Render(); @@ -291,43 +289,19 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re .arg(name).arg(g.nx()).arg(g.ny()).arg(g.vmin).arg(g.vmax)); }); - // ── 工具条「二维/三维」:仅切换整场景相机预设,不改内容可见性 ──────────── - QObject::connect(act2D, &QAction::triggered, vtkWidget, - [cameraMode, applyCurrentCamera, rendererPtr, renderWindowPtr]() { - *cameraMode = CameraMode::Top2D; - applyCurrentCamera(); - rendererPtr->ResetCamera(); - renderWindowPtr->Render(); - }); - QObject::connect(act3D, &QAction::triggered, vtkWidget, - [cameraMode, applyCurrentCamera, rendererPtr, renderWindowPtr]() { - *cameraMode = CameraMode::Free3D; - applyCurrentCamera(); - rendererPtr->ResetCamera(); - renderWindowPtr->Render(); - }); + // ── 工具条「二维地图/三维视图」:切换互斥视图 → 按当前勾选集重建对应内容 ── + QObject::connect(act2D, &QAction::triggered, vtkWidget, [viewMode, rebuildCentral]() { + *viewMode = ViewMode::Map2D; + rebuildCentral(); + }); + QObject::connect(act3D, &QAction::triggered, vtkWidget, [viewMode, rebuildCentral]() { + *viewMode = ViewMode::View3D; + rebuildCentral(); + }); - // ── 启动默认:dd_section 已设为 Checked,但 itemChanged 在 connect 之前触发故未渲染。 - // 这里 connect 之后对已勾选 DS 主动触发一次帘面显示。 - { - QList stack; - for (int i = 0; i < tree->topLevelItemCount(); ++i) stack.append(tree->topLevelItem(i)); - while (!stack.isEmpty()) { - QTreeWidgetItem* cur = stack.takeFirst(); - const QString dsId = cur->data(0, kRoleDsId).toString(); - if (!dsId.isEmpty() && cur->checkState(0) == Qt::Checked) { - const QString ddType = cur->data(0, kRoleDdType).toString(); - PropList props = buildCurtainProps(dsId, ddType); - const bool wasEmpty = !hasVisible(); - for (const auto& p : props) rendererPtr->AddViewProp(p); - (*dsProps)[dsId] = std::move(props); - if (wasEmpty) applyCurrentCamera(); - } - for (int i = 0; i < cur->childCount(); ++i) stack.append(cur->child(i)); - } - rendererPtr->ResetCamera(); - renderWindowPtr->Render(); - } + // ── 启动默认:dd_section 已勾选,但 itemChanged 在 connect 之前触发故未渲染。 + // 这里 connect 之后主动按默认视图(二维地图)重建一次中央内容。 + rebuildCentral(); } } // namespace