diff --git a/Modules/Core/include/mitkRenderingManager.h b/Modules/Core/include/mitkRenderingManager.h index 6518d232d8..b46fb50b2d 100644 --- a/Modules/Core/include/mitkRenderingManager.h +++ b/Modules/Core/include/mitkRenderingManager.h @@ -1,487 +1,503 @@ /*============================================================================ 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 MITKRENDERINGMANAGER_H #define MITKRENDERINGMANAGER_H #include #include #include #include #include #include #include #include class vtkRenderWindow; class vtkObject; namespace mitk { class RenderingManagerFactory; class BaseGeometry; class SliceNavigationController; class BaseRenderer; class DataStorage; /** * \brief Manager for coordinating the rendering process. * * RenderingManager is a central instance retrieving and executing * RenderWindow update requests. Its main purpose is to coordinate * distributed requests which cannot be aware of each other - lacking the * knowledge of whether they are really necessary or not. For example, two * objects might determine that a specific RenderWindow needs to be updated. * This would result in one unnecessary update, if both executed the update * on their own. * * The RenderingManager addresses this by letting each such object * request an update, and waiting for other objects to possibly * issue the same request. The actual update will then only be executed at a * well-defined point in the main event loop (this may be each time after * event processing is done). * * Convinience methods for updating all RenderWindows which have been * registered with the RenderingManager exist. If theses methods are not * used, it is not required to register (add) RenderWindows prior to using * the RenderingManager. * * The methods #ForceImmediateUpdate() and #ForceImmediateUpdateAll() can * be used to force the RenderWindow update execution without any delay, * bypassing the request functionality. * * The interface of RenderingManager is platform independent. Platform * specific subclasses have to be implemented, though, to supply an * appropriate event issueing for controlling the update execution process. * See method documentation for a description of how this can be done. * * \sa TestingRenderingManager An "empty" RenderingManager implementation which * can be used in tests etc. * */ class MITKCORE_EXPORT RenderingManager : public itk::Object { public: mitkClassMacroItkParent(RenderingManager, itk::Object); typedef std::vector RenderWindowVector; typedef std::vector FloatVector; typedef std::vector BoolVector; typedef itk::SmartPointer DataStoragePointer; enum RequestType { REQUEST_UPDATE_ALL = 0, REQUEST_UPDATE_2DWINDOWS, REQUEST_UPDATE_3DWINDOWS }; static Pointer New(); /** Set the object factory which produces the desired platform specific * RenderingManager singleton instance. */ static void SetFactory(RenderingManagerFactory *factory); /** Get the object factory which produces the platform specific * RenderingManager instances. */ static const RenderingManagerFactory *GetFactory(); /** Returns true if a factory has already been set. */ static bool HasFactory(); /** Get the RenderingManager singleton instance. */ static RenderingManager *GetInstance(); /** Returns true if the singleton instance does already exist. */ static bool IsInstantiated(); /** Adds a RenderWindow. This is required if the methods #RequestUpdateAll * or #ForceImmediateUpdate are to be used. */ void AddRenderWindow(vtkRenderWindow *renderWindow); /** Removes a RenderWindow. */ void RemoveRenderWindow(vtkRenderWindow *renderWindow); /** Get a list of all registered RenderWindows */ const RenderWindowVector &GetAllRegisteredRenderWindows(); /** Requests an update for the specified RenderWindow, to be executed as * soon as the main loop is ready for rendering. */ void RequestUpdate(vtkRenderWindow *renderWindow); /** Immediately executes an update of the specified RenderWindow. */ void ForceImmediateUpdate(vtkRenderWindow *renderWindow); /** Requests all currently registered RenderWindows to be updated. * If only 2D or 3D windows should be updated, this can be specified * via the parameter requestType. */ void RequestUpdateAll(RequestType type = REQUEST_UPDATE_ALL); /** Immediately executes an update of all registered RenderWindows. * If only 2D or 3D windows should be updated, this can be specified * via the parameter requestType. */ void ForceImmediateUpdateAll(RequestType type = REQUEST_UPDATE_ALL); /** * @brief Initialize the render windows by the aggregated geometry of all objects that are held in * the data storage. * * @param dataStorage The data storage from which the bounding object can be retrieved */ virtual void InitializeViewsByBoundingObjects(const DataStorage* dataStorage); + /** + * @brief Initialize the given render window by the aggregated geometry of all objects that are held in + * the data storage. + * + * @param renderWindow The specifid render window to update + * @param dataStorage The data storage from which the bounding object can be retrieved + * @param resetCamera If this parameter is set to true, the camera controller will be + * set / fit to the center of the rendered image. If set to false, only the + * the slice navigation controller is reset to the geometry without changing + * the camera view / position. + */ + + virtual void InitializeViewByBoundingObjects(vtkRenderWindow* renderWindow, + const DataStorage* dataStorage, + bool resetCamera = true); + /** * @brief Initialize the render windows specified by "type" to the given geometry. * * Throws an exception if bounding box has 0 extent due to exceeding double precision range. * * @param geometry The geometry to be used to initialize / update a * render window's time and slice navigation controller * @param type The type of update request: * - REQUEST_UPDATE_ALL will initialize / update the * time and slice navigation controller of all retrieved render windows * - REQUEST_UPDATE_2DWINDOWS will only initialize / update the * time and slice navigation controller of 2D render windows * - REQUEST_UPDATE_3DWINDOWS will only initialize / update the * time and slice navigation controller of 3D render windows * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ virtual bool InitializeViews(const BaseGeometry *geometry, RequestType type = REQUEST_UPDATE_ALL, bool resetCamera = true); /** * @brief Initialize the render windows specified by "type" to the given geometry. * * Throws an exception if bounding box has 0 extent due to exceeding double precision range. * * @param geometry The geometry to be used to initialize / update a * render window's time- and slice navigation controller * @param type The type of update request: * - REQUEST_UPDATE_ALL will initialize / update the * time- and slice navigation controller of all retrieved render windows * - REQUEST_UPDATE_2DWINDOWS will only initialize / update the * time- and slice navigation controller of 2D render windows * - REQUEST_UPDATE_3DWINDOWS will only initialize / update the * time- and slice navigation controller of 3D render windows * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ virtual bool InitializeViews(const TimeGeometry *geometry, RequestType type = REQUEST_UPDATE_ALL, bool resetCamera = true); /** * @brief Initialize the render windows specified by "type" to the default viewing direction * without updating the geometry information. * * @param type The type of update request: * - REQUEST_UPDATE_ALL will initialize the * slice navigation controller of all retrieved render windows * - REQUEST_UPDATE_2DWINDOWS will only initialize the * slice navigation controller of 2D render windows * - REQUEST_UPDATE_3DWINDOWS will only initialize the * slice navigation controller of 3D render windows */ virtual bool InitializeViews(RequestType type = REQUEST_UPDATE_ALL); /** * @brief Initialize the specified render window to the given geometry. * * Throws an exception if bounding box has 0 extent due to exceeding double precision range. * * @param renderWindow The specifid render window to update * @param geometry The geometry to be used to initialize / update the * render window's time- and slice navigation controller * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ virtual bool InitializeView(vtkRenderWindow *renderWindow, const BaseGeometry *geometry, bool resetCamera = true); /** * @brief Initialize the specified render window to the given geometry. * * Throws an exception if bounding box has 0 extent due to exceeding double precision range. * * @param renderWindow The specifid render window to update * @param geometry The geometry to be used to initialize / update the * render window's time- and slice navigation controller * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ virtual bool InitializeView(vtkRenderWindow *renderWindow, const TimeGeometry *geometry, bool resetCamera = true); /** * @brief Initialize the specified render window to the default viewing direction * without updating the geometry information. * * @param renderWindow The specifid render window to update */ virtual bool InitializeView(vtkRenderWindow *renderWindow); /** Gets the (global) SliceNavigationController responsible for * time-slicing. */ const SliceNavigationController *GetTimeNavigationController() const; /** Gets the (global) SliceNavigationController responsible for * time-slicing. */ SliceNavigationController *GetTimeNavigationController(); ~RenderingManager() override; /** Executes all pending requests. This method has to be called by the * system whenever a RenderingManager induced request event occurs in * the system pipeline (see concrete RenderingManager implementations). */ virtual void ExecutePendingRequests(); bool IsRendering() const; void AbortRendering(); /** En-/Disable LOD increase globally. */ itkSetMacro(LODIncreaseBlocked, bool); /** En-/Disable LOD increase globally. */ itkGetMacro(LODIncreaseBlocked, bool); /** En-/Disable LOD increase globally. */ itkBooleanMacro(LODIncreaseBlocked); /** En-/Disable LOD abort mechanism. */ itkSetMacro(LODAbortMechanismEnabled, bool); /** En-/Disable LOD abort mechanism. */ itkGetMacro(LODAbortMechanismEnabled, bool); /** En-/Disable LOD abort mechanism. */ itkBooleanMacro(LODAbortMechanismEnabled); /** Force a sub-class to start a timer for a pending hires-rendering request */ virtual void StartOrResetTimer(){}; /** To be called by a sub-class from a timer callback */ void ExecutePendingHighResRenderingRequest(); virtual void DoStartRendering(){}; virtual void DoMonitorRendering(){}; virtual void DoFinishAbortRendering(){}; int GetNextLOD(BaseRenderer *renderer); /** Set current LOD (nullptr means all renderers)*/ void SetMaximumLOD(unsigned int max); void SetShading(bool state, unsigned int lod); bool GetShading(unsigned int lod); void SetClippingPlaneStatus(bool status); bool GetClippingPlaneStatus(); void SetShadingValues(float ambient, float diffuse, float specular, float specpower); FloatVector &GetShadingValues(); /** Returns a property list */ PropertyList::Pointer GetPropertyList() const; /** Returns a property from m_PropertyList */ BaseProperty *GetProperty(const char *propertyKey) const; /** Sets or adds (if not present) a property in m_PropertyList */ void SetProperty(const char *propertyKey, BaseProperty *propertyValue); /** * \brief Setter for internal DataStorage * * Sets the DataStorage that is used internally. This instance holds all DataNodes that are * rendered by the registered BaseRenderers. * * If this DataStorage is changed at runtime by calling SetDataStorage(), * all currently registered BaseRenderers are automatically given the correct instance. * When a new BaseRenderer is added, it is automatically initialized with the currently active DataStorage. */ void SetDataStorage(DataStorage *storage); /** * \brief Getter for internal DataStorage * * Returns the DataStorage that is used internally. This instance holds all DataNodes that are * rendered by the registered BaseRenderers. */ itkGetMacro(DataStorage, DataStorage*); itkGetConstMacro(DataStorage, DataStorage*); /** * @brief Sets a flag to the given renderwindow to indicated that it has the focus e.g. has been clicked recently. * @param focusWindow */ void SetRenderWindowFocus(vtkRenderWindow *focusWindow); itkGetMacro(FocusedRenderWindow, vtkRenderWindow *); itkSetMacro(ConstrainedPanningZooming, bool); itkGetConstMacro(ConstrainedPanningZooming, bool); void SetAntiAliasing(AntiAliasing antiAliasing); itkGetConstMacro(AntiAliasing, AntiAliasing); protected: enum { RENDERING_INACTIVE = 0, RENDERING_REQUESTED, RENDERING_INPROGRESS }; RenderingManager(); /** Abstract method for generating a system specific event for rendering * request. This method is called whenever an update is requested */ virtual void GenerateRenderingRequestEvent() = 0; virtual void InitializePropertyList(); bool m_UpdatePending; typedef std::map RendererIntMap; typedef std::map RendererBoolMap; RendererBoolMap m_RenderingAbortedMap; RendererIntMap m_NextLODMap; unsigned int m_MaxLOD; bool m_LODIncreaseBlocked; bool m_LODAbortMechanismEnabled; BoolVector m_ShadingEnabled; bool m_ClippingPlaneEnabled; FloatVector m_ShadingValues; static void RenderingStartCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata); static void RenderingProgressCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata); static void RenderingEndCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata); typedef std::map RenderWindowList; RenderWindowList m_RenderWindowList; RenderWindowVector m_AllRenderWindows; struct RenderWindowCallbacks { vtkCallbackCommand *commands[3u]; }; typedef std::map RenderWindowCallbacksList; RenderWindowCallbacksList m_RenderWindowCallbacksList; itk::SmartPointer m_TimeNavigationController; static RenderingManager::Pointer s_Instance; static RenderingManagerFactory *s_RenderingManagerFactory; PropertyList::Pointer m_PropertyList; DataStoragePointer m_DataStorage; bool m_ConstrainedPanningZooming; private: /** * @brief Initialize the specified renderer to the given geometry. * * @param baseRenderer The specifid renderer to update * @param geometry The geometry to be used to initialize / update the * render window's slice navigation controller * @param boundingBoxInitialized If this parameter is set to true, the slice navigation controller will be * initialized / updated with the given geometry. If set to false, the geometry * of the slice navigation controller is not updated. * @param mapperID The mapper ID is used to define if the given renderer is a 2D or a 3D renderer. * In case of a 2D renderer and if "boundingBoxInitialized" is set to true (slice * navigation controller will be updated with a new geometry), the position of the * slice navigation controller is set to the center slice. * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ void InternalViewInitialization(BaseRenderer *baseRenderer, const TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID, bool resetCamera); /** * @brief Extend the bounding box of the given geometry to make sure the bounding box has an extent bigger than * zero in any direction. * * @param originalGeometry The original geometry to be extended * @param modifiedGeometry The modified geometry where the new bounds (extended bounding box) are used / set */ bool ExtendGeometryForBoundingBox(const TimeGeometry* originalGeometry, TimeGeometry::Pointer& modifiedGeometry); vtkRenderWindow *m_FocusedRenderWindow; AntiAliasing m_AntiAliasing; }; #pragma GCC visibility push(default) itkEventMacroDeclaration(RenderingManagerEvent, itk::AnyEvent); itkEventMacroDeclaration(RenderingManagerViewsInitializedEvent, RenderingManagerEvent); #pragma GCC visibility pop itkEventMacroDeclaration(FocusChangedEvent, itk::AnyEvent); /** * Generic RenderingManager implementation for "non-rendering-plattform", * e.g. for tests. Its factory (TestingRenderingManagerFactory) is * automatically on start-up and is used by default if not other * RenderingManagerFactory is instantiated explicitly thereafter. * (see mitkRenderingManager.cpp) */ class MITKCORE_EXPORT TestingRenderingManager : public RenderingManager { public: mitkClassMacro(TestingRenderingManager, RenderingManager); itkFactorylessNewMacro(Self); itkCloneMacro(Self); protected: void GenerateRenderingRequestEvent() override {}; }; } // namespace mitk #endif // MITKRENDERINGMANAGER_H diff --git a/Modules/Core/src/Controllers/mitkRenderingManager.cpp b/Modules/Core/src/Controllers/mitkRenderingManager.cpp index db64173826..b9a0cf2ce1 100644 --- a/Modules/Core/src/Controllers/mitkRenderingManager.cpp +++ b/Modules/Core/src/Controllers/mitkRenderingManager.cpp @@ -1,770 +1,793 @@ /*============================================================================ 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 #include #include #include #include #include #include namespace mitk { itkEventMacroDefinition(RenderingManagerEvent, itk::AnyEvent); itkEventMacroDefinition(RenderingManagerViewsInitializedEvent, RenderingManagerEvent); itkEventMacroDefinition(FocusChangedEvent, itk::AnyEvent); RenderingManager::Pointer RenderingManager::s_Instance = nullptr; RenderingManagerFactory *RenderingManager::s_RenderingManagerFactory = nullptr; RenderingManager::RenderingManager() : m_UpdatePending(false), m_MaxLOD(1), m_LODIncreaseBlocked(false), m_LODAbortMechanismEnabled(false), m_ClippingPlaneEnabled(false), m_TimeNavigationController(SliceNavigationController::New()), m_DataStorage(nullptr), m_ConstrainedPanningZooming(true), m_FocusedRenderWindow(nullptr), m_AntiAliasing(AntiAliasing::FastApproximate) { m_ShadingEnabled.assign(3, false); m_ShadingValues.assign(4, 0.0); InitializePropertyList(); } RenderingManager::~RenderingManager() { // Decrease reference counts of all registered vtkRenderWindows for // proper destruction RenderWindowVector::iterator it; for (it = m_AllRenderWindows.begin(); it != m_AllRenderWindows.end(); ++it) { (*it)->UnRegister(nullptr); auto callbacks_it = this->m_RenderWindowCallbacksList.find(*it); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { (*it)->RemoveObserver(callbacks_it->second.commands[0u]); (*it)->RemoveObserver(callbacks_it->second.commands[1u]); (*it)->RemoveObserver(callbacks_it->second.commands[2u]); } } } void RenderingManager::SetFactory(RenderingManagerFactory *factory) { s_RenderingManagerFactory = factory; } const RenderingManagerFactory *RenderingManager::GetFactory() { return s_RenderingManagerFactory; } bool RenderingManager::HasFactory() { if (RenderingManager::s_RenderingManagerFactory) { return true; } else { return false; } } RenderingManager::Pointer RenderingManager::New() { const RenderingManagerFactory *factory = GetFactory(); if (factory == nullptr) return nullptr; return factory->CreateRenderingManager(); } RenderingManager *RenderingManager::GetInstance() { if (!RenderingManager::s_Instance) { if (s_RenderingManagerFactory) { s_Instance = s_RenderingManagerFactory->CreateRenderingManager(); } } return s_Instance; } bool RenderingManager::IsInstantiated() { if (RenderingManager::s_Instance) return true; else return false; } void RenderingManager::AddRenderWindow(vtkRenderWindow *renderWindow) { if (renderWindow && (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.end())) { m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_AllRenderWindows.push_back(renderWindow); if (m_DataStorage.IsNotNull()) BaseRenderer::GetInstance(renderWindow)->SetDataStorage(m_DataStorage.GetPointer()); // Register vtkRenderWindow instance renderWindow->Register(nullptr); // Add callbacks for rendering abort mechanism // BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); vtkCallbackCommand *startCallbackCommand = vtkCallbackCommand::New(); startCallbackCommand->SetCallback(RenderingManager::RenderingStartCallback); renderWindow->AddObserver(vtkCommand::StartEvent, startCallbackCommand); vtkCallbackCommand *progressCallbackCommand = vtkCallbackCommand::New(); progressCallbackCommand->SetCallback(RenderingManager::RenderingProgressCallback); renderWindow->AddObserver(vtkCommand::AbortCheckEvent, progressCallbackCommand); vtkCallbackCommand *endCallbackCommand = vtkCallbackCommand::New(); endCallbackCommand->SetCallback(RenderingManager::RenderingEndCallback); renderWindow->AddObserver(vtkCommand::EndEvent, endCallbackCommand); RenderWindowCallbacks callbacks; callbacks.commands[0u] = startCallbackCommand; callbacks.commands[1u] = progressCallbackCommand; callbacks.commands[2u] = endCallbackCommand; this->m_RenderWindowCallbacksList[renderWindow] = callbacks; // Delete vtk variables correctly startCallbackCommand->Delete(); progressCallbackCommand->Delete(); endCallbackCommand->Delete(); } } void RenderingManager::RemoveRenderWindow(vtkRenderWindow *renderWindow) { if (m_RenderWindowList.erase(renderWindow)) { auto callbacks_it = this->m_RenderWindowCallbacksList.find(renderWindow); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { renderWindow->RemoveObserver(callbacks_it->second.commands[0u]); renderWindow->RemoveObserver(callbacks_it->second.commands[1u]); renderWindow->RemoveObserver(callbacks_it->second.commands[2u]); this->m_RenderWindowCallbacksList.erase(callbacks_it); } auto rw_it = std::find(m_AllRenderWindows.begin(), m_AllRenderWindows.end(), renderWindow); if (rw_it != m_AllRenderWindows.cend()) { // Decrease reference count for proper destruction (*rw_it)->UnRegister(nullptr); m_AllRenderWindows.erase(rw_it); } } } const RenderingManager::RenderWindowVector &RenderingManager::GetAllRegisteredRenderWindows() { return m_AllRenderWindows; } void RenderingManager::RequestUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } m_RenderWindowList[renderWindow] = RENDERING_REQUESTED; if (!m_UpdatePending) { m_UpdatePending = true; this->GenerateRenderingRequestEvent(); } } void RenderingManager::ForceImmediateUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } // Erase potentially pending requests for this window m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_UpdatePending = false; // Immediately repaint this window (implementation platform specific) // If the size is 0 it crashes int *size = renderWindow->GetSize(); if (0 != size[0] && 0 != size[1]) { // prepare the camera etc. before rendering // Note: this is a very important step which should be called before the VTK render! // If you modify the camera anywhere else or after the render call, the scene cannot be seen. auto *vPR = dynamic_cast(BaseRenderer::GetInstance(renderWindow)); if (vPR) vPR->PrepareRender(); // Execute rendering renderWindow->Render(); } } void RenderingManager::RequestUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->RequestUpdate(it->first); } } } void RenderingManager::ForceImmediateUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { // Immediately repaint this window (implementation platform specific) // If the size is 0, it crashes this->ForceImmediateUpdate(it->first); } } } void RenderingManager::InitializeViewsByBoundingObjects(const DataStorage* dataStorage) { if (nullptr == dataStorage) { return; } // get all nodes that have not set "includeInBoundingBox" to false auto pred = NodePredicateNot::New(NodePredicateProperty::New("includeInBoundingBox", BoolProperty::New(false))); DataStorage::SetOfObjects::ConstPointer filteredNodes = dataStorage->GetSubset(pred); TimeGeometry::ConstPointer boundingGeometry; if (!filteredNodes->empty()) { // calculate bounding geometry of these nodes boundingGeometry = dataStorage->ComputeBoundingGeometry3D(filteredNodes, "visible"); } // initialize the views to the bounding geometry this->InitializeViews(boundingGeometry); } + void RenderingManager::InitializeViewByBoundingObjects(vtkRenderWindow* renderWindow, const DataStorage* dataStorage, bool resetCamera) + { + if (nullptr == dataStorage) + { + return; + } + + // get all nodes that have not set "includeInBoundingBox" to false + auto pred = NodePredicateNot::New(NodePredicateProperty::New("includeInBoundingBox", BoolProperty::New(false))); + DataStorage::SetOfObjects::ConstPointer filteredNodes = dataStorage->GetSubset(pred); + + BaseRenderer* baseRenderer = BaseRenderer::GetInstance(renderWindow); + TimeGeometry::ConstPointer boundingGeometry; + if (!filteredNodes->empty()) + { + // calculate bounding geometry of these nodes + boundingGeometry = dataStorage->ComputeBoundingGeometry3D(filteredNodes, "visible", baseRenderer); + } + + // initialize the views to the bounding geometry + this->InitializeView(renderWindow, boundingGeometry, resetCamera); + } + bool RenderingManager::InitializeViews(const BaseGeometry* geometry, RequestType type, bool resetCamera) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); return this->InitializeViews(propTimeGeometry, type, resetCamera); } bool RenderingManager::InitializeViews(const TimeGeometry* geometry, RequestType type, bool resetCamera) { bool boundingBoxInitialized = false; TimeGeometry::Pointer modifiedGeometry = nullptr; try { boundingBoxInitialized = this->ExtendGeometryForBoundingBox(geometry, modifiedGeometry); } catch (Exception& exception) { mitkReThrow(exception); } RenderWindowVector allRenderWindows = this->GetAllRegisteredRenderWindows(); RenderWindowVector::const_iterator it; for (it = allRenderWindows.cbegin(); it != allRenderWindows.cend(); ++it) { BaseRenderer *baseRenderer = BaseRenderer::GetInstance(*it); baseRenderer->SetConstrainZoomingAndPanning(this->GetConstrainedPanningZooming()); int id = baseRenderer->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->InternalViewInitialization(baseRenderer, modifiedGeometry, boundingBoxInitialized, id, resetCamera); } } if (boundingBoxInitialized) { this->GetTimeNavigationController()->SetInputWorldTimeGeometry(modifiedGeometry); } this->GetTimeNavigationController()->Update(); this->RequestUpdateAll(type); // inform listeners that views have been initialized this->InvokeEvent(RenderingManagerViewsInitializedEvent()); return boundingBoxInitialized; } bool RenderingManager::InitializeViews(RequestType type) { const RenderWindowVector allRenderWindows = this->GetAllRegisteredRenderWindows(); RenderWindowVector::const_iterator it; for (it = allRenderWindows.cbegin(); it != allRenderWindows.cend(); ++it) { BaseRenderer *baseRenderer = BaseRenderer::GetInstance(*it); int id = baseRenderer->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->InternalViewInitialization(baseRenderer, nullptr, false, id, false); } } this->RequestUpdateAll(type); // inform listeners that views have been initialized this->InvokeEvent(RenderingManagerViewsInitializedEvent()); return true; } bool RenderingManager::InitializeView(vtkRenderWindow* renderWindow, const BaseGeometry* geometry, bool resetCamera) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); return this->InitializeView(renderWindow, propTimeGeometry, resetCamera); } bool RenderingManager::InitializeView(vtkRenderWindow* renderWindow, const TimeGeometry* geometry, bool resetCamera) { bool boundingBoxInitialized = false; TimeGeometry::Pointer modifiedGeometry = nullptr; try { boundingBoxInitialized = this->ExtendGeometryForBoundingBox(geometry, modifiedGeometry); } catch (Exception &exception) { mitkReThrow(exception); } BaseRenderer* baseRenderer = BaseRenderer::GetInstance(renderWindow); baseRenderer->SetConstrainZoomingAndPanning(this->GetConstrainedPanningZooming()); int id = baseRenderer->GetMapperID(); this->InternalViewInitialization(baseRenderer, modifiedGeometry, boundingBoxInitialized, id, resetCamera); if (boundingBoxInitialized) { this->GetTimeNavigationController()->SetInputWorldTimeGeometry(modifiedGeometry); } this->GetTimeNavigationController()->Update(); this->RequestUpdate(renderWindow); // inform listeners that views have been initialized this->InvokeEvent(RenderingManagerViewsInitializedEvent()); return boundingBoxInitialized; } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow) { BaseRenderer *baseRenderer = BaseRenderer::GetInstance(renderWindow); int id = baseRenderer->GetMapperID(); this->InternalViewInitialization(baseRenderer, nullptr, false, id, false); this->RequestUpdate(renderWindow); // inform listeners that views have been initialized this->InvokeEvent(RenderingManagerViewsInitializedEvent()); return true; } void RenderingManager::InternalViewInitialization(BaseRenderer *baseRenderer, const TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID, bool resetCamera) { SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); if (boundingBoxInitialized) { // Set geometry for NC nc->SetInputWorldTimeGeometry(geometry); nc->Update(); if (resetCamera) { if (mapperID == BaseRenderer::Standard2D) { // For 2D SNCs, steppers are set so that the cross is centered in the image nc->GetSlice()->SetPos(nc->GetSlice()->GetSteps() / 2); baseRenderer->GetCameraController()->Fit(); } else if (mapperID == BaseRenderer::Standard3D) { baseRenderer->GetCameraController()->SetViewToAnterior(); } } } else { nc->Update(); } } bool RenderingManager::ExtendGeometryForBoundingBox(const TimeGeometry *geometry, TimeGeometry::Pointer& modifiedGeometry) { bool boundingBoxInitialized = false; if (nullptr == geometry) { return boundingBoxInitialized; } modifiedGeometry = geometry->Clone(); if (modifiedGeometry.IsNull()) { return boundingBoxInitialized; } if (modifiedGeometry->GetBoundingBoxInWorld()->GetDiagonalLength2() > eps) { boundingBoxInitialized = true; } // make sure bounding box has an extent bigger than zero in any direction for (TimeStepType step = 0; step < modifiedGeometry->CountTimeSteps(); ++step) { BaseGeometry::BoundsArrayType newBounds = modifiedGeometry->GetGeometryForTimeStep(step)->GetBounds(); for (unsigned int dimension = 0; (2 * dimension) < newBounds.Size(); dimension++) { // check for equality but for an epsilon if (Equal(newBounds[2 * dimension], newBounds[2 * dimension + 1])) { newBounds[2 * dimension + 1] += 1; if (Equal( newBounds[2 * dimension], newBounds[2 * dimension + 1])) // newBounds will still be equal if values are beyond double precision { mitkThrow() << "One dimension of object data has zero length, please make sure you're not using numbers " "beyond double precision as coordinates."; } } } modifiedGeometry->GetGeometryForTimeStep(step)->SetBounds(newBounds); } return boundingBoxInitialized; } const SliceNavigationController *RenderingManager::GetTimeNavigationController() const { return m_TimeNavigationController.GetPointer(); } SliceNavigationController *RenderingManager::GetTimeNavigationController() { return m_TimeNavigationController.GetPointer(); } void RenderingManager::ExecutePendingRequests() { m_UpdatePending = false; // Satisfy all pending update requests RenderWindowList::const_iterator it; int i = 0; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it, ++i) { if (it->second == RENDERING_REQUESTED) { this->ForceImmediateUpdate(it->first); } } } void RenderingManager::RenderingStartCallback(vtkObject *caller, unsigned long, void *, void *) { auto renderingManager = RenderingManager::GetInstance(); auto renderWindow = dynamic_cast(caller); if (nullptr != renderWindow) renderingManager->m_RenderWindowList[renderWindow] = RENDERING_INPROGRESS; renderingManager->m_UpdatePending = false; } void RenderingManager::RenderingProgressCallback(vtkObject *caller, unsigned long, void *, void *) { auto renderingManager = RenderingManager::GetInstance(); if (renderingManager->m_LODAbortMechanismEnabled) { auto renderWindow = dynamic_cast(caller); if (nullptr != renderWindow) { auto renderer = BaseRenderer::GetInstance(renderWindow); if (nullptr != renderer && 0 < renderer->GetNumberOfVisibleLODEnabledMappers()) renderingManager->DoMonitorRendering(); } } } void RenderingManager::RenderingEndCallback(vtkObject *caller, unsigned long, void *, void *) { auto renderWindow = dynamic_cast(caller); if (nullptr == renderWindow) { return; } auto renderer = BaseRenderer::GetInstance(renderWindow); if (nullptr == renderer) { return; } auto renderingManager = RenderingManager::GetInstance(); renderingManager->m_RenderWindowList[renderer->GetRenderWindow()] = RENDERING_INACTIVE; if (0 < renderer->GetNumberOfVisibleLODEnabledMappers()) { if (0 == renderingManager->m_NextLODMap[renderer]) { renderingManager->StartOrResetTimer(); } else { renderingManager->m_NextLODMap[renderer] = 0; } } } bool RenderingManager::IsRendering() const { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { return true; } } return false; } void RenderingManager::AbortRendering() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { it->first->SetAbortRender(true); m_RenderingAbortedMap[BaseRenderer::GetInstance(it->first)] = true; } } } int RenderingManager::GetNextLOD(BaseRenderer *renderer) { if (renderer != nullptr) { return m_NextLODMap[renderer]; } else { return 0; } } void RenderingManager::ExecutePendingHighResRenderingRequest() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { BaseRenderer *renderer = BaseRenderer::GetInstance(it->first); if (renderer->GetNumberOfVisibleLODEnabledMappers() > 0) { if (m_NextLODMap[renderer] == 0) { m_NextLODMap[renderer] = 1; RequestUpdate(it->first); } } } } void RenderingManager::SetMaximumLOD(unsigned int max) { m_MaxLOD = max; } // enable/disable shading void RenderingManager::SetShading(bool state, unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return; } m_ShadingEnabled[lod] = state; } bool RenderingManager::GetShading(unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return false; } return m_ShadingEnabled[lod]; } // enable/disable the clipping plane void RenderingManager::SetClippingPlaneStatus(bool status) { m_ClippingPlaneEnabled = status; } bool RenderingManager::GetClippingPlaneStatus() { return m_ClippingPlaneEnabled; } void RenderingManager::SetShadingValues(float ambient, float diffuse, float specular, float specpower) { m_ShadingValues[0] = ambient; m_ShadingValues[1] = diffuse; m_ShadingValues[2] = specular; m_ShadingValues[3] = specpower; } RenderingManager::FloatVector &RenderingManager::GetShadingValues() { return m_ShadingValues; } void RenderingManager::InitializePropertyList() { if (m_PropertyList.IsNull()) { m_PropertyList = PropertyList::New(); } this->SetProperty("coupled-zoom", BoolProperty::New(false)); this->SetProperty("coupled-plane-rotation", BoolProperty::New(false)); this->SetProperty("MIP-slice-rendering", BoolProperty::New(false)); } PropertyList::Pointer RenderingManager::GetPropertyList() const { return m_PropertyList; } BaseProperty *RenderingManager::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void RenderingManager::SetProperty(const char *propertyKey, BaseProperty *propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void RenderingManager::SetDataStorage(DataStorage *storage) { if (storage != nullptr) { m_DataStorage = storage; RenderingManager::RenderWindowVector::const_iterator iter; for (iter = m_AllRenderWindows.cbegin(); iter < m_AllRenderWindows.cend(); ++iter) { BaseRenderer::GetInstance((*iter))->SetDataStorage(m_DataStorage.GetPointer()); } } } void RenderingManager::SetRenderWindowFocus(vtkRenderWindow *focusWindow) { if (focusWindow != m_FocusedRenderWindow) { if (!focusWindow || (m_RenderWindowList.find(focusWindow) != m_RenderWindowList.cend())) { m_FocusedRenderWindow = focusWindow; this->InvokeEvent(FocusChangedEvent()); return; } MITK_ERROR << "Tried to set a RenderWindow that does not exist."; } } void RenderingManager::SetAntiAliasing(AntiAliasing antiAliasing) { if (m_AntiAliasing != antiAliasing) { auto renderingManager = RenderingManager::GetInstance(); auto renderWindows = renderingManager->GetAllRegisteredRenderWindows(); for (auto renderWindow : renderWindows) { auto renderers = renderWindow->GetRenderers(); if (nullptr != renderers) { renderers->InitTraversal(); auto renderer = renderers->GetNextItem(); while (nullptr != renderer) { renderer->SetUseFXAA(AntiAliasing::FastApproximate == antiAliasing); renderer = renderers->GetNextItem(); } renderingManager->RequestUpdate(renderWindow); } } m_AntiAliasing = antiAliasing; } } // Create and register generic RenderingManagerFactory. TestingRenderingManagerFactory renderingManagerFactory; } // namespace diff --git a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp index 1c1f4f2138..b571dcd6a8 100644 --- a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp @@ -1,284 +1,291 @@ /*============================================================================ 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 "QmitkMxNMultiWidget.h" #include "QmitkRenderWindowWidget.h" // mitk core #include #include #include // qt #include QmitkMxNMultiWidget::QmitkMxNMultiWidget(QWidget* parent, Qt::WindowFlags f/* = 0*/, const QString& multiWidgetName/* = "mxnmulti"*/) : QmitkAbstractMultiWidget(parent, f, multiWidgetName) , m_TimeNavigationController(nullptr) , m_CrosshairVisibility(false) { m_TimeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); } QmitkMxNMultiWidget::~QmitkMxNMultiWidget() { auto allRenderWindows = this->GetRenderWindows(); for (auto& renderWindow : allRenderWindows) { m_TimeNavigationController->Disconnect(renderWindow->GetSliceNavigationController()); } } void QmitkMxNMultiWidget::InitializeMultiWidget() { SetLayout(1, 1); SetDisplayActionEventHandler(std::make_unique()); auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } } void QmitkMxNMultiWidget::Synchronize(bool synchronized) { if (synchronized) { SetDisplayActionEventHandler(std::make_unique()); } else { SetDisplayActionEventHandler(std::make_unique()); } auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const QString& widgetName) const { if ("axial" == widgetName || "sagittal" == widgetName || "coronal" == widgetName || "3d" == widgetName) { return GetActiveRenderWindowWidget()->GetRenderWindow(); } return QmitkAbstractMultiWidget::GetRenderWindow(widgetName); } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const mitk::BaseRenderer::ViewDirection& /*viewDirection*/) const { // currently no mapping between view directions and render windows // simply return the currently active render window return GetActiveRenderWindowWidget()->GetRenderWindow(); } void QmitkMxNMultiWidget::SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget) { auto currentActiveRenderWindowWidget = GetActiveRenderWindowWidget(); if (currentActiveRenderWindowWidget == activeRenderWindowWidget) { return; } // reset the decoration color of the previously active render window widget if (nullptr != currentActiveRenderWindowWidget) { auto decorationColor = currentActiveRenderWindowWidget->GetDecorationColor(); QColor hexColor(decorationColor[0] * 255, decorationColor[1] * 255, decorationColor[2] * 255); currentActiveRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid " + hexColor.name(QColor::HexRgb) + "; }"); } // set the new decoration color of the currently active render window widget if (nullptr != activeRenderWindowWidget) { activeRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid #FF6464; }"); } QmitkAbstractMultiWidget::SetActiveRenderWindowWidget(activeRenderWindowWidget); } void QmitkMxNMultiWidget::SetSelectedPosition(const mitk::Point3D& newPosition, const QString& widgetName) { RenderWindowWidgetPointer renderWindowWidget; if (widgetName.isNull()) { renderWindowWidget = GetActiveRenderWindowWidget(); } else { renderWindowWidget = GetRenderWindowWidget(widgetName); } if (nullptr != renderWindowWidget) { renderWindowWidget->GetSliceNavigationController()->SelectSliceByPoint(newPosition); renderWindowWidget->RequestUpdate(); return; } MITK_ERROR << "Position can not be set for an unknown render window widget."; } const mitk::Point3D QmitkMxNMultiWidget::GetSelectedPosition(const QString& /*widgetName*/) const { // see T26208 return mitk::Point3D(); } void QmitkMxNMultiWidget::SetCrosshairVisibility(bool activate) { auto renderWindowWidgets = GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { renderWindowWidget.second->ActivateCrosshair(activate); } m_CrosshairVisibility = activate; } void QmitkMxNMultiWidget::ResetCrosshair() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } - mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(dataStorage); + // get the specific render window that sent the signal + QmitkRenderWindow* renderwindow = qobject_cast(sender()); + if (nullptr == renderwindow) + { + return; + } + + mitk::RenderingManager::GetInstance()->InitializeViewByBoundingObjects(renderwindow->GetRenderWindow(), dataStorage); SetWidgetPlaneMode(mitk::InteractionSchemeSwitcher::MITKStandard); } void QmitkMxNMultiWidget::SetWidgetPlaneMode(int userMode) { MITK_DEBUG << "Changing crosshair mode to " << userMode; switch (userMode) { case 0: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKStandard); break; case 1: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationUncoupled); break; case 2: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationCoupled); break; case 3: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKSwivel); break; } } mitk::SliceNavigationController* QmitkMxNMultiWidget::GetTimeNavigationController() { return m_TimeNavigationController; } ////////////////////////////////////////////////////////////////////////// // PUBLIC SLOTS // MOUSE EVENTS ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::wheelEvent(QWheelEvent* e) { emit WheelMoved(e); } void QmitkMxNMultiWidget::mousePressEvent(QMouseEvent*) { // nothing here, but necessary for mouse interactions (.xml-configuration files) } void QmitkMxNMultiWidget::moveEvent(QMoveEvent* e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the overlays as the MultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } void QmitkMxNMultiWidget::RemoveRenderWindowWidget() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); auto iterator = renderWindowWidgets.find(this->GetNameFromIndex(this->GetNumberOfRenderWindowWidgets() - 1)); if (iterator == renderWindowWidgets.end()) { return; } // disconnect each signal of this render window widget RenderWindowWidgetPointer renderWindowWidgetToRemove = iterator->second; m_TimeNavigationController->Disconnect(renderWindowWidgetToRemove->GetSliceNavigationController()); QmitkAbstractMultiWidget::RemoveRenderWindowWidget(); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::SetLayoutImpl() { int requiredRenderWindowWidgets = GetRowCount() * GetColumnCount(); int existingRenderWindowWidgets = GetRenderWindowWidgets().size(); int difference = requiredRenderWindowWidgets - existingRenderWindowWidgets; while (0 < difference) { // more render window widgets needed CreateRenderWindowWidget(); --difference; } while (0 > difference) { // less render window widgets needed RemoveRenderWindowWidget(); ++difference; } auto firstRenderWindowWidget = GetFirstRenderWindowWidget(); if (nullptr != firstRenderWindowWidget) { SetActiveRenderWindowWidget(firstRenderWindowWidget); } GetMultiWidgetLayoutManager()->SetLayoutDesign(QmitkMultiWidgetLayoutManager::LayoutDesign::DEFAULT); } void QmitkMxNMultiWidget::CreateRenderWindowWidget() { // create the render window widget and connect signal / slot QString renderWindowWidgetName = GetNameFromIndex(GetNumberOfRenderWindowWidgets()); RenderWindowWidgetPointer renderWindowWidget = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); renderWindowWidget->SetCornerAnnotationText(renderWindowWidgetName.toStdString()); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget); auto renderWindow = renderWindowWidget->GetRenderWindow(); auto layoutManager = GetMultiWidgetLayoutManager(); connect(renderWindow, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(renderWindow, &QmitkRenderWindow::ResetView, this, &QmitkMxNMultiWidget::ResetCrosshair); connect(renderWindow, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkMxNMultiWidget::SetCrosshairVisibility); connect(renderWindow, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkMxNMultiWidget::SetWidgetPlaneMode); // connect time navigation controller to react on geometry time events with the render window's slice naviation controller m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow->GetSliceNavigationController()); // reverse connection between the render window's slice navigation controller and the time navigation controller renderWindow->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); }