diff --git a/Core/Code/Controllers/mitkPlanePositionManager.cpp b/Core/Code/Controllers/mitkPlanePositionManager.cpp new file mode 100644 index 0000000000..274da7a21b --- /dev/null +++ b/Core/Code/Controllers/mitkPlanePositionManager.cpp @@ -0,0 +1,94 @@ +#include "mitkPlanePositionManager.h" +#include "mitkInteractionConst.h" + +#include +#include +#include + + +mitk::PlanePositionManagerService::PlanePositionManagerService() +{ +} + +mitk::PlanePositionManagerService::~PlanePositionManagerService() +{ + for (unsigned int i = 0; i < m_PositionList.size(); i++) + { + delete m_PositionList.at(i); + } +} + +unsigned int mitk::PlanePositionManagerService::AddNewPlanePosition ( const Geometry2D* plane, unsigned int sliceIndex ) +{ + for (unsigned int i = 0; i < m_PositionList.size(); i++) + { + if (m_PositionList.at(i) != 0) + { + bool isSameMatrix(true); + bool isSameOffset(true); + isSameOffset = mitk::Equal(m_PositionList.at(i)->GetTransform()->GetOffset(), plane->GetIndexToWorldTransform()->GetOffset()); + if(!isSameOffset || sliceIndex != m_PositionList.at(i)->GetPos()) + continue; + isSameMatrix = mitk::MatrixEqualElementWise(m_PositionList.at(i)->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix()); + if(isSameMatrix) + return i; + } + + } + + AffineTransform3D::Pointer transform = AffineTransform3D::New(); + Matrix3D matrix; + matrix.GetVnlMatrix().set_column(0, plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); + matrix.GetVnlMatrix().set_column(1, plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); + matrix.GetVnlMatrix().set_column(2, plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); + transform->SetMatrix(matrix); + transform->SetOffset(plane->GetIndexToWorldTransform()->GetOffset()); + + mitk::Vector3D direction; + direction[0] = plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)[0]; + direction[1] = plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)[1]; + direction[2] = plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)[2]; + direction.Normalize(); + + mitk::RestorePlanePositionOperation* newOp = new mitk::RestorePlanePositionOperation (OpRESTOREPLANEPOSITION, plane->GetExtent(0), + plane->GetExtent(1), plane->GetSpacing(), sliceIndex, direction, transform); + + m_PositionList.push_back( newOp ); + return GetNumberOfPlanePositions()-1; +} + +bool mitk::PlanePositionManagerService::RemovePlanePosition( unsigned int ID ) +{ + if (m_PositionList.size() > ID && ID >= 0) + { + m_PositionList.erase(m_PositionList.begin()+ID); + return true; + } + else + { + return false; + } +} + +mitk::RestorePlanePositionOperation* mitk::PlanePositionManagerService::GetPlanePosition ( unsigned int ID ) +{ + if ( ID < m_PositionList.size() ) + { + return m_PositionList.at(ID); + } + else + { + MITK_WARN<<"GetPlanePosition returned NULL!"; + return 0; + } +} + +unsigned int mitk::PlanePositionManagerService::GetNumberOfPlanePositions() +{ + return m_PositionList.size(); +} + +void mitk::PlanePositionManagerService::RemoveAllPlanePositions() +{ + m_PositionList.clear(); +} diff --git a/Core/Code/Controllers/mitkPlanePositionManager.h b/Core/Code/Controllers/mitkPlanePositionManager.h new file mode 100644 index 0000000000..f74e1dbc8b --- /dev/null +++ b/Core/Code/Controllers/mitkPlanePositionManager.h @@ -0,0 +1,104 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision$ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#ifndef mitkPlanePositionManager_h_Included +#define mitkPlanePositionManager_h_Included + +#include "mitkCommon.h" +#include "MitkExtExports.h" +#include "mitkRestorePlanePositionOperation.h" +#include "mitkDataStorage.h" +#include +#include +#include + +class MitkCoreActivator; + +namespace mitk +{ + +/** + The mitk::PlanePositionManagerService holds and manages a list of certain planepositions. + To store a new position you need to specify the first slice of your slicestack and the + slicenumber you want to restore in the mitk::PlanePositionManager::AddNewPlanePosition() function. + + To restore a position call mitk::PlanePositionManagerService::GetPlanePosition(ID) where ID is the position + in the plane positionlist (returned by AddNewPlanePostion). This will give a mitk::RestorePlanePositionOperation + which can be executed by the SliceNavigationController of the slicestack. + + \sa QmitkSegmentationView.cpp + */ + class MITK_CORE_EXPORT PlanePositionManagerService : public itk::LightObject + { + + public: + + /** + \brief Adds a new plane position to the list. If this geometry is identical to one of the list nothing will be added + + \a plane THE FIRST! slice of the slice stack + \a sliceIndex the slice number of the selected slice + \return returns the ID i.e. the position in the positionlist. If the PlaneGeometry which is to be added already exists the existing + ID will be returned. + */ + unsigned int AddNewPlanePosition(const mitk::Geometry2D* plane, unsigned int sliceIndex = 0); + + /** + \brief Removes the plane at the position \a ID from the list. + + \a ID the plane ID which should be removed, i.e. its position in the list + \return true if the plane was removed successfully and false if it is an invalid ID + */ + bool RemovePlanePosition(unsigned int ID); + + /// \brief Clears the complete positionlist + void RemoveAllPlanePositions(); + + /** + \brief Getter for a specific plane position with a given ID + + \a ID the ID of the plane position + \return Returns a RestorePlanePositionOperation which can be executed by th SliceNavigationController or NULL for an invalid ID + */ + mitk::RestorePlanePositionOperation* GetPlanePosition( unsigned int ID); + + /// \brief Getting the number of all stored planes + unsigned int GetNumberOfPlanePositions(); + + friend class ::MitkCoreActivator; + + private: + + + mitkClassMacro(PlanePositionManagerService, LightObject); + + itkFactorylessNewMacro(PlanePositionManagerService); + + PlanePositionManagerService(); + ~PlanePositionManagerService(); + + // Disable copy constructor and assignment operator. + PlanePositionManagerService(const PlanePositionManagerService&); + PlanePositionManagerService& operator=(const PlanePositionManagerService&); + + static PlanePositionManagerService* m_Instance; + std::vector m_PositionList; + }; +} +MITK_DECLARE_SERVICE_INTERFACE(mitk::PlanePositionManagerService, "org.mitk.PlanePositionManagerService") + +#endif diff --git a/Core/Code/Controllers/mitkSliceNavigationController.cpp b/Core/Code/Controllers/mitkSliceNavigationController.cpp index 2be5807b77..544e6ac62a 100644 --- a/Core/Code/Controllers/mitkSliceNavigationController.cpp +++ b/Core/Code/Controllers/mitkSliceNavigationController.cpp @@ -1,728 +1,736 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkSliceNavigationController.h" #include "mitkBaseRenderer.h" #include "mitkSlicedGeometry3D.h" #include "mitkPlaneGeometry.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkStateEvent.h" #include "mitkCrosshairPositionEvent.h" #include "mitkPositionEvent.h" #include "mitkInteractionConst.h" #include "mitkAction.h" #include "mitkGlobalInteraction.h" #include "mitkEventMapper.h" #include "mitkFocusManager.h" #include "mitkVtkPropRenderer.h" #include "mitkRenderingManager.h" #include "mitkInteractionConst.h" #include "mitkPointOperation.h" #include "mitkPlaneOperation.h" #include "mitkUndoController.h" #include "mitkOperationEvent.h" #include "mitkNodePredicateDataType.h" #include "mitkStatusBar.h" #include #include "mitkMemoryUtilities.h" #include namespace mitk { SliceNavigationController::SliceNavigationController( const char *type ) : BaseController( type ), m_InputWorldGeometry( NULL ), m_CreatedWorldGeometry( NULL ), m_ViewDirection( Transversal ), m_DefaultViewDirection( Transversal ), m_RenderingManager( NULL ), m_Renderer( NULL ), m_Top( false ), m_FrontSide( false ), m_Rotated( false ), m_BlockUpdate( false ), m_SliceLocked( false ), m_SliceRotationLocked( false ), m_OldPos(0) { typedef itk::SimpleMemberCommand< SliceNavigationController > SNCCommandType; SNCCommandType::Pointer sliceStepperChangedCommand, timeStepperChangedCommand; sliceStepperChangedCommand = SNCCommandType::New(); timeStepperChangedCommand = SNCCommandType::New(); sliceStepperChangedCommand->SetCallbackFunction( this, &SliceNavigationController::SendSlice ); timeStepperChangedCommand->SetCallbackFunction( this, &SliceNavigationController::SendTime ); m_Slice->AddObserver( itk::ModifiedEvent(), sliceStepperChangedCommand ); m_Time->AddObserver( itk::ModifiedEvent(), timeStepperChangedCommand ); m_Slice->SetUnitName( "mm" ); m_Time->SetUnitName( "ms" ); m_Top = false; m_FrontSide = false; m_Rotated = false; } SliceNavigationController::~SliceNavigationController() { } void SliceNavigationController::SetInputWorldGeometry( const Geometry3D *geometry ) { if ( geometry != NULL ) { if ( const_cast< BoundingBox * >( geometry->GetBoundingBox()) ->GetDiagonalLength2() < eps ) { itkWarningMacro( "setting an empty bounding-box" ); geometry = NULL; } } if ( m_InputWorldGeometry != geometry ) { m_InputWorldGeometry = geometry; this->Modified(); } } RenderingManager * SliceNavigationController::GetRenderingManager() const { mitk::RenderingManager* renderingManager = m_RenderingManager.GetPointer(); if (renderingManager != NULL) return renderingManager; if ( m_Renderer != NULL ) { renderingManager = m_Renderer->GetRenderingManager(); if (renderingManager != NULL) return renderingManager; } return mitk::RenderingManager::GetInstance(); } void SliceNavigationController::SetViewDirectionToDefault() { m_ViewDirection = m_DefaultViewDirection; } void SliceNavigationController::Update() { if ( !m_BlockUpdate ) { if ( m_ViewDirection == Transversal ) { this->Update( Transversal, false, false, true ); } else { this->Update( m_ViewDirection ); } } } void SliceNavigationController::Update( SliceNavigationController::ViewDirection viewDirection, bool top, bool frontside, bool rotated ) { const TimeSlicedGeometry* worldTimeSlicedGeometry = dynamic_cast< const TimeSlicedGeometry * >( m_InputWorldGeometry.GetPointer() ); if( m_BlockUpdate || m_InputWorldGeometry.IsNull() || ( (worldTimeSlicedGeometry != NULL) && (worldTimeSlicedGeometry->GetTimeSteps() == 0) ) ) { return; } m_BlockUpdate = true; if ( m_LastUpdateTime < m_InputWorldGeometry->GetMTime() ) { Modified(); } this->SetViewDirection( viewDirection ); this->SetTop( top ); this->SetFrontSide( frontside ); this->SetRotated( rotated ); if ( m_LastUpdateTime < GetMTime() ) { m_LastUpdateTime = GetMTime(); // initialize the viewplane SlicedGeometry3D::Pointer slicedWorldGeometry = NULL; m_CreatedWorldGeometry = NULL; switch ( viewDirection ) { case Original: if ( worldTimeSlicedGeometry != NULL ) { m_CreatedWorldGeometry = static_cast< TimeSlicedGeometry * >( m_InputWorldGeometry->Clone().GetPointer() ); worldTimeSlicedGeometry = m_CreatedWorldGeometry.GetPointer(); slicedWorldGeometry = dynamic_cast< SlicedGeometry3D * >( m_CreatedWorldGeometry->GetGeometry3D( this->GetTime()->GetPos() ) ); if ( slicedWorldGeometry.IsNotNull() ) { break; } } else { const SlicedGeometry3D *worldSlicedGeometry = dynamic_cast< const SlicedGeometry3D * >( m_InputWorldGeometry.GetPointer()); if ( worldSlicedGeometry != NULL ) { slicedWorldGeometry = static_cast< SlicedGeometry3D * >( m_InputWorldGeometry->Clone().GetPointer()); break; } } //else: use Transversal: no "break" here!! case Transversal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( m_InputWorldGeometry, PlaneGeometry::Transversal, top, frontside, rotated ); slicedWorldGeometry->SetSliceNavigationController( this ); break; case Frontal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( m_InputWorldGeometry, PlaneGeometry::Frontal, top, frontside, rotated ); slicedWorldGeometry->SetSliceNavigationController( this ); break; case Sagittal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( m_InputWorldGeometry, PlaneGeometry::Sagittal, top, frontside, rotated ); slicedWorldGeometry->SetSliceNavigationController( this ); break; default: itkExceptionMacro("unknown ViewDirection"); } m_Slice->SetPos( 0 ); m_Slice->SetSteps( (int)slicedWorldGeometry->GetSlices() ); if ( m_CreatedWorldGeometry.IsNull() ) { // initialize TimeSlicedGeometry m_CreatedWorldGeometry = TimeSlicedGeometry::New(); } if ( worldTimeSlicedGeometry == NULL ) { m_CreatedWorldGeometry->InitializeEvenlyTimed( slicedWorldGeometry, 1 ); m_Time->SetSteps( 0 ); m_Time->SetPos( 0 ); m_Time->InvalidateRange(); } else { m_BlockUpdate = true; m_Time->SetSteps( worldTimeSlicedGeometry->GetTimeSteps() ); m_Time->SetPos( 0 ); const TimeBounds &timeBounds = worldTimeSlicedGeometry->GetTimeBounds(); m_Time->SetRange( timeBounds[0], timeBounds[1] ); m_BlockUpdate = false; assert( worldTimeSlicedGeometry->GetGeometry3D( this->GetTime()->GetPos() ) != NULL ); slicedWorldGeometry->SetTimeBounds( worldTimeSlicedGeometry->GetGeometry3D( this->GetTime()->GetPos() )->GetTimeBounds() ); //@todo implement for non-evenly-timed geometry! m_CreatedWorldGeometry->InitializeEvenlyTimed( slicedWorldGeometry, worldTimeSlicedGeometry->GetTimeSteps() ); } } // unblock update; we may do this now, because if m_BlockUpdate was already // true before this method was entered, then we will never come here. m_BlockUpdate = false; // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry and time/slice data. this->SendCreatedWorldGeometry(); this->SendSlice(); this->SendTime(); // Adjust the stepper range of slice stepper according to geometry this->AdjustSliceStepperRange(); } void SliceNavigationController::SendCreatedWorldGeometry() { // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry. if ( !m_BlockUpdate ) { this->InvokeEvent( GeometrySendEvent(m_CreatedWorldGeometry, 0) ); } } void SliceNavigationController::SendCreatedWorldGeometryUpdate() { if ( !m_BlockUpdate ) { this->InvokeEvent( GeometryUpdateEvent(m_CreatedWorldGeometry, m_Slice->GetPos()) ); } } void SliceNavigationController::SendSlice() { if ( !m_BlockUpdate ) { if ( m_CreatedWorldGeometry.IsNotNull() ) { this->InvokeEvent( GeometrySliceEvent(m_CreatedWorldGeometry, m_Slice->GetPos()) ); // send crosshair event crosshairPositionEvent.Send(); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SendTime() { if ( !m_BlockUpdate ) { if ( m_CreatedWorldGeometry.IsNotNull() ) { this->InvokeEvent( GeometryTimeEvent(m_CreatedWorldGeometry, m_Time->GetPos()) ); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SetGeometry( const itk::EventObject & ) { } void SliceNavigationController ::SetGeometryTime( const itk::EventObject &geometryTimeEvent ) { const SliceNavigationController::GeometryTimeEvent *timeEvent = dynamic_cast< const SliceNavigationController::GeometryTimeEvent * >( &geometryTimeEvent); assert( timeEvent != NULL ); TimeSlicedGeometry *timeSlicedGeometry = timeEvent->GetTimeSlicedGeometry(); assert( timeSlicedGeometry != NULL ); if ( m_CreatedWorldGeometry.IsNotNull() ) { int timeStep = (int) timeEvent->GetPos(); ScalarType timeInMS; timeInMS = timeSlicedGeometry->TimeStepToMS( timeStep ); timeStep = m_CreatedWorldGeometry->MSToTimeStep( timeInMS ); this->GetTime()->SetPos( timeStep ); } } void SliceNavigationController ::SetGeometrySlice(const itk::EventObject & geometrySliceEvent) { const SliceNavigationController::GeometrySliceEvent* sliceEvent = dynamic_cast( &geometrySliceEvent); assert(sliceEvent!=NULL); this->GetSlice()->SetPos(sliceEvent->GetPos()); } void SliceNavigationController::SelectSliceByPoint( const Point3D &point ) { //@todo add time to PositionEvent and use here!! SlicedGeometry3D* slicedWorldGeometry = dynamic_cast< SlicedGeometry3D * >( m_CreatedWorldGeometry->GetGeometry3D( this->GetTime()->GetPos() ) ); if ( slicedWorldGeometry ) { int bestSlice = -1; double bestDistance = itk::NumericTraits::max(); int s, slices; slices = slicedWorldGeometry->GetSlices(); if ( slicedWorldGeometry->GetEvenlySpaced() ) { mitk::Geometry2D *plane = slicedWorldGeometry->GetGeometry2D( 0 ); const Vector3D &direction = slicedWorldGeometry->GetDirectionVector(); Point3D projectedPoint; plane->Project( point, projectedPoint ); // Check whether the point is somewhere within the slice stack volume; // otherwise, the defualt slice (0) will be selected if ( direction[0] * (point[0] - projectedPoint[0]) + direction[1] * (point[1] - projectedPoint[1]) + direction[2] * (point[2] - projectedPoint[2]) >= 0 ) { bestSlice = (int)(plane->Distance( point ) / slicedWorldGeometry->GetSpacing()[2] + 0.5); } } else { Point3D projectedPoint; for ( s = 0; s < slices; ++s ) { slicedWorldGeometry->GetGeometry2D( s )->Project( point, projectedPoint ); Vector3D distance = projectedPoint - point; ScalarType currentDistance = distance.GetSquaredNorm(); if ( currentDistance < bestDistance ) { bestDistance = currentDistance; bestSlice = s; } } } if ( bestSlice >= 0 ) { this->GetSlice()->SetPos( bestSlice ); } else { this->GetSlice()->SetPos( 0 ); } this->SendCreatedWorldGeometryUpdate(); } } void SliceNavigationController::ReorientSlices( const Point3D &point, const Vector3D &normal ) { PlaneOperation op( OpORIENT, point, normal ); m_CreatedWorldGeometry->ExecuteOperation( &op ); this->SendCreatedWorldGeometryUpdate(); } const mitk::TimeSlicedGeometry * SliceNavigationController::GetCreatedWorldGeometry() { return m_CreatedWorldGeometry; } const mitk::Geometry3D * SliceNavigationController::GetCurrentGeometry3D() { if ( m_CreatedWorldGeometry.IsNotNull() ) { return m_CreatedWorldGeometry->GetGeometry3D( this->GetTime()->GetPos() ); } else { return NULL; } } const mitk::PlaneGeometry * SliceNavigationController::GetCurrentPlaneGeometry() { const mitk::SlicedGeometry3D *slicedGeometry = dynamic_cast< const mitk::SlicedGeometry3D * > ( this->GetCurrentGeometry3D() ); if ( slicedGeometry ) { const mitk::PlaneGeometry *planeGeometry = dynamic_cast< mitk::PlaneGeometry * > ( slicedGeometry->GetGeometry2D(this->GetSlice()->GetPos()) ); return planeGeometry; } else { return NULL; } } void SliceNavigationController::SetRenderer( BaseRenderer *renderer ) { m_Renderer = renderer; } BaseRenderer * SliceNavigationController::GetRenderer() const { return m_Renderer; } void SliceNavigationController::AdjustSliceStepperRange() { const mitk::SlicedGeometry3D *slicedGeometry = dynamic_cast< const mitk::SlicedGeometry3D * > ( this->GetCurrentGeometry3D() ); const Vector3D &direction = slicedGeometry->GetDirectionVector(); int c = 0; int i, k = 0; for ( i = 0; i < 3; ++i ) { if ( fabs( (float) direction[i] ) < 0.000000001 ) { ++c; } else { k = i; } } if ( c == 2 ) { ScalarType min = m_InputWorldGeometry->GetOrigin()[k]; ScalarType max = min + m_InputWorldGeometry->GetExtentInMM( k ); m_Slice->SetRange( min, max ); } else { m_Slice->InvalidateRange(); } } void SliceNavigationController::ExecuteOperation( Operation *operation ) { // switch on type // - select best slice for a given point // - rotate created world geometry according to Operation->SomeInfo() if ( !operation ) { return; } switch ( operation->GetOperationType() ) { case OpMOVE: // should be a point operation { if ( !m_SliceLocked ) //do not move the cross position { // select a slice PointOperation *po = dynamic_cast< PointOperation * >( operation ); if ( po && po->GetIndex() == -1 ) { this->SelectSliceByPoint( po->GetPoint() ); } else if ( po && po->GetIndex() != -1 ) // undo case because index != -1, index holds the old position of this slice { this->GetSlice()->SetPos( po->GetIndex() ); } } break; } + case OpRESTOREPLANEPOSITION: + { + m_CreatedWorldGeometry->ExecuteOperation( operation ); + + this->SendCreatedWorldGeometryUpdate(); + + break; + } default: { // do nothing break; } } } // Relict from the old times, when automous decisions were accepted // behavior. Remains in here, because some RenderWindows do exist outside // of StdMultiWidgets. bool SliceNavigationController ::ExecuteAction( Action* action, StateEvent const* stateEvent ) { bool ok = false; const PositionEvent* posEvent = dynamic_cast< const PositionEvent * >( stateEvent->GetEvent() ); if ( posEvent != NULL ) { if ( m_CreatedWorldGeometry.IsNull() ) { return true; } switch (action->GetActionId()) { case AcMOVE: { BaseRenderer *baseRenderer = posEvent->GetSender(); if ( !baseRenderer ) { baseRenderer = const_cast( GlobalInteraction::GetInstance()->GetFocus() ); } if ( baseRenderer ) if ( baseRenderer->GetMapperID() == 1 ) { PointOperation* doOp = new mitk::PointOperation(OpMOVE, posEvent->GetWorldPosition()); if (m_UndoEnabled) { m_OldPos = this->GetSlice()->GetPos(); // m_OldPos holds the old slice position. For the undo controller this old position will be stored as index in mitk::PointOperation PointOperation* undoOp = new mitk::PointOperation(OpMOVE, posEvent->GetWorldPosition(), m_OldPos); OperationEvent *operationEvent = new mitk::OperationEvent(this, doOp, undoOp, "Move slices"); m_UndoController->SetOperationEvent(operationEvent); } this->ExecuteOperation( doOp ); // If click was performed in this render window than we have to update the status bar information about position and pixel value. if(baseRenderer == m_Renderer) { { std::string statusText; TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = baseRenderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); mitk::Point3D worldposition = posEvent->GetWorldPosition(); int maxlayer = -32768; mitk::Image::Pointer image3D; // find image with largest layer, that is the image shown on top in the render window for (unsigned int x = 0; x < nodes->size(); x++) { //Just consider image data that is no helper object. E.g. do not consider nodes created for the slice interpolation bool isHelper (false); nodes->at(x)->GetBoolProperty("helper object", isHelper); if(nodes->at(x)->GetData()->GetGeometry()->IsInside(worldposition) && isHelper == false) { int layer = 0; if(!(nodes->at(x)->GetIntProperty("layer", layer))) continue; if(layer > maxlayer) { if(static_cast(nodes->at(x))->IsVisible(m_Renderer)) { image3D = dynamic_cast(nodes->at(x)->GetData()); maxlayer = layer; } } } } std::stringstream stream; stream.imbue(std::locale::classic()); // get the position and gray value from the image and build up status bar text if(image3D.IsNotNull()) { Index3D p; image3D->GetGeometry()->WorldToIndex(worldposition, p); stream.precision(2); stream<<"Position: <" << std::fixed < mm"; stream<<"; Index: <"< "; mitk::ScalarType pixelValue = image3D->GetPixelValueByIndex(p, baseRenderer->GetTimeStep()); if (fabs(pixelValue)>1000000) { stream<<"; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: "<GetPixelValueByIndex(p, baseRenderer->GetTimeStep())<<" "; } else { stream<<"; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: "<GetPixelValueByIndex(p, baseRenderer->GetTimeStep())<<" "; } } else { stream << "No image information at this position!"; } statusText = stream.str(); mitk::StatusBar::GetInstance()->DisplayGreyValueText(statusText.c_str()); } } ok = true; break; } } default: ok = true; break; } return ok; } const DisplayPositionEvent *displPosEvent = dynamic_cast< const DisplayPositionEvent * >( stateEvent->GetEvent() ); if ( displPosEvent != NULL ) { return true; } return false; } } // namespace diff --git a/Core/Code/Controllers/mitkSliceNavigationController.h b/Core/Code/Controllers/mitkSliceNavigationController.h index 74bf7bc9c4..4573793b71 100644 --- a/Core/Code/Controllers/mitkSliceNavigationController.h +++ b/Core/Code/Controllers/mitkSliceNavigationController.h @@ -1,512 +1,512 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F #define SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F #include #include "mitkBaseController.h" #include "mitkRenderingManager.h" #include "mitkTimeSlicedGeometry.h" #include "mitkMessage.h" #pragma GCC visibility push(default) #include #pragma GCC visibility pop #include #include #include +#include "mitkRestorePlanePositionOperation.h" namespace mitk { #define mitkTimeSlicedGeometryEventMacro( classname , super ) \ class MITK_CORE_EXPORT classname : public super { \ public: \ typedef classname Self; \ typedef super Superclass; \ classname(TimeSlicedGeometry* aTimeSlicedGeometry, unsigned int aPos) \ : Superclass(aTimeSlicedGeometry, aPos) {} \ virtual ~classname() {} \ virtual const char * GetEventName() const { return #classname; } \ virtual bool CheckEvent(const ::itk::EventObject* e) const \ { return dynamic_cast(e); } \ virtual ::itk::EventObject* MakeObject() const \ { return new Self(GetTimeSlicedGeometry(), GetPos()); } \ private: \ void operator=(const Self&); \ } class PlaneGeometry; class Geometry3D; class BaseRenderer; /** * \brief Controls the selection of the slice the associated BaseRenderer * will display * * A SliceNavigationController takes a Geometry3D as input world geometry * (TODO what are the exact requirements?) and generates a TimeSlicedGeometry * as output. The TimeSlicedGeometry holds a number of SlicedGeometry3Ds and * these in turn hold a series of Geometry2Ds. One of these Geometry2Ds is * selected as world geometry for the BaseRenderers associated to 2D views. * * The SliceNavigationController holds has Steppers (one for the slice, a * second for the time step), which control the selection of a single * Geometry2D from the TimeSlicedGeometry. SliceNavigationController generates * ITK events to tell observers, like a BaseRenderer, when the selected slice * or timestep changes. * * SliceNavigationControllers are registered as listeners to GlobalInteraction * by the QmitkStdMultiWidget. In ExecuteAction, the controllers react to * PositionEvents by setting the steppers to the slice which is nearest to the * point of the PositionEvent. * * Example: * \code * // Initialization * sliceCtrl = mitk::SliceNavigationController::New(); * * // Tell the navigator the geometry to be sliced (with geometry a * // Geometry3D::ConstPointer) * sliceCtrl->SetInputWorldGeometry(geometry.GetPointer()); * * // Tell the navigator in which direction it shall slice the data * sliceCtrl->SetViewDirection(mitk::SliceNavigationController::Transversal); * * // Connect one or more BaseRenderer to this navigator, i.e.: events sent * // by the navigator when stepping through the slices (e.g. by * // sliceCtrl->GetSlice()->Next()) will be received by the BaseRenderer * // (in this example only slice-changes, see also ConnectGeometryTimeEvent * // and ConnectGeometryEvents.) * sliceCtrl->ConnectGeometrySliceEvent(renderer.GetPointer()); * * //create a world geometry and send the information to the connected renderer(s) * sliceCtrl->Update(); * \endcode * * * You can connect visible navigators to a SliceNavigationController, e.g., a * QmitkSliderNavigator (for Qt): * * \code * // Create the visible navigator (a slider with a spin-box) * QmitkSliderNavigator* navigator = * new QmitkSliderNavigator(parent, "slidernavigator"); * * // Connect the navigator to the slice-stepper of the * // SliceNavigationController. For initialization (position, mininal and * // maximal values) the values of the SliceNavigationController are used. * // Thus, accessing methods of a navigator is normally not necessary, since * // everything can be set via the (Qt-independent) SliceNavigationController. * // The QmitkStepperAdapter converts the Qt-signals to Qt-independent * // itk-events. * new QmitkStepperAdapter(navigator, sliceCtrl->GetSlice(), "navigatoradaptor"); * \endcode * * If you do not want that all renderwindows are updated when a new slice is * selected, you can use a specific RenderingManager, which updates only those * renderwindows that should be updated. This is sometimes useful when a 3D view * does not need to be updated when the slices in some 2D views are changed. * QmitkSliderNavigator (for Qt): * * \code * // create a specific RenderingManager * mitk::RenderingManager::Pointer myManager = mitk::RenderingManager::New(); * * // tell the RenderingManager to update only renderwindow1 and renderwindow2 * myManager->AddRenderWindow(renderwindow1); * myManager->AddRenderWindow(renderwindow2); * * // tell the SliceNavigationController of renderwindow1 and renderwindow2 * // to use the specific RenderingManager instead of the global one * renderwindow1->GetSliceNavigationController()->SetRenderingManager(myManager); * renderwindow2->GetSliceNavigationController()->SetRenderingManager(myManager); * \endcode * * \todo implement for non-evenly-timed geometry! * \ingroup NavigationControl */ class MITK_CORE_EXPORT SliceNavigationController : public BaseController { public: mitkClassMacro(SliceNavigationController,BaseController); itkNewMacro(Self); mitkNewMacro1Param(Self, const char *); /** * \brief Possible view directions, \a Original will uses * the Geometry2D instances in a SlicedGeometry3D provided * as input world geometry (by SetInputWorldGeometry). */ enum ViewDirection{Transversal, Sagittal, Frontal, Original}; /** * \brief Set the input world geometry out of which the * geometries for slicing will be created. */ void SetInputWorldGeometry(const mitk::Geometry3D* geometry); itkGetConstObjectMacro(InputWorldGeometry, mitk::Geometry3D); /** * \brief Access the created geometry */ itkGetConstObjectMacro(CreatedWorldGeometry, mitk::Geometry3D); /** * \brief Set the desired view directions * * \sa ViewDirection * \sa Update(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(ViewDirection, ViewDirection); itkGetEnumMacro(ViewDirection, ViewDirection); /** * \brief Set the default view direction * * This is used to re-initialize the view direction of the SNC to the * default value with SetViewDirectionToDefault() * * \sa ViewDirection * \sa Update(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(DefaultViewDirection, ViewDirection); itkGetEnumMacro(DefaultViewDirection, ViewDirection); virtual void SetViewDirectionToDefault(); /** * \brief Do the actual creation and send it to the connected * observers (renderers) * */ virtual void Update(); /** * \brief Extended version of Update, additionally allowing to * specify the direction/orientation of the created geometry. * */ virtual void Update(ViewDirection viewDirection, bool top = true, bool frontside = true, bool rotated = false); /** * \brief Send the created geometry to the connected * observers (renderers) * * Called by Update(). */ virtual void SendCreatedWorldGeometry(); /** * \brief Tell observers to re-read the currently selected 2D geometry * * Called by mitk::SlicesRotator during rotation. */ virtual void SendCreatedWorldGeometryUpdate(); /** * \brief Send the currently selected slice to the connected * observers (renderers) * * Called by Update(). */ virtual void SendSlice(); /** * \brief Send the currently selected time to the connected * observers (renderers) * * Called by Update(). */ virtual void SendTime(); /** * \brief Set the RenderingManager to be used * * If \a NULL, the default RenderingManager will be used. */ itkSetObjectMacro(RenderingManager, RenderingManager); mitk::RenderingManager* GetRenderingManager() const; #pragma GCC visibility push(default) itkEventMacro( UpdateEvent, itk::AnyEvent ); #pragma GCC visibility pop class MITK_CORE_EXPORT TimeSlicedGeometryEvent : public itk::AnyEvent { public: typedef TimeSlicedGeometryEvent Self; typedef itk::AnyEvent Superclass; TimeSlicedGeometryEvent( TimeSlicedGeometry* aTimeSlicedGeometry, unsigned int aPos) : m_TimeSlicedGeometry(aTimeSlicedGeometry), m_Pos(aPos) {} virtual ~TimeSlicedGeometryEvent() {} virtual const char * GetEventName() const { return "TimeSlicedGeometryEvent"; } virtual bool CheckEvent(const ::itk::EventObject* e) const { return dynamic_cast(e); } virtual ::itk::EventObject* MakeObject() const { return new Self(m_TimeSlicedGeometry, m_Pos); } TimeSlicedGeometry* GetTimeSlicedGeometry() const { return m_TimeSlicedGeometry; } unsigned int GetPos() const { return m_Pos; } private: TimeSlicedGeometry::Pointer m_TimeSlicedGeometry; unsigned int m_Pos; // TimeSlicedGeometryEvent(const Self&); void operator=(const Self&); //just hide }; mitkTimeSlicedGeometryEventMacro( GeometrySendEvent,TimeSlicedGeometryEvent ); mitkTimeSlicedGeometryEventMacro( GeometryUpdateEvent, TimeSlicedGeometryEvent ); mitkTimeSlicedGeometryEventMacro( GeometryTimeEvent, TimeSlicedGeometryEvent ); mitkTimeSlicedGeometryEventMacro( GeometrySliceEvent, TimeSlicedGeometryEvent ); template void ConnectGeometrySendEvent(T* receiver) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometry); AddObserver(GeometrySendEvent(NULL,0), eventReceptorCommand); } template void ConnectGeometryUpdateEvent(T* receiver) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::UpdateGeometry); AddObserver(GeometryUpdateEvent(NULL,0), eventReceptorCommand); } template void ConnectGeometrySliceEvent(T* receiver, bool connectSendEvent=true) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometrySlice); AddObserver(GeometrySliceEvent(NULL,0), eventReceptorCommand); if(connectSendEvent) ConnectGeometrySendEvent(receiver); } template void ConnectGeometryTimeEvent(T* receiver, bool connectSendEvent=true) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometryTime); AddObserver(GeometryTimeEvent(NULL,0), eventReceptorCommand); if(connectSendEvent) ConnectGeometrySendEvent(receiver); } template void ConnectGeometryEvents(T* receiver) { //connect sendEvent only once ConnectGeometrySliceEvent(receiver, false); ConnectGeometryTimeEvent(receiver); } Message<> crosshairPositionEvent; /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface * \warning not implemented */ virtual void SetGeometry(const itk::EventObject & geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ virtual void SetGeometrySlice(const itk::EventObject & geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ virtual void SetGeometryTime(const itk::EventObject & geometryTimeEvent); /** \brief Positions the SNC according to the specified point */ void SelectSliceByPoint( const mitk::Point3D &point ); /** \brief Returns the TimeSlicedGeometry created by the SNC. */ const mitk::TimeSlicedGeometry *GetCreatedWorldGeometry(); /** \brief Returns the Geometry3D of the currently selected time step. */ const mitk::Geometry3D *GetCurrentGeometry3D(); /** \brief Returns the currently selected Plane in the current * Geometry3D (if existent). */ const mitk::PlaneGeometry *GetCurrentPlaneGeometry(); /** \brief Sets the BaseRenderer associated with this SNC (if any). While * the BaseRenderer is not directly used by SNC, this is a convenience * method to enable BaseRenderer access via the SNC. */ void SetRenderer( BaseRenderer *renderer ); /** \brief Gets the BaseRenderer associated with this SNC (if any). While * the BaseRenderer is not directly used by SNC, this is a convenience * method to enable BaseRenderer access via the SNC. Returns NULL if no * BaseRenderer has been specified*/ BaseRenderer *GetRenderer() const; /** \brief Re-orients the slice stack to include the plane specified by * the given point an normal vector. */ void ReorientSlices( const mitk::Point3D &point, const mitk::Vector3D &normal ); - virtual bool ExecuteAction( Action* action, mitk::StateEvent const* stateEvent); void ExecuteOperation(Operation* operation); /** * \brief Feature option to lock planes during mouse interaction. * This option flag disables the mouse event which causes the center * cross to move near by. */ itkSetMacro(SliceLocked, bool); itkGetMacro(SliceLocked, bool); itkBooleanMacro(SliceLocked); /** * \brief Feature option to lock slice rotation. * * This option flag disables separately the rotation of a slice which is * implemented in mitkSliceRotator. */ itkSetMacro(SliceRotationLocked, bool); itkGetMacro(SliceRotationLocked, bool); itkBooleanMacro(SliceRotationLocked); /** * \brief Adjusts the numerical range of the slice stepper according to * the current geometry orientation of this SNC's SlicedGeometry. */ void AdjustSliceStepperRange(); protected: SliceNavigationController(const char * type = NULL); virtual ~SliceNavigationController(); template static void buildstring( mitkIpPicDescriptor *pic, itk::Point p, std::string &s, T = 0) { std::string value; std::stringstream stream; stream.imbue(std::locale::classic()); stream<=0 && p[1] >=0 && p[2]>=0) && (unsigned int)p[0] < pic->n[0] && (unsigned int)p[1] < pic->n[1] && (unsigned int)p[2] < pic->n[2] ) { if(pic->bpe!=24) { stream<<(((T*) pic->data)[ p[0] + p[1]*pic->n[0] + p[2]*pic->n[0]*pic->n[1] ]); } else { stream<<(((T*) pic->data)[p[0]*3 + 0 + p[1]*pic->n[0]*3 + p[2]*pic->n[0]*pic->n[1]*3 ]); stream<<(((T*) pic->data)[p[0]*3 + 1 + p[1]*pic->n[0]*3 + p[2]*pic->n[0]*pic->n[1]*3 ]); stream<<(((T*) pic->data)[p[0]*3 + 2 + p[1]*pic->n[0]*3 + p[2]*pic->n[0]*pic->n[1]*3 ]); } s = stream.str(); } else { s+= "point out of data"; } }; mitk::Geometry3D::ConstPointer m_InputWorldGeometry; mitk::Geometry3D::Pointer m_ExtendedInputWorldGeometry; mitk::TimeSlicedGeometry::Pointer m_CreatedWorldGeometry; ViewDirection m_ViewDirection; ViewDirection m_DefaultViewDirection; mitk::RenderingManager::Pointer m_RenderingManager; mitk::BaseRenderer *m_Renderer; itkSetMacro(Top, bool); itkGetMacro(Top, bool); itkBooleanMacro(Top); itkSetMacro(FrontSide, bool); itkGetMacro(FrontSide, bool); itkBooleanMacro(FrontSide); itkSetMacro(Rotated, bool); itkGetMacro(Rotated, bool); itkBooleanMacro(Rotated); bool m_Top; bool m_FrontSide; bool m_Rotated; bool m_BlockUpdate; bool m_SliceLocked; bool m_SliceRotationLocked; unsigned int m_OldPos; }; } // namespace mitk #endif /* SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F */ diff --git a/Core/Code/DataManagement/mitkGeometry3D.cpp b/Core/Code/DataManagement/mitkGeometry3D.cpp index 7fb073bd35..7cbd89fd0e 100644 --- a/Core/Code/DataManagement/mitkGeometry3D.cpp +++ b/Core/Code/DataManagement/mitkGeometry3D.cpp @@ -1,732 +1,741 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkGeometry3D.h" #include "mitkMatrixConvert.h" #include "mitkRotationOperation.h" +#include "mitkRestorePlanePositionOperation.h" #include "mitkPointOperation.h" #include "mitkInteractionConst.h" //#include "mitkStatusBar.h" #include #include // Standard constructor for the New() macro. Sets the geometry to 3 dimensions mitk::Geometry3D::Geometry3D() : m_ParametricBoundingBox(NULL), m_ImageGeometry(false), m_Valid(true), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0) { FillVector3D(m_FloatSpacing, 1,1,1); m_VtkMatrix = vtkMatrix4x4::New(); m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New(); m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix); Initialize(); } mitk::Geometry3D::Geometry3D(const Geometry3D& other) : Superclass(), m_ParametricBoundingBox(other.m_ParametricBoundingBox),m_TimeBounds(other.m_TimeBounds), m_ImageGeometry(other.m_ImageGeometry), m_Valid(other.m_Valid), m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_RotationQuaternion( other.m_RotationQuaternion ) , m_Origin(other.m_Origin) { // AffineGeometryFrame SetBounds(other.GetBounds()); //SetIndexToObjectTransform(other.GetIndexToObjectTransform()); //SetObjectToNodeTransform(other.GetObjectToNodeTransform()); //SetIndexToWorldTransform(other.GetIndexToWorldTransform()); // this is not used in AffineGeometryFrame of ITK, thus there are not Get and Set methods // m_IndexToNodeTransform = other.m_IndexToNodeTransform; // m_InvertedTransform = TransformType::New(); // m_InvertedTransform = TransformType::New(); // m_InvertedTransform->DeepCopy(other.m_InvertedTransform); m_VtkMatrix = vtkMatrix4x4::New(); m_VtkMatrix->DeepCopy(other.m_VtkMatrix); if (other.m_ParametricBoundingBox.IsNotNull()) { m_ParametricBoundingBox = other.m_ParametricBoundingBox->DeepCopy(); } FillVector3D(m_FloatSpacing,other.m_FloatSpacing[0],other.m_FloatSpacing[1],other.m_FloatSpacing[2]); m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New(); m_VtkIndexToWorldTransform->DeepCopy(other.m_VtkIndexToWorldTransform); m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix); other.InitializeGeometry(this); } mitk::Geometry3D::~Geometry3D() { m_VtkMatrix->Delete(); m_VtkIndexToWorldTransform->Delete(); } static void CopySpacingFromTransform(mitk::AffineTransform3D* transform, mitk::Vector3D& spacing, float floatSpacing[3]) { mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = transform->GetMatrix().GetVnlMatrix(); spacing[0]=vnlmatrix.get_column(0).magnitude(); spacing[1]=vnlmatrix.get_column(1).magnitude(); spacing[2]=vnlmatrix.get_column(2).magnitude(); floatSpacing[0]=spacing[0]; floatSpacing[1]=spacing[1]; floatSpacing[2]=spacing[2]; } void mitk::Geometry3D::Initialize() { float b[6] = {0,1,0,1,0,1}; SetFloatBounds(b); m_IndexToObjectTransform = TransformType::New(); m_ObjectToNodeTransform = TransformType::New(); if(m_IndexToWorldTransform.IsNull()) m_IndexToWorldTransform = TransformType::New(); else m_IndexToWorldTransform->SetIdentity(); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); m_VtkMatrix->Identity(); m_TimeBounds[0]=ScalarTypeNumericTraits::NonpositiveMin(); m_TimeBounds[1]=ScalarTypeNumericTraits::max(); m_FrameOfReferenceID = 0; m_ImageGeometry = false; } void mitk::Geometry3D::TransferItkToVtkTransform() { // copy m_IndexToWorldTransform into m_VtkIndexToWorldTransform TransferItkTransformToVtkMatrix(m_IndexToWorldTransform.GetPointer(), m_VtkMatrix); m_VtkIndexToWorldTransform->Modified(); } void mitk::Geometry3D::TransferVtkToItkTransform() { TransferVtkMatrixToItkTransform(m_VtkMatrix, m_IndexToWorldTransform.GetPointer()); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); } void mitk::Geometry3D::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4* vtkmatrix) { m_VtkMatrix->DeepCopy(vtkmatrix); TransferVtkToItkTransform(); } void mitk::Geometry3D::SetTimeBounds(const TimeBounds& timebounds) { if(m_TimeBounds != timebounds) { m_TimeBounds = timebounds; Modified(); } } void mitk::Geometry3D::SetFloatBounds(const float bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const float *input = bounds; int i=0; for(mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6 ;++i) *it++ = (mitk::ScalarType)*input++; SetBoundsArray(b, m_BoundingBox); } void mitk::Geometry3D::SetFloatBounds(const double bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const double *input = bounds; int i=0; for(mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6 ;++i) *it++ = (mitk::ScalarType)*input++; SetBoundsArray(b, m_BoundingBox); } void mitk::Geometry3D::SetParametricBounds(const BoundingBox::BoundsArrayType& bounds) { SetBoundsArray(bounds, m_ParametricBoundingBox); } void mitk::Geometry3D::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const { BackTransform(pt_mm, pt_units); } void mitk::Geometry3D::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const { pt_mm = m_IndexToWorldTransform->TransformPoint(pt_units); } void mitk::Geometry3D::WorldToIndex(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { MITK_WARN<<"Warning! Call of the deprecated function Geometry3D::WorldToIndex(point, vec, vec). Use Geometry3D::WorldToIndex(vec, vec) instead!"; //BackTransform(atPt3d_mm, vec_mm, vec_units); this->WorldToIndex(vec_mm, vec_units); } void mitk::Geometry3D::WorldToIndex( const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { BackTransform( vec_mm, vec_units); } void mitk::Geometry3D::IndexToWorld(const mitk::Point3D &/*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { MITK_WARN<<"Warning! Call of the deprecated function Geometry3D::IndexToWorld(point, vec, vec). Use Geometry3D::IndexToWorld(vec, vec) instead!"; //vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); this->IndexToWorld(vec_units, vec_mm); } void mitk::Geometry3D::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); } void mitk::Geometry3D::SetIndexToWorldTransform(mitk::AffineTransform3D* transform) { if(m_IndexToWorldTransform.GetPointer() != transform) { Superclass::SetIndexToWorldTransform(transform); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); TransferItkToVtkTransform(); Modified(); } } mitk::AffineGeometryFrame3D::Pointer mitk::Geometry3D::Clone() const { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } /* void mitk::Geometry3D::InitializeGeometry(Geometry3D * newGeometry) const { Superclass::InitializeGeometry(newGeometry); newGeometry->SetTimeBounds(m_TimeBounds); //newGeometry->GetVtkTransform()->SetMatrix(m_VtkIndexToWorldTransform->GetMatrix()); IW //newGeometry->TransferVtkToItkTransform(); //MH newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); newGeometry->m_ImageGeometry = m_ImageGeometry; } */ void mitk::Geometry3D::SetExtentInMM(int direction, ScalarType extentInMM) { ScalarType len = GetExtentInMM(direction); if(fabs(len - extentInMM)>=mitk::eps) { AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); if(len>extentInMM) vnlmatrix.set_column(direction, vnlmatrix.get_column(direction)/len*extentInMM); else vnlmatrix.set_column(direction, vnlmatrix.get_column(direction)*extentInMM/len); Matrix3D matrix; matrix = vnlmatrix; m_IndexToWorldTransform->SetMatrix(matrix); Modified(); } } mitk::BoundingBox::Pointer mitk::Geometry3D::CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D* transform) const { mitk::BoundingBox::PointsContainer::Pointer pointscontainer=mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid=0; unsigned char i; if(transform!=NULL) { mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); transform->GetInverse(inverse); for(i=0; i<8; ++i) pointscontainer->InsertElement( pointid++, inverse->TransformPoint( GetCornerPoint(i) )); } else { for(i=0; i<8; ++i) pointscontainer->InsertElement( pointid++, GetCornerPoint(i) ); } mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } #include void mitk::Geometry3D::ExecuteOperation(Operation* operation) { vtkTransform *vtktransform = vtkTransform::New(); vtktransform->SetMatrix(m_VtkMatrix); switch (operation->GetOperationType()) { case OpNOTHING: break; case OpMOVE: { mitk::PointOperation *pointOp = dynamic_cast(operation); if (pointOp == NULL) { //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); return; } mitk::Point3D newPos = pointOp->GetPoint(); ScalarType data[3]; vtktransform->GetPosition(data); vtktransform->PostMultiply(); vtktransform->Translate(newPos[0], newPos[1], newPos[2]); vtktransform->PreMultiply(); break; } case OpSCALE: { mitk::PointOperation *pointOp = dynamic_cast(operation); if (pointOp == NULL) { //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); return; } mitk::Point3D newScale = pointOp->GetPoint(); ScalarType data[3]; /* calculate new scale: newscale = oldscale * (oldscale + scaletoadd)/oldscale */ data[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude()); data[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude()); data[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude()); mitk::Point3D center = const_cast(m_BoundingBox.GetPointer())->GetCenter(); ScalarType pos[3]; vtktransform->GetPosition(pos); vtktransform->PostMultiply(); vtktransform->Translate(-pos[0], -pos[1], -pos[2]); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->PreMultiply(); vtktransform->Scale(data[0], data[1], data[2]); vtktransform->PostMultiply(); vtktransform->Translate(+center[0], +center[1], +center[2]); vtktransform->Translate(pos[0], pos[1], pos[2]); vtktransform->PreMultiply(); break; } case OpROTATE: { mitk::RotationOperation *rotateOp = dynamic_cast(operation); if (rotateOp == NULL) { //mitk::StatusBar::GetInstance()->DisplayText("received wrong type of operation!See mitkAffineInteractor.cpp", 10000); return; } Vector3D rotationVector = rotateOp->GetVectorOfRotation(); Point3D center = rotateOp->GetCenterOfRotation(); ScalarType angle = rotateOp->GetAngleOfRotation(); vtktransform->PostMultiply(); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->RotateWXYZ(angle, rotationVector[0], rotationVector[1], rotationVector[2]); vtktransform->Translate(center[0], center[1], center[2]); vtktransform->PreMultiply(); break; } + case OpRESTOREPLANEPOSITION: + { + //Copy necessary to avoid vtk warning + vtkMatrix4x4* matrix = vtkMatrix4x4::New(); + TransferItkTransformToVtkMatrix(dynamic_cast(operation)->GetTransform().GetPointer(), matrix); + vtktransform->SetMatrix(matrix); + break; + } default: vtktransform->Delete(); return; } m_VtkMatrix->DeepCopy(vtktransform->GetMatrix()); TransferVtkToItkTransform(); Modified(); vtktransform->Delete(); } void mitk::Geometry3D::BackTransform(const mitk::Point3D &in, mitk::Point3D& out) const { ScalarType temp[3]; unsigned int i, j; const TransformType::OffsetType& offset = m_IndexToWorldTransform->GetOffset(); // Remove offset for (j = 0; j < 3; j++) { temp[j] = in[j] - offset[j]; } // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) { m_InvertedTransform = TransformType::New(); if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) { itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); } m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); } // Check for valid matrix inversion const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); if(inverse.GetVnlMatrix().has_nans()) { itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse ); } // Transform point for (i = 0; i < 3; i++) { out[i] = 0.0; for (j = 0; j < 3; j++) { out[i] += inverse[i][j]*temp[j]; } } } void mitk::Geometry3D::BackTransform(const mitk::Point3D &/*at*/, const mitk::Vector3D &in, mitk::Vector3D& out) const { MITK_INFO<<"Warning! Call of the deprecated function Geometry3D::BackTransform(point, vec, vec). Use Geometry3D::BackTransform(vec, vec) instead!"; //// Get WorldToIndex transform //if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) //{ // m_InvertedTransform = TransformType::New(); // if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) // { // itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); // } // m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); //} //// Check for valid matrix inversion //const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); //if(inverse.GetVnlMatrix().has_nans()) //{ // itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl // << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl // << inverse ); //} //// Transform vector //for (unsigned int i = 0; i < 3; i++) //{ // out[i] = 0.0; // for (unsigned int j = 0; j < 3; j++) // { // out[i] += inverse[i][j]*in[j]; // } //} this->BackTransform(in, out); } void mitk::Geometry3D::BackTransform(const mitk::Vector3D& in, mitk::Vector3D& out) const { // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != m_IndexToWorldTransform->GetMTime()) { m_InvertedTransform = TransformType::New(); if (!m_IndexToWorldTransform->GetInverse( m_InvertedTransform.GetPointer() )) { itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed." ); } m_IndexToWorldTransformLastModified = m_IndexToWorldTransform->GetMTime(); } // Check for valid matrix inversion const TransformType::MatrixType& inverse = m_InvertedTransform->GetMatrix(); if(inverse.GetVnlMatrix().has_nans()) { itkExceptionMacro( "Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl << m_IndexToWorldTransform->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse ); } // Transform vector for (unsigned int i = 0; i < 3; i++) { out[i] = 0.0; for (unsigned int j = 0; j < 3; j++) { out[i] += inverse[i][j]*in[j]; } } } const float* mitk::Geometry3D::GetFloatSpacing() const { return m_FloatSpacing; } void mitk::Geometry3D::SetSpacing(const mitk::Vector3D& aSpacing) { if(mitk::Equal(m_Spacing, aSpacing) == false) { assert(aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0); m_Spacing = aSpacing; AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); mitk::VnlVector col; col = vnlmatrix.get_column(0); col.normalize(); col*=aSpacing[0]; vnlmatrix.set_column(0, col); col = vnlmatrix.get_column(1); col.normalize(); col*=aSpacing[1]; vnlmatrix.set_column(1, col); col = vnlmatrix.get_column(2); col.normalize(); col*=aSpacing[2]; vnlmatrix.set_column(2, col); Matrix3D matrix; matrix = vnlmatrix; AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(matrix); transform->SetOffset(m_IndexToWorldTransform->GetOffset()); SetIndexToWorldTransform(transform.GetPointer()); itk2vtk(m_Spacing, m_FloatSpacing); } } void mitk::Geometry3D::SetOrigin(const Point3D & origin) { if(origin!=GetOrigin()) { m_Origin = origin; m_IndexToWorldTransform->SetOffset(m_Origin.GetVectorFromOrigin()); Modified(); TransferItkToVtkTransform(); } } void mitk::Geometry3D::Translate(const Vector3D & vector) { if((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) { m_IndexToWorldTransform->SetOffset(m_IndexToWorldTransform->GetOffset()+vector); TransferItkToVtkTransform(); Modified(); } } void mitk::Geometry3D::SetIdentity() { m_IndexToWorldTransform->SetIdentity(); m_Origin.Fill(0); Modified(); TransferItkToVtkTransform(); } void mitk::Geometry3D::Compose( const mitk::AffineGeometryFrame3D::TransformType * other, bool pre ) { m_IndexToWorldTransform->Compose(other, pre); CopySpacingFromTransform(m_IndexToWorldTransform, m_Spacing, m_FloatSpacing); vtk2itk(m_IndexToWorldTransform->GetOffset(), m_Origin); Modified(); TransferItkToVtkTransform(); } void mitk::Geometry3D::Compose( const vtkMatrix4x4 * vtkmatrix, bool pre ) { mitk::AffineGeometryFrame3D::TransformType::Pointer itkTransform = mitk::AffineGeometryFrame3D::TransformType::New(); TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); Compose(itkTransform, pre); } const char* mitk::Geometry3D::GetTransformAsString( TransformType* transformType ) { static char buffer[255]; for ( int j=0; j<255; j++) buffer[j] = '\0'; ostrstream out( buffer, 255 ); out << '['; for( int i=0; i<3; ++i ) { out << '['; for( int j=0; j<3; ++j ) out << transformType->GetMatrix().GetVnlMatrix().get(i, j) << ' '; out << ']'; } out << "]["; for( int i=0; i<3; ++i ) out << transformType->GetOffset()[i] << ' '; out << "]\0"; return buffer; } void mitk::Geometry3D::PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; if(m_IndexToWorldTransform.IsNull()) os << "NULL" << std::endl; else { // from itk::MatrixOffsetTransformBase unsigned int i, j; os << std::endl; os << indent << "Matrix: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << m_IndexToWorldTransform->GetMatrix()[i][j] << " "; } os << std::endl; } os << indent << "Offset: " << m_IndexToWorldTransform->GetOffset() << std::endl; os << indent << "Center: " << m_IndexToWorldTransform->GetCenter() << std::endl; os << indent << "Translation: " << m_IndexToWorldTransform->GetTranslation() << std::endl; os << indent << "Inverse: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << m_IndexToWorldTransform->GetInverseMatrix()[i][j] << " "; } os << std::endl; } // from itk::ScalableAffineTransform os << indent << "Scale : "; for (i = 0; i < 3; i++) { os << m_IndexToWorldTransform->GetScale()[i] << " "; } os << std::endl; } os << indent << " BoundingBox: "; if(m_BoundingBox.IsNull()) os << "NULL" << std::endl; else { os << indent << "( "; for (unsigned int i=0; i<3; i++) { os << m_BoundingBox->GetBounds()[2*i] << "," << m_BoundingBox->GetBounds()[2*i+1] << " "; } os << " )" << std::endl; } os << indent << " Origin: " << m_Origin << std::endl; os << indent << " ImageGeometry: " << m_ImageGeometry << std::endl; os << indent << " Spacing: " << m_Spacing << std::endl; os << indent << " TimeBounds: " << m_TimeBounds << std::endl; } mitk::Point3D mitk::Geometry3D::GetCornerPoint(int id) const { assert(id >= 0); assert(m_BoundingBox.IsNotNull()); BoundingBox::BoundsArrayType bounds = m_BoundingBox->GetBounds(); Point3D cornerpoint; switch(id) { case 0: FillVector3D(cornerpoint, bounds[0],bounds[2],bounds[4]); break; case 1: FillVector3D(cornerpoint, bounds[0],bounds[2],bounds[5]); break; case 2: FillVector3D(cornerpoint, bounds[0],bounds[3],bounds[4]); break; case 3: FillVector3D(cornerpoint, bounds[0],bounds[3],bounds[5]); break; case 4: FillVector3D(cornerpoint, bounds[1],bounds[2],bounds[4]); break; case 5: FillVector3D(cornerpoint, bounds[1],bounds[2],bounds[5]); break; case 6: FillVector3D(cornerpoint, bounds[1],bounds[3],bounds[4]); break; case 7: FillVector3D(cornerpoint, bounds[1],bounds[3],bounds[5]); break; default: { itkExceptionMacro(<<"A cube only has 8 corners. These are labeled 0-7."); return NULL; } } if(m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0]-0.5, cornerpoint[1]-0.5, cornerpoint[2]-0.5); } return m_IndexToWorldTransform->TransformPoint(cornerpoint); } mitk::Point3D mitk::Geometry3D::GetCornerPoint(bool xFront, bool yFront, bool zFront) const { assert(m_BoundingBox.IsNotNull()); BoundingBox::BoundsArrayType bounds = m_BoundingBox->GetBounds(); Point3D cornerpoint; cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); if(m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0]-0.5, cornerpoint[1]-0.5, cornerpoint[2]-0.5); } return m_IndexToWorldTransform->TransformPoint(cornerpoint); } void mitk::Geometry3D::ResetSubTransforms() { } void mitk::Geometry3D::ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ) { // If Geometry is switched to ImageGeometry, you have to put an offset to the origin, because // imageGeometries origins are pixel-center-based // ... and remove the offset, if you switch an imageGeometry back to a normal geometry // For more information please see the Geometry documentation page if(m_ImageGeometry == isAnImageGeometry) return; const BoundingBox::BoundsArrayType& boundsarray = this->GetBoundingBox()->GetBounds(); Point3D originIndex; FillVector3D(originIndex, boundsarray[0], boundsarray[2], boundsarray[4]); if(isAnImageGeometry == true) FillVector3D( originIndex, originIndex[0] + 0.5, originIndex[1] + 0.5, originIndex[2] + 0.5 ); else FillVector3D( originIndex, originIndex[0] - 0.5, originIndex[1] - 0.5, originIndex[2] - 0.5 ); Point3D originWorld; originWorld = GetIndexToWorldTransform() ->TransformPoint( originIndex ); // instead could as well call IndexToWorld(originIndex,originWorld); SetOrigin(originWorld); this->SetImageGeometry(isAnImageGeometry); } diff --git a/Core/Code/DataManagement/mitkPlaneGeometry.cpp b/Core/Code/DataManagement/mitkPlaneGeometry.cpp index c6241f734f..ad0d1184c3 100644 --- a/Core/Code/DataManagement/mitkPlaneGeometry.cpp +++ b/Core/Code/DataManagement/mitkPlaneGeometry.cpp @@ -1,755 +1,778 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkPlaneGeometry.h" #include "mitkPlaneOperation.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include #include namespace mitk { mitk::PlaneGeometry::PlaneGeometry() { Initialize(); } mitk::PlaneGeometry::~PlaneGeometry() { } void PlaneGeometry::Initialize() { Superclass::Initialize(); } void PlaneGeometry::EnsurePerpendicularNormal(mitk::AffineTransform3D *transform) { //ensure row(2) of transform to be perpendicular to plane, keep length. VnlVector normal = vnl_cross_3d( transform->GetMatrix().GetVnlMatrix().get_column(0), transform->GetMatrix().GetVnlMatrix().get_column(1) ); normal.normalize(); ScalarType len = transform->GetMatrix() .GetVnlMatrix().get_column(2).two_norm(); if (len==0) len = 1; normal*=len; Matrix3D matrix = transform->GetMatrix(); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); } void PlaneGeometry::SetIndexToWorldTransform(mitk::AffineTransform3D *transform) { EnsurePerpendicularNormal(transform); Superclass::SetIndexToWorldTransform(transform); } void PlaneGeometry::SetBounds(const BoundingBox::BoundsArrayType &bounds) { //currently the unit rectangle must be starting at the origin [0,0] assert(bounds[0]==0); assert(bounds[2]==0); //the unit rectangle must be two-dimensional assert(bounds[1]>0); assert(bounds[3]>0); Superclass::SetBounds(bounds); } void PlaneGeometry::IndexToWorld( const Point2D &pt_units, Point2D &pt_mm ) const { pt_mm[0]=m_ScaleFactorMMPerUnitX*pt_units[0]; pt_mm[1]=m_ScaleFactorMMPerUnitY*pt_units[1]; } void PlaneGeometry::WorldToIndex( const Point2D &pt_mm, Point2D &pt_units ) const { pt_units[0]=pt_mm[0]*(1.0/m_ScaleFactorMMPerUnitX); pt_units[1]=pt_mm[1]*(1.0/m_ScaleFactorMMPerUnitY); } void PlaneGeometry::IndexToWorld( const Point2D &atPt2d_units, const Vector2D &vec_units, Vector2D &vec_mm) const { MITK_WARN<<"Warning! Call of the deprecated function PlaneGeometry::IndexToWorld(point, vec, vec). Use PlaneGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void PlaneGeometry::IndexToWorld(const Vector2D &vec_units, Vector2D &vec_mm) const { vec_mm[0] = m_ScaleFactorMMPerUnitX * vec_units[0]; vec_mm[1] = m_ScaleFactorMMPerUnitY * vec_units[1]; } void PlaneGeometry::WorldToIndex( const Point2D &atPt2d_mm, const Vector2D &vec_mm, Vector2D &vec_units) const { MITK_WARN<<"Warning! Call of the deprecated function PlaneGeometry::WorldToIndex(point, vec, vec). Use PlaneGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void PlaneGeometry::WorldToIndex( const Vector2D &vec_mm, Vector2D &vec_units) const { vec_units[0] = vec_mm[0] * ( 1.0 / m_ScaleFactorMMPerUnitX ); vec_units[1] = vec_mm[1] * ( 1.0 / m_ScaleFactorMMPerUnitY ); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const Vector3D & spacing, PlaneGeometry::PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated ) { AffineTransform3D::Pointer transform; transform = AffineTransform3D::New(); AffineTransform3D::MatrixType matrix; AffineTransform3D::MatrixType::InternalMatrixType &vnlmatrix = matrix.GetVnlMatrix(); vnlmatrix.set_identity(); vnlmatrix(0,0) = spacing[0]; vnlmatrix(1,1) = spacing[1]; vnlmatrix(2,2) = spacing[2]; transform->SetIdentity(); transform->SetMatrix(matrix); InitializeStandardPlane(width, height, transform.GetPointer(), planeorientation, zPosition, frontside, rotated); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const AffineTransform3D* transform, PlaneGeometry::PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated ) { Superclass::Initialize(); //construct standard view Point3D origin; VnlVector rightDV(3), bottomDV(3); origin.Fill(0); int normalDirection; switch(planeorientation) { case Transversal: if(frontside) { if(rotated==false) { FillVector3D(origin, 0, 0, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else { FillVector3D(origin, width, height, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } else { if(rotated==false) { FillVector3D(origin, width, 0, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else { FillVector3D(origin, 0, height, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } normalDirection = 2; break; case Frontal: if(frontside) { if(rotated==false) { FillVector3D(origin, 0, zPosition, 0); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else { FillVector3D(origin, width, zPosition, height); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if(rotated==false) { FillVector3D(origin, width, zPosition, 0); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else { FillVector3D(origin, 0, zPosition, height); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 1; break; case Sagittal: if(frontside) { if(rotated==false) { FillVector3D(origin, zPosition, 0, 0); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, 1); } else { FillVector3D(origin, zPosition, width, height); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if(rotated==false) { FillVector3D(origin, zPosition, width, 0); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, 1); } else { FillVector3D(origin, zPosition, 0, height); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 0; break; default: itkExceptionMacro("unknown PlaneOrientation"); } if ( transform != NULL ) { origin = transform->TransformPoint( origin ); rightDV = transform->TransformVector( rightDV ); bottomDV = transform->TransformVector( bottomDV ); } ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); if ( transform == NULL ) { this->SetMatrixByVectors( rightDV, bottomDV ); } else { this->SetMatrixByVectors( rightDV, bottomDV, transform->GetMatrix().GetVnlMatrix() .get_column(normalDirection).magnitude() ); } this->SetOrigin(origin); } void PlaneGeometry::InitializeStandardPlane( const Geometry3D *geometry3D, PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated ) { this->SetReferenceGeometry( const_cast< Geometry3D * >( geometry3D ) ); ScalarType width, height; const BoundingBox::BoundsArrayType& boundsarray = geometry3D->GetBoundingBox()->GetBounds(); Vector3D originVector; FillVector3D(originVector, boundsarray[0], boundsarray[2], boundsarray[4]); - MITK_DEBUG << originVector; if(geometry3D->GetImageGeometry()) { FillVector3D( originVector, originVector[0] - 0.5, originVector[1] - 0.5, originVector[2] - 0.5 ); } switch(planeorientation) { case Transversal: width = geometry3D->GetExtent(0); height = geometry3D->GetExtent(1); break; case Frontal: width = geometry3D->GetExtent(0); height = geometry3D->GetExtent(2); break; case Sagittal: width = geometry3D->GetExtent(1); height = geometry3D->GetExtent(2); break; default: itkExceptionMacro("unknown PlaneOrientation"); } InitializeStandardPlane( width, height, geometry3D->GetIndexToWorldTransform(), planeorientation, zPosition, frontside, rotated ); ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); Point3D origin; originVector = geometry3D->GetIndexToWorldTransform() ->TransformVector( originVector ); origin = GetOrigin() + originVector; SetOrigin(origin); } void PlaneGeometry::InitializeStandardPlane( const Geometry3D *geometry3D, bool top, PlaneOrientation planeorientation, bool frontside, bool rotated ) { ScalarType zPosition; switch(planeorientation) { case Transversal: zPosition = (top ? 0.5 : geometry3D->GetExtent(2)-1+0.5); break; case Frontal: zPosition = (top ? 0.5 : geometry3D->GetExtent(1)-1+0.5); break; case Sagittal: zPosition = (top ? 0.5 : geometry3D->GetExtent(0)-1+0.5); break; default: itkExceptionMacro("unknown PlaneOrientation"); } InitializeStandardPlane( geometry3D, planeorientation, zPosition, frontside, rotated ); } void PlaneGeometry::InitializeStandardPlane( const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing ) { InitializeStandardPlane( rightVector.Get_vnl_vector(), downVector.Get_vnl_vector(), spacing ); } void PlaneGeometry::InitializeStandardPlane( const VnlVector& rightVector, const VnlVector &downVector, const Vector3D *spacing ) { ScalarType width = rightVector.magnitude(); ScalarType height = downVector.magnitude(); InitializeStandardPlane( width, height, rightVector, downVector, spacing ); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing ) { InitializeStandardPlane( width, height, rightVector.Get_vnl_vector(), downVector.Get_vnl_vector(), spacing ); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing ) { assert(width > 0); assert(height > 0); VnlVector rightDV = rightVector; rightDV.normalize(); VnlVector downDV = downVector; downDV.normalize(); VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); if(spacing!=NULL) { rightDV *= (*spacing)[0]; downDV *= (*spacing)[1]; normal *= (*spacing)[2]; } AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightDV); matrix.GetVnlMatrix().set_column(1, downDV); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(m_IndexToWorldTransform->GetOffset()); ScalarType bounds[6] = { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); this->SetIndexToWorldTransform( transform ); } void PlaneGeometry::InitializePlane( const Point3D &origin, const Vector3D &normal ) { VnlVector rightVectorVnl(3), downVectorVnl; if( Equal( normal[1], 0.0f ) == false ) { FillVector3D( rightVectorVnl, 1.0f, -normal[0]/normal[1], 0.0f ); rightVectorVnl.normalize(); } else { FillVector3D( rightVectorVnl, 0.0f, 1.0f, 0.0f ); } downVectorVnl = vnl_cross_3d( normal.Get_vnl_vector(), rightVectorVnl ); downVectorVnl.normalize(); InitializeStandardPlane( rightVectorVnl, downVectorVnl ); SetOrigin(origin); } void PlaneGeometry::SetMatrixByVectors( const VnlVector &rightVector, const VnlVector &downVector, ScalarType thickness ) { VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); normal *= thickness; AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightVector); matrix.GetVnlMatrix().set_column(1, downVector); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(m_IndexToWorldTransform->GetOffset()); SetIndexToWorldTransform(transform); } Vector3D PlaneGeometry::GetNormal() const { Vector3D frontToBack; frontToBack.Set_vnl_vector( m_IndexToWorldTransform ->GetMatrix().GetVnlMatrix().get_column(2) ); return frontToBack; } VnlVector PlaneGeometry::GetNormalVnl() const { return m_IndexToWorldTransform ->GetMatrix().GetVnlMatrix().get_column(2); } ScalarType PlaneGeometry::DistanceFromPlane( const Point3D &pt3d_mm ) const { return fabs(SignedDistance( pt3d_mm )); } ScalarType PlaneGeometry::SignedDistance( const Point3D &pt3d_mm ) const { return SignedDistanceFromPlane(pt3d_mm); } bool PlaneGeometry::IsAbove( const Point3D &pt3d_mm ) const { return SignedDistanceFromPlane(pt3d_mm) > 0; } bool PlaneGeometry::IntersectionLine( const PlaneGeometry* plane, Line3D& crossline ) const { Vector3D normal = this->GetNormal(); normal.Normalize(); Vector3D planeNormal = plane->GetNormal(); planeNormal.Normalize(); Vector3D direction = itk::CrossProduct( normal, planeNormal ); if ( direction.GetSquaredNorm() < eps ) return false; crossline.SetDirection( direction ); double N1dN2 = normal * planeNormal; double determinant = 1.0 - N1dN2 * N1dN2; Vector3D origin = this->GetOrigin().GetVectorFromOrigin(); Vector3D planeOrigin = plane->GetOrigin().GetVectorFromOrigin(); double d1 = normal * origin; double d2 = planeNormal * planeOrigin; double c1 = ( d1 - d2 * N1dN2 ) / determinant; double c2 = ( d2 - d1 * N1dN2 ) / determinant; Vector3D p = normal * c1 + planeNormal * c2; crossline.GetPoint().Get_vnl_vector() = p.Get_vnl_vector(); return true; } unsigned int PlaneGeometry::IntersectWithPlane2D( const PlaneGeometry* plane, Point2D& lineFrom, Point2D &lineTo ) const { Line3D crossline; if ( this->IntersectionLine( plane, crossline ) == false ) return 0; Point2D point2; Vector2D direction2; this->Map( crossline.GetPoint(), point2 ); this->Map( crossline.GetPoint(), crossline.GetDirection(), direction2 ); return Line3D::RectangleLineIntersection( 0, 0, GetExtentInMM(0), GetExtentInMM(1), point2, direction2, lineFrom, lineTo ); } double PlaneGeometry::Angle( const PlaneGeometry *plane ) const { return angle(plane->GetMatrixColumn(2), GetMatrixColumn(2)); } double PlaneGeometry::Angle( const Line3D &line ) const { return vnl_math::pi_over_2 - angle( line.GetDirection().Get_vnl_vector(), GetMatrixColumn(2) ); } bool PlaneGeometry::IntersectionPoint( const Line3D &line, Point3D &intersectionPoint ) const { Vector3D planeNormal = this->GetNormal(); planeNormal.Normalize(); Vector3D lineDirection = line.GetDirection(); lineDirection.Normalize(); double t = planeNormal * lineDirection; if ( fabs( t ) < eps ) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = ( planeNormal * diff ) / t; intersectionPoint = line.GetPoint() + lineDirection * t; return true; } bool PlaneGeometry::IntersectionPointParam( const Line3D &line, double &t ) const { Vector3D planeNormal = this->GetNormal(); Vector3D lineDirection = line.GetDirection(); t = planeNormal * lineDirection; if ( fabs( t ) < eps ) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = ( planeNormal * diff ) / t; return true; } bool PlaneGeometry::IsParallel( const PlaneGeometry *plane ) const { return ( (Angle(plane) < 10.0 * mitk::sqrteps ) || ( Angle(plane) > ( vnl_math::pi - 10.0 * sqrteps ) ) ) ; } bool PlaneGeometry::IsOnPlane( const Point3D &point ) const { return Distance(point) < eps; } bool PlaneGeometry::IsOnPlane( const Line3D &line ) const { return ( (Distance( line.GetPoint() ) < eps) && (Distance( line.GetPoint2() ) < eps) ); } bool PlaneGeometry::IsOnPlane( const PlaneGeometry *plane ) const { return ( IsParallel( plane ) && (Distance( plane->GetOrigin() ) < eps) ); } Point3D PlaneGeometry::ProjectPointOntoPlane( const Point3D& pt ) const { ScalarType len = this->GetNormalVnl().two_norm(); return pt - this->GetNormal() * this->SignedDistanceFromPlane( pt ) / len; } AffineGeometryFrame3D::Pointer PlaneGeometry::Clone() const { Self::Pointer newGeometry = new PlaneGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void PlaneGeometry::ExecuteOperation( Operation *operation ) { vtkTransform *transform = vtkTransform::New(); transform->SetMatrix( m_VtkMatrix ); switch ( operation->GetOperationType() ) { case OpORIENT: { mitk::PlaneOperation *planeOp = dynamic_cast< mitk::PlaneOperation * >( operation ); if ( planeOp == NULL ) { return; } Point3D center = planeOp->GetPoint(); Vector3D orientationVector = planeOp->GetNormal(); Vector3D defaultVector; FillVector3D( defaultVector, 0.0, 0.0, 1.0 ); Vector3D rotationAxis = itk::CrossProduct( orientationVector, defaultVector ); //vtkFloatingPointType rotationAngle = acos( orientationVector[2] / orientationVector.GetNorm() ); vtkFloatingPointType rotationAngle = atan2( (double) rotationAxis.GetNorm(), (double) (orientationVector * defaultVector) ); rotationAngle *= 180.0 / vnl_math::pi; transform->PostMultiply(); transform->Identity(); transform->Translate( center[0], center[1], center[2] ); transform->RotateWXYZ( rotationAngle, rotationAxis[0], rotationAxis[1], rotationAxis[2] ); transform->Translate( -center[0], -center[1], -center[2] ); break; } + case OpRESTOREPLANEPOSITION: + { + RestorePlanePositionOperation *op = dynamic_cast< mitk::RestorePlanePositionOperation* >(operation); + if(op == NULL) + { + return; + } + AffineTransform3D::Pointer transform2 = AffineTransform3D::New(); + Matrix3D matrix; + matrix.GetVnlMatrix().set_column(0, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(0)); + matrix.GetVnlMatrix().set_column(1, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(1)); + matrix.GetVnlMatrix().set_column(2, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(2)); + transform2->SetMatrix(matrix); + Vector3D offset = op->GetTransform()->GetOffset(); + transform2->SetOffset(offset); + + this->SetIndexToWorldTransform(transform2); + ScalarType bounds[6] = {0, op->GetWidth(), 0, op->GetHeight(), 0 ,1 }; + this->SetBounds(bounds); + TransferItkToVtkTransform(); + this->Modified(); + transform->Delete(); + return; + } default: Superclass::ExecuteOperation( operation ); transform->Delete(); return; } m_VtkMatrix->DeepCopy(transform->GetMatrix()); this->TransferVtkToItkTransform(); this->Modified(); transform->Delete(); } void PlaneGeometry::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os,indent); os << indent << " Normal: " << GetNormal() << std::endl; } } // namespace diff --git a/Core/Code/DataManagement/mitkPlaneGeometry.h b/Core/Code/DataManagement/mitkPlaneGeometry.h index 7b2c6a0477..a36b1b1119 100644 --- a/Core/Code/DataManagement/mitkPlaneGeometry.h +++ b/Core/Code/DataManagement/mitkPlaneGeometry.h @@ -1,422 +1,423 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef PLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #define PLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #include #include "mitkGeometry2D.h" +#include "mitkRestorePlanePositionOperation.h" #include namespace mitk { template < class TCoordRep, unsigned int NPointDimension > class Line; typedef Line Line3D; /** * \brief Describes a two-dimensional, rectangular plane * * \ingroup Geometry */ class MITK_CORE_EXPORT PlaneGeometry : public Geometry2D { public: mitkClassMacro(PlaneGeometry,Geometry2D); /** Method for creation through the object factory. */ itkNewMacro(Self); enum PlaneOrientation { Transversal, Sagittal, Frontal }; virtual void IndexToWorld(const Point2D &pt_units, Point2D &pt_mm) const; virtual void WorldToIndex(const Point2D &pt_mm, Point2D &pt_units) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## @deprecated First parameter (Point2D) is not used. If possible, please use void IndexToWorld(const mitk::Vector2D& vec_units, mitk::Vector2D& vec_mm) const. //## For further information about coordinates types, please see the Geometry documentation virtual void IndexToWorld(const mitk::Point2D &atPt2d_untis, const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation virtual void IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## @deprecated First parameter (Point2D) is not used. If possible, please use void WorldToIndex(const mitk::Vector2D& vec_mm, mitk::Vector2D& vec_units) const. //## For further information about coordinates types, please see the Geometry documentation virtual void WorldToIndex(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## For further information about coordinates types, please see the Geometry documentation virtual void WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const; virtual void Initialize(); /** * \brief Initialize a plane with orientation \a planeorientation * (default: transversal) with respect to \a geometry3D (default: identity). * Spacing also taken from \a geometry3D. * * \warning A former version of this method created a geometry with unit * spacing. For unit spacing use * * \code * // for in-plane unit spacing: * thisgeometry->SetSizeInUnits(thisgeometry->GetExtentInMM(0), * thisgeometry->GetExtentInMM(1)); * // additionally, for unit spacing in normal direction (former version * // did not do this): * thisgeometry->SetExtentInMM(2, 1.0); * \endcode */ virtual void InitializeStandardPlane( const Geometry3D* geometry3D, PlaneOrientation planeorientation = Transversal, ScalarType zPosition = 0, bool frontside=true, bool rotated=false ); /** * \brief Initialize a plane with orientation \a planeorientation * (default: transversal) with respect to \a geometry3D (default: identity). * Spacing also taken from \a geometry3D. * * \param top if \a true, create plane at top, otherwise at bottom * (for PlaneOrientation Transversal, for other plane locations respectively) */ virtual void InitializeStandardPlane( const Geometry3D* geometry3D, bool top, PlaneOrientation planeorientation = Transversal, bool frontside=true, bool rotated=false ); /** * \brief Initialize a plane with orientation \a planeorientation * (default: transversal) with respect to \a transform (default: identity) * given width and height in units. * */ virtual void InitializeStandardPlane( ScalarType width, ScalarType height, const AffineTransform3D* transform = NULL, PlaneOrientation planeorientation = Transversal, ScalarType zPosition = 0, bool frontside=true, bool rotated=false ); /** * \brief Initialize plane with orientation \a planeorientation * (default: transversal) given width, height and spacing. * */ virtual void InitializeStandardPlane( ScalarType width, ScalarType height, const Vector3D & spacing, PlaneOrientation planeorientation = Transversal, ScalarType zPosition = 0, bool frontside = true, bool rotated = false ); /** * \brief Initialize plane by width and height in pixels, right-/down-vector * (itk) to describe orientation in world-space (vectors will be normalized) * and spacing (default: 1.0 mm in all directions). * * The vectors are normalized and multiplied by the respective spacing before * they are set in the matrix. */ virtual void InitializeStandardPlane( ScalarType width, ScalarType height, const Vector3D& rightVector, const Vector3D& downVector, const Vector3D *spacing = NULL ); /** * \brief Initialize plane by width and height in pixels, * right-/down-vector (vnl) to describe orientation in world-space (vectors * will be normalized) and spacing (default: 1.0 mm in all directions). * * The vectors are normalized and multiplied by the respective spacing * before they are set in the matrix. */ virtual void InitializeStandardPlane( ScalarType width, ScalarType height, const VnlVector& rightVector, const VnlVector& downVector, const Vector3D * spacing = NULL ); /** * \brief Initialize plane by right-/down-vector (itk) and spacing * (default: 1.0 mm in all directions). * * The length of the right-/-down-vector is used as width/height in units, * respectively. Then, the vectors are normalized and multiplied by the * respective spacing before they are set in the matrix. */ virtual void InitializeStandardPlane( const Vector3D& rightVector, const Vector3D& downVector, const Vector3D * spacing = NULL ); /** * \brief Initialize plane by right-/down-vector (vnl) and spacing * (default: 1.0 mm in all directions). * * The length of the right-/-down-vector is used as width/height in units, * respectively. Then, the vectors are normalized and multiplied by the * respective spacing before they are set in the matrix. */ virtual void InitializeStandardPlane( const VnlVector& rightVector, const VnlVector& downVector, const Vector3D * spacing = NULL ); /** * \brief Initialize plane by origin and normal (size is 1.0 mm in * all directions, direction of right-/down-vector valid but * undefined). * */ virtual void InitializePlane( const Point3D& origin, const Vector3D& normal); /** * \brief Initialize plane by right-/down-vector. * * \warning The vectors are set into the matrix as they are, * \em without normalization! */ void SetMatrixByVectors( const VnlVector& rightVector, const VnlVector& downVector, ScalarType thickness=1.0 ); /** * \brief Change \a transform so that the third column of the * transform-martix is perpendicular to the first two columns * */ static void EnsurePerpendicularNormal( AffineTransform3D* transform ); /** * \brief Normal of the plane * */ Vector3D GetNormal() const; /** * \brief Normal of the plane as VnlVector * */ VnlVector GetNormalVnl() const; virtual ScalarType SignedDistance( const Point3D& pt3d_mm ) const; virtual bool IsAbove( const Point3D& pt3d_mm ) const; /** * \brief Distance of the point from the plane * (bounding-box \em not considered) * */ ScalarType DistanceFromPlane( const Point3D& pt3d_mm ) const ; /** * \brief Signed distance of the point from the plane * (bounding-box \em not considered) * * > 0 : point is in the direction of the direction vector. */ inline ScalarType SignedDistanceFromPlane( const Point3D& pt3d_mm ) const { ScalarType len = GetNormalVnl().two_norm(); if( len == 0 ) return 0; return (pt3d_mm-GetOrigin())*GetNormal() / len; } /** * \brief Distance of the plane from another plane * (bounding-box \em not considered) * * Result is 0 if planes are not parallel. */ ScalarType DistanceFromPlane(const PlaneGeometry* plane) const { return fabs(SignedDistanceFromPlane(plane)); } /** * \brief Signed distance of the plane from another plane * (bounding-box \em not considered) * * Result is 0 if planes are not parallel. */ inline ScalarType SignedDistanceFromPlane( const PlaneGeometry *plane ) const { if(IsParallel(plane)) { return SignedDistance(plane->GetOrigin()); } return 0; } /** * \brief Calculate the intersecting line of two planes * * \return \a true planes are intersecting * \return \a false planes do not intersect */ bool IntersectionLine( const PlaneGeometry *plane, Line3D &crossline ) const; /** * \brief Calculate two points where another plane intersects the border of this plane * * \return number of intersection points (0..2). First interection point (if existing) * is returned in \a lineFrom, second in \a lineTo. */ unsigned int IntersectWithPlane2D(const PlaneGeometry *plane, Point2D &lineFrom, Point2D &lineTo ) const ; /** * \brief Calculate the angle between two planes * * \return angle in radiants */ double Angle( const PlaneGeometry *plane ) const; /** * \brief Calculate the angle between the plane and a line * * \return angle in radiants */ double Angle( const Line3D &line ) const; /** * \brief Calculate intersection point between the plane and a line * * \param intersectionPoint intersection point * \return \a true if \em unique intersection exists, i.e., if line * is \em not on or parallel to the plane */ bool IntersectionPoint( const Line3D &line, Point3D &intersectionPoint ) const; /** * \brief Calculate line parameter of intersection point between the * plane and a line * * \param t parameter of line: intersection point is * line.GetPoint()+t*line.GetDirection() * \return \a true if \em unique intersection exists, i.e., if line * is \em not on or parallel to the plane */ bool IntersectionPointParam( const Line3D &line, double &t ) const; /** * \brief Returns whether the plane is parallel to another plane * * @return true iff the normal vectors both point to the same or exactly oposit direction */ bool IsParallel( const PlaneGeometry *plane ) const; /** * \brief Returns whether the point is on the plane * (bounding-box \em not considered) */ bool IsOnPlane( const Point3D &point ) const; /** * \brief Returns whether the line is on the plane * (bounding-box \em not considered) */ bool IsOnPlane( const Line3D &line ) const; /** * \brief Returns whether the plane is on the plane * (bounding-box \em not considered) * * @return true iff the normal vector of the planes point to the same or the exactly oposit direction and * the distance of the planes is < eps * */ bool IsOnPlane( const PlaneGeometry *plane ) const; /** * \brief Returns the lot from the point to the plane */ Point3D ProjectPointOntoPlane( const Point3D &pt ) const; virtual void SetIndexToWorldTransform( AffineTransform3D *transform); virtual void SetBounds( const BoundingBox::BoundsArrayType &bounds ); AffineGeometryFrame3D::Pointer Clone() const; /** Implements operation to re-orient the plane */ virtual void ExecuteOperation( Operation *operation ); protected: PlaneGeometry(); virtual ~PlaneGeometry(); virtual void PrintSelf( std::ostream &os, itk::Indent indent ) const; private: /** * \brief Compares plane with another plane: \a true if IsOnPlane * (bounding-box \em not considered) */ virtual bool operator==( const PlaneGeometry * ) const { return false; }; /** * \brief Compares plane with another plane: \a false if IsOnPlane * (bounding-box \em not considered) */ virtual bool operator!=( const PlaneGeometry * ) const { return false; }; }; } // namespace mitk #endif /* PLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C */ diff --git a/Core/Code/DataManagement/mitkRestorePlanePositionOperation.cpp b/Core/Code/DataManagement/mitkRestorePlanePositionOperation.cpp new file mode 100644 index 0000000000..b15f11010d --- /dev/null +++ b/Core/Code/DataManagement/mitkRestorePlanePositionOperation.cpp @@ -0,0 +1,67 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision$ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + + +#include "mitkRestorePlanePositionOperation.h" + +namespace mitk +{ + +RestorePlanePositionOperation +::RestorePlanePositionOperation( OperationType operationType, float width, float height, Vector3D spacing , unsigned int pos, Vector3D direction, AffineTransform3D::Pointer transform/*, PlaneOrientation orientation*/) +: Operation(operationType), m_Width( width ), m_Height( height ), m_Spacing( spacing ), m_Pos(pos), m_DirectionVector(direction), m_Transform(transform)/*, m_Orientation(orientation)*/ +{ + //MITK_INFO<<"Width: "<InitializeSlicedGeometry( m_Slices ); + this->InitializeSlicedGeometry( m_Slices ); } mitk::SlicedGeometry3D::SlicedGeometry3D(const SlicedGeometry3D& other) : Superclass(other), -m_EvenlySpaced( other.m_EvenlySpaced ), -m_Slices( other.m_Slices ), -m_ReferenceGeometry( other.m_ReferenceGeometry ), -m_SliceNavigationController( other.m_SliceNavigationController ) + m_EvenlySpaced( other.m_EvenlySpaced ), + m_Slices( other.m_Slices ), + m_ReferenceGeometry( other.m_ReferenceGeometry ), + m_SliceNavigationController( other.m_SliceNavigationController ) { m_DirectionVector.Fill(0); - SetSpacing( other.GetSpacing() ); - SetDirectionVector( other.GetDirectionVector() ); - - if ( m_EvenlySpaced ) - { - AffineGeometryFrame3D::Pointer geometry = other.m_Geometry2Ds[0]->Clone(); - Geometry2D* geometry2D = dynamic_cast(geometry.GetPointer()); - assert(geometry2D!=NULL); - SetGeometry2D(geometry2D, 0); - } - else - { - unsigned int s; - for ( s = 0; s < other.m_Slices; ++s ) + SetSpacing( other.GetSpacing() ); + SetDirectionVector( other.GetDirectionVector() ); + + if ( m_EvenlySpaced ) + { + AffineGeometryFrame3D::Pointer geometry = other.m_Geometry2Ds[0]->Clone(); + Geometry2D* geometry2D = dynamic_cast(geometry.GetPointer()); + assert(geometry2D!=NULL); + SetGeometry2D(geometry2D, 0); + } + else + { + unsigned int s; + for ( s = 0; s < other.m_Slices; ++s ) + { + if ( other.m_Geometry2Ds[s].IsNull() ) { - if ( other.m_Geometry2Ds[s].IsNull() ) - { - assert(other.m_EvenlySpaced); - m_Geometry2Ds[s] = NULL; - } - else - { - AffineGeometryFrame3D::Pointer geometry = other.m_Geometry2Ds[s]->Clone(); - Geometry2D* geometry2D = dynamic_cast(geometry.GetPointer()); - assert(geometry2D!=NULL); - SetGeometry2D(geometry2D, s); - } + assert(other.m_EvenlySpaced); + m_Geometry2Ds[s] = NULL; } - } + else + { + AffineGeometryFrame3D::Pointer geometry = other.m_Geometry2Ds[s]->Clone(); + Geometry2D* geometry2D = dynamic_cast(geometry.GetPointer()); + assert(geometry2D!=NULL); + SetGeometry2D(geometry2D, s); + } + } + } } mitk::SlicedGeometry3D::~SlicedGeometry3D() { } mitk::Geometry2D * mitk::SlicedGeometry3D::GetGeometry2D( int s ) const { - mitk::Geometry2D::Pointer geometry2D = NULL; - - if ( this->IsValidSlice(s) ) - { - geometry2D = m_Geometry2Ds[s]; - - // If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored - // for the requested slice, and (c) the first slice (s=0) - // is a PlaneGeometry instance, then we calculate the geometry of the - // requested as the plane of the first slice shifted by m_Spacing[2]*s - // in the direction of m_DirectionVector. - if ( (m_EvenlySpaced) && (geometry2D.IsNull()) ) + mitk::Geometry2D::Pointer geometry2D = NULL; + + if ( this->IsValidSlice(s) ) + { + geometry2D = m_Geometry2Ds[s]; + + // If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored + // for the requested slice, and (c) the first slice (s=0) + // is a PlaneGeometry instance, then we calculate the geometry of the + // requested as the plane of the first slice shifted by m_Spacing[2]*s + // in the direction of m_DirectionVector. + if ( (m_EvenlySpaced) && (geometry2D.IsNull()) ) + { + PlaneGeometry *firstSlice = dynamic_cast< PlaneGeometry * > ( + m_Geometry2Ds[0].GetPointer() ); + + if ( firstSlice != NULL ) { - PlaneGeometry *firstSlice = dynamic_cast< PlaneGeometry * > ( - m_Geometry2Ds[0].GetPointer() ); - - if ( firstSlice != NULL ) - { - if ( (m_DirectionVector[0] == 0.0) - && (m_DirectionVector[1] == 0.0) - && (m_DirectionVector[2] == 0.0) ) - { - - m_DirectionVector = firstSlice->GetNormal(); - m_DirectionVector.Normalize(); - } - - Vector3D direction; - direction = m_DirectionVector * m_Spacing[2]; - - mitk::PlaneGeometry::Pointer requestedslice; - requestedslice = static_cast< mitk::PlaneGeometry * >( - firstSlice->Clone().GetPointer() ); - - - requestedslice->SetOrigin( - requestedslice->GetOrigin() + direction * s ); - - geometry2D = requestedslice; - m_Geometry2Ds[s] = geometry2D; - } + if ( (m_DirectionVector[0] == 0.0) + && (m_DirectionVector[1] == 0.0) + && (m_DirectionVector[2] == 0.0) ) + { + m_DirectionVector = firstSlice->GetNormal(); + m_DirectionVector.Normalize(); + } + + Vector3D direction; + direction = m_DirectionVector * m_Spacing[2]; + + mitk::PlaneGeometry::Pointer requestedslice; + requestedslice = static_cast< mitk::PlaneGeometry * >( + firstSlice->Clone().GetPointer() ); + + requestedslice->SetOrigin( + requestedslice->GetOrigin() + direction * s ); + + geometry2D = requestedslice; + m_Geometry2Ds[s] = geometry2D; } - return geometry2D; - } - else - { - return NULL; - } + } + return geometry2D; + } + else + { + return NULL; + } } const mitk::BoundingBox * mitk::SlicedGeometry3D::GetBoundingBox() const { - assert(m_BoundingBox.IsNotNull()); - return m_BoundingBox.GetPointer(); + assert(m_BoundingBox.IsNotNull()); + return m_BoundingBox.GetPointer(); } bool mitk::SlicedGeometry3D::SetGeometry2D( mitk::Geometry2D *geometry2D, int s ) { - if ( this->IsValidSlice(s) ) - { - m_Geometry2Ds[s] = geometry2D; - m_Geometry2Ds[s]->SetReferenceGeometry( m_ReferenceGeometry ); - return true; - } - return false; + if ( this->IsValidSlice(s) ) + { + m_Geometry2Ds[s] = geometry2D; + m_Geometry2Ds[s]->SetReferenceGeometry( m_ReferenceGeometry ); + return true; + } + return false; } void mitk::SlicedGeometry3D::InitializeSlicedGeometry( unsigned int slices ) { - Superclass::Initialize(); - m_Slices = slices; - - Geometry2D::Pointer gnull = NULL; - m_Geometry2Ds.assign( m_Slices, gnull ); + Superclass::Initialize(); + m_Slices = slices; - Vector3D spacing; - spacing.Fill( 1.0 ); - this->SetSpacing( spacing ); + Geometry2D::Pointer gnull = NULL; + m_Geometry2Ds.assign( m_Slices, gnull ); + + Vector3D spacing; + spacing.Fill( 1.0 ); + this->SetSpacing( spacing ); - m_DirectionVector.Fill( 0 ); + m_DirectionVector.Fill( 0 ); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced( - mitk::Geometry2D* geometry2D, unsigned int slices, bool flipped ) + mitk::Geometry2D* geometry2D, unsigned int slices, bool flipped ) { - assert( geometry2D != NULL ); - this->InitializeEvenlySpaced( - geometry2D, geometry2D->GetExtentInMM(2)/geometry2D->GetExtent(2), - slices, flipped ); + assert( geometry2D != NULL ); + this->InitializeEvenlySpaced( + geometry2D, geometry2D->GetExtentInMM(2)/geometry2D->GetExtent(2), + slices, flipped ); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced( - mitk::Geometry2D* geometry2D, mitk::ScalarType zSpacing, - unsigned int slices, bool flipped ) + mitk::Geometry2D* geometry2D, mitk::ScalarType zSpacing, + unsigned int slices, bool flipped ) { - assert( geometry2D != NULL ); - assert( geometry2D->GetExtent(0) > 0 ); - assert( geometry2D->GetExtent(1) > 0 ); - - geometry2D->Register(); - - Superclass::Initialize(); - m_Slices = slices; - - BoundingBox::BoundsArrayType bounds = geometry2D->GetBounds(); - bounds[4] = 0; - bounds[5] = slices; - - // clear and reserve - Geometry2D::Pointer gnull = NULL; - m_Geometry2Ds.assign( m_Slices, gnull ); - - Vector3D directionVector = geometry2D->GetAxisVector(2); - directionVector.Normalize(); - directionVector *= zSpacing; - - if ( flipped == false ) - { - // Normally we should use the following four lines to create a copy of - // the transform contrained in geometry2D, because it may not be changed - // by us. But we know that SetSpacing creates a new transform without - // changing the old (coming from geometry2D), so we can use the fifth - // line instead. We check this at (**). - // - // AffineTransform3D::Pointer transform = AffineTransform3D::New(); - // transform->SetMatrix(geometry2D->GetIndexToWorldTransform()->GetMatrix()); - // transform->SetOffset(geometry2D->GetIndexToWorldTransform()->GetOffset()); - // SetIndexToWorldTransform(transform); - - m_IndexToWorldTransform = const_cast< AffineTransform3D * >( - geometry2D->GetIndexToWorldTransform() ); - } - else - { - directionVector *= -1.0; - m_IndexToWorldTransform = AffineTransform3D::New(); - m_IndexToWorldTransform->SetMatrix( - geometry2D->GetIndexToWorldTransform()->GetMatrix() ); - - AffineTransform3D::OutputVectorType scaleVector; - FillVector3D(scaleVector, 1.0, 1.0, -1.0); - m_IndexToWorldTransform->Scale(scaleVector, true); - m_IndexToWorldTransform->SetOffset( - geometry2D->GetIndexToWorldTransform()->GetOffset() ); - } - - mitk::Vector3D spacing; - FillVector3D( spacing, - geometry2D->GetExtentInMM(0) / bounds[1], - geometry2D->GetExtentInMM(1) / bounds[3], - zSpacing ); - - // Ensure that spacing differs from m_Spacing to make SetSpacing change the - // matrix. - m_Spacing[2] = zSpacing - 1; - - this->SetDirectionVector( directionVector ); - this->SetBounds( bounds ); - this->SetGeometry2D( geometry2D, 0 ); - this->SetSpacing( spacing ); - this->SetEvenlySpaced(); - this->SetTimeBounds( geometry2D->GetTimeBounds() ); - - assert(m_IndexToWorldTransform.GetPointer() - != geometry2D->GetIndexToWorldTransform()); // (**) see above. - - this->SetFrameOfReferenceID( geometry2D->GetFrameOfReferenceID() ); - this->SetImageGeometry( geometry2D->GetImageGeometry() ); - - geometry2D->UnRegister(); + assert( geometry2D != NULL ); + assert( geometry2D->GetExtent(0) > 0 ); + assert( geometry2D->GetExtent(1) > 0 ); + + geometry2D->Register(); + + Superclass::Initialize(); + m_Slices = slices; + + BoundingBox::BoundsArrayType bounds = geometry2D->GetBounds(); + bounds[4] = 0; + bounds[5] = slices; + + // clear and reserve + Geometry2D::Pointer gnull = NULL; + m_Geometry2Ds.assign( m_Slices, gnull ); + + Vector3D directionVector = geometry2D->GetAxisVector(2); + directionVector.Normalize(); + directionVector *= zSpacing; + + if ( flipped == false ) + { + // Normally we should use the following four lines to create a copy of + // the transform contrained in geometry2D, because it may not be changed + // by us. But we know that SetSpacing creates a new transform without + // changing the old (coming from geometry2D), so we can use the fifth + // line instead. We check this at (**). + // + // AffineTransform3D::Pointer transform = AffineTransform3D::New(); + // transform->SetMatrix(geometry2D->GetIndexToWorldTransform()->GetMatrix()); + // transform->SetOffset(geometry2D->GetIndexToWorldTransform()->GetOffset()); + // SetIndexToWorldTransform(transform); + + m_IndexToWorldTransform = const_cast< AffineTransform3D * >( + geometry2D->GetIndexToWorldTransform() ); + } + else + { + directionVector *= -1.0; + m_IndexToWorldTransform = AffineTransform3D::New(); + m_IndexToWorldTransform->SetMatrix( + geometry2D->GetIndexToWorldTransform()->GetMatrix() ); + + AffineTransform3D::OutputVectorType scaleVector; + FillVector3D(scaleVector, 1.0, 1.0, -1.0); + m_IndexToWorldTransform->Scale(scaleVector, true); + m_IndexToWorldTransform->SetOffset( + geometry2D->GetIndexToWorldTransform()->GetOffset() ); + } + + mitk::Vector3D spacing; + FillVector3D( spacing, + geometry2D->GetExtentInMM(0) / bounds[1], + geometry2D->GetExtentInMM(1) / bounds[3], + zSpacing ); + + // Ensure that spacing differs from m_Spacing to make SetSpacing change the + // matrix. + m_Spacing[2] = zSpacing - 1; + + this->SetDirectionVector( directionVector ); + this->SetBounds( bounds ); + this->SetGeometry2D( geometry2D, 0 ); + this->SetSpacing( spacing ); + this->SetEvenlySpaced(); + this->SetTimeBounds( geometry2D->GetTimeBounds() ); + + assert(m_IndexToWorldTransform.GetPointer() + != geometry2D->GetIndexToWorldTransform()); // (**) see above. + + this->SetFrameOfReferenceID( geometry2D->GetFrameOfReferenceID() ); + this->SetImageGeometry( geometry2D->GetImageGeometry() ); + + geometry2D->UnRegister(); } void mitk::SlicedGeometry3D::InitializePlanes( - const mitk::Geometry3D *geometry3D, - mitk::PlaneGeometry::PlaneOrientation planeorientation, - bool top, bool frontside, bool rotated ) + const mitk::Geometry3D *geometry3D, + mitk::PlaneGeometry::PlaneOrientation planeorientation, + bool top, bool frontside, bool rotated ) { - m_ReferenceGeometry = const_cast< Geometry3D * >( geometry3D ); - - PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); - planeGeometry->InitializeStandardPlane( - geometry3D, top, planeorientation, frontside, rotated ); - - ScalarType viewSpacing = 1; - unsigned int slices = 1; - - switch ( planeorientation ) - { - case PlaneGeometry::Transversal: + m_ReferenceGeometry = const_cast< Geometry3D * >( geometry3D ); + + PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); + planeGeometry->InitializeStandardPlane( + geometry3D, top, planeorientation, frontside, rotated ); + + ScalarType viewSpacing = 1; + unsigned int slices = 1; + + switch ( planeorientation ) + { + case PlaneGeometry::Transversal: viewSpacing = geometry3D->GetSpacing()[2]; slices = (unsigned int) geometry3D->GetExtent( 2 ); break; - case PlaneGeometry::Frontal: + case PlaneGeometry::Frontal: viewSpacing = geometry3D->GetSpacing()[1]; slices = (unsigned int) geometry3D->GetExtent( 1 ); break; - case PlaneGeometry::Sagittal: + case PlaneGeometry::Sagittal: viewSpacing = geometry3D->GetSpacing()[0]; slices = (unsigned int) geometry3D->GetExtent( 0 ); break; - default: + default: itkExceptionMacro("unknown PlaneOrientation"); - } + } - mitk::Vector3D normal = this->AdjustNormal( planeGeometry->GetNormal() ); + mitk::Vector3D normal = this->AdjustNormal( planeGeometry->GetNormal() ); - ScalarType directedExtent = + ScalarType directedExtent = fabs( m_ReferenceGeometry->GetExtentInMM( 0 ) * normal[0] ) - + fabs( m_ReferenceGeometry->GetExtentInMM( 1 ) * normal[1] ) - + fabs( m_ReferenceGeometry->GetExtentInMM( 2 ) * normal[2] ); - - if ( directedExtent >= viewSpacing ) - { - slices = static_cast< int >(directedExtent / viewSpacing + 0.5); - } - else - { - slices = 1; - } - - bool flipped = (top == false); - - if ( frontside == false ) - { - flipped = !flipped; - } - if ( planeorientation == PlaneGeometry::Frontal ) - { - flipped = !flipped; - } - - this->InitializeEvenlySpaced( planeGeometry, viewSpacing, slices, flipped ); + + fabs( m_ReferenceGeometry->GetExtentInMM( 1 ) * normal[1] ) + + fabs( m_ReferenceGeometry->GetExtentInMM( 2 ) * normal[2] ); + + if ( directedExtent >= viewSpacing ) + { + slices = static_cast< int >(directedExtent / viewSpacing + 0.5); + } + else + { + slices = 1; + } + + bool flipped = (top == false); + + if ( frontside == false ) + { + flipped = !flipped; + } + if ( planeorientation == PlaneGeometry::Frontal ) + { + flipped = !flipped; + } + + this->InitializeEvenlySpaced( planeGeometry, viewSpacing, slices, flipped ); } void mitk::SlicedGeometry3D ::ReinitializePlanes( const Point3D ¢er, const Point3D &referencePoint ) { - // Need a reference frame to align the rotated planes - if ( !m_ReferenceGeometry ) - { - return; - } - - // Get first plane of plane stack - PlaneGeometry *firstPlane = - dynamic_cast< PlaneGeometry * >( m_Geometry2Ds[0].GetPointer() ); - - // If plane stack is empty, exit - if ( firstPlane == NULL ) - { - return; - } - - // Calculate the "directed" spacing when taking the plane (defined by its axes - // vectors and normal) as the reference coordinate frame. - // - // This is done by calculating the radius of the ellipsoid defined by the - // original volume spacing axes, in the direction of the respective axis of the - // reference frame. - mitk::Vector3D axis0 = firstPlane->GetAxisVector(0); - mitk::Vector3D axis1 = firstPlane->GetAxisVector(1); - mitk::Vector3D normal = firstPlane->GetNormal(); - normal.Normalize(); - - Vector3D spacing; - spacing[0] = this->CalculateSpacing( axis0 ); - spacing[1] = this->CalculateSpacing( axis1 ); - spacing[2] = this->CalculateSpacing( normal ); - - Superclass::SetSpacing( spacing ); - - - // Now we need to calculate the number of slices in the plane's normal - // direction, so that the entire volume is covered. This is done by first - // calculating the dot product between the volume diagonal (the maximum - // distance inside the volume) and the normal, and dividing this value by - // the directed spacing calculated above. - ScalarType directedExtent = + // Need a reference frame to align the rotated planes + if ( !m_ReferenceGeometry ) + { + return; + } + + // Get first plane of plane stack + PlaneGeometry *firstPlane = + dynamic_cast< PlaneGeometry * >( m_Geometry2Ds[0].GetPointer() ); + + // If plane stack is empty, exit + if ( firstPlane == NULL ) + { + return; + } + + // Calculate the "directed" spacing when taking the plane (defined by its axes + // vectors and normal) as the reference coordinate frame. + // + // This is done by calculating the radius of the ellipsoid defined by the + // original volume spacing axes, in the direction of the respective axis of the + // reference frame. + mitk::Vector3D axis0 = firstPlane->GetAxisVector(0); + mitk::Vector3D axis1 = firstPlane->GetAxisVector(1); + mitk::Vector3D normal = firstPlane->GetNormal(); + normal.Normalize(); + + Vector3D spacing; + spacing[0] = this->CalculateSpacing( axis0 ); + spacing[1] = this->CalculateSpacing( axis1 ); + spacing[2] = this->CalculateSpacing( normal ); + + Superclass::SetSpacing( spacing ); + + + // Now we need to calculate the number of slices in the plane's normal + // direction, so that the entire volume is covered. This is done by first + // calculating the dot product between the volume diagonal (the maximum + // distance inside the volume) and the normal, and dividing this value by + // the directed spacing calculated above. + ScalarType directedExtent = fabs( m_ReferenceGeometry->GetExtentInMM( 0 ) * normal[0] ) - + fabs( m_ReferenceGeometry->GetExtentInMM( 1 ) * normal[1] ) - + fabs( m_ReferenceGeometry->GetExtentInMM( 2 ) * normal[2] ); - - if ( directedExtent >= spacing[2] ) - { - m_Slices = static_cast< unsigned int >(directedExtent / spacing[2] + 0.5); - } - else - { - m_Slices = 1; - } - - // The origin of our "first plane" needs to be adapted to this new extent. - // To achieve this, we first calculate the current distance to the volume's - // center, and then shift the origin in the direction of the normal by the - // difference between this distance and half of the new extent. - double centerOfRotationDistance = - firstPlane->SignedDistanceFromPlane( center ); - - if ( centerOfRotationDistance > 0 ) - { - firstPlane->SetOrigin( firstPlane->GetOrigin() - + normal * (centerOfRotationDistance - directedExtent / 2.0) - ); - m_DirectionVector = normal; - } - else - { - firstPlane->SetOrigin( firstPlane->GetOrigin() - + normal * (directedExtent / 2.0 + centerOfRotationDistance) - ); - m_DirectionVector = -normal; - } - - // Now we adjust this distance according with respect to the given reference - // point: we need to make sure that the point is touched by one slice of the - // new slice stack. - double referencePointDistance = - firstPlane->SignedDistanceFromPlane( referencePoint ); - - int referencePointSlice = static_cast< int >( - referencePointDistance / spacing[2]); - - double alignmentValue = - referencePointDistance / spacing[2] - referencePointSlice; - - firstPlane->SetOrigin( - firstPlane->GetOrigin() + normal * alignmentValue * spacing[2] ); - - - // Finally, we can clear the previous geometry stack and initialize it with - // our re-initialized "first plane". - m_Geometry2Ds.assign( m_Slices, Geometry2D::Pointer( NULL ) ); - - if ( m_Slices > 0 ) - { - m_Geometry2Ds[0] = firstPlane; - } - - // Reinitialize SNC with new number of slices - m_SliceNavigationController->GetSlice()->SetSteps( m_Slices ); - - this->Modified(); + + fabs( m_ReferenceGeometry->GetExtentInMM( 1 ) * normal[1] ) + + fabs( m_ReferenceGeometry->GetExtentInMM( 2 ) * normal[2] ); + + if ( directedExtent >= spacing[2] ) + { + m_Slices = static_cast< unsigned int >(directedExtent / spacing[2] + 0.5); + } + else + { + m_Slices = 1; + } + + // The origin of our "first plane" needs to be adapted to this new extent. + // To achieve this, we first calculate the current distance to the volume's + // center, and then shift the origin in the direction of the normal by the + // difference between this distance and half of the new extent. + double centerOfRotationDistance = + firstPlane->SignedDistanceFromPlane( center ); + + if ( centerOfRotationDistance > 0 ) + { + firstPlane->SetOrigin( firstPlane->GetOrigin() + + normal * (centerOfRotationDistance - directedExtent / 2.0) + ); + m_DirectionVector = normal; + } + else + { + firstPlane->SetOrigin( firstPlane->GetOrigin() + + normal * (directedExtent / 2.0 + centerOfRotationDistance) + ); + m_DirectionVector = -normal; + } + + // Now we adjust this distance according with respect to the given reference + // point: we need to make sure that the point is touched by one slice of the + // new slice stack. + double referencePointDistance = + firstPlane->SignedDistanceFromPlane( referencePoint ); + + int referencePointSlice = static_cast< int >( + referencePointDistance / spacing[2]); + + double alignmentValue = + referencePointDistance / spacing[2] - referencePointSlice; + + firstPlane->SetOrigin( + firstPlane->GetOrigin() + normal * alignmentValue * spacing[2] ); + + + // Finally, we can clear the previous geometry stack and initialize it with + // our re-initialized "first plane". + m_Geometry2Ds.assign( m_Slices, Geometry2D::Pointer( NULL ) ); + + if ( m_Slices > 0 ) + { + m_Geometry2Ds[0] = firstPlane; + } + + // Reinitialize SNC with new number of slices + m_SliceNavigationController->GetSlice()->SetSteps( m_Slices ); + + this->Modified(); } double mitk::SlicedGeometry3D::CalculateSpacing( const mitk::Vector3D &d ) const { - // Need the spacing of the underlying dataset / geometry - if ( !m_ReferenceGeometry ) - { - return 1.0; - } - - // The following can be derived from the ellipsoid equation - // - // 1 = x^2/a^2 + y^2/b^2 + z^2/c^2 - // - // where (a,b,c) = spacing of original volume (ellipsoid radii) - // and (x,y,z) = scaled coordinates of vector d (according to ellipsoid) - // - const mitk::Vector3D &spacing = m_ReferenceGeometry->GetSpacing(); - - double scaling = d[0]*d[0] / (spacing[0] * spacing[0]) - + d[1]*d[1] / (spacing[1] * spacing[1]) - + d[2]*d[2] / (spacing[2] * spacing[2]); - - scaling = sqrt( scaling ); - - return ( sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] ) / scaling ); + // Need the spacing of the underlying dataset / geometry + if ( !m_ReferenceGeometry ) + { + return 1.0; + } + + // The following can be derived from the ellipsoid equation + // + // 1 = x^2/a^2 + y^2/b^2 + z^2/c^2 + // + // where (a,b,c) = spacing of original volume (ellipsoid radii) + // and (x,y,z) = scaled coordinates of vector d (according to ellipsoid) + // + const mitk::Vector3D &spacing = m_ReferenceGeometry->GetSpacing(); + + double scaling = d[0]*d[0] / (spacing[0] * spacing[0]) + + d[1]*d[1] / (spacing[1] * spacing[1]) + + d[2]*d[2] / (spacing[2] * spacing[2]); + + scaling = sqrt( scaling ); + + return ( sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] ) / scaling ); } mitk::Vector3D mitk::SlicedGeometry3D::AdjustNormal( const mitk::Vector3D &normal ) const { - Geometry3D::TransformType::Pointer inverse = Geometry3D::TransformType::New(); - m_ReferenceGeometry->GetIndexToWorldTransform()->GetInverse( inverse ); - - Vector3D transformedNormal = inverse->TransformVector( normal ); + Geometry3D::TransformType::Pointer inverse = Geometry3D::TransformType::New(); + m_ReferenceGeometry->GetIndexToWorldTransform()->GetInverse( inverse ); + + Vector3D transformedNormal = inverse->TransformVector( normal ); - transformedNormal.Normalize(); - return transformedNormal; + transformedNormal.Normalize(); + return transformedNormal; } void mitk::SlicedGeometry3D::SetImageGeometry( const bool isAnImageGeometry ) { - Superclass::SetImageGeometry( isAnImageGeometry ); - - mitk::Geometry3D* geometry; - - unsigned int s; - for ( s = 0; s < m_Slices; ++s ) - { - geometry = m_Geometry2Ds[s]; - if ( geometry!=NULL ) - { - geometry->SetImageGeometry( isAnImageGeometry ); - } - } + Superclass::SetImageGeometry( isAnImageGeometry ); + + mitk::Geometry3D* geometry; + + unsigned int s; + for ( s = 0; s < m_Slices; ++s ) + { + geometry = m_Geometry2Ds[s]; + if ( geometry!=NULL ) + { + geometry->SetImageGeometry( isAnImageGeometry ); + } + } } void mitk::SlicedGeometry3D::ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ) { - mitk::Geometry3D* geometry; - - unsigned int s; - for ( s = 0; s < m_Slices; ++s ) - { - geometry = m_Geometry2Ds[s]; - if ( geometry!=NULL ) - { - geometry->ChangeImageGeometryConsideringOriginOffset( isAnImageGeometry ); - } - } - - Superclass::ChangeImageGeometryConsideringOriginOffset( isAnImageGeometry ); + mitk::Geometry3D* geometry; + + unsigned int s; + for ( s = 0; s < m_Slices; ++s ) + { + geometry = m_Geometry2Ds[s]; + if ( geometry!=NULL ) + { + geometry->ChangeImageGeometryConsideringOriginOffset( isAnImageGeometry ); + } + } + + Superclass::ChangeImageGeometryConsideringOriginOffset( isAnImageGeometry ); } bool mitk::SlicedGeometry3D::IsValidSlice( int s ) const { - return ((s >= 0) && (s < (int)m_Slices)); + return ((s >= 0) && (s < (int)m_Slices)); } void mitk::SlicedGeometry3D::SetReferenceGeometry( Geometry3D *referenceGeometry ) { - m_ReferenceGeometry = referenceGeometry; + m_ReferenceGeometry = referenceGeometry; - std::vector::iterator it; + std::vector::iterator it; - for ( it = m_Geometry2Ds.begin(); it != m_Geometry2Ds.end(); ++it ) - { - (*it)->SetReferenceGeometry( referenceGeometry ); - } + for ( it = m_Geometry2Ds.begin(); it != m_Geometry2Ds.end(); ++it ) + { + (*it)->SetReferenceGeometry( referenceGeometry ); + } } void mitk::SlicedGeometry3D::SetSpacing( const mitk::Vector3D &aSpacing ) { - bool hasEvenlySpacedPlaneGeometry = false; - mitk::Point3D origin; - mitk::Vector3D rightDV, bottomDV; - BoundingBox::BoundsArrayType bounds; - - assert(aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0); - - // In case of evenly-spaced data: re-initialize instances of Geometry2D, - // since the spacing influences them - if ((m_EvenlySpaced) && (m_Geometry2Ds.size() > 0)) - { - mitk::Geometry2D::ConstPointer firstGeometry = - m_Geometry2Ds[0].GetPointer(); - - const PlaneGeometry *planeGeometry = - dynamic_cast< const PlaneGeometry * >( firstGeometry.GetPointer() ); - - if (planeGeometry != NULL ) - { - this->WorldToIndex( planeGeometry->GetOrigin(), origin ); - this->WorldToIndex( planeGeometry->GetAxisVector(0), rightDV ); - this->WorldToIndex( planeGeometry->GetAxisVector(1), bottomDV ); - - bounds = planeGeometry->GetBounds(); - hasEvenlySpacedPlaneGeometry = true; - } - } - - Superclass::SetSpacing(aSpacing); - - mitk::Geometry2D::Pointer firstGeometry; - - // In case of evenly-spaced data: re-initialize instances of Geometry2D, - // since the spacing influences them - if ( hasEvenlySpacedPlaneGeometry ) - { - //create planeGeometry according to new spacing - this->IndexToWorld( origin, origin ); - this->IndexToWorld( rightDV, rightDV ); - this->IndexToWorld( bottomDV, bottomDV ); - - mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); - planeGeometry->SetImageGeometry( this->GetImageGeometry() ); - - planeGeometry->SetReferenceGeometry( m_ReferenceGeometry ); - planeGeometry->InitializeStandardPlane( - rightDV.Get_vnl_vector(), bottomDV.Get_vnl_vector(), &m_Spacing ); - planeGeometry->SetOrigin(origin); - planeGeometry->SetBounds(bounds); - - firstGeometry = planeGeometry; - } - else if ( (m_EvenlySpaced) && (m_Geometry2Ds.size() > 0) ) - { - firstGeometry = m_Geometry2Ds[0].GetPointer(); - } - - //clear and reserve - Geometry2D::Pointer gnull=NULL; - m_Geometry2Ds.assign(m_Slices, gnull); - - if ( m_Slices > 0 ) - { - m_Geometry2Ds[0] = firstGeometry; - } - - this->Modified(); + bool hasEvenlySpacedPlaneGeometry = false; + mitk::Point3D origin; + mitk::Vector3D rightDV, bottomDV; + BoundingBox::BoundsArrayType bounds; + + assert(aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0); + + // In case of evenly-spaced data: re-initialize instances of Geometry2D, + // since the spacing influences them + if ((m_EvenlySpaced) && (m_Geometry2Ds.size() > 0)) + { + mitk::Geometry2D::ConstPointer firstGeometry = + m_Geometry2Ds[0].GetPointer(); + + const PlaneGeometry *planeGeometry = + dynamic_cast< const PlaneGeometry * >( firstGeometry.GetPointer() ); + + if (planeGeometry != NULL ) + { + this->WorldToIndex( planeGeometry->GetOrigin(), origin ); + this->WorldToIndex( planeGeometry->GetAxisVector(0), rightDV ); + this->WorldToIndex( planeGeometry->GetAxisVector(1), bottomDV ); + + bounds = planeGeometry->GetBounds(); + hasEvenlySpacedPlaneGeometry = true; + } + } + + Superclass::SetSpacing(aSpacing); + + mitk::Geometry2D::Pointer firstGeometry; + + // In case of evenly-spaced data: re-initialize instances of Geometry2D, + // since the spacing influences them + if ( hasEvenlySpacedPlaneGeometry ) + { + //create planeGeometry according to new spacing + this->IndexToWorld( origin, origin ); + this->IndexToWorld( rightDV, rightDV ); + this->IndexToWorld( bottomDV, bottomDV ); + + mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); + planeGeometry->SetImageGeometry( this->GetImageGeometry() ); + + planeGeometry->SetReferenceGeometry( m_ReferenceGeometry ); + planeGeometry->InitializeStandardPlane( + rightDV.Get_vnl_vector(), bottomDV.Get_vnl_vector(), &m_Spacing ); + planeGeometry->SetOrigin(origin); + planeGeometry->SetBounds(bounds); + + firstGeometry = planeGeometry; + } + else if ( (m_EvenlySpaced) && (m_Geometry2Ds.size() > 0) ) + { + firstGeometry = m_Geometry2Ds[0].GetPointer(); + } + + //clear and reserve + Geometry2D::Pointer gnull=NULL; + m_Geometry2Ds.assign(m_Slices, gnull); + + if ( m_Slices > 0 ) + { + m_Geometry2Ds[0] = firstGeometry; + } + + this->Modified(); } void mitk::SlicedGeometry3D ::SetSliceNavigationController( SliceNavigationController *snc ) { - m_SliceNavigationController = snc; + m_SliceNavigationController = snc; } mitk::SliceNavigationController * mitk::SlicedGeometry3D::GetSliceNavigationController() { - return m_SliceNavigationController; + return m_SliceNavigationController; } void mitk::SlicedGeometry3D::SetEvenlySpaced(bool on) { - if(m_EvenlySpaced!=on) - { - m_EvenlySpaced=on; - this->Modified(); - } + if(m_EvenlySpaced!=on) + { + m_EvenlySpaced=on; + this->Modified(); + } } void mitk::SlicedGeometry3D ::SetDirectionVector( const mitk::Vector3D& directionVector ) { Vector3D newDir = directionVector; newDir.Normalize(); if ( newDir != m_DirectionVector ) - { + { m_DirectionVector = newDir; - this->Modified(); - } + this->Modified(); + } } void mitk::SlicedGeometry3D::SetTimeBounds( const mitk::TimeBounds& timebounds ) { - Superclass::SetTimeBounds( timebounds ); - - unsigned int s; - for ( s = 0; s < m_Slices; ++s ) - { - if(m_Geometry2Ds[s].IsNotNull()) - { - m_Geometry2Ds[s]->SetTimeBounds( timebounds ); - } - } - m_TimeBounds = timebounds; + Superclass::SetTimeBounds( timebounds ); + + unsigned int s; + for ( s = 0; s < m_Slices; ++s ) + { + if(m_Geometry2Ds[s].IsNotNull()) + { + m_Geometry2Ds[s]->SetTimeBounds( timebounds ); + } + } + m_TimeBounds = timebounds; } mitk::AffineGeometryFrame3D::Pointer mitk::SlicedGeometry3D::Clone() const { - Self::Pointer newGeometry = new SlicedGeometry3D(*this); - newGeometry->UnRegister(); - return newGeometry.GetPointer(); + Self::Pointer newGeometry = new SlicedGeometry3D(*this); + newGeometry->UnRegister(); + return newGeometry.GetPointer(); } void mitk::SlicedGeometry3D::PrintSelf( std::ostream& os, itk::Indent indent ) const { - Superclass::PrintSelf(os,indent); - os << indent << " EvenlySpaced: " << m_EvenlySpaced << std::endl; - if ( m_EvenlySpaced ) - { - os << indent << " DirectionVector: " << m_DirectionVector << std::endl; - } - os << indent << " Slices: " << m_Slices << std::endl; - - os << std::endl; - os << indent << " GetGeometry2D(0): "; - if ( this->GetGeometry2D(0) == NULL ) - { - os << "NULL" << std::endl; - } - else - { - this->GetGeometry2D(0)->Print(os, indent); - } + Superclass::PrintSelf(os,indent); + os << indent << " EvenlySpaced: " << m_EvenlySpaced << std::endl; + if ( m_EvenlySpaced ) + { + os << indent << " DirectionVector: " << m_DirectionVector << std::endl; + } + os << indent << " Slices: " << m_Slices << std::endl; + + os << std::endl; + os << indent << " GetGeometry2D(0): "; + if ( this->GetGeometry2D(0) == NULL ) + { + os << "NULL" << std::endl; + } + else + { + this->GetGeometry2D(0)->Print(os, indent); + } } void mitk::SlicedGeometry3D::ExecuteOperation(Operation* operation) { - switch ( operation->GetOperationType() ) - { - case OpNOTHING: - break; - - case OpROTATE: - if ( m_EvenlySpaced ) + switch ( operation->GetOperationType() ) + { + case OpNOTHING: + break; + + case OpROTATE: + if ( m_EvenlySpaced ) + { + // Need a reference frame to align the rotation + if ( m_ReferenceGeometry ) { - // Need a reference frame to align the rotation - if ( m_ReferenceGeometry ) - { - // Clear all generated geometries and then rotate only the first slice. - // The other slices will be re-generated on demand - - // Save first slice - Geometry2D::Pointer geometry2D = m_Geometry2Ds[0]; - - RotationOperation *rotOp = dynamic_cast< RotationOperation * >( operation ); - - // Generate a RotationOperation using the dataset center instead of - // the supplied rotation center. This is necessary so that the rotated - // zero-plane does not shift away. The supplied center is instead used - // to adjust the slice stack afterwards. - Point3D center = m_ReferenceGeometry->GetCenter(); - - RotationOperation centeredRotation( - rotOp->GetOperationType(), - center, - rotOp->GetVectorOfRotation(), - rotOp->GetAngleOfRotation() - ); - - // Rotate first slice - geometry2D->ExecuteOperation( ¢eredRotation ); - - // Clear the slice stack and adjust it according to the center of - // the dataset and the supplied rotation center (see documentation of - // ReinitializePlanes) - this->ReinitializePlanes( center, rotOp->GetCenterOfRotation() ); - - if ( m_SliceNavigationController ) - { - m_SliceNavigationController->SelectSliceByPoint( - rotOp->GetCenterOfRotation() ); - m_SliceNavigationController->AdjustSliceStepperRange(); - } - - Geometry3D::ExecuteOperation( ¢eredRotation ); - } + // Clear all generated geometries and then rotate only the first slice. + // The other slices will be re-generated on demand + + // Save first slice + Geometry2D::Pointer geometry2D = m_Geometry2Ds[0]; + + RotationOperation *rotOp = dynamic_cast< RotationOperation * >( operation ); + + // Generate a RotationOperation using the dataset center instead of + // the supplied rotation center. This is necessary so that the rotated + // zero-plane does not shift away. The supplied center is instead used + // to adjust the slice stack afterwards. + Point3D center = m_ReferenceGeometry->GetCenter(); + + RotationOperation centeredRotation( + rotOp->GetOperationType(), + center, + rotOp->GetVectorOfRotation(), + rotOp->GetAngleOfRotation() + ); + + // Rotate first slice + geometry2D->ExecuteOperation( ¢eredRotation ); + + // Clear the slice stack and adjust it according to the center of + // the dataset and the supplied rotation center (see documentation of + // ReinitializePlanes) + this->ReinitializePlanes( center, rotOp->GetCenterOfRotation() ); + + geometry2D->SetSpacing(this->GetSpacing()); + + if ( m_SliceNavigationController ) + { + m_SliceNavigationController->SelectSliceByPoint( + rotOp->GetCenterOfRotation() ); + m_SliceNavigationController->AdjustSliceStepperRange(); + } + + Geometry3D::ExecuteOperation( ¢eredRotation ); } - else + } + else + { + // Reach through to all slices + for (std::vector::iterator iter = m_Geometry2Ds.begin(); + iter != m_Geometry2Ds.end(); + ++iter) { - // Reach through to all slices - for (std::vector::iterator iter = m_Geometry2Ds.begin(); - iter != m_Geometry2Ds.end(); - ++iter) - { - (*iter)->ExecuteOperation(operation); - } + (*iter)->ExecuteOperation(operation); } - break; + } + break; - case OpORIENT: - if ( m_EvenlySpaced ) - { - // Save first slice - Geometry2D::Pointer geometry2D = m_Geometry2Ds[0]; + case OpORIENT: + if ( m_EvenlySpaced ) + { + // Save first slice + Geometry2D::Pointer geometry2D = m_Geometry2Ds[0]; - PlaneGeometry *planeGeometry = dynamic_cast< PlaneGeometry * >( - geometry2D.GetPointer() ); + PlaneGeometry *planeGeometry = dynamic_cast< PlaneGeometry * >( + geometry2D.GetPointer() ); - PlaneOperation *planeOp = dynamic_cast< PlaneOperation * >( operation ); + PlaneOperation *planeOp = dynamic_cast< PlaneOperation * >( operation ); - // Need a PlaneGeometry, a PlaneOperation and a reference frame to - // carry out the re-orientation - if ( m_ReferenceGeometry && planeGeometry && planeOp ) - { - // Clear all generated geometries and then rotate only the first slice. - // The other slices will be re-generated on demand + // Need a PlaneGeometry, a PlaneOperation and a reference frame to + // carry out the re-orientation + if ( m_ReferenceGeometry && planeGeometry && planeOp ) + { + // Clear all generated geometries and then rotate only the first slice. + // The other slices will be re-generated on demand - // Generate a RotationOperation by calculating the angle between - // the current and the requested slice orientation - Point3D center = m_ReferenceGeometry->GetCenter(); + // Generate a RotationOperation by calculating the angle between + // the current and the requested slice orientation + Point3D center = m_ReferenceGeometry->GetCenter(); - const mitk::Vector3D ¤tNormal = planeGeometry->GetNormal(); - const mitk::Vector3D &newNormal = planeOp->GetNormal(); + const mitk::Vector3D ¤tNormal = planeGeometry->GetNormal(); + const mitk::Vector3D &newNormal = planeOp->GetNormal(); - Vector3D rotationAxis = itk::CrossProduct( newNormal, currentNormal ); + Vector3D rotationAxis = itk::CrossProduct( newNormal, currentNormal ); - vtkFloatingPointType rotationAngle = - atan2( - (double) rotationAxis.GetNorm(), - (double) (newNormal * currentNormal) ); + vtkFloatingPointType rotationAngle = - atan2( + (double) rotationAxis.GetNorm(), + (double) (newNormal * currentNormal) ); - rotationAngle *= 180.0 / vnl_math::pi; + rotationAngle *= 180.0 / vnl_math::pi; - RotationOperation centeredRotation( - mitk::OpROTATE, - center, - rotationAxis, - rotationAngle - ); + RotationOperation centeredRotation( + mitk::OpROTATE, + center, + rotationAxis, + rotationAngle + ); - // Rotate first slice - geometry2D->ExecuteOperation( ¢eredRotation ); + // Rotate first slice + geometry2D->ExecuteOperation( ¢eredRotation ); - // Clear the slice stack and adjust it according to the center of - // rotation and plane position (see documentation of ReinitializePlanes) - this->ReinitializePlanes( center, planeOp->GetPoint() ); + // Clear the slice stack and adjust it according to the center of + // rotation and plane position (see documentation of ReinitializePlanes) + this->ReinitializePlanes( center, planeOp->GetPoint() ); - if ( m_SliceNavigationController ) - { - m_SliceNavigationController->SelectSliceByPoint( planeOp->GetPoint() ); - m_SliceNavigationController->AdjustSliceStepperRange(); - } + if ( m_SliceNavigationController ) + { + m_SliceNavigationController->SelectSliceByPoint( planeOp->GetPoint() ); + m_SliceNavigationController->AdjustSliceStepperRange(); + } - Geometry3D::ExecuteOperation( ¢eredRotation ); - } + Geometry3D::ExecuteOperation( ¢eredRotation ); } - else + } + else + { + // Reach through to all slices + for (std::vector::iterator iter = m_Geometry2Ds.begin(); + iter != m_Geometry2Ds.end(); + ++iter) + { + (*iter)->ExecuteOperation(operation); + } + } + break; + + case OpRESTOREPLANEPOSITION: + if ( m_EvenlySpaced ) + { + // Save first slice + Geometry2D::Pointer geometry2D = m_Geometry2Ds[0]; + + PlaneGeometry* planeGeometry = dynamic_cast< PlaneGeometry * >( + geometry2D.GetPointer() ); + + RestorePlanePositionOperation *restorePlaneOp = dynamic_cast< RestorePlanePositionOperation* >( operation ); + + // Need a PlaneGeometry, a PlaneOperation and a reference frame to + // carry out the re-orientation + if ( m_ReferenceGeometry && planeGeometry && restorePlaneOp ) { - // Reach through to all slices - for (std::vector::iterator iter = m_Geometry2Ds.begin(); - iter != m_Geometry2Ds.end(); - ++iter) + // Clear all generated geometries and then rotate only the first slice. + // The other slices will be re-generated on demand + + // Rotate first slice + geometry2D->ExecuteOperation( restorePlaneOp ); + + m_DirectionVector = restorePlaneOp->GetDirectionVector(); + + double centerOfRotationDistance = + planeGeometry->SignedDistanceFromPlane( m_ReferenceGeometry->GetCenter() ); + + if ( centerOfRotationDistance > 0 ) + { + m_DirectionVector = m_DirectionVector; + } + else + { + m_DirectionVector = -m_DirectionVector; + } + + Vector3D spacing = restorePlaneOp->GetSpacing(); + + Superclass::SetSpacing( spacing ); + + // /*Now we need to calculate the number of slices in the plane's normal + // direction, so that the entire volume is covered. This is done by first + // calculating the dot product between the volume diagonal (the maximum + // distance inside the volume) and the normal, and dividing this value by + // the directed spacing calculated above.*/ + ScalarType directedExtent = + fabs( m_ReferenceGeometry->GetExtentInMM( 0 ) * m_DirectionVector[0] ) + + fabs( m_ReferenceGeometry->GetExtentInMM( 1 ) * m_DirectionVector[1] ) + + fabs( m_ReferenceGeometry->GetExtentInMM( 2 ) * m_DirectionVector[2] ); + + if ( directedExtent >= spacing[2] ) + { + m_Slices = static_cast< unsigned int >(directedExtent / spacing[2] + 0.5); + } + else + { + m_Slices = 1; + } + + m_Geometry2Ds.assign( m_Slices, Geometry2D::Pointer( NULL ) ); + + if ( m_Slices > 0 ) { - (*iter)->ExecuteOperation(operation); + m_Geometry2Ds[0] = geometry2D; } + + m_SliceNavigationController->GetSlice()->SetSteps( m_Slices ); + + this->Modified(); + + //End Reinitialization + + if ( m_SliceNavigationController ) + { + m_SliceNavigationController->GetSlice()->SetPos( restorePlaneOp->GetPos() ); + m_SliceNavigationController->AdjustSliceStepperRange(); + } + Geometry3D::ExecuteOperation(restorePlaneOp); } - break; - } + } + else + { + // Reach through to all slices + for (std::vector::iterator iter = m_Geometry2Ds.begin(); + iter != m_Geometry2Ds.end(); + ++iter) + { + (*iter)->ExecuteOperation(operation); + } + } + break; + + } - this->Modified(); + this->Modified(); } diff --git a/Core/Code/DataManagement/mitkSlicedGeometry3D.h b/Core/Code/DataManagement/mitkSlicedGeometry3D.h index d3c5ae5580..a6246df353 100644 --- a/Core/Code/DataManagement/mitkSlicedGeometry3D.h +++ b/Core/Code/DataManagement/mitkSlicedGeometry3D.h @@ -1,323 +1,323 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #define MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #include "mitkGeometry3D.h" #include "mitkPlaneGeometry.h" namespace mitk { class SliceNavigationController; class NavigationController; /** \brief Describes the geometry of a data object consisting of slices. * * A Geometry2D can be requested for each slice. In the case of * \em evenly-spaced, \em plane geometries (m_EvenlySpaced==true), * only the 2D-geometry of the first slice has to be set (to an instance of * PlaneGeometry). The 2D geometries of the other slices are calculated * by shifting the first slice in the direction m_DirectionVector by * m_Spacing.z * sliceNumber. The m_Spacing member (which is only * relevant in the case m_EvenlySpaced==true) descibes the size of a voxel * (in mm), i.e., m_Spacing.x is the voxel width in the x-direction of the * plane. It is derived from the reference geometry of this SlicedGeometry3D, * which usually would be the global geometry describing how datasets are to * be resliced. * * By default, slices are oriented in the direction of one of the main axes * (x, y, z). However, by means of rotation, it is possible to realign the * slices in any possible direction. In case of an inclined plane, the spacing * is derived as a product of the (regular) geometry spacing and the direction * vector of the plane. * * SlicedGeometry3D and the associated Geometry2Ds have to be initialized in * the method GenerateOutputInformation() of BaseProcess (or CopyInformation / * UpdateOutputInformation of BaseData, if possible, e.g., by analyzing pic * tags in Image) subclasses. See also * * \sa itk::ProcessObject::GenerateOutputInformation(), * \sa itk::DataObject::CopyInformation() and * \a itk::DataObject::UpdateOutputInformation(). * * Rule: everything is in mm (or ms for temporal information) if not * stated otherwise. * * \warning The hull (i.e., transform, bounding-box and * time-bounds) is only guaranteed to be up-to-date after calling * UpdateInformation(). * * \ingroup Geometry */ class MITK_CORE_EXPORT SlicedGeometry3D : public mitk::Geometry3D { public: mitkClassMacro(SlicedGeometry3D, Geometry3D); /** Method for creation through the object factory. */ itkNewMacro(Self); /** * \brief Returns the Geometry2D of the slice (\a s). * * If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored * for the requested slice, and (c) the first slice (s=0) * is a PlaneGeometry instance, then we calculate the geometry of the * requested as the plane of the first slice shifted by m_Spacing[3]*s * in the direction of m_DirectionVector. * * \warning The Geometry2Ds are not necessarily up-to-date and not even * initialized. * * The Geometry2Ds have to be initialized in the method * GenerateOutputInformation() of BaseProcess (or CopyInformation / * UpdateOutputInformation of BaseData, if possible, e.g., by analyzing * pic tags in Image) subclasses. See also * * \sa itk::ProcessObject::GenerateOutputInformation(), * \sa itk::DataObject::CopyInformation() and * \sa itk::DataObject::UpdateOutputInformation(). */ virtual mitk::Geometry2D* GetGeometry2D( int s ) const; /** * \brief Set Geometry2D of slice \a s. */ virtual bool SetGeometry2D( mitk::Geometry2D *geometry2D, int s ); //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to change the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and changes the origin respectively. virtual void ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ); virtual void SetTimeBounds( const mitk::TimeBounds& timebounds ); virtual const mitk::BoundingBox* GetBoundingBox() const; /** * \brief Get the number of slices */ itkGetConstMacro( Slices, unsigned int ); /** * \brief Check whether a slice exists */ virtual bool IsValidSlice( int s = 0 ) const; virtual void SetReferenceGeometry( Geometry3D *referenceGeometry ); /** * \brief Set the spacing (m_Spacing), in direction of the plane normal. * * INTERNAL METHOD. */ virtual void SetSpacing( const mitk::Vector3D &aSpacing ); /** * \brief Set the SliceNavigationController corresponding to this sliced * geometry. * * The SNC needs to be informed when the number of slices in the geometry * changes, which can occur whenthe slices are re-oriented by rotation. */ virtual void SetSliceNavigationController( mitk::SliceNavigationController *snc ); mitk::SliceNavigationController *GetSliceNavigationController(); /** * \brief Set/Get whether the SlicedGeometry3D is evenly-spaced * (m_EvenlySpaced) * * If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored for * the requested slice, and (c) the first slice (s=0) is a PlaneGeometry * instance, then we calculate the geometry of the requested as the plane * of the first slice shifted by m_Spacing.z * s in the direction of * m_DirectionVector. * * \sa GetGeometry2D */ itkGetConstMacro(EvenlySpaced, bool); virtual void SetEvenlySpaced(bool on = true); /** * \brief Set/Get the vector between slices for the evenly-spaced case * (m_EvenlySpaced==true). * * If the direction-vector is (0,0,0) (the default) and the first * 2D geometry is a PlaneGeometry, then the direction-vector will be * calculated from the plane normal. * * \sa m_DirectionVector */ virtual void SetDirectionVector(const mitk::Vector3D& directionVector); itkGetConstMacro(DirectionVector, const mitk::Vector3D&); virtual AffineGeometryFrame3D::Pointer Clone() const; static const std::string SLICES; const static std::string DIRECTION_VECTOR; const static std::string EVENLY_SPACED; /** * \brief Tell this instance how many Geometry2Ds it shall manage. Bounding * box and the Geometry2Ds must be set additionally by calling the respective * methods! * * \warning Bounding box and the 2D-geometries must be set additionally: use * SetBounds(), SetGeometry(). */ virtual void InitializeSlicedGeometry( unsigned int slices ); /** * \brief Completely initialize this instance as evenly-spaced with slices * parallel to the provided Geometry2D that is used as the first slice and * for spacing calculation. * * Initializes the bounding box according to the width/height of the * Geometry2D and \a slices. The spacing is calculated from the Geometry2D. */ virtual void InitializeEvenlySpaced( mitk::Geometry2D *geometry2D, unsigned int slices, bool flipped=false ); /** * \brief Completely initialize this instance as evenly-spaced with slices * parallel to the provided Geometry2D that is used as the first slice and * for spacing calculation (except z-spacing). * * Initializes the bounding box according to the width/height of the * Geometry2D and \a slices. The x-/y-spacing is calculated from the * Geometry2D. */ virtual void InitializeEvenlySpaced( mitk::Geometry2D *geometry2D, mitk::ScalarType zSpacing, unsigned int slices, bool flipped=false ); /** * \brief Completely initialize this instance as evenly-spaced plane slices * parallel to a side of the provided Geometry3D and using its spacing * information. * * Initializes the bounding box according to the width/height of the * Geometry3D and the number of slices according to * Geometry3D::GetExtent(2). * * \param planeorientation side parallel to which the slices will be oriented * \param top if \a true, create plane at top, otherwise at bottom * (for PlaneOrientation Transversal, for other plane locations respectively) * \param frontside defines the side of the plane (the definition of * front/back is somewhat arbitrary) * * \param rotate rotates the plane by 180 degree around its normal (the * definition of rotated vs not rotated is somewhat arbitrary) */ virtual void InitializePlanes( const mitk::Geometry3D *geometry3D, mitk::PlaneGeometry::PlaneOrientation planeorientation, bool top=true, bool frontside=true, bool rotated=false ); virtual void SetImageGeometry(const bool isAnImageGeometry); virtual void ExecuteOperation(Operation* operation); protected: SlicedGeometry3D(); SlicedGeometry3D(const SlicedGeometry3D& other); virtual ~SlicedGeometry3D(); /** * Reinitialize plane stack after rotation. More precisely, the first plane * of the stack needs to spatially aligned, in two respects: * * 1. Re-alignment with respect to the dataset center; this is necessary - * since the distance from the first palne to the center could otherwise + * since the distance from the first plane to the center could otherwise * continuously decrease or increase. * 2. Re-alignment with respect to a given reference point; the reference * point is a location which the user wants to be exactly touched by one * plane of the plane stack. The first plane is minimally shifted to * ensure this touching. Usually, the reference point would be the * point around which the geometry is rotated. */ virtual void ReinitializePlanes( const Point3D ¢er, const Point3D &referencePoint ); ScalarType GetLargestExtent( const Geometry3D *geometry ); void PrintSelf(std::ostream& os, itk::Indent indent) const; /** Calculate "directed spacing", i.e. the spacing in directions * non-orthogonal to the coordinate axes. This is done via the * ellipsoid equation. */ double CalculateSpacing( const mitk::Vector3D &direction ) const; /** The extent of the slice stack, i.e. the number of slices, depends on the * plane normal. For rotated geometries, the geometry's transform needs to * be accounted in this calculation. */ mitk::Vector3D AdjustNormal( const mitk::Vector3D &normal ) const; /** * Container for the 2D-geometries contained within this SliceGeometry3D. */ mutable std::vector m_Geometry2Ds; /** * If (a) m_EvenlySpaced==true, (b) we don't have a Geometry2D stored * for the requested slice, and (c) the first slice (s=0) * is a PlaneGeometry instance, then we calculate the geometry of the * requested as the plane of the first slice shifted by m_Spacing.z*s * in the direction of m_DirectionVector. * * \sa GetGeometry2D */ bool m_EvenlySpaced; /** * Vector between slices for the evenly-spaced case (m_EvenlySpaced==true). * If the direction-vector is (0,0,0) (the default) and the first * 2D geometry is a PlaneGeometry, then the direction-vector will be * calculated from the plane normal. */ mutable mitk::Vector3D m_DirectionVector; /** Number of slices this SliceGeometry3D is descibing. */ unsigned int m_Slices; /** Underlying Geometry3D for this SlicedGeometry */ mitk::Geometry3D *m_ReferenceGeometry; /** SNC correcsponding to this geometry; used to reflect changes in the * number of slices due to rotation. */ //mitk::NavigationController *m_NavigationController; mitk::SliceNavigationController *m_SliceNavigationController; }; } // namespace mitk #endif /* MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/Interactions/mitkInteractionConst.h b/Core/Code/Interactions/mitkInteractionConst.h index 43be353356..2d7e31abda 100644 --- a/Core/Code/Interactions/mitkInteractionConst.h +++ b/Core/Code/Interactions/mitkInteractionConst.h @@ -1,753 +1,754 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKINTERACTCONST_H #define MITKINTERACTCONST_H //##Documentation //## @file mitkInteractionConst.h //## @brief Constants for most interaction classes, due to the generic StateMachines. //## //## Changes in Type, ButtonState or Key has to be don in mitkEventMapper.cpp, too. //## @ingroup Interaction /*Prefixes for Constants: E = Enumeration EID = EventId's Op = Operations Ac = Action Type_ = Type of Event BS_ = ButtonStates and Buttons Key_ = Keys like in QT */ namespace mitk{ //Constants for EventIds; use the according constant to through an event in the code enum EEventIds { EIDNULLEVENT = 0, EIDLEFTMOUSEBTN = 1, EIDRIGHTMOUSEBTN = 2, EIDLEFTMOUSEBTNANDSHIFT = 3, EIDMIDDLEMOUSEBTN = 4, EIDLEFTMOUSEBTNANDCTRL = 5, EIDMIDDLEMOUSEBTNANDCTRL = 6, EIDRIGHTMOUSEBTNANDCTRL = 7, EIDLEFTMOUSEBTNDOUBLECLICK = 8, EIDMOUSEWHEEL = 9, EIDLEFTMOUSERELEASE = 505, EIDMIDDLEMOUSERELEASE = 506, EIDRIGHTMOUSERELEASE = 507, EIDLEFTMOUSERELEASEANDSHIFT = 508, EIDMOUSEMOVE = 520, EIDLEFTMOUSEBTNANDMOUSEWHEEL = 521, EIDRIGHTMOUSEBTNANDMOUSEWHEEL = 522, EIDMIDDLEMOUSEBTNANDMOUSEWHEEL = 523, EIDLEFTMOUSEBTNANDMOUSEMOVE = 530, EIDRIGHTMOUSEBTNANDMOUSEMOVE = 531, EIDMIDDLEMOUSEBTNANDMOUSEMOVE = 533, EIDCTRLANDLEFTMOUSEBTNANDMOUSEMOVE = 534, EIDCTRLANDRIGHTMOUSEBTNANDMOUSEMOVE = 535, EIDCTRLANDMIDDLEMOUSEBTNANDMOUSEMOVE = 536, EIDCTRLANDLEFTMOUSEBTNRELEASE = 537, EIDCTRLANDRIGHTMOUSEBTNRELEASE = 538, EIDCTRLANDMIDDLEMOUSEBTNRELEASE = 539, EIDSHIFTANDCTRLANDMIDDLEMOUSEBTN = 540, EIDSHIFTANDLEFTMOUSEBTNANDMOUSEMOVE = 541, EIDSHIFTANDCTRLANDMOUSEMOVE = 542, EIDSHIFTANDCTRLANDMOUSERELEASE = 543, EIDALTANDLEFTMOUSEBTN = 600, EIDALTANDLEFTMOUSEBTNANDMOUSEMOVE = 610, EIDALTANDLEFTMOUSERELEASE = 620, EIDCTRLANDLEFTMOUSEWHEEL = 630, EIDALTANDMOUSEWHEEL = 640, EIDALTANDMIDDLEMOUSEBTN = 641, EIDALTANDMIDDLEMOUSEBTNANDMOVE = 642, EIDALTANDMIDDLEMOUSEBTNRELEASE = 643, EIDALTANDSHIFTANDRIGHTMOUSEBTN = 644, EIDALTANDSHIFTANDRIGHTMOUSEBTNANDMOUSEMOVE = 645, EIDALTANDSHIFTANDRIGHTMOUSEBTNRELEASE = 646, EIDSHIFTANDRIGHTMOUSEPRESS = 2000, EIDSHIFTANDRIGHTMOUSEMOVE = 2001, EIDSHIFTANDRIGHTMOUSERELEASE = 2002, EIDSHIFTANDMIDDLEMOUSEPRESS = 2003, EIDSHIFTANDMIDDLEMOUSEMOVE = 2004, EIDSHIFTANDMIDDLEMOUSERELEASE = 2005, EIDSPACENAVIGATORINPUT = 4001, // 3d Mouse, SpaceNavigator input EIDSPACENAVIGATORKEYDOWN = 4002, // 3d Mouse, KeyDown EIDWIIMOTEINPUT = 4003, // WiiMote input EIDWIIMOTEBUTTON = 4004, // WiiMote home button EIDWIIMOTEBUTTONB = 4005, // WiiMote b button EIDSTRGANDN = 10, EIDSTRGANDE = 11, EIDDELETE = 12, EIDN = 13, EIDESCAPE = 14, EIDP = 15, EIDR = 16, EIDT = 17, EIDS = 18, EIDE = 19, EIDSTRGANDALTANDA = 20, EIDSTRGANDALTANDB = 21, EIDH = 22, EIDRETURN = 23, EIDENTER = 24, EIDSPACE = 25, EIDPLUS = 26, EIDMINUS = 27, EIDSTRGANDALTANDH = 30, EIDSTRGANDALTANDI = 31, EIDSTRGANDALTANDS = 40, EIDALT = 90, EIDSTRGANDB = 91, EIDNEW = 1000, EIDOLD = 1001, EIDFINISHED = 1002, EIDNO = 1003, EIDYES = 1004, EIDSAME = 1005, EIDNOANDLASTOBJECT = 1006, EIDNOANDNOTLASTOBJECT = 1007, EIDLAST = 1008, EIDNOTLAST = 1009, EIDSTSMALERNMINUS1 = 1010, EIDSTLARGERNMINUS1 = 1011, EIDPOSITIONEVENT = 1012, EIDEDIT = 1013, EIDSMALLERN = 1014, EIDEQUALSN = 1015, EIDLARGERN = 1016, EIDEMPTY = 1017, EIDSUBDESELECT = 1020, EIDSMTOSELECTED = 1030, EIDSMTODESELECTED = 1031, EIDTIP = 1050, EIDHEAD = 1051, EIDBODY = 1052, EIDCLEAR = 1100, EIDACTIVATETOOL = 1300, EIDPRINT = 3001, EV_INIT = 5551001, EV_PREVIOUS = 5551002, EV_PATH_COLLECTION_SELECTED = 5551003, EV_NAVIGATION_SELECTED = 5551004, EV_LESS_THEN_MIN_COUNT = 5551005, EV_READY = 5551006, EV_NEXT = 5551007, EV_DONE = 5551008, EV_NEW_LANDMARK = 5551009, EV_REMOVE_LANDMARK = 5551010, EIDINSIDE = 2500, EIDFIGUREHOVER = 12340, EIDNOFIGUREHOVER = 12341 }; //##Constants for Operations //## xomments are always examples of the usage enum EOperations { OpNOTHING = 0, OpTEST = 1, OpNEWCELL = 10, //add a new cell OpADD = 100, //add a point or a vessel OpUNDOADD = 101, OpADDLINE = 1001, //add a line OpINSERT = 200, //insert a point at position OpINSERTLINE = 201, //insert a line at position OpINSERTPOINT = 202, OpCLOSECELL = 250, //close a cell (to a polygon) OpOPENCELL = 251, //close a cell (to a polygon) OpMOVE = 300, //move a point OpMOVELINE = 301, //move a line OpMOVECELL = 302, //move a line OpUNDOMOVE = 303, OpMOVEPOINTUP = 304, OpMOVEPOINTDOWN = 305, OpREMOVE = 400, //remove a point at position OpREMOVELINE = 401, //remove a line at position OpREMOVECELL = 402, //remove a cell OpREMOVEPOINT = 403, OpDELETE = 500, //delete OpDELETELINE = 501, //delete the last line in a cell OpUNDELETE = 502, OpDELETECELL = 505, OpSTATECHANGE = 600, //change a state OpTIMECHANGE = 601, //change a state OpTERMINATE = 666, //change a state OpSELECTPOINT = 700, OpSELECTLINE = 701, OpSELECTCELL = 702, OpSELECTSUBOBJECT = 703, //for VesselGraphInteractor //OpSELECTNEWSUBOBJECT = 704, //for VesselGraphInteractor OpSELECT = 705, OpDESELECTPOINT = 800, OpDESELECTLINE = 801, OpDESELECTCELL = 802, OpDESELECTSUBOBJECT = 803, //for VesselGraphInteractor OpDESELECTALL = 804, //for VesselGraphInteractor OpDESELECT = 805, OpNAVIGATE = 900, OpZOOM = 1000, OpSCALE = 1100, OpROTATE = 1200, OpORIENT = 1201, + OpRESTOREPLANEPOSITION = 1202, OpSETPOINTTYPE = 1210, OpMODECHANGE = 1500, OpSENDCOORDINATES = 1600, OpPERIPHERYSEARCH = 2000, //used in VesselGraphInteractor OpROOTSEARCH = 2001, //used in VesselGraphInteractor OpTHICKSTVESSELSEARCH = 2002, //used in VesselGraphInteractor OpSHORTESTPATHSEARCH = 2003, //used in VesselGraphInteractor OpATTRIBUTATION = 2004, //used in VesselGraphInteractor OpDEFAULT = 2006, //used in VesselGraphInteractor OpSURFACECHANGED = 3000, // used for changing polydata in surfaces }; //##Constants for EventMapping... //##connects the statemachine.xml-File with the implemented conditions. //##within one statemachine the choice of the actionconstants is freely //## //## ActionId enum EActions { AcDONOTHING = 0, AcINITNEWOBJECT = 5, AcINITEDITOBJECT = 6, AcINITEDITGROUP = 7, AcINITMOVEMENT = 8, AcINITMOVE = 9, AcINITFOREGROUND = 45, // used in SeedsInteractor for setting the foreground seeds AcINITBACKGROUND = 46, // used in SeedsInteractor for setting the background seeds AcINITNEUTRAL = 47, // used in SeedsInteractor for setting the neutral seeds (rubber) AcINITUPDATE = 1235, // For shape model deformation AcADDPOINT = 10, AcADD = 11, AcADDLINE = 12, AcADDANDFINISH = 13, AcADDSELECTEDTOGROUP = 64, AcCHECKPOINT = 21, AcCHECKLINE = 22, AcCHECKCELL = 23, AcCHECKELEMENT = 30, // check if there is a element close enough (picking) AcCHECKOBJECT = 31, // check if an object is hit AcCHECKNMINUS1 = 32, // check if the number of elements is equal to N-1 AcCHECKEQUALS1 = 33, // check if the number of elements in the data is equal to 1 AcCHECKNUMBEROFPOINTS = 330, //check the number of elements in the data AcCHECKSELECTED = 34, // check if the given element is selected or not AcCHECKONESELECTED = 340, //check if there is an element that is selected AcCHECKHOVERING = 341, //check if there is an element that is selected AcCHECKGREATERZERO = 35, // check if the current number of elements is greater than 0 AcCHECKGREATERTWO = 36, // check if the current number of elements is greater than two AcCHECKOPERATION = 37, // check if the operation is of one spectial type AcCHECKONESUBINTERACTOR = 38, AcCHECKSUBINTERACTORS = 39, AcFINISHOBJECT = 40, AcFINISHGROUP = 41, AcFINISHMOVEMENT = 42, AcFINISHMOVE = 43, AcFINISH = 44, AcSEARCHOBJECT = 50, AcSEARCHGROUP = 51, AcSEARCHANOTHEROBJECT = 52, // one object is selected and another object is to be added to selection AcSELECTPICKEDOBJECT = 60, // select the picked object and deselect others AcSELECTANOTHEROBJECT = 61, AcSELECTGROUP = 62, AcSELECTALL = 63, AcSELECT = 65, AcSELECTPOINT = 66, AcSELECTLINE = 68, AcSELECTCELL = 67, AcSELECTSUBOBJECT = 69, // used in VesselGraphInteractor AcDESELECTOBJECT = 70, // deselect picked from group AcDESELECTALL = 72, AcDESELECT = 75, AcDESELECTPOINT = 76, AcDESELECTLINE = 78, AcDESELECTCELL = 77, AcNEWPOINT = 80, AcNEWSUBOBJECT = 81, AcMOVEPOINT = 90, AcMOVESELECTED = 91, AcMOVE = 92, AcMOVEPOINTUP = 93, AcMOVEPOINTDOWN = 94, AcREMOVEPOINT = 100, AcREMOVE = 101, AcREMOVELINE = 102, AcREMOVEALL = 103, AcREMOVESELECTEDSUBOBJECT = 104, // used in VesselGraphInteractor AcWHEEL = 105, AcPLUS = 106, AcMINUS = 107, AcDELETEPOINT = 120, AcCLEAR = 130, // clear all elements from a list AcINSERTPOINT = 110, AcINSERTLINE = 111, AC_SET_NEXT_BUTTON_VISIBLE = 5550001, AC_SET_NEXT_BUTTON_INVISIBLE = 5550002, AC_SET_PREVIOUS_BUTTON_VISIBLE = 5550003, AC_SET_PREVIOUS_BUTTON_INVISIBLE = 5550004, AC_SET_ASSISTAND_WIDGET_STECK = 5550005, AC_SETMAX_COUNT_REF_POINTS = 5550006, AC_SET_NEXT_BUTTON_TEXT = 5550007, AC_CHECK_LANDMARK_COUNT = 5550008, AC_SET_DONE_FALSE = 5550009, AC_INIT = 55500010, AC_SET_APPLICATION_SELECTED_FALSE = 55500011, AC_SENSOR_ATTACHED = 55500012, AC_CLOSE_ASSISTENT = 55500013, AC_START_APPLICATION_TEXT = 55500014, AC_START_NAVIGATION = 55500015, AC_START_PATHCOLLECTION = 55500016, AC_LOAD_LANDMARKS = 55500017, AC_CALCULATE_LANDMARK_TRANSFORM = 55500018, AcTERMINATE_INTERACTION = 666, AcTRANSLATESTART = 1000, AcTRANSLATE = 1001, AcSCALESTART = 1002, AcSCALE = 1003, AcROTATESTART = 1004, AcROTATE = 1005, AcINITAFFINEINTERACTIONS = 1006, AcFINISHAFFINEINTERACTIONS = 1007, AcTRANSLATEEND = 1008, AcSCALEEND = 1009, AcROTATEEND = 1010, AcINITZOOM = 1011, AcZOOM = 1012, AcSCROLL = 1013, AcLEVELWINDOW = 1014, AcSCROLLMOUSEWHEEL = 1015, AcSETSTARTPOINT = 1050, AcMODEDESELECT = 1100, // set interactor in not selected mode AcMODESELECT = 1101, // set interactor in selected mode AcMODESUBSELECT = 1102, // set interacor in sub selected mode AcINFORMLISTENERS = 1200, // GlobalInteraction AcASKINTERACTORS = 1201, // GlobalInteraction AcCHECKGREATERONE = 1500, AcCHECKBOUNDINGBOX = 1510, AcFORCESUBINTERACTORS = 1550, AcSENDCOORDINATES = 1600, AcTRANSMITEVENT = 2000, // to transmit an event to a lower Interactor/Statemachine AcPERIPHERYSEARCH = 3000, // used in VesselGraphInteractor AcROOTSEARCH = 3001, // used in VesselGraphInteractor AcTHICKSTVESSELSEARCH = 3002, // used in VesselGraphInteractor AcSHORTESTPATHSEARCH = 3003, // used in VesselGraphInteractor AcSINGLE = 3004, // used in VesselGraphInteractor AcATTRIBUTATION = 3005, // used in VesselGraphInteractor AcDEFAULT = 3007, // used in VesselGraphInteractor AcSETVESSELELEMENT = 3008, // used in VesselGraphInteractor AcCHECKBARRIERSTATUS = 3010, // used in VesselGraphInteractor AcUPDATEMESH = 1234, // For Shape Model Interaction AcINCREASE = 49012, AcDECREASE = 49013, AcMODIFY = 49014, AcUNDOUPDATE = 1236, // For restoring a mesh after an update AcENTEROBJECT = 48000, AcLEAVEOBJECT = 48001, AcSWITCHOBJECT = 48002, AcUPDATELINE = 48003, AcINITLINE = 48004, AcTERMINATELINE = 48005, AcCREATEBOX = 48006, AcCREATEOBJECTFROMLINE = 48007, AcCANCEL = 48008, AcACTIVATETOOL = 48009, AcROTATEAROUNDPOINT1 = 49002, AcROTATEAROUNDPOINT2 = 49003, AcMOVEPOINT1 = 49004, AcMOVEPOINT2 = 49005, AcUPDATEPOINT = 49006, AcUPDATERADIUSMOUSEWHEEL = 49007, AcDISPLAYOPTIONS = 49009, AcCYCLE = 49010, AcACCEPT = 49011, AcONSPACENAVIGATORMOUSEINPUT = 4001, // On input of 3D Mouse AcONPACENAVIGATORKEYDOWN = 4002, // On input of 3D Mouse AcONWIIMOTEINPUT = 4003, // used for wiimote to signal IR input AcRESETVIEW = 4004, // used for wiimote to reset view AcONWIIMOTEBUTTONRELEASED = 4005, // stops the surface interaction AcCHECKPOSITION = 5000, AcINITIALIZECONTOUR = 5001, AcCALCULATENEWSEGMENTATION_SP= 5002, AcINTERACTOR = 5003, AcCALCULATENEWSEGMENTATION_BB= 5004 }; /* //!!!!!!!!!!!!!!!!!!!!!!!! //!!!!!!!!!!!!!!!!!!!!!!!! //EventMechanism: //If you change anything from here on, then change in mitkEventMapper.cpp (Array of constants) as well. //!!!!!!!!!!!!!!!!!!!!!!!! //!!!!!!!!!!!!!!!!!!!!!!!! */ //Type of an Event; enum EEventType { Type_None = 0, // invalid event Type_Timer = 1, // timer event Type_MouseButtonPress = 2, // mouse button pressed Type_MouseButtonRelease = 3, // mouse button released Type_MouseButtonDblClick = 4, // mouse button double click Type_MouseMove = 5, // mouse move Type_KeyPress = 6, // key pressed Type_KeyRelease = 7, // key released Type_FocusIn = 8, // keyboard focus received Type_FocusOut = 9, // keyboard focus lost Type_Enter = 10, // mouse enters widget Type_Leave = 11, // mouse leaves widget Type_Paint = 12, // paint widget Type_Move = 13, // move widget Type_Resize = 14, // resize widget Type_Create = 15, // after object creation Type_Destroy = 16, // during object destruction Type_Show = 17, // widget is shown Type_Hide = 18, // widget is hidden Type_Close = 19, // request to close widget Type_Quit = 20, // request to quit application Type_Reparent = 21, // widget has been reparented Type_ShowMinimized = 22, // widget is shown minimized Type_ShowNormal = 23, // widget is shown normal Type_WindowActivate = 24, // window was activated Type_WindowDeactivate = 25, // window was deactivated Type_ShowToParent = 26, // widget is shown to parent Type_HideToParent = 27, // widget is hidden to parent Type_ShowMaximized = 28, // widget is shown maximized Type_ShowFullScreen = 29, // widget is shown full-screen Type_Accel = 30, // accelerator event Type_Wheel = 31, // wheel event Type_AccelAvailable = 32, // accelerator available event Type_CaptionChange = 33, // caption changed Type_IconChange = 34, // icon changed Type_ParentFontChange = 35, // parent font changed Type_ApplicationFontChange = 36, // application font changed Type_ParentPaletteChange = 37, // parent palette changed Type_ApplicationPaletteChange = 38, // application palette changed Type_PaletteChange = 39, // widget palette changed Type_Clipboard = 40, // internal clipboard event Type_Speech = 42, // reserved for speech input Type_SockAct = 50, // socket activation Type_AccelOverride = 51, // accelerator override event Type_DeferredDelete = 52, // deferred delete event Type_DragEnter = 60, // drag moves into widget Type_DragMove = 61, // drag moves in widget Type_DragLeave = 62, // drag leaves or is cancelled Type_Drop = 63, // actual drop Type_DragResponse = 64, // drag accepted/rejected Type_ChildInserted = 70, // new child widget Type_ChildRemoved = 71, // deleted child widget Type_LayoutHint = 72, // child min/max size changed Type_ShowWindowRequest = 73, // widget's window should be mapped Type_ActivateControl = 80, // ActiveX activation Type_DeactivateControl = 81, // ActiveX deactivation Type_ContextMenu = 82, // context popup menu Type_IMStart = 83, // input method composition start Type_IMCompose = 84, // input method composition Type_IMEnd = 85, // input method composition end Type_Accessibility = 86, // accessibility information is requested Type_TabletMove = 87, // Wacom tablet event Type_LocaleChange = 88, // the system locale changed Type_LanguageChange = 89, // the application language changed Type_LayoutDirectionChange = 90, // the layout direction changed Type_Style = 91, // internal style event Type_TabletPress = 92, // tablet press Type_TabletRelease = 93, // tablet release Type_User = 1000, // first user event id Type_SpaceNavigatorInput = 1094, // 3D mouse input occured Type_SpaceNavigatorKeyDown = 1095, // 3D mouse input occured Type_WiiMoteInput = 1096, // WiiMote input occured Type_WiiMoteButton= 1097, // WiiMote button pressed Type_MaxUser = 65535 }; //##ButtonState // mouse/keyboard state values //QT combinations if MOUSEBUTTONRelease: left MouseButton + ControlButton: 0x201 enum EButtonStates { BS_NoButton = 0x0000, BS_LeftButton = 0x0001, BS_RightButton = 0x0002, BS_MidButton = 0x0004, BS_MouseButtonMask = 0x0007, BS_ShiftButton = 0x0100, BS_ControlButton = 0x0200, BS_AltButton = 0x0400, BS_MetaButton = 0x0800, BS_KeyButtonMask = 0x0f00, BS_Keypad = 0x4000 }; //##Key enum EKeys { Key_Escape = 0x1000, // misc keys Key_Tab = 0x1001, Key_Backtab = 0x1002, Key_BackTab = 0x1002, //= Key_Backtab Key_Backspace = 0x1003, Key_BackSpace = 0x1003, //= Key_Backspace Key_Return = 0x1004, Key_Enter = 0x1005, Key_Insert = 0x1006, Key_Delete = 0x1007, Key_Pause = 0x1008, Key_Print = 0x1009, Key_SysReq = 0x100a, Key_Home = 0x1010, // cursor movement Key_End = 0x1011, Key_Left = 0x1012, Key_Up = 0x1013, Key_Right = 0x1014, Key_Down = 0x1015, Key_Prior = 0x1016, Key_PageUp = 0x1016, //=Key_Prior Key_Next = 0x1017, Key_PageDown = 0x1017, //=Key_Next Key_Shift = 0x1020, // modifiers Key_Control = 0x1021, Key_Meta = 0x1022, Key_Alt = 0x1023, Key_CapsLock = 0x1024, Key_NumLock = 0x1025, Key_ScrollLock = 0x1026, Key_F1 = 0x1030, // function keys Key_F2 = 0x1031, Key_F3 = 0x1032, Key_F4 = 0x1033, Key_F5 = 0x1034, Key_F6 = 0x1035, Key_F7 = 0x1036, Key_F8 = 0x1037, Key_F9 = 0x1038, Key_F10 = 0x1039, Key_F11 = 0x103a, Key_F12 = 0x103b, Key_F13 = 0x103c, Key_F14 = 0x103d, Key_F15 = 0x103e, Key_F16 = 0x103f, Key_F17 = 0x1040, Key_F18 = 0x1041, Key_F19 = 0x1042, Key_F20 = 0x1043, Key_F21 = 0x1044, Key_F22 = 0x1045, Key_F23 = 0x1046, Key_F24 = 0x1047, Key_F25 = 0x1048, // F25 .. F35 only on X11 Key_F26 = 0x1049, Key_F27 = 0x104a, Key_F28 = 0x104b, Key_F29 = 0x104c, Key_F30 = 0x104d, Key_F31 = 0x104e, Key_F32 = 0x104f, Key_F33 = 0x1050, Key_F34 = 0x1051, Key_F35 = 0x1052, Key_Super_L = 0x1053, // extra keys Key_Super_R = 0x1054, Key_Menu = 0x1055, Key_Hyper_L = 0x1056, Key_Hyper_R = 0x1057, Key_Help = 0x1058, // International input method support (X keycode - = 0xEE00) // Only interesting if you are writing your own input method Key_Muhenkan = 0x1122, // Cancel Conversion Key_Henkan = 0x1123, // Start/Stop Conversion Key_Hiragana_Katakana = 0x1127, // Hiragana/Katakana toggle Key_Zenkaku_Hankaku = 0x112A, // Zenkaku/Hankaku toggle Key_Space = 0x20, // 7 bit printable ASCII Key_Any = 0x20, //= Key_Space Key_Exclam = 0x21, Key_QuoteDbl = 0x22, Key_NumberSign = 0x23, Key_Dollar = 0x24, Key_Percent = 0x25, Key_Ampersand = 0x26, Key_Apostrophe = 0x27, Key_ParenLeft = 0x28, Key_ParenRight = 0x29, Key_Asterisk = 0x2a, Key_Plus = 0x2b, Key_Comma = 0x2c, Key_Minus = 0x2d, Key_Period = 0x2e, Key_Slash = 0x2f, Key_0 = 0x30, Key_1 = 0x31, Key_2 = 0x32, Key_3 = 0x33, Key_4 = 0x34, Key_5 = 0x35, Key_6 = 0x36, Key_7 = 0x37, Key_8 = 0x38, Key_9 = 0x39, Key_Colon = 0x3a, Key_Semicolon = 0x3b, Key_Less = 0x3c, Key_Equal = 0x3d, Key_Greater = 0x3e, Key_Question = 0x3f, Key_At = 0x40, Key_A = 0x41, Key_B = 0x42, Key_C = 0x43, Key_D = 0x44, Key_E = 0x45, Key_F = 0x46, Key_G = 0x47, Key_H = 0x48, Key_I = 0x49, Key_J = 0x4a, Key_K = 0x4b, Key_L = 0x4c, Key_M = 0x4d, Key_N = 0x4e, Key_O = 0x4f, Key_P = 0x50, Key_Q = 0x51, Key_R = 0x52, Key_S = 0x53, Key_T = 0x54, Key_U = 0x55, Key_V = 0x56, Key_W = 0x57, Key_X = 0x58, Key_Y = 0x59, Key_Z = 0x5a, Key_BracketLeft = 0x5b, Key_Backslash = 0x5c, Key_BracketRight = 0x5d, Key_AsciiCircum = 0x5e, Key_Underscore = 0x5f, Key_QuoteLeft = 0x60, Key_BraceLeft = 0x7b, Key_Bar = 0x7c, Key_BraceRight = 0x7d, Key_AsciiTilde = 0x7e, Key_nobreakspace = 0x0a0, Key_exclamdown = 0x0a1, Key_cent = 0x0a2, Key_sterling = 0x0a3, Key_currency = 0x0a4, Key_yen = 0x0a5, Key_brokenbar = 0x0a6, Key_section = 0x0a7, Key_diaeresis = 0x0a8, Key_copyright = 0x0a9, Key_ordfeminine = 0x0aa, Key_guillemotleft = 0x0ab, // left angle quotation mark Key_notsign = 0x0ac, Key_hyphen = 0x0ad, Key_registered = 0x0ae, Key_macron = 0x0af, Key_degree = 0x0b0, Key_plusminus = 0x0b1, Key_twosuperior = 0x0b2, Key_threesuperior = 0x0b3, Key_acute = 0x0b4, Key_mu = 0x0b5, Key_paragraph = 0x0b6, Key_periodcentered = 0x0b7, Key_cedilla = 0x0b8, Key_onesuperior = 0x0b9, Key_masculine = 0x0ba, Key_guillemotright = 0x0bb, // right angle quotation mark Key_onequarter = 0x0bc, Key_onehalf = 0x0bd, Key_threequarters = 0x0be, Key_questiondown = 0x0bf, Key_Agrave = 0x0c0, Key_Aacute = 0x0c1, Key_Acircumflex = 0x0c2, Key_Atilde = 0x0c3, Key_Adiaeresis = 0x0c4, Key_Aring = 0x0c5, Key_AE = 0x0c6, Key_Ccedilla = 0x0c7, Key_Egrave = 0x0c8, Key_Eacute = 0x0c9, Key_Ecircumflex = 0x0ca, Key_Ediaeresis = 0x0cb, Key_Igrave = 0x0cc, Key_Iacute = 0x0cd, Key_Icircumflex = 0x0ce, Key_Idiaeresis = 0x0cf, Key_ETH = 0x0d0, Key_Ntilde = 0x0d1, Key_Ograve = 0x0d2, Key_Oacute = 0x0d3, Key_Ocircumflex = 0x0d4, Key_Otilde = 0x0d5, Key_Odiaeresis = 0x0d6, Key_multiply = 0x0d7, Key_Ooblique = 0x0d8, Key_Ugrave = 0x0d9, Key_Uacute = 0x0da, Key_Ucircumflex = 0x0db, Key_Udiaeresis = 0x0dc, Key_Yacute = 0x0dd, Key_THORN = 0x0de, Key_ssharp = 0x0df, Key_agrave = 0x0e0, Key_aacute = 0x0e1, Key_acircumflex = 0x0e2, Key_atilde = 0x0e3, Key_adiaeresis = 0x0e4, Key_aring = 0x0e5, Key_ae = 0x0e6, Key_ccedilla = 0x0e7, Key_egrave = 0x0e8, Key_eacute = 0x0e9, Key_ecircumflex = 0x0ea, Key_ediaeresis = 0x0eb, Key_igrave = 0x0ec, Key_iacute = 0x0ed, Key_icircumflex = 0x0ee, Key_idiaeresis = 0x0ef, Key_eth = 0x0f0, Key_ntilde = 0x0f1, Key_ograve = 0x0f2, Key_oacute = 0x0f3, Key_ocircumflex = 0x0f4, Key_otilde = 0x0f5, Key_odiaeresis = 0x0f6, Key_division = 0x0f7, Key_oslash = 0x0f8, Key_ugrave = 0x0f9, Key_uacute = 0x0fa, Key_ucircumflex = 0x0fb, Key_udiaeresis = 0x0fc, Key_yacute = 0x0fd, Key_thorn = 0x0fe, Key_ydiaeresis = 0x0ff, Key_unknown = 0xffff, Key_none = 0xffff//= Key_unknown }; }//namespace mitk #endif //ifndef MITKINTERACTCONST_H diff --git a/Core/Code/Service/mitkCoreActivator.cpp b/Core/Code/Service/mitkCoreActivator.cpp index 78c6107eeb..dfeffc4e2e 100644 --- a/Core/Code/Service/mitkCoreActivator.cpp +++ b/Core/Code/Service/mitkCoreActivator.cpp @@ -1,47 +1,54 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkRenderingManager.h" +#include "mitkPlanePositionManager.h" #include /* * This is the module activator for the "Mitk" module. It registers core services * like ... */ class MitkCoreActivator : public mitk::ModuleActivator { public: void Load(mitk::ModuleContext* context) { //m_RenderingManager = mitk::RenderingManager::New(); //context->RegisterService(renderingManager.GetPointer()); + m_PlanePositionManager = mitk::PlanePositionManagerService::New(); + m_PlanePositionManagerReg = context->RegisterService(m_PlanePositionManager); } void Unload(mitk::ModuleContext* ) { + m_PlanePositionManagerReg.Unregister(); + m_PlanePositionManager = 0; } private: mitk::RenderingManager::Pointer m_RenderingManager; + mitk::PlanePositionManagerService::Pointer m_PlanePositionManager; + mitk::ServiceRegistration m_PlanePositionManagerReg; }; MITK_EXPORT_MODULE_ACTIVATOR(Mitk, MitkCoreActivator) diff --git a/Core/Code/Testing/files.cmake b/Core/Code/Testing/files.cmake index 7a11434d55..7f43dda1e1 100644 --- a/Core/Code/Testing/files.cmake +++ b/Core/Code/Testing/files.cmake @@ -1,118 +1,119 @@ # tests with no extra command line parameter SET(MODULE_TESTS mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkPointSetWriterTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkEnumerationPropertyTest.cpp mitkEventTest.cpp mitkFocusManagerTest.cpp mitkGenericPropertyTest.cpp mitkGeometry3DTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkGlobalInteractionTest.cpp mitkImageDataItemTest.cpp #mitkImageMapper2DTest.cpp mitkImageGeneratorTest.cpp mitkBaseDataTest.cpp #mitkImageToItkTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkInteractorTest.cpp mitkITKThreadingTest.cpp mitkLDAPFilterTest.cpp # mitkLevelWindowManagerTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp mitkModuleTest.cpp #mitkPipelineSmartPointerCorrectnessTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetTest.cpp mitkPointSetInteractorTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp #mitkRegistrationBaseTest.cpp #mitkSegmentationInterpolationTest.cpp mitkServiceListenerTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkStateMachineTest.cpp mitkStateTest.cpp mitkSurfaceTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeSlicedGeometryTest.cpp mitkTransitionTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp #mitkAbstractTransformGeometryTest.cpp #mitkPicFileIOTest.cpp mitkStepperTest.cpp itkTotalVariationDenoisingImageFilterTest.cpp mitkRenderingManagerTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp ) # test with image filename as an extra command line parameter SET(MODULE_IMAGE_TESTS + mitkPlanePositionManagerTest.cpp mitkSurfaceVtkWriterTest.cpp mitkPicFileWriterTest.cpp #mitkImageSliceSelectorTest.cpp mitkImageTimeSelectorTest.cpp mitkPicFileReaderTest.cpp # mitkVtkPropRendererTest.cpp mitkDataNodeFactoryTest.cpp #mitkSTLFileReaderTest.cpp ) # list of images for which the tests are run SET(MODULE_TESTIMAGES US4DCyl.pic.gz Pic3D.pic.gz Pic2DplusT.pic.gz BallBinary30x30x30.pic.gz binary.stl ball.stl ) SET(MODULE_CUSTOM_TESTS #mitkLabeledImageToSurfaceFilterTest.cpp #mitkExternalToolsTest.cpp mitkDataStorageTest.cpp mitkDataNodeTest.cpp mitkDicomSeriesReaderTest.cpp mitkDICOMLocaleTest.cpp mitkEventMapperTest.cpp mitkNodeDependentPointSetInteractorTest.cpp mitkStateMachineFactoryTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageWriterTest.cpp ) # Create an artificial module initializing class for # the mitkServiceListenerTest.cpp SET(module_name_orig ${MODULE_NAME}) SET(module_libname_orig ${MODULE_LIBNAME}) SET(MODULE_NAME "${MODULE_NAME}TestDriver") SET(MODULE_LIBNAME "") SET(MODULE_DEPENDS_STR "Mitk") SET(MODULE_PACKAGE_DEPENDS_STR "") SET(MODULE_VERSION "0.1.0") SET(MODULE_QT_BOOL "false") SET(testdriver_init_file "${CMAKE_CURRENT_BINARY_DIR}/MitkTestDriver_init.cpp") CONFIGURE_FILE("${MITK_SOURCE_DIR}/CMake/mitkModuleInit.cpp" ${testdriver_init_file} @ONLY) SET(TEST_CPP_FILES ${testdriver_init_file}) SET(MODULE_NAME ${module_name_orig}) SET(MODULE_LIBNAME ${module_libname_orig}) diff --git a/Core/Code/Testing/mitkPlanePositionManagerTest.cpp b/Core/Code/Testing/mitkPlanePositionManagerTest.cpp new file mode 100644 index 0000000000..a783e80e81 --- /dev/null +++ b/Core/Code/Testing/mitkPlanePositionManagerTest.cpp @@ -0,0 +1,255 @@ +#include "mitkRotationOperation.h" +#include "mitkTestingMacros.h" +#include "mitkPlanePositionManager.h" +#include "mitkSliceNavigationController.h" +#include "mitkGeometry3D.h" +#include "mitkPlaneGeometry.h" +#include "mitkImage.h" +#include "mitkSurface.h" +#include "mitkStandaloneDataStorage.h" +#include "mitkDataNode.h" +#include "mitkStringProperty.h" +#include "mitkBaseProperty.h" +#include "mitkInteractionConst.h" +#include "vnl/vnl_vector.h" +#include +#include "mitkGetModuleContext.h" + +std::vector m_Geometries; +std::vector m_SliceIndices; + +mitk::PlanePositionManagerService* m_Service; + + +int SetUpBeforeTest() +{ + //Getting Service + mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); + m_Service = dynamic_cast(mitk::GetModuleContext()->GetService(serviceRef)); + + if (m_Service == 0) + return EXIT_FAILURE; + + //Creating different Geometries + m_Geometries.reserve(100); + mitk::PlaneGeometry::PlaneOrientation views[] = {mitk::PlaneGeometry::Transversal, mitk::PlaneGeometry::Sagittal, mitk::PlaneGeometry::Frontal}; + for (unsigned int i = 0; i < 100; ++i) + { + mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); + + mitk::ScalarType width = 256+(0.01*i); + mitk::ScalarType height = 256+(0.002*i); + + mitk::Vector3D right; + mitk::Vector3D down; + right[0] = 1; + right[1] = i; + right[2] = 0.5; + down[0] = i*0.02; + down[1] = 1; + down[2] = i*0.03; + + mitk::Vector3D spacing; + mitk::FillVector3D(spacing, 1.0*0.02*i, 1.0*0.15*i, 1.0); + + mitk::Vector3D rightVector; + mitk::FillVector3D(rightVector, 0.02*(i+1), 0+(0.05*i), 1.0); + + mitk::Vector3D downVector; + mitk::FillVector3D(downVector, 1, 3-0.01*i, 0.0345*i); + + vnl_vector normal = vnl_cross_3d(rightVector.GetVnlVector(), downVector.GetVnlVector()); + normal.normalize(); + normal *= 1.5; + + mitk::Vector3D origin; + origin.Fill(1); + origin[0] = 12 + 0.03*i; + + + mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); + mitk::Matrix3D matrix; + matrix.GetVnlMatrix().set_column(0, rightVector.GetVnlVector()); + matrix.GetVnlMatrix().set_column(1, downVector.GetVnlVector()); + matrix.GetVnlMatrix().set_column(2, normal); + transform->SetMatrix(matrix); + transform->SetOffset(origin); + + + plane->InitializeStandardPlane(width, height, transform, + views[i%3], i, + true, false); + + m_Geometries.push_back(plane); + } +} + +int testAddPlanePosition() +{ + MITK_TEST_OUTPUT(<<"Starting Test: ######### A d d P l a n e P o s i t i o n #########"); + + MITK_TEST_CONDITION(m_Service != NULL, "Testing getting of PlanePositionManagerService"); + + unsigned int currentID(m_Service->AddNewPlanePosition(m_Geometries.at(0),0)); + + bool error = ((m_Service->GetNumberOfPlanePositions() != 1)||(currentID != 0)); + + if(error) + { + MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == 1,"Checking for correct number of planepositions"); + MITK_TEST_CONDITION(currentID == 0, "Testing for correct ID"); + return EXIT_FAILURE; + } + + //Adding new planes + for(unsigned int i = 1; i < m_Geometries.size(); ++i) + { + unsigned int newID = m_Service->AddNewPlanePosition(m_Geometries.at(i),i); + error = ((m_Service->GetNumberOfPlanePositions() != i+1)||(newID != (currentID+1))); + + if (error) + { + MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == i+1,"Checking for correct number of planepositions"); + MITK_TEST_CONDITION(newID == (currentID+1), "Testing for correct ID"); + MITK_TEST_OUTPUT(<<"New: "<GetNumberOfPlanePositions(); + + //Adding existing planes -> nothing should change + for(unsigned int i = 0; i < (m_Geometries.size()-1)*0.5; ++i) + { + unsigned int newID = m_Service->AddNewPlanePosition(m_Geometries.at(i*2),i*2); + error = ((m_Service->GetNumberOfPlanePositions() != numberOfPlanePos)||(newID != i*2)); + if (error) + { + MITK_TEST_CONDITION( m_Service->GetNumberOfPlanePositions() == numberOfPlanePos, "Checking for correct number of planepositions"); + MITK_TEST_CONDITION(newID == i*2, "Testing for correct ID"); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; + +} + +int testGetPlanePosition() +{ + mitk::PlaneGeometry* plane; + mitk::RestorePlanePositionOperation* op; + bool error(true); + + MITK_TEST_OUTPUT(<<"Starting Test: ######### G e t P l a n e P o s i t i o n #########"); + + //Testing for existing planepositions + for (unsigned int i = 0; i < m_Geometries.size(); ++i) + { + plane = m_Geometries.at(i); + mitk::PlaneGeometry* test = m_Geometries.at(i); + op = m_Service->GetPlanePosition(i); + error = ( !mitk::Equal(op->GetHeight(),plane->GetExtent(1)) || + !mitk::Equal(op->GetWidth(),plane->GetExtent(0)) || + !mitk::Equal(op->GetSpacing(),plane->GetSpacing()) || + !mitk::Equal(op->GetTransform()->GetOffset(),plane->GetIndexToWorldTransform()->GetOffset()) || + !mitk::Equal(op->GetDirectionVector().Get_vnl_vector(),plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).normalize()) || + !mitk::MatrixEqualElementWise(op->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix()) ); + + if( error ) + { + MITK_TEST_OUTPUT(<<"Iteration: "<GetHeight(),plane->GetExtent(1)) && mitk::Equal(op->GetWidth(),plane->GetExtent(0)), "Checking for correct extent"); + MITK_TEST_CONDITION( mitk::Equal(op->GetSpacing(),plane->GetSpacing()), "Checking for correct spacing"); + MITK_TEST_CONDITION( mitk::Equal(op->GetTransform()->GetOffset(),plane->GetIndexToWorldTransform()->GetOffset()), "Checking for correct offset"); + MITK_INFO<<"Op: "<GetDirectionVector()<<" plane: "<GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)<<"\n"; + MITK_TEST_CONDITION( mitk::Equal(op->GetDirectionVector().Get_vnl_vector(),plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)), "Checking for correct direction"); + MITK_TEST_CONDITION( mitk::MatrixEqualElementWise(op->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix()), "Checking for correct matrix"); + return EXIT_FAILURE; + } + } + + //Testing for not existing planepositions + error = ( m_Service->GetPlanePosition(100000000) != 0 || + m_Service->GetPlanePosition(-1) != 0 ); + + if (error) + { + MITK_TEST_CONDITION(m_Service->GetPlanePosition(100000000) == 0, "Trying to get non existing pos"); + MITK_TEST_CONDITION(m_Service->GetPlanePosition(-1) == 0, "Trying to get non existing pos"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int testRemovePlanePosition() +{ + MITK_TEST_OUTPUT(<<"Starting Test: ######### R e m o v e P l a n e P o s i t i o n #########"); + unsigned int size = m_Service->GetNumberOfPlanePositions(); + + bool removed (true); + //Testing for invalid IDs + removed = m_Service->RemovePlanePosition( -1 ); + removed = m_Service->RemovePlanePosition( 1000000 ); + unsigned int size2 = m_Service->GetNumberOfPlanePositions(); + + if (removed) + { + MITK_TEST_CONDITION(removed == false, "Testing remove not existing planepositions"); + MITK_TEST_CONDITION(size == size2, "Testing remove not existing planepositions"); + return EXIT_FAILURE; + } + + //Testing for valid IDs + for (unsigned int i = 0; i < m_Geometries.size()*0.5; i++) + { + removed = m_Service->RemovePlanePosition( i ); + unsigned int size2 = m_Service->GetNumberOfPlanePositions(); + removed = (size2 == (size-(i+1))); + if (!removed) + { + MITK_TEST_CONDITION(removed == true, "Testing remove existing planepositions"); + MITK_TEST_CONDITION(size == (size-i+1), "Testing remove existing planepositions"); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +int testRemoveAll() +{ + MITK_TEST_OUTPUT(<<"Starting Test: ######### R e m o v e A l l #########"); + + unsigned int numPos = m_Service->GetNumberOfPlanePositions(); + MITK_INFO<RemoveAllPlanePositions(); + + bool error (true); + + error = (m_Service->GetNumberOfPlanePositions() != 0 || + m_Service->GetPlanePosition(60) != 0); + + if (error) + { + MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == 0, "Testing remove all pos"); + MITK_TEST_CONDITION(m_Service->GetPlanePosition(60) == 0, "Testing remove all pos"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +int mitkPlanePositionManagerTest(int argc, char* argv[]) +{ + MITK_TEST_OUTPUT(<<"Starting Test PlanePositionManager"); + SetUpBeforeTest(); + int result; + MITK_TEST_CONDITION_REQUIRED( (result = testAddPlanePosition()) == EXIT_SUCCESS, ""); + MITK_TEST_CONDITION_REQUIRED( (result = testGetPlanePosition()) == EXIT_SUCCESS, ""); + MITK_TEST_CONDITION_REQUIRED( (result = testRemovePlanePosition()) == EXIT_SUCCESS, ""); + MITK_TEST_CONDITION_REQUIRED( (result = testRemoveAll()) == EXIT_SUCCESS, ""); + return EXIT_SUCCESS; +} diff --git a/Core/Code/Testing/mitkSliceNavigationControllerTest.cpp b/Core/Code/Testing/mitkSliceNavigationControllerTest.cpp index 3ad52e6217..5fe7e907e7 100644 --- a/Core/Code/Testing/mitkSliceNavigationControllerTest.cpp +++ b/Core/Code/Testing/mitkSliceNavigationControllerTest.cpp @@ -1,300 +1,420 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkSliceNavigationController.h" #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" #include "mitkTimeSlicedGeometry.h" +#include "mitkRotationOperation.h" +#include "mitkInteractionConst.h" +#include "mitkPlanePositionManager.h" +#include "mitkTestingMacros.h" +#include "mitkGetModuleContext.h" #include #include #include bool operator==(const mitk::Geometry3D & left, const mitk::Geometry3D & right) { mitk::BoundingBox::BoundsArrayType leftbounds, rightbounds; leftbounds =left.GetBounds(); rightbounds=right.GetBounds(); unsigned int i; for(i=0;i<6;++i) if(mitk::Equal(leftbounds[i],rightbounds[i])==false) return false; const mitk::Geometry3D::TransformType::MatrixType & leftmatrix = left.GetIndexToWorldTransform()->GetMatrix(); const mitk::Geometry3D::TransformType::MatrixType & rightmatrix = right.GetIndexToWorldTransform()->GetMatrix(); unsigned int j; for(i=0;i<3;++i) { const mitk::Geometry3D::TransformType::MatrixType::ValueType* leftvector = leftmatrix[i]; const mitk::Geometry3D::TransformType::MatrixType::ValueType* rightvector = rightmatrix[i]; for(j=0;j<3;++j) if(mitk::Equal(leftvector[i],rightvector[i])==false) return false; } const mitk::Geometry3D::TransformType::OffsetType & leftoffset = left.GetIndexToWorldTransform()->GetOffset(); const mitk::Geometry3D::TransformType::OffsetType & rightoffset = right.GetIndexToWorldTransform()->GetOffset(); for(i=0;i<3;++i) if(mitk::Equal(leftoffset[i],rightoffset[i])==false) return false; return true; } int compareGeometry(const mitk::Geometry3D & geometry, const mitk::ScalarType& width, const mitk::ScalarType& height, const mitk::ScalarType& numSlices, const mitk::ScalarType& widthInMM, const mitk::ScalarType& heightInMM, const mitk::ScalarType& thicknessInMM, const mitk::Point3D& cornerpoint0, const mitk::Vector3D& right, const mitk::Vector3D& bottom, const mitk::Vector3D& normal) { std::cout << "Testing width, height and thickness (in units): "; if((mitk::Equal(geometry.GetExtent(0),width)==false) || (mitk::Equal(geometry.GetExtent(1),height)==false) || (mitk::Equal(geometry.GetExtent(2),numSlices)==false) ) { std::cout<<"[FAILED]"<GetCornerPoint(0), cornerpoint0)==false) { std::cout<<"[FAILED]"<SetInputWorldGeometry(geometry); std::cout<<"[PASSED]"<SetViewDirection(mitk::SliceNavigationController::Transversal); std::cout<<"[PASSED]"<Update(); std::cout<<"[PASSED]"<GetCreatedWorldGeometry(), width, height, numSlices, widthInMM, heightInMM, thicknessInMM*numSlices, transversalcornerpoint0, right, bottom*(-1.0), normal*(-1.0)); if(result!=EXIT_SUCCESS) { std::cout<<"[FAILED]"<SetViewDirection(mitk::SliceNavigationController::Frontal); std::cout<<"[PASSED]"<Update(); std::cout<<"[PASSED]"<GetAxisVector(1)*(+0.5/geometry->GetExtent(1)); result = compareGeometry(*sliceCtrl->GetCreatedWorldGeometry(), width, numSlices, height, widthInMM, thicknessInMM*numSlices, heightInMM, frontalcornerpoint0, right, normal, bottom); if(result!=EXIT_SUCCESS) { std::cout<<"[FAILED]"<SetViewDirection(mitk::SliceNavigationController::Sagittal); std::cout<<"[PASSED]"<Update(); std::cout<<"[PASSED]"<GetAxisVector(0)*(+0.5/geometry->GetExtent(0)); result = compareGeometry(*sliceCtrl->GetCreatedWorldGeometry(), height, numSlices, width, heightInMM, thicknessInMM*numSlices, widthInMM, sagittalcornerpoint0, bottom, normal, right); if(result!=EXIT_SUCCESS) { std::cout<<"[FAILED]"<InitializeStandardPlane(right.Get_vnl_vector(), bottom.Get_vnl_vector(), &spacing); + planegeometry->SetOrigin(origin); + + //Create SlicedGeometry3D out of planeGeometry + mitk::SlicedGeometry3D::Pointer slicedgeometry1 = mitk::SlicedGeometry3D::New(); + unsigned int numSlices = 300; + slicedgeometry1->InitializeEvenlySpaced(planegeometry, thicknessInMM, numSlices, false); + + //Create another slicedgeo which will be rotated + mitk::SlicedGeometry3D::Pointer slicedgeometry2 = mitk::SlicedGeometry3D::New(); + slicedgeometry2->InitializeEvenlySpaced(planegeometry, thicknessInMM, numSlices, false); + + //Create geo3D as reference + mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); + geometry->SetBounds(slicedgeometry1->GetBounds()); + geometry->SetIndexToWorldTransform(slicedgeometry1->GetIndexToWorldTransform()); + + //Initialize planes + for (int i=0; i < numSlices; i++) + { + mitk::PlaneGeometry::Pointer geo2d = mitk::PlaneGeometry::New(); + geo2d->Initialize(); + geo2d->SetReferenceGeometry(geometry); + slicedgeometry1->SetGeometry2D(geo2d,i); + } + + for (int i=0; i < numSlices; i++) + { + mitk::PlaneGeometry::Pointer geo2d = mitk::PlaneGeometry::New(); + geo2d->Initialize(); + geo2d->SetReferenceGeometry(geometry); + slicedgeometry2->SetGeometry2D(geo2d,i); + } + + slicedgeometry1->SetReferenceGeometry(geometry); + slicedgeometry2->SetReferenceGeometry(geometry); + + //Create SNC + mitk::SliceNavigationController::Pointer sliceCtrl1 = mitk::SliceNavigationController::New(); + sliceCtrl1->SetInputWorldGeometry(slicedgeometry1); + sliceCtrl1->Update(); + + mitk::SliceNavigationController::Pointer sliceCtrl2 = mitk::SliceNavigationController::New(); + sliceCtrl2->SetInputWorldGeometry(slicedgeometry2); + sliceCtrl2->Update(); + + slicedgeometry1->SetSliceNavigationController(sliceCtrl1); + slicedgeometry2->SetSliceNavigationController(sliceCtrl2); + + //Rotate slicedgeo2 + double angle = 63.84; + mitk::Vector3D rotationVector; mitk::FillVector3D( rotationVector, 0.5, 0.95, 0.23 ); + mitk::Point3D center = slicedgeometry2->GetCenter(); + mitk::RotationOperation* op = new mitk::RotationOperation( mitk::OpROTATE, center, rotationVector, angle ); + slicedgeometry2->ExecuteOperation(op); + sliceCtrl2->Update(); + + mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); + mitk::PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->GetService(serviceRef)); + service->AddNewPlanePosition(slicedgeometry2->GetGeometry2D(0), 178); + sliceCtrl1->ExecuteOperation(service->GetPlanePosition(0)); + sliceCtrl1->Update(); + mitk::Geometry2D* planeRotated = slicedgeometry2->GetGeometry2D(178); + mitk::Geometry2D* planeRestored = dynamic_cast< const mitk::SlicedGeometry3D*>(sliceCtrl1->GetCurrentGeometry3D())->GetGeometry2D(178); + bool error = ( !mitk::MatrixEqualElementWise(planeRotated->GetIndexToWorldTransform()->GetMatrix(), planeRestored->GetIndexToWorldTransform()->GetMatrix()) || + !mitk::Equal(planeRotated->GetOrigin(), planeRestored->GetOrigin()) || + !mitk::Equal(planeRotated->GetSpacing(), planeRestored->GetSpacing()) || + !mitk::Equal(slicedgeometry2->GetDirectionVector(), dynamic_cast< const mitk::SlicedGeometry3D*>(sliceCtrl1->GetCurrentGeometry3D())->GetDirectionVector()) || + !mitk::Equal(slicedgeometry2->GetSlices(), dynamic_cast< const mitk::SlicedGeometry3D*>(sliceCtrl1->GetCurrentGeometry3D())->GetSlices()) || + !mitk::MatrixEqualElementWise(slicedgeometry2->GetIndexToWorldTransform()->GetMatrix(), dynamic_cast< const mitk::SlicedGeometry3D*>(sliceCtrl1->GetCurrentGeometry3D())->GetIndexToWorldTransform()->GetMatrix()) + ); + + if (error) + { + MITK_TEST_CONDITION(mitk::MatrixEqualElementWise(planeRotated->GetIndexToWorldTransform()->GetMatrix(), planeRestored->GetIndexToWorldTransform()->GetMatrix()),"Testing for IndexToWorld"); + MITK_INFO<<"Rotated: \n"<GetIndexToWorldTransform()->GetMatrix()<<" Restored: \n"<GetIndexToWorldTransform()->GetMatrix(); + MITK_TEST_CONDITION(mitk::Equal(planeRotated->GetOrigin(), planeRestored->GetOrigin()),"Testing for origin"); + MITK_INFO<<"Rotated: \n"<GetOrigin()<<" Restored: \n"<GetOrigin(); + MITK_TEST_CONDITION(mitk::Equal(planeRotated->GetSpacing(), planeRestored->GetSpacing()),"Testing for spacing"); + MITK_INFO<<"Rotated: \n"<GetSpacing()<<" Restored: \n"<GetSpacing(); + MITK_TEST_CONDITION(mitk::Equal(slicedgeometry2->GetDirectionVector(), dynamic_cast< const mitk::SlicedGeometry3D*>(sliceCtrl1->GetCurrentGeometry3D())->GetDirectionVector()),"Testing for directionvector"); + MITK_INFO<<"Rotated: \n"<GetDirectionVector()<<" Restored: \n"<(sliceCtrl1->GetCurrentGeometry3D())->GetDirectionVector(); + MITK_TEST_CONDITION(mitk::Equal(slicedgeometry2->GetSlices(), dynamic_cast< const mitk::SlicedGeometry3D*>(sliceCtrl1->GetCurrentGeometry3D())->GetSlices()),"Testing for numslices"); + MITK_INFO<<"Rotated: \n"<GetSlices()<<" Restored: \n"<(sliceCtrl1->GetCurrentGeometry3D())->GetSlices(); + MITK_TEST_CONDITION(mitk::MatrixEqualElementWise(slicedgeometry2->GetIndexToWorldTransform()->GetMatrix(), dynamic_cast< const mitk::SlicedGeometry3D*>(sliceCtrl1->GetCurrentGeometry3D())->GetIndexToWorldTransform()->GetMatrix()),"Testing for IndexToWorld"); + MITK_INFO<<"Rotated: \n"<GetIndexToWorldTransform()->GetMatrix()<<" Restored: \n"<(sliceCtrl1->GetCurrentGeometry3D())->GetIndexToWorldTransform()->GetMatrix(); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + int mitkSliceNavigationControllerTest(int /*argc*/, char* /*argv*/[]) { int result=EXIT_FAILURE; std::cout << "Creating and initializing a PlaneGeometry: "; mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); mitk::Point3D origin; mitk::Vector3D right, bottom, normal; mitk::ScalarType width, height; mitk::ScalarType widthInMM, heightInMM, thicknessInMM; width = 100; widthInMM = width; height = 200; heightInMM = height; thicknessInMM = 1.5; // mitk::FillVector3D(origin, 0, 0, thicknessInMM*0.5); mitk::FillVector3D(origin, 4.5, 7.3, 11.2); mitk::FillVector3D(right, widthInMM, 0, 0); mitk::FillVector3D(bottom, 0, heightInMM, 0); mitk::FillVector3D(normal, 0, 0, thicknessInMM); mitk::Vector3D spacing; normal.Normalize(); normal *= thicknessInMM; mitk::FillVector3D(spacing, 1.0, 1.0, thicknessInMM); planegeometry->InitializeStandardPlane(right.Get_vnl_vector(), bottom.Get_vnl_vector(), &spacing); planegeometry->SetOrigin(origin); std::cout<<"[PASSED]"<InitializeEvenlySpaced(planegeometry, thicknessInMM, numSlices, false); std::cout<<"[PASSED]"<SetBounds(slicedgeometry->GetBounds()); geometry->SetIndexToWorldTransform(slicedgeometry->GetIndexToWorldTransform()); std::cout<<"[PASSED]"<GetCornerPoint(0); result=testGeometry(geometry, width, height, numSlices, widthInMM, heightInMM, thicknessInMM, cornerpoint0, right, bottom, normal); if(result!=EXIT_SUCCESS) return result; mitk::BoundingBox::BoundsArrayType bounds = geometry->GetBounds(); mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); transform->SetMatrix(geometry->GetIndexToWorldTransform()->GetMatrix()); mitk::BoundingBox::Pointer boundingbox = geometry->CalculateBoundingBoxRelativeToTransform(transform); geometry->SetBounds(boundingbox->GetBounds()); cornerpoint0 = geometry->GetCornerPoint(0); result=testGeometry(geometry, width, height, numSlices, widthInMM, heightInMM, thicknessInMM, cornerpoint0, right, bottom, normal); if(result!=EXIT_SUCCESS) return result; std::cout << "Changing the IndexToWorldTransform of the geometry to a rotated version by SetIndexToWorldTransform() (keep cornerpoint0): "; transform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = planegeometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::VnlVector axis(3); mitk::FillVector3D(axis, 1.0, 1.0, 1.0); axis.normalize(); vnl_quaternion rotation(axis, 0.223); vnlmatrix = rotation.rotation_matrix_transpose()*vnlmatrix; mitk::Matrix3D matrix; matrix = vnlmatrix; transform->SetMatrix(matrix); transform->SetOffset(cornerpoint0.GetVectorFromOrigin()); right.Set_vnl_vector( rotation.rotation_matrix_transpose()*right.Get_vnl_vector() ); bottom.Set_vnl_vector(rotation.rotation_matrix_transpose()*bottom.Get_vnl_vector()); normal.Set_vnl_vector(rotation.rotation_matrix_transpose()*normal.Get_vnl_vector()); geometry->SetIndexToWorldTransform(transform); std::cout<<"[PASSED]"<GetCornerPoint(0); result = testGeometry(geometry, width, height, numSlices, widthInMM, heightInMM, thicknessInMM, cornerpoint0, right, bottom, normal); if(result!=EXIT_SUCCESS) return result; - - - + //Testing Execute RestorePlanePositionOperation + result = testRestorePlanePostionOperation(); + if(result!=EXIT_SUCCESS) + return result; std::cout<<"[TEST DONE]"< #include #include "QmitkSegmentationView.h" #include "QmitkSegmentationPostProcessing.h" #include "QmitkSegmentationOrganNamesHandling.cpp" #include #include #include "mitkVtkResliceInterpolationProperty.h" +#include "mitkGetModuleContext.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) { } 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->EnableInterpolation( 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(); + + mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); + mitk::PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->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(); } void QmitkSegmentationView::RenderingManagerReinitialized(const itk::EventObject&) { CheckImageAlignment(); } 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()>2) { // 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_LINEAR) ); // 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 ); } 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: // // // TODO write comment // // //MITK_INFO<<"PropertyChanged"; // mitk::DataNode* node = const_cast(dynamic_cast(caller)); // if (node) // MITK_INFO<<"Caller: "<GetName(); if (!m_Controls) return; // might happen on initialization (preferences loaded) mitk::DataNode::Pointer referenceData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); mitk::DataNode::Pointer referenceDataNew; mitk::DataNode::Pointer workingData; bool workingNodeIsVisible (true); mitk::DataNode::Pointer refTemp; unsigned int numberOfSelectedSegmentations (0); // 1. //if (referenceData.IsNotNull()) //{ // 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); //Selected node = invoker -> do nothing if (node->IsSelected() && isSegmentation) { //MITK_INFO<<"WorkingData: "<GetName(); workingNodeIsVisible = node->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); if (!workingNodeIsVisible) return; numberOfSelectedSegmentations++; workingData = node; referenceDataNew = this->GetDefaultDataStorage()->GetSources(node)->ElementAt(0); //MITK_INFO<<"REFNew: "<GetName(); if (workingNodeIsVisible) 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* ) ) ); //SetToolManagerSelection(referenceDataNew, workingData); 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); if(node->GetBoolProperty("binary", isSeg) && !isHelperObject) { mitk::DataNode* tempNode = const_cast(node); //MITK_INFO<<"Delete: "<GetName(); node->GetProperty("visible")->RemoveObserver( m_WorkingDataObserverTags[tempNode] ); m_WorkingDataObserverTags.erase(tempNode); 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(); m_MultiWidget->SetWidgetPlaneMode(0); } 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(); } } //to remember the contour positions void QmitkSegmentationView::CheckboxRememberContourPositionsStateChanged (int 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 == Qt::Checked) { manualSegmentationTool->SetRememberContourPositions( true ); } else { manualSegmentationTool->SetRememberContourPositions( 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 = "Contourmarker"; + 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(); // itk::MemberCommand::Pointer command = itk::MemberCommand::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); } } //New since rotated contour drawing is allowed. Effects a reorientation of the plane of the affected widget to the marker`s position void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode *node) { - - const mitk::PlaneGeometry* markerGeometry = - dynamic_cast ( node->GetData()->GetGeometry() ); - + //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) { - mitk::Point3D centerP = markerGeometry->GetOrigin(); - selectedRenderWindow->GetSliceNavigationController()->SelectSliceByPoint( - centerP); - selectedRenderWindow->GetSliceNavigationController()->ReorientSlices( - centerP, markerGeometry->GetNormal()); - + std::string nodeName = node->GetName(); + unsigned int t = nodeName.find_last_of(" "); + unsigned int id = atof(nodeName.substr(t+1).c_str())-1; + + mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); + mitk::PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->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(); } // 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(); } } } } 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 ) ); } if (wrongAlignment) { m_Controls->lblAlignmentWarning->show(); } } } 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 ); } 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 ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); // set visibility node->SetVisibility(node == referenceData); } } // 2. if (workingData.IsNotNull()) { 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)) ); //To remember the position of each contour connect( m_Controls->cbRememberContourPositions, SIGNAL(stateChanged(int)), this, SLOT(CheckboxRememberContourPositionsStateChanged(int))); connect(m_Controls->MaskSurfaces, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSurfaceSelectionChanged( ) ) ); 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/Modules/Bundles/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h b/Modules/Bundles/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h index 7375a5b0d7..f29145cece 100644 --- a/Modules/Bundles/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h +++ b/Modules/Bundles/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h @@ -1,169 +1,169 @@ /*====================================================================== Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 1.12 $ - + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #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(); QmitkSegmentationView(const QmitkSegmentationView& other) { Q_UNUSED(other) throw std::runtime_error("Copy constructor not implemented"); } 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(); - + 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 CheckboxRememberContourPositionsStateChanged(int state); void OnWorkingNodeVisibilityChanged(); 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_*/ diff --git a/Modules/MitkExt/Interactions/mitkSegTool2D.cpp b/Modules/MitkExt/Interactions/mitkSegTool2D.cpp index 86270978c4..d4b3dfc881 100644 --- a/Modules/MitkExt/Interactions/mitkSegTool2D.cpp +++ b/Modules/MitkExt/Interactions/mitkSegTool2D.cpp @@ -1,330 +1,295 @@ /*========================================================================= - + Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ - + Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. - + This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. - + =========================================================================*/ #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 "mitkGetModuleContext.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 ("Contourmarker"), + m_Contourmarkername ("Position"), m_RememberContourPositions (false) { // great magic numbers CONNECT_ACTION( 80, OnMousePressed ); CONNECT_ACTION( 90, OnMouseMoved ); CONNECT_ACTION( 42, OnMouseReleased ); CONNECT_ACTION( 49014, OnInvertLogic ); } mitk::SegTool2D::~SegTool2D() { } bool mitk::SegTool2D::OnMousePressed (Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( positionEvent->GetSender()->GetMapperID() != BaseRenderer::Standard2D ) return false; // we don't want anything but 2D m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); return true; } bool mitk::SegTool2D::OnMouseMoved (Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; - + if ( m_LastEventSender != positionEvent->GetSender() ) return false; if ( m_LastEventSlice != m_LastEventSender->GetSlice() ) return false; return true; } bool mitk::SegTool2D::OnMouseReleased(Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( m_LastEventSender != positionEvent->GetSender() ) return false; if ( m_LastEventSlice != m_LastEventSender->GetSlice() ) return false; - + return true; } - + bool mitk::SegTool2D::OnInvertLogic(Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; - + if ( m_LastEventSender != positionEvent->GetSender() ) return false; if ( m_LastEventSlice != m_LastEventSender->GetSlice() ) return false; return true; } 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 ); // transversal 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; int affectedDimension( -1 ); int affectedSlice( -1 ); DetermineAffectedImageSlice( image, planeGeometry, affectedDimension, affectedSlice ); if ( DetermineAffectedImageSlice( image, planeGeometry, affectedDimension, affectedSlice ) ) { try { // now we extract the correct slice from the volume, resulting in a 2D image ExtractImageFilter::Pointer extractor= ExtractImageFilter::New(); extractor->SetInput( image ); extractor->SetSliceDimension( affectedDimension ); extractor->SetSliceIndex( affectedSlice ); extractor->SetTimeStep( timeStep ); extractor->Update(); // here we have a single slice that can be modified Image::Pointer slice = extractor->GetOutput(); - + return slice; } catch(...) { // not working return NULL; } } else { - ExtractDirectedPlaneImageFilterNew::Pointer newExtractor = ExtractDirectedPlaneImageFilterNew::New(); - newExtractor->SetInput( image ); - newExtractor->SetActualInputTimestep( timeStep ); - newExtractor->SetCurrentWorldGeometry2D( planeGeometry ); - newExtractor->Update(); - Image::Pointer slice = newExtractor->GetOutput(); - return slice; + ExtractDirectedPlaneImageFilterNew::Pointer newExtractor = ExtractDirectedPlaneImageFilterNew::New(); + newExtractor->SetInput( image ); + newExtractor->SetActualInputTimestep( timeStep ); + newExtractor->SetCurrentWorldGeometry2D( planeGeometry ); + newExtractor->Update(); + Image::Pointer slice = newExtractor->GetOutput(); + 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::SetRememberContourPositions(bool status) { m_RememberContourPositions = status; -} + } void mitk::SegTool2D::AddContourmarker ( const PositionEvent* positionEvent ) { - mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(positionEvent->GetSender()->GetCurrentWorldGeometry2D())); - if ( ( currentGeometry2D != NULL ) && ( !ContourmarkerAlreadyExists( currentGeometry2D )) ) - { - //Creating PlanarFigure which serves as marker - mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New(); - contourMarker->SetGeometry2D( currentGeometry2D ); - - //Here we check which consecutive number must be the suffix to the markers name - std::stringstream markerStream; - markerStream << m_Contourmarkername; - int markerCount = 0; - mitk::DataNode* workingNode (m_ToolManager->GetWorkingData(0)); - while (m_ToolManager->GetDataStorage()->GetNamedDerivedNode(markerStream.str().c_str(), workingNode)) - { - m_ToolManager->GetDataStorage()->GetNamedDerivedNode(markerStream.str().c_str(),workingNode)->SetProperty( "visible", mitk::BoolProperty::New(false) ); - markerCount++; - markerStream.str(""); - markerStream.clear(); - markerStream << m_Contourmarkername ; - markerStream << " "; - markerStream << markerCount; - } - - //Now we place the figure in the image and the DataManager as a new Node - Point2D controlPoint2D; - Point3D controlPoint3D = positionEvent->GetWorldPosition(); - currentGeometry2D->Map(controlPoint3D ,controlPoint2D); - DataNode::Pointer rotatedContourNode = DataNode::New(); - rotatedContourNode->SetData(contourMarker); - rotatedContourNode->SetProperty( "name", StringProperty::New(markerStream.str()) ); - rotatedContourNode->SetBoolProperty( "PlanarFigureInitializedWindow", true, positionEvent->GetSender() ); - rotatedContourNode->SetProperty( "includeInBoundingBox", BoolProperty::New(false)); - m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); - } -} + const mitk::Geometry2D* plane = dynamic_cast (dynamic_cast< const mitk::SlicedGeometry3D*>( + positionEvent->GetSender()->GetSliceNavigationController()->GetCurrentGeometry3D())->GetGeometry2D(0)); -bool mitk::SegTool2D::ContourmarkerAlreadyExists ( const mitk::PlaneGeometry* currentGeometry2D ) -{ - itk::VectorContainer::ConstPointer allNodes = m_ToolManager->GetDataStorage()->GetDerivations( m_ToolManager->GetWorkingData(0) ); - mitk::DataNode* currentNode; - for( unsigned int i = 0; i < allNodes->Size(); i++ ) + 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()); + + if (plane) + { + + if ( id == size ) { - currentNode = allNodes->GetElement(i); - std::string nodeName = currentNode->GetName(); - const mitk::PlaneGeometry* nodeGeometry = dynamic_cast( currentNode->GetData()->GetGeometry() ); - if ( !nodeGeometry ) continue; - Point3D nodeCenter = nodeGeometry->GetCenter(); - Point3D currentCenter = currentGeometry2D->GetCenter(); - /*mitk::ScalarType distance1 = currentGeometry2D->DistanceFromPlane(nodeCenter); - mitk::ScalarType distance2 = nodeGeometry->DistanceFromPlane(currentCenter);*/ - Vector3D nodeNormal = nodeGeometry->GetNormal(); - Vector3D currentNormal = currentGeometry2D->GetNormal(); - //Timestep... - - bool isSameGeometry = ( (nodeCenter == currentCenter) && (nodeNormal == currentNormal)); - - - /*bool isSameGeometry = ( ( nodeGeometry->GetOrigin() == currentGeometry2D->GetOrigin() ) && - ( nodeGeometry->GetBounds() == currentGeometry2D->GetBounds() ) && - ( nodeGeometry->GetIndexToWorldTransform()->GetMatrix() == currentGeometry2D->GetIndexToWorldTransform()->GetMatrix() ) && - ( nodeGeometry->GetIndexToWorldTransform()->GetOffset() == currentGeometry2D->GetIndexToWorldTransform()->GetOffset() ) && - ( nodeGeometry->GetSpacing() == currentGeometry2D->GetSpacing() ) && - ( nodeGeometry->GetTimeBounds() ) == currentGeometry2D->GetTimeBounds() ) && - ( nodeGeometry->GetImageGeometry() == currentGeometry2D->GetImageGeometry() );*/ - - int stringPosition = nodeName.find( m_Contourmarkername ); - if ( ( stringPosition == 0 ) && ( isSameGeometry ) ) - return true; - } + //Creating PlanarFigure which currently serves as marker + mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New(); + contourMarker->SetGeometry2D( const_cast(plane)); - return false; -} + 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)); + 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; + << " " << 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/Modules/MitkExt/Interactions/mitkSegTool2D.h b/Modules/MitkExt/Interactions/mitkSegTool2D.h index 3fe0e170ac..75a5876067 100644 --- a/Modules/MitkExt/Interactions/mitkSegTool2D.h +++ b/Modules/MitkExt/Interactions/mitkSegTool2D.h @@ -1,134 +1,130 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef mitkSegTool2D_h_Included #define mitkSegTool2D_h_Included #include "mitkCommon.h" #include "MitkExtExports.h" #include "mitkTool.h" #include "mitkImage.h" #include "mitkStateEvent.h" #include "mitkPositionEvent.h" -//ContourSet - Test -#include "mitkContourSet.h" +#include "mitkPlanePositionManager.h" +#include "mitkRestorePlanePositionOperation.h" +#include "mitkInteractionConst.h" namespace mitk { class BaseRenderer; /** \brief Abstract base class for segmentation tools. \sa Tool \ingroup Interaction \ingroup ToolManagerEtAl Implements 2D segmentation specific helper methods, that might be of use to all kind of 2D segmentation tools. At the moment these are: - Determination of the slice where the user paints upon (DetermineAffectedImageSlice) - Projection of a 3D contour onto a 2D plane/slice SegTool2D tries to structure the interaction a bit. If you pass "PressMoveRelease" as the interaction type of your derived tool, you might implement the methods OnMousePressed, OnMouseMoved, and OnMouseReleased. Yes, your guess about when they are called is correct. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ class MitkExt_EXPORT SegTool2D : public Tool { public: mitkClassMacro(SegTool2D, Tool); /** \brief Calculates for a given Image and PlaneGeometry, which slice of the image (in index corrdinates) is meant by the plane. \return false, if no slice direction seems right (e.g. rotated planes) \param affectedDimension The image dimension, which is constant for all points in the plane, e.g. Transversal --> 2 \param affectedSlice The index of the image slice */ static bool DetermineAffectedImageSlice( const Image* image, const PlaneGeometry* plane, int& affectedDimension, int& affectedSlice ); void SetRememberContourPositions(bool); protected: SegTool2D(); // purposely hidden SegTool2D(const char*); // purposely hidden virtual ~SegTool2D(); virtual bool OnMousePressed (Action*, const StateEvent*); virtual bool OnMouseMoved (Action*, const StateEvent*); virtual bool OnMouseReleased(Action*, const StateEvent*); virtual bool OnInvertLogic (Action*, const StateEvent*); /** \brief Extract the slice of an image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position. */ Image::Pointer GetAffectedImageSliceAs2DImage(const PositionEvent*, const Image* image); /** \brief Extract the slice of the currently selected working image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no working image is selected. */ Image::Pointer GetAffectedWorkingSlice(const PositionEvent*); /** \brief Extract the slice of the currently selected reference image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no reference image is selected. */ Image::Pointer GetAffectedReferenceSlice(const PositionEvent*); /** - \brief Adds a new node called @a contourmarker which holds a mitk::PlanarFigure. By selecting this node the slices will be oriented according to the planarGeometry + \brief Adds a new node called Contourmarker to the datastorage which holds a mitk::PlanarFigure. + By selecting this node the slicestack will be reoriented according to the PlanarFigure's Geometry */ void AddContourmarker ( const PositionEvent* ); - void AddContour ( const mitk::Contour::Pointer ); void InteractiveSegmentationBugMessage( const std::string& message ); bool m_RememberContourPositions; private: BaseRenderer* m_LastEventSender; unsigned int m_LastEventSlice; - //The prefix of the contourmarkernames. Suffix is a consecutive number + //The prefix of the contourmarkername. Suffix is a consecutive number const std::string m_Contourmarkername; - - /** - \brief Checks whether a marker for the currentGeometry already exists - */ - bool ContourmarkerAlreadyExists ( const mitk::PlaneGeometry* currentGeometry2D ); }; } // namespace #endif