diff --git a/Documentation/Doxygen/3-DeveloperManual/HowTos/InteractionHowTo.dox b/Documentation/Doxygen/3-DeveloperManual/HowTos/InteractionHowTo.dox index a0747ff32d..0f4fa2b26a 100644 --- a/Documentation/Doxygen/3-DeveloperManual/HowTos/InteractionHowTo.dox +++ b/Documentation/Doxygen/3-DeveloperManual/HowTos/InteractionHowTo.dox @@ -1,87 +1,87 @@ /** \page InteractionHowTo Interaction Related Examples in MITK \tableofcontents \section EnableDisableInteraction How to disable/re-enable/modify Display Interaction using Microservices Display Interaction is implemented as EventObservers that are registered as Microservices. This allows them to be queried and modified from anywhere in the code. Application scenarios are e.g. wanting to disable certain interaction during a task, to avoid any conflicting actions, or to adapt the behavior to a special tool that is selected. One example in the MITK Workbench are the measurement tools. They are designed to operate on a single slice, such that we do not want the user to move the cross-hair once he started a measurement. Once he finished the measurement the usual interaction should be restored. The following code demonstrates how this is done. To change the mitk::DisplayActionEventBroadcast behavior, we first need to set up MicroService capabilities in the module by adding: \code #include US_INITIALIZE_MODULE \endcode Furthermore, following includes are needed for the code snipped to work: \code // Header #include "usServiceRegistration.h" #include // Source #include "mitkInteractionEventObserver.h" #include "mitkDisplayActionEventBroadcast.h" #include "usGetModuleContext.h" #include "usModuleContext.h" \endcode The first code snippet queries the us for the DisplayActionEventBroadcast and then exchanges its configuration for a minimal version, that does not allow cross-hair actions. The original configuration is stored in a member that allows restoration of the original behavior once we're done with our action. \code m_DisplayInteractorConfigs.clear(); auto eventObservers = us::GetModuleContext()->GetServiceReferences(); for (const auto& eventObserver : eventObservers) { auto displayActionEventBroadcast = dynamic_cast( us::GetModuleContext()->GetService(eventObserver)); if (nullptr != displayActionEventBroadcast) { // remember the original configuration m_DisplayInteractorConfigs.insert(std::make_pair(eventObserver, displayActionEventBroadcast->GetEventConfig())); // here the alternative configuration is loaded displayActionEventBroadcast->AddEventConfig("DisplayConfigBlockLMB.xml"); } } \endcode To restore the old configuration, query the DisplayActionEventBroadcast again and then restore the saved configuration: \code for (const auto& displayInteractorConfig : m_DisplayInteractorConfigs) { if (displayInteractorConfig.first) { auto displayActionEventBroadcast = static_cast( - us::GetModuleContext()->GetService(displayInteractorConfig.first)); + us::GetModuleContext()->GetService(displayInteractorConfig.first)); if (nullptr != displayActionEventBroadcast) { // here the regular configuration is loaded again displayActionEventBroadcast->SetEventConfig(displayInteractorConfig.second); } } } m_DisplayInteractorConfigs.clear(); \endcode Member declaration: \code // holds configuration objects that have been deactivated std::map m_DisplayInteractorConfigs; \endcode */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox index ea9e4d01e8..648fcc9361 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox @@ -1,501 +1,501 @@ /** \page Step10Page MITK Tutorial - Step 10: Adding new Interaction \tableofcontents \section HowToUseDataInteractor Step 01 - How to use an existing DataInteractor in your Module/Plugin/... MITK provides finished DataInteractors for a variety of tasks, they can be found in Core/Code/Interactions. They can be used with state machine patterns and config files located under Core/Code/Resources/Interactions. A mitk::DataInteractor consists of four parts. The class describing the functionality and two XML files; one describes the state machine pattern, that is the workflow of an interaction and the second describes the user events which trigger an action. Lastly every mitk::DataInteractor works on a mitk::DataNode in which it stores and manipulates data. To use a mitk::DataInteractor these parts have to be brought together. ** TODO add code of mitk::PointSetDataInteractor Plugin .. This code demonstrates the use of an existing mitk::DataInteractor exemplary for the mitk::PointSetDataInteractor: First we need a mitk::DataNode in which the PointSets is stored. It has to be added to the mitk::DataStorage. \code mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); GetDataStorage()->Add(dataNode.GetPointer()); \endcode Then we create an instance of a mitk::PointSetDataInteractor and load a predefined statemachine pattern as well as a configuration for it: \code m_CurrentInteractor = mitk::PointSetDataInteractor::New(); m_CurrentInteractor->LoadStateMachine("PointSet.xml"); m_CurrentInteractor->SetEventConfig("PointSetConfig.xml"); \endcode Lastly the mitk::DataNode is added to the mitk::DataInteractor \code m_CurrentInteractor->SetDataNode(dataNode); \endcode now the mitk::DataInteractor is ready for usage. \section HowToModifyInteraction Step 02 - How to modify the behaviour of a DataInteractor The behavior of a mitk::DataInteractor is determined by two aspects. One, the state machine pattern which describes the flow/order of actions that are performed. Secondly the configuration which determines which user interaction triggers the actions. -\subsection ModifyDisplayInteractorBehavior How to modify the display interactor behavior +\subsection ModifyDisplayInteractionBehavior How to modify the display interaction behavior -Sometimes it may be desirable to change the behaviour of the mitk::DisplayInteractor which controls zooming, panning and scrolling, e.g. when a -Tool is activated that reacts to the same events. Changing the behavior of the DisplayInteractor (or possibly any other EventHandler) can be achieved from anywhere +Sometimes it may be desirable to change the behaviour of the mitk::DisplayActionEventBroadcast which controls zooming, panning and scrolling, e.g. when a +Tool is activated that reacts to the same events. Changing the behavior of the DisplayActionEventBroadcast (or possibly any other EventHandler) can be achieved from anywhere in the code by requesting the InteractionEventObserver and assigning an alternative configuration to it, as demonstrated in this example: \code std::ifstream* configStream = new std::ifstream( #path to alternative configuration file# ); mitk::EventConfig newConfig(configStream); // Requesting all registered EventObservers std::list listEventObserver = GetModuleContext()->GetServiceReferences(); for (std::list::iterator it = listEventObserver.begin(); it != listEventObserver.end(); ++it) { - DisplayInteractor* displayInteractor = dynamic_cast(GetModuleContext()->GetService(*it)); - // filtering: only adjust the DisplayInteractor - if (displayInteractor != NULL) + auto* displayActionEventBroadcast = dynamic_cast(GetModuleContext()->GetService(*it)); + // filtering: only adjust the displayActionEventBroadcast + if (nullptr != displayActionEventBroadcast) { - displayInteractor->SetEventConfig(newConfig); + displayActionEventBroadcast->SetEventConfig(newConfig); } } \endcode \section SectionImplementationDataInteractor How to implement a new DataInteractor This second part of the tutorial step goes beyond the activation of an interactor, that modifies data by user interaction) as shown above. It shows what needs to be implemented to add a new way of interaction within your MITK application. Please see \ref DataInteractionPage as an introduction to the MITK interaction mechanism. This tutorial is structured as follows: The first section deals with config files, describing all the parameters of events and how to use them in a configuration file. In the second section the basics are described that are needed to write a state machine pattern. The last section deals with brining configuration, state machine pattern and code together and gives an exemplary implementation of a mitk::DataInteractor. \section ConfigFileDescriptionSection How to create a Config-File \subsection EventDescriptionSection Event Description Events are described by their parameters. Each event type has its own set of parameters that can be set in the configuration file. If a parameter is omitted it is set to its default value. All possible parameters are listed and described below. Event parameters are also described in the documentation of the event class itself. Mandatory for each event description is the event class and the event variant. The parameters of an event are set by attribute tags. \note Refer to \ref EventClassSection for the meaning of event class. \b Mouse \b Buttons \n mitk::InteractionEvent::MouseButtons represent the buttons. They can be used for two attributes. First the EventButton attribute which describes the button that triggered the event, this always is a single button. Secondly the ButtonState attribute that describes which buttons were pressed at the moment the event has been generated. For example assume the right mouse button and the middle mouse button are already pressed, now the left mouse button is pressed too and generates a second event, this would be described as follows: \code \endcode Note: Technically the LeftMouseButton is also pressed and should be listed in the ButtonState, but this is taken care of by the mitk::EventFactory . Key Events \n mitk::InteractionKeyEvent represents a pressed key, which key is pressed is provided with the Key attribute like this \code \endcode or \code \endcode \note Key Events do not require an explicit configuration, for all key events there exists a predefined event variant with the name 'Std' + value, that is key a is named 'StdA'. The names for special keys are listed here: \dontinclude mitkInteractionEvent.h \skipline // Special Keys \until // End special keys Modifier Keys \n mitk::InteractionEvent::ModifierKeys represent the combination of pressed modifier keys, several modifier keys pressed at the same time are denoted by listing them all separated by commas. \code \endcode \b ScrollDirection \n This attribute is unique to the mitk::MouseWheelEvent and describes the direction in which the mouse wheel is rotated. In the event description actual only the direction is provided, but the event is generated with the actual value, and this value can be retrieved from the object. \code \endcode \subsection ExamplesSection Examples Examples for key events: \code \endcode Examples for MousePress events: \code \endcode There exists a standard configuration file for the most common events called GlobalConfig.xml that can be used to as a default and can be extended by a specific definition. \subsection ParameterDescriptionSection Parameter Description It is also possible to store parameters in the config file. Those are stored using the param-tag, like this: \code \endcode Within the application these properties can then be access via a mitk::PropertyList like this: \code // sm - state machine loaded with config file example2 mitk::PropertyList::Pointer properties = GetAttributes(); std::string prop1; properties->GetStringProperty("property1",prop1); \endcode \section HowToStateMachine HowTo Write a State Machine A state machine pattern is described in a XML file. \subsection StateSection States States are described using the state-tag. Each state has to have a name. Exactly one state has to be a start state in each state machine to indicate the state in which the state machine is set when it is constructed. So a valid, but rather useless state machine would like like this: \code \endcode Optionally a state can be assigned a special mode that influences the event distribution. These modes are GRAB_INPUT , PREFER_INPUT and REGULAR (where REGULAR is default and does not need to be indicated). See \ref DataInteractionTechnicalPage_DispatcherEventDistSection for a description of these modes. Use the special modes only when necessary as they prevent other DataInteractors to receive events. \code \endcode \subsection TransitionSection Transitions Transitions are part of a state and describe all possible state switches, and are therefore important for modeling an interaction scheme. Transitions consist a part that describes the event which triggers the transition (event class and event variant) and a target which is state to which the state machine switches after executing a transition. An event class describes the event type (see mitk::InteractionEvent for the different classes) and the event variant is a specification thereof and the exact description is taken from a config file. Together they determine which event can trigger this transition. For example this state machine will switch from state A to state B when the StdMousePressPrimaryButton event (left mouse button is pressed) occurs. \subsection EventClassSection Event Class The event class description supports the polymorphism of the event classes. Therefore state machine patters should be written in the most general ways possible. So for a given class hierarchy like this: \dot digraph { node [shape=record, fontname=Helvetica, fontsize=10]; a [ label="{InteractionPositionEvent}"]; b [ label="{MousePressEvent}" ]; c [ label="MouseReleaseEvent" ]; d [ label="TouchEvent", style=dotted ]; a -> b; a -> c; a -> d; } \enddot in the state machine pattern the mitk::InteractionPositionEvent can be declared as event class to restrict to the events which hold a position information. The actual implementation is then given in the configuration file. In this case it allows to define events of the classes mitk::InteractionPositionEvent itself, or mitk::MousePressEvent, mitk::MouseReleaseEvent, mitk::TouchEvent. This has the advantage that the patterns remain the same no matter what input devices are used, and the state machine patterns can be configured for newly added event classes as long as they match the class hierarchy (this ensures they hold the necessary properties). \code \endcode \subsection ActionSection Actions Actions can be added to transitions and represent functions in the mitk::DataInteractor that are executed on taking a transition. The following simple state machine will listen for left mouse clicks and execute two actions (and actually never stop). \code \endcode In order to tell the mitk::DataInteractor which function to execute these actions are made known to the mitk::DataInteractor using the CONNECT_FUNCTION macro. This example assumes that there exists an ExampleInteractor which inherits from mitkDataInteractor. This class implements the functions AddPoint and CountClicks. The actions are introduced by implementing the virtual method ConnectActionsAndFunctions(): \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("countClicks", CountClicks); } \endcode \subsection ConditionSection Conditions Conditions can be added to transitions and represent functions in the mitk::DataInteractor that are executed on taking a transition. A condition is used to determine if a following action should be executed or not. \code \endcode In order to tell the mitk::DataInteractor which function to execute these conditions are made known to the mitk::DataInteractor using the CONNECT_CONDITION macro. The ConnectActionsAndFunctions() method has to be augmented accordingly: \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("checkPoint", CheckPoint); CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("countClicks", CountClicks); } \endcode \section ReferenceToIncludeFiles Integration of the pattern and configuration files The usage of custom files slightly differs from the existing ones. Custom pattern and config files have to be stored in the /Resources/Interactions directory of the Module that they were designed for. When loading files from a module location into an interactor, the module has to be supplied as a parameter: \code m_CurrentInteractor = mitk::CustomDataInteractor::New(); m_CurrentInteractor->LoadStateMachine("CustomStateMachinePattern.xml", us::GetModuleContext()->GetModule()); m_CurrentInteractor->SetEventConfig("CustomConfig.xml", us::GetModuleContext()->GetModule()); \endcode See \ref IncludeFiles for a description. \section HowToDataInteractor Implementation of a new mitk::DataInteractor DataInteractors are to inherit from mitk::DataInteractor. Their functionality is implemented in functions that follow this interface: For Actions: \code bool SomeFunctionality(StateMachineAction* , InteractionEvent*); \endcode For Conditions: \code bool SomeFunctionality(const InteractionEvent*); \endcode Your functions are connected with actions and conditions by implementing the function ConnectActionsAndFunctions(), e.g. \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("checkPoint", CheckPoint); CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("enoughPoints", EnoughPoints); } \endcode Now all that is left is to write a state machine pattern and a config file as is described in the tutorials. To provide a useful example the mitk::PointSetDataInteractor is annotated with comments that describe the important parts for an implementation of a mitk::DataInteractor. This step assumes knowledge of the Interaction concept described in \ref DataInteractionPage and some background of the implementation. Please refer to these pages before proceeding. Now all that is left it to write a state machine pattern and a config file as is described in the tutorials. \subsection ExampleInternalEvent Example Interactor using InternalEvent A useful tool in creating DataInteractors is mitk::InternalEvent which allows the mitk::DataInteractor to send signals on its own. The following will describe how to build a mitk::DataInteractor that allows to add points until a certain number of points is reached. The number of accepted points is provided in the config file as a parameter. So we start by writing a state machine pattern that add points until it receives an mitk::InternalEvent telling it, that enough points have been added. \code <--! dead state, nothing happens any more, once we reached this --> \endcode In our config file we set the number of maximal points to 10, and define AddPointClick as a right mouse click with the ctrl button pressed. \code \endcode The implementation is described in the following. \see Step10.h \see Step10.cpp \dontinclude Step10.h Implementation of protected functions: \skipline protected: \until virtual void ConfigurationChanged(); ConnectActionsAndFunctions - Is inherited from mitk::InteractionStateMachine, here action strings from the xml are connected with functions in the mitk::DataInteractor (as seen above). In our example this looks like this: \dontinclude Step10.cpp \skipline void mitk::ExampleInteractor::ConnectActionsAndFunctions() \until } ConfigurationChanged - Is called whenever a new configuration file is loaded (by the mitk::InteractionEventHandler super class), this function allows to implement initialization code that depends on configuration values. In our example we want to set the limit of allowed points: \dontinclude Step10.cpp \skipline void mitk::ExampleInteractor::ConfigurationChang \until } Next the actual functionality of the DataInteractor is implemented, by providing one function per action, following this prototype described before. \dontinclude Step10.h \skipline private: \until bool CheckPoint(cons \dontinclude Step10.cpp \skipline bool mitk::ExampleInteractor::AddPoint(StateM \until //- If the conditions returns false the calling transition and the included actions will not be executed. If a condition fails the event is considered as untreated, and will be offered to other Interactors. \dontinclude Step10.cpp \skipline bool mitk::ExampleInteractor::CheckPoint( \until //end Here we see an internal event used to signal that the point set reached the maximal number of allowed points. The event is created and added to the Dispatchers event queue. \dontinclude Step10.cpp \skipline // create internal \until positionEvent->GetSender( \note Internal events do not need any mapping to event variants. Their signal name is equivalent with the event variant. There are also two documented classes implementing a mitk::DataInteractor and a mitk::InteractionEventObserver which can be looked at for further understanding: \see mitk::PointSetDataInteractor -\see mitk::DisplayInteractor +\see mitk::DisplayActionEventBroadcast Have fun with creating your own interaction and please think about contributing it to MITK! If you meet any difficulties during this step, don't hesitate to ask on the MITK mailing list mitk-users@lists.sourceforge.net! People there are kind and will try to help you. \ref Step09Page "[Previous step]" \ref TutorialPage "[Main tutorial page]" */ diff --git a/Examples/QtFreeRender/QtFreeRender.cpp b/Examples/QtFreeRender/QtFreeRender.cpp index 9bf7497de4..eb917cbe3b 100644 --- a/Examples/QtFreeRender/QtFreeRender.cpp +++ b/Examples/QtFreeRender/QtFreeRender.cpp @@ -1,330 +1,319 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkRenderWindow.h" -#include "mitkCameraController.h" -#include "mitkDisplayInteractor.h" -#include "mitkDisplayInteractor.h" -#include "mitkInteractionConst.h" -#include "mitkLine.h" -#include "mitkPlaneGeometryDataMapper2D.h" -#include "mitkProperties.h" -#include "mitkVtkLayerController.h" +#include +#include +#include +#include +#include +#include +#include #include #include #include #include #include -#include "mitkDataStorage.h" -#include "mitkIOUtil.h" - -#include "vtkAnnotatedCubeActor.h" -#include "vtkCornerAnnotation.h" -#include "vtkMitkRectangleProp.h" -#include "vtkOrientationMarkerWidget.h" -#include "vtkProperty.h" -#include "vtkRenderWindow.h" -#include "vtkRenderWindowInteractor.h" -#include "vtkTextProperty.h" +#include +#include -// us -#include "usGetModuleContext.h" -#include "usModuleContext.h" - -#include "mitkInteractionEventObserver.h" +#include +#include +#include +#include +#include +#include +#include +#include //##Documentation //## @brief Example of a NON QT DEPENDENT MITK RENDERING APPLICATION. mitk::RenderWindow::Pointer mitkWidget1; mitk::RenderWindow::Pointer mitkWidget2; mitk::RenderWindow::Pointer mitkWidget3; mitk::RenderWindow::Pointer mitkWidget4; -mitk::DisplayInteractor::Pointer m_DisplayInteractor; +mitk::DisplayActionEventBroadcast::Pointer m_DisplayActionEventBroadcast; vtkSmartPointer m_RectangleRendering1; vtkSmartPointer m_RectangleRendering2; vtkSmartPointer m_RectangleRendering3; vtkSmartPointer m_RectangleRendering4; mitk::SliceNavigationController *m_TimeNavigationController = nullptr; mitk::DataStorage::Pointer m_DataStorage; mitk::DataNode::Pointer m_PlaneNode1; mitk::DataNode::Pointer m_PlaneNode2; mitk::DataNode::Pointer m_PlaneNode3; mitk::DataNode::Pointer m_Node; void InitializeWindows() { // Set default view directions for SNCs mitkWidget1->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); mitkWidget2->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Sagittal); mitkWidget3->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Coronal); mitkWidget4->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Original); // initialize m_TimeNavigationController: send time via sliceNavigationControllers m_TimeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); 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->GetVtkRenderWindow())); // 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); mitkWidget4->GetRenderer()->GetVtkRenderer()->SetBackground(0.1, 0.1, 0.1); mitkWidget4->GetRenderer()->GetVtkRenderer()->SetBackground(0.5, 0.5, 0.5); mitkWidget4->GetRenderer()->GetVtkRenderer()->GradientBackgroundOn(); m_RectangleRendering1 = vtkSmartPointer::New(); m_RectangleRendering1->SetColor(1.0, 0.0, 0.0); mitkWidget1->GetRenderer()->GetVtkRenderer()->AddViewProp(m_RectangleRendering1); m_RectangleRendering2 = vtkSmartPointer::New(); m_RectangleRendering2->SetColor(0.0, 1.0, 0.0); mitkWidget2->GetRenderer()->GetVtkRenderer()->AddViewProp(m_RectangleRendering2); m_RectangleRendering3 = vtkSmartPointer::New(); m_RectangleRendering3->SetColor(0.0, 0.0, 1.0); mitkWidget3->GetRenderer()->GetVtkRenderer()->AddViewProp(m_RectangleRendering3); m_RectangleRendering4 = vtkSmartPointer::New(); m_RectangleRendering4->SetColor(1.0, 1.0, 0.0); mitkWidget4->GetRenderer()->GetVtkRenderer()->AddViewProp(m_RectangleRendering4); } void AddDisplayPlaneSubTree() { // add the displayed planes of the multiwidget to a node to which the subtree // @a planesSubTree points ... float white[3] = {1.0f, 1.0f, 1.0f}; mitk::PlaneGeometryDataMapper2D::Pointer mapper; mitk::IntProperty::Pointer layer = mitk::IntProperty::New(1000); // ... of widget 1 m_PlaneNode1 = (mitk::BaseRenderer::GetInstance(mitkWidget1->GetVtkRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode1->SetColor(white, mitk::BaseRenderer::GetInstance(mitkWidget4->GetVtkRenderWindow())); m_PlaneNode1->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode1->SetProperty("name", mitk::StringProperty::New("widget1Plane")); m_PlaneNode1->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode1->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PlaneNode1->SetProperty("layer", layer); m_PlaneNode1->SetColor(1.0, 0.0, 0.0); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode1->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 2 m_PlaneNode2 = (mitk::BaseRenderer::GetInstance(mitkWidget2->GetVtkRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode2->SetColor(white, mitk::BaseRenderer::GetInstance(mitkWidget4->GetVtkRenderWindow())); m_PlaneNode2->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode2->SetProperty("name", mitk::StringProperty::New("widget2Plane")); m_PlaneNode2->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode2->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PlaneNode2->SetProperty("layer", layer); m_PlaneNode2->SetColor(0.0, 1.0, 0.0); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode2->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 3 m_PlaneNode3 = (mitk::BaseRenderer::GetInstance(mitkWidget3->GetVtkRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode3->SetColor(white, mitk::BaseRenderer::GetInstance(mitkWidget4->GetVtkRenderWindow())); m_PlaneNode3->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode3->SetProperty("name", mitk::StringProperty::New("widget3Plane")); m_PlaneNode3->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode3->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PlaneNode3->SetProperty("layer", layer); m_PlaneNode3->SetColor(0.0, 0.0, 1.0); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode3->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // AddPlanesToDataStorage if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_Node.IsNotNull()) { if (m_DataStorage.IsNotNull()) { m_DataStorage->Add(m_PlaneNode1); m_DataStorage->Add(m_PlaneNode2); m_DataStorage->Add(m_PlaneNode3); } } } void Fit() { vtkRenderer *vtkrenderer; mitk::BaseRenderer::GetInstance(mitkWidget1->GetVtkRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget2->GetVtkRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget3->GetVtkRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget4->GetVtkRenderWindow())->GetCameraController()->Fit(); int w = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget1->GetVtkRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget2->GetVtkRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget3->GetVtkRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget4->GetVtkRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkObject::SetGlobalWarningDisplay(w); } int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s [filename1] [filename2] ...\n\n", ""); return 1; } // Create a DataStorage m_DataStorage = mitk::StandaloneDataStorage::New(); //************************************************************************* // Part II: Create some data by reading files //************************************************************************* int i; for (i = 1; i < argc; ++i) { // For testing if (strcmp(argv[i], "-testing") == 0) continue; std::string filename = argv[i]; try { // Read the file and add it as a data node to the data storage mitk::DataStorage::SetOfObjects::Pointer nodes = mitk::IOUtil::Load(filename, *m_DataStorage); for (mitk::DataStorage::SetOfObjects::Iterator nodeIter = nodes->Begin(), nodeIterEnd = nodes->End(); nodeIter != nodeIterEnd; ++nodeIter) { mitk::DataNode::Pointer node = nodeIter->Value(); mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image.IsNotNull()) { // Set the property "volumerendering" to the Boolean value "true" node->SetProperty("volumerendering", mitk::BoolProperty::New(false)); node->SetProperty("name", mitk::StringProperty::New("testimage")); node->SetProperty("layer", mitk::IntProperty::New(1)); } } } catch (...) { std::cerr << "Could not open file " << filename << std::endl; exit(2); } } //************************************************************************* // Part V: Create window and pass the tree to it //************************************************************************* // Create renderwindows mitkWidget1 = mitk::RenderWindow::New(); mitkWidget2 = mitk::RenderWindow::New(); mitkWidget3 = mitk::RenderWindow::New(); mitkWidget4 = mitk::RenderWindow::New(); mitkWidget1->GetRenderer()->PrepareRender(); mitkWidget2->GetRenderer()->PrepareRender(); mitkWidget3->GetRenderer()->PrepareRender(); // Tell the renderwindow which (part of) the datastorage to render mitkWidget1->GetRenderer()->SetDataStorage(m_DataStorage); mitkWidget2->GetRenderer()->SetDataStorage(m_DataStorage); mitkWidget3->GetRenderer()->SetDataStorage(m_DataStorage); mitkWidget4->GetRenderer()->SetDataStorage(m_DataStorage); // instantiate display interactor - if (m_DisplayInteractor.IsNull()) + if (m_DisplayActionEventBroadcast.IsNull()) { - m_DisplayInteractor = mitk::DisplayInteractor::New(); - m_DisplayInteractor->LoadStateMachine("DisplayInteraction.xml"); - m_DisplayInteractor->SetEventConfig("DisplayConfigMITK.xml"); - // Register as listener via micro services - - us::ModuleContext *context = us::GetModuleContext(); - context->RegisterService(m_DisplayInteractor.GetPointer()); + m_DisplayActionEventBroadcast = mitk::DisplayActionEventBroadcast::New(); + m_DisplayActionEventBroadcast->LoadStateMachine("DisplayInteraction.xml"); + m_DisplayActionEventBroadcast->SetEventConfig("DisplayConfigMITK.xml"); } // Use it as a 2D View mitkWidget1->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard2D); mitkWidget2->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard2D); mitkWidget3->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard2D); mitkWidget4->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard3D); mitkWidget1->SetSize(400, 400); mitkWidget2->GetVtkRenderWindow()->SetPosition(mitkWidget1->GetVtkRenderWindow()->GetPosition()[0] + 420, mitkWidget1->GetVtkRenderWindow()->GetPosition()[1]); mitkWidget2->SetSize(400, 400); mitkWidget3->GetVtkRenderWindow()->SetPosition(mitkWidget1->GetVtkRenderWindow()->GetPosition()[0], mitkWidget1->GetVtkRenderWindow()->GetPosition()[1] + 450); mitkWidget3->SetSize(400, 400); mitkWidget4->GetVtkRenderWindow()->SetPosition(mitkWidget1->GetVtkRenderWindow()->GetPosition()[0] + 420, mitkWidget1->GetVtkRenderWindow()->GetPosition()[1] + 450); mitkWidget4->SetSize(400, 400); InitializeWindows(); AddDisplayPlaneSubTree(); Fit(); // Initialize the RenderWindows auto geo = m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()); mitk::RenderingManager::GetInstance()->InitializeViews(geo); m_DataStorage->Print(std::cout); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // reinit the mitkVTKEventProvider; // this is only necessary once after calling // ForceImmediateUpdateAll() for the first time mitkWidget1->ReinitEventProvider(); mitkWidget2->ReinitEventProvider(); mitkWidget3->ReinitEventProvider(); mitkWidget1->GetVtkRenderWindow()->Render(); mitkWidget2->GetVtkRenderWindow()->Render(); mitkWidget3->GetVtkRenderWindow()->Render(); mitkWidget4->GetVtkRenderWindow()->Render(); mitkWidget4->GetVtkRenderWindowInteractor()->Start(); return 0; } diff --git a/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h b/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h index 91cc4c2c38..f338639cc9 100644 --- a/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h +++ b/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h @@ -1,136 +1,136 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkBoundingShapeInteractor_h #define mitkBoundingShapeInteractor_h #include #include #include #include #include namespace mitk { // create events for interactions #pragma GCC visibility push(default) itkEventMacroDeclaration(BoundingShapeInteractionEvent, itk::AnyEvent); #pragma GCC visibility pop /** * @brief Basic interaction methods for mitk::GeometryData * * Inherit from DataInteratcor, this provides functionality of a state machine and configurable inputs. * * \ingroup Interaction */ class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor : public DataInteractor { public: mitkClassMacro(BoundingShapeInteractor, DataInteractor); itkFactorylessNewMacro(Self); itkCloneMacro(Self); void SetDataNode(DataNode *dataNode) override; void SetRotationEnabled(bool rotationEnabled); protected: BoundingShapeInteractor(); ~BoundingShapeInteractor() override; /** * Here actions strings from the loaded state machine pattern are mapped to functions of * the DataInteractor. These functions are called when an action from the state machine pattern is executed. */ void ConnectActionsAndFunctions() override; /** * @brief Called when a DataNode has been set/changed. */ void DataNodeChanged() override; void HandlePositionChanged(const InteractionEvent *interactionEvent, Point3D ¢er); /** * @brief Checks if the mouse pointer is over the object. */ virtual bool CheckOverObject(const InteractionEvent *); /** * @brief Checks if the mouse pointer is over one of the assigned handles. */ virtual bool CheckOverHandles(const InteractionEvent *interactionEvent); /** * @brief Called if the mouse pointer is over the object indicated by a color change */ virtual void SelectObject(StateMachineAction *, InteractionEvent *); /** * @brief Called if the mouse pointer leaves the area of the object */ virtual void DeselectObject(StateMachineAction *, InteractionEvent *); /** * @brief Called if the mouse pointer is over one of the handles indicated by a color change */ virtual void SelectHandle(StateMachineAction *, InteractionEvent *); /** * @brief Performs a translation of the object relative to the mouse movement */ virtual void TranslateObject(StateMachineAction *, InteractionEvent *); /** * @brief Performs a object shape change by influencing the scaling of the initial bounding box */ virtual void ScaleObject(StateMachineAction *, InteractionEvent *); /** * @brief Initializes the movement, stores starting position */ virtual void InitInteraction(StateMachineAction *, InteractionEvent *interactionEvent); /** * @brief Deselects all Handles at the end of interaction */ virtual void DeselectHandles(StateMachineAction *, InteractionEvent *interactionEvent); /** * @brief Restore default properties of bounding box and handles */ virtual void RestoreNodeProperties(); /** * @brief Initializes member variables. */ bool InitMembers(InteractionEvent *interactionEvent); private: /** * @brief Enables default crosshair properties */ - void EnableCrosshairNavigation(); + void EnableOriginalInteraction(); /** * @brief Sets limited crosshair properties (disable crosshair movement) */ - void DisableCrosshairNavigation(); + void DisableOriginalInteraction(); class Impl; Impl *m_Impl; }; } #endif diff --git a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp index 4125f76aa8..d75fea310d 100644 --- a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp +++ b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp @@ -1,628 +1,605 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "../DataManagement/mitkBoundingShapeUtil.h" #include -#include #include #include #include #include #include #include #include #include #include #include #include #include #include "usGetModuleContext.h" #include "usModuleRegistry.h" // Properties to allow the user to interact with the base data const char *selectedColorPropertyName = "Bounding Shape.Selected Color"; const char *deselectedColorPropertyName = "Bounding Shape.Deselected Color"; const char *activeHandleIdPropertyName = "Bounding Shape.Active Handle ID"; const char *boundingShapePropertyName = "Bounding Shape"; namespace mitk { itkEventMacroDefinition(BoundingShapeInteractionEvent, itk::AnyEvent); class BoundingShapeInteractor::Impl { public: - Impl() : ScrollEnabled(false), RotationEnabled(false) + Impl() : OriginalInteractionEnabled(false), RotationEnabled(false) { Point3D initialPoint; initialPoint.Fill(0.0); for (int i = 0; i < 6; ++i) Handles.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } ~Impl() {} - bool ScrollEnabled; + bool OriginalInteractionEnabled; Point3D InitialPickedWorldPoint; Point3D LastPickedWorldPoint; Point2D InitialPickedDisplayPoint; std::vector Handles; Handle ActiveHandle; Geometry3D::Pointer OriginalGeometry; bool RotationEnabled; - std::map DisplayInteractorConfigs; + std::map DisplayInteractionConfigs; }; } mitk::BoundingShapeInteractor::BoundingShapeInteractor() : m_Impl(new Impl) { } mitk::BoundingShapeInteractor::~BoundingShapeInteractor() { this->RestoreNodeProperties(); delete m_Impl; } void mitk::BoundingShapeInteractor::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually // executing an action CONNECT_CONDITION("isHoveringOverObject", CheckOverObject); CONNECT_CONDITION("isHoveringOverHandles", CheckOverHandles); // **Function** in the statemachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectObject", SelectObject); CONNECT_FUNCTION("deselectObject", DeselectObject); CONNECT_FUNCTION("deselectHandles", DeselectHandles); CONNECT_FUNCTION("initInteraction", InitInteraction); CONNECT_FUNCTION("translateObject", TranslateObject); CONNECT_FUNCTION("selectHandle", SelectHandle); CONNECT_FUNCTION("scaleObject", ScaleObject); // CONNECT_FUNCTION("rotateObject",RotateObject); } // RotateObject(StateMachineAction*, InteractionEvent* interactionEvent) // void mitk::BoundingShapeInteractor::RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry* // geometry) //{ // mitk::Vector3D rotationAxis = geometry->GetAxisVector(rotationaxis); // float pointX = 0.0f; // float pointY = 0.0f; // float pointZ = 0.0f; // mitk::Point3D pointOfRotation; // pointOfRotation.Fill(0.0); // this->GetDataNode()->GetFloatProperty(anchorPointX, pointX); // this->GetDataNode()->GetFloatProperty(anchorPointY, pointY); // this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ); // pointOfRotation[0] = pointX; // pointOfRotation[1] = pointY; // pointOfRotation[2] = pointZ; // // mitk::RotationOperation* doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle); // // geometry->ExecuteOperation(doOp); // delete doOp; //} void mitk::BoundingShapeInteractor::SetRotationEnabled(bool rotationEnabled) { m_Impl->RotationEnabled = rotationEnabled; } void mitk::BoundingShapeInteractor::DataNodeChanged() { mitk::DataNode::Pointer newInputNode = this->GetDataNode(); if (newInputNode == nullptr) return; // add color properties mitk::ColorProperty::Pointer selectedColor = dynamic_cast(newInputNode->GetProperty(selectedColorPropertyName)); mitk::ColorProperty::Pointer deselectedColor = dynamic_cast(newInputNode->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNull()) newInputNode->AddProperty(selectedColorPropertyName, mitk::ColorProperty::New(0.0, 1.0, 0.0)); if (deselectedColor.IsNull()) newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(1.0, 1.0, 1.0)); newInputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(true)); newInputNode->AddProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); newInputNode->SetProperty("layer", mitk::IntProperty::New(101)); newInputNode->SetBoolProperty("fixedLayer", mitk::BoolProperty::New(true)); newInputNode->SetBoolProperty("pickable", true); mitk::ColorProperty::Pointer initialColor = dynamic_cast(newInputNode->GetProperty(deselectedColorPropertyName)); if (initialColor.IsNotNull()) { newInputNode->SetColor(initialColor->GetColor()); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::BoundingShapeInteractor::HandlePositionChanged(const InteractionEvent *interactionEvent, Point3D ¢er) { GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); std::vector cornerPoints = GetCornerPoints(geometry, true); if (m_Impl->Handles.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->Handles[0].SetPosition(pointLeft); m_Impl->Handles[1].SetPosition(pointRight); m_Impl->Handles[2].SetPosition(pointTop); m_Impl->Handles[3].SetPosition(pointBottom); m_Impl->Handles[4].SetPosition(pointFront); m_Impl->Handles[5].SetPosition(pointBack); // calculate center based on half way of the distance between two opposing cornerpoints center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); } } void mitk::BoundingShapeInteractor::SetDataNode(DataNode *node) { this->RestoreNodeProperties(); // if there is another node set, restore it's color if (node == nullptr) return; DataInteractor::SetDataNode(node); // calls DataNodeChanged internally this->DataNodeChanged(); } bool mitk::BoundingShapeInteractor::CheckOverObject(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); // calculates translation based on offset+extent not on the transformation matrix (because the cube is located in the // center not in the origin) vtkSmartPointer imageTransform = geometry->GetVtkTransform()->GetMatrix(); Point3D center = geometry->GetCenter(); auto translation = vtkSmartPointer::New(); auto transform = vtkSmartPointer::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); mitk::Vector3D extent; for (unsigned int i = 0; i < 3; ++i) extent[i] = (geometry->GetExtent(i)); Point3D currentWorldPosition; Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); interactionEvent->GetSender()->DisplayToWorld(currentDisplayPosition, currentWorldPosition); ScalarType transformedPosition[4]; transformedPosition[0] = currentWorldPosition[0]; transformedPosition[1] = currentWorldPosition[1]; transformedPosition[2] = currentWorldPosition[2]; transformedPosition[3] = 1; // transform point from world to object coordinates transform->GetInverse()->TransformPoint(transformedPosition, transformedPosition); // check if the world point is within bounds bool isInside = (transformedPosition[0] >= (-extent[0] / 2.0)) && (transformedPosition[0] <= (extent[0] / 2.0)) && (transformedPosition[1] >= (-extent[1] / 2.0)) && (transformedPosition[1] <= (extent[1] / 2.0)) && (transformedPosition[2] >= (-extent[2] / 2.0)) && (transformedPosition[2] <= (extent[2] / 2.0)); return isInside; } bool mitk::BoundingShapeInteractor::CheckOverHandles(const InteractionEvent *interactionEvent) { Point3D boundingBoxCenter; HandlePositionChanged(interactionEvent, boundingBoxCenter); const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; Point2D displayCenterPoint; // to do: change to actual time step (currently not necessary because geometry remains the same for each timestep int timeStep = 0; GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); std::vector cornerPoints = GetCornerPoints(geometry, true); interactionEvent->GetSender()->WorldToDisplay(boundingBoxCenter, displayCenterPoint); double scale = interactionEvent->GetSender()->GetScaleFactorMMPerDisplayUnit(); // GetDisplaySizeInMM mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else initialHandleSize = 1.0 / 40.0; mitk::Point2D displaysize = interactionEvent->GetSender()->GetDisplaySizeInMM(); ScalarType handlesize = ((displaysize[0] + displaysize[1]) / 2.0) * initialHandleSize; unsigned int handleNum = 0; for (auto &handle : m_Impl->Handles) { Point2D centerpoint; interactionEvent->GetSender()->WorldToDisplay(handle.GetPosition(), centerpoint); Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); if ((currentDisplayPosition.EuclideanDistanceTo(centerpoint) < (handlesize / scale)) && (currentDisplayPosition.EuclideanDistanceTo(displayCenterPoint) > (handlesize / scale))) // check if mouse is hovering over center point { handle.SetActive(true); m_Impl->ActiveHandle = handle; this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(handleNum++)); this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return true; } else { handleNum++; handle.SetActive(false); } this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); } return false; } void mitk::BoundingShapeInteractor::SelectHandle(StateMachineAction *, InteractionEvent *) { - this->DisableCrosshairNavigation(); + this->DisableOriginalInteraction(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNotNull()) { this->GetDataNode()->GetPropertyList()->SetProperty("color", selectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectHandles(StateMachineAction *, InteractionEvent *) { - this->DisableCrosshairNavigation(); + this->DisableOriginalInteraction(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::SelectObject(StateMachineAction *, InteractionEvent *) { - this->DisableCrosshairNavigation(); // disable crosshair interaction and scolling if user is hovering over the object + this->DisableOriginalInteraction(); // disable crosshair interaction and scolling if user is hovering over the object DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(selectedColorPropertyName)); if (selectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", selectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectObject(StateMachineAction *, InteractionEvent *) { - this->EnableCrosshairNavigation(); // enable crosshair interaction and scolling if user is hovering over the object + this->EnableOriginalInteraction(); // enable crosshair interaction and scolling if user is hovering over the object DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer deselectedColor = dynamic_cast(node->GetProperty(deselectedColorPropertyName)); if (deselectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", deselectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::InitInteraction(StateMachineAction *, InteractionEvent *interactionEvent) { InitMembers(interactionEvent); } bool mitk::BoundingShapeInteractor::InitMembers(InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; // get initial position coordinates m_Impl->InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); m_Impl->InitialPickedWorldPoint = positionEvent->GetPositionInWorld(); m_Impl->LastPickedWorldPoint = positionEvent->GetPositionInWorld(); return true; } void mitk::BoundingShapeInteractor::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); Vector3D spacing = geometry->GetSpacing(); Point3D currentPickedPoint; interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); Vector3D interactionMove; // pixel aligned shifting of the bounding box interactionMove[0] = std::round((currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]) / spacing[0]) * spacing[0]; interactionMove[1] = std::round((currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]) / spacing[1]) * spacing[1]; interactionMove[2] = std::round((currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]) / spacing[2]) * spacing[2]; if ((interactionMove[0] + interactionMove[1] + interactionMove[2]) != 0.0) // only update current position if a movement occured { m_Impl->LastPickedWorldPoint = currentPickedPoint; geometry->SetOrigin(geometry->GetOrigin() + interactionMove); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } return; } void mitk::BoundingShapeInteractor::ScaleObject(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); Point3D handlePickedPoint = m_Impl->ActiveHandle.GetPosition(); Point3D currentPickedPoint; interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); Vector3D spacing = geometry->GetSpacing(); // pixel aligned bounding box Vector3D interactionMove; interactionMove[0] = (currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]); interactionMove[1] = (currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]); interactionMove[2] = (currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]); std::vector faces = m_Impl->ActiveHandle.GetFaceIndices(); auto pointscontainer = mitk::BoundingBox::PointsContainer::New(); // calculate cornerpoints from geometry plus visualization offset std::vector cornerPoints = GetCornerPoints(geometry, true); unsigned int num = 0; for (const auto &point : cornerPoints) { pointscontainer->InsertElement(num++, point); } // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); Vector3D faceNormal; faceNormal[0] = handlePickedPoint[0] - center[0]; faceNormal[1] = handlePickedPoint[1] - center[1]; faceNormal[2] = handlePickedPoint[2] - center[2]; Vector3D faceShift = ((faceNormal * interactionMove) / (faceNormal.GetNorm() * faceNormal.GetNorm())) * faceNormal; // calculate cornerpoints from geometry without visualization offset to update actual geometry cornerPoints = GetCornerPoints(geometry, false); num = 0; for (const auto &point : cornerPoints) { pointscontainer->InsertElement(num++, point); } bool positionChangeThreshold = true; for (int numFaces = 0; numFaces < 8; numFaces++) // estimate the corresponding face and shift its assigned points { if ((numFaces != faces[0]) && (numFaces != faces[1]) && (numFaces != faces[2]) && (numFaces != faces[3])) { Point3D point = pointscontainer->GetElement(numFaces); if (m_Impl->RotationEnabled) // apply if geometry is rotated at a pixel aligned shift is not possible { point[0] += faceShift[0]; point[1] += faceShift[1]; point[2] += faceShift[2]; } else // shift pixelwise { point[0] += std::round(faceShift[0] / spacing[0]) * spacing[0]; point[1] += std::round(faceShift[1] / spacing[1]) * spacing[1]; point[2] += std::round(faceShift[2] / spacing[2]) * spacing[2]; } if (point == pointscontainer->GetElement(numFaces)) positionChangeThreshold = false; else m_Impl->LastPickedWorldPoint = point; pointscontainer->InsertElement(numFaces, point); } } if (positionChangeThreshold) // update only if bounding box is shifted at least by one pixel { auto inverse = mitk::AffineTransform3D::New(); geometry->GetIndexToWorldTransform()->GetInverse(inverse); for (unsigned int pointid = 0; pointid < 8; pointid++) { pointscontainer->InsertElement(pointid, inverse->TransformPoint(pointscontainer->GetElement(pointid))); } auto bbox = mitk::BoundingBox::New(); bbox->SetPoints(pointscontainer); bbox->ComputeBoundingBox(); mitk::Point3D BBmin = bbox->GetMinimum(); mitk::Point3D BBmax = bbox->GetMaximum(); if (std::abs(BBmin[0] - BBmax[0]) > 0.01 && std::abs(BBmin[1] - BBmax[1]) > 0.01 && std::abs(BBmin[2] - BBmax[2]) > 0.01) // TODO: check if the extent is greater than zero { geometry->SetBounds(bbox->GetBounds()); geometry->Modified(); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } return; } void mitk::BoundingShapeInteractor::RestoreNodeProperties() { mitk::DataNode::Pointer inputNode = this->GetDataNode(); if (inputNode.IsNull()) return; mitk::ColorProperty::Pointer color = (mitk::ColorProperty::New(1.0, 1.0, 1.0)); if (color.IsNotNull()) { inputNode->GetPropertyList()->SetProperty("color", color); } inputNode->SetProperty("layer", mitk::IntProperty::New(99)); inputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(false)); inputNode->GetPropertyList()->DeleteProperty(activeHandleIdPropertyName); - EnableCrosshairNavigation(); + EnableOriginalInteraction(); // update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } -void mitk::BoundingShapeInteractor::EnableCrosshairNavigation() +void mitk::BoundingShapeInteractor::EnableOriginalInteraction() { - // enable the crosshair navigation // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework - for (auto it = m_Impl->DisplayInteractorConfigs.begin(); - it != m_Impl->DisplayInteractorConfigs.end(); - ++it) + for (const auto& displayInteractionConfig : m_Impl->DisplayInteractionConfigs) { - if (it->first) + if (displayInteractionConfig.first) { - mitk::DisplayInteractor *displayInteractor = static_cast( - us::GetModuleContext()->GetService(it->first)); - if (displayInteractor != nullptr) - { - // here the regular configuration is loaded again - displayInteractor->SetEventConfig(it->second); - } + auto displayActionEventBroadcast = static_cast( + us::GetModuleContext()->GetService(displayInteractionConfig.first)); - mitk::DisplayActionEventBroadcast *displayActionEventBroadcast = static_cast( - us::GetModuleContext()->GetService(it->first)); - if (displayActionEventBroadcast != nullptr) + if (nullptr != displayActionEventBroadcast) { // here the regular configuration is loaded again - displayActionEventBroadcast->SetEventConfig(it->second); + displayActionEventBroadcast->SetEventConfig(displayInteractionConfig.second); } } } - m_Impl->DisplayInteractorConfigs.clear(); - m_Impl->ScrollEnabled = true; + + m_Impl->DisplayInteractionConfigs.clear(); + m_Impl->OriginalInteractionEnabled = true; } -void mitk::BoundingShapeInteractor::DisableCrosshairNavigation() +void mitk::BoundingShapeInteractor::DisableOriginalInteraction() { // dont deactivate twice, else we will clutter the config list ... - if (m_Impl->ScrollEnabled == false) + if (false == m_Impl->OriginalInteractionEnabled) return; - // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts + // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts // with tools // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction // will still be enabled - m_Impl->DisplayInteractorConfigs.clear(); - std::vector> listEventObserver = - us::GetModuleContext()->GetServiceReferences(); - for (auto it = listEventObserver.begin(); - it != listEventObserver.end(); - ++it) + m_Impl->DisplayInteractionConfigs.clear(); + auto eventObservers = us::GetModuleContext()->GetServiceReferences(); + for (const auto& eventObserver : eventObservers) { - auto *displayInteractor = - dynamic_cast(us::GetModuleContext()->GetService(*it)); - if (displayInteractor != nullptr) - { - // remember the original configuration - m_Impl->DisplayInteractorConfigs.insert(std::make_pair(*it, displayInteractor->GetEventConfig())); - // here the alternative configuration is loaded - displayInteractor->AddEventConfig("DisplayConfigBlockLMB.xml"); - } - - auto *displayActionEventBroadcast = - dynamic_cast(us::GetModuleContext()->GetService(*it)); - if (displayActionEventBroadcast != nullptr) + auto *displayActionEventBroadcast = dynamic_cast( + us::GetModuleContext()->GetService(eventObserver)); + if (nullptr != displayActionEventBroadcast) { // remember the original configuration - m_Impl->DisplayInteractorConfigs.insert(std::make_pair(*it, displayActionEventBroadcast->GetEventConfig())); + m_Impl->DisplayInteractionConfigs.insert(std::make_pair(eventObserver, displayActionEventBroadcast->GetEventConfig())); // here the alternative configuration is loaded displayActionEventBroadcast->AddEventConfig("DisplayConfigBlockLMB.xml"); } } - m_Impl->ScrollEnabled = false; + m_Impl->OriginalInteractionEnabled = false; } diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index 386eff7948..1052ed23af 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,325 +1,324 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkCoreActivator.cpp mitkCoreObjectFactoryBase.cpp mitkCoreObjectFactory.cpp mitkCoreServices.cpp mitkException.cpp Algorithms/mitkBaseDataSource.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkCompositePixelValueToString.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkExtractSliceFilter2.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkTemporalJoinImagesFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAnatomicalStructureColorPresets.cpp DataManagement/mitkArbitraryTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataStorage.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGeometryTransformHolder.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkGenericIDRelationRule.cpp DataManagement/mitkIdentifiable.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkIPropertyOwner.cpp DataManagement/mitkIPropertyPersistence.cpp DataManagement/mitkIPropertyProvider.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLine.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMaterial.cpp DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDataUID.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateFunction.cpp DataManagement/mitkNodePredicateGeometry.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateDataProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkNodePredicateSubGeometry.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkPropertyKeyPath.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkPropertyNameHelper.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkPropertyPersistence.cpp DataManagement/mitkPropertyPersistenceInfo.cpp DataManagement/mitkPropertyRelationRuleBase.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkScaleOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkSourceImageRelationRule.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTemporoSpatialStringProperty.cpp DataManagement/mitkUIDManipulator.cpp DataManagement/mitkVector.cpp DataManagement/mitkVectorProperty.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkIPropertyRelations.cpp DataManagement/mitkPropertyRelations.cpp Interactions/mitkAction.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayActionEventBroadcast.cpp Interactions/mitkDisplayActionEventFunctions.cpp Interactions/mitkDisplayActionEventHandler.cpp Interactions/mitkDisplayActionEventHandlerDesynchronized.cpp Interactions/mitkDisplayActionEventHandlerStd.cpp Interactions/mitkDisplayActionEventHandlerSynchronized.cpp Interactions/mitkDisplayCoordinateOperation.cpp - Interactions/mitkDisplayInteractor.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventFactory.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkInteractionEventObserver.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractionSchemeSwitcher.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkXML2EventParser.cpp IO/mitkAbstractFileIO.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkCustomMimeType.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileReaderWriterBase.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp IO/mitkGeometry3DToXML.cpp IO/mitkIFileIO.cpp IO/mitkIFileReader.cpp IO/mitkIFileWriter.cpp IO/mitkGeometryDataReaderService.cpp IO/mitkGeometryDataWriterService.cpp IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp IO/mitkIOConstants.cpp IO/mitkIOMimeTypes.cpp IO/mitkIOUtil.cpp IO/mitkItkImageIO.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkLegacyFileReaderService.cpp IO/mitkLegacyFileWriterService.cpp IO/mitkLocaleSwitch.cpp IO/mitkLog.cpp IO/mitkMimeType.cpp IO/mitkMimeTypeProvider.cpp IO/mitkOperation.cpp IO/mitkPixelType.cpp IO/mitkPointSetReaderService.cpp IO/mitkPointSetWriterService.cpp IO/mitkProportionalTimeGeometryToXML.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSurfaceStlIO.cpp IO/mitkSurfaceVtkIO.cpp IO/mitkSurfaceVtkLegacyIO.cpp IO/mitkSurfaceVtkXmlIO.cpp IO/mitkUtf8Util.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkPreferenceListReaderOptionsFunctor.cpp IO/mitkIOMetaInformationPropertyConstants.cpp Rendering/mitkAbstractAnnotationRenderer.cpp Rendering/mitkAnnotationUtils.cpp Rendering/mitkBaseRenderer.cpp #Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module Rendering/mitkGradientBackground.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/mitkMapper.cpp Rendering/mitkAnnotation.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowFrame.cpp #Rendering/mitkSurfaceGLMapper2D.cpp Moved to deprecated LegacyGL Module Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigMITKBase.xml Interactions/DisplayConfigPACSBase.xml Interactions/DisplayConfigCrosshair.xml Interactions/DisplayConfigRotation.xml Interactions/DisplayConfigActivateCoupling.xml Interactions/DisplayConfigSwivel.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigBlockLMB.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml mitkAnatomicalStructureColorPresets.xml ) diff --git a/Modules/Core/include/mitkDisplayInteractor.h b/Modules/Core/include/mitkDisplayInteractor.h deleted file mode 100644 index 6b35f4022c..0000000000 --- a/Modules/Core/include/mitkDisplayInteractor.h +++ /dev/null @@ -1,281 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#ifndef 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 Increases the time step in 3d+t data - */ - virtual void IncreaseTimeStep(StateMachineAction *, InteractionEvent *); - - /** - * \brief Decreases the time step in 3d+t data - */ - virtual void DecreaseTimeStep(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); - - private: - /** - * @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; - - typedef std::vector SNCVector; - 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/Interactions/mitkDisplayInteractor.cpp b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp deleted file mode 100644 index def8774def..0000000000 --- a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp +++ /dev/null @@ -1,944 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "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) - { - HandleEvent(interactionEvent, nullptr); - } -} - -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() -{ - // nothing here -} - -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); - - CONNECT_FUNCTION("IncreaseTimeStep", IncreaseTimeStep); - CONNECT_FUNCTION("DecreaseTimeStep", DecreaseTimeStep); -} - -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 = RenderingManager::GetInstance()->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 = RenderingManager::GetInstance()->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 *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); - RenderingManager::GetInstance()->RequestUpdate(sender->GetRenderWindow()); - m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); -} - -void mitk::DisplayInteractor::SetCrosshair(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) -{ - auto* positionEvent = static_cast(interactionEvent); - Point3D pos = positionEvent->GetPositionInWorld(); - - const BaseRenderer::Pointer sender = interactionEvent->GetSender(); - auto renWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); - for (auto renWin : renWindows) - { - if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sender->GetRenderWindow()) - { - BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->SelectSliceByPoint(pos); - } - } -} - -void mitk::DisplayInteractor::IncreaseTimeStep(StateMachineAction *, InteractionEvent *) -{ - auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); - auto stepper = sliceNaviController->GetTime(); - stepper->SetAutoRepeat(true); - stepper->Next(); -} - -void mitk::DisplayInteractor::DecreaseTimeStep(StateMachineAction *, InteractionEvent *) -{ - auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); - auto stepper = sliceNaviController->GetTime(); - stepper->SetAutoRepeat(true); - stepper->Previous(); -} - -void mitk::DisplayInteractor::Zoom(StateMachineAction *, InteractionEvent *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; - } - - auto* positionEvent = static_cast(interactionEvent); - m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; - m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); - - if (factor != 1.0) - { - const BaseRenderer::Pointer sender = interactionEvent->GetSender(); - sender->GetCameraController()->Zoom(factor, m_StartCoordinateInMM); - RenderingManager::GetInstance()->RequestUpdate(sender->GetRenderWindow()); - } -} - -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; - } - } - - m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; - m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); - - // set the new position - sliceNaviController->GetSlice()->SetPos(newPos); - } -} - -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); - - RenderingManager::GetInstance()->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.as_ref()); - - // 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 (nullptr == posEvent) - { - return; - } - - const mitk::BaseRenderer::Pointer baseRenderer = posEvent->GetSender(); - TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); - auto globalCurrentTimePoint = baseRenderer->GetTime(); - mitk::DataStorage::SetOfObjects::ConstPointer nodes = baseRenderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); - if (nodes.IsNull()) - { - return; - } - - // 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; - baseRenderer->DisplayToWorld(posEvent->GetPointerPositionOnScreen(), worldposition); - - mitk::Image::Pointer image3D; - mitk::DataNode::Pointer node; - mitk::DataNode::Pointer topSourceNode; - - int component = 0; - - node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, baseRenderer); - if (node.IsNull()) - { - return; - } - - bool isBinary(false); - node->GetBoolProperty("binary", isBinary); - if (isBinary) - { - mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = baseRenderer->GetDataStorage()->GetSources(node, nullptr, true); - if (!sourcenodes->empty()) - { - topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, worldposition, globalCurrentTimePoint, 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::IOPixelEnum::RGB || pixelType == itk::IOPixelEnum::RGBA) - { - std::string pixelValue = "Pixel RGB(A) value: "; - pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); - statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str()); - } - else if (pixelType == itk::IOPixelEnum::DIFFUSIONTENSOR3D || pixelType == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR) - { - std::string pixelValue = "See ODF Details view. "; - statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str()); - } - else - { - mitk::ScalarType pixelValue; - mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess, - image3D->GetChannelDescriptor().GetPixelType(), - image3D, - image3D->GetVolumeData(image3D->GetTimeGeometry()->TimePointToTimeStep(globalCurrentTimePoint)), - p, - pixelValue, - component); - statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, 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; - } - } -} diff --git a/Modules/Segmentation/Interactions/mitkSegmentationInteractor.cpp b/Modules/Segmentation/Interactions/mitkSegmentationInteractor.cpp index 5cbc3921b9..10defccf89 100644 --- a/Modules/Segmentation/Interactions/mitkSegmentationInteractor.cpp +++ b/Modules/Segmentation/Interactions/mitkSegmentationInteractor.cpp @@ -1,64 +1,63 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSegmentationInteractor.h" #include "mitkInteractionPositionEvent.h" #include "mitkLabelSetImage.h" #include "mitkToolManager.h" #include "mitkToolManagerProvider.h" #include #include void mitk::SegmentationInteractor::ConnectActionsAndFunctions() { Superclass::ConnectActionsAndFunctions(); // CONNECT_FUNCTION("change_active_label", ChangeActiveLabel); } -bool mitk::SegmentationInteractor::ChangeActiveLabel(StateMachineAction *, InteractionEvent *interactionEvent) +void mitk::SegmentationInteractor::ChangeActiveLabel(StateMachineAction*, InteractionEvent* interactionEvent) { BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto positionEvent = static_cast(interactionEvent); auto toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); assert(toolManager); DataNode *workingNode(toolManager->GetWorkingData(0)); if (workingNode && positionEvent) { //TODO T28561 //Code uses a deprecated method. deactivated whole code until the refactorization is done. throw "TODO T28561. Was forgot to refactor in context of T28524. The new MultiLabelSegmentation class will have a dedicated function for querying the label of world position."; //auto *workingImage = dynamic_cast(workingNode->GetData()); //assert(workingImage); //const auto timestep = positionEvent->GetSender()->GetTimeStep(workingImage); //int pixelValue = static_cast(workingImage->GetPixelValueByWorldCoordinate(positionEvent->GetPositionInWorld(), timestep)); //workingImage->GetActiveLabelSet()->SetActiveLabel(pixelValue); // can be the background // // Call Events // // workingImage->ActiveLabelEvent.Send(pixelValue); // // MLI TODO // // toolManager->WorkingDataModified.Send(); //TODO END Refactor with T28524 } RenderingManager::GetInstance()->RequestUpdateAll(); - return true; } diff --git a/Modules/Segmentation/Interactions/mitkSegmentationInteractor.h b/Modules/Segmentation/Interactions/mitkSegmentationInteractor.h index b5fcb1f559..4fce3da3c2 100644 --- a/Modules/Segmentation/Interactions/mitkSegmentationInteractor.h +++ b/Modules/Segmentation/Interactions/mitkSegmentationInteractor.h @@ -1,51 +1,52 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkSegmentationInteractor_h #define mitkSegmentationInteractor_h #include "MitkSegmentationExports.h" -#include "mitkDisplayInteractor.h" +#include namespace mitk { /** *\class SegmentationInteractor *@brief Observer that adds interaction with a multi-label segmentation session to the default display interaction. * * At the moment, this includes changing the active label. * * @ingroup Interaction **/ - class MITKSEGMENTATION_EXPORT SegmentationInteractor : public DisplayInteractor + class MITKSEGMENTATION_EXPORT SegmentationInteractor : public DisplayActionEventBroadcast { public: - mitkClassMacro(SegmentationInteractor, DisplayInteractor); + mitkClassMacro(SegmentationInteractor, DisplayActionEventBroadcast); itkNewMacro(Self); - protected : SegmentationInteractor(){}; + protected: + SegmentationInteractor(){}; ~SegmentationInteractor() 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; /** * Changes the active label. */ - bool ChangeActiveLabel(StateMachineAction *, InteractionEvent *); + void ChangeActiveLabel(StateMachineAction*, InteractionEvent*); }; } #endif diff --git a/Modules/Segmentation/Interactions/mitkTool.cpp b/Modules/Segmentation/Interactions/mitkTool.cpp index b5d0b702ed..36416c4c88 100644 --- a/Modules/Segmentation/Interactions/mitkTool.cpp +++ b/Modules/Segmentation/Interactions/mitkTool.cpp @@ -1,353 +1,339 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTool.h" #include -#include "mitkDisplayInteractor.h" #include "mitkDisplayActionEventBroadcast.h" #include "mitkImageReadAccessor.h" #include "mitkImageWriteAccessor.h" #include "mitkLevelWindowProperty.h" #include "mitkLookupTableProperty.h" #include "mitkProperties.h" #include "mitkVtkResliceInterpolationProperty.h" #include #include // us #include #include // itk #include namespace mitk { itkEventMacroDefinition(ToolEvent, itk::ModifiedEvent); } mitk::Tool::Tool(const char *type, const us::Module *interactorModule) : m_EventConfig(""), m_ToolManager(nullptr), m_PredicateImages(NodePredicateDataType::New("Image")), // for reference images m_PredicateDim3(NodePredicateDimension::New(3, 1)), m_PredicateDim4(NodePredicateDimension::New(4, 1)), m_PredicateDimension(mitk::NodePredicateOr::New(m_PredicateDim3, m_PredicateDim4)), m_PredicateImage3D(NodePredicateAnd::New(m_PredicateImages, m_PredicateDimension)), m_PredicateBinary(NodePredicateProperty::New("binary", BoolProperty::New(true))), m_PredicateNotBinary(NodePredicateNot::New(m_PredicateBinary)), m_PredicateSegmentation(NodePredicateProperty::New("segmentation", BoolProperty::New(true))), m_PredicateNotSegmentation(NodePredicateNot::New(m_PredicateSegmentation)), m_PredicateHelper(NodePredicateProperty::New("helper object", BoolProperty::New(true))), m_PredicateNotHelper(NodePredicateNot::New(m_PredicateHelper)), m_PredicateImageColorful(NodePredicateAnd::New(m_PredicateNotBinary, m_PredicateNotSegmentation)), m_PredicateImageColorfulNotHelper(NodePredicateAnd::New(m_PredicateImageColorful, m_PredicateNotHelper)), m_PredicateReference(NodePredicateAnd::New(m_PredicateImage3D, m_PredicateImageColorfulNotHelper)), m_IsSegmentationPredicate( NodePredicateAnd::New(NodePredicateOr::New(m_PredicateBinary, m_PredicateSegmentation), m_PredicateNotHelper)), m_InteractorType(type), - m_DisplayInteractorConfigs(), + m_DisplayInteractionConfigs(), m_InteractorModule(interactorModule) { } mitk::Tool::~Tool() { } bool mitk::Tool::CanHandle(const BaseData* referenceData, const BaseData* /*workingData*/) const { if (referenceData == nullptr) return false; return true; } void mitk::Tool::InitializeStateMachine() { if (m_InteractorType.empty()) return; try { auto isThisModule = nullptr == m_InteractorModule; auto module = isThisModule ? us::GetModuleContext()->GetModule() : m_InteractorModule; LoadStateMachine(m_InteractorType + ".xml", module); SetEventConfig(isThisModule ? "SegmentationToolsConfig.xml" : m_InteractorType + "Config.xml", module); } catch (const std::exception &e) { MITK_ERROR << "Could not load statemachine pattern " << m_InteractorType << ".xml with exception: " << e.what(); } } void mitk::Tool::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) { this->HandleEvent(interactionEvent, nullptr); } } void mitk::Tool::ConnectActionsAndFunctions() { } bool mitk::Tool::FilterEvents(InteractionEvent *, DataNode *) { return true; } const char *mitk::Tool::GetGroup() const { return "default"; } void mitk::Tool::SetToolManager(ToolManager *manager) { m_ToolManager = manager; } mitk::ToolManager* mitk::Tool::GetToolManager() const { return m_ToolManager; } mitk::DataStorage* mitk::Tool::GetDataStorage() const { if (nullptr != m_ToolManager) { return m_ToolManager->GetDataStorage(); } return nullptr; } void mitk::Tool::Activated() { // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts // with tools // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction // will still be enabled - m_DisplayInteractorConfigs.clear(); - std::vector> listEventObserver = - us::GetModuleContext()->GetServiceReferences(); - for (auto it = listEventObserver.begin(); it != listEventObserver.end(); ++it) + m_DisplayInteractionConfigs.clear(); + auto eventObservers = us::GetModuleContext()->GetServiceReferences(); + for (const auto& eventObserver : eventObservers) { - auto displayInteractor = dynamic_cast(us::GetModuleContext()->GetService(*it)); - if (displayInteractor != nullptr) + auto displayActionEventBroadcast = dynamic_cast( + us::GetModuleContext()->GetService(eventObserver)); + if (nullptr != displayActionEventBroadcast) { // remember the original configuration - m_DisplayInteractorConfigs.insert(std::make_pair(*it, displayInteractor->GetEventConfig())); - // here the alternative configuration is loaded - displayInteractor->AddEventConfig(m_EventConfig.c_str()); - } - - auto displayActionEventBroadcast = dynamic_cast(us::GetModuleContext()->GetService(*it)); - if (displayActionEventBroadcast != nullptr) - { - // remember the original configuration - m_DisplayInteractorConfigs.insert(std::make_pair(*it, displayActionEventBroadcast->GetEventConfig())); + m_DisplayInteractionConfigs.insert(std::make_pair(eventObserver, displayActionEventBroadcast->GetEventConfig())); // here the alternative configuration is loaded displayActionEventBroadcast->AddEventConfig(m_EventConfig.c_str()); } } } void mitk::Tool::Deactivated() { // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework - for (auto it = m_DisplayInteractorConfigs.begin(); it != m_DisplayInteractorConfigs.end(); ++it) + for (const auto& displayInteractionConfig : m_DisplayInteractionConfigs) { - if (it->first) + if (displayInteractionConfig.first) { - auto displayInteractor = static_cast(us::GetModuleContext()->GetService(it->first)); - if (displayInteractor != nullptr) - { - // here the regular configuration is loaded again - displayInteractor->SetEventConfig(it->second); - } + auto displayActionEventBroadcast = static_cast( + us::GetModuleContext()->GetService(displayInteractionConfig.first)); - auto displayActionEventBroadcast = dynamic_cast(us::GetModuleContext()->GetService(it->first)); - if (displayActionEventBroadcast != nullptr) + if (nullptr != displayActionEventBroadcast) { // here the regular configuration is loaded again - displayActionEventBroadcast->SetEventConfig(it->second); + displayActionEventBroadcast->SetEventConfig(displayInteractionConfig.second); } } } - m_DisplayInteractorConfigs.clear(); + + m_DisplayInteractionConfigs.clear(); } itk::Object::Pointer mitk::Tool::GetGUI(const std::string &toolkitPrefix, const std::string &toolkitPostfix) { itk::Object::Pointer object; std::string classname = this->GetNameOfClass(); std::string guiClassname = toolkitPrefix + classname + toolkitPostfix; std::list allGUIs = itk::ObjectFactoryBase::CreateAllInstance(guiClassname.c_str()); for (auto iter = allGUIs.begin(); iter != allGUIs.end(); ++iter) { if (object.IsNull()) { object = dynamic_cast(iter->GetPointer()); } else { MITK_ERROR << "There is more than one GUI for " << classname << " (several factories claim ability to produce a " << guiClassname << " ) " << std::endl; return nullptr; // people should see and fix this error } } return object; } mitk::NodePredicateBase::ConstPointer mitk::Tool::GetReferenceDataPreference() const { return m_PredicateReference.GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::Tool::GetWorkingDataPreference() const { return m_IsSegmentationPredicate.GetPointer(); } mitk::DataNode::Pointer mitk::Tool::CreateEmptySegmentationNode(const Image *original, const std::string &organName, const mitk::Color &color) const { // we NEED a reference image for size etc. if (!original) return nullptr; // actually create a new empty segmentation PixelType pixelType(mitk::MakeScalarPixelType()); LabelSetImage::Pointer segmentation = LabelSetImage::New(); if (original->GetDimension() == 2) { const unsigned int dimensions[] = {original->GetDimension(0), original->GetDimension(1), 1}; segmentation->Initialize(pixelType, 3, dimensions); segmentation->AddLayer(); } else { segmentation->Initialize(original); } mitk::Label::Pointer label = mitk::Label::New(); label->SetName(organName); label->SetColor(color); label->SetValue(1); segmentation->GetActiveLabelSet()->AddLabel(label); segmentation->GetActiveLabelSet()->SetActiveLabel(1); unsigned int byteSize = sizeof(mitk::Label::PixelType); if (segmentation->GetDimension() < 4) { for (unsigned int dim = 0; dim < segmentation->GetDimension(); ++dim) { byteSize *= segmentation->GetDimension(dim); } mitk::ImageWriteAccessor writeAccess(segmentation.GetPointer(), segmentation->GetVolumeData(0)); memset(writeAccess.GetData(), 0, byteSize); } else { // if we have a time-resolved image we need to set memory to 0 for each time step for (unsigned int dim = 0; dim < 3; ++dim) { byteSize *= segmentation->GetDimension(dim); } for (unsigned int volumeNumber = 0; volumeNumber < segmentation->GetDimension(3); volumeNumber++) { mitk::ImageWriteAccessor writeAccess(segmentation.GetPointer(), segmentation->GetVolumeData(volumeNumber)); memset(writeAccess.GetData(), 0, byteSize); } } if (original->GetTimeGeometry()) { TimeGeometry::Pointer originalGeometry = original->GetTimeGeometry()->Clone(); segmentation->SetTimeGeometry(originalGeometry); } else { Tool::ErrorMessage("Original image does not have a 'Time sliced geometry'! Cannot create a segmentation."); return nullptr; } return CreateSegmentationNode(segmentation, organName, color); } mitk::DataNode::Pointer mitk::Tool::CreateSegmentationNode(Image *image, const std::string &organName, const mitk::Color &color) const { if (!image) return nullptr; // decorate the datatreenode with some properties DataNode::Pointer segmentationNode = DataNode::New(); segmentationNode->SetData(image); // name segmentationNode->SetProperty("name", StringProperty::New(organName)); // visualization properties segmentationNode->SetProperty("binary", BoolProperty::New(true)); segmentationNode->SetProperty("color", ColorProperty::New(color)); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); lut->SetType(mitk::LookupTable::MULTILABEL); mitk::LookupTableProperty::Pointer lutProp = mitk::LookupTableProperty::New(); lutProp->SetLookupTable(lut); segmentationNode->SetProperty("LookupTable", lutProp); segmentationNode->SetProperty("texture interpolation", BoolProperty::New(false)); segmentationNode->SetProperty("layer", IntProperty::New(10)); segmentationNode->SetProperty("levelwindow", LevelWindowProperty::New(LevelWindow(0.5, 1))); segmentationNode->SetProperty("opacity", FloatProperty::New(0.3)); segmentationNode->SetProperty("segmentation", BoolProperty::New(true)); segmentationNode->SetProperty("reslice interpolation", VtkResliceInterpolationProperty::New()); // otherwise -> segmentation appears in 2 // slices sometimes (only visual effect, not // different data) // For MITK-3M3 release, the volume of all segmentations should be shown segmentationNode->SetProperty("showVolume", BoolProperty::New(true)); return segmentationNode; } us::ModuleResource mitk::Tool::GetIconResource() const { // Each specific tool should load its own resource. This one will be invalid return us::ModuleResource(); } us::ModuleResource mitk::Tool::GetCursorIconResource() const { // Each specific tool should load its own resource. This one will be invalid return us::ModuleResource(); } diff --git a/Modules/Segmentation/Interactions/mitkTool.h b/Modules/Segmentation/Interactions/mitkTool.h index c2f6af5a78..221c4019e9 100644 --- a/Modules/Segmentation/Interactions/mitkTool.h +++ b/Modules/Segmentation/Interactions/mitkTool.h @@ -1,275 +1,275 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkTool_h_Included #define mitkTool_h_Included #include "itkObjectFactoryBase.h" #include "itkVersion.h" #include "mitkCommon.h" #include "mitkDataNode.h" #include "mitkEventStateMachine.h" #include "mitkInteractionEventObserver.h" #include "mitkLabelSetImage.h" #include "mitkMessage.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateDimension.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateProperty.h" #include "mitkToolEvents.h" #include "mitkToolFactoryMacro.h" #include #include #include #include #include #include #include "usServiceRegistration.h" namespace us { class ModuleResource; } namespace mitk { class ToolManager; /** \brief Base class of all tools used by mitk::ToolManager. \sa ToolManager \sa SegTool2D \ingroup Interaction \ingroup ToolManagerEtAl Every tool is a mitk::StateMachine, which can follow any transition pattern that it likes. One important thing to know is, that every derived tool should always call SuperClass::Deactivated() at the end of its own implementation of Deactivated, because mitk::Tool resets the StateMachine in this method. Only if you are very sure that you covered all possible things that might happen to your own tool, you should consider not to reset the StateMachine from time to time. To learn about the MITK implementation of state machines in general, have a look at \ref InteractionPage. To derive a non-abstract tool, you inherit from mitk::Tool (or some other base class further down the inheritance tree), and in your own parameterless constructor (that is called from the itkFactorylessNewMacro that you use) you pass a StateMachine pattern name to the superclass. Names for valid patterns can be found in StateMachine.xml (which might be enhanced by you). You have to implement at least GetXPM() and GetName() to provide some identification. Each Tool knows its ToolManager, which can provide the data that the tool should work on. \warning Only to be instantiated by mitk::ToolManager (because SetToolManager has to be called). All other uses are unsupported. $Author$ */ class MITKSEGMENTATION_EXPORT Tool : public EventStateMachine, public InteractionEventObserver { public: typedef mitk::Label::PixelType DefaultSegmentationDataType; /** * \brief To let GUI process new events (e.g. qApp->processEvents() ) */ Message<> GUIProcessEventsMessage; /** * \brief To send error messages (to be shown by some GUI) */ Message1 ErrorMessage; /** * \brief To send whether the tool is busy (to be shown by some GUI) */ Message1 CurrentlyBusy; /** * \brief To send general messages (to be shown by some GUI) */ Message1 GeneralMessage; mitkClassMacro(Tool, EventStateMachine); // no New(), there should only be subclasses /** \brief Returns an icon in the XPM format. This icon has to fit into some kind of button in most applications, so make it smaller than 25x25 pixels. XPM is e.g. supported by The Gimp. But if you open any XPM file in your text editor, you will see that you could also "draw" it with an editor. */ virtual const char **GetXPM() const = 0; /** * \brief Returns the path of an icon. * * This icon is preferred to the XPM icon. */ virtual std::string GetIconPath() const { return ""; } /** * \brief Returns the path of a cursor icon. * */ virtual us::ModuleResource GetCursorIconResource() const; /** * @brief Returns the tool button icon of the tool wrapped by a usModuleResource * @return a valid ModuleResource or an invalid if this function * is not reimplemented */ virtual us::ModuleResource GetIconResource() const; /** \brief Returns the name of this tool. Make it short! This name has to fit into some kind of button in most applications, so take some time to think of a good name! */ virtual const char *GetName() const = 0; /** \brief Name of a group. You can group several tools by assigning a group name. Graphical tool selectors might use this information to group tools. (What other reason could there be?) */ virtual const char *GetGroup() const; virtual void InitializeStateMachine(); /** * \brief Interface for GUI creation. * * This is the basic interface for creation of a GUI object belonging to one tool. * * Tools that support a GUI (e.g. for display/editing of parameters) should follow some rules: * * - A Tool and its GUI are two separate classes * - There may be several instances of a GUI at the same time. * - mitk::Tool is toolkit (Qt, wxWidgets, etc.) independent, the GUI part is of course dependent * - The GUI part inherits both from itk::Object and some GUI toolkit class * - The GUI class name HAS to be constructed like "toolkitPrefix" tool->GetClassName() + "toolkitPostfix", e.g. * MyTool -> wxMyToolGUI * - For each supported toolkit there is a base class for tool GUIs, which contains some convenience methods * - Tools notify the GUI about changes using ITK events. The GUI must observe interesting events. * - The GUI base class may convert all ITK events to the GUI toolkit's favoured messaging system (Qt -> signals) * - Calling methods of a tool by its GUI is done directly. * In some cases GUIs don't want to be notified by the tool when they cause a change in a tool. * There is a macro CALL_WITHOUT_NOTICE(method()), which will temporarily disable all notifications during a * method call. */ virtual itk::Object::Pointer GetGUI(const std::string &toolkitPrefix, const std::string &toolkitPostfix); virtual NodePredicateBase::ConstPointer GetReferenceDataPreference() const; virtual NodePredicateBase::ConstPointer GetWorkingDataPreference() const; DataNode::Pointer CreateEmptySegmentationNode(const Image *original, const std::string &organName, const mitk::Color &color) const; DataNode::Pointer CreateSegmentationNode(Image *image, const std::string &organName, const mitk::Color &color) const; /** Function used to check if a tool can handle the referenceData and (if specified) the working data. @pre referenceData must be a valid pointer @param referenceData Pointer to the data that should be checked as valid reference for the tool. @param workingData Pointer to the data that should be checked as valid working data for this tool. This parameter can be null if no working data is specified so far.*/ virtual bool CanHandle(const BaseData *referenceData, const BaseData *workingData) const; protected: friend class ToolManager; virtual void SetToolManager(ToolManager *); /** Returns the pointer to the tool manager of the tool. May be null.*/ ToolManager* GetToolManager() const; /** Returns the data storage provided by the toolmanager. May be null (e.g. if ToolManager is not set).*/ mitk::DataStorage* GetDataStorage() const; void ConnectActionsAndFunctions() override; /** \brief Called when the tool gets activated. Derived tools should call their parents implementation at the beginning of the overriding function. */ virtual void Activated(); /** \brief Called when the tool gets deactivated. Derived tools should call their parents implementation at the end of the overriding function. */ virtual void Deactivated(); /** \brief Let subclasses change their event configuration. */ std::string m_EventConfig; Tool(const char *, const us::Module *interactorModule = nullptr); // purposely hidden ~Tool() override; void Notify(InteractionEvent *interactionEvent, bool isHandled) override; bool FilterEvents(InteractionEvent *, DataNode *) override; private: ToolManager* m_ToolManager; // for reference data NodePredicateDataType::Pointer m_PredicateImages; NodePredicateDimension::Pointer m_PredicateDim3; NodePredicateDimension::Pointer m_PredicateDim4; NodePredicateOr::Pointer m_PredicateDimension; NodePredicateAnd::Pointer m_PredicateImage3D; NodePredicateProperty::Pointer m_PredicateBinary; NodePredicateNot::Pointer m_PredicateNotBinary; NodePredicateProperty::Pointer m_PredicateSegmentation; NodePredicateNot::Pointer m_PredicateNotSegmentation; NodePredicateProperty::Pointer m_PredicateHelper; NodePredicateNot::Pointer m_PredicateNotHelper; NodePredicateAnd::Pointer m_PredicateImageColorful; NodePredicateAnd::Pointer m_PredicateImageColorfulNotHelper; NodePredicateAnd::Pointer m_PredicateReference; // for working data NodePredicateAnd::Pointer m_IsSegmentationPredicate; std::string m_InteractorType; - std::map m_DisplayInteractorConfigs; + std::map m_DisplayInteractionConfigs; const us::Module *m_InteractorModule; }; } // namespace #endif diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp index 9eab83e044..a57eab8f0e 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp @@ -1,513 +1,512 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkImageCropperView.h" #include -#include #include #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkImageCropperView::VIEW_ID = "org.mitk.views.qmitkimagecropper"; QmitkImageCropperView::QmitkImageCropperView(QObject *) : m_ParentWidget(nullptr) , m_BoundingShapeInteractor(nullptr) , m_CropOutsideValue(0) { CreateBoundingShapeInteractor(false); } QmitkImageCropperView::~QmitkImageCropperView() { //disable interactor if (m_BoundingShapeInteractor != nullptr) { m_BoundingShapeInteractor->SetDataNode(nullptr); m_BoundingShapeInteractor->EnableInteraction(false); } } void QmitkImageCropperView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Controls.imageSelectionWidget->SetDataStorage(GetDataStorage()); m_Controls.imageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(mitk::TNodePredicateDataType::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); m_Controls.imageSelectionWidget->SetSelectionIsOptional(true); m_Controls.imageSelectionWidget->SetAutoSelectNewNodes(true); m_Controls.imageSelectionWidget->SetEmptyInfo(QString("Please select an image node")); m_Controls.imageSelectionWidget->SetPopUpTitel(QString("Select image node")); connect(m_Controls.imageSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkImageCropperView::OnImageSelectionChanged); m_Controls.boundingBoxSelectionWidget->SetDataStorage(GetDataStorage()); m_Controls.boundingBoxSelectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); m_Controls.boundingBoxSelectionWidget->SetSelectionIsOptional(true); m_Controls.boundingBoxSelectionWidget->SetAutoSelectNewNodes(true); m_Controls.boundingBoxSelectionWidget->SetEmptyInfo(QString("Please select a bounding box")); m_Controls.boundingBoxSelectionWidget->SetPopUpTitel(QString("Select bounding box node")); connect(m_Controls.boundingBoxSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkImageCropperView::OnBoundingBoxSelectionChanged); connect(m_Controls.buttonCreateNewBoundingBox, SIGNAL(clicked()), this, SLOT(OnCreateNewBoundingBox())); connect(m_Controls.buttonCropping, SIGNAL(clicked()), this, SLOT(OnCropping())); connect(m_Controls.buttonMasking, SIGNAL(clicked()), this, SLOT(OnMasking())); auto lambda = [this]() { m_Controls.groupImageSettings->setVisible(!m_Controls.groupImageSettings->isVisible()); }; connect(m_Controls.buttonAdvancedSettings, &ctkExpandButton::clicked, this, lambda); connect(m_Controls.spinBoxOutsidePixelValue, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueChanged(int))); SetDefaultGUI(); m_ParentWidget = parent; this->OnImageSelectionChanged(m_Controls.imageSelectionWidget->GetSelectedNodes()); this->OnBoundingBoxSelectionChanged(m_Controls.boundingBoxSelectionWidget->GetSelectedNodes()); } void QmitkImageCropperView::OnImageSelectionChanged(QList) { bool rotationEnabled = false; m_Controls.labelWarningRotation->setVisible(false); auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { SetDefaultGUI(); return; } auto image = dynamic_cast(imageNode->GetData()); if (nullptr != image) { if (image->GetDimension() < 3) { QMessageBox::warning(nullptr, tr("Invalid image selected"), tr("ImageCropper only works with 3 or more dimensions."), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); SetDefaultGUI(); return; } m_ParentWidget->setEnabled(true); m_Controls.buttonCreateNewBoundingBox->setEnabled(true); vtkSmartPointer imageMat = image->GetGeometry()->GetVtkMatrix(); // check whether the image geometry is rotated; if so, no pixel aligned cropping or masking can be performed if ((imageMat->GetElement(1, 0) == 0.0) && (imageMat->GetElement(0, 1) == 0.0) && (imageMat->GetElement(1, 2) == 0.0) && (imageMat->GetElement(2, 1) == 0.0) && (imageMat->GetElement(2, 0) == 0.0) && (imageMat->GetElement(0, 2) == 0.0)) { rotationEnabled = false; m_Controls.labelWarningRotation->setVisible(false); } else { rotationEnabled = true; m_Controls.labelWarningRotation->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); m_Controls.labelWarningRotation->setVisible(true); } this->CreateBoundingShapeInteractor(rotationEnabled); if (itk::IOPixelEnum::SCALAR == image->GetPixelType().GetPixelType()) { // Might be changed with the upcoming new image statistics plugin //(recomputation might be very expensive for large images ;) ) auto statistics = image->GetStatistics(); auto minPixelValue = statistics->GetScalarValueMin(); auto maxPixelValue = statistics->GetScalarValueMax(); if (minPixelValue < std::numeric_limits::min()) { minPixelValue = std::numeric_limits::min(); } if (maxPixelValue > std::numeric_limits::max()) { maxPixelValue = std::numeric_limits::max(); } m_Controls.spinBoxOutsidePixelValue->setEnabled(true); m_Controls.spinBoxOutsidePixelValue->setMaximum(static_cast(maxPixelValue)); m_Controls.spinBoxOutsidePixelValue->setMinimum(static_cast(minPixelValue)); m_Controls.spinBoxOutsidePixelValue->setValue(static_cast(minPixelValue)); } else { m_Controls.spinBoxOutsidePixelValue->setEnabled(false); } unsigned int dim = image->GetDimension(); if (dim < 2 || dim > 4) { m_ParentWidget->setEnabled(false); } if (m_Controls.boundingBoxSelectionWidget->GetSelectedNode().IsNotNull()) { m_Controls.buttonCropping->setEnabled(true); m_Controls.buttonMasking->setEnabled(true); m_Controls.buttonAdvancedSettings->setEnabled(true); m_Controls.groupImageSettings->setEnabled(true); } } } void QmitkImageCropperView::OnBoundingBoxSelectionChanged(QList) { auto boundingBoxNode = m_Controls.boundingBoxSelectionWidget->GetSelectedNode(); if (boundingBoxNode.IsNull()) { SetDefaultGUI(); m_BoundingShapeInteractor->EnableInteraction(false); m_BoundingShapeInteractor->SetDataNode(nullptr); if (m_Controls.imageSelectionWidget->GetSelectedNode().IsNotNull()) { m_Controls.buttonCreateNewBoundingBox->setEnabled(true); } return; } auto boundingBox = dynamic_cast(boundingBoxNode->GetData()); if (nullptr != boundingBox) { // node newly selected boundingBoxNode->SetVisibility(true); m_BoundingShapeInteractor->EnableInteraction(true); m_BoundingShapeInteractor->SetDataNode(boundingBoxNode); mitk::RenderingManager::GetInstance()->InitializeViews(); if (m_Controls.imageSelectionWidget->GetSelectedNode().IsNotNull()) { m_Controls.buttonCropping->setEnabled(true); m_Controls.buttonMasking->setEnabled(true); m_Controls.buttonAdvancedSettings->setEnabled(true); m_Controls.groupImageSettings->setEnabled(true); } } } void QmitkImageCropperView::OnCreateNewBoundingBox() { auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { return; } if (nullptr == imageNode->GetData()) { return; } QString name = QString::fromStdString(imageNode->GetName() + " Bounding Shape"); auto boundingShape = this->GetDataStorage()->GetNode(mitk::NodePredicateFunction::New([&name](const mitk::DataNode *node) { return 0 == node->GetName().compare(name.toStdString()); })); if (nullptr != boundingShape) { name = this->AdaptBoundingObjectName(name); } // get current timestep to support 3d+t images auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); const auto timePoint = renderWindowPart->GetSelectedTimePoint(); const auto imageGeometry = imageNode->GetData()->GetTimeGeometry()->GetGeometryForTimePoint(timePoint); auto boundingBox = mitk::GeometryData::New(); boundingBox->SetGeometry(static_cast(this->InitializeWithImageGeometry(imageGeometry))); auto boundingBoxNode = mitk::DataNode::New(); boundingBoxNode->SetData(boundingBox); boundingBoxNode->SetProperty("name", mitk::StringProperty::New(name.toStdString())); boundingBoxNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); boundingBoxNode->SetProperty("opacity", mitk::FloatProperty::New(0.6)); boundingBoxNode->SetProperty("layer", mitk::IntProperty::New(99)); boundingBoxNode->AddProperty("handle size factor", mitk::DoubleProperty::New(1.0 / 40.0)); boundingBoxNode->SetBoolProperty("pickable", true); if (!this->GetDataStorage()->Exists(boundingBoxNode)) { GetDataStorage()->Add(boundingBoxNode, imageNode); } m_Controls.boundingBoxSelectionWidget->SetCurrentSelectedNode(boundingBoxNode); } void QmitkImageCropperView::OnCropping() { this->ProcessImage(false); } void QmitkImageCropperView::OnMasking() { this->ProcessImage(true); } void QmitkImageCropperView::OnSliderValueChanged(int slidervalue) { m_CropOutsideValue = slidervalue; } void QmitkImageCropperView::CreateBoundingShapeInteractor(bool rotationEnabled) { if (m_BoundingShapeInteractor.IsNull()) { m_BoundingShapeInteractor = mitk::BoundingShapeInteractor::New(); m_BoundingShapeInteractor->LoadStateMachine("BoundingShapeInteraction.xml", us::ModuleRegistry::GetModule("MitkBoundingShape")); m_BoundingShapeInteractor->SetEventConfig("BoundingShapeMouseConfig.xml", us::ModuleRegistry::GetModule("MitkBoundingShape")); } m_BoundingShapeInteractor->SetRotationEnabled(rotationEnabled); } mitk::Geometry3D::Pointer QmitkImageCropperView::InitializeWithImageGeometry(const mitk::BaseGeometry* geometry) const { // convert a BaseGeometry into a Geometry3D (otherwise IO is not working properly) if (geometry == nullptr) mitkThrow() << "Geometry is not valid."; auto boundingGeometry = mitk::Geometry3D::New(); boundingGeometry->SetBounds(geometry->GetBounds()); boundingGeometry->SetImageGeometry(geometry->GetImageGeometry()); boundingGeometry->SetOrigin(geometry->GetOrigin()); boundingGeometry->SetSpacing(geometry->GetSpacing()); boundingGeometry->SetIndexToWorldTransform(geometry->GetIndexToWorldTransform()->Clone()); boundingGeometry->Modified(); return boundingGeometry; } void QmitkImageCropperView::ProcessImage(bool mask) { auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); const auto timePoint = renderWindowPart->GetSelectedTimePoint(); auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing."); return; } auto boundingBoxNode = m_Controls.boundingBoxSelectionWidget->GetSelectedNode(); if (boundingBoxNode.IsNull()) { QMessageBox::information(nullptr, "Warning", "Please load and select a cropping object before starting image processing."); return; } if (!imageNode->GetData()->GetTimeGeometry()->IsValidTimePoint(timePoint)) { QMessageBox::information(nullptr, "Warning", "Please select a time point that is within the time bounds of the selected image."); return; } const auto timeStep = imageNode->GetData()->GetTimeGeometry()->TimePointToTimeStep(timePoint); auto image = dynamic_cast(imageNode->GetData()); auto boundingBox = dynamic_cast(boundingBoxNode->GetData()); if (nullptr != image && nullptr != boundingBox) { // Check if initial node name is already in box name std::string imagePrefix = ""; if (boundingBoxNode->GetName().find(imageNode->GetName()) != 0) { imagePrefix = imageNode->GetName() + "_"; } QString imageName; if (mask) { imageName = QString::fromStdString(imagePrefix + boundingBoxNode->GetName() + "_masked"); } else { imageName = QString::fromStdString(imagePrefix + boundingBoxNode->GetName() + "_cropped"); } if (m_Controls.checkBoxCropTimeStepOnly->isChecked()) { imageName = imageName + "_T" + QString::number(timeStep); } // image and bounding shape ok, set as input auto croppedImageNode = mitk::DataNode::New(); auto cutter = mitk::BoundingShapeCropper::New(); cutter->SetGeometry(boundingBox); // adjustable in advanced settings cutter->SetUseWholeInputRegion(mask); //either mask (mask=true) or crop (mask=false) cutter->SetOutsideValue(m_CropOutsideValue); cutter->SetUseCropTimeStepOnly(m_Controls.checkBoxCropTimeStepOnly->isChecked()); cutter->SetCurrentTimeStep(timeStep); // TODO: Add support for MultiLayer (right now only Mulitlabel support) auto labelsetImageInput = dynamic_cast(image); if (nullptr != labelsetImageInput) { cutter->SetInput(labelsetImageInput); // do the actual cutting try { cutter->Update(); } catch (const itk::ExceptionObject& e) { std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); QMessageBox::warning(nullptr, tr("Cropping not possible!"), tr(message.c_str()), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); return; } auto labelSetImage = mitk::LabelSetImage::New(); labelSetImage->InitializeByLabeledImage(cutter->GetOutput()); for (unsigned int i = 0; i < labelsetImageInput->GetNumberOfLayers(); i++) { labelSetImage->AddLabelSetToLayer(i, labelsetImageInput->GetLabelSet(i)); } croppedImageNode->SetData(labelSetImage); croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString())); //add cropping result to the current data storage as child node to the image node if (!m_Controls.checkOverwriteImage->isChecked()) { if (!this->GetDataStorage()->Exists(croppedImageNode)) { this->GetDataStorage()->Add(croppedImageNode, imageNode); } } else // original image will be overwritten by the result image and the bounding box of the result is adjusted { imageNode->SetData(labelSetImage); imageNode->Modified(); // Adjust coordinate system by doing a reinit on auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); tempDataStorage->InsertElement(0, imageNode); // initialize the views to the bounding geometry auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); mitk::RenderingManager::GetInstance()->InitializeViews(bounds); } } else { cutter->SetInput(image); // do the actual cutting try { cutter->Update(); } catch (const itk::ExceptionObject& e) { std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); QMessageBox::warning(nullptr, tr("Cropping not possible!"), tr(message.c_str()), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); return; } //add cropping result to the current data storage as child node to the image node if (!m_Controls.checkOverwriteImage->isChecked()) { croppedImageNode->SetData(cutter->GetOutput()); croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString())); croppedImageNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); mitk::LevelWindow levelWindow; imageNode->GetLevelWindow(levelWindow); croppedImageNode->SetLevelWindow(levelWindow); if (!this->GetDataStorage()->Exists(croppedImageNode)) { this->GetDataStorage()->Add(croppedImageNode, imageNode); imageNode->SetVisibility(mask); // Give the user a visual clue that something happened when image was cropped } } else // original image will be overwritten by the result image and the bounding box of the result is adjusted { mitk::LevelWindow levelWindow; imageNode->GetLevelWindow(levelWindow); imageNode->SetData(cutter->GetOutput()); imageNode->SetLevelWindow(levelWindow); // Adjust coordinate system by doing a reinit on auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); tempDataStorage->InsertElement(0, imageNode); // initialize the views to the bounding geometry auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); mitk::RenderingManager::GetInstance()->InitializeViews(bounds); } } } else { QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing."); } } void QmitkImageCropperView::SetDefaultGUI() { m_Controls.buttonCreateNewBoundingBox->setEnabled(false); m_Controls.buttonCropping->setEnabled(false); m_Controls.buttonMasking->setEnabled(false); m_Controls.buttonAdvancedSettings->setEnabled(false); m_Controls.groupImageSettings->setEnabled(false); m_Controls.groupImageSettings->setVisible(false); m_Controls.checkOverwriteImage->setChecked(false); m_Controls.checkBoxCropTimeStepOnly->setChecked(false); } QString QmitkImageCropperView::AdaptBoundingObjectName(const QString& name) const { unsigned int counter = 2; QString newName = QString("%1 %2").arg(name).arg(counter); while (nullptr != this->GetDataStorage()->GetNode(mitk::NodePredicateFunction::New([&newName](const mitk::DataNode *node) { return 0 == node->GetName().compare(newName.toStdString()); }))) { newName = QString("%1 %2").arg(name).arg(++counter); } return newName; }