diff --git a/Core/Code/Interactions/mitkBindDispatcherInteractor.cpp b/Core/Code/Interactions/mitkBindDispatcherInteractor.cpp index 8bf243c4d0..f5a087b327 100644 --- a/Core/Code/Interactions/mitkBindDispatcherInteractor.cpp +++ b/Core/Code/Interactions/mitkBindDispatcherInteractor.cpp @@ -1,117 +1,117 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBindDispatcherInteractor.h" #include "mitkMessage.h" #include // us #include "mitkGetModuleContext.h" #include "mitkModule.h" #include "mitkModuleRegistry.h" #include "mitkInformer.h" mitk::BindDispatcherInteractor::BindDispatcherInteractor() : m_DataStorage(NULL),m_InformerService(NULL) { ModuleContext* context = ModuleRegistry::GetModule(1)->GetModuleContext(); if (context == NULL) { MITK_ERROR<< "BindDispatcherInteractor() - Context could not be obtained."; return; } ServiceReference serviceRef = context->GetServiceReference(); if (!serviceRef) { // the service should only be registered once m_InformerService = new InformerService(); context->RegisterService(m_InformerService); } m_Dispatcher = Dispatcher::New(); } void mitk::BindDispatcherInteractor::SetDataStorage(mitk::DataStorage::Pointer dataStorage) { // Set/Change Datastorage. This registers BDI to listen for events of DataStorage, to be informed when // a DataNode with a Interactor is added/modified/removed. // clean up events from previous datastorage UnRegisterDataStorageEvents(); m_DataStorage = dataStorage; RegisterDataStorageEvents(); } mitk::BindDispatcherInteractor::~BindDispatcherInteractor() { if (m_DataStorage.IsNotNull()) { UnRegisterDataStorageEvents(); } } void mitk::BindDispatcherInteractor::RegisterInteractor(const mitk::DataNode* dataNode) { if (m_Dispatcher.IsNotNull()) { m_Dispatcher->AddDataInteractor(dataNode); } } void mitk::BindDispatcherInteractor::RegisterDataStorageEvents() { if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.AddListener( MessageDelegate1(this, &BindDispatcherInteractor::RegisterInteractor)); m_DataStorage->RemoveNodeEvent.AddListener( MessageDelegate1(this, &BindDispatcherInteractor::UnRegisterInteractor)); m_DataStorage->ChangedNodeEvent.AddListener( MessageDelegate1(this, &BindDispatcherInteractor::RegisterInteractor)); } } void mitk::BindDispatcherInteractor::UnRegisterInteractor(const DataNode* dataNode) { if (m_Dispatcher.IsNotNull()) { m_Dispatcher->RemoveDataInteractor(dataNode); } } -mitk::Dispatcher::Pointer mitk::BindDispatcherInteractor::GetDispatcher() +const mitk::Dispatcher::Pointer mitk::BindDispatcherInteractor::GetDispatcher() { return m_Dispatcher; } void mitk::BindDispatcherInteractor::SetDispatcher(Dispatcher::Pointer dispatcher) { m_Dispatcher = dispatcher; } void mitk::BindDispatcherInteractor::UnRegisterDataStorageEvents() { if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1(this, &BindDispatcherInteractor::RegisterInteractor)); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1(this, &BindDispatcherInteractor::UnRegisterInteractor)); m_DataStorage->ChangedNodeEvent.RemoveListener( MessageDelegate1(this, &BindDispatcherInteractor::RegisterInteractor)); } } diff --git a/Core/Code/Interactions/mitkBindDispatcherInteractor.h b/Core/Code/Interactions/mitkBindDispatcherInteractor.h index 773ad37736..4636546c8e 100644 --- a/Core/Code/Interactions/mitkBindDispatcherInteractor.h +++ b/Core/Code/Interactions/mitkBindDispatcherInteractor.h @@ -1,87 +1,82 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkBindDispatcherInteractor_h #define mitkBindDispatcherInteractor_h -#include "itkObject.h" -#include "itkObjectFactory.h" #include "mitkCommon.h" #include #include "mitkDataStorage.h" #include "mitkDataNode.h" #include "mitkDispatcher.h" namespace mitk { class InformerService; /** * \class BindDispatcherInteractor * \brief This Class is used to connect a DataStorage with the Dispatcher. + * * This is done by registering for DataStorage Events and sending the Events to the registered Dispatcher. * When a DataInteractor is registered with a DataNode the Dispatcher will be notified. * Also this class registers the MicroService at which EventObservers can register to receive events. * * \ingroup Interaction */ - class MITK_CORE_EXPORT BindDispatcherInteractor: public itk::Object + class MITK_CORE_EXPORT BindDispatcherInteractor { public: - mitkClassMacro(BindDispatcherInteractor, itk::Object); - itkNewMacro(Self); + BindDispatcherInteractor(); + ~BindDispatcherInteractor(); /** * Sets the DataStorage to which Events the class subscribes. */ void SetDataStorage(DataStorage::Pointer dataStorage); /** * Sets Dispatcher which will be notified. By default each RenderWindow gets its own Dispatcher, * this function can be used to override this behavior. */ void SetDispatcher(Dispatcher::Pointer dispatcher); /** * Return currently active Dispatcher. */ - Dispatcher::Pointer GetDispatcher(); + const Dispatcher::Pointer GetDispatcher(); - protected: - - BindDispatcherInteractor(); - virtual ~BindDispatcherInteractor(); private: /** @brief Registers for events from DataStorage. * * This way whenever a DataNode is added the Dispatcher is notified about this change, and checks whether a DataInteractor * is set for this DataNode */ void RegisterInteractor(const DataNode* dataNode); void UnRegisterInteractor(const DataNode* dataNode); void RegisterDataStorageEvents(); void UnRegisterDataStorageEvents(); Dispatcher::Pointer m_Dispatcher; DataStorage::Pointer m_DataStorage; InformerService* m_InformerService; // holds reference to MicroService that notifies listeners }; } #endif /* mitkBindDispatcherInteractor_h */ diff --git a/Core/Code/Interactions/mitkDataInteractor.cpp b/Core/Code/Interactions/mitkDataInteractor.cpp index 93963e30b6..57ea5f84f7 100644 --- a/Core/Code/Interactions/mitkDataInteractor.cpp +++ b/Core/Code/Interactions/mitkDataInteractor.cpp @@ -1,121 +1,93 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDataInteractor.h" #include "mitkInteractor.h" #include "mitkPointSet.h" #include "mitkDataNode.h" #include "mitkStateMachineState.h" mitk::DataInteractor::DataInteractor() { - m_SelectionAccuracy = 3.5; } -mitk::DataNode::Pointer mitk::DataInteractor::GetDataNode() +const mitk::DataNode::Pointer mitk::DataInteractor::GetDataNode() { return m_DataNode; } void mitk::DataInteractor::SetDataNode(DataNode::Pointer dataNode) { if (dataNode == m_DataNode) return; if (m_DataNode.IsNotNull()) { // if DataInteractors' DataNode is set to null, the "old" DataNode has to be notified (else the Dispatcher won't be notified either) m_DataNode->SetDataInteractor(NULL); } m_DataNode = dataNode; if (m_DataNode.IsNotNull()) { m_DataNode->SetDataInteractor(this); } // notify implementations ... DataNodeChanged(); } int mitk::DataInteractor::GetLayer() { int layer = -1; if (m_DataNode.IsNotNull()) { m_DataNode->GetIntProperty("layer", layer); } return layer; } mitk::DataInteractor::~DataInteractor() { if (m_DataNode.IsNotNull()) { m_DataNode->SetInteractor(NULL); } } -void mitk::DataInteractor::SetAccuracy(float accuracy) -{ - m_SelectionAccuracy = accuracy; -} - void mitk::DataInteractor::ConnectActionsAndFunctions() { MITK_WARN<< "ConnectActionsAndFunctions in DataInteractor not implemented.\n DataInteractor will not be able to process any events."; } mitk::ProcessEventMode mitk::DataInteractor::GetMode() { if (GetCurrentState()->GetMode() == "PREFER_INPUT") { return PREFERINPUT; } if (GetCurrentState()->GetMode() == "GRAB_INPUT") { return GRABINPUT; } return REGULAR; } -int mitk::DataInteractor::GetPointIndexByPosition(Point3D position, int time) -{ - - // iterate over point set and check if it contains a point close enough to the pointer to be selected - PointSet* points = dynamic_cast(GetDataNode()->GetData()); - int index = -1; - if (points == NULL) - { - return index; - } - PointSet::PointsContainer* pointsContainer = points->GetPointSet(time)->GetPoints(); - float minDistance = m_SelectionAccuracy; - for (PointSet::PointsIterator it = pointsContainer->Begin(); it != pointsContainer->End(); it++) - { - float distance = sqrt(position.SquaredEuclideanDistanceTo(points->GetPoint(it->Index(), time))); // TODO: support time! - if (distance < minDistance) // if several points fall within the margin, choose the one with minimal distance to position - { // TODO: does this make sense, which unit is it? - index = it->Index(); - } - } - return index; -} void mitk::DataInteractor::DataNodeChanged() { } diff --git a/Core/Code/Interactions/mitkDataInteractor.h b/Core/Code/Interactions/mitkDataInteractor.h index ec9982b3d3..1d25c29ec4 100644 --- a/Core/Code/Interactions/mitkDataInteractor.h +++ b/Core/Code/Interactions/mitkDataInteractor.h @@ -1,126 +1,100 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKEVENTINTERACTOR_H_ #define MITKEVENTINTERACTOR_H_ -#include "itkObject.h" #include "itkSmartPointer.h" #include "itkObjectFactory.h" #include "mitkCommon.h" #include #include "mitkEventStateMachine.h" #include namespace mitk { /** * \class DataInteractor * * \brief Base class from with interactors that handle DataNodes are to be derived. * * Base class from with interactors that handle DataNodes are to be derived. * Provides an interface that is relevant for the interactor to work together with the dispatcher. * To implement a new interactor overwrite the ConnectActionsAndFunctions to connect the actions. */ // Public 'cause it's also used by the mitk::Dispatcher enum ProcessEventMode { REGULAR = 0, GRABINPUT = 1, PREFERINPUT = 2, CONNECTEDMOUSEACTION = 3 // only used by mitk::Dispatcher }; class DataNode; class MITK_CORE_EXPORT DataInteractor: public EventStateMachine { public: typedef itk::SmartPointer NodeType; - mitkClassMacro(DataInteractor, EventStateMachine); - itkNewMacro(Self); + mitkClassMacro(DataInteractor, EventStateMachine) + itkNewMacro(Self) /** * Set/Change the DataNode of the DataInteractor */ void SetDataNode(NodeType); - /** - * Sets the maximum distance that is accepted when looking for a point at a certain position using the GetPointIndexByPosition function. - */ - void SetAccuracy(float accuracy); - /** * @brief Returns the mode the DataInteractor currently is in. See in mitkDispatcher the description of m_ProcessingMode for further details. */ ProcessEventMode GetMode(); - NodeType GetDataNode(); + const NodeType GetDataNode(); int GetLayer(); protected: DataInteractor(); virtual ~DataInteractor(); /** * @brief Overwrite this function to connect actions from StateMachine description with functions. * * Following example shows how to connect the 'addpoint' action from the StateMachine XML description using the CONNECT_FUNCTION macro * with the AddPoint() function in the TestInteractor. * @code * void mitk::TestInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addpoint", AddPoint); } * @endcode */ virtual void ConnectActionsAndFunctions(); - /** - * @ brief Return index in PointSet of the point that is within given accuracy to the provided position. - * - * Assumes that the DataNode contains a PointSet, if so it iterates over all points - * in the DataNode to check if it contains a point near the pointer position. - * If a point is found its index-position is returned, else -1 is returned. - */ - virtual int GetPointIndexByPosition(Point3D position, int time = 0); - /** \brief Is called when a DataNode is initially set or changed * To be implemented by sub-classes for initialization code which require a DataNode. * \note New DataInteractors usually are expected to have the focus, but this only works if they have the highest Layer, * since empty DataNodes have a layer of -1, the DataNode must be filled here in order to get a layer assigned. * \note Is also called when the DataNode is set to NULL. - * */ virtual void DataNodeChanged(); - /** \brief Is called when a DataNode is initially set or changed - * - * To be implemented by sub-classes for initialization code which require a DataNode. - * \note New DataInteractors usually are expected to have the focus, but this only works if they have the highest Layer, - * since empty DataNodes have a layer of -1, the DataNode must be filled here in order to get a layer assigned. - * \note Is also called when the DataNode is set to NULL. - * - */ - private: NodeType m_DataNode; - float m_SelectionAccuracy; }; } /* namespace mitk */ #endif /* MITKEVENTINTERACTOR_H_ */ diff --git a/Core/Code/Interactions/mitkDispatcher.h b/Core/Code/Interactions/mitkDispatcher.h index 8750937781..51ea8e34e8 100644 --- a/Core/Code/Interactions/mitkDispatcher.h +++ b/Core/Code/Interactions/mitkDispatcher.h @@ -1,129 +1,130 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkDispatcher_h #define mitkDispatcher_h -#include "itkObject.h" +#include "itkLightObject.h" #include "itkObjectFactory.h" #include "mitkCommon.h" #include "mitkDataNode.h" #include "mitkDataInteractor.h" #include #include namespace mitk { class InternalEvent; class InteractionEvent; class InformerService; /** * \class Dispatcher * \brief Manages event distribution * * Receives Events (Mouse-,Key-, ... Events) and dispatches them to the registered DataInteractor Objects. * The order in which DataInteractors are offered to handle an event is determined by layer of their associated DataNode. * Higher layers are preferred. * * \ingroup Interaction */ - class MITK_CORE_EXPORT Dispatcher: public itk::Object + class MITK_CORE_EXPORT Dispatcher: public itk::LightObject { public: - mitkClassMacro(Dispatcher, itk::Object); + mitkClassMacro(Dispatcher, itk::LightObject); itkNewMacro(Self); typedef std::list ListInteractorType; typedef std::list > ListEventsType; /** * To post new Events which are to be handled by the Dispatcher. * * @return Returns true if the event has been handled by an DataInteractor, and false else. */ bool ProcessEvent(InteractionEvent* event); /** * Adds an Event to the Dispatchers EventQueue, these events will be processed after a a regular posted event has been fully handled. - * This allows Interactors to post their own events without interrupting regular Dispatching workflow. - * NOTES: 1) If an event is added from an other source than an Interactor / Listener its execution will be delayed until the next regular event - * comes in. - * 2) Make sure you're not causing infinite loops! + * This allows DataInteractors to post their own events without interrupting regular Dispatching workflow. + * It is important to note that the queued events will be processed AFTER the state change of a current transition (which queued the events) + * is performed. * + * \note 1) If an event is added from an other source than an DataInteractor / Observer its execution will be delayed until the next regular event + * comes in. + * \note 2) Make sure you're not causing infinite loops! */ - void QueueEvent(InteractionEvent* event); /** * Adds the DataInteractor that is associated with the DataNode to the Dispatcher Queue. * If there already exists an DataInteractor that has a reference to the same DataNode, it is removed. * Note that within this method also all other DataInteractors are checked and removed if they are no longer active, * and were not removed properly. */ void AddDataInteractor(const DataNode* dataNode); /** * Remove all DataInteractors related to this Node, to prevent double entries and dead references. */ void RemoveDataInteractor(const DataNode* dataNode); size_t GetNumberOfInteractors(); // DEBUG TESTING protected: Dispatcher(); virtual ~Dispatcher(); private: struct cmp{ bool operator()(DataInteractor::Pointer d1, DataInteractor::Pointer d2){ return (d1->GetLayer() > d2->GetLayer()); } }; std::list m_Interactors; ListEventsType m_QueuedEvents; /** * Removes all Interactors without a DataNode pointing to them, this is necessary especially when a DataNode is assigned to a new Interactor */ void RemoveOrphanedInteractors(); /** * See \ref DispatcherEventDistSection for a description of ProcessEventModes */ ProcessEventMode m_ProcessingMode; DataInteractor::Pointer m_SelectedInteractor; void SetEventProcessingMode(DataInteractor::Pointer); /** * Function to handle special internal events, * such as events that are directed at a specific DataInteractor, * or the request to delete an Interactor and its DataNode. */ bool HandleInternalEvent(InternalEvent* internalEvent); /** * Hold microservice reference to object that takes care of informing the EventObservers about InteractionEvents */ InformerService* m_InformerService; }; } /* namespace mitk */ #endif /* mitkDispatcher_h */ diff --git a/Core/Code/Interactions/mitkEventHandler.h b/Core/Code/Interactions/mitkEventHandler.h index f376ccf09a..a621ac0b2a 100644 --- a/Core/Code/Interactions/mitkEventHandler.h +++ b/Core/Code/Interactions/mitkEventHandler.h @@ -1,83 +1,83 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKEVENTHANDLER_H_ #define MITKEVENTHANDLER_H_ -#include "itkObject.h" +#include "itkLightObject.h" #include "itkObjectFactory.h" #include "mitkEvent.h" #include "mitkCommon.h" #include #include "mitkEventConfig.h" #include "mitkPropertyList.h" #include #include namespace mitk { /** * \class EventHandler * Base Class for Interaction handling classes. * Handles loading of configuration object and mapping of events to variant names. */ class InteractionEvent; - class MITK_CORE_EXPORT EventHandler : public itk::Object { + class MITK_CORE_EXPORT EventHandler : public itk::LightObject { public: - mitkClassMacro(EventHandler, itk::Object); - itkNewMacro(Self); + mitkClassMacro(EventHandler, itk::LightObject) + itkNewMacro(Self) /** * @brief Loads XML resource * * Loads a XML resource file in the given module context. * Default is the Mitk module (core). * The files have to be placed in the Resources/Interaction folder of their respective module. * This method will remove all existing configuration and replaces it with the new one. */ virtual bool LoadEventConfig(std::string filename, std::string moduleName = "Mitk"); /** * This method EXTENDs the configuration. * The configuration from the resource provided is loaded and only the ones conflicting are replaced by the new one. * This way several configuration files can be combined */ virtual bool AddEventConfig(std::string filename, std::string moduleName = "Mitk"); protected: EventHandler(); virtual ~EventHandler(); /** * Returns a PropertyList in which the parameters defined in the config file are listed. */ PropertyList::Pointer GetAttributes(); std::string MapToEventVariant(InteractionEvent* interactionEvent); /** * Is called whenever a new config object ist set. * Overwrite this method e.g. to initialize EventHandler with parameters in configuration file. */ virtual void ConfigurationChanged(); private: vtkSmartPointer m_EventConfig; }; } /* namespace mitk */ #endif /* MITKEVENTHANDLER_H_ */ diff --git a/Core/Code/Interactions/mitkEventStateMachine.h b/Core/Code/Interactions/mitkEventStateMachine.h index 26266be58d..72915c4639 100644 --- a/Core/Code/Interactions/mitkEventStateMachine.h +++ b/Core/Code/Interactions/mitkEventStateMachine.h @@ -1,176 +1,176 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKEVENTSTATEMACHINE_H_ #define MITKEVENTSTATEMACHINE_H_ #include "itkObject.h" #include "itkObjectFactory.h" #include "mitkCommon.h" #include "mitkEventHandler.h" #include #include namespace mitk { class StateMachineContainer; class StateMachineAction; class InteractionEvent; class StateMachineState; class DataNode; /** * \class TActionFunctor * \brief Base class of ActionFunctors, to provide an easy to connect actions with functions. */ class MITK_CORE_EXPORT TActionFunctor { public: virtual bool DoAction(StateMachineAction*, InteractionEvent*)=0; virtual ~TActionFunctor() { } }; /** * \class TSpecificActionFunctor * Specific implementation of ActionFunctor class, implements a reference to the function which is to be executed. It takes two arguments: * StateMachineAction - the action by which the function call is invoked, InteractionEvent - the event that caused the transition. */ template class TSpecificActionFunctor: public TActionFunctor { public: TSpecificActionFunctor(T* object, bool (T::*memberFunctionPointer)(StateMachineAction*, InteractionEvent*)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } virtual ~TSpecificActionFunctor() { } virtual bool DoAction(StateMachineAction* action, InteractionEvent* event) { return (*m_Object.*m_MemberFunctionPointer)(action, event);// executes member function } private: T* m_Object; bool (T::*m_MemberFunctionPointer)(StateMachineAction*, InteractionEvent*); }; /** Macro that can be used to connect a StateMachineAction with a function. * It assumes that there is a typedef Classname Self in classes that use this macro, as is provided by e.g. mitkClassMacro */ #define CONNECT_FUNCTION(a, f) \ EventStateMachine::AddActionFunction(a, new TSpecificActionFunctor(this, &Self::f)); /** * \class EventStateMachine * * \brief Super-class that provides the functionality of a StateMachine to DataInteractors. * * A state machine is created by loading a state machine pattern. It consists of states, transitions and action. * The state represent the current status of the interaction, transitions are means to switch between states. Each transition * is triggered by an event and it is associated with actions that are to be executed when the state change is performed. * */ class MITK_CORE_EXPORT EventStateMachine: public virtual EventHandler { public: - mitkClassMacro(EventStateMachine, EventHandler); - itkNewMacro(Self); + mitkClassMacro(EventStateMachine, EventHandler) + itkNewMacro(Self) typedef std::map ActionFunctionsMapType; typedef itk::SmartPointer StateMachineStateType; /** * @brief Loads XML resource * * Loads a XML resource file in the given module context. * Default is the Mitk module (core). * The files have to be placed in the Resources/Interaction folder of their respective module. **/ bool LoadStateMachine(std::string filename, std::string moduleName="Mitk"); /** * Receives Event from Dispatcher. * Event is mapped using the EventConfig Object to a variant, then it is checked if the StateMachine is listening for * such an Event. If this is the case, the transition to the next state it performed and all actions associated with the transition executed, * and true is returned to the caller. * If the StateMachine can't handle this event false is returned. * Attention: * If a transition is associated with multiple actions - "true" is returned if one action returns true, * and the event is treated as HANDLED even though some actions might not have been executed! So be sure that all actions that occur within * one transitions have the same conditions. */ bool HandleEvent(InteractionEvent* event, DataNode* dataNode); protected: EventStateMachine(); virtual ~EventStateMachine(); /** * Connects action from StateMachine (String in XML file) with a function that is called when this action is to be executed. */ void AddActionFunction(std::string action, TActionFunctor* functor); StateMachineState* GetCurrentState(); /** * Is called after loading a statemachine. * Overwrite this function in specific interactor implementations. * Connect actions and functions using the CONNECT_FUNCTION macro within this function. */ virtual void ConnectActionsAndFunctions(); /** * Looks up function that is associated with action and executes it. * To implement your own execution scheme overwrite this in your DataInteractor. */ virtual bool ExecuteAction(StateMachineAction* action, InteractionEvent* interactionEvent); /** * Implements filter scheme for events. * Standard implementation accepts events from 2d and 3d windows, * and rejects events if DataNode is not visible. * \return true if event is accepted, else false * * Overwrite this function to adapt for your own needs, for example to filter out events from * 3d windows like this: \code bool mitk::EventStateMachine::FilterEvents(InteractionEvent* interactionEvent, DataNode*dataNode) { return interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard2D; // only 2D mappers } \endcode * or to enforce that the interactor only reacts when the corresponding DataNode is selected in the DataManager view.. */ virtual bool FilterEvents(InteractionEvent* interactionEvent, DataNode* dataNode); private: StateMachineContainer* m_StateMachineContainer; // storage of all states, action, transitions on which the statemachine operates. ActionFunctionsMapType m_ActionFunctionsMap; // stores association between action string StateMachineStateType m_CurrentState; }; } /* namespace mitk */ #endif /* MITKEVENTSTATEMACHINE_H_ */ diff --git a/Core/Code/Interactions/mitkPointSetDataInteractor.cpp b/Core/Code/Interactions/mitkPointSetDataInteractor.cpp index e8902028a7..c4c7416a21 100644 --- a/Core/Code/Interactions/mitkPointSetDataInteractor.cpp +++ b/Core/Code/Interactions/mitkPointSetDataInteractor.cpp @@ -1,304 +1,333 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSetDataInteractor.h" #include "mitkMouseMoveEvent.h" #include #include "mitkInteractionConst.h" // TODO: refactor file #include "mitkRenderingManager.h" #include "mitkInternalEvent.h" // #include "mitkDispatcher.h" #include "mitkBaseRenderer.h" void mitk::PointSetDataInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addpoint", AddPoint); CONNECT_FUNCTION("selectpoint", SelectPoint); CONNECT_FUNCTION("unselect", UnSelectPoint); CONNECT_FUNCTION("initMove", InitMove); CONNECT_FUNCTION("initMovePointSet", InitMoveAll); CONNECT_FUNCTION("movePoint", MovePoint); CONNECT_FUNCTION("movePointSet", MoveSet); CONNECT_FUNCTION("finishMovement", FinishMove); CONNECT_FUNCTION("removePoint", RemovePoint); } bool mitk::PointSetDataInteractor::AddPoint(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { // Find the position, the point is to be added to: first entry with // empty index. If the Set is empty, then start with 0. if not empty, // then take the first index which is not occupied int lastPosition = 0; PointSet::PointsContainer* pointsContainer = m_PointSet->GetPointSet(0)->GetPoints(); if (!pointsContainer->empty()) { mitk::PointSet::PointsIterator it, end; it = pointsContainer->Begin(); end = pointsContainer->End(); while (it != end) { if (!pointsContainer->IndexExists(lastPosition)) break; ++it; ++lastPosition; } } InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { IsClosedContour(stateMachineAction, interactionEvent); // Get time step from BaseRenderer int timeStep = positionEvent->GetSender()->GetTimeStep(); mitk::Point3D point = positionEvent->GetPositionInWorld(); m_PointSet->InsertPoint(lastPosition, point, timeStep); m_NumberOfPoints++; GetDataNode()->SetData(m_PointSet); GetDataNode()->Modified(); if (m_MaxNumberOfPoints != 0 && m_NumberOfPoints >= m_MaxNumberOfPoints) { InternalEvent::Pointer event = InternalEvent::New(NULL, this, "MaxNumberOfPoints"); positionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } else { return false; } } bool mitk::PointSetDataInteractor::SelectPoint(StateMachineAction*, InteractionEvent* interactionEvent) { InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { int timeStep = positionEvent->GetSender()->GetTimeStep(); Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected if (GetPointIndexByPosition(point, timeStep) != -1) { //TODO FIXME is this safe ? can pointset and renderer have different sizes ? m_SelectedPointIndex = GetPointIndexByPosition(point, timeStep); GetDataNode()->SetProperty("contourcolor", ColorProperty::New(0.0, 0.0, 1.0)); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } return false; } else { return false; } } mitk::PointSetDataInteractor::PointSetDataInteractor() : - m_NumberOfPoints(0), m_MaxNumberOfPoints(0),m_SelectedPointIndex(-1) + m_NumberOfPoints(0), m_MaxNumberOfPoints(0),m_SelectedPointIndex(-1),m_SelectionAccuracy(3.5) { } mitk::PointSetDataInteractor::~PointSetDataInteractor() { } bool mitk::PointSetDataInteractor::RemovePoint(StateMachineAction*, InteractionEvent*) { if (m_SelectedPointIndex != -1) { Point3D point; PointOperation* doOp = new PointOperation(mitk::OpREMOVE, point, m_SelectedPointIndex); GetDataNode()->GetData()->ExecuteOperation(doOp); m_SelectedPointIndex = -1; mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_NumberOfPoints--; return true; } else { return false; } } bool mitk::PointSetDataInteractor::IsClosedContour(StateMachineAction*, InteractionEvent* interactionEvent) { InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { int timeStep = positionEvent->GetSender()->GetTimeStep(); Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected if (GetPointIndexByPosition(point, timeStep) != -1 && m_PointSet->GetSize(timeStep) >= 3) { InternalEvent::Pointer event = InternalEvent::New(NULL, this, "ClosedContour"); positionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); return true; } } return false; } bool mitk::PointSetDataInteractor::MovePoint(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { IsClosedContour(stateMachineAction, interactionEvent); // Get time step from BaseRenderer int timeStep = positionEvent->GetSender()->GetTimeStep(); mitk::Point3D point = positionEvent->GetPositionInWorld(); m_PointSet->SetPoint(m_SelectedPointIndex, point, timeStep); GetDataNode()->SetData(m_PointSet); GetDataNode()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); IsClosedContour(stateMachineAction,interactionEvent); return true; } else { return false; } } bool mitk::PointSetDataInteractor::MoveSet(StateMachineAction*, InteractionEvent* interactionEvent) { InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { int timeStep = positionEvent->GetSender()->GetTimeStep(); // Vector that represents movement relative to last position Point3D movementVector; movementVector[0] = positionEvent->GetPositionInWorld()[0] - m_PointSet->GetPoint(m_SelectedPointIndex, timeStep)[0]; movementVector[1] = positionEvent->GetPositionInWorld()[1] - m_PointSet->GetPoint(m_SelectedPointIndex, timeStep)[1]; movementVector[2] = positionEvent->GetPositionInWorld()[2] - m_PointSet->GetPoint(m_SelectedPointIndex, timeStep)[2]; PointSet* points = dynamic_cast(GetDataNode()->GetData()); PointSet::PointsContainer* pointsContainer = points->GetPointSet(timeStep)->GetPoints(); // Iterate over point set and update each point Point3D newPoint; for (PointSet::PointsIterator it = pointsContainer->Begin(); it != pointsContainer->End(); it++) { newPoint[0] = m_PointSet->GetPoint(it->Index(), timeStep)[0] + movementVector[0]; newPoint[1] = m_PointSet->GetPoint(it->Index(), timeStep)[1] + movementVector[1]; newPoint[2] = m_PointSet->GetPoint(it->Index(), timeStep)[2] + movementVector[2]; m_PointSet->SetPoint(it->Index(), newPoint, timeStep); } GetDataNode()->SetData(m_PointSet); GetDataNode()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } else { return false; } } bool mitk::PointSetDataInteractor::UnSelectPoint(StateMachineAction*, InteractionEvent* interactionEvent) { InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { int timeStep = positionEvent->GetSender()->GetTimeStep(); Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected int index = GetPointIndexByPosition(point, timeStep); // here it is ensured that we don't switch from one point being selected to another one being selected, // without accepting the unselect of the current point if (index == -1 || index != m_SelectedPointIndex) { m_SelectedPointIndex = -1; GetDataNode()->SetProperty("contourcolor", ColorProperty::New(1.0, 0.0, 1.0)); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } return false; } else { return false; } } bool mitk::PointSetDataInteractor::Abort(StateMachineAction*, InteractionEvent* interactionEvent) { InternalEvent::Pointer event = InternalEvent::New(NULL, this, IntDeactivateMe); interactionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); return true; } /* * Check whether the DataNode contains a pointset, if not create one and add it. */ void mitk::PointSetDataInteractor::DataNodeChanged() { if (GetDataNode().IsNotNull()) { // find proper place for this command! // maybe when DN is created ? GetDataNode()->SetBoolProperty("show contour", true); PointSet* points = dynamic_cast(GetDataNode()->GetData()); if (points == NULL) { m_PointSet = PointSet::New(); GetDataNode()->SetData(m_PointSet); } else { m_PointSet = points; } // load config file parameter: maximal number of points mitk::PropertyList::Pointer properties = GetAttributes(); std::string strNumber; if (properties->GetStringProperty("MaxPoints", strNumber)) { m_MaxNumberOfPoints = atoi(strNumber.c_str()); } } } bool mitk::PointSetDataInteractor::InitMove(StateMachineAction*, InteractionEvent*) { GetDataNode()->SetProperty("contourcolor", ColorProperty::New(1.0, 1.0, 1.0)); return true; } bool mitk::PointSetDataInteractor::FinishMove(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { IsClosedContour(stateMachineAction, interactionEvent); GetDataNode()->SetProperty("contourcolor", ColorProperty::New(1.0, 0.0, 0.0)); return true; } bool mitk::PointSetDataInteractor::InitMoveAll(StateMachineAction*, InteractionEvent* interactionEvent) { InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { m_LastMovePosition = positionEvent->GetPositionInWorld(); return true; } else { return false; } } + +void mitk::PointSetDataInteractor::SetAccuracy(float accuracy) +{ + m_SelectionAccuracy = accuracy; +} + +int mitk::PointSetDataInteractor::GetPointIndexByPosition(Point3D position, int time) +{ + + // iterate over point set and check if it contains a point close enough to the pointer to be selected + PointSet* points = dynamic_cast(GetDataNode()->GetData()); + int index = -1; + if (points == NULL) + { + return index; + } + PointSet::PointsContainer* pointsContainer = points->GetPointSet(time)->GetPoints(); + + float minDistance = m_SelectionAccuracy; + for (PointSet::PointsIterator it = pointsContainer->Begin(); it != pointsContainer->End(); it++) + { + float distance = sqrt(position.SquaredEuclideanDistanceTo(points->GetPoint(it->Index(), time))); // TODO: support time! + if (distance < minDistance) // if several points fall within the margin, choose the one with minimal distance to position + { // TODO: does this make sense, which unit is it? + index = it->Index(); + } + } + return index; +} diff --git a/Core/Code/Interactions/mitkPointSetDataInteractor.h b/Core/Code/Interactions/mitkPointSetDataInteractor.h index 4fbef1a603..75bee9b476 100644 --- a/Core/Code/Interactions/mitkPointSetDataInteractor.h +++ b/Core/Code/Interactions/mitkPointSetDataInteractor.h @@ -1,124 +1,141 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkPointSetDataInteractor_h_ #define mitkPointSetDataInteractor_h_ #include "itkObject.h" #include "itkSmartPointer.h" #include "itkObjectFactory.h" #include "mitkCommon.h" #include #include "mitkDataInteractor.h" #include namespace mitk { /** * Class PointSetDataInteractor * \brief Implementation of the PointSetInteractor * * Interactor operates on a point set and supports to: * - add points * - remove points * - move single points * - move complete pointset * - select/unselect a point * * in 2d and 3d render windows. */ // Inherit from DataInteratcor, this provides functionality of a state machine and configurable inputs. class MITK_CORE_EXPORT PointSetDataInteractor: public DataInteractor { public: - mitkClassMacro(PointSetDataInteractor, DataInteractor); - itkNewMacro(Self); + mitkClassMacro(PointSetDataInteractor, DataInteractor) + itkNewMacro(Self) protected: PointSetDataInteractor(); virtual ~PointSetDataInteractor(); /** * 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. */ virtual void ConnectActionsAndFunctions(); /** * This function is called when a DataNode has been set/changed. * It is used to initialize the DataNode, e.g. if no PointSet exists yet it is created * and added to the DataNode. */ virtual void DataNodeChanged(); + + /** + * Sets the maximum distance that is accepted when looking for a point at a certain position using the GetPointIndexByPosition function. + */ + void SetAccuracy(float accuracy); + + /** + * @ brief Return index in PointSet of the point that is within given accuracy to the provided position. + * + * Assumes that the DataNode contains a PointSet, if so it iterates over all points + * in the DataNode to check if it contains a point near the pointer position. + * If a point is found its index-position is returned, else -1 is returned. + */ + virtual int GetPointIndexByPosition(Point3D position, int time = 0); + /** Adds a point at the given coordinates. * Every time a point is added it is also checked if the maximal number of points is reached, * and if so an InternalEvent with the signal name "MaxNumberOfPoints" is triggered. */ virtual bool AddPoint(StateMachineAction*, InteractionEvent*); /** Removes point that is selected */ virtual bool RemovePoint(StateMachineAction*, InteractionEvent*); /** * Checks if new point is close enough to an old one, * if so, trigger the ClosedContour signal which can be caught by the state machine. */ virtual bool IsClosedContour(StateMachineAction*, InteractionEvent*); /** * Moves the currenlty selected point to the new coodinates. */ virtual bool MovePoint(StateMachineAction*, InteractionEvent*); /** * Initializes the movement, stores starting position. */ virtual bool InitMove(StateMachineAction*, InteractionEvent*); /** * Is called when a movement is finished, changes back to regular color. */ virtual bool FinishMove(StateMachineAction*, InteractionEvent*); /** * Stores original position from which movement can be calculated. */ virtual bool InitMoveAll(StateMachineAction*, InteractionEvent*); /** * Moves all points of the PointSet by the same vector, which is the relative distance from the * initialization point. */ virtual bool MoveSet(StateMachineAction*, InteractionEvent*); /** * Selects a point from the PointSet as currently active. */ virtual bool SelectPoint(StateMachineAction*, InteractionEvent*); /** * Unselects a point, e.g. the pointer coordinates point to empty space. */ virtual bool UnSelectPoint(StateMachineAction*, InteractionEvent*); /** * Calls for inactivation of the DataInteractor */ virtual bool Abort(StateMachineAction*, InteractionEvent*); private: PointSet::Pointer m_PointSet; int m_NumberOfPoints; // to keep track of number of points in pointset int m_MaxNumberOfPoints; // maximum of allowed number of points int m_SelectedPointIndex; // index of currently selected point in PointSet /** member to keep track of PointSet movements */ Point3D m_LastMovePosition; + + float m_SelectionAccuracy; // accuracy that's needed to select a point }; } #endif diff --git a/Core/Code/Rendering/mitkBaseRenderer.cpp b/Core/Code/Rendering/mitkBaseRenderer.cpp index 845a0b27e2..4af01eb1b6 100644 --- a/Core/Code/Rendering/mitkBaseRenderer.cpp +++ b/Core/Code/Rendering/mitkBaseRenderer.cpp @@ -1,830 +1,835 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBaseRenderer.h" #include "mitkMapper.h" #include "mitkResliceMethodProperty.h" // Geometries #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" // Controllers #include "mitkCameraController.h" #include "mitkSliceNavigationController.h" #include "mitkCameraRotationController.h" #include "mitkVtkInteractorCameraController.h" #ifdef MITK_USE_TD_MOUSE #include "mitkTDMouseVtkCameraController.h" #else #include "mitkCameraController.h" #endif #include "mitkVtkLayerController.h" // Events // TODO: INTERACTION_LEGACY #include "mitkEventMapper.h" #include "mitkGlobalInteraction.h" #include "mitkPositionEvent.h" #include "mitkDisplayPositionEvent.h" #include "mitkProperties.h" #include "mitkWeakPointerProperty.h" #include "mitkInteractionConst.h" // VTK #include #include #include #include #include #include #include mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::baseRendererMap; mitk::BaseRenderer* mitk::BaseRenderer::GetInstance(vtkRenderWindow * renWin) { for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); mapit++) { if ((*mapit).first == renWin) return (*mapit).second; } return NULL; } void mitk::BaseRenderer::AddInstance(vtkRenderWindow* renWin, BaseRenderer* baseRenderer) { if (renWin == NULL || baseRenderer == NULL) return; // ensure that no BaseRenderer is managed twice mitk::BaseRenderer::RemoveInstance(renWin); baseRendererMap.insert(BaseRendererMapType::value_type(renWin, baseRenderer)); } void mitk::BaseRenderer::RemoveInstance(vtkRenderWindow* renWin) { BaseRendererMapType::iterator mapit = baseRendererMap.find(renWin); if (mapit != baseRendererMap.end()) baseRendererMap.erase(mapit); } mitk::BaseRenderer* mitk::BaseRenderer::GetByName(const std::string& name) { for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); mapit++) { if ((*mapit).second->m_Name == name) return (*mapit).second; } return NULL; } vtkRenderWindow* mitk::BaseRenderer::GetRenderWindowByName(const std::string& name) { for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); mapit++) { if ((*mapit).second->m_Name == name) return (*mapit).first; } return NULL; } mitk::BaseRenderer::BaseRenderer(const char* name, vtkRenderWindow * renWin, mitk::RenderingManager* rm) : m_RenderWindow(NULL), m_VtkRenderer(NULL), m_MapperID(defaultMapper), m_DataStorage(NULL), m_RenderingManager(rm), m_LastUpdateTime(0), m_CameraController( NULL), m_SliceNavigationController(NULL), m_CameraRotationController(NULL), /*m_Size(),*/ m_Focused(false), m_WorldGeometry(NULL), m_TimeSlicedWorldGeometry(NULL), m_CurrentWorldGeometry(NULL), m_CurrentWorldGeometry2D(NULL), m_DisplayGeometry( NULL), m_Slice(0), m_TimeStep(), m_CurrentWorldGeometry2DUpdateTime(), m_DisplayGeometryUpdateTime(), m_TimeStepUpdateTime(), m_WorldGeometryData( NULL), m_DisplayGeometryData(NULL), m_CurrentWorldGeometry2DData(NULL), m_WorldGeometryNode(NULL), m_DisplayGeometryNode(NULL), m_CurrentWorldGeometry2DNode( NULL), m_DisplayGeometryTransformTime(0), m_CurrentWorldGeometry2DTransformTime(0), m_Name(name), /*m_Bounds(),*/m_EmptyWorldGeometry( true), m_DepthPeelingEnabled(true), m_MaxNumberOfPeels(100), m_NumberOfVisibleLODEnabledMappers(0) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; if (name != NULL) { m_Name = name; } else { m_Name = "unnamed renderer"; itkWarningMacro(<< "Created unnamed renderer. Bad for serialization. Please choose a name."); } if (renWin != NULL) { m_RenderWindow = renWin; m_RenderWindow->Register(NULL); } else { itkWarningMacro(<< "Created mitkBaseRenderer without vtkRenderWindow present."); } m_Size[0] = 0; m_Size[1] = 0; //instances.insert( this ); //adding this BaseRenderer to the List of all BaseRenderer // TODO: INTERACTION_LEGACY m_RenderingManager->GetGlobalInteraction()->AddFocusElement(this); - m_BindDispatcherInteractor = mitk::BindDispatcherInteractor::New(); + m_BindDispatcherInteractor = new mitk::BindDispatcherInteractor(); WeakPointerProperty::Pointer rendererProp = WeakPointerProperty::New((itk::Object*) this); m_CurrentWorldGeometry2D = mitk::PlaneGeometry::New(); m_CurrentWorldGeometry2DData = mitk::Geometry2DData::New(); m_CurrentWorldGeometry2DData->SetGeometry2D(m_CurrentWorldGeometry2D); m_CurrentWorldGeometry2DNode = mitk::DataNode::New(); m_CurrentWorldGeometry2DNode->SetData(m_CurrentWorldGeometry2DData); m_CurrentWorldGeometry2DNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_CurrentWorldGeometry2DNode->GetPropertyList()->SetProperty("layer", IntProperty::New(1000)); m_CurrentWorldGeometry2DNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); m_CurrentWorldGeometry2DNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(1)); m_CurrentWorldGeometry2DTransformTime = m_CurrentWorldGeometry2DNode->GetVtkTransform()->GetMTime(); m_DisplayGeometry = mitk::DisplayGeometry::New(); m_DisplayGeometry->SetWorldGeometry(m_CurrentWorldGeometry2D); m_DisplayGeometryData = mitk::Geometry2DData::New(); m_DisplayGeometryData->SetGeometry2D(m_DisplayGeometry); m_DisplayGeometryNode = mitk::DataNode::New(); m_DisplayGeometryNode->SetData(m_DisplayGeometryData); m_DisplayGeometryNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_DisplayGeometryTransformTime = m_DisplayGeometryNode->GetVtkTransform()->GetMTime(); mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::SliceNavigationController::New("navigation"); sliceNavigationController->SetRenderer(this); sliceNavigationController->ConnectGeometrySliceEvent(this); sliceNavigationController->ConnectGeometryUpdateEvent(this); sliceNavigationController->ConnectGeometryTimeEvent(this, false); m_SliceNavigationController = sliceNavigationController; m_CameraRotationController = mitk::CameraRotationController::New(); m_CameraRotationController->SetRenderWindow(m_RenderWindow); m_CameraRotationController->AcquireCamera(); //if TD Mouse Interaction is activated, then call TDMouseVtkCameraController instead of VtkInteractorCameraController #ifdef MITK_USE_TD_MOUSE m_CameraController = mitk::TDMouseVtkCameraController::New(); #else m_CameraController = mitk::CameraController::New(NULL); #endif m_VtkRenderer = vtkRenderer::New(); if (mitk::VtkLayerController::GetInstance(m_RenderWindow) == NULL) { mitk::VtkLayerController::AddInstance(m_RenderWindow, m_VtkRenderer); mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } else mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } mitk::BaseRenderer::~BaseRenderer() { if (m_VtkRenderer != NULL) { m_VtkRenderer->Delete(); m_VtkRenderer = NULL; } if (m_CameraController.IsNotNull()) m_CameraController->SetRenderer(NULL); m_RenderingManager->GetGlobalInteraction()->RemoveFocusElement(this); mitk::VtkLayerController::RemoveInstance(m_RenderWindow); RemoveAllLocalStorages(); m_DataStorage = NULL; + if (m_BindDispatcherInteractor != NULL) + { + delete m_BindDispatcherInteractor; + } + if (m_RenderWindow != NULL) { m_RenderWindow->Delete(); m_RenderWindow = NULL; } } void mitk::BaseRenderer::RemoveAllLocalStorages() { this->InvokeEvent(mitk::BaseRenderer::RendererResetEvent()); std::list::iterator it; for (it = m_RegisteredLocalStorageHandlers.begin(); it != m_RegisteredLocalStorageHandlers.end(); it++) (*it)->ClearLocalStorage(this, false); m_RegisteredLocalStorageHandlers.clear(); } void mitk::BaseRenderer::RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.push_back(lsh); } -mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() +const mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() { return m_BindDispatcherInteractor->GetDispatcher(); } mitk::Point3D mitk::BaseRenderer::Map2DRendererPositionTo3DWorldPosition(Point2D* mousePosition) const { Point2D p_mm; Point3D position; if (m_MapperID == 1) { GetDisplayGeometry()->ULDisplayToDisplay(*mousePosition, *mousePosition); GetDisplayGeometry()->DisplayToWorld(*mousePosition, p_mm); GetDisplayGeometry()->Map(p_mm, position); } else if (m_MapperID == 2) { GetDisplayGeometry()->ULDisplayToDisplay(*mousePosition, *mousePosition); PickWorldPoint(*mousePosition, position); } return position; } void mitk::BaseRenderer::UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.remove(lsh); } void mitk::BaseRenderer::SetDataStorage(DataStorage* storage) { if (storage != NULL) { m_DataStorage = storage; m_BindDispatcherInteractor->SetDataStorage(m_DataStorage); this->Modified(); } } const mitk::BaseRenderer::MapperSlotId mitk::BaseRenderer::defaultMapper = 1; void mitk::BaseRenderer::Paint() { } void mitk::BaseRenderer::Initialize() { } void mitk::BaseRenderer::Resize(int w, int h) { m_Size[0] = w; m_Size[1] = h; if (m_CameraController) m_CameraController->Resize(w, h); //(formerly problematic on windows: vtkSizeBug) GetDisplayGeometry()->SetSizeInDisplayUnits(w, h); } void mitk::BaseRenderer::InitRenderer(vtkRenderWindow* renderwindow) { if (m_RenderWindow != NULL) { m_RenderWindow->Delete(); } m_RenderWindow = renderwindow; if (m_RenderWindow != NULL) { m_RenderWindow->Register(NULL); } RemoveAllLocalStorages(); if (m_CameraController.IsNotNull()) { m_CameraController->SetRenderer(this); } //BUG (#1551) added settings for depth peeling m_RenderWindow->SetAlphaBitPlanes(1); m_VtkRenderer->SetUseDepthPeeling(m_DepthPeelingEnabled); m_VtkRenderer->SetMaximumNumberOfPeels(m_MaxNumberOfPeels); m_VtkRenderer->SetOcclusionRatio(0.1); } void mitk::BaseRenderer::InitSize(int w, int h) { m_Size[0] = w; m_Size[1] = h; GetDisplayGeometry()->SetSizeInDisplayUnits(w, h, false); GetDisplayGeometry()->Fit(); } void mitk::BaseRenderer::SetSlice(unsigned int slice) { if (m_Slice != slice) { m_Slice = slice; if (m_TimeSlicedWorldGeometry.IsNotNull()) { SlicedGeometry3D* slicedWorldGeometry = dynamic_cast(m_TimeSlicedWorldGeometry->GetGeometry3D(m_TimeStep)); if (slicedWorldGeometry != NULL) { if (m_Slice >= slicedWorldGeometry->GetSlices()) m_Slice = slicedWorldGeometry->GetSlices() - 1; SetCurrentWorldGeometry2D(slicedWorldGeometry->GetGeometry2D(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } void mitk::BaseRenderer::SetTimeStep(unsigned int timeStep) { if (m_TimeStep != timeStep) { m_TimeStep = timeStep; m_TimeStepUpdateTime.Modified(); if (m_TimeSlicedWorldGeometry.IsNotNull()) { if (m_TimeStep >= m_TimeSlicedWorldGeometry->GetTimeSteps()) m_TimeStep = m_TimeSlicedWorldGeometry->GetTimeSteps() - 1; SlicedGeometry3D* slicedWorldGeometry = dynamic_cast(m_TimeSlicedWorldGeometry->GetGeometry3D(m_TimeStep)); if (slicedWorldGeometry != NULL) { SetCurrentWorldGeometry2D(slicedWorldGeometry->GetGeometry2D(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } int mitk::BaseRenderer::GetTimeStep(const mitk::BaseData* data) const { if ((data == NULL) || (data->IsInitialized() == false)) { return -1; } return data->GetTimeSlicedGeometry()->MSToTimeStep(GetTime()); } mitk::ScalarType mitk::BaseRenderer::GetTime() const { if (m_TimeSlicedWorldGeometry.IsNull()) { return 0; } else { ScalarType timeInMS = m_TimeSlicedWorldGeometry->TimeStepToMS(GetTimeStep()); if (timeInMS == ScalarTypeNumericTraits::NonpositiveMin()) return 0; else return timeInMS; } } void mitk::BaseRenderer::SetWorldGeometry(mitk::Geometry3D* geometry) { itkDebugMacro("setting WorldGeometry to " << geometry); if (m_WorldGeometry != geometry) { if (geometry->GetBoundingBox()->GetDiagonalLength2() == 0) return; m_WorldGeometry = geometry; m_TimeSlicedWorldGeometry = dynamic_cast(geometry); SlicedGeometry3D* slicedWorldGeometry; if (m_TimeSlicedWorldGeometry.IsNotNull()) { itkDebugMacro("setting TimeSlicedWorldGeometry to " << m_TimeSlicedWorldGeometry); if (m_TimeStep >= m_TimeSlicedWorldGeometry->GetTimeSteps()) m_TimeStep = m_TimeSlicedWorldGeometry->GetTimeSteps() - 1; slicedWorldGeometry = dynamic_cast(m_TimeSlicedWorldGeometry->GetGeometry3D(m_TimeStep)); } else { slicedWorldGeometry = dynamic_cast(geometry); } Geometry2D::Pointer geometry2d; if (slicedWorldGeometry != NULL) { if (m_Slice >= slicedWorldGeometry->GetSlices() && (m_Slice != 0)) m_Slice = slicedWorldGeometry->GetSlices() - 1; geometry2d = slicedWorldGeometry->GetGeometry2D(m_Slice); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(slicedWorldGeometry); geometry2d = plane; } SetCurrentWorldGeometry(slicedWorldGeometry); } else { geometry2d = dynamic_cast(geometry); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = PlaneGeometry::New(); plane->InitializeStandardPlane(geometry); geometry2d = plane; } SetCurrentWorldGeometry(geometry); } SetCurrentWorldGeometry2D(geometry2d); // calls Modified() } if (m_CurrentWorldGeometry2D.IsNull()) itkWarningMacro("m_CurrentWorldGeometry2D is NULL"); } void mitk::BaseRenderer::SetDisplayGeometry(mitk::DisplayGeometry* geometry2d) { itkDebugMacro("setting DisplayGeometry to " << geometry2d); if (m_DisplayGeometry != geometry2d) { m_DisplayGeometry = geometry2d; m_DisplayGeometryData->SetGeometry2D(m_DisplayGeometry); m_DisplayGeometryUpdateTime.Modified(); Modified(); } } void mitk::BaseRenderer::SetCurrentWorldGeometry2D(mitk::Geometry2D* geometry2d) { if (m_CurrentWorldGeometry2D != geometry2d) { m_CurrentWorldGeometry2D = geometry2d; m_CurrentWorldGeometry2DData->SetGeometry2D(m_CurrentWorldGeometry2D); m_DisplayGeometry->SetWorldGeometry(m_CurrentWorldGeometry2D); m_CurrentWorldGeometry2DUpdateTime.Modified(); Modified(); } } void mitk::BaseRenderer::SendUpdateSlice() { m_DisplayGeometryUpdateTime.Modified(); m_CurrentWorldGeometry2DUpdateTime.Modified(); } void mitk::BaseRenderer::SetCurrentWorldGeometry(mitk::Geometry3D* geometry) { m_CurrentWorldGeometry = geometry; if (geometry == NULL) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; m_EmptyWorldGeometry = true; return; } BoundingBox::Pointer boundingBox = m_CurrentWorldGeometry->CalculateBoundingBoxRelativeToTransform(NULL); const BoundingBox::BoundsArrayType& worldBounds = boundingBox->GetBounds(); m_Bounds[0] = worldBounds[0]; m_Bounds[1] = worldBounds[1]; m_Bounds[2] = worldBounds[2]; m_Bounds[3] = worldBounds[3]; m_Bounds[4] = worldBounds[4]; m_Bounds[5] = worldBounds[5]; if (boundingBox->GetDiagonalLength2() <= mitk::eps) m_EmptyWorldGeometry = true; else m_EmptyWorldGeometry = false; } void mitk::BaseRenderer::SetGeometry(const itk::EventObject & geometrySendEvent) { const SliceNavigationController::GeometrySendEvent* sendEvent = dynamic_cast(&geometrySendEvent); assert(sendEvent!=NULL); SetWorldGeometry(sendEvent->GetTimeSlicedGeometry()); } void mitk::BaseRenderer::UpdateGeometry(const itk::EventObject & geometryUpdateEvent) { const SliceNavigationController::GeometryUpdateEvent* updateEvent = dynamic_cast(&geometryUpdateEvent); if (updateEvent == NULL) return; if (m_CurrentWorldGeometry.IsNotNull()) { SlicedGeometry3D* slicedWorldGeometry = dynamic_cast(m_CurrentWorldGeometry.GetPointer()); if (slicedWorldGeometry) { Geometry2D* geometry2D = slicedWorldGeometry->GetGeometry2D(m_Slice); SetCurrentWorldGeometry2D(geometry2D); // calls Modified() } } } void mitk::BaseRenderer::SetGeometrySlice(const itk::EventObject & geometrySliceEvent) { const SliceNavigationController::GeometrySliceEvent* sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent!=NULL); SetSlice(sliceEvent->GetPos()); } void mitk::BaseRenderer::SetGeometryTime(const itk::EventObject & geometryTimeEvent) { const SliceNavigationController::GeometryTimeEvent * timeEvent = dynamic_cast(&geometryTimeEvent); assert(timeEvent!=NULL); SetTimeStep(timeEvent->GetPos()); } const double* mitk::BaseRenderer::GetBounds() const { return m_Bounds; } void mitk::BaseRenderer::MousePressEvent(mitk::MouseEvent *me) { //set the Focus on the renderer /*bool success =*/m_RenderingManager->GetGlobalInteraction()->SetFocus(this); /* if (! success) mitk::StatusBar::GetInstance()->DisplayText("Warning! from mitkBaseRenderer.cpp: Couldn't focus this BaseRenderer!"); */ //if (m_CameraController) //{ // if(me->GetButtonState()!=512) // provisorisch: Ctrl nicht durchlassen. Bald wird aus m_CameraController eine StateMachine // m_CameraController->MousePressEvent(me); //} if (m_MapperID == 1) { Point2D p(me->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::PositionEvent event(this, me->GetType(), me->GetButton(), me->GetButtonState(), mitk::Key_unknown, p, position); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID > 1) //==2 for 3D and ==5 for stencil { Point2D p(me->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); me->SetDisplayPosition(p); mitk::EventMapper::MapEvent(me, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::MouseReleaseEvent(mitk::MouseEvent *me) { //if (m_CameraController) //{ // if(me->GetButtonState()!=512) // provisorisch: Ctrl nicht durchlassen. Bald wird aus m_CameraController eine StateMachine // m_CameraController->MouseReleaseEvent(me); //} if (m_MapperID == 1) { Point2D p(me->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::PositionEvent event(this, me->GetType(), me->GetButton(), me->GetButtonState(), mitk::Key_unknown, p, position); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID == 2) { Point2D p(me->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); me->SetDisplayPosition(p); mitk::EventMapper::MapEvent(me, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::MouseMoveEvent(mitk::MouseEvent *me) { //if (m_CameraController) //{ // if((me->GetButtonState()<=512) || (me->GetButtonState()>=516))// provisorisch: Ctrl nicht durchlassen. Bald wird aus m_CameraController eine StateMachine // m_CameraController->MouseMoveEvent(me); //} if (m_MapperID == 1) { Point2D p(me->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::PositionEvent event(this, me->GetType(), me->GetButton(), me->GetButtonState(), mitk::Key_unknown, p, position); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID == 2) { Point2D p(me->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); me->SetDisplayPosition(p); mitk::EventMapper::MapEvent(me, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::PickWorldPoint(const mitk::Point2D& displayPoint, mitk::Point3D& worldPoint) const { mitk::Point2D worldPoint2D; GetDisplayGeometry()->DisplayToWorld(displayPoint, worldPoint2D); GetDisplayGeometry()->Map(worldPoint2D, worldPoint); } void mitk::BaseRenderer::WheelEvent(mitk::WheelEvent * we) { if (m_MapperID == 1) { Point2D p(we->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::PositionEvent event(this, we->GetType(), we->GetButton(), we->GetButtonState(), mitk::Key_unknown, p, position); mitk::EventMapper::MapEvent(we, m_RenderingManager->GetGlobalInteraction()); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID == 2) { Point2D p(we->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); we->SetDisplayPosition(p); mitk::EventMapper::MapEvent(we, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::KeyPressEvent(mitk::KeyEvent *ke) { if (m_MapperID == 1) { Point2D p(ke->GetDisplayPosition()); Point2D p_mm; Point3D position; GetDisplayGeometry()->ULDisplayToDisplay(p, p); GetDisplayGeometry()->DisplayToWorld(p, p_mm); GetDisplayGeometry()->Map(p_mm, position); mitk::KeyEvent event(this, ke->GetType(), ke->GetButton(), ke->GetButtonState(), ke->GetKey(), ke->GetText(), p); mitk::EventMapper::MapEvent(&event, m_RenderingManager->GetGlobalInteraction()); } else if (m_MapperID == 2) { Point2D p(ke->GetDisplayPosition()); GetDisplayGeometry()->ULDisplayToDisplay(p, p); ke->SetDisplayPosition(p); mitk::EventMapper::MapEvent(ke, m_RenderingManager->GetGlobalInteraction()); } } void mitk::BaseRenderer::DrawOverlayMouse(mitk::Point2D& itkNotUsed(p2d)) { MITK_INFO<<"BaseRenderer::DrawOverlayMouse()- should be inconcret implementation OpenGLRenderer."<RequestUpdate(this->m_RenderWindow); } void mitk::BaseRenderer::ForceImmediateUpdate() { m_RenderingManager->ForceImmediateUpdate(this->m_RenderWindow); } unsigned int mitk::BaseRenderer::GetNumberOfVisibleLODEnabledMappers() const { return m_NumberOfVisibleLODEnabledMappers; } mitk::RenderingManager* mitk::BaseRenderer::GetRenderingManager() const { return m_RenderingManager.GetPointer(); } /*! Sets the new Navigation controller */ void mitk::BaseRenderer::SetSliceNavigationController(mitk::SliceNavigationController *SlicenavigationController) { if (SlicenavigationController == NULL) return; //disconnect old from globalinteraction m_RenderingManager->GetGlobalInteraction()->RemoveListener(SlicenavigationController); //copy worldgeometry SlicenavigationController->SetInputWorldGeometry(SlicenavigationController->GetCreatedWorldGeometry()); SlicenavigationController->Update(); //set new m_SliceNavigationController = SlicenavigationController; m_SliceNavigationController->SetRenderer(this); if (m_SliceNavigationController.IsNotNull()) { m_SliceNavigationController->ConnectGeometrySliceEvent(this); m_SliceNavigationController->ConnectGeometryUpdateEvent(this); m_SliceNavigationController->ConnectGeometryTimeEvent(this, false); } } /*! Sets the new camera controller and deletes the vtkRenderWindowInteractor in case of the VTKInteractorCameraController */ void mitk::BaseRenderer::SetCameraController(CameraController* cameraController) { mitk::VtkInteractorCameraController::Pointer vtkInteractorCameraController = dynamic_cast(cameraController); if (vtkInteractorCameraController.IsNotNull()) MITK_INFO<<"!!!WARNING!!!: RenderWindow interaction events are no longer handled via CameraController (See Bug #954)."<SetRenderer(NULL); m_CameraController = NULL; m_CameraController = cameraController; m_CameraController->SetRenderer(this); } void mitk::BaseRenderer::PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << " MapperID: " << m_MapperID << std::endl; os << indent << " Slice: " << m_Slice << std::endl; os << indent << " TimeStep: " << m_TimeStep << std::endl; os << indent << " WorldGeometry: "; if (m_WorldGeometry.IsNull()) os << "NULL" << std::endl; else m_WorldGeometry->Print(os, indent); os << indent << " CurrentWorldGeometry2D: "; if (m_CurrentWorldGeometry2D.IsNull()) os << "NULL" << std::endl; else m_CurrentWorldGeometry2D->Print(os, indent); os << indent << " CurrentWorldGeometry2DUpdateTime: " << m_CurrentWorldGeometry2DUpdateTime << std::endl; os << indent << " CurrentWorldGeometry2DTransformTime: " << m_CurrentWorldGeometry2DTransformTime << std::endl; os << indent << " DisplayGeometry: "; if (m_DisplayGeometry.IsNull()) os << "NULL" << std::endl; else m_DisplayGeometry->Print(os, indent); os << indent << " DisplayGeometryTransformTime: " << m_DisplayGeometryTransformTime << std::endl; Superclass::PrintSelf(os, indent); } void mitk::BaseRenderer::SetDepthPeelingEnabled(bool enabled) { m_DepthPeelingEnabled = enabled; m_VtkRenderer->SetUseDepthPeeling(enabled); } void mitk::BaseRenderer::SetMaxNumberOfPeels(int maxNumber) { m_MaxNumberOfPeels = maxNumber; m_VtkRenderer->SetMaximumNumberOfPeels(maxNumber); } diff --git a/Core/Code/Rendering/mitkBaseRenderer.h b/Core/Code/Rendering/mitkBaseRenderer.h index 33ba6ea9d3..7543877c03 100644 --- a/Core/Code/Rendering/mitkBaseRenderer.h +++ b/Core/Code/Rendering/mitkBaseRenderer.h @@ -1,633 +1,633 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 #define BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 #include "mitkDataStorage.h" #include "mitkGeometry2D.h" #include "mitkTimeSlicedGeometry.h" #include "mitkDisplayGeometry.h" #include "mitkGeometry2DData.h" #include "mitkCameraController.h" #include "mitkDisplayPositionEvent.h" #include "mitkWheelEvent.h" //#include "mitkMapper.h" #include "mitkSliceNavigationController.h" #include "mitkCameraController.h" #include "mitkCameraRotationController.h" #include "mitkBindDispatcherInteractor.h" #include "mitkDispatcher.h" #include #include #include #include namespace mitk { class NavigationController; class SliceNavigationController; class CameraRotationController; class CameraController; class DataStorage; class Mapper; class BaseLocalStorageHandler; //##Documentation //## @brief Organizes the rendering process //## //## Organizes the rendering process. A Renderer contains a reference to a //## DataStorage and asks the mappers of the data objects to render //## the data into the renderwindow it is associated to. //## //## \#Render() checks if rendering is currently allowed by calling //## RenderWindow::PrepareRendering(). Initialization of a rendering context //## can also be performed in this method. //## //## The actual rendering code has been moved to \#Repaint() //## Both \#Repaint() and \#Update() are declared protected now. //## //## Note: Separation of the Repaint and Update processes (rendering vs //## creating a vtk prop tree) still needs to be worked on. The whole //## rendering process also should be reworked to use VTK based classes for //## both 2D and 3D rendering. //## @ingroup Renderer class MITK_CORE_EXPORT BaseRenderer: public itk::Object { public: typedef std::map BaseRendererMapType; static BaseRendererMapType baseRendererMap; static BaseRenderer* GetInstance(vtkRenderWindow * renWin); static void AddInstance(vtkRenderWindow* renWin, BaseRenderer* baseRenderer); static void RemoveInstance(vtkRenderWindow* renWin); static BaseRenderer* GetByName(const std::string& name); static vtkRenderWindow* GetRenderWindowByName(const std::string& name); #pragma GCC visibility push(default) itkEventMacro( RendererResetEvent, itk::AnyEvent ); #pragma GCC visibility pop /** Standard class typedefs. */ mitkClassMacro(BaseRenderer, itk::Object); BaseRenderer(const char* name = NULL, vtkRenderWindow * renWin = NULL, mitk::RenderingManager* rm = NULL); //##Documentation //## @brief MapperSlotId defines which kind of mapper (e.g., 2D or 3D) shoud be used. typedef int MapperSlotId; enum StandardMapperSlot { Standard2D = 1, Standard3D = 2 }; virtual void SetDataStorage(DataStorage* storage); ///< set the datastorage that will be used for rendering //##Documentation //## return the DataStorage that is used for rendering virtual DataStorage::Pointer GetDataStorage() const { return m_DataStorage.GetPointer(); }; //##Documentation //## @brief Access the RenderWindow into which this renderer renders. vtkRenderWindow* GetRenderWindow() const { return m_RenderWindow; } vtkRenderer* GetVtkRenderer() const { return m_VtkRenderer; } //##Documentation //## @brief Returns the Dispatcher which handles Events for this BaseRenderer - Dispatcher::Pointer GetDispatcher(); + const Dispatcher::Pointer GetDispatcher(); //##Documentation //## @brief Default mapper id to use. static const MapperSlotId defaultMapper; //##Documentation //## @brief Do the rendering and flush the result. virtual void Paint(); //##Documentation //## @brief Initialize the RenderWindow. Should only be called from RenderWindow. virtual void Initialize(); //##Documentation //## @brief Called to inform the renderer that the RenderWindow has been resized. virtual void Resize(int w, int h); //##Documentation //## @brief Initialize the renderer with a RenderWindow (@a renderwindow). virtual void InitRenderer(vtkRenderWindow* renderwindow); //##Documentation //## @brief Set the initial size. Called by RenderWindow after it has become //## visible for the first time. virtual void InitSize(int w, int h); //##Documentation //## @brief Draws a point on the widget. //## Should be used during conferences to show the position of the remote mouse virtual void DrawOverlayMouse(Point2D& p2d); //##Documentation //## @brief Set/Get the WorldGeometry (m_WorldGeometry) for 3D and 2D rendering, that describing the //## (maximal) area to be rendered. //## //## Depending of the type of the passed Geometry3D more or less information can be extracted: //## \li if it is a Geometry2D (which is a sub-class of Geometry3D), m_CurrentWorldGeometry2D is //## also set to point to it. m_TimeSlicedWorldGeometry is set to NULL. //## \li if it is a TimeSlicedGeometry, m_TimeSlicedWorldGeometry is also set to point to it. //## If m_TimeSlicedWorldGeometry contains instances of SlicedGeometry3D, m_CurrentWorldGeometry2D is set to //## one of geometries stored in the SlicedGeometry3D according to the value of m_Slice; otherwise //## a PlaneGeometry describing the top of the bounding-box of the Geometry3D is set as the //## m_CurrentWorldGeometry2D. //## \li otherwise a PlaneGeometry describing the top of the bounding-box of the Geometry3D //## is set as the m_CurrentWorldGeometry2D. m_TimeSlicedWorldGeometry is set to NULL. //## @todo add calculation of PlaneGeometry describing the top of the bounding-box of the Geometry3D //## when the passed Geometry3D is not sliced. //## \sa m_WorldGeometry //## \sa m_TimeSlicedWorldGeometry //## \sa m_CurrentWorldGeometry2D virtual void SetWorldGeometry(Geometry3D* geometry); itkGetConstObjectMacro(WorldGeometry, Geometry3D) ; //##Documentation //## @brief Get the current 3D-worldgeometry (m_CurrentWorldGeometry) used for 3D-rendering itkGetConstObjectMacro(CurrentWorldGeometry, Geometry3D) ; //##Documentation //## @brief Get the current 2D-worldgeometry (m_CurrentWorldGeometry2D) used for 2D-rendering itkGetConstObjectMacro(CurrentWorldGeometry2D, Geometry2D) ; //##Documentation //## Calculates the bounds of the DataStorage (if it contains any valid data), //## creates a geometry from these bounds and sets it as world geometry of the renderer. //## //## Call this method to re-initialize the renderer to the current DataStorage //## (e.g. after loading an additional dataset), to ensure that the view is //## aligned correctly. //## \warn This is not implemented yet. virtual bool SetWorldGeometryToDataStorageBounds() { return false; } //##Documentation //## @brief Set/Get the DisplayGeometry (for 2D rendering) //## //## The DisplayGeometry describes which part of the Geometry2D m_CurrentWorldGeometry2D //## is displayed. virtual void SetDisplayGeometry(DisplayGeometry* geometry2d); itkGetConstObjectMacro(DisplayGeometry, DisplayGeometry) ; itkGetObjectMacro(DisplayGeometry, DisplayGeometry) ; //##Documentation //## @brief Set/Get m_Slice which defines together with m_TimeStep the 2D geometry //## stored in m_TimeSlicedWorldGeometry used as m_CurrentWorldGeometry2D //## //## \sa m_Slice virtual void SetSlice(unsigned int slice); itkGetConstMacro(Slice, unsigned int) ; //##Documentation //## @brief Set/Get m_TimeStep which defines together with m_Slice the 2D geometry //## stored in m_TimeSlicedWorldGeometry used as m_CurrentWorldGeometry2D //## //## \sa m_TimeStep virtual void SetTimeStep(unsigned int timeStep); itkGetConstMacro(TimeStep, unsigned int) ; //##Documentation //## @brief Get the time-step of a BaseData object which //## exists at the time of the currently displayed content //## //## Returns -1 or mitk::BaseData::m_TimeSteps if there //## is no data at the current time. //## \sa GetTimeStep, m_TimeStep int GetTimeStep(const BaseData* data) const; //##Documentation //## @brief Get the time in ms of the currently displayed content //## //## \sa GetTimeStep, m_TimeStep ScalarType GetTime() const; //##Documentation //## @brief SetWorldGeometry is called according to the geometrySliceEvent, //## which is supposed to be a SliceNavigationController::GeometrySendEvent virtual void SetGeometry(const itk::EventObject & geometrySliceEvent); //##Documentation //## @brief UpdateWorldGeometry is called to re-read the 2D geometry from the //## slice navigation controller virtual void UpdateGeometry(const itk::EventObject & geometrySliceEvent); //##Documentation //## @brief SetSlice is called according to the geometrySliceEvent, //## which is supposed to be a SliceNavigationController::GeometrySliceEvent virtual void SetGeometrySlice(const itk::EventObject & geometrySliceEvent); //##Documentation //## @brief SetTimeStep is called according to the geometrySliceEvent, //## which is supposed to be a SliceNavigationController::GeometryTimeEvent virtual void SetGeometryTime(const itk::EventObject & geometryTimeEvent); //##Documentation //## @brief Get a data object containing the DisplayGeometry (for 2D rendering) itkGetObjectMacro(DisplayGeometryData, Geometry2DData) ; //##Documentation //## @brief Get a data object containing the WorldGeometry (for 2D rendering) itkGetObjectMacro(WorldGeometryData, Geometry2DData) ; //##Documentation //## @brief Get a DataNode pointing to a data object containing the WorldGeometry (3D and 2D rendering) itkGetObjectMacro(WorldGeometryNode, DataNode) ; //##Documentation //## @brief Get a DataNode pointing to a data object containing the DisplayGeometry (for 2D rendering) itkGetObjectMacro(DisplayGeometryNode, DataNode) ; //##Documentation //## @brief Get a DataNode pointing to a data object containing the current 2D-worldgeometry m_CurrentWorldGeometry2D (for 2D rendering) itkGetObjectMacro(CurrentWorldGeometry2DNode, DataNode) ; //##Documentation //## @brief Sets timestamp of CurrentWorldGeometry2D and DisplayGeometry and forces so reslicing in that renderwindow void SendUpdateSlice(); //##Documentation //## @brief Get timestamp of last call of SetCurrentWorldGeometry2D unsigned long GetCurrentWorldGeometry2DUpdateTime() { return m_CurrentWorldGeometry2DUpdateTime; } ; //##Documentation //## @brief Get timestamp of last call of SetDisplayGeometry unsigned long GetDisplayGeometryUpdateTime() { return m_CurrentWorldGeometry2DUpdateTime; } ; //##Documentation //## @brief Get timestamp of last change of current TimeStep unsigned long GetTimeStepUpdateTime() { return m_TimeStepUpdateTime; } ; //##Documentation //## @brief Perform a picking: find the x,y,z world coordinate of a //## display x,y coordinate. //## @warning Has to be overwritten in subclasses for the 3D-case. //## //## Implemented here only for 2D-rendering by using //## m_DisplayGeometry virtual void PickWorldPoint(const Point2D& diplayPosition, Point3D& worldPosition) const; /** \brief Determines the object (mitk::DataNode) closest to the current * position by means of picking * * \warning Implementation currently empty for 2D rendering; intended to be * implemented for 3D renderers */ virtual DataNode* PickObject(const Point2D& /*displayPosition*/, Point3D& /*worldPosition*/) const { return NULL; } ; //##Documentation //## @brief Get the MapperSlotId to use. itkGetMacro(MapperID, MapperSlotId) ;itkGetConstMacro(MapperID, MapperSlotId) ; //##Documentation //## @brief Set the MapperSlotId to use. itkSetMacro(MapperID, MapperSlotId) ; //##Documentation //## @brief Has the renderer the focus? itkGetMacro(Focused, bool) ; //##Documentation //## @brief Tell the renderer that it is focused. The caller is responsible for focus management, //## not the renderer itself. itkSetMacro(Focused, bool) ; //##Documentation //## @brief Sets whether depth peeling is enabled or not void SetDepthPeelingEnabled(bool enabled); //##Documentation //## @brief Sets maximal number of peels void SetMaxNumberOfPeels(int maxNumber); itkGetMacro(Size, int*) ; void SetSliceNavigationController(SliceNavigationController* SlicenavigationController); void SetCameraController(CameraController* cameraController);itkGetObjectMacro(CameraController, CameraController) ;itkGetObjectMacro(SliceNavigationController, SliceNavigationController) ;itkGetObjectMacro(CameraRotationController, CameraRotationController) ; itkGetMacro(EmptyWorldGeometry, bool) ; //##Documentation //## @brief Mouse event dispatchers //## @note for internal use only. preliminary. virtual void MousePressEvent(MouseEvent*); //##Documentation //## @brief Mouse event dispatchers //## @note for internal use only. preliminary. virtual void MouseReleaseEvent(MouseEvent*); //##Documentation //## @brief Mouse event dispatchers //## @note for internal use only. preliminary. virtual void MouseMoveEvent(MouseEvent*); //##Documentation //## @brief Wheel event dispatcher //## @note for internal use only. preliminary. virtual void WheelEvent(mitk::WheelEvent* we); //##Documentation //## @brief Key event dispatcher //## @note for internal use only. preliminary. virtual void KeyPressEvent(KeyEvent*); //##Documentation //## @brief get the name of the Renderer //## @note const char * GetName() const { return m_Name.c_str(); } //##Documentation //## @brief get the x_size of the RendererWindow //## @note int GetSizeX() const { return m_Size[0]; } //##Documentation //## @brief get the y_size of the RendererWindow //## @note int GetSizeY() const { return m_Size[1]; } const double* GetBounds() const; void RequestUpdate(); void ForceImmediateUpdate(); /** Returns number of mappers which are visible and have level-of-detail * rendering enabled */ unsigned int GetNumberOfVisibleLODEnabledMappers() const; ///** //* \brief Setter for the RenderingManager that handles this instance of BaseRenderer //*/ //void SetRenderingManager( mitk::RenderingManager* ); /** * \brief Getter for the RenderingManager that handles this instance of BaseRenderer */ virtual mitk::RenderingManager* GetRenderingManager() const; /** * \brief Provides (1) world coordinates for a given mouse position and (2) * translates mousePosition to Display coordinates */ virtual Point3D Map2DRendererPositionTo3DWorldPosition(Point2D* mousePosition) const; protected: virtual ~BaseRenderer(); //##Documentation //## @brief Call update of all mappers. To be implemented in subclasses. virtual void Update() = 0; vtkRenderWindow* m_RenderWindow; vtkRenderer* m_VtkRenderer; //##Documentation //## @brief MapperSlotId to use. Defines which kind of mapper (e.g., 2D or 3D) shoud be used. MapperSlotId m_MapperID; //##Documentation //## @brief The DataStorage that is used for rendering. DataStorage::Pointer m_DataStorage; //##Documentation //## @brief The RenderingManager that manages this instance RenderingManager::Pointer m_RenderingManager; //##Documentation //## @brief Timestamp of last call of Update(). unsigned long m_LastUpdateTime; //##Documentation //## @brief CameraController for 3D rendering //## @note preliminary. CameraController::Pointer m_CameraController; SliceNavigationController::Pointer m_SliceNavigationController; CameraRotationController::Pointer m_CameraRotationController; //##Documentation //## @brief Size of the RenderWindow. int m_Size[2]; //##Documentation //## @brief Contains whether the renderer that it is focused. The caller of //## SetFocused is responsible for focus management, not the renderer itself. //## is doubled because of mitk::FocusManager in GlobalInteraction!!! (ingmar) bool m_Focused; //##Documentation //## @brief Sets m_CurrentWorldGeometry2D virtual void SetCurrentWorldGeometry2D(Geometry2D* geometry2d); //##Documentation //## @brief Sets m_CurrentWorldGeometry virtual void SetCurrentWorldGeometry(Geometry3D* geometry); private: //##Documentation //## Pointer to the worldgeometry, describing the maximal area to be rendered //## (3D as well as 2D). //## It is const, since we are not allowed to change it (it may be taken //## directly from the geometry of an image-slice and thus it would be //## very strange when suddenly the image-slice changes its geometry). //## \sa SetWorldGeometry Geometry3D::Pointer m_WorldGeometry; //##Documentation //## m_TimeSlicedWorldGeometry is set by SetWorldGeometry if the passed Geometry3D is a //## TimeSlicedGeometry (or a sub-class of it). If it contains instances of SlicedGeometry3D, //## m_Slice and m_TimeStep (set via SetSlice and SetTimeStep, respectively) define //## which 2D geometry stored in m_TimeSlicedWorldGeometry (if available) //## is used as m_CurrentWorldGeometry2D. //## \sa m_CurrentWorldGeometry2D TimeSlicedGeometry::Pointer m_TimeSlicedWorldGeometry; //##Documentation //## Pointer to the current 3D-worldgeometry. Geometry3D::Pointer m_CurrentWorldGeometry; //##Documentation //## Pointer to the current 2D-worldgeometry. The 2D-worldgeometry //## describes the maximal area (2D manifold) to be rendered in case we //## are doing 2D-rendering. More precisely, a subpart of this according //## to m_DisplayGeometry is displayed. //## It is const, since we are not allowed to change it (it may be taken //## directly from the geometry of an image-slice and thus it would be //## very strange when suddenly the image-slice changes its geometry). Geometry2D::Pointer m_CurrentWorldGeometry2D; //##Documentation //## Pointer to the displaygeometry. The displaygeometry describes the //## geometry of the \em visible area in the window controlled by the renderer //## in case we are doing 2D-rendering. //## It is const, since we are not allowed to change it. DisplayGeometry::Pointer m_DisplayGeometry; //##Documentation //## Defines together with m_Slice which 2D geometry stored in m_TimeSlicedWorldGeometry //## is used as m_CurrentWorldGeometry2D: m_TimeSlicedWorldGeometry->GetGeometry2D(m_Slice, m_TimeStep). //## \sa m_TimeSlicedWorldGeometry unsigned int m_Slice; //##Documentation //## Defines together with m_TimeStep which 2D geometry stored in m_TimeSlicedWorldGeometry //## is used as m_CurrentWorldGeometry2D: m_TimeSlicedWorldGeometry->GetGeometry2D(m_Slice, m_TimeStep). //## \sa m_TimeSlicedWorldGeometry unsigned int m_TimeStep; //##Documentation //## @brief timestamp of last call of SetWorldGeometry itk::TimeStamp m_CurrentWorldGeometry2DUpdateTime; //##Documentation //## @brief timestamp of last call of SetDisplayGeometry itk::TimeStamp m_DisplayGeometryUpdateTime; //##Documentation //## @brief timestamp of last change of the current time step itk::TimeStamp m_TimeStepUpdateTime; //##Documentation //## @brief Helper class which establishes connection between Interactors and Dispatcher via a common DataStorage. - BindDispatcherInteractor::Pointer m_BindDispatcherInteractor; + BindDispatcherInteractor* m_BindDispatcherInteractor; protected: virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; //##Documentation //## Data object containing the m_WorldGeometry defined above. Geometry2DData::Pointer m_WorldGeometryData; //##Documentation //## Data object containing the m_DisplayGeometry defined above. Geometry2DData::Pointer m_DisplayGeometryData; //##Documentation //## Data object containing the m_CurrentWorldGeometry2D defined above. Geometry2DData::Pointer m_CurrentWorldGeometry2DData; //##Documentation //## DataNode objects containing the m_WorldGeometryData defined above. DataNode::Pointer m_WorldGeometryNode; //##Documentation //## DataNode objects containing the m_DisplayGeometryData defined above. DataNode::Pointer m_DisplayGeometryNode; //##Documentation //## DataNode objects containing the m_CurrentWorldGeometry2DData defined above. DataNode::Pointer m_CurrentWorldGeometry2DNode; //##Documentation //## @brief test only unsigned long m_DisplayGeometryTransformTime; //##Documentation //## @brief test only unsigned long m_CurrentWorldGeometry2DTransformTime; std::string m_Name; double m_Bounds[6]; bool m_EmptyWorldGeometry; bool m_DepthPeelingEnabled; int m_MaxNumberOfPeels; typedef std::set LODEnabledMappersType; /** Number of mappers which are visible and have level-of-detail * rendering enabled */ unsigned int m_NumberOfVisibleLODEnabledMappers; // Local Storage Handling for mappers protected: std::list m_RegisteredLocalStorageHandlers; public: void RemoveAllLocalStorages(); void RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh); void UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh); }; } // namespace mitk #endif /* BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 */ diff --git a/Core/Code/Resources/Interactions/PointSetConfig.xml b/Core/Code/Resources/Interactions/PointSetConfig.xml index dfd8c206a2..f38f80b075 100644 --- a/Core/Code/Resources/Interactions/PointSetConfig.xml +++ b/Core/Code/Resources/Interactions/PointSetConfig.xml @@ -1,40 +1,40 @@ - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + diff --git a/Core/Documentation/Doxygen/Concepts/DataInteraction.dox b/Core/Documentation/Doxygen/Concepts/DataInteraction.dox index 46adae02d9..4f0ee16645 100644 --- a/Core/Documentation/Doxygen/Concepts/DataInteraction.dox +++ b/Core/Documentation/Doxygen/Concepts/DataInteraction.dox @@ -1,204 +1,204 @@ /** \page DataInteractionPage Interaction Concepts \tableofcontents \section InteractionPage_Introduction Introduction to Interaction in MITK Interaction is a very important task in medical image processing software. Therefore MITK provides a special interaction concept that provides the developer with an easy way to develop and maintain user interaction separately from the algorithms processing the input. This allows e.g. for common interaction schemes to be re-used in different contexts. The core of the interaction concept is based on entities called \b DataInteractors that listen for certain pre-defined events and execute actions when such an event is triggered.\n In the following the different components of the interaction concept are explained. First a a high-level overview about how the different components interact is given, then some parts are explained in more detail. \subsection FurtherReadingInteraction Topics related to interaction - further information: See the \ref DataInteractionTechnicalPage page for a more technical explanation. \n Consult \ref HowToUseDataInteractor for usage information.\n See \ref SectionImplementationDataInteractor for an example on how to implement a new DataInteractor \n for information about how to create new events refer to ImplementNewEventsPage.\n The documentation of the depricated former concept can be found at \ref InteractionPage. \section HandlingSection Event Handling & Window Manager Abstraction The following sequence diagram gives an examplary overview of the process from creating an event until executing an action in the DataInteractor. This diagram assumes the usage of the Qt framework, but also shows that the interaction concept itself is implemented independent of any specific window manager. \image html event_handling.png
  1. a user event is triggered and send to MITK
  2. this layler serves as an adapter from window manager (here Qt) events to MITK internal events (later refered to as InteractionEvents).
  3. once the event is adapted it is send to a Dispatcher, which is linked to a render window, to be handled.
  4. on Dispatcher level all objects are known that can react to incoming events (DataInteractors)
  5. a DataInteractor is offered an event and checks its EventConfig object, which returns if a variant if this event has been defined for this DataInteractor.
  6. if the DataInteractor has a variant for the event, it consults its state machine to check if the input can be handled in the current state
  7. the actions asociated with a state change (transition) are executed and the event is successfully handled.
