You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmRender/nmVTKRotationCenterPicker.cpp

317 lines
9.3 KiB
C++

#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;
}