Index: mitkSlicesRotatorMovement.cpp =================================================================== --- mitkSlicesRotatorMovement.cpp (revision 0) +++ mitkSlicesRotatorMovement.cpp (revision 0) @@ -0,0 +1,595 @@ +/* +* Copyright (c) 2009, +* Computational Image and Simulation Technologies in Biomedicine (CISTIB), +* Universitat Pompeu Fabra (UPF), Barcelona, Spain. All rights reserved. +* See license.txt file for details. +*/ + +#include "mitkSlicesRotatorMovement.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "rotate_cursor.xpm" +#include "move_cursor.xpm" +#include "move_single_plane_cursor.xpm" + +using namespace mitk; + +//! In pixels +const double ThreshHoldDistancePixelsMin = 20; +//! In pixels +const double ThreshHoldDistancePixelsMax = 80; + + +mitk::SlicesRotatorMovement::SlicesRotatorMovement(const char* machine) +: mitk::SlicesRotator(machine) +{ + m_vtkLastRenderWindow = NULL; +} + +SlicesRotatorMovement::~SlicesRotatorMovement() +{ + +} + + +bool mitk::SlicesRotatorMovement::ExecuteAction(Action* action, StateEvent const* stateEvent) +{ + + bool ok = false; + + //std::cout + // << "StateEventID: " << stateEvent->GetId() << ", " + // << "Action: " << action->GetActionId() << ", " + // << "Next State: " << GetCurrentState()->GetName() + // << std::endl; + + switch ( action->GetActionId() ) + { + case AcMOVE: + { + // just reach through + for (SNCVector::iterator iter = m_SNCsToBeMoved.begin(); iter != m_SNCsToBeMoved.end(); ++iter) + { + if ( !(*iter)->GetSliceRotationLocked() ) + { + (*iter)->ExecuteAction(action, stateEvent); + } + } + + ok = true; + break; + } + case AcROTATE: + { + const DisplayPositionEvent* posEvent = dynamic_cast(stateEvent->GetEvent()); + if (!posEvent) break; + + Point3D cursor = posEvent->GetWorldPosition(); + + Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation; + Vector3D toCursor = cursor - m_CenterOfRotation; + + // cross product: | A x B | = |A| * |B| * sin(angle) + Vector3D axisOfRotation; + vnl_vector_fixed< ScalarType, 3 > vnlDirection = vnl_cross_3d( toCursor.GetVnlVector(), toProjected.GetVnlVector() ); + axisOfRotation.SetVnlVector(vnlDirection); + + // scalar product: A * B = |A| * |B| * cos(angle) + // tan = sin / cos + ScalarType angle = - atan2( (double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected) ); + angle *= 180.0 / vnl_math::pi; + m_LastCursorPosition = cursor; + + // create RotationOperation and apply to all SNCs that should be rotated + RotationOperation op(OpROTATE, m_CenterOfRotation, axisOfRotation, angle); + + // TEST + int i = 0; + + for (SNCVector::iterator iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) + { + if ( !(*iter)->GetSliceRotationLocked() ) + { + BaseRenderer *renderer = (*iter)->GetRenderer(); + if ( renderer == NULL ) + { + break; + } + + DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry(); + + // std::cout << i << ":" << std::endl; + + Point2D point2DWorld, point2DDisplayPre, point2DDisplayPost; + displayGeometry->Map( m_CenterOfRotation, point2DWorld ); + displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPre ); + + // std::cout << " WorldPre: " << point2DWorld << " / DisplayPre: " << point2DDisplayPre << std::endl; + + const Geometry3D* geometry3D = (*iter)->GetCreatedWorldGeometry(); + const TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast(geometry3D); + if (!timeSlicedGeometry) continue; + + const_cast(timeSlicedGeometry)->ExecuteOperation(&op); + + displayGeometry->Map( m_CenterOfRotation, point2DWorld ); + displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPost ); + Vector2D vector2DDisplayDiff = point2DDisplayPost - point2DDisplayPre; + + Vector2D origin = displayGeometry->GetOriginInMM(); + // std::cout << " WorldPost: " << point2DWorld << " / DisplayPost: " << point2DDisplayPost << std::endl; + // std::cout << " Diff - " << vector2DDisplayDiff << std::endl; + // std::cout << " Origin - " << origin << std::endl; + ++i; + + displayGeometry->MoveBy( vector2DDisplayDiff ); + + (*iter)->SendCreatedWorldGeometryUpdate(); + } + } + // std::cout << "--------------------------------" << std::endl; + + + + // TEST + //BaseRenderer* renderer = stateEvent->GetEvent()->GetSender(); // TODO this is NOT SNC-specific! Should be! + // + //DisplayGeometry* displayGeometry = renderer->GetDisplayGeometry(); + //if (!displayGeometry) break; + + //Point2D point2DWorld, point2DDisplay; + //displayGeometry->Map( m_CenterOfRotation, point2DWorld ); + //displayGeometry->WorldToDisplay( point2DWorld, point2DDisplay ); + + //std::cout << "RotationCenter: " << m_CenterOfRotation << std::endl; + //std::cout << "PointWorld: " << point2DWorld << std::endl; + //std::cout << "PointDisplay: " << point2DDisplay << std::endl; + //std::cout << "--------------------------------------------" << std::endl; + + + + RenderingManager::GetInstance()->RequestUpdateAll(); + + ok = true; + break; + } + case AcCHECKPOINT: + { + // decide between moving and rotation + // Configure next event + EActions action = AcDONOTHING; + + + // Alle SNCs (Anzahl N) nach dem Abstand von posEvent->GetWorldPosition() zur aktuellen Ebene fragen. + // Anzahl der Ebenen zaehlen, die naeher als ein gewisser Schwellwertwert sind -> nNah. + // Wenn nNah == N + // Generiere ein PointEvent und schicke das an alle SNCs -> bewege den kreuz-mittelpunkt + // Wenn nNah == 2 + // Streiche stateEvent->Sender aus der Liste der nahen Ebenen + // fuer die uebrigen generiere eine RotationOperation und fuehre die aus + // sonst + // + bool positionIsSet = false; + Point3D cursor; + + const DisplayPositionEvent* posEvent = dynamic_cast(stateEvent->GetEvent()); + if (posEvent) + { + cursor = posEvent->GetWorldPosition(); + positionIsSet = true; + } + const KeyEvent* keyEvent = dynamic_cast(stateEvent->GetEvent()); + if ( keyEvent && keyEvent->GetSender() ) + { + keyEvent->GetSender()->PickWorldPoint( keyEvent->GetDisplayPosition(), cursor ); + positionIsSet = true; + } + + if ( positionIsSet ) + { + //m_LastCursorPosition = cursor; + + m_SNCsToBeRotated.clear(); + m_SNCsToBeMoved.clear(); + std::vector geometryArray; + geometryArray.resize( GEOMETRY_MAX ); + for ( int i = 0 ; i < GEOMETRY_MAX ; i++ ) + { + geometryArray[ i ] = 0; + } + + + // TODO this is NOT SNC-specific! Should be! + BaseRenderer* renderer = stateEvent->GetEvent()->GetSender(); + if ( renderer != NULL ) + { + GetGeometries( + renderer, + cursor, + geometryArray ); + } + + action = ComputeMoveOrRotate( + renderer, + cursor, + geometryArray ); + } + + + // question in state machine is: "rotate or move?" + StateEvent* newStateEvent; + switch( action ) + { + case AcMOVE: + // Move + if ( m_SNCsToBeMoved.size() == 2 ) + { + newStateEvent = new StateEvent(EIDAXISCENTERMOVEMENT, stateEvent->GetEvent()); + } + else if ( m_SNCsToBeMoved.size() == 1 ) + { + newStateEvent = new StateEvent(EIDAIXSSINGLEMOVEMENT, stateEvent->GetEvent()); + } + else + { + newStateEvent = new StateEvent(EIDNO, stateEvent->GetEvent()); + } + break; + case AcROTATE: + { + // Rotate + newStateEvent = new StateEvent(EIDROTATE, stateEvent->GetEvent()); + } + break; + default: + // Nothing + newStateEvent = new StateEvent(EIDNO, stateEvent->GetEvent()); + break; + } + + this->HandleEvent( newStateEvent ); + delete newStateEvent; + + ok = true; + break; + } + case AcROTATESTART: + { + BaseRenderer* renderer = stateEvent->GetEvent()->GetSender(); + m_vtkLastRenderWindow = renderer->GetRenderWindow(); + ApplicationCursor::GetInstance( )->PushCursor( m_vtkLastRenderWindow, rotate_cursor_xpm, 0, 0); + break; + } + case AcROTATEEND: + { + ApplicationCursor::GetInstance()->PopCursor( m_vtkLastRenderWindow ); + break; + } + case AcSINGLEAXISMOVEMENTSTART: + { + BaseRenderer* renderer = stateEvent->GetEvent()->GetSender(); + m_vtkLastRenderWindow = renderer->GetRenderWindow(); + ApplicationCursor::GetInstance( )->PushCursor( + m_vtkLastRenderWindow, move_single_plane_cursor_xpm, 0, 0); + break; + } + case AcSINGLEAXISMOVEMENTEND: + { + ApplicationCursor::GetInstance()->PopCursor( m_vtkLastRenderWindow ); + break; + } + case AcAXISCENTERMOVEMENTSTART: + { + BaseRenderer* renderer = stateEvent->GetEvent()->GetSender(); + m_vtkLastRenderWindow = renderer->GetRenderWindow(); + ApplicationCursor::GetInstance( )->PushCursor( + m_vtkLastRenderWindow, move_cursor_xpm, 0, 0); + break; + } + case AcAXISCENTERMOVEMENTEND: + { + ApplicationCursor::GetInstance()->PopCursor( m_vtkLastRenderWindow ); + break; + } + default: + { + break; + } + } + + + return ok; +} + + +EActions mitk::SlicesRotatorMovement::ComputeMoveOrRotate( + mitk::BaseRenderer *renderer, + mitk::Point3D cursor, + std::vector &geometryArray ) +{ + + EActions action = AcDONOTHING; + + DisplayGeometry* displayGeometry = renderer->GetDisplayGeometry(); + if ( displayGeometry == NULL ) + { + return AcDONOTHING; + } + + bool isCenterOfRotationComputed = false; + ScalarType cursorCenterDistance = 0; + isCenterOfRotationComputed = ComputeCursorAndCenterOfRotation( + cursor, + displayGeometry, + geometryArray, + cursorCenterDistance ); + + if ( !isCenterOfRotationComputed ) + { + return AcDONOTHING; + } + + int iMinThresholdDistanceCount = 0; + int iMaxThresholdDistanceCount = 0; + double planesDistance = 0; + for ( size_t i = 0 ; i < geometryArray.size() ; i++ ) + { + ScalarType distanceMM = geometryArray[ GEOMETRY_CLICKED ]->Distance( cursor ); + + mitk::ScalarType distancePixels; + distancePixels = + distanceMM / displayGeometry->GetScaleFactorMMPerDisplayUnit(); + + planesDistance += pow ( distancePixels, 2 ); + if ( distancePixels < ThreshHoldDistancePixelsMin ) + { + iMinThresholdDistanceCount++; + } + else if ( distancePixels > ThreshHoldDistancePixelsMax ) + { + iMaxThresholdDistanceCount++; + } + } + + planesDistance = sqrt( planesDistance ); + if ( cursorCenterDistance < ThreshHoldDistancePixelsMin && + iMinThresholdDistanceCount > 2 ) + { + action = AcMOVE; + } + else if ( cursorCenterDistance > ThreshHoldDistancePixelsMin && + cursorCenterDistance < ThreshHoldDistancePixelsMax && + iMinThresholdDistanceCount > 1 ) + { + action = AcMOVE; + } + else if ( cursorCenterDistance > ThreshHoldDistancePixelsMax && + iMinThresholdDistanceCount > 1 ) + { + action = AcROTATE; + } + + + return action; +} + +bool mitk::SlicesRotatorMovement::ComputeCursorAndCenterOfRotation( + mitk::Point3D cursor, + DisplayGeometry* displayGeometry, + std::vector &geometryArray, + ScalarType &cursorCenterDistance ) +{ + cursorCenterDistance = 0; + + // determine center of rotation TODO requires two plane geometries... + PlaneGeometry* planeGeometry = dynamic_cast(geometryArray[ GEOMETRY_CLICKED ]); + PlaneGeometry* planeGeometry1 = dynamic_cast(geometryArray[ GEOMETRY_ROTATED ]); + PlaneGeometry* planeGeometry2 = dynamic_cast(geometryArray[ GEOMETRY_OTHER ]); + + if (!planeGeometry || !planeGeometry1 || !planeGeometry2) return false; + + Line3D intersection; + if (!planeGeometry->IntersectionLine( planeGeometry1, intersection )) return false; + m_LastCursorPosition = intersection.Project(cursor); + if (!planeGeometry2->IntersectionPoint(intersection, m_CenterOfRotation)) return false; + + // Compute distance from the cursor to the center of the three planes + cursorCenterDistance = 0; + for(int i=0; i<3; i++) cursorCenterDistance += pow(cursor[i] - m_CenterOfRotation[i],2); + cursorCenterDistance = sqrt( cursorCenterDistance ); + cursorCenterDistance /= displayGeometry->GetScaleFactorMMPerDisplayUnit(); + + return true; +} + +void mitk::SlicesRotatorMovement::GetGeometries( + BaseRenderer* renderer, + mitk::Point3D cursor, + std::vector &geometryArray ) +{ + std::vector controllerArray; + + bool found = false; + // Get clicked geometry + found = GetClickedGeometry( renderer, controllerArray ); + if ( !found ) + { + return; + } + + // Get second closest controllerArray + SliceNavigationController* controller; + mitk::ScalarType distance; + controller = FindClosestController( renderer, cursor, controllerArray, distance ); + if ( !controller || distance >= ThreshHoldDistancePixelsMin ) + { + return; + } + + // Add to the lists if distance < ThreshHoldDistancePixelsMin + controllerArray.push_back( controller ); + m_SNCsToBeMoved.push_back( controller ); + m_SNCsToBeRotated.push_back( controller ); + + + // Get third controller + controller = FindClosestController( renderer, cursor, controllerArray, distance ); + if ( !controller ) + { + return; + } + + // If this plane is also close to the cursor -> Move both planes + // else move only the second one + if ( distance < ThreshHoldDistancePixelsMin ) + { + m_SNCsToBeMoved.push_back( controller ); + } + + controllerArray.push_back( controller ); + + // Rotate this plane if link planes is on + if ( m_LinkPlanes ) + { + m_SNCsToBeRotated.push_back( controller ); + } + + + // Fill geometryArray with the found controllers + for (int i = 0 ; i < controllerArray.size(); i++ ) + { + geometryArray[ i ] = GetGeometry2D( controllerArray[ i ] ); + } + + +} + +bool mitk::SlicesRotatorMovement::GetClickedGeometry( + BaseRenderer* renderer, + std::vector &controllerArray ) +{ + SNCVector::iterator it = std::find( + m_RelevantSNCs.begin(), + m_RelevantSNCs.end(), + renderer->GetSliceNavigationController() ); + + if ( it != m_RelevantSNCs.end() ) + { + controllerArray.push_back( *it ); + return true; + } + + return false; +} + +Geometry2D* mitk::SlicesRotatorMovement::GetGeometry2D( + SliceNavigationController* controllerArray ) +{ + unsigned int slice = controllerArray->GetSlice()->GetPos(); + unsigned int time = controllerArray->GetTime()->GetPos(); + + const Geometry3D* geometry3D = controllerArray->GetCreatedWorldGeometry(); + const TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast( geometry3D ); + if (!timeSlicedGeometry) return NULL; + + const SlicedGeometry3D* slicedGeometry = dynamic_cast( + timeSlicedGeometry->GetGeometry3D(time) ); + if (!slicedGeometry) return NULL; + + Geometry2D* geometry2D = slicedGeometry->GetGeometry2D(slice); + if (!geometry2D) return NULL; // this is not necessary? + + return geometry2D; +} + +SliceNavigationController* mitk::SlicesRotatorMovement::FindClosestController( + BaseRenderer* renderer, + mitk::Point3D cursor, + std::vector &controllerArray, + mitk::ScalarType &minDistancePixels ) +{ + // Get display geometry + DisplayGeometry* displayGeometry = renderer->GetDisplayGeometry(); + if ( displayGeometry == NULL ) + { + return false; + } + + // Get the other two geometries + SliceNavigationController* foundController = NULL; + minDistancePixels = FLT_MAX; + for (SNCVector::iterator iter = m_RelevantSNCs.begin(); iter != m_RelevantSNCs.end(); ++iter) + { + // Search in the found controllers if direction is different and + // is not found + SNCVector::iterator it; + bool validController = true; + for ( it = controllerArray.begin(); it != controllerArray.end() ;it++ ) + { + if ( *it == *iter || + (*it)->GetViewDirection( ) == (*iter)->GetViewDirection( ) ) + { + validController = false; + } + } + if ( !validController ) + { + continue; + } + + mitk::ScalarType distancePixels = ComputeDistance( renderer, (*iter), cursor ); + + // The first plane found close to the cursor + if ( distancePixels <= minDistancePixels ) + { + minDistancePixels = distancePixels; + foundController = (*iter); + } + } + + return foundController; +} + +double mitk::SlicesRotatorMovement::ComputeDistance( + BaseRenderer* renderer, + SliceNavigationController* controller, + mitk::Point3D cursor ) +{ + // Get display geometry + DisplayGeometry* displayGeometry = renderer->GetDisplayGeometry(); + if ( displayGeometry == NULL ) + { + return false; + } + + + // Compute distance + Geometry2D* geometry2D = GetGeometry2D( controller ); + + mitk::ScalarType distancePixels = + geometry2D->Distance( cursor ) / displayGeometry->GetScaleFactorMMPerDisplayUnit(); + + return distancePixels; +} + + Property changes on: mitkSlicesRotatorMovement.cpp ___________________________________________________________________ Added: svn:eol-style + native Index: mitkSlicesRotatorMovement.h =================================================================== --- mitkSlicesRotatorMovement.h (revision 0) +++ mitkSlicesRotatorMovement.h (revision 0) @@ -0,0 +1,126 @@ +/* +* Copyright (c) 2009, +* Computational Image and Simulation Technologies in Biomedicine (CISTIB), +* Universitat Pompeu Fabra (UPF), Barcelona, Spain. All rights reserved. +* See license.txt file for details. +*/ + +#ifndef __mitkSlicesRotatorMovement_H +#define __mitkSlicesRotatorMovement_H + +#include +#include +#include + +namespace mitk{ class Geometry2D; } + +class vtkRenderWindow; + +namespace mitk { + +typedef enum{ + GEOMETRY_CLICKED, + GEOMETRY_ROTATED, + GEOMETRY_OTHER, + GEOMETRY_MAX +} GEOMETRY_TYPE; + +/** + \brief Redefinition of mitk::SlicesRotator, unifying the rotation and + translation. The user needs to drag and drop the axis. The cursor + icon is automatically changed when the mouse pointer can perform + any of the actions. + + There are three states depending on the position of the mouse pointer + related to the axis: + - Center: Axis can be moved in any direction + - Middle: Axis can be translated + - Extremes: Axis can be rotated + + \ingroup NavigationControl + \date 19 09 08 + \author Xavi Planes + */ +class MITK_CORE_EXPORT SlicesRotatorMovement : public mitk::SlicesRotator +{ +public: + + mitkClassMacro(SlicesRotatorMovement, mitk::SlicesRotator); + + mitkNewMacro1Param(Self, const char*); + +protected: + + //! + SlicesRotatorMovement(const char* machine); + + //! Clear list of controllers + virtual ~SlicesRotatorMovement(); + + //! + virtual bool ExecuteAction(mitk::Action * action, mitk::StateEvent const* stateEvent); + + //! + mitk::EActions ComputeMoveOrRotate( + mitk::BaseRenderer *renderer, + mitk::Point3D cursor, + std::vector &geometryArray ); + + //! + bool ComputeCursorAndCenterOfRotation( + mitk::Point3D cursor, + mitk::DisplayGeometry* displayGeometry, + std::vector &geometryArray, + mitk::ScalarType &cursorCenterDistance ); + + //! + void GetGeometries( + mitk::BaseRenderer* renderer, + mitk::Point3D cursor, + std::vector &geometryArray ); + + //! Get the geometry where the event comes from + bool GetClickedGeometry( + BaseRenderer* renderer, + std::vector &controllerArray ); + + /** Find closest controller to cursor with distance < ThreshHoldDistancePixelsMin + not present in controllerArray, with direction != controller + */ + SliceNavigationController* FindClosestController( + BaseRenderer* renderer, + mitk::Point3D cursor, + std::vector &controllerArray, + mitk::ScalarType &minDistancePixels); + + //! + Geometry2D* GetGeometry2D( SliceNavigationController* controller ); + + /** Check distance from the controller 2D geometry to the cursor in the + display geometry of the renderer < threshold + */ + double ComputeDistance( + BaseRenderer* renderer, + SliceNavigationController* controller, + mitk::Point3D cursor ); + +protected: + + /** + \brief Last accessed vtkRenderWindow + We need to restore the cursor of the mouse when this interactor is + disabled from the global interactor + */ + vtkRenderWindow *m_vtkLastRenderWindow; + + + /// all SNCs that will be moved + SNCVector m_SNCsToBeMoved; + +}; + +} // namespace + +#endif // __mitkSlicesRotatorMovement_H + + Property changes on: mitkSlicesRotatorMovement.h ___________________________________________________________________ Added: svn:eol-style + native