diff --git a/Core/Code/Interactions/mitkDisplayVectorInteractor.cpp b/Core/Code/Interactions/mitkDisplayVectorInteractor.cpp index 909afd859a..2bec3f0dca 100644 --- a/Core/Code/Interactions/mitkDisplayVectorInteractor.cpp +++ b/Core/Code/Interactions/mitkDisplayVectorInteractor.cpp @@ -1,148 +1,166 @@ /*=================================================================== 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 "mitkDisplayVectorInteractor.h" #include "mitkOperation.h" #include "mitkDisplayCoordinateOperation.h" #include "mitkDisplayPositionEvent.h" #include "mitkUndoController.h" #include "mitkStateEvent.h" #include "mitkInteractionConst.h" #include "mitkAction.h" void mitk::DisplayVectorInteractor::ExecuteOperation(Operation* itkNotUsed( operation ) ) { /*DisplayCoordinateOperation* dcOperation = static_cast(operation); if(dcOperation==NULL) return; switch(operation->GetOperationType()) { case OpSELECTPOINT: m_Sender=dcOperation->GetRenderer(); m_StartDisplayCoordinate=dcOperation->GetStartDisplayCoordinate(); m_LastDisplayCoordinate=dcOperation->GetLastDisplayCoordinate(); m_CurrentDisplayCoordinate=dcOperation->GetCurrentDisplayCoordinate(); // MITK_INFO << m_CurrentDisplayCoordinate << std::endl; MITK_INFO<<"Message from DisplayVectorInteractor.cpp::ExecuteOperation() : " << "StartDisplayCoordinate:" << m_StartDisplayCoordinate << "LastDisplayCoordinate:" << m_LastDisplayCoordinate << "CurrentDisplayCoordinate:" << m_CurrentDisplayCoordinate << std::endl; break; }*/ } +float mitk::DisplayVectorInteractor::CanHandleEvent(const StateEvent *stateEvent) const +{ + const DisplayPositionEvent* posEvent=dynamic_cast(stateEvent->GetEvent()); + if(posEvent==NULL) return 0.0; + + //StateEvents from "moveNzoom", "alternativePan", "alternativeZoom" interaction pattern. If EventID can be handled by these statemachine patterns return a high value + if (stateEvent->GetId() == EIDRIGHTMOUSEBTN || stateEvent->GetId() == EIDMIDDLEMOUSEBTN || stateEvent->GetId() == EIDRIGHTMOUSEBTNANDCTRL || + stateEvent->GetId() == EIDMIDDLEMOUSERELEASE || stateEvent->GetId() == EIDRIGHTMOUSERELEASE || stateEvent->GetId() == EIDRIGHTMOUSEBTNANDMOUSEMOVE || + stateEvent->GetId() == EIDMIDDLEMOUSEBTNANDMOUSEMOVE || stateEvent->GetId() == EIDCTRLANDRIGHTMOUSEBTNANDMOUSEMOVE || stateEvent->GetId() == EIDCTRLANDRIGHTMOUSEBTNRELEASE ) + { + return 0.9; + } + else + { + return 0.0; + } +} + bool mitk::DisplayVectorInteractor::ExecuteAction(Action* action, mitk::StateEvent const* stateEvent) { bool ok=false; const DisplayPositionEvent* posEvent=dynamic_cast(stateEvent->GetEvent()); if(posEvent==NULL) return false; int actionId = action->GetActionId(); //initzoom and initmove is the same! if (actionId == AcINITZOOM) actionId = AcINITMOVE; switch(actionId) { //case 0: // { // DisplayCoordinateOperation* doOp = new mitk::DisplayCoordinateOperation(OpTEST, posEvent->GetSender(), posEvent->GetDisplayPosition(), posEvent->GetDisplayPosition(), posEvent->GetDisplayPosition()); // // //execute the Operation // m_Destination->ExecuteOperation(doOp); // ok = true; // break; // } case AcSENDCOORDINATES: { DisplayCoordinateOperation* doOp = new mitk::DisplayCoordinateOperation(OpSENDCOORDINATES, posEvent->GetSender(), posEvent->GetDisplayPosition(), posEvent->GetDisplayPosition(), posEvent->GetDisplayPosition()); m_Destination->ExecuteOperation(doOp); ok = true; break; } case AcINITMOVE: { m_Sender=posEvent->GetSender(); mitk::Vector2D origin = m_Sender->GetDisplayGeometry()->GetOriginInMM(); double scaleFactorMMPerDisplayUnit = m_Sender->GetDisplayGeometry()->GetScaleFactorMMPerDisplayUnit(); m_StartDisplayCoordinate=posEvent->GetDisplayPosition(); m_LastDisplayCoordinate=posEvent->GetDisplayPosition(); m_CurrentDisplayCoordinate=posEvent->GetDisplayPosition(); m_StartCoordinateInMM=mitk::Point2D( ( origin+m_StartDisplayCoordinate.GetVectorFromOrigin()*scaleFactorMMPerDisplayUnit ).GetDataPointer() ); ok = true; break; } case AcMOVE: { DisplayCoordinateOperation* doOp = new DisplayCoordinateOperation(OpMOVE, m_Sender, m_StartDisplayCoordinate, m_CurrentDisplayCoordinate, posEvent->GetDisplayPosition()); //make Operation m_LastDisplayCoordinate=m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate=posEvent->GetDisplayPosition(); //execute the Operation m_Destination->ExecuteOperation(doOp); ok = true; break; } case AcFINISHMOVE: { ok = true; break; } case AcZOOM: { DisplayCoordinateOperation* doOp = new DisplayCoordinateOperation(OpZOOM, m_Sender, m_StartDisplayCoordinate, m_LastDisplayCoordinate, posEvent->GetDisplayPosition(),m_StartCoordinateInMM); //make Operation m_LastDisplayCoordinate=m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate=posEvent->GetDisplayPosition(); //MITK_INFO << m_CurrentDisplayCoordinate << std::endl; //execute the Operation m_Destination->ExecuteOperation(doOp); ok = true; break; } default: ok = false; break; } return ok; } mitk::DisplayVectorInteractor::DisplayVectorInteractor(const char * type, mitk::OperationActor* destination) : mitk::StateMachine(type), m_Sender(NULL), m_Destination(destination) { m_StartDisplayCoordinate.Fill(0); m_LastDisplayCoordinate.Fill(0); m_CurrentDisplayCoordinate.Fill(0); if(m_Destination==NULL) m_Destination=this; } mitk::DisplayVectorInteractor::~DisplayVectorInteractor() { } diff --git a/Core/Code/Interactions/mitkDisplayVectorInteractor.h b/Core/Code/Interactions/mitkDisplayVectorInteractor.h index 2facfac41c..7c829e0a09 100644 --- a/Core/Code/Interactions/mitkDisplayVectorInteractor.h +++ b/Core/Code/Interactions/mitkDisplayVectorInteractor.h @@ -1,79 +1,85 @@ /*=================================================================== 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 MITKDISPLAYVECTORINTERACTOR_H_HEADER_INCLUDED_C10DC4EB #define MITKDISPLAYVECTORINTERACTOR_H_HEADER_INCLUDED_C10DC4EB #include #include "mitkBaseRenderer.h" #include "mitkStateMachine.h" namespace mitk { class Operation; class OperationActor; /** *@brief Interactor for displaying different slices in orthogonal views. * This includes the interaction of Zooming and Panning. * @ingroup Interaction **/ class MITK_CORE_EXPORT DisplayVectorInteractor : public StateMachine { public: mitkClassMacro(DisplayVectorInteractor, StateMachine); mitkNewMacro2Param(Self, const char*, OperationActor*); /** * @brief Method derived from OperationActor to recieve and execute operations **/ virtual void ExecuteOperation(Operation* operation); + /** + * @brief Method that returns how well this event can be handled by the DisplayVectorInteractor + * a right click into a 2D renderwindow can be handled very well! + **/ + float CanHandleEvent(const StateEvent *stateEvent) const; + protected: /** * @brief Default Constructor **/ DisplayVectorInteractor(const char * type, mitk::OperationActor* destination=NULL); /** * @brief Default Destructor **/ virtual ~DisplayVectorInteractor(); /** * @brief Method derived from StateMachine to implement the own actions **/ virtual bool ExecuteAction(Action* action, mitk::StateEvent const* stateEvent); private: BaseRenderer::Pointer m_Sender; mitk::Point2D m_StartDisplayCoordinate; mitk::Point2D m_LastDisplayCoordinate; mitk::Point2D m_CurrentDisplayCoordinate; mitk::Point2D m_StartCoordinateInMM; OperationActor* m_Destination; }; } // namespace mitk #endif /* MITKDISPLAYVECTORINTERACTOR_H_HEADER_INCLUDED_C10DC4EB */ diff --git a/Core/Code/Interactions/mitkGlobalInteraction.cpp b/Core/Code/Interactions/mitkGlobalInteraction.cpp index a73ccb5d74..3389c4461a 100755 --- a/Core/Code/Interactions/mitkGlobalInteraction.cpp +++ b/Core/Code/Interactions/mitkGlobalInteraction.cpp @@ -1,457 +1,510 @@ /*=================================================================== 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 "mitkGlobalInteraction.h" #include "mitkInteractionConst.h" #include "mitkStateEvent.h" #include "mitkPositionEvent.h" #include #include mitk::GlobalInteraction::GlobalInteraction() : StateMachine(NULL) , m_StateMachineFactory(NULL) , m_EventMapper(NULL) , m_CurrentlyInInformListenersLoop(false) , m_CurrentlyInInformInteractorsLoop(false) , m_IsInitialized(false) +, m_EventNotificationPolicy(INFORM_MULTIPLE) { } mitk::GlobalInteraction::~GlobalInteraction() { //s_GlobalInteraction doesn't have to be set = NULL; // StateMachineFactory and EventMapper have to be deleted explicitly, as they inherit from Vtk if (this->IsInitialized()) { m_StateMachineFactory->Delete(); m_StateMachineFactory = NULL; m_EventMapper->Delete(); m_EventMapper = NULL; } m_ListenerList.clear(); m_InteractorList.clear(); m_SelectedList.clear(); - m_JurisdictionMap.clear(); + m_InteractorRelevanceMap.clear(); + m_ListenerRelevanceMap.clear(); m_FocusManager = NULL; } inline mitk::StateEvent* GenerateEmptyStateEvent(int eventId) { mitk::Event *noEvent = new mitk::Event(NULL, mitk::Type_User, mitk::BS_NoButton, mitk::BS_NoButton, mitk::Key_none); mitk::StateEvent *stateEvent = new mitk::StateEvent(eventId, noEvent); return stateEvent; } void mitk::GlobalInteraction::AddListener(mitk::StateMachine* listener) { if(listener == NULL) return; if(dynamic_cast(listener)!=NULL) { MITK_WARN << "Trying to add an Interactor (" << listener->GetNameOfClass() << ") as a listener. " << "This will probably cause problems"; } if ( std::find(m_ListenerList.begin(), m_ListenerList.end(),listener) == m_ListenerList.end() ) { m_ListenerList.push_back(listener); } } bool mitk::GlobalInteraction::RemoveListener(mitk::StateMachine* listener) { // Defers removal to a time after the current event handling is finished. Otherwise the implementation of InformListeners would crash sometimes. m_ListenersFlaggedForRemoval.push_back(listener); StateMachineListIter position = std::find(m_ListenerList.begin(), m_ListenerList.end(),listener); bool removePossible = (position != m_ListenerList.end()); RemoveFlaggedListeners(); + //check if in RelevanceMap + for (StateMachineMapIter it = m_ListenerRelevanceMap.begin(); it != m_ListenerRelevanceMap.end(); it++) + { + if ((*it).second == listener) + { + m_ListenerRelevanceMap.erase(it); + break; + } + } + return removePossible; } void mitk::GlobalInteraction::RemoveFlaggedListeners() { if (m_CurrentlyInInformListenersLoop) return; // iterate flagged listeners, remove them if possible if (m_ListenersFlaggedForRemoval.empty()) return; for (StateMachineCPointerListIter it = m_ListenersFlaggedForRemoval.begin(); it != m_ListenersFlaggedForRemoval.end(); ++it) { StateMachineListIter foundPosition = std::find( m_ListenerList.begin(), m_ListenerList.end(), *it ); if (foundPosition != m_ListenerList.end()) { m_ListenerList.erase( foundPosition ); } } m_ListenersFlaggedForRemoval.clear(); } void mitk::GlobalInteraction::AddInteractor(mitk::Interactor* interactor) { if(interactor == NULL) return; if ( std::find(m_InteractorList.begin(), m_InteractorList.end(),interactor) == m_InteractorList.end() ) { m_InteractorList.push_back(interactor); //if Interactor already selected, then add to selected list if (interactor->GetMode()==Interactor::SMSELECTED) this->AddToSelectedInteractors(interactor); } } bool mitk::GlobalInteraction::InteractorRegistered (mitk::Interactor* interactor) { if ( std::find(m_InteractorList.begin(), m_InteractorList.end(), interactor) == m_InteractorList.end() ) return false; else return true; } bool mitk::GlobalInteraction::ListenerRegistered (mitk::StateMachine* listener) { if ( std::find(m_ListenerList.begin(), m_ListenerList.end(), listener) == m_ListenerList.end() ) return false; else return true; } bool mitk::GlobalInteraction::RemoveInteractor(mitk::Interactor* interactor) { InteractorListIter position = std::find(m_InteractorList.begin(), m_InteractorList.end(),interactor); if (position == m_InteractorList.end()) return false; position = m_InteractorList.erase(position); //check if the interactor is also held in SelectedList this->RemoveFromSelectedInteractors(interactor); - //check if in JurisdictionMap - for (InteractorMapIter it = m_JurisdictionMap.begin(); it != m_JurisdictionMap.end(); it++) + //check if in RelevanceMap + for (InteractorMapIter it = m_InteractorRelevanceMap.begin(); it != m_InteractorRelevanceMap.end(); it++) { if ((*it).second == interactor) { if (m_CurrentInteractorIter == it) - m_CurrentInteractorIter = m_JurisdictionMap.end(); - m_JurisdictionMap.erase(it); + m_CurrentInteractorIter = m_InteractorRelevanceMap.end(); + m_InteractorRelevanceMap.erase(it); break; } } return true; } void mitk::GlobalInteraction::InformListeners(mitk::StateEvent const* stateEvent) { m_CurrentlyInInformListenersLoop = true; for (StateMachineListIter it = m_ListenerList.begin(); it != m_ListenerList.end(); it++) { if((*it).IsNotNull()) (*it)->HandleEvent(stateEvent); } m_CurrentlyInInformListenersLoop = false; RemoveFlaggedListeners(); } bool mitk::GlobalInteraction::AskSelected(mitk::StateEvent const* stateEvent) { if (m_SelectedList.empty()) return false; bool ok = false, oneOk = false; //copy of m_SelectedList to be stable if an iterator gets removed during the following steps InteractorList copyOfSelectedList = m_SelectedList; InteractorListIter it = copyOfSelectedList.begin(); for (; it != copyOfSelectedList.end(); it++) { oneOk = (*it)->HandleEvent(stateEvent); //if one HandleEvent did succeed, then set returnvalue on true; if (oneOk) ok = true; } return ok; } -void mitk::GlobalInteraction::FillJurisdictionMap(mitk::StateEvent const* stateEvent, float threshold) +void mitk::GlobalInteraction::FillInteractorRelevanceMap(mitk::StateEvent const* stateEvent, float threshold) { - m_JurisdictionMap.clear(); + m_InteractorRelevanceMap.clear(); for (InteractorListIter it = m_InteractorList.begin(); it != m_InteractorList.end(); it++) { if ((*it).IsNotNull()) { //first ask for CanHandleEvent(..) and write it into the map if > 0 float value = (*it)->CanHandleEvent(stateEvent); if (value > threshold) { - m_JurisdictionMap.insert(InteractorMap::value_type(value, (*it))); + m_InteractorRelevanceMap.insert(InteractorMap::value_type(value, (*it))); } } } //set the iterator to the first element to start stepping through interactors - if (! m_JurisdictionMap.empty()) - m_CurrentInteractorIter = m_JurisdictionMap.begin(); + if (! m_InteractorRelevanceMap.empty()) + m_CurrentInteractorIter = m_InteractorRelevanceMap.begin(); else - m_CurrentInteractorIter = m_JurisdictionMap.end(); + m_CurrentInteractorIter = m_InteractorRelevanceMap.end(); +} + +void mitk::GlobalInteraction::FillListenerRelevanceMap(const mitk::StateEvent *stateEvent, float threshold) +{ + m_ListenerRelevanceMap.clear(); + + for (StateMachineListIter it = m_ListenerList.begin(); it != m_ListenerList.end(); it++) + { + if((*it).IsNotNull()) + { + float value = (*it)->CanHandleEvent(stateEvent); + if (value > threshold) + { + m_ListenerRelevanceMap.insert(StateMachineMap::value_type(value, (*it))); + } + } + } } /* * Go through the list of interactors, that could possibly handle an event and ask if it has handled the event. * If an interactor has handled an event, it should add itself to the list of selectedInteractors * Ask as long as no interactor answers, that it could be handled */ bool mitk::GlobalInteraction::AskCurrentInteractor(mitk::StateEvent const* stateEvent) { - //no need to check if we don't have any interactors. nearly equal to m_CurrentInteractorIter == m_JurisdictionMap.end - if (m_JurisdictionMap.empty()) + //no need to check if we don't have any interactors. nearly equal to m_CurrentInteractorIter == m_InteractorRelevanceMap.end + if (m_InteractorRelevanceMap.empty()) return false; bool handled = false; - while ( m_CurrentInteractorIter != m_JurisdictionMap.end()&& !handled) + while ( m_CurrentInteractorIter != m_InteractorRelevanceMap.end()&& !handled) { handled = (*m_CurrentInteractorIter).second->HandleEvent(stateEvent); if (!handled) m_CurrentInteractorIter++; } //loop for later usage - if (m_CurrentInteractorIter == m_JurisdictionMap.end()) - m_CurrentInteractorIter = m_JurisdictionMap.begin(); + if (m_CurrentInteractorIter == m_InteractorRelevanceMap.end()) + m_CurrentInteractorIter = m_InteractorRelevanceMap.begin(); return handled; } bool mitk::GlobalInteraction::AddFocusElement(mitk::FocusManager::FocusElement* element) { return m_FocusManager->AddElement(element); } bool mitk::GlobalInteraction::RemoveFocusElement(mitk::FocusManager::FocusElement* element) { return m_FocusManager->RemoveElement(element); } mitk::FocusManager::FocusElement* mitk::GlobalInteraction::GetFocus() { return m_FocusManager->GetFocused(); } bool mitk::GlobalInteraction::SetFocus(mitk::FocusManager::FocusElement* element) { return m_FocusManager->SetFocused(element); } mitk::FocusManager* mitk::GlobalInteraction::GetFocusManager() { return m_FocusManager.GetPointer(); } mitk::EventMapper* mitk::GlobalInteraction::GetEventMapper() { if (!this->IsInitialized()) { MITK_FATAL <<"Global Interaction needs initialization!\n"; return NULL; } return m_EventMapper; } bool mitk::GlobalInteraction::ExecuteAction(Action* action, mitk::StateEvent const* stateEvent) { bool ok = false; ok = false; switch (action->GetActionId()) { case AcDONOTHING: ok = true; break; case AcINFORMLISTENERS: - InformListeners(stateEvent); - ok = true; - break; + if (m_EventNotificationPolicy == INFORM_MULTIPLE) + { + InformListeners(stateEvent); + ok = true; + break; + } + else + { + //0.5 since this is the default value which is returned by the superclass implementation of statemachine + this->FillListenerRelevanceMap(stateEvent, 0.5); + if (!m_ListenerRelevanceMap.empty()) + { + StateMachineMapIter iter = m_ListenerRelevanceMap.begin(); + ok = (*iter).second->HandleEvent(stateEvent); + } + break; + } case AcASKINTERACTORS: if (! AskSelected(stateEvent))//no interactor selected anymore { - //fill the jurisdictionMap to ask them bit by bit. + //fill the RelevanceMap to ask them bit by bit. //currentInteractor is set here to the beginning - FillJurisdictionMap(stateEvent, 0); + FillInteractorRelevanceMap(stateEvent, 0); //ask the Interactors to handle that event AskCurrentInteractor(stateEvent); } ok = true; break; default: ok = true; } return ok; } mitk::GlobalInteraction* mitk::GlobalInteraction::GetInstance() { static mitk::GlobalInteraction::Pointer s_GlobalInteraction; if (s_GlobalInteraction.IsNull()) { s_GlobalInteraction = mitk::GlobalInteraction::New(); } return s_GlobalInteraction; } mitk::State* mitk::GlobalInteraction::GetStartState(const char* type) { if ( this->IsInitialized() ) return m_StateMachineFactory->GetStartState(type); MITK_FATAL << "Fatal Error in mitkGlobalInteraction.cpp: GlobalInteraction not initialized!\n"; return NULL; } bool mitk::GlobalInteraction::AddToSelectedInteractors(mitk::Interactor* interactor) { InteractorListIter position = std::find(m_SelectedList.begin(), m_SelectedList.end(),interactor); if (position != m_SelectedList.end()) { //already added so don't add once more! return true; } else m_SelectedList.push_back(interactor); return true; } bool mitk::GlobalInteraction::RemoveFromSelectedInteractors(mitk::Interactor* interactor) { if (interactor == NULL) return false; InteractorListIter position = std::find(m_SelectedList.begin(), m_SelectedList.end(),interactor); if (position != m_SelectedList.end()) { position = m_SelectedList.erase(position); return true; } else return false; } mitk::StateMachineFactory* mitk::GlobalInteraction::GetStateMachineFactory() { return m_StateMachineFactory; } bool mitk::GlobalInteraction::Initialize(const char* globalInteractionName, const std::string XMLBehaviorInput) { if (this->IsInitialized()) { MITK_WARN <<"Global Interaction has already been initialized.\n"; return false; } m_FocusManager = FocusManager::New(); // instantiates m_StateMachineFactory and load interaction patterns from XML string //if factory has been initialized before, delete it and initialize once more to not add patterns if (m_StateMachineFactory) m_StateMachineFactory->Delete(); m_StateMachineFactory = StateMachineFactory::New(); //if EventMapper was initialized before, delete it and initialize once more // to create new event descriptions and not to add them if (m_EventMapper) m_EventMapper->Delete(); m_EventMapper = EventMapper::New(); bool success = true; if (XMLBehaviorInput == "") { //load default behavior success &= m_StateMachineFactory->LoadStandardBehavior(); success &= m_EventMapper->LoadStandardBehavior(); } else if (itksys::SystemTools::FileExists(XMLBehaviorInput.c_str()) ) { // load standard behavior from file success &= m_StateMachineFactory->LoadBehavior(XMLBehaviorInput); success &= m_EventMapper->LoadBehavior(XMLBehaviorInput); } else { //load standard behavior from XML string success &= m_StateMachineFactory->LoadBehaviorString(XMLBehaviorInput); success &= m_EventMapper->LoadBehaviorString(XMLBehaviorInput); } if(!success) { MITK_FATAL << "Error initializing global interaction!\n"; return false; } //now instantiate what could not be done in InitializeStateStates because StateMachineFactory was not up yet: // (Re-) Initialize Superclass (StateMachine), because type was not given at time of construction m_Type = globalInteractionName; //get the start state of the pattern State::Pointer startState = m_StateMachineFactory->GetStartState(globalInteractionName); if (startState.IsNull()) { MITK_FATAL << "Fatal Error in mitkGlobalInteraction.cpp: No StartState recieved from StateMachineFactory!\n"; return false; } //clear the vector m_CurrentStateVector.clear(); //add the start state pointer for the first time step to the list m_CurrentStateVector.push_back(startState); m_IsInitialized = true; return true; } +void mitk::GlobalInteraction::SetEventNotificationPolicy(EVENT_NOTIFICATION_POLICY policy) +{ + this->m_EventNotificationPolicy = policy; +} + +mitk::GlobalInteraction::EVENT_NOTIFICATION_POLICY mitk::GlobalInteraction::GetEventNotificationPolicy() const +{ + return this->m_EventNotificationPolicy; +} + diff --git a/Core/Code/Interactions/mitkGlobalInteraction.h b/Core/Code/Interactions/mitkGlobalInteraction.h index 8947e9e0e7..44dfc86b15 100755 --- a/Core/Code/Interactions/mitkGlobalInteraction.h +++ b/Core/Code/Interactions/mitkGlobalInteraction.h @@ -1,274 +1,317 @@ /*=================================================================== 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 GLOBALINTERACTION_H_HEADER_INCLUDED_C152938A #define GLOBALINTERACTION_H_HEADER_INCLUDED_C152938A #include "mitkFocusManager.h" #include #include "mitkStateMachineFactory.h" #include "mitkEventMapper.h" #include "mitkInteractor.h" namespace mitk { class PositionEvent; //##Documentation //## @brief handles all global Events //## //## superior statemachine, that spreads the events to all other interactors //## //## Initialization //## Attention: GlobalInteraction must be initialized by the Initialize() method //## before usage by giving it an XML scheme. Possibilities are giving it an empty string (default), //## the filename of an XML file or the actual XML content as std::string. If an empty string is given, //## the content is tried to be loaded from the default file location. //## //## Concept of sending events: //## In this concept of interaction, the statemachines can be divided into two main statemachines: //## Listeners and interactors. //## Listeners only receive the event to process it, but don't change any data. They want to listen to all events. //## Interactors do change data according to the received event. They do not need to receive all events, only //## those they are interested in. + //## Additional to that a EVENT_NOTIFICATION_POLICY can be set. This can be either INFORM_MULTIPLE (the event is passed + //## to all listeners) or INFORM_ONE (event is passed to the listener which can handle the event best and only to this one). //## - //## To divide these two types of statemachine this class holds three lists and one map: - //## m_ListenerList, m_InteractorList, m_SelectedList and m_JurisdictionMap + //## To divide these two types of statemachine this class holds three lists and two maps: + //## m_ListenerList, m_InteractorList, m_SelectedList and m_InteractorRelevanceMap and m_ListenerRelevanceMap //## The list m_ListenerList holds all listeners. //## m_InteractorList holds all interactors, and the List m_SelectedList holds all machines, that were set to SELECTED or SUBSELECTED. - //## m_JurisdictionMap maps values returned from CanHandleEvent to the asked Interactors. - //## Through this map stepping through interactors, that were not selected and could handle that event, can be done. + //## m_InteractorRelevanceMap and m_ListenerRelevanceMap map values returned from CanHandleEvent to the asked Interactors and Listeners. + //## Through m_InteractorRelevanceMap stepping through interactors, that were not selected and could handle that event, can be done. + //## Through m_ListenerRelevanceMap the listener which can handle the event best can be determined. In case of the INFORM_ONE notification policy + //## the event is passed to just this listener //## //## First the listeners are informed with the event. //## Then the selected or subselected interactors are asked if they can handle that event. //## They can handle it, if the mode of the interactor after HandleEvent(..) is still in SMSELECTED or SMSUBSELECTED. //## They can't handle it, if the mode changed to SMDESELECTED. Then the interactor is removed from the selected-list. - //## In that case, all interactors are asked to calculate and return their area of jurisdiction. + //## In that case, all interactors are asked to calculate and return their area of Relevance. //## An iterator is held on one interactor in the map. With the iterator, the map can be looped through so //## so that several geometric objects, that lie on top of each other, can be selected. //## @ingroup Interaction class MITK_CORE_EXPORT GlobalInteraction : public StateMachine { public: mitkClassMacro(GlobalInteraction, StateMachine); itkNewMacro(Self); typedef std::vector StateMachineList; typedef std::vector StateMachineCPointerList; typedef StateMachineList::iterator StateMachineListIter; typedef StateMachineCPointerList::iterator StateMachineCPointerListIter; typedef std::vector InteractorList; typedef InteractorList::iterator InteractorListIter; typedef std::multimap > InteractorMap; + typedef std::multimap > StateMachineMap; typedef InteractorMap::iterator InteractorMapIter; + typedef StateMachineMap::iterator StateMachineMapIter; + + /** + * Enum for setting the event notification policy of the GlobalInteraction. + */ + enum EVENT_NOTIFICATION_POLICY + { + INFORM_MULTIPLE, /** For setting that all registered listeners are informed */ + INFORM_ONE /** For setting that just the listener that can handle the event best is informed */ + }; //##Documentation //## @brief add an Interactor to the list of all interactors that are asked for handling an event //## //## returns true in case of success void AddInteractor(Interactor* interactor); //##Documentation //## @brief remove a certain Interactor from the set of interactors that are asked for handling an event //## //## returns true in case of success bool RemoveInteractor(Interactor* interactor); //##Documentation //## @brief returns true, if the given interactor is already added to the Interactor-List bool InteractorRegistered (Interactor* interactor); //##Documentation //## @brief add a Listener to the list of all Listeners that are informed of an event //## //## returns true in case of success void AddListener(StateMachine* listener); //##Documentation //## @brief remove a certain Listener from the set of Listeners that are informed of an event //## //## returns true in case of success bool RemoveListener(StateMachine* listener); //##Documentation //## @brief returns true, if the given interactor is already added to the Listener-List bool ListenerRegistered (StateMachine* listener); //##Documentation //## @brief adds an element in the list in FocusManager //## //## true if success, false if the element is already in list bool AddFocusElement(FocusManager::FocusElement* element); //##Documentation //## @brief Removes an element in FocusManager //## //## true if success, false if the element was not in the list bool RemoveFocusElement(FocusManager::FocusElement* element); //##Documentation //## @brief Returns the focused Element in FocusManager FocusManager::FocusElement* GetFocus(); //##Documentation //## @brief Sets the given Element to focused //## //## returns true if the given element was found and focused bool SetFocus(FocusManager::FocusElement* element); //##Documentation //## @brief Returns the pointer to the FocusManager //## //## to add the observer for an event FocusManager* GetFocusManager(); //##Documentation //## @brief Returns the pointer to the EventMapper //## //## to add an addon EventMapper* GetEventMapper(); /** * @brief Return StateMachineFactory **/ StateMachineFactory* GetStateMachineFactory(); /** * @brief Returns the StartState of the StateMachine with the name type; * * Asks member StateMachineFactory for the StartState. * Returns NULL if no entry with name type is found. **/ State* GetStartState(const char* type); //##Documentation //## @brief Returns the global (singleton) instance of //## GlobalInteraction. Create it, if it does not exist. static GlobalInteraction* GetInstance(); //##Documentation //## @brief Initializes the global (singleton) instance of //## GlobalInteraction via an XML string. Must! be done before usage. Can be done only once. //## Can be used with an empty string (default), a file name with path, or the actual XML content as string. bool Initialize(const char* globalInteractionName, const std::string XMLBehaviorInput = ""); //##Documentation //## @brief Check if GlobalInteraction has already been initialized. Init must! be done before usage. - bool IsInitialized() {return m_IsInitialized;}; + bool IsInitialized() {return m_IsInitialized;} + + /** + * @brief Set the policy of how the global interaction informs listeners and interactors + * + * INFORM_MULTIPLE broadcasts the event to all listeners and interactors that can handle the event + * INFORM_ONE only informs the listener or interactor which can handle the event best + **/ + void SetEventNotificationPolicy(EVENT_NOTIFICATION_POLICY); + + /** + * Return the current set eventspreading policy + * @returns the current event spreading policy + **/ + EVENT_NOTIFICATION_POLICY GetEventNotificationPolicy() const; //so that the interactors can call AddToSelectedInteractors() and RemoveFromSelectedInteractors() friend class Interactor; protected: /** * @brief Default Constructor with type to load the StateMachinePattern of the StateMachine * @param XMLbehaviorFile the file which contains the statemachine and event patterns * @param type the name of the statemachine pattern this class shall use **/ GlobalInteraction(); /** * @brief Default destructor. **/ ~GlobalInteraction(); virtual bool ExecuteAction(Action* action, mitk::StateEvent const* stateEvent); /* *@brief adds the given interactor to the list of selected interactors. * This list is asked first to handle an event. */ virtual bool AddToSelectedInteractors(Interactor* interactor); /* *@brief removes the given interactor from the list of selected interactors * This list is asked first to handle an event. */ virtual bool RemoveFromSelectedInteractors(Interactor* interactor); private: //##Documentation //##@brief informing all statemachines that are held in the list m_ListenerList void InformListeners(mitk::StateEvent const* stateEvent); //##Documentation //##@brief asking the selected Interactor if an event can be handled //## //## returns false if no Interactor could handle the event bool AskSelected(mitk::StateEvent const* stateEvent); //##Documentation - //##@brief asking next interactor of m_JurisdictionMap + //##@brief asking next interactor of m_RelevanceMap bool AskCurrentInteractor(mitk::StateEvent const* stateEvent); //##Documentation - //##@brief filling m_JurisdictionMap + //##@brief filling m_InteractorRelevanceMap //## - //## @ params swell: if the calculated jurisdiction value is above swell, then add it to the map - void FillJurisdictionMap(mitk::StateEvent const* stateEvent, float threshold); + //## @ params threshold: if the calculated Relevance value is above threshold, then add it to the map + void FillInteractorRelevanceMap(mitk::StateEvent const* stateEvent, float threshold); + + //##Documentation + //##@brief filling m_ListenerRelevanceMap + //## + //## @ params threshold: if the calculated Relevance value is above threshold, then add it to the map + void FillListenerRelevanceMap(mitk::StateEvent const* stateEvent, float threshold); void RemoveFlaggedListeners(); StateMachineCPointerList m_ListenersFlaggedForRemoval; //##Documentation //## @brief list of all listening statemachines, that want to receive all events StateMachineList m_ListenerList; //##Documentation //## @brief list of all interactors (statemachine, that change data) InteractorList m_InteractorList; //##Documentation //## @brief list of all interactors, that are in Mode SELECTED or SUBSELECTED InteractorList m_SelectedList; //##Documentation //## @brief map for sorting all interactors by the value returned from CanHandleEvent(..). //## //## With that list certain interactors can be looped through like diving through layers - InteractorMap m_JurisdictionMap; + InteractorMap m_InteractorRelevanceMap; + + //##Documentation + //## @brief map for sorting all listeners by the value returned from CanHandleEvent(..). + //## + //## With that list certain listeners can be looped through like diving through layers + StateMachineMap m_ListenerRelevanceMap; //##Documentation - //## @brief iterator on an entry in m_JurisdictionMap for stepping through interactors + //## @brief iterator on an entry in m_RelevanceMap for stepping through interactors InteractorMapIter m_CurrentInteractorIter; //##Documentation //## @brief holds a list of BaseRenderer and one focused FocusManager::Pointer m_FocusManager; /** * @brief StatemachineFactory loads statemachine patterns and provides start states **/ StateMachineFactory* m_StateMachineFactory; /** * @brief EventMapper loads event patterns **/ EventMapper* m_EventMapper; bool m_CurrentlyInInformListenersLoop; bool m_CurrentlyInInformInteractorsLoop; bool m_IsInitialized; + + EVENT_NOTIFICATION_POLICY m_EventNotificationPolicy; }; } // namespace mitk #endif /* GLOBALINTERACTION_H_HEADER_INCLUDED_C152938A */ diff --git a/Core/Code/Interactions/mitkStateMachine.cpp b/Core/Code/Interactions/mitkStateMachine.cpp index f9bdf67199..2864894d2a 100644 --- a/Core/Code/Interactions/mitkStateMachine.cpp +++ b/Core/Code/Interactions/mitkStateMachine.cpp @@ -1,315 +1,320 @@ /*=================================================================== 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 "mitkStateMachine.h" #include "mitkStateTransitionOperation.h" #include "mitkInteractionConst.h" #include "mitkInteractor.h" #include "mitkTransition.h" #include "mitkOperationEvent.h" #include "mitkStateEvent.h" #include "mitkAction.h" #include "mitkUndoController.h" #include #include "mitkGlobalInteraction.h" #include /** * @brief Constructor * daclares a new StateMachine and connects * it to a StateMachine of Type type; * Also the undo mechanism is instanciated and enabled/disabled **/ mitk::StateMachine::StateMachine(const char * type) : m_UndoController(NULL), m_Type("") { if(type!=NULL) //no need to throw a warning here, because the statemachine yet here doesn't have to be set up. { m_Type = type; //the statemachine doesn't know yet anything about the number of timesteps of the data. So we initialize it with one element. this->InitializeStartStates(1); } if (!m_UndoController) { m_UndoController = new UndoController(UndoController::VERBOSE_LIMITEDLINEARUNDO);//switch to LLU or add LLU /** * here the Undo mechanism is enabled / disabled for all interactors. **/ m_UndoEnabled = true; } m_TimeStep = 0; } mitk::StateMachine::~StateMachine() { //clean up map using deletes for ( mitk::StateMachine::ActionFunctionsMapType::iterator iter = m_ActionFunctionsMap.begin(); iter != m_ActionFunctionsMap.end(); ++iter ) { delete iter->second; } delete m_UndoController; } std::string mitk::StateMachine::GetType() const { return m_Type; } const mitk::State* mitk::StateMachine::GetCurrentState(unsigned int timeStep) const { if (m_CurrentStateVector.size() > timeStep) //the size of the vector has to be one integer higher than the timeStep. return m_CurrentStateVector[timeStep].GetPointer(); return NULL; } void mitk::StateMachine::ResetStatemachineToStartState(unsigned int timeStep) { mitk::State* startState = mitk::GlobalInteraction::GetInstance()->GetStartState((const char *)(&m_Type[0])); if ( m_UndoEnabled ) //write to UndoMechanism if Undo is enabled { //UNDO for this statechange; StateTransitionOperation* doOp = new StateTransitionOperation(OpSTATECHANGE, startState, timeStep); StateTransitionOperation* undoOp = new StateTransitionOperation(OpSTATECHANGE, m_CurrentStateVector[timeStep], timeStep); OperationEvent *operationEvent = new OperationEvent(((mitk::OperationActor*)(this)), doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } //can be done without calling this->ExecuteOperation() m_CurrentStateVector[timeStep] = startState; } +float mitk::StateMachine::CanHandleEvent(const StateEvent *stateEvent) const +{ + return 0.5; +} + bool mitk::StateMachine::HandleEvent(StateEvent const* stateEvent) { if (stateEvent == NULL) return false; if (m_CurrentStateVector.empty()) { STATEMACHINE_ERROR << "Error in mitkStateMachine.cpp: StateMachine not initialized!\n"; return false;//m_CurrentStateVector needs to be initialized! } if (m_TimeStep >= m_CurrentStateVector.size()) { STATEMACHINE_ERROR << "Error in mitkStateMachine.cpp: StateMachine not initialized for this time step!\n"; return false; } if (m_CurrentStateVector[m_TimeStep].IsNull()) { STATEMACHINE_ERROR << "Error in mitkStateMachine.cpp: StateMachine not initialized with the right temporal information!\n"; return false;//m_CurrentState needs to be initialized! } //get the Transition from m_CurrentState which waits for this EventId const Transition *tempTransition = m_CurrentStateVector[m_TimeStep]->GetTransition(stateEvent->GetId()); if (tempTransition == NULL) //no transition in this state for that EventId { return false; } //get next State State *tempNextState = tempTransition->GetNextState(); if (tempNextState == NULL) //wrong built up statemachine! { STATEMACHINE_ERROR << "Error in mitkStateMachine.cpp: StateMachinePattern not defined correctly!\n"; return false; } //and ActionId to execute later on if ( m_CurrentStateVector[m_TimeStep]->GetId() != tempNextState->GetId() )//statechange only if there is a real statechange { if ( m_UndoEnabled ) //write to UndoMechanism if Undo is enabled { //UNDO for this statechange; since we directly change the state, we don't need the do-Operation in case m_UndoEnables == false StateTransitionOperation* doOp = new StateTransitionOperation(OpSTATECHANGE, tempNextState, m_TimeStep); StateTransitionOperation* undoOp = new StateTransitionOperation(OpSTATECHANGE, m_CurrentStateVector[m_TimeStep], m_TimeStep); OperationEvent *operationEvent = new OperationEvent(((mitk::OperationActor*)(this)), doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } STATEMACHINE_DEBUG << "from " << m_CurrentStateVector[m_TimeStep]->GetId() << " " << m_CurrentStateVector[m_TimeStep]->GetName() << " to " << tempNextState->GetId() <<" "<GetName(); //first following StateChange(or calling ExecuteOperation(tempNextStateOp)), then operation(action) m_CurrentStateVector[m_TimeStep] = tempNextState; } mitk::Transition::ActionVectorIterator actionIdIterator = tempTransition->GetActionBeginIterator(); mitk::Transition::ActionVectorConstIterator actionIdIteratorEnd = tempTransition->GetActionEndIterator(); bool ok = true; while ( actionIdIterator != actionIdIteratorEnd ) { if ( !ExecuteAction(*actionIdIterator, stateEvent) ) { ok = false; } actionIdIterator++; } return ok; } void mitk::StateMachine::EnableUndo(bool enable) { m_UndoEnabled = enable; } void mitk::StateMachine::IncCurrGroupEventId() { mitk::OperationEvent::IncCurrGroupEventId(); } /// look up which object method is associated to the given action and call the method bool mitk::StateMachine::ExecuteAction(Action* action, StateEvent const* stateEvent) { if (!action) return false; int actionId = action->GetActionId(); TStateMachineFunctor* actionFunction = m_ActionFunctionsMap[actionId]; if (!actionFunction) return false; bool retVal = actionFunction->DoAction(action, stateEvent); return retVal; } void mitk::StateMachine::AddActionFunction(int action, mitk::TStateMachineFunctor* functor) { if (!functor) return; // make sure double calls for same action won't cause memory leaks delete m_ActionFunctionsMap[action]; // delete NULL does no harm m_ActionFunctionsMap[action] = functor; } void mitk::StateMachine::ExecuteOperation(Operation* operation) { switch (operation->GetOperationType()) { case OpNOTHING: break; case OpSTATECHANGE: { mitk::StateTransitionOperation* stateTransOp = dynamic_cast(operation); if (stateTransOp == NULL) { STATEMACHINE_WARN<<"Error! see mitkStateMachine.cpp"; return; } unsigned int time = stateTransOp->GetTime(); m_CurrentStateVector[time] = stateTransOp->GetState(); } break; case OpTIMECHANGE: { mitk::StateTransitionOperation* stateTransOp = dynamic_cast(operation); if (stateTransOp == NULL) { STATEMACHINE_WARN<<"Error! see mitkStateMachine.cpp"; return; } m_TimeStep = stateTransOp->GetTime(); } break; case OpDELETE: { //delete this! //before all lower statemachines has to be deleted in a action //this->Delete();//might not work!!!check itk! } case OpUNDELETE: { //now the m_CurrentState has to be set on a special State //that way a delete of a StateMachine can be undone //IMPORTANT: The type has to be the same!!!Done by a higher instance, that creates this! mitk::StateTransitionOperation* stateTransOp = dynamic_cast(operation); if (stateTransOp != NULL) { unsigned int time = stateTransOp->GetTime(); m_CurrentStateVector[time] = stateTransOp->GetState(); } } default: ; } } void mitk::StateMachine::InitializeStartStates(unsigned int timeSteps) { //get the startstate of the pattern State::Pointer startState = mitk::GlobalInteraction::GetInstance()->GetStartState(m_Type.c_str()); if (startState.IsNull()) { STATEMACHINE_FATAL << "Fatal Error in mitkStateMachine.cpp: Initialization of statemachine unsuccessfull! Initialize GlobalInteraction!\n"; } //clear the vector m_CurrentStateVector.clear(); //add n=timesteps pointers pointing to to the startstate for (unsigned int i = 0; i < timeSteps; i++) m_CurrentStateVector.push_back(startState); } // Check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient pre-initialized // elements. // // NOTE: This method will never REDUCE the vector size; it should only // be used to make sure that the vector has enough elements to include the // specified time step. void mitk::StateMachine::ExpandStartStateVector(unsigned int timeSteps) { unsigned int oldSize = m_CurrentStateVector.size(); if ( timeSteps > oldSize ) { State::Pointer startState = mitk::GlobalInteraction::GetInstance()->GetStartState(m_Type.c_str()); for ( unsigned int i = oldSize; i < timeSteps; ++i ) m_CurrentStateVector.insert(m_CurrentStateVector.end(), startState); } } void mitk::StateMachine::UpdateTimeStep(unsigned int timeStep) { //don't need to fill up the memory if the time is uptodate if (timeStep == m_TimeStep) return; //create an operation that changes the time and send it to undocontroller StateTransitionOperation* doOp = new StateTransitionOperation(OpTIMECHANGE, NULL, timeStep); if ( m_UndoEnabled ) //write to UndoMechanism if Undo is enabled { StateTransitionOperation* undoOp = new StateTransitionOperation(OpTIMECHANGE, NULL, m_TimeStep); OperationEvent *operationEvent = new OperationEvent(((mitk::OperationActor*)(this)), doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } this->ExecuteOperation(doOp); } diff --git a/Core/Code/Interactions/mitkStateMachine.h b/Core/Code/Interactions/mitkStateMachine.h index 3816ecdfb6..7ba7330f87 100644 --- a/Core/Code/Interactions/mitkStateMachine.h +++ b/Core/Code/Interactions/mitkStateMachine.h @@ -1,291 +1,301 @@ /*=================================================================== 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 STATEMACHINE_H_HEADER_INCLUDED_C18896BD #define STATEMACHINE_H_HEADER_INCLUDED_C18896BD #include #include #include "mitkOperationActor.h" #include #include "mitkState.h" #include "mitkUndoModel.h" namespace mitk { class Action; class StateEvent; class UndoController; // base class of statem machine functors class MITK_CORE_EXPORT TStateMachineFunctor { public: virtual bool DoAction(Action*, const StateEvent*)=0; // call using function virtual ~TStateMachineFunctor() {} }; // the template functor for arbitrary StateMachine derivations template class TSpecificStateMachineFunctor : public TStateMachineFunctor { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables TSpecificStateMachineFunctor(T* object, bool(T::*memberFunctionPointer)(Action*, const StateEvent*)) :m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } virtual ~TSpecificStateMachineFunctor() {} // virtual destructor // override function "Call" virtual bool DoAction(Action* action, const StateEvent* stateEvent) { return (*m_Object.*m_MemberFunctionPointer)(action, stateEvent); // execute member function } private: T* m_Object; // pointer to object bool (T::*m_MemberFunctionPointer)(Action*, const StateEvent*); // pointer to member function }; /// Can be uses by derived classes of StateMachine to connect action IDs to methods /// Assumes that there is a typedef Classname Self in classes that use this macro #define CONNECT_ACTION(a, f) \ StateMachine::AddActionFunction(a, new TSpecificStateMachineFunctor(this, &Self::f)); #define STATEMACHINE_INFO MITK_INFO("StateMachine") << "[type: " << GetType() << "] " #define STATEMACHINE_WARN MITK_WARN("StateMachine") << "[type: " << GetType() << "] " #define STATEMACHINE_FATAL MITK_FATAL("StateMachine") << "[type: " << GetType() << "] " #define STATEMACHINE_ERROR MITK_ERROR("StateMachine") << "[type: " << GetType() << "] " #define STATEMACHINE_DEBUG MITK_DEBUG("StateMachine") << "[type: " << GetType() << "] " /** @brief Superior statemachine @ingroup Interaction Realizes the methods, that every statemachine has to have. Undo can be enabled and disabled through EnableUndo. To implement your own state machine, you have to derive a class from mitk::StateMachine and either - override ExecuteAction() or - Write bool methods that take (Action*, const StateEvent*) as parameter and use the CONNECT_ACTION macro in your constructor The second version is recommended, since it provides more structured code. The following piece of code demonstrates how to use the CONNECT_ACTION macro. The important detail is to provide a typedef classname Self \code class LightSwitch : public StateMachine { public: mitkClassMacro(LightSwitch, StateMachine); // this creates the Self typedef LightSwitch(const char*); bool DoSwitchOn(Action*, const StateEvent*); bool DoSwitchOff(Action*, const StateEvent*); } LightSwitch::LightSwitch(const char* type) :StateMachine(type) { // make sure that AcSWITCHON and AcSWITCHOFF are defined int constants somewhere (e.g. mitkInteractionConst.h) CONNECT_ACTION( AcSWITCHON, DoSwitchOn ); CONNECT_ACTION( AcSWITCHOFF, DoSwitchOff ); } bool LightSwitch::DoSwitchOn(Action*, const StateEvent*) { std::cout << "Enlightenment" << std::endl; } bool LightSwitch::DoSwitchOff(Action*, const StateEvent*) { std::cout << "Confusion" << std::endl; } \endcode What CONNECT_ACTION does, is call StateMachine::AddActionFunction(...) to add some function pointer wrapping class (functor) to a std::map of StateMachine. Whenever StateMachines ExecuteAction is called, StateMachine will lookup the desired Action in its map and call the appropriate method in your derived class. **/ class MITK_CORE_EXPORT StateMachine : public itk::Object, public mitk::OperationActor { public: mitkClassMacro(StateMachine,itk::Object); /** * @brief New Macro with one parameter for creating this object with static New(..) method **/ mitkNewMacro1Param(Self, const char*); /** * @brief Map to connect action IDs with method calls. Use AddActionFunction or (even better) the CONNECT_ACTION macro to fill the map. **/ typedef std::map ActionFunctionsMapType; /** * @brief Type for a vector of StartStatePointers **/ typedef std::vector StartStateVectorType; /** * @brief Get the name and with this the type of the StateMachine **/ std::string GetType() const; /** * @brief handles an Event accordingly to its current State * * Statechange with Undo functionality; * EventMapper gives each event a new objectEventId * and a StateMachine::ExecuteAction can descide weather it gets a * new GroupEventId or not, depending on its state (e.g. finishedNewObject then new GroupEventId). * Object- and group-EventId can also be accessed through static methods from OperationEvent **/ virtual bool HandleEvent(StateEvent const* stateEvent); + /** + * @brief calculates how good this statemachine can handle the event. + * + * Returns a value between 0 and 1 + * where 0 represents not responsible and 1 represents definitive responsible! + * Standard function to override if needed. + * (Used by GlobalInteraction to decide which DESELECTED statemachine to send the event to.) + **/ + virtual float CanHandleEvent(StateEvent const* stateEvent) const; + /** * @brief Enables or disabled Undo. **/ void EnableUndo(bool enable); /** * @brief A statemachine is also an OperationActor due to the UndoMechanism. * * The statechange is done in ExecuteOperation, so that the statechange can be undone by UndoMechanism. * Is set private here and in superclass it is set public, so UndoController * can reach ist, but it can't be overwritten by a subclass * *ATTENTION*: THIS METHOD SHOULD NOT BE CALLED FROM OTHER CLASSES DIRECTLY! **/ virtual void ExecuteOperation(Operation* operation); /** * @brief Friend so that UndoModel can call ExecuteOperation for Undo. **/ friend class UndoModel; friend class GlobalInteraction; protected: /** * @brief Default Constructor. Obsolete to instanciate it with this method! Use ::New(..) method instead. Set the "type" and with this the pattern of the StateMachine **/ StateMachine(const char * type); /** * @brief Default Destructor **/ ~StateMachine(); /** * @brief Adds the Function to ActionList. **/ void AddActionFunction(int action, TStateMachineFunctor* functor); /** * @brief Method called in HandleEvent after Statechange. * * Each statechange has actions, which can be assigned by it's number. * If you are developing a new statemachine, declare all your operations here and send them to Undo-Controller and to the Data. * Object- and group-EventId can also be accessed through static methods from OperationEvent **/ virtual bool ExecuteAction(Action* action, StateEvent const* stateEvent); /** * @brief returns the current state **/ const State* GetCurrentState(unsigned int timeStep = 0) const; /** * @brief if true, then UndoFunctionality is enabled * * Default value is true; **/ bool m_UndoEnabled; /** * @brief Friend protected function of OperationEvent; that way all StateMachines can set GroupEventId to be incremented! **/ void IncCurrGroupEventId(); /** * @brief holds an UndoController, that can be accessed from all StateMachines. For ExecuteAction **/ UndoController* m_UndoController; /** * @brief Resets the current state from the given timeStep to the StartState with undo functionality! Use carefully! * @param[in] timeStep If the statemachine has several timesteps to take care of, specify the according timestep **/ void ResetStatemachineToStartState(unsigned int timeStep = 0); /** * @brief Check if the number of timeSteps is equal to the number of stored StartStates. Nothing is changed if the number is equal. **/ void ExpandStartStateVector(unsigned int timeSteps); /** * @brief initializes m_CurrentStateVector **/ void InitializeStartStates(unsigned int timeSteps); /** * @brief Update the TimeStep of the statemachine with undo-support if undo enabled **/ virtual void UpdateTimeStep(unsigned int timeStep); /** * @brief Current TimeStep if the data which is to be interacted on, has more than 1 TimeStep **/ unsigned int m_TimeStep; private: /** * @brief The type of the StateMachine. This string specifies the StateMachinePattern that is loaded from the StateMachineFactory. **/ std::string m_Type; /** * @brief Points to the current state. **/ StartStateVectorType m_CurrentStateVector; /** * @brief Map of the added Functions **/ ActionFunctionsMapType m_ActionFunctionsMap; }; } // namespace mitk #endif /* STATEMACHINE_H_HEADER_INCLUDED_C18896BD */ diff --git a/Modules/MitkExt/Controllers/mitkToolManager.cpp b/Modules/MitkExt/Controllers/mitkToolManager.cpp index b80265e03b..30854299e1 100644 --- a/Modules/MitkExt/Controllers/mitkToolManager.cpp +++ b/Modules/MitkExt/Controllers/mitkToolManager.cpp @@ -1,522 +1,528 @@ /*=================================================================== 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 "mitkToolManager.h" #include "mitkGlobalInteraction.h" #include "mitkCoreObjectFactory.h" #include #include #include mitk::ToolManager::ToolManager(DataStorage* storage) :m_ActiveTool(NULL), m_ActiveToolID(-1), m_RegisteredClients(0), m_DataStorage(storage) { CoreObjectFactory::GetInstance(); // to make sure a CoreObjectFactory was instantiated (and in turn, possible tools are registered) - bug 1029 // get a list of all known mitk::Tools std::list thingsThatClaimToBeATool = itk::ObjectFactoryBase::CreateAllInstance("mitkTool"); // remember these tools for ( std::list::iterator iter = thingsThatClaimToBeATool.begin(); iter != thingsThatClaimToBeATool.end(); ++iter ) { if ( Tool* tool = dynamic_cast( iter->GetPointer() ) ) { tool->SetToolManager(this); // important to call right after instantiation tool->ErrorMessage += MessageDelegate1( this, &ToolManager::OnToolErrorMessage ); tool->GeneralMessage += MessageDelegate1( this, &ToolManager::OnGeneralToolMessage ); m_Tools.push_back( tool ); } } //ActivateTool(0); // first one is default } mitk::ToolManager::~ToolManager() { for (DataVectorType::iterator dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter) (*dataIter)->RemoveObserver(m_WorkingDataObserverTags[(*dataIter)]); if(this->GetDataStorage() != NULL) this->GetDataStorage()->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1 ( this, &ToolManager::OnNodeRemoved )); if (m_ActiveTool) { m_ActiveTool->Deactivated(); GlobalInteraction::GetInstance()->RemoveListener( m_ActiveTool ); m_ActiveTool = NULL; m_ActiveToolID = -1; // no tool active ActiveToolChanged.Send(); } for ( NodeTagMapType::iterator observerTagMapIter = m_ReferenceDataObserverTags.begin(); observerTagMapIter != m_ReferenceDataObserverTags.end(); ++observerTagMapIter ) { observerTagMapIter->first->RemoveObserver( observerTagMapIter->second ); } } void mitk::ToolManager::OnToolErrorMessage(std::string s) { this->ToolErrorMessage(s); } void mitk::ToolManager::OnGeneralToolMessage(std::string s) { this->GeneralToolMessage(s); } const mitk::ToolManager::ToolVectorTypeConst mitk::ToolManager::GetTools() { ToolVectorTypeConst resultList; for ( ToolVectorType::iterator iter = m_Tools.begin(); iter != m_Tools.end(); ++iter ) { resultList.push_back( iter->GetPointer() ); } return resultList; } mitk::Tool* mitk::ToolManager::GetToolById(int id) { try { return m_Tools.at(id); } catch(std::exception&) { return NULL; } } bool mitk::ToolManager::ActivateTool(int id) { if(this->GetDataStorage()) { this->GetDataStorage()->RemoveNodeEvent.AddListener( mitk::MessageDelegate1 ( this, &ToolManager::OnNodeRemoved ) ); } //MITK_INFO << "ToolManager::ActivateTool("<SetEventNotificationPolicy(GlobalInteraction::INFORM_MULTIPLE); + if ( GetToolById( id ) == m_ActiveTool ) return true; // no change needed static int nextTool = -1; nextTool = id; //MITK_INFO << "ToolManager::ActivateTool("<Deactivated(); GlobalInteraction::GetInstance()->RemoveListener( m_ActiveTool ); } m_ActiveTool = GetToolById( nextTool ); m_ActiveToolID = m_ActiveTool ? nextTool : -1; // current ID if tool is valid, otherwise -1 ActiveToolChanged.Send(); if (m_ActiveTool) { if (m_RegisteredClients > 0) { m_ActiveTool->Activated(); GlobalInteraction::GetInstance()->AddListener( m_ActiveTool ); + //If a tool is activated set event notification policy to one + GlobalInteraction::GetInstance()->SetEventNotificationPolicy(GlobalInteraction::INFORM_ONE); } } } inActivateTool = false; return (m_ActiveTool != NULL); } void mitk::ToolManager::SetReferenceData(DataVectorType data) { if (data != m_ReferenceData) { // remove observers from old nodes for ( DataVectorType::iterator dataIter = m_ReferenceData.begin(); dataIter != m_ReferenceData.end(); ++dataIter ) { NodeTagMapType::iterator searchIter = m_ReferenceDataObserverTags.find( *dataIter ); if ( searchIter != m_ReferenceDataObserverTags.end() ) { //MITK_INFO << "Stopping observation of " << (void*)(*dataIter) << std::endl; (*dataIter)->RemoveObserver( searchIter->second ); } } m_ReferenceData = data; // TODO tell active tool? // attach new observers m_ReferenceDataObserverTags.clear(); for ( DataVectorType::iterator dataIter = m_ReferenceData.begin(); dataIter != m_ReferenceData.end(); ++dataIter ) { //MITK_INFO << "Observing " << (void*)(*dataIter) << std::endl; itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &ToolManager::OnOneOfTheReferenceDataDeleted ); command->SetCallbackFunction( this, &ToolManager::OnOneOfTheReferenceDataDeletedConst ); m_ReferenceDataObserverTags.insert( std::pair( (*dataIter), (*dataIter)->AddObserver( itk::DeleteEvent(), command ) ) ); } ReferenceDataChanged.Send(); } } void mitk::ToolManager::OnOneOfTheReferenceDataDeletedConst(const itk::Object* caller, const itk::EventObject& e) { OnOneOfTheReferenceDataDeleted( const_cast(caller), e ); } void mitk::ToolManager::OnOneOfTheReferenceDataDeleted(itk::Object* caller, const itk::EventObject& itkNotUsed(e)) { //MITK_INFO << "Deleted: " << (void*)caller << " Removing from reference data list." << std::endl; DataVectorType v; for (DataVectorType::iterator dataIter = m_ReferenceData.begin(); dataIter != m_ReferenceData.end(); ++dataIter ) { //MITK_INFO << " In list: " << (void*)(*dataIter); if ( (void*)(*dataIter) != (void*)caller ) { v.push_back( *dataIter ); //MITK_INFO << " kept" << std::endl; } else { //MITK_INFO << " removed" << std::endl; m_ReferenceDataObserverTags.erase( *dataIter ); // no tag to remove anymore } } this->SetReferenceData( v ); } void mitk::ToolManager::SetReferenceData(DataNode* data) { //MITK_INFO << "ToolManager::SetReferenceData(" << (void*)data << ")" << std::endl; DataVectorType v; if (data) { v.push_back(data); } SetReferenceData(v); } void mitk::ToolManager::SetWorkingData(DataVectorType data) { if ( data != m_WorkingData ) { // remove observers from old nodes for ( DataVectorType::iterator dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter ) { NodeTagMapType::iterator searchIter = m_WorkingDataObserverTags.find( *dataIter ); if ( searchIter != m_WorkingDataObserverTags.end() ) { //MITK_INFO << "Stopping observation of " << (void*)(*dataIter) << std::endl; (*dataIter)->RemoveObserver( searchIter->second ); } } m_WorkingData = data; // TODO tell active tool? // attach new observers m_WorkingDataObserverTags.clear(); for ( DataVectorType::iterator dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter ) { //MITK_INFO << "Observing " << (void*)(*dataIter) << std::endl; itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &ToolManager::OnOneOfTheWorkingDataDeleted ); command->SetCallbackFunction( this, &ToolManager::OnOneOfTheWorkingDataDeletedConst ); m_WorkingDataObserverTags.insert( std::pair( (*dataIter), (*dataIter)->AddObserver( itk::DeleteEvent(), command ) ) ); } WorkingDataChanged.Send(); } } void mitk::ToolManager::OnOneOfTheWorkingDataDeletedConst(const itk::Object* caller, const itk::EventObject& e) { OnOneOfTheWorkingDataDeleted( const_cast(caller), e ); } void mitk::ToolManager::OnOneOfTheWorkingDataDeleted(itk::Object* caller, const itk::EventObject& itkNotUsed(e)) { //MITK_INFO << "Deleted: " << (void*)caller << " Removing from reference data list." << std::endl; DataVectorType v; for (DataVectorType::iterator dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter ) { //MITK_INFO << " In list: " << (void*)(*dataIter); if ( (void*)(*dataIter) != (void*)caller ) { v.push_back( *dataIter ); //MITK_INFO << " kept" << std::endl; } else { //MITK_INFO << " removed" << std::endl; m_WorkingDataObserverTags.erase( *dataIter ); // no tag to remove anymore } } this->SetWorkingData( v ); } void mitk::ToolManager::SetWorkingData(DataNode* data) { DataVectorType v; if (data) // don't allow for NULL nodes { v.push_back(data); } SetWorkingData(v); } void mitk::ToolManager::SetRoiData(DataVectorType data) { if (data != m_RoiData) { // remove observers from old nodes for ( DataVectorType::iterator dataIter = m_RoiData.begin(); dataIter != m_RoiData.end(); ++dataIter ) { NodeTagMapType::iterator searchIter = m_RoiDataObserverTags.find( *dataIter ); if ( searchIter != m_RoiDataObserverTags.end() ) { //MITK_INFO << "Stopping observation of " << (void*)(*dataIter) << std::endl; (*dataIter)->RemoveObserver( searchIter->second ); } } m_RoiData = data; // TODO tell active tool? // attach new observers m_RoiDataObserverTags.clear(); for ( DataVectorType::iterator dataIter = m_RoiData.begin(); dataIter != m_RoiData.end(); ++dataIter ) { //MITK_INFO << "Observing " << (void*)(*dataIter) << std::endl; itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &ToolManager::OnOneOfTheRoiDataDeleted ); command->SetCallbackFunction( this, &ToolManager::OnOneOfTheRoiDataDeletedConst ); m_RoiDataObserverTags.insert( std::pair( (*dataIter), (*dataIter)->AddObserver( itk::DeleteEvent(), command ) ) ); } RoiDataChanged.Send(); } } void mitk::ToolManager::SetRoiData(DataNode* data) { DataVectorType v; if(data) { v.push_back(data); } this->SetRoiData(v); } void mitk::ToolManager::OnOneOfTheRoiDataDeletedConst(const itk::Object* caller, const itk::EventObject& e) { OnOneOfTheRoiDataDeleted( const_cast(caller), e ); } void mitk::ToolManager::OnOneOfTheRoiDataDeleted(itk::Object* caller, const itk::EventObject& itkNotUsed(e)) { //MITK_INFO << "Deleted: " << (void*)caller << " Removing from roi data list." << std::endl; DataVectorType v; for (DataVectorType::iterator dataIter = m_RoiData.begin(); dataIter != m_RoiData.end(); ++dataIter ) { //MITK_INFO << " In list: " << (void*)(*dataIter); if ( (void*)(*dataIter) != (void*)caller ) { v.push_back( *dataIter ); //MITK_INFO << " kept" << std::endl; } else { //MITK_INFO << " removed" << std::endl; m_RoiDataObserverTags.erase( *dataIter ); // no tag to remove anymore } } this->SetRoiData( v ); } mitk::ToolManager::DataVectorType mitk::ToolManager::GetReferenceData() { return m_ReferenceData; } mitk::DataNode* mitk::ToolManager::GetReferenceData(int idx) { try { return m_ReferenceData.at(idx); } catch(std::exception&) { return NULL; } } mitk::ToolManager::DataVectorType mitk::ToolManager::GetWorkingData() { return m_WorkingData; } mitk::ToolManager::DataVectorType mitk::ToolManager::GetRoiData() { return m_RoiData; } mitk::DataNode* mitk::ToolManager::GetRoiData(int idx) { try { return m_RoiData.at(idx); } catch(std::exception&) { return NULL; } } mitk::DataStorage* mitk::ToolManager::GetDataStorage() { if ( m_DataStorage.IsNotNull() ) { return m_DataStorage; } else { return NULL; } } void mitk::ToolManager::SetDataStorage(DataStorage& storage) { m_DataStorage = &storage; } mitk::DataNode* mitk::ToolManager::GetWorkingData(int idx) { try { return m_WorkingData.at(idx); } catch(std::exception&) { return NULL; } } int mitk::ToolManager::GetActiveToolID() { return m_ActiveToolID; } mitk::Tool* mitk::ToolManager::GetActiveTool() { return m_ActiveTool; } void mitk::ToolManager::RegisterClient() { if ( m_RegisteredClients < 1 ) { if ( m_ActiveTool ) { m_ActiveTool->Activated(); GlobalInteraction::GetInstance()->AddListener( m_ActiveTool ); } } ++m_RegisteredClients; } void mitk::ToolManager::UnregisterClient() { if ( m_RegisteredClients < 1) return; --m_RegisteredClients; if ( m_RegisteredClients < 1 ) { if ( m_ActiveTool ) { m_ActiveTool->Deactivated(); GlobalInteraction::GetInstance()->RemoveListener( m_ActiveTool ); } } } int mitk::ToolManager::GetToolID( const Tool* tool ) { int id(0); for ( ToolVectorType::iterator iter = m_Tools.begin(); iter != m_Tools.end(); ++iter, ++id ) { if ( tool == iter->GetPointer() ) { return id; } } return -1; } void mitk::ToolManager::OnNodeRemoved(const mitk::DataNode* node) { //check if the data of the node is typeof Image /*if(dynamic_cast(node->GetData())) {*/ //check all storage vectors OnOneOfTheReferenceDataDeleted(const_cast(node), itk::DeleteEvent()); OnOneOfTheRoiDataDeleted(const_cast(node),itk::DeleteEvent()); OnOneOfTheWorkingDataDeleted(const_cast(node),itk::DeleteEvent()); //} } diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp index 6fa80d90bc..c410f95907 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp @@ -1,347 +1,355 @@ /*=================================================================== 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 "mitkSegTool2D.h" #include "mitkToolManager.h" #include "mitkDataStorage.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkExtractImageFilter.h" #include "mitkExtractDirectedPlaneImageFilter.h" //Include of the new ImageExtractor #include "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkPlanarCircle.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkGetModuleContext.h" //Includes for 3DSurfaceInterpolation #include "mitkImageToContourFilter.h" #include "mitkSurfaceInterpolationController.h" //includes for resling and overwriting #include #include #include #include #include #include "mitkOperationEvent.h" #include "mitkUndoController.h" #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) mitk::SegTool2D::SegTool2D(const char* type) :Tool(type), m_LastEventSender(NULL), m_LastEventSlice(0), m_Contourmarkername ("Position"), m_ShowMarkerNodes (true) { } mitk::SegTool2D::~SegTool2D() { } float mitk::SegTool2D::CanHandleEvent( StateEvent const *stateEvent) const { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return 0.0; if ( positionEvent->GetSender()->GetMapperID() != BaseRenderer::Standard2D ) return 0.0; // we don't want anything but 2D - if( m_LastEventSender != positionEvent->GetSender()) return 0.0; - if( m_LastEventSlice != positionEvent->GetSender()->GetSlice() ) return 0.0; - - return 1.0; + //This are the mouse event that are used by the statemachine patterns for zooming and panning. This must be possible although a tool is activ + if (stateEvent->GetId() == EIDRIGHTMOUSEBTN || stateEvent->GetId() == EIDMIDDLEMOUSEBTN || stateEvent->GetId() == EIDRIGHTMOUSEBTNANDCTRL || + stateEvent->GetId() == EIDMIDDLEMOUSERELEASE || stateEvent->GetId() == EIDRIGHTMOUSERELEASE || stateEvent->GetId() == EIDRIGHTMOUSEBTNANDMOUSEMOVE || + stateEvent->GetId() == EIDMIDDLEMOUSEBTNANDMOUSEMOVE || stateEvent->GetId() == EIDCTRLANDRIGHTMOUSEBTNANDMOUSEMOVE || stateEvent->GetId() == EIDCTRLANDRIGHTMOUSEBTNRELEASE ) + { + //Since the usual segmentation tools currently do not need right click interaction but the mitkDisplayVectorInteractor + return 0.0; + } + else + { + return 1.0; + } } bool mitk::SegTool2D::DetermineAffectedImageSlice( const Image* image, const PlaneGeometry* plane, int& affectedDimension, int& affectedSlice ) { assert(image); assert(plane); // compare normal of plane to the three axis vectors of the image Vector3D normal = plane->GetNormal(); Vector3D imageNormal0 = image->GetSlicedGeometry()->GetAxisVector(0); Vector3D imageNormal1 = image->GetSlicedGeometry()->GetAxisVector(1); Vector3D imageNormal2 = image->GetSlicedGeometry()->GetAxisVector(2); normal.Normalize(); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); imageNormal0.Set_vnl_vector( vnl_cross_3d(normal.Get_vnl_vector(),imageNormal0.Get_vnl_vector()) ); imageNormal1.Set_vnl_vector( vnl_cross_3d(normal.Get_vnl_vector(),imageNormal1.Get_vnl_vector()) ); imageNormal2.Set_vnl_vector( vnl_cross_3d(normal.Get_vnl_vector(),imageNormal2.Get_vnl_vector()) ); double eps( 0.00001 ); // axial if ( imageNormal2.GetNorm() <= eps ) { affectedDimension = 2; } // sagittal else if ( imageNormal1.GetNorm() <= eps ) { affectedDimension = 1; } // frontal else if ( imageNormal0.GetNorm() <= eps ) { affectedDimension = 0; } else { affectedDimension = -1; // no idea return false; } // determine slice number in image Geometry3D* imageGeometry = image->GetGeometry(0); Point3D testPoint = imageGeometry->GetCenter(); Point3D projectedPoint; plane->Project( testPoint, projectedPoint ); Point3D indexPoint; imageGeometry->WorldToIndex( projectedPoint, indexPoint ); affectedSlice = ROUND( indexPoint[affectedDimension] ); MITK_DEBUG << "indexPoint " << indexPoint << " affectedDimension " << affectedDimension << " affectedSlice " << affectedSlice; // check if this index is still within the image if ( affectedSlice < 0 || affectedSlice >= static_cast(image->GetDimension(affectedDimension)) ) return false; return true; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PositionEvent* positionEvent, const Image* image) { if (!positionEvent) return NULL; assert( positionEvent->GetSender() ); // sure, right? unsigned int timeStep = positionEvent->GetSender()->GetTimeStep( image ); // get the timestep of the visible part (time-wise) of the image // first, we determine, which slice is affected const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); if ( !image || !planeGeometry ) return NULL; //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); //set to false to extract a slice reslice->SetOverwriteMode(false); reslice->Modified(); //use ExtractSliceFilter with our specific vtkImageReslice for overwriting and extracting mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput( image ); extractor->SetTimeStep( timeStep ); extractor->SetWorldGeometry( planeGeometry ); extractor->SetVtkOutputRequest(false); extractor->SetResliceTransformByGeometry( image->GetTimeSlicedGeometry()->GetGeometry3D( timeStep ) ); extractor->Modified(); extractor->Update(); Image::Pointer slice = extractor->GetOutput(); /*============= BEGIN undo feature block ========================*/ //specify the undo operation with the non edited slice m_undoOperation = new DiffSliceOperation(const_cast(image), extractor->GetVtkOutput(), slice->GetGeometry(), timeStep, const_cast(planeGeometry)); /*============= END undo feature block ========================*/ return slice; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedWorkingSlice(const PositionEvent* positionEvent) { DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if ( !workingNode ) return NULL; Image* workingImage = dynamic_cast(workingNode->GetData()); if ( !workingImage ) return NULL; return GetAffectedImageSliceAs2DImage( positionEvent, workingImage ); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const PositionEvent* positionEvent) { DataNode* referenceNode( m_ToolManager->GetReferenceData(0) ); if ( !referenceNode ) return NULL; Image* referenceImage = dynamic_cast(referenceNode->GetData()); if ( !referenceImage ) return NULL; return GetAffectedImageSliceAs2DImage( positionEvent, referenceImage ); } void mitk::SegTool2D::WriteBackSegmentationResult (const PositionEvent* positionEvent, Image* slice) { const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); Image* image = dynamic_cast(workingNode->GetData()); unsigned int timeStep = positionEvent->GetSender()->GetTimeStep( image ); //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); //Set the slice as 'input' reslice->SetInputSlice(slice->GetVtkImageData()); //set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput( image ); extractor->SetTimeStep( timeStep ); extractor->SetWorldGeometry( planeGeometry ); extractor->SetVtkOutputRequest(true); extractor->SetResliceTransformByGeometry( image->GetTimeSlicedGeometry()->GetGeometry3D( timeStep ) ); extractor->Modified(); extractor->Update(); //the image was modified within the pipeline, but not marked so image->Modified(); /*============= BEGIN undo feature block ========================*/ //specify the undo operation with the edited slice m_doOperation = new DiffSliceOperation(image, extractor->GetVtkOutput(),slice->GetGeometry(), timeStep, const_cast(planeGeometry)); //create an operation event for the undo stack OperationEvent* undoStackItem = new OperationEvent( DiffSliceOperationApplier::GetInstance(), m_doOperation, m_undoOperation, "Segmentation" ); //add it to the undo controller UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); //clear the pointers as the operation are stored in the undocontroller and also deleted from there m_undoOperation = NULL; m_doOperation = NULL; /*============= END undo feature block ========================*/ slice->DisconnectPipeline(); ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); contourExtractor->SetInput(slice); contourExtractor->Update(); mitk::Surface::Pointer contour = contourExtractor->GetOutput(); if (contour->GetVtkPolyData()->GetNumberOfPoints() > 0 ) { unsigned int pos = this->AddContourmarker(positionEvent); mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->GetService(serviceRef)); mitk::SurfaceInterpolationController::GetInstance()->AddNewContour( contour, service->GetPlanePosition(pos)); contour->DisconnectPipeline(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::SegTool2D::SetShowMarkerNodes(bool status) { m_ShowMarkerNodes = status; } unsigned int mitk::SegTool2D::AddContourmarker ( const PositionEvent* positionEvent ) { const mitk::Geometry2D* plane = dynamic_cast (dynamic_cast< const mitk::SlicedGeometry3D*>( positionEvent->GetSender()->GetSliceNavigationController()->GetCurrentGeometry3D())->GetGeometry2D(0)); mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->GetService(serviceRef)); unsigned int size = service->GetNumberOfPlanePositions(); unsigned int id = service->AddNewPlanePosition(plane, positionEvent->GetSender()->GetSliceNavigationController()->GetSlice()->GetPos()); mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New(); contourMarker->SetGeometry2D( const_cast(plane)); std::stringstream markerStream; mitk::DataNode* workingNode (m_ToolManager->GetWorkingData(0)); markerStream << m_Contourmarkername ; markerStream << " "; markerStream << id+1; DataNode::Pointer rotatedContourNode = DataNode::New(); rotatedContourNode->SetData(contourMarker); rotatedContourNode->SetProperty( "name", StringProperty::New(markerStream.str()) ); rotatedContourNode->SetProperty( "isContourMarker", BoolProperty::New(true)); rotatedContourNode->SetBoolProperty( "PlanarFigureInitializedWindow", true, positionEvent->GetSender() ); rotatedContourNode->SetProperty( "includeInBoundingBox", BoolProperty::New(false)); rotatedContourNode->SetProperty( "helper object", mitk::BoolProperty::New(!m_ShowMarkerNodes)); if (plane) { if ( id == size ) { m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } else { mitk::NodePredicateProperty::Pointer isMarker = mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer markers = m_ToolManager->GetDataStorage()->GetDerivations(workingNode,isMarker); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = markers->begin(); iter != markers->end(); ++iter) { std::string nodeName = (*iter)->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int markerId = atof(nodeName.substr(t+1).c_str())-1; if(id == markerId) { return id; } } m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } } return id; } void mitk::SegTool2D::InteractiveSegmentationBugMessage( const std::string& message ) { MITK_ERROR << "********************************************************************************" << std::endl << " " << message << std::endl << "********************************************************************************" << std::endl << " " << std::endl << " If your image is rotated or the 2D views don't really contain the patient image, try to press the button next to the image selection. " << std::endl << " " << std::endl << " Please file a BUG REPORT: " << std::endl << " http://bugs.mitk.org" << std::endl << " Contain the following information:" << std::endl << " - What image were you working on?" << std::endl << " - Which region of the image?" << std::endl << " - Which tool did you use?" << std::endl << " - What did you do?" << std::endl << " - What happened (not)? What did you expect?" << std::endl; } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp index ab2ecebbff..c4142a51a5 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp @@ -1,1228 +1,1211 @@ /*=================================================================== 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 "mitkDataNodeObject.h" #include "mitkProperties.h" #include "mitkSegTool2D.h" #include "mitkGlobalInteraction.h" #include "QmitkStdMultiWidget.h" #include "QmitkNewSegmentationDialog.h" #include #include #include "QmitkSegmentationView.h" #include "QmitkSegmentationPostProcessing.h" #include "QmitkSegmentationOrganNamesHandling.cpp" #include #include //For Segmentation in rotated slices //TODO clean up includes #include "mitkVtkResliceInterpolationProperty.h" #include "mitkPlanarCircle.h" #include "mitkGetModuleContext.h" #include "mitkModule.h" #include "mitkModuleRegistry.h" #include "mitkSegmentationObjectFactory.h" const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation"; // public methods QmitkSegmentationView::QmitkSegmentationView() :m_Parent(NULL) ,m_Controls(NULL) ,m_MultiWidget(NULL) ,m_RenderingManagerObserverTag(0) { RegisterSegmentationObjectFactory(); } QmitkSegmentationView::~QmitkSegmentationView() { // delete m_PostProcessing; delete m_Controls; } void QmitkSegmentationView::NewNodesGenerated() { // ForceDisplayPreferencesUponAllImages(); } void QmitkSegmentationView::NewNodeObjectsGenerated(mitk::ToolManager::DataVectorType* nodes) { if (!nodes) return; mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); if (!toolManager) return; for (mitk::ToolManager::DataVectorType::iterator iter = nodes->begin(); iter != nodes->end(); ++iter) { this->FireNodeSelected( *iter ); // only last iteration meaningful, multiple generated objects are not taken into account here } } void QmitkSegmentationView::Activated() { // should be moved to ::BecomesVisible() or similar if( m_Controls ) { m_Controls->m_ManualToolSelectionBox->setEnabled( true ); m_Controls->m_OrganToolSelectionBox->setEnabled( true ); m_Controls->m_LesionToolSelectionBox->setEnabled( true ); m_Controls->m_SlicesInterpolator->Enable3DInterpolation( m_Controls->widgetStack->currentWidget() == m_Controls->pageManual ); //TODO Remove Observer itk::ReceptorMemberCommand::Pointer command1 = itk::ReceptorMemberCommand::New(); command1->SetCallbackFunction( this, &QmitkSegmentationView::RenderingManagerReinitialized ); m_RenderingManagerObserverTag = mitk::RenderingManager::GetInstance()->AddObserver( mitk::RenderingManagerViewsInitializedEvent(), command1 ); //Adding observers for node visibility to existing segmentations mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isSegmentation = mitk::NodePredicateAnd::New( isImage, isBinary ); mitk::DataStorage::SetOfObjects::ConstPointer segmentations = this->GetDefaultDataStorage()->GetSubset( isSegmentation ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = segmentations->begin(); iter != segmentations->end(); ++iter) { mitk::DataNode* node = *iter; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnWorkingNodeVisibilityChanged); m_WorkingDataObserverTags.insert( std::pair( node, node->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ) ) ); } if(segmentations->Size() > 0) { FireNodeSelected(segmentations->ElementAt(0)); segmentations->ElementAt(0)->GetProperty("visible")->Modified(); } } } void QmitkSegmentationView::Deactivated() { if( m_Controls ) { mitk::RenderingManager::GetInstance()->RemoveObserver( m_RenderingManagerObserverTag ); m_Controls->m_ManualToolSelectionBox->setEnabled( false ); //deactivate all tools m_Controls->m_ManualToolSelectionBox->GetToolManager()->ActivateTool(-1); m_Controls->m_OrganToolSelectionBox->setEnabled( false ); m_Controls->m_LesionToolSelectionBox->setEnabled( false ); m_Controls->m_SlicesInterpolator->EnableInterpolation( false ); //Removing all observers for ( NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter ) { (*dataIter).first->GetProperty("visible")->RemoveObserver( (*dataIter).second ); } m_WorkingDataObserverTags.clear(); if (m_MultiWidget) { mitk::SlicesCoordinator *coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) coordinator->RemoveObserver(m_SlicesRotationObserverTag1); coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) coordinator->RemoveObserver(m_SlicesRotationObserverTag2); } // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); service->RemoveAllPlanePositions(); } } void QmitkSegmentationView::StdMultiWidgetAvailable( QmitkStdMultiWidget& stdMultiWidget ) { SetMultiWidget(&stdMultiWidget); } void QmitkSegmentationView::StdMultiWidgetNotAvailable() { SetMultiWidget(NULL); } void QmitkSegmentationView::StdMultiWidgetClosed( QmitkStdMultiWidget& /*stdMultiWidget*/ ) { SetMultiWidget(NULL); } void QmitkSegmentationView::SetMultiWidget(QmitkStdMultiWidget* multiWidget) { if (m_MultiWidget) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) { coordinator->RemoveObserver( m_SlicesRotationObserverTag1 ); } coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) { coordinator->RemoveObserver( m_SlicesRotationObserverTag2 ); } } // save the current multiwidget as the working widget m_MultiWidget = multiWidget; //TODO Remove Observers if (m_MultiWidget) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) { itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSegmentationView::SliceRotation ); m_SlicesRotationObserverTag1 = coordinator->AddObserver( mitk::SliceRotationEvent(), command2 ); } coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) { itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSegmentationView::SliceRotation ); m_SlicesRotationObserverTag2 = coordinator->AddObserver( mitk::SliceRotationEvent(), command2 ); } } //TODO End Remove Observers if (m_Parent) { m_Parent->setEnabled(m_MultiWidget); } // tell the interpolation about toolmanager and multiwidget (and data storage) if (m_Controls && m_MultiWidget) { mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); m_Controls->m_SlicesInterpolator->SetDataStorage( *(this->GetDefaultDataStorage())); m_Controls->m_SlicesInterpolator->Initialize( toolManager, m_MultiWidget ); } } void QmitkSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences*) { ForceDisplayPreferencesUponAllImages(); } //TODO remove function void QmitkSegmentationView::RenderingManagerReinitialized(const itk::EventObject&) { CheckImageAlignment(); } //TODO remove function void QmitkSegmentationView::SliceRotation(const itk::EventObject&) { CheckImageAlignment(); } // protected slots void QmitkSegmentationView::CreateNewSegmentation() { mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull()) { if (image->GetDimension()>1) { // ask about the name and organ type of the new segmentation QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog( m_Parent ); // needs a QWidget as parent, "this" is not QWidget QString storedList = QString::fromStdString( this->GetPreferences()->GetByteArray("Organ-Color-List","") ); QStringList organColors; if (storedList.isEmpty()) { organColors = GetDefaultOrganColorString(); } else { /* a couple of examples of how organ names are stored: a simple item is built up like 'name#AABBCC' where #AABBCC is the hexadecimal notation of a color as known from HTML items are stored separated by ';' this makes it necessary to escape occurrences of ';' in name. otherwise the string "hugo;ypsilon#AABBCC;eugen#AABBCC" could not be parsed as two organs but we would get "hugo" and "ypsilon#AABBCC" and "eugen#AABBCC" so the organ name "hugo;ypsilon" is stored as "hugo\;ypsilon" and must be unescaped after loading the following lines could be one split with Perl's negative lookbehind */ // recover string list from BlueBerry view's preferences QString storedString = QString::fromStdString( this->GetPreferences()->GetByteArray("Organ-Color-List","") ); MITK_DEBUG << "storedString: " << storedString.toStdString(); // match a string consisting of any number of repetitions of either "anything but ;" or "\;". This matches everything until the next unescaped ';' QRegExp onePart("(?:[^;]|\\\\;)*"); MITK_DEBUG << "matching " << onePart.pattern().toStdString(); int count = 0; int pos = 0; while( (pos = onePart.indexIn( storedString, pos )) != -1 ) { ++count; int length = onePart.matchedLength(); if (length == 0) break; QString matchedString = storedString.mid(pos, length); MITK_DEBUG << " Captured length " << length << ": " << matchedString.toStdString(); pos += length + 1; // skip separating ';' // unescape possible occurrences of '\;' in the string matchedString.replace("\\;", ";"); // add matched string part to output list organColors << matchedString; } MITK_DEBUG << "Captured " << count << " organ name/colors"; } dialog->SetSuggestionList( organColors ); int dialogReturnValue = dialog->exec(); if ( dialogReturnValue == QDialog::Rejected ) return; // user clicked cancel or pressed Esc or something similar // ask the user about an organ type and name, add this information to the image's (!) propertylist // create a new image of the same dimensions and smallest possible pixel type mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); mitk::Tool* firstTool = toolManager->GetToolById(0); if (firstTool) { try { mitk::DataNode::Pointer emptySegmentation = firstTool->CreateEmptySegmentationNode( image, dialog->GetSegmentationName().toStdString(), dialog->GetColor() ); //Here we change the reslice interpolation mode for a segmentation, so that contours in rotated slice can be shown correctly emptySegmentation->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_NEAREST) ); // initialize showVolume to false to prevent recalculating the volume while working on the segmentation emptySegmentation->SetProperty( "showVolume", mitk::BoolProperty::New( false ) ); if (!emptySegmentation) return; // could be aborted by user UpdateOrganList( organColors, dialog->GetSegmentationName(), dialog->GetColor() ); /* escape ';' here (replace by '\;'), see longer comment above */ std::string stringForStorage = organColors.replaceInStrings(";","\\;").join(";").toStdString(); MITK_DEBUG << "Will store: " << stringForStorage; this->GetPreferences()->PutByteArray("Organ-Color-List", stringForStorage ); this->GetPreferences()->Flush(); if(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)) { m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)->SetSelected(false); } emptySegmentation->SetSelected(true); this->GetDefaultDataStorage()->Add( emptySegmentation, node ); // add as a child, because the segmentation "derives" from the original this->FireNodeSelected( emptySegmentation ); this->OnSelectionChanged( emptySegmentation ); this->SetToolManagerSelection(node, emptySegmentation); } catch (std::bad_alloc) { QMessageBox::warning(NULL,"Create new segmentation","Could not allocate memory for new segmentation"); } } } else { QMessageBox::information(NULL,"Segmentation","Segmentation is currently not supported for 2D images"); } } } else { MITK_ERROR << "'Create new segmentation' button should never be clickable unless a patient image is selected..."; } } void QmitkSegmentationView::OnWorkingNodeVisibilityChanged(/*const itk::Object* caller, const itk::EventObject& e*/) { if (!m_Parent || !m_Parent->isVisible()) return; // The new selection behaviour is: // // When clicking on the checkbox of a segmentation the node will e selected and its reference node either // The previous selected segmentation (if there is one) will be deselected. Additionally a reinit on the // selected segmenation will be performed. // If more than one segmentation is selected the tools will be disabled. if (!m_Controls) return; // might happen on initialization (preferences loaded) mitk::DataNode::Pointer referenceDataNew = mitk::DataNode::New(); mitk::DataNode::Pointer workingData; bool workingNodeIsVisible (true); unsigned int numberOfSelectedSegmentations (0); // iterate all images mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDefaultDataStorage()->GetSubset( isImage ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); bool isSegmentation(false); node->GetBoolProperty("binary", isSegmentation); if (node->IsSelected() && isSegmentation) { workingNodeIsVisible = node->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); if (!workingNodeIsVisible) return; numberOfSelectedSegmentations++; workingData = node; if (this->GetDefaultDataStorage()->GetSources(node)->Size() != 0) { referenceDataNew = this->GetDefaultDataStorage()->GetSources(node)->ElementAt(0); } bool isBinary(false); //Find topmost source or first source which is no binary image while (referenceDataNew && this->GetDefaultDataStorage()->GetSources(referenceDataNew)->Size() != 0) { referenceDataNew = this->GetDefaultDataStorage()->GetSources(referenceDataNew)->ElementAt(0); referenceDataNew->GetBoolProperty("binary",isBinary); if (!isBinary) break; } if (workingNodeIsVisible && referenceDataNew) { //Since the binary property of a segmentation can be set to false and afterwards you can create a new segmentation out of it //->could lead to a deadloop NodeTagMapType::iterator searchIter = m_WorkingDataObserverTags.find( referenceDataNew ); if ( searchIter != m_WorkingDataObserverTags.end()) { referenceDataNew->GetProperty("visible")->RemoveObserver( (*searchIter).second ); } referenceDataNew->SetVisibility(true); } //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(referenceDataNew) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); continue; } if (workingData.IsNull() || (workingNodeIsVisible && node != referenceDataNew)) { node->SetVisibility((false)); } } if(numberOfSelectedSegmentations == 1) SetToolManagerSelection(referenceDataNew, workingData); mitk::DataStorage::SetOfObjects::Pointer temp = mitk::DataStorage::SetOfObjects::New(); temp->InsertElement(0,workingData); mitk::TimeSlicedGeometry::Pointer bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(temp); // initialize the views to the bounding geometry /*mitk::RenderingManager::GetInstance()->InitializeViews(bounds); mitk::RenderingManager::GetInstance()->RequestUpdateAll();*/ } void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node) { bool isSeg(false); bool isHelperObject(false); node->GetBoolProperty("helper object", isHelperObject); node->GetBoolProperty("binary", isSeg); if(isSeg && !isHelperObject) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(node, mitk::NodePredicateProperty::New("isContourMarker" , mitk::BoolProperty::New(true))); // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; service->RemovePlanePosition(id); this->GetDataStorage()->Remove(it->Value()); } mitk::DataNode* tempNode = const_cast(node); node->GetProperty("visible")->RemoveObserver( m_WorkingDataObserverTags[tempNode] ); m_WorkingDataObserverTags.erase(tempNode); } if((m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0) == node)|| (m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0) == node)) { //as we don't know which node was actually remove e.g. our reference node, disable 'New Segmentation' button. //consider the case that there is no more image in the datastorage this->SetToolManagerSelection(NULL, NULL); } } void QmitkSegmentationView::CreateSegmentationFromSurface() { mitk::DataNode::Pointer surfaceNode = m_Controls->MaskSurfaces->GetSelectedNode(); mitk::Surface::Pointer surface(0); if(surfaceNode.IsNotNull()) surface = dynamic_cast ( surfaceNode->GetData() ); if(surface.IsNull()) { this->HandleException( "No surface selected.", m_Parent, true); return; } mitk::DataNode::Pointer imageNode = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); mitk::Image::Pointer image(0); if (imageNode.IsNotNull()) image = dynamic_cast( imageNode->GetData() ); if(image.IsNull()) { this->HandleException( "No image selected.", m_Parent, true); return; } mitk::SurfaceToImageFilter::Pointer s2iFilter = mitk::SurfaceToImageFilter::New(); s2iFilter->MakeOutputBinaryOn(); s2iFilter->SetInput(surface); s2iFilter->SetImage(image); s2iFilter->Update(); mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); std::string nameOfResultImage = imageNode->GetName(); nameOfResultImage.append(surfaceNode->GetName()); resultNode->SetProperty("name", mitk::StringProperty::New(nameOfResultImage) ); resultNode->SetProperty("binary", mitk::BoolProperty::New(true) ); resultNode->SetData( s2iFilter->GetOutput() ); this->GetDataStorage()->Add(resultNode, imageNode); } -void QmitkSegmentationView::ManualToolSelected(int id) -{ - // disable crosshair movement when a manual drawing tool is active (otherwise too much visual noise) - if (m_MultiWidget) - { - if (id >= 0) - { - m_MultiWidget->DisableNavigationControllerEventListening(); - } - else - { - m_MultiWidget->EnableNavigationControllerEventListening(); - } - } -} - void QmitkSegmentationView::ToolboxStackPageChanged(int id) { // interpolation only with manual tools visible m_Controls->m_SlicesInterpolator->EnableInterpolation( id == 0 ); if( id == 0 ) { mitk::DataNode::Pointer workingData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0); if( workingData.IsNotNull() ) { m_Controls->lblSegmentation->setText( workingData->GetName().c_str() ); m_Controls->lblSegImage->show(); m_Controls->lblSegmentation->show(); } } else { m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } // this is just a workaround, should be removed when all tools support 3D+t if (id==2) // lesions { mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull()) { if (image->GetDimension()>3) { m_Controls->widgetStack->setCurrentIndex(0); QMessageBox::information(NULL,"Segmentation","Lesion segmentation is currently not supported for 4D images"); } } } } } // protected void QmitkSegmentationView::OnComboBoxSelectionChanged( const mitk::DataNode* node ) { mitk::DataNode* selectedNode = const_cast(node); if( selectedNode != NULL ) { m_Controls->refImageSelector->show(); m_Controls->lblReferenceImageSelectionWarning->hide(); bool isBinary(false); selectedNode->GetBoolProperty("binary", isBinary); if ( isBinary ) { FireNodeSelected(selectedNode); selectedNode->SetVisibility(true); } else if (node != m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)) { if (m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)) m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)->SetVisibility(false); if (m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)) { m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)->SetVisibility(false); } FireNodeSelected(selectedNode); selectedNode->SetVisibility(true); SetToolManagerSelection(selectedNode, NULL); } } else { m_Controls->refImageSelector->hide(); m_Controls->lblReferenceImageSelectionWarning->show(); } } void QmitkSegmentationView::OnShowMarkerNodes (bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetTools().size(); for(unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetToolById(i)); if (manualSegmentationTool) { if(state == true) { manualSegmentationTool->SetShowMarkerNodes( true ); } else { manualSegmentationTool->SetShowMarkerNodes( false ); } } } } void QmitkSegmentationView::OnSelectionChanged(mitk::DataNode* node) { std::vector nodes; nodes.push_back( node ); this->OnSelectionChanged( nodes ); } void QmitkSegmentationView::OnSurfaceSelectionChanged() { // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull())) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); } void QmitkSegmentationView::OnSelectionChanged(std::vector nodes) { // if the selected node is a contourmarker if ( !nodes.empty() ) { std::string markerName = "Position"; unsigned int numberOfNodes = nodes.size(); std::string nodeName = nodes.at( 0 )->GetName(); if ( ( numberOfNodes == 1 ) && ( nodeName.find( markerName ) == 0) ) { this->OnContourMarkerSelected( nodes.at( 0 ) ); } } // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull())) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); if (!m_Parent || !m_Parent->isVisible()) return; // reaction to BlueBerry selection events // this method will try to figure out if a relevant segmentation and its corresponding original image were selected // a warning is issued if the selection is invalid // appropriate reactions are triggered otherwise mitk::DataNode::Pointer referenceData = FindFirstRegularImage( nodes ); //m_Controls->refImageSelector->GetSelectedNode(); //FindFirstRegularImage( nodes ); mitk::DataNode::Pointer workingData = FindFirstSegmentation( nodes ); if(referenceData.IsNull() && workingData.IsNull()) return; bool invalidSelection( !nodes.empty() && ( nodes.size() > 2 || // maximum 2 selected nodes (nodes.size() == 2 && (workingData.IsNull() || referenceData.IsNull()) ) || // with two nodes, one must be the original image, one the segmentation ( workingData.GetPointer() == referenceData.GetPointer() ) //one node is selected as reference and working image // one item is always ok (might be working or reference or nothing ) ); if (invalidSelection) { // TODO visible warning when two images are selected MITK_ERROR << "WARNING: No image, too many (>2) or two equal images were selected."; workingData = NULL; if( m_Controls->refImageSelector->GetSelectedNode().IsNull() ) referenceData = NULL; } if ( workingData.IsNotNull() && referenceData.IsNull() ) { // find the DataStorage parent of workingData // try to find a "normal image" parent, select this as reference image mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer isNotBinary = mitk::NodePredicateNot::New( isBinary ); mitk::NodePredicateAnd::Pointer isNormalImage = mitk::NodePredicateAnd::New( isImage, isNotBinary ); mitk::DataStorage::SetOfObjects::ConstPointer possibleParents = this->GetDefaultDataStorage()->GetSources( workingData, isNormalImage ); if (possibleParents->size() > 0) { if (possibleParents->size() > 1) { // TODO visible warning for this rare case MITK_ERROR << "Selected binary image has multiple parents. Using arbitrary first one for segmentation."; } referenceData = (*possibleParents)[0]; } NodeTagMapType::iterator searchIter = m_WorkingDataObserverTags.find( workingData ); if ( searchIter == m_WorkingDataObserverTags.end() ) { //MITK_INFO<<"Creating new observer"; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnWorkingNodeVisibilityChanged); m_WorkingDataObserverTags.insert( std::pair( workingData, workingData->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ) ) ); workingData->GetProperty("visible")->Modified(); return; } if(workingData->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1")))) { //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(workingData) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull()) || (!referenceData)) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); SetToolManagerSelection(referenceData, workingData); FireNodeSelected(workingData); } else { SetToolManagerSelection(NULL, NULL); FireNodeSelected(workingData); } } else { //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(referenceData) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull()) || (!referenceData)) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); SetToolManagerSelection(referenceData, workingData); FireNodeSelected(referenceData); } } void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode *node) { //TODO renderWindow anders bestimmen, siehe CheckAlignment QmitkRenderWindow* selectedRenderWindow = 0; QmitkRenderWindow* RenderWindow1 = this->GetActiveStdMultiWidget()->GetRenderWindow1(); QmitkRenderWindow* RenderWindow2 = this->GetActiveStdMultiWidget()->GetRenderWindow2(); QmitkRenderWindow* RenderWindow3 = this->GetActiveStdMultiWidget()->GetRenderWindow3(); QmitkRenderWindow* RenderWindow4 = this->GetActiveStdMultiWidget()->GetRenderWindow4(); bool PlanarFigureInitializedWindow = false; // find initialized renderwindow if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow1->GetRenderer())) { selectedRenderWindow = RenderWindow1; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow2->GetRenderer())) { selectedRenderWindow = RenderWindow2; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow3->GetRenderer())) { selectedRenderWindow = RenderWindow3; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow4->GetRenderer())) { selectedRenderWindow = RenderWindow4; } // make node visible if (selectedRenderWindow) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id)); selectedRenderWindow->GetRenderer()->GetDisplayGeometry()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } mitk::DataNode::Pointer QmitkSegmentationView::FindFirstRegularImage( std::vector nodes ) { if (nodes.empty()) return NULL; for(unsigned int i = 0; i < nodes.size(); ++i) { //mitk::DataNode::Pointer node = i.value() bool isImage(false); if (nodes.at(i)->GetData()) { isImage = dynamic_cast(nodes.at(i)->GetData()) != NULL; } // make sure this is not a binary image bool isSegmentation(false); nodes.at(i)->GetBoolProperty("binary", isSegmentation); // return first proper mitk::Image if (isImage && !isSegmentation) return nodes.at(i); } return NULL; } mitk::DataNode::Pointer QmitkSegmentationView::FindFirstSegmentation( std::vector nodes ) { if (nodes.empty()) return NULL; for(unsigned int i = 0; i < nodes.size(); ++i) { bool isImage(false); if (nodes.at(i)->GetData()) { isImage = dynamic_cast(nodes.at(i)->GetData()) != NULL; } bool isSegmentation(false); nodes.at(i)->GetBoolProperty("binary", isSegmentation); // return first proper binary mitk::Image if (isImage && isSegmentation) { return nodes.at(i); } } return NULL; } void QmitkSegmentationView::SetToolManagerSelection(const mitk::DataNode* referenceData, const mitk::DataNode* workingData) { // called as a result of new BlueBerry selections // tells the ToolManager for manual segmentation about new selections // updates GUI information about what the user should select mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); toolManager->SetReferenceData(const_cast(referenceData)); toolManager->SetWorkingData( const_cast(workingData)); // check original image m_Controls->btnNewSegmentation->setEnabled(referenceData != NULL); if (referenceData) { m_Controls->lblReferenceImageSelectionWarning->hide(); } else { m_Controls->lblReferenceImageSelectionWarning->show(); m_Controls->lblWorkingImageSelectionWarning->hide(); m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } //TODO remove statement // check, wheter reference image is aligned like render windows. Otherwise display a visible warning (because 2D tools will probably not work) CheckImageAlignment(); // check segmentation if (referenceData) { if (!workingData) { m_Controls->lblWorkingImageSelectionWarning->show(); if( m_Controls->widgetStack->currentIndex() == 0 ) { m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } } else { m_Controls->lblWorkingImageSelectionWarning->hide(); this->FireNodeSelected(const_cast(workingData)); if( m_Controls->widgetStack->currentIndex() == 0 ) { m_Controls->lblSegmentation->setText( workingData->GetName().c_str() ); m_Controls->lblSegmentation->show(); m_Controls->lblSegImage->show(); } } } } //TODO remove function void QmitkSegmentationView::CheckImageAlignment() { bool wrongAlignment(true); mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull() && m_MultiWidget) { wrongAlignment = !( IsRenderWindowAligned(m_MultiWidget->GetRenderWindow1(), image ) && IsRenderWindowAligned(m_MultiWidget->GetRenderWindow2(), image ) && IsRenderWindowAligned(m_MultiWidget->GetRenderWindow3(), image ) ); } } m_Controls->lblAlignmentWarning->setVisible(wrongAlignment); } //TODO remove function bool QmitkSegmentationView::IsRenderWindowAligned(QmitkRenderWindow* renderWindow, mitk::Image* image) { if (!renderWindow) return false; // for all 2D renderwindows of m_MultiWidget check alignment mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast( renderWindow->GetRenderer()->GetCurrentWorldGeometry2D() ); if (displayPlane.IsNull()) return false; int affectedDimension(-1); int affectedSlice(-1); return mitk::SegTool2D::DetermineAffectedImageSlice( image, displayPlane, affectedDimension, affectedSlice ); } //TODO remove function void QmitkSegmentationView::ForceDisplayPreferencesUponAllImages() { if (!m_Parent || !m_Parent->isVisible()) return; // check all images and segmentations in DataStorage: // (items in brackets are implicitly done by previous steps) // 1. // if a reference image is selected, // show the reference image // and hide all other images (orignal and segmentation), // (and hide all segmentations of the other original images) // and show all the reference's segmentations // if no reference image is selected, do do nothing // // 2. // if a segmentation is selected, // show it // (and hide all all its siblings (childs of the same parent, incl, NULL parent)) // if no segmentation is selected, do nothing if (!m_Controls) return; // might happen on initialization (preferences loaded) mitk::DataNode::Pointer referenceData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); mitk::DataNode::Pointer workingData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0); // 1. if (referenceData.IsNotNull()) { // iterate all images mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDefaultDataStorage()->GetSubset( isImage ); //mitk::DataStorage::SetOfObjects::ConstPointer allSegmentationChilds = this->GetDefaultDataStorage()->GetDerivations(referenceData, isImage ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); // set visibility if(!node->IsSelected() || (node->IsSelected() && !node->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))))) node->SetVisibility((node == referenceData) || node->IsSelected() ); } } // 2. //if (workingData.IsNotNull() && !workingData->IsSelected()) //{ // workingData->SetVisibility(true); //} mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node) { if (!node) return; bool isBinary(false); node->GetPropertyValue("binary", isBinary); if (isBinary) { node->SetProperty( "outline binary", mitk::BoolProperty::New( this->GetPreferences()->GetBool("draw outline", true)) ); node->SetProperty( "outline width", mitk::FloatProperty::New( 2.0 ) ); node->SetProperty( "opacity", mitk::FloatProperty::New( this->GetPreferences()->GetBool("draw outline", true) ? 1.0 : 0.3 ) ); node->SetProperty( "volumerendering", mitk::BoolProperty::New( this->GetPreferences()->GetBool("volume rendering", false) ) ); } } void QmitkSegmentationView::CreateQtPartControl(QWidget* parent) { // setup the basic GUI of this view m_Parent = parent; m_Controls = new Ui::QmitkSegmentationControls; m_Controls->setupUi(parent); m_Controls->lblWorkingImageSelectionWarning->hide(); m_Controls->lblAlignmentWarning->hide(); m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); m_Controls->refImageSelector->SetDataStorage(this->GetDefaultDataStorage()); m_Controls->refImageSelector->SetPredicate(mitk::NodePredicateDataType::New("Image")); if( m_Controls->refImageSelector->GetSelectedNode().IsNotNull() ) m_Controls->lblReferenceImageSelectionWarning->hide(); else m_Controls->refImageSelector->hide(); mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); toolManager->SetDataStorage( *(this->GetDefaultDataStorage()) ); assert ( toolManager ); // all part of open source MITK m_Controls->m_ManualToolSelectionBox->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox->SetToolGUIArea( m_Controls->m_ManualToolGUIContainer ); m_Controls->m_ManualToolSelectionBox->SetDisplayedToolGroups("Add Subtract Paint Wipe 'Region Growing' Correction Fill Erase"); m_Controls->m_ManualToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingData ); // available only in the 3M application if ( !m_Controls->m_OrganToolSelectionBox->children().count() ) { m_Controls->widgetStack->setItemEnabled( 1, false ); } m_Controls->m_OrganToolSelectionBox->SetToolManager( *toolManager ); m_Controls->m_OrganToolSelectionBox->SetToolGUIArea( m_Controls->m_OrganToolGUIContainer ); m_Controls->m_OrganToolSelectionBox->SetDisplayedToolGroups("'Hippocampus left' 'Hippocampus right' 'Lung left' 'Lung right' 'Liver' 'Heart LV' 'Endocard LV' 'Epicard LV' 'Prostate'"); m_Controls->m_OrganToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceData ); // available only in the 3M application if ( !m_Controls->m_LesionToolSelectionBox->children().count() ) { m_Controls->widgetStack->setItemEnabled( 2, false ); } m_Controls->m_LesionToolSelectionBox->SetToolManager( *toolManager ); m_Controls->m_LesionToolSelectionBox->SetToolGUIArea( m_Controls->m_LesionToolGUIContainer ); m_Controls->m_LesionToolSelectionBox->SetDisplayedToolGroups("'Lymph Node'"); m_Controls->m_LesionToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceData ); toolManager->NewNodesGenerated += mitk::MessageDelegate( this, &QmitkSegmentationView::NewNodesGenerated ); // update the list of segmentations toolManager->NewNodeObjectsGenerated += mitk::MessageDelegate1( this, &QmitkSegmentationView::NewNodeObjectsGenerated ); // update the list of segmentations // create signal/slot connections connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); connect( m_Controls->btnNewSegmentation, SIGNAL(clicked()), this, SLOT(CreateNewSegmentation()) ); connect( m_Controls->CreateSegmentationFromSurface, SIGNAL(clicked()), this, SLOT(CreateSegmentationFromSurface()) ); - connect( m_Controls->m_ManualToolSelectionBox, SIGNAL(ToolSelected(int)), this, SLOT(ManualToolSelected(int)) ); connect( m_Controls->widgetStack, SIGNAL(currentChanged(int)), this, SLOT(ToolboxStackPageChanged(int)) ); connect(m_Controls->MaskSurfaces, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSurfaceSelectionChanged( ) ) ); connect(m_Controls->MaskSurfaces, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSurfaceSelectionChanged( ) ) ); connect(m_Controls->m_SlicesInterpolator, SIGNAL(SignalShowMarkerNodes(bool)), this, SLOT(OnShowMarkerNodes(bool))); m_Controls->MaskSurfaces->SetDataStorage(this->GetDefaultDataStorage()); m_Controls->MaskSurfaces->SetPredicate(mitk::NodePredicateDataType::New("Surface")); //// create helper class to provide context menus for segmentations in data manager // m_PostProcessing = new QmitkSegmentationPostProcessing(this->GetDefaultDataStorage(), this, m_Parent); } //void QmitkSegmentationView::OnPlaneModeChanged(int i) //{ // //if plane mode changes, disable all tools // if (m_MultiWidget) // { // mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); // // if (toolManager) // { // if (toolManager->GetActiveToolID() >= 0) // { // toolManager->ActivateTool(-1); // } // else // { // m_MultiWidget->EnableNavigationControllerEventListening(); // } // } // } //} // ATTENTION some methods for handling the known list of (organ names, colors) are defined in QmitkSegmentationOrganNamesHandling.cpp diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h index 86802542e1..5d114f3800 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h @@ -1,166 +1,163 @@ /*=================================================================== 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 QmitkSegmentationView_h #define QmitkSegmentationView_h #include "QmitkFunctionality.h" #include #include "ui_QmitkSegmentationControls.h" class QmitkRenderWindow; // class QmitkSegmentationPostProcessing; /** * \ingroup ToolManagerEtAl * \ingroup org_mitk_gui_qt_segmentation_internal * \warning Implementation of this class is split up into two .cpp files to make things more compact. Check both this file and QmitkSegmentationOrganNamesHandling.cpp */ class QmitkSegmentationView : public QmitkFunctionality { Q_OBJECT public: QmitkSegmentationView(); virtual ~QmitkSegmentationView(); typedef std::map NodeTagMapType; /*! \brief Invoked when the DataManager selection changed */ virtual void OnSelectionChanged(mitk::DataNode* node); virtual void OnSelectionChanged(std::vector nodes); // reaction to new segmentations being created by segmentation tools void NewNodesGenerated(); void NewNodeObjectsGenerated(mitk::ToolManager::DataVectorType*); // QmitkFunctionality's activate/deactivate virtual void Activated(); virtual void Deactivated(); // QmitkFunctionality's changes regarding THE QmitkStdMultiWidget virtual void StdMultiWidgetAvailable(QmitkStdMultiWidget& stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); virtual void StdMultiWidgetClosed(QmitkStdMultiWidget& stdMultiWidget); // BlueBerry's notification about preference changes (e.g. from a dialog) virtual void OnPreferencesChanged(const berry::IBerryPreferences*); // observer to mitk::RenderingManager's RenderingManagerViewsInitializedEvent event void RenderingManagerReinitialized(const itk::EventObject&); // observer to mitk::SliceController's SliceRotation event void SliceRotation(const itk::EventObject&); static const std::string VIEW_ID; protected slots: void OnComboBoxSelectionChanged(const mitk::DataNode* node); // reaction to the button "New segmentation" void CreateNewSegmentation(); // reaction to the button "New segmentation" void CreateSegmentationFromSurface(); - // called when a segmentation tool is activated - void ManualToolSelected(int id); - // called when one of "Manual", "Organ", "Lesion" pages of the QToolbox is selected void ToolboxStackPageChanged(int id); void OnSurfaceSelectionChanged(); //called when the checkbox Remember Contour Positions is selected/deselected void OnWorkingNodeVisibilityChanged(); void OnShowMarkerNodes(bool); protected: // a type for handling lists of DataNodes typedef std::vector NodeList; // set available multiwidget void SetMultiWidget(QmitkStdMultiWidget* multiWidget); // actively query the current selection of data manager //void PullCurrentDataManagerSelection(); // reactions to selection events from data manager (and potential other senders) //void BlueBerrySelectionChanged(berry::IWorkbenchPart::Pointer sourcepart, berry::ISelection::ConstPointer selection); mitk::DataNode::Pointer FindFirstRegularImage( std::vector nodes ); mitk::DataNode::Pointer FindFirstSegmentation( std::vector nodes ); // propagate BlueBerry selection to ToolManager for manual segmentation void SetToolManagerSelection(const mitk::DataNode* referenceData, const mitk::DataNode* workingData); // checks if selected reference image is aligned with the slices stack orientation of the StdMultiWidget void CheckImageAlignment(); // checks if given render window aligns with the slices of given image bool IsRenderWindowAligned(QmitkRenderWindow* renderWindow, mitk::Image* image); // make sure all images/segmentations look as selected by the users in this view's preferences void ForceDisplayPreferencesUponAllImages(); // decorates a DataNode according to the user preference settings void ApplyDisplayOptions(mitk::DataNode* node); // GUI setup void CreateQtPartControl(QWidget* parent); // handling of a list of known (organ name, organ color) combination // ATTENTION these methods are defined in QmitkSegmentationOrganNamesHandling.cpp QStringList GetDefaultOrganColorString(); void UpdateOrganList(QStringList& organColors, const QString& organname, mitk::Color colorname); void AppendToOrganList(QStringList& organColors, const QString& organname, int r, int g, int b); // If a contourmarker is selected, the plane in the related widget will be reoriented according to the marker`s geometry void OnContourMarkerSelected (const mitk::DataNode* node); void NodeRemoved(const mitk::DataNode* node); // the Qt parent of our GUI (NOT of this object) QWidget* m_Parent; // our GUI Ui::QmitkSegmentationControls * m_Controls; // THE currently existing QmitkStdMultiWidget QmitkStdMultiWidget * m_MultiWidget; // QmitkSegmentationPostProcessing* m_PostProcessing; unsigned long m_RenderingManagerObserverTag; unsigned long m_SlicesRotationObserverTag1; unsigned long m_SlicesRotationObserverTag2; unsigned long m_VisibilityChangedObserverTag; NodeTagMapType m_WorkingDataObserverTags; }; #endif /*QMITKsegmentationVIEW_H_*/