diff --git a/Modules/QtWidgetsExt/include/QmitkSliderNavigatorWidget.h b/Modules/QtWidgetsExt/include/QmitkSliderNavigatorWidget.h index a01edca994..99d5e3fd59 100644 --- a/Modules/QtWidgetsExt/include/QmitkSliderNavigatorWidget.h +++ b/Modules/QtWidgetsExt/include/QmitkSliderNavigatorWidget.h @@ -1,117 +1,126 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKSLIDERNAVIGATORWIDGET_H_ #define QMITKSLIDERNAVIGATORWIDGET_H_ #include "MitkQtWidgetsExtExports.h" #include "ui_QmitkSliderNavigator.h" #include #include #include class MITKQTWIDGETSEXT_EXPORT QmitkSliderNavigatorWidget : public QWidget, public Ui::QmitkSliderNavigator { Q_OBJECT public: QmitkSliderNavigatorWidget(QWidget *parent = nullptr, Qt::WindowFlags f = nullptr); QString GetLabelUnit(); /** * \brief Converts the passed value to a QString representation. * * If the value exceeds a certain maximum, "INF" (for "infinity") is displayed * instead. */ QString ClippedValueToString(float value); /** * \brief Returns range-minimum (displayed as label left of slider if enabled) */ QString GetMinValueLabel(); QString GetMaxValueLabel(); int GetPos(); + bool GetInverseDirection() const; + + bool GetInvertedControls() const; + public slots: /** * \brief Updates the slider with the recent changes applied to the navigator. * * Intended to be called via event mechanism, e.g. if the connected * mitk::Stepper is modified. */ void Refetch(); void SetStepper(mitk::Stepper *stepper); void ShowLabels(bool show); /** * \brief En-/disables displaying of the unit label (range will be displayed * without unit if enabled). */ void ShowLabelUnit(bool show); void SetPos(int val); void SetInverseDirection(bool inverseDirection); + void SetInvertedControls(bool invertedControls); + protected slots: - void slider_valueChanged(int); + void slider_valueChanged(double); /** * \brief Set range minimum and maximum (displayed as labels left and right * of slider if enabled) */ void SetLabelValues(float min, float max); void SetLabelValuesValid(bool minValid, bool maxValid); /** * \brief Set range unit (e.g. mm or ms) which will be displayed below range * labels if enabled. */ void SetLabelUnit(const char *unit); /** * \brief Configure slider with labels according to range and unit settings */ void SetLabels(); - void spinBox_valueChanged(int); + void spinBox_valueChanged(double); + protected: bool m_HasLabelUnit; bool m_MaxValueValid; bool m_MinValueValid; QString m_LabelUnit; mitk::Stepper::Pointer m_Stepper; bool m_InRefetch; bool m_HasLabels; float m_MinValue; float m_MaxValue; bool m_InverseDirection; + bool m_InvertedControls; + }; #endif diff --git a/Modules/QtWidgetsExt/src/QmitkSliderNavigator.ui b/Modules/QtWidgetsExt/src/QmitkSliderNavigator.ui index 458316b523..fcb08fc3da 100644 --- a/Modules/QtWidgetsExt/src/QmitkSliderNavigator.ui +++ b/Modules/QtWidgetsExt/src/QmitkSliderNavigator.ui @@ -1,191 +1,151 @@ QmitkSliderNavigator 0 0 235 137 0 0 0 0 32767 32767 QmitkSliderNavigator 2 2 2 2 0 - + - + 0 0 - - - 50 - 0 - - - - 0 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - 0 - - - Qt::Horizontal + + + + true - - - - 0 0 45 0 45 32767 <p align="center">XXX</p> false - - - - true + + + + + 0 + 0 + + + + + 0 0 45 0 45 32767 <p align="center">XXX</p> false + + + ctkDoubleSlider + QWidget +
ctkDoubleSlider.h
+ 1 +
+ + ctkDoubleSpinBox + QWidget +
ctkDoubleSpinBox.h
+ 1 +
+
mitkStepper.h - - - m_Slider - valueChanged(int) - QmitkSliderNavigator - slider_valueChanged(int) - - - 20 - 20 - - - 20 - 20 - - - - - m_SpinBox - valueChanged(int) - QmitkSliderNavigator - spinBox_valueChanged(int) - - - 20 - 20 - - - 20 - 20 - - - - +
diff --git a/Modules/QtWidgetsExt/src/QmitkSliderNavigatorWidget.cpp b/Modules/QtWidgetsExt/src/QmitkSliderNavigatorWidget.cpp index 03709e0ffe..c0b1918479 100644 --- a/Modules/QtWidgetsExt/src/QmitkSliderNavigatorWidget.cpp +++ b/Modules/QtWidgetsExt/src/QmitkSliderNavigatorWidget.cpp @@ -1,263 +1,311 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkSliderNavigatorWidget.h" QmitkSliderNavigatorWidget::QmitkSliderNavigatorWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { this->setupUi(this); + m_Slider->setOrientation(Qt::Horizontal); + m_Slider->setMinimum(0); + m_Slider->setMaximum(0); + m_Slider->setValue(0); + m_Slider->setSingleStep(1); + m_Slider->setPageStep(10); + + m_SpinBox->setMinimum( 0 ); + m_SpinBox->setMaximum(0); + m_SpinBox->setValue(0); + m_SpinBox->setDecimals(0); + m_SpinBox->setSingleStep(1); + + this->connect(m_Slider, SIGNAL(valueChanged(double)), SLOT(slider_valueChanged(double))); + this->connect(m_SpinBox, SIGNAL(valueChanged(double)), SLOT(spinBox_valueChanged(double))); + // this avoids trying to use m_Stepper until it is set to something != NULL // (additionally to the avoiding recursions during refetching) m_InRefetch = true; // Hide slider labeling -- until it is explicitly activated this->ShowLabels(false); // Set label values as invalid (N/A) this->SetLabelValuesValid(false, false); m_HasLabels = false; m_HasLabelUnit = true; m_InverseDirection = false; + m_InvertedControls = false; } void QmitkSliderNavigatorWidget::Refetch() { if (!m_InRefetch) { m_InRefetch = true; - m_Slider->setMinimum(0); - m_Slider->setMaximum(m_Stepper->GetSteps() - 1); - if (m_InverseDirection) + if (m_Stepper->GetSteps() == 0) { - m_Slider->setValue(m_Stepper->GetSteps() - 1 - m_Stepper->GetPos()); + m_Slider->setMaximum(0); + m_Slider->setValue(0); + m_SpinBox->setMaximum(0); + m_SpinBox->setValue(0); } else { - m_Slider->setValue(m_Stepper->GetPos()); - } + m_Slider->setMaximum(m_Stepper->GetSteps() - 1); + if (m_InverseDirection) + { + m_Slider->setValue(m_Stepper->GetSteps() - 1 - m_Stepper->GetPos()); + } + else + { + m_Slider->setValue(m_Stepper->GetPos()); + } - m_SpinBox->setMinimum(0); - m_SpinBox->setMaximum(m_Stepper->GetSteps() - 1); - if (m_InverseDirection) - { - m_SpinBox->setValue(m_Stepper->GetSteps() - 1 - m_Stepper->GetPos()); - } - else - { - m_SpinBox->setValue(m_Stepper->GetPos()); + m_SpinBox->setMaximum(m_Stepper->GetSteps() - 1); + if (m_InverseDirection) + { + m_SpinBox->setValue(m_Stepper->GetSteps() - 1 - m_Stepper->GetPos()); + } + else + { + m_SpinBox->setValue(m_Stepper->GetPos()); + } } if (m_Stepper->HasRange() && m_HasLabels) { // Show slider with labels according to below settings m_SliderLabelLeft->setHidden(false); m_SliderLabelRight->setHidden(false); if (m_Stepper->HasValidRange()) { this->SetLabelValuesValid(true, true); this->SetLabelValues(m_Stepper->GetRangeMin(), m_Stepper->GetRangeMax()); } else { this->SetLabelValuesValid(false, false); } if (m_Stepper->HasUnitName()) { this->SetLabelUnit(m_Stepper->GetUnitName()); } } else { // Show slider without any labels m_SliderLabelLeft->setHidden(true); m_SliderLabelRight->setHidden(true); } // Update GUI according to above settings this->SetLabels(); m_InRefetch = false; } } void QmitkSliderNavigatorWidget::SetStepper(mitk::Stepper *stepper) { m_Stepper = stepper; // this avoids trying to use m_Stepper until it is set to something != NULL // (additionally to the avoiding recursions during refetching) m_InRefetch = (stepper == nullptr); } -void QmitkSliderNavigatorWidget::slider_valueChanged(int) + +void QmitkSliderNavigatorWidget::slider_valueChanged(double) { if (!m_InRefetch) { if (m_InverseDirection) { m_Stepper->SetPos(m_Stepper->GetSteps() - 1 - m_Slider->value()); } else { m_Stepper->SetPos(m_Slider->value()); } this->Refetch(); } } void QmitkSliderNavigatorWidget::ShowLabels(bool show) { m_HasLabels = show; } void QmitkSliderNavigatorWidget::ShowLabelUnit(bool show) { m_HasLabelUnit = show; } void QmitkSliderNavigatorWidget::SetLabelValues(float min, float max) { m_MinValue = min; m_MaxValue = max; } void QmitkSliderNavigatorWidget::SetLabelValuesValid(bool minValid, bool maxValid) { m_MinValueValid = minValid; m_MaxValueValid = maxValid; } void QmitkSliderNavigatorWidget::SetLabelUnit(const char *unit) { m_LabelUnit = unit; } QString QmitkSliderNavigatorWidget::GetLabelUnit() { return m_LabelUnit; } QString QmitkSliderNavigatorWidget::ClippedValueToString(float value) { if (value < -10000000.0) { return "-INF"; } else if (value > 10000000.0) { return "+INF"; } else { return QString::number(value, 'f', 2); } } QString QmitkSliderNavigatorWidget::GetMinValueLabel() { if (m_MinValueValid) { return this->ClippedValueToString(m_MinValue); } else { return "N/A"; } } QString QmitkSliderNavigatorWidget::GetMaxValueLabel() { if (m_MaxValueValid) { return this->ClippedValueToString(m_MaxValue); } else { return "N/A"; } } void QmitkSliderNavigatorWidget::SetLabels() { QString minText = "