\section EventPage Events Events can describe any sort of user input, such as key strokes, mouse clicks or touch gestures. These events are mapped from an UI framework like Qt to an MITK internal representation and send to the Dispatcher which in turn deals with further processing of the event. These events are not limited to classical input devices but can be extened at will, by introducing new classes which e.g. describe events from tracking devices, etc. Refer to \ref ImplementNewEventsPage to see how new events and thereby input devices can be integrated. For an overview of available Events see mitk::InteractionEvent, for on overview of parameters see the \ref DataInteractionTechnicalPage. \section EventHandlerSection EventHandler Is the term describing objects in general that can handle events. These objects can be devided into two groups, namely DataInteractors and EventObserver. Their difference is that DataInteractors are linked with a DataNode which they manipulate, whereas EventObserver do not have a DataNode and therefore are not supposed to manipulate any data. \dot digraph linker_deps { node [shape=record, fontname=Helvetica, fontsize=10]; a [ label="EventHandler" ]; d [ label="{StateMachine|HandleEvent()}" ]; b [ label="{DataInteractor|HandleEvent()}" ]; c [ label="{EventObserver|Notify()}" ]; a -> d; d -> b; d -> c; } \enddot \subsection DataInteractorsSection DataInteractors DataInteractors are specialized EventHandler which handle events for one spefific DataNode. They are implemented following a concept called state machines (see e.g. Wikipedia ). \subsubsection StateMachinesSection StateMachines A specific events action is usually desired to dependent on the content of the data object the state of the interaction. For example if the when adding a line by clicking with the mouse, the first to clicks are supposed to add a point. But the second click should additionally finish the interaction and a subsequent third click should be ignored. State machines provide a great way to model such interaction in which the same user interaction can trigger different actions depending on the current state. Therefore DataInteractors work with so called state machine patterns. The basic idea here is that each interaction can be described by states and transitions which in turn trigger actions. These patterns define a workflow and different patterns can be applied to the same DataInteractor and cause this DataInteractor to perform different user interactions. This principle is best described by an example. Imagine a DataInteractor with the functionality (1) to add Points at a given mouse position and connect them by a line and (2) check if two points are on the same position. Using this DataInteractor, different StateMachine patterns/descriptions can be given which each cause the DataInteractor to perform different interaction schemes. State machine pattern 1: We want the user to draw a line. A simple state machine could express this by three states like this: \dot digraph linker_deps { node [shape=circle, fontname=Helvetica, fontsize=10]; a [ label="NoPoints" ]; b [ label="OnePoint" ]; c [ label="TwoPoints" ]; a -> b [label="MousePress/AddPoint",fontname=Helvetica, fontsize=10]; b -> c [label="MousePress/AddPoint",fontname=Helvetica, fontsize=10]; { rank=same; a b c } } \enddot With each MousePress event the AddPoint function is called and adds a point at the mouse position, unless two points already exist. State machine pattern 2: The same DataInteractor can also operate after the following state machine, which models the interaction to input a closed contour. The DataInteractor can detect an AddPoint event on an already existing point and will trigger a PointsMatch event. \dot digraph { node [shape=circle, fontname=Helvetica, fontsize=10]; a [ label="StartState" ]; b [ label="ClosedContour"]; a -> a [label="MousePress/AddPoint",fontname=Helvetica, fontsize=10]; a -> b [label="PointsMatch/AddPoint",fontname=Helvetica, fontsize=10]; } \enddot In this way state machines provide both, a nice and structured way to represent interaction tasks and description of the interaction which is separated from the code. One DataInteractor can be re-used for different tasks by simply exchanging the state machine pattern. These patterns are described in XML files. \subsubsection DefinitionStateMachine Definition of a State Machine The definition is made up out of three components.
  • States - represent the current status of the interaction
  • Transitions - describe the events needed to change from one state to another
  • Actions - are executed, when a transition is taken
