diff --git a/Modules/Core/include/mitkDataStorage.h b/Modules/Core/include/mitkDataStorage.h index 55e63e9cd3..80eaf630c2 100644 --- a/Modules/Core/include/mitkDataStorage.h +++ b/Modules/Core/include/mitkDataStorage.h @@ -1,440 +1,448 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#ifndef MITKDATASTORAGE_H_HEADER_INCLUDED_ -#define MITKDATASTORAGE_H_HEADER_INCLUDED_ +#ifndef MITKDATASTORAGE_H +#define MITKDATASTORAGE_H #include "itkObject.h" #include "itkSimpleFastMutexLock.h" #include "itkVectorContainer.h" #include "mitkDataNode.h" #include "mitkGeometry3D.h" #include "mitkMessage.h" #include #include namespace mitk { class NodePredicateBase; class DataNode; class BaseRenderer; //##Documentation //## @brief Data management class that handles 'was created by' relations //## //## The DataStorage provides data storage and management functionality. //## It handles a 'was created by' relation by associating each data object with a //## set of source objects, that this object was created from. //## Thus, nodes are stored in a noncyclical directed graph data structure. //## If a new node is added to the DataStorage, AddNodeEvent is emitted. //## If a node is removed, RemoveNodeEvent is emitted. //## //## //## \ingroup DataStorage class MITKCORE_EXPORT DataStorage : public itk::Object { public: mitkClassMacroItkParent(DataStorage, itk::Object); //##Documentation //## @brief A Container of objects that is used as a result set of GetSubset() query operations (Set of //SmartPointers // to DataNodes). - typedef itk::VectorContainer SetOfObjects; + typedef itk::VectorContainer SetOfObjects; //##Documentation //## @brief Adds a DataNode containing a data object to its internal storage //## //## This Method adds a new data object to the DataStorage. The new object is //## passed in the first parameter. The second parameter is a set //## of source objects, that were used to create this object. The new object will have //## a 'was created from' relation to its source objects. //## the addition of a new object will fire the notification mechanism. //## If the node parameter is nullptr or if the DataNode has already been added, //## an exception will be thrown. - virtual void Add(mitk::DataNode *node, const mitk::DataStorage::SetOfObjects *parents = nullptr) = 0; + virtual void Add(DataNode *node, const DataStorage::SetOfObjects *parents = nullptr) = 0; //##Documentation //## @brief Convenience method to add a node that has one parent //## - void Add(mitk::DataNode *node, mitk::DataNode *parent); + void Add(DataNode *node, DataNode *parent); //##Documentation //## @brief Removes node from the DataStorage //## - virtual void Remove(const mitk::DataNode *node) = 0; + virtual void Remove(const DataNode *node) = 0; //##Documentation //## @brief Checks if a node exists in the DataStorage //## - virtual bool Exists(const mitk::DataNode *node) const = 0; + virtual bool Exists(const DataNode *node) const = 0; //##Documentation //## @brief Removes a set of nodes from the DataStorage //## - void Remove(const mitk::DataStorage::SetOfObjects *nodes); + void Remove(const DataStorage::SetOfObjects *nodes); //##Documentation //## @brief returns a set of data objects that meet the given condition(s) //## //## GetSubset returns a set of objects with a specific data type that meet the condition(s) //## specified in the condition parameter. Conditions can be //## - data type of the data object //## - is source object of specific object (e.g. all source objects of node x) //## - has property with specific value (e.g. OrganType is Liver) //## - negation of any condition //## - conjunction of a set of conditions //## - disjunction of a set of conditions //## Conditions are implemented as predicates using the Composite Design Pattern //## (see definition of NodePredicateBase for details). //## The method returns a set of SmartPointers to the DataNodes that fulfill the //## conditions. A set of all objects can be retrieved with the GetAll() method; SetOfObjects::ConstPointer GetSubset(const NodePredicateBase *condition) const; //##Documentation //## @brief returns a set of source objects for a given node that meet the given condition(s). //## - virtual SetOfObjects::ConstPointer GetSources(const mitk::DataNode *node, + virtual SetOfObjects::ConstPointer GetSources(const DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectSources = true) const = 0; //##Documentation //## @brief returns a set of derived objects for a given node. //## //## GetDerivations() returns a set of objects that are derived from the DataNode node. //## This means, that node was used to create the returned objects. If the parameter //## onlyDirectDerivations is set to true (default value), only objects that directly have //## node as one of their source objects will be returned. Otherwise, objects that are //## derived from derivations of node are returned too. //## The derived objects can be filtered with a predicate object as described in the GetSubset() //## method by providing a predicate as the condition parameter. - virtual SetOfObjects::ConstPointer GetDerivations(const mitk::DataNode *node, + virtual SetOfObjects::ConstPointer GetDerivations(const DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectDerivations = true) const = 0; //##Documentation //## @brief returns a set of all data objects that are stored in the data storage //## virtual SetOfObjects::ConstPointer GetAll() const = 0; //##Documentation //## @brief Convenience method to get the first node that matches the predicate condition //## - mitk::DataNode *GetNode(const NodePredicateBase *condition = nullptr) const; + DataNode *GetNode(const NodePredicateBase *condition = nullptr) const; //##Documentation //## @brief Convenience method to get the first node with a given name //## - mitk::DataNode *GetNamedNode(const char *name) const; + DataNode *GetNamedNode(const char *name) const; //##Documentation //## @brief Convenience method to get the first node with a given name //## - mitk::DataNode *GetNamedNode(const std::string name) const { return this->GetNamedNode(name.c_str()); } + DataNode *GetNamedNode(const std::string name) const { return this->GetNamedNode(name.c_str()); } //##Documentation //## @brief Convenience method to get the first node with a given name that is derived from sourceNode //## - mitk::DataNode *GetNamedDerivedNode(const char *name, - const mitk::DataNode *sourceNode, - bool onlyDirectDerivations = true) const; + DataNode *GetNamedDerivedNode(const char *name, + const DataNode *sourceNode, + bool onlyDirectDerivations = true) const; //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name //## template DataType *GetNamedObject(const char *name) const { if (name == nullptr) return nullptr; - mitk::DataNode *n = this->GetNamedNode(name); + DataNode *n = this->GetNamedNode(name); if (n == nullptr) return nullptr; else return dynamic_cast(n->GetData()); } //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name //## template DataType *GetNamedObject(const std::string name) const { return this->GetNamedObject(name.c_str()); } //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name that is derived // from a specific node //## template DataType *GetNamedDerivedObject(const char *name, - const mitk::DataNode *sourceNode, + const DataNode *sourceNode, bool onlyDirectDerivations = true) const { if (name == nullptr) return nullptr; - mitk::DataNode *n = this->GetNamedDerivedNode(name, sourceNode, onlyDirectDerivations); + DataNode *n = this->GetNamedDerivedNode(name, sourceNode, onlyDirectDerivations); if (n == nullptr) return nullptr; else return dynamic_cast(n->GetData()); } //##Documentation //## @brief Returns a list of used grouptags //## const DataNode::GroupTagList GetGroupTags() const; /*ITK Mutex */ mutable itk::SimpleFastMutexLock m_MutexOne; /* Public Events */ - typedef Message1 DataStorageEvent; + typedef Message1 DataStorageEvent; //##Documentation //## @brief AddEvent is emitted whenever a new node has been added to the DataStorage. //## //## Observers should register to this event by calling myDataStorage->AddNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been added to the DataStorage. //## Observers should unregister by calling myDataStorage->AddNodeEvent.RemoveListener(myObject, //MyObject::MyMethod). //## Note: AddEvents are _not_ emitted if a node is added to DataStorage by adding it to the the underlying //DataTree! // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent AddNodeEvent; //##Documentation //## @brief RemoveEvent is emitted directly before a node is removed from the DataStorage. //## //## Observers should register to this event by calling myDataStorage->RemoveNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been added to the DataStorage. //## Observers should unregister by calling myDataStorage->RemoveNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Note: RemoveEvents are also emitted if a node was removed from the DataStorage by deleting it from the //underlying // DataTree // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent RemoveNodeEvent; //##Documentation //## @brief ChangedEvent is emitted directly after a node was changed. //## //## Observers should register to this event by calling myDataStorage->ChangedNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been changed. //## Observers should unregister by calling myDataStorage->ChangedNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Internally the DataStorage listens to itk::ModifiedEvents on the nodes and forwards them //## to the listeners of this event. // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent ChangedNodeEvent; //##Documentation //## @brief DeleteNodeEvent is emitted directly before a node is deleted. //## //## Observers should register to this event by calling myDataStorage->DeleteNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called when a node is deleted. //## Observers should unregister by calling myDataStorage->DeleteNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Internally the DataStorage listens to itk::DeleteEvents on the nodes and forwards them //## to the listeners of this event. // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent DeleteNodeEvent; DataStorageEvent InteractorChangedNodeEvent; //##Documentation //## @brief Compute the axis-parallel bounding geometry of the input objects //## //## Throws std::invalid_argument exception if input is nullptr //## @param input set of objects of the DataStorage to be included in the bounding geometry //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey - mitk::TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const SetOfObjects *input, - const char *boolPropertyKey = nullptr, - const mitk::BaseRenderer *renderer = nullptr, - const char *boolPropertyKey2 = nullptr) const; + TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const SetOfObjects *input, + const char *boolPropertyKey = nullptr, + const BaseRenderer *renderer = nullptr, + const char *boolPropertyKey2 = nullptr) const; //##Documentation //## @brief Compute the axis-parallel bounding geometry of the data tree //## (bounding box, minimal spacing of the considered nodes, live-span) //## //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey - mitk::TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const char *boolPropertyKey = nullptr, - const mitk::BaseRenderer *renderer = nullptr, - const char *boolPropertyKey2 = nullptr) const; + TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const char *boolPropertyKey = nullptr, + const BaseRenderer *renderer = nullptr, + const char *boolPropertyKey2 = nullptr) const; //##Documentation //## @brief Compute the axis-parallel bounding geometry of all visible parts of the //## data tree bounding box, minimal spacing of the considered nodes, live-span) //## //## Simply calls ComputeBoundingGeometry3D(it, "visible", renderer, boolPropertyKey). //## it -> an iterator of a data tree structure //## @param renderer the reference to the renderer //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. - mitk::TimeGeometry::ConstPointer ComputeVisibleBoundingGeometry3D(const mitk::BaseRenderer *renderer = nullptr, - const char *boolPropertyKey = nullptr); + TimeGeometry::ConstPointer ComputeVisibleBoundingGeometry3D(const BaseRenderer *renderer = nullptr, + const char *boolPropertyKey = nullptr); //##Documentation //## @brief Compute the bounding box of data tree structure //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey - mitk::BoundingBox::Pointer ComputeBoundingBox(const char *boolPropertyKey = nullptr, - const mitk::BaseRenderer *renderer = nullptr, - const char *boolPropertyKey2 = nullptr); + BoundingBox::Pointer ComputeBoundingBox(const char *boolPropertyKey = nullptr, + const BaseRenderer *renderer = nullptr, + const char *boolPropertyKey2 = nullptr); //##Documentation //## \brief Compute the bounding box of all visible parts of the data tree structure, for general //## rendering or renderer specific visibility property checking //## //## Simply calls ComputeBoundingBox(it, "visible", renderer, boolPropertyKey). //## it -> an iterator of a data tree structure //## @param renderer the reference to the renderer //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. - mitk::BoundingBox::Pointer ComputeVisibleBoundingBox(const mitk::BaseRenderer *renderer = nullptr, - const char *boolPropertyKey = nullptr) + BoundingBox::Pointer ComputeVisibleBoundingBox(const BaseRenderer *renderer = nullptr, + const char *boolPropertyKey = nullptr) { return ComputeBoundingBox("visible", renderer, boolPropertyKey); } //##Documentation //## @brief Compute the time-bounds of the contents of a data tree structure //## //## The methods returns only [-infinity, +infinity], if all data-objects have an infinite live-span. Otherwise, //## all data-objects with infinite live-span are ignored. //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the time-bounds calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey - mitk::TimeBounds ComputeTimeBounds(const char *boolPropertyKey, - const mitk::BaseRenderer *renderer, - const char *boolPropertyKey2); + TimeBounds ComputeTimeBounds(const char *boolPropertyKey, + const BaseRenderer *renderer, + const char *boolPropertyKey2); //##Documentation //## @brief Compute the time-bounds of all visible parts of the data tree structure, for general //## rendering or renderer specific visibility property checking //## //## The methods returns only [-infinity, +infinity], if all data-objects have an infinite live-span. Otherwise, //## all data-objects with infinite live-span are ignored. //## Simply calls ComputeTimeBounds(it, "visible", renderer, boolPropertyKey). //## @param it an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the time-bounds calculation. //## @param renderer see @a boolPropertyKey - mitk::TimeBounds ComputeTimeBounds(const mitk::BaseRenderer *renderer, const char *boolPropertyKey) + TimeBounds ComputeTimeBounds(const BaseRenderer *renderer, const char *boolPropertyKey) { return ComputeTimeBounds("visible", renderer, boolPropertyKey); } //##Documentation //## @brief Defines whether or not NodeChangedEvent is invoked . //## //## This method can be used to set m_BlockNodeModifiedEvents. //## //## If this flag is true, NodeChangedEvent is not invoked when a //## DataNode is modified. This might be undesired when setting //## many properties on a datanode and you do not want anyone to //## react. void BlockNodeModifiedEvents(bool block); protected: //##Documentation //## @brief EmitAddNodeEvent emits the AddNodeEvent //## //## This method should be called by subclasses to emit the AddNodeEvent - void EmitAddNodeEvent(const mitk::DataNode *node); + void EmitAddNodeEvent(const DataNode *node); //##Documentation //## @brief EmitRemoveNodeEvent emits the RemoveNodeEvent //## //## This method should be called by subclasses to emit the RemoveNodeEvent - void EmitRemoveNodeEvent(const mitk::DataNode *node); + void EmitRemoveNodeEvent(const DataNode *node); void OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &event); //##Documentation //## @brief OnNodeModified listens to modified events of DataNodes. //## //## The node is hidden behind the caller parameter, which has to be casted first. //## If the cast succeeds the ChangedNodeEvent is emitted with this node. void OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event); //##Documentation //## @brief Adds a Modified-Listener to the given Node. - void AddListeners(const mitk::DataNode *_Node); + void AddListeners(const DataNode *_Node); //##Documentation //## @brief Removes a Modified-Listener from the given Node. - void RemoveListeners(const mitk::DataNode *_Node); + void RemoveListeners(const DataNode *_Node); //##Documentation //## @brief Saves Modified-Observer Tags for each node in order to remove the event listeners again. - std::map m_NodeModifiedObserverTags; + std::map m_NodeModifiedObserverTags; - std::map m_NodeInteractorChangedObserverTags; + std::map m_NodeInteractorChangedObserverTags; //##Documentation //## @brief Saves Delete-Observer Tags for each node in order to remove the event listeners again. - std::map m_NodeDeleteObserverTags; + std::map m_NodeDeleteObserverTags; //##Documentation //## @brief If this class changes nodes itself, set this to TRUE in order //## to suppress NodeChangedEvent to be emitted. bool m_BlockNodeModifiedEvents; //##Documentation //## @brief Standard Constructor for ::New() instantiation DataStorage(); //##Documentation //## @brief Standard Destructor ~DataStorage() override; //##Documentation //## @brief Filters a SetOfObjects by the condition. If no condition is provided, the original set is returned SetOfObjects::ConstPointer FilterSetOfObjects(const SetOfObjects *set, const NodePredicateBase *condition) const; //##Documentation //## @brief Prints the contents of the DataStorage to os. Do not call directly, call ->Print() instead void PrintSelf(std::ostream &os, itk::Indent indent) const override; }; + + //##Documentation + //## @brief returns the topmost visible node of a given list of nodes. + //## + MITKCORE_EXPORT DataNode::Pointer FindTopmostVisibleNode(const DataStorage::SetOfObjects* nodes, + const Point3D worldposition, + const TimePointType timePoint, + const BaseRenderer* baseRender); } // namespace mitk -#endif /* MITKDATASTORAGE_H_HEADER_INCLUDED_ */ +#endif // MITKDATASTORAGE_H diff --git a/Modules/Core/include/mitkDisplayInteractor.h b/Modules/Core/include/mitkDisplayInteractor.h index 065131e0d1..99012b554d 100644 --- a/Modules/Core/include/mitkDisplayInteractor.h +++ b/Modules/Core/include/mitkDisplayInteractor.h @@ -1,276 +1,272 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkDisplayInteractor_h #define mitkDisplayInteractor_h #include "mitkInteractionEventObserver.h" #include namespace mitk { /** *\class DisplayInteractor *@brief Observer that manages the interaction with the display. * * This includes the interaction of Zooming, Panning, Scrolling and adjusting the LevelWindow. * * @ingroup Interaction **/ /** * Inherits from mitk::InteractionEventObserver since it doesn't alter any data (only their representation), * and its actions cannot be associated with a DataNode. Also inherits from EventStateMachine */ class MITKCORE_EXPORT DisplayInteractor : public EventStateMachine, public InteractionEventObserver { public: mitkClassMacro(DisplayInteractor, EventStateMachine) itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** * By this function the Observer gets notified about new events. * Here it is adapted to pass the events to the state machine in order to use * its infrastructure. * It also checks if event is to be accepted when it already has been processed by a DataInteractor. */ void Notify(InteractionEvent *interactionEvent, bool isHandled) override; protected: DisplayInteractor(); ~DisplayInteractor() override; /** * Derived function. * Connects the action names used in the state machine pattern with functions implemented within * this InteractionEventObserver. This is only necessary here because the events are processed by the state machine. */ void ConnectActionsAndFunctions() override; /** * Derived function. * Is executed when config object is set / changed. * Here it is used to read out the parameters set in the configuration file, * and set the member variables accordingly. */ void ConfigurationChanged() override; /** * Derived function. * Is executed when config object is set / changed. * Here it is used to read out the parameters set in the configuration file, * and set the member variables accordingly. */ bool FilterEvents(InteractionEvent *interactionEvent, DataNode *dataNode) override; virtual bool CheckPositionEvent(const InteractionEvent *interactionEvent); virtual bool CheckRotationPossible(const InteractionEvent *interactionEvent); virtual bool CheckSwivelPossible(const InteractionEvent *interactionEvent); /** * \brief Initializes an interaction, saves the pointers start position for further reference. */ virtual void Init(StateMachineAction *, InteractionEvent *); /** * \brief Performs panning of the data set in the render window. */ virtual void Move(StateMachineAction *, InteractionEvent *); /** * \brief Sets crosshair at clicked position* */ virtual void SetCrosshair(StateMachineAction *, InteractionEvent *); /** * \brief Performs zooming relative to mouse/pointer movement. * * Behavior is determined by \see m_ZoomDirection and \see m_ZoomFactor. * */ virtual void Zoom(StateMachineAction *, InteractionEvent *); /** * \brief Performs scrolling relative to mouse/pointer movement. * * Behavior is determined by \see m_ScrollDirection and \see m_AutoRepeat. * */ virtual void Scroll(StateMachineAction *, InteractionEvent *); /** * \brief Scrolls one layer up */ virtual void ScrollOneDown(StateMachineAction *, InteractionEvent *); /** * \brief Scrolls one layer down */ virtual void ScrollOneUp(StateMachineAction *, InteractionEvent *); /** * \brief Adjusts the level windows relative to mouse/pointer movement. */ virtual void AdjustLevelWindow(StateMachineAction *, InteractionEvent *); /** * \brief Starts crosshair rotation */ virtual void StartRotation(StateMachineAction *, InteractionEvent *); /** * \brief Ends crosshair rotation */ virtual void EndRotation(StateMachineAction *, InteractionEvent *); /** * \brief */ virtual void Rotate(StateMachineAction *, InteractionEvent *event); virtual void Swivel(StateMachineAction *, InteractionEvent *event); /** * \brief Updates the Statusbar information with the information about the clicked position */ virtual void UpdateStatusbar(StateMachineAction *, InteractionEvent *event); /** * \brief Method to retrieve bool-value for given property from string-property * in given propertylist. */ bool GetBoolProperty(mitk::PropertyList::Pointer propertyList, const char *propertyName, bool defaultValue); // Typedefs typedef std::vector SNCVector; private: - mitk::DataNode::Pointer GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes, - mitk::Point3D worldposition, - BaseRenderer *ren); - /** * @brief UpdateStatusBar * @param image3D * @param idx * @param time * @param component If the PixelType of image3D is a vector (for example a 2D velocity vector), then only one of the vector components can be * displayed at once. Setting this parameter will determine which of the vector's components will be used to determine the displayed PixelValue. * Set this to 0 for scalar images */ void UpdateStatusBar(itk::SmartPointer image3D, itk::Index<3> idx, TimeStepType time=0, int component=0); /** * \brief Coordinate of the pointer at begin of an interaction translated to mm unit */ mitk::Point2D m_StartCoordinateInMM; /** * \brief Coordinate of the pointer in the last step within an interaction. */ mitk::Point2D m_LastDisplayCoordinate; /** * \brief Coordinate of the pointer in the last step within an interaction translated to mm unit */ mitk::Point2D m_LastCoordinateInMM; /** * \brief Current coordinates of the pointer. */ mitk::Point2D m_CurrentDisplayCoordinate; /** * \brief Modifier that defines how many slices are scrolled per pixel that the mouse has moved * * This modifier defines how many slices the scene is scrolled per pixel that the mouse cursor has moved. * By default the modifier is 4. This means that when the user moves the cursor by 4 pixels in Y-direction * the scene is scrolled by one slice. If the user has moved the the cursor by 20 pixels, the scene is * scrolled by 5 slices. * * If the cursor has moved less than m_IndexToSliceModifier pixels the scene is scrolled by one slice. */ int m_IndexToSliceModifier; /** Defines behavior at end of data set. * If set to true it will restart at end of data set from the beginning. */ bool m_AutoRepeat; /** * Defines scroll behavior. * Default is up/down movement of pointer performs scrolling */ std::string m_ScrollDirection; /** * Defines how the axis of interaction influences scroll behavior. */ bool m_InvertScrollDirection; /** * Defines scroll behavior. * Default is up/down movement of pointer performs zooming */ std::string m_ZoomDirection; /** * Defines how the axis of interaction influences zoom behavior. */ bool m_InvertZoomDirection; /** * Defines how the axis of interaction influences move behavior. */ bool m_InvertMoveDirection; /** * Defines level/window behavior. * Default is left/right movement of pointer modifies the level. */ std::string m_LevelDirection; /** * Defines how the axis of interaction influences level/window behavior. */ bool m_InvertLevelWindowDirection; /** * Determines if the Observer reacts to events that already have been processed by a DataInteractor. * The default value is false. */ bool m_AlwaysReact; /** * Factor to adjust zooming speed. */ float m_ZoomFactor; ///// Members to deal with rotating slices /** * @brief m_LinkPlanes Determines if angle between crosshair remains fixed when rotating */ bool m_LinkPlanes; SNCVector m_RotatableSNCs; /// all SNCs that currently have CreatedWorldGeometries, that can be rotated. SNCVector m_SNCsToBeRotated; /// all SNCs that will be rotated (exceptions are the ones parallel to the one being clicked) Point3D m_LastCursorPosition; /// used for calculation of the rotation angle Point3D m_CenterOfRotation; /// used for calculation of the rotation angle Point2D m_ReferenceCursor; Vector3D m_RotationPlaneNormal; Vector3D m_RotationPlaneXVector; Vector3D m_RotationPlaneYVector; Vector3D m_PreviousRotationAxis; ScalarType m_PreviousRotationAngle; }; } #endif diff --git a/Modules/Core/src/DataManagement/mitkDataStorage.cpp b/Modules/Core/src/DataManagement/mitkDataStorage.cpp index 43a69e91bd..f25faaa1b8 100644 --- a/Modules/Core/src/DataManagement/mitkDataStorage.cpp +++ b/Modules/Core/src/DataManagement/mitkDataStorage.cpp @@ -1,516 +1,574 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDataStorage.h" #include "itkCommand.h" #include "itkMutexLockHolder.h" #include "mitkDataNode.h" #include "mitkGroupTagProperty.h" #include "mitkImage.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include "mitkArbitraryTimeGeometry.h" - mitk::DataStorage::DataStorage() : itk::Object(), m_BlockNodeModifiedEvents(false) { } mitk::DataStorage::~DataStorage() { ///// we can not call GetAll() in destructor, because it is implemented in a subclass // SetOfObjects::ConstPointer all = this->GetAll(); // for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) // this->RemoveListeners(it->Value()); // m_NodeModifiedObserverTags.clear(); // m_NodeDeleteObserverTags.clear(); } -void mitk::DataStorage::Add(mitk::DataNode *node, mitk::DataNode *parent) +void mitk::DataStorage::Add(DataNode *node, DataNode *parent) { - mitk::DataStorage::SetOfObjects::Pointer parents = mitk::DataStorage::SetOfObjects::New(); + DataStorage::SetOfObjects::Pointer parents = DataStorage::SetOfObjects::New(); if (parent != nullptr) //< Return empty set if parent is null parents->InsertElement(0, parent); this->Add(node, parents); } -void mitk::DataStorage::Remove(const mitk::DataStorage::SetOfObjects *nodes) +void mitk::DataStorage::Remove(const DataStorage::SetOfObjects *nodes) { if (nodes == nullptr) return; - for (mitk::DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); it++) + for (DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); it++) this->Remove(it.Value()); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::GetSubset(const NodePredicateBase *condition) const { - mitk::DataStorage::SetOfObjects::ConstPointer result = this->FilterSetOfObjects(this->GetAll(), condition); + DataStorage::SetOfObjects::ConstPointer result = this->FilterSetOfObjects(this->GetAll(), condition); return result; } mitk::DataNode *mitk::DataStorage::GetNamedNode(const char *name) const { if (name == nullptr) return nullptr; - mitk::StringProperty::Pointer s(mitk::StringProperty::New(name)); - mitk::NodePredicateProperty::Pointer p = mitk::NodePredicateProperty::New("name", s); - mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(p); + StringProperty::Pointer s(StringProperty::New(name)); + NodePredicateProperty::Pointer p = NodePredicateProperty::New("name", s); + DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(p); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } mitk::DataNode *mitk::DataStorage::GetNode(const NodePredicateBase *condition) const { if (condition == nullptr) return nullptr; - mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(condition); + DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(condition); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } mitk::DataNode *mitk::DataStorage::GetNamedDerivedNode(const char *name, - const mitk::DataNode *sourceNode, + const DataNode *sourceNode, bool onlyDirectDerivations) const { if (name == nullptr) return nullptr; - mitk::StringProperty::Pointer s(mitk::StringProperty::New(name)); - mitk::NodePredicateProperty::Pointer p = mitk::NodePredicateProperty::New("name", s); - mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetDerivations(sourceNode, p, onlyDirectDerivations); + StringProperty::Pointer s(StringProperty::New(name)); + NodePredicateProperty::Pointer p = NodePredicateProperty::New("name", s); + DataStorage::SetOfObjects::ConstPointer rs = this->GetDerivations(sourceNode, p, onlyDirectDerivations); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } void mitk::DataStorage::PrintSelf(std::ostream &os, itk::Indent indent) const { // Superclass::PrintSelf(os, indent); - mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetAll(); + DataStorage::SetOfObjects::ConstPointer all = this->GetAll(); os << indent << "DataStorage " << this << " is managing " << all->Size() << " objects. List of objects:" << std::endl; - for (mitk::DataStorage::SetOfObjects::ConstIterator allIt = all->Begin(); allIt != all->End(); allIt++) + for (DataStorage::SetOfObjects::ConstIterator allIt = all->Begin(); allIt != all->End(); allIt++) { std::string name; allIt.Value()->GetName(name); std::string datatype; if (allIt.Value()->GetData() != nullptr) datatype = allIt.Value()->GetData()->GetNameOfClass(); os << indent << " " << allIt.Value().GetPointer() << "<" << datatype << ">: " << name << std::endl; - mitk::DataStorage::SetOfObjects::ConstPointer parents = this->GetSources(allIt.Value()); + DataStorage::SetOfObjects::ConstPointer parents = this->GetSources(allIt.Value()); if (parents->Size() > 0) { os << indent << " Direct sources: "; - for (mitk::DataStorage::SetOfObjects::ConstIterator parentIt = parents->Begin(); parentIt != parents->End(); + for (DataStorage::SetOfObjects::ConstIterator parentIt = parents->Begin(); parentIt != parents->End(); parentIt++) os << parentIt.Value().GetPointer() << ", "; os << std::endl; } - mitk::DataStorage::SetOfObjects::ConstPointer derivations = this->GetDerivations(allIt.Value()); + DataStorage::SetOfObjects::ConstPointer derivations = this->GetDerivations(allIt.Value()); if (derivations->Size() > 0) { os << indent << " Direct derivations: "; - for (mitk::DataStorage::SetOfObjects::ConstIterator derivationIt = derivations->Begin(); + for (DataStorage::SetOfObjects::ConstIterator derivationIt = derivations->Begin(); derivationIt != derivations->End(); derivationIt++) os << derivationIt.Value().GetPointer() << ", "; os << std::endl; } } os << std::endl; } -mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::FilterSetOfObjects( - const SetOfObjects *set, const NodePredicateBase *condition) const +mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::FilterSetOfObjects(const SetOfObjects *set, + const NodePredicateBase *condition) const { if (set == nullptr) return nullptr; - mitk::DataStorage::SetOfObjects::Pointer result = mitk::DataStorage::SetOfObjects::New(); - for (mitk::DataStorage::SetOfObjects::ConstIterator it = set->Begin(); it != set->End(); it++) + DataStorage::SetOfObjects::Pointer result = DataStorage::SetOfObjects::New(); + for (DataStorage::SetOfObjects::ConstIterator it = set->Begin(); it != set->End(); it++) if (condition == nullptr || condition->CheckNode(it.Value()) == - true) // alway copy the set, otherwise the iterator in mitk::DataStorage::Remove() will crash + true) // alway copy the set, otherwise the iterator in DataStorage::Remove() will crash result->InsertElement(result->Size(), it.Value()); - return mitk::DataStorage::SetOfObjects::ConstPointer(result); + return DataStorage::SetOfObjects::ConstPointer(result); } const mitk::DataNode::GroupTagList mitk::DataStorage::GetGroupTags() const { DataNode::GroupTagList result; SetOfObjects::ConstPointer all = this->GetAll(); if (all.IsNull()) return result; - for (mitk::DataStorage::SetOfObjects::ConstIterator nodeIt = all->Begin(); nodeIt != all->End(); + for (DataStorage::SetOfObjects::ConstIterator nodeIt = all->Begin(); nodeIt != all->End(); nodeIt++) // for each node { - mitk::PropertyList *pl = nodeIt.Value()->GetPropertyList(); + PropertyList *pl = nodeIt.Value()->GetPropertyList(); for (auto propIt = pl->GetMap()->begin(); propIt != pl->GetMap()->end(); ++propIt) - if (dynamic_cast(propIt->second.GetPointer()) != nullptr) + if (dynamic_cast(propIt->second.GetPointer()) != nullptr) result.insert(propIt->first); } return result; } -void mitk::DataStorage::EmitAddNodeEvent(const mitk::DataNode *node) +void mitk::DataStorage::EmitAddNodeEvent(const DataNode *node) { AddNodeEvent.Send(node); } -void mitk::DataStorage::EmitRemoveNodeEvent(const mitk::DataNode *node) +void mitk::DataStorage::EmitRemoveNodeEvent(const DataNode *node) { RemoveNodeEvent.Send(node); } void mitk::DataStorage::OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &) { - const auto *_Node = dynamic_cast(caller); + const auto *_Node = dynamic_cast(caller); if (_Node) { InteractorChangedNodeEvent.Send(_Node); } } void mitk::DataStorage::OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event) { if (m_BlockNodeModifiedEvents) return; - const auto *_Node = dynamic_cast(caller); + const auto *_Node = dynamic_cast(caller); if (_Node) { const auto *modEvent = dynamic_cast(&event); if (modEvent) ChangedNodeEvent.Send(_Node); else DeleteNodeEvent.Send(_Node); } } -void mitk::DataStorage::AddListeners(const mitk::DataNode *_Node) +void mitk::DataStorage::AddListeners(const DataNode *_Node) { itk::MutexLockHolder locked(m_MutexOne); // node must not be 0 and must not be yet registered - auto *NonConstNode = const_cast(_Node); + auto *NonConstNode = const_cast(_Node); if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) == m_NodeModifiedObserverTags.end()) { - itk::MemberCommand::Pointer nodeModifiedCommand = itk::MemberCommand::New(); - nodeModifiedCommand->SetCallbackFunction(this, &mitk::DataStorage::OnNodeModifiedOrDeleted); + itk::MemberCommand::Pointer nodeModifiedCommand = itk::MemberCommand::New(); + nodeModifiedCommand->SetCallbackFunction(this, &DataStorage::OnNodeModifiedOrDeleted); m_NodeModifiedObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); - itk::MemberCommand::Pointer interactorChangedCommand = - itk::MemberCommand::New(); - interactorChangedCommand->SetCallbackFunction(this, &mitk::DataStorage::OnNodeInteractorChanged); + itk::MemberCommand::Pointer interactorChangedCommand = + itk::MemberCommand::New(); + interactorChangedCommand->SetCallbackFunction(this, &DataStorage::OnNodeInteractorChanged); m_NodeInteractorChangedObserverTags[NonConstNode] = - NonConstNode->AddObserver(mitk::DataNode::InteractorChangedEvent(), interactorChangedCommand); + NonConstNode->AddObserver(DataNode::InteractorChangedEvent(), interactorChangedCommand); // add itk delete listener on datastorage - itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); - deleteCommand->SetCallbackFunction(this, &mitk::DataStorage::OnNodeModifiedOrDeleted); + itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); + deleteCommand->SetCallbackFunction(this, &DataStorage::OnNodeModifiedOrDeleted); // add observer m_NodeDeleteObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::DeleteEvent(), deleteCommand); } } -void mitk::DataStorage::RemoveListeners(const mitk::DataNode *_Node) +void mitk::DataStorage::RemoveListeners(const DataNode *_Node) { itk::MutexLockHolder locked(m_MutexOne); // node must not be 0 and must be registered - auto *NonConstNode = const_cast(_Node); + auto *NonConstNode = const_cast(_Node); if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) != m_NodeModifiedObserverTags.end()) { // const cast is bad! but sometimes it is necessary. removing an observer does not really // touch the internal state NonConstNode->RemoveObserver(m_NodeModifiedObserverTags.find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeDeleteObserverTags.find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeInteractorChangedObserverTags.find(NonConstNode)->second); m_NodeModifiedObserverTags.erase(NonConstNode); m_NodeDeleteObserverTags.erase(NonConstNode); m_NodeInteractorChangedObserverTags.erase(NonConstNode); } } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeBoundingGeometry3D(const SetOfObjects *input, - const char *boolPropertyKey, - const mitk::BaseRenderer *renderer, - const char *boolPropertyKey2) const + const char *boolPropertyKey, + const BaseRenderer *renderer, + const char *boolPropertyKey2) const { if (input == nullptr) throw std::invalid_argument("DataStorage: input is invalid"); BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid = 0; Point3D point; Vector3D minSpacing; - minSpacing.Fill(itk::NumericTraits::max()); + minSpacing.Fill(itk::NumericTraits::max()); - ScalarType stmax = itk::NumericTraits::max(); - ScalarType stmin = itk::NumericTraits::NonpositiveMin(); + ScalarType stmax = itk::NumericTraits::max(); + ScalarType stmin = itk::NumericTraits::NonpositiveMin(); std::set existingTimePoints; ScalarType maximalTime = 0; // Needed for check of zero bounding boxes - mitk::ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; + ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); for (SetOfObjects::ConstIterator it = input->Begin(); it != input->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *timeGeometry = node->GetData()->GetUpdatedTimeGeometry(); if (timeGeometry != nullptr) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = timeGeometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for (i = 0; i < 8; ++i) { point = timeGeometry->GetCornerPointInWorld(i); if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large) pointscontainer->InsertElement(pointid++, point); else { itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node); } } try { // time bounds // iterate over all time steps // Attention: Objects with zero bounding box are not respected in time bound calculation for (TimeStepType i = 0; i < timeGeometry->CountTimeSteps(); i++) { // We must not use 'node->GetData()->GetGeometry(i)->GetSpacing()' here, as it returns the spacing // in its original space, which, in case of an image geometry, can have the values in different // order than in world space. For the further calculations, we need to have the spacing values // in world coordinate order (sag-cor-ax). Vector3D spacing; spacing.Fill(1.0); node->GetData()->GetGeometry(i)->IndexToWorld(spacing, spacing); for (int axis = 0; axis < 3; ++ axis) { ScalarType space = std::abs(spacing[axis]); if (space < minSpacing[axis]) { minSpacing[axis] = space; } } const auto curTimeBounds = timeGeometry->GetTimeBounds(i); if ((curTimeBounds[0] > stmin) && (curTimeBounds[0] < stmax)) { existingTimePoints.insert(curTimeBounds[0]); } if ((curTimeBounds[1] > maximalTime) && (curTimeBounds[1] < stmax)) { maximalTime = curTimeBounds[1]; } } } catch (itk::ExceptionObject &e) { MITK_ERROR << e << std::endl; } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); // compute the number of time steps if (existingTimePoints.empty()) // make sure that there is at least one time sliced geometry in the data storage { existingTimePoints.insert(0.0); maximalTime = 1.0; } ArbitraryTimeGeometry::Pointer timeGeometry = nullptr; if (result->GetPoints()->Size() > 0) { // Initialize a geometry of a single time step Geometry3D::Pointer geometry = Geometry3D::New(); geometry->Initialize(); // correct bounding-box (is now in mm, should be in index-coordinates) // according to spacing BoundingBox::BoundsArrayType bounds = result->GetBounds(); AffineTransform3D::OutputVectorType offset; for (int i = 0; i < 3; ++i) { offset[i] = bounds[i * 2]; bounds[i * 2] = 0.0; bounds[i * 2 + 1] = (bounds[i * 2 + 1] - offset[i]) / minSpacing[i]; } geometry->GetIndexToWorldTransform()->SetOffset(offset); geometry->SetBounds(bounds); geometry->SetSpacing(minSpacing); // Initialize the time sliced geometry auto tsIterator = existingTimePoints.cbegin(); auto tsPredecessor = tsIterator++; auto tsEnd = existingTimePoints.cend(); timeGeometry = ArbitraryTimeGeometry::New(); for (; tsIterator != tsEnd; ++tsIterator, ++tsPredecessor) { timeGeometry->AppendNewTimeStep(geometry, *tsPredecessor, *tsIterator); } timeGeometry->AppendNewTimeStep(geometry, *tsPredecessor, maximalTime); timeGeometry->Update(); } return timeGeometry.GetPointer(); } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeBoundingGeometry3D(const char *boolPropertyKey, - const mitk::BaseRenderer *renderer, - const char *boolPropertyKey2) const + const BaseRenderer *renderer, + const char *boolPropertyKey2) const { return this->ComputeBoundingGeometry3D(this->GetAll(), boolPropertyKey, renderer, boolPropertyKey2); } -mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeVisibleBoundingGeometry3D(const mitk::BaseRenderer *renderer, - const char *boolPropertyKey) +mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeVisibleBoundingGeometry3D(const BaseRenderer *renderer, + const char *boolPropertyKey) { return ComputeBoundingGeometry3D("visible", renderer, boolPropertyKey); } mitk::BoundingBox::Pointer mitk::DataStorage::ComputeBoundingBox(const char *boolPropertyKey, - const mitk::BaseRenderer *renderer, + const BaseRenderer *renderer, const char *boolPropertyKey2) { BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid = 0; Point3D point; // Needed for check of zero bounding boxes - mitk::ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; + ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != nullptr) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = geometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for (i = 0; i < 8; ++i) { point = geometry->GetCornerPointInWorld(i); if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large) pointscontainer->InsertElement(pointid++, point); else { itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node); } } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } mitk::TimeBounds mitk::DataStorage::ComputeTimeBounds(const char *boolPropertyKey, - const mitk::BaseRenderer *renderer, + const BaseRenderer *renderer, const char *boolPropertyKey2) { TimeBounds timeBounds; ScalarType stmin, stmax, cur; - stmin = itk::NumericTraits::NonpositiveMin(); - stmax = itk::NumericTraits::max(); + stmin = itk::NumericTraits::NonpositiveMin(); + stmax = itk::NumericTraits::max(); timeBounds[0] = stmax; timeBounds[1] = stmin; SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != nullptr) { const TimeBounds &curTimeBounds = geometry->GetTimeBounds(); cur = curTimeBounds[0]; // is it after -infinity, but before everything else that we found until now? if ((cur > stmin) && (cur < timeBounds[0])) timeBounds[0] = cur; cur = curTimeBounds[1]; // is it before infinity, but after everything else that we found until now? if ((cur < stmax) && (cur > timeBounds[1])) timeBounds[1] = cur; } } } if (!(timeBounds[0] < stmax)) { timeBounds[0] = stmin; timeBounds[1] = stmax; } return timeBounds; } void mitk::DataStorage::BlockNodeModifiedEvents(bool block) { m_BlockNodeModifiedEvents = block; } + +mitk::DataNode::Pointer mitk::FindTopmostVisibleNode(const DataStorage::SetOfObjects* nodes, + const Point3D worldposition, + const TimePointType timePoint, + const BaseRenderer* baseRender) +{ + DataNode::Pointer topLayerNode; + + if (nullptr == nodes) + return nullptr; + + int maxLayer = std::numeric_limits::min(); + + for (auto node : *nodes) + { + if (node.IsNull()) + continue; + + bool isHelperObject = false; + node->GetBoolProperty("helper object", isHelperObject); + + if (isHelperObject) + continue; + + auto data = node->GetData(); + + if (nullptr == data) + continue; + + auto geometry = data->GetGeometry(); + + if (nullptr == geometry || !geometry->IsInside(worldposition)) + continue; + + auto timeGeometry = data->GetUpdatedTimeGeometry(); + + if (timeGeometry == nullptr) + continue; + + if (!timeGeometry->IsValidTimePoint(timePoint)) + continue; + + int layer = 0; + + if (!node->GetIntProperty("layer", layer)) + continue; + + if (layer <= maxLayer) + continue; + + if (!node->IsVisible(baseRender)) + continue; + + topLayerNode = node; + maxLayer = layer; + } + + return topLayerNode; +} diff --git a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp index e7f4668682..1407d33712 100644 --- a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp @@ -1,976 +1,925 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDisplayInteractor.h" #include "mitkBaseRenderer.h" #include "mitkCameraController.h" #include "mitkInteractionPositionEvent.h" #include "mitkPropertyList.h" #include #include #include // level window #include "mitkLevelWindow.h" #include "mitkLevelWindowProperty.h" #include "mitkLine.h" #include "mitkNodePredicateDataType.h" #include "mitkStandaloneDataStorage.h" #include "vtkRenderWindowInteractor.h" // Rotation #include "mitkInteractionConst.h" #include "rotate_cursor.xpm" #include #include #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include "mitkStatusBar.h" #include void mitk::DisplayInteractor::Notify(InteractionEvent *interactionEvent, bool isHandled) { // to use the state machine pattern, // the event is passed to the state machine interface to be handled if (!isHandled || m_AlwaysReact) { this->HandleEvent(interactionEvent, nullptr); } } void mitk::DisplayInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("check_position_event", CheckPositionEvent); CONNECT_CONDITION("check_can_rotate", CheckRotationPossible); CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible); CONNECT_FUNCTION("init", Init); CONNECT_FUNCTION("move", Move); CONNECT_FUNCTION("zoom", Zoom); CONNECT_FUNCTION("scroll", Scroll); CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown); CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); CONNECT_FUNCTION("levelWindow", AdjustLevelWindow); CONNECT_FUNCTION("setCrosshair", SetCrosshair); CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar) CONNECT_FUNCTION("startRotation", StartRotation); CONNECT_FUNCTION("endRotation", EndRotation); CONNECT_FUNCTION("rotate", Rotate); CONNECT_FUNCTION("swivel", Swivel); } mitk::DisplayInteractor::DisplayInteractor() : m_IndexToSliceModifier(4), m_AutoRepeat(false), m_InvertScrollDirection(false), m_InvertZoomDirection(false), m_InvertMoveDirection(false), m_InvertLevelWindowDirection(false), m_AlwaysReact(false), m_ZoomFactor(2), m_LinkPlanes(true) { m_StartCoordinateInMM.Fill(0); m_LastDisplayCoordinate.Fill(0); m_LastCoordinateInMM.Fill(0); m_CurrentDisplayCoordinate.Fill(0); } mitk::DisplayInteractor::~DisplayInteractor() { } bool mitk::DisplayInteractor::CheckPositionEvent(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) { return false; } return true; } bool mitk::DisplayInteractor::CheckRotationPossible(const mitk::InteractionEvent *interactionEvent) { // Decide between moving and rotation slices. /* Detailed logic: 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Needs not even be counted or checked. 2. Inspect every other SliceNavigationController - calculate the line intersection of this SliceNavigationController's plane with our rendering plane - if there is NO interesection, ignore and continue - IF there is an intersection - check the mouse cursor's distance from that line. 0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in "locked" mode) 1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate 2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to rotate - if yes, we just push this line to the "other" lines and rotate it along - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to rotate */ const auto *posEvent = dynamic_cast(interactionEvent); if (posEvent == nullptr) return false; BaseRenderer *clickedRenderer = posEvent->GetSender(); const PlaneGeometry *ourViewportGeometry = (clickedRenderer->GetCurrentWorldPlaneGeometry()); if (!ourViewportGeometry) return false; Point3D cursorPosition = posEvent->GetPositionInWorld(); const auto spacing = ourViewportGeometry->GetSpacing(); const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY) Line3D intersectionLineWithGeometryToBeRotated; bool hitMultipleLines(false); m_SNCsToBeRotated.clear(); const double threshholdDistancePixels = 12.0; auto renWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; const PlaneGeometry *otherRenderersRenderPlane = snc->GetCurrentPlaneGeometry(); if (otherRenderersRenderPlane == nullptr) continue; // ignore, we don't see a plane // check if there is an intersection Line3D intersectionLine; // between rendered/clicked geometry and the one being analyzed if (!ourViewportGeometry->IntersectionLine(otherRenderersRenderPlane, intersectionLine)) { continue; // we ignore this plane, it's parallel to our plane } // check distance from intersection line const double distanceFromIntersectionLine = intersectionLine.Distance(cursorPosition) / spacing[snc->GetDefaultViewDirection()]; // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { anyOtherGeometry = otherRenderersRenderPlane; // we just take the last one, so overwrite each iteration (we just // need some crossing point) // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used if (m_LinkPlanes) { m_SNCsToBeRotated.push_back(snc); } } else // close to cursor { if (geometryToBeRotated == nullptr) // first one close to the cursor { geometryToBeRotated = otherRenderersRenderPlane; intersectionLineWithGeometryToBeRotated = intersectionLine; m_SNCsToBeRotated.push_back(snc); } else { // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane // together with the primary one // if different, DON'T rotate if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) && intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < mitk::eps) { m_SNCsToBeRotated.push_back(snc); } else { hitMultipleLines = true; } } } } bool moveSlices(true); if (geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !hitMultipleLines) { // assure all three are valid, so calculation of center of rotation can be done moveSlices = false; } // question in state machine is: "rotate?" if (moveSlices) // i.e. NOT rotate { return false; } else { // we DO have enough information for rotation m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project( cursorPosition); // remember where the last cursor position ON THE LINE has been observed if (anyOtherGeometry->IntersectionPoint( intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) // find center of rotation by intersection with any of the OTHER lines { return true; } else { return false; } } return false; } bool mitk::DisplayInteractor::CheckSwivelPossible(const mitk::InteractionEvent *interactionEvent) { const ScalarType ThresholdDistancePixels = 6.0; // Decide between moving and rotation: if we're close to the crossing // point of the planes, moving mode is entered, otherwise // rotation/swivel mode const auto *posEvent = dynamic_cast(interactionEvent); BaseRenderer *renderer = interactionEvent->GetSender(); if (!posEvent || !renderer) return false; const Point3D &cursor = posEvent->GetPositionInWorld(); m_SNCsToBeRotated.clear(); const PlaneGeometry *clickedGeometry(nullptr); const PlaneGeometry *otherGeometry1(nullptr); const PlaneGeometry *otherGeometry2(nullptr); auto renWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; // unsigned int slice = (*iter)->GetSlice()->GetPos(); // unsigned int time = (*iter)->GetTime()->GetPos(); const PlaneGeometry *planeGeometry = snc->GetCurrentPlaneGeometry(); if (!planeGeometry) continue; if (snc == renderer->GetSliceNavigationController()) { clickedGeometry = planeGeometry; m_SNCsToBeRotated.push_back(snc); } else { if (otherGeometry1 == nullptr) { otherGeometry1 = planeGeometry; } else { otherGeometry2 = planeGeometry; } if (m_LinkPlanes) { // If planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } } mitk::Line3D line; mitk::Point3D point; if ((clickedGeometry != nullptr) && (otherGeometry1 != nullptr) && (otherGeometry2 != nullptr) && clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point)) { m_CenterOfRotation = point; if (m_CenterOfRotation.EuclideanDistanceTo(cursor) < ThresholdDistancePixels) { return false; } else { m_ReferenceCursor = posEvent->GetPointerPositionOnScreen(); // Get main axes of rotation plane and store it for rotation step m_RotationPlaneNormal = clickedGeometry->GetNormal(); ScalarType xVector[] = {1.0, 0.0, 0.0}; ScalarType yVector[] = {0.0, 1.0, 0.0}; clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector); clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector); m_RotationPlaneNormal.Normalize(); m_RotationPlaneXVector.Normalize(); m_RotationPlaneYVector.Normalize(); m_PreviousRotationAxis.Fill(0.0); m_PreviousRotationAxis[2] = 1.0; m_PreviousRotationAngle = 0.0; return true; } } else { return false; } return false; } void mitk::DisplayInteractor::Init(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_CurrentDisplayCoordinate = m_LastDisplayCoordinate; positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM); m_LastCoordinateInMM = m_StartCoordinateInMM; } void mitk::DisplayInteractor::Move(StateMachineAction *, InteractionEvent *interactionEvent) { BaseRenderer *sender = interactionEvent->GetSender(); auto *positionEvent = static_cast(interactionEvent); float invertModifier = -1.0; if (m_InvertMoveDirection) { invertModifier = 1.0; } // perform translation Vector2D moveVector = (positionEvent->GetPointerPositionOnScreen() - m_LastDisplayCoordinate) * invertModifier; moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); sender->GetCameraController()->MoveBy(moveVector); sender->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } void mitk::DisplayInteractor::SetCrosshair(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { const BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto renWindows = sender->GetRenderingManager()->GetAllRegisteredRenderWindows(); auto *positionEvent = static_cast(interactionEvent); Point3D pos = positionEvent->GetPositionInWorld(); for (auto renWin : renWindows) { if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sender->GetRenderWindow()) BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->SelectSliceByPoint(pos); } } void mitk::DisplayInteractor::Zoom(StateMachineAction *, InteractionEvent *interactionEvent) { const BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto *positionEvent = static_cast(interactionEvent); float factor = 1.0; float distance = 0; if (m_ZoomDirection == "updown") { distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertZoomDirection) { distance *= -1.0; } // set zooming speed if (distance < 0.0) { factor = 1.0 / m_ZoomFactor; } else if (distance > 0.0) { factor = 1.0 * m_ZoomFactor; } if (factor != 1.0) { sender->GetCameraController()->Zoom(factor, m_StartCoordinateInMM); sender->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); } m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } void mitk::DisplayInteractor::Scroll(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = static_cast(interactionEvent); mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (sliceNaviController) { int delta = 0; // Scrolling direction if (m_ScrollDirection == "updown") { delta = static_cast(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]); } else { delta = static_cast(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]); } if (m_InvertScrollDirection) { delta *= -1; } // Set how many pixels the mouse has to be moved to scroll one slice // if we moved less than 'm_IndexToSliceModifier' pixels slice ONE slice only if (delta > 0 && delta < m_IndexToSliceModifier) { delta = m_IndexToSliceModifier; } else if (delta < 0 && delta > -m_IndexToSliceModifier) { delta = -m_IndexToSliceModifier; } delta /= m_IndexToSliceModifier; int newPos = sliceNaviController->GetSlice()->GetPos() + delta; // if auto repeat is on, start at first slice if you reach the last slice and vice versa int maxSlices = sliceNaviController->GetSlice()->GetSteps(); if (m_AutoRepeat) { while (newPos < 0) { newPos += maxSlices; } while (newPos >= maxSlices) { newPos -= maxSlices; } } else { // if the new slice is below 0 we still show slice 0 // due to the stepper using unsigned int we have to do this ourselves if (newPos < 1) { newPos = 0; } } // set the new position sliceNaviController->GetSlice()->SetPos(newPos); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } } void mitk::DisplayInteractor::ScrollOneDown(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper *stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Next(); } } void mitk::DisplayInteractor::ScrollOneUp(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper *stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Previous(); } } void mitk::DisplayInteractor::AdjustLevelWindow(StateMachineAction *, InteractionEvent *interactionEvent) { BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto *positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // search for active image mitk::DataStorage::Pointer storage = sender->GetDataStorage(); mitk::DataNode::Pointer node = nullptr; mitk::DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(mitk::NodePredicateDataType::New("Image")); for (unsigned int i = 0; i < allImageNodes->size(); i++) { bool isActiveImage = false; bool propFound = allImageNodes->at(i)->GetBoolProperty("imageForLevelWindow", isActiveImage); if (propFound && isActiveImage) { node = allImageNodes->at(i); continue; } } if (node.IsNull()) { node = storage->GetNode(mitk::NodePredicateDataType::New("Image")); } if (node.IsNull()) { return; } mitk::LevelWindow lv = mitk::LevelWindow(); node->GetLevelWindow(lv); ScalarType level = lv.GetLevel(); ScalarType window = lv.GetWindow(); int levelIndex = 0; int windowIndex = 1; if (m_LevelDirection != "leftright") { levelIndex = 1; windowIndex = 0; } int directionModifier = 1; if (m_InvertLevelWindowDirection) { directionModifier = -1; } // calculate adjustments from mouse movements level += (m_CurrentDisplayCoordinate[levelIndex] - m_LastDisplayCoordinate[levelIndex]) * static_cast(2) * directionModifier; window += (m_CurrentDisplayCoordinate[windowIndex] - m_LastDisplayCoordinate[windowIndex]) * static_cast(2) * directionModifier; lv.SetLevelWindow(level, window); dynamic_cast(node->GetProperty("levelwindow"))->SetLevelWindow(lv); sender->GetRenderingManager()->RequestUpdateAll(); } void mitk::DisplayInteractor::StartRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->SetMouseCursor(rotate_cursor_xpm, 0, 0); } void mitk::DisplayInteractor::EndRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->ResetMouseCursor(); } void mitk::DisplayInteractor::Rotate(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto *posEvent = dynamic_cast(event); if (posEvent == nullptr) return; Point3D cursor = posEvent->GetPositionInWorld(); Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation; Vector3D toCursor = cursor - m_CenterOfRotation; // cross product: | A x B | = |A| * |B| * sin(angle) Vector3D axisOfRotation; vnl_vector_fixed vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector()); axisOfRotation.SetVnlVector(vnlDirection); // scalar product: A * B = |A| * |B| * cos(angle) // tan = sin / cos ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected)); angle *= 180.0 / vnl_math::pi; m_LastCursorPosition = cursor; // create RotationOperation and apply to all SNCs that should be rotated RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle); // iterate the OTHER slice navigation controllers: these are filled in DoDecideBetweenRotationAndSliceSelection for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&rotationOperation); (*iter)->SendCreatedWorldGeometryUpdate(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayInteractor::Swivel(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto *posEvent = dynamic_cast(event); if (!posEvent) return; // Determine relative mouse movement projected onto world space Point2D cursor = posEvent->GetPointerPositionOnScreen(); Vector2D relativeCursor = cursor - m_ReferenceCursor; Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1]; // Determine rotation axis (perpendicular to rotation plane and cursor // movement) Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis); ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0; // Restore the initial plane pose by undoing the previous rotation // operation RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle); SNCVector::iterator iter; for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&op); (*iter)->SendCreatedWorldGeometryUpdate(); } } // Apply new rotation operation to all relevant SNCs RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle); for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { // Retrieve the TimeGeometry of this SliceNavigationController TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; // Execute the new rotation timeGeometry->ExecuteOperation(&op2); // Notify listeners (*iter)->SendCreatedWorldGeometryUpdate(); } } m_PreviousRotationAxis = rotationAxis; m_PreviousRotationAngle = rotationAngle; RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::DisplayInteractor::UpdateStatusbar(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto *posEvent = dynamic_cast(event); if (!posEvent) return; std::string statusText; TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); - mitk::DataStorage::SetOfObjects::ConstPointer nodes = - posEvent->GetSender()->GetDataStorage()->GetSubset(isImageData).GetPointer(); + mitk::BaseRenderer* baseRenderer = posEvent->GetSender(); + mitk::DataStorage::SetOfObjects::ConstPointer nodes = baseRenderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); // posEvent->GetPositionInWorld() would return the world position at the // time of initiating the interaction. However, we need to update the // status bar with the position after changing slice. Therefore, we // translate the same display position with the renderer again to // get the new world position. Point3D worldposition; event->GetSender()->DisplayToWorld(posEvent->GetPointerPositionOnScreen(), worldposition); mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; int component = 0; - node = this->GetTopLayerNode(nodes, worldposition, posEvent->GetSender()); + node = FindTopmostVisibleNode(nodes, worldposition, baseRenderer->GetTime(), baseRenderer); if (node.IsNotNull()) { bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { - mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = - posEvent->GetSender()->GetDataStorage()->GetSources(node, nullptr, true); + mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = baseRenderer->GetDataStorage()->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { - topSourceNode = this->GetTopLayerNode(sourcenodes, worldposition, posEvent->GetSender()); + topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, worldposition, baseRenderer->GetTime(), baseRenderer); } if (topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } // get the position and gray value from the image and build up status bar text auto statusBar = StatusBar::GetInstance(); if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(worldposition, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); - statusBar->DisplayImageInfo(worldposition, p, posEvent->GetSender()->GetTime(), pixelValue.c_str()); + statusBar->DisplayImageInfo(worldposition, p, baseRenderer->GetTime(), pixelValue.c_str()); } else if ( pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR ) { std::string pixelValue = "See ODF Details view. "; - statusBar->DisplayImageInfo(worldposition, p, posEvent->GetSender()->GetTime(), pixelValue.c_str()); + statusBar->DisplayImageInfo(worldposition, p, baseRenderer->GetTime(), pixelValue.c_str()); } else { mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, - image3D->GetVolumeData(posEvent->GetSender()->GetTimeStep()), + image3D->GetVolumeData(baseRenderer->GetTimeStep()), p, pixelValue, component); - statusBar->DisplayImageInfo(worldposition, p, posEvent->GetSender()->GetTime(), pixelValue); + statusBar->DisplayImageInfo(worldposition, p, baseRenderer->GetTime(), pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } void mitk::DisplayInteractor::ConfigurationChanged() { mitk::PropertyList::Pointer properties = GetAttributes(); // auto repeat std::string strAutoRepeat = ""; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } else { m_AutoRepeat = false; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } else { m_IndexToSliceModifier = 4; } // scroll direction if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection)) { m_ScrollDirection = "updown"; } m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false); // zoom direction if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection)) { m_ZoomDirection = "updown"; } m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false); m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false); if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection)) { m_LevelDirection = "leftright"; } m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false); // coupled rotation std::string strCoupled = ""; if (properties->GetStringProperty("coupled", strCoupled)) { if (strCoupled == "true") m_LinkPlanes = true; else m_LinkPlanes = false; } // zoom factor std::string strZoomFactor = ""; properties->GetStringProperty("zoomFactor", strZoomFactor); m_ZoomFactor = .05; if (atoi(strZoomFactor.c_str()) > 0) { m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0); } // allwaysReact std::string strAlwaysReact = ""; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } else { m_AlwaysReact = false; } } else { m_AlwaysReact = false; } } bool mitk::DisplayInteractor::FilterEvents(InteractionEvent *interactionEvent, DataNode * /*dataNode*/) { if (interactionEvent->GetSender() == nullptr) return false; if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D) return false; return true; } bool mitk::DisplayInteractor::GetBoolProperty(mitk::PropertyList::Pointer propertyList, const char *propertyName, bool defaultValue) { std::string valueAsString; if (!propertyList->GetStringProperty(propertyName, valueAsString)) { return defaultValue; } else { if (valueAsString == "true") { return true; } else { return false; } } } - -mitk::DataNode::Pointer mitk::DisplayInteractor::GetTopLayerNode(DataStorage::SetOfObjects::ConstPointer nodes, - Point3D worldposition, - BaseRenderer* baseRender) -{ - DataNode::Pointer topLayerNode; - - if (nodes.IsNull()) - return nullptr; - - int maxLayer = std::numeric_limits::min(); - - for (auto node : *nodes) - { - if (node.IsNull()) - continue; - - bool isHelperObject = false; - node->GetBoolProperty("helper object", isHelperObject); - - if (isHelperObject) - continue; - - auto data = node->GetData(); - - if (nullptr == data) - continue; - - auto geometry = data->GetGeometry(); - - if (nullptr == geometry || !geometry->IsInside(worldposition)) - continue; - - int layer = 0; - - if (!node->GetIntProperty("layer", layer)) - continue; - - if (layer <= maxLayer) - continue; - - if (!node->IsVisible(baseRender)) - continue; - - topLayerNode = node; - maxLayer = layer; - } - - return topLayerNode; -} diff --git a/Modules/QtWidgets/include/QmitkStdMultiWidget.h b/Modules/QtWidgets/include/QmitkStdMultiWidget.h index 34d26f65f7..a5c2a2cd7e 100644 --- a/Modules/QtWidgets/include/QmitkStdMultiWidget.h +++ b/Modules/QtWidgets/include/QmitkStdMultiWidget.h @@ -1,433 +1,431 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkStdMultiWidget_h #define QmitkStdMultiWidget_h #include "MitkQtWidgetsExports.h" #include #include #include #include #include #include #include #include #include class QHBoxLayout; class QVBoxLayout; class QGridLayout; class QSpacerItem; class QmitkLevelWindowWidget; class QmitkRenderWindow; class vtkCornerAnnotation; class vtkMitkRectangleProp; namespace mitk { class RenderingManager; } /// \ingroup QmitkModule class MITKQTWIDGETS_EXPORT QmitkStdMultiWidget : public QWidget { Q_OBJECT public: QmitkStdMultiWidget( QWidget *parent = nullptr, Qt::WindowFlags f = nullptr, mitk::RenderingManager *renderingManager = nullptr, mitk::BaseRenderer::RenderingMode::Type renderingMode = mitk::BaseRenderer::RenderingMode::Standard, const QString &name = "stdmulti"); ~QmitkStdMultiWidget() override; mitk::SliceNavigationController *GetTimeNavigationController(); void RequestUpdate(); void ForceImmediateUpdate(); mitk::MouseModeSwitcher *GetMouseModeSwitcher(); QmitkRenderWindow *GetRenderWindow1() const; QmitkRenderWindow *GetRenderWindow2() const; QmitkRenderWindow *GetRenderWindow3() const; QmitkRenderWindow *GetRenderWindow4() const; const mitk::Point3D GetCrossPosition() const; void EnablePositionTracking(); void DisablePositionTracking(); int GetLayout() const; bool GetGradientBackgroundFlag() const; /*! \brief Access node of widget plane 1 \return DataNode holding widget plane 1 */ mitk::DataNode::Pointer GetWidgetPlane1(); /*! \brief Access node of widget plane 2 \return DataNode holding widget plane 2 */ mitk::DataNode::Pointer GetWidgetPlane2(); /*! \brief Access node of widget plane 3 \return DataNode holding widget plane 3 */ mitk::DataNode::Pointer GetWidgetPlane3(); /*! \brief Convenience method to access node of widget planes \param id number of widget plane to be returned \return DataNode holding widget plane 3 */ mitk::DataNode::Pointer GetWidgetPlane(int id); bool IsColoredRectanglesEnabled() const; bool IsDepartmentLogoEnabled() const; void InitializeWidget(); /// called when the StdMultiWidget is closed to remove the 3 widget planes and the helper node from the DataStorage void RemovePlanesFromDataStorage(); void AddPlanesToDataStorage(); void SetDataStorage(mitk::DataStorage *ds); /** \brief Listener to the CrosshairPositionEvent Ensures the CrosshairPositionEvent is handled only once and at the end of the Qt-Event loop */ void HandleCrosshairPositionEvent(); /// activate Menu Widget. true: activated, false: deactivated void ActivateMenuWidget(bool state); bool IsMenuWidgetEnabled() const; void SetCornerAnnotationVisibility(bool visibility); bool IsCornerAnnotationVisible(void) const; protected: void UpdateAllWidgets(); void HideAllWidgetToolbars(); - mitk::DataNode::Pointer GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes); - public slots: /// Receives the signal from HandleCrosshairPositionEvent, executes the StatusBar update void HandleCrosshairPositionEventDelayed(); void changeLayoutTo2DImagesUp(); void changeLayoutTo2DImagesLeft(); void changeLayoutToDefault(); void changeLayoutToBig3D(); void changeLayoutToWidget1(); void changeLayoutToWidget2(); void changeLayoutToWidget3(); void changeLayoutToRowWidget3And4(); void changeLayoutToColumnWidget3And4(); void changeLayoutToRowWidgetSmall3andBig4(); void changeLayoutToSmallUpperWidget2Big3and4(); void changeLayoutTo2x2Dand3DWidget(); void changeLayoutToLeft2Dand3DRight2D(); /** Changes the layout to one 2D window up and a 3D window down. * The 2D window can be defined by its id (1-3). If MITK default * settings were not changed, 1 is axial, 2 is sagittal and 3 is coronal. */ void changeLayoutTo2DUpAnd3DDown(unsigned int id2Dwindow = 2); void Fit(); void InitPositionTracking(); void AddDisplayPlaneSubTree(); void EnableStandardLevelWindow(); void DisableStandardLevelWindow(); bool InitializeStandardViews(const mitk::Geometry3D *geometry); void wheelEvent(QWheelEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void moveEvent(QMoveEvent *e) override; void EnsureDisplayContainsPoint(mitk::BaseRenderer *renderer, const mitk::Point3D &p); void MoveCrossToPosition(const mitk::Point3D &newPosition); // void EnableNavigationControllerEventListening(); // void DisableNavigationControllerEventListening(); void EnableGradientBackground(); void DisableGradientBackground(); void EnableDepartmentLogo(); void DisableDepartmentLogo(); void EnableColoredRectangles(); void DisableColoredRectangles(); void SetWidgetPlaneVisibility(const char *widgetName, bool visible, mitk::BaseRenderer *renderer = nullptr); void SetWidgetPlanesVisibility(bool visible, mitk::BaseRenderer *renderer = nullptr); void SetWidgetPlanesLocked(bool locked); void SetWidgetPlanesRotationLocked(bool locked); void SetWidgetPlanesRotationLinked(bool link); void SetWidgetPlaneMode(int mode); void SetGradientBackgroundColors(const mitk::Color &upper, const mitk::Color &lower); void SetDepartmentLogo(const char *path); void SetWidgetPlaneModeToSlicing(bool activate); void SetWidgetPlaneModeToRotation(bool activate); void SetWidgetPlaneModeToSwivel(bool activate); void OnLayoutDesignChanged(int layoutDesignIndex); void ResetCrosshair(); signals: void LeftMouseClicked(mitk::Point3D pointValue); void WheelMoved(QWheelEvent *); void WidgetPlanesRotationLinked(bool); void WidgetPlanesRotationEnabled(bool); void ViewsInitialized(); void WidgetPlaneModeSlicing(bool); void WidgetPlaneModeRotation(bool); void WidgetPlaneModeSwivel(bool); void WidgetPlaneModeChange(int); void WidgetNotifyNewCrossHairMode(int); void Moved(); public: /** Define RenderWindow (public)*/ QmitkRenderWindow *mitkWidget1; QmitkRenderWindow *mitkWidget2; QmitkRenderWindow *mitkWidget3; QmitkRenderWindow *mitkWidget4; QmitkLevelWindowWidget *levelWindowWidget; /********************************/ enum { PLANE_MODE_SLICING = 0, PLANE_MODE_ROTATION, PLANE_MODE_SWIVEL }; enum { LAYOUT_DEFAULT = 0, LAYOUT_2D_IMAGES_UP, LAYOUT_2D_IMAGES_LEFT, LAYOUT_BIG_3D, LAYOUT_WIDGET1, LAYOUT_WIDGET2, LAYOUT_WIDGET3, LAYOUT_2X_2D_AND_3D_WIDGET, LAYOUT_ROW_WIDGET_3_AND_4, LAYOUT_COLUMN_WIDGET_3_AND_4, LAYOUT_ROW_WIDGET_SMALL3_AND_BIG4, LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4, LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET, LAYOUT_2D_UP_AND_3D_DOWN }; enum { AXIAL, SAGITTAL, CORONAL, THREE_D }; /** * @brief SetCornerAnnotation Create a corner annotation for a widget. * @param text The text of the annotation. * @param color The color. * @param widgetNumber The widget (0-3). */ void SetDecorationProperties(std::string text, mitk::Color color, int widgetNumber); /** * @brief GetRenderWindow convinience method to get a widget. * @param number of the widget (0-3) * @return The renderwindow widget. */ QmitkRenderWindow *GetRenderWindow(unsigned int number); /** * @brief SetGradientBackgroundColorForRenderWindow background for a widget. * * If two different input colors are, a gradient background is generated. * * @param upper Upper color of the gradient background. * @param lower Lower color of the gradient background. * @param widgetNumber The widget (0-3). */ void SetGradientBackgroundColorForRenderWindow(const mitk::Color &upper, const mitk::Color &lower, unsigned int widgetNumber); /** * @brief GetDecorationColorForWidget Get the color for annotation, crosshair and rectangle. * @param widgetNumber Number of the renderwindow (0-3). * @return Color in mitk format. */ mitk::Color GetDecorationColor(unsigned int widgetNumber); /** * @brief SetDecorationColor Set the color of the decoration of the 4 widgets. * * This is used to color the frame of the renderwindow and the corner annatation. * For the first 3 widgets, this color is a property of the helper object nodes * which contain the respective plane geometry. For widget 4, this is a member, * since there is no data node for this widget. */ void SetDecorationColor(unsigned int widgetNumber, mitk::Color color); /** * @brief GetCornerAnnotationText Getter for corner annotation text. * @param widgetNumber the widget number (0-3). * @return The text in the corner annotation. */ std::string GetCornerAnnotationText(unsigned int widgetNumber); /** * @brief GetGradientColors Getter for gradientbackground colors. * @param widgetNumber the widget number (0-3). * @return A pair of colors. First: upper, second: lower. */ std::pair GetGradientColors(unsigned int widgetNumber); protected: QHBoxLayout *QmitkStdMultiWidgetLayout; int m_Layout; int m_PlaneMode; mitk::RenderingManager *m_RenderingManager; mitk::LogoAnnotation::Pointer m_LogoRendering; bool m_GradientBackgroundFlag; mitk::MouseModeSwitcher::Pointer m_MouseModeSwitcher; mitk::SliceNavigationController *m_TimeNavigationController; mitk::DataStorage::Pointer m_DataStorage; /** * @brief m_PlaneNode1 the 3 helper objects which contain the plane geometry. */ mitk::DataNode::Pointer m_PlaneNode1; mitk::DataNode::Pointer m_PlaneNode2; mitk::DataNode::Pointer m_PlaneNode3; /** * @brief m_ParentNodeForGeometryPlanes This helper object is added to the datastorage * and contains the 3 planes for displaying the image geometry (crosshair and 3D planes). */ mitk::DataNode::Pointer m_ParentNodeForGeometryPlanes; /** * @brief m_DecorationColorWidget4 color for annotation and rectangle of widget 4. * * For other widgets1-3, the color is a property of the respective data node. * There is no node for widget 4, hence, we need an extra member. */ mitk::Color m_DecorationColorWidget4; /** * @brief m_GradientBackgroundColors Contains the colors of the gradient background. * */ std::pair m_GradientBackgroundColors[4]; QSplitter *m_MainSplit; QSplitter *m_LayoutSplit; QSplitter *m_SubSplit1; QSplitter *m_SubSplit2; QWidget *mitkWidget1Container; QWidget *mitkWidget2Container; QWidget *mitkWidget3Container; QWidget *mitkWidget4Container; vtkSmartPointer m_CornerAnnotations[4]; vtkSmartPointer m_RectangleProps[4]; bool m_PendingCrosshairPositionEvent; bool m_CrosshairNavigationEnabled; /** * @brief CreateCornerAnnotation helper method to create a corner annotation. * @param text of the annotation. * @param color of the annotation. * @return the complete CornerAnnotation. */ vtkSmartPointer CreateCornerAnnotation(std::string text, mitk::Color color); /** * @brief FillGradientBackgroundWithBlack Internal helper method to initialize the * gradient background colors with black. */ void FillGradientBackgroundWithBlack(); }; #endif /*QmitkStdMultiWidget_h*/ diff --git a/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp b/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp index b83d16e973..5a7ce4e17e 100644 --- a/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp @@ -1,2065 +1,2033 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #define SMW_INFO MITK_INFO("widget.stdmulti") #include "QmitkStdMultiWidget.h" #include #include #include #include #include #include #include #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QmitkStdMultiWidget::QmitkStdMultiWidget(QWidget *parent, Qt::WindowFlags f, mitk::RenderingManager *renderingManager, mitk::BaseRenderer::RenderingMode::Type renderingMode, const QString &name) : QWidget(parent, f), mitkWidget1(nullptr), mitkWidget2(nullptr), mitkWidget3(nullptr), mitkWidget4(nullptr), levelWindowWidget(nullptr), QmitkStdMultiWidgetLayout(nullptr), m_Layout(LAYOUT_DEFAULT), m_PlaneMode(PLANE_MODE_SLICING), m_RenderingManager(renderingManager), m_GradientBackgroundFlag(true), m_TimeNavigationController(nullptr), m_MainSplit(nullptr), m_LayoutSplit(nullptr), m_SubSplit1(nullptr), m_SubSplit2(nullptr), mitkWidget1Container(nullptr), mitkWidget2Container(nullptr), mitkWidget3Container(nullptr), mitkWidget4Container(nullptr), m_PendingCrosshairPositionEvent(false), m_CrosshairNavigationEnabled(false) { /****************************************************** * Use the global RenderingManager if none was specified * ****************************************************/ if (m_RenderingManager == nullptr) { m_RenderingManager = mitk::RenderingManager::GetInstance(); } m_TimeNavigationController = m_RenderingManager->GetTimeNavigationController(); /*******************************/ // Create Widget manually /*******************************/ // create Layouts QmitkStdMultiWidgetLayout = new QHBoxLayout(this); QmitkStdMultiWidgetLayout->setContentsMargins(0, 0, 0, 0); // Set Layout to widget this->setLayout(QmitkStdMultiWidgetLayout); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(Qt::Vertical, m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // creae Widget Container mitkWidget1Container = new QWidget(m_SubSplit1); mitkWidget2Container = new QWidget(m_SubSplit1); mitkWidget3Container = new QWidget(m_SubSplit2); mitkWidget4Container = new QWidget(m_SubSplit2); mitkWidget1Container->setContentsMargins(0, 0, 0, 0); mitkWidget2Container->setContentsMargins(0, 0, 0, 0); mitkWidget3Container->setContentsMargins(0, 0, 0, 0); mitkWidget4Container->setContentsMargins(0, 0, 0, 0); // create Widget Layout QHBoxLayout *mitkWidgetLayout1 = new QHBoxLayout(mitkWidget1Container); QHBoxLayout *mitkWidgetLayout2 = new QHBoxLayout(mitkWidget2Container); QHBoxLayout *mitkWidgetLayout3 = new QHBoxLayout(mitkWidget3Container); QHBoxLayout *mitkWidgetLayout4 = new QHBoxLayout(mitkWidget4Container); m_CornerAnnotations[0] = vtkSmartPointer::New(); m_CornerAnnotations[1] = vtkSmartPointer::New(); m_CornerAnnotations[2] = vtkSmartPointer::New(); m_CornerAnnotations[3] = vtkSmartPointer::New(); m_RectangleProps[0] = vtkSmartPointer::New(); m_RectangleProps[1] = vtkSmartPointer::New(); m_RectangleProps[2] = vtkSmartPointer::New(); m_RectangleProps[3] = vtkSmartPointer::New(); mitkWidgetLayout1->setMargin(0); mitkWidgetLayout2->setMargin(0); mitkWidgetLayout3->setMargin(0); mitkWidgetLayout4->setMargin(0); // set Layout to Widget Container mitkWidget1Container->setLayout(mitkWidgetLayout1); mitkWidget2Container->setLayout(mitkWidgetLayout2); mitkWidget3Container->setLayout(mitkWidgetLayout3); mitkWidget4Container->setLayout(mitkWidgetLayout4); // set SizePolicy mitkWidget1Container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mitkWidget2Container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mitkWidget3Container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mitkWidget4Container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // insert Widget Container into the splitters m_SubSplit1->addWidget(mitkWidget1Container); m_SubSplit1->addWidget(mitkWidget2Container); m_SubSplit2->addWidget(mitkWidget3Container); m_SubSplit2->addWidget(mitkWidget4Container); // Create RenderWindows 1 mitkWidget1 = new QmitkRenderWindow(mitkWidget1Container, name + ".widget1", nullptr, m_RenderingManager, renderingMode); mitkWidget1->SetLayoutIndex(AXIAL); mitkWidgetLayout1->addWidget(mitkWidget1); // Create RenderWindows 2 mitkWidget2 = new QmitkRenderWindow(mitkWidget2Container, name + ".widget2", nullptr, m_RenderingManager, renderingMode); mitkWidget2->setEnabled(true); mitkWidget2->SetLayoutIndex(SAGITTAL); mitkWidgetLayout2->addWidget(mitkWidget2); // Create RenderWindows 3 mitkWidget3 = new QmitkRenderWindow(mitkWidget3Container, name + ".widget3", nullptr, m_RenderingManager, renderingMode); mitkWidget3->SetLayoutIndex(CORONAL); mitkWidgetLayout3->addWidget(mitkWidget3); // Create RenderWindows 4 mitkWidget4 = new QmitkRenderWindow(mitkWidget4Container, name + ".widget4", nullptr, m_RenderingManager, renderingMode); mitkWidget4->SetLayoutIndex(THREE_D); mitkWidgetLayout4->addWidget(mitkWidget4); // create SignalSlot Connection connect(mitkWidget1, SIGNAL(SignalLayoutDesignChanged(int)), this, SLOT(OnLayoutDesignChanged(int))); connect(mitkWidget1, SIGNAL(ResetView()), this, SLOT(ResetCrosshair())); connect(mitkWidget1, SIGNAL(ChangeCrosshairRotationMode(int)), this, SLOT(SetWidgetPlaneMode(int))); connect(this, SIGNAL(WidgetNotifyNewCrossHairMode(int)), mitkWidget1, SLOT(OnWidgetPlaneModeChanged(int))); connect(mitkWidget2, SIGNAL(SignalLayoutDesignChanged(int)), this, SLOT(OnLayoutDesignChanged(int))); connect(mitkWidget2, SIGNAL(ResetView()), this, SLOT(ResetCrosshair())); connect(mitkWidget2, SIGNAL(ChangeCrosshairRotationMode(int)), this, SLOT(SetWidgetPlaneMode(int))); connect(this, SIGNAL(WidgetNotifyNewCrossHairMode(int)), mitkWidget2, SLOT(OnWidgetPlaneModeChanged(int))); connect(mitkWidget3, SIGNAL(SignalLayoutDesignChanged(int)), this, SLOT(OnLayoutDesignChanged(int))); connect(mitkWidget3, SIGNAL(ResetView()), this, SLOT(ResetCrosshair())); connect(mitkWidget3, SIGNAL(ChangeCrosshairRotationMode(int)), this, SLOT(SetWidgetPlaneMode(int))); connect(this, SIGNAL(WidgetNotifyNewCrossHairMode(int)), mitkWidget3, SLOT(OnWidgetPlaneModeChanged(int))); connect(mitkWidget4, SIGNAL(SignalLayoutDesignChanged(int)), this, SLOT(OnLayoutDesignChanged(int))); connect(mitkWidget4, SIGNAL(ResetView()), this, SLOT(ResetCrosshair())); connect(mitkWidget4, SIGNAL(ChangeCrosshairRotationMode(int)), this, SLOT(SetWidgetPlaneMode(int))); connect(this, SIGNAL(WidgetNotifyNewCrossHairMode(int)), mitkWidget4, SLOT(OnWidgetPlaneModeChanged(int))); // Create Level Window Widget levelWindowWidget = new QmitkLevelWindowWidget(m_MainSplit); // this levelWindowWidget->setObjectName(QString::fromUtf8("levelWindowWidget")); QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(levelWindowWidget->sizePolicy().hasHeightForWidth()); levelWindowWidget->setSizePolicy(sizePolicy); levelWindowWidget->setMaximumWidth(50); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // show mainSplitt and add to Layout m_MainSplit->show(); // resize Image. this->resize(QSize(364, 477).expandedTo(minimumSizeHint())); // Initialize the widgets. this->InitializeWidget(); // Activate Widget Menu this->ActivateMenuWidget(true); } void QmitkStdMultiWidget::InitializeWidget() { // Make all black and overwrite renderwindow 4 this->FillGradientBackgroundWithBlack(); // This is #191919 in hex float tmp1[3] = {0.098f, 0.098f, 0.098f}; // This is #7F7F7F in hex float tmp2[3] = {0.498f, 0.498f, 0.498f}; m_GradientBackgroundColors[3] = std::make_pair(mitk::Color(tmp1), mitk::Color(tmp2)); // Yellow is default color for widget4 m_DecorationColorWidget4[0] = 1.0f; m_DecorationColorWidget4[1] = 1.0f; m_DecorationColorWidget4[2] = 0.0f; // transfer colors in WorldGeometry-Nodes of the associated Renderer mitk::IntProperty::Pointer layer; // of widget 1 m_PlaneNode1 = mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode1->SetColor(GetDecorationColor(0)); layer = mitk::IntProperty::New(1000); m_PlaneNode1->SetProperty("layer", layer); // ... of widget 2 m_PlaneNode2 = mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode2->SetColor(GetDecorationColor(1)); layer = mitk::IntProperty::New(1000); m_PlaneNode2->SetProperty("layer", layer); // ... of widget 3 m_PlaneNode3 = mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode3->SetColor(GetDecorationColor(2)); layer = mitk::IntProperty::New(1000); m_PlaneNode3->SetProperty("layer", layer); // The parent node m_ParentNodeForGeometryPlanes = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->GetCurrentWorldPlaneGeometryNode(); layer = mitk::IntProperty::New(1000); m_ParentNodeForGeometryPlanes->SetProperty("layer", layer); mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->SetMapperID(mitk::BaseRenderer::Standard3D); // Set plane mode (slicing/rotation behavior) to slicing (default) m_PlaneMode = PLANE_MODE_SLICING; // Set default view directions for SNCs mitkWidget1->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); mitkWidget2->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Sagittal); mitkWidget3->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Frontal); mitkWidget4->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Original); SetDecorationProperties("Axial", GetDecorationColor(0), 0); SetDecorationProperties("Sagittal", GetDecorationColor(1), 1); SetDecorationProperties("Coronal", GetDecorationColor(2), 2); SetDecorationProperties("3D", GetDecorationColor(3), 3); // connect to the "time navigation controller": send time via sliceNavigationControllers m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget1->GetSliceNavigationController(), false); m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget2->GetSliceNavigationController(), false); m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget3->GetSliceNavigationController(), false); m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget4->GetSliceNavigationController(), false); mitkWidget1->GetSliceNavigationController()->ConnectGeometrySendEvent( mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())); // reverse connection between sliceNavigationControllers and m_TimeNavigationController mitkWidget1->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); mitkWidget2->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); mitkWidget3->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); // mitkWidget4->GetSliceNavigationController() // ->ConnectGeometryTimeEvent(m_TimeNavigationController, false); m_MouseModeSwitcher = mitk::MouseModeSwitcher::New(); // setup the department logo rendering m_LogoRendering = mitk::LogoAnnotation::New(); mitk::BaseRenderer::Pointer renderer4 = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow()); m_LogoRendering->SetOpacity(0.5); mitk::Point2D offset; offset.Fill(0.03); m_LogoRendering->SetOffsetVector(offset); m_LogoRendering->SetRelativeSize(0.25); m_LogoRendering->SetCornerPosition(1); SetDepartmentLogo(":/org.mitk.gui.qt.stdmultiwidgeteditor/defaultWatermark.png"); mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_LogoRendering.GetPointer(), renderer4); } void QmitkStdMultiWidget::FillGradientBackgroundWithBlack() { // We have 4 widgets and ... for (unsigned int i = 0; i < 4; ++i) { float black[3] = { 0.0f, 0.0f, 0.0f }; m_GradientBackgroundColors[i] = std::make_pair(mitk::Color(black), mitk::Color(black)); } } std::pair QmitkStdMultiWidget::GetGradientColors(unsigned int widgetNumber) { if (widgetNumber > 3) { MITK_ERROR << "Decoration color for unknown widget!"; float black[3] = { 0.0f, 0.0f, 0.0f }; return std::make_pair(mitk::Color(black), mitk::Color(black)); } return m_GradientBackgroundColors[widgetNumber]; } mitk::Color QmitkStdMultiWidget::GetDecorationColor(unsigned int widgetNumber) { // The implementation looks a bit messy here, but it avoids // synchronization of the color of the geometry nodes and an // internal member here. // Default colors were chosen for decent visibitliy. // Feel free to change your preferences in the workbench. float tmp[3] = {0.0f, 0.0f, 0.0f}; switch (widgetNumber) { case 0: { if (m_PlaneNode1.IsNotNull()) { if (m_PlaneNode1->GetColor(tmp)) { return dynamic_cast(m_PlaneNode1->GetProperty("color"))->GetColor(); } } float red[3] = {0.753f, 0.0f, 0.0f}; // This is #C00000 in hex return mitk::Color(red); } case 1: { if (m_PlaneNode2.IsNotNull()) { if (m_PlaneNode2->GetColor(tmp)) { return dynamic_cast(m_PlaneNode2->GetProperty("color"))->GetColor(); } } float green[3] = {0.0f, 0.69f, 0.0f}; // This is #00B000 in hex return mitk::Color(green); } case 2: { if (m_PlaneNode3.IsNotNull()) { if (m_PlaneNode3->GetColor(tmp)) { return dynamic_cast(m_PlaneNode3->GetProperty("color"))->GetColor(); } } float blue[3] = {0.0, 0.502f, 1.0f}; // This is #0080FF in hex return mitk::Color(blue); } case 3: { return m_DecorationColorWidget4; } default: MITK_ERROR << "Decoration color for unknown widget!"; float black[3] = {0.0f, 0.0f, 0.0f}; return mitk::Color(black); } } std::string QmitkStdMultiWidget::GetCornerAnnotationText(unsigned int widgetNumber) { if (widgetNumber > 3) { MITK_ERROR << "Decoration color for unknown widget!"; return std::string(""); } return std::string(m_CornerAnnotations[widgetNumber]->GetText(0)); } QmitkStdMultiWidget::~QmitkStdMultiWidget() { DisablePositionTracking(); // DisableNavigationControllerEventListening(); m_TimeNavigationController->Disconnect(mitkWidget1->GetSliceNavigationController()); m_TimeNavigationController->Disconnect(mitkWidget2->GetSliceNavigationController()); m_TimeNavigationController->Disconnect(mitkWidget3->GetSliceNavigationController()); m_TimeNavigationController->Disconnect(mitkWidget4->GetSliceNavigationController()); } void QmitkStdMultiWidget::RemovePlanesFromDataStorage() { if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) { if (m_DataStorage.IsNotNull()) { m_DataStorage->Remove(m_PlaneNode1); m_DataStorage->Remove(m_PlaneNode2); m_DataStorage->Remove(m_PlaneNode3); m_DataStorage->Remove(m_ParentNodeForGeometryPlanes); } } } void QmitkStdMultiWidget::AddPlanesToDataStorage() { if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) { if (m_DataStorage.IsNotNull()) { m_DataStorage->Add(m_ParentNodeForGeometryPlanes); m_DataStorage->Add(m_PlaneNode1, m_ParentNodeForGeometryPlanes); m_DataStorage->Add(m_PlaneNode2, m_ParentNodeForGeometryPlanes); m_DataStorage->Add(m_PlaneNode3, m_ParentNodeForGeometryPlanes); } } } void QmitkStdMultiWidget::changeLayoutTo2DImagesUp() { SMW_INFO << "changing layout to 2D images up... " << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // Set Layout to widget this->setLayout(QmitkStdMultiWidgetLayout); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(Qt::Vertical, m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // insert Widget Container into splitter top m_SubSplit1->addWidget(mitkWidget1Container); m_SubSplit1->addWidget(mitkWidget2Container); m_SubSplit1->addWidget(mitkWidget3Container); // set SplitterSize for splitter top QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes(splitterSize); // insert Widget Container into splitter bottom m_SubSplit2->addWidget(mitkWidget4Container); // set SplitterSize for splitter m_LayoutSplit splitterSize.clear(); splitterSize.push_back(400); splitterSize.push_back(1000); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt m_MainSplit->show(); // show Widget if hidden if (mitkWidget1->isHidden()) mitkWidget1->show(); if (mitkWidget2->isHidden()) mitkWidget2->show(); if (mitkWidget3->isHidden()) mitkWidget3->show(); if (mitkWidget4->isHidden()) mitkWidget4->show(); // Change Layout Name m_Layout = LAYOUT_2D_IMAGES_UP; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_2D_IMAGES_UP); mitkWidget2->LayoutDesignListChanged(LAYOUT_2D_IMAGES_UP); mitkWidget3->LayoutDesignListChanged(LAYOUT_2D_IMAGES_UP); mitkWidget4->LayoutDesignListChanged(LAYOUT_2D_IMAGES_UP); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutTo2DImagesLeft() { SMW_INFO << "changing layout to 2D images left... " << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(Qt::Vertical, m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // insert Widget into the splitters m_SubSplit1->addWidget(mitkWidget1Container); m_SubSplit1->addWidget(mitkWidget2Container); m_SubSplit1->addWidget(mitkWidget3Container); // set splitterSize of SubSplit1 QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes(splitterSize); m_SubSplit2->addWidget(mitkWidget4Container); // set splitterSize of Layout Split splitterSize.clear(); splitterSize.push_back(400); splitterSize.push_back(1000); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt and add to Layout m_MainSplit->show(); // show Widget if hidden if (mitkWidget1->isHidden()) mitkWidget1->show(); if (mitkWidget2->isHidden()) mitkWidget2->show(); if (mitkWidget3->isHidden()) mitkWidget3->show(); if (mitkWidget4->isHidden()) mitkWidget4->show(); // update Layout Name m_Layout = LAYOUT_2D_IMAGES_LEFT; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_2D_IMAGES_LEFT); mitkWidget2->LayoutDesignListChanged(LAYOUT_2D_IMAGES_LEFT); mitkWidget3->LayoutDesignListChanged(LAYOUT_2D_IMAGES_LEFT); mitkWidget4->LayoutDesignListChanged(LAYOUT_2D_IMAGES_LEFT); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::SetDecorationProperties(std::string text, mitk::Color color, int widgetNumber) { if (widgetNumber > 3) { MITK_ERROR << "Unknown render window for annotation."; return; } vtkRenderer *renderer = this->GetRenderWindow(widgetNumber)->GetRenderer()->GetVtkRenderer(); if (!renderer) return; vtkSmartPointer annotation = m_CornerAnnotations[widgetNumber]; annotation->SetText(0, text.c_str()); annotation->SetMaximumFontSize(12); annotation->GetTextProperty()->SetColor(color[0], color[1], color[2]); if (!renderer->HasViewProp(annotation)) { renderer->AddViewProp(annotation); } vtkSmartPointer frame = m_RectangleProps[widgetNumber]; frame->SetColor(color[0], color[1], color[2]); if (!renderer->HasViewProp(frame)) { renderer->AddViewProp(frame); } } void QmitkStdMultiWidget::SetCornerAnnotationVisibility(bool visibility) { for (int i = 0; i < 4; ++i) { m_CornerAnnotations[i]->SetVisibility(visibility); } } bool QmitkStdMultiWidget::IsCornerAnnotationVisible(void) const { return m_CornerAnnotations[0]->GetVisibility() > 0; } QmitkRenderWindow *QmitkStdMultiWidget::GetRenderWindow(unsigned int number) { switch (number) { case 0: return this->GetRenderWindow1(); case 1: return this->GetRenderWindow2(); case 2: return this->GetRenderWindow3(); case 3: return this->GetRenderWindow4(); default: MITK_ERROR << "Requested unknown render window"; break; } return nullptr; } void QmitkStdMultiWidget::changeLayoutToDefault() { SMW_INFO << "changing layout to default... " << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(Qt::Vertical, m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // insert Widget container into the splitters m_SubSplit1->addWidget(mitkWidget1Container); m_SubSplit1->addWidget(mitkWidget2Container); m_SubSplit2->addWidget(mitkWidget3Container); m_SubSplit2->addWidget(mitkWidget4Container); // set splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes(splitterSize); m_SubSplit2->setSizes(splitterSize); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt and add to Layout m_MainSplit->show(); // show Widget if hidden if (mitkWidget1->isHidden()) mitkWidget1->show(); if (mitkWidget2->isHidden()) mitkWidget2->show(); if (mitkWidget3->isHidden()) mitkWidget3->show(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_DEFAULT; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_DEFAULT); mitkWidget2->LayoutDesignListChanged(LAYOUT_DEFAULT); mitkWidget3->LayoutDesignListChanged(LAYOUT_DEFAULT); mitkWidget4->LayoutDesignListChanged(LAYOUT_DEFAULT); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToBig3D() { SMW_INFO << "changing layout to big 3D ..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // add widget Splitter to main Splitter m_MainSplit->addWidget(mitkWidget4Container); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets mitkWidget1->hide(); mitkWidget2->hide(); mitkWidget3->hide(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_BIG_3D; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_BIG_3D); mitkWidget2->LayoutDesignListChanged(LAYOUT_BIG_3D); mitkWidget3->LayoutDesignListChanged(LAYOUT_BIG_3D); mitkWidget4->LayoutDesignListChanged(LAYOUT_BIG_3D); // update Alle Widgets this->UpdateAllWidgets(); mitk::RenderingManager::GetInstance()->SetRenderWindowFocus(mitkWidget4->GetVtkRenderWindow()); } void QmitkStdMultiWidget::changeLayoutToWidget1() { SMW_INFO << "changing layout to big Widget1 ..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // add widget Splitter to main Splitter m_MainSplit->addWidget(mitkWidget1Container); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets if (mitkWidget1->isHidden()) mitkWidget1->show(); mitkWidget2->hide(); mitkWidget3->hide(); mitkWidget4->hide(); m_Layout = LAYOUT_WIDGET1; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_WIDGET1); mitkWidget2->LayoutDesignListChanged(LAYOUT_WIDGET1); mitkWidget3->LayoutDesignListChanged(LAYOUT_WIDGET1); mitkWidget4->LayoutDesignListChanged(LAYOUT_WIDGET1); // update Alle Widgets this->UpdateAllWidgets(); mitk::RenderingManager::GetInstance()->SetRenderWindowFocus(mitkWidget1->GetVtkRenderWindow()); } void QmitkStdMultiWidget::changeLayoutToWidget2() { SMW_INFO << "changing layout to big Widget2 ..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // add widget Splitter to main Splitter m_MainSplit->addWidget(mitkWidget2Container); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets mitkWidget1->hide(); if (mitkWidget2->isHidden()) mitkWidget2->show(); mitkWidget3->hide(); mitkWidget4->hide(); m_Layout = LAYOUT_WIDGET2; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_WIDGET2); mitkWidget2->LayoutDesignListChanged(LAYOUT_WIDGET2); mitkWidget3->LayoutDesignListChanged(LAYOUT_WIDGET2); mitkWidget4->LayoutDesignListChanged(LAYOUT_WIDGET2); // update Alle Widgets this->UpdateAllWidgets(); mitk::RenderingManager::GetInstance()->SetRenderWindowFocus(mitkWidget2->GetVtkRenderWindow()); } void QmitkStdMultiWidget::changeLayoutToWidget3() { SMW_INFO << "changing layout to big Widget3 ..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // add widget Splitter to main Splitter m_MainSplit->addWidget(mitkWidget3Container); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets mitkWidget1->hide(); mitkWidget2->hide(); if (mitkWidget3->isHidden()) mitkWidget3->show(); mitkWidget4->hide(); m_Layout = LAYOUT_WIDGET3; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_WIDGET3); mitkWidget2->LayoutDesignListChanged(LAYOUT_WIDGET3); mitkWidget3->LayoutDesignListChanged(LAYOUT_WIDGET3); mitkWidget4->LayoutDesignListChanged(LAYOUT_WIDGET3); // update Alle Widgets this->UpdateAllWidgets(); mitk::RenderingManager::GetInstance()->SetRenderWindowFocus(mitkWidget3->GetVtkRenderWindow()); } void QmitkStdMultiWidget::changeLayoutToRowWidget3And4() { SMW_INFO << "changing layout to Widget3 and 4 in a Row..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(Qt::Vertical, m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // add Widgets to splitter m_LayoutSplit->addWidget(mitkWidget3Container); m_LayoutSplit->addWidget(mitkWidget4Container); // set Splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets mitkWidget1->hide(); mitkWidget2->hide(); if (mitkWidget3->isHidden()) mitkWidget3->show(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_ROW_WIDGET_3_AND_4; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_ROW_WIDGET_3_AND_4); mitkWidget2->LayoutDesignListChanged(LAYOUT_ROW_WIDGET_3_AND_4); mitkWidget3->LayoutDesignListChanged(LAYOUT_ROW_WIDGET_3_AND_4); mitkWidget4->LayoutDesignListChanged(LAYOUT_ROW_WIDGET_3_AND_4); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToColumnWidget3And4() { SMW_INFO << "changing layout to Widget3 and 4 in one Column..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // add Widgets to splitter m_LayoutSplit->addWidget(mitkWidget3Container); m_LayoutSplit->addWidget(mitkWidget4Container); // set SplitterSize QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets mitkWidget1->hide(); mitkWidget2->hide(); if (mitkWidget3->isHidden()) mitkWidget3->show(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_COLUMN_WIDGET_3_AND_4; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_COLUMN_WIDGET_3_AND_4); mitkWidget2->LayoutDesignListChanged(LAYOUT_COLUMN_WIDGET_3_AND_4); mitkWidget3->LayoutDesignListChanged(LAYOUT_COLUMN_WIDGET_3_AND_4); mitkWidget4->LayoutDesignListChanged(LAYOUT_COLUMN_WIDGET_3_AND_4); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToRowWidgetSmall3andBig4() { SMW_INFO << "changing layout to Widget3 and 4 in a Row..." << std::endl; this->changeLayoutToRowWidget3And4(); m_Layout = LAYOUT_ROW_WIDGET_SMALL3_AND_BIG4; } void QmitkStdMultiWidget::changeLayoutToSmallUpperWidget2Big3and4() { SMW_INFO << "changing layout to Widget3 and 4 in a Row..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(Qt::Vertical, m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(Qt::Vertical, m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // insert Widget into the splitters m_SubSplit1->addWidget(mitkWidget2Container); m_SubSplit2->addWidget(mitkWidget3Container); m_SubSplit2->addWidget(mitkWidget4Container); // set Splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit2->setSizes(splitterSize); splitterSize.clear(); splitterSize.push_back(500); splitterSize.push_back(1000); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt m_MainSplit->show(); // show Widget if hidden mitkWidget1->hide(); if (mitkWidget2->isHidden()) mitkWidget2->show(); if (mitkWidget3->isHidden()) mitkWidget3->show(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4); mitkWidget2->LayoutDesignListChanged(LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4); mitkWidget3->LayoutDesignListChanged(LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4); mitkWidget4->LayoutDesignListChanged(LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutTo2x2Dand3DWidget() { SMW_INFO << "changing layout to 2 x 2D and 3D Widget" << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(Qt::Vertical, m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // add Widgets to splitter m_SubSplit1->addWidget(mitkWidget1Container); m_SubSplit1->addWidget(mitkWidget2Container); m_SubSplit2->addWidget(mitkWidget4Container); // set Splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes(splitterSize); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets if (mitkWidget1->isHidden()) mitkWidget1->show(); if (mitkWidget2->isHidden()) mitkWidget2->show(); mitkWidget3->hide(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_2X_2D_AND_3D_WIDGET; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_2X_2D_AND_3D_WIDGET); mitkWidget2->LayoutDesignListChanged(LAYOUT_2X_2D_AND_3D_WIDGET); mitkWidget3->LayoutDesignListChanged(LAYOUT_2X_2D_AND_3D_WIDGET); mitkWidget4->LayoutDesignListChanged(LAYOUT_2X_2D_AND_3D_WIDGET); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToLeft2Dand3DRight2D() { SMW_INFO << "changing layout to 2D and 3D left, 2D right Widget" << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(Qt::Vertical, m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // add Widgets to splitter m_SubSplit1->addWidget(mitkWidget1Container); m_SubSplit1->addWidget(mitkWidget4Container); m_SubSplit2->addWidget(mitkWidget2Container); // set Splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes(splitterSize); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets if (mitkWidget1->isHidden()) mitkWidget1->show(); if (mitkWidget2->isHidden()) mitkWidget2->show(); mitkWidget3->hide(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET); mitkWidget2->LayoutDesignListChanged(LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET); mitkWidget3->LayoutDesignListChanged(LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET); mitkWidget4->LayoutDesignListChanged(LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutTo2DUpAnd3DDown(unsigned int id2Dwindow) { SMW_INFO << "changing layout to 2D up and 3D down" << std::endl; if (id2Dwindow < 1 || id2Dwindow > 3) { MITK_WARN << "Only 2D render window IDs 1,2 or 3 are valid. Got ID " << id2Dwindow << ". " << "Using default ID 2 instead."; id2Dwindow = 2; } // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // Set Layout to widget this->setLayout(QmitkStdMultiWidgetLayout); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(Qt::Vertical, m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // insert Widget Container into splitter top switch (id2Dwindow) { case 1: m_SubSplit1->addWidget(mitkWidget1Container); break; case 2: m_SubSplit1->addWidget(mitkWidget2Container); break; case 3: m_SubSplit1->addWidget(mitkWidget3Container); break; } // set SplitterSize for splitter top QList splitterSize; // insert Widget Container into splitter bottom m_SubSplit2->addWidget(mitkWidget4Container); // set SplitterSize for splitter m_LayoutSplit splitterSize.clear(); splitterSize.push_back(700); splitterSize.push_back(700); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt m_MainSplit->show(); // show/hide Widgets switch (id2Dwindow) { case 1: if (mitkWidget1->isHidden()) mitkWidget1->show(); mitkWidget2->hide(); mitkWidget3->hide(); break; case 2: if (mitkWidget2->isHidden()) mitkWidget2->show(); mitkWidget1->hide(); mitkWidget3->hide(); break; case 3: if (mitkWidget3->isHidden()) mitkWidget3->show(); mitkWidget1->hide(); mitkWidget2->hide(); break; } //always show 3D widget if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_2D_UP_AND_3D_DOWN; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_2D_UP_AND_3D_DOWN); mitkWidget2->LayoutDesignListChanged(LAYOUT_2D_UP_AND_3D_DOWN); mitkWidget3->LayoutDesignListChanged(LAYOUT_2D_UP_AND_3D_DOWN); mitkWidget4->LayoutDesignListChanged(LAYOUT_2D_UP_AND_3D_DOWN); // update all Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::SetDataStorage(mitk::DataStorage *ds) { if (ds == m_DataStorage) { return; } mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->SetDataStorage(ds); mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->SetDataStorage(ds); mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->SetDataStorage(ds); mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->SetDataStorage(ds); m_DataStorage = ds; } void QmitkStdMultiWidget::Fit() { vtkSmartPointer vtkrenderer; vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->GetCameraController()->Fit(); int w = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); vtkObject::SetGlobalWarningDisplay(w); } void QmitkStdMultiWidget::InitPositionTracking() { // TODO POSITIONTRACKER } void QmitkStdMultiWidget::AddDisplayPlaneSubTree() { // add the displayed planes of the multiwidget to a node to which the subtree // @a planesSubTree points ... mitk::PlaneGeometryDataMapper2D::Pointer mapper; // ... of widget 1 mitk::BaseRenderer *renderer1 = mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow()); m_PlaneNode1 = renderer1->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode1->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode1->SetProperty("name", mitk::StringProperty::New(std::string(renderer1->GetName()) + ".plane")); m_PlaneNode1->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode1->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode1->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 2 mitk::BaseRenderer *renderer2 = mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow()); m_PlaneNode2 = renderer2->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode2->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode2->SetProperty("name", mitk::StringProperty::New(std::string(renderer2->GetName()) + ".plane")); m_PlaneNode2->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode2->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode2->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 3 mitk::BaseRenderer *renderer3 = mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow()); m_PlaneNode3 = renderer3->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode3->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode3->SetProperty("name", mitk::StringProperty::New(std::string(renderer3->GetName()) + ".plane")); m_PlaneNode3->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode3->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode3->SetMapper(mitk::BaseRenderer::Standard2D, mapper); m_ParentNodeForGeometryPlanes = mitk::DataNode::New(); m_ParentNodeForGeometryPlanes->SetProperty("name", mitk::StringProperty::New("Widgets")); m_ParentNodeForGeometryPlanes->SetProperty("helper object", mitk::BoolProperty::New(true)); } mitk::SliceNavigationController *QmitkStdMultiWidget::GetTimeNavigationController() { return m_TimeNavigationController; } void QmitkStdMultiWidget::EnableStandardLevelWindow() { levelWindowWidget->disconnect(this); levelWindowWidget->SetDataStorage(mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->GetDataStorage()); levelWindowWidget->show(); } void QmitkStdMultiWidget::DisableStandardLevelWindow() { levelWindowWidget->disconnect(this); levelWindowWidget->hide(); } // CAUTION: Legacy code for enabling Qt-signal-controlled view initialization. // Use RenderingManager::InitializeViews() instead. bool QmitkStdMultiWidget::InitializeStandardViews(const mitk::Geometry3D *geometry) { return m_RenderingManager->InitializeViews(geometry); } void QmitkStdMultiWidget::RequestUpdate() { m_RenderingManager->RequestUpdate(mitkWidget1->GetRenderWindow()); m_RenderingManager->RequestUpdate(mitkWidget2->GetRenderWindow()); m_RenderingManager->RequestUpdate(mitkWidget3->GetRenderWindow()); m_RenderingManager->RequestUpdate(mitkWidget4->GetRenderWindow()); } void QmitkStdMultiWidget::ForceImmediateUpdate() { m_RenderingManager->ForceImmediateUpdate(mitkWidget1->GetRenderWindow()); m_RenderingManager->ForceImmediateUpdate(mitkWidget2->GetRenderWindow()); m_RenderingManager->ForceImmediateUpdate(mitkWidget3->GetRenderWindow()); m_RenderingManager->ForceImmediateUpdate(mitkWidget4->GetRenderWindow()); } void QmitkStdMultiWidget::wheelEvent(QWheelEvent *e) { emit WheelMoved(e); } void QmitkStdMultiWidget::mousePressEvent(QMouseEvent *) { } void QmitkStdMultiWidget::moveEvent(QMoveEvent *e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the Annotation as the StdMultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } QmitkRenderWindow *QmitkStdMultiWidget::GetRenderWindow1() const { return mitkWidget1; } QmitkRenderWindow *QmitkStdMultiWidget::GetRenderWindow2() const { return mitkWidget2; } QmitkRenderWindow *QmitkStdMultiWidget::GetRenderWindow3() const { return mitkWidget3; } QmitkRenderWindow *QmitkStdMultiWidget::GetRenderWindow4() const { return mitkWidget4; } const mitk::Point3D QmitkStdMultiWidget::GetCrossPosition() const { const mitk::PlaneGeometry *plane1 = mitkWidget1->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry *plane2 = mitkWidget2->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry *plane3 = mitkWidget3->GetSliceNavigationController()->GetCurrentPlaneGeometry(); mitk::Line3D line; if ((plane1 != nullptr) && (plane2 != nullptr) && (plane1->IntersectionLine(plane2, line))) { mitk::Point3D point; if ((plane3 != nullptr) && (plane3->IntersectionPoint(line, point))) { return point; } } // TODO BUG POSITIONTRACKER; mitk::Point3D p; return p; // return m_LastLeftClickPositionSupplier->GetCurrentPoint(); } void QmitkStdMultiWidget::EnablePositionTracking() { } void QmitkStdMultiWidget::DisablePositionTracking() { } void QmitkStdMultiWidget::EnsureDisplayContainsPoint(mitk::BaseRenderer *renderer, const mitk::Point3D &p) { mitk::Point2D pointOnDisplay; renderer->WorldToDisplay(p, pointOnDisplay); if (pointOnDisplay[0] < renderer->GetVtkRenderer()->GetOrigin()[0] || pointOnDisplay[1] < renderer->GetVtkRenderer()->GetOrigin()[1] || pointOnDisplay[0] > renderer->GetVtkRenderer()->GetOrigin()[0] + renderer->GetViewportSize()[0] || pointOnDisplay[1] > renderer->GetVtkRenderer()->GetOrigin()[1] + renderer->GetViewportSize()[1]) { mitk::Point2D pointOnPlane; renderer->GetCurrentWorldPlaneGeometry()->Map(p, pointOnPlane); renderer->GetCameraController()->MoveCameraToPoint(pointOnPlane); } } void QmitkStdMultiWidget::MoveCrossToPosition(const mitk::Point3D &newPosition) { mitkWidget1->GetSliceNavigationController()->SelectSliceByPoint(newPosition); mitkWidget2->GetSliceNavigationController()->SelectSliceByPoint(newPosition); mitkWidget3->GetSliceNavigationController()->SelectSliceByPoint(newPosition); m_RenderingManager->RequestUpdateAll(); } void QmitkStdMultiWidget::HandleCrosshairPositionEvent() { if (!m_PendingCrosshairPositionEvent) { m_PendingCrosshairPositionEvent = true; QTimer::singleShot(0, this, SLOT(HandleCrosshairPositionEventDelayed())); } } -mitk::DataNode::Pointer QmitkStdMultiWidget::GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes) -{ - mitk::Point3D crosshairPos = this->GetCrossPosition(); - mitk::DataNode::Pointer node; - int maxlayer = -32768; - - if (nodes.IsNotNull()) - { - mitk::BaseRenderer *baseRenderer = this->mitkWidget1->GetSliceNavigationController()->GetRenderer(); - // find node with largest layer, that is the node shown on top in the render window - for (unsigned int x = 0; x < nodes->size(); x++) - { - if ((nodes->at(x)->GetData()->GetGeometry() != nullptr) && - nodes->at(x)->GetData()->GetGeometry()->IsInside(crosshairPos)) - { - int layer = 0; - if (!(nodes->at(x)->GetIntProperty("layer", layer))) - continue; - if (layer > maxlayer) - { - if (static_cast(nodes->at(x))->IsVisible(baseRenderer)) - { - node = nodes->at(x); - maxlayer = layer; - } - } - } - } - } - return node; -} - void QmitkStdMultiWidget::HandleCrosshairPositionEventDelayed() { m_PendingCrosshairPositionEvent = false; // find image with highest layer mitk::TNodePredicateDataType::Pointer isImageData = mitk::TNodePredicateDataType::New(); - mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->m_DataStorage->GetSubset(isImageData).GetPointer(); + mitk::DataStorage::SetOfObjects::ConstPointer nodes = m_DataStorage->GetSubset(isImageData).GetPointer(); + mitk::Point3D crosshairPos = GetCrossPosition(); + mitk::BaseRenderer* baseRenderer = mitkWidget1->GetSliceNavigationController()->GetRenderer(); + mitk::DataNode::Pointer node = mitk::FindTopmostVisibleNode(nodes, crosshairPos, baseRenderer->GetTime(), baseRenderer); - mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; mitk::Image::Pointer image; bool isBinary = false; - node = this->GetTopLayerNode(nodes); int component = 0; + if (node.IsNotNull()) { node->GetBoolProperty("binary", isBinary); if (isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = m_DataStorage->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { - topSourceNode = this->GetTopLayerNode(sourcenodes); + topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, crosshairPos, baseRenderer->GetTime(), baseRenderer); } if (topSourceNode.IsNotNull()) { image = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } - mitk::Point3D crosshairPos = this->GetCrossPosition(); std::string statusText; std::stringstream stream; itk::Index<3> p; - mitk::BaseRenderer *baseRenderer = this->mitkWidget1->GetSliceNavigationController()->GetRenderer(); unsigned int timestep = baseRenderer->GetTimeStep(); if (image.IsNotNull() && (image->GetTimeSteps() > timestep)) { image->GetGeometry()->WorldToIndex(crosshairPos, p); stream.precision(2); stream << "Position: <" << std::fixed << crosshairPos[0] << ", " << std::fixed << crosshairPos[1] << ", " << std::fixed << crosshairPos[2] << "> mm"; stream << "; Index: <" << p[0] << ", " << p[1] << ", " << p[2] << "> "; mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess, image->GetChannelDescriptor().GetPixelType(), image, image->GetVolumeData(baseRenderer->GetTimeStep()), p, pixelValue, component); if (fabs(pixelValue) > 1000000 || fabs(pixelValue) < 0.01) { stream << "; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: " << std::scientific << pixelValue << " "; } else { stream << "; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: " << pixelValue << " "; } } else { stream << "No image information at this position!"; } statusText = stream.str(); mitk::StatusBar::GetInstance()->DisplayGreyValueText(statusText.c_str()); } int QmitkStdMultiWidget::GetLayout() const { return m_Layout; } bool QmitkStdMultiWidget::GetGradientBackgroundFlag() const { return m_GradientBackgroundFlag; } void QmitkStdMultiWidget::EnableGradientBackground() { // gradient background is by default only in widget 4, otherwise // interferences between 2D rendering and VTK rendering may occur. for (unsigned int i = 0; i < 4; ++i) { GetRenderWindow(i)->GetRenderer()->GetVtkRenderer()->GradientBackgroundOn(); } m_GradientBackgroundFlag = true; } void QmitkStdMultiWidget::DisableGradientBackground() { for (unsigned int i = 0; i < 4; ++i) { GetRenderWindow(i)->GetRenderer()->GetVtkRenderer()->GradientBackgroundOff(); } m_GradientBackgroundFlag = false; } void QmitkStdMultiWidget::EnableDepartmentLogo() { m_LogoRendering->SetVisibility(true); RequestUpdate(); } void QmitkStdMultiWidget::DisableDepartmentLogo() { m_LogoRendering->SetVisibility(false); RequestUpdate(); } bool QmitkStdMultiWidget::IsDepartmentLogoEnabled() const { return m_LogoRendering->IsVisible(); } void QmitkStdMultiWidget::SetWidgetPlaneVisibility(const char *widgetName, bool visible, mitk::BaseRenderer *renderer) { if (m_DataStorage.IsNotNull()) { mitk::DataNode *n = m_DataStorage->GetNamedNode(widgetName); if (n != nullptr) n->SetVisibility(visible, renderer); } } void QmitkStdMultiWidget::SetWidgetPlanesVisibility(bool visible, mitk::BaseRenderer *renderer) { if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetVisibility(visible, renderer); } if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetVisibility(visible, renderer); } if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetVisibility(visible, renderer); } m_RenderingManager->RequestUpdateAll(); } void QmitkStdMultiWidget::SetWidgetPlanesLocked(bool locked) { // do your job and lock or unlock slices. GetRenderWindow1()->GetSliceNavigationController()->SetSliceLocked(locked); GetRenderWindow2()->GetSliceNavigationController()->SetSliceLocked(locked); GetRenderWindow3()->GetSliceNavigationController()->SetSliceLocked(locked); } void QmitkStdMultiWidget::SetWidgetPlanesRotationLocked(bool locked) { // do your job and lock or unlock slices. GetRenderWindow1()->GetSliceNavigationController()->SetSliceRotationLocked(locked); GetRenderWindow2()->GetSliceNavigationController()->SetSliceRotationLocked(locked); GetRenderWindow3()->GetSliceNavigationController()->SetSliceRotationLocked(locked); } void QmitkStdMultiWidget::SetWidgetPlanesRotationLinked(bool link) { emit WidgetPlanesRotationLinked(link); } void QmitkStdMultiWidget::SetWidgetPlaneMode(int userMode) { MITK_DEBUG << "Changing crosshair mode to " << userMode; emit WidgetNotifyNewCrossHairMode(userMode); // Convert user interface mode to actual mode { switch (userMode) { case 0: m_MouseModeSwitcher->SetInteractionScheme(mitk::MouseModeSwitcher::InteractionScheme::MITK); break; case 1: m_MouseModeSwitcher->SetInteractionScheme(mitk::MouseModeSwitcher::InteractionScheme::ROTATION); break; case 2: m_MouseModeSwitcher->SetInteractionScheme(mitk::MouseModeSwitcher::InteractionScheme::ROTATIONLINKED); break; case 3: m_MouseModeSwitcher->SetInteractionScheme(mitk::MouseModeSwitcher::InteractionScheme::SWIVEL); break; } } } void QmitkStdMultiWidget::SetGradientBackgroundColorForRenderWindow(const mitk::Color &upper, const mitk::Color &lower, unsigned int widgetNumber) { if (widgetNumber > 3) { MITK_ERROR << "Gradientbackground for unknown widget!"; return; } m_GradientBackgroundColors[widgetNumber].first = upper; m_GradientBackgroundColors[widgetNumber].second = lower; vtkRenderer *renderer = GetRenderWindow(widgetNumber)->GetRenderer()->GetVtkRenderer(); renderer->SetBackground2(upper[0], upper[1], upper[2]); renderer->SetBackground(lower[0], lower[1], lower[2]); m_GradientBackgroundFlag = true; } void QmitkStdMultiWidget::SetGradientBackgroundColors(const mitk::Color &upper, const mitk::Color &lower) { for (unsigned int i = 0; i < 4; ++i) { vtkRenderer *renderer = GetRenderWindow(i)->GetRenderer()->GetVtkRenderer(); renderer->SetBackground2(upper[0], upper[1], upper[2]); renderer->SetBackground(lower[0], lower[1], lower[2]); } m_GradientBackgroundFlag = true; } void QmitkStdMultiWidget::SetDepartmentLogo(const char *path) { QImage* qimage = new QImage(path); vtkSmartPointer qImageToVtk; qImageToVtk = vtkSmartPointer::New(); qImageToVtk->SetQImage(qimage); qImageToVtk->Update(); m_LogoRendering->SetLogoImage(qImageToVtk->GetOutput()); mitk::BaseRenderer *renderer = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow()); m_LogoRendering->Update(renderer); RequestUpdate(); } void QmitkStdMultiWidget::SetWidgetPlaneModeToSlicing(bool activate) { if (activate) { this->SetWidgetPlaneMode(PLANE_MODE_SLICING); } } void QmitkStdMultiWidget::SetWidgetPlaneModeToRotation(bool activate) { if (activate) { this->SetWidgetPlaneMode(PLANE_MODE_ROTATION); } } void QmitkStdMultiWidget::SetWidgetPlaneModeToSwivel(bool activate) { if (activate) { this->SetWidgetPlaneMode(PLANE_MODE_SWIVEL); } } void QmitkStdMultiWidget::OnLayoutDesignChanged(int layoutDesignIndex) { switch (layoutDesignIndex) { case LAYOUT_DEFAULT: { this->changeLayoutToDefault(); break; } case LAYOUT_2D_IMAGES_UP: { this->changeLayoutTo2DImagesUp(); break; } case LAYOUT_2D_IMAGES_LEFT: { this->changeLayoutTo2DImagesLeft(); break; } case LAYOUT_BIG_3D: { this->changeLayoutToBig3D(); break; } case LAYOUT_WIDGET1: { this->changeLayoutToWidget1(); break; } case LAYOUT_WIDGET2: { this->changeLayoutToWidget2(); break; } case LAYOUT_WIDGET3: { this->changeLayoutToWidget3(); break; } case LAYOUT_2X_2D_AND_3D_WIDGET: { this->changeLayoutTo2x2Dand3DWidget(); break; } case LAYOUT_ROW_WIDGET_3_AND_4: { this->changeLayoutToRowWidget3And4(); break; } case LAYOUT_COLUMN_WIDGET_3_AND_4: { this->changeLayoutToColumnWidget3And4(); break; } case LAYOUT_ROW_WIDGET_SMALL3_AND_BIG4: { this->changeLayoutToRowWidgetSmall3andBig4(); break; } case LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4: { this->changeLayoutToSmallUpperWidget2Big3and4(); break; } case LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET: { this->changeLayoutToLeft2Dand3DRight2D(); break; } }; } void QmitkStdMultiWidget::UpdateAllWidgets() { mitkWidget1->resize(mitkWidget1Container->frameSize().width() - 1, mitkWidget1Container->frameSize().height()); mitkWidget1->resize(mitkWidget1Container->frameSize().width(), mitkWidget1Container->frameSize().height()); mitkWidget2->resize(mitkWidget2Container->frameSize().width() - 1, mitkWidget2Container->frameSize().height()); mitkWidget2->resize(mitkWidget2Container->frameSize().width(), mitkWidget2Container->frameSize().height()); mitkWidget3->resize(mitkWidget3Container->frameSize().width() - 1, mitkWidget3Container->frameSize().height()); mitkWidget3->resize(mitkWidget3Container->frameSize().width(), mitkWidget3Container->frameSize().height()); mitkWidget4->resize(mitkWidget4Container->frameSize().width() - 1, mitkWidget4Container->frameSize().height()); mitkWidget4->resize(mitkWidget4Container->frameSize().width(), mitkWidget4Container->frameSize().height()); } void QmitkStdMultiWidget::HideAllWidgetToolbars() { mitkWidget1->HideRenderWindowMenu(); mitkWidget2->HideRenderWindowMenu(); mitkWidget3->HideRenderWindowMenu(); mitkWidget4->HideRenderWindowMenu(); } void QmitkStdMultiWidget::ActivateMenuWidget(bool state) { mitkWidget1->ActivateMenuWidget(state, this); mitkWidget2->ActivateMenuWidget(state, this); mitkWidget3->ActivateMenuWidget(state, this); mitkWidget4->ActivateMenuWidget(state, this); } bool QmitkStdMultiWidget::IsMenuWidgetEnabled() const { return mitkWidget1->GetActivateMenuWidgetFlag(); } void QmitkStdMultiWidget::SetDecorationColor(unsigned int widgetNumber, mitk::Color color) { switch (widgetNumber) { case 0: if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetColor(color); } break; case 1: if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetColor(color); } break; case 2: if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetColor(color); } break; case 3: m_DecorationColorWidget4 = color; break; default: MITK_ERROR << "Decoration color for unknown widget!"; break; } } void QmitkStdMultiWidget::ResetCrosshair() { if (m_DataStorage.IsNotNull()) { m_RenderingManager->InitializeViewsByBoundingObjects(m_DataStorage); // m_RenderingManager->InitializeViews( m_DataStorage->ComputeVisibleBoundingGeometry3D() ); // reset interactor to normal slicing this->SetWidgetPlaneMode(PLANE_MODE_SLICING); } } void QmitkStdMultiWidget::EnableColoredRectangles() { m_RectangleProps[0]->SetVisibility(1); m_RectangleProps[1]->SetVisibility(1); m_RectangleProps[2]->SetVisibility(1); m_RectangleProps[3]->SetVisibility(1); } void QmitkStdMultiWidget::DisableColoredRectangles() { m_RectangleProps[0]->SetVisibility(0); m_RectangleProps[1]->SetVisibility(0); m_RectangleProps[2]->SetVisibility(0); m_RectangleProps[3]->SetVisibility(0); } bool QmitkStdMultiWidget::IsColoredRectanglesEnabled() const { return m_RectangleProps[0]->GetVisibility() > 0; } mitk::MouseModeSwitcher *QmitkStdMultiWidget::GetMouseModeSwitcher() { return m_MouseModeSwitcher; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane1() { return this->m_PlaneNode1; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane2() { return this->m_PlaneNode2; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane3() { return this->m_PlaneNode3; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane(int id) { switch (id) { case 1: return this->m_PlaneNode1; break; case 2: return this->m_PlaneNode2; break; case 3: return this->m_PlaneNode3; break; default: return nullptr; } } diff --git a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp index 83e346333a..7f623d94d0 100644 --- a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp +++ b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp @@ -1,638 +1,607 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkImageNavigatorView.h" #include #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkImageNavigatorView::VIEW_ID = "org.mitk.views.imagenavigator"; - -static mitk::DataNode::Pointer GetTopLayerNode(const mitk::DataStorage::SetOfObjects::ConstPointer nodes, const mitk::Point3D &position, mitk::BaseRenderer* renderer) -{ - mitk::DataNode::Pointer node; - int maxlayer = -32768; - - if(nodes.IsNotNull()) - { - // find node with largest layer, that is the node shown on top in the render window - for (unsigned int x = 0; x < nodes->size(); x++) - { - if ( (nodes->at(x)->GetData()->GetGeometry() != nullptr) && - nodes->at(x)->GetData()->GetGeometry()->IsInside(position) ) - { - int layer = 0; - if(!(nodes->at(x)->GetIntProperty("layer", layer))) continue; - if(layer > maxlayer) - { - if( static_cast(nodes->at(x))->IsVisible(renderer) ) - { - node = nodes->at(x); - maxlayer = layer; - } - } - } - } - } - - return node; -} - QmitkImageNavigatorView::QmitkImageNavigatorView() : m_AxialStepper(nullptr) , m_SagittalStepper(nullptr) , m_FrontalStepper(nullptr) , m_TimeStepper(nullptr) , m_Parent(nullptr) , m_IRenderWindowPart(nullptr) { } QmitkImageNavigatorView::~QmitkImageNavigatorView() { } void QmitkImageNavigatorView::CreateQtPartControl(QWidget *parent) { // create GUI widgets m_Parent = parent; m_Controls.setupUi(parent); connect(m_Controls.m_XWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); connect(m_Controls.m_YWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); connect(m_Controls.m_ZWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); m_Parent->setEnabled(false); mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); this->RenderWindowPartActivated(renderPart); } void QmitkImageNavigatorView::SetFocus () { m_Controls.m_XWorldCoordinateSpinBox->setFocus(); } void QmitkImageNavigatorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (this->m_IRenderWindowPart != renderWindowPart) { this->m_IRenderWindowPart = renderWindowPart; this->m_Parent->setEnabled(true); QmitkRenderWindow* renderWindow = renderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { if (m_AxialStepper) m_AxialStepper->deleteLater(); m_AxialStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorAxial, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorAxialFromSimpleExample"); m_Controls.m_SliceNavigatorAxial->setEnabled(true); m_Controls.m_AxialLabel->setEnabled(true); m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(true); connect(m_AxialStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); connect(m_AxialStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorAxial->setEnabled(false); m_Controls.m_AxialLabel->setEnabled(false); m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(false); } renderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal"); if (renderWindow) { if (m_SagittalStepper) m_SagittalStepper->deleteLater(); m_SagittalStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorSagittal, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorSagittalFromSimpleExample"); m_Controls.m_SliceNavigatorSagittal->setEnabled(true); m_Controls.m_SagittalLabel->setEnabled(true); m_Controls.m_YWorldCoordinateSpinBox->setEnabled(true); connect(m_SagittalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); connect(m_SagittalStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorSagittal->setEnabled(false); m_Controls.m_SagittalLabel->setEnabled(false); m_Controls.m_YWorldCoordinateSpinBox->setEnabled(false); } renderWindow = renderWindowPart->GetQmitkRenderWindow("coronal"); if (renderWindow) { if (m_FrontalStepper) m_FrontalStepper->deleteLater(); m_FrontalStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorFrontal, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorFrontalFromSimpleExample"); m_Controls.m_SliceNavigatorFrontal->setEnabled(true); m_Controls.m_CoronalLabel->setEnabled(true); m_Controls.m_XWorldCoordinateSpinBox->setEnabled(true); connect(m_FrontalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); connect(m_FrontalStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorFrontal->setEnabled(false); m_Controls.m_CoronalLabel->setEnabled(false); m_Controls.m_XWorldCoordinateSpinBox->setEnabled(false); } mitk::SliceNavigationController* timeController = renderWindowPart->GetTimeNavigationController(); if (timeController) { if (m_TimeStepper) m_TimeStepper->deleteLater(); m_TimeStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorTime, timeController->GetTime(), "sliceNavigatorTimeFromSimpleExample"); m_Controls.m_SliceNavigatorTime->setEnabled(true); m_Controls.m_TimeLabel->setEnabled(true); connect(m_TimeStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorTime->setEnabled(false); m_Controls.m_TimeLabel->setEnabled(false); } this->OnRefetch(); this->UpdateStatusBar(); } } void QmitkImageNavigatorView::UpdateStatusBar() { if (m_IRenderWindowPart != nullptr) { mitk::Point3D position = m_IRenderWindowPart->GetSelectedPosition(); - mitk::BaseRenderer::Pointer renderer = mitk::BaseRenderer::GetInstance(m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetVtkRenderWindow()); + mitk::BaseRenderer::Pointer baseRenderer = mitk::BaseRenderer::GetInstance(m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetVtkRenderWindow()); mitk::TNodePredicateDataType::Pointer isImageData = mitk::TNodePredicateDataType::New(); - mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetDataStorage()->GetSubset(isImageData).GetPointer(); + mitk::DataStorage::SetOfObjects::ConstPointer nodes = GetDataStorage()->GetSubset(isImageData).GetPointer(); if (nodes.IsNotNull()) { mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; int component = 0; - node = GetTopLayerNode(nodes, position, renderer); + node = mitk::FindTopmostVisibleNode(nodes, position, baseRenderer->GetTime(), baseRenderer); if (node.IsNotNull()) { bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { - mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = this->GetDataStorage()->GetSources(node, nullptr, true); + mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = GetDataStorage()->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { - topSourceNode = GetTopLayerNode(sourcenodes, position, renderer); + topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, position, baseRenderer->GetTime(), baseRenderer); } if (topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } // get the position and pixel value from the image and build up status bar text auto statusBar = mitk::StatusBar::GetInstance(); if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(position, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); - statusBar->DisplayImageInfo(position, p, renderer->GetTime(), pixelValue.c_str()); + statusBar->DisplayImageInfo(position, p, baseRenderer->GetTime(), pixelValue.c_str()); } - else if ( pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR ) + else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR) { std::string pixelValue = "See ODF Details view. "; - statusBar->DisplayImageInfo(position, p, renderer->GetTime(), pixelValue.c_str()); + statusBar->DisplayImageInfo(position, p, baseRenderer->GetTime(), pixelValue.c_str()); } else { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(position, p); mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5( mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(renderer->GetTimeStep()), p, pixelValue, component); - statusBar->DisplayImageInfo(position, p, renderer->GetTime(), pixelValue); + statusBar->DisplayImageInfo(position, p, baseRenderer->GetTime(), pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } } } void QmitkImageNavigatorView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_IRenderWindowPart = nullptr; m_Parent->setEnabled(false); } int QmitkImageNavigatorView::GetSizeFlags(bool width) { if(!width) { return berry::Constants::MIN | berry::Constants::MAX | berry::Constants::FILL; } else { return 0; } } int QmitkImageNavigatorView::ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult) { if(width==false) { return 200; } else { return preferredResult; } } int QmitkImageNavigatorView::GetClosestAxisIndex(mitk::Vector3D normal) { // cos(theta) = normal . axis // cos(theta) = (a, b, c) . (d, e, f) // cos(theta) = (a, b, c) . (1, 0, 0) = a // cos(theta) = (a, b, c) . (0, 1, 0) = b // cos(theta) = (a, b, c) . (0, 0, 1) = c double absCosThetaWithAxis[3]; for (int i = 0; i < 3; i++) { absCosThetaWithAxis[i] = fabs(normal[i]); } int largestIndex = 0; double largestValue = absCosThetaWithAxis[0]; for (int i = 1; i < 3; i++) { if (absCosThetaWithAxis[i] > largestValue) { largestValue = absCosThetaWithAxis[i]; largestIndex = i; } } return largestIndex; } void QmitkImageNavigatorView::SetBorderColors() { if (m_IRenderWindowPart) { QString decoColor; QmitkRenderWindow* renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("sagittal"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("coronal"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } } } QString QmitkImageNavigatorView::GetDecorationColorOfGeometry(QmitkRenderWindow* renderWindow) { QColor color; float rgb[3] = {1.0f, 1.0f, 1.0f}; float rgbMax = 255.0f; mitk::BaseRenderer::GetInstance(renderWindow->GetVtkRenderWindow())->GetCurrentWorldPlaneGeometryNode()->GetColor(rgb); color.setRed(static_cast(rgb[0]*rgbMax + 0.5)); color.setGreen(static_cast(rgb[1]*rgbMax + 0.5)); color.setBlue(static_cast(rgb[2]*rgbMax + 0.5)); QString colorAsString = QString(color.name()); return colorAsString; } void QmitkImageNavigatorView::SetBorderColor(int axis, QString colorAsStyleSheetString) { if (axis == 0) { this->SetBorderColor(m_Controls.m_XWorldCoordinateSpinBox, colorAsStyleSheetString); } else if (axis == 1) { this->SetBorderColor(m_Controls.m_YWorldCoordinateSpinBox, colorAsStyleSheetString); } else if (axis == 2) { this->SetBorderColor(m_Controls.m_ZWorldCoordinateSpinBox, colorAsStyleSheetString); } } void QmitkImageNavigatorView::SetBorderColor(QDoubleSpinBox *spinBox, QString colorAsStyleSheetString) { assert(spinBox); spinBox->setStyleSheet(QString("border: 2px solid ") + colorAsStyleSheetString + ";"); } void QmitkImageNavigatorView::SetStepSizes() { this->SetStepSize(0); this->SetStepSize(1); this->SetStepSize(2); } void QmitkImageNavigatorView::SetStepSize(int axis) { if (m_IRenderWindowPart) { mitk::BaseGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D(); if (geometry.IsNotNull()) { mitk::Point3D crossPositionInIndexCoordinates; mitk::Point3D crossPositionInIndexCoordinatesPlus1; mitk::Point3D crossPositionInMillimetresPlus1; mitk::Vector3D transformedAxisDirection; mitk::Point3D crossPositionInMillimetres = m_IRenderWindowPart->GetSelectedPosition(); geometry->WorldToIndex(crossPositionInMillimetres, crossPositionInIndexCoordinates); crossPositionInIndexCoordinatesPlus1 = crossPositionInIndexCoordinates; crossPositionInIndexCoordinatesPlus1[axis] += 1; geometry->IndexToWorld(crossPositionInIndexCoordinatesPlus1, crossPositionInMillimetresPlus1); transformedAxisDirection = crossPositionInMillimetresPlus1 - crossPositionInMillimetres; int closestAxisInMillimetreSpace = this->GetClosestAxisIndex(transformedAxisDirection); double stepSize = transformedAxisDirection.GetNorm(); this->SetStepSize(closestAxisInMillimetreSpace, stepSize); } } } void QmitkImageNavigatorView::SetStepSize(int axis, double stepSize) { if (axis == 0) { m_Controls.m_XWorldCoordinateSpinBox->setSingleStep(stepSize); } else if (axis == 1) { m_Controls.m_YWorldCoordinateSpinBox->setSingleStep(stepSize); } else if (axis == 2) { m_Controls.m_ZWorldCoordinateSpinBox->setSingleStep(stepSize); } } void QmitkImageNavigatorView::OnMillimetreCoordinateValueChanged() { if (m_IRenderWindowPart) { mitk::TimeGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (geometry.IsNotNull()) { mitk::Point3D positionInWorldCoordinates; positionInWorldCoordinates[0] = m_Controls.m_XWorldCoordinateSpinBox->value(); positionInWorldCoordinates[1] = m_Controls.m_YWorldCoordinateSpinBox->value(); positionInWorldCoordinates[2] = m_Controls.m_ZWorldCoordinateSpinBox->value(); m_IRenderWindowPart->SetSelectedPosition(positionInWorldCoordinates); } } } void QmitkImageNavigatorView::OnRefetch() { if (m_IRenderWindowPart) { mitk::BaseGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D(); mitk::TimeGeometry::ConstPointer timeGeometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (geometry.IsNull() && timeGeometry.IsNotNull()) { mitk::TimeStepType timeStep = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetTime()->GetPos(); geometry = timeGeometry->GetGeometryForTimeStep(timeStep); } if (geometry.IsNotNull()) { mitk::BoundingBox::BoundsArrayType bounds = geometry->GetBounds(); mitk::Point3D cornerPoint1InIndexCoordinates; cornerPoint1InIndexCoordinates[0] = bounds[0]; cornerPoint1InIndexCoordinates[1] = bounds[2]; cornerPoint1InIndexCoordinates[2] = bounds[4]; mitk::Point3D cornerPoint2InIndexCoordinates; cornerPoint2InIndexCoordinates[0] = bounds[1]; cornerPoint2InIndexCoordinates[1] = bounds[3]; cornerPoint2InIndexCoordinates[2] = bounds[5]; if (!geometry->GetImageGeometry()) { cornerPoint1InIndexCoordinates[0] += 0.5; cornerPoint1InIndexCoordinates[1] += 0.5; cornerPoint1InIndexCoordinates[2] += 0.5; cornerPoint2InIndexCoordinates[0] -= 0.5; cornerPoint2InIndexCoordinates[1] -= 0.5; cornerPoint2InIndexCoordinates[2] -= 0.5; } mitk::Point3D crossPositionInWorldCoordinates = m_IRenderWindowPart->GetSelectedPosition(); mitk::Point3D cornerPoint1InWorldCoordinates; mitk::Point3D cornerPoint2InWorldCoordinates; geometry->IndexToWorld(cornerPoint1InIndexCoordinates, cornerPoint1InWorldCoordinates); geometry->IndexToWorld(cornerPoint2InIndexCoordinates, cornerPoint2InWorldCoordinates); m_Controls.m_XWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_YWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_XWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); m_Controls.m_YWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); m_Controls.m_ZWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); m_Controls.m_XWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); m_Controls.m_YWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); m_Controls.m_ZWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); m_Controls.m_XWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[0]); m_Controls.m_YWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[1]); m_Controls.m_ZWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[2]); m_Controls.m_XWorldCoordinateSpinBox->blockSignals(false); m_Controls.m_YWorldCoordinateSpinBox->blockSignals(false); m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(false); /// Calculating 'inverse direction' property. mitk::AffineTransform3D::MatrixType matrix = geometry->GetIndexToWorldTransform()->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); for (int worldAxis = 0; worldAxis < 3; ++worldAxis) { QmitkRenderWindow* renderWindow = worldAxis == 0 ? m_IRenderWindowPart->GetQmitkRenderWindow("sagittal") : worldAxis == 1 ? m_IRenderWindowPart->GetQmitkRenderWindow("coronal") : m_IRenderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { const mitk::BaseGeometry* rendererGeometry = renderWindow->GetRenderer()->GetCurrentWorldGeometry(); /// Because of some problems with the current way of event signalling, /// 'Modified' events are sent out from the stepper while the renderer /// does not have a geometry yet. Therefore, we do a nullptr check here. /// See bug T22122. This check can be resolved after T22122 got fixed. if (rendererGeometry) { int dominantAxis = itk::Function::Max3( inverseMatrix[0][worldAxis], inverseMatrix[1][worldAxis], inverseMatrix[2][worldAxis]); bool referenceGeometryAxisInverted = inverseMatrix[dominantAxis][worldAxis] < 0; bool rendererZAxisInverted = rendererGeometry->GetAxisVector(2)[worldAxis] < 0; /// `referenceGeometryAxisInverted` tells if the direction of the corresponding axis /// of the reference geometry is flipped compared to the 'world direction' or not. /// /// `rendererZAxisInverted` tells if direction of the renderer geometry z axis is /// flipped compared to the 'world direction' or not. This is the same as the indexing /// direction in the slice navigation controller and matches the 'top' property when /// initialising the renderer planes. (If 'top' was true then the direction is /// inverted.) /// /// The world direction can be +1 ('up') that means right, anterior or superior, or /// it can be -1 ('down') that means left, posterior or inferior, respectively. /// /// If these two do not match, we have to invert the index between the slice navigation /// controller and the slider navigator widget, so that the user can see and control /// the index according to the reference geometry, rather than the slice navigation /// controller. The index in the slice navigation controller depends on in which way /// the reference geometry has been resliced for the renderer, and it does not necessarily /// match neither the world direction, nor the direction of the corresponding axis of /// the reference geometry. Hence, it is a merely internal information that should not /// be exposed to the GUI. /// /// So that one can navigate in the same world direction by dragging the slider /// right, regardless of the direction of the corresponding axis of the reference /// geometry, we invert the direction of the controls if the reference geometry axis /// is inverted but the direction is not ('inversDirection' is false) or the other /// way around. bool inverseDirection = referenceGeometryAxisInverted != rendererZAxisInverted; QmitkSliderNavigatorWidget* navigatorWidget = worldAxis == 0 ? m_Controls.m_SliceNavigatorSagittal : worldAxis == 1 ? m_Controls.m_SliceNavigatorFrontal : m_Controls.m_SliceNavigatorAxial; navigatorWidget->SetInverseDirection(inverseDirection); // This should be a preference (see T22254) // bool invertedControls = referenceGeometryAxisInverted != inverseDirection; // navigatorWidget->SetInvertedControls(invertedControls); } } } } this->SetBorderColors(); } }