" + this->GetMinValueLabel(); QString maxText = "

" + this->GetMaxValueLabel(); if (m_HasLabelUnit) { minText += " " + this->GetLabelUnit(); maxText += " " + this->GetLabelUnit(); } if (m_MinValueValid) { minText += "
(pos 0)"; } if (m_MaxValueValid) { maxText += "
(pos " + QString::number(m_Stepper->GetSteps() - 1) + ")"; } minText += "

"; maxText += "

"; m_SliderLabelLeft->setText(minText); m_SliderLabelRight->setText(maxText); } -void QmitkSliderNavigatorWidget::spinBox_valueChanged(int) +void QmitkSliderNavigatorWidget::spinBox_valueChanged(double) { if (!m_InRefetch) { if (m_InverseDirection) { m_Stepper->SetPos(m_Stepper->GetSteps() - 1 - m_SpinBox->value()); } else { m_Stepper->SetPos(m_SpinBox->value()); } this->Refetch(); } } int QmitkSliderNavigatorWidget::GetPos() { return m_Stepper->GetPos(); } void QmitkSliderNavigatorWidget::SetPos(int val) { if (!m_InRefetch) { m_Stepper->SetPos(val); } } +bool QmitkSliderNavigatorWidget::GetInverseDirection() const +{ + return m_InverseDirection; +} + void QmitkSliderNavigatorWidget::SetInverseDirection(bool inverseDirection) { if (inverseDirection != m_InverseDirection) { m_InverseDirection = inverseDirection; this->Refetch(); } } + +bool QmitkSliderNavigatorWidget::GetInvertedControls() const +{ + return m_InvertedControls; +} + +void QmitkSliderNavigatorWidget::SetInvertedControls(bool invertedControls) +{ + if (invertedControls != m_InvertedControls) + { + m_InvertedControls = invertedControls; + m_Slider->setInvertedAppearance(invertedControls); + m_Slider->setInvertedControls(invertedControls); + m_SpinBox->setInvertedControls(invertedControls); + } +} + diff --git a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp index dabc27cba8..88d71f0fe6 100644 --- a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp +++ b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp @@ -1,623 +1,631 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkImageNavigatorView.h" #include #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkImageNavigatorView::VIEW_ID = "org.mitk.views.imagenavigator"; static mitk::DataNode::Pointer GetTopLayerNode(const mitk::DataStorage::SetOfObjects::ConstPointer nodes, const mitk::Point3D &position, mitk::BaseRenderer* renderer) { mitk::DataNode::Pointer node; int maxlayer = -32768; if(nodes.IsNotNull()) { // find node with largest layer, that is the node shown on top in the render window for (unsigned int x = 0; x < nodes->size(); x++) { if ( (nodes->at(x)->GetData()->GetGeometry() != NULL) && nodes->at(x)->GetData()->GetGeometry()->IsInside(position) ) { int layer = 0; if(!(nodes->at(x)->GetIntProperty("layer", layer))) continue; if(layer > maxlayer) { if( static_cast(nodes->at(x))->IsVisible(renderer) ) { node = nodes->at(x); maxlayer = layer; } } } } } return node; } QmitkImageNavigatorView::QmitkImageNavigatorView() : m_AxialStepper(0) , m_SagittalStepper(0) , m_FrontalStepper(0) , m_TimeStepper(0) , m_Parent(0) , m_IRenderWindowPart(0) { } QmitkImageNavigatorView::~QmitkImageNavigatorView() { } void QmitkImageNavigatorView::CreateQtPartControl(QWidget *parent) { // create GUI widgets m_Parent = parent; m_Controls.setupUi(parent); connect(m_Controls.m_XWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); connect(m_Controls.m_YWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); connect(m_Controls.m_ZWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); m_Parent->setEnabled(false); mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); this->RenderWindowPartActivated(renderPart); } void QmitkImageNavigatorView::SetFocus () { m_Controls.m_XWorldCoordinateSpinBox->setFocus(); } void QmitkImageNavigatorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (this->m_IRenderWindowPart != renderWindowPart) { this->m_IRenderWindowPart = renderWindowPart; this->m_Parent->setEnabled(true); QmitkRenderWindow* renderWindow = renderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { if (m_AxialStepper) m_AxialStepper->deleteLater(); m_AxialStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorAxial, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorAxialFromSimpleExample"); m_Controls.m_SliceNavigatorAxial->setEnabled(true); m_Controls.m_AxialLabel->setEnabled(true); m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(true); connect(m_AxialStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); connect(m_AxialStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorAxial->setEnabled(false); m_Controls.m_AxialLabel->setEnabled(false); m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(false); } renderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal"); if (renderWindow) { if (m_SagittalStepper) m_SagittalStepper->deleteLater(); m_SagittalStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorSagittal, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorSagittalFromSimpleExample"); m_Controls.m_SliceNavigatorSagittal->setEnabled(true); m_Controls.m_SagittalLabel->setEnabled(true); m_Controls.m_YWorldCoordinateSpinBox->setEnabled(true); connect(m_SagittalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); connect(m_SagittalStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorSagittal->setEnabled(false); m_Controls.m_SagittalLabel->setEnabled(false); m_Controls.m_YWorldCoordinateSpinBox->setEnabled(false); } renderWindow = renderWindowPart->GetQmitkRenderWindow("coronal"); if (renderWindow) { if (m_FrontalStepper) m_FrontalStepper->deleteLater(); m_FrontalStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorFrontal, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorFrontalFromSimpleExample"); m_Controls.m_SliceNavigatorFrontal->setEnabled(true); m_Controls.m_CoronalLabel->setEnabled(true); m_Controls.m_XWorldCoordinateSpinBox->setEnabled(true); connect(m_FrontalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); connect(m_FrontalStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorFrontal->setEnabled(false); m_Controls.m_CoronalLabel->setEnabled(false); m_Controls.m_XWorldCoordinateSpinBox->setEnabled(false); } mitk::SliceNavigationController* timeController = renderWindowPart->GetTimeNavigationController(); if (timeController) { if (m_TimeStepper) m_TimeStepper->deleteLater(); m_TimeStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorTime, timeController->GetTime(), "sliceNavigatorTimeFromSimpleExample"); m_Controls.m_SliceNavigatorTime->setEnabled(true); m_Controls.m_TimeLabel->setEnabled(true); connect(m_TimeStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorTime->setEnabled(false); m_Controls.m_TimeLabel->setEnabled(false); } this->OnRefetch(); this->UpdateStatusBar(); } } void QmitkImageNavigatorView::UpdateStatusBar() { if (m_IRenderWindowPart != nullptr) { mitk::Point3D position = m_IRenderWindowPart->GetSelectedPosition(); mitk::BaseRenderer::Pointer renderer = mitk::BaseRenderer::GetInstance(m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetVtkRenderWindow()); mitk::TNodePredicateDataType::Pointer isImageData = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetDataStorage()->GetSubset(isImageData).GetPointer(); if (nodes.IsNotNull()) { mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; int component = 0; node = GetTopLayerNode(nodes, position, renderer); if (node.IsNotNull()) { bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = this->GetDataStorage()->GetSources(node, NULL, true); if (!sourcenodes->empty()) { topSourceNode = GetTopLayerNode(sourcenodes, position, renderer); } if (topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } // get the position and pixel value from the image and build up status bar text auto statusBar = mitk::StatusBar::GetInstance(); if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(position, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); statusBar->DisplayImageInfo(position, p, renderer->GetTime(), pixelValue.c_str()); } else { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(position, p); mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5( mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(renderer->GetTimeStep()), p, pixelValue, component); statusBar->DisplayImageInfo(position, p, renderer->GetTime(), pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } } } void QmitkImageNavigatorView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_IRenderWindowPart = 0; m_Parent->setEnabled(false); } int QmitkImageNavigatorView::GetSizeFlags(bool width) { if(!width) { return berry::Constants::MIN | berry::Constants::MAX | berry::Constants::FILL; } else { return 0; } } int QmitkImageNavigatorView::ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult) { if(width==false) { return 200; } else { return preferredResult; } } int QmitkImageNavigatorView::GetClosestAxisIndex(mitk::Vector3D normal) { // cos(theta) = normal . axis // cos(theta) = (a, b, c) . (d, e, f) // cos(theta) = (a, b, c) . (1, 0, 0) = a // cos(theta) = (a, b, c) . (0, 1, 0) = b // cos(theta) = (a, b, c) . (0, 0, 1) = c double absCosThetaWithAxis[3]; for (int i = 0; i < 3; i++) { absCosThetaWithAxis[i] = fabs(normal[i]); } int largestIndex = 0; double largestValue = absCosThetaWithAxis[0]; for (int i = 1; i < 3; i++) { if (absCosThetaWithAxis[i] > largestValue) { largestValue = absCosThetaWithAxis[i]; largestIndex = i; } } return largestIndex; } void QmitkImageNavigatorView::SetBorderColors() { if (m_IRenderWindowPart) { QString decoColor; QmitkRenderWindow* renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("sagittal"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("coronal"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } } } QString QmitkImageNavigatorView::GetDecorationColorOfGeometry(QmitkRenderWindow* renderWindow) { QColor color; float rgb[3] = {1.0f, 1.0f, 1.0f}; float rgbMax = 255.0f; mitk::BaseRenderer::GetInstance(renderWindow->GetVtkRenderWindow())->GetCurrentWorldPlaneGeometryNode()->GetColor(rgb); color.setRed(static_cast(rgb[0]*rgbMax + 0.5)); color.setGreen(static_cast(rgb[1]*rgbMax + 0.5)); color.setBlue(static_cast(rgb[2]*rgbMax + 0.5)); QString colorAsString = QString(color.name()); return colorAsString; } void QmitkImageNavigatorView::SetBorderColor(int axis, QString colorAsStyleSheetString) { if (axis == 0) { this->SetBorderColor(m_Controls.m_XWorldCoordinateSpinBox, colorAsStyleSheetString); } else if (axis == 1) { this->SetBorderColor(m_Controls.m_YWorldCoordinateSpinBox, colorAsStyleSheetString); } else if (axis == 2) { this->SetBorderColor(m_Controls.m_ZWorldCoordinateSpinBox, colorAsStyleSheetString); } } void QmitkImageNavigatorView::SetBorderColor(QDoubleSpinBox *spinBox, QString colorAsStyleSheetString) { assert(spinBox); spinBox->setStyleSheet(QString("border: 2px solid ") + colorAsStyleSheetString + ";"); } void QmitkImageNavigatorView::SetStepSizes() { this->SetStepSize(0); this->SetStepSize(1); this->SetStepSize(2); } void QmitkImageNavigatorView::SetStepSize(int axis) { if (m_IRenderWindowPart) { mitk::BaseGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D(); if (geometry.IsNotNull()) { mitk::Point3D crossPositionInIndexCoordinates; mitk::Point3D crossPositionInIndexCoordinatesPlus1; mitk::Point3D crossPositionInMillimetresPlus1; mitk::Vector3D transformedAxisDirection; mitk::Point3D crossPositionInMillimetres = m_IRenderWindowPart->GetSelectedPosition(); geometry->WorldToIndex(crossPositionInMillimetres, crossPositionInIndexCoordinates); crossPositionInIndexCoordinatesPlus1 = crossPositionInIndexCoordinates; crossPositionInIndexCoordinatesPlus1[axis] += 1; geometry->IndexToWorld(crossPositionInIndexCoordinatesPlus1, crossPositionInMillimetresPlus1); transformedAxisDirection = crossPositionInMillimetresPlus1 - crossPositionInMillimetres; int closestAxisInMillimetreSpace = this->GetClosestAxisIndex(transformedAxisDirection); double stepSize = transformedAxisDirection.GetNorm(); this->SetStepSize(closestAxisInMillimetreSpace, stepSize); } } } void QmitkImageNavigatorView::SetStepSize(int axis, double stepSize) { if (axis == 0) { m_Controls.m_XWorldCoordinateSpinBox->setSingleStep(stepSize); } else if (axis == 1) { m_Controls.m_YWorldCoordinateSpinBox->setSingleStep(stepSize); } else if (axis == 2) { m_Controls.m_ZWorldCoordinateSpinBox->setSingleStep(stepSize); } } void QmitkImageNavigatorView::OnMillimetreCoordinateValueChanged() { if (m_IRenderWindowPart) { mitk::TimeGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (geometry.IsNotNull()) { mitk::Point3D positionInWorldCoordinates; positionInWorldCoordinates[0] = m_Controls.m_XWorldCoordinateSpinBox->value(); positionInWorldCoordinates[1] = m_Controls.m_YWorldCoordinateSpinBox->value(); positionInWorldCoordinates[2] = m_Controls.m_ZWorldCoordinateSpinBox->value(); m_IRenderWindowPart->SetSelectedPosition(positionInWorldCoordinates); } } } void QmitkImageNavigatorView::OnRefetch() { if (m_IRenderWindowPart) { mitk::BaseGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D(); mitk::TimeGeometry::ConstPointer timeGeometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (geometry.IsNull() && timeGeometry.IsNotNull()) { mitk::TimeStepType timeStep = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetTime()->GetPos(); geometry = timeGeometry->GetGeometryForTimeStep(timeStep); } if (geometry.IsNotNull()) { mitk::BoundingBox::BoundsArrayType bounds = geometry->GetBounds(); mitk::Point3D cornerPoint1InIndexCoordinates; cornerPoint1InIndexCoordinates[0] = bounds[0]; cornerPoint1InIndexCoordinates[1] = bounds[2]; cornerPoint1InIndexCoordinates[2] = bounds[4]; mitk::Point3D cornerPoint2InIndexCoordinates; cornerPoint2InIndexCoordinates[0] = bounds[1]; cornerPoint2InIndexCoordinates[1] = bounds[3]; cornerPoint2InIndexCoordinates[2] = bounds[5]; if (!geometry->GetImageGeometry()) { cornerPoint1InIndexCoordinates[0] += 0.5; cornerPoint1InIndexCoordinates[1] += 0.5; cornerPoint1InIndexCoordinates[2] += 0.5; cornerPoint2InIndexCoordinates[0] -= 0.5; cornerPoint2InIndexCoordinates[1] -= 0.5; cornerPoint2InIndexCoordinates[2] -= 0.5; } mitk::Point3D crossPositionInWorldCoordinates = m_IRenderWindowPart->GetSelectedPosition(); mitk::Point3D cornerPoint1InWorldCoordinates; mitk::Point3D cornerPoint2InWorldCoordinates; geometry->IndexToWorld(cornerPoint1InIndexCoordinates, cornerPoint1InWorldCoordinates); geometry->IndexToWorld(cornerPoint2InIndexCoordinates, cornerPoint2InWorldCoordinates); m_Controls.m_XWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_YWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_XWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); m_Controls.m_YWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); m_Controls.m_ZWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); m_Controls.m_XWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); m_Controls.m_YWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); m_Controls.m_ZWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); m_Controls.m_XWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[0]); m_Controls.m_YWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[1]); m_Controls.m_ZWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[2]); m_Controls.m_XWorldCoordinateSpinBox->blockSignals(false); m_Controls.m_YWorldCoordinateSpinBox->blockSignals(false); m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(false); /// Calculating 'inverse direction' property. mitk::AffineTransform3D::MatrixType matrix = geometry->GetIndexToWorldTransform()->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); for (int worldAxis = 0; worldAxis < 3; ++worldAxis) { QmitkRenderWindow* renderWindow = worldAxis == 0 ? m_IRenderWindowPart->GetQmitkRenderWindow("sagittal") : worldAxis == 1 ? m_IRenderWindowPart->GetQmitkRenderWindow("coronal") : m_IRenderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { const mitk::BaseGeometry* rendererGeometry = renderWindow->GetRenderer()->GetCurrentWorldGeometry(); /// Because of some problems with the current way of event signalling, /// 'Modified' events are sent out from the stepper while the renderer /// does not have a geometry yet. Therefore, we do a nullptr check here. /// See bug T22122. This check can be resolved after T22122 got fixed. if (rendererGeometry) { int dominantAxis = itk::Function::Max3( inverseMatrix[0][worldAxis], inverseMatrix[1][worldAxis], inverseMatrix[2][worldAxis]); bool referenceGeometryAxisInverted = inverseMatrix[dominantAxis][worldAxis] < 0; bool rendererZAxisInverted = rendererGeometry->GetAxisVector(2)[worldAxis] < 0; /// `referenceGeometryAxisInverted` tells if the direction of the corresponding axis /// of the reference geometry is flipped compared to the 'world direction' or not. /// /// `rendererZAxisInverted` tells if direction of the renderer geometry z axis is /// flipped compared to the 'world direction' or not. This is the same as the indexing /// direction in the slice navigation controller and matches the 'top' property when /// initialising the renderer planes. (If 'top' was true then the direction is /// inverted.) /// /// The world direction can be +1 ('up') that means right, anterior or superior, or /// it can be -1 ('down') that means left, posterior or inferior, respectively. /// /// If these two do not match, we have to invert the index between the slice navigation /// controller and the slider navigator widget, so that the user can see and control /// the index according to the reference geometry, rather than the slice navigation /// controller. The index in the slice navigation controller depends on in which way /// the reference geometry has been resliced for the renderer, and it does not necessarily /// match neither the world direction, nor the direction of the corresponding axis of /// the reference geometry. Hence, it is a merely internal information that should not /// be exposed to the GUI. + /// + /// So that one can navigate in the same world direction by dragging the slider + /// right, regardless of the direction of the corresponding axis of the reference + /// geometry, we invert the direction of the controls if the reference geometry axis + /// is inverted but the direction is not ('inversDirection' is false) or the other + /// way around. bool inverseDirection = referenceGeometryAxisInverted != rendererZAxisInverted; + bool invertedControls = referenceGeometryAxisInverted != inverseDirection; QmitkSliderNavigatorWidget* navigatorWidget = worldAxis == 0 ? m_Controls.m_SliceNavigatorSagittal : worldAxis == 1 ? m_Controls.m_SliceNavigatorFrontal : m_Controls.m_SliceNavigatorAxial; navigatorWidget->SetInverseDirection(inverseDirection); + navigatorWidget->SetInvertedControls(invertedControls); } } } } this->SetBorderColors(); } }