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