diff --git a/Modules/Core/include/mitkCrosshairManager.h b/Modules/Core/include/mitkCrosshairManager.h index 6f074fe677..70cde209de 100644 --- a/Modules/Core/include/mitkCrosshairManager.h +++ b/Modules/Core/include/mitkCrosshairManager.h @@ -1,102 +1,105 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKCROSSHAIRMANAGER_H #define MITKCROSSHAIRMANAGER_H #include #include #include #include #include #include #include namespace mitk { /** * \brief The CrosshairManager takes care of the correct settings for the plane geometries * that form the crosshair. * * The CrosshairManager keeps track of a TimeGeometry, which is used to compute * the three different plane geometries (axial, coronal, sagittal). * These three plane geometries can be added to / removed from the data storage * using this CrosshairManager. Adding / removing them to / from the data storage * will change the rendering behavior for the crosshair - only data nodes * inside the data storage will be rendered. * * */ class MITKCORE_EXPORT CrosshairManager : public itk::Object { public: mitkClassMacroItkParent(CrosshairManager, itk::Object); mitkNewMacro2Param(Self, DataStorage*, BaseRenderer*); itkSetObjectMacro(DataStorage, DataStorage); itkSetObjectMacro(BaseRenderer, BaseRenderer); /** * \brief Set the input time geometry out of which the oriented geometries will be created. */ void ComputeOrientedTimeGeometries(const TimeGeometry* geometry); void SetCrosshairPosition(const Point3D& selectedPoint); Point3D GetCrosshairPosition() const; + void UpdateSlice(const SliceNavigationController* sliceNavigationController); + void SetCrosshairVisibility(bool visible); bool GetCrosshairVisibility() const; void SetCrosshairGap(unsigned int gapSize); void AddPlanesToDataStorage(); void RemovePlanesFromDataStorage(); protected: CrosshairManager(DataStorage* dataStorage, BaseRenderer* baseRenderer); ~CrosshairManager(); void InitializePlaneProperties(DataNode::Pointer planeNode, const std::string& planeName); void InitializePlaneData(DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int& slice); + void UpdatePlaneSlice(DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int slice); void SetCrosshairPosition(const Point3D& selectedPoint, DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int& slice); void AddPlaneToDataStorage(DataNode::Pointer planeNode, DataNode::Pointer parent); DataStorage* m_DataStorage; BaseRenderer* m_BaseRenderer; TimeGeometry::ConstPointer m_InputTimeGeometry; TimeGeometry::Pointer m_AxialTimeGeometry; TimeGeometry::Pointer m_CoronalTimeGeometry; TimeGeometry::Pointer m_SagittalTimeGeometry; unsigned int m_AxialSlice; unsigned int m_CoronalSlice; unsigned int m_SagittalSlice; /** * @brief The 3 helper objects which contain the plane geometry. */ DataNode::Pointer m_AxialPlaneNode; DataNode::Pointer m_CoronalPlaneNode; DataNode::Pointer m_SagittalPlaneNode; DataNode::Pointer m_ParentNodeForGeometryPlanes; }; } // namespace mitk #endif // MITKCROSSHAIRMANAGER_H diff --git a/Modules/Core/src/Controllers/mitkCrosshairManager.cpp b/Modules/Core/src/Controllers/mitkCrosshairManager.cpp index f27467e1d7..20ed92193c 100644 --- a/Modules/Core/src/Controllers/mitkCrosshairManager.cpp +++ b/Modules/Core/src/Controllers/mitkCrosshairManager.cpp @@ -1,351 +1,410 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkCrosshairManager.h" #include #include #include #include #include #include mitk::CrosshairManager::CrosshairManager(DataStorage* dataStorage, BaseRenderer* baseRenderer) : m_DataStorage(dataStorage) , m_BaseRenderer(baseRenderer) , m_InputTimeGeometry() , m_AxialTimeGeometry() , m_CoronalTimeGeometry() , m_SagittalTimeGeometry() { m_AxialPlaneNode = mitk::DataNode::New(); m_CoronalPlaneNode = mitk::DataNode::New(); m_SagittalPlaneNode = mitk::DataNode::New(); std::string rendererName = std::string(m_BaseRenderer->GetName()); this->InitializePlaneProperties(m_AxialPlaneNode, std::string(rendererName + "axial.plane")); this->InitializePlaneProperties(m_CoronalPlaneNode, std::string(rendererName + "coronal.plane")); this->InitializePlaneProperties(m_SagittalPlaneNode, std::string(rendererName + "sagittal.plane")); m_ParentNodeForGeometryPlanes = mitk::DataNode::New(); m_ParentNodeForGeometryPlanes->SetProperty("name", mitk::StringProperty::New(rendererName)); m_ParentNodeForGeometryPlanes->SetProperty("helper object", mitk::BoolProperty::New(true)); } mitk::CrosshairManager::~CrosshairManager() { this->RemovePlanesFromDataStorage(); } void mitk::CrosshairManager::ComputeOrientedTimeGeometries(const TimeGeometry* geometry) { if (nullptr != geometry) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } if (m_InputTimeGeometry == geometry) { return; } m_InputTimeGeometry = geometry; if (m_InputTimeGeometry.IsNull()) { return; } if (0 == m_InputTimeGeometry->CountTimeSteps()) { return; } try { m_AxialTimeGeometry = SliceNavigationHelper::CreateOrientedTimeGeometry( m_InputTimeGeometry, PlaneGeometry::Axial, false, false, true); m_CoronalTimeGeometry = SliceNavigationHelper::CreateOrientedTimeGeometry( m_InputTimeGeometry, PlaneGeometry::Coronal, false, true, false); m_SagittalTimeGeometry = SliceNavigationHelper::CreateOrientedTimeGeometry( m_InputTimeGeometry, PlaneGeometry::Sagittal, true, true, false); } catch (const mitk::Exception& e) { MITK_ERROR << "Unable to create oriented time geometries\n" << "Reason: " << e.GetDescription(); } this->InitializePlaneData(m_AxialPlaneNode, m_AxialTimeGeometry, m_AxialSlice); this->InitializePlaneData(m_CoronalPlaneNode, m_CoronalTimeGeometry, m_CoronalSlice); this->InitializePlaneData(m_SagittalPlaneNode, m_SagittalTimeGeometry, m_SagittalSlice); RenderingManager::GetInstance()->RequestUpdate(m_BaseRenderer->GetRenderWindow()); } void mitk::CrosshairManager::SetCrosshairPosition(const Point3D& selectedPoint) { if (m_AxialPlaneNode.IsNull() || m_CoronalPlaneNode.IsNull() || m_SagittalPlaneNode.IsNull()) { return; } if (m_AxialTimeGeometry.IsNull() || m_CoronalTimeGeometry.IsNull() || m_SagittalTimeGeometry.IsNull()) { return; } this->SetCrosshairPosition(selectedPoint, m_AxialPlaneNode, m_AxialTimeGeometry, m_AxialSlice); this->SetCrosshairPosition(selectedPoint, m_CoronalPlaneNode, m_CoronalTimeGeometry, m_CoronalSlice); this->SetCrosshairPosition(selectedPoint, m_SagittalPlaneNode, m_SagittalTimeGeometry, m_SagittalSlice); } mitk::Point3D mitk::CrosshairManager::GetCrosshairPosition() const { if (m_InputTimeGeometry.IsNull()) { // return null-point since we don't show the crosshair anyway return Point3D(0.0); } Point3D point = m_InputTimeGeometry->GetCenterInWorld(); PlaneGeometry* axialPlaneGeometry = nullptr; PlaneGeometry* coronalPlaneGeometry = nullptr; PlaneGeometry* sagittalPlaneGeometry = nullptr; // get the currently selected time point auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); try { axialPlaneGeometry = SliceNavigationHelper::GetCurrentPlaneGeometry(m_AxialTimeGeometry, selectedTimePoint, m_AxialSlice); coronalPlaneGeometry = SliceNavigationHelper::GetCurrentPlaneGeometry(m_CoronalTimeGeometry, selectedTimePoint, m_CoronalSlice); sagittalPlaneGeometry = SliceNavigationHelper::GetCurrentPlaneGeometry(m_SagittalTimeGeometry, selectedTimePoint, m_SagittalSlice); } catch (const mitk::Exception& e) { MITK_ERROR << "Unable to get plane geometries. Using default center point.\n" << "Reason: " << e.GetDescription(); } Line3D line; if (nullptr != axialPlaneGeometry && nullptr != coronalPlaneGeometry && axialPlaneGeometry->IntersectionLine(coronalPlaneGeometry, line)) { if (nullptr != sagittalPlaneGeometry && sagittalPlaneGeometry->IntersectionPoint(line, point)) { return point; } } // return input geometry center point if no intersection point was found return point; } +void mitk::CrosshairManager::UpdateSlice(const SliceNavigationController* sliceNavigationController) +{ + auto viewDirection = sliceNavigationController->GetViewDirection(); + unsigned int slicePosition = sliceNavigationController->GetSlice()->GetPos(); + switch (viewDirection) + { + case mitk::SliceNavigationController::Original: + return; + case mitk::SliceNavigationController::Axial: + { + m_AxialSlice = slicePosition; + this->UpdatePlaneSlice(m_AxialPlaneNode, m_AxialTimeGeometry, m_AxialSlice); + break; + } + case mitk::SliceNavigationController::Coronal: + { + m_CoronalSlice = slicePosition; + this->UpdatePlaneSlice(m_CoronalPlaneNode, m_CoronalTimeGeometry, m_CoronalSlice); + break; + } + case mitk::SliceNavigationController::Sagittal: + { + m_SagittalSlice = slicePosition; + this->UpdatePlaneSlice(m_SagittalPlaneNode, m_SagittalTimeGeometry, m_SagittalSlice); + break; + } + } +} + void mitk::CrosshairManager::SetCrosshairVisibility(bool visible) { if (m_AxialPlaneNode.IsNull() || m_CoronalPlaneNode.IsNull() || m_SagittalPlaneNode.IsNull()) { return; } m_AxialPlaneNode->SetVisibility(visible, m_BaseRenderer); m_CoronalPlaneNode->SetVisibility(visible, m_BaseRenderer); m_SagittalPlaneNode->SetVisibility(visible, m_BaseRenderer); } bool mitk::CrosshairManager::GetCrosshairVisibility() const { if (m_AxialPlaneNode.IsNull() || m_CoronalPlaneNode.IsNull() || m_SagittalPlaneNode.IsNull()) { return false; } bool axialVisibility = false; if (m_AxialPlaneNode->GetVisibility(axialVisibility, m_BaseRenderer)) { bool coronalVisibility = false; if (m_CoronalPlaneNode->GetVisibility(coronalVisibility, m_BaseRenderer)) { bool sagittalVisibility = false; if (m_SagittalPlaneNode->GetVisibility(sagittalVisibility, m_BaseRenderer)) { if (axialVisibility == coronalVisibility && coronalVisibility == sagittalVisibility) { return axialVisibility; } } } } mitkThrow() << "Invalid state of plane visibility."; } void mitk::CrosshairManager::SetCrosshairGap(unsigned int gapSize) { m_AxialPlaneNode->SetIntProperty("Crosshair.Gap Size", gapSize); m_CoronalPlaneNode->SetIntProperty("Crosshair.Gap Size", gapSize); m_SagittalPlaneNode->SetIntProperty("Crosshair.Gap Size", gapSize); } void mitk::CrosshairManager::AddPlanesToDataStorage() { if (nullptr == m_DataStorage) { return; } if (m_AxialPlaneNode.IsNull() || m_CoronalPlaneNode.IsNull() || m_SagittalPlaneNode.IsNull() || m_ParentNodeForGeometryPlanes.IsNull()) { return; } this->AddPlaneToDataStorage(m_ParentNodeForGeometryPlanes, nullptr); this->AddPlaneToDataStorage(m_AxialPlaneNode, m_ParentNodeForGeometryPlanes); this->AddPlaneToDataStorage(m_CoronalPlaneNode, m_ParentNodeForGeometryPlanes); this->AddPlaneToDataStorage(m_SagittalPlaneNode, m_ParentNodeForGeometryPlanes); } void mitk::CrosshairManager::RemovePlanesFromDataStorage() { if (nullptr == m_DataStorage) { return; } if (m_AxialPlaneNode.IsNotNull() && m_CoronalPlaneNode.IsNotNull() && m_SagittalPlaneNode.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) { m_DataStorage->Remove(m_AxialPlaneNode); m_DataStorage->Remove(m_CoronalPlaneNode); m_DataStorage->Remove(m_SagittalPlaneNode); m_DataStorage->Remove(m_ParentNodeForGeometryPlanes); } } void mitk::CrosshairManager::InitializePlaneProperties(DataNode::Pointer planeNode, const std::string& planeName) { planeNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); planeNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(5)); planeNode->SetProperty("Crosshair.Gap Size", mitk::IntProperty::New(32)); // set the crosshair only visible for this specific renderer planeNode->SetVisibility(false); planeNode->SetVisibility(true, m_BaseRenderer); planeNode->SetProperty("name", mitk::StringProperty::New(planeName)); planeNode->SetProperty("helper object", mitk::BoolProperty::New(true)); } void mitk::CrosshairManager::InitializePlaneData(DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int& slice) { if (planeNode.IsNull()) { return; } if (nullptr == timeGeometry) { return; } // get the BaseGeometry of the selected time point auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); auto currentGeometry = timeGeometry->GetGeometryForTimePoint(selectedTimePoint); if (nullptr == currentGeometry) { // time point not valid for the time geometry mitkThrow() << "Cannot extract a base geometry. A time point is selected that is not covered by" << "the given time geometry. Selected time point: " << selectedTimePoint; } const auto* slicedGeometry = dynamic_cast(currentGeometry.GetPointer()); if (nullptr == slicedGeometry) { return; } slice = slicedGeometry->GetSlices() / 2; // center slice PlaneGeometryData::Pointer planeData = PlaneGeometryData::New(); planeData->SetPlaneGeometry(slicedGeometry->GetPlaneGeometry(slice)); planeNode->SetData(planeData); planeNode->SetMapper(mitk::BaseRenderer::Standard2D, mitk::PlaneGeometryDataMapper2D::New()); } +void mitk::CrosshairManager::UpdatePlaneSlice(DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int slice) +{ + mitk::PlaneGeometry* planeGeometry = nullptr; + // get the currently selected time point + auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); + try + { + planeGeometry = SliceNavigationHelper::GetCurrentPlaneGeometry(timeGeometry, selectedTimePoint, slice); + } + catch (const mitk::Exception& e) + { + MITK_ERROR << "Unable to get plane geometries\n" + << "Reason: " << e.GetDescription(); + } + + if (nullptr == planeGeometry) + { + return; + } + + PlaneGeometryData::Pointer planeData = dynamic_cast(planeNode->GetData()); + if (nullptr == planeData) + { + return; + } + + planeData->SetPlaneGeometry(planeGeometry); + planeNode->SetData(planeData); +} + void mitk::CrosshairManager::SetCrosshairPosition(const Point3D& selectedPoint, DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int& slice) { PlaneGeometryData::Pointer planeData = dynamic_cast(planeNode->GetData()); int selectedSlice = -1; try { selectedSlice = SliceNavigationHelper::SelectSliceByPoint(timeGeometry, selectedPoint); } catch (const mitk::Exception& e) { MITK_ERROR << "Unable to select a slice by the given point " << selectedPoint << "\n" << "Reason: " << e.GetDescription(); } if (-1 == selectedSlice) { return; } slice = selectedSlice; mitk::PlaneGeometry* planeGeometry = nullptr; // get the currently selected time point auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); try { planeGeometry = SliceNavigationHelper::GetCurrentPlaneGeometry(timeGeometry, selectedTimePoint, slice); } catch (const mitk::Exception& e) { MITK_ERROR << "Unable to get plane geometries\n" << "Reason: " << e.GetDescription(); } if (nullptr == planeGeometry) { return; } planeData->SetPlaneGeometry(planeGeometry); planeNode->SetData(planeData); } void mitk::CrosshairManager::AddPlaneToDataStorage(DataNode::Pointer planeNode, DataNode::Pointer parent) { if (!m_DataStorage->Exists(planeNode) && (nullptr == parent || m_DataStorage->Exists(parent))) { try { m_DataStorage->Add(planeNode, parent); } catch (std::invalid_argument& /*e*/) { return; } } } diff --git a/Modules/QtWidgets/include/QmitkRenderWindowWidget.h b/Modules/QtWidgets/include/QmitkRenderWindowWidget.h index 22e62fc3d6..16b5131244 100644 --- a/Modules/QtWidgets/include/QmitkRenderWindowWidget.h +++ b/Modules/QtWidgets/include/QmitkRenderWindowWidget.h @@ -1,125 +1,126 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITKRENDERWINDOWWIDGET_H #define QMITKRENDERWINDOWWIDGET_H // qt widgets module #include "MitkQtWidgetsExports.h" #include "QmitkRenderWindow.h" #include "QmitkRenderWindowUtilityWidget.h" // mitk core #include #include #include // qt #include #include class vtkCornerAnnotation; /** * @brief The 'QmitkRenderWindowWidget' is a QFrame that holds a render window * and some associates properties, e.g. decorations. * Decorations are corner annotation (text and color), frame color or background color * and can be set using this class. * The 'QmitkRenderWindowWidget' is used inside a 'QmitkAbstractMultiWidget', where a map contains * several render window widgets to create the multi widget display. * This class uses a CrosshairManager, which allows to use plane geometries as crosshair. */ class MITKQTWIDGETS_EXPORT QmitkRenderWindowWidget : public QFrame { Q_OBJECT public: QmitkRenderWindowWidget( QWidget* parent = nullptr, const QString& widgetName = "", mitk::DataStorage* dataStorage = nullptr, bool windowControls = false); ~QmitkRenderWindowWidget() override; void SetDataStorage(mitk::DataStorage* dataStorage); const QString& GetWidgetName() const { return m_WidgetName; }; QmitkRenderWindow* GetRenderWindow() const { return m_RenderWindow; }; mitk::SliceNavigationController* GetSliceNavigationController() const; void RequestUpdate(); void ForceImmediateUpdate(); void SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower); void ShowGradientBackground(bool enable); std::pair GetGradientBackgroundColors() const { return m_GradientBackgroundColors; }; bool IsGradientBackgroundOn() const; void SetDecorationColor(const mitk::Color& color); mitk::Color GetDecorationColor() const { return m_DecorationColor; }; void ShowColoredRectangle(bool show); bool IsColoredRectangleVisible() const; void ShowCornerAnnotation(bool show); bool IsCornerAnnotationVisible() const; void SetCornerAnnotationText(const std::string& cornerAnnotation); std::string GetCornerAnnotationText() const; bool IsRenderWindowMenuActivated() const; void SetCrosshairVisibility(bool visible); bool GetCrosshairVisibility(); void SetCrosshairGap(unsigned int gapSize); void AddPlanesToDataStorage(); void RemovePlanesFromDataStorage(); void SetCrosshairPosition(const mitk::Point3D& newPosition); mitk::Point3D GetCrosshairPosition() const; void SetGeometry(const itk::EventObject& event); + void SetGeometrySlice(const itk::EventObject& event); private Q_SLOTS: void OnReinitAction(QList selectedNodes); void OnResetAction(QList selectedNodes); private: void InitializeGUI(); void InitializeDecorations(); void ComputeInvertedSliceNavigation(); QString m_WidgetName; QVBoxLayout* m_Layout; mitk::DataStorage* m_DataStorage; QmitkRenderWindow* m_RenderWindow; mitk::CrosshairManager::Pointer m_CrosshairManager; std::pair m_GradientBackgroundColors; mitk::Color m_DecorationColor; vtkSmartPointer m_CornerAnnotation; QmitkRenderWindowUtilityWidget* m_UtilityWidget; bool m_WindowControls; }; #endif // QMITKRENDERWINDOWWIDGET_H diff --git a/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp b/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp index 31b5213918..05c9d31331 100644 --- a/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp +++ b/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp @@ -1,414 +1,425 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkRenderWindowWidget.h" #include #include #include // itk #include // vtk #include #include QmitkRenderWindowWidget::QmitkRenderWindowWidget(QWidget* parent/* = nullptr*/, const QString& widgetName/* = ""*/, mitk::DataStorage* dataStorage/* = nullptr*/, bool windowControls/* = false */) : QFrame(parent) , m_WidgetName(widgetName) , m_DataStorage(dataStorage) , m_RenderWindow(nullptr) , m_CrosshairManager(nullptr) , m_UtilityWidget(nullptr) , m_WindowControls(windowControls) { this->InitializeGUI(); } QmitkRenderWindowWidget::~QmitkRenderWindowWidget() { auto sliceNavigationController = m_RenderWindow->GetSliceNavigationController(); if (nullptr != sliceNavigationController) { sliceNavigationController->SetCrosshairEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkRenderWindowWidget::SetCrosshairPosition)); } } void QmitkRenderWindowWidget::SetDataStorage(mitk::DataStorage* dataStorage) { if (dataStorage == m_DataStorage) { return; } m_DataStorage = dataStorage; if (nullptr != m_RenderWindow) { mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow())->SetDataStorage(dataStorage); } m_CrosshairManager->SetDataStorage(m_DataStorage); } mitk::SliceNavigationController* QmitkRenderWindowWidget::GetSliceNavigationController() const { return m_RenderWindow->GetSliceNavigationController(); } void QmitkRenderWindowWidget::RequestUpdate() { mitk::RenderingManager::GetInstance()->RequestUpdate(m_RenderWindow->renderWindow()); } void QmitkRenderWindowWidget::ForceImmediateUpdate() { mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(m_RenderWindow->renderWindow()); } void QmitkRenderWindowWidget::SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower) { vtkRenderer* vtkRenderer = m_RenderWindow->GetRenderer()->GetVtkRenderer(); if (nullptr == vtkRenderer) { return; } m_GradientBackgroundColors.first = upper; m_GradientBackgroundColors.second = lower; vtkRenderer->SetBackground(lower[0], lower[1], lower[2]); vtkRenderer->SetBackground2(upper[0], upper[1], upper[2]); ShowGradientBackground(true); } void QmitkRenderWindowWidget::ShowGradientBackground(bool show) { m_RenderWindow->GetRenderer()->GetVtkRenderer()->SetGradientBackground(show); } bool QmitkRenderWindowWidget::IsGradientBackgroundOn() const { return m_RenderWindow->GetRenderer()->GetVtkRenderer()->GetGradientBackground(); } void QmitkRenderWindowWidget::SetDecorationColor(const mitk::Color& color) { m_DecorationColor = color; m_CornerAnnotation->GetTextProperty()->SetColor(m_DecorationColor[0], m_DecorationColor[1], m_DecorationColor[2]); QColor hexColor(m_DecorationColor[0] * 255, m_DecorationColor[1] * 255, m_DecorationColor[2] * 255); setStyleSheet("QmitkRenderWindowWidget { border: 2px solid " + hexColor.name(QColor::HexRgb) + "; }"); } void QmitkRenderWindowWidget::ShowColoredRectangle(bool show) { if (show) { setFrameStyle(QFrame::Box | QFrame::Plain); } else { setFrameStyle(NoFrame); } } bool QmitkRenderWindowWidget::IsColoredRectangleVisible() const { return frameStyle() > 0; } void QmitkRenderWindowWidget::ShowCornerAnnotation(bool show) { m_CornerAnnotation->SetVisibility(show); } bool QmitkRenderWindowWidget::IsCornerAnnotationVisible() const { return m_CornerAnnotation->GetVisibility() > 0; } void QmitkRenderWindowWidget::SetCornerAnnotationText(const std::string& cornerAnnotation) { m_CornerAnnotation->SetText(0, cornerAnnotation.c_str()); } std::string QmitkRenderWindowWidget::GetCornerAnnotationText() const { return std::string(m_CornerAnnotation->GetText(0)); } bool QmitkRenderWindowWidget::IsRenderWindowMenuActivated() const { return m_RenderWindow->GetActivateMenuWidgetFlag(); } void QmitkRenderWindowWidget::SetCrosshairVisibility(bool visible) { m_CrosshairManager->SetCrosshairVisibility(visible); this->RequestUpdate(); } bool QmitkRenderWindowWidget::GetCrosshairVisibility() { return m_CrosshairManager->GetCrosshairVisibility(); } void QmitkRenderWindowWidget::SetCrosshairGap(unsigned int gapSize) { m_CrosshairManager->SetCrosshairGap(gapSize); } void QmitkRenderWindowWidget::AddPlanesToDataStorage() { m_CrosshairManager->AddPlanesToDataStorage(); } void QmitkRenderWindowWidget::RemovePlanesFromDataStorage() { m_CrosshairManager->RemovePlanesFromDataStorage(); } void QmitkRenderWindowWidget::InitializeGUI() { m_Layout = new QVBoxLayout(this); m_Layout->setMargin(0); setLayout(m_Layout); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setContentsMargins(0, 0, 0, 0); if (nullptr == m_DataStorage) { return; } mitk::RenderingManager::GetInstance()->SetDataStorage(m_DataStorage); // create render window for this render window widget m_RenderWindow = new QmitkRenderWindow(this, m_WidgetName, nullptr); m_RenderWindow->SetLayoutIndex(mitk::BaseRenderer::ViewDirection::SAGITTAL); auto sliceNavigationController = m_RenderWindow->GetSliceNavigationController(); sliceNavigationController->SetDefaultViewDirection(mitk::SliceNavigationController::Sagittal); if (m_WindowControls) { m_UtilityWidget = new QmitkRenderWindowUtilityWidget(this, m_RenderWindow, m_DataStorage); m_Layout->addWidget(m_UtilityWidget); connect(m_UtilityWidget, &QmitkRenderWindowUtilityWidget::ReinitAction, this, &QmitkRenderWindowWidget::OnReinitAction); connect(m_UtilityWidget, &QmitkRenderWindowUtilityWidget::ResetAction, this, &QmitkRenderWindowWidget::OnResetAction); } m_Layout->addWidget(m_RenderWindow); // set colors and corner annotation InitializeDecorations(); // use crosshair manager m_CrosshairManager = mitk::CrosshairManager::New(m_DataStorage, m_RenderWindow->GetRenderer()); sliceNavigationController->SetCrosshairEvent.AddListener( mitk::MessageDelegate1( this, &QmitkRenderWindowWidget::SetCrosshairPosition)); // finally add observer, after all relevant objects have been created / initialized sliceNavigationController->ConnectGeometrySendEvent(this); + sliceNavigationController->ConnectGeometrySliceEvent(this); mitk::TimeGeometry::ConstPointer timeGeometry = m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()); mitk::RenderingManager::GetInstance()->InitializeView(m_RenderWindow->GetVtkRenderWindow(), timeGeometry); } void QmitkRenderWindowWidget::InitializeDecorations() { vtkRenderer* vtkRenderer = m_RenderWindow->GetRenderer()->GetVtkRenderer(); if (nullptr == vtkRenderer) { return; } // initialize background color gradients float black[3] = { 0.0f, 0.0f, 0.0f }; SetGradientBackgroundColors(black, black); // initialize annotation text and decoration color setFrameStyle(QFrame::Box | QFrame::Plain); m_CornerAnnotation = vtkSmartPointer::New(); m_CornerAnnotation->SetText(0, "Sagittal"); m_CornerAnnotation->SetMaximumFontSize(12); if (0 == vtkRenderer->HasViewProp(m_CornerAnnotation)) { vtkRenderer->AddViewProp(m_CornerAnnotation); } float white[3] = { 1.0f, 1.0f, 1.0f }; SetDecorationColor(mitk::Color(white)); } void QmitkRenderWindowWidget::SetCrosshairPosition(const mitk::Point3D& newPosition) { m_CrosshairManager->SetCrosshairPosition(newPosition); this->RequestUpdate(); } mitk::Point3D QmitkRenderWindowWidget::GetCrosshairPosition() const { return m_CrosshairManager->GetCrosshairPosition(); } void QmitkRenderWindowWidget::SetGeometry(const itk::EventObject& event) { if (!mitk::SliceNavigationController::GeometrySendEvent(nullptr, 0).CheckEvent(&event)) { return; } auto sliceNavigationController = m_RenderWindow->GetSliceNavigationController(); const auto* inputTimeGeometry = sliceNavigationController->GetInputWorldTimeGeometry(); m_CrosshairManager->ComputeOrientedTimeGeometries(inputTimeGeometry); if (m_WindowControls) { this->ComputeInvertedSliceNavigation(); } +} + +void QmitkRenderWindowWidget::SetGeometrySlice(const itk::EventObject& event) +{ + if (!mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0).CheckEvent(&event)) + { + return; + } + auto sliceNavigationController = this->GetSliceNavigationController(); + m_CrosshairManager->UpdateSlice(sliceNavigationController); } void QmitkRenderWindowWidget::ComputeInvertedSliceNavigation() { auto sliceNavigationController = m_RenderWindow->GetSliceNavigationController(); auto viewDirection = sliceNavigationController->GetViewDirection(); unsigned int axis = 0; switch (viewDirection) { case mitk::SliceNavigationController::Original: return; case mitk::SliceNavigationController::Axial: { axis = 2; break; } case mitk::SliceNavigationController::Coronal: { axis = 1; break; } case mitk::SliceNavigationController::Sagittal: { axis = 0; break; } } const auto* inputTimeGeometry = sliceNavigationController->GetInputWorldTimeGeometry(); const mitk::BaseGeometry* rendererGeometry = m_RenderWindow->GetRenderer()->GetCurrentWorldGeometry(); // todo: check timepoint / timestep mitk::TimeStepType timeStep = sliceNavigationController->GetTime()->GetPos(); mitk::BaseGeometry::ConstPointer geometry = inputTimeGeometry->GetGeometryForTimeStep(timeStep); mitk::AffineTransform3D::MatrixType matrix = geometry->GetIndexToWorldTransform()->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); int dominantAxis = itk::Function::Max3(inverseMatrix[0][axis], inverseMatrix[1][axis], inverseMatrix[2][axis]); bool referenceGeometryAxisInverted = inverseMatrix[dominantAxis][axis] < 0; bool rendererZAxisInverted = rendererGeometry->GetAxisVector(2)[axis] < 0; m_UtilityWidget->SetInvertedSliceNavigation(referenceGeometryAxisInverted != rendererZAxisInverted); } void QmitkRenderWindowWidget::OnReinitAction(QList selectedNodes) { if (selectedNodes.empty()) { return; } auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow()); auto boundingBoxPredicate = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false), baseRenderer)); mitk::DataStorage::SetOfObjects::Pointer nodes = mitk::DataStorage::SetOfObjects::New(); for (const auto& dataNode : selectedNodes) { if (boundingBoxPredicate->CheckNode(dataNode)) { nodes->InsertElement(nodes->Size(), dataNode); } } if (nodes->empty()) { return; } if (1 == nodes->Size()) { auto selectedImage = dynamic_cast(nodes->ElementAt(0)->GetData()); if (nullptr != selectedImage) { mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), selectedImage->GetTimeGeometry()); return; } } auto boundingGeometry = m_DataStorage->ComputeBoundingGeometry3D(nodes, "visible", baseRenderer); mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), boundingGeometry); } void QmitkRenderWindowWidget::OnResetAction(QList selectedNodes) { if (selectedNodes.empty()) { return; } auto selectedImage = dynamic_cast(selectedNodes.front()->GetData()); if (nullptr == selectedImage) { return; } const mitk::TimeGeometry* referenceGeometry = selectedImage->GetTimeGeometry(); if (nullptr == referenceGeometry) { return; } mitk::TimeStepType imageTimeStep = 0; // store the current position to set it again later, if the camera should not be reset mitk::Point3D currentPosition = this->GetCrosshairPosition(); // store the current time step to set it again later, if the camera should not be reset auto* renderingManager = mitk::RenderingManager::GetInstance(); const auto currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); if (referenceGeometry->IsValidTimePoint(currentTimePoint)) { imageTimeStep = referenceGeometry->TimePointToTimeStep(currentTimePoint); } auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow()); renderingManager->InitializeView(baseRenderer->GetRenderWindow(), referenceGeometry, false); // reset position and time step this->GetSliceNavigationController()->SelectSliceByPoint(currentPosition); this->SetCrosshairPosition(currentPosition); renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); }