#include "nmVTKRotationCenterPicker.h" #include #include #include #include #include #include #include #include #include #include #include #include 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(nPosition) + 0.5) * static_cast(nRenderExtent) / static_cast(nWidgetExtent) - 0.5; int nResult = static_cast(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(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(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(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(pEvent); if(static_cast(pMouseEvent->button()) == m_nPendingReleaseButton) { m_nPendingReleaseButton = static_cast(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(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(pEvent); if(pMouseEvent->button() != Qt::LeftButton && pMouseEvent->button() != Qt::RightButton) { return PickEventResult(); } m_nPendingReleaseButton = static_cast(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 pCellPicker = vtkSmartPointer::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; }