Page MenuHomePhabricator

all-changes-in-one-patch.diff

Authored By
maleike
Oct 3 2012, 12:52 AM
Size
37 KB
Referenced Files
None
Subscribers
None

all-changes-in-one-patch.diff

diff --git a/Core/Code/Controllers/mitkSlicesCoordinator.cpp b/Core/Code/Controllers/mitkSlicesCoordinator.cpp
index 056075e..e7aceb3 100644
--- a/Core/Code/Controllers/mitkSlicesCoordinator.cpp
+++ b/Core/Code/Controllers/mitkSlicesCoordinator.cpp
@@ -92,10 +92,10 @@ void SlicesCoordinator::OnSliceControllerRemoved(SliceNavigationController*)
// implement in subclasses
}
-bool SlicesCoordinator::ExecuteAction(Action*, StateEvent const*)
+bool SlicesCoordinator::ExecuteAction(Action* a, StateEvent const* e)
{
- // implement in subclasses
- return false;
+ // implement in subclasses, BUT call default implementation to make these CONNECT_ACTION statements work out
+ return Superclass::ExecuteAction(a,e);
}
} // namespace
diff --git a/Core/Code/Controllers/mitkSlicesRotator.cpp b/Core/Code/Controllers/mitkSlicesRotator.cpp
index c2a7336..e923b2e 100644
--- a/Core/Code/Controllers/mitkSlicesRotator.cpp
+++ b/Core/Code/Controllers/mitkSlicesRotator.cpp
@@ -51,7 +51,12 @@ SlicesRotator::Pointer SlicesRotator::New()
SlicesRotator::SlicesRotator(const char* machine)
: SlicesCoordinator(machine)
{
-
+ // make sure that AcSWITCHON and AcSWITCHOFF are defined int constants somewhere (e.g. mitkInteractionConst.h)
+ CONNECT_ACTION( AcMOVE, DoSelectSlice );
+ CONNECT_ACTION( AcCHECKPOINT, DoDecideBetweenRotationAndSliceSelection );
+ CONNECT_ACTION( AcROTATESTART, DoStartRotation );
+ CONNECT_ACTION( AcROTATE, DoRotationStep );
+ CONNECT_ACTION( AcROTATEEND, DoEndRotation );
}
SlicesRotator::~SlicesRotator()
@@ -60,7 +65,6 @@ SlicesRotator::~SlicesRotator()
}
-// check if the slices of this SliceNavigationController can be rotated (???) Possible
void SlicesRotator::OnSliceControllerAdded(SliceNavigationController* snc)
{
if (!snc) return;
@@ -72,7 +76,7 @@ void SlicesRotator::OnSliceControllerAdded(SliceNavigationController* snc)
void SlicesRotator::OnSliceControllerRemoved(SliceNavigationController* snc)
{
if (!snc) return;
- // nothing to do
+ // nothing to do, base class does the bookkeeping
}
/// Is called whenever a SliceNavigationController invokes an event. Will update the list
@@ -81,7 +85,7 @@ void SlicesRotator::SetGeometry(const itk::EventObject& /*EventObject*/)
{
// there is no way to determine the sender?
// ==> update whole list of SNCs
- UpdateRelevantSNCs();
+ UpdateRotatableSNCs();
}
@@ -92,7 +96,7 @@ void SlicesRotator::RotateToPoint( SliceNavigationController *rotationPlaneSNC,
SliceNavigationController *thirdSNC = NULL;
SNCVector::iterator iter;
- for ( iter = m_RelevantSNCs.begin(); iter != m_RelevantSNCs.end(); ++iter )
+ for ( iter = m_RotatableSNCs.begin(); iter != m_RotatableSNCs.end(); ++iter )
{
if ( ((*iter) != rotationPlaneSNC)
&& ((*iter) != rotatedPlaneSNC) )
@@ -196,7 +200,7 @@ void SlicesRotator::RotateToPoint( SliceNavigationController *rotationPlaneSNC,
else
{
SNCVector::iterator iter;
- for ( iter = m_RelevantSNCs.begin(); iter != m_RelevantSNCs.end(); ++iter )
+ for ( iter = m_RotatableSNCs.begin(); iter != m_RotatableSNCs.end(); ++iter )
{
BaseRenderer *renderer = (*iter)->GetRenderer();
if ( renderer == NULL )
@@ -231,13 +235,13 @@ void SlicesRotator::RotateToPoint( SliceNavigationController *rotationPlaneSNC,
(*iter)->SendCreatedWorldGeometryUpdate();
}
}
-}
+} // end RotateToPoint
/// Updates the list of SliceNavigationControllers that can handle rotation
-void SlicesRotator::UpdateRelevantSNCs()
+void SlicesRotator::UpdateRotatableSNCs()
{
- m_RelevantSNCs.clear();
+ m_RotatableSNCs.clear();
for (SNCVector::iterator iter = m_SliceNavigationControllers.begin(); iter != m_SliceNavigationControllers.end(); ++iter)
{
@@ -248,333 +252,262 @@ void SlicesRotator::UpdateRelevantSNCs()
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);
- Vector3D up2 = firstSlice->GetAxisVector(1);
- vnl_vector_fixed< ScalarType, 3 > vnlDirection2 = vnl_cross_3d(right2.GetVnlVector(), up2.GetVnlVector());
- Vector3D direction2;
- direction2.SetVnlVector(vnlDirection2);
-
- bool equal = true;
- const ScalarType eps = 0.0001;
- for (int i = 0; i < 3; ++i)
- if ( fabs(direction1[i] - direction2[i]) > eps )
- equal = false;
-
- if (equal) // equal direction vectors
+ if (slicedGeometry->IsValidSlice(0))
{
- m_RelevantSNCs.push_back( *iter );
+ // there were some lines of additional checks here in previous versions,
+ // all of which would always evaluate to true, so the check was irrelevant.
+ // Since the original intent was not documented, I removed all checks,
+ // i.e. m_RotatableSNCs ends up being a list of all the registered
+ // SliceNavigationControllers which have a SlicedGeometry3D with at least one slice,
+ // which covers most standard cases.
+ m_RotatableSNCs.push_back( *iter );
}
}
}
-bool SlicesRotator::ExecuteAction(Action* action, StateEvent const* stateEvent)
+bool SlicesRotator::DoSelectSlice(Action* a, const StateEvent* e)
{
- const ScalarType ThreshHoldDistancePixels = 12.0;
-
- bool ok = false;
-
- switch ( action->GetActionId() )
+ // just reach through
+ for (SNCVector::iterator iter = m_RotatableSNCs.begin(); iter != m_RotatableSNCs.end(); ++iter)
{
- case AcMOVE:
+ if ( !(*iter)->GetSliceLocked() )
{
- // just reach through
- for (SNCVector::iterator iter = m_RelevantSNCs.begin(); iter != m_RelevantSNCs.end(); ++iter)
- {
- if ( !(*iter)->GetSliceLocked() )
- {
- (*iter)->ExecuteAction(action, stateEvent);
- }
- }
-
- ok = true;
- break;
+ (*iter)->ExecuteAction(a,e);
}
- case AcROTATE:
- {
- const DisplayPositionEvent* posEvent = dynamic_cast<const DisplayPositionEvent*>(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);
+ return true;
+}
- // TEST
- int i = 0;
+bool SlicesRotator::DoDecideBetweenRotationAndSliceSelection(Action*, const StateEvent* e)
+{
+ // Decide between moving and rotation slices.
+ // For basic decision logic see class documentation.
+
+ /*
+ Detail logic:
+
+ 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Must not even be counted or checked..
+ 2. Inspect every other SliceNavigationController
+ - calculate the line intersection of this SliceNavigationController's plane with our rendering plane
+ - if there is no interesection, ignore and continue
+ - IF there is an intersection
+ - check the mouse cursor's distance from that line.
+ 0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in "locked" mode)
+ 1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate
+ 2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to rotate
+ - if yes, we just push this line to the "other" lines and rotate it along
+ - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to rotate
+ */
+ const DisplayPositionEvent* posEvent = dynamic_cast<const DisplayPositionEvent*>(e->GetEvent());
+ if (!posEvent) return false;
+
+ BaseRenderer* clickedRenderer = e->GetEvent()->GetSender();
+ const PlaneGeometry* ourViewportGeometry = dynamic_cast<const PlaneGeometry*>( clickedRenderer->GetCurrentWorldGeometry2D() );
+ if (!ourViewportGeometry) return false;
- for (SNCVector::iterator iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
- {
- BaseRenderer *renderer = (*iter)->GetRenderer();
- if ( renderer == NULL )
- {
- continue;
- }
+ DisplayGeometry* clickedDisplayGeometry = clickedRenderer->GetDisplayGeometry();
+ if (!clickedDisplayGeometry) return false;
+
+ MITK_DEBUG << "=============================================";
+ MITK_DEBUG << "Renderer under cursor is " << clickedRenderer->GetName();
+
+ Point3D cursorPosition = posEvent->GetWorldPosition();
+ const PlaneGeometry* geometryToBeRotated = NULL; // this one is under the mouse cursor
+ const PlaneGeometry* anyOtherGeometry = NULL; // this is also visible (for calculation of intersection ONLY)
+ Line3D intersectionLineWithGeometryToBeRotated;
- DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry();
+ bool hitMultipleLines(false);
+ m_SNCsToBeRotated.clear();
- // MITK_INFO << i << ":" << std::endl;
+ const double threshholdDistancePixels = 12.0;
+
+ for (SNCVector::iterator iter = m_RotatableSNCs.begin(); iter != m_RotatableSNCs.end(); ++iter)
+ {
+ const PlaneGeometry* otherRenderersRenderPlane = (*iter)->GetCurrentPlaneGeometry();
+ if (otherRenderersRenderPlane == NULL) continue; // ignore, we don't see a plane
+ MITK_DEBUG << " Checking plane of renderer " << (*iter)->GetRenderer()->GetName();
+
+ // check if there is an intersection
+ Line3D intersectionLine; // between rendered/clicked geometry and the one being analyzed
+ if (!ourViewportGeometry->IntersectionLine( otherRenderersRenderPlane, intersectionLine ))
+ {
+ continue; // we ignore this plane, it's parallel to our plane
+ }
- Point2D point2DWorld, point2DDisplayPre, point2DDisplayPost;
- displayGeometry->Map( m_CenterOfRotation, point2DWorld );
- displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPre );
+ // check distance from intersection line
+ double distanceFromIntersectionLine = intersectionLine.Distance( cursorPosition );
+ ScalarType distancePixels = distanceFromIntersectionLine / clickedDisplayGeometry->GetScaleFactorMMPerDisplayUnit();
+ MITK_DEBUG << " Distance of plane from cursor " << distanceFromIntersectionLine << " mm, which is around " << distancePixels << " px" ;
- // MITK_INFO << " WorldPre: " << point2DWorld << " / DisplayPre: " << point2DDisplayPre << std::endl;
+ // far away line, only remember for linked rotation if necessary
+ if (distanceFromIntersectionLine > threshholdDistancePixels)
+ {
+ MITK_DEBUG << " Plane is too far away --> remember as otherRenderersRenderPlane";
+ anyOtherGeometry = otherRenderersRenderPlane; // we just take the last one, so overwrite each iteration (we just need some crossing point)
+ // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used
- const Geometry3D* geometry3D = (*iter)->GetCreatedWorldGeometry();
- const TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast<const TimeSlicedGeometry*>(geometry3D);
- if (!timeSlicedGeometry) continue;
-
- const_cast<TimeSlicedGeometry*>(timeSlicedGeometry)->ExecuteOperation(&op);
+ if (m_LinkPlanes)
+ {
+ m_SNCsToBeRotated.push_back(*iter);
+ }
+ }
+ else // close to cursor
+ {
+ MITK_DEBUG << " Plane is close enough to cursor...";
+ if ( geometryToBeRotated == NULL ) // first one close to the cursor
+ {
+ MITK_DEBUG << " It is the first close enough geometry, remember as geometryToBeRotated";
+ geometryToBeRotated = otherRenderersRenderPlane;
+ intersectionLineWithGeometryToBeRotated = intersectionLine;
+ m_SNCsToBeRotated.push_back(*iter);
+ }
+ else
+ {
+ MITK_DEBUG << " Second or later close enough geometry";
+ // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane together with the primary one
+ // if different, DON'T rotate
- //vtkLinearTransform *inverseTransformVtk =
- // displayGeometry->GetVtkTransform()->GetLinearInverse();
+ if ( intersectionLine.IsParallel( intersectionLineWithGeometryToBeRotated )
+ && intersectionLine.Distance( intersectionLineWithGeometryToBeRotated.GetPoint1() ) < mitk::eps )
+ {
+ MITK_DEBUG << " This line is the same as intersectionLineWithGeometryToBeRotated which we already know";
+ m_SNCsToBeRotated.push_back(*iter);
+ }
+ else
+ {
+ MITK_DEBUG << " This line is NOT the same as intersectionLineWithGeometryToBeRotated which we already know";
+ hitMultipleLines = true;
+ }
+ }
+ }
- //ScalarType pvtkCenterOfRotation[3];
- //pvtkCenterOfRotation[0] = m_CenterOfRotation[0];
- //pvtkCenterOfRotation[1] = m_CenterOfRotation[1];
- //pvtkCenterOfRotation[2] = m_CenterOfRotation[2];
+ }
- //ScalarType scaleFactorMMPerUnitX =
- // displayGeometry->GetExtentInMM(0) / displayGeometry->GetExtent(0);
- //ScalarType scaleFactorMMPerUnitY =
- // displayGeometry->GetExtentInMM(1) / displayGeometry->GetExtent(1);
+ bool moveSlices(true);
- //ScalarType scaleFactorMMPerDisplayUnit = displayGeometry->GetScaleFactorMMPerDisplayUnit();
- //Vector2D &originInMM = displayGeometry->GetOriginInMM();
+ if ( geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !hitMultipleLines )
+ {
+ // assure all three are valid, so calculation of center of rotation can be done
+ moveSlices = false;
+ }
- ////displayGeometry->Map( m_CenterOfRotation, point2DWorld );
+ MITK_DEBUG << "geometryToBeRotated: " << (void*)geometryToBeRotated;
+ MITK_DEBUG << "anyOtherGeometry: " << (void*)anyOtherGeometry;
+ MITK_DEBUG << "ourViewportGeometry: " << (void*)ourViewportGeometry;
+ MITK_DEBUG << "hitMultipleLines? " << hitMultipleLines;
+ MITK_DEBUG << "moveSlices? " << moveSlices;
- //ScalarType pvtkDisplayPost[3];
- //inverseTransformVtk->TransformPoint( pvtkCenterOfRotation, pvtkDisplayPost );
- //pvtkDisplayPost[0] *= scaleFactorMMPerUnitX;
- //pvtkDisplayPost[1] *= scaleFactorMMPerUnitY;
+ StateEvent* decidedEvent(NULL);
- ////displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPost );
- //pvtkDisplayPost[0] -= originInMM[0];
- //pvtkDisplayPost[1] -= originInMM[1];
+ // question in state machine is: "rotate?"
+ if (moveSlices) // i.e. NOT rotate
+ {
+ // move all planes to posEvent->GetWorldPosition()
+ decidedEvent = new StateEvent(EIDNO, e->GetEvent());
+ MITK_DEBUG << "Rotation not possible, not enough information (other planes crossing rendering plane) ";
+ }
+ else
+ { // we DO have enough information for rotation
+ m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(cursorPosition); // remember where the last cursor position ON THE LINE has been observed
- //pvtkDisplayPost[0] /= scaleFactorMMPerDisplayUnit;
- //pvtkDisplayPost[1] /= scaleFactorMMPerDisplayUnit;
+ if (anyOtherGeometry->IntersectionPoint(intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) // find center of rotation by intersection with any of the OTHER lines
+ {
+ decidedEvent = new StateEvent(EIDYES, e->GetEvent());
+ MITK_DEBUG << "Rotation possible";
+ }
+ else
+ {
+ MITK_DEBUG << "Rotation not possible, cannot determine the center of rotation!?";
+ decidedEvent = new StateEvent(EIDNO, e->GetEvent());
+ }
+ }
- //point2DDisplayPost[0] = pvtkDisplayPost[0];
- //point2DDisplayPost[1] = pvtkDisplayPost[1];
+ this->HandleEvent( decidedEvent );
+ delete decidedEvent;
+ return true;
+}
- displayGeometry->Map( m_CenterOfRotation, point2DWorld );
- displayGeometry->WorldToDisplay( point2DWorld, point2DDisplayPost );
- Vector2D vector2DDisplayDiff = point2DDisplayPost - point2DDisplayPre;
+bool SlicesRotator::DoStartRotation(Action*, const StateEvent*)
+{
+ this->SetMouseCursor( rotate_cursor_xpm, 0, 0 );
+ this->InvokeEvent( SliceRotationEvent() ); // notify listeners
+ return true;
+}
- //Vector2D origin = displayGeometry->GetOriginInMM();
- // MITK_INFO << " WorldPost: " << point2DWorld << " / DisplayPost: " << point2DDisplayPost << std::endl;
- // MITK_INFO << " Diff - " << vector2DDisplayDiff << std::endl;
- // MITK_INFO << " Origin - " << origin << std::endl;
- ++i;
+bool SlicesRotator::DoEndRotation(Action*, const StateEvent*)
+{
+ this->ResetMouseCursor();
+ this->InvokeEvent( SliceRotationEvent() ); // notify listeners
+ return true;
+}
- displayGeometry->MoveBy( vector2DDisplayDiff );
- (*iter)->SendCreatedWorldGeometryUpdate();
- }
- // MITK_INFO << "--------------------------------" << std::endl;
+bool SlicesRotator::DoRotationStep(Action*, const StateEvent* e)
+{
+ const DisplayPositionEvent* posEvent = dynamic_cast<const DisplayPositionEvent*>(e->GetEvent());
+ if (!posEvent) return false;
-
-
- // TEST
- //BaseRenderer* renderer = stateEvent->GetEvent()->GetSender(); // TODO this is NOT SNC-specific! Should be!
- //
- //DisplayGeometry* displayGeometry = renderer->GetDisplayGeometry();
- //if (!displayGeometry) break;
+ Point3D cursor = posEvent->GetWorldPosition();
- //Point2D point2DWorld, point2DDisplay;
- //displayGeometry->Map( m_CenterOfRotation, point2DWorld );
- //displayGeometry->WorldToDisplay( point2DWorld, point2DDisplay );
+ Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation;
+ Vector3D toCursor = cursor - m_CenterOfRotation;
- //MITK_INFO << "RotationCenter: " << m_CenterOfRotation << std::endl;
- //MITK_INFO << "PointWorld: " << point2DWorld << std::endl;
- //MITK_INFO << "PointDisplay: " << point2DDisplay << std::endl;
- //MITK_INFO << "--------------------------------------------" << std::endl;
+ // 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 rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle);
- RenderingManager::GetInstance()->RequestUpdateAll();
-
- this->InvokeEvent( SliceRotationEvent() ); // notify listeners
-
- ok = true;
- break;
- }
- case AcCHECKPOINT:
- {
- // decide between moving and rotation
-
- // 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
- //
- const DisplayPositionEvent* posEvent = dynamic_cast<const DisplayPositionEvent*>(stateEvent->GetEvent());
- if (!posEvent) break;
-
- Point3D cursor = posEvent->GetWorldPosition();
- //m_LastCursorPosition = cursor;
-
- unsigned int numNearPlanes = 0;
- m_SNCsToBeRotated.clear();
- Geometry2D* geometryToBeRotated = NULL; // this one is grabbed
- Geometry2D* otherGeometry = NULL; // this is also visible (for calculation of intersection)
- Geometry2D* clickedGeometry = NULL; // the event originates from this one
- //SlicedGeometry3D* clickedSlicedGeometry;
-
- for (SNCVector::iterator iter = m_RelevantSNCs.begin(); iter != m_RelevantSNCs.end(); ++iter)
- {
- unsigned int slice = (*iter)->GetSlice()->GetPos();
- unsigned int time = (*iter)->GetTime()->GetPos();
-
- const Geometry3D* geometry3D = (*iter)->GetCreatedWorldGeometry();
- const TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast<const TimeSlicedGeometry*>( geometry3D );
- if (!timeSlicedGeometry) continue;
+ // iterate the OTHER slice navigation controllers: these are filled in DoDecideBetweenRotationAndSliceSelection
+ for (SNCVector::iterator iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter)
+ {
+ // - remember the center of rotation on the 2D display BEFORE rotation
+ // - execute rotation
+ // - calculate new center of rotation on 2D display
+ // - move display IF the center of rotation has moved slightly before and after rotation
- const SlicedGeometry3D* slicedGeometry = dynamic_cast<const SlicedGeometry3D*>( timeSlicedGeometry->GetGeometry3D(time) );
- if (!slicedGeometry) continue;
+ // DM 2012-10: this must probably be due to rounding errors only, right?
+ // We don't have documentation on if/why this code is needed
+ BaseRenderer *renderer = (*iter)->GetRenderer();
+ if ( !renderer ) continue;
- Geometry2D* geometry2D = slicedGeometry->GetGeometry2D(slice);
- if (!geometry2D) continue; // this is not necessary?
+ DisplayGeometry *displayGeometry = renderer->GetDisplayGeometry();
- ScalarType distanceMM = geometry2D->Distance( cursor );
+ Point2D rotationCenter2DWorld, point2DDisplayPreRotation, point2DDisplayPostRotation;
+ displayGeometry->Map( m_CenterOfRotation, rotationCenter2DWorld );
+ displayGeometry->WorldToDisplay( rotationCenter2DWorld, point2DDisplayPreRotation );
- BaseRenderer* renderer = stateEvent->GetEvent()->GetSender(); // TODO this is NOT SNC-specific! Should be!
-
- DisplayGeometry* displayGeometry = renderer->GetDisplayGeometry();
- if (!displayGeometry) continue;
-
- ScalarType distancePixels = distanceMM / displayGeometry->GetScaleFactorMMPerDisplayUnit();
- if ( distancePixels <= ThreshHoldDistancePixels )
- {
- ++numNearPlanes; // count this one as a plane near to the cursor
- }
-
- if ( *iter == renderer->GetSliceNavigationController() ) // don't rotate the one where the user clicked
- {
- clickedGeometry = geometry2D;
- //clickedSlicedGeometry = const_cast<SlicedGeometry3D*>(slicedGeometry);
- }
- else
- {
- // @TODO here waits some bug to be found - maybe fixed by the || m_LinkPlanes in next line
- if ( (distancePixels <= ThreshHoldDistancePixels)
- && !(*iter)->GetSliceRotationLocked()
- && (m_SNCsToBeRotated.empty() || m_LinkPlanes) )
- {
- // this one is behind the clicked "line"
- m_SNCsToBeRotated.push_back(*iter);
- geometryToBeRotated = geometry2D;
- }
- else
- {
- otherGeometry = geometry2D;
-
- if ( m_LinkPlanes )
- {
- // All slices are rotated, i.e. the relative angles between
- // slices remain fixed
- m_SNCsToBeRotated.push_back(*iter);
- }
- }
- }
- }
+ const Geometry3D* geometry3D = (*iter)->GetCreatedWorldGeometry();
+ const TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast<const TimeSlicedGeometry*>(geometry3D);
+ if (!timeSlicedGeometry) continue;
- bool move (true);
+ const_cast<TimeSlicedGeometry*>(timeSlicedGeometry)->ExecuteOperation(&rotationOperation);
- if ( geometryToBeRotated && otherGeometry && clickedGeometry
- && ( numNearPlanes == 2 ) )
- {
- // assure all three are valid, so calculation of center of rotation can be done
- move = false;
- }
-
- StateEvent *newStateEvent(NULL);
+ displayGeometry->Map( m_CenterOfRotation, rotationCenter2DWorld );
+ displayGeometry->WorldToDisplay( rotationCenter2DWorld, point2DDisplayPostRotation );
+ Vector2D vector2DDisplayDiff = point2DDisplayPostRotation - point2DDisplayPreRotation;
- // question in state machine is: "rotate?"
- if (move)
- {
- // move all planes to posEvent->GetWorldPosition()
- newStateEvent = new StateEvent(EIDNO, stateEvent->GetEvent());
- }
- else
- {
- // determine center of rotation TODO requires two plane geometries...
- PlaneGeometry* planeGeometry = dynamic_cast<PlaneGeometry*>(clickedGeometry);
- PlaneGeometry* planeGeometry1 = dynamic_cast<PlaneGeometry*>(geometryToBeRotated);
- PlaneGeometry* planeGeometry2 = dynamic_cast<PlaneGeometry*>(otherGeometry);
-
- if (!planeGeometry || !planeGeometry1 || !planeGeometry2) break;
-
- Line3D intersection;
- if (!planeGeometry->IntersectionLine( planeGeometry1, intersection )) break;
- m_LastCursorPosition = intersection.Project(cursor);
- if (!planeGeometry2->IntersectionPoint(intersection, m_CenterOfRotation)) break;
- // everything's fine
- newStateEvent = new StateEvent(EIDYES, stateEvent->GetEvent());
+ displayGeometry->MoveBy( vector2DDisplayDiff );
- }
+ (*iter)->SendCreatedWorldGeometryUpdate();
+ }
- if (!newStateEvent) MITK_ERROR << "rotation would be nice but is impossible... " << std::endl;
-
- this->HandleEvent( newStateEvent );
- delete newStateEvent;
+ RenderingManager::GetInstance()->RequestUpdateAll();
- ok = true;
- break;
- }
- case AcROTATESTART:
- {
- this->SetMouseCursor( rotate_cursor_xpm, 0, 0 );
- this->InvokeEvent( SliceRotationEvent() ); // notify listeners
- break;
- }
- case AcROTATEEND:
- {
- this->ResetMouseCursor();
- this->InvokeEvent( SliceRotationEvent() ); // notify listeners
- break;
- }
- default:
- {
- break;
- }
- }
+ this->InvokeEvent( SliceRotationEvent() ); // notify listeners
- return ok;
+ return true;
}
} // namespace
diff --git a/Core/Code/Controllers/mitkSlicesRotator.h b/Core/Code/Controllers/mitkSlicesRotator.h
index cf02880..9b737b3 100644
--- a/Core/Code/Controllers/mitkSlicesRotator.h
+++ b/Core/Code/Controllers/mitkSlicesRotator.h
@@ -27,79 +27,134 @@ See LICENSE.txt or http://www.mitk.org for details.
namespace mitk {
/**
- * \brief Enables rotation of visible slices (for sliced geometries).
- * \ingroup NavigationControl
- *
- * This class takes care of several SliceNavigationControllers and handles
- * slice selection / slice rotation. It is added as listener to
- * GlobalInteraction by QmitkStdMultiWidget.
- *
- * The SlicesRotator 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 SlicesRotator, the rule to choose between slice rotation and
- * selection is simple: For a mouse down event, count the number of visible
- * planes, which are "near" the cursor. If this number equals 2 (one for the
- * window, which currently holds the cursor, one for the intersection line of
- * another visible slice), then initiate rotation, else select slices near
- * the cursor. 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
- * directly selected plane.
- *
- * 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.
- *
- * \sa SlicesSwiveller
+ \brief Coordinates rotation of multiple visible rendering planes (represented as lines in other render windows).
+ \ingroup NavigationControl
+
+ This class takes care of several SliceNavigationControllers and handles slice selection
+ / slice rotation. It is added as listener to GlobalInteraction by QmitkStdMultiWidget.
+
+ The SlicesRotator class adds the possibility of slice rotation to the "normal" behaviour
+ of SliceNavigationControllers (which is picking one plane from a stack of planes).
+
+ This additional class SlicesRotator is needed, because one has to be aware of multiple
+ "visible slices" (selected Geometry2Ds of some SliceNavigationControllers) in order to
+ choose between rotation and slice selection. Such functionality could not be implemented
+ by a single SliceNavigationController.
+
+ Rotation is achieved by modifying (rotating) the generated TimeSlicedGeometry of the
+ corresponding SliceNavigationControllers.
+
+ \section mitkSlicesRotator_StandardCase The standard case: three orthogonal views (MPR)
+
+ With SlicesRotator, the rule to choose between slice rotation and selection is simple:
+ For a mouse down event, count the number of visible planes, which are "near" the cursor.
+ If this number is 2 (one for the window, which currently holds the cursor, one for the
+ intersection line of another visible slice), then initiate rotation, else select slices
+ near the cursor. If the "LinkPlanes" flag is set, the rotation is applied to the planes
+ of all registered SNCs, not only of the one associated with the directly selected plane.
+
+ 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.
+
+ \section mitkSlicesRotator_GeneralizedCase The generalized case: any number of views
+
+ Above section as well as the original implementation of this class assumes that we have
+ exactly three 2D vies in our scene. This used to be the standard setup of the MITK
+ associated application for a long time. With custom applications based on MITK it is
+ easy to create different situations. One usual use case would be to have one extra
+ render window display the contents of any of the other ones and behave exactly like it
+ (could e.g. be used on a second screen).
+
+ In this situation the above assumption "we rotate when there are exactly 2 slices close
+ to the cursor" will not hold: since we always have two render windows displaying the
+ exact same slice, the number of 2 is the minimum we get. Whenever the user clicks in one
+ of those windows and the cursor is close to one of the orthogonal planes, we will get a
+ count of 3 or more planes that are "close to the cursor".
+
+ For the class to behave correctly, we actually need to distinguish three separate cases:
+ 1. the cursor is not close to any orthogonal planes. This should result in slice selection.
+ 2. the cursor is close to just one orthogonal plane OR multiple which are not distinguishable visually. This should result in rotation.
+ 3. the cursor is close to multiple orthogonal planes which are rendered as distinguishable lines on the render window. This is the case when we hit the crosshair-center of the view. In this case, we need to also just select slices.
+
+ \section mitkSlicesRotator_Solution Deciding between slice selection and rotation
+
+ The "counting nearby lines in the renderwindow" can also work for the general case
+ described above. Only one details needs to be accounted for: we must not count a line
+ when it is identical to another line. I.e. we just count how many visible lines on the
+ screen are very close to the cursor. When this number is 1, we rotate, otherwise we let
+ the SliceNavigationControllers do their slice selection job.
+
+ \sa SlicesSwiveller
*/
class MITK_CORE_EXPORT SlicesRotator : public SlicesCoordinator
{
-public:
+ public:
- mitkClassMacro(SlicesRotator, SlicesCoordinator);
-
- static Pointer New();
+ mitkClassMacro(SlicesRotator, SlicesCoordinator);
+
+ static Pointer New();
+
+ /**
+ \brief New Macro with one parameter for creating this object with static New(..) method.
+
+ Needs to be the "slices-rotator" pattern of StateMachine.xml to work as expected.
+ **/
+ mitkNewMacro1Param(Self, const char*);
+
+ /**
+ \brief Callback for modifications in observed SliceNavigationControllers -- forwards to UpdateRotatableSNCs().
- /**
- * @brief New Macro with one parameter for creating this object with static New(..) method
- **/
- mitkNewMacro1Param(Self, const char*);
+ This method is called when an observed SliceNavigationController changes its
+ world geometry. The connection is established by calling the other SliceNavigationController's
+ method ConnectGeometrySendEvent (or similar).
+ */
+ virtual void SetGeometry(const itk::EventObject& EventObject);
- virtual void SetGeometry(const itk::EventObject& EventObject);
+ /**
+ \brief NOT USED by anything open-source.
- virtual void RotateToPoint( SliceNavigationController *rotationPlaneSNC,
- SliceNavigationController *rotatedPlaneSNC,
- const Point3D &point, bool linked = false );
+ \TODO check if this is actually still needed, with SliceNavigationController::ReorientSlices() now being implemented this could be obsolete!?
+ */
+ virtual void RotateToPoint( SliceNavigationController *rotationPlaneSNC,
+ SliceNavigationController *rotatedPlaneSNC,
+ const Point3D &point,
+ bool linked = false );
-protected:
+ protected:
- SlicesRotator(const char* machine);
- // clear list of controllers
- virtual ~SlicesRotator();
+ SlicesRotator(const char* machine);
+ virtual ~SlicesRotator();
- // check if the slices of this SliceNavigationController can be rotated (???) Possible
- virtual void OnSliceControllerAdded(SliceNavigationController* snc);
+ /**
+ \brief Called from SlicesCoordinator after a new controller is added (to internal list m_SliceNavigationControllers).
+ */
+ virtual void OnSliceControllerAdded(SliceNavigationController* snc);
- virtual void OnSliceControllerRemoved(SliceNavigationController* snc);
+ /*
+ \brief Called from SlicesCoordinator after a new controller is being removed (to internal list m_SliceNavigationControllers).
+ */
+ virtual void OnSliceControllerRemoved(SliceNavigationController* snc);
- virtual void UpdateRelevantSNCs();
+ /**
+ \brief Check all observed SliceNavigationControllers: remember those that are rotatable in m_RotatableSNCs.
+ */
+ virtual void UpdateRotatableSNCs();
- virtual bool ExecuteAction(Action * action, StateEvent const* stateEvent);
+ // following methods called from superclass ExecuteAction
+ bool DoSelectSlice(Action*, const StateEvent*);
+ bool DoDecideBetweenRotationAndSliceSelection(Action*, const StateEvent*);
+ bool DoStartRotation(Action*, const StateEvent*);
+ bool DoEndRotation(Action*, const StateEvent*);
+ bool DoRotationStep(Action*, const StateEvent*);
- SNCVector m_RelevantSNCs; /// all SNCs that currently have CreatedWorldGeometries, that can be rotated.
- SNCVector m_SNCsToBeRotated; /// all SNCs that will be rotated
+ SNCVector m_RotatableSNCs; /// all SNCs that currently have CreatedWorldGeometries, that can be rotated.
+ SNCVector m_SNCsToBeRotated; /// all SNCs that will be rotated (exceptions are the ones parallel to the one being clicked)
- Point3D m_LastCursorPosition;
- Point3D m_CenterOfRotation;
+ Point3D m_LastCursorPosition; /// used for calculation of the rotation angle
+ Point3D m_CenterOfRotation; /// used for calculation of the rotation angle
};

File Metadata

Mime Type
text/plain
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
883
Default Alt Text
all-changes-in-one-patch.diff (37 KB)

Event Timeline

Patch against MITK v2012.09.0