geopro/src/render/interact/PickInteractorStyle.cpp

151 lines
5.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "interact/PickInteractorStyle.hpp"
#include <chrono>
#include <vtkCallbackCommand.h>
#include <vtkCamera.h>
#include <vtkCellPicker.h>
#include <vtkMath.h>
#include <vtkNew.h>
#include <vtkObjectFactory.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkTransform.h>
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) {
auto* iren = this->GetInteractor();
if (!iren) return false;
const int* pos = iren->GetEventPosition();
// 用交互器解析被点中的 renderer基类 FindPokedRenderer 仅设 CurrentRenderer、返回 void
auto* ren = iren->FindPokedRenderer(pos[0], pos[1]);
if (!ren) return false;
// CellPicker返回表面交点世界坐标命中切片纹理面/帘面等)。
vtkNew<vtkCellPicker> picker;
picker->SetTolerance(0.005);
if (!picker->Pick(pos[0], pos[1], 0.0, ren)) return false;
double w[3];
picker->GetPickPosition(w);
out = {w[0], w[1], w[2]};
return true;
}
void PickInteractorStyle::OnLeftButtonDown() {
auto* iren = this->GetInteractor();
// 二维分析:左键拖动=平移(等同中键),不拾取/不旋转 → 仅平移+缩放。抬键由基类按 State 收尾。
if (lock2D_) {
const int* p = iren ? iren->GetEventPosition() : nullptr;
if (p) this->FindPokedRenderer(p[0], p[1]);
if (!this->CurrentRenderer) return;
this->GrabFocus(this->EventCallbackCommand);
this->StartPan();
return;
}
Vec3 world;
const bool hit = pickWorld(world);
// 手动双击判定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);
lastDownTime_ = -1.0; // 重置,避免三击连判
return; // 不进入拖动旋转
}
if (hit) {
// 单击命中 → 选中所在切片onPick 内仅选中, 不动相机)。
if (onPick) onPick(world);
}
// 不在按下时动相机(动相机=跳);绕选中物旋转在 Rotate() 内做(增量绕支点,不跳)。
Superclass::OnLeftButtonDown();
}
void PickInteractorStyle::Rotate() {
if (lock2D_) return; // 二维分析禁旋转(仅平移+缩放)
Vec3 c;
if (!this->CurrentRenderer || !getRotateCenter || !getRotateCenter(c)) {
Superclass::Rotate(); // 无选中物 → 默认绕焦点旋转
return;
}
auto* rwi = this->Interactor;
auto* cam = this->CurrentRenderer->GetActiveCamera();
if (!rwi || !cam) return;
const int dx = rwi->GetEventPosition()[0] - rwi->GetLastEventPosition()[0];
const int dy = rwi->GetEventPosition()[1] - rwi->GetLastEventPosition()[1];
const int* size = this->CurrentRenderer->GetRenderWindow()->GetSize();
if (size[0] <= 0 || size[1] <= 0) return;
// 与 TrackballCamera 同口径的角度映射。
const double azimuth = dx * (-20.0 / size[0]) * this->MotionFactor;
const double elevation = dy * (-20.0 / size[1]) * this->MotionFactor;
double up[3], dop[3], right[3];
cam->GetViewUp(up);
cam->GetDirectionOfProjection(dop); // 归一化的 (focal-pos)
vtkMath::Cross(dop, up, right); // 屏幕"右"轴
vtkMath::Normalize(right);
// 绕中心 c 的支点T(c)·R(up,azimuth)·R(right,elevation)·T(-c),作用于 position/focalup 只转不平移。
vtkNew<vtkTransform> t;
t->Identity();
t->Translate(c[0], c[1], c[2]);
t->RotateWXYZ(azimuth, up[0], up[1], up[2]);
t->RotateWXYZ(elevation, right[0], right[1], right[2]);
t->Translate(-c[0], -c[1], -c[2]);
double pos[3], foc[3], npos[3], nfoc[3], nup[3];
cam->GetPosition(pos);
cam->GetFocalPoint(foc);
t->TransformPoint(pos, npos);
t->TransformPoint(foc, nfoc);
t->TransformVector(up, nup); // 仅旋转部分作用于向量
cam->SetPosition(npos);
cam->SetFocalPoint(nfoc);
cam->SetViewUp(nup);
cam->OrthogonalizeViewUp();
if (this->AutoAdjustCameraClippingRange) this->CurrentRenderer->ResetCameraClippingRange();
rwi->Render();
}
void PickInteractorStyle::OnMouseWheelForward() {
if (onWheelStep && onWheelStep(+1)) return; // 有选中切片 → 推进,消费滚轮
Superclass::OnMouseWheelForward(); // 否则默认缩放
}
void PickInteractorStyle::OnMouseWheelBackward() {
if (onWheelStep && onWheelStep(-1)) return;
Superclass::OnMouseWheelBackward();
}
} // namespace geopro::render::interact