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