Commit Graph

98 Commits

Author SHA1 Message Date
gaozheng 5dbbb2576c feat(render): brick 分页器(LRU 工作集,内存恒定核外渲染)
实现 geopro::render::BrickPager:驻留 ≤ budget 个解压块,
按 LRU 淘汰,与体总大小无关。requestVisible 按请求顺序更新
LRU 并淘汰至预算;get 命中返回数据指针、不改 LRU。
键为完整 BrickId(含 level);std::list 记录 recency +
unordered_map 存数据与迭代器,touch/淘汰均 O(1)。
TDD:test_brick_pager 验证恒定驻留与最早块淘汰。
2026-06-23 12:08:29 +08:00
gaozheng 86e2b6b8a8 fix(store): brickRange 用 hasRange 标志替代 (0,0) 哨兵
(0,0) 是合法值域(真实全零块,kBlank=INT16_MIN 非 0),旧实现用
(vmin==0&&vmax==0) 当未计算哨兵会误判,导致全零块每次 brickRange
都无谓解压重算,且 buildPyramid 后仍走惰性。

- BrickEntry 加 bool hasRange 显式标志
- brickRange: hasRange 真→直接返回;假→惰性算并就地缓存(mutable levels_)
- meta.json 序列化/反序列化带 hasRange(老 store 缺字段→false,惰性兼容)
- buildPyramid 回填值域时一并置 hasRange=true
- 补测试:真实全零块 brickRange 返回 (0,0) 不退化(金字塔/老 store 两路)
2026-06-23 12:02:17 +08:00
gaozheng c21226a3d7 fix(detail): measurement 对话框/工具条视觉返工对齐原版
以原版 web 为准返工 measurement 散点交互视觉:
- 数据过滤:1000px;左直方图(hover柱变红+tooltip)+右信息区(数值范围/占比/原始点数/
  当前点数橙色高亮)+底部双手柄范围滑块(新增RangeSlider)+计算分布/重置;min/max输入
  最大在上最小在下;三方联动(输入↔滑块↔直方图)