Each state machine needs exactly one designated start state into which the state machine is set in the beginning. An example of a state machine describing the interaction of example 2 looks like this: \code \endcode Example 1: State machine pattern, that describes adding points to a contour until the PointsMatch event is triggered. For a more detailed desciption of state machine patterns see here. \subsection EventObserverSection EventObserver EventObserver are objects which will receive all user input and are intented for observation only, they should never modify any DataNodes. For EventObserver it is optional to use the state machine functionality, the default is without. How to use the state machine functionality is described in the documentation of mitk::EventObserver::Notify. \subsection ConfigurationSection Configuration In a lot of cases it is preferable to implement interactions independent of a specific event (e.g. left click with mouse), such that is it possible to easily change this. This is achieved through configuration of EventHandlers. This allows to change the behavior at runtime. The EventHandler provides an interface to easily modify the user input that triggers an action by loading a different configuration, this allows to implement user-specific behavior of the software on an abstract level and to switch in at runtime. This is achieved through XML files describing a configuration. These files can be loaded by the EventHandler and will lead to an internal mapping from specific user input to an absract description of the event given in the config file. In order to do this we distinguish between a spefic event and an event variant. A specific event is described by its event class, which determines the category of an event, e.g. the class MousePressEvent, and its parameter which make this event unique, e.g. LeftMouseButton pressed and no modifier keys pressed. The event variant is a name that is assigned to a spefific event, and to which an EventHandler listens. To illustrate this, an example is given here for two different configuration files. We assume an EventHandler that listens to the event variant 'AddPoint', two possible config files could then look like this: \code - + - + \endcode Example 2: Event description of a left click with the mouse and \code - + - + \endcode Example 3: Event description of a left click with the mouse while pressing the shift-key If the EventHandler is loaded with the first configuration the event variant 'MousePress' is triggered when the user performs a mouse click, while when the second configuration is loaded 'MousePress' is triggered when the user performs a right click while pressing the shift button. In this way all objects derived by EventHandler can be configured. For a detailed description about how to create the XML file see \ref ConfigurationTechnical . \section DispatcherSection Dispatcher This unit receives all events and distributes them to the DataInteractors. This is done by ordering the DataInteractors according to the layer of their DataNode in descending order. Then the event is offered to the first DataInteractor, which in turn checks if it can handle the event. This is done for each DataInteractor until the first processes the event, after this the other DataInteractors are skipped and all EventObservers are notified. */ diff --git a/Documentation/Doxygen/Tutorial/Step10.dox b/Documentation/Doxygen/Tutorial/Step10.dox index 82ee944421..f97ee130ed 100644 --- a/Documentation/Doxygen/Tutorial/Step10.dox +++ b/Documentation/Doxygen/Tutorial/Step10.dox @@ -1,432 +1,432 @@ /** \page Step10Page MITK Tutorial - Step 10: Adding new Interaction \tableofcontents \section HowToUseDataInteractor How to use an existing DataInteractor MITK provides finished DataInteractors for a variety of tasks, they can be found in Code/Core/Interactors. They can be used with state machine patterns and config files located under Resources/Interaction. A 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 DataInteractor works on a DataNode in which it stores and manipulates data. To use a DataInteractor these parts have to be brought together. This code demonstrates the use of an existing DataInteractor exemplary for the PointSetDataInteractor: First we need a DataNode that is added to the DataStorage. \code mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); GetDataStorage()->Add(dataNode.GetPointer()); \endcode Then we create an instance of to PointSetDataInteractor and load a statemachine pattern as well as a configuration for it: \code m_CurrentInteractor = mitk::PointSetDataInteractor::New(); m_CurrentInteractor->LoadStateMachine("PointSet.xml"); m_CurrentInteractor->LoadEventConfig("PointSetConfig.xml"); \endcode Lastly the DataNode is added to the DataInteractor \code m_CurrentInteractor->SetDataNode(dataNode); \endcode now the DataInteractor is ready for usage. \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, you may also want to read \ref DataInteractionTechnicalPage. 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 examplary implementation of a 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 ommitted it is set to its default value. Following all possible parameters are listed and described, to which parameters events have is decribed in their respective documentation. 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::MouseButtons represent the buttons. They can be used for two attributes. First the EventButton which describes the button that triggered the event, this allways 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 also 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 all, 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 mitkInteractionEventConst.h \skipline // Special Keys \until // End special keys Modifier Keys \n mitk::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 as 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 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 modelling 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. \subsubsection 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="{PositionEvent}"]; b [ label="{MousePressEvent}" ]; c [ label="MouseReleaseEvent" ]; d [ label="TouchEvent", style=dotted ]; a -> b; a -> c; a -> d; } \enddot in the state machine pattern the PositionEvent 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 event of the classes PositionEvent itself, or MousePressEvent,MouseReleaseEvent,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 hierachy (this ensures they hold the neccessary properties). \code \endcode \subsection ActionSection Actions Actions can be added to transitions and represent functions in the 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 DataInteractor which function to execute these actions are made known to the DataInteractor using the CONNECT_FUNCTION macro. This example assumes that there exists an ExampleInteractor which inherits from DataInteractor. 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 \section HowToDataInteractor Implementation a new DataInteractor DataInteractors are to inherit from mitk::DataInteractor. Their functionality is implemented in functions that follow this interface: \code bool SomeFunctionality(StateMachineAction* , InteractionEvent*); \endcode Your functions are connected with actions by implementing the function ConnectActionsAndFunctions(), e.g. \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("enoughPoints", EnoughPoints); } \endcode Now all that is left it to write a state machine pattern and a config file as is described in the tutorials. \subsection ExampleInternalEvent PointSetDataInteractor To provide a useful example the mitk::PointSetDataInteractor is annotated with comments that describe the important parts for an implementation of a DataInteractor. This step assumes knowlege of the Interaction concept described in \ref DataInteractionPage and some background of the implementation which is described in \ref DataInteractionPageTechnical. Please refer to these pages before proceeding. DataInteractor are to inherit from mitk::DataInteractor. Their functionality is implemented in functions that follow this interface: \code bool SomeFunctionality(StateMachineAction* , InteractionEvent*); \endcode Your functions are connected with actions by implementing the function ConnectActionsAndFunctions(), e.g. \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("enoughPoints", EnoughPoints); } \endcode 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 are mitk::InternalEvents which allow to the DataInteractor send signals on its own. The following will describe how to build a 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 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 desribed 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 Interactor (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::EventHandler 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: \code bool FUNCTION_NAME (StateMachineAction* , InteractionEvent*); \endcode \dontinclude Step10.h \skipline private: \until bool EnoughPoints(StateMac Each function has to return a boolean value. True if the action has been executed, and false if the action has not been executed. \dontinclude Step10.cpp \skipline bool mitk::ExampleInteractor::AddPoint(StateM \until //- 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 That internal events do not need any mapping to event variants. Their signal same is equivalent with the event variant. There are also two documented classes implementing a DataInteractor and an EventObserver which can be looked at for further understanding: \see mitk::PointSetDataInteractor \see mitk::DisplayInteractor 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]" */ \ No newline at end of file