diff --git a/Core/Code/Interactions/mitkPointSetDataInteractor.cpp b/Core/Code/Interactions/mitkPointSetDataInteractor.cpp index b368af85b1..e2c0b5bb18 100644 --- a/Core/Code/Interactions/mitkPointSetDataInteractor.cpp +++ b/Core/Code/Interactions/mitkPointSetDataInteractor.cpp @@ -1,679 +1,721 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSetDataInteractor.h" #include "mitkMouseMoveEvent.h" #include "mitkOperationEvent.h" #include #include "mitkInteractionConst.h" // TODO: refactor file #include "mitkRenderingManager.h" #include "mitkInternalEvent.h" // #include "mitkDispatcher.h" #include "mitkBaseRenderer.h" #include "mitkUndoController.h" void mitk::PointSetDataInteractor::ConnectActionsAndFunctions() { // Condition which is evaluated before transition is taken // following actions in the statemachine are only executed if it returns TRUE CONNECT_CONDITION("isoverpoint", CheckSelection); CONNECT_FUNCTION("addpoint", AddPoint); CONNECT_FUNCTION("selectpoint", SelectPoint); CONNECT_FUNCTION("unselect", UnSelectPointAtPosition); CONNECT_FUNCTION("unselectAll", UnSelectAll); CONNECT_FUNCTION("initMove", InitMove); CONNECT_FUNCTION("movePoint", MovePoint); CONNECT_FUNCTION("finishMovement", FinishMove); CONNECT_FUNCTION("removePoint", RemovePoint); - CONNECT_FUNCTION("update", UpdatePointSet) } bool mitk::PointSetDataInteractor::AddPoint(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); if (m_MaxNumberOfPoints > 0 && m_PointSet->GetSize(timeStep) >= m_MaxNumberOfPoints) { return false; } // To add a point the minimal information is the position, this method accepts all InteractionsPositionEvents InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { // 1) Undo/Redo Supports Grouping of Operations that are undone as a set, // this statement indicates that a new Operation starts here mitk::OperationEvent::IncCurrObjectEventId(); mitk::Point3D itkPoint = positionEvent->GetPositionInWorld(); this->UnselectAll( timeStep, timeInMs); int lastPosition = 0; mitk::PointSet::PointsIterator it, end; it = m_PointSet->Begin(); end = m_PointSet->End(); while( it != end ) { if (!m_PointSet->IndexExists(lastPosition)) break; ++it; ++lastPosition; } // Insert a Point to the PointSet // 2) Create the Operation inserting the point PointOperation* doOp = new mitk::PointOperation(OpINSERT,timeInMs, itkPoint, lastPosition); // 3) If Undo is enabled, also create the inverse Operation if (m_UndoEnabled) { PointOperation *undoOp = new mitk::PointOperation( OpREMOVE,timeInMs, itkPoint, lastPosition); // 4) Do and Undo Operations are combined in an Operation event which also contains the target of the operations (here m_PointSet) OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Add point"); // 5) Store the Operation in the UndoController m_UndoController->SetOperationEvent(operationEvent); } // 6) Execute the Operation performs the actual insertion of the point into the PointSet m_PointSet->ExecuteOperation(doOp); // 7) If Undo is not enabled the Do-Operation is to be dropped to prevent memory leaks. if ( !m_UndoEnabled ) delete doOp; // Request update only in the display in which interaction happened interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); // Check if points form a closed contour now, if so fire an InternalEvent IsClosedContour(stateMachineAction, interactionEvent); if (m_MaxNumberOfPoints > 0 && m_PointSet->GetSize(timeStep) >= m_MaxNumberOfPoints) { // Signal that DataNode is fully filled this->NotifyResultReady(); // Send internal event that can be used by StateMachines to switch in a different state InternalEvent::Pointer event = InternalEvent::New(NULL, this, "MaximalNumberOfPoints"); positionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); } return true; } else { return false; } } bool mitk::PointSetDataInteractor::SelectPoint(StateMachineAction*, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected int index = GetPointIndexByPosition(point, timeStep); if (index != -1) { //first deselect the other points //undoable deselect of all points in the DataList this->UnselectAll( timeStep, timeInMs); PointOperation* doOp = new mitk::PointOperation(OpSELECTPOINT, point, index); //Undo if (m_UndoEnabled) { PointOperation* undoOp = new mitk::PointOperation(OpDESELECTPOINT,point, index); OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation m_PointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return true; } } return false; } +void mitk::PointSetDataInteractor::DeletePointByIndex(unsigned int index, unsigned int timeStep) +{ + UnselectAll(timeStep,0); + + mitk::Point3D itkPoint; + PointSet::PointType pt = m_PointSet->GetPoint(index, timeStep); + itkPoint[0] = pt[0]; + itkPoint[1] = pt[1]; + itkPoint[2] = pt[2]; + + PointOperation* doOp = new mitk::PointOperation(OpREMOVE,0, itkPoint, index); + if (m_UndoEnabled) //write to UndoMechanism + { + PointOperation* undoOp = new mitk::PointOperation(OpINSERT,0, itkPoint, index); + OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Remove point"); + m_UndoController->SetOperationEvent(operationEvent); + } + //execute the Operation + m_PointSet->ExecuteOperation(doOp); + + if ( !m_UndoEnabled ) + delete doOp; + + /*now select the point "position-1", + and if it is the first in list, + then continue at the last in list*/ + //only then a select of a point is possible! + if (m_PointSet->GetSize( timeStep ) > 0) + { + if (index>0)//not the first in list + { + this->SelectPoint( index-1, timeStep,0); + } + //it was the first point in list, that was removed, so select + //the last in list + else + { + index = m_PointSet->GetSize( timeStep ) - 1; //last in list + this->SelectPoint( index, timeStep, 0 ); + } + } + +} + mitk::PointSetDataInteractor::PointSetDataInteractor() : m_MaxNumberOfPoints(0),m_SelectionAccuracy(3.5) { } mitk::PointSetDataInteractor::~PointSetDataInteractor() { } bool mitk::PointSetDataInteractor::RemovePoint(StateMachineAction*, InteractionEvent* interactionEvent) { - unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { mitk::OperationEvent::IncCurrObjectEventId(); mitk::Point3D itkPoint = positionEvent->GetPositionInWorld(); //search the point in the list int position = m_PointSet->SearchPoint( itkPoint , m_SelectionAccuracy, timeStep); if (position>=0)//found a point { PointSet::PointType pt = m_PointSet->GetPoint(position, timeStep); itkPoint[0] = pt[0]; itkPoint[1] = pt[1]; itkPoint[2] = pt[2]; PointOperation* doOp = new mitk::PointOperation(OpREMOVE,timeInMs, itkPoint, position); if (m_UndoEnabled) //write to UndoMechanism { PointOperation* undoOp = new mitk::PointOperation(OpINSERT,timeInMs, itkPoint, position); OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Remove point"); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation m_PointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; /*now select the point "position-1", and if it is the first in list, then continue at the last in list*/ //only then a select of a point is possible! if (m_PointSet->GetSize( timeStep ) > 0) { if (position>0)//not the first in list { this->SelectPoint( position-1, timeStep,timeInMs); } //it was the first point in list, that was removed, so select //the last in list else { position = m_PointSet->GetSize( timeStep ) - 1; //last in list this->SelectPoint( position, timeStep, timeInMs ); } } } interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } else { return false; } return true; } bool mitk::PointSetDataInteractor::IsClosedContour(StateMachineAction*, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected if (GetPointIndexByPosition(point, timeStep) != -1 && m_PointSet->GetSize(timeStep) >= 3) { InternalEvent::Pointer event = InternalEvent::New(NULL, this, "ClosedContour"); positionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); return true; } } return false; } bool mitk::PointSetDataInteractor::MovePoint(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { IsClosedContour(stateMachineAction, interactionEvent); mitk::Point3D newPoint, resultPoint; newPoint = positionEvent->GetPositionInWorld(); // search the elements in the list that are selected then calculate the // vector, because only with the vector we can move several elements in // the same direction // newPoint - lastPoint = vector // then move all selected and set the lastPoint = newPoint. // then add all vectors to a summeryVector (to be able to calculate the // startpoint for undoOperation) mitk::Vector3D dirVector = newPoint - m_LastPoint; //sum up all Movement for Undo in FinishMovement m_SumVec = m_SumVec + dirVector; mitk::PointSet::PointsIterator it, end; it = m_PointSet->Begin(); end = m_PointSet->End(); while( it != end ) { int position = it->Index(); if ( m_PointSet->GetSelectInfo(position, timeStep) )//if selected { PointSet::PointType pt = m_PointSet->GetPoint(position, timeStep); mitk::Point3D sumVec; sumVec[0] = pt[0]; sumVec[1] = pt[1]; sumVec[2] = pt[2]; resultPoint = sumVec + dirVector; PointOperation doOp(OpMOVE, resultPoint, position); //execute the Operation //here no undo is stored, because the movement-steps aren't interesting. // only the start and the end is interisting to store for undo. m_PointSet->ExecuteOperation(&doOp); } ++it; } m_LastPoint = newPoint;//for calculation of the direction vector // Update the display interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); IsClosedContour(stateMachineAction,interactionEvent); return true; } else { return false; } } bool mitk::PointSetDataInteractor::UnSelectPointAtPosition(StateMachineAction*, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected int index = GetPointIndexByPosition(point, timeStep); // here it is ensured that we don't switch from one point being selected to another one being selected, // without accepting the unselect of the current point if (index != -1) { PointOperation* doOp = new mitk::PointOperation(OpDESELECTPOINT,timeInMs, point, index); if (m_UndoEnabled) //write to UndoMechanism { PointOperation* undoOp = new mitk::PointOperation(OpSELECTPOINT,timeInMs, point, index); OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation m_PointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; return true; } } return false; } bool mitk::PointSetDataInteractor::UnSelectAll(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { Point3D positioninWorld = positionEvent->GetPositionInWorld(); PointSet::PointsContainer::Iterator it, end; PointSet::DataType *itkPointSet = m_PointSet->GetPointSet( timeStep ); end = itkPointSet->GetPoints()->End(); for (it = itkPointSet->GetPoints()->Begin(); it != end; it++) { int position = it->Index(); //then declare an operation which unselects this point; //UndoOperation as well! if ( m_PointSet->GetSelectInfo(position,timeStep) ) { float distance = sqrt(positioninWorld.SquaredEuclideanDistanceTo(m_PointSet->GetPoint(position, timeStep))); if (distance > m_SelectionAccuracy) { mitk::Point3D noPoint; noPoint.Fill( 0 ); mitk::PointOperation* doOp = new mitk::PointOperation(OpDESELECTPOINT,timeInMs, noPoint, position); if ( m_UndoEnabled ) { mitk::PointOperation* undoOp = new mitk::PointOperation(OpSELECTPOINT, timeInMs, noPoint, position); OperationEvent *operationEvent = new OperationEvent( m_PointSet, doOp, undoOp ); m_UndoController->SetOperationEvent( operationEvent ); } m_PointSet->ExecuteOperation( doOp ); if ( !m_UndoEnabled ) delete doOp; } } } } else { this->UnselectAll(timeStep,timeInMs); } return true; } bool mitk::PointSetDataInteractor::UpdatePointSet(mitk::StateMachineAction*, mitk::InteractionEvent*) { mitk::PointSet* pointSet = dynamic_cast(this->GetDataNode()->GetData()); if ( pointSet == NULL ) { return false; MITK_ERROR << "PointSetDataInteractor:: No valid point set ."; } m_PointSet = pointSet; return true; } bool mitk::PointSetDataInteractor::Abort(StateMachineAction*, InteractionEvent* interactionEvent) { InternalEvent::Pointer event = InternalEvent::New(NULL, this, IntDeactivateMe); interactionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); return true; } /* * Check whether the DataNode contains a pointset, if not create one and add it. */ void mitk::PointSetDataInteractor::DataNodeChanged() { if (GetDataNode().IsNotNull()) { PointSet* points = dynamic_cast(GetDataNode()->GetData()); if (points == NULL) { m_PointSet = PointSet::New(); GetDataNode()->SetData(m_PointSet); } else { m_PointSet = points; } // load config file parameter: maximal number of points mitk::PropertyList::Pointer properties = GetAttributes(); std::string strNumber; if (properties->GetStringProperty("MaxPoints", strNumber)) { m_MaxNumberOfPoints = atoi(strNumber.c_str()); } } } bool mitk::PointSetDataInteractor::InitMove(StateMachineAction*, InteractionEvent* interactionEvent) { InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent == NULL) return false; mitk::OperationEvent::IncCurrObjectEventId(); // start of the Movement is stored to calculate the undoKoordinate // in FinishMovement m_LastPoint = positionEvent->GetPositionInWorld(); // initialize a value to calculate the movement through all // MouseMoveEvents from MouseClick to MouseRelease m_SumVec.Fill(0); GetDataNode()->SetProperty("contourcolor", ColorProperty::New(1.0, 1.0, 1.0)); return true; } bool mitk::PointSetDataInteractor::FinishMove(StateMachineAction*, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { //finish the movement: //the final point is m_LastPoint //m_SumVec stores the movement in a vector //the operation would not be necessary, but we need it for the undo Operation. //m_LastPoint is for the Operation //the point for undoOperation calculates from all selected //elements (point) - m_SumVec //search all selected elements and move them with undo-functionality. mitk::PointSet::PointsIterator it, end; it = m_PointSet->Begin(); end = m_PointSet->End(); while( it != end ) { int position = it->Index(); if ( m_PointSet->GetSelectInfo(position, timeStep) )//if selected { PointSet::PointType pt = m_PointSet->GetPoint(position, timeStep); Point3D itkPoint; itkPoint[0] = pt[0]; itkPoint[1] = pt[1]; itkPoint[2] = pt[2]; PointOperation* doOp = new mitk::PointOperation(OpMOVE,timeInMs,itkPoint, position); if ( m_UndoEnabled )//&& (posEvent->GetType() == mitk::Type_MouseButtonRelease) { //set the undo-operation, so the final position is undo-able //calculate the old Position from the already moved position - m_SumVec mitk::Point3D undoPoint = ( itkPoint - m_SumVec ); PointOperation* undoOp = new mitk::PointOperation(OpMOVE,timeInMs, undoPoint, position); OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Move point"); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation m_PointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; } ++it; } // Update the display interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); OperationEvent::IncCurrGroupEventId(); } else {return false; } this->NotifyResultReady(); return true; } void mitk::PointSetDataInteractor::SetAccuracy(float accuracy) { m_SelectionAccuracy = accuracy; } void mitk::PointSetDataInteractor::SetMaxPoints(unsigned int maxNumber) { m_MaxNumberOfPoints = maxNumber; } int mitk::PointSetDataInteractor::GetPointIndexByPosition(Point3D position, int time, float accuracy) { // iterate over point set and check if it contains a point close enough to the pointer to be selected PointSet* points = dynamic_cast(GetDataNode()->GetData()); int index = -1; if (points == NULL) { return index; } PointSet::PointsContainer* pointsContainer = points->GetPointSet(time)->GetPoints(); float minDistance = m_SelectionAccuracy; if (accuracy != -1 ) minDistance = accuracy; for (PointSet::PointsIterator it = pointsContainer->Begin(); it != pointsContainer->End(); it++) { float distance = sqrt(position.SquaredEuclideanDistanceTo(points->GetPoint(it->Index(), time))); if (distance < minDistance) // if several points fall within the margin, choose the one with minimal distance to position { index = it->Index(); } } return index; } bool mitk::PointSetDataInteractor::CheckSelection(const mitk::InteractionEvent *interactionEvent) { const InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { int timeStep = positionEvent->GetSender()->GetTimeStep(); Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected int index = GetPointIndexByPosition(point, timeStep); if (index != -1) return true; } return false; } void mitk::PointSetDataInteractor::UnselectAll(unsigned int timeStep, ScalarType timeInMs) { mitk::PointSet *pointSet = dynamic_cast( GetDataNode()->GetData() ); if ( pointSet == NULL ) { return; } mitk::PointSet::DataType *itkPointSet = pointSet->GetPointSet( timeStep ); if ( itkPointSet == NULL ) { return; } mitk::PointSet::PointsContainer::Iterator it, end; end = itkPointSet->GetPoints()->End(); for (it = itkPointSet->GetPoints()->Begin(); it != end; it++) { int position = it->Index(); PointSet::PointDataType pointData = {0, false, PTUNDEFINED}; itkPointSet->GetPointData( position, &pointData ); //then declare an operation which unselects this point; //UndoOperation as well! if ( pointData.selected ) { mitk::Point3D noPoint; noPoint.Fill( 0 ); mitk::PointOperation *doOp = new mitk::PointOperation(OpDESELECTPOINT,timeInMs, noPoint, position); if ( m_UndoEnabled ) { mitk::PointOperation *undoOp = new mitk::PointOperation(OpSELECTPOINT, timeInMs, noPoint, position); OperationEvent *operationEvent = new OperationEvent( pointSet, doOp, undoOp ); m_UndoController->SetOperationEvent( operationEvent ); } pointSet->ExecuteOperation( doOp ); if ( !m_UndoEnabled ) delete doOp; } } } void mitk::PointSetDataInteractor::SelectPoint(int position, unsigned int timeStep, ScalarType timeInMS) { mitk::PointSet *pointSet = dynamic_cast< mitk::PointSet * >( this->GetDataNode()->GetData() ); - //if List is empty, then no select of a point can be done! + //if List is empty, then no selection of a point can be done! if ( (pointSet == NULL) || (pointSet->GetSize( timeStep ) <= 0) ) { return; } //dummyPoint... not needed anyway mitk::Point3D noPoint; noPoint.Fill(0); mitk::PointOperation *doOp = new mitk::PointOperation( OpSELECTPOINT,timeInMS, noPoint, position); if ( m_UndoEnabled ) { mitk::PointOperation* undoOp = new mitk::PointOperation(OpDESELECTPOINT,timeInMS, noPoint, position); OperationEvent *operationEvent = new OperationEvent(pointSet, doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } pointSet->ExecuteOperation( doOp ); if ( !m_UndoEnabled ) delete doOp; } diff --git a/Core/Code/Interactions/mitkPointSetDataInteractor.h b/Core/Code/Interactions/mitkPointSetDataInteractor.h index 4a3631468c..186260bba4 100644 --- a/Core/Code/Interactions/mitkPointSetDataInteractor.h +++ b/Core/Code/Interactions/mitkPointSetDataInteractor.h @@ -1,177 +1,178 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkPointSetDataInteractor_h_ #define mitkPointSetDataInteractor_h_ #include "itkObject.h" #include "itkSmartPointer.h" #include "itkObjectFactory.h" #include "mitkCommon.h" #include #include "mitkDataInteractor.h" #include namespace mitk { /** * Class PointSetDataInteractor * \brief Implementation of the PointSetInteractor * * Interactor operates on a point set and supports to: * - add points * - remove points * - move single points * - move complete pointset * - select/unselect a point * * in 2d and 3d render windows. */ // Inherit from DataInteratcor, this provides functionality of a state machine and configurable inputs. class MITK_CORE_EXPORT PointSetDataInteractor: public DataInteractor { public: mitkClassMacro(PointSetDataInteractor, DataInteractor) itkFactorylessNewMacro(Self) itkCloneMacro(Self) + + void DeletePointByIndex(unsigned int index, unsigned int timeStep = 0); + protected: PointSetDataInteractor(); virtual ~PointSetDataInteractor(); /** * Here actions strings from the loaded state machine pattern are mapped to functions of * the DataInteractor. These functions are called when an action from the state machine pattern is executed. */ virtual void ConnectActionsAndFunctions(); /** * This function is called when a DataNode has been set/changed. * It is used to initialize the DataNode, e.g. if no PointSet exists yet it is created * and added to the DataNode. */ virtual void DataNodeChanged(); /** * Sets the maximum distance that is accepted when looking for a point at a certain position using the GetPointIndexByPosition function. */ void SetAccuracy(float accuracy); /** * @brief SetMaxPoints Sets the maximal number of points for the pointset * Default ist zero, which result in infinite number of allowed points * @param maxNumber */ void SetMaxPoints(unsigned int maxNumber = 0); /** * \brief Return index in PointSet of the point that is within given accuracy to the provided position. * * Assumes that the DataNode contains a PointSet, if so it iterates over all points * in the DataNode to check if it contains a point near the pointer position. * If a point is found its index-position is returned, else -1 is returned. */ virtual int GetPointIndexByPosition(Point3D position, int time = 0, float accuracy = -1); virtual bool CheckSelection( const InteractionEvent* interactionEvent ); /** Adds a point at the given coordinates. * Every time a point is added it is also checked if the maximal number of points is reached, * and if so an InternalEvent with the signal name "MaxNumberOfPoints" is triggered. */ virtual bool AddPoint(StateMachineAction*, InteractionEvent* event); /** Removes point that is selected */ virtual bool RemovePoint(StateMachineAction*, InteractionEvent*interactionEvent); /** * Checks if new point is close enough to an old one, * if so, trigger the ClosedContour signal which can be caught by the state machine. */ virtual bool IsClosedContour(StateMachineAction*, InteractionEvent*); /** * Moves the currently selected point to the new coodinates. */ virtual bool MovePoint(StateMachineAction*, InteractionEvent*); /** * Initializes the movement, stores starting position. */ virtual bool InitMove(StateMachineAction*, InteractionEvent*interactionEvent); /** * Is called when a movement is finished, changes back to regular color. */ virtual bool FinishMove(StateMachineAction*, InteractionEvent*); /** * Selects a point from the PointSet as currently active. */ virtual bool SelectPoint(StateMachineAction*, InteractionEvent*); /** * Unselects a point at the given coordinate. */ virtual bool UnSelectPointAtPosition(StateMachineAction*, InteractionEvent*); /** * Unselects all points out of reach. */ virtual bool UnSelectAll(StateMachineAction*, InteractionEvent*); /** * @brief UpdatePointSet Updates the member variable that holds the point set, evaluating the time step of the sender. */ virtual bool UpdatePointSet(StateMachineAction* stateMachineAction, InteractionEvent*); /** * Calls for inactivation of the DataInteractor */ virtual bool Abort(StateMachineAction*, InteractionEvent*); /** \brief to calculate a direction vector from last point and actual * point */ - Point3D m_LastPoint; /** \brief summ-vector for Movement */ Vector3D m_SumVec; private: // DATA PointSet::Pointer m_PointSet; int m_MaxNumberOfPoints; // maximum of allowed number of points - float m_SelectionAccuracy; // accuracy that's needed to select a point // FUNCTIONS void UnselectAll(unsigned int timeStep , ScalarType timeInMs); void SelectPoint(int position, unsigned int timeStep , ScalarType timeInMS); }; } #endif diff --git a/Modules/QtWidgetsExt/QmitkPointListModel.cpp b/Modules/QtWidgetsExt/QmitkPointListModel.cpp index 94c6b4eea0..349c310682 100644 --- a/Modules/QtWidgetsExt/QmitkPointListModel.cpp +++ b/Modules/QtWidgetsExt/QmitkPointListModel.cpp @@ -1,333 +1,330 @@ /*=================================================================== 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 "QmitkPointListModel.h" #include #include "mitkInteractionConst.h" #include "mitkPointOperation.h" #include "mitkRenderingManager.h" -#include +#include #include #include #include +#include QmitkPointListModel::QmitkPointListModel( mitk::DataNode* pointSetNode, int t, QObject* parent ) :QAbstractListModel(parent), m_PointSetNode(NULL), m_PointSetModifiedObserverTag(0), m_PointSetDeletedObserverTag(0), m_TimeStep(t) { ObserveNewPointSet( pointSetNode ); } Qt::ItemFlags QmitkPointListModel::flags(const QModelIndex& /*index*/) const { // no editing so far, return default (enabled, selectable) return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } QmitkPointListModel::~QmitkPointListModel() { this->ObserveNewPointSet( NULL ); } void QmitkPointListModel::SetPointSetNode( mitk::DataNode* pointSetNode ) { this->ObserveNewPointSet( pointSetNode ); QAbstractListModel::reset(); emit SignalUpdateSelection(); } mitk::PointSet* QmitkPointListModel::GetPointSet() const { return this->CheckForPointSetInNode(m_PointSetNode); } void QmitkPointListModel::SetTimeStep(int t) { m_TimeStep = t; QAbstractListModel::reset(); emit SignalUpdateSelection(); } int QmitkPointListModel::GetTimeStep() const { return m_TimeStep; } void QmitkPointListModel::ObserveNewPointSet( mitk::DataNode* pointSetNode ) { //remove old observers if (m_PointSetNode != NULL) { mitk::PointSet::Pointer oldPointSet = dynamic_cast(m_PointSetNode->GetData()); if (oldPointSet.IsNotNull()) { oldPointSet->RemoveObserver(m_PointSetModifiedObserverTag); oldPointSet->RemoveObserver(m_PointSetDeletedObserverTag); } } //get the new pointset mitk::PointSet::Pointer pointSet = this->CheckForPointSetInNode(pointSetNode); m_PointSetNode = pointSetNode; if ( pointSet.IsNotNull()) { // add new observer for modified if necessary itk::ReceptorMemberCommand::Pointer modCommand = itk::ReceptorMemberCommand::New(); modCommand->SetCallbackFunction( this, &QmitkPointListModel::OnPointSetChanged ); m_PointSetModifiedObserverTag = pointSet->AddObserver( itk::ModifiedEvent(), modCommand ); // add new observer for detele if necessary itk::ReceptorMemberCommand::Pointer delCommand = itk::ReceptorMemberCommand::New(); delCommand->SetCallbackFunction( this, &QmitkPointListModel::OnPointSetDeleted ); m_PointSetDeletedObserverTag = pointSet->AddObserver( itk::DeleteEvent(), delCommand ); } else { m_PointSetModifiedObserverTag = 0; m_PointSetDeletedObserverTag = 0; } } void QmitkPointListModel::OnPointSetChanged(const itk::EventObject&) { QAbstractListModel::reset(); emit SignalUpdateSelection(); } void QmitkPointListModel::OnPointSetDeleted(const itk::EventObject&) { mitk::PointSet::Pointer ps = CheckForPointSetInNode(m_PointSetNode); if (ps) { ps->RemoveObserver(m_PointSetModifiedObserverTag); ps->RemoveObserver(m_PointSetDeletedObserverTag); } m_PointSetModifiedObserverTag = 0; m_PointSetDeletedObserverTag = 0; QAbstractListModel::reset(); } int QmitkPointListModel::rowCount(const QModelIndex&) const { mitk::PointSet::Pointer pointSet = this->CheckForPointSetInNode(m_PointSetNode); if ( pointSet.IsNotNull() ) { return pointSet->GetSize(m_TimeStep); } else { return 0; } } QVariant QmitkPointListModel::data(const QModelIndex& index, int role) const { mitk::PointSet::Pointer pointSet = this->CheckForPointSetInNode(m_PointSetNode); if ( pointSet.IsNull() ) { return QVariant(); } if ( !index.isValid() ) { return QVariant(); } if ( index.row() >= pointSet->GetSize(m_TimeStep) ) { return QVariant(); } if (role == Qt::DisplayRole) { mitk::PointSet::PointsContainer::ElementIdentifier id; mitk::PointSet::PointType p; bool pointFound = this->GetPointForModelIndex(index, p, id); if (pointFound == false) return QVariant(); QString s = QString("%0: (%1, %2, %3)") .arg( id, 3) .arg( p[0], 0, 'f', 3 ) .arg( p[1], 0, 'f', 3 ) .arg( p[2], 0, 'f', 3 ); return QVariant(s); } else { return QVariant(); } } QVariant QmitkPointListModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) { return QVariant(); } if (orientation == Qt::Horizontal) { return QString("Coordinates").arg(section); } else { return QString("Row %1").arg(section); } } bool QmitkPointListModel::GetPointForModelIndex( const QModelIndex &index, mitk::PointSet::PointType& p, mitk::PointSet::PointIdentifier& id) const { mitk::PointSet::Pointer pointSet = this->CheckForPointSetInNode(m_PointSetNode); if (pointSet.IsNull()) return false; if ((index.row() < 0) || (index.row() >= (int)pointSet->GetPointSet(m_TimeStep)->GetPoints()->Size())) return false; // get the nth. element, if it exists. // we can not use the index directly, because PointSet uses a map container, // where the index is not necessarily the same as the key. // Therefore we have to count the elements mitk::PointSet::PointsContainer::Iterator it = pointSet->GetPointSet(m_TimeStep)->GetPoints()->Begin(); for (int i = 0; i < index.row(); ++i) { ++it; if (it == pointSet->GetPointSet(m_TimeStep)->GetPoints()->End()) return false; } if (it != pointSet->GetPointSet(m_TimeStep)->GetPoints()->End()) // not at the end, { p = it->Value(); id = it->Index(); return true; } return false; } bool QmitkPointListModel::GetModelIndexForPointID(mitk::PointSet::PointIdentifier id, QModelIndex& index) const { mitk::PointSet::Pointer pointSet = this->CheckForPointSetInNode(m_PointSetNode); if (pointSet.IsNull()) return false; mitk::PointSet::PointsContainer::Pointer points = pointSet->GetPointSet(m_TimeStep)->GetPoints(); if (points->IndexExists(id) == false) return false; unsigned int idx = 0; for (mitk::PointSet::PointsContainer::Iterator it = points->Begin(); it != points->End(); ++it) { if (it->Index() == id) // we found the correct element { index = this->index(idx); return true; } idx++; } return false; // nothing found } void QmitkPointListModel::MoveSelectedPointUp() { mitk::PointSet::Pointer pointSet = this->CheckForPointSetInNode(m_PointSetNode); if (pointSet.IsNull()) return; mitk::PointSet::PointIdentifier selectedID; selectedID = pointSet->SearchSelectedPoint(m_TimeStep); mitk::ScalarType tsInMS = pointSet->GetTimeGeometry()->TimeStepToTimePoint(m_TimeStep); mitk::PointOperation* doOp = new mitk::PointOperation(mitk::OpMOVEPOINTUP,tsInMS, pointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); pointSet->ExecuteOperation(doOp); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Workaround for update problem in Pointset/Mapper } void QmitkPointListModel::MoveSelectedPointDown() { mitk::PointSet::Pointer pointSet = this->CheckForPointSetInNode(m_PointSetNode); if (pointSet.IsNull()) return; mitk::PointSet::PointIdentifier selectedID; selectedID = pointSet->SearchSelectedPoint(m_TimeStep); mitk::ScalarType tsInMS = pointSet->GetTimeGeometry()->TimeStepToTimePoint(m_TimeStep); mitk::PointOperation* doOp = new mitk::PointOperation(mitk::OpMOVEPOINTDOWN, tsInMS, pointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); pointSet->ExecuteOperation(doOp); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Workaround for update problem in Pointset/Mapper } void QmitkPointListModel::RemoveSelectedPoint() { mitk::PointSet::Pointer pointSet = this->CheckForPointSetInNode(m_PointSetNode); if (pointSet.IsNull()) return; //get corresponding interactor to PointSet - mitk::PointSetInteractor::Pointer interactor = dynamic_cast(m_PointSetNode->GetInteractor()); + mitk::PointSetDataInteractor::Pointer interactor = dynamic_cast(m_PointSetNode->GetDataInteractor().GetPointer()); if (interactor.IsNull()) { if (m_PointSetNode->GetInteractor()==NULL && m_PointSetNode != NULL) //no Interactor set to node { - interactor = mitk::PointSetInteractor::New("pointsetinteractor",m_PointSetNode); - m_PointSetNode->SetInteractor(interactor); + interactor->LoadStateMachine("PointSet.xml"); + interactor->SetEventConfig("PointSetConfig.xml"); + m_PointSetNode->SetDataInteractor(interactor.GetPointer()); } else { MITK_WARN<<"Unexpected interactor found!\n"; return; } } - - - //send a DEL event to pointsetinteractor - const mitk::Event* delEvent = new mitk::Event(NULL, mitk::Type_KeyPress, mitk::BS_NoButton, mitk::BS_NoButton, mitk::Key_Delete); - mitk::StateEvent* delStateEvent = new mitk::StateEvent(mitk::EIDDELETE, delEvent); - interactor->HandleEvent(delStateEvent); - delete delEvent; - delete delStateEvent; +// mitk::InternalEvent::Pointer event = mitk::InternalEvent::New(NULL,interactor,"RemovePoint"); +// interactor->HandleEvent(event.GetPointer(), interactor->GetDataNode()); +// MITK_INFO << "!!!!!!!!!!!!!!!!!!!!!!! EXECUTED !!!!!!!!!!!!!!!!!!!!"; mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Workaround for update problem in PointSet/Mapper } mitk::PointSet* QmitkPointListModel::CheckForPointSetInNode(mitk::DataNode* node) const { if (node != NULL) { mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNotNull()) return pointSet; } return NULL; } diff --git a/Plugins/org.mitk.gui.qt.registration/src/internal/QmitkPointBasedRegistrationView.h b/Plugins/org.mitk.gui.qt.registration/src/internal/QmitkPointBasedRegistrationView.h index eb166f9efd..0a48c43b99 100644 --- a/Plugins/org.mitk.gui.qt.registration/src/internal/QmitkPointBasedRegistrationView.h +++ b/Plugins/org.mitk.gui.qt.registration/src/internal/QmitkPointBasedRegistrationView.h @@ -1,297 +1,294 @@ /*=================================================================== 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. ===================================================================*/ #if !defined(QMITK_POINTBASEDREGISTRATION_H__INCLUDED) #define QMITK_POINTBASEDREGISTRATION_H__INCLUDED #include "QmitkFunctionality.h" #include "berryISelectionListener.h" #include "berryIStructuredSelection.h" //#include "mitkTestingConfig.h" // IMPORTANT: this defines or undefines BUILD_TESTING ! -#include -#include -#include #include #include #include //#include "QmitkMessageBoxHelper.h" #include "ui_QmitkPointBasedRegistrationViewControls.h" #include /*! \brief The PointBasedRegistration functionality is used to perform point based registration. This functionality allows you to register 2D as well as 3D images in a rigid and deformable manner via corresponding PointSets. Register means to align two images, so that they become as similar as possible. Therefore you have to set corresponding points in both images, which will be matched. The movement, which has to be performed on the points to align them will be performed on the moving image as well. The result is shown in the multi-widget. For more informations see: \ref QmitkPointBasedRegistrationUserManual \sa QmitkFunctionality \ingroup Functionalities \ingroup PointBasedRegistration */ class REGISTRATION_EXPORT QmitkPointBasedRegistrationView : public QmitkFunctionality { friend struct SelListenerPointBasedRegistration; Q_OBJECT public: static const std::string VIEW_ID; /*! \brief Default constructor */ QmitkPointBasedRegistrationView(QObject *parent=0, const char *name=0); /*! \brief Default destructor */ virtual ~QmitkPointBasedRegistrationView(); /*! \brief method for creating the applications main widget */ virtual void CreateQtPartControl(QWidget *parent); /*! \brief Sets the StdMultiWidget and connects it to the functionality. */ virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); /*! \brief Removes the StdMultiWidget and disconnects it from the functionality. */ virtual void StdMultiWidgetNotAvailable(); /*! \brief Method for creating the connections of main and control widget */ virtual void CreateConnections(); virtual void Activated(); virtual void Deactivated(); virtual void Visible(); virtual void Hidden(); // // #ifdef BUILD_TESTING // / ** // \brief Testing entry point // * / // virtual int TestYourself(); // // / ** // \brief Helper method for testing // * / // bool TestAllTools(); // // // protected slots: // /** // \brief Helper method for testing // */ // void RegistrationErrorDialogFound( QWidget* widget ); // // /** // \brief Helper method for testing // */ // void ClearPointSetDialogFound( QWidget* widget ); // // private: // bool m_MessageBox; // // // public: // #else // // slot function is needed, because moc ignores our #ifdefs // void RegistrationErrorDialogFound( QWidget* widget ) {} // // slot function is needed, because moc ignores our #ifdefs // void ClearPointSetDialogFound(QWidget* widget){} // #endif void DataNodeHasBeenRemoved(const mitk::DataNode* node); protected slots: /*! \brief Sets the fixed Image according to TreeNodeSelector widget */ void FixedSelected(mitk::DataNode::Pointer fixedImage); /*! \brief Sets the moving Image according to TreeNodeSelector widget */ void MovingSelected(mitk::DataNode::Pointer movingImage); /*! \brief Calculates registration with vtkLandmarkTransform */ void calculateLandmarkbased(); /*! \brief Calculates registration with itkLandmarkWarping */ void calculateLandmarkWarping(); /*! \brief Calculates registration with ICP and vtkLandmarkTransform */ void calculateLandmarkbasedWithICP(); /*! \brief lets the fixed image become invisible and the moving image visible */ void HideMovingImage(bool hide); /*! \brief lets the moving image become invisible and the fixed image visible */ void HideFixedImage(bool hide); /*! \brief Checks if registration is possible */ bool CheckCalculate(); /*! \brief Performs an undo for the last transform. */ void UndoTransformation(); /*! \brief Performs a redo for the last undo transform. */ void RedoTransformation(); /*! \brief Stores whether the image will be shown in grayvalues or in red for fixed image and green for moving image @param show if true, then images will be shown in red and green */ void showRedGreen(bool show); /*! \brief Sets the selected opacity for moving image @param opacity the selected opacity */ void OpacityUpdate(float opacity); /*! \brief Sets the selected opacity for moving image @param opacity the selected opacity */ void OpacityUpdate(int opacity); /*! \brief Updates the moving landmarks */ void updateMovingLandmarksList(); /*! \brief Updates the fixed landmarks */ void updateFixedLandmarksList(); /*! \brief Sets the images to gray values or fixed image to red and moving image to green @param redGreen if true, then images will be shown in red and green */ void setImageColor(bool redGreen); /*! \brief Clears the undo and redo transformation lists. */ void clearTransformationLists(); /*! \brief Calculates the landmark error for the selected transformation. */ void checkLandmarkError(); /*! \brief Changes the transformation type and calls checkLandmarkError(). */ void transformationChanged(int transform); /*! \brief Checks whether the registration can be performed. */ bool checkCalculateEnabled(); /*! \brief Performs the registration. */ void calculate(); void SetImagesVisible(berry::ISelection::ConstPointer /*selection*/); void SwitchImages(); protected: berry::ISelectionListener::Pointer m_SelListener; berry::IStructuredSelection::ConstPointer m_CurrentSelection; /*! * default main widget containing 4 windows showing 3 * orthogonal slices of the volume and a 3d render window */ QmitkStdMultiWidget * m_MultiWidget; /*! * control widget to make all changes for point based registration */ Ui::QmitkPointBasedRegistrationControls m_Controls; mitk::PointSet::Pointer m_FixedLandmarks; mitk::PointSet::Pointer m_MovingLandmarks; mitk::DataNode::Pointer m_MovingPointSetNode; mitk::DataNode::Pointer m_FixedPointSetNode; mitk::DataNode::Pointer m_MovingNode; mitk::DataNode::Pointer m_FixedNode; std::list m_UndoGeometryList; std::list m_UndoPointsGeometryList; std::list m_RedoGeometryList; std::list m_RedoPointsGeometryList; bool m_ShowRedGreen; float m_Opacity; float m_OriginalOpacity; mitk::Color m_FixedColor; mitk::Color m_MovingColor; int m_Transformation; bool m_HideFixedImage; bool m_HideMovingImage; std::string m_OldFixedLabel; std::string m_OldMovingLabel; bool m_Deactivated; int m_CurrentFixedLandmarksObserverID; int m_CurrentMovingLandmarksObserverID; itk::SimpleMemberCommand::Pointer m_FixedLandmarksChangedCommand; itk::SimpleMemberCommand::Pointer m_MovingLandmarksChangedCommand; }; #endif // !defined(QMITK_POINTBASEDREGISTRATION_H__INCLUDED)