geopro/docs/superpowers/plans/2026-06-07-m1-phase0-spikes.md

14 KiB
Raw Blame History

M1 Phase 0环境引导 + Spike 预研 实现计划

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 证明全 vcpkg(Qt+VTK[qt] 共用一份 Qt)工具链可编译/运行/部署,并跑通三个最高风险技术点(构建部署、ADS+QVTK 停靠稳定、真实样本 banded contour 渲染),作为展开完整 M1 实现的前置门槛。

Architecture: 在已铺好的工程骨架(src/app 冒烟程序)上,先打通构建与部署,再增量接入 ADS,最后用一个独立 render spike 复刻设计图 #18(网格等值面)验证 VTK 管线选型。每个 spike 有明确的「通过判据」。

Tech Stack: CMake 3.21+ / vcpkg(manifest) / MSVC 2022 / Qt 6.8 / VTK 9.3+ / ADS / GoogleTest。

⚠️ 本计划部分步骤将按「方案②-修订」更新(官方 MSVC Qt + 源码 VTK + vcpkg 非 Qt 依赖)。当前 Task 1/3 的"全 vcpkg"描述待改写;权威构建步骤见设计 §11 + docs/ENV_SETUP_Windows.md(§4/§5)。核心差异:Qt 用官方 MSVC kit(不 vcpkg)、VTK 源码编到 external/vtk-install、ADS/QtKeychain 走 FetchContent。

前置条件(由你完成,见 docs/ENV_SETUP_Windows.md): 已装 VS18(MSVC 14.51)、Git、vcpkg 并设 VCPKG_ROOT官方 Qt 6.11.1 的 MSVC 2022 64-bit kit。所有命令在 x64 Native Tools 命令行、项目根目录执行。

通过判据汇总(三 spike 全绿才进入 Phase 1):

  • S1 构建/部署:cmake --buildgeopro_desktop.exe,运行显示锥体;exe 目录只有一份 Qt6*.dll;ctest 通过。
  • S2 ADS+QVTK:VTK 面板可停靠/浮动/重停靠,无黑屏/崩溃,相机交互正常。
  • S3 渲染管线:用真实 剖面网格数据1.txt + 色阶,VTK 渲染出 banded 等值面+等值线,目视对照 docs/_validate/ref_18_grid.png 一致。

Task 1vcpkg 基线锁定 + 首次构建(Spike S1构建)

Files:

  • Modify: vcpkg.json(写入 builtin-baseline)

  • Step 1: 锁定依赖基线

Run:

vcpkg x-update-baseline --add-initial-baseline

Expected: vcpkg.json 新增 "builtin-baseline": "<40位SHA>"git diff vcpkg.json 可见该字段。

  • Step 2: 首次 configure(会编译 Qt+VTK,耗时长)

Run:

cmake --preset msvc-debug

Expected: 末尾 -- Configuring done / -- Generating done。首次会构建 qtbase/vtk 等,可能数十分钟到数小时(建议先配 VCPKG_BINARY_SOURCES 缓存)。 若报 qt-advanced-docking-system 端口不可用 → 暂时从 vcpkg.json 移除该项重跑(ADS 改 Task 3 用 FetchContent),记录该事实。

  • Step 3: 构建冒烟程序

Run:

cmake --build build/debug --target geopro_desktop

Expected: 生成 build/debug/src/app/geopro_desktop.exe,无编译/链接错误。

  • Step 4: 运行,肉眼验证渲染

Run:

.\build\debug\src\app\geopro_desktop.exe

Expected: 弹出深色背景窗口,中央显示一个白色锥体,可被默认 VTK 交互器旋转。

  • Step 5: 验证单一 Qt(无双份)

Run:

Get-ChildItem build\debug\src\app\Qt6Core.dll | Select-Object FullName
Get-ChildItem build\debug\src\app -Filter "Qt6*.dll" | Measure-Object | Select-Object Count

Expected: 仅一份来自 vcpkg 的 Qt6Core.dll(路径在 exe 目录);无另一处官方 Qt 混入。

  • Step 6: 提交
git add vcpkg.json
git commit -m "build: 锁定 vcpkg 基线, 打通全 vcpkg Qt+VTK 构建(spike S1)"

Task 2gtest 工具链冒烟(Spike S1测试)

