diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..4e7bb15 --- /dev/null +++ b/.clang-format @@ -0,0 +1,24 @@ +# 统一代码风格(规约 §10.1:约束 AI 输出风格漂移) +BasedOnStyle: Google +Language: Cpp +Standard: c++17 +ColumnLimit: 100 +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +AccessModifierOffset: -4 +PointerAlignment: Left +DerivePointerAlignment: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterFunction: true + AfterNamespace: false + AfterControlStatement: false +SortIncludes: true +IncludeBlocks: Regroup +NamespaceIndentation: None +FixNamespaceComments: true diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..32069ef --- /dev/null +++ b/.clangd @@ -0,0 +1,18 @@ +# clangd 读取编译数据库,为 IDE / AI 工具提供精确类型上下文(规约 §10.1) +CompileFlags: + CompilationDatabase: build/debug + +Diagnostics: + UnusedIncludes: Strict + ClangTidy: + Add: + - bugprone-* + - performance-* + - modernize-* + Remove: + - modernize-use-trailing-return-type + +# 架构铁律(供人 / AI 参阅,设计 §3): +# - core 绝不 include Qt/VTK +# - VTK actor / RenderWindow 仅由 render 层持有;view 不 new actor +# - 信号槽连接集中于 *Controller::wireUp() diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e1c9428 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +[*.{json,yml,yaml}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c568d71 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.21) +project(geopro_desktop LANGUAGES CXX) + +# ---- Global C++ settings (规约 §3.2: C++17, 可渐进 C++20) ---- +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Qt 自动工具(仅 view/app 层需要 moc/uic/rcc) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +if(MSVC) + add_compile_options(/utf-8 /MP /W4 /permissive-) +endif() + +# ---- Dependencies(全 vcpkg;见 vcpkg.json / docs/ENV_SETUP_Windows.md)---- +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Network Sql Concurrent) +find_package(VTK REQUIRED) # 含 GUISupportQt(vtk[qt])→ QVTKOpenGLStereoWidget +# 以下依赖随分层逐步启用(spike 后接入业务层): +# find_package(GDAL CONFIG REQUIRED) +# find_package(PROJ CONFIG REQUIRED) +# find_package(Eigen3 CONFIG REQUIRED) +# find_package(spdlog CONFIG REQUIRED) +# find_package(nlohmann_json CONFIG REQUIRED) +# find_package(OpenSSL REQUIRED) +# find_package(Qt6Keychain CONFIG REQUIRED) +# find_package(qtadvanceddocking-qt6 CONFIG REQUIRED) # ADS, spike#2 接入(端口名以实际为准) + +add_subdirectory(src) + +enable_testing() +add_subdirectory(tests) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..0b9a338 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,29 @@ +{ + "version": 3, + "cmakeMinimumRequired": { "major": 3, "minor": 21, "patch": 0 }, + "configurePresets": [ + { + "name": "msvc-debug", + "displayName": "MSVC Debug (vcpkg)", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "VCPKG_TARGET_TRIPLET": "x64-windows", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "msvc-release", + "displayName": "MSVC Release (vcpkg)", + "inherits": "msvc-debug", + "binaryDir": "${sourceDir}/build/release", + "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } + } + ], + "buildPresets": [ + { "name": "debug", "configurePreset": "msvc-debug" }, + { "name": "release", "configurePreset": "msvc-release" } + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..e883264 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Geopro 3.0 桌面客户端 + +地球物理勘探数据可视化桌面客户端(Qt 6 + VTK 9,C++)。复刻 Geopro 3.0 核心「项目分析视图」。 + +> M1 范围、架构与决策见 **[docs/superpowers/specs/2026-06-07-geopro-desktop-m1-design.md](docs/superpowers/specs/2026-06-07-geopro-desktop-m1-design.md)**。 +> 上位技术基线见 **[docs/Geopro3.0_技术选型与架构规约.md](docs/Geopro3.0_技术选型与架构规约.md)**。 +> 环境搭建见 **[docs/ENV_SETUP_Windows.md](docs/ENV_SETUP_Windows.md)**。 + +## 技术栈 + +Qt 6.8 LTS(QtWidgets)+ VTK 9.3+ · CMake + vcpkg(全量,含 Qt)· MSVC 2022 / C++17 · ADS 停靠 · GDAL/PROJ · OpenSSL · QtKeychain。 + +## 目录(设计 §3) + +``` +src/core/ 纯业务,零 Qt/VTK +src/data/ Repository + 解析器 +src/net/ ApiClient / AuthService / Credential +src/render/ VTK 场景与 actor(单一场景 + 相机预设) +src/view/ QtWidgets 面板 +src/controller/ 联动编排 +src/app/ 入口 + 主窗(M1 spike:Qt+VTK 冒烟程序) +tests/ gtest / Qt Test +tools/ 离线验证脚本(validate_samples.py) +docs/ 规约、API、样本数据、设计文档 +``` + +## 快速开始 + +前置:VS2022(C++ 桌面开发)、Git、vcpkg(设 `VCPKG_ROOT`)。详见 ENV_SETUP_Windows.md。 + +```powershell +# x64 Native Tools 命令行,项目根 +vcpkg x-update-baseline --add-initial-baseline # 锁依赖版本 +cmake --preset msvc-debug # 首次编译 Qt+VTK,较久 +cmake --build build/debug +.\build\debug\src\app\geopro_desktop.exe # spike 冒烟:应显示一个锥体 +ctest --test-dir build/debug # 运行单测 +``` + +## 当前状态 + +M1 设计完成(v2,经双专家评审)。进入 **spike 预研**(设计 §15):① 全 vcpkg 构建/部署 ② ADS + QVTKOpenGLStereoWidget 停靠稳定 ③ 真实样本跑通 banded contour。spike 通过后展开完整实现计划。 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..bad9a5e --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,11 @@ +# 分层(设计 §3)。M1 spike 阶段先只构建 app 冒烟程序; +# 随实现推进逐层启用为静态库并由 app 链接: +# +# add_subdirectory(core) # 纯业务,零 Qt/VTK +# add_subdirectory(data) # Repository + 解析器 +# add_subdirectory(net) # ApiClient / AuthService / Credential +# add_subdirectory(render) # VTK 场景与 actor +# add_subdirectory(view) # QtWidgets 面板 +# add_subdirectory(controller) # 联动编排 +# +add_subdirectory(app) diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt new file mode 100644 index 0000000..c65faca --- /dev/null +++ b/src/app/CMakeLists.txt @@ -0,0 +1,21 @@ +# M1 spike 冒烟程序:QApplication + QMainWindow + QVTKOpenGLStereoWidget(锥体) +# 目的:验证「全 vcpkg:Qt + VTK[qt] 共用一份 Qt」可编译、可运行、可渲染(spike#1)。 +# ADS 停靠(spike#2)在此基础上增量接入。 + +add_executable(geopro_desktop WIN32 main.cpp) + +target_link_libraries(geopro_desktop PRIVATE + Qt6::Core Qt6::Gui Qt6::Widgets + ${VTK_LIBRARIES} +) + +# VTK 9 模块需 autoinit,否则渲染后端/工厂未注册,运行期黑屏或报错 +vtk_module_autoinit(TARGETS geopro_desktop MODULES ${VTK_LIBRARIES}) + +# 运行期 DLL 自动拷贝(单一 vcpkg 链路,规避双 Qt;设计 §11) +if(WIN32) + add_custom_command(TARGET geopro_desktop POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ $ + COMMAND_EXPAND_LISTS) +endif() diff --git a/src/app/main.cpp b/src/app/main.cpp new file mode 100644 index 0000000..b900b6f --- /dev/null +++ b/src/app/main.cpp @@ -0,0 +1,55 @@ +// M1 spike 冒烟程序(设计 §15 spike#1/#2 起点)。 +// +// 验证目标: +// 1) 全 vcpkg 下 Qt6 + VTK[qt] 共用同一份 Qt,可编译/链接/运行(无双 Qt 冲突)。 +// 2) QVTKOpenGLStereoWidget(QOpenGLWidget 系,ADS reparent 友好)能在 Qt 窗口里渲染。 +// +// 跑通后:在此基础上接入 ADS 停靠(spike#2)、再展开 render/view 分层(实现计划)。 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // VTK 要求在创建任何 QVTKOpenGL* 前设置默认 surface format + QSurfaceFormat::setDefaultFormat(QVTKOpenGLStereoWidget::defaultFormat()); + + QApplication app(argc, argv); + + QMainWindow window; + window.setWindowTitle(QStringLiteral("Geopro 3.0 — spike smoke test (Qt + VTK)")); + window.resize(1024, 720); + + auto* vtkWidget = new QVTKOpenGLStereoWidget(&window); + window.setCentralWidget(vtkWidget); + + vtkNew renderWindow; + vtkWidget->setRenderWindow(renderWindow); + + vtkNew cone; + cone->SetResolution(32); + + vtkNew mapper; + mapper->SetInputConnection(cone->GetOutputPort()); + + vtkNew actor; + actor->SetMapper(mapper); + + vtkNew renderer; + renderer->AddActor(actor); + renderer->SetBackground(0.12, 0.13, 0.16); // 科学软件深色背景 + renderer->ResetCamera(); + renderWindow->AddRenderer(renderer); + + window.show(); + return app.exec(); +} diff --git a/src/controller/README.md b/src/controller/README.md new file mode 100644 index 0000000..8155110 --- /dev/null +++ b/src/controller/README.md @@ -0,0 +1,7 @@ +# controller — 联动编排层 + +按交互闭环拆分(避免 God Object,设计 §3)。信号槽连接集中于各 controller 的 `wireUp()`。 + +- `SelectionController` — 勾选 / 选中状态 +- `RenderSyncController` — 状态 → Scene 渲染同步(勾选 GS/TM → 按 dd 类型筛选 ds → 渲染) +- `DetailSyncController` — 列表 ↔ 详情 ↔ 视图定位三向联动(含 render 拾取回流) diff --git a/src/core/README.md b/src/core/README.md new file mode 100644 index 0000000..a7319b2 --- /dev/null +++ b/src/core/README.md @@ -0,0 +1,8 @@ +# core — 纯业务层 + +**铁律:绝不 `#include` 任何 Qt / VTK 头**(含 `IInterpolator`,返回 `core::ScalarVolume`)。可独立单元测试。 + +子目录(设计 §3): +- `model/` — 领域模型:Project, GsObject, TmObject, DsObject, Anomaly, ColorScale, Grid, ScatterField, ScalarVolume +- `geo/` — LocalFrame(原点 + Z 基准 + 轴向)、CrsTransform(PROJ 封装,多 CRS) +- `algo/` — IInterpolator 接口 + IdwInterpolator(返回 core 中立的 ScalarVolume) diff --git a/src/data/README.md b/src/data/README.md new file mode 100644 index 0000000..4e706df --- /dev/null +++ b/src/data/README.md @@ -0,0 +1,11 @@ +# data — 数据访问层 + +Repository 抽象(**异步契约**:QFuture/回调 + 取消 + 分页),DTO 与领域模型分离。 + +子目录(设计 §3、§6): +- `repo/` — IProjectRepository, IDatasetRepository +- `local/` — LocalSampleRepository(M1,QtConcurrent 跑解析)+ 各格式解析器 +- `api/` — ApiRepository(M1 骨架,签名对齐 pop-api) +- `dto/` — 后端 JSON DTO + → model 映射 + +解析约定见设计 §6.1(v 为 [j=y][i=x]、east/north 名值颠倒、影像 EPSG:3857 等)。 diff --git a/src/net/README.md b/src/net/README.md new file mode 100644 index 0000000..54ad8b9 --- /dev/null +++ b/src/net/README.md @@ -0,0 +1,7 @@ +# net — 网络与认证层 + +- `ApiClient` — QtNetwork 封装:基址 `http://tenant.geomative.cn/pop-api`、注入 `geomativeauthorization: Geomative ` 头、超时、错误码、401 处理。 +- `AuthService` — 验证码(getImageCode/verifyCodeCheck)+ **JSEncrypt RSA-2048 加密密码** + `login2`(token = `data.accessToken`)。 +- `Credential` — QtKeychain 凭证存取(严禁明文,规约 §7.4)。 + +详见设计 §8(含 §8.3 前置确认项:RSA 公钥常量、token 生命周期)。 diff --git a/src/render/README.md b/src/render/README.md new file mode 100644 index 0000000..4ec49f7 --- /dev/null +++ b/src/render/README.md @@ -0,0 +1,13 @@ +# render — VTK 渲染层 + +**独占 `vtkRenderWindow`,统一创建/持有所有 actor**(view 不持有 actor)。单一场景 + 相机预设(设计 §4)。 + +子目录: +- `Scene` — 场景图、项目世界坐标空间、可见性;持有 RenderWindow +- `actors/` — ScatterActor, GridContourActor, VoxelVolumeActor, AnomalyActor, TerrainActor +- `color/` — ColorLutBuilder(colorBar → 离散 vtkLookupTable), ScalarBar +- `camera/` — CameraPreset(Top2D / Free3D) +- `interact/` — InteractionManager + InteractionTool(Measure/Slice/PickSelect);切片用 vtkResliceCursorWidget +- `ground/` — IGroundLayer + DemImageGroundLayer(M1);TileGroundLayer(M1.5) + +网格管线:`vtkImageData(+vtkWarpScalar) → vtkDataSetSurfaceFilter → vtkBandedPolyDataContourFilter(GenerateContourEdgesOn)`(设计 §4.3)。 diff --git a/src/view/README.md b/src/view/README.md new file mode 100644 index 0000000..6eafb19 --- /dev/null +++ b/src/view/README.md @@ -0,0 +1,10 @@ +# view — QtWidgets 视图层 + +被动视图。持有 `QVTKOpenGLStereoWidget` 外壳(不 new actor),把交互事件注入 render、把拾取/选择回流给 controller。 + +子目录: +- `login/` — LoginWindow(样式参考 web 登录页) +- `panels/` — ObjectTreePanel, DatasetListPanel, MapViewPanel(QVTKOpenGLStereoWidget), DataDetailPanel, AnomalyPanel, ObjectPropertyPanel, PropertyPanel +- `widgets/` — ColorScaleEditor, ToolbarBits + +布局用 ADS 停靠(设计 §9);VTK 面板默认不可浮动(缓解 reparent 上下文丢失)。 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..ef605af --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,10 @@ +# 单元测试(设计 §12)。M1 spike 阶段先放一个 gtest 冒烟用例, +# 验证 gtest 经 vcpkg 接入、ctest 可跑;随 core/data/algo 实现补充真实用例。 + +find_package(GTest CONFIG REQUIRED) + +add_executable(geopro_tests smoke_test.cpp) +target_link_libraries(geopro_tests PRIVATE GTest::gtest GTest::gtest_main) + +include(GoogleTest) +gtest_discover_tests(geopro_tests) diff --git a/tests/smoke_test.cpp b/tests/smoke_test.cpp new file mode 100644 index 0000000..a47f0cd --- /dev/null +++ b/tests/smoke_test.cpp @@ -0,0 +1,10 @@ +// gtest 冒烟:验证测试工具链(vcpkg gtest + ctest)已就绪。 +// 随 core/data/algo 实现,逐步替换为真实用例 +//(坐标 rebase/轴向、colorBar LUT 映射、v[j][i] 灌点序、IDW 正确性 等,设计 §12)。 + +#include + +TEST(SmokeTest, ToolchainWorks) +{ + EXPECT_EQ(1 + 1, 2); +} diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..03b07c2 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,28 @@ +{ + "name": "geopro-desktop", + "version": "0.1.0", + "description": "Geopro 3.0 desktop client (Qt6 + VTK9) - M1", + "dependencies": [ + { + "name": "qtbase", + "default-features": false, + "features": ["gui", "widgets", "network", "sql", "sql-sqlite", "concurrent", "opengl"] + }, + "qttools", + { + "name": "vtk", + "default-features": false, + "features": ["qt", "opengl", "gdal", "proj"] + }, + "gdal", + "proj", + "eigen3", + "spdlog", + "fmt", + "nlohmann-json", + "openssl", + "qtkeychain", + "qt-advanced-docking-system", + "gtest" + ] +}