fix(vtk): P3 切片交互手感修复(用户实测反馈)
- 删左上「视图详情」遗留的禁用「切片」复选框(P1占位,已被P3工具条取代) - 点击跳变 + 绕点旋转无效: onPicked 焦点与相机位置同步平移同一delta, 图像不跳、旋转中心移到命中点(原来只改焦点致视图突变) - 双击正视无效: 弃 vtkRenderHWindowInteractor::GetRepeatCount(QVTK+Win不可靠), 改 std::chrono 手动判双击(间隔<350ms且位置相近); 避开 vtkTimerLog 依赖 - 选中移除: 随上面拾取修复,单击切片可选中→关闭移除选中项(不再只能倒序)
This commit is contained in:
parent
85d4ff57df
commit
87b90a2022
|
|
@ -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); // 默认二维,不显示图层浮层
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include "interact/PickInteractorStyle.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <vtkCellPicker.h>
|
||||
#include <vtkNew.h>
|
||||
#include <vtkObjectFactory.h>
|
||||
|
|
@ -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<double, std::milli>(
|
||||
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 默认拖动(旋转/平移)。
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue