From dc4671e09e11832c6e7fea8fed944ee5126a4495 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Wed, 1 Jul 2026 00:43:58 +0800 Subject: [PATCH] =?UTF-8?q?refactor(vtk):=20=E6=B8=85=E9=80=802D=E6=8B=BE?= =?UTF-8?q?=E5=8F=96=E6=8B=96=E5=8A=A8=E6=AD=BB=E4=BA=A4=E4=BA=92=E5=AD=90?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=20+=20=E6=AE=B5=E5=9B=BE=E6=A0=87=E4=B8=8A?= =?UTF-8?q?=E9=99=90=E9=92=89=E4=B8=BA3=20+=20=E6=B3=A8=E9=87=8A=E9=99=A4?= =?UTF-8?q?=E9=94=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix 1 (死代码清退): E1 退役 per-ds 拖Z 2D 交互后, render/interact 层的另一半残留 接线为空。grep 确认零活调用方后, 移除整条 2D 交互通路: - InteractionManager::setMode2D / pickStyle (decl+def, 唯一调用方 analysisModeChanged lambda 已删) - PickInteractorStyle::lock2D_/setLock2D/isLock2D, onPick2D/onDrag2D/onDrag2DEnd/onWheel2D, worldPerPixelZ/kWheelStepPx/dragging2D_/lastDragY_ 及各事件处理器内的 lock2D_ 分支 仅删 2D 锁分支, 完整保留 3D 拾取/绕支点旋转/切片拖动/双击正视/滚轮推进/Esc 取消。 Fix 2 (spec §6): CategorySection 段头图标条上限由 setMaxIcons(acts.size()) 钉为 命名常量 kDefaultMaxIcons=3, 恢复计数溢出折叠分支(原恒=操作数→永不触发)。宽度挤压分支不动。 Fix 3 (注释除锈): 修正失真注释 —— categoryConfigs/CategorySpec → categoryCatalog()/ CategoryDescriptor; 删除 section("trajectory")返回 nullptr 的过期断言(C2 已构造该段); VtkSceneController 注释引用已删的 gridCache_ → sectionGridCache_。 --- .../panels/columns/CategoryAnalysisTab.cpp | 4 +- .../panels/columns/CategoryAnalysisTab.hpp | 6 +- src/app/panels/columns/CategorySection.cpp | 8 ++- src/controller/VtkSceneController.hpp | 2 +- src/render/interact/InteractionManager.cpp | 21 ------- src/render/interact/InteractionManager.hpp | 7 --- src/render/interact/PickInteractorStyle.cpp | 63 +------------------ src/render/interact/PickInteractorStyle.hpp | 23 ------- 8 files changed, 15 insertions(+), 119 deletions(-) diff --git a/src/app/panels/columns/CategoryAnalysisTab.cpp b/src/app/panels/columns/CategoryAnalysisTab.cpp index 515c4e2..8e1c4d9 100644 --- a/src/app/panels/columns/CategoryAnalysisTab.cpp +++ b/src/app/panels/columns/CategoryAnalysisTab.cpp @@ -106,8 +106,8 @@ void CategoryAnalysisTab::relayoutSections() { } void CategoryAnalysisTab::setBuckets(const CategoryBuckets& b) { - // splitByCategory 现按 categoryCatalog() 分桶(含 trajectory 第 5 桶,自然分发到轨迹段)。 - // 注:trajectory 段在 Task C2 才加入构造,当前 section("trajectory") 返回 nullptr → guard 跳过。 + // splitByCategory 现按 categoryCatalog() 分桶(含 trajectory 桶,自然分发到轨迹段)。 + // 轨迹段已随 catalog 在构造时建出(Task C2),section() 命中存在的段;下方 guard 仍保平衡兜底。 const auto& cat = geopro::data::categoryCatalog(); for (std::size_t i = 0; i < cat.size() && i < b.segments.size(); ++i) { // voxel(三维体) 段数据来自 mock voxelTree(体/切片/异常),由调用方单独 section("voxel")->setDatasets diff --git a/src/app/panels/columns/CategoryAnalysisTab.hpp b/src/app/panels/columns/CategoryAnalysisTab.hpp index 39b548e..7a939c3 100644 --- a/src/app/panels/columns/CategoryAnalysisTab.hpp +++ b/src/app/panels/columns/CategoryAnalysisTab.hpp @@ -26,10 +26,10 @@ class CategoryAnalysisTab : public QWidget { public: explicit CategoryAnalysisTab(geopro::data::DatasetFieldDictionary* dict, QWidget* parent = nullptr); - void setBuckets(const CategoryBuckets& b); // 分发到 5 段(与 categoryConfigs 同序) + void setBuckets(const CategoryBuckets& b); // 分发到各大类段(与 categoryCatalog() 同序) void setStructure(const std::vector& nodes); // 转发各段 void refreshArrayFilters(); // 装置枚举异步加载后,重填各段装置筛选下拉 - CategorySection* section(const std::string& id) const; // 按 CategorySpec.id 取段 + CategorySection* section(const std::string& id) const; // 按 CategoryDescriptor.id 取段 // ── 三维体生成后:自动勾选触发渲染 + 标题前等待动画(spinner)反馈(转发到含该 ds 的段)── void setItemChecked(const QString& dsId, bool on); // 自动勾选(触发渲染) void setItemBusy(const QString& dsId, bool busy); // 复选框↔等待 spinner 切换 @@ -68,7 +68,7 @@ private: void updatePlaceholderAndVisibility(); std::map sections_; - std::vector ordered_; // 按 categoryConfigs 顺序(relayout 遍历用) + std::vector ordered_; // 按 categoryCatalog() 顺序(relayout 遍历用) QScrollArea* scroll_ = nullptr; // 外层滚动区(scrollItemToTop 定位用) QWidget* content_ = nullptr; // 滚动内容容器(坐标映射用) QVBoxLayout* col_ = nullptr; // 段堆叠布局(末项=尾部弹簧) diff --git a/src/app/panels/columns/CategorySection.cpp b/src/app/panels/columns/CategorySection.cpp index 708c2f0..e8d8e69 100644 --- a/src/app/panels/columns/CategorySection.cpp +++ b/src/app/panels/columns/CategorySection.cpp @@ -30,6 +30,12 @@ namespace geopro::app { using geopro::data::DsRow; using geopro::data::DsTypeFields; +namespace { +// 段头图标条默认上限(spec §6):超过则末位收进「…」下拉。钉死为常量而非随操作数浮动, +// 否则上限恒=操作数、计数溢出折叠分支永不触发(当前各段操作 ≤3,对用户不可见,但保证分支正确)。 +constexpr int kDefaultMaxIcons = 3; +} // namespace + CategorySection::CategorySection(const geopro::data::CategoryDescriptor& desc, geopro::data::DatasetFieldDictionary* dict, QWidget* parent) : QWidget(parent), desc_(desc), dict_(dict) { @@ -92,7 +98,7 @@ CategorySection::CategorySection(const geopro::data::CategoryDescriptor& desc, break; } } - iconBar_->setMaxIcons(static_cast(acts.size())); // 段头操作数即可见上限(够宽全显,不够收进「…」) + iconBar_->setMaxIcons(kDefaultMaxIcons); // spec §6 默认上限 3(够宽全显,超数/不够宽收进「…」) iconBar_->setActions(acts); hl->addWidget(iconBar_); root->addWidget(headerRow); diff --git a/src/controller/VtkSceneController.hpp b/src/controller/VtkSceneController.hpp index ac5c354..fac8ffe 100644 --- a/src/controller/VtkSceneController.hpp +++ b/src/controller/VtkSceneController.hpp @@ -143,7 +143,7 @@ private: // 缓存(按 dsId):避免重复读盘/插值。 std::map colorScaleCache_; - // 帘面源网格缓存:帘面重着色需 grid 重建 addCurtain(loadSection 的 s.grid 不在 gridCache_)。 + // 帘面源网格缓存:帘面重着色需 grid 重建 addCurtain(loadSection 的 s.grid 缓存于此)。 std::map sectionGridCache_; std::map volumeCache_; // 三维体色阶缓存:mock 体在 dsRepo_ 无条目,色阶随 loadVolume 一起交付并缓存于此。 diff --git a/src/render/interact/InteractionManager.cpp b/src/render/interact/InteractionManager.cpp index 35b11d8..0c8e628 100644 --- a/src/render/interact/InteractionManager.cpp +++ b/src/render/interact/InteractionManager.cpp @@ -293,27 +293,6 @@ void InteractionManager::closeAll() { safeRender(); } -PickInteractorStyle* InteractionManager::pickStyle() const { return style_; } - -void InteractionManager::setMode2D(bool is2D) { - // 进入二维分析:主动取消「三维前视图」的所有选中。否则残留的选中切片会让 onWheel 持续消费滚轮 - // (二维下无法缩放),且切回三维仍残留高亮。清 selected_ + 切片高亮;再经 onSliceSelectionChanged("") - // 联动清三维分析列表选中行与异常高亮(app 层接线)。与 VtkSceneView::setAnalysisMode2D 离开二维时 - // clearMapLineSelection 清足迹选中相对称。 - if (is2D) { - if (selected_ >= 0) { - selected_ = -1; - updateSelectionVisual(); // 清切片高亮(切回三维不残留选中) - } - if (onSliceSelectionChanged) onSliceSelectionChanged(std::string{}); - } - // 切片属三维内容:二维分析隐藏(不销毁→切回零重建)、三维分析显示。 - for (auto& s : slices_) - if (s) s->setVisible(!is2D); - if (style_) style_->setLock2D(is2D); // 二维=禁旋转、左键平移(仅平移+缩放) - // 不在此渲染:相机/地形/底图/维度显隐及统一 Render 由 VtkSceneView::setAnalysisMode2D 收尾。 -} - void InteractionManager::flipView() { if (!renderer_) return; auto* cam = renderer_->GetActiveCamera(); diff --git a/src/render/interact/InteractionManager.hpp b/src/render/interact/InteractionManager.hpp index d32d0d6..6c0ba37 100644 --- a/src/render/interact/InteractionManager.hpp +++ b/src/render/interact/InteractionManager.hpp @@ -68,9 +68,6 @@ public: // 关闭并释放所有切片(切到二维 / 清场 / 体素重建前调)。 void closeAll(); - // 切二维分析(is2D=true)/三维分析(false):翻所有切片显隐(不销毁,切回零重建) + 锁/解锁交互样式 - // (二维=仅平移+缩放、禁旋转)。地形/底图/相机由 VtkSceneView::setAnalysisMode2D 处理,此处不渲染。 - void setMode2D(bool is2D); // 关闭并释放某体下的所有切片(该体移除/重建时;不动其它体的切片)。 void closeSlicesOfVolume(const std::string& volumeDsId); @@ -126,10 +123,6 @@ public: void installStyle(); void uninstallStyle(); - // 暴露交互样式:供 app 层注入二维分析 B 期的足迹拾取/Z 拖动回调(onPick2D/onDrag2D/onDrag2DEnd)。 - // 定义在 .cpp(此处 PickInteractorStyle 仅前置声明,vtkSmartPointer→裸指针下转需完整类型)。 - PickInteractorStyle* pickStyle() const; - private: // 拾取回调实现(PickInteractorStyle 注入)。 void onPicked(const Vec3& worldPoint); // 选中所在切片 + 焦点 diff --git a/src/render/interact/PickInteractorStyle.cpp b/src/render/interact/PickInteractorStyle.cpp index 7194ec6..00dc8cd 100644 --- a/src/render/interact/PickInteractorStyle.cpp +++ b/src/render/interact/PickInteractorStyle.cpp @@ -1,7 +1,6 @@ #include "interact/PickInteractorStyle.hpp" #include -#include #include #include @@ -50,22 +49,6 @@ bool PickInteractorStyle::pickWorld(Vec3& out) { void PickInteractorStyle::OnLeftButtonDown() { auto* iren = this->GetInteractor(); - // 二维分析:左键命中足迹→进入高程 Z 拖动(B 期);否则=平移(等同中键),禁旋转。抬键由 OnLeftButtonUp 收尾。 - if (lock2D_) { - const int* p = iren ? iren->GetEventPosition() : nullptr; - if (p) this->FindPokedRenderer(p[0], p[1]); - if (!this->CurrentRenderer) return; - const bool additive = iren && iren->GetControlKey(); // Ctrl=多选 - if (onPick2D && p && onPick2D(p[0], p[1], additive)) { // 命中足迹 → Z 拖动 - dragging2D_ = true; - lastDragY_ = p[1]; - this->GrabFocus(this->EventCallbackCommand); - return; - } - this->GrabFocus(this->EventCallbackCommand); // 未命中 → 平移 - this->StartPan(); - return; - } Vec3 world; const bool hit = pickWorld(world); // 仍用于取选中所需世界点(onPick) // 命中切片【精确判定】:光标射线穿过某切片真实矩形内才算(不靠带容差的 picker 点)。 @@ -107,7 +90,6 @@ void PickInteractorStyle::OnLeftButtonDown() { } void PickInteractorStyle::Rotate() { - if (lock2D_) return; // 二维分析禁旋转(仅平移+缩放) if (!this->CurrentRenderer || !hasRotatePivot_) { Superclass::Rotate(); // 无支点 → 默认绕焦点旋转 return; @@ -154,49 +136,14 @@ void PickInteractorStyle::Rotate() { rwi->Render(); } -double PickInteractorStyle::worldPerPixelZ() const { - if (!this->CurrentRenderer) return 1.0; - auto* cam = this->CurrentRenderer->GetActiveCamera(); - auto* rw = this->CurrentRenderer->GetRenderWindow(); - if (!cam || !rw) return 1.0; - const int* sz = rw->GetSize(); - const double h = (sz && sz[1] > 0) ? static_cast(sz[1]) : 800.0; - if (cam->GetParallelProjection()) - return 2.0 * cam->GetParallelScale() / h; // 平行投影:可见世界高度=2*parallelScale - // 透视:可见世界高度 = 2*d*tan(viewAngle/2),d=相机到焦点距离。 - double pos[3], fp[3]; - cam->GetPosition(pos); - cam->GetFocalPoint(fp); - const double dx = pos[0] - fp[0], dy = pos[1] - fp[1], dz = pos[2] - fp[2]; - const double d = std::sqrt(dx * dx + dy * dy + dz * dz); - const double va = vtkMath::RadiansFromDegrees(cam->GetViewAngle()); - return 2.0 * d * std::tan(va * 0.5) / h; -} - void PickInteractorStyle::OnMouseMove() { - if (dragging2D_) { // B 期:竖向拖动 → 选中足迹 Z 增量(仅改 Z)。鼠标上移(y 增)→ 抬高。 - auto* rwi = this->Interactor; - if (rwi) { - const int y = rwi->GetEventPosition()[1]; - const int dyPix = y - lastDragY_; - lastDragY_ = y; - if (dyPix != 0 && onDrag2D) onDrag2D(worldPerPixelZ() * dyPix); - } - return; // 不走基类(不平移/不旋转) - } Superclass::OnMouseMove(); } void PickInteractorStyle::OnLeftButtonUp() { - if (dragging2D_) { // 结束 Z 拖动 - dragging2D_ = false; - if (this->Interactor) this->ReleaseFocus(); - if (onDrag2DEnd) onDrag2DEnd(); - return; - } // 单击(抬键位移<阈值=非拖动)且按下未命中切片 → 取消选中(点空/点体;体 PickableOff 故点体也 hit=false)。 // 拖空白旋转:抬键位移大 → 不取消,保留"绕选中切片旋转"。Esc 仍是完全拉近时的兜底。 - if (!lock2D_ && !downHitSlice_ && onDeselect) { + if (!downHitSlice_ && onDeselect) { auto* iren = this->GetInteractor(); const int* up = iren ? iren->GetEventPosition() : nullptr; if (up) { @@ -208,19 +155,13 @@ void PickInteractorStyle::OnLeftButtonUp() { Superclass::OnLeftButtonUp(); // 平移/旋转/缩放等由基类按 State 收尾 } -namespace { -constexpr double kWheelStepPx = 24.0; // 滚轮一格升降 ≈ 拖动 24 像素的世界 Z 量(与拖动手感一致) -} - void PickInteractorStyle::OnMouseWheelForward() { - // 二维分析有选中足迹 → 滚轮抬升其高程(消费滚轮);否则按切片推进 / 默认缩放。 - if (lock2D_ && onWheel2D && onWheel2D(worldPerPixelZ() * kWheelStepPx)) return; + // 有选中切片 → 沿法向推进(消费滚轮);否则默认缩放。 if (onWheelStep && onWheelStep(+1)) return; // 有选中切片 → 推进,消费滚轮 Superclass::OnMouseWheelForward(); // 否则默认缩放 } void PickInteractorStyle::OnMouseWheelBackward() { - if (lock2D_ && onWheel2D && onWheel2D(-worldPerPixelZ() * kWheelStepPx)) return; if (onWheelStep && onWheelStep(-1)) return; Superclass::OnMouseWheelBackward(); } diff --git a/src/render/interact/PickInteractorStyle.hpp b/src/render/interact/PickInteractorStyle.hpp index 81089be..71623b2 100644 --- a/src/render/interact/PickInteractorStyle.hpp +++ b/src/render/interact/PickInteractorStyle.hpp @@ -37,20 +37,6 @@ public: // 点帘面/其它非切片物/边界外 → 返回 false → 单击即取消选中。 std::function hitTestSlice; - // 二维分析锁:开 → 左键拖动改为平移、禁旋转(仅平移+缩放);关 → 恢复三维拾取/旋转交互。 - void setLock2D(bool on) { lock2D_ = on; } - bool isLock2D() const { return lock2D_; } - - // ── 二维分析 B 期:选中足迹沿高程 Z 拖动 ──(仅 lock2D 下生效;回调由 app 层注入) - // onPick2D:左键按下时在(x,y)拾取足迹(additive=Ctrl 多选),返回是否有选中→有则进入 Z 拖动、否则平移。 - // onDrag2D:拖动中把竖向像素换算成的世界 Z 增量(本类按相机算)交给 app 施加到选中足迹(仅改 Z)。 - // onDrag2DEnd:松开结束拖动(供 app 收起高程读数浮层)。 - std::function onPick2D; - std::function onDrag2D; - std::function onDrag2DEnd; - // 滚轮升降:有选中足迹时滚轮改其高程 Z(本类按相机算 worldDz);app 施加并返回是否消费(无选中→false→默认缩放)。 - std::function onWheel2D; - void OnMouseMove() override; void OnLeftButtonUp() override; @@ -68,8 +54,6 @@ protected: private: // 在当前鼠标位置拾取世界点;命中返回 true 并填 out。 bool pickWorld(Vec3& out); - // 当前相机下:竖向一屏幕像素对应的世界 Z(米/像素),用于把拖动像素换算成 Z 增量。 - double worldPerPixelZ() const; // 手动双击判定:QVTK+Windows 下 vtkRenderWindowInteractor::GetRepeatCount() 不可靠(评审 M5)。 // 记上次左键按下时刻+屏幕位置,两次按下间隔 < kDoubleClickMs 且位置相近视为双击。 @@ -84,13 +68,6 @@ private: // 选中切片=其中心;否则=光标射线穿过的体中段点。无则 hasRotatePivot_=false→默认绕焦点。 Vec3 rotatePivot_{}; bool hasRotatePivot_ = false; - - // 二维分析模式:左键=平移、禁旋转(仅平移+缩放)。由 InteractionManager 在切 tab 时设。 - bool lock2D_ = false; - - // B 期足迹 Z 拖动状态:左键命中足迹时进入,记上次鼠标 y 以算增量。 - bool dragging2D_ = false; - int lastDragY_ = 0; }; } // namespace geopro::render::interact