diff --git a/Core/Code/Controllers/mitkSliceNavigationController.cpp b/Core/Code/Controllers/mitkSliceNavigationController.cpp index 909a7ff1fe..2a5a310167 100644 --- a/Core/Code/Controllers/mitkSliceNavigationController.cpp +++ b/Core/Code/Controllers/mitkSliceNavigationController.cpp @@ -1,737 +1,737 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "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 "mitkMemoryUtilities.h" #include namespace mitk { SliceNavigationController::SliceNavigationController( const char *type ) : BaseController( type ), m_InputWorldGeometry( NULL ), m_CreatedWorldGeometry( NULL ), m_ViewDirection( Axial ), m_DefaultViewDirection( Axial ), 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 == Axial ) { this->Update( Axial, 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 Axial: no "break" here!! case Axial: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( m_InputWorldGeometry, PlaneGeometry::Axial, 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(); } void SliceNavigationController::ReorientSlices(const mitk::Point3D &point, - const mitk::Vector3D &normal, const mitk::Vector3D &axisVec0 ) + const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1 ) { - PlaneOperation op( OpORIENT, point, normal, axisVec0 ); + PlaneOperation op( OpORIENT, point, axisVec0, axisVec1 ); 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(OpMOVE, posEvent->GetWorldPosition()); 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 2396871ae3..72e1e51cb3 100644 --- a/Core/Code/Controllers/mitkSliceNavigationController.h +++ b/Core/Code/Controllers/mitkSliceNavigationController.h @@ -1,553 +1,553 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef 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 "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::Axial); * * // 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 { #ifdef _MSC_VER Transversal, // deprecated #endif Axial = 0, Sagittal, Frontal, Original }; #ifdef __GNUC__ __attribute__ ((deprecated)) static const ViewDirection Transversal = ViewDirection(Axial); #endif /** * \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); unsigned long tag = AddObserver(GeometrySendEvent(NULL,0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometryUpdateEvent(T* receiver) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::UpdateGeometry); unsigned long tag = AddObserver(GeometryUpdateEvent(NULL,0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometrySliceEvent(T* receiver, bool connectSendEvent=true) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometrySlice); unsigned long tag = AddObserver(GeometrySliceEvent(NULL,0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); 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); unsigned long tag = AddObserver(GeometryTimeEvent(NULL,0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); if(connectSendEvent) ConnectGeometrySendEvent(receiver); } template void ConnectGeometryEvents(T* receiver) { //connect sendEvent only once ConnectGeometrySliceEvent(receiver, false); ConnectGeometryTimeEvent(receiver); } // use a templated method to get the right offset when casting to void* template void Disconnect(T* receiver) { ObserverTagsMapType::iterator i = m_ReceiverToObserverTagsMap.find(static_cast(receiver)); if (i == m_ReceiverToObserverTagsMap.end()) return; const std::list& tags = i->second; for (std::list::const_iterator tagIter = tags.begin(); tagIter != tags.end(); ++tagIter) { RemoveObserver(*tagIter); } m_ReceiverToObserverTagsMap.erase(i); } 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 ); - /** \brief Re-orients the slice stack to include the plane specified by - * the given point an normal vector and rotates to fit the given axis vector - */ + /** \brief Re-orients the slice stack so that all planes are oriented according to the + * given axis vectors. The given Point eventually defines selected slice. + */ void ReorientSlices( - const mitk::Point3D &point, const mitk::Vector3D &normal, const mitk::Vector3D &axisVec0 ); + const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1 ); 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; typedef std::map > ObserverTagsMapType; ObserverTagsMapType m_ReceiverToObserverTagsMap; }; } // namespace mitk #endif /* SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F */ diff --git a/Core/Code/DataManagement/mitkPlaneOperation.cpp b/Core/Code/DataManagement/mitkPlaneOperation.cpp index 77790c5751..b2556ab004 100644 --- a/Core/Code/DataManagement/mitkPlaneOperation.cpp +++ b/Core/Code/DataManagement/mitkPlaneOperation.cpp @@ -1,69 +1,63 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlaneOperation.h" #include "mitkLogMacros.h" namespace mitk { PlaneOperation ::PlaneOperation( OperationType operationType, Point3D point, Vector3D normal ) -: PointOperation( operationType, point ), m_Normal( normal ), m_AxisVec0IsDefined (false ) +: PointOperation( operationType, point ), m_Normal( normal ), m_AreAxisDefined (false ) { } PlaneOperation -::PlaneOperation( OperationType operationType, Point3D point, Vector3D normal, Vector3D axisVec0 ) -: PointOperation( operationType, point ), m_Normal( normal ), m_AxisVec0( axisVec0 ), m_AxisVec0IsDefined (true ) +::PlaneOperation( OperationType operationType, Point3D point, Vector3D axisVec0, Vector3D axisVec1 ) +: PointOperation( operationType, point ), m_AxisVec0( axisVec0 ), m_AxisVec1( axisVec1 ), m_AreAxisDefined (true ) { - // axis vector HAS to be 90° to normalVector - // check this.. - - float angle = acos (m_Normal[0]*m_AxisVec0[0] + m_Normal[1]*m_AxisVec0[1] + m_Normal[2]*m_AxisVec0[2]); - angle *= 180.0 / vnl_math::pi; - if ( (abs(90-angle)) > 0.00001) - { - MITK_WARN << "Normal and Axisvector need to at be 90°! Here it is " << angle ; - } - - - + } PlaneOperation ::~PlaneOperation() { } Vector3D mitk::PlaneOperation::GetNormal() { return m_Normal; } Vector3D mitk::PlaneOperation::GetAxisVec0() { return m_AxisVec0; } -bool mitk::PlaneOperation::IsAxisVec0Defined() +Vector3D mitk::PlaneOperation::GetAxisVec1() { - return m_AxisVec0IsDefined; + return m_AxisVec1; } - + +bool mitk::PlaneOperation::AreAxisDefined() + { + return m_AreAxisDefined; + } + } // namespace mitk diff --git a/Core/Code/DataManagement/mitkPlaneOperation.h b/Core/Code/DataManagement/mitkPlaneOperation.h index c2a3d06278..b1c36e52ce 100644 --- a/Core/Code/DataManagement/mitkPlaneOperation.h +++ b/Core/Code/DataManagement/mitkPlaneOperation.h @@ -1,58 +1,60 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPlaneOperation_H #define MITKPlaneOperation_H #include #include "mitkPointOperation.h" #include "mitkVector.h" namespace mitk { /** * @brief Operation for setting a plane (defined by its origin and normal) * * @ingroup Undo */ class MITK_CORE_EXPORT PlaneOperation : public PointOperation { public: PlaneOperation( OperationType operationType, Point3D point, Vector3D normal ); - PlaneOperation( OperationType operationType, Point3D point, Vector3D normal, Vector3D axisVec0 ); + PlaneOperation( OperationType operationType, Point3D point, Vector3D axisVec0, Vector3D axisVec1 ); virtual ~PlaneOperation(); Vector3D GetNormal(); Vector3D GetAxisVec0(); - bool IsAxisVec0Defined(); + Vector3D GetAxisVec1(); + bool AreAxisDefined(); private: Vector3D m_Normal; Vector3D m_AxisVec0; - bool m_AxisVec0IsDefined; + Vector3D m_AxisVec1; + bool m_AreAxisDefined; }; } //namespace mitk #endif /* MITKPlaneOperation_H */ diff --git a/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp b/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp index 5576bed383..0dbe02c7c4 100644 --- a/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp +++ b/Core/Code/DataManagement/mitkSlicedGeometry3D.cpp @@ -1,1028 +1,1027 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSlicedGeometry3D.h" #include "mitkPlaneGeometry.h" #include "mitkRotationOperation.h" #include "mitkPlaneOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkInteractionConst.h" #include "mitkSliceNavigationController.h" const float PI = 3.14159265359; mitk::SlicedGeometry3D::SlicedGeometry3D() : m_EvenlySpaced( true ), m_Slices( 0 ), m_ReferenceGeometry( NULL ), m_SliceNavigationController( NULL ) { m_DirectionVector.Fill(0); 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_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 ) { 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); } } } } 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()) ) { 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; } } return geometry2D; } else { return NULL; } } const mitk::BoundingBox * mitk::SlicedGeometry3D::GetBoundingBox() const { 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; } void mitk::SlicedGeometry3D::InitializeSlicedGeometry( unsigned int slices ) { Superclass::Initialize(); m_Slices = slices; Geometry2D::Pointer gnull = NULL; m_Geometry2Ds.assign( m_Slices, gnull ); Vector3D spacing; spacing.Fill( 1.0 ); this->SetSpacing( spacing ); m_DirectionVector.Fill( 0 ); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced( mitk::Geometry2D* geometry2D, unsigned int slices, bool 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 ) { 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 ) { 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::Axial: viewSpacing = geometry3D->GetSpacing()[2]; slices = (unsigned int) geometry3D->GetExtent( 2 ); break; case PlaneGeometry::Frontal: viewSpacing = geometry3D->GetSpacing()[1]; slices = (unsigned int) geometry3D->GetExtent( 1 ); break; case PlaneGeometry::Sagittal: viewSpacing = geometry3D->GetSpacing()[0]; slices = (unsigned int) geometry3D->GetExtent( 0 ); break; default: itkExceptionMacro("unknown PlaneOrientation"); } mitk::Vector3D normal = this->AdjustNormal( planeGeometry->GetNormal() ); 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 ); } 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 = 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(); } double mitk::SlicedGeometry3D::CalculateSpacing( const mitk::Vector3D &d ) const { // Need the spacing of the underlying dataset / geometry if ( !m_ReferenceGeometry ) { return 1.0; } const mitk::Vector3D &spacing = m_ReferenceGeometry->GetSpacing(); return SlicedGeometry3D::CalculateSpacing( spacing, d ); } double mitk::SlicedGeometry3D::CalculateSpacing( const mitk::Vector3D spacing, const mitk::Vector3D &d ) { // 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) // 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 ); 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 ); } } } 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 ); } bool mitk::SlicedGeometry3D::IsValidSlice( int s ) const { return ((s >= 0) && (s < (int)m_Slices)); } void mitk::SlicedGeometry3D::SetReferenceGeometry( Geometry3D *referenceGeometry ) { m_ReferenceGeometry = referenceGeometry; std::vector::iterator it; 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(); } void mitk::SlicedGeometry3D ::SetSliceNavigationController( SliceNavigationController *snc ) { m_SliceNavigationController = snc; } mitk::SliceNavigationController * mitk::SlicedGeometry3D::GetSliceNavigationController() { return m_SliceNavigationController; } void mitk::SlicedGeometry3D::SetEvenlySpaced(bool on) { 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(); } } 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; } mitk::AffineGeometryFrame3D::Pointer mitk::SlicedGeometry3D::Clone() const { 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); } } void mitk::SlicedGeometry3D::ExecuteOperation(Operation* operation) { switch ( operation->GetOperationType() ) { case OpNOTHING: break; case OpROTATE: if ( m_EvenlySpaced ) { // 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() ); geometry2D->SetSpacing(this->GetSpacing()); if ( m_SliceNavigationController ) { m_SliceNavigationController->SelectSliceByPoint( rotOp->GetCenterOfRotation() ); m_SliceNavigationController->AdjustSliceStepperRange(); } Geometry3D::ExecuteOperation( ¢eredRotation ); } else { // we also have to consider the case, that there is no reference geometry available. if ( m_Geometry2Ds.size() > 0 ) { // Reach through to all slices in my container for (std::vector::iterator iter = m_Geometry2Ds.begin(); iter != m_Geometry2Ds.end(); ++iter) { (*iter)->ExecuteOperation(operation); } // rotate overall geometry RotationOperation *rotOp = dynamic_cast< RotationOperation * >( operation ); Geometry3D::ExecuteOperation( rotOp); } } } else { // Reach through to all slices for (std::vector::iterator iter = m_Geometry2Ds.begin(); iter != m_Geometry2Ds.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpORIENT: if ( m_EvenlySpaced ) { - // The "orient" operation assings a new normal vector to the sliced geometry an all its - // containing 2D planes. Keep in mind, that this might change the axis vectors as well quite a lot. - // After the orientation, the orient function can also rotate the plane around its normal - // to fit a given axis vector (if desired) + // get operation data + PlaneOperation *planeOp = dynamic_cast< PlaneOperation * >( operation ); // Get first slice Geometry2D::Pointer geometry2D = m_Geometry2Ds[0]; PlaneGeometry *planeGeometry = dynamic_cast< PlaneGeometry * >( geometry2D.GetPointer() ); - - // get operation data - 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 ) + // carry out the re-orientation. If not all avaialble, stop here + 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(); - - mitk::Vector3D currentNormal = planeGeometry->GetNormal(); - mitk::Vector3D newNormal = planeOp->GetNormal(); - - // Get Rotation axis und angle - currentNormal.Normalize(); - newNormal.Normalize(); - float rotationAngle = acos (currentNormal[0]*newNormal[0] + currentNormal[1]*newNormal[1] + currentNormal[2]*newNormal[2]); - rotationAngle *= 180.0 / vnl_math::pi; - Vector3D rotationAxis = itk::CrossProduct( currentNormal, newNormal ); - if (abs(rotationAngle-180) < mitk::eps ) - { - // current Normal and desired normal are not linear independent!!(e.g 1,0,0 and -1,0,0). - // Rotation Axis should be ANY vector that is 90° to current Normal - mitk::Vector3D helpNormal; - helpNormal = currentNormal; - helpNormal[0] += 1; - helpNormal[1] -= 1; - helpNormal[2] += 1; - helpNormal.Normalize(); - rotationAxis = itk::CrossProduct( helpNormal, currentNormal ); - } - - RotationOperation centeredRotation( - mitk::OpROTATE, - center, - rotationAxis, - rotationAngle - ); + break; + } - // Rotate first slice - geometry2D->ExecuteOperation( ¢eredRotation ); + // General Behavior: + // Clear all generated geometries and then rotate only the first slice. + // The other slices will be re-generated on demand + + // + // 1st Step: Reorient Normal Vector of first plane + // + Point3D center = m_ReferenceGeometry->GetCenter(); + mitk::Vector3D currentNormal = planeGeometry->GetNormal(); + mitk::Vector3D newNormal; + if (planeOp->AreAxisDefined()) + { + // If planeOp was defined by one centerpoint and two axis vectors + newNormal = CrossProduct(planeOp->GetAxisVec0(), planeOp->GetAxisVec1()); + } + else + { + // If planeOp was defined by one centerpoint and one normal vector + newNormal = planeOp->GetNormal(); + } - // check if rotation direction was correct, or standing on top - planeGeometry = dynamic_cast< PlaneGeometry * >( - geometry2D.GetPointer() ); + // Get Rotation axis und angle + currentNormal.Normalize(); + newNormal.Normalize(); + float rotationAngle = angle(currentNormal.Get_vnl_vector(),newNormal.Get_vnl_vector()); -/* - // This code was just to figure out if the rotation was good - mitk::Vector3D normalAfterwards = planeGeometry->GetNormal(); - normalAfterwards.Normalize(); - float angleAfter = acos (normalAfterwards[0]*newNormal[0] + normalAfterwards[1]*newNormal[1] + normalAfterwards[2]*newNormal[2]); - angleAfter *= 180.0 / vnl_math::pi; - MITK_INFO <<" rotangleAfter: " << angleAfter; -*/ + //MITK_INFO << rotationAngle; + rotationAngle *= 180.0 / vnl_math::pi; // from rad to deg + Vector3D rotationAxis = itk::CrossProduct( currentNormal, newNormal ); + if (abs(rotationAngle-180) < mitk::eps ) + { + // current Normal and desired normal are not linear independent!!(e.g 1,0,0 and -1,0,0). + // Rotation Axis should be ANY vector that is 90° to current Normal + mitk::Vector3D helpNormal; + helpNormal = currentNormal; + helpNormal[0] += 1; + helpNormal[1] -= 1; + helpNormal[2] += 1; + helpNormal.Normalize(); + rotationAxis = itk::CrossProduct( helpNormal, currentNormal ); + } + RotationOperation centeredRotation( + mitk::OpROTATE, + center, + rotationAxis, + rotationAngle + ); + + // Rotate first slice + //MITK_INFO << "geometry2DMatrix before OP: " << geometry2D->GetMatrixColumn(0); + //MITK_INFO << "opCenter: " << center << " opRotAxis: " << rotationAxis << "opRotAngle: " << rotationAngle; + geometry2D->ExecuteOperation( ¢eredRotation ); + //MITK_INFO << "geometry2DMatrix after OP: " << geometry2D->GetMatrixColumn(0); + + // 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() ); + //MITK_INFO << "geometry2DMatrix after initPlanes: " << geometry2D->GetMatrixColumn(0); + + if ( m_SliceNavigationController ) + { + m_SliceNavigationController->SelectSliceByPoint( planeOp->GetPoint() ); + m_SliceNavigationController->AdjustSliceStepperRange(); + } + // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) + Geometry3D::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() ); + // + // 2nd step. If axis vectors were defined, rotate the plane around its normal to fit these + // - if ( m_SliceNavigationController ) - { - m_SliceNavigationController->SelectSliceByPoint( planeOp->GetPoint() ); - m_SliceNavigationController->AdjustSliceStepperRange(); - } + if (planeOp->AreAxisDefined()) + { + mitk::Vector3D vecAxixNew = planeOp->GetAxisVec0(); + vecAxixNew.Normalize(); + mitk::Vector3D VecAxisCurr = geometry2D->GetAxisVector(0); + VecAxisCurr.Normalize(); - // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) - Geometry3D::ExecuteOperation( ¢eredRotation ); + float rotationAngle = angle(VecAxisCurr.Get_vnl_vector(),vecAxixNew.Get_vnl_vector()); + rotationAngle = rotationAngle * 180 / PI; // Rad to Deg - - // Re-Orientation of the Normalvector is now finished! - // Now, if AxisVec0 was defined, rotate plane around normal so it fits Axis Vector - if (planeOp->IsAxisVec0Defined()) - { - mitk::Vector3D vecAxixNew = planeOp->GetAxisVec0(); - vecAxixNew.Normalize(); - mitk::Vector3D VecAxisCurr = geometry2D->GetAxisVector(0); - VecAxisCurr.Normalize(); - - rotationAngle = acos (VecAxisCurr[0]*vecAxixNew[0] + VecAxisCurr[1]*vecAxixNew[1] + VecAxisCurr[2]*vecAxixNew[2]); - rotationAngle = rotationAngle * 180 / PI; - rotationAxis = itk::CrossProduct( VecAxisCurr, vecAxixNew ); - // A Prerequisite is that VexAxisCur and VecAxis New lie BOTH in the plane - // rotationAxis is either the normal or the negative normal. To find out we need the crossproduct. - // but in case of a 180° rotation, we can simply take the current normal. - if (abs(rotationAngle-180) < mitk::eps ) - { - // current Normal and desired normal are not linear independent!!(e.g 1,0,0 and -1,0,0). - rotationAxis = newNormal; - } + // we rotate around the normal of the plane, but we do not know, if we need to rotate clockwise + // or anti-clockwise. So we rotate around the crossproduct of old and new Axisvector. + // Since both axis vectors lie in the plane, the crossproduct is the planes normal or the negative planes normal - mitk::RotationOperation op(mitk::OpROTATE, center, rotationAxis, rotationAngle); - geometry2D->ExecuteOperation( &op ); + rotationAxis = itk::CrossProduct( VecAxisCurr, vecAxixNew ); + if (abs(rotationAngle-180) < mitk::eps ) + { + // current axisVec and desired axisVec are not linear independent!!(e.g 1,0,0 and -1,0,0). + // Rotation Axis can be just plane Normal. (have to rotate by 180°) + rotationAxis = newNormal; + } - /* - // This code was just to determine if rotation was correct - VecAxisCurr = geometry2D->GetAxisVector(0); - VecAxisCurr.Normalize(); - angleAfter = acos (VecAxisCurr[0]*vecAxixNew[0] + VecAxisCurr[1]*vecAxixNew[1] + VecAxisCurr[2]*vecAxixNew[2]); - angleAfter = angleAfter * 180 / PI; - MITK_INFO << "angle nach rotrot: " << angleAfter; - */ + // Perfom Rotation + mitk::RotationOperation op(mitk::OpROTATE, center, rotationAxis, rotationAngle); + geometry2D->ExecuteOperation( &op ); - // Apply changes on first slice to whole slice stack ( - // Todo: Is this really done?? Is Axis Vector adapted? - this->ReinitializePlanes( center, planeOp->GetPoint() ); + // Apply changes on first slice to whole slice stack + this->ReinitializePlanes( center, planeOp->GetPoint() ); - // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) - Geometry3D::ExecuteOperation( &op ); - } + if ( m_SliceNavigationController ) + { + m_SliceNavigationController->SelectSliceByPoint( planeOp->GetPoint() ); + m_SliceNavigationController->AdjustSliceStepperRange(); + } + // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) + Geometry3D::ExecuteOperation( &op ); } } 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 ) { // 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 ) { 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); } } 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(); }