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/nmVTKOrientationMarkerContr...

273 lines
8.6 KiB
C++

#include "nmVTKOrientationMarkerController.h"
#include <QCoreApplication>
#include <QThread>
#include <vtkAxesActor.h>
#include <vtkCaptionActor2D.h>
#include <vtkMath.h>
#include <vtkOrientationMarkerWidget.h>
#include <vtkProperty.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkTextProperty.h>
namespace {
// OrientationMarkerWidget 会维护观察关系和辅助 Renderer只能在 GUI 线程操作。
bool isOrientationMarkerGuiThread()
{
QCoreApplication* pApplication = QCoreApplication::instance();
return pApplication == nullptr ||
pApplication->thread() == QThread::currentThread();
}
// 方向轴杆和箭头使用各自的方向色,标签颜色由独立函数统一设置。
void setAxisColor(vtkProperty* pShaftProperty,
vtkProperty* pTipProperty,
double dRed,
double dGreen,
double dBlue)
{
if(pShaftProperty != nullptr) {
pShaftProperty->SetColor(dRed, dGreen, dBlue);
}
if(pTipProperty != nullptr) {
pTipProperty->SetColor(dRed, dGreen, dBlue);
}
}
void setCaptionColor(vtkCaptionActor2D* pCaptionActor,
double dRed,
double dGreen,
double dBlue)
{
if(pCaptionActor != nullptr &&
pCaptionActor->GetCaptionTextProperty() != nullptr) {
pCaptionActor->GetCaptionTextProperty()->SetColor(
dRed, dGreen, dBlue);
}
}
}
nmVTKOrientationMarkerController::nmVTKOrientationMarkerController()
: m_pAxesActor(vtkSmartPointer<vtkAxesActor>::New()),
m_pMarkerWidget(
vtkSmartPointer<vtkOrientationMarkerWidget>::New()),
m_pInteractor(nullptr),
m_pRenderer(nullptr),
m_bVisible(true),
m_bViewAvailable(false)
{
Q_ASSERT(isOrientationMarkerGuiThread());
m_dViewport[0] = 0.015;
m_dViewport[1] = 0.015;
m_dViewport[2] = 0.13;
m_dViewport[3] = 0.13;
if(m_pAxesActor != nullptr) {
m_pAxesActor->SetXAxisLabelText("X");
m_pAxesActor->SetYAxisLabelText("Y");
m_pAxesActor->SetZAxisLabelText("Z");
setAxisColor(m_pAxesActor->GetXAxisShaftProperty(),
m_pAxesActor->GetXAxisTipProperty(),
1.0, 0.0, 0.0);
setAxisColor(m_pAxesActor->GetYAxisShaftProperty(),
m_pAxesActor->GetYAxisTipProperty(),
0.0, 1.0, 0.0);
setAxisColor(m_pAxesActor->GetZAxisShaftProperty(),
m_pAxesActor->GetZAxisTipProperty(),
0.0, 0.0, 1.0);
setCaptionColor(m_pAxesActor->GetXAxisCaptionActor2D(),
1.0, 1.0, 1.0);
setCaptionColor(m_pAxesActor->GetYAxisCaptionActor2D(),
1.0, 1.0, 1.0);
setCaptionColor(m_pAxesActor->GetZAxisCaptionActor2D(),
1.0, 1.0, 1.0);
}
if(m_pMarkerWidget != nullptr) {
// 构造阶段尚未绑定 Interactor只关闭键盘入口并保存默认视口。
m_pMarkerWidget->KeyPressActivationOff();
m_pMarkerWidget->SetViewport(m_dViewport);
}
}
nmVTKOrientationMarkerController::~nmVTKOrientationMarkerController()
{
Q_ASSERT(isOrientationMarkerGuiThread());
unbindInteractor();
m_pMarkerWidget = nullptr;
m_pAxesActor = nullptr;
}
bool nmVTKOrientationMarkerController::bindInteractor(
vtkRenderWindowInteractor* pInteractor,
vtkRenderer* pRenderer)
{
Q_ASSERT(isOrientationMarkerGuiThread());
if(!isOrientationMarkerGuiThread()) {
return false;
}
// 每次绑定都从干净状态开始,避免 Widget 保留旧窗口的观察关系。
unbindInteractor();
vtkRenderWindow* pRenderWindow = pInteractor != nullptr ?
pInteractor->GetRenderWindow() : nullptr;
if(pInteractor == nullptr || pRenderer == nullptr ||
pRenderWindow == nullptr || pRenderer->GetRenderWindow() != pRenderWindow ||
m_pAxesActor == nullptr || m_pMarkerWidget == nullptr) {
return false;
}
m_pInteractor = pInteractor;
m_pRenderer = pRenderer;
// 显式指定主 Renderer不能依赖鼠标位置推断多 Renderer 窗口的父相机。
m_pMarkerWidget->SetOrientationMarker(m_pAxesActor);
m_pMarkerWidget->SetDefaultRenderer(m_pRenderer);
m_pMarkerWidget->SetCurrentRenderer(m_pRenderer);
m_pMarkerWidget->SetInteractor(m_pInteractor);
updateEnabledState();
bool bBindingValid =
m_pMarkerWidget->GetInteractor() == m_pInteractor &&
m_pMarkerWidget->GetDefaultRenderer() == m_pRenderer &&
m_pMarkerWidget->GetOrientationMarker() ==
m_pAxesActor.GetPointer();
if(bBindingValid && m_bVisible && m_bViewAvailable) {
bBindingValid = m_pMarkerWidget->GetEnabled() != 0 &&
m_pMarkerWidget->GetInteractive() == 0 &&
m_pMarkerWidget->GetCurrentRenderer() == m_pRenderer;
}
if(!bBindingValid) {
unbindInteractor();
return false;
}
return true;
}
void nmVTKOrientationMarkerController::unbindInteractor()
{
Q_ASSERT(isOrientationMarkerGuiThread());
if(!isOrientationMarkerGuiThread()) {
return;
}
if(m_pMarkerWidget == nullptr) {
m_pInteractor = nullptr;
m_pRenderer = nullptr;
return;
}
// 只有已绑定且实际启用时才禁用,重复解绑不会触发 VTK 状态警告。
if(m_pMarkerWidget->GetInteractor() != nullptr &&
m_pMarkerWidget->GetEnabled() != 0) {
m_pMarkerWidget->SetEnabled(0);
}
// 完整解绑会释放 Widget 持有的 Actor、Renderer 和 Interactor 引用。
m_pMarkerWidget->SetOrientationMarker(nullptr);
m_pMarkerWidget->SetDefaultRenderer(nullptr);
m_pMarkerWidget->SetCurrentRenderer(nullptr);
m_pMarkerWidget->SetInteractor(nullptr);
m_pInteractor = nullptr;
m_pRenderer = nullptr;
}
void nmVTKOrientationMarkerController::setVisible(bool bVisible)
{
Q_ASSERT(isOrientationMarkerGuiThread());
if(!isOrientationMarkerGuiThread()) {
return;
}
m_bVisible = bVisible;
updateEnabledState();
}
bool nmVTKOrientationMarkerController::isVisible() const
{
return m_bVisible;
}
void nmVTKOrientationMarkerController::setViewAvailable(bool bAvailable)
{
Q_ASSERT(isOrientationMarkerGuiThread());
if(!isOrientationMarkerGuiThread()) {
return;
}
m_bViewAvailable = bAvailable;
updateEnabledState();
}
bool nmVTKOrientationMarkerController::isViewAvailable() const
{
return m_bViewAvailable;
}
bool nmVTKOrientationMarkerController::setViewport(
double dXMinimum,
double dYMinimum,
double dXMaximum,
double dYMaximum)
{
Q_ASSERT(isOrientationMarkerGuiThread());
if(!isOrientationMarkerGuiThread() ||
!vtkMath::IsFinite(dXMinimum) ||
!vtkMath::IsFinite(dYMinimum) ||
!vtkMath::IsFinite(dXMaximum) ||
!vtkMath::IsFinite(dYMaximum) ||
dXMinimum < 0.0 || dYMinimum < 0.0 ||
dXMaximum > 1.0 || dYMaximum > 1.0 ||
dXMaximum <= dXMinimum || dYMaximum <= dYMinimum ||
m_pMarkerWidget == nullptr) {
return false;
}
m_dViewport[0] = dXMinimum;
m_dViewport[1] = dYMinimum;
m_dViewport[2] = dXMaximum;
m_dViewport[3] = dYMaximum;
m_pMarkerWidget->SetViewport(m_dViewport);
return true;
}
void nmVTKOrientationMarkerController::updateEnabledState()
{
if(m_pMarkerWidget == nullptr) {
return;
}
const bool bShouldEnable = m_pAxesActor != nullptr &&
m_pInteractor != nullptr &&
m_pRenderer != nullptr &&
m_pMarkerWidget->GetInteractor() == m_pInteractor &&
m_pMarkerWidget->GetDefaultRenderer() == m_pRenderer &&
m_pMarkerWidget->GetOrientationMarker() ==
m_pAxesActor.GetPointer() &&
m_bVisible && m_bViewAvailable;
if(bShouldEnable) {
// VTK 7.1 禁用 Widget 后会清空 CurrentRenderer恢复前必须重新指定。
m_pMarkerWidget->SetCurrentRenderer(m_pRenderer);
m_pMarkerWidget->SetEnabled(1);
if(m_pMarkerWidget->GetEnabled() != 0 &&
m_pMarkerWidget->GetInteractive() != 0) {
// Interactive 只有在首次成功启用后设置才会被 VTK 正确接受。
m_pMarkerWidget->SetInteractive(0);
}
return;
}
// 临时隐藏只禁用 Widget保留 Marker、Interactor 和默认 Renderer 绑定。
if(m_pMarkerWidget->GetInteractor() != nullptr &&
m_pMarkerWidget->GetEnabled() != 0) {
m_pMarkerWidget->SetEnabled(0);
}
}