From a2efef8adae88af0fc9b40f298a251658158f075 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Mon, 8 Jun 2026 09:42:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(view):=203D=E3=80=8C=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E3=80=8D=E5=9B=BE=E5=B1=82=E6=B5=AE=E5=B1=82?= =?UTF-8?q?=20+=20=E4=BD=93=E7=B4=A0=E6=AD=A3=E7=BB=8F=E6=8E=A5=E5=85=A5(?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=E5=8E=9F=E5=9E=8B,=20=E5=A2=9E=E9=87=8F3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 中央 QVTK 左上浮层(QFrame, 仅三维显示, 工具条下方, raise 置顶): 图层勾选「帘面 / 体素」。 - rebuildCentral: 帘面层 gate buildCurtain; 体素层 → buildVoxelFromScatters 体绘制(同纵向夸张)。 showCurtain(默认开)/showVoxel(默认关)/crs 共享态; 切视图自动显隐浮层。 - 体素经此正经接入(取代 42a7ed1 移除的困惑工具条开关, 这才是它对齐原型的归宿)。 - main() 自动定位 PROJ_DATA(候选路径; 部署须随包附带 proj 数据); PROJ 不可用→体素勾选禁用+提示。 - app 构建干净; 待人工登录复核(浮层渲染于 QVTK 之上 + 勾体素出十字片)。 --- docs/superpowers/STATUS.md | 6 +- .../plans/2026-06-08-m1-prototype-layout.md | 6 +- src/app/main.cpp | 134 +++++++++++++++--- 3 files changed, 126 insertions(+), 20 deletions(-) diff --git a/docs/superpowers/STATUS.md b/docs/superpowers/STATUS.md index b92c36a..cb35038 100644 --- a/docs/superpowers/STATUS.md +++ b/docs/superpowers/STATUS.md @@ -17,7 +17,7 @@ - **左下 数据真实显示栏**(对齐原型):单击测线 → 列其采集批次(数据集,tab 数据/文件);单击采集批次 → 数据详情+异常列表+属性。启动自动选首测线+首数据集。 - **中央 二维地图 / 三维视图**(两个**真内容**,非相机切换): - 二维地图 = `MapLineActor`:测线 `lat/lon` 轨迹**红线**俯视(浅底),像地图。 - - 三维视图 = `CurtainActor`:沿测线的**竖直断面墙**(分段色带,z 纵向夸张×3,沿弯曲测线弯)。中央工具条**仅**「二维地图/三维视图」(对齐原型,无体素按钮)。 + - 三维视图 = `CurtainActor`:沿测线的**竖直断面墙**(分段色带,z 纵向夸张×3,沿弯曲测线弯)。中央工具条**仅**「二维地图/三维视图」。**3D 左上「视图详情」浮层**(对齐原型,仅 3D 显示):图层勾选 **帘面 / 体素**(体素=`buildVoxelFromScatters` 两交叉测线散点经 EPSG:4547 配准 IDW,与帘面同纵向夸张;PROJ 不可用则禁用)。 - **下方 数据详情**:工具条「原数据/网格数据」切换 +「显示异常」开关(对齐原型命名)。单击数据集 → 网格数据=`GridContourActor` 平面剖面(#18,colorBar 真实非均匀分段值上色,纵向夸张×1.5);原数据=`ScatterActor` 彩色方块散点(#17,x=距离/y=深度取负,用散点自带色阶);显示异常=`AnomalyActor` 在上图叠加异常 dashed 折线(同纵向夸张对齐)。 - **右上 异常列表**(对齐原型):单击数据集→列该数据集异常(颜色块+名称(类型)+派生「位置/深/尺寸」),勾选框显隐,与数据详情异常叠加联动(取消勾选→该异常虚线隐藏)。 - **右下 属性**:名称/网格 nx×ny/vmin·vmax/异常数。 @@ -69,7 +69,7 @@ 1. ~~**散点 #17**:`ScatterActor`(剖面原数据 2597 点彩色散点),数据详情"原数据"视图~~ ✅ **已完成**(2026-06-08,离屏 PNG 核对吻合 Python 真值,接入数据详情「反演剖面/原数据」切换;app 待人工登录肉眼复核交互)。 2. ~~**异常叠加**:`AnomalyActor`(markType 点/线/面)~~ ✅ **已完成**(2026-06-08,叠加在数据详情 #18/#17 上,「显示异常」开关默认开;离屏 `verify_section_anomaly.png` 折线位置吻合 ref_18;样本 3 异常均 markType=2 dashed;app 待人工登录复核)。**注**:dashed 点画在 VTK OpenGL2 下偏弱(几乎实线),几何/颜色/位置正确,纯观感项可后续调。 3. **DEM/影像地形**:加 vcpkg `gdal`;GDAL 读 dem.tif/image.tif;**影像 EPSG:3857 必须 PROJ 重投影到世界系**;`vtkWarpScalar` 地形面 + 纹理。 -4. **dd_voxel 回归**:🔶 **引擎已完成并验证**(2026-06-08,CRS 已定 EPSG:4547)。`render::buildVoxelFromScatters`:散点 projX/Y→4547→4326→GeoLocalFrame 配准 + IDW(maxDist 裁剪)→ `buildVoxel`;离屏 `verify_voxel_top.png` 两臂支撑吻合 ref voxel_hslice、`verify_voxel_3d.png` profile1 片贴合帘面;+1 单测(VoxelRegister,需 PROJ_DATA)。**UI 未接入**:曾加"体素"工具条开关,但与二维/三维平级令人困惑且不在原型,**已移除**;待做 **3D 图层控制(对齐原型「视图详情」浮层)** 再正经接入(届时 main() 需设 PROJ_DATA + 部署随包附带 proj 数据)。**注**:仅 2 交叉线→薄十字片(15.9% 充填,半透明偏淡),可信满体需≥3线(设计 §10/§14)。**dd_slice 交互切片未做**(buildVoxel 已暴露 image 供 reslice widget)。 +4. **dd_voxel 回归**:✅ **已完成**(2026-06-08,CRS 已定 EPSG:4547)。`render::buildVoxelFromScatters`:散点 projX/Y→4547→4326→GeoLocalFrame 配准 + IDW(maxDist 裁剪)→ `buildVoxel`;离屏 `verify_voxel_top.png` 两臂支撑吻合 ref voxel_hslice、`verify_voxel_3d.png` profile1 片贴合帘面;+1 单测(VoxelRegister,需 PROJ_DATA)。**UI 已接入**(增量3):3D「视图详情」浮层「体素」图层勾选驱动;main() 自动设 PROJ_DATA(部署须随包附带 proj 数据);PROJ 不可用则该层禁用。**注**:仅 2 交叉线→薄十字片(15.9% 充填,半透明偏淡),可信满体需≥3线(设计 §10/§14)。**dd_slice 交互切片未做**(buildVoxel 已暴露 image 供 reslice widget)。 5. **底图瓦片**(二维地图,天地图/Mapbox):M1.5。 6. **Credential(QtKeychain)**:记住一个月免登录持久化(P3 Task2 未做)。 7. 多测线:当前样本仅 1 条 dd_section(grid1);多条共存机制已就绪,加数据即叠加。 @@ -78,7 +78,7 @@ 10. **布局对齐原型**(权威参考 `http://prototype.geomative.cn/`;截图存 `.playwright-mcp/`)。**计划见 `plans/2026-06-08-m1-prototype-layout.md`(六面板 + view/ 抽取,增量序列)**。进度: - ✅ **增量1 右上「异常列表」**(2026-06-08,`panels/AnomalyListPanel`,与数据详情显隐联动;待人工复核)。 - ✅ **增量2 左下「数据列表」+ 对象树到 TM 层**(2026-06-08,`panels/DatasetListPanel`;树 GS→TM 复选驱动中央, DS 移出树入数据列表 tab 数据/文件, DS 单击→详情+异常+属性, 启动自动选首测线/首数据集;待人工复核)。 - - ⬜ **增量3 3D「视图详情」图层浮层**(体素的正确归宿;体素引擎已就绪,UI 待此接入)。 + - ✅ **增量3 3D「视图详情」图层浮层**(2026-06-08,QFrame 浮于 QVTK 左上,仅 3D 显示;帘面/体素图层勾选;体素经此正经接入=之前移除的工具条开关的正确归宿;main() 设 PROJ_DATA;待人工复核浮层渲染+体素显隐)。 - ⬜ **增量4 数据详情富工具条 + 电极标记 + 数值标签**;底图影像=DEM/底图任务。 - 架构:新面板抽到 `src/app/panels/`(暂随 app 编译,如 login/),控制 main.cpp 体量;后续可升 `src/view/` 库。 diff --git a/docs/superpowers/plans/2026-06-08-m1-prototype-layout.md b/docs/superpowers/plans/2026-06-08-m1-prototype-layout.md index ad1b22b..1634ee9 100644 --- a/docs/superpowers/plans/2026-06-08-m1-prototype-layout.md +++ b/docs/superpowers/plans/2026-06-08-m1-prototype-layout.md @@ -70,7 +70,11 @@ - **人工复核**:点 ERT1 → 左下列出采集批次 → 单击 → 数据详情出图。 - 提交 `feat(view): 左下数据列表 + 对象树到测线层(DS 移出树)`。 -### 增量 3:3D「视图详情」图层浮层(体素的正确归宿) +### 增量 3:3D「视图详情」图层浮层 ✅ 已完成(2026-06-08) +> main.cpp 内联 QFrame 浮层(child of centerWidget,move 到 QVTK 左上=工具条下方,raise;仅 3D setVisible)。 +> 含 帘面/体素 QCheckBox。rebuildCentral:帘面层 gate buildCurtain;体素层→buildVoxelFromScatters 加体绘制 +> (同纵向夸张)。showCurtain/showVoxel/crs 共享态。main() 自动设 PROJ_DATA;crs 失败→体素勾选禁用。 +> app 构建净;**待人工复核**(浮层是否正确浮于 QVTK 之上、勾体素是否出十字片)。**原计划存档**: - 中央三维视图左上浮层(QWidget overlay on QVTK,或工具区):勾选显示 测线/帘面/**体素**/(地形)。 - 勾"体素" → 调 `buildVoxelFromScatters`(已验证)加入 3D 场景;勾选驱动 rebuildCentral 的图层集。 - main() 设 PROJ_DATA(体素配准需 proj.db;按候选路径,部署随包附带)。 diff --git a/src/app/main.cpp b/src/app/main.cpp index c355186..d9fecc5 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -3,10 +3,9 @@ // - 左下 数据真实显示栏:单击测线 → 列其采集批次(数据集,tab 数据/文件)。单击采集批次 → 数据详情+异常+属性。 // - 中央「二维地图 / 三维视图」:两个互斥视图(内容不同,不是同一物体换相机)。 // 二维地图 = 对每个勾选数据集 buildSurveyLine(lat/lon 红线俯视,z=0)+ applyTop2D(浅底背景)。 -// 三维视图 = 对每个勾选数据集 buildCurtain(竖直断面墙),actor SetScale(1,1,3) 纵向夸张 + applyFree3D(白底)。 -// 切视图 / 勾选变化 → 按当前勾选集重建对应内容。 -// 注:dd_voxel 体素引擎(render::buildVoxelFromScatters)已就绪并验证,但**不**作为工具条开关 -// (那样与二维/三维平级会令人困惑、且不在原型);待做 3D 图层控制(对齐原型「视图详情」浮层)再接。 +// 三维视图 = 勾选测线的 buildCurtain(竖直断面墙),actor SetScale(1,1,3) 纵向夸张 + applyFree3D(白底)。 +// 三维左上「视图详情」浮层(对齐原型):图层勾选 帘面 / 体素(dd_voxel,散点经 EPSG:4547 配准 IDW)。 +// 切视图 / 勾选变化 / 图层变化 → 重建对应内容。 // - 下方「数据详情」:独立 QVTK 小视图 + 工具条「原数据 / 网格数据」切换 +「显示异常」开关(对齐原型)。 // 单击某 DS → 显示该数据集: // 网格数据 = #18 banded 等值面+等值线(两 actor SetScale(1,1.5,1) 纵向夸张)。 @@ -24,11 +23,15 @@ #include #include +#include #include +#include +#include #include #include #include #include +#include #include #include #include @@ -53,15 +56,18 @@ #include "CameraPreset.hpp" #include "Scene.hpp" +#include "VoxelFromScatters.hpp" #include "actors/AnomalyActor.hpp" #include "actors/CurtainActor.hpp" #include "actors/GridContourActor.hpp" #include "actors/MapLineActor.hpp" #include "actors/ScatterActor.hpp" +#include "geo/CrsTransform.hpp" #include "geo/GeoLocalFrame.hpp" #include +#include #include #include #include @@ -140,6 +146,10 @@ constexpr float kScatterPointSize = 4.0F; constexpr double kCurtainZScale = 3.0; constexpr double kDetailYScale = 1.5; +// 项目 CRS(实证确定,STATUS §4):散点 projX/Y→经纬→GeoLocalFrame 配准体素到世界系。 +constexpr const char* kProjectCrs = "EPSG:4547"; // CGCS2000 / 3-degree GK CM 114E +constexpr const char* kWgs84 = "EPSG:4326"; + // 在给定 QMainWindow 上构建 M1 工作台。 // repo 生命周期须覆盖到事件循环结束(由调用方保证)。 void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& repo) @@ -166,6 +176,16 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re // 当前视图模式(全局共享,切视图/勾选时据此重建内容)。默认二维地图。 auto viewMode = std::make_shared(ViewMode::Map2D); + // 三维图层显隐(由「视图详情」浮层控制)+ 项目 CRS→WGS84(体素配准)。 + auto showCurtain = std::make_shared(true); // 帘面,默认显示 + auto showVoxel = std::make_shared(false); // 体素,默认关 + std::shared_ptr crs; // PROJ 失败→空→体素层无效(不崩) + try { + crs = std::make_shared(kProjectCrs, kWgs84); + } catch (const std::exception&) { + crs.reset(); + } + auto* dockManager = new ads::CDockManager(&window); window.setCentralWidget(dockManager); @@ -189,6 +209,31 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re centerLayout->addWidget(viewToolBar); centerLayout->addWidget(vtkWidget, 1); + // ──「视图详情」图层浮层(对齐原型 3D 视图左上):浮在 QVTK 之上,控制三维图层显隐。 + // 仅三维视图显示;含 帘面 / 体素 勾选(体素=两交叉测线散点配准 IDW 的派生层,正确归宿)。 + auto* layerPanel = new QFrame(centerWidget); + layerPanel->setFrameShape(QFrame::StyledPanel); + layerPanel->setStyleSheet( + QStringLiteral("QFrame{background:rgba(255,255,255,0.92);border:1px solid #b0b4bb;" + "border-radius:6px;} QCheckBox{padding:1px;}")); + auto* layerLayout = new QVBoxLayout(layerPanel); + layerLayout->setContentsMargins(10, 8, 12, 8); + layerLayout->setSpacing(4); + auto* layerTitle = new QLabel(QStringLiteral("视图详情")); + layerTitle->setStyleSheet(QStringLiteral("font-weight:bold;border:none;background:transparent;")); + auto* chkCurtain = new QCheckBox(QStringLiteral("帘面(断面墙)")); + chkCurtain->setChecked(true); + auto* chkVoxel = new QCheckBox(QStringLiteral("体素(dd_voxel)")); + chkVoxel->setChecked(false); + if (!crs) { // PROJ 不可用 → 体素层禁用并提示 + chkVoxel->setEnabled(false); + chkVoxel->setToolTip(QStringLiteral("PROJ 数据(proj.db)缺失,体素配准不可用")); + } + layerLayout->addWidget(layerTitle); + layerLayout->addWidget(chkCurtain); + layerLayout->addWidget(chkVoxel); + layerPanel->setVisible(false); // 默认二维,不显示图层浮层 + auto* vtkDock = new ads::CDockWidget(QStringLiteral("二维地图/三维视图")); vtkDock->setWidget(centerWidget); auto* centerDockArea = dockManager->addDockWidget(ads::CenterDockWidgetArea, vtkDock); @@ -277,19 +322,19 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re // 三维视图 = buildCurtain(断面墙)SetScale(1,1,kCurtainZScale) + applyFree3D(白底)。 // frame/structure 全局共享;切视图/勾选变化都调用此函数重建当前视图。 auto rebuildCentral = [scene, rendererPtr, renderWindowPtr, viewMode, &repo, frame, tree, - structure]() { + structure, showCurtain, showVoxel, crs]() { scene->clear(); const bool is2D = (*viewMode == ViewMode::Map2D); rendererPtr->SetBackground(is2D ? 0.96 : 1.0, is2D ? 0.97 : 1.0, is2D ? 0.99 : 1.0); - // 渲染单个 dd_section 数据集到当前视图。 + // 渲染单个 dd_section 数据集:二维=测线线;三维=帘面(受「帘面」图层开关控制)。 auto renderSection = [&](const std::string& id) { const auto g = repo.loadGrid(id); if (is2D) { auto line = geopro::render::buildSurveyLine(g, *frame); if (line) scene->addActor(line); - } else { + } else if (*showCurtain) { const auto cs = repo.loadColorScale(id); auto curtain = geopro::render::buildCurtain(g, cs, *frame); if (curtain) { @@ -315,6 +360,17 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re if (ds.ddType == "dd_section") renderSection(ds.id); } + // 三维「体素」图层:两交叉测线散点经 CRS 配准 IDW 成体素(派生层),与帘面同纵向夸张。 + if (!is2D && *showVoxel && crs) { + const auto profs = repo.loadVoxelScatters(); + const auto vcs = repo.loadScatterColorScale("grid1"); + auto vr = geopro::render::buildVoxelFromScatters(profs, vcs, *crs, *frame); + if (vr.valid()) { + vr.volume->SetScale(1.0, 1.0, kCurtainZScale); + rendererPtr->AddVolume(vr.volume); + } + } + if (is2D) geopro::render::applyTop2D(rendererPtr); else @@ -461,15 +517,43 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re rebuildDetail(); }); - // ── 工具条「二维地图/三维视图」:切换互斥视图 → 按当前勾选集重建对应内容 ── - QObject::connect(act2D, &QAction::triggered, vtkWidget, [viewMode, rebuildCentral]() { - *viewMode = ViewMode::Map2D; - rebuildCentral(); - }); - QObject::connect(act3D, &QAction::triggered, vtkWidget, [viewMode, rebuildCentral]() { - *viewMode = ViewMode::View3D; - rebuildCentral(); - }); + // 「视图详情」浮层显隐:仅三维显示,置于 QVTK 左上(工具条下方)并置顶。 + auto showLayerPanel = [layerPanel, viewToolBar](bool show3D) { + if (show3D) { + layerPanel->move(14, viewToolBar->height() + 12); + layerPanel->adjustSize(); + layerPanel->setVisible(true); + layerPanel->raise(); + } else { + layerPanel->setVisible(false); + } + }; + + // ── 工具条「二维地图/三维视图」:切换互斥视图 → 重建内容 + 图层浮层显隐 ── + QObject::connect(act2D, &QAction::triggered, vtkWidget, + [viewMode, rebuildCentral, showLayerPanel]() { + *viewMode = ViewMode::Map2D; + showLayerPanel(false); + rebuildCentral(); + }); + QObject::connect(act3D, &QAction::triggered, vtkWidget, + [viewMode, rebuildCentral, showLayerPanel]() { + *viewMode = ViewMode::View3D; + showLayerPanel(true); + rebuildCentral(); + }); + + // ──「视图详情」图层勾选 → 更新图层显隐 → 重建中央 ── + QObject::connect(chkCurtain, &QCheckBox::toggled, vtkWidget, + [showCurtain, rebuildCentral](bool on) { + *showCurtain = on; + rebuildCentral(); + }); + QObject::connect(chkVoxel, &QCheckBox::toggled, vtkWidget, + [showVoxel, rebuildCentral](bool on) { + *showVoxel = on; + rebuildCentral(); + }); // ── 启动默认:测线已勾选,但 itemChanged 在 connect 之前触发故未渲染;这里重建一次中央内容。 rebuildCentral(); @@ -502,6 +586,24 @@ int main(int argc, char* argv[]) QSurfaceFormat::setDefaultFormat(QVTKOpenGLStereoWidget::defaultFormat()); QApplication app(argc, argv); + // PROJ 数据(proj.db)定位:体素配准的 CrsTransform 需要。优先已设环境变量; + // 否则按 exe 旁 / 构建目录候选设置。部署时须随包附带 proj 数据并设此变量。 + if (qEnvironmentVariableIsEmpty("PROJ_DATA")) { + const QString appDir = QCoreApplication::applicationDirPath(); + const QStringList candidates = { + appDir + "/proj", + appDir + "/../../vcpkg_installed/x64-windows/share/proj", + QStringLiteral( + "D:/Git/lanbingtech/geopro/build/release/vcpkg_installed/x64-windows/share/proj"), + }; + for (const auto& c : candidates) { + if (QFile::exists(c + "/proj.db")) { + qputenv("PROJ_DATA", c.toUtf8()); + break; + } + } + } + // 网络层:共享会话 ApiClient + 登录编排 AuthService(RSA 公钥从 resources 读取)。 geopro::net::ApiClient api(QStringLiteral("http://tenant.geomative.cn/pop-api")); const std::string pem = readPem("D:/Git/lanbingtech/geopro/resources/rsa_public_key.pem");