Files:

  • 已存在 tests/CMakeLists.txt / tests/smoke_test.cpp(脚手架)

  • Step 1: 构建测试目标

Run:

cmake --build build/debug --target geopro_tests

Expected: 生成 build/debug/tests/geopro_tests.exe,无错误。

  • Step 2: 运行 ctest

Run:

ctest --test-dir build/debug --output-on-failure

Expected: 100% tests passed(SmokeTest.ToolchainWorks)。

  • Step 3: 提交(若有 CMake 调整)
git commit -am "test: 验证 gtest+ctest 工具链(spike S1)" --allow-empty

Task 3ADS 停靠集成 + VTK 面板稳定性(Spike S2)

Files:

  • Modify: src/app/CMakeLists.txt(链接 ADS)

  • Modify: src/app/main.cpp(用 ADS CDockManager 承载 VTK 面板)

  • Modify: CMakeLists.txt(find_package ADS 或 FetchContent)

  • Step 1: 在 CMake 接入 ADS

CMakeLists.txt 顶层依赖区追加(端口名以 vcpkg 实际为准,二选一):

# 优先 vcpkg 端口(名称在 spike 期确认,常见为 qtadvanceddocking-qt6
find_package(qtadvanceddocking-qt6 CONFIG QUIET)
if(NOT qtadvanceddocking-qt6_FOUND)
  include(FetchContent)
  FetchContent_Declare(ads
    GIT_REPOSITORY https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System.git
    GIT_TAG 4.3.1)
  set(ADS_VERSION 4.3.1 CACHE STRING "" FORCE)
  set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
  FetchContent_MakeAvailable(ads)
endif()

Expected: configure 时 ADS 可被找到或拉取。

  • Step 2: app 链接 ADS

src/app/CMakeLists.txttarget_link_libraries(geopro_desktop PRIVATE ...) 追加 ADS 目标(名称二选一,configure 报错时换):

target_link_libraries(geopro_desktop PRIVATE ads::qt6advanceddocking)
# 若上者不存在,试 ads::ads 或 qt6advanceddocking
  • Step 3: 改 main.cpp:用 ADS 承载 VTK 面板

src/app/main.cppsetCentralWidget 替换为 ADS 停靠管理器,VTK widget 放入一个 DockWidget:

#include <DockManager.h>
#include <DockWidget.h>
#include <DockAreaWidget.h>
// ... main() 内,创建 vtkWidget 后:
auto* dockManager = new ads::CDockManager(&window);
auto* vtkDock = new ads::CDockWidget(QStringLiteral("三维视图"));
vtkDock->setWidget(vtkWidget);
dockManager->addDockWidget(ads::CenterDockWidgetArea, vtkDock);
// 再加两个占位 dock 以便测试停靠/浮动:
auto* leftDock = new ads::CDockWidget(QStringLiteral("对象列表"));
leftDock->setWidget(new QLabel(QStringLiteral("(占位)")));
dockManager->addDockWidget(ads::LeftDockWidgetArea, leftDock);
// 移除原 window.setCentralWidget(vtkWidget);

