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 <MitkCoreExports.h>
 #include <itkObjectFactory.h>
 
 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 <MitkCoreExports.h>
 #pragma GCC visibility push(default)
 #include <itkEventObject.h>
 #pragma GCC visibility pop
 #include "mitkDataStorage.h"
 #include "mitkRestorePlanePositionOperation.h"
 #include <itkCommand.h>
 #include <sstream>
-// DEPRECATED
-#include <mitkTimeSlicedGeometry.h>
 
 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<const Self *>(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<const Self *>(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<const Self *>(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 <typename T>
     void ConnectGeometrySendEvent(T *receiver)
     {
       typedef typename itk::ReceptorMemberCommand<T>::Pointer ReceptorMemberCommandPointer;
       ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand<T>::New();
       eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometry);
       unsigned long tag = AddObserver(GeometrySendEvent(nullptr, 0), eventReceptorCommand);
       m_ReceiverToObserverTagsMap[static_cast<void *>(receiver)].push_back(tag);
     }
 
     template <typename T>
     void ConnectGeometryUpdateEvent(T *receiver)
     {
       typedef typename itk::ReceptorMemberCommand<T>::Pointer ReceptorMemberCommandPointer;
       ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand<T>::New();
       eventReceptorCommand->SetCallbackFunction(receiver, &T::UpdateGeometry);
       unsigned long tag = AddObserver(GeometryUpdateEvent(nullptr, 0), eventReceptorCommand);
       m_ReceiverToObserverTagsMap[static_cast<void *>(receiver)].push_back(tag);
     }
 
     template <typename T>
     void ConnectGeometrySliceEvent(T *receiver, bool connectSendEvent = true)
     {
       typedef typename itk::ReceptorMemberCommand<T>::Pointer ReceptorMemberCommandPointer;
       ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand<T>::New();
       eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometrySlice);
       unsigned long tag = AddObserver(GeometrySliceEvent(nullptr, 0), eventReceptorCommand);
       m_ReceiverToObserverTagsMap[static_cast<void *>(receiver)].push_back(tag);
       if (connectSendEvent)
         ConnectGeometrySendEvent(receiver);
     }
 
     template <typename T>
     void ConnectGeometryTimeEvent(T *receiver, bool connectSendEvent = true)
     {
       typedef typename itk::ReceptorMemberCommand<T>::Pointer ReceptorMemberCommandPointer;
       ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand<T>::New();
       eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometryTime);
       unsigned long tag = AddObserver(GeometryTimeEvent(nullptr, 0), eventReceptorCommand);
       m_ReceiverToObserverTagsMap[static_cast<void *>(receiver)].push_back(tag);
       if (connectSendEvent)
         ConnectGeometrySendEvent(receiver);
     }
 
     template <typename T>
     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 <typename T>
     void Disconnect(T *receiver)
     {
       auto i = m_ReceiverToObserverTagsMap.find(static_cast<void *>(receiver));
       if (i == m_ReceiverToObserverTagsMap.end())
         return;
       const std::list<unsigned long> &tags = i->second;
       for (auto tagIter = tags.begin(); tagIter != tags.end(); ++tagIter)
       {
         RemoveObserver(*tagIter);
       }
       m_ReceiverToObserverTagsMap.erase(i);
     }
 
     Message1<mitk::Point3D> 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<void *, std::list<unsigned long>> 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 <itkCommand.h>
 
 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<SliceNavigationController> 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<SlicedGeometry3D *>(
               m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer());
 
             if (slicedWorldGeometry.IsNotNull())
             {
               break;
             }
           }
           else
           {
             const auto *worldSlicedGeometry =
               dynamic_cast<const SlicedGeometry3D *>(currentGeometry.GetPointer());
 
           if ( worldSlicedGeometry != nullptr )
             {
               slicedWorldGeometry = static_cast<SlicedGeometry3D *>(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<const mitk::ProportionalTimeGeometry*>( 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<const SliceNavigationController::GeometrySliceEvent *>(&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<SlicedGeometry3D *>(
       m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer());
 
     if (slicedWorldGeometry)
     {
       int bestSlice = -1;
       double bestDistance = itk::NumericTraits<double>::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<const mitk::SlicedGeometry3D *>(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<const mitk::SlicedGeometry3D *>(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<PointOperation *>(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 <mitkGeometry3D.h>
 #include <mitkPlaneGeometry.h>
 #include <mitkSlicedGeometry3D.h>
 #include <mitkSliceNavigationController.h>
+#include <mitkArbitraryTimeGeometry.h>
 
 #include <mitkTestFixture.h>
 #include <mitkTestingMacros.h>
 
 // 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)