From 87b90a20229013785a6c3af6856179b9a1165c5e Mon Sep 17 00:00:00 2001 From: gaozheng Date: Tue, 16 Jun 2026 09:09:38 +0800 Subject: [PATCH] =?UTF-8?q?fix(vtk):=20P3=20=E5=88=87=E7=89=87=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E6=89=8B=E6=84=9F=E4=BF=AE=E5=A4=8D(=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=AE=9E=E6=B5=8B=E5=8F=8D=E9=A6=88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删左上「视图详情」遗留的禁用「切片」复选框(P1占位,已被P3工具条取代) - 点击跳变 + 绕点旋转无效: onPicked 焦点与相机位置同步平移同一delta, 图像不跳、旋转中心移到命中点(原来只改焦点致视图突变) - 双击正视无效: 弃 vtkRenderHWindowInteractor::GetRepeatCount(QVTK+Win不可靠), 改 std::chrono 手动判双击(间隔<350ms且位置相近); 避开 vtkTimerLog 依赖 - 选中移除: 随上面拾取修复,单击切片可选中→关闭移除选中项(不再只能倒序) --- src/app/main.cpp | 7 +--- src/render/interact/InteractionManager.cpp | 12 +++++-- src/render/interact/PickInteractorStyle.cpp | 40 ++++++++++++++++++--- src/render/interact/PickInteractorStyle.hpp | 5 +++ 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/app/main.cpp b/src/app/main.cpp index 08c11c3..4b872d2 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -416,20 +416,15 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re chkVoxel->setChecked(false); auto* chkTerrain = new QCheckBox(QStringLiteral("地形(DEM+影像)")); chkTerrain->setChecked(false); - auto* chkSlice = new QCheckBox(QStringLiteral("切片(dd_slice)")); - chkSlice->setChecked(false); + // 切片交互已由 P3 左下「切片」工具条提供,此处不再放(原 P1 占位禁用项已移除)。 if (!crsAvailable) { // PROJ 不可用 → 体素/地形层(都需配准)禁用并提示 const QString tip = QStringLiteral("PROJ 数据(proj.db)缺失,配准不可用"); chkVoxel->setEnabled(false); chkVoxel->setToolTip(tip); chkTerrain->setEnabled(false); chkTerrain->setToolTip(tip); } - // 切片(dd_slice)交互切片留待 P3:本轮禁用。 - chkSlice->setEnabled(false); - chkSlice->setToolTip(QStringLiteral("(切片交互 P3 接入)")); layerLayout->addWidget(layerTitle); layerLayout->addWidget(chkCurtain); layerLayout->addWidget(chkVoxel); - layerLayout->addWidget(chkSlice); layerLayout->addWidget(chkTerrain); layerPanel->setVisible(false); // 默认二维,不显示图层浮层 diff --git a/src/render/interact/InteractionManager.cpp b/src/render/interact/InteractionManager.cpp index 2cebadb..a9f79c0 100644 --- a/src/render/interact/InteractionManager.cpp +++ b/src/render/interact/InteractionManager.cpp @@ -124,10 +124,18 @@ int InteractionManager::nearestSlice(const Vec3& worldPoint) const { } void InteractionManager::onPicked(const Vec3& worldPoint) { - // 焦点设到命中点 → 拖动绕其旋转(spec C38/D39)。 + // 旋转中心设到命中点(spec C38/D39):焦点与相机位置**同步平移同一 delta**, + // 使渲染图像不变(视向/距离不变)、只把旋转中心移到命中点——否则只改焦点会让视图突然跳变(评审)。 if (renderer_) { - if (auto* cam = renderer_->GetActiveCamera()) + if (auto* cam = renderer_->GetActiveCamera()) { + double f[3], p[3]; + cam->GetFocalPoint(f); + cam->GetPosition(p); + const double d[3] = {worldPoint[0] - f[0], worldPoint[1] - f[1], worldPoint[2] - f[2]}; cam->SetFocalPoint(worldPoint[0], worldPoint[1], worldPoint[2]); + cam->SetPosition(p[0] + d[0], p[1] + d[1], p[2] + d[2]); + renderer_->ResetCameraClippingRange(); + } } // 若命中点落在某切片上(阈值内),选中之(供滚轮推进/关闭)。 const int idx = nearestSlice(worldPoint); diff --git a/src/render/interact/PickInteractorStyle.cpp b/src/render/interact/PickInteractorStyle.cpp index d07ee7d..9791747 100644 --- a/src/render/interact/PickInteractorStyle.cpp +++ b/src/render/interact/PickInteractorStyle.cpp @@ -1,5 +1,7 @@ #include "interact/PickInteractorStyle.hpp" +#include + #include #include #include @@ -8,6 +10,18 @@ namespace geopro::render::interact { +namespace { +constexpr double kDoubleClickMs = 350.0; // 两次左键按下间隔阈值 +constexpr int kClickSlopPx2 = 36; // 位置相近阈值平方(6px) + +// 当前单调时钟(毫秒)。用 std::chrono 避免依赖 VTK::CommonSystem(vtkTimerLog)。 +double nowMs() { + return std::chrono::duration( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); +} +} // namespace + vtkStandardNewMacro(PickInteractorStyle); bool PickInteractorStyle::pickWorld(Vec3& out) { @@ -32,13 +46,31 @@ void PickInteractorStyle::OnLeftButtonDown() { Vec3 world; const bool hit = pickWorld(world); - if (hit && iren && iren->GetRepeatCount() > 0) { - // 双击命中 → 正视所在切片(交给 manager 找最近切片 + 算相机)。 + // 手动双击判定(GetRepeatCount 在 QVTK+Windows 不可靠,评审 M5): + // 两次左键按下间隔 < 阈值且屏幕位置相近 → 双击。 + const double now = nowMs(); + const int* pos = iren ? iren->GetEventPosition() : nullptr; + bool isDouble = false; + if (hit && pos && lastDownTime_ >= 0.0) { + const double dtMs = now - lastDownTime_; + const int dx = pos[0] - lastDownPos_[0]; + const int dy = pos[1] - lastDownPos_[1]; + if (dtMs < kDoubleClickMs && (dx * dx + dy * dy) <= kClickSlopPx2) isDouble = true; + } + if (pos) { + lastDownPos_[0] = pos[0]; + lastDownPos_[1] = pos[1]; + } + lastDownTime_ = now; + + if (isDouble) { + // 双击命中 → 正视所在切片(manager 找最近切片 + 算相机)。 if (onDoubleClick) onDoubleClick(world); - return; // 不进入拖动旋转 + lastDownTime_ = -1.0; // 重置,避免三击连判 + return; // 不进入拖动旋转 } if (hit) { - // 单击命中 → 选中 + 以命中点为焦点(拖动绕其旋转)。 + // 单击命中 → 选中 + 设旋转中心为命中点(拖动绕其旋转)。 if (onPick) onPick(world); } // 始终保留 TrackballCamera 默认拖动(旋转/平移)。 diff --git a/src/render/interact/PickInteractorStyle.hpp b/src/render/interact/PickInteractorStyle.hpp index 41fd586..f66c554 100644 --- a/src/render/interact/PickInteractorStyle.hpp +++ b/src/render/interact/PickInteractorStyle.hpp @@ -38,6 +38,11 @@ protected: private: // 在当前鼠标位置拾取世界点;命中返回 true 并填 out。 bool pickWorld(Vec3& out); + + // 手动双击判定:QVTK+Windows 下 vtkRenderWindowInteractor::GetRepeatCount() 不可靠(评审 M5)。 + // 记上次左键按下时刻+屏幕位置,两次按下间隔 < kDoubleClickMs 且位置相近视为双击。 + double lastDownTime_ = -1.0; // 单调时钟(毫秒),-1=无 + int lastDownPos_[2] = {0, 0}; }; } // namespace geopro::render::interact