(顶部补 #include <QLabel>。)

  • Step 4: 构建并运行

Run:

cmake --build build/debug --target geopro_desktop
.\build\debug\src\app\geopro_desktop.exe

Expected: 窗口出现「对象列表」「三维视图」两个停靠面板,三维面板里是锥体。

  • Step 5: 手工稳定性测试(S2 判据)

操作:把「三维视图」面板拖出浮动 → 再拖回停靠 → 切换到不同停靠区,反复 3 次。 Expected: 每次重停靠后锥体正常渲染、不黑屏、不崩溃。若黑屏 → 记录,按设计 §K-9 改用 QVTKOpenGLStereoWidget(已在用)并将 VTK 面板设为不可浮动 vtkDock->setFeature(ads::CDockWidget::DockWidgetFloatable, false) 后复测。

  • Step 6: 提交
git add -A
git commit -m "feat(app): ADS 停靠承载 VTK 面板, 验证浮动/重停靠稳定(spike S2)"

Task 4真实样本 banded contour 渲染(Spike S3)

验证设计 §4.3 网格管线选型:vtkImageData(+warp) → vtkDataSetSurfaceFilter → vtkBandedPolyDataContourFilter(GenerateContourEdgesOn) 能复刻图 #18。本任务产出一个独立小程序,代码后续移入 render/actors/GridContourActor

Files:

  • Create: tests/spike/grid_contour_spike.cpp

  • Create: tests/spike/CMakeLists.txt

  • Modify: tests/CMakeLists.txt(add_subdirectory spike)

  • 读数据:docs/剖面网格数据的色阶数据2等文件/剖面网格数据1.txt剖面网格数据的色阶数据1.txt

  • Step 1: 写 spike 程序(读网格 JSON → vtkImageData → banded contour → 截图)

Create tests/spike/grid_contour_spike.cpp:

// Spike S3用真实网格样本验证 banded contour 管线,离屏渲染保存 PNG
// 目视对照 docs/_validate/ref_18_grid.png。
#include <fstream>
#include <vector>
#include <string>
#include <nlohmann/json.hpp>

#include <vtkNew.h>
#include <vtkImageData.h>
#include <vtkDoubleArray.h>
#include <vtkPointData.h>
#include <vtkDataSetSurfaceFilter.h>
#include <vtkBandedPolyDataContourFilter.h>
#include <vtkLookupTable.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkWindowToImageFilter.h>
#include <vtkPNGWriter.h>

using json = nlohmann::json;

int main(int argc, char** argv) {
  const std::string dir = "../../docs/剖面网格数据的色阶数据2等文件/";  // 相对 build/debug/tests/spike
  std::ifstream gf(dir + "剖面网格数据1.txt");
  json g = json::parse(gf)["data"];
  auto x = g["x"].get<std::vector<double>>();   // 100, 规则
  auto y = g["y"].get<std::vector<double>>();   // 22, 规则
  auto v = g["v"].get<std::vector<std::vector<double>>>();  // [22][100] = [j=y][i=x]
  const int nx = (int)x.size(), ny = (int)y.size();
  const double dx = x[1] - x[0], dy = y[1] - y[0];

  vtkNew<vtkImageData> img;
  img->SetDimensions(nx, ny, 1);
  img->SetOrigin(x[0], y[0], 0.0);
  img->SetSpacing(dx, dy, 1.0);
  vtkNew<vtkDoubleArray> sc;
  sc->SetName("v");
  sc->SetNumberOfTuples(nx * ny);
  for (int j = 0; j < ny; ++j)
    for (int i = 0; i < nx; ++i)
      sc->SetValue(j * nx + i, v[j][i]);   // i 最快,匹配 vtkImageData 点序
  img->GetPointData()->SetScalars(sc);

  // 色阶 → 离散 LUT
  std::ifstream cf(dir + "剖面网格数据的色阶数据1.txt");
  json cb = json::parse(cf)["data"]["properties"]["colorBar"];
  std::vector<double> stops;
  for (auto& pr : cb) stops.push_back(std::stod(pr[0].get<std::string>()));
  std::sort(stops.begin(), stops.end());

  vtkNew<vtkDataSetSurfaceFilter> surf;
  surf->SetInputData(img);

  vtkNew<vtkBandedPolyDataContourFilter> banded;
  banded->SetInputConnection(surf->GetOutputPort());
  banded->GenerateValues((int)stops.size(), stops.front(), stops.back());
  banded->GenerateContourEdgesOn();   // 同时产出等值线边

  vtkNew<vtkLookupTable> lut;
  lut->SetNumberOfTableValues((int)stops.size());
  lut->SetTableRange(stops.front(), stops.back());
  // 颜色解析略spike 阶段可先用 lut->Build() 默认色带验证管线,色彩精确映射放 render 层
  lut->Build();

  vtkNew<vtkPolyDataMapper> mapper;
  mapper->SetInputConnection(banded->GetOutputPort());
  mapper->SetScalarModeToUseCellData();
  mapper->SetLookupTable(lut);
  mapper->SetScalarRange(stops.front(), stops.back());

  vtkNew<vtkActor> actor; actor->SetMapper(mapper);
  vtkNew<vtkRenderer> ren; ren->AddActor(actor); ren->SetBackground(1,1,1); ren->ResetCamera();
  vtkNew<vtkRenderWindow> rw; rw->SetOffScreenRendering(1); rw->AddRenderer(ren); rw->SetSize(1100, 320); rw->Render();

  vtkNew<vtkWindowToImageFilter> w2i; w2i->SetInput(rw);
  vtkNew<vtkPNGWriter> png; png->SetFileName("spike_grid_contour.png");
  png->SetInputConnection(w2i->GetOutputPort()); png->Write();
  return 0;
}
  • Step 2: 写 spike 的 CMake

Create tests/spike/CMakeLists.txt:

find_package(nlohmann_json CONFIG REQUIRED)
add_executable(grid_contour_spike grid_contour_spike.cpp)
target_link_libraries(grid_contour_spike PRIVATE ${VTK_LIBRARIES} nlohmann_json::nlohmann_json)
vtk_module_autoinit(TARGETS grid_contour_spike MODULES ${VTK_LIBRARIES})

tests/CMakeLists.txt 末尾追加:

add_subdirectory(spike)
  • Step 3: 构建

Run:

cmake --build build/debug --target grid_contour_spike

Expected: 生成 build/debug/tests/spike/grid_contour_spike.exe,编译通过(关键:证明 vtkImageData→surface→banded contour 管线类型正确,设计 §4.3 的 B-1/B-2 修正成立)。

  • Step 4: 运行并对照

Run:

cd build\debug\tests\spike
.\grid_contour_spike.exe
cd ..\..\..\..

Expected: 生成 build/debug/tests/spike/spike_grid_contour.png,呈现网格的 banded 填色面 + 等值线,形态与 docs/_validate/ref_18_grid.png 一致(色彩精确映射后续在 render 层做,此处只验证管线与形态)。

  • Step 5: 提交
git add tests/spike/ tests/CMakeLists.txt
git commit -m "spike(S3): 真实样本验证 vtkImageData->surface->banded contour 管线(图#18)"

Task 5Spike 门槛结论

Files:

  • Create: docs/superpowers/plans/2026-06-07-m1-phase0-spike-report.md

  • Step 1: 记录三 spike 结果

写一份简短报告,逐项记录 S1/S2/S3 是否通过、实际遇到的偏差(如 ADS 端口名、是否需禁浮动、是否需调 VTK 模块),以及对设计文档的回写项(若有)。

  • Step 2: 门槛判定

  • 三项全绿 → 标记 Phase 0 完成,进入 Phase 1(写 core 基础层实现计划)。

  • 任一红 → 按报告中的偏差更新设计 §4/§9/§11/§15,调整后复测,不进入 Phase 1

  • Step 3: 提交

git add docs/superpowers/plans/2026-06-07-m1-phase0-spike-report.md
git commit -m "docs: Phase 0 spike 结论与门槛判定"

后续计划(spike 通过后逐份编写)

  1. Phase 1 — core 基础层:geo(LocalFrame/CrsTransform + PROJ,轴向/Z 基准/rebase)、algo(IdwInterpolator→ScalarVolume)、model、colorBar→LUT 逻辑。纯 C++ + gtest,以 tools/validate_samples.py 产出为地面真值。
  2. Phase 2 — data 层:Repository(异步契约)+ LocalSampleRepository + 各格式解析器 + DTO 映射。
  3. Phase 3 — net/登录:ApiClient + AuthService(验证码+JSEncrypt RSA+login2)+ QtKeychain;登录页样式复刻。
  4. Phase 4 — render 层:Scene/actors(散点/网格/异常/体素/地形)/相机预设/交互工具/切片。
  5. Phase 5 — view+controller:ADS 三区工作台 + 七面板 + 三 controller 联动。
  6. Phase 6 — 集成与 M1 验收(对照设计 §13 M1-a/M1-b)。

Self-Review 备注

  • 覆盖:本计划覆盖设计 §15 三个 spike + 工具链/测试 bring-up;不涉及业务实现(留后续 Phase)。
  • 无占位:命令与 spike 代码均为具体内容;ADS 目标名/端口名标注了「configure 报错时切换」的真实不确定性(spike 的本职)。
  • 类型一致:spike 代码中 v[j][i](j=y,i=x)灌点序与设计 §6.1 一致;vtkImageData→DataSetSurfaceFilter→BandedPolyDataContourFilter 与设计 §4.3 一致。