refactor(vtk): 清退2D拾取拖动死交互子系统 + 段图标上限钉为3 + 注释除锈
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_。
This commit is contained in:
parent
e8df41b9f2
commit
dc4671e09e
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<geopro::data::StructNode>& 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<std::string, CategorySection*> sections_;
|
||||
std::vector<CategorySection*> ordered_; // 按 categoryConfigs 顺序(relayout 遍历用)
|
||||
std::vector<CategorySection*> ordered_; // 按 categoryCatalog() 顺序(relayout 遍历用)
|
||||
QScrollArea* scroll_ = nullptr; // 外层滚动区(scrollItemToTop 定位用)
|
||||
QWidget* content_ = nullptr; // 滚动内容容器(坐标映射用)
|
||||
QVBoxLayout* col_ = nullptr; // 段堆叠布局(末项=尾部弹簧)
|
||||
|
|
|
|||
|
|
@ -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<int>(acts.size())); // 段头操作数即可见上限(够宽全显,不够收进「…」)
|
||||
iconBar_->setMaxIcons(kDefaultMaxIcons); // spec §6 默认上限 3(够宽全显,超数/不够宽收进「…」)
|
||||
iconBar_->setActions(acts);
|
||||
hl->addWidget(iconBar_);
|
||||
root->addWidget(headerRow);
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ private:
|
|||
|
||||
// 缓存(按 dsId):避免重复读盘/插值。
|
||||
std::map<std::string, geopro::core::ColorScale> colorScaleCache_;
|
||||
// 帘面源网格缓存:帘面重着色需 grid 重建 addCurtain(loadSection 的 s.grid 不在 gridCache_)。
|
||||
// 帘面源网格缓存:帘面重着色需 grid 重建 addCurtain(loadSection 的 s.grid 缓存于此)。
|
||||
std::map<std::string, geopro::core::Grid> sectionGridCache_;
|
||||
std::map<std::string, data::VolumeGrid> volumeCache_;
|
||||
// 三维体色阶缓存:mock 体在 dsRepo_ 无条目,色阶随 loadVolume 一起交付并缓存于此。
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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); // 选中所在切片 + 焦点
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#include "interact/PickInteractorStyle.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include <vtkCallbackCommand.h>
|
||||
|
|
@ -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<double>(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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,20 +37,6 @@ public:
|
|||
// 点帘面/其它非切片物/边界外 → 返回 false → 单击即取消选中。
|
||||
std::function<bool()> 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<bool(int x, int y, bool additive)> onPick2D;
|
||||
std::function<void(double worldDz)> onDrag2D;
|
||||
std::function<void()> onDrag2DEnd;
|
||||
// 滚轮升降:有选中足迹时滚轮改其高程 Z(本类按相机算 worldDz);app 施加并返回是否消费(无选中→false→默认缩放)。
|
||||
std::function<bool(double worldDz)> 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
|
||||
|
|
|
|||
Loading…
Reference in New Issue