feat(app): 中央二维地图(测线线)/三维视图(断面墙)两视图 + 数据详情(#18纵向夸张), 接入已验证渲染积木
This commit is contained in:
parent
e59b6b3dfe
commit
ebd7779b51
200
src/app/main.cpp
200
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 <fstream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
|
@ -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 <QVTKOpenGLStereoWidget.h>
|
||||
#include <vtkGenericOpenGLRenderWindow.h>
|
||||
#include <vtkProp.h>
|
||||
#include <vtkRenderer.h>
|
||||
#include <vtkSmartPointer.h>
|
||||
|
||||
|
|
@ -104,8 +105,12 @@ double median(std::vector<double> 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>(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>(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,67 +198,58 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
rightDock->setWidget(propLabel);
|
||||
dockManager->addDockWidget(ads::RightDockWidgetArea, rightDock);
|
||||
|
||||
// ── 勾选驱动帘面显示(核心)─────────────────────────────────────────
|
||||
// 已显示数据集 → 其 VTK props 映射(帘面=单个 actor)。多数据集叠加共存,不 clear 场景。
|
||||
using PropList = std::vector<vtkSmartPointer<vtkProp>>;
|
||||
auto dsProps = std::make_shared<std::map<QString, PropList>>();
|
||||
// ── 中央视图重建(核心)─────────────────────────────────────────────
|
||||
// 两个互斥视图按当前勾选集整体重建: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<bool>(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<QTreeWidgetItem*> 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);
|
||||
if (is2D) {
|
||||
auto line = geopro::render::buildSurveyLine(g, *frame);
|
||||
if (line) scene->addActor(line);
|
||||
} else {
|
||||
const auto cs = repo.loadColorScale(id);
|
||||
auto curtain = geopro::render::buildCurtain(g, cs, *frame);
|
||||
if (curtain) props.push_back(curtain);
|
||||
if (curtain) {
|
||||
curtain->SetScale(1.0, 1.0, kCurtainZScale); // 纵向夸张成墙
|
||||
scene->addActor(curtain);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
}
|
||||
|
||||
if (is2D)
|
||||
geopro::render::applyTop2D(rendererPtr);
|
||||
else
|
||||
geopro::render::applyFree3D(rendererPtr);
|
||||
rendererPtr->ResetCamera();
|
||||
renderWindowPtr->Render();
|
||||
};
|
||||
|
||||
// 勾选态变化:勾选 → 构建/显示帘面;取消 → 移除其 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();
|
||||
} else {
|
||||
auto it = dsProps->find(dsId);
|
||||
if (it != dsProps->end()) {
|
||||
for (const auto& p : it->second) rendererPtr->RemoveViewProp(p);
|
||||
dsProps->erase(it);
|
||||
}
|
||||
}
|
||||
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 → 下方数据详情显示平面反演剖面 + 右侧属性(与勾选区分;不改帘面可见性)──
|
||||
|
|
@ -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(act2D, &QAction::triggered, vtkWidget, [viewMode, rebuildCentral]() {
|
||||
*viewMode = ViewMode::Map2D;
|
||||
rebuildCentral();
|
||||
});
|
||||
QObject::connect(act3D, &QAction::triggered, vtkWidget,
|
||||
[cameraMode, applyCurrentCamera, rendererPtr, renderWindowPtr]() {
|
||||
*cameraMode = CameraMode::Free3D;
|
||||
applyCurrentCamera();
|
||||
rendererPtr->ResetCamera();
|
||||
renderWindowPtr->Render();
|
||||
QObject::connect(act3D, &QAction::triggered, vtkWidget, [viewMode, rebuildCentral]() {
|
||||
*viewMode = ViewMode::View3D;
|
||||
rebuildCentral();
|
||||
});
|
||||
|
||||
// ── 启动默认:dd_section 已设为 Checked,但 itemChanged 在 connect 之前触发故未渲染。
|
||||
// 这里 connect 之后对已勾选 DS 主动触发一次帘面显示。
|
||||
{
|
||||
QList<QTreeWidgetItem*> 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
|
||||
|
|
|
|||
Loading…
Reference in New Issue