diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index c7323502f0..3621706abc 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,316 +1,316 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkCoreActivator.cpp mitkCoreObjectFactoryBase.cpp mitkCoreObjectFactory.cpp mitkCoreServices.cpp mitkException.cpp Algorithms/mitkBaseDataSource.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkCompositePixelValueToString.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAnatomicalStructureColorPresets.cpp DataManagement/mitkArbitraryTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataStorage.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGeometryTransformHolder.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkIPropertyPersistence.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLine.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMaterial.cpp DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateGeometry.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateDataProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkPropertyNameHelper.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkPropertyPersistence.cpp DataManagement/mitkPropertyPersistenceInfo.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkScaleOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTemporoSpatialStringProperty.cpp DataManagement/mitkVector.cpp DataManagement/mitkVectorProperty.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp Interactions/mitkAction.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkDisplayActionEventBroadcast.cpp - Interactions/mitkDisplayActionEventDefaultFunctions.cpp + Interactions/mitkDisplayActionEventFunctions.cpp Interactions/mitkDisplayActionEventHandler.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventFactory.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkInteractionEventObserver.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkStdDisplayActionEventHandler.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkXML2EventParser.cpp IO/mitkAbstractFileIO.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkCustomMimeType.cpp IO/mitkDicomSeriesReader.cpp IO/mitkDicomSeriesReaderService.cpp IO/mitkDicomSR_GantryTiltInformation.cpp IO/mitkDicomSR_ImageBlockDescriptor.cpp IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp IO/mitkDicomSR_LoadDICOMRGBPixel.cpp IO/mitkDicomSR_LoadDICOMScalar4D.cpp IO/mitkDicomSR_LoadDICOMScalar.cpp IO/mitkDicomSR_SliceGroupingResult.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileReaderWriterBase.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp IO/mitkGeometry3DToXML.cpp IO/mitkIFileIO.cpp IO/mitkIFileReader.cpp IO/mitkIFileWriter.cpp IO/mitkGeometryDataReaderService.cpp IO/mitkGeometryDataWriterService.cpp IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp IO/mitkIOConstants.cpp IO/mitkIOMimeTypes.cpp IO/mitkIOUtil.cpp IO/mitkItkImageIO.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkLegacyFileReaderService.cpp IO/mitkLegacyFileWriterService.cpp IO/mitkLocaleSwitch.cpp IO/mitkLog.cpp IO/mitkMimeType.cpp IO/mitkMimeTypeProvider.cpp IO/mitkOperation.cpp IO/mitkPixelType.cpp IO/mitkPointSetReaderService.cpp IO/mitkPointSetWriterService.cpp IO/mitkProportionalTimeGeometryToXML.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSurfaceStlIO.cpp IO/mitkSurfaceVtkIO.cpp IO/mitkSurfaceVtkLegacyIO.cpp IO/mitkSurfaceVtkXmlIO.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkPreferenceListReaderOptionsFunctor.cpp Rendering/mitkAbstractAnnotationRenderer.cpp Rendering/mitkAnnotationUtils.cpp Rendering/mitkBaseRenderer.cpp #Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module Rendering/mitkGradientBackground.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/mitkMapper.cpp Rendering/mitkAnnotation.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowFrame.cpp #Rendering/mitkSurfaceGLMapper2D.cpp Moved to deprecated LegacyGL Module Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigPACS.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITK.xml Interactions/DisplayConfigMITKNoCrosshair.xml Interactions/DisplayConfigMITKRotation.xml Interactions/DisplayConfigMITKRotationUnCoupled.xml Interactions/DisplayConfigMITKSwivel.xml Interactions/DisplayConfigMITKLimited.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml mitkAnatomicalStructureColorPresets.xml ) diff --git a/Modules/Core/include/mitkDisplayActionEventBroadcast.h b/Modules/Core/include/mitkDisplayActionEventBroadcast.h index d573f09a2b..f2ebf996c2 100644 --- a/Modules/Core/include/mitkDisplayActionEventBroadcast.h +++ b/Modules/Core/include/mitkDisplayActionEventBroadcast.h @@ -1,218 +1,218 @@ /*=================================================================== 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 MITKDISPLAYACTIONEVENTBROADCAST_H #define MITKDISPLAYACTIONEVENTBROADCAST_H #include "mitkInteractionEventObserver.h" #include namespace mitk { /** * @brief This class serves as an event state machine while simultaneously observing interaction events. * It connects the actions from the event state machine .xml-file with concrete functions of this class. */ class MITKCORE_EXPORT DisplayActionEventBroadcast : public EventStateMachine, public InteractionEventObserver { public: mitkClassMacro(DisplayActionEventBroadcast, EventStateMachine) itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** * By this function this observer is notified about about every 'InteractionEvent'. * The interaction event is passed to the state machine in order to use its infrastructure. * For more information see @see InteractionEventObserver. * * @par interactionEvent The event that was observed and triggered this notification. * @par isHandled Flag that indicates if a 'DataInteractor' has already handled the event. */ virtual void Notify(InteractionEvent* interactionEvent, bool isHandled) override; protected: DisplayActionEventBroadcast(); virtual ~DisplayActionEventBroadcast() override; /** * @brief Connects the action names used in the state machine pattern with functions implemented within this InteractionEventObserver. */ void ConnectActionsAndFunctions() override; /** * @brief This function is executed when a config object is set / changed (via 'SetEventConfig' or 'AddEventConfig' in 'InteractionEventObserver'). * It is used to read out the parameters set in the configuration file and to set the member variables accordingly. */ virtual void ConfigurationChanged() override; /** * @brief Filters the event resp. the sender of the event. * * @par interactionEvent The event whose sender has to be checked * @par data node The data node is ignored in this specific implementation. * * @return True, if the sender of the event is a valid sender and the sending renderer is a 2D-renderer. False, if not. */ virtual bool FilterEvents(InteractionEvent* interactionEvent, DataNode* dataNode) override; ////////////////////////////////////////////////////////////////////////// // Functions to react to interaction events (actions) ////////////////////////////////////////////////////////////////////////// /** * @brief Check if the given interaction event is actually an 'InteractionPositionEvent'. * * @par interactionEvent The interaction event that is checked. * * @return True, if the given event can be dynamically cast to an 'InteractionPositionEvent'. False, if not. */ virtual bool CheckPositionEvent(const InteractionEvent* interactionEvent); virtual bool CheckRotationPossible(const InteractionEvent* interactionEvent); virtual bool CheckSwivelPossible(const InteractionEvent* interactionEvent); virtual void Init(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); virtual void Move(StateMachineAction* stateMachineAction , InteractionEvent* interactionEvent); virtual void SetCrosshair(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); virtual void Zoom(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); virtual void Scroll(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); - virtual void ScrollOneDown(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); - virtual void ScrollOneUp(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + virtual void ScrollOneDown(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + virtual void AdjustLevelWindow(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); virtual void StartRotation(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); virtual void EndRotation(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); virtual void Rotate(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); virtual void Swivel(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); private: virtual void UpdateStatusbar(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); bool GetBoolProperty(mitk::PropertyList::Pointer propertyList, const char* propertyName, bool defaultValue); mitk::DataNode::Pointer GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes, mitk::Point3D worldposition, BaseRenderer* renderer); /** * @brief Reference to the service registration of the observer. * This is needed to unregister the observer on unload. */ us::ServiceRegistration m_ServiceRegistration; /** * @brief Determines if this broadcast class reacts to events that already have been processed by a DataInteractor. * The default value is false. */ bool m_AlwaysReact; /** * @brief Coordinate of the mouse pointer at beginning of an interaction (translated to mm unit). */ mitk::Point2D m_StartCoordinateInMM; /** * @brief Coordinate of the mouse pointer in the last step within an interaction. */ mitk::Point2D m_LastDisplayCoordinate; /** * @brief Coordinate of the mouse pointer in the last step within an interaction (translated to mm unit). */ mitk::Point2D m_LastCoordinateInMM; /** * \brief Current coordinates of the pointer. */ mitk::Point2D m_CurrentDisplayCoordinate; /** * @brief Defines the behavior at the end of a data set. * If set to true, it will restart at end of data set from the beginning. */ bool m_AutoRepeat; /** * @brief Defines how many slices are scrolled per pixel that the mouse pointer was moved. * By default the modifier is 4. This means that when the user moves the cursor by 4 pixels in Y-direction, * the scene is scrolled by one slice. If the user has moved the the cursor by 20 pixels, the scene is * scrolled by 5 slices. * * If the cursor has moved less than m_IndexToSliceModifier pixels, the scene is scrolled by one slice. */ int m_IndexToSliceModifier; /** * @brief Defines the scroll behavior. * Default is up/down movement of pointer performs scrolling */ std::string m_ScrollDirection; /** * @brief Defines how the axis of interaction influences scroll behavior. */ bool m_InvertScrollDirection; /** * @brief Defines the zoom behavior. * Default is up/down movement of pointer performs zooming */ std::string m_ZoomDirection; /** * @brief Defines how the axis of interaction influences zoom behavior. */ bool m_InvertZoomDirection; /** * @brief Factor to adjust zooming speed. */ float m_ZoomFactor; /** * @brief Defines how the axis of interaction influences move behavior. */ bool m_InvertMoveDirection; /** * @brief Defines the level-window behavior. * Default is left/right movement of pointer modifies the level. */ std::string m_LevelDirection; /** * @brief Defines how the axis of interaction influences level-window behavior. */ bool m_InvertLevelWindowDirection; /** * @brief Determines if the angle between crosshair remains fixed when rotating. */ bool m_LinkPlanes; typedef std::vector SNCVector; SNCVector m_RotatableSNCs; SNCVector m_SNCsToBeRotated; Point3D m_LastCursorPosition; Point3D m_CenterOfRotation; Point2D m_ReferenceCursor; Vector3D m_RotationPlaneNormal; Vector3D m_RotationPlaneXVector; Vector3D m_RotationPlaneYVector; Vector3D m_PreviousRotationAxis; ScalarType m_PreviousRotationAngle; }; } #endif // MITKDISPLAYACTIONEVENTBROADCAST_H diff --git a/Modules/Core/include/mitkDisplayActionEventDefaultFunctions.h b/Modules/Core/include/mitkDisplayActionEventDefaultFunctions.h deleted file mode 100644 index ddb41527e9..0000000000 --- a/Modules/Core/include/mitkDisplayActionEventDefaultFunctions.h +++ /dev/null @@ -1,52 +0,0 @@ -/*=================================================================== - -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 MITKDISPLAYACTIONEVENTDEFAULTFUNCTIONS_H -#define MITKDISPLAYACTIONEVENTDEFAULTFUNCTIONS_H - -namespace mitk -{ - namespace DisplayActionEventDefaultFunctions - { - /** - * @brief Registers a command as observer of the observable broadcast class. - * This command reacts on the 'DisplayMoveEvent' and performs a move of the camera controller - * by a vector that was previously determined by the mouse interaction event. - */ - StdFunctionCommand::ActionFunction DefaultMoveAction(); - /** - * @brief Registers a command as observer of the observable broadcast class. - * This command reacts on the 'DisplaySetCrosshairEvent' and performs a slice selection of the slice navigation controller. - * The new position was previously determined by the mouse interaction event. - */ - StdFunctionCommand::ActionFunction DefaultSetCrosshairAction(); - /** - * @brief Registers a command as observer of the observable broadcast class. - * This command reacts on the 'DisplayZoomEvent' and performs a zoom of the camera controller - * by a zoom factor that was previously determined by the mouse interaction event. - */ - StdFunctionCommand::ActionFunction DefaultZoomAction(); - /** - * @brief Registers a command as observer of the observable broadcast class. - * This command reacts on the 'DisplayScrollEvent' and performs a slice selection of the slice navigation controller. - * The new position was previously determined by the mouse interaction event. - */ - StdFunctionCommand::ActionFunction DefaultScrollAction(); - - } // end namespace DisplayActionEventDefaultFunctions -} // end namespace mitk - -#endif // MITKDISPLAYACTIONEVENTDEFAULTFUNCTIONS_H diff --git a/Modules/Core/include/mitkDisplayActionEventFunctions.h b/Modules/Core/include/mitkDisplayActionEventFunctions.h new file mode 100644 index 0000000000..341674995e --- /dev/null +++ b/Modules/Core/include/mitkDisplayActionEventFunctions.h @@ -0,0 +1,55 @@ +/*=================================================================== + +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 MITKDISPLAYACTIONEVENTFUNCTIONS_H +#define MITKDISPLAYACTIONEVENTFUNCTIONS_H + +#include "mitkStdFunctionCommand.h" + +namespace mitk +{ + namespace DisplayActionEventFunctions + { + /** + * @brief Returns a command that reacts on the 'DisplayMoveEvent'. + * The command performs a move of the camera controller of the sending renderer by a vector + * that was previously determined by the mouse interaction event. + */ + StdFunctionCommand::ActionFunction MoveSenderCameraAction(); + /** + * @brief Returns a command that reacts on the 'DisplaySetCrosshairEvent'. + * The command performs a slice selection of the slice navigation controller and will set + * the cross hair for all render windows. + * The new position was previously determined by the mouse interaction event. + */ + StdFunctionCommand::ActionFunction SetCrosshairAction(); + /** + * @brief Returns a command that reacts on the 'DisplayZoomEvent'. + * The command performs a zoom of the camera controller of the sending renderer by a zoom factor + * that was previously determined by the mouse interaction event. + */ + StdFunctionCommand::ActionFunction ZoomSenderCameraAction(); + /** + * @brief Returns a command that reacts on the 'DisplayScrollEvent'. + * The command performs a slice scrolling of the slice navigation controller of the sending renderer. + * The new position was previously determined by the mouse interaction event. + */ + StdFunctionCommand::ActionFunction ScrollSliceStepperAction(); + + } // end namespace DisplayActionEventFunctions +} // end namespace mitk + +#endif // MITKDISPLAYACTIONEVENTFUNCTIONS_H diff --git a/Modules/Core/include/mitkDisplayActionEvents.h b/Modules/Core/include/mitkDisplayActionEvents.h index dd1081ab96..671d47dc0d 100644 --- a/Modules/Core/include/mitkDisplayActionEvents.h +++ b/Modules/Core/include/mitkDisplayActionEvents.h @@ -1,142 +1,150 @@ /*=================================================================== 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 #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; } DisplayActionEvent(const Self& s) : Superclass(s), m_InteractionEvent(s.m_InteractionEvent) {}; 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.m_MoveVector) {}; 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.m_Position) {}; 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.m_ZoomFactor), m_StartCoordinate(s.m_StartCoordinate) {}; 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 position) + DisplayScrollEvent(InteractionEvent* interactionEvent, int sliceDelta) : Superclass(interactionEvent) - , m_Position(position) + , m_SliceDelta(sliceDelta) { } virtual ~DisplayScrollEvent() {} virtual const char* GetEventName() const override { return "DisplayScrollEvent"; } - virtual itk::EventObject* MakeObject() const override { return new Self(GetInteractionEvent(), m_Position); } - int GetPosition() const { return m_Position; } - DisplayScrollEvent(const Self& s) : Superclass(s), m_Position(s.m_Position) {}; + 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); } + int GetSliceDelta() const { return m_SliceDelta; } + DisplayScrollEvent(const Self& s) : Superclass(s), m_SliceDelta(s.m_SliceDelta) {}; private: - int m_Position; + int m_SliceDelta; }; } // end namespace #endif // MITKDISPLAYACTIONEVENTS_H diff --git a/Modules/Core/include/mitkStdFunctionCommand.h b/Modules/Core/include/mitkStdFunctionCommand.h index b483b52a40..e1ce8dfc2a 100644 --- a/Modules/Core/include/mitkStdFunctionCommand.h +++ b/Modules/Core/include/mitkStdFunctionCommand.h @@ -1,92 +1,95 @@ /*=================================================================== 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 MITKSTDFUNCTIONCOMMAND_H #define MITKSTDFUNCTIONCOMMAND_H #include // itk #include +// c++ +#include + namespace mitk { // define custom command to accept std::functions as "filter" and as "action" class MITKCORE_EXPORT StdFunctionCommand : public itk::Command { public: - using Self = StdFunctionCommand; + using Self = StdFunctionCommand; using Pointer = itk::SmartPointer; using FilterFunction = std::function; using ActionFunction = std::function; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(StdFunctionCommand, itk::Command); void SetCommandFilter(FilterFunction stdFunctionFilter) { m_StdFilterFunction = stdFunctionFilter; } void SetCommandAction(ActionFunction stdFunctionAction) { m_StdActionFunction = stdFunctionAction; } virtual void Execute(Object*, const itk::EventObject& event) override { if (m_StdFilterFunction && m_StdActionFunction) { if (m_StdFilterFunction(event)) { m_StdActionFunction(event); } } } virtual void Execute(const Object*, const itk::EventObject& event) override { if (m_StdFilterFunction && m_StdActionFunction) { if (m_StdFilterFunction(event)) { m_StdActionFunction(event); } } } protected: FilterFunction m_StdFilterFunction; ActionFunction m_StdActionFunction; StdFunctionCommand() : m_StdFilterFunction(nullptr) , m_StdActionFunction(nullptr) {} virtual ~StdFunctionCommand() {} private: ITK_DISALLOW_COPY_AND_ASSIGN(StdFunctionCommand); }; } // end namespace mitk #endif // MITKSTDFUNCTIONCOMMAND_H diff --git a/Modules/Core/include/mitkStepper.h b/Modules/Core/include/mitkStepper.h index 05ed8d3f47..a67cfdf805 100644 --- a/Modules/Core/include/mitkStepper.h +++ b/Modules/Core/include/mitkStepper.h @@ -1,146 +1,148 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef STEPPER_H_HEADER_INCLUDED_C1E77191 #define STEPPER_H_HEADER_INCLUDED_C1E77191 #include "mitkNumericTypes.h" #include #include #include #include #include namespace mitk { /** * \brief Helper class to step through a list * * A helper class to step through a list. Does not contain the list, just the * position in the list (between 0 and GetSteps()). Provides methods like * First (go to the first element), Next (go to the next one), etc. * * Besides the actual number of steps, the stepper can also hold a stepping * range, indicating the scalar values corresponding to the covered steps. * For example, steppers are generally used to slice a dataset with a plane; * Hereby, Steps indicates the total number of steps (positions) available for * the plane, Pos indicates the current step, and Range indicates the physical * minimum and maximum values for the plane, in this case a value in mm. * * The range can also be supplied with a unit name (a string) which can be * used by classes providing information about the stepping (e.g. graphical * sliders). * * \ingroup NavigationControl */ class MITKCORE_EXPORT Stepper : public itk::Object { public: mitkClassMacroItkParent(Stepper, itk::Object); itkFactorylessNewMacro(Self) itkCloneMacro(Self) itkGetConstMacro(Pos, unsigned int); virtual void SetPos(unsigned int pos) { // copied from itkMacro.h, itkSetClampMacro(...) unsigned int newPos; if (m_Steps != 0) { newPos = (pos > m_Steps - 1 ? m_Steps - 1 : pos); } else { newPos = 0; } if (this->m_Pos != newPos) { this->m_Pos = newPos; this->Modified(); } } itkGetConstMacro(Steps, unsigned int); itkSetMacro(Steps, unsigned int); itkGetConstMacro(AutoRepeat, bool); itkSetMacro(AutoRepeat, bool); itkBooleanMacro(AutoRepeat); /** Causes the stepper to shift direction when the boundary is reached */ itkSetMacro(PingPong, bool); itkGetConstMacro(PingPong, bool); itkBooleanMacro(PingPong); /** If set to true, the Next() decreases the stepper and Previous() * decreases it */ itkSetMacro(InverseDirection, bool); itkGetConstMacro(InverseDirection, bool); itkBooleanMacro(InverseDirection); void SetRange(ScalarType min, ScalarType max); void InvalidateRange(); ScalarType GetRangeMin() const; ScalarType GetRangeMax() const; bool HasValidRange() const; void RemoveRange(); bool HasRange() const; void SetUnitName(const char *unitName); const char *GetUnitName() const; void RemoveUnitName(); bool HasUnitName() const; virtual void Next(); virtual void Previous(); + virtual void MoveSlice(int sliceDelta); + virtual void First(); virtual void Last(); protected: Stepper(); virtual ~Stepper(); void Increase(); void Decrease(); unsigned int m_Pos; unsigned int m_Steps; bool m_AutoRepeat; bool m_PingPong; bool m_InverseDirection; ScalarType m_RangeMin; ScalarType m_RangeMax; bool m_RangeValid; bool m_HasRange; std::string m_UnitName; bool m_HasUnitName; }; } // namespace mitk #endif /* STEPPER_H_HEADER_INCLUDED_C1E77191 */ diff --git a/Modules/Core/src/Controllers/mitkStepper.cpp b/Modules/Core/src/Controllers/mitkStepper.cpp index cdecc526e3..14e980a2dd 100644 --- a/Modules/Core/src/Controllers/mitkStepper.cpp +++ b/Modules/Core/src/Controllers/mitkStepper.cpp @@ -1,180 +1,209 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkStepper.h" mitk::Stepper::Stepper() : m_Pos(0), m_Steps(0), m_AutoRepeat(false), m_PingPong(false), m_InverseDirection(false), m_RangeMin(0.0), m_RangeMax(-1.0), m_RangeValid(false), m_HasRange(false), m_HasUnitName(false) { } mitk::Stepper::~Stepper() { } void mitk::Stepper::SetRange(ScalarType min, ScalarType max) { m_RangeMin = min; m_RangeMax = max; m_HasRange = true; m_RangeValid = true; this->Modified(); } void mitk::Stepper::InvalidateRange() { m_HasRange = true; m_RangeValid = false; this->Modified(); } mitk::ScalarType mitk::Stepper::GetRangeMin() const { return m_RangeMin; } mitk::ScalarType mitk::Stepper::GetRangeMax() const { return m_RangeMax; } void mitk::Stepper::RemoveRange() { m_HasRange = false; this->Modified(); } bool mitk::Stepper::HasValidRange() const { return (m_HasRange && m_RangeValid); } bool mitk::Stepper::HasRange() const { return m_HasRange; } void mitk::Stepper::SetUnitName(const char *unitName) { m_UnitName = std::string(unitName); m_HasUnitName = true; this->Modified(); } const char *mitk::Stepper::GetUnitName() const { return m_UnitName.c_str(); } void mitk::Stepper::RemoveUnitName() { m_HasUnitName = false; this->Modified(); } bool mitk::Stepper::HasUnitName() const { return m_HasUnitName; } void mitk::Stepper::Increase() { if (this->GetPos() < this->GetSteps() - 1) { this->SetPos(this->GetPos() + 1); } else if (m_AutoRepeat) { if (!m_PingPong) { this->SetPos(0); } else { m_InverseDirection = true; if (this->GetPos() > 0) { this->SetPos(this->GetPos() - 1); } } } } void mitk::Stepper::Decrease() { if (this->GetPos() > 0) { this->SetPos(this->GetPos() - 1); } else if (m_AutoRepeat) { if (!m_PingPong) { this->SetPos(this->GetSteps() - 1); } else { m_InverseDirection = false; if (this->GetPos() < this->GetSteps() - 1) { this->SetPos(this->GetPos() + 1); } } } } void mitk::Stepper::Next() { if (!m_InverseDirection) { this->Increase(); } else { this->Decrease(); } } void mitk::Stepper::Previous() { if (!m_InverseDirection) { this->Decrease(); } else { this->Increase(); } } +void mitk::Stepper::MoveSlice(int sliceDelta) +{ + int newPosition = this->GetPos() + sliceDelta; + // if auto repeat is on, increasing continues at the first slice if the last slice was reached and vice versa + int maxSlices = this->GetSteps(); + if (m_AutoRepeat) + { + while (newPosition < 0) + { + newPosition += maxSlices; + } + while (newPosition >= maxSlices) + { + newPosition -= maxSlices; + } + } + else + { + // if the new slice is below 0 we still show slice 0 + // due to the stepper using unsigned int we have to do this ourselves + if (newPosition < 1) + { + newPosition = 0; + } + } + + this->SetPos(newPosition); +} + void mitk::Stepper::First() { this->SetPos(0); } void mitk::Stepper::Last() { this->SetPos(this->GetSteps() - 1); } diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp index f9762886d1..584d0563ee 100644 --- a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp @@ -1,682 +1,651 @@ /*=================================================================== 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 "mitkDisplayActionEvents.h" #include "mitkInteractionPositionEvent.h" #include "mitkLine.h" 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 as 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("ScrollOneDown", ScrollOneDown); 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); } void mitk::DisplayActionEventBroadcast::ConfigurationChanged() { mitk::PropertyList::Pointer properties = GetAttributes(); // allwaysReact std::string strAlwaysReact = ""; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } else { m_AlwaysReact = false; } } else { m_AlwaysReact = false; } // auto repeat std::string strAutoRepeat = ""; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } else { m_AutoRepeat = false; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } else { m_IndexToSliceModifier = 4; } // 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 = ""; if (properties->GetStringProperty("coupled", strCoupled)) { if (strCoupled == "true") { m_LinkPlanes = true; } else { m_LinkPlanes = false; } } // 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*/) { if (nullptr == interactionEvent->GetSender()) { return false; } if (BaseRenderer::Standard3D == interactionEvent->GetSender()->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 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 auto* posEvent = dynamic_cast(interactionEvent); if (nullptr == posEvent) { return false; } BaseRenderer* clickedRenderer = posEvent->GetSender(); const PlaneGeometry* ourViewportGeometry = clickedRenderer->GetCurrentWorldPlaneGeometry(); if (nullptr == ourViewportGeometry) { return false; } Point3D cursorPosition = posEvent->GetPositionInWorld(); const auto spacing = ourViewportGeometry->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 double threshholdDistancePixels = 12.0; auto renWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; const PlaneGeometry *otherRenderersRenderPlane = snc->GetCurrentPlaneGeometry(); if (nullptr == otherRenderersRenderPlane) { continue; // ignore, we don't see a plane } // 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 } // check distance from intersection line const double distanceFromIntersectionLine = intersectionLine.Distance(cursorPosition) / spacing[snc->GetDefaultViewDirection()]; // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { 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 if (m_LinkPlanes) { m_SNCsToBeRotated.push_back(snc); } } else // close to cursor { if (nullptr == geometryToBeRotated) // first one close to the cursor { geometryToBeRotated = otherRenderersRenderPlane; 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()) < mitk::eps) { m_SNCsToBeRotated.push_back(snc); } else { hitMultipleLines = true; } } } } bool moveSlices(true); if (geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !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 DO have enough information for rotation // remember where the last cursor position ON THE LINE has been observed m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(cursorPosition); // 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) { const ScalarType ThresholdDistancePixels = 6.0; // 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 *posEvent = dynamic_cast(interactionEvent); BaseRenderer *renderer = interactionEvent->GetSender(); if (!posEvent || !renderer) { return false; } const Point3D& cursor = posEvent->GetPositionInWorld(); m_SNCsToBeRotated.clear(); const PlaneGeometry* clickedGeometry(nullptr); const PlaneGeometry* otherGeometry1(nullptr); const PlaneGeometry* otherGeometry2(nullptr); auto registeredRenderWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : registeredRenderWindows) { SliceNavigationController* snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; // unsigned int slice = (*iter)->GetSlice()->GetPos(); // unsigned int time = (*iter)->GetTime()->GetPos(); const PlaneGeometry *planeGeometry = snc->GetCurrentPlaneGeometry(); if (!planeGeometry) continue; if (snc == renderer->GetSliceNavigationController()) { clickedGeometry = planeGeometry; m_SNCsToBeRotated.push_back(snc); } else { if (otherGeometry1 == nullptr) { otherGeometry1 = planeGeometry; } else { otherGeometry2 = planeGeometry; } if (m_LinkPlanes) { // If planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } } Line3D line; Point3D point; if ((clickedGeometry != nullptr) && (otherGeometry1 != nullptr) && (otherGeometry2 != nullptr) && clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point)) { m_CenterOfRotation = point; if (m_CenterOfRotation.EuclideanDistanceTo(cursor) < ThresholdDistancePixels) { return false; } else { m_ReferenceCursor = posEvent->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) { auto* positionEvent = static_cast(interactionEvent); 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) { auto* positionEvent = static_cast(interactionEvent); - float invertModifier = -1.0; + BaseRenderer* sender = interactionEvent->GetSender(); + Vector2D moveVector = m_LastDisplayCoordinate - positionEvent->GetPointerPositionOnScreen(); + if (m_InvertMoveDirection) { - invertModifier = 1.0; + moveVector *= -1.0; } - // compute translation - BaseRenderer* sender = interactionEvent->GetSender(); - Vector2D moveVector = (positionEvent->GetPointerPositionOnScreen() - m_LastDisplayCoordinate) * invertModifier; 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) { auto* positionEvent = static_cast(interactionEvent); Point3D position = positionEvent->GetPositionInWorld(); // propagate set crosshair event with computed geometry values InvokeEvent(DisplaySetCrosshairEvent(interactionEvent, position)); } void mitk::DisplayActionEventBroadcast::Zoom(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { auto* positionEvent = static_cast(interactionEvent); 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) { auto* positionEvent = static_cast(interactionEvent); - mitk::SliceNavigationController::Pointer sliceNavigationController = interactionEvent->GetSender()->GetSliceNavigationController(); - if (sliceNavigationController) - { - int delta = 0; - - // scroll direction - if (m_ScrollDirection == "updown") - { - delta = static_cast(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]); - } - else - { - delta = static_cast(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]); - } - - if (m_InvertScrollDirection) - { - delta *= -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 (delta > 0 && delta < m_IndexToSliceModifier) - { - delta = m_IndexToSliceModifier; - } - else if (delta < 0 && delta > -m_IndexToSliceModifier) - { - delta = -m_IndexToSliceModifier; - } - delta /= m_IndexToSliceModifier; + int sliceDelta = 0; - int newPosition = sliceNavigationController->GetSlice()->GetPos() + delta; + // scroll direction + if (m_ScrollDirection == "updown") + { + sliceDelta = static_cast(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]); + } + else + { + sliceDelta = static_cast(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]); + } - // if auto repeat is on, start at first slice if you reach the last slice and vice versa - int maxSlices = sliceNavigationController->GetSlice()->GetSteps(); - if (m_AutoRepeat) - { - while (newPosition < 0) - { - newPosition += maxSlices; - } + if (m_InvertScrollDirection) + { + sliceDelta *= -1; + } - while (newPosition >= maxSlices) - { - newPosition -= maxSlices; - } - } - else - { - // if the new slice is below 0 we still show slice 0 - // due to the stepper using unsigned int we have to do this ourselves - if (newPosition < 1) - { - newPosition = 0; - } - } + // 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(); + // store new display coordinates + m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; + m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); - // propagate scroll event with computed geometry values - InvokeEvent(DisplayScrollEvent(interactionEvent, newPosition)); - } + // propagate scroll event with computed geometry values + InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta)); } -void mitk::DisplayActionEventBroadcast::ScrollOneDown(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +void mitk::DisplayActionEventBroadcast::ScrollOneUp(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { - // #TODO: needed here? - mitk::SliceNavigationController::Pointer sliceNavigationController = interactionEvent->GetSender()->GetSliceNavigationController(); - if (sliceNavigationController) + int sliceDelta = 1; + if (m_InvertScrollDirection) { - if (!sliceNavigationController->GetSliceLocked()) - { - mitk::Stepper* stepper = sliceNavigationController->GetSlice(); - if (stepper->GetSteps() <= 1) - { - stepper = sliceNavigationController->GetTime(); - } - stepper->Next(); - } + sliceDelta = -1; } + + // propagate scroll event with a single slice delta (increase) + InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta)); } -void mitk::DisplayActionEventBroadcast::ScrollOneUp(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +void mitk::DisplayActionEventBroadcast::ScrollOneDown(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { - // nothing here; no event sent + int sliceDelta = -1; + if (m_InvertScrollDirection) + { + sliceDelta = 1; + } + // propagate scroll event with a single slice delta (decrease) + InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta)); } void mitk::DisplayActionEventBroadcast::AdjustLevelWindow(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { // nothing here; no event sent } void mitk::DisplayActionEventBroadcast::StartRotation(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { // nothing here; no event sent } void mitk::DisplayActionEventBroadcast::EndRotation(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { // nothing here; no event sent } void mitk::DisplayActionEventBroadcast::Rotate(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { // nothing here; no event sent } void mitk::DisplayActionEventBroadcast::Swivel(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { // nothing here; no event sent } void mitk::DisplayActionEventBroadcast::UpdateStatusbar(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { } bool mitk::DisplayActionEventBroadcast::GetBoolProperty(mitk::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; } } } mitk::DataNode::Pointer mitk::DisplayActionEventBroadcast::GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes, mitk::Point3D worldposition, BaseRenderer* renderer) { // #TODO: see T24173 return nodes->front(); } diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventDefaultFunctions.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp similarity index 68% rename from Modules/Core/src/Interactions/mitkDisplayActionEventDefaultFunctions.cpp rename to Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp index c205962415..07b3907e7c 100644 --- a/Modules/Core/src/Interactions/mitkDisplayActionEventDefaultFunctions.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp @@ -1,100 +1,127 @@ /*=================================================================== 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 "DisplayActionEventDefaultFunctions.h" +#include "mitkDisplayActionEventFunctions.h" + +// mitk core +#include "mitkBaseRenderer.h" +#include "mitkCameraController.h" +#include "mitkDisplayActionEvents.h" +#include "mitkInteractionEvent.h" // itk #include -StdFunctionCommand::ActionFunction mitk::DisplayActionEventDefaultFunctions::DefaultMoveAction() +mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::MoveSenderCameraAction() { - auto actionFunction = [](const itk::EventObject& displayInteractorEvent) + mitk::StdFunctionCommand::ActionFunction actionFunction = [](const itk::EventObject& displayInteractorEvent) { if (DisplayMoveEvent().CheckEvent(&displayInteractorEvent)) { const DisplayMoveEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetInteractionEvent()->GetSender(); // concrete action sendingRenderer->GetCameraController()->MoveBy(displayActionEvent->GetMoveVector()); sendingRenderer->GetRenderingManager()->RequestUpdate(sendingRenderer->GetRenderWindow()); } }; return actionFunction; } -StdFunctionCommand::ActionFunction mitk::DisplayActionEventDefaultFunctions::DefaultSetCrosshairAction() +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->GetInteractionEvent()->GetSender(); // concrete action auto allRenderWindows = sendingRenderer->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : allRenderWindows) { if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sendingRenderer->GetRenderWindow()) { BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->SelectSliceByPoint(displayActionEvent->GetPosition()); } } } }; return actionFunction; } -void mitk::DisplayActionEventDefaultFunctions::DefaultZoomAction() +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->GetInteractionEvent()->GetSender(); // concrete action if (1.0 != displayActionEvent->GetZoomFactor()) { sendingRenderer->GetCameraController()->Zoom(displayActionEvent->GetZoomFactor(), displayActionEvent->GetStartCoordinate()); sendingRenderer->GetRenderingManager()->RequestUpdate(sendingRenderer->GetRenderWindow()); } } }; return actionFunction; } -void mitk::DisplayActionEventDefaultFunctions::DefaultScrollAction() +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->GetInteractionEvent()->GetSender(); // concrete action - sendingRenderer->GetSliceNavigationController()->GetSlice()->SetPos(displayActionEvent->GetPosition()); + 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->MoveSlice(displayActionEvent->GetSliceDelta()); } }; - return; + return actionFunction; } diff --git a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp index acf45cb311..d8eb152114 100644 --- a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp @@ -1,1007 +1,1003 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDisplayInteractor.h" #include "mitkBaseRenderer.h" #include "mitkCameraController.h" #include "mitkInteractionPositionEvent.h" #include "mitkPropertyList.h" #include #include #include // level window #include "mitkLevelWindow.h" #include "mitkLevelWindowProperty.h" #include "mitkLine.h" #include "mitkNodePredicateDataType.h" #include "mitkStandaloneDataStorage.h" #include "vtkRenderWindowInteractor.h" // Rotation #include "mitkInteractionConst.h" #include "rotate_cursor.xpm" #include #include #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include "mitkStatusBar.h" #include #include "mitkDisplayActionEvents.h" void mitk::DisplayInteractor::Notify(InteractionEvent *interactionEvent, bool isHandled) { // to use the state machine pattern, // the event is passed to the state machine interface to be handled if (!isHandled || m_AlwaysReact) { HandleEvent(interactionEvent, nullptr); } } mitk::DisplayInteractor::DisplayInteractor() : m_IndexToSliceModifier(4) , m_AutoRepeat(false) , m_InvertScrollDirection(false) , m_InvertZoomDirection(false) , m_InvertMoveDirection(false) , m_InvertLevelWindowDirection(false) , m_AlwaysReact(false) , m_ZoomFactor(2) , m_LinkPlanes(true) { m_StartCoordinateInMM.Fill(0); m_LastDisplayCoordinate.Fill(0); m_LastCoordinateInMM.Fill(0); m_CurrentDisplayCoordinate.Fill(0); } mitk::DisplayInteractor::~DisplayInteractor() { // nothing here } void mitk::DisplayInteractor::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("ScrollOneDown", ScrollOneDown); CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); 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); } bool mitk::DisplayInteractor::CheckPositionEvent(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) { return false; } return true; } bool mitk::DisplayInteractor::CheckRotationPossible(const mitk::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 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 auto *posEvent = dynamic_cast(interactionEvent); if (posEvent == nullptr) return false; BaseRenderer *clickedRenderer = posEvent->GetSender(); const PlaneGeometry *ourViewportGeometry = (clickedRenderer->GetCurrentWorldPlaneGeometry()); if (!ourViewportGeometry) return false; Point3D cursorPosition = posEvent->GetPositionInWorld(); const auto spacing = ourViewportGeometry->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 double threshholdDistancePixels = 12.0; auto renWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; const PlaneGeometry *otherRenderersRenderPlane = snc->GetCurrentPlaneGeometry(); if (otherRenderersRenderPlane == nullptr) continue; // ignore, we don't see a plane // 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 } // check distance from intersection line const double distanceFromIntersectionLine = intersectionLine.Distance(cursorPosition) / spacing[snc->GetDefaultViewDirection()]; // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { 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 if (m_LinkPlanes) { m_SNCsToBeRotated.push_back(snc); } } else // close to cursor { if (geometryToBeRotated == nullptr) // first one close to the cursor { geometryToBeRotated = otherRenderersRenderPlane; 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()) < mitk::eps) { m_SNCsToBeRotated.push_back(snc); } else { hitMultipleLines = true; } } } } bool moveSlices(true); if (geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !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 DO have enough information for rotation m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project( cursorPosition); // remember where the last cursor position ON THE LINE has been observed if (anyOtherGeometry->IntersectionPoint( intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) // find center of rotation by intersection with any of the OTHER lines { return true; } else { return false; } } return false; } bool mitk::DisplayInteractor::CheckSwivelPossible(const mitk::InteractionEvent *interactionEvent) { const ScalarType ThresholdDistancePixels = 6.0; // 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 *posEvent = dynamic_cast(interactionEvent); BaseRenderer *renderer = interactionEvent->GetSender(); if (!posEvent || !renderer) return false; const Point3D &cursor = posEvent->GetPositionInWorld(); m_SNCsToBeRotated.clear(); const PlaneGeometry *clickedGeometry(nullptr); const PlaneGeometry *otherGeometry1(nullptr); const PlaneGeometry *otherGeometry2(nullptr); auto renWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; // unsigned int slice = (*iter)->GetSlice()->GetPos(); // unsigned int time = (*iter)->GetTime()->GetPos(); const PlaneGeometry *planeGeometry = snc->GetCurrentPlaneGeometry(); if (!planeGeometry) continue; if (snc == renderer->GetSliceNavigationController()) { clickedGeometry = planeGeometry; m_SNCsToBeRotated.push_back(snc); } else { if (otherGeometry1 == nullptr) { otherGeometry1 = planeGeometry; } else { otherGeometry2 = planeGeometry; } if (m_LinkPlanes) { // If planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } } mitk::Line3D line; mitk::Point3D point; if ((clickedGeometry != nullptr) && (otherGeometry1 != nullptr) && (otherGeometry2 != nullptr) && clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point)) { m_CenterOfRotation = point; if (m_CenterOfRotation.EuclideanDistanceTo(cursor) < ThresholdDistancePixels) { return false; } else { m_ReferenceCursor = posEvent->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::DisplayInteractor::Init(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_CurrentDisplayCoordinate = m_LastDisplayCoordinate; positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM); m_LastCoordinateInMM = m_StartCoordinateInMM; } void mitk::DisplayInteractor::Move(StateMachineAction *, InteractionEvent *interactionEvent) { BaseRenderer *sender = interactionEvent->GetSender(); auto *positionEvent = static_cast(interactionEvent); float invertModifier = -1.0; if (m_InvertMoveDirection) { invertModifier = 1.0; } // perform translation Vector2D moveVector = (positionEvent->GetPointerPositionOnScreen() - m_LastDisplayCoordinate) * invertModifier; moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); sender->GetCameraController()->MoveBy(moveVector); sender->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); /* auto renWindows = sender->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D) { BaseRenderer::GetInstance(renWin)->GetCameraController()->MoveBy(moveVector); BaseRenderer::GetInstance(renWin)->GetRenderingManager()->RequestUpdate(renWin); } } */ m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } void mitk::DisplayInteractor::SetCrosshair(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { auto* positionEvent = static_cast(interactionEvent); Point3D pos = positionEvent->GetPositionInWorld(); const BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto renWindows = sender->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sender->GetRenderWindow()) { BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->SelectSliceByPoint(pos); } } } void mitk::DisplayInteractor::Zoom(StateMachineAction *, InteractionEvent *interactionEvent) { 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; } auto* positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); if (factor != 1.0) { const BaseRenderer::Pointer sender = interactionEvent->GetSender(); sender->GetCameraController()->Zoom(factor, m_StartCoordinateInMM); sender->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); //InvokeEvent(DisplayZoomEvent(interactionEvent, factor, m_StartCoordinateInMM)); /* auto renWindows = sender->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sender->GetRenderWindow()) { BaseRenderer::GetInstance(renWin)->GetCameraController()->Zoom(factor, m_StartCoordinateInMM); BaseRenderer::GetInstance(renWin)->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); } } */ } } void mitk::DisplayInteractor::Scroll(StateMachineAction *, InteractionEvent *interactionEvent) { auto* positionEvent = static_cast(interactionEvent); mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (sliceNaviController) { int delta = 0; // Scrolling direction if (m_ScrollDirection == "updown") { delta = static_cast(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]); } else { delta = static_cast(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]); } if (m_InvertScrollDirection) { delta *= -1; } // Set how many pixels the mouse has to be moved to scroll one slice // if we moved less than 'm_IndexToSliceModifier' pixels slice ONE slice only if (delta > 0 && delta < m_IndexToSliceModifier) { delta = m_IndexToSliceModifier; } else if (delta < 0 && delta > -m_IndexToSliceModifier) { delta = -m_IndexToSliceModifier; } delta /= m_IndexToSliceModifier; int newPos = sliceNaviController->GetSlice()->GetPos() + delta; // if auto repeat is on, start at first slice if you reach the last slice and vice versa int maxSlices = sliceNaviController->GetSlice()->GetSteps(); if (m_AutoRepeat) { while (newPos < 0) { newPos += maxSlices; } while (newPos >= maxSlices) { newPos -= maxSlices; } } else { // if the new slice is below 0 we still show slice 0 // due to the stepper using unsigned int we have to do this ourselves if (newPos < 1) { newPos = 0; } } m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // set the new position sliceNaviController->GetSlice()->SetPos(newPos); - // #TODO: why not - // sliceNaviController->SelectSliceByPoint(pos); /* const BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto renWindows = sender->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sender->GetRenderWindow()) { BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->GetSlice()->SetPos(newPos); - // #TODO: why not - // BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->SelectSliceByPoint(pos); } } */ } } void mitk::DisplayInteractor::ScrollOneDown(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper *stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Next(); } } void mitk::DisplayInteractor::ScrollOneUp(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper *stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Previous(); } } void mitk::DisplayInteractor::AdjustLevelWindow(StateMachineAction *, InteractionEvent *interactionEvent) { BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto *positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // search for active image mitk::DataStorage::Pointer storage = sender->GetDataStorage(); mitk::DataNode::Pointer node = nullptr; mitk::DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(mitk::NodePredicateDataType::New("Image")); for (unsigned int i = 0; i < allImageNodes->size(); ++i) { bool isActiveImage = false; bool propFound = allImageNodes->at(i)->GetBoolProperty("imageForLevelWindow", isActiveImage); if (propFound && isActiveImage) { node = allImageNodes->at(i); continue; } } if (node.IsNull()) { node = storage->GetNode(mitk::NodePredicateDataType::New("Image")); } if (node.IsNull()) { return; } mitk::LevelWindow lv = mitk::LevelWindow(); node->GetLevelWindow(lv); ScalarType level = lv.GetLevel(); ScalarType window = lv.GetWindow(); int levelIndex = 0; int windowIndex = 1; if (m_LevelDirection != "leftright") { levelIndex = 1; windowIndex = 0; } int directionModifier = 1; if (m_InvertLevelWindowDirection) { directionModifier = -1; } // calculate adjustments from mouse movements level += (m_CurrentDisplayCoordinate[levelIndex] - m_LastDisplayCoordinate[levelIndex]) * static_cast(2) * directionModifier; window += (m_CurrentDisplayCoordinate[windowIndex] - m_LastDisplayCoordinate[windowIndex]) * static_cast(2) * directionModifier; lv.SetLevelWindow(level, window); dynamic_cast(node->GetProperty("levelwindow"))->SetLevelWindow(lv); sender->GetRenderingManager()->RequestUpdateAll(); } void mitk::DisplayInteractor::StartRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->SetMouseCursor(rotate_cursor_xpm, 0, 0); } void mitk::DisplayInteractor::EndRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->ResetMouseCursor(); } void mitk::DisplayInteractor::Rotate(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto *posEvent = dynamic_cast(event); if (posEvent == nullptr) return; Point3D cursor = posEvent->GetPositionInWorld(); 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 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); // iterate the OTHER slice navigation controllers: these are filled in DoDecideBetweenRotationAndSliceSelection for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&rotationOperation); (*iter)->SendCreatedWorldGeometryUpdate(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayInteractor::Swivel(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto *posEvent = dynamic_cast(event); if (!posEvent) return; // Determine relative mouse movement projected onto world space Point2D cursor = posEvent->GetPointerPositionOnScreen(); Vector2D relativeCursor = cursor - m_ReferenceCursor; Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1]; // Determine rotation axis (perpendicular to rotation plane and cursor // movement) Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis); ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0; // Restore the initial plane pose by undoing the previous rotation // operation RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle); SNCVector::iterator iter; for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!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 (!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::DisplayInteractor::UpdateStatusbar(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto *posEvent = dynamic_cast(event); if (!posEvent) return; std::string statusText; TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = posEvent->GetSender()->GetDataStorage()->GetSubset(isImageData).GetPointer(); // posEvent->GetPositionInWorld() would return the world position at the // time of initiating the interaction. However, we need to update the // status bar with the position after changing slice. Therefore, we // translate the same display position with the renderer again to // get the new world position. Point3D worldposition; event->GetSender()->DisplayToWorld(posEvent->GetPointerPositionOnScreen(), worldposition); mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; int component = 0; node = this->GetTopLayerNode(nodes, worldposition, posEvent->GetSender()); if (node.IsNotNull()) { bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = posEvent->GetSender()->GetDataStorage()->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = this->GetTopLayerNode(sourcenodes, worldposition, posEvent->GetSender()); } 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 gray 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, posEvent->GetSender()->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, posEvent->GetSender()->GetTime(), pixelValue.c_str()); } else { mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(posEvent->GetSender()->GetTimeStep()), p, pixelValue, component); statusBar->DisplayImageInfo(worldposition, p, posEvent->GetSender()->GetTime(), pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } void mitk::DisplayInteractor::ConfigurationChanged() { mitk::PropertyList::Pointer properties = GetAttributes(); // auto repeat std::string strAutoRepeat = ""; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } else { m_AutoRepeat = false; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } else { m_IndexToSliceModifier = 4; } // 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 = ""; if (properties->GetStringProperty("coupled", strCoupled)) { if (strCoupled == "true") m_LinkPlanes = true; else m_LinkPlanes = false; } // 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); } // allwaysReact std::string strAlwaysReact = ""; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } else { m_AlwaysReact = false; } } else { m_AlwaysReact = false; } } bool mitk::DisplayInteractor::FilterEvents(InteractionEvent *interactionEvent, DataNode * /*dataNode*/) { if (interactionEvent->GetSender() == nullptr) return false; if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D) return false; return true; } bool mitk::DisplayInteractor::GetBoolProperty(mitk::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; } } } mitk::DataNode::Pointer mitk::DisplayInteractor::GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes, mitk::Point3D worldposition, BaseRenderer *ren) { mitk::DataNode::Pointer node; if (nodes.IsNotNull()) { int maxlayer = -32768; bool isHelper(false); for (unsigned int x = 0; x < nodes->size(); x++) { nodes->at(x)->GetBoolProperty("helper object", isHelper); if (nodes->at(x)->GetData()->GetGeometry()->IsInside(worldposition) && isHelper == false) { int layer = 0; if (!(nodes->at(x)->GetIntProperty("layer", layer))) continue; if (layer > maxlayer) { if (static_cast(nodes->at(x))->IsVisible(ren)) { node = nodes->at(x); maxlayer = layer; } } } } } return node; } diff --git a/Modules/Core/src/Interactions/mitkStdDisplayActionEventHandler.cpp b/Modules/Core/src/Interactions/mitkStdDisplayActionEventHandler.cpp index 22452c5b7e..06eea8b3bb 100644 --- a/Modules/Core/src/Interactions/mitkStdDisplayActionEventHandler.cpp +++ b/Modules/Core/src/Interactions/mitkStdDisplayActionEventHandler.cpp @@ -1,44 +1,44 @@ /*=================================================================== 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 "mitkDisplayActionEventDefaultFunctions.h" +#include "mitkDisplayActionEventFunctions.h" // itk #include void mitk::StdDisplayActionEventHandler::InitStdActions() { // #TODO: change function call for new mitk::WeakPointer if (m_ObservableBroadcast.IsNull()) { mitkThrow() << "No display action event broadcast class set to observe. Use 'SetObservableBroadcast' before initializing actions."; } - StdFunctionCommand::ActionFunction actionFunction = DisplayActionEventDefaultFunctions::DefaultMoveAction(); + StdFunctionCommand::ActionFunction actionFunction = DisplayActionEventFunctions::MoveSenderCameraAction(); ConnectDisplayActionEvent(DisplayMoveEvent(nullptr, Vector2D()), actionFunction); - StdFunctionCommand::ActionFunction actionFunction = DisplayActionEventDefaultFunctions::DefaultSetCrosshairAction(); + actionFunction = DisplayActionEventFunctions::SetCrosshairAction(); ConnectDisplayActionEvent(DisplaySetCrosshairEvent(nullptr, Point3D()), actionFunction); - StdFunctionCommand::ActionFunction actionFunction = DisplayActionEventDefaultFunctions::DefaultZoomAction(); + actionFunction = DisplayActionEventFunctions::ZoomSenderCameraAction(); ConnectDisplayActionEvent(DisplayZoomEvent(nullptr, 0.0, Point2D()), actionFunction); - StdFunctionCommand::ActionFunction actionFunction = DisplayActionEventDefaultFunctions::DefaultScrollAction(); + actionFunction = DisplayActionEventFunctions::ScrollSliceStepperAction(); ConnectDisplayActionEvent(DisplayScrollEvent(nullptr, 0), actionFunction); } diff --git a/Modules/QtWidgets/include/QmitkCustomMultiWidget.h b/Modules/QtWidgets/include/QmitkCustomMultiWidget.h index fd25c8f8fc..56a93600ce 100644 --- a/Modules/QtWidgets/include/QmitkCustomMultiWidget.h +++ b/Modules/QtWidgets/include/QmitkCustomMultiWidget.h @@ -1,224 +1,222 @@ /*=================================================================== 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 QMITKCUSTOMMULTIWIDGET_H #define QMITKCUSTOMMULTIWIDGET_H // qt widgets module #include "MitkQtWidgetsExports.h" #include "QmitkRenderWindowWidget.h" // mitk core #include -#include #include #include #include #include // qt #include class QHBoxLayout; class QVBoxLayout; class QGridLayout; class QSpacerItem; class QmitkLevelWindowWidget; class QmitkRenderWindow; class vtkCornerAnnotation; namespace mitk { class RenderingManager; } /** * @brief The 'QmitkCustomMultiWidget' is a 'QWidget' that is used to display multiple render windows at once. * * Render windows can dynamically be added and removed to change the layout of the multi widget. */ class MITKQTWIDGETS_EXPORT QmitkCustomMultiWidget : public QWidget { Q_OBJECT public: QmitkCustomMultiWidget(QWidget* parent = 0, Qt::WindowFlags f = 0, mitk::RenderingManager* renderingManager = nullptr, mitk::BaseRenderer::RenderingMode::Type renderingMode = mitk::BaseRenderer::RenderingMode::Standard, const QString& multiWidgetName = "custommulti"); virtual ~QmitkCustomMultiWidget(); void SetDataStorage(mitk::DataStorage* dataStorage); void InitRenderWindowWidgets(); using RenderWindowWidgetMap = std::map; using RenderWindowHash = QHash; RenderWindowWidgetMap GetRenderWindowWidgets() const; QmitkRenderWindowWidget* GetRenderWindowWidget(const QString& widgetID) const; QmitkRenderWindow* GetRenderWindow(const QString& widgetID) const; QmitkRenderWindowWidget* GetActiveRenderWindowWidget() const; QmitkRenderWindowWidget* GetFirstRenderWindowWidget() const; QmitkRenderWindowWidget* GetLastRenderWindowWidget() const; unsigned int GetNumberOfRenderWindowWidgets() const; void RequestUpdate(const QString& widgetID); void RequestUpdateAll(); void ForceImmediateUpdate(const QString& widgetID); void ForceImmediateUpdateAll(); mitk::MouseModeSwitcher* GetMouseModeSwitcher(); const mitk::Point3D GetCrossPosition(const QString& widgetID) const; public slots: void ShowLevelWindowWidget(const QString& widgetID, bool show); void ShowAllLevelWindowWidgets(bool show); /** * @brief Set a background color gradient for a specific render window. * * If two different input colors are used, a gradient background is generated. * * @param upper The color of the gradient background. * @param lower The color of the gradient background. * @param widgetID The widget identifier. */ void SetBackgroundColorGradient(const mitk::Color& upper, const mitk::Color& lower, const QString& widgetID); /** * @brief Set a background color gradient for all available render windows. * * If two different input colors are used, a gradient background is generated. * * @param upper The color of the gradient background. * @param lower The color of the gradient background. */ // #TODO: 'backgroundgradientcolor' void SetAllBackgroundColorGradients(const mitk::Color& upper, const mitk::Color& lower); void FillAllBackgroundColorGradientsWithBlack(); void ShowBackgroundColorGradient(const QString& widgetID, bool show); void ShowAllBackgroundColorGradients(bool show); /** * @rief Return a render window (widget) specific background color gradient * * @param widgetID The widget identifier. * * @return A color gradient as a pair of colors. * First entry: upper color value * Second entry: lower color value */ std::pair GetBackgroundColorGradient(const QString& widgetID) const; bool GetBackgroundColorGradientFlag(const QString& widgetID) const; void SetDepartmentLogoPath(const char* path); void ShowDepartmentLogo(const QString& widgetID, bool show); void ShowAllDepartmentLogos(bool show); void SetDecorationColor(const QString& widgetID, const mitk::Color& color); mitk::Color GetDecorationColor(const QString& widgetID) const; void ShowColoredRectangle(const QString& widgetID, bool show); void ShowAllColoredRectangles(bool show); bool IsColoredRectangleVisible(const QString& widgetID) const; void ShowCornerAnnotation(const QString& widgetID, bool show); void ShowAllCornerAnnotations(bool show); bool IsCornerAnnotationVisible(const QString& widgetID) const; void SetCornerAnnotationText(const QString& widgetID, const std::string& cornerAnnotation); std::string GetCornerAnnotationText(const QString& widgetID) const; /** * @brief Listener to the CrosshairPositionEvent * * Ensures the CrosshairPositionEvent is handled only once and at the end of the Qt-Event loop */ void HandleCrosshairPositionEvent(); /** * @brief Receives the signal from HandleCrosshairPositionEvent, executes the StatusBar update * */ void HandleCrosshairPositionEventDelayed(); void Fit(); void EnsureDisplayContainsPoint(mitk::BaseRenderer *renderer, const mitk::Point3D &p); void MoveCrossToPosition(const QString& widgetID, const mitk::Point3D& newPosition); void ResetCrosshair(); // mouse events void wheelEvent(QWheelEvent* e) override; void mousePressEvent(QMouseEvent* e) override; void moveEvent(QMoveEvent* e) override; signals: void WheelMoved(QWheelEvent *); void Moved(); public: enum { AXIAL, SAGITTAL, CORONAL, THREE_D }; private: void InitializeGUI(); void InitializeWidget(); void InitializeDisplayActionEventHandling(); void AddRenderWindowWidget(int column, int row, const std::string& cornerAnnotation = ""); // #TODO: see T24173 mitk::DataNode::Pointer GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes); QGridLayout* m_CustomMultiWidgetLayout; RenderWindowWidgetMap m_RenderWindowWidgets; QmitkRenderWindowWidget* m_ActiveRenderWindowWidget; int m_PlaneMode; mitk::RenderingManager* m_RenderingManager; mitk::BaseRenderer::RenderingMode::Type m_RenderingMode; QString m_MultiWidgetName; mitk::MouseModeSwitcher::Pointer m_MouseModeSwitcher; mitk::SliceNavigationController* m_TimeNavigationController; mitk::DisplayActionEventBroadcast::Pointer m_DisplayActionEventBroadcast; - std::unique_ptr m_CustomDisplayActionEventHandler; std::unique_ptr m_StdDisplayActionEventHandler; mitk::DataStorage::Pointer m_DataStorage; bool m_PendingCrosshairPositionEvent; bool m_CrosshairNavigationEnabled; }; #endif // QMITKCUSTOMMULTIWIDGET_H diff --git a/Modules/QtWidgets/src/QmitkCustomMultiWidget.cpp b/Modules/QtWidgets/src/QmitkCustomMultiWidget.cpp index fb0ea067f6..984ebf7400 100644 --- a/Modules/QtWidgets/src/QmitkCustomMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkCustomMultiWidget.cpp @@ -1,753 +1,750 @@ /*=================================================================== 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 "QmitkCustomMultiWidget.h" #include #include #include #include // mitk core #include #include #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // qt #include QmitkCustomMultiWidget::QmitkCustomMultiWidget(QWidget* parent, Qt::WindowFlags f/* = 0*/, mitk::RenderingManager* renderingManager/* = nullptr*/, mitk::BaseRenderer::RenderingMode::Type renderingMode/* = mitk::BaseRenderer::RenderingMode::Standard*/, const QString& multiWidgetName/* = "custommulti"*/) : QWidget(parent, f) , m_CustomMultiWidgetLayout(nullptr) , m_RenderingManager(renderingManager) , m_RenderingMode(renderingMode) , m_MultiWidgetName(multiWidgetName) , m_PendingCrosshairPositionEvent(false) , m_CrosshairNavigationEnabled(false) , m_DisplayActionEventBroadcast(nullptr) - , m_CustomDisplayActionEventHandler(nullptr) , m_StdDisplayActionEventHandler(nullptr) { // create widget manually // create and set layout InitializeGUI(); resize(QSize(364, 477).expandedTo(minimumSizeHint())); InitializeWidget(); InitializeDisplayActionEventHandling(); } QmitkCustomMultiWidget::~QmitkCustomMultiWidget() { // nothing here } void QmitkCustomMultiWidget::SetDataStorage(mitk::DataStorage* dataStorage) { if (dataStorage == m_DataStorage) { return; } m_DataStorage = dataStorage; for (const auto& renderWindowWidget : m_RenderWindowWidgets) { renderWindowWidget.second->SetDataStorage(m_DataStorage); } } void QmitkCustomMultiWidget::InitRenderWindowWidgets() { // create three render window (widgets) initially AddRenderWindowWidget(0, 0, "2015-01-14 - CT"); AddRenderWindowWidget(0, 1, "2015-01-14 - MR"); AddRenderWindowWidget(1, 0, "2015-08-19 - CT"); AddRenderWindowWidget(1, 1, "2015-08-19 - MR"); AddRenderWindowWidget(2, 0, "2016-06-29 - CT"); AddRenderWindowWidget(2, 1, "2016-06-29 - MR"); } QmitkCustomMultiWidget::RenderWindowWidgetMap QmitkCustomMultiWidget::GetRenderWindowWidgets() const { return m_RenderWindowWidgets; } QmitkRenderWindowWidget* QmitkCustomMultiWidget::GetRenderWindowWidget(const QString& widgetID) const { RenderWindowWidgetMap::const_iterator it = m_RenderWindowWidgets.find(widgetID); if (it == m_RenderWindowWidgets.end()) { return nullptr; } return it->second; } QmitkRenderWindow* QmitkCustomMultiWidget::GetRenderWindow(const QString& widgetID) const { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { return renderWindowWidget->GetRenderWindow(); } return nullptr; } QmitkRenderWindowWidget* QmitkCustomMultiWidget::GetActiveRenderWindowWidget() const { //return m_ActiveRenderWindowWidget; return m_RenderWindowWidgets.begin()->second; } QmitkRenderWindowWidget* QmitkCustomMultiWidget::GetFirstRenderWindowWidget() const { return m_RenderWindowWidgets.begin()->second; } QmitkRenderWindowWidget* QmitkCustomMultiWidget::GetLastRenderWindowWidget() const { return m_RenderWindowWidgets.rbegin()->second; } unsigned int QmitkCustomMultiWidget::GetNumberOfRenderWindowWidgets() const { return m_RenderWindowWidgets.size(); } void QmitkCustomMultiWidget::RequestUpdate(const QString& widgetID) { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { return renderWindowWidget->RequestUpdate(); } } void QmitkCustomMultiWidget::RequestUpdateAll() { // #TODO: Update only render windows that show the same image? // #TODO: Update only type specific render windows (2D / 3D)? for (const auto& renderWindowWidget : m_RenderWindowWidgets) { renderWindowWidget.second->RequestUpdate(); } } void QmitkCustomMultiWidget::ForceImmediateUpdate(const QString& widgetID) { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { renderWindowWidget->ForceImmediateUpdate(); } } void QmitkCustomMultiWidget::ForceImmediateUpdateAll() { // #TODO: Update only render windows that show the same image? // #TODO: Update only type specific render windows (2D / 3D)? for (const auto& renderWindowWidget : m_RenderWindowWidgets) { renderWindowWidget.second->ForceImmediateUpdate(); } } mitk::MouseModeSwitcher* QmitkCustomMultiWidget::GetMouseModeSwitcher() { return m_MouseModeSwitcher; } const mitk::Point3D QmitkCustomMultiWidget::GetCrossPosition(const QString& widgetID) const { /* const mitk::PlaneGeometry *plane1 = mitkWidget1->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry *plane2 = mitkWidget2->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry *plane3 = mitkWidget3->GetSliceNavigationController()->GetCurrentPlaneGeometry(); mitk::Line3D line; if ((plane1 != NULL) && (plane2 != NULL) && (plane1->IntersectionLine(plane2, line))) { mitk::Point3D point; if ((plane3 != NULL) && (plane3->IntersectionPoint(line, point))) { return point; } } // TODO BUG POSITIONTRACKER; mitk::Point3D p; return p; // return m_LastLeftClickPositionSupplier->GetCurrentPoint(); */ return mitk::Point3D(); } ////////////////////////////////////////////////////////////////////////// // PUBLIC SLOTS ////////////////////////////////////////////////////////////////////////// void QmitkCustomMultiWidget::ShowLevelWindowWidget(const QString& widgetID, bool show) { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { renderWindowWidget->ShowLevelWindowWidget(show); return; } MITK_ERROR << "Level window widget can not be shown for an unknown widget."; } void QmitkCustomMultiWidget::ShowAllLevelWindowWidgets(bool show) { for (const auto& renderWindowWidget : m_RenderWindowWidgets) { renderWindowWidget.second->ShowLevelWindowWidget(show); } } void QmitkCustomMultiWidget::SetBackgroundColorGradient(const mitk::Color& upper, const mitk::Color& lower, const QString& widgetID) { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { renderWindowWidget->SetBackgroundColorGradient(upper, lower); return; } MITK_ERROR << "Background color gradient can not be set for an unknown widget."; } void QmitkCustomMultiWidget::SetAllBackgroundColorGradients(const mitk::Color& upper, const mitk::Color& lower) { for (const auto& renderWindowWidget : m_RenderWindowWidgets) { renderWindowWidget.second->SetBackgroundColorGradient(upper, lower); } } void QmitkCustomMultiWidget::FillAllBackgroundColorGradientsWithBlack() { float black[3] = { 0.0f, 0.0f, 0.0f }; SetAllBackgroundColorGradients(black, black); } void QmitkCustomMultiWidget::ShowBackgroundColorGradient(const QString& widgetID, bool show) { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { renderWindowWidget->ShowBackgroundColorGradient(show); return; } MITK_ERROR << "Background color gradient can not be shown for an unknown widget."; } void QmitkCustomMultiWidget::ShowAllBackgroundColorGradients(bool show) { for (const auto& renderWindowWidget : m_RenderWindowWidgets) { renderWindowWidget.second->ShowBackgroundColorGradient(show); } } std::pair QmitkCustomMultiWidget::GetBackgroundColorGradient(const QString& widgetID) const { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { return renderWindowWidget->GetRendererBackgroundColorGradient(); } MITK_ERROR << "Background color gradient can not be retrieved for an unknown widget. Returning black color pair."; float black[3] = { 0.0f, 0.0f, 0.0f }; return std::make_pair(mitk::Color(black), mitk::Color(black)); } bool QmitkCustomMultiWidget::GetBackgroundColorGradientFlag(const QString& widgetID) const { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { return renderWindowWidget->GetBackgroundColorGradientFlag(); } MITK_ERROR << "Background color gradient flag can not be retrieved for an unknown widget. Returning 'false'."; return false; } void QmitkCustomMultiWidget::SetDepartmentLogoPath(const char* path) { /* old m_LogoRendering->SetLogoImagePath(path); mitk::BaseRenderer *renderer = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow()); m_LogoRendering->Update(renderer); RequestUpdate(); */ /* new QImage* qimage = new QImage(path); vtkSmartPointer qImageToVtk; qImageToVtk = vtkSmartPointer::New(); qImageToVtk->SetQImage(qimage); qImageToVtk->Update(); m_LogoRendering->SetLogoImage(qImageToVtk->GetOutput()); mitk::BaseRenderer *renderer = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow()); m_LogoRendering->Update(renderer); RequestUpdate(); */ } void QmitkCustomMultiWidget::ShowDepartmentLogo(const QString& widgetID, bool show) { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { GetRenderWindowWidget(widgetID)->ShowDepartmentLogo(show); RequestUpdate(widgetID); } MITK_ERROR << "Department logo can not be shown for an unknown widget."; return; } void QmitkCustomMultiWidget::ShowAllDepartmentLogos(bool show) { for (const auto& renderWindowWidget : m_RenderWindowWidgets) { renderWindowWidget.second->ShowDepartmentLogo(show); } } void QmitkCustomMultiWidget::SetDecorationColor(const QString& widgetID, const mitk::Color& color) { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { renderWindowWidget->SetDecorationColor(color); } MITK_ERROR << "Decoration color can not be set for an unknown widget."; } mitk::Color QmitkCustomMultiWidget::GetDecorationColor(const QString& widgetID) const { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { renderWindowWidget->GetDecorationColor(); } MITK_ERROR << "Decoration color can not be retrieved for an unknown widget. Returning black color!"; float black[3] = { 0.0f, 0.0f, 0.0f }; return mitk::Color(black); } void QmitkCustomMultiWidget::ShowColoredRectangle(const QString& widgetID, bool show) { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { renderWindowWidget->ShowColoredRectangle(show); } MITK_ERROR << "Colored rectangle can not be set for an unknown widget."; } void QmitkCustomMultiWidget::ShowAllColoredRectangles(bool show) { for (const auto& renderWindowWidget : m_RenderWindowWidgets) { renderWindowWidget.second->ShowColoredRectangle(show); } } bool QmitkCustomMultiWidget::IsColoredRectangleVisible(const QString& widgetID) const { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { renderWindowWidget->IsColoredRectangleVisible(); } MITK_ERROR << "Colored rectangle visibility can not be retrieved for an unknown widget. Returning 'false'."; return false; } void QmitkCustomMultiWidget::SetCornerAnnotationText(const QString& widgetID, const std::string& cornerAnnotation) { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { renderWindowWidget->SetCornerAnnotationText(cornerAnnotation); } MITK_ERROR << "Corner annotation text can not be retrieved for an unknown widget."; } std::string QmitkCustomMultiWidget::GetCornerAnnotationText(const QString& widgetID) const { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { renderWindowWidget->GetCornerAnnotationText(); } MITK_ERROR << "Corner annotation text can not be retrieved for an unknown widget."; return ""; } void QmitkCustomMultiWidget::ShowCornerAnnotation(const QString& widgetID, bool show) { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { renderWindowWidget->ShowCornerAnnotation(show); } MITK_ERROR << "Corner annotation can not be set for an unknown widget."; } void QmitkCustomMultiWidget::ShowAllCornerAnnotations(bool show) { for (const auto& renderWindowWidget : m_RenderWindowWidgets) { renderWindowWidget.second->ShowCornerAnnotation(show); } } bool QmitkCustomMultiWidget::IsCornerAnnotationVisible(const QString& widgetID) const { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { renderWindowWidget->IsCornerAnnotationVisible(); } MITK_ERROR << "Corner annotation visibility can not be retrieved for an unknown widget. Returning 'false'."; return false; } void QmitkCustomMultiWidget::HandleCrosshairPositionEvent() { /* if (!m_PendingCrosshairPositionEvent) { m_PendingCrosshairPositionEvent = true; QTimer::singleShot(0, this, SLOT(HandleCrosshairPositionEventDelayed())); } */ } void QmitkCustomMultiWidget::HandleCrosshairPositionEventDelayed() { /* m_PendingCrosshairPositionEvent = false; // find image with highest layer mitk::TNodePredicateDataType::Pointer isImageData = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->m_DataStorage->GetSubset(isImageData).GetPointer(); mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; mitk::Image::Pointer image; bool isBinary = false; node = this->GetTopLayerNode(nodes); int component = 0; if (node.IsNotNull()) { node->GetBoolProperty("binary", isBinary); if (isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = m_DataStorage->GetSources(node, NULL, true); if (!sourcenodes->empty()) { topSourceNode = this->GetTopLayerNode(sourcenodes); } 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); } } mitk::Point3D crosshairPos = this->GetCrossPosition(); std::string statusText; std::stringstream stream; itk::Index<3> p; mitk::BaseRenderer *baseRenderer = GetRenderWindow()->GetSliceNavigationController()->GetRenderer(); 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(baseRenderer->GetTimeStep()), p, pixelValue, component); if (fabs(pixelValue) > 1000000 || fabs(pixelValue) < 0.01) { stream << "; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: " << std::scientific << pixelValue << " "; } else { stream << "; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: " << pixelValue << " "; } } else { stream << "No image information at this position!"; } statusText = stream.str(); mitk::StatusBar::GetInstance()->DisplayGreyValueText(statusText.c_str()); */ } void QmitkCustomMultiWidget::Fit() { // #TODO: what is this function's purpose? /* vtkSmartPointer vtkrenderer; size_t numberOfRenderWindowWidgets = m_RenderWindowWidgets.size(); for (size_t i = 0; i < numberOfRenderWindowWidgets; ++i) { vtkRenderer* renderer = GetRenderWindow(widgetID)->GetRenderer()->GetVtkRenderer(); mitk::BaseRenderer* baseRenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow(i)->GetRenderWindow()); vtkrenderer = baseRenderer->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } baseRenderer->GetCameraController()->Fit(); } int w = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); vtkObject::SetGlobalWarningDisplay(w); */ } void QmitkCustomMultiWidget::EnsureDisplayContainsPoint(mitk::BaseRenderer *renderer, const mitk::Point3D &p) { // #TODO: what is this function's purpose? /* 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 QmitkCustomMultiWidget::MoveCrossToPosition(const QString& widgetID, const mitk::Point3D& newPosition) { QmitkRenderWindowWidget* renderWindowWidget = GetRenderWindowWidget(widgetID); if (nullptr != renderWindowWidget) { GetRenderWindowWidget(widgetID)->GetSliceNavigationController()->SelectSliceByPoint(newPosition); GetRenderWindowWidget(widgetID)->RequestUpdate(); } MITK_ERROR << "Geometry plane can not be shown for an unknown widget."; } void QmitkCustomMultiWidget::ResetCrosshair() { // #TODO: new concept: we do not want to initialize all views; // we do not want to rely on the geometry planes /* if (m_DataStorage.IsNotNull()) { m_RenderingManager->InitializeViewsByBoundingObjects(m_DataStorage); // m_RenderingManager->InitializeViews( m_DataStorage->ComputeVisibleBoundingGeometry3D() ); // reset interactor to normal slicing SetWidgetPlaneMode(PLANE_MODE_SLICING); } */ } ////////////////////////////////////////////////////////////////////////// // MOUSE EVENTS ////////////////////////////////////////////////////////////////////////// void QmitkCustomMultiWidget::wheelEvent(QWheelEvent* e) { emit WheelMoved(e); } void QmitkCustomMultiWidget::mousePressEvent(QMouseEvent* e) { } void QmitkCustomMultiWidget::moveEvent(QMoveEvent* e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the overlays as the StdMultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkCustomMultiWidget::InitializeGUI() { m_CustomMultiWidgetLayout = new QGridLayout(this); m_CustomMultiWidgetLayout->setContentsMargins(0, 0, 0, 0); setLayout(m_CustomMultiWidgetLayout); } void QmitkCustomMultiWidget::InitializeWidget() { // #TODO: some things have to be handled globally (hold for all render window (widgets) // analyse those things and design a controlling mechanism // necessary here? mouse mode is valid for all render windows (and also used in editor) - m_MouseModeSwitcher = mitk::MouseModeSwitcher::New(); - m_MouseModeSwitcher->SetInteractionScheme(mitk::MouseModeSwitcher::InteractionScheme::MITK); + //m_MouseModeSwitcher = mitk::MouseModeSwitcher::New(); + //m_MouseModeSwitcher->SetInteractionScheme(mitk::MouseModeSwitcher::InteractionScheme::MITK); // setup the department logo rendering /* m_LogoRendering = mitk::LogoOverlay::New(); mitk::BaseRenderer::Pointer renderer4 = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow()); m_LogoRendering->SetOpacity(0.5); mitk::Point2D offset; offset.Fill(0.03); m_LogoRendering->SetOffsetVector(offset); m_LogoRendering->SetRelativeSize(0.15); m_LogoRendering->SetCornerPosition(1); m_LogoRendering->SetLogoImagePath("DefaultLogo"); renderer4->GetOverlayManager()->AddOverlay(m_LogoRendering.GetPointer(), renderer4); */ } void QmitkCustomMultiWidget::InitializeDisplayActionEventHandling() { m_DisplayActionEventBroadcast = mitk::DisplayActionEventBroadcast::New(); m_DisplayActionEventBroadcast->LoadStateMachine("DisplayInteraction.xml"); - m_DisplayActionEventBroadcast->SetEventConfig("DisplayConfigMITK.xml"); + m_DisplayActionEventBroadcast->SetEventConfig("DisplayConfigPACS.xml"); - //m_CustomDisplayActionEventHandler = std::make_unique(); - //m_CustomDisplayActionEventHandler->SetObservableBroadcast(m_DisplayActionEventBroadcast); m_StdDisplayActionEventHandler = std::make_unique(); m_StdDisplayActionEventHandler->SetObservableBroadcast(m_DisplayActionEventBroadcast); m_StdDisplayActionEventHandler->InitStdActions(); } void QmitkCustomMultiWidget::AddRenderWindowWidget(int column, int row, const std::string& cornerAnnotation/* = ""*/) { // #TODO: add QSplitter? // #TODO: include technique, to set the image to level-slide on using the render window manager // create the render window widget and connect signals / slots mitk::UIDGenerator generator; std::string renderWindowUID = generator.GetUID(); QString UID = m_MultiWidgetName + "_" + QString::fromStdString(renderWindowUID); QmitkRenderWindowWidget* renderWindowWidget = new QmitkRenderWindowWidget(this, UID, m_DataStorage); renderWindowWidget->SetCornerAnnotationText(cornerAnnotation); // create connections connect(renderWindowWidget, SIGNAL(ResetView()), this, SLOT(ResetCrosshair())); connect(renderWindowWidget, SIGNAL(ChangeCrosshairRotationMode(int)), this, SLOT(SetWidgetPlaneMode(int))); // store the newly created render window widget with the UID m_RenderWindowWidgets.insert(std::pair(UID, renderWindowWidget)); // #TODO: define the grid cell to add the new render window widget // add the newly created render window widget to this multi widget m_CustomMultiWidgetLayout->addWidget(renderWindowWidget, row, column); /* auto callback = [](const itk::EventObject& displayInteractorEvent) { if (mitk::DisplayZoomEvent().CheckEvent(&displayInteractorEvent)) { std::cout << "Success: CheckEvent" << std::endl; } std::cout << "lambda called" << std::endl; }; m_CustomDisplayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayZoomEvent(nullptr, 0, mitk::Point2D()), std::move(callback)); */ } mitk::DataNode::Pointer QmitkCustomMultiWidget::GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes) { // #TODO: see T24173 return nodes->front(); }