diff --git a/Modules/Core/include/mitkDisplayActionEvents.h b/Modules/Core/include/mitkDisplayActionEvents.h index 22c61e1248..7c66bb9ad7 100644 --- a/Modules/Core/include/mitkDisplayActionEvents.h +++ b/Modules/Core/include/mitkDisplayActionEvents.h @@ -1,188 +1,189 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. 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 MITKDISPLAYACTIONEVENTS_H #define MITKDISPLAYACTIONEVENTS_H #include // mitk core #include "mitkInteractionEvent.h" // itk #include #include #include namespace mitk { class MITKCORE_EXPORT DisplayActionEvent : public itk::AnyEvent { public: typedef DisplayActionEvent Self; typedef itk::AnyEvent Superclass; DisplayActionEvent() : m_InteractionEvent(nullptr) {} DisplayActionEvent(InteractionEvent* interactionEvent) : m_InteractionEvent(interactionEvent) {} virtual ~DisplayActionEvent() {} virtual const char* GetEventName() const override { return "DisplayActionEvent"; } virtual bool CheckEvent(const itk::EventObject* e) const override { return dynamic_cast(e) != nullptr; } virtual itk::EventObject* MakeObject() const override { return new Self(m_InteractionEvent); } InteractionEvent* GetInteractionEvent() const { return m_InteractionEvent; } BaseRenderer* GetSender() const { return m_InteractionEvent != nullptr ? m_InteractionEvent->GetSender() : nullptr; } DisplayActionEvent(const Self& s) : Superclass(s), m_InteractionEvent(s.GetInteractionEvent()) {}; private: InteractionEvent* m_InteractionEvent; void operator=(const Self &); }; class MITKCORE_EXPORT DisplayMoveEvent : public DisplayActionEvent { public: typedef DisplayMoveEvent Self; typedef DisplayActionEvent Superclass; DisplayMoveEvent() : Superclass() {} DisplayMoveEvent(InteractionEvent* interactionEvent, const Vector2D& moveVector) : Superclass(interactionEvent) , m_MoveVector(moveVector) { } virtual ~DisplayMoveEvent() {} virtual const char* GetEventName() const override { return "DisplayMoveEvent"; } virtual bool CheckEvent(const itk::EventObject* e) const override { return dynamic_cast(e) != nullptr; } virtual itk::EventObject* MakeObject() const override { return new Self(GetInteractionEvent(), m_MoveVector); } const Vector2D& GetMoveVector() const { return m_MoveVector; } DisplayMoveEvent(const Self& s) : Superclass(s), m_MoveVector(s.GetMoveVector()) {}; private: Vector2D m_MoveVector; }; class MITKCORE_EXPORT DisplaySetCrosshairEvent : public DisplayActionEvent { public: typedef DisplaySetCrosshairEvent Self; typedef DisplayActionEvent Superclass; DisplaySetCrosshairEvent() : Superclass() {} DisplaySetCrosshairEvent(InteractionEvent* interactionEvent, const Point3D& position) : Superclass(interactionEvent) , m_Position(position) { } virtual ~DisplaySetCrosshairEvent() {} virtual const char* GetEventName() const override { return "DisplaySetCrosshairEvent"; } virtual bool CheckEvent(const itk::EventObject* e) const override { return dynamic_cast(e) != nullptr; } virtual itk::EventObject* MakeObject() const override { return new Self(GetInteractionEvent(), m_Position); } const Point3D& GetPosition() const { return m_Position; } DisplaySetCrosshairEvent(const Self& s) : Superclass(s), m_Position(s.GetPosition()) {}; private: Point3D m_Position; }; class MITKCORE_EXPORT DisplayZoomEvent : public DisplayActionEvent { public: typedef DisplayZoomEvent Self; typedef DisplayActionEvent Superclass; DisplayZoomEvent() : Superclass() {} DisplayZoomEvent(InteractionEvent* interactionEvent, float zoomFactor, const Point2D& startCoordinate) : Superclass(interactionEvent) , m_ZoomFactor(zoomFactor) , m_StartCoordinate(startCoordinate) { } virtual ~DisplayZoomEvent() {} virtual const char* GetEventName() const override { return "DisplayZoomEvent"; } virtual bool CheckEvent(const itk::EventObject* e) const override { return dynamic_cast(e) != nullptr; } virtual itk::EventObject* MakeObject() const override { return new Self(GetInteractionEvent(), m_ZoomFactor, m_StartCoordinate); } float GetZoomFactor() const { return m_ZoomFactor; } const Point2D& GetStartCoordinate() const { return m_StartCoordinate; } DisplayZoomEvent(const Self& s) : Superclass(s), m_ZoomFactor(s.GetZoomFactor()), m_StartCoordinate(s.GetStartCoordinate()) {}; private: float m_ZoomFactor; Point2D m_StartCoordinate; }; class MITKCORE_EXPORT DisplayScrollEvent : public DisplayActionEvent { public: typedef DisplayScrollEvent Self; typedef DisplayActionEvent Superclass; DisplayScrollEvent() : Superclass() {} - DisplayScrollEvent(InteractionEvent* interactionEvent, int sliceDelta) + DisplayScrollEvent(InteractionEvent* interactionEvent, int sliceDelta, bool autoRepeat) : Superclass(interactionEvent) , m_SliceDelta(sliceDelta) + , m_AutoRepeat(autoRepeat) { } virtual ~DisplayScrollEvent() {} virtual const char* GetEventName() const override { return "DisplayScrollEvent"; } virtual bool CheckEvent(const itk::EventObject* e) const override { return dynamic_cast(e) != nullptr; } - virtual itk::EventObject* MakeObject() const override { return new Self(GetInteractionEvent(), m_SliceDelta); } + virtual itk::EventObject* MakeObject() const override { return new Self(GetInteractionEvent(), m_SliceDelta, m_AutoRepeat); } int GetSliceDelta() const { return m_SliceDelta; } - DisplayScrollEvent(const Self& s) : Superclass(s), m_SliceDelta(s.GetSliceDelta()) {}; + bool GetAutoRepeat() const { return m_AutoRepeat; } + DisplayScrollEvent(const Self& s) : Superclass(s), m_SliceDelta(s.GetSliceDelta()), m_AutoRepeat(s.GetAutoRepeat()) {}; private: int m_SliceDelta; + bool m_AutoRepeat; }; class MITKCORE_EXPORT DisplaySetLevelWindowEvent : public DisplayActionEvent { public: typedef DisplaySetLevelWindowEvent Self; typedef DisplayActionEvent Superclass; DisplaySetLevelWindowEvent() : Superclass() {} DisplaySetLevelWindowEvent(InteractionEvent* interactionEvent, ScalarType level, ScalarType window) : Superclass(interactionEvent) , m_Level(level) , m_Window(window) { } virtual ~DisplaySetLevelWindowEvent() {} virtual const char* GetEventName() const override { return "DisplaySetLevelWindowEvent"; } virtual bool CheckEvent(const itk::EventObject* e) const override - { - return dynamic_cast(e) != nullptr; - } + { return dynamic_cast(e) != nullptr; } virtual itk::EventObject* MakeObject() const override { return new Self(GetInteractionEvent(), m_Level, m_Window); } ScalarType GetLevel() const { return m_Level; } ScalarType GetWindow() const { return m_Window; } DisplaySetLevelWindowEvent(const Self& s) : Superclass(s), m_Level(s.GetLevel()), m_Window(s.GetWindow()) {}; private: ScalarType m_Level; ScalarType m_Window; }; } // end namespace #endif // MITKDISPLAYACTIONEVENTS_H diff --git a/Modules/Core/resource/Interactions/DisplayConfigPACS.xml b/Modules/Core/resource/Interactions/DisplayConfigPACS.xml index 40dee1e9d7..6d242ef483 100644 --- a/Modules/Core/resource/Interactions/DisplayConfigPACS.xml +++ b/Modules/Core/resource/Interactions/DisplayConfigPACS.xml @@ -1,70 +1,82 @@ + - + - + - + + - + + - + + - - - - - - - - - - - + + + + + + + + + diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp index 948f7d26e1..1a235caea2 100644 --- a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp @@ -1,918 +1,918 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. 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 "mitkDisplayActionEventBroadcast.h" // us #include "usGetModuleContext.h" #include "usModuleContext.h" // mitk core module #include #include #include #include #include #include #include #include #include #include #include #include mitk::DisplayActionEventBroadcast::DisplayActionEventBroadcast() : m_AlwaysReact(false) , m_AutoRepeat(false) , m_IndexToSliceModifier(4) , m_InvertScrollDirection(false) , m_InvertZoomDirection(false) , m_ZoomFactor(2) , m_InvertMoveDirection(false) , m_InvertLevelWindowDirection(false) , m_LinkPlanes(true) { m_StartCoordinateInMM.Fill(0); m_LastDisplayCoordinate.Fill(0); m_LastCoordinateInMM.Fill(0); m_CurrentDisplayCoordinate.Fill(0); // register the broadcast class (itself) as an interaction event observer via micro services us::ServiceProperties props; props["name"] = std::string("DisplayActionEventBroadcast"); m_ServiceRegistration = us::GetModuleContext()->RegisterService(this, props); } mitk::DisplayActionEventBroadcast::~DisplayActionEventBroadcast() { m_ServiceRegistration.Unregister(); } void mitk::DisplayActionEventBroadcast::Notify(InteractionEvent* interactionEvent, bool isHandled) { // the event is passed to the state machine interface to be handled if (!isHandled || m_AlwaysReact) { HandleEvent(interactionEvent, nullptr); } } void mitk::DisplayActionEventBroadcast::ConnectActionsAndFunctions() { CONNECT_CONDITION("check_position_event", CheckPositionEvent); CONNECT_CONDITION("check_can_rotate", CheckRotationPossible); CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible); CONNECT_FUNCTION("init", Init); CONNECT_FUNCTION("move", Move); CONNECT_FUNCTION("zoom", Zoom); CONNECT_FUNCTION("scroll", Scroll); CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown); CONNECT_FUNCTION("levelWindow", AdjustLevelWindow); CONNECT_FUNCTION("setCrosshair", SetCrosshair); CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar) CONNECT_FUNCTION("startRotation", StartRotation); CONNECT_FUNCTION("endRotation", EndRotation); CONNECT_FUNCTION("rotate", Rotate); CONNECT_FUNCTION("swivel", Swivel); CONNECT_FUNCTION("IncreaseTimeStep", IncreaseTimeStep); CONNECT_FUNCTION("DecreaseTimeStep", DecreaseTimeStep); } void mitk::DisplayActionEventBroadcast::ConfigurationChanged() { PropertyList::Pointer properties = GetAttributes(); // allwaysReact std::string strAlwaysReact = ""; m_AlwaysReact = false; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } } // auto repeat std::string strAutoRepeat = ""; m_AutoRepeat = false; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; m_IndexToSliceModifier = 4; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } // scroll direction if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection)) { m_ScrollDirection = "updown"; } m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false); // zoom direction if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection)) { m_ZoomDirection = "updown"; } m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false); m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false); if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection)) { m_LevelDirection = "leftright"; } m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false); // coupled rotation std::string strCoupled = ""; m_LinkPlanes = false; if (properties->GetStringProperty("coupled", strCoupled)) { if (strCoupled == "true") { m_LinkPlanes = true; } } // zoom factor std::string strZoomFactor = ""; properties->GetStringProperty("zoomFactor", strZoomFactor); m_ZoomFactor = .05; if (atoi(strZoomFactor.c_str()) > 0) { m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0); } } bool mitk::DisplayActionEventBroadcast::FilterEvents(InteractionEvent* interactionEvent, DataNode * /*dataNode*/) { BaseRenderer* sendingRenderer = interactionEvent->GetSender(); if (nullptr == sendingRenderer) { return false; } if (BaseRenderer::Standard3D == sendingRenderer->GetMapperID()) { return false; } return true; } bool mitk::DisplayActionEventBroadcast::CheckPositionEvent(const InteractionEvent *interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } return true; } bool mitk::DisplayActionEventBroadcast::CheckRotationPossible(const InteractionEvent *interactionEvent) { // Decide between moving and rotation slices. /* Detailed logic: 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Needs 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 intersection, 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 auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } BaseRenderer* renderer = positionEvent->GetSender(); if (nullptr == renderer) { return false; } const PlaneGeometry* rendererWorldPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (nullptr == rendererWorldPlaneGeometry) { return false; } Point3D position = positionEvent->GetPositionInWorld(); const auto spacing = rendererWorldPlaneGeometry->GetSpacing(); const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY) Line3D intersectionLineWithGeometryToBeRotated; bool hitMultipleLines(false); m_SNCsToBeRotated.clear(); const ScalarType threshholdDistancePixels = 12.0; auto allRenderWindows = renderer->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { SliceNavigationController* snc = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::Standard3D == BaseRenderer::GetInstance(renderWindow)->GetMapperID()) { continue; } const PlaneGeometry* rendererPlaneGeometry = snc->GetCurrentPlaneGeometry(); if (nullptr == rendererPlaneGeometry) { continue; // ignore, we don't see a plane } // check if there is an intersection between rendered / clicked geometry and the one being analyzed Line3D intersectionLine; if (!rendererWorldPlaneGeometry->IntersectionLine(rendererPlaneGeometry, intersectionLine)) { continue; // we ignore this plane, it's parallel to our plane } // check distance from intersection line const double distanceFromIntersectionLine = intersectionLine.Distance(position) / spacing[snc->GetDefaultViewDirection()]; // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { // 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 anyOtherGeometry = rendererPlaneGeometry; if (m_LinkPlanes) { // if planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } else // close to cursor { if (nullptr == geometryToBeRotated) // first one close to the cursor { geometryToBeRotated = rendererPlaneGeometry; intersectionLineWithGeometryToBeRotated = intersectionLine; m_SNCsToBeRotated.push_back(snc); } else { // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane // together with the primary one // if different, DON'T rotate if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) && intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < eps) { m_SNCsToBeRotated.push_back(snc); } else { hitMultipleLines = true; } } } } bool moveSlices(true); if (geometryToBeRotated && anyOtherGeometry && rendererWorldPlaneGeometry && !hitMultipleLines) { // assure all three are valid, so calculation of center of rotation can be done moveSlices = false; } // question in state machine is: "rotate?" if (moveSlices) // i.e. NOT rotate { return false; } else { // we have enough information for rotation // remember where the last cursor position ON THE LINE has been observed m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(position); // find center of rotation by intersection with any of the OTHER lines if (anyOtherGeometry->IntersectionPoint(intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) { return true; } else { return false; } } return false; } bool mitk::DisplayActionEventBroadcast::CheckSwivelPossible(const InteractionEvent *interactionEvent) { // 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 auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } BaseRenderer* renderer = positionEvent->GetSender(); if (nullptr == renderer) { return false; } const Point3D& position = positionEvent->GetPositionInWorld(); m_SNCsToBeRotated.clear(); const PlaneGeometry* clickedGeometry(nullptr); const PlaneGeometry* otherGeometry1(nullptr); const PlaneGeometry* otherGeometry2(nullptr); const ScalarType threshholdDistancePixels = 6.0; auto allRenderWindows = renderer->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { SliceNavigationController* snc = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::Standard3D == BaseRenderer::GetInstance(renderWindow)->GetMapperID()) { continue; } const PlaneGeometry* rendererPlaneGeometry = snc->GetCurrentPlaneGeometry(); if (nullptr == rendererPlaneGeometry) { continue; // ignore, we don't see a plane } if (snc == renderer->GetSliceNavigationController()) { clickedGeometry = rendererPlaneGeometry; m_SNCsToBeRotated.push_back(snc); } else { if (nullptr == otherGeometry1) { otherGeometry1 = rendererPlaneGeometry; } else { otherGeometry2 = rendererPlaneGeometry; } if (m_LinkPlanes) { // if planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } } Line3D line; Point3D point; if ((nullptr != clickedGeometry) && (nullptr != otherGeometry1) && (nullptr != otherGeometry2) && clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point)) { m_CenterOfRotation = point; if (m_CenterOfRotation.EuclideanDistanceTo(position) < threshholdDistancePixels) { return false; } else { m_ReferenceCursor = positionEvent->GetPointerPositionOnScreen(); // 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->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector); clickedGeometry->BaseGeometry::IndexToWorld(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; return true; } } else { return false; } return false; } void mitk::DisplayActionEventBroadcast::Init(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_CurrentDisplayCoordinate = m_LastDisplayCoordinate; positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM); m_LastCoordinateInMM = m_StartCoordinateInMM; } void mitk::DisplayActionEventBroadcast::Move(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } BaseRenderer* sender = interactionEvent->GetSender(); Vector2D moveVector = m_LastDisplayCoordinate - positionEvent->GetPointerPositionOnScreen(); if (m_InvertMoveDirection) { moveVector *= -1.0; } moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); // #TODO: put here? // store new display coordinate m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate move event with computed geometry values InvokeEvent(DisplayMoveEvent(interactionEvent, moveVector)); } void mitk::DisplayActionEventBroadcast::SetCrosshair(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } Point3D position = positionEvent->GetPositionInWorld(); // propagate set crosshair event with computed geometry values InvokeEvent(DisplaySetCrosshairEvent(interactionEvent, position)); } void mitk::DisplayActionEventBroadcast::Zoom(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } float factor = 1.0; float distance = 0; if (m_ZoomDirection == "updown") { distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertZoomDirection) { distance *= -1.0; } // set zooming speed if (distance < 0.0) { factor = 1.0 / m_ZoomFactor; } else if (distance > 0.0) { factor = 1.0 * m_ZoomFactor; } // store new display coordinates m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate zoom event with computed geometry values InvokeEvent(DisplayZoomEvent(interactionEvent, factor, m_StartCoordinateInMM)); } void mitk::DisplayActionEventBroadcast::Scroll(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } int sliceDelta = 0; // scroll direction if (m_ScrollDirection == "updown") { sliceDelta = static_cast(m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]); } else { sliceDelta = static_cast(m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]); } if (m_InvertScrollDirection) { sliceDelta *= -1; } // set how many pixels the mouse has to be moved to scroll one slice // if the mouse has been moved less than 'm_IndexToSliceModifier', pixels slice ONE slice only if (sliceDelta > 0 && sliceDelta < m_IndexToSliceModifier) { sliceDelta = m_IndexToSliceModifier; } else if (sliceDelta < 0 && sliceDelta > -m_IndexToSliceModifier) { sliceDelta = -m_IndexToSliceModifier; } sliceDelta /= m_IndexToSliceModifier; // store new display coordinates m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate scroll event with computed geometry values - InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta)); + InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat)); } void mitk::DisplayActionEventBroadcast::ScrollOneUp(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { int sliceDelta = 1; if (m_InvertScrollDirection) { sliceDelta = -1; } // propagate scroll event with a single slice delta (increase) - InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta)); + InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat)); } void mitk::DisplayActionEventBroadcast::ScrollOneDown(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { int sliceDelta = -1; if (m_InvertScrollDirection) { sliceDelta = 1; } // propagate scroll event with a single slice delta (decrease) - InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta)); + InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat)); } void mitk::DisplayActionEventBroadcast::AdjustLevelWindow(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } ScalarType level; ScalarType window; if (m_LevelDirection == "leftright") { level = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; window = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { level = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; window = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertLevelWindowDirection) { level *= -1; window *= -1; } level *= static_cast(2); window *= static_cast(2); // store new display coordinates m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate set level window event with the level and window delta InvokeEvent(DisplaySetLevelWindowEvent(interactionEvent, level, window)); } void mitk::DisplayActionEventBroadcast::StartRotation(StateMachineAction* /*stateMachineAction*/, InteractionEvent* /*interactionEvent*/) { SetMouseCursor(rotate_cursor_xpm, 0, 0); } void mitk::DisplayActionEventBroadcast::EndRotation(StateMachineAction* /*stateMachineAction*/, InteractionEvent* /*interactionEvent*/) { ResetMouseCursor(); } void mitk::DisplayActionEventBroadcast::Rotate(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } Point3D position = positionEvent->GetPositionInWorld(); Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation; Vector3D toCursor = position - m_CenterOfRotation; // cross product: | A x B | = |A| * |B| * sin(angle) Vector3D axisOfRotation; vnl_vector_fixed 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 = position; // create RotationOperation and apply to all SNCs that should be rotated RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle); // iterate the OTHER slice navigation controllers for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (nullptr == timeGeometry) { continue; } timeGeometry->ExecuteOperation(&rotationOperation); (*iter)->SendCreatedWorldGeometryUpdate(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayActionEventBroadcast::Swivel(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } // Determine relative mouse movement projected onto world space Point2D position = positionEvent->GetPointerPositionOnScreen(); Vector2D relativeCursor = position - 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()) { TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (nullptr == timeGeometry) { continue; } timeGeometry->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()) { // Retrieve the TimeGeometry of this SliceNavigationController TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (nullptr == timeGeometry) { continue; } // Execute the new rotation timeGeometry->ExecuteOperation(&op2); // Notify listeners (*iter)->SendCreatedWorldGeometryUpdate(); } } m_PreviousRotationAxis = rotationAxis; m_PreviousRotationAngle = rotationAngle; RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::DisplayActionEventBroadcast::IncreaseTimeStep(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { auto sliceNaviController = interactionEvent->GetSender()->GetRenderingManager()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Next(); } void mitk::DisplayActionEventBroadcast::DecreaseTimeStep(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { auto sliceNaviController = interactionEvent->GetSender()->GetRenderingManager()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Previous(); } void mitk::DisplayActionEventBroadcast::UpdateStatusbar(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } BaseRenderer::Pointer renderer = positionEvent->GetSender(); TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); DataStorage::SetOfObjects::ConstPointer nodes = renderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); if (nodes.IsNull()) { return; } Point3D worldposition; renderer->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), worldposition); auto globalCurrentTimePoint = renderer->GetTime(); Image::Pointer image3D; DataNode::Pointer node; DataNode::Pointer topSourceNode; int component = 0; node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, renderer); if (node.IsNull()) { return; } bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { DataStorage::SetOfObjects::ConstPointer sourcenodes = renderer->GetDataStorage()->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, renderer); } if (topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } // get the position and pixel value from the image and build up status bar text auto statusBar = StatusBar::GetInstance(); if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(worldposition, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str()); } else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR) { std::string pixelValue = "See ODF Details view. "; statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str()); } else { ScalarType pixelValue; mitkPixelTypeMultiplex5( FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(renderer->GetTimeStep()), p, pixelValue, component); statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } bool mitk::DisplayActionEventBroadcast::GetBoolProperty(PropertyList::Pointer propertyList, const char* propertyName, bool defaultValue) { std::string valueAsString; if (!propertyList->GetStringProperty(propertyName, valueAsString)) { return defaultValue; } else { if (valueAsString == "true") { return true; } else { return false; } } } diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp index 0b35416d73..8d95e5ff6e 100644 --- a/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp @@ -1,321 +1,323 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. 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 "mitkDisplayActionEventFunctions.h" // mitk core #include "mitkBaseRenderer.h" #include "mitkCameraController.h" #include "mitkDisplayActionEvents.h" #include "mitkInteractionPositionEvent.h" #include "mitkLevelWindow.h" #include "mitkLevelWindowProperty.h" #include "mitkNodePredicateDataType.h" ////////////////////////////////////////////////////////////////////////// // STANDARD FUNCTIONS ////////////////////////////////////////////////////////////////////////// mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::MoveSenderCameraAction() { mitk::StdFunctionCommand::ActionFunction actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayMoveEvent().CheckEvent(&displayInteractorEvent)) { const DisplayMoveEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } sendingRenderer->GetCameraController()->MoveBy(displayActionEvent->GetMoveVector()); sendingRenderer->GetRenderingManager()->RequestUpdate(sendingRenderer->GetRenderWindow()); } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::SetCrosshairAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplaySetCrosshairEvent().CheckEvent(&displayInteractorEvent)) { const DisplaySetCrosshairEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } BaseRenderer::GetInstance(sendingRenderer->GetRenderWindow())->GetSliceNavigationController()->SelectSliceByPoint(displayActionEvent->GetPosition()); } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::ZoomSenderCameraAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayZoomEvent().CheckEvent(&displayInteractorEvent)) { const DisplayZoomEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } if (1.0 != displayActionEvent->GetZoomFactor()) { sendingRenderer->GetCameraController()->Zoom(displayActionEvent->GetZoomFactor(), displayActionEvent->GetStartCoordinate()); sendingRenderer->GetRenderingManager()->RequestUpdate(sendingRenderer->GetRenderWindow()); } } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::ScrollSliceStepperAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayScrollEvent().CheckEvent(&displayInteractorEvent)) { const DisplayScrollEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } mitk::SliceNavigationController* sliceNavigationController = sendingRenderer->GetSliceNavigationController(); if (nullptr == sliceNavigationController) { return; } if (sliceNavigationController->GetSliceLocked()) { return; } mitk::Stepper* sliceStepper = sliceNavigationController->GetSlice(); if (nullptr == sliceStepper) { return; } // if only a single slice image was loaded, scrolling will affect the time steps if (sliceStepper->GetSteps() <= 1) { sliceStepper = sliceNavigationController->GetTime(); } + sliceStepper->SetAutoRepeat(displayActionEvent->GetAutoRepeat()); sliceStepper->MoveSlice(displayActionEvent->GetSliceDelta()); } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::SetLevelWindowAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplaySetLevelWindowEvent().CheckEvent(&displayInteractorEvent)) { const DisplaySetLevelWindowEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } // get the the topmost visible image of the sending renderer DataStorage::Pointer storage = sendingRenderer->GetDataStorage(); DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(NodePredicateDataType::New("Image")); Point3D worldposition; const auto* positionEvent = dynamic_cast(displayActionEvent->GetInteractionEvent()); sendingRenderer->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), worldposition); auto globalCurrentTimePoint = sendingRenderer->GetTime(); DataNode::Pointer node = FindTopmostVisibleNode(allImageNodes, worldposition, globalCurrentTimePoint, sendingRenderer); if (node.IsNull()) { return; } LevelWindow levelWindow = LevelWindow(); node->GetLevelWindow(levelWindow); ScalarType level = levelWindow.GetLevel(); ScalarType window = levelWindow.GetWindow(); level += displayActionEvent->GetLevel(); window += displayActionEvent->GetWindow(); levelWindow.SetLevelWindow(level, window); auto* levelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); if (nullptr != levelWindowProperty) { levelWindowProperty->SetLevelWindow(levelWindow); sendingRenderer->GetRenderingManager()->RequestUpdateAll(); } } }; return actionFunction; } ////////////////////////////////////////////////////////////////////////// // SYNCHRONIZED FUNCTIONS ////////////////////////////////////////////////////////////////////////// mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::MoveCameraSynchronizedAction() { mitk::StdFunctionCommand::ActionFunction actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayMoveEvent().CheckEvent(&displayInteractorEvent)) { const DisplayMoveEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } auto allRenderWindows = sendingRenderer->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { if (BaseRenderer::GetInstance(renderWindow)->GetMapperID() == BaseRenderer::Standard2D) { BaseRenderer* currentRenderer = BaseRenderer::GetInstance(renderWindow); currentRenderer->GetCameraController()->MoveBy(displayActionEvent->GetMoveVector()); currentRenderer->GetRenderingManager()->RequestUpdate(currentRenderer->GetRenderWindow()); } } } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::SetCrosshairSynchronizedAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplaySetCrosshairEvent().CheckEvent(&displayInteractorEvent)) { const DisplaySetCrosshairEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } auto allRenderWindows = sendingRenderer->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { if (BaseRenderer::GetInstance(renderWindow)->GetMapperID() == BaseRenderer::Standard2D) { BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController()->SelectSliceByPoint(displayActionEvent->GetPosition()); } } } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::ZoomCameraSynchronizedAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayZoomEvent().CheckEvent(&displayInteractorEvent)) { const DisplayZoomEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } if (1.0 != displayActionEvent->GetZoomFactor()) { auto allRenderWindows = sendingRenderer->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { if (BaseRenderer::GetInstance(renderWindow)->GetMapperID() == BaseRenderer::Standard2D) { BaseRenderer* currentRenderer = BaseRenderer::GetInstance(renderWindow); currentRenderer->GetCameraController()->Zoom(displayActionEvent->GetZoomFactor(), displayActionEvent->GetStartCoordinate()); currentRenderer->GetRenderingManager()->RequestUpdate(currentRenderer->GetRenderWindow()); } } } } }; return actionFunction; } mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::ScrollSliceStepperSynchronizedAction() { auto actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayScrollEvent().CheckEvent(&displayInteractorEvent)) { const DisplayScrollEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); if (nullptr == sendingRenderer) { return; } auto allRenderWindows = sendingRenderer->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { if (BaseRenderer::GetInstance(renderWindow)->GetMapperID() == BaseRenderer::Standard2D) { mitk::SliceNavigationController* sliceNavigationController = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController(); if (nullptr == sliceNavigationController) { return; } if (sliceNavigationController->GetSliceLocked()) { return; } mitk::Stepper* sliceStepper = sliceNavigationController->GetSlice(); if (nullptr == sliceStepper) { return; } // if only a single slice image was loaded, scrolling will affect the time steps if (sliceStepper->GetSteps() <= 1) { sliceStepper = sliceNavigationController->GetTime(); } + sliceStepper->SetAutoRepeat(displayActionEvent->GetAutoRepeat()); sliceStepper->MoveSlice(displayActionEvent->GetSliceDelta()); } } } }; return actionFunction; } diff --git a/Modules/Core/src/Interactions/mitkStdDisplayActionEventHandler.cpp b/Modules/Core/src/Interactions/mitkStdDisplayActionEventHandler.cpp index 95bae2ae2a..13f702aba0 100644 --- a/Modules/Core/src/Interactions/mitkStdDisplayActionEventHandler.cpp +++ b/Modules/Core/src/Interactions/mitkStdDisplayActionEventHandler.cpp @@ -1,45 +1,45 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. 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 "mitkStdDisplayActionEventHandler.h" // mitk core #include "mitkDisplayActionEventFunctions.h" // itk #include void mitk::StdDisplayActionEventHandler::InitStdActions() { if (m_ObservableBroadcast.IsExpired()) { mitkThrow() << "No display action event broadcast class set to observe. Use 'SetObservableBroadcast' before initializing actions."; } auto observableBroadcast = m_ObservableBroadcast.Lock(); StdFunctionCommand::ActionFunction actionFunction = DisplayActionEventFunctions::MoveSenderCameraAction(); ConnectDisplayActionEvent(DisplayMoveEvent(nullptr, Vector2D()), actionFunction); actionFunction = DisplayActionEventFunctions::SetCrosshairAction(); ConnectDisplayActionEvent(DisplaySetCrosshairEvent(nullptr, Point3D()), actionFunction); actionFunction = DisplayActionEventFunctions::ZoomSenderCameraAction(); ConnectDisplayActionEvent(DisplayZoomEvent(nullptr, 0.0, Point2D()), actionFunction); actionFunction = DisplayActionEventFunctions::ScrollSliceStepperAction(); - ConnectDisplayActionEvent(DisplayScrollEvent(nullptr, 0), actionFunction); + ConnectDisplayActionEvent(DisplayScrollEvent(nullptr, 0, true), actionFunction); } diff --git a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp index 05b76a1813..2329a53788 100644 --- a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp @@ -1,302 +1,302 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. 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 "QmitkMxNMultiWidget.h" #include "QmitkRenderWindowWidget.h" // mitk core #include #include #include #include // qt #include QmitkMxNMultiWidget::QmitkMxNMultiWidget(QWidget* parent, Qt::WindowFlags f/* = 0*/, mitk::RenderingManager* renderingManager/* = nullptr*/, mitk::BaseRenderer::RenderingMode::Type renderingMode/* = mitk::BaseRenderer::RenderingMode::Standard*/, const QString& multiWidgetName/* = "mxnmulti"*/) : QmitkAbstractMultiWidget(parent, f, renderingManager, renderingMode, multiWidgetName) , m_CrosshairVisibility(false) { // set display action events / event functions Synchronize(true); } void QmitkMxNMultiWidget::InitializeMultiWidget() { SetLayout(1, 1); ActivateMenuWidget(true); } void QmitkMxNMultiWidget::MultiWidgetOpened() { SetCrosshairVisibility(true); } void QmitkMxNMultiWidget::MultiWidgetClosed() { SetCrosshairVisibility(false); } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const QString& widgetName) const { if ("axial" == widgetName || "sagittal" == widgetName || "coronal" == widgetName || "3d" == widgetName) { return GetActiveRenderWindowWidget()->GetRenderWindow(); } return QmitkAbstractMultiWidget::GetRenderWindow(widgetName); } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const mitk::BaseRenderer::ViewDirection& viewDirection) const { // currently no mapping between view directions and render windows // simply return the currently active render window return GetActiveRenderWindowWidget()->GetRenderWindow(); } void QmitkMxNMultiWidget::Synchronize(bool synchronized) { auto displayActionEventHandler = GetActionEventHandler(); if (nullptr == displayActionEventHandler) { return; } auto allObserverTags = displayActionEventHandler->GetAllObserverTags(); for (auto observerTag : allObserverTags) { displayActionEventHandler->DisconnectObserver(observerTag); } if (synchronized) { mitk::StdFunctionCommand::ActionFunction actionFunction = mitk::DisplayActionEventFunctions::MoveCameraSynchronizedAction(); displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayMoveEvent(nullptr, mitk::Vector2D()), actionFunction); actionFunction = mitk::DisplayActionEventFunctions::SetCrosshairSynchronizedAction(); displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplaySetCrosshairEvent(nullptr, mitk::Point3D()), actionFunction); actionFunction = mitk::DisplayActionEventFunctions::ZoomCameraSynchronizedAction(); displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayZoomEvent(nullptr, 0.0, mitk::Point2D()), actionFunction); actionFunction = mitk::DisplayActionEventFunctions::ScrollSliceStepperSynchronizedAction(); - displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayScrollEvent(nullptr, 0), actionFunction); + displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayScrollEvent(nullptr, 0, true), actionFunction); } else { mitk::StdFunctionCommand::ActionFunction actionFunction = mitk::DisplayActionEventFunctions::MoveSenderCameraAction(); displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayMoveEvent(nullptr, mitk::Vector2D()), actionFunction); actionFunction = mitk::DisplayActionEventFunctions::SetCrosshairAction(); displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplaySetCrosshairEvent(nullptr, mitk::Point3D()), actionFunction); actionFunction = mitk::DisplayActionEventFunctions::ZoomSenderCameraAction(); displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayZoomEvent(nullptr, 0.0, mitk::Point2D()), actionFunction); actionFunction = mitk::DisplayActionEventFunctions::ScrollSliceStepperAction(); - displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayScrollEvent(nullptr, 0), actionFunction); + displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayScrollEvent(nullptr, 0, true), actionFunction); } // use the standard 'set level window' action for both modes mitk::StdFunctionCommand::ActionFunction actionFunction = mitk::DisplayActionEventFunctions::SetLevelWindowAction(); displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplaySetLevelWindowEvent(nullptr, mitk::ScalarType(), mitk::ScalarType()), actionFunction); } void QmitkMxNMultiWidget::SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget) { auto currentActiveRenderWindowWidget = GetActiveRenderWindowWidget(); if (currentActiveRenderWindowWidget == activeRenderWindowWidget) { return; } // reset the decoration color of the previously active render window widget if (nullptr != currentActiveRenderWindowWidget) { auto decorationColor = currentActiveRenderWindowWidget->GetDecorationColor(); QColor hexColor(decorationColor[0] * 255, decorationColor[1] * 255, decorationColor[2] * 255); currentActiveRenderWindowWidget->setStyleSheet("border: 2px solid " + hexColor.name(QColor::HexRgb)); } // set the new decoration color of the currently active render window widget if (nullptr != activeRenderWindowWidget) { activeRenderWindowWidget->setStyleSheet("border: 2px solid #FF6464"); } QmitkAbstractMultiWidget::SetActiveRenderWindowWidget(activeRenderWindowWidget); } void QmitkMxNMultiWidget::SetSelectedPosition(const mitk::Point3D& newPosition, const QString& widgetName) { RenderWindowWidgetPointer renderWindowWidget; if (widgetName.isNull()) { renderWindowWidget = GetActiveRenderWindowWidget(); } else { renderWindowWidget = GetRenderWindowWidget(widgetName); } if (nullptr != renderWindowWidget) { renderWindowWidget->GetSliceNavigationController()->SelectSliceByPoint(newPosition); renderWindowWidget->RequestUpdate(); return; } MITK_ERROR << "Position can not be set for an unknown render window widget."; } const mitk::Point3D QmitkMxNMultiWidget::GetSelectedPosition(const QString& /*widgetName*/) const { // see T26208 return mitk::Point3D(); } void QmitkMxNMultiWidget::SetCrosshairVisibility(bool activate) { auto renderWindowWidgets = GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { renderWindowWidget.second->ActivateCrosshair(activate); } m_CrosshairVisibility = activate; } void QmitkMxNMultiWidget::ResetCrosshair() { // retrieve the render window from the sending object auto renderWindow = dynamic_cast(QObject::sender()); if (nullptr == renderWindow) { return; } SetWidgetPlaneMode(mitk::InteractionSchemeSwitcher::MITKStandard); ResetRenderWindow(renderWindow); } ////////////////////////////////////////////////////////////////////////// // PUBLIC SLOTS // MOUSE EVENTS ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::wheelEvent(QWheelEvent* e) { emit WheelMoved(e); } void QmitkMxNMultiWidget::mousePressEvent(QMouseEvent* e) { if (QEvent::MouseButtonPress != e->type()) { return; } auto renderWindowWidget = dynamic_cast(this->sender()); if (nullptr == renderWindowWidget) { return; } auto renderWindowWidgetPointer = GetRenderWindowWidget(renderWindowWidget->GetWidgetName()); SetActiveRenderWindowWidget(renderWindowWidgetPointer); } void QmitkMxNMultiWidget::moveEvent(QMoveEvent* e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the overlays as the MultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::SetLayoutImpl() { int requiredRenderWindowWidgets = GetRowCount() * GetColumnCount(); int existingRenderWindowWidgets = GetRenderWindowWidgets().size(); int difference = requiredRenderWindowWidgets - existingRenderWindowWidgets; while (0 < difference) { // more render window widgets needed CreateRenderWindowWidget(); --difference; } while (0 > difference) { // less render window widgets needed RemoveRenderWindowWidget(); ++difference; } auto firstRenderWindowWidget = GetFirstRenderWindowWidget(); if (nullptr != firstRenderWindowWidget) { SetActiveRenderWindowWidget(firstRenderWindowWidget); } GetMultiWidgetLayoutManager()->OnLayoutDesignChanged(QmitkMultiWidgetLayoutManager::LayoutDesign::DEFAULT); } void QmitkMxNMultiWidget::ResetRenderWindow(QmitkRenderWindow* renderWindow) { auto renderingManager = GetRenderingManager(); if (nullptr == renderingManager) { return; } // define predicate to get all nodes that have a fixed layer for the given renderer auto fixedLayer = mitk::NodePredicateProperty::New("fixedLayer", mitk::BoolProperty::New(true), renderWindow->GetRenderer()); // initialize the views to the bounding geometry renderingManager->InitializeViewByPredicate(renderWindow->GetRenderWindow(), fixedLayer); } void QmitkMxNMultiWidget::CreateRenderWindowWidget() { // create the render window widget and connect signal / slot QString renderWindowWidgetName = GetNameFromIndex(GetNumberOfRenderWindowWidgets()); RenderWindowWidgetPointer renderWindowWidget = std::make_shared(this, renderWindowWidgetName, GetDataStorage(), GetRenderingManager()); renderWindowWidget->SetCornerAnnotationText(renderWindowWidgetName.toStdString()); connect(renderWindowWidget.get(), &QmitkRenderWindowWidget::MouseEvent, this, &QmitkMxNMultiWidget::mousePressEvent); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget); auto renderWindow = renderWindowWidget->GetRenderWindow(); auto layoutManager = GetMultiWidgetLayoutManager(); connect(renderWindow, &QmitkRenderWindow::ResetView, this, &QmitkMxNMultiWidget::ResetCrosshair); connect(renderWindow, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkMxNMultiWidget::SetCrosshairVisibility); connect(renderWindow, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkMxNMultiWidget::SetWidgetPlaneMode); connect(renderWindow, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::OnLayoutDesignChanged); connect(renderWindow, &QmitkRenderWindow::LayoutIndexChanged, this, [this, renderWindow] { ResetRenderWindow(renderWindow); }); } diff --git a/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp b/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp index 83e838f3f4..598eb4b34b 100644 --- a/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp @@ -1,818 +1,818 @@ /*=================================================================== 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. ===================================================================*/ #define SMW_INFO MITK_INFO("widget.stdmulti") #include "QmitkStdMultiWidget.h" #include "QmitkRenderWindowWidget.h" // mitk core #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // qt #include #include #include // vtk #include // c++ #include QmitkStdMultiWidget::QmitkStdMultiWidget(QWidget *parent, Qt::WindowFlags f/* = 0*/, mitk::RenderingManager *renderingManager/* = nullptr*/, mitk::BaseRenderer::RenderingMode::Type renderingMode/* = mitk::BaseRenderer::RenderingMode::Standard*/, const QString &name/* = "stdmulti"*/) : QmitkAbstractMultiWidget(parent, f, renderingManager, renderingMode, name) , m_TimeNavigationController(nullptr) , m_PendingCrosshairPositionEvent(false) { m_TimeNavigationController = GetRenderingManager()->GetTimeNavigationController(); // set display action events / event functions auto displayActionEventHandler = GetActionEventHandler(); if (nullptr == displayActionEventHandler) { return; } auto allObserverTags = displayActionEventHandler->GetAllObserverTags(); for (auto observerTag : allObserverTags) { displayActionEventHandler->DisconnectObserver(observerTag); } // multi render window crosshair navigation (synchronized) mitk::StdFunctionCommand::ActionFunction actionFunction = mitk::DisplayActionEventFunctions::SetCrosshairSynchronizedAction(); displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplaySetCrosshairEvent(nullptr, mitk::Point3D()), actionFunction); // single render window movement / panning (not synchronized) actionFunction = mitk::DisplayActionEventFunctions::MoveSenderCameraAction(); displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayMoveEvent(nullptr, mitk::Vector2D()), actionFunction); // single render window zooming (not synchronized) actionFunction = mitk::DisplayActionEventFunctions::ZoomSenderCameraAction(); displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayZoomEvent(nullptr, 0.0, mitk::Point2D()), actionFunction); // single render window scrolling (not synchronized) actionFunction = mitk::DisplayActionEventFunctions::ScrollSliceStepperAction(); - displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayScrollEvent(nullptr, 0), actionFunction); + displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayScrollEvent(nullptr, 0, true), actionFunction); // multi render window level window adjusting (synchronized) actionFunction = mitk::DisplayActionEventFunctions::SetLevelWindowAction(); displayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplaySetLevelWindowEvent(nullptr, mitk::ScalarType(), mitk::ScalarType()), actionFunction); } QmitkStdMultiWidget::~QmitkStdMultiWidget() { m_TimeNavigationController->Disconnect(GetRenderWindow1()->GetSliceNavigationController()); m_TimeNavigationController->Disconnect(GetRenderWindow2()->GetSliceNavigationController()); m_TimeNavigationController->Disconnect(GetRenderWindow3()->GetSliceNavigationController()); m_TimeNavigationController->Disconnect(GetRenderWindow4()->GetSliceNavigationController()); } void QmitkStdMultiWidget::InitializeMultiWidget() { // yellow is default color for widget4 m_DecorationColorWidget4[0] = 1.0f; m_DecorationColorWidget4[1] = 1.0f; m_DecorationColorWidget4[2] = 0.0f; SetLayout(2, 2); AddDisplayPlaneSubTree(); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow(const QString& widgetName) const { if ("axial" == widgetName) { return GetRenderWindow1(); } if ("sagittal" == widgetName) { return GetRenderWindow2(); } if ("coronal" == widgetName) { return GetRenderWindow3(); } if ("3d" == widgetName) { return GetRenderWindow4(); } return QmitkAbstractMultiWidget::GetRenderWindow(widgetName); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow(const mitk::BaseRenderer::ViewDirection& viewDirection) const { return GetRenderWindow(static_cast(viewDirection)); } void QmitkStdMultiWidget::SetSelectedPosition(const mitk::Point3D& newPosition, const QString& /*widgetName*/) { GetRenderWindow1()->GetSliceNavigationController()->SelectSliceByPoint(newPosition); GetRenderWindow2()->GetSliceNavigationController()->SelectSliceByPoint(newPosition); GetRenderWindow3()->GetSliceNavigationController()->SelectSliceByPoint(newPosition); RequestUpdateAll(); } const mitk::Point3D QmitkStdMultiWidget::GetSelectedPosition(const QString& /*widgetName*/) const { const mitk::PlaneGeometry* plane1 = GetRenderWindow1()->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry* plane2 = GetRenderWindow2()->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry* plane3 = GetRenderWindow3()->GetSliceNavigationController()->GetCurrentPlaneGeometry(); mitk::Line3D line; if ((plane1 != nullptr) && (plane2 != nullptr) && (plane1->IntersectionLine(plane2, line))) { mitk::Point3D point; if ((plane3 != nullptr) && (plane3->IntersectionPoint(line, point))) { return point; } } return mitk::Point3D(); } void QmitkStdMultiWidget::SetCrosshairVisibility(bool visible) { if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetVisibility(visible); } if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetVisibility(visible); } if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetVisibility(visible); } RequestUpdateAll(); } bool QmitkStdMultiWidget::GetCrosshairVisibility() const { bool crosshairVisibility = true; if (m_PlaneNode1.IsNotNull()) { bool visibilityProperty = false; m_PlaneNode1->GetVisibility(visibilityProperty, nullptr); crosshairVisibility &= visibilityProperty; } if (m_PlaneNode2.IsNotNull()) { bool visibilityProperty = false; crosshairVisibility &= m_PlaneNode2->GetVisibility(visibilityProperty, nullptr); crosshairVisibility &= visibilityProperty; } if (m_PlaneNode3.IsNotNull()) { bool visibilityProperty = false; crosshairVisibility &= m_PlaneNode3->GetVisibility(visibilityProperty, nullptr); crosshairVisibility &= visibilityProperty; } return crosshairVisibility; } void QmitkStdMultiWidget::ResetCrosshair() { SetWidgetPlaneMode(mitk::InteractionSchemeSwitcher::MITKStandard); ResetRenderWindows(); } mitk::SliceNavigationController* QmitkStdMultiWidget::GetTimeNavigationController() { return m_TimeNavigationController; } void QmitkStdMultiWidget::AddPlanesToDataStorage() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) { dataStorage->Add(m_ParentNodeForGeometryPlanes); dataStorage->Add(m_PlaneNode1, m_ParentNodeForGeometryPlanes); dataStorage->Add(m_PlaneNode2, m_ParentNodeForGeometryPlanes); dataStorage->Add(m_PlaneNode3, m_ParentNodeForGeometryPlanes); } } void QmitkStdMultiWidget::RemovePlanesFromDataStorage() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) { dataStorage->Remove(m_PlaneNode1); dataStorage->Remove(m_PlaneNode2); dataStorage->Remove(m_PlaneNode3); dataStorage->Remove(m_ParentNodeForGeometryPlanes); } } void QmitkStdMultiWidget::HandleCrosshairPositionEvent() { if (!m_PendingCrosshairPositionEvent) { m_PendingCrosshairPositionEvent = true; QTimer::singleShot(0, this, SLOT(HandleCrosshairPositionEventDelayed())); } } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow(unsigned int number) const { switch (number) { case 0: return GetRenderWindow1(); case 1: return GetRenderWindow2(); case 2: return GetRenderWindow3(); case 3: return GetRenderWindow4(); default: MITK_ERROR << "Requested unknown render window"; break; } return nullptr; } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow1() const { return QmitkAbstractMultiWidget::GetRenderWindow(GetNameFromIndex(0, 0)); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow2() const { return QmitkAbstractMultiWidget::GetRenderWindow(GetNameFromIndex(0, 1)); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow3() const { return QmitkAbstractMultiWidget::GetRenderWindow(GetNameFromIndex(1, 0)); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow4() const { return QmitkAbstractMultiWidget::GetRenderWindow(GetNameFromIndex(1, 1)); } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane1() const { return m_PlaneNode1; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane2() const { return m_PlaneNode2; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane3() const { return m_PlaneNode3; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane(unsigned number) const { switch (number) { case 1: return m_PlaneNode1; case 2: return m_PlaneNode2; case 3: return m_PlaneNode3; default: MITK_ERROR << "Requested unknown render window"; break; } return nullptr; } void QmitkStdMultiWidget::SetDecorationColor(unsigned int widgetNumber, mitk::Color color) { switch (widgetNumber) { case 0: if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetColor(color); } break; case 1: if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetColor(color); } break; case 2: if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetColor(color); } break; case 3: m_DecorationColorWidget4 = color; break; default: MITK_ERROR << "Decoration color for unknown widget!"; break; } } mitk::Color QmitkStdMultiWidget::GetDecorationColor(unsigned int widgetNumber) { // The implementation looks a bit messy here, but it avoids // synchronization of the color of the geometry nodes and an // internal member here. // Default colors were chosen for decent visibility. // Feel free to change your preferences in the workbench. float tmp[3] = { 0.0f, 0.0f, 0.0f }; switch (widgetNumber) { case 0: { if (m_PlaneNode1.IsNotNull()) { if (m_PlaneNode1->GetColor(tmp)) { return dynamic_cast(m_PlaneNode1->GetProperty("color"))->GetColor(); } } float red[3] = { 0.753f, 0.0f, 0.0f }; // This is #C00000 in hex return mitk::Color(red); } case 1: { if (m_PlaneNode2.IsNotNull()) { if (m_PlaneNode2->GetColor(tmp)) { return dynamic_cast(m_PlaneNode2->GetProperty("color"))->GetColor(); } } float green[3] = { 0.0f, 0.69f, 0.0f }; // This is #00B000 in hex return mitk::Color(green); } case 2: { if (m_PlaneNode3.IsNotNull()) { if (m_PlaneNode3->GetColor(tmp)) { return dynamic_cast(m_PlaneNode3->GetProperty("color"))->GetColor(); } } float blue[3] = { 0.0, 0.502f, 1.0f }; // This is #0080FF in hex return mitk::Color(blue); } case 3: { return m_DecorationColorWidget4; } default: MITK_ERROR << "Decoration color for unknown widget!"; float black[3] = { 0.0f, 0.0f, 0.0f }; return mitk::Color(black); } } void QmitkStdMultiWidget::mousePressEvent(QMouseEvent* e) { if (QEvent::MouseButtonPress != e->type()) { return; } auto renderWindowWidget = dynamic_cast(this->sender()); if (nullptr == renderWindowWidget) { return; } auto renderWindowWidgetPointer = GetRenderWindowWidget(renderWindowWidget->GetWidgetName()); SetActiveRenderWindowWidget(renderWindowWidgetPointer); } void QmitkStdMultiWidget::moveEvent(QMoveEvent* e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the Annotation as the StdMultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } void QmitkStdMultiWidget::wheelEvent(QWheelEvent* e) { emit WheelMoved(e); } void QmitkStdMultiWidget::HandleCrosshairPositionEventDelayed() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } m_PendingCrosshairPositionEvent = false; // find image with highest layer mitk::TNodePredicateDataType::Pointer isImageData = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = dataStorage->GetSubset(isImageData).GetPointer(); mitk::Point3D crosshairPos = GetSelectedPosition(""); mitk::BaseRenderer* baseRenderer = GetRenderWindow1()->GetSliceNavigationController()->GetRenderer(); auto globalCurrentTimePoint = baseRenderer->GetTime(); mitk::DataNode::Pointer node = mitk::FindTopmostVisibleNode(nodes, crosshairPos, globalCurrentTimePoint, baseRenderer); mitk::DataNode::Pointer topSourceNode; mitk::Image::Pointer image; bool isBinary = false; int component = 0; if (node.IsNotNull()) { node->GetBoolProperty("binary", isBinary); if (isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = dataStorage->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, crosshairPos, globalCurrentTimePoint, baseRenderer); } if (topSourceNode.IsNotNull()) { image = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } std::string statusText; std::stringstream stream; itk::Index<3> p; unsigned int timestep = baseRenderer->GetTimeStep(); if (image.IsNotNull() && (image->GetTimeSteps() > timestep)) { image->GetGeometry()->WorldToIndex(crosshairPos, p); stream.precision(2); stream << "Position: <" << std::fixed << crosshairPos[0] << ", " << std::fixed << crosshairPos[1] << ", " << std::fixed << crosshairPos[2] << "> mm"; stream << "; Index: <" << p[0] << ", " << p[1] << ", " << p[2] << "> "; mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess, image->GetChannelDescriptor().GetPixelType(), image, image->GetVolumeData(image->GetTimeGeometry()->TimePointToTimeStep(globalCurrentTimePoint)), p, pixelValue, component); if (fabs(pixelValue) > 1000000 || fabs(pixelValue) < 0.01) { stream << "; Time: " << globalCurrentTimePoint << " ms; Pixelvalue: " << std::scientific << pixelValue << " "; } else { stream << "; Time: " << globalCurrentTimePoint << " ms; Pixelvalue: " << pixelValue << " "; } } else { stream << "No image information at this position!"; } statusText = stream.str(); mitk::StatusBar::GetInstance()->DisplayGreyValueText(statusText.c_str()); } void QmitkStdMultiWidget::Fit() { vtkSmartPointer vtkrenderer; vtkrenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow1()->GetRenderWindow())->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } vtkrenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow2()->GetRenderWindow())->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } vtkrenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow3()->GetRenderWindow())->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } vtkrenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow4()->GetRenderWindow())->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } mitk::BaseRenderer::GetInstance(GetRenderWindow1()->GetRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(GetRenderWindow2()->GetRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(GetRenderWindow3()->GetRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(GetRenderWindow4()->GetRenderWindow())->GetCameraController()->Fit(); int w = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); vtkObject::SetGlobalWarningDisplay(w); } void QmitkStdMultiWidget::AddDisplayPlaneSubTree() { // add the displayed planes of the multiwidget to a node to which the subtree // @a planesSubTree points ... mitk::PlaneGeometryDataMapper2D::Pointer mapper; mitk::IntProperty::Pointer layer = mitk::IntProperty::New(1000); // ... of widget 1 mitk::BaseRenderer* renderer1 = mitk::BaseRenderer::GetInstance(GetRenderWindow1()->GetRenderWindow()); m_PlaneNode1 = renderer1->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode1->SetColor(GetDecorationColor(0)); m_PlaneNode1->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode1->SetProperty("name", mitk::StringProperty::New(std::string(renderer1->GetName()) + ".plane")); m_PlaneNode1->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode1->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PlaneNode1->SetProperty("layer", layer); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode1->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 2 mitk::BaseRenderer* renderer2 = mitk::BaseRenderer::GetInstance(GetRenderWindow2()->GetRenderWindow()); m_PlaneNode2 = renderer2->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode2->SetColor(GetDecorationColor(1)); m_PlaneNode2->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode2->SetProperty("name", mitk::StringProperty::New(std::string(renderer2->GetName()) + ".plane")); m_PlaneNode2->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode2->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PlaneNode2->SetProperty("layer", layer); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode2->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 3 mitk::BaseRenderer *renderer3 = mitk::BaseRenderer::GetInstance(GetRenderWindow3()->GetRenderWindow()); m_PlaneNode3 = renderer3->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode3->SetColor(GetDecorationColor(2)); m_PlaneNode3->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode3->SetProperty("name", mitk::StringProperty::New(std::string(renderer3->GetName()) + ".plane")); m_PlaneNode3->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode3->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PlaneNode3->SetProperty("layer", layer); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode3->SetMapper(mitk::BaseRenderer::Standard2D, mapper); m_ParentNodeForGeometryPlanes = mitk::DataNode::New(); m_ParentNodeForGeometryPlanes->SetProperty("name", mitk::StringProperty::New("Widgets")); m_ParentNodeForGeometryPlanes->SetProperty("helper object", mitk::BoolProperty::New(true)); m_ParentNodeForGeometryPlanes->SetProperty("layer", layer); } void QmitkStdMultiWidget::EnsureDisplayContainsPoint(mitk::BaseRenderer *renderer, const mitk::Point3D &p) { mitk::Point2D pointOnDisplay; renderer->WorldToDisplay(p, pointOnDisplay); if (pointOnDisplay[0] < renderer->GetVtkRenderer()->GetOrigin()[0] || pointOnDisplay[1] < renderer->GetVtkRenderer()->GetOrigin()[1] || pointOnDisplay[0] > renderer->GetVtkRenderer()->GetOrigin()[0] + renderer->GetViewportSize()[0] || pointOnDisplay[1] > renderer->GetVtkRenderer()->GetOrigin()[1] + renderer->GetViewportSize()[1]) { mitk::Point2D pointOnPlane; renderer->GetCurrentWorldPlaneGeometry()->Map(p, pointOnPlane); renderer->GetCameraController()->MoveCameraToPoint(pointOnPlane); } } void QmitkStdMultiWidget::SetWidgetPlaneVisibility(const char *widgetName, bool visible, mitk::BaseRenderer *renderer) { auto dataStorage = GetDataStorage(); if (nullptr != dataStorage) { mitk::DataNode* dataNode = dataStorage->GetNamedNode(widgetName); if (dataNode != nullptr) { dataNode->SetVisibility(visible, renderer); } } } void QmitkStdMultiWidget::SetWidgetPlanesVisibility(bool visible, mitk::BaseRenderer *renderer) { if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetVisibility(visible, renderer); } if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetVisibility(visible, renderer); } if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetVisibility(visible, renderer); } GetRenderingManager()->RequestUpdateAll(); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkStdMultiWidget::SetLayoutImpl() { CreateRenderWindowWidgets(); GetMultiWidgetLayoutManager()->OnLayoutDesignChanged(QmitkMultiWidgetLayoutManager::LayoutDesign::DEFAULT); // Initialize views as axial, sagittal, coronal to all data objects in DataStorage auto geo = GetDataStorage()->ComputeBoundingGeometry3D(GetDataStorage()->GetAll()); GetRenderingManager()->InitializeViews(geo); } void QmitkStdMultiWidget::ResetRenderWindows() { auto renderingManager = GetRenderingManager(); if (nullptr == renderingManager) { return; } auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } renderingManager->InitializeViewsByBoundingObjects(dataStorage); } void QmitkStdMultiWidget::CreateRenderWindowWidgets() { // create axial render window (widget) QString renderWindowWidgetName = GetNameFromIndex(0, 0); RenderWindowWidgetPointer renderWindowWidget1 = std::make_shared(this, renderWindowWidgetName, GetDataStorage(), GetRenderingManager()); auto renderWindow1 = renderWindowWidget1->GetRenderWindow(); renderWindow1->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); renderWindowWidget1->SetDecorationColor(GetDecorationColor(0)); renderWindowWidget1->SetCornerAnnotationText("Axial"); renderWindowWidget1->GetRenderWindow()->SetLayoutIndex(ViewDirection::AXIAL); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget1); // create sagittal render window (widget) renderWindowWidgetName = GetNameFromIndex(0, 1); RenderWindowWidgetPointer renderWindowWidget2 = std::make_shared(this, renderWindowWidgetName, GetDataStorage(), GetRenderingManager()); auto renderWindow2 = renderWindowWidget2->GetRenderWindow(); renderWindow2->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Sagittal); renderWindowWidget2->SetDecorationColor(GetDecorationColor(1)); renderWindowWidget2->setStyleSheet("border: 0px"); renderWindowWidget2->SetCornerAnnotationText("Sagittal"); renderWindowWidget2->GetRenderWindow()->SetLayoutIndex(ViewDirection::SAGITTAL); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget2); // create coronal render window (widget) renderWindowWidgetName = GetNameFromIndex(1, 0); RenderWindowWidgetPointer renderWindowWidget3 = std::make_shared(this, renderWindowWidgetName, GetDataStorage(), GetRenderingManager()); auto renderWindow3 = renderWindowWidget3->GetRenderWindow(); renderWindow3->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Frontal); renderWindowWidget3->SetDecorationColor(GetDecorationColor(2)); renderWindowWidget3->SetCornerAnnotationText("Coronal"); renderWindowWidget3->GetRenderWindow()->SetLayoutIndex(ViewDirection::CORONAL); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget3); // create 3D render window (widget) renderWindowWidgetName = GetNameFromIndex(1, 1); RenderWindowWidgetPointer renderWindowWidget4 = std::make_shared(this, renderWindowWidgetName, GetDataStorage(), GetRenderingManager()); auto renderWindow4 = renderWindowWidget4->GetRenderWindow(); renderWindow4->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Original); renderWindowWidget4->SetDecorationColor(GetDecorationColor(3)); renderWindowWidget4->SetCornerAnnotationText("3D"); renderWindowWidget4->GetRenderWindow()->SetLayoutIndex(ViewDirection::THREE_D); mitk::BaseRenderer::GetInstance(renderWindowWidget4->GetRenderWindow()->GetRenderWindow())->SetMapperID(mitk::BaseRenderer::Standard3D); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget4); SetActiveRenderWindowWidget(renderWindowWidget1); // connect to the "time navigation controller": send time via sliceNavigationControllers m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow1->GetSliceNavigationController(), false); m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow2->GetSliceNavigationController(), false); m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow3->GetSliceNavigationController(), false); m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow4->GetSliceNavigationController(), false); renderWindow1->GetSliceNavigationController()->ConnectGeometrySendEvent( mitk::BaseRenderer::GetInstance(renderWindow4->GetRenderWindow())); // reverse connection between sliceNavigationControllers and timeNavigationController renderWindow1->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); renderWindow2->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); renderWindow3->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); //renderWindow4->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); auto layoutManager = GetMultiWidgetLayoutManager(); connect(renderWindowWidget1.get(), &QmitkRenderWindowWidget::MouseEvent, this, &QmitkStdMultiWidget::mousePressEvent); connect(renderWindow1, &QmitkRenderWindow::ResetView, this, &QmitkStdMultiWidget::ResetCrosshair); connect(renderWindow1, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkStdMultiWidget::SetCrosshairVisibility); connect(renderWindow1, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkStdMultiWidget::SetWidgetPlaneMode); connect(renderWindow1, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::OnLayoutDesignChanged); connect(renderWindow1, &QmitkRenderWindow::LayoutIndexChanged, this, &QmitkStdMultiWidget::ResetRenderWindows); connect(renderWindowWidget2.get(), &QmitkRenderWindowWidget::MouseEvent, this, &QmitkStdMultiWidget::mousePressEvent); connect(renderWindow2, &QmitkRenderWindow::ResetView, this, &QmitkStdMultiWidget::ResetCrosshair); connect(renderWindow2, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkStdMultiWidget::SetCrosshairVisibility); connect(renderWindow2, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkStdMultiWidget::SetWidgetPlaneMode); connect(renderWindow2, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::OnLayoutDesignChanged); connect(renderWindow2, &QmitkRenderWindow::LayoutIndexChanged, this, &QmitkStdMultiWidget::ResetRenderWindows); connect(renderWindowWidget3.get(), &QmitkRenderWindowWidget::MouseEvent, this, &QmitkStdMultiWidget::mousePressEvent); connect(renderWindow3, &QmitkRenderWindow::ResetView, this, &QmitkStdMultiWidget::ResetCrosshair); connect(renderWindow3, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkStdMultiWidget::SetCrosshairVisibility); connect(renderWindow3, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkStdMultiWidget::SetWidgetPlaneMode); connect(renderWindow3, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::OnLayoutDesignChanged); connect(renderWindow3, &QmitkRenderWindow::LayoutIndexChanged, this, &QmitkStdMultiWidget::ResetRenderWindows); connect(renderWindowWidget4.get(), &QmitkRenderWindowWidget::MouseEvent, this, &QmitkStdMultiWidget::mousePressEvent); connect(renderWindow4, &QmitkRenderWindow::ResetView, this, &QmitkStdMultiWidget::ResetCrosshair); connect(renderWindow4, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkStdMultiWidget::SetCrosshairVisibility); connect(renderWindow4, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkStdMultiWidget::SetWidgetPlaneMode); connect(renderWindow4, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::OnLayoutDesignChanged); connect(renderWindow4, &QmitkRenderWindow::LayoutIndexChanged, this, &QmitkStdMultiWidget::ResetRenderWindows); }