Enables arbitrary rotation of visible slices around a swivel point (for sliced geometries).This class takes care of several SliceNavigationControllers and handles slice selection / slice rotation. It is added as listener to GlobalInteraction by QmitkStdMultiWidget. More...
#include <mitkSlicesSwiveller.h>
Public Member Functions | |
mitkClassMacro (SlicesSwiveller, SlicesCoordinator) | |
mitkNewMacro1Param (Self, const char *) | |
New Macro with one parameter for creating this object with static New(..) method. | |
virtual void | SetGeometry (const itk::EventObject &EventObject) |
Is called whenever a SliceNavigationController invokes an event. Will. | |
Static Public Member Functions | |
static Pointer | New () |
Protected Member Functions | |
SlicesSwiveller (const char *machine) | |
virtual | ~SlicesSwiveller () |
virtual void | OnSliceControllerAdded (SliceNavigationController *snc) |
virtual void | OnSliceControllerRemoved (SliceNavigationController *snc) |
virtual void | UpdateRelevantSNCs () |
Updates the list of SliceNavigationControllers that can handle rotation. | |
virtual bool | ExecuteAction (Action *action, StateEvent const *stateEvent) |
Protected Attributes | |
SNCVector | m_RelevantSNCs |
SNCVector | m_SNCsToBeRotated |
Point3D | m_LastCursorPosition |
Point3D | m_CenterOfRotation |
Point2D | m_ReferenceCursor |
Vector3D | m_RotationPlaneNormal |
Vector3D | m_RotationPlaneXVector |
Vector3D | m_RotationPlaneYVector |
Vector3D | m_PreviousRotationAxis |
ScalarType | m_PreviousRotationAngle |
Enables arbitrary rotation of visible slices around a swivel point (for sliced geometries).
This class takes care of several SliceNavigationControllers and handles slice selection / slice rotation. It is added as listener to GlobalInteraction by QmitkStdMultiWidget.
The SlicesSwiveller class adds the possibility of slice rotation to the "normal" behaviour of SliceNavigationControllers. This additional class is needed, because one has to be aware of several "visible slices" (selected Geometry2Ds of some SliceNavigationControllers) in order to choose between rotation and slice selection.
Rotation is achieved by modifying (rotating) the generated TimeSlicedGeometry of the corresponding SliceNavigationController.
With SlicesSwiveller, slice rotation works as follows: the user clicks onto a 2D view (2D plane) and drags the mouse; the relative direction and angle of the dragged mouse movement directly effects the rotation axis and angle. If "LinkPlanes" is set to true, the rotation is applied to the planes of all registered SNCs, not only of the one associated with the plane clicked on.
In contrast to the situation without the SlicesRotator, the SliceNavigationControllers are now not directly registered as listeners to GlobalInteraction. SlicesRotator is registered as a listener and decides whether something should be rotated or whether another slice should be selected. In the latter case, a PositionEvent is just forwarded to the SliceNavigationController.
Definition at line 63 of file mitkSlicesSwiveller.h.
mitk::SlicesSwiveller::SlicesSwiveller | ( | const char * | machine ) | [protected] |
Definition at line 27 of file mitkSlicesSwiveller.cpp.
{
mitk::SlicesSwiveller::~SlicesSwiveller | ( | ) | [protected, virtual] |
Definition at line 33 of file mitkSlicesSwiveller.cpp.
{
bool mitk::SlicesSwiveller::ExecuteAction | ( | Action * | action, |
StateEvent const * | stateEvent | ||
) | [protected, virtual] |
for implementation in subclasses
Reimplemented from mitk::SlicesCoordinator.
Definition at line 131 of file mitkSlicesSwiveller.cpp.
{ if ( fabs(direction1[i] - direction2[i]) > eps ) { equal = false; } } if (equal) // equal direction vectors { m_RelevantSNCs.push_back( *iter ); } } } bool SlicesSwiveller ::ExecuteAction(Action* action, StateEvent const* stateEvent) { const ScalarType ThresholdDistancePixels = 6.0; bool ok = false; switch ( action->GetActionId() ) { case AcMOVE: { // just reach through SNCVector::iterator iter; for ( iter = m_RelevantSNCs.begin(); iter != m_RelevantSNCs.end(); ++iter ) { if ( !(*iter)->GetSliceRotationLocked() ) { (*iter)->ExecuteAction(action, stateEvent); } } ok = true; break; } case AcROTATE: { const DisplayPositionEvent *posEvent = dynamic_cast<const DisplayPositionEvent*>(stateEvent->GetEvent()); if (!posEvent) break; // Determine relative mouse movement projected onto world space Point2D cursor = posEvent->GetDisplayPosition(); Vector2D relativeCursor = cursor - m_ReferenceCursor; Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1]; // Determine rotation axis (perpendicular to rotation plane and cursor // movement) Vector3D rotationAxis = itk::CrossProduct( m_RotationPlaneNormal, relativeCursorAxis ); ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0; // Restore the initial plane pose by undoing the previous rotation // operation RotationOperation op( OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle ); SNCVector::iterator iter; for ( iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter ) { if ( !(*iter)->GetSliceRotationLocked() ) { const Geometry3D* geometry3D = (*iter)->GetCreatedWorldGeometry(); const TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast<const TimeSlicedGeometry*>(geometry3D); if (!timeSlicedGeometry) continue; const_cast<TimeSlicedGeometry*>(timeSlicedGeometry) ->ExecuteOperation(&op); (*iter)->SendCreatedWorldGeometryUpdate(); } } // Apply new rotation operation to all relevant SNCs RotationOperation op2( OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle ); for ( iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if ( !(*iter)->GetSliceRotationLocked() ) { //BaseRenderer *renderer = (*iter)->GetRenderer(); //DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry(); //Point2D point2DWorld, point2DDisplayPre, point2DDisplayPost; //displayGeometry->Map( m_CenterOfRotation, point2DWorld ); //displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPre ); // Retrieve the TimeSlicedGeometry of this SliceNavigationController const Geometry3D* geometry3D = (*iter)->GetCreatedWorldGeometry(); const TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast<const TimeSlicedGeometry*>(geometry3D); if (!timeSlicedGeometry) continue; // Execute the new rotation const_cast<TimeSlicedGeometry*>(timeSlicedGeometry) ->ExecuteOperation(&op2); //displayGeometry->Map( m_CenterOfRotation, point2DWorld ); //displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPost ); //Vector2D vector2DDisplayDiff = point2DDisplayPost - point2DDisplayPre; //Vector2D origin = displayGeometry->GetOriginInMM(); //displayGeometry->MoveBy( vector2DDisplayDiff ); // Notify listeners (*iter)->SendCreatedWorldGeometryUpdate(); } } m_PreviousRotationAxis = rotationAxis; m_PreviousRotationAngle = rotationAngle; RenderingManager::GetInstance()->RequestUpdateAll(); this->InvokeEvent( SliceRotationEvent() ); // notify listeners ok = true; break; } case AcCHECKPOINT: { // Decide between moving and rotation: if we're close to the crossing // point of the planes, moving mode is entered, otherwise // rotation/swivel mode const DisplayPositionEvent *posEvent = dynamic_cast<const DisplayPositionEvent*>(stateEvent->GetEvent()); BaseRenderer *renderer = stateEvent->GetEvent()->GetSender(); if ( !posEvent || !renderer ) { break; } const Point3D &cursor = posEvent->GetWorldPosition(); m_SNCsToBeRotated.clear(); const PlaneGeometry *clickedGeometry( NULL ); const PlaneGeometry *otherGeometry1( NULL ); const PlaneGeometry *otherGeometry2( NULL ); SNCVector::iterator iter; for ( iter = m_RelevantSNCs.begin(); iter != m_RelevantSNCs.end(); ++iter ) { //unsigned int slice = (*iter)->GetSlice()->GetPos(); //unsigned int time = (*iter)->GetTime()->GetPos(); const PlaneGeometry *planeGeometry = (*iter)->GetCurrentPlaneGeometry(); if ( !planeGeometry ) continue; if ( *iter == renderer->GetSliceNavigationController() ) { clickedGeometry = planeGeometry; m_SNCsToBeRotated.push_back(*iter); } else { if ( otherGeometry1 == NULL ) { otherGeometry1 = planeGeometry; } else { otherGeometry2 = planeGeometry; } if ( m_LinkPlanes ) { // If planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(*iter); } } } StateEvent *newStateEvent( NULL ); mitk::Line3D line; mitk::Point3D point; if ( (clickedGeometry != NULL) && (otherGeometry1 != NULL) && (otherGeometry2 != NULL) && clickedGeometry->IntersectionLine( otherGeometry1, line ) && otherGeometry2->IntersectionPoint( line, point )) { m_CenterOfRotation = point; if ( m_CenterOfRotation.EuclideanDistanceTo( cursor ) < ThresholdDistancePixels ) { newStateEvent = new StateEvent(EIDNO, stateEvent->GetEvent()); } else { m_ReferenceCursor = posEvent->GetDisplayPosition(); // Get main axes of rotation plane and store it for rotation step m_RotationPlaneNormal = clickedGeometry->GetNormal(); ScalarType xVector[] = { 1.0, 0.0, 0.0 }; ScalarType yVector[] = { 0.0, 1.0, 0.0 }; clickedGeometry->Geometry3D::IndexToWorld( Point3D(), Vector3D( xVector), m_RotationPlaneXVector ); clickedGeometry->Geometry3D::IndexToWorld( Point3D(), Vector3D( yVector), m_RotationPlaneYVector ); m_RotationPlaneNormal.Normalize(); m_RotationPlaneXVector.Normalize(); m_RotationPlaneYVector.Normalize(); m_PreviousRotationAxis.Fill( 0.0 ); m_PreviousRotationAxis[2] = 1.0; m_PreviousRotationAngle = 0.0; newStateEvent = new StateEvent(EIDYES, stateEvent->GetEvent()); } } else { newStateEvent = new StateEvent(EIDNO, stateEvent->GetEvent()); } this->HandleEvent( newStateEvent ); delete newStateEvent;
mitk::SlicesSwiveller::mitkClassMacro | ( | SlicesSwiveller | , |
SlicesCoordinator | |||
) |
mitk::SlicesSwiveller::mitkNewMacro1Param | ( | Self | , |
const char * | |||
) |
New Macro with one parameter for creating this object with static New(..) method.
Reimplemented from mitk::SlicesCoordinator.
SlicesSwiveller::Pointer mitk::SlicesSwiveller::New | ( | ) | [static] |
Definition at line 22 of file mitkSlicesSwiveller.cpp.
Referenced by QmitkStdMultiWidget::InitializeWidget(), and OnSliceControllerAdded().
{
void mitk::SlicesSwiveller::OnSliceControllerAdded | ( | SliceNavigationController * | snc ) | [protected, virtual] |
for implementation in subclasses
Reimplemented from mitk::SlicesCoordinator.
Definition at line 40 of file mitkSlicesSwiveller.cpp.
References New().
{ SlicesSwiveller::Pointer SlicesSwiveller::New() { return SlicesSwiveller::New("slices-rotator");
void mitk::SlicesSwiveller::OnSliceControllerRemoved | ( | SliceNavigationController * | snc ) | [protected, virtual] |
for implementation in subclasses
Reimplemented from mitk::SlicesCoordinator.
Definition at line 49 of file mitkSlicesSwiveller.cpp.
: SlicesCoordinator(machine), m_PreviousRotationAngle( 0.0 ) { }
void mitk::SlicesSwiveller::SetGeometry | ( | const itk::EventObject & | EventObject ) | [virtual] |
Is called whenever a SliceNavigationController invokes an event. Will.
Definition at line 57 of file mitkSlicesSwiveller.cpp.
{
void mitk::SlicesSwiveller::UpdateRelevantSNCs | ( | ) | [protected, virtual] |
Updates the list of SliceNavigationControllers that can handle rotation.
Definition at line 65 of file mitkSlicesSwiveller.cpp.
{ if (!snc) return; // nothing to do } // update the list of SliceNavigationControllers that can handle rotation void SlicesSwiveller::SetGeometry(const itk::EventObject& /*EventObject*/) { // there is no way to determine the sender? // ==> update whole list of SNCs UpdateRelevantSNCs(); } void SlicesSwiveller::UpdateRelevantSNCs() { m_RelevantSNCs.clear(); SNCVector::iterator iter; for ( iter = m_SliceNavigationControllers.begin(); iter != m_SliceNavigationControllers.end(); ++iter) { const Geometry3D* geometry3D = (*iter)->GetCreatedWorldGeometry(); const TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast<const TimeSlicedGeometry*>( geometry3D ); if (!timeSlicedGeometry) continue; const SlicedGeometry3D* slicedGeometry = dynamic_cast<const SlicedGeometry3D*>( timeSlicedGeometry->GetGeometry3D(0) ); if (!slicedGeometry) continue; Geometry2D *firstSlice( NULL ); Geometry2D *secondSlice( NULL ); if (slicedGeometry->IsValidSlice(0)) { firstSlice = slicedGeometry->GetGeometry2D(0); } if (slicedGeometry->IsValidSlice(1)) { secondSlice = slicedGeometry->GetGeometry2D(1); } // If the direction vector of these two slices is the same, then accept // this slice stack as rotatable Vector3D right1 = firstSlice->GetAxisVector(0); Vector3D up1 = firstSlice->GetAxisVector(1); vnl_vector_fixed< ScalarType, 3 > vnlDirection1 = vnl_cross_3d(right1.GetVnlVector(), up1.GetVnlVector()); Vector3D direction1; direction1.SetVnlVector(vnlDirection1); Vector3D right2 = firstSlice->GetAxisVector(0);
Point3D mitk::SlicesSwiveller::m_CenterOfRotation [protected] |
Definition at line 101 of file mitkSlicesSwiveller.h.
Point3D mitk::SlicesSwiveller::m_LastCursorPosition [protected] |
Definition at line 99 of file mitkSlicesSwiveller.h.
ScalarType mitk::SlicesSwiveller::m_PreviousRotationAngle [protected] |
Definition at line 110 of file mitkSlicesSwiveller.h.
Vector3D mitk::SlicesSwiveller::m_PreviousRotationAxis [protected] |
Definition at line 109 of file mitkSlicesSwiveller.h.
Point2D mitk::SlicesSwiveller::m_ReferenceCursor [protected] |
Definition at line 103 of file mitkSlicesSwiveller.h.
SNCVector mitk::SlicesSwiveller::m_RelevantSNCs [protected] |
All SNCs that currently have CreatedWorldGeometries, that can be rotated
Definition at line 94 of file mitkSlicesSwiveller.h.
Vector3D mitk::SlicesSwiveller::m_RotationPlaneNormal [protected] |
Definition at line 105 of file mitkSlicesSwiveller.h.
Vector3D mitk::SlicesSwiveller::m_RotationPlaneXVector [protected] |
Definition at line 106 of file mitkSlicesSwiveller.h.
Vector3D mitk::SlicesSwiveller::m_RotationPlaneYVector [protected] |
Definition at line 107 of file mitkSlicesSwiveller.h.
SNCVector mitk::SlicesSwiveller::m_SNCsToBeRotated [protected] |
SNCs that will be rotated (clicked plane + all relevant others, if linked)
Definition at line 97 of file mitkSlicesSwiveller.h.