diff --git a/Modules/Core/include/mitkBaseController.h b/Modules/Core/include/mitkBaseController.h index 5c823d4847..f928e6ab75 100644 --- a/Modules/Core/include/mitkBaseController.h +++ b/Modules/Core/include/mitkBaseController.h @@ -1,78 +1,80 @@ /*============================================================================ 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 BASECONTROLLER_H_HEADER_INCLUDED_C1E745A3 #define BASECONTROLLER_H_HEADER_INCLUDED_C1E745A3 #include "mitkEventStateMachine.h" #include "mitkOperationActor.h" #include "mitkStepper.h" #include #include namespace mitk { class BaseRenderer; //##Documentation //## @brief Baseclass for renderer slice-/camera-control //## //## Tells the render (subclass of BaseRenderer) which slice (subclass //## SliceNavigationController) or from which direction (subclass //## CameraController) it has to render. Contains two Stepper for stepping //## through the slices or through different camera views (e.g., for the //## creation of a movie around the data), respectively, and through time, if //## there is 3D+t data. //## @note not yet implemented //## @ingroup NavigationControl class MITKCORE_EXPORT BaseController : public mitk::OperationActor, public itk::Object { public: /** Standard class typedefs. */ mitkClassMacroItkParent(BaseController, mitk::OperationActor); itkFactorylessNewMacro(Self); /** Method for creation through ::New */ // mitkNewMacro(Self); //##Documentation //## @brief Get the Stepper through the slices mitk::Stepper *GetSlice(); + const mitk::Stepper* GetSlice() const; //##Documentation //## @brief Get the Stepper through the time mitk::Stepper *GetTime(); + const mitk::Stepper* GetTime() const; protected: /** * @brief Default Constructor **/ BaseController(); /** * @brief Default Destructor **/ ~BaseController() override; void ExecuteOperation(Operation *) override; //## @brief Stepper through the time Stepper::Pointer m_Time; //## @brief Stepper through the slices Stepper::Pointer m_Slice; unsigned long m_LastUpdateTime; }; } // namespace mitk #endif /* BASECONTROLLER_H_HEADER_INCLUDED_C1E745A3 */ diff --git a/Modules/Core/include/mitkSliceNavigationController.h b/Modules/Core/include/mitkSliceNavigationController.h index 093f43d205..ad0555773e 100644 --- a/Modules/Core/include/mitkSliceNavigationController.h +++ b/Modules/Core/include/mitkSliceNavigationController.h @@ -1,506 +1,476 @@ /*============================================================================ 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 SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F #define SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F #include "mitkBaseController.h" #include "mitkMessage.h" #include "mitkRenderingManager.h" #include "mitkTimeGeometry.h" #include #pragma GCC visibility push(default) #include #pragma GCC visibility pop #include "mitkDataStorage.h" #include "mitkRestorePlanePositionOperation.h" #include #include -// DEPRECATED -#include namespace mitk { -#define mitkTimeSlicedGeometryEventMacro(classname, super) \ - class MITKCORE_EXPORT DEPRECATED(classname) : public super \ - { \ - public: \ - typedef classname Self; \ - typedef super Superclass; \ - classname(TimeGeometry *aTimeGeometry, unsigned int aPos) : Superclass(aTimeGeometry, aPos) {} \ - virtual ~classname() {} \ - virtual const char *GetEventName() const { return #classname; } \ - virtual bool CheckEvent(const ::itk::EventObject *e) const { return dynamic_cast(e); } \ - virtual ::itk::EventObject *MakeObject() const { return new Self(GetTimeGeometry(), GetPos()); } \ - private: \ - void operator=(const Self &); \ - } #define mitkTimeGeometryEventMacro(classname, super) \ class MITKCORE_EXPORT classname : public super \ { \ public: \ typedef classname Self; \ typedef super Superclass; \ classname(TimeGeometry *aTimeGeometry, unsigned int aPos) : Superclass(aTimeGeometry, aPos) {} \ virtual ~classname() {} \ virtual const char *GetEventName() const { return #classname; } \ virtual bool CheckEvent(const ::itk::EventObject *e) const { return dynamic_cast(e); } \ virtual ::itk::EventObject *MakeObject() const { return new Self(GetTimeGeometry(), GetPos()); } \ private: \ void operator=(const Self &); \ } class PlaneGeometry; class BaseGeometry; class BaseRenderer; /** * \brief Controls the selection of the slice the associated BaseRenderer * will display * * A SliceNavigationController takes a BaseGeometry or a TimeGeometry as input world geometry * (TODO what are the exact requirements?) and generates a TimeGeometry * as output. The TimeGeometry holds a number of SlicedGeometry3Ds and * these in turn hold a series of PlaneGeometries. One of these PlaneGeometries is * selected as world geometry for the BaseRenderers associated to 2D views. * * The SliceNavigationController holds has Steppers (one for the slice, a * second for the time step), which control the selection of a single * PlaneGeometry from the TimeGeometry. SliceNavigationController generates * ITK events to tell observers, like a BaseRenderer, when the selected slice * or timestep changes. * * Example: * \code * // Initialization * sliceCtrl = mitk::SliceNavigationController::New(); * * // Tell the navigator the geometry to be sliced (with geometry a * // BaseGeometry::ConstPointer) - * sliceCtrl->SetInputWorldGeometry(geometry.GetPointer()); + * sliceCtrl->SetInputWorldGeometry3D(geometry.GetPointer()); * * // Tell the navigator in which direction it shall slice the data * sliceCtrl->SetViewDirection(mitk::SliceNavigationController::Axial); * * // Connect one or more BaseRenderer to this navigator, i.e.: events sent * // by the navigator when stepping through the slices (e.g. by * // sliceCtrl->GetSlice()->Next()) will be received by the BaseRenderer * // (in this example only slice-changes, see also ConnectGeometryTimeEvent * // and ConnectGeometryEvents.) * sliceCtrl->ConnectGeometrySliceEvent(renderer.GetPointer()); * * //create a world geometry and send the information to the connected renderer(s) * sliceCtrl->Update(); * \endcode * * * You can connect visible navigators to a SliceNavigationController, e.g., a * QmitkSliderNavigator (for Qt): * * \code * // Create the visible navigator (a slider with a spin-box) * QmitkSliderNavigator* navigator = * new QmitkSliderNavigator(parent, "slidernavigator"); * * // Connect the navigator to the slice-stepper of the * // SliceNavigationController. For initialization (position, mininal and * // maximal values) the values of the SliceNavigationController are used. * // Thus, accessing methods of a navigator is normally not necessary, since * // everything can be set via the (Qt-independent) SliceNavigationController. * // The QmitkStepperAdapter converts the Qt-signals to Qt-independent * // itk-events. * new QmitkStepperAdapter(navigator, sliceCtrl->GetSlice(), "navigatoradaptor"); * \endcode * * If you do not want that all renderwindows are updated when a new slice is * selected, you can use a specific RenderingManager, which updates only those * renderwindows that should be updated. This is sometimes useful when a 3D view * does not need to be updated when the slices in some 2D views are changed. * QmitkSliderNavigator (for Qt): * * \code * // create a specific RenderingManager * mitk::RenderingManager::Pointer myManager = mitk::RenderingManager::New(); * * // tell the RenderingManager to update only renderwindow1 and renderwindow2 * myManager->AddRenderWindow(renderwindow1); * myManager->AddRenderWindow(renderwindow2); * * // tell the SliceNavigationController of renderwindow1 and renderwindow2 * // to use the specific RenderingManager instead of the global one * renderwindow1->GetSliceNavigationController()->SetRenderingManager(myManager); * renderwindow2->GetSliceNavigationController()->SetRenderingManager(myManager); * \endcode * * \todo implement for non-evenly-timed geometry! * \ingroup NavigationControl */ class MITKCORE_EXPORT SliceNavigationController : public BaseController { public: mitkClassMacro(SliceNavigationController, BaseController); // itkFactorylessNewMacro(Self) // mitkNewMacro1Param(Self, const char *); itkNewMacro(Self); // itkCloneMacro(Self) /** * \brief Possible view directions, \a Original will uses * the PlaneGeometry instances in a SlicedGeometry3D provided - * as input world geometry (by SetInputWorldGeometry). + * as input world geometry (by SetInputWorldGeometry3D). */ enum ViewDirection { Axial, Sagittal, Frontal, Original }; /** * \brief Set the input world geometry3D out of which the * geometries for slicing will be created. * * Any previous previous set input geometry (3D or Time) will * be ignored in future. */ void SetInputWorldGeometry3D(const mitk::BaseGeometry *geometry); itkGetConstObjectMacro(InputWorldGeometry3D, mitk::BaseGeometry); - /** - * \brief Set the input world geometry3D out of which the - * geometries for slicing will be created. - * - * Any previous previous set input geometry (3D or Time) will - * be ignored in future. - * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see - * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 - */ - DEPRECATED(void SetInputWorldGeometry(const mitk::TimeSlicedGeometry *geometry)); - /** - * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see - * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 - */ - DEPRECATED(TimeSlicedGeometry *GetInputWorldGeometry()); - void SetInputWorldTimeGeometry(const mitk::TimeGeometry *geometry); itkGetConstObjectMacro(InputWorldTimeGeometry, mitk::TimeGeometry); /** * \brief Access the created geometry */ itkGetConstObjectMacro(CreatedWorldGeometry, mitk::TimeGeometry); /** * \brief Set the desired view directions * * \sa ViewDirection * \sa Update(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(ViewDirection, ViewDirection); itkGetEnumMacro(ViewDirection, ViewDirection); /** * \brief Set the default view direction * * This is used to re-initialize the view direction of the SNC to the * default value with SetViewDirectionToDefault() * * \sa ViewDirection * \sa Update(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(DefaultViewDirection, ViewDirection); itkGetEnumMacro(DefaultViewDirection, ViewDirection); const char *GetViewDirectionAsString() const; virtual void SetViewDirectionToDefault(); /** * \brief Do the actual creation and send it to the connected * observers (renderers) * */ virtual void Update(); /** * \brief Extended version of Update, additionally allowing to * specify the direction/orientation of the created geometry. * */ virtual void Update(ViewDirection viewDirection, bool top = true, bool frontside = true, bool rotated = false); /** * \brief Send the created geometry to the connected * observers (renderers) * * Called by Update(). */ virtual void SendCreatedWorldGeometry(); /** * \brief Tell observers to re-read the currently selected 2D geometry * */ virtual void SendCreatedWorldGeometryUpdate(); /** * \brief Send the currently selected slice to the connected * observers (renderers) * * Called by Update(). */ virtual void SendSlice(); /** * \brief Send the currently selected time to the connected * observers (renderers) * * Called by Update(). */ virtual void SendTime(); #pragma GCC visibility push(default) itkEventMacro(UpdateEvent, itk::AnyEvent); #pragma GCC visibility pop class MITKCORE_EXPORT TimeGeometryEvent : public itk::AnyEvent { public: typedef TimeGeometryEvent Self; typedef itk::AnyEvent Superclass; TimeGeometryEvent(TimeGeometry *aTimeGeometry, unsigned int aPos) : m_TimeGeometry(aTimeGeometry), m_Pos(aPos) {} ~TimeGeometryEvent() override {} const char *GetEventName() const override { return "TimeGeometryEvent"; } bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast(e); } ::itk::EventObject *MakeObject() const override { return new Self(m_TimeGeometry, m_Pos); } TimeGeometry *GetTimeGeometry() const { return m_TimeGeometry; } unsigned int GetPos() const { return m_Pos; } private: TimeGeometry::Pointer m_TimeGeometry; unsigned int m_Pos; // TimeGeometryEvent(const Self&); void operator=(const Self &); // just hide }; - /** - * \deprecatedSince{2013_09} Please use TimeGeometryEvent instead: For additional information see - * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 - */ - DEPRECATED(typedef TimeGeometryEvent TimeSlicedGeometryEvent); mitkTimeGeometryEventMacro(GeometrySendEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometryUpdateEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometryTimeEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometrySliceEvent, TimeGeometryEvent); template void ConnectGeometrySendEvent(T *receiver) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometry); unsigned long tag = AddObserver(GeometrySendEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometryUpdateEvent(T *receiver) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::UpdateGeometry); unsigned long tag = AddObserver(GeometryUpdateEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometrySliceEvent(T *receiver, bool connectSendEvent = true) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometrySlice); unsigned long tag = AddObserver(GeometrySliceEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); if (connectSendEvent) ConnectGeometrySendEvent(receiver); } template void ConnectGeometryTimeEvent(T *receiver, bool connectSendEvent = true) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometryTime); unsigned long tag = AddObserver(GeometryTimeEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); if (connectSendEvent) ConnectGeometrySendEvent(receiver); } template void ConnectGeometryEvents(T *receiver) { // connect sendEvent only once ConnectGeometrySliceEvent(receiver, false); ConnectGeometryTimeEvent(receiver); } // use a templated method to get the right offset when casting to void* template void Disconnect(T *receiver) { auto i = m_ReceiverToObserverTagsMap.find(static_cast(receiver)); if (i == m_ReceiverToObserverTagsMap.end()) return; const std::list &tags = i->second; for (auto tagIter = tags.begin(); tagIter != tags.end(); ++tagIter) { RemoveObserver(*tagIter); } m_ReceiverToObserverTagsMap.erase(i); } Message1 SetCrosshairEvent; /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface * \warning not implemented */ virtual void SetGeometry(const itk::EventObject &geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ virtual void SetGeometrySlice(const itk::EventObject &geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ virtual void SetGeometryTime(const itk::EventObject &geometryTimeEvent); /** \brief Positions the SNC according to the specified point */ void SelectSliceByPoint(const mitk::Point3D &point); /** \brief Returns the TimeGeometry created by the SNC. */ mitk::TimeGeometry *GetCreatedWorldGeometry(); /** \brief Returns the BaseGeometry of the currently selected time step. */ const mitk::BaseGeometry *GetCurrentGeometry3D(); /** \brief Returns the currently selected Plane in the current * BaseGeometry (if existent). */ const mitk::PlaneGeometry *GetCurrentPlaneGeometry(); /** \brief Sets the BaseRenderer associated with this SNC (if any). While * the BaseRenderer is not directly used by SNC, this is a convenience * method to enable BaseRenderer access via the SNC. */ void SetRenderer(BaseRenderer *renderer); /** \brief Gets the BaseRenderer associated with this SNC (if any). While * the BaseRenderer is not directly used by SNC, this is a convenience * method to enable BaseRenderer access via the SNC. Returns nullptr if no * BaseRenderer has been specified*/ BaseRenderer *GetRenderer() const; /** \brief Re-orients the slice stack. All slices will be oriented to the given normal vector. The given point (world coordinates) defines the selected slice. Careful: The resulting axis vectors are not clearly defined this way. If you want to define them clearly, use ReorientSlices (const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1). */ void ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &normal); /** \brief Re-orients the slice stack so that all planes are oriented according to the * given axis vectors. The given Point eventually defines selected slice. */ void ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1); void ExecuteOperation(Operation *operation) override; /** * \brief Feature option to lock planes during mouse interaction. * This option flag disables the mouse event which causes the center * cross to move near by. */ itkSetMacro(SliceLocked, bool); itkGetMacro(SliceLocked, bool); itkBooleanMacro(SliceLocked); /** * \brief Feature option to lock slice rotation. * * This option flag disables separately the rotation of a slice which is * implemented in mitkSliceRotator. */ itkSetMacro(SliceRotationLocked, bool); itkGetMacro(SliceRotationLocked, bool); itkBooleanMacro(SliceRotationLocked); /** * \brief Adjusts the numerical range of the slice stepper according to * the current geometry orientation of this SNC's SlicedGeometry. */ void AdjustSliceStepperRange(); + /** \brief Convinience method that returns the time step currently selected by the controller.*/ + TimeStepType GetSelectedTimeStep() const; + + /** \brief Convinience method that returns the time point that corresponds to the selected + *time step. The conversion is done using the time geometry of the SliceNavigationControler.*/ + TimePointType GetSelectedTimePoint() const; + protected: SliceNavigationController(); ~SliceNavigationController() override; mitk::BaseGeometry::ConstPointer m_InputWorldGeometry3D; mitk::TimeGeometry::ConstPointer m_InputWorldTimeGeometry; mitk::TimeGeometry::Pointer m_CreatedWorldGeometry; ViewDirection m_ViewDirection; ViewDirection m_DefaultViewDirection; mitk::RenderingManager::Pointer m_RenderingManager; mitk::BaseRenderer *m_Renderer; itkSetMacro(Top, bool); itkGetMacro(Top, bool); itkBooleanMacro(Top); itkSetMacro(FrontSide, bool); itkGetMacro(FrontSide, bool); itkBooleanMacro(FrontSide); itkSetMacro(Rotated, bool); itkGetMacro(Rotated, bool); itkBooleanMacro(Rotated); bool m_Top; bool m_FrontSide; bool m_Rotated; bool m_BlockUpdate; bool m_SliceLocked; bool m_SliceRotationLocked; unsigned int m_OldPos; typedef std::map> ObserverTagsMapType; ObserverTagsMapType m_ReceiverToObserverTagsMap; }; } // namespace mitk #endif /* SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F */ diff --git a/Modules/Core/src/Controllers/mitkBaseController.cpp b/Modules/Core/src/Controllers/mitkBaseController.cpp index 738ba09be8..135ac0763f 100644 --- a/Modules/Core/src/Controllers/mitkBaseController.cpp +++ b/Modules/Core/src/Controllers/mitkBaseController.cpp @@ -1,38 +1,48 @@ /*============================================================================ 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 "mitkBaseController.h" #include "mitkBaseRenderer.h" mitk::BaseController::BaseController() : m_LastUpdateTime(0) { m_Slice = Stepper::New(); m_Time = Stepper::New(); } mitk::BaseController::~BaseController() { } void mitk::BaseController::ExecuteOperation(mitk::Operation * /* *operation */) { } mitk::Stepper *mitk::BaseController::GetSlice() { return m_Slice.GetPointer(); } +const mitk::Stepper* mitk::BaseController::GetSlice() const +{ + return m_Slice.GetPointer(); +} + mitk::Stepper *mitk::BaseController::GetTime() { return m_Time.GetPointer(); } + +const mitk::Stepper* mitk::BaseController::GetTime() const +{ + return m_Time.GetPointer(); +} diff --git a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp index d5ca0d7991..be9f3aefba 100644 --- a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp +++ b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp @@ -1,632 +1,658 @@ /*============================================================================ 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 "mitkSliceNavigationController.h" #include "mitkAction.h" #include "mitkBaseRenderer.h" #include "mitkCrosshairPositionEvent.h" #include "mitkInteractionConst.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkPlaneGeometry.h" #include "mitkProportionalTimeGeometry.h" #include "mitkArbitraryTimeGeometry.h" #include "mitkRenderingManager.h" #include "mitkSlicedGeometry3D.h" #include "mitkVtkPropRenderer.h" #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkInteractionConst.h" #include "mitkNodePredicateDataType.h" #include "mitkOperationEvent.h" #include "mitkPixelTypeMultiplex.h" #include "mitkPlaneOperation.h" #include "mitkPointOperation.h" #include "mitkStatusBar.h" #include "mitkUndoController.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkMemoryUtilities.h" #include namespace mitk { SliceNavigationController::SliceNavigationController() : BaseController(), m_InputWorldGeometry3D( mitk::BaseGeometry::ConstPointer() ), m_InputWorldTimeGeometry( mitk::TimeGeometry::ConstPointer() ), m_CreatedWorldGeometry( mitk::TimeGeometry::Pointer() ), m_ViewDirection(Axial), m_DefaultViewDirection(Axial), m_RenderingManager( mitk::RenderingManager::Pointer() ), m_Renderer( nullptr ), m_Top(false), m_FrontSide(false), m_Rotated(false), m_BlockUpdate(false), m_SliceLocked(false), m_SliceRotationLocked(false), m_OldPos(0) { typedef itk::SimpleMemberCommand SNCCommandType; SNCCommandType::Pointer sliceStepperChangedCommand, timeStepperChangedCommand; sliceStepperChangedCommand = SNCCommandType::New(); timeStepperChangedCommand = SNCCommandType::New(); sliceStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendSlice); timeStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendTime); m_Slice->AddObserver(itk::ModifiedEvent(), sliceStepperChangedCommand); m_Time->AddObserver(itk::ModifiedEvent(), timeStepperChangedCommand); m_Slice->SetUnitName("mm"); m_Time->SetUnitName("ms"); m_Top = false; m_FrontSide = false; m_Rotated = false; } SliceNavigationController::~SliceNavigationController() {} void SliceNavigationController::SetInputWorldGeometry3D(const BaseGeometry *geometry) { if ( geometry != nullptr ) { if (geometry->GetBoundingBox()->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } if (m_InputWorldGeometry3D != geometry) { m_InputWorldGeometry3D = geometry; m_InputWorldTimeGeometry = mitk::TimeGeometry::ConstPointer(); this->Modified(); } } void SliceNavigationController::SetInputWorldTimeGeometry(const TimeGeometry *geometry) { if ( geometry != nullptr ) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } if (m_InputWorldTimeGeometry != geometry) { m_InputWorldTimeGeometry = geometry; m_InputWorldGeometry3D = mitk::BaseGeometry::ConstPointer(); this->Modified(); } } void SliceNavigationController::SetViewDirectionToDefault() { m_ViewDirection = m_DefaultViewDirection; } const char *SliceNavigationController::GetViewDirectionAsString() const { const char *viewDirectionString; switch (m_ViewDirection) { case SliceNavigationController::Axial: viewDirectionString = "Axial"; break; case SliceNavigationController::Sagittal: viewDirectionString = "Sagittal"; break; case SliceNavigationController::Frontal: viewDirectionString = "Coronal"; break; case SliceNavigationController::Original: viewDirectionString = "Original"; break; default: viewDirectionString = "No View Direction Available"; break; } return viewDirectionString; } void SliceNavigationController::Update() { if (!m_BlockUpdate) { if (m_ViewDirection == Sagittal) { this->Update(Sagittal, true, true, false); } else if (m_ViewDirection == Frontal) { this->Update(Frontal, false, true, false); } else if (m_ViewDirection == Axial) { this->Update(Axial, false, false, true); } else { this->Update(m_ViewDirection); } } } void SliceNavigationController::Update(SliceNavigationController::ViewDirection viewDirection, bool top, bool frontside, bool rotated) { TimeGeometry::ConstPointer worldTimeGeometry = m_InputWorldTimeGeometry; if (m_BlockUpdate || (m_InputWorldTimeGeometry.IsNull() && m_InputWorldGeometry3D.IsNull()) || ((worldTimeGeometry.IsNotNull()) && (worldTimeGeometry->CountTimeSteps() == 0))) { return; } m_BlockUpdate = true; if (m_InputWorldTimeGeometry.IsNotNull() && m_LastUpdateTime < m_InputWorldTimeGeometry->GetMTime()) { Modified(); } if (m_InputWorldGeometry3D.IsNotNull() && m_LastUpdateTime < m_InputWorldGeometry3D->GetMTime()) { Modified(); } this->SetViewDirection(viewDirection); this->SetTop(top); this->SetFrontSide(frontside); this->SetRotated(rotated); if (m_LastUpdateTime < GetMTime()) { m_LastUpdateTime = GetMTime(); // initialize the viewplane SlicedGeometry3D::Pointer slicedWorldGeometry = SlicedGeometry3D::Pointer(); BaseGeometry::ConstPointer currentGeometry = BaseGeometry::ConstPointer(); if (m_InputWorldTimeGeometry.IsNotNull()) if (m_InputWorldTimeGeometry->IsValidTimeStep(GetTime()->GetPos())) currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(GetTime()->GetPos()); else currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(0); else currentGeometry = m_InputWorldGeometry3D; m_CreatedWorldGeometry = mitk::TimeGeometry::Pointer(); switch (viewDirection) { case Original: if (worldTimeGeometry.IsNotNull()) { m_CreatedWorldGeometry = worldTimeGeometry->Clone(); worldTimeGeometry = m_CreatedWorldGeometry.GetPointer(); slicedWorldGeometry = dynamic_cast( m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer()); if (slicedWorldGeometry.IsNotNull()) { break; } } else { const auto *worldSlicedGeometry = dynamic_cast(currentGeometry.GetPointer()); if ( worldSlicedGeometry != nullptr ) { slicedWorldGeometry = static_cast(currentGeometry->Clone().GetPointer()); break; } } slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::None, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; case Axial: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Axial, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; case Frontal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Frontal, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; case Sagittal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Sagittal, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; default: itkExceptionMacro("unknown ViewDirection"); } m_Slice->SetPos(0); m_Slice->SetSteps((int)slicedWorldGeometry->GetSlices()); if ( worldTimeGeometry.IsNull() ) { auto createdTimeGeometry = ProportionalTimeGeometry::New(); createdTimeGeometry->Initialize( slicedWorldGeometry, 1 ); m_CreatedWorldGeometry = createdTimeGeometry; m_Time->SetSteps(0); m_Time->SetPos(0); m_Time->InvalidateRange(); } else { m_BlockUpdate = true; m_Time->SetSteps(worldTimeGeometry->CountTimeSteps()); m_Time->SetPos(0); const TimeBounds &timeBounds = worldTimeGeometry->GetTimeBounds(); m_Time->SetRange(timeBounds[0], timeBounds[1]); m_BlockUpdate = false; const auto currentTemporalPosition = this->GetTime()->GetPos(); assert( worldTimeGeometry->GetGeometryForTimeStep( currentTemporalPosition ).IsNotNull() ); if ( dynamic_cast( worldTimeGeometry.GetPointer() ) != nullptr ) { const TimePointType minimumTimePoint = worldTimeGeometry->TimeStepToTimePoint( currentTemporalPosition ); const TimePointType stepDuration = worldTimeGeometry->TimeStepToTimePoint( currentTemporalPosition + 1 ) - minimumTimePoint; auto createdTimeGeometry = ProportionalTimeGeometry::New(); createdTimeGeometry->Initialize( slicedWorldGeometry, worldTimeGeometry->CountTimeSteps() ); createdTimeGeometry->SetFirstTimePoint( minimumTimePoint ); createdTimeGeometry->SetStepDuration( stepDuration ); m_CreatedWorldGeometry = createdTimeGeometry; } else { auto createdTimeGeometry = mitk::ArbitraryTimeGeometry::New(); const TimeStepType numberOfTimeSteps = worldTimeGeometry->CountTimeSteps(); createdTimeGeometry->ReserveSpaceForGeometries( numberOfTimeSteps ); for ( TimeStepType i = 0; i < numberOfTimeSteps; ++i ) { const BaseGeometry::Pointer clonedGeometry = slicedWorldGeometry->Clone().GetPointer(); const auto bounds = worldTimeGeometry->GetTimeBounds( i ); createdTimeGeometry->AppendNewTimeStep( clonedGeometry, bounds[0], bounds[1]); } createdTimeGeometry->Update(); m_CreatedWorldGeometry = createdTimeGeometry; } } } // unblock update; we may do this now, because if m_BlockUpdate was already // true before this method was entered, then we will never come here. m_BlockUpdate = false; // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry and time/slice data. this->SendCreatedWorldGeometry(); this->SendSlice(); this->SendTime(); // Adjust the stepper range of slice stepper according to geometry this->AdjustSliceStepperRange(); } void SliceNavigationController::SendCreatedWorldGeometry() { // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry. if (!m_BlockUpdate) { this->InvokeEvent(GeometrySendEvent(m_CreatedWorldGeometry, 0)); } } void SliceNavigationController::SendCreatedWorldGeometryUpdate() { if (!m_BlockUpdate) { this->InvokeEvent(GeometryUpdateEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); } } void SliceNavigationController::SendSlice() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { this->InvokeEvent(GeometrySliceEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); RenderingManager::GetInstance()->RequestUpdateAll(); } } } void SliceNavigationController::SendTime() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { this->InvokeEvent(GeometryTimeEvent(m_CreatedWorldGeometry, m_Time->GetPos())); RenderingManager::GetInstance()->RequestUpdateAll(); } } } void SliceNavigationController::SetGeometry(const itk::EventObject &) {} void SliceNavigationController::SetGeometryTime(const itk::EventObject &geometryTimeEvent) { if (m_CreatedWorldGeometry.IsNull()) { return; } const auto *timeEvent = dynamic_cast< const SliceNavigationController::GeometryTimeEvent * >(&geometryTimeEvent); assert( timeEvent != nullptr ); TimeGeometry *timeGeometry = timeEvent->GetTimeGeometry(); assert( timeGeometry != nullptr ); auto timeStep = (int)timeEvent->GetPos(); ScalarType timeInMS; timeInMS = timeGeometry->TimeStepToTimePoint(timeStep); timeStep = m_CreatedWorldGeometry->TimePointToTimeStep(timeInMS); this->GetTime()->SetPos(timeStep); } void SliceNavigationController::SetGeometrySlice(const itk::EventObject &geometrySliceEvent) { const auto *sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent!=nullptr); this->GetSlice()->SetPos(sliceEvent->GetPos()); } void SliceNavigationController::SelectSliceByPoint(const Point3D &point) { if (m_CreatedWorldGeometry.IsNull()) { return; } //@todo add time to PositionEvent and use here!! SlicedGeometry3D *slicedWorldGeometry = dynamic_cast( m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer()); if (slicedWorldGeometry) { int bestSlice = -1; double bestDistance = itk::NumericTraits::max(); int s, slices; slices = slicedWorldGeometry->GetSlices(); if (slicedWorldGeometry->GetEvenlySpaced()) { mitk::PlaneGeometry *plane = slicedWorldGeometry->GetPlaneGeometry(0); const Vector3D &direction = slicedWorldGeometry->GetDirectionVector(); Point3D projectedPoint; plane->Project(point, projectedPoint); // Check whether the point is somewhere within the slice stack volume; // otherwise, the default slice (0) will be selected if (direction[0] * (point[0] - projectedPoint[0]) + direction[1] * (point[1] - projectedPoint[1]) + direction[2] * (point[2] - projectedPoint[2]) >= 0) { bestSlice = (int)(plane->Distance(point) / slicedWorldGeometry->GetSpacing()[2] + 0.5); } } else { Point3D projectedPoint; for (s = 0; s < slices; ++s) { slicedWorldGeometry->GetPlaneGeometry(s)->Project(point, projectedPoint); const Vector3D distance = projectedPoint - point; ScalarType currentDistance = distance.GetSquaredNorm(); if (currentDistance < bestDistance) { bestDistance = currentDistance; bestSlice = s; } } } if (bestSlice >= 0) { this->GetSlice()->SetPos(bestSlice); } else { this->GetSlice()->SetPos(0); } this->SendCreatedWorldGeometryUpdate(); // send crosshair event SetCrosshairEvent.Send(point); } } void SliceNavigationController::ReorientSlices(const Point3D &point, const Vector3D &normal) { if (m_CreatedWorldGeometry.IsNull()) { return; } PlaneOperation op(OpORIENT, point, normal); m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } void SliceNavigationController::ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1) { if (m_CreatedWorldGeometry) { PlaneOperation op(OpORIENT, point, axisVec0, axisVec1); m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } } mitk::TimeGeometry *SliceNavigationController::GetCreatedWorldGeometry() { return m_CreatedWorldGeometry; } const mitk::BaseGeometry *SliceNavigationController::GetCurrentGeometry3D() { if (m_CreatedWorldGeometry.IsNotNull()) { return m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()); } else { return nullptr; } } const mitk::PlaneGeometry *SliceNavigationController::GetCurrentPlaneGeometry() { const auto *slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); if (slicedGeometry) { const mitk::PlaneGeometry *planeGeometry = (slicedGeometry->GetPlaneGeometry(this->GetSlice()->GetPos())); return planeGeometry; } else { return nullptr; } } void SliceNavigationController::SetRenderer(BaseRenderer *renderer) { m_Renderer = renderer; } BaseRenderer *SliceNavigationController::GetRenderer() const { return m_Renderer; } void SliceNavigationController::AdjustSliceStepperRange() { const auto *slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); const Vector3D &direction = slicedGeometry->GetDirectionVector(); int c = 0; int i, k = 0; for (i = 0; i < 3; ++i) { if (fabs(direction[i]) < 0.000000001) { ++c; } else { k = i; } } if (c == 2) { ScalarType min = slicedGeometry->GetOrigin()[k]; ScalarType max = min + slicedGeometry->GetExtentInMM(k); m_Slice->SetRange(min, max); } else { m_Slice->InvalidateRange(); } } void SliceNavigationController::ExecuteOperation(Operation *operation) { // switch on type // - select best slice for a given point // - rotate created world geometry according to Operation->SomeInfo() if (!operation || m_CreatedWorldGeometry.IsNull()) { return; } switch (operation->GetOperationType()) { case OpMOVE: // should be a point operation { if (!m_SliceLocked) // do not move the cross position { // select a slice auto *po = dynamic_cast(operation); if (po && po->GetIndex() == -1) { this->SelectSliceByPoint(po->GetPoint()); } else if (po && po->GetIndex() != -1) // undo case because index != -1, index holds the old position of this slice { this->GetSlice()->SetPos(po->GetIndex()); } } break; } case OpRESTOREPLANEPOSITION: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } case OpAPPLYTRANSFORMMATRIX: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } default: { // do nothing break; } } } + /** \brief Convinience method that returns the time step currently selected by the controller.*/ + TimeStepType SliceNavigationController::GetSelectedTimeStep() const + { + return this->GetTime()->GetPos(); + } + + /** \brief Convinience method that returns the time point that corresponds to the selected + *time step. The conversion is done using the time geometry of the SliceNavigationControler.*/ + TimePointType SliceNavigationController::GetSelectedTimePoint() const + { + auto timeStep = this->GetSelectedTimeStep(); + if (m_CreatedWorldGeometry.IsNull()) + { + mitkThrow() << "SliceNavigationController is in an invalid state as m_CreatedWorldGeometry is invalid."; + } + + if (!m_CreatedWorldGeometry->IsValidTimeStep(timeStep)) + { + mitkThrow() << "SliceNavigationController is in an invalid state. It has a time step" + << "selected that is not covered by its time geometry. Selected time step: " + << timeStep << "; TimeGeometry steps count: " << m_CreatedWorldGeometry->CountTimeSteps(); + } + + return m_CreatedWorldGeometry->TimeStepToTimePoint(timeStep); + } + } // namespace diff --git a/Modules/Core/test/mitkSliceNavigationControllerTest.cpp b/Modules/Core/test/mitkSliceNavigationControllerTest.cpp index df1861e2bb..6e8165031c 100644 --- a/Modules/Core/test/mitkSliceNavigationControllerTest.cpp +++ b/Modules/Core/test/mitkSliceNavigationControllerTest.cpp @@ -1,167 +1,205 @@ /*============================================================================ 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 #include #include #include +#include #include #include // T22254 class mitkSliceNavigationControllerTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSliceNavigationControllerTestSuite); CPPUNIT_TEST(validateAxialViewDirection); CPPUNIT_TEST(validateCoronalViewDirection); CPPUNIT_TEST(validateSagittalViewDirection); + CPPUNIT_TEST(GetSelectedTimePoint); CPPUNIT_TEST_SUITE_END(); + mitk::Geometry3D::Pointer m_Geometry3D; + mitk::ArbitraryTimeGeometry::Pointer m_TimeGeometry; + public: void setUp() override { mitk::Point3D origin; mitk::FillVector3D(origin, 10.0, 20.0, 30.0); mitk::Vector3D firstAxisVector; mitk::FillVector3D(firstAxisVector, 100.0, 0.0, 0.0); mitk::Vector3D secondAxisVector; mitk::FillVector3D(secondAxisVector, 0.0, 50.0, 0.0); mitk::Vector3D spacing; mitk::FillVector3D(spacing, 1.0, 1.0, 2.0); auto planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(firstAxisVector, secondAxisVector, &spacing); planeGeometry->SetOrigin(origin); unsigned int numberOfSlices = 100U; auto slicedGeometry3D = mitk::SlicedGeometry3D::New(); slicedGeometry3D->InitializeEvenlySpaced(planeGeometry, numberOfSlices); m_Geometry3D = mitk::Geometry3D::New(); m_Geometry3D->SetBounds(slicedGeometry3D->GetBounds()); m_Geometry3D->SetIndexToWorldTransform(slicedGeometry3D->GetIndexToWorldTransform()); + + m_TimeGeometry = mitk::ArbitraryTimeGeometry::New(); + m_TimeGeometry->AppendNewTimeStepClone(m_Geometry3D, 0.5, 10.); + m_TimeGeometry->AppendNewTimeStepClone(m_Geometry3D, 10., 30.); + m_TimeGeometry->AppendNewTimeStepClone(m_Geometry3D, 30., 50.); + m_TimeGeometry->AppendNewTimeStepClone(m_Geometry3D, 50., 60.); + m_TimeGeometry->Update(); } void tearDown() override { } void validateAxialViewDirection() { auto sliceNavigationController = mitk::SliceNavigationController::New(); sliceNavigationController->SetInputWorldGeometry3D(m_Geometry3D); sliceNavigationController->SetViewDirection(mitk::SliceNavigationController::Axial); sliceNavigationController->Update(); mitk::Point3D origin; mitk::FillVector3D(origin, 10.0, 70.0, 229.0); mitk::Vector3D firstAxisVector; mitk::FillVector3D(firstAxisVector, 100.0, 0.0, 0.0); mitk::Vector3D secondAxisVector; mitk::FillVector3D(secondAxisVector, 0.0, -50.0, 0.0); mitk::Vector3D thirdAxisVector; mitk::FillVector3D(thirdAxisVector, 0.0, 0.0, -200.0); std::cout << "Axial view direction" << std::endl; CPPUNIT_ASSERT(this->validateGeometry(sliceNavigationController->GetCurrentGeometry3D(), origin, firstAxisVector, secondAxisVector, thirdAxisVector)); } void validateCoronalViewDirection() { auto sliceNavigationController = mitk::SliceNavigationController::New(); sliceNavigationController->SetInputWorldGeometry3D(m_Geometry3D); sliceNavigationController->SetViewDirection(mitk::SliceNavigationController::Frontal); sliceNavigationController->Update(); mitk::Point3D origin; mitk::FillVector3D(origin, 10.0, 69.5, 30.0); mitk::Vector3D firstAxisVector; mitk::FillVector3D(firstAxisVector, 100.0, 0.0, 0.0); mitk::Vector3D secondAxisVector; mitk::FillVector3D(secondAxisVector, 0.0, 0.0, 200.0); mitk::Vector3D thirdAxisVector; mitk::FillVector3D(thirdAxisVector, 0.0, -50.0, 0.0); std::cout << "Coronal view direction" << std::endl; CPPUNIT_ASSERT(this->validateGeometry(sliceNavigationController->GetCurrentGeometry3D(), origin, firstAxisVector, secondAxisVector, thirdAxisVector)); } void validateSagittalViewDirection() { auto sliceNavigationController = mitk::SliceNavigationController::New(); sliceNavigationController->SetInputWorldGeometry3D(m_Geometry3D); sliceNavigationController->SetViewDirection(mitk::SliceNavigationController::Sagittal); sliceNavigationController->Update(); mitk::Point3D origin; mitk::FillVector3D(origin, 10.5, 20.0, 30.0); mitk::Vector3D firstAxisVector; mitk::FillVector3D(firstAxisVector, 0.0, 50.0, 0.0); mitk::Vector3D secondAxisVector; mitk::FillVector3D(secondAxisVector, 0.0, 0.0, 200.0); mitk::Vector3D thirdAxisVector; mitk::FillVector3D(thirdAxisVector, 100.0, 0.0, 0.0); std::cout << "Sagittal view direction" << std::endl; CPPUNIT_ASSERT(this->validateGeometry(sliceNavigationController->GetCurrentGeometry3D(), origin, firstAxisVector, secondAxisVector, thirdAxisVector)); } + void GetSelectedTimePoint() + { + auto sliceNavigationController = mitk::SliceNavigationController::New(); + + CPPUNIT_ASSERT_THROW(sliceNavigationController->GetSelectedTimePoint(), mitk::Exception); + + sliceNavigationController->SetInputWorldTimeGeometry(m_TimeGeometry); + sliceNavigationController->SetViewDirection(mitk::SliceNavigationController::Sagittal); + sliceNavigationController->Update(); + + CPPUNIT_ASSERT(sliceNavigationController->GetSelectedTimeStep() == 0); + CPPUNIT_ASSERT(sliceNavigationController->GetSelectedTimePoint() == 0.5); + + sliceNavigationController->GetTime()->SetPos(2); + CPPUNIT_ASSERT(sliceNavigationController->GetSelectedTimeStep() == 2); + CPPUNIT_ASSERT(sliceNavigationController->GetSelectedTimePoint() == 30.0); + + auto sliceNavigationController2 = mitk::SliceNavigationController::New(); + + sliceNavigationController2->SetInputWorldGeometry3D(m_Geometry3D); + sliceNavigationController2->SetViewDirection(mitk::SliceNavigationController::Sagittal); + sliceNavigationController2->Update(); + + CPPUNIT_ASSERT(sliceNavigationController2->GetSelectedTimeStep() == 0); + CPPUNIT_ASSERT(sliceNavigationController2->GetSelectedTimePoint() == 0.0); + } + private: bool validateGeometry(mitk::BaseGeometry::ConstPointer geometry, const mitk::Point3D &origin, const mitk::Vector3D &firstAxisVector, const mitk::Vector3D &secondAxisVector, const mitk::Vector3D &thirdAxisVector) { bool result = true; std::cout << " Origin" << std::endl; if (!mitk::Equal(geometry->GetOrigin(), origin, mitk::eps, true)) result = false; std::cout << " First axis vector" << std::endl; if (!mitk::Equal(geometry->GetAxisVector(0), firstAxisVector, mitk::eps, true)) result = false; std::cout << " Second axis vector" << std::endl; if (!mitk::Equal(geometry->GetAxisVector(1), secondAxisVector, mitk::eps, true)) result = false; std::cout << " Third axis vector" << std::endl; if (!mitk::Equal(geometry->GetAxisVector(2), thirdAxisVector, mitk::eps, true)) result = false; return result; } - mitk::Geometry3D::Pointer m_Geometry3D; }; MITK_TEST_SUITE_REGISTRATION(mitkSliceNavigationController)