|
|
|
|
|
#include "nmVTKRotationCenterPicker.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include <QCoreApplication>
|
|
|
|
|
|
#include <QEvent>
|
|
|
|
|
|
#include <QKeyEvent>
|
|
|
|
|
|
#include <QMouseEvent>
|
|
|
|
|
|
#include <QThread>
|
|
|
|
|
|
#include <QVTKWidget.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include <vtkCellPicker.h>
|
|
|
|
|
|
#include <vtkMath.h>
|
|
|
|
|
|
#include <vtkProp.h>
|
|
|
|
|
|
#include <vtkRenderer.h>
|
|
|
|
|
|
#include <vtkRenderWindow.h>
|
|
|
|
|
|
#include <vtkSmartPointer.h>
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
|
|
// Qt 控件状态和 VTK Picker 均只允许在 GUI 线程中修改。
|
|
|
|
|
|
bool isRotationCenterPickerGuiThread()
|
|
|
|
|
|
{
|
|
|
|
|
|
QCoreApplication* pApplication = QCoreApplication::instance();
|
|
|
|
|
|
return pApplication == nullptr ||
|
|
|
|
|
|
pApplication->thread() == QThread::currentThread();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int scalePixelCoordinate(int nPosition,
|
|
|
|
|
|
int nWidgetExtent,
|
|
|
|
|
|
int nRenderExtent)
|
|
|
|
|
|
{
|
|
|
|
|
|
const double dScaled =
|
|
|
|
|
|
(static_cast<double>(nPosition) + 0.5) *
|
|
|
|
|
|
static_cast<double>(nRenderExtent) /
|
|
|
|
|
|
static_cast<double>(nWidgetExtent) - 0.5;
|
|
|
|
|
|
int nResult = static_cast<int>(dScaled + 0.5);
|
|
|
|
|
|
if(nResult < 0) {
|
|
|
|
|
|
nResult = 0;
|
|
|
|
|
|
} else if(nResult >= nRenderExtent) {
|
|
|
|
|
|
nResult = nRenderExtent - 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
return nResult;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nmVTKRotationCenterPicker::nmVTKRotationCenterPicker()
|
|
|
|
|
|
: m_pPickTarget(nullptr),
|
|
|
|
|
|
m_pWidget(nullptr),
|
|
|
|
|
|
m_pRenderer(nullptr),
|
|
|
|
|
|
m_oPreviousCursor(),
|
|
|
|
|
|
m_nPendingReleaseButton(static_cast<int>(Qt::NoButton)),
|
|
|
|
|
|
m_bHadExplicitCursor(false),
|
|
|
|
|
|
m_bCursorOverridden(false),
|
|
|
|
|
|
m_bActive(false),
|
|
|
|
|
|
m_bHasPickedPosition(false)
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT(isRotationCenterPickerGuiThread());
|
|
|
|
|
|
m_dPickedPosition[0] = 0.0;
|
|
|
|
|
|
m_dPickedPosition[1] = 0.0;
|
|
|
|
|
|
m_dPickedPosition[2] = 0.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nmVTKRotationCenterPicker::~nmVTKRotationCenterPicker()
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT(isRotationCenterPickerGuiThread());
|
|
|
|
|
|
// 先恢复 Qt 临时状态,再解除对拾取目标的弱引用观察关系。
|
|
|
|
|
|
reset();
|
|
|
|
|
|
m_pPickTarget = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmVTKRotationCenterPicker::setPickTarget(vtkProp* pTarget)
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT(isRotationCenterPickerGuiThread());
|
|
|
|
|
|
if(!isRotationCenterPickerGuiThread()) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(m_pPickTarget.GetPointer() == pTarget) {
|
|
|
|
|
|
// 弱目标已经失效时,显式清空仍必须结束可能遗留的交互状态。
|
|
|
|
|
|
if(pTarget == nullptr && (m_bActive || hasPendingRelease())) {
|
|
|
|
|
|
reset();
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 网格更换时旧 Press/Release 序列不再有效,必须先结束当前模式。
|
|
|
|
|
|
reset();
|
|
|
|
|
|
m_pPickTarget = pTarget;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
vtkProp* nmVTKRotationCenterPicker::getPickTarget() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_pPickTarget.GetPointer();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmVTKRotationCenterPicker::hasPickTarget() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_pPickTarget.GetPointer() != nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmVTKRotationCenterPicker::start(
|
|
|
|
|
|
QVTKWidget* pWidget,
|
|
|
|
|
|
vtkRenderer* pRenderer)
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT(isRotationCenterPickerGuiThread());
|
|
|
|
|
|
if(!isRotationCenterPickerGuiThread() || m_bActive ||
|
|
|
|
|
|
hasPendingRelease() || pWidget == nullptr || pRenderer == nullptr ||
|
|
|
|
|
|
!hasPickTarget()) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
vtkRenderWindow* pRenderWindow = pRenderer->GetRenderWindow();
|
|
|
|
|
|
if(pRenderWindow == nullptr ||
|
|
|
|
|
|
pWidget->GetRenderWindow() != pRenderWindow) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_pWidget = pWidget;
|
|
|
|
|
|
m_pRenderer = pRenderer;
|
|
|
|
|
|
m_bHasPickedPosition = false;
|
|
|
|
|
|
m_bHadExplicitCursor = pWidget->testAttribute(Qt::WA_SetCursor);
|
|
|
|
|
|
m_oPreviousCursor = pWidget->cursor();
|
|
|
|
|
|
pWidget->setCursor(Qt::CrossCursor);
|
|
|
|
|
|
m_bCursorOverridden = true;
|
|
|
|
|
|
m_bActive = true;
|
|
|
|
|
|
pWidget->setFocus(Qt::OtherFocusReason);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nmVTKRotationCenterPicker::PickEventResult
|
|
|
|
|
|
nmVTKRotationCenterPicker::cancel()
|
|
|
|
|
|
{
|
|
|
|
|
|
const bool bWasActive = m_bActive;
|
|
|
|
|
|
reset();
|
|
|
|
|
|
return PickEventResult(false,
|
|
|
|
|
|
bWasActive ? PickOutcome_Cancelled :
|
|
|
|
|
|
PickOutcome_None);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmVTKRotationCenterPicker::reset()
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT(isRotationCenterPickerGuiThread());
|
|
|
|
|
|
if(!isRotationCenterPickerGuiThread()) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_bActive = false;
|
|
|
|
|
|
m_nPendingReleaseButton = static_cast<int>(Qt::NoButton);
|
|
|
|
|
|
restoreCursor();
|
|
|
|
|
|
m_pWidget = nullptr;
|
|
|
|
|
|
m_pRenderer = nullptr;
|
|
|
|
|
|
m_bHasPickedPosition = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmVTKRotationCenterPicker::isActive() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_bActive;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmVTKRotationCenterPicker::hasPendingRelease() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_nPendingReleaseButton != static_cast<int>(Qt::NoButton);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nmVTKRotationCenterPicker::PickEventResult
|
|
|
|
|
|
nmVTKRotationCenterPicker::handleEvent(QEvent* pEvent)
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT(isRotationCenterPickerGuiThread());
|
|
|
|
|
|
if(!isRotationCenterPickerGuiThread() || pEvent == nullptr) {
|
|
|
|
|
|
return PickEventResult();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(pEvent->type() == QEvent::MouseButtonRelease &&
|
|
|
|
|
|
hasPendingRelease()) {
|
|
|
|
|
|
QMouseEvent* pMouseEvent = static_cast<QMouseEvent*>(pEvent);
|
|
|
|
|
|
if(static_cast<int>(pMouseEvent->button()) ==
|
|
|
|
|
|
m_nPendingReleaseButton) {
|
|
|
|
|
|
m_nPendingReleaseButton = static_cast<int>(Qt::NoButton);
|
|
|
|
|
|
return PickEventResult(true, PickOutcome_None);
|
|
|
|
|
|
}
|
|
|
|
|
|
return PickEventResult();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(!m_bActive) {
|
|
|
|
|
|
return PickEventResult();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(pEvent->type() == QEvent::ShortcutOverride ||
|
|
|
|
|
|
pEvent->type() == QEvent::KeyPress) {
|
|
|
|
|
|
QKeyEvent* pKeyEvent = static_cast<QKeyEvent*>(pEvent);
|
|
|
|
|
|
if(pKeyEvent->key() == Qt::Key_Escape) {
|
|
|
|
|
|
if(pEvent->type() == QEvent::ShortcutOverride) {
|
|
|
|
|
|
pEvent->accept();
|
|
|
|
|
|
return PickEventResult(true, PickOutcome_None);
|
|
|
|
|
|
}
|
|
|
|
|
|
finishActiveMode();
|
|
|
|
|
|
return PickEventResult(true, PickOutcome_Cancelled);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(pEvent->type() != QEvent::MouseButtonPress) {
|
|
|
|
|
|
return PickEventResult();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QMouseEvent* pMouseEvent = static_cast<QMouseEvent*>(pEvent);
|
|
|
|
|
|
if(pMouseEvent->button() != Qt::LeftButton &&
|
|
|
|
|
|
pMouseEvent->button() != Qt::RightButton) {
|
|
|
|
|
|
return PickEventResult();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_nPendingReleaseButton = static_cast<int>(pMouseEvent->button());
|
|
|
|
|
|
if(pMouseEvent->button() == Qt::RightButton) {
|
|
|
|
|
|
finishActiveMode();
|
|
|
|
|
|
return PickEventResult(true, PickOutcome_Cancelled);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const bool bHit = pickAt(pMouseEvent);
|
|
|
|
|
|
finishActiveMode();
|
|
|
|
|
|
return PickEventResult(true,
|
|
|
|
|
|
bHit ? PickOutcome_Hit : PickOutcome_Miss);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmVTKRotationCenterPicker::getPickedPosition(
|
|
|
|
|
|
double dPosition[3]) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if(dPosition == nullptr || !m_bHasPickedPosition) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
dPosition[0] = m_dPickedPosition[0];
|
|
|
|
|
|
dPosition[1] = m_dPickedPosition[1];
|
|
|
|
|
|
dPosition[2] = m_dPickedPosition[2];
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmVTKRotationCenterPicker::pickAt(
|
|
|
|
|
|
const QMouseEvent* pMouseEvent)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_bHasPickedPosition = false;
|
|
|
|
|
|
if(pMouseEvent == nullptr || m_pWidget == nullptr ||
|
|
|
|
|
|
m_pRenderer == nullptr) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
vtkProp* pTarget = m_pPickTarget.GetPointer();
|
|
|
|
|
|
vtkRenderWindow* pRenderWindow = m_pRenderer->GetRenderWindow();
|
|
|
|
|
|
if(pTarget == nullptr || pRenderWindow == nullptr ||
|
|
|
|
|
|
m_pWidget->GetRenderWindow() != pRenderWindow) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const int nWidgetWidth = m_pWidget->width();
|
|
|
|
|
|
const int nWidgetHeight = m_pWidget->height();
|
|
|
|
|
|
int* pRenderSize = pRenderWindow->GetActualSize();
|
|
|
|
|
|
if(nWidgetWidth <= 0 || nWidgetHeight <= 0 || pRenderSize == nullptr ||
|
|
|
|
|
|
pRenderSize[0] <= 0 || pRenderSize[1] <= 0 ||
|
|
|
|
|
|
pMouseEvent->x() < 0 || pMouseEvent->x() >= nWidgetWidth ||
|
|
|
|
|
|
pMouseEvent->y() < 0 || pMouseEvent->y() >= nWidgetHeight) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const int nRenderX = scalePixelCoordinate(
|
|
|
|
|
|
pMouseEvent->x(), nWidgetWidth, pRenderSize[0]);
|
|
|
|
|
|
const int nRenderYFromTop = scalePixelCoordinate(
|
|
|
|
|
|
pMouseEvent->y(), nWidgetHeight, pRenderSize[1]);
|
|
|
|
|
|
const int nRenderY = pRenderSize[1] - 1 - nRenderYFromTop;
|
|
|
|
|
|
|
|
|
|
|
|
// Picker 也限定在本次调用内,避免命中结果缓存延长 Mesh Actor 生命周期。
|
|
|
|
|
|
vtkSmartPointer<vtkCellPicker> pCellPicker =
|
|
|
|
|
|
vtkSmartPointer<vtkCellPicker>::New();
|
|
|
|
|
|
if(pCellPicker == nullptr) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
pCellPicker->PickFromListOn();
|
|
|
|
|
|
pCellPicker->InitializePickList();
|
|
|
|
|
|
pCellPicker->AddPickList(pTarget);
|
|
|
|
|
|
const int nPicked = pCellPicker->Pick(
|
|
|
|
|
|
nRenderX, nRenderY, 0.0, m_pRenderer);
|
|
|
|
|
|
const vtkIdType nCellId = pCellPicker->GetCellId();
|
|
|
|
|
|
double* pPosition = pCellPicker->GetPickPosition();
|
|
|
|
|
|
const bool bValid = nPicked != 0 && nCellId >= 0 &&
|
|
|
|
|
|
pPosition != nullptr &&
|
|
|
|
|
|
vtkMath::IsFinite(pPosition[0]) &&
|
|
|
|
|
|
vtkMath::IsFinite(pPosition[1]) &&
|
|
|
|
|
|
vtkMath::IsFinite(pPosition[2]);
|
|
|
|
|
|
if(bValid) {
|
|
|
|
|
|
m_dPickedPosition[0] = pPosition[0];
|
|
|
|
|
|
m_dPickedPosition[1] = pPosition[1];
|
|
|
|
|
|
m_dPickedPosition[2] = pPosition[2];
|
|
|
|
|
|
m_bHasPickedPosition = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
pCellPicker->InitializePickList();
|
|
|
|
|
|
return bValid;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmVTKRotationCenterPicker::finishActiveMode()
|
|
|
|
|
|
{
|
|
|
|
|
|
m_bActive = false;
|
|
|
|
|
|
restoreCursor();
|
|
|
|
|
|
m_pWidget = nullptr;
|
|
|
|
|
|
m_pRenderer = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmVTKRotationCenterPicker::restoreCursor()
|
|
|
|
|
|
{
|
|
|
|
|
|
if(!m_bCursorOverridden || m_pWidget == nullptr) {
|
|
|
|
|
|
m_bCursorOverridden = false;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(m_bHadExplicitCursor) {
|
|
|
|
|
|
m_pWidget->setCursor(m_oPreviousCursor);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
m_pWidget->unsetCursor();
|
|
|
|
|
|
}
|
|
|
|
|
|
m_bCursorOverridden = false;
|
|
|
|
|
|
}
|