- 另存为(RawData):280px、标题"数据另存为"、确认/取消
- 色阶/另存/过滤成功 toast
- 信息面板 A红/B蓝/M绿/N橙(#F4B008);tooltip"查看散点属性"/"散点的点选"
- X/Y/V/值类型下拉固定宽 120/160/160/120;无高程禁用 X/Y
- 导出置工具条最右(页头HeaderAction跨ddCode静态)

API 字段未动。build all 绿。
2026-06-23 11:53:13 +08:00
gaozheng 687edfeca1 feat(store): ChunkedVolumeStore 增加多分辨率金字塔与每块 min/max
- buildPyramid(levels): level 0 全分辨率(复用 data.bin),level 1..levels
  逐级 2x 平均降采样(ceil(n/2));非 blank 取均值 round,全 blank 置 kBlank。
  各级独立 data_L<level>.bin + 逐块 qCompress。
- 每级每块计算并存 (min,max)(跳过 kBlank;全 blank 块 = (kBlank,kBlank)),
  写入 meta.json 的 levels 数组并回填原 bricks 索引。
- 新增 levels()/bricksX|Y|Z(level)/dims(level)/readBrick(level,...)/
  brickRange(level,...);保留兼容重载 readBrick(bx,by,bz)==readBrick(0,...)。
- 不破坏 Task 6/8:write/readBrick(bx,by,bz)/meta/bricksXYZ() 语义不变;
  老 store 无 levels 时 levels()=1,brickRange(0,...) 惰性读块计算。
2026-06-23 11:52:47 +08:00
gaozheng c15555dd8a feat(io/gpr): 多通道 .iprb+.ord 装配 GprSurvey
assembleGprSurvey 把一条测线若干通道 .iprb(同名 .iprh)+.ord 装配为
geopro::core::GprSurvey:校验各通道 samples 一致、ntraces 取最小值对齐、
按 .ord 横偏 Y 升序重排通道(values 同步置换)、x0/z0=0、dx=道距、
dz=depthOfSample(1,h);通道数与 .ord 有效通道数不符抛 runtime_error。
索引 64 位。纯 C++17,零 Qt/VTK。
2026-06-23 11:36:56 +08:00
gaozheng cc3c5bf755 feat(render): IVolumeRenderSource 接缝 + WholeVolumeSource(B) 整卷重组
新增 B/C 共用的体渲染数据源接口 IVolumeRenderSource(meta/update/
currentImages/sliceSource),并实现 B 的 WholeVolumeSource:构造时读
ChunkedVolumeStore,遍历所有 brick 按全局坐标(vtkIdType 64 位)重组为
整卷 VTK_SHORT vtkImageData(含边缘块),供整卷体绘制与切片 reslice。

VoxelActor 新增 buildVoxelI16FromImage 重载:直接以预构建 VTK_SHORT
图像成体,传函/着色复用量化域逻辑(抽出 assembleVolumeI16),不改动
Task 7 现有 buildVoxel/buildVoxelI16 行为。

geopro_render 链 geopro_store;新增 test_whole_volume_source 校验
dims/类型/边缘块重组位置。
2026-06-23 11:23:37 +08:00
gaozheng b362156364 feat(render): VoxelActor 新增 buildVoxelI16 量化域体绘制
int16 量化体经 vtkShortArray 填 vtkImageData,vtkSmartVolumeMapper GPU 体绘制。
传递函数在量化域取控制点:qmin/qmax=q.toQ(vmin/vmaxPhys),颜色对每量化级用
q.toPhys 反查物理值再经 ColorScale 取色;kBlank→不透明度0(透明)。抽 assembleVolume
公用 mapper/property 配置,double 版 buildVoxel 行为不变。附无窗冒烟测试。
2026-06-23 11:12:25 +08:00
gaozheng d908556166 feat(store): GPR 三维体分块压缩落盘 ChunkedVolumeStore
新增 geopro_store 库(B/C 方案共用基座):int16 体逐块 qCompress 压缩写入
data.bin + nlohmann-json sidecar(meta.json 记几何/量化/逐块偏移索引)。
write/readMeta/readBrick 三接口 + 边缘块(< brick)支持;偏移/长度全程 64 位。
不引入 vtkHDFWriter,不加 vcpkg 依赖(压缩用 QtCore 自带 zlib)。
2026-06-23 11:00:47 +08:00
gaozheng 8f167b62c9 fix(detail): 白化 tmObjectId 经 open 链路从数据集列表透传(修模板列表为空)
白化「白化模板/模型」方式文件列表为空:原 plan A 用 getDsObjectDetail(dsId)
取 structParentId,实测该响应不含此字段。原版 web 取自数据集行所属 TM
(dsFileRow.structParentId)。

改为 plan B:tmObjectId 从 datasetsLoaded(tmObjectId) 存入树节点新角色
kDsTmObjectIdRole,双击/右键打开时读出,经 openDataset→datasetOpened 信号
→Panel→Page→DetailViewFactory→GridDataChartView 透传,openWhitening 直接用,
删除 getDsObjectDetail 懒拉。

build all(app 链接) + test 全绿,320/320。
2026-06-23 10:51:12 +08:00
gaozheng a9e8eb9d5c feat(core): GPR 结构化建体 buildGprVolume(X/Z 落格 + Y 向 1D 线性插值 → int16 量化体)
- 新增 GprSurvey 规则化建体输入模型(放 core/model 保持 geopro_core 自洽,避免 core->io 反向依赖)
- buildGprVolume: X/Z 取最近道/采样落格,仅跨通道 Y 做 1D 线性插值,边界外不外推
- int16 量化用值域中点为 offset 对称铺满 ~64000 码位,两端留余量不撞 int16/kBlank
- 整型乘积索引走 size_t
2026-06-23 10:45:06 +08:00
gaozheng ec4a7e81ef feat(detail): 补全详情视图剩余交互(框选/绘形/直方图/行级可见性/富文本/白化)
继续数据集详情视图 100% 复刻,补齐上批后置/降级的 6 项。

- M2 measurement 列表行级可见性:DataTableView 载荷驱动可交互开关列
  (仅 measurement 启用),行级 popconfirm → saveDisplayStatus
- M3 数据过滤直方图:新增自绘 ScatterHistogramView,分布柱 + 选区高亮 +
  与 min/max 输入联动
- M14 散点框选:ScatterMarqueePicker 橡皮筋框选 + ScatterPlotItem 选中高亮,
  显示/隐藏对选中子集操作
- I9 异常图上绘形:ContourDrawTool 在等值面上交互绘制 点/线/面/文字
  (先弹窗填类型/名称→图上绘制→newException),坐标表保留为兜底
- I14 富文本描述:DescriptionPanel 升级富文本(粗体/斜体/下划线/字色/字号/
  标题/列表) + QuillDelta 与 Quill Delta 常见格式往返(非 Quill 不可字节级1:1)
- I3 白化 tmObjectId:openWhitening 经 getDsObjectDetail 取 structParentId

修复 ScatterHistogram 命名冲突(widget 改名 ScatterHistogramView,与
ScatterDataOps 的分箱结构 struct 区分),desktop 目标恢复可链接。

抽纯函数 ChartPickGeometry/QuillDelta/buildScatterHistogram + 单测。
build app + test 全绿,318/318 通过。
2026-06-23 10:33:14 +08:00
gaozheng c6ff9c2271 feat(core): 新增 int16 量化体类型 ScalarVolumeI16 + Quant
GPR 三维体地基:int16 量化标量体,内存/显存/磁盘为 double 体的 1/4。
- Quant: 物理值↔int16 映射,toQ 下钳保留 INT16_MIN 给 kBlank 哨兵
- ScalarVolumeI16: 与 double ScalarVolume 并列,i 最快 k 最慢布局
- idx(i,j,k) 64 位计算(整卷可达约 96 亿体素,防 int 溢出)
- header-only,纯 C++17,零 Qt/VTK
2026-06-23 10:28:40 +08:00
gaozheng 0bbed9c0c3 feat(io/gpr): GPR 几何-通道横偏解析与采样深度换算
新增 geopro::io::gpr 两个纯 C++17 几何函数:
- parseChannelXOffsets: 解析 .ord 末列==1 的有效通道横向偏移
- depthOfSample: 按物理把采样序号换算为深度米(samples<=1 防除零)

含失败先行的单测,GprGeometry.cpp 接入 geopro_io_gpr,
test 接入 geopro_tests。
2026-06-23 10:19:11 +08:00
gaozheng 0d7f646941 feat(io/gpr): 实现 .iprb B-scan 二进制读取器
readIprb 读取 int16 雷达剖面(布局 [trace*samples + s]):
traces=lastTrace+1;校验文件字节数=samples*traces*2,
不符或打不开抛 std::runtime_error。纯 C++17,零 Qt/VTK。
2026-06-23 10:00:08 +08:00
gaozheng c395921ca8 feat(io/gpr): 新增 .iprh 头解析器(纯 C++17,零 Qt/VTK)
- IprHeader/parseIprHeader:按行解析 KEY: value,支持含空格键名
  (LAST TRACE/SOIL VELOCITY/DISTANCE INTERVAL)
- SOIL VELOCITY 由 m/µs 统一换算为 m/s 存储(×1e6)
- 缺 SAMPLES/LAST TRACE/CHANNELS 任一抛 std::runtime_error
- CMake 接线:src/io(gpr) 静态库 geopro_io_gpr + tests 链接
- TDD:2 个新用例,全测试套件 100% 通过
2026-06-23 09:45:13 +08:00
gaozheng 12813bd8d0 feat(detail): 数据集详情视图交互复刻(measurement/inversion/grid)
对照原版 web 1:1 复刻数据集详情视图的写操作交互,补齐既有视图的全部
可交互能力。

基础设施
- 新增写操作命令仓储 IDatasetCommandRepository + ApiDatasetCommandRepository
  (26 个写/查接口,端点逐字对照原版 apis),回调式异步沿用 ApiColorTemplateRepository 模式
- 写操作注入链平行 setColorTemplateRepo:main→Panel→Page→DetailViewFactory→视图,
  透传 cmdRepo + dsIdGetter
- 新增共享对话框 InversionFormDialog/SaveAsDialog/ScatterFilterDialog/GridWizardDialog/
  WhiteningDialog/FilterDialog/ExceptionDialog/ExceptionDetailDialog/AutoAnnotationDialog
- 纯函数 InversionFormParse/ScatterDataOps/InversionProcessOps/ContourSimplify + 单测

measurement(M1-M13):可见性持久化、数据过滤、X/Y/V轴、值类型、色阶配置、
  生成视电阻率、反演运算、另存为、导出DAT、信息点选
inversion 网格(I1-I15):网格化向导、白化、滤波、等值线提示、简化容差(真生效)、
  异常增删改查+定位、自动标注、描述保存、另存为
inversion 原数据(O1-O3) + grid 反演(G1,functionList 驱动)

后置/降级(台账 §6.4):M14框选、M2行级可见性、M3过滤直方图、I9图上绘形、
  I14富文本(Qt无Quill)、I3白化tmObjectId透传

测试 285/285 通过
2026-06-23 09:21:37 +08:00
gaozheng 8e91351dab fix(vtk): 二维足迹不可见 + 台湾区天地图底图全占位图
三处缺陷,均由「勾选二维数据集 → VTK 看不到渲染/底图」串起:

1) 摆放默认关闭致足迹静默丢弃
   Column2DDataset「2D视图」下拉可见默认项为 Z=0(setCurrentIndex(1)),
   但该初始信号在 connect 前发射、且组件早于 main.cpp 接线构造 → 永不送达控制器,
   控制器 placement2dMode_ 仍为 0(关闭),勾选被记录却不入场(setChecked2DDatasets
   守卫 placement!=0 不通过)。改:控制器默认 1、main.cpp view2dMode 默认 1,
   与下拉可见默认项对齐,彻底摆脱对信号时序的依赖。

2) 足迹未重锚 frame → 投到数百公里外、移动视角也找不到
   GeoLocalFrame 启动锚在样本 grid1 中位经纬;addCurtain 会重锚到剖面真实经纬,
   但 addMapLine 未重锚 → 台湾足迹(经120.8/纬24.7)按样本锚点投到世界原点数十万米外。
   改:抽出 anchorFrameIfNeeded(剖面/足迹共用),首个带经纬数据(无论帘面或足迹)
   重锚原点;控制器 setChecked2DDatasets 在空场景首批足迹时取景(fitOnArrival)。

3) 台湾区天地图卫星只覆盖到 z16,z17/z18 返回固定「无影像」占位图
   底图四叉树拉近时细分到 kMaxZoom=18 → 台湾中心瓦片全是占位图(实测 z17/z18
   字节恒等 size=4769/MD5 c0edbdcb,z16 为真实影像;内地有 z18 故正常)。
   改:TileBasemap 加自适应 satMaxZoom_,isTiandituNoImagery 按 大小+MD5 精确识别
   占位图 → 学习把卫星上限降到 z-1 并重铺(台湾 18→16 收敛,用 z16 真实影像放大);
   refineTile 卫星层用学习上限,街道矢量仍到 z18;show()/换源复位、refresh 保留。
   内地项目零影响(z18 有影像,永不触发降级)。

测试:253/253 通过;新增 TwoDDefaultPlacementRendersAtZeroOnCheck 回归,
原依赖「默认关闭」的两个用例改为显式 set2DPlacement(0)。
2026-06-22 20:31:25 +08:00
gaozheng 5e60446210 feat(vtk): 色阶编辑器(2D/3D共享)+模板库后端+剖面着色修正+二维数据集足迹
本会话主要交付:
- 色阶配置对话框 1:1 复刻原版(colorLevel/contourLevel/contourLine/colorEditor + colorUtils):
  左三列⚙表格(层级/线形/颜色) + 层级⚙/线形⚙/颜色⚙ 子对话框 + 连续渐变(直方图/读出/min-max/反转)
  + .lvl/.clr 导入导出;文案/校验对齐原版精确 i18n。
- lvl/clr 模板库接真实后端:IColorTemplateRepository + ApiColorTemplateRepository,
  另存/打开/新建色阶/配色方案下拉 经仓储注入 2D(GridDataChartView)与 3D(主对话框)。
- 剖面帘面着色对齐原版 threeContour.js getTerrainColor:上界 stop 取色 + 满 RGB,
  修正"色带整体下移一格 / 发浅发灰 / 丢 alpha"导致与原版差异大的问题。
- 二维数据集视图首切片:勾选轨迹类数据集 → 足迹平铺进 View3D 地图
  (Api3dRepository::loadMapLine 走 dd/ert/trajectory/line + MapLineActor + col2D 接线),
  view2DMode 控摆放高度,顶/底锚真实地表高程(zRefElev)。
- 测试 252 全绿。

并含本分支前序未提交的 UI 工作(ToastOverlay/TopBar/Theme/DynamicForm/若干 panel),
经 CMakeLists/main.cpp 纠缠,随此 checkpoint 一并提交。未纳入未跟踪的 png/yml 及审查报告 txt。
2026-06-22 12:48:45 +08:00
gaozheng b97ea68109 feat(vtk): 三维体/切片数据详情对话框(#6)-右键属性表+体统计持久化
- VolumePropertiesDialog/SlicePropertiesDialog 只读属性对话框(仿异常详情)
- Api3dRepository::volumeInfo 取参数+统计;StoredVolume 持久化聚合测点数(finalizeVolume)
- main.cpp 右键「数据详情」按 ddCode 分派(dd_voxel→体/dd_slice→切片);接口与 LocalSample 零改动
- 切片不含统计(切面网格仓储不持久化,守 YAGNI);体统计仅 loaded 时显示,否则占位
- 单测 +2(volumeInfo loaded前/未知id) → 230/230 绿
2026-06-18 20:28:15 +08:00
gaozheng 44d31a824d feat(vtk): 异常列表选中→VTK高亮联动(#4c-2, R84 list→VTK)
- I3dSceneView::setSelectedAnomaly + VtkSceneView 实现(选中异常 actor 加粗高亮、其余恢复);
  anomalyProps_ 改 vtkActor 以设线宽/点尺寸
- main:Column3DAnalysis::anomalySelected → sceneView->setSelectedAnomaly
- FakeView 补 setSelectedAnomaly 空实现

编译全绿(build.bat all);用户实测通过。
反向(VTK点异常→回选列表)本期未做(需异常 actor 拾取)。
2026-06-18 19:06:14 +08:00
gaozheng 4e1b8e7635 feat(vtk): 异常3D基础(#4a)-Anomaly补3D几何+buildAnomaly3D+视图异常方法+Api mock持久化(挂三维体)
异常功能地基(对象→三维体→异常;异常挂三维体非切片,见记忆 vtk-3d-persistence-structure):
- core::Anomaly 补 id/volumeDsId(=remarkSourceId)/consortiumId/worldPts(3D世界点)/planeNormal·planeOrigin,
  保留 2D localPts(剖面详情兼容)
- render::buildAnomaly3D(单异常→世界坐标 点/折线/闭合多边形 actor,不翻y/不压z;抽 buildActor 共享 2D/3D)
- I3dSceneView + VtkSceneView:addAnomaly/removeAnomaly/clearAnomalies/setAnomalyVisible(按 id 跟踪 actor,
  worldPts 已含VE 故不再 SetScale;clear 一并清异常)
- Api3dRepository:StoredAnomaly + saveAnomaly/loadAnomalyTree(按 volumeDsId 过滤+consortiumId 分组异常体)/
  deleteAnomaly/deleteAnomalyGroup 内存 mock(取代 stub onErr)

同时修复测试漂移(此前 3a 加 isVolumeDataset 纯虚 + 3c 改 loadVolume 回调签名后,geopro_tests 一直未随之更新):
- FakeView 补异常方法;FakeSceneRepo 补 isVolumeDataset(可配置 volumeIds)+修 loadVolume 签名
- test_3d_repo loadVolume 回调改 (VolumeGrid,ColorScale)
- 控制器测试 View3DWithVoxelAddsVolume 按新"类型分流"语义重写为 View3DVolumeDatasetAddsVolume(体素XOR帘面)

编译全绿(build.bat all);228/228 单元测试通过。4a 为地基(无 UI 接线、尚不可见),圈定/保存见 4b。
2026-06-18 13:04:14 +08:00
gaozheng c03dc35469 feat(vtk): 勾选/取消增量渲染-按ds跟踪图元,diff增删不全量重建;clear保留底图;增量不重置相机(全量/首批才取景) 2026-06-17 10:49:06 +08:00
gaozheng 8684e52939 feat(vtk): 底图瓦片坐标数学 TileMath(EPSG:3857 经纬↔z/x/y+瓦片边界)+单测(P5基石) 2026-06-16 21:29:59 +08:00
gaozheng 744b55c1b6 feat(vtk): I3dSceneRepository 加异步 loadSection(帘面Grid+色阶)+LocalSample样本实现(①.1) 2026-06-16 18:18:38 +08:00
gaozheng 77f1b5543e feat(vtk): I3dSceneRepository 补齐切片/异常/任务接口设计(spec §6.3-6.5)+LocalSample内存态stub 2026-06-16 18:11:21 +08:00
gaozheng 3f24ad81e3 feat(vtk): 维度过滤纯函数 splitByDimension + 单测 2026-06-16 15:11:07 +08:00
gaozheng 85d4ff57df feat(vtk): P3 三维分析切片交互(核心) — 轴向/任意切片+滚轮+拾取+正视
interact/ 交互层(README早规划,本期落地):
- SlicePlaneMath: 纯几何(法向/45°/滚轮平移/双击正视相机含竖直兜底/最近切片), 25 单测
- SliceTool: 封装 vtkImagePlaneWidget — 轴向(上下/前后/左右,角度固定)+任意45°(Origin/Pt1/Pt2),
  套色阶LUT, reslice着色(非cutter), close幂等
- PickInteractorStyle: 继承TrackballCamera+vtkCellPicker, 拾取/双击/滚轮回调
- InteractionManager: 活动切片/选中态/滚轮分发/拾取联动/翻转, 体素变更先closeAll再附着
- VtkSceneView 暴露 currentVolumeImage_(含VE) + onVolumeChanged; main.cpp 切片工具条(上下/前后/左右/任意/翻转/关闭)
- ctest 221/221

评审修复:
- H1 vtkTrivialProducer 提为成员(局部变量构造后析构→管线断裂崩溃)
- H2 uninstallStyle 向 interactor 注销 style
- H3 safeRender 统一守 destroying_ 跳过析构期 Render
- M1 advance 刚性平移 origin+point1+point2(只移origin致轴向切面变形)
- M2 closeSelected 选中位就近不跳0; M4 sliceBar 加 BottomLeftAnchor 随resize

范围外(P4): 切片保存/导出/删除为数据集/三维分析树/右键菜单/异常圈定/详情
2026-06-16 08:23:16 +08:00
gaozheng 3dea339ddc feat(vtk): P2 三维数据集栏 — 坐标轴/比例/快捷视图/Zoom
- CameraPreset 扩 6 向快捷视图(前后左右上下) + zoomBy(透视改视角/正交改parallelScale) + fitView
- AxesActor(新, vtkCubeAxesActor): 显示模式 标准(外缘)/三维立体(静态边+网格线)/不显示;
  刻度 无/米/英尺(×3.28084)/经纬度(GeoLocalFrame反算); 字号12(字体待1.0确认)
- GeoLocalFrame 补 toLatLon 反算(等距圆柱)
- I3dSceneView 扩 setAxes/applyCameraView/zoom/fitView; VtkSceneController 加对应槽
  (坐标轴随场景重建; 快捷视图/zoom 仅改相机不重建)
- main.cpp 三维视图工具条: 坐标轴/刻度下拉(枚举绑itemData)+比例滑块(1-10)+6向钮+Zoom钮, 仅3D显示
- 测试 +24(toLatLon往返/相机6向/坐标轴单位换算/控制器编排), ctest 196/196

评审修复:
- HIGH rebuildAxes 异步路径坐标轴 prop 累积 → 持 currentAxes_ 重建前先移除(幂等)
- MEDIUM combo index 魔数 → itemData/currentData 取枚举值(防项序调整静默错位)

注: 坐标轴标准/立体语义 + 字体 + O点 为合理近似, 待 Geopro 1.0 实地确认精修
2026-06-15 21:54:48 +08:00
gaozheng 0f521c5b24 feat(vtk): P1 复活中央渲染 — VtkSceneController + I3dSceneRepository + Scene加vtkProp
复活被 6241eb3 摘除的中央 VTK 数据驱动渲染:
- Scene 新增 addViewProp(vtkProp*):体绘制 vtkVolume(非 vtkActor)经此进场
- I3dSceneRepository(异步回调契约) + LocalSample3dRepository:dimensionOf 维度映射 /
  loadVolume→VolumeGrid(std::array 去裸数组) / loadTerrainPaths;data 层零 VTK 依赖
- VtkSceneController(QObject) 取代 main.cpp 死掉的 rebuildCentral lambda + 裸 show* 标志:
  勾选数据集/视图模式/图层/比例 → 经仓储取 core::* → I3dSceneView 重建场景;
  QPointer+generation 守异步回调生命周期与新鲜度;inRebuild_ 避免同步路径双 render
- I3dSceneView 抽象解耦编排与 VTK(VtkSceneView 真实现 + 测试 fake)
- 删除被取代的 CentralScene;main.cpp 接线 对象勾选/2D-3D/图层/主题(主题 context 用 sceneCtrl 防悬垂)
- 新增测试 14(Scene/3d-repo/VtkSceneController),ctest 172/172 全绿

构建基建修复(本就潜在缺陷,任何 clean 构建/新人 checkout 都会撞):
- vcpkg.json 加 builtin-baseline:新版 vcpkg manifest 模式必需,否则全新 checkout 无法 configure
- build.bat 修 vswhere(VS2026 预览 -latest 恒空 → -all -prerelease -requires VC.Tools)
  + 括号块内路径变量加引号(防 Program Files (x86) 的 ) 提前闭合)
2026-06-15 21:01:26 +08:00
gaozheng 85e455ae4b feat(workbench): 对象/数据集交互对齐规格 + 属性面板可编辑保存
提交体按 bundle/OpenAPI 源码确证修正(替换早期推断版本):
- 新建/编辑 GS:{gsTypeId,parentId(仅新建),projectId,name,responsiblePersonName,properties}
- 新建/编辑 TM:{tmTypeId,id?,name,properties,projectId,parentId,parentType:"1"}
- 去除误用的 typeId/type/structParentId/structParentConfType

动态表单:
- displayComponentType 全集控件映射(1-11 + 默认步进数字)
- requiredType 语义:1=必填、2=只读禁用、其他=可选
- 顶层固定字段(gsTypeId/tmTypeId/name/responsiblePersonName)

新建对象:
- 单弹框 + 框内类型下拉,选后刷新动态表单(GS/TM 对称)
- 方法类型用全局 project/tmList;GS 类型用 project/gsList
- 父对象按节点类型解析:选 TM 仅新建同级 TM(父=其GS/根);项目根按 GS 处理
- 对象树右键 + 面板「添加+」按钮按节点类型分项

属性面板可编辑保存:
- 对象属性面板可编辑 → PUT gsObject/tmObject
- 数据集属性面板:元字段只读 + 描述可编辑 → updateDsObject
  {dsObjectId,description,attachedParameters:{deltaContent}}

数据集:导入向导(multipart) + 模板导出对话框(templateExport/export)

文档:spec(§B 提交体/§E 源码补全) 与 plan 同步更新
2026-06-15 11:52:41 +08:00
gaozheng 1f0081ee34 feat(workbench): 对象/数据集视图交互 — 右键菜单/筛选/删除 + 动态表单编辑保存与新建TM
Batch1(骨架+读联动+删除+筛选):
- ApiClient 补 putJsonAsync/deleteAsync
- 删除 GS/TM/DS(真实 DELETE + 确认框 + 成功刷新)
- 对象树右键菜单(9项)/数据集右键菜单:属性·异常详情·详情联动接现有面板;显示隐藏/定位等 2D/3D 占位
- 快速筛选器(对象按类型批量勾选/反选;数据集按类型+创建日期客户端过滤)+ 数据集单击 tooltip
- 复选框手势修复:点勾选不再触发"选中"重载(viewport 事件过滤 + 复选框命中判定)

Batch2(动态表单引擎+写操作):
- DynamicFormEditor:getDynamicForm schema 驱动(comp1/4/6/7/8 + 必填校验 + properties 预填)
- ObjectFormDialog:拉真实表单→校验→提交
- 编辑保存 PUT /business/{gs|tm}Object(成功刷新,失败回显后端 msg)
- 新建 TM:queryTmType 选型→空表单→POST(带父 GS 上下文)
- 插件子菜单:列出真实 model/list(启动缓存)

数据层:
- EditableForm/TmTypeOption/ModelInfo DTO + parseEditableForm/parseTmTypes/parseModels
- StructNode.typeId;repo loadEditableForm/queryTmTypes/submitObject/listModels;controller currentProjectId()

注:保存/新建请求体为推断(OpenAPI 未文档化提交 body),待真实提交验证后定版。
docs: plans/2026-06-13-object-dataset-interactions.md, specs/2026-06-13-batch2-object-dataset-dialogs.md
2026-06-13 20:03:30 +08:00
gaozheng 2cf2b6aaa7 feat(dataset-detail): dd_grid 白化数据列表 + 引擎服务端分页(vxe-pager)
⑤ dd_grid 详情:单「列表」页签,序号/x/y 三列(均居中),服务端分页。
按原版(vxe-table)实测复刻:序号列前插、按页偏移自增;total 取 data.total;
分页器对齐 vxe-pager(上一页/页码…/下一页 + 前往N页 + 每页条数 50/100/500/1000 默认50 + 共N条记录)。

引擎新增分页能力(通用,后续分页型详情复用):
- TablePayload 加 pageNo/pageSize(>0 才渲染分页器;0=全量列表,measurement/trajectory 不受影响)
- GridDto.parseGridTable 复用通用 parseGridHeaderTable,前插序号列 + 回填分页态
- 仓储 loadAsync 增 pageNo/pageSize 透传,新增 grid.rows 加载器(端点 dd/ert/grid/rows,默认50条/页)
- 控制器新增 loadTabPaged(保留 3 参 loadTab 以维持 tabNeeded 连接)
- TablePager 分页器组件 + DataTableView 按 pageSize>0 显隐并转发 pageRequested
  → DatasetDetailPage/Panel.tabPageNeeded → Controller.loadTabPaged 反向链路
- GridStrategy(dd_grid 单分页页签) 注册入 main

测试:test_grid_dto(序号偏移/total/分页态/空数据) + grid.rows 分派 + GridStrategy 注册
+ 控制器 loadTabPaged 透传/默认页参;154/154 通过。
ABI 关键头(DetailPayloads.hpp)变更后全量重编 geopro 代码并验 obj 新鲜度。
2026-06-13 17:27:39 +08:00
gaozheng b033dc2a2c feat(dataset-detail): dd_trajectory_data 轨迹详情(地图+列表+高程)
在按类型渲染引擎上落地轨迹三页签,新增 LineProfile + WebMap 两个视图 kind。

- 地图(TrajectoryMapView:QWebEngineView + 本地 MapLibre GL JS 4.7.1):
  WebGL 引擎,天地图栅格 WMTS 底图(街道 vec_w+cva_w / 卫星 img_w+cia_w,token,
  t0-t7 子域,逐层 maxzoom);over-zoom 上采样平滑;街道/卫星图层切换 + 回到当前
  位置定位按钮;实心橙点(#ff8c00 白环)+ 连线 + 起点标;经纬度取自
  trajectory/line?frontCrsCode=EPSG:4326(服务端转 WGS84);runJavaScript 喂点(无 WebChannel)
- 高程(LineChartView:QwtPlot + QwtPlotCurve):X=电极号 #1..#40,Y=高程,
  数据空间预算样条平滑(非 Fitted,避免像素重拟合漂移);hover 用 QwtWidgetOverlay
  画虚线参考线+实心点(叠加层不 replot 主图→曲线零漂移)+ 自定义浮动框(#编号/●高程(m) 值)
- 列表:复用通用 DataTableView + 通用 gridHeaderDisplay→TablePayload 解析(14 列)
- LinePayload/MapPayload(DetailPayloads.hpp 加性);TrajectoryDto;TrajectoryStrategy
  (地图/WebMap+列表/Table+高程/LineProfile);ApiDatasetRepository loaderKey
  traj.map/traj.rows/traj.elev;DetailViewFactory LineProfile/WebMap;main 注册
- 构建:geopro_desktop 链接 Qt6::WebEngineWidgets/WebEngineQuick;main 加
  QtWebEngineQuick::initialize()+AA_ShareOpenGLContexts;CMake 部署 WebEngine 运行时

测试 143→148 全绿。夹具 tests/fixtures/dd/ert-trajectory-{rows,line}.json。
2026-06-13 16:36:28 +08:00
gaozheng bc5613f0d2 feat(dataset-detail): dd_ert_measurement_gr_data 接地电阻柱状图详情
在按类型渲染引擎上新增 Bar 视图 kind。

- 柱状图(BarChartView:QwtPlotBarChart 单系列 / QwtPlotMultiBarChart 分组):
  X=电极点 #1..#40(electrodeId),Y=电阻(欧姆)=p1Rg,单系列 P1 实心 #5470c6
  (任一行 p2Rg 非空时加分组系列 P2);Y 轴标题「电阻(单位:欧姆)」横排左上、
  X 轴标题「电极点」、图例 P1;主题跟随;析构 clearSeries 防悬垂
- 列表:复用通用 DataTableView,固定 7 列(ID/日期/时间/P1 Rg(Ω)/P1状态/
  P2 Rg(Ω)/P2状态)
- BarPayload(DetailPayloads.hpp 加性);GrMeasurementDto(parseGrBar/parseGrTable,
  响应为 JSON 数组,p1/p2 isDouble 守卫);GrMeasurementStrategy(柱状图/Bar+列表/Table);
  ApiDatasetRepository loaderKey gr.bar/gr.rows(同一端点不同解析);
  DetailViewFactory Bar case;main 注册

测试 138→143 全绿。夹具 tests/fixtures/dd/ert-gr-rows.json。
2026-06-13 11:46:47 +08:00
gaozheng a00aeb9a56 feat(dataset-detail): 按类型渲染引擎 + inversion 迁移 + dd_ert_measurement_data 详情
将数据集详情从硬编码 ERT 反演重构为通用「ddCode→页签→视图kind」渲染引擎,
把已验收的 dd_inversion_data 迁入新引擎(渲染零变化),并在引擎上实现
dd_ert_measurement_data(ERT原始数据)详情。

引擎:
- 策略声明页签:IDatasetChartStrategy::tabs()→vector<TabSpec{title,ViewKind,
  loaderKey,lazy}> 取代 hasGridPhase();控制器通用化(datasetOpened/loadTab/
  tabReady),按页签槽位保留 abort-and-replace + 句柄身份比对 + deleteLater 三件套
- 单一异步句柄 DetailLoad/ApiDetailLoad(QVariant 载荷),仓储 loadAsync(loaderKey,
  dsId) 扁平分派;删 ChartLoad/GridLoad/loadChartAsync/loadGridAsync/DatasetLoads.hpp
- 视图 IDetailView{widget,setPayload(QVariant)} + DetailViewFactory 按 ViewKind 实例化;
  载荷 Scatter/Contour/TablePayload(core/model/detail/DetailPayloads.hpp)
- Page 按 TabSpec 建页签 + 通用 lazy;inversion 散点/网格迁入,渲染代码未改

dd_ert_measurement_data(③散点伪剖面 + 列表):
- 散点复用 RawDataChartView/ScatterPlotItem:x=斜距/y=伪深度(负向)/色=视电阻率,
  连续 cauto(含负离群值),colorBar 取自 colorGradation businessCode=R0&type=3(混合
  hex+rgba/AlphaScale::Unit);电极灰菱形(#BEBEBE实心+白描边 size16);A/B/M/N 与电极
  浮动 hover;数据点尺寸对齐原版 12px;色阶底部横向(与反演一致)
- 通用 DataTableView(gridHeaderDisplay/filedList+rowList,均衡等宽列+居中+隐藏/显示
  开关列);MeasurementDto 解析(散点位置数组 + vmap 摊平)
- 工具条视觉 1:1(信息/框选/显示/隐藏/数据过滤/x·y·v·计算方法下拉/色阶配置/生成视电阻
  率/反演运算/另存为);显示隐藏+x/y 下拉轻交互,其余占位

测试 75→138 全绿。Phase0 真实响应夹具 tests/fixtures/dd/*。
2026-06-13 10:51:24 +08:00
gaozheng 10072eb4b3 feat(dataset-detail+app): 数据集树/按根分页 + 暗色主题保真 + 详情图保真 + 桌面日志崩溃捕获
本分支累积的数据集详情与桌面端健壮性工作(多轮迭代,已逐项实测/用户验收),一次性提交。

数据集列表树化 + 按根分页:
- 原版数据列表为 el-table 树(派生数据按 sourceShowParentId 挂源「原始数据」下);
  DsRow 加 parentId,parseDsRows 解析 sourceShowParentId(回退 parentId),
  DatasetListPanel 由 QListWidget 改建 QTreeWidget(卡片委托泛化到 QAbstractItemView)。
- 后端 data/page 按扁平 DS 分页 → 改为客户端按「第一层节点(根)」分页:
  loadRowsAsync 加 pageSize,selectObject 一次取全,新增 emitNextDataRootPage 按根切页(5根/页),
  loadMoreData 改同步切页;main.cpp 加载更多计数改按根。

暗色主题保真(浅色保持与原版 1:1,仅暗色改 token):
- 新增 ChartTheme::applyChartPlotTheme:按 isDarkTheme() 设 QwtPlot 画布/轴字/网格/零线配色,连 ThemeManager::changed 热切换。
- ColorBarWidget、LoadingOverlay 底色/蒙板/文字同步跟随主题。

详情图渲染保真:
- colorBar alpha 标度修复(混合 hex+rgba 格式,rgba alpha 为 0–1,Bit255→Unit)。
- 散点 cauto 归一化(ColorMapService.setDataRange 解耦色阶形状与数据归一化)。
- 散点 hover 提示(ScatterHoverTip,X/Y/值 3 位小数,canvas mouseTracking)。
- 详情页签用数据名命名。

桌面端日志 + 崩溃捕获:
- Logging:滚动日志 + MiniDump 崩溃捕获 + VEH 抛点符号化;main.cpp GuardedApplication::notify 顶层异常护栏。
- 根 CMakeLists Release 产出 PDB(/Zi /DEBUG);ColorMapService NaN/Inf 守卫。

测试 116→122 全绿(+ParseDsRowsParentIdForTree / DataPaginatesByRootNodeNotFlatCount / 散点/colormap 回归)。
2026-06-12 19:00:32 +08:00
gaozheng 6b4267d78a harden(net+app): 登录句柄终态置 aborted_ + connect 用成员 QPointer + ApiChain 首步同步契约注释 + LoginLoad step 抛异常离线测 + 注释更正(Part B 评审 I-1/I-2/I-3/M-2/M-4) 2026-06-12 09:11:54 +08:00
gaozheng d1c1bf96b1 feat(net+app): AuthService/登录异步化(CaptchaLoad/LoginLoad+ApiChain, LoginWindow 不冻可取消, test_auth 异步化)
B1: AuthService 改异步——新增 net 层句柄 CaptchaLoad/LoginLoad(AuthLoads.{hpp,cpp}),
    fetchCaptchaAsync 返回 CaptchaLoad;loginAsync 用 ApiChain 编排 verifyCodeCheck->RSA->login2
    返回 LoginLoad。删同步 fetchCaptcha/login/LoginResult。句柄遵 spec §5.0(aborted_ 守卫/deleteLater)。
B2: LoginWindow 异步化——refreshCaptcha/attemptLogin 连句柄信号(身份比对),删 repaint() hack,
    析构 abort 在飞句柄(退出契约)。公共 API(token/remember/exec)不变。
B4: test_auth.cpp 改 QSignalSpy::wait 异步等待(仍 live)。
新增离线句柄单测 test_auth_loads.cpp(CaptchaLoad/LoginLoad done/failed/abort)。
[B3 删 ApiClient 同步 get/postJson 因 ProjectListDialog/ApiProjectRepository 仍同步而 BLOCKED,故保留]
2026-06-12 09:01:07 +08:00
gaozheng 0cb0ed8aa0 refactor(detail): 控制器按 ddCode 走 ChartStrategyRegistry 分派, 未注册优雅降级 (替代硬编码 dd_inversion_data)
- IDatasetChartStrategy + ChartStrategyRegistry 下移到控制器层 (src/controller, namespace geopro::controller), 删 app 层那份, 修层级倒置 (控制器不得依赖 app)
- 接口加 hasGridPhase(); ErtInversionStrategy 留 app 层, 改继承 controller 接口, hasGridPhase()=true
- DatasetDetailController 构造注入 ChartStrategyRegistry&; openDataset 用 registry.supports 降级; loadGridData 用 strategy->hasGridPhase 判定
- main.cpp 构造 registry 注册 ErtInversionStrategy 并注入 (registry 先于 detailCtrl 声明)
- 测试: registry 加 hasGridPhase 断言; 控制器加空注册表降级 + 无网格阶段跳过网格加载用例; 全量 109/109 绿 (基线 106)
2026-06-12 08:14:19 +08:00
gaozheng 62352395ba harden(controller+net): setCheckedTms 去重 + loadMore 失败回滚页号 + 非拥有所有权注释更正 + ApiChain 待用注释 + selectObject 部分失败测试(Part A 评审 I-2/I-3/I-4/M-1/M-4) 2026-06-12 08:04:08 +08:00
gaozheng b097fa6e56 feat(controller): WorkbenchNavController 异步化(NavRequest续延+并发计数, abort-and-replace+身份比对, 删busy_/drain/BusyGuard, busyChanged=在飞存在性) + 单测
- 控制器依赖切换到 IAsyncProjectRepository(异步句柄)
- 删除 busy_/BusyGuard/drainPendingCheckedTms/checkedTmsPending_/pendingCheckedTms_/friend struct BusyGuard
- start/switchWorkspace 用 NavRequest 续延依赖链(startStepReq_ 跟踪当前在飞级)
- switchProject/loadMore*/selectDataset 单请求 + abort-and-replace + 身份比对
- selectObject 三并发(data/file/detail), 各自身份比对独立 emit
- setCheckedTms 并发拉取未命中缓存项, 计数汇聚; 新勾选 abort 旧批(以最后一次为准); tmExceptionCache_ 命中不发请求
- busyChanged 由 anyInflight() 驱动(emitBusyIfChanged 去抖, 值变才发)
- 析构 abortAll() 退出契约
- 对外信号面零改动, main.cpp 接线据引用绑定自动切换(无需改)
- 新增 9 个控制器单测(依赖链/并发/abort-and-replace/busyChanged/缓存语义/回灌防护/失败路径)
- 测试 96 -> 105 全绿
2026-06-12 07:51:35 +08:00
gaozheng 4beb7a9523 feat(data): NavRequest 单请求异步句柄(QVariant payload, abort闸门) + 元类型声明 + 离线单测 2026-06-12 07:38:59 +08:00
gaozheng 22a7f2339e feat(net): ApiChain 顺序依赖链原语(fail-fast+abort闸门+工厂可抛) + 离线单测 2026-06-12 07:36:50 +08:00
gaozheng 350f46060d harden(controller): 析构 abort 在飞句柄(退出契约 spec §7) + Grid 路径回灌/失败用例对称覆盖(评审 I-1/M-3/M-4) 2026-06-11 20:51:45 +08:00
gaozheng e57985c057 feat(data+controller): ApiDatasetRepository 改异步 + DatasetDetailController abort-and-replace+句柄身份比对+loadStarted(移除 busy_/catch)+ 回灌防护测试 2026-06-11 20:37:10 +08:00
gaozheng 8cdd6679a9 harden(data): 句柄 emit done 移出 try + catch(...) 兜底 + parse 抛异常测试(评审 I-1/M-5) 2026-06-11 20:31:07 +08:00
gaozheng bb602e2011 feat(data): 异步仓储接口 + ChartLoad/GridLoad 句柄(抽象基+Api实现) + 离线单测 2026-06-11 20:19:32 +08:00
gaozheng e980ddd346 harden(net): ApiBatch 契约断言(非空calls/非空谓词)+fail-fast注释+单元素测试(评审 I-1/I-2/M-1/M-2) 2026-06-11 20:13:48 +08:00
gaozheng 72b300d722 feat(net): ApiBatch 并发汇聚+fail-fast+abort闸门 + 离线单测 2026-06-11 20:05:53 +08:00
gaozheng 78f96dbc08 fix(review): 修 cpp-review HIGH/MEDIUM —散点ys越界/colorSvc析构泄漏/QwtPlot autoDelete注释/控制器catch(...)防busy死锁 + 清死代码simplifyInPlace/simplifyTol + 填充等比限幅 + DTO行数校验+枚举钳制 + ContourLine.level默认NaN 2026-06-11 18:59:19 